├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── conda ├── bld.bat ├── build.sh ├── conda_build_config.yaml └── meta.yaml ├── dev_requirements.txt ├── docs ├── .buildinfo ├── .doctrees │ ├── contributing.doctree │ ├── environment.pickle │ ├── index.doctree │ ├── license.doctree │ ├── modules.doctree │ ├── nbsphinx │ │ ├── tutorials_getting_started_10_0.png │ │ ├── tutorials_getting_started_15_0.png │ │ ├── tutorials_getting_started_19_0.png │ │ ├── tutorials_getting_started_27_0.png │ │ ├── tutorials_getting_started_30_0.png │ │ ├── tutorials_getting_started_33_0.png │ │ ├── tutorials_getting_started_39_0.png │ │ ├── tutorials_getting_started_42_0.png │ │ ├── tutorials_getting_started_45_0.png │ │ ├── tutorials_getting_started_50_0.png │ │ ├── tutorials_getting_started_53_0.png │ │ ├── tutorials_getting_started_56_0.png │ │ ├── tutorials_getting_started_59_0.png │ │ ├── tutorials_getting_started_63_0.png │ │ ├── tutorials_getting_started_66_0.png │ │ ├── tutorials_getting_started_69_0.png │ │ ├── tutorials_getting_started_72_0.png │ │ ├── tutorials_getting_started_75_0.png │ │ ├── tutorials_icp_12_0.png │ │ ├── tutorials_icp_17_0.png │ │ ├── tutorials_icp_25_0.png │ │ ├── tutorials_icp_26_0.png │ │ ├── tutorials_icp_31_0.png │ │ ├── tutorials_icp_36_0.png │ │ ├── tutorials_icp_37_0.png │ │ ├── tutorials_stem_detection_14_0.png │ │ ├── tutorials_stem_detection_26_0.png │ │ ├── tutorials_stem_detection_31_0.png │ │ ├── tutorials_stem_detection_34_0.png │ │ ├── tutorials_stem_detection_37_0.png │ │ ├── tutorials_stem_detection_44_0.png │ │ └── tutorials_stem_detection_49_0.png │ ├── pyoints.doctree │ ├── pyoints.examples.doctree │ ├── pyoints.examples.file_examples.doctree │ ├── pyoints.grid.doctree │ ├── pyoints.registration.doctree │ ├── pyoints.storage.doctree │ ├── readme.doctree │ └── tutorials │ │ ├── getting_started.doctree │ │ ├── icp.doctree │ │ └── stem_detection.doctree ├── .nojekyll ├── _downloads │ ├── 367705e6cf4662ba0cd57a54d730eb25 │ │ └── icp.ipynb │ ├── 61a40433bbbf8ad0e26addb57a9fa5fd │ │ └── getting_started.py │ ├── 7dfed88757b76770b18d6dc7cb5e533e │ │ └── stem_detection.py │ ├── b36bcebf03a6dac43196225c4ab51848 │ │ └── stem_detection.ipynb │ ├── d9c564c5802e6e3aee0ea2d72df062e9 │ │ └── icp.py │ └── da8eea4cbb6a1fe449ad0bf83a4546bb │ │ └── getting_started.ipynb ├── _images │ ├── tutorials_getting_started_10_0.png │ ├── tutorials_getting_started_15_0.png │ ├── tutorials_getting_started_19_0.png │ ├── tutorials_getting_started_27_0.png │ ├── tutorials_getting_started_30_0.png │ ├── tutorials_getting_started_33_0.png │ ├── tutorials_getting_started_39_0.png │ ├── tutorials_getting_started_42_0.png │ ├── tutorials_getting_started_45_0.png │ ├── tutorials_getting_started_50_0.png │ ├── tutorials_getting_started_53_0.png │ ├── tutorials_getting_started_56_0.png │ ├── tutorials_getting_started_59_0.png │ ├── tutorials_getting_started_63_0.png │ ├── tutorials_getting_started_66_0.png │ ├── tutorials_getting_started_69_0.png │ ├── tutorials_getting_started_72_0.png │ ├── tutorials_getting_started_75_0.png │ ├── tutorials_icp_12_0.png │ ├── tutorials_icp_17_0.png │ ├── tutorials_icp_25_0.png │ ├── tutorials_icp_26_0.png │ ├── tutorials_icp_31_0.png │ ├── tutorials_icp_36_0.png │ ├── tutorials_icp_37_0.png │ ├── tutorials_stem_detection_14_0.png │ ├── tutorials_stem_detection_26_0.png │ ├── tutorials_stem_detection_31_0.png │ ├── tutorials_stem_detection_34_0.png │ ├── tutorials_stem_detection_37_0.png │ ├── tutorials_stem_detection_44_0.png │ └── tutorials_stem_detection_49_0.png ├── _modules │ ├── index.html │ └── pyoints │ │ ├── assertion.html │ │ ├── assign.html │ │ ├── classification.html │ │ ├── clustering.html │ │ ├── coords.html │ │ ├── distance.html │ │ ├── extent.html │ │ ├── filters.html │ │ ├── fit.html │ │ ├── georecords.html │ │ ├── grid │ │ ├── grid.html │ │ └── transformation.html │ │ ├── indexkd.html │ │ ├── interpolate.html │ │ ├── misc.html │ │ ├── normals.html │ │ ├── nptools.html │ │ ├── polar.html │ │ ├── projection.html │ │ ├── registration │ │ ├── icp.html │ │ ├── registration.html │ │ └── rototranslations.html │ │ ├── smoothing.html │ │ ├── storage │ │ ├── BaseGeoHandler.html │ │ ├── CsvHandler.html │ │ ├── DumpHandler.html │ │ ├── LasHandler.html │ │ ├── PlyHandler.html │ │ ├── RasterHandler.html │ │ ├── dtype_converters.html │ │ ├── misc.html │ │ └── structured.html │ │ ├── surface.html │ │ ├── transformation.html │ │ └── vector.html ├── _sources │ ├── contributing.rst.txt │ ├── index.rst.txt │ ├── license.rst.txt │ ├── modules.rst.txt │ ├── pyoints.examples.file_examples.rst.txt │ ├── pyoints.examples.rst.txt │ ├── pyoints.grid.rst.txt │ ├── pyoints.registration.rst.txt │ ├── pyoints.rst.txt │ ├── pyoints.storage.rst.txt │ ├── readme.rst.txt │ └── tutorials │ │ ├── getting_started.ipynb.txt │ │ ├── icp.ipynb.txt │ │ └── stem_detection.ipynb.txt ├── _static │ ├── ajax-loader.gif │ ├── basic.css │ ├── classic.css │ ├── comment-bright.png │ ├── comment-close.png │ ├── comment.png │ ├── doctools.js │ ├── documentation_options.js │ ├── down-pressed.png │ ├── down.png │ ├── file.png │ ├── jquery-3.2.1.js │ ├── jquery.js │ ├── logo_pyoints.png │ ├── minus.png │ ├── plus.png │ ├── pygments.css │ ├── searchtools.js │ ├── sidebar.js │ ├── underscore-1.3.1.js │ ├── underscore.js │ ├── up-pressed.png │ ├── up.png │ └── websupport.js ├── contributing.html ├── genindex.html ├── index.html ├── license.html ├── modules.html ├── objects.inv ├── py-modindex.html ├── pyoints.examples.file_examples.html ├── pyoints.examples.html ├── pyoints.grid.html ├── pyoints.html ├── pyoints.registration.html ├── pyoints.storage.html ├── readme.html ├── search.html ├── searchindex.js └── tutorials │ ├── getting_started.html │ ├── icp.html │ └── stem_detection.html ├── figures ├── logo_pyoints.odg └── logo_pyoints.png ├── paper ├── paper.bib └── paper.md ├── pyoints ├── __init__.py ├── about.py ├── assertion.py ├── assign.py ├── classification.py ├── clustering.py ├── coords.py ├── distance.py ├── examples │ ├── __init__.py │ ├── base_example.py │ ├── data │ │ ├── forest.las │ │ └── logo_pyoints.jpg │ ├── file_examples │ │ ├── __init__.py │ │ ├── csv_example.py │ │ ├── dump_example.py │ │ ├── las_example.py │ │ ├── ply_example.py │ │ ├── raster_example.py │ │ └── structured_example.py │ ├── grid_example.py │ ├── output │ │ └── .gitignore │ └── stemfilter_example.py ├── extent.py ├── filters.py ├── fit.py ├── georecords.py ├── grid │ ├── __init__.py │ ├── grid.py │ └── transformation.py ├── indexkd.py ├── interpolate.py ├── misc.py ├── normals.py ├── nptools.py ├── polar.py ├── projection.py ├── registration │ ├── __init__.py │ ├── icp.py │ ├── registration.py │ └── rototranslations.py ├── smoothing.py ├── storage │ ├── BaseGeoHandler.py │ ├── CsvHandler.py │ ├── DumpHandler.py │ ├── LasHandler.py │ ├── PlyHandler.py │ ├── RasterHandler.py │ ├── __init__.py │ ├── dtype_converters.py │ ├── misc.py │ └── structured.py ├── surface.py ├── transformation.py └── vector.py ├── requirements.txt ├── scripts ├── autopep8.sh ├── conda_build.sh ├── generateDocs.sh ├── templates │ ├── GPL_LICENSE.txt │ └── LICENSE_NOTES.txt └── updateLicense.sh ├── setup.py ├── sphinx ├── .gitignore ├── Makefile ├── conf.py ├── contributing.rst ├── index.rst ├── license.rst ├── make.bat └── readme.rst ├── tests └── test_pyoints.py └── tutorials ├── .gitignore ├── bun000_binary.ply ├── bun045_binary.ply ├── bun090_binary.ply ├── forest.las ├── getting_started.ipynb ├── icp.ipynb └── stem_detection.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | # Generic files to ignore 2 | *~ 3 | build 4 | venv* 5 | dist 6 | *.egg 7 | *.egg-info 8 | 9 | # Python specific 10 | *.pyc 11 | 12 | # IDE 13 | .spyproject 14 | .spyderproject* 15 | 16 | sphinx-build 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We gladly invite you to contribute to *Pyoints* by making suggestions, report 4 | bugs, or making changes on the code. Contributions can be made via your GitHub 5 | account. 6 | 7 | 8 | ## Making Suggestions 9 | 10 | You can make suggestions or report bugs by using the issue board on GitHub or 11 | by sensing an email to the package maintainer(s). 12 | 13 | 14 | ## Making Changes 15 | 16 | To contribute your code, please fork *Pyoints* on GitHub. Push your changes 17 | with meaningfull commit messages to a topic branch. Then create a pull request 18 | to propose to incorporate your changes to the main project. To ensure a uniform 19 | quality of code, please follow our [coding conventions](#coding-conventions). 20 | 21 | 22 | ## Coding Conventions 23 | 24 | 25 | ### Style Guide. 26 | 27 | Please follow the [PEP8](https://www.python.org/dev/peps/pep-0008/) style 28 | guidelines. 29 | 30 | ### Documentation 31 | 32 | Please make sure to document your contributed code appropiatly. The *Pyoints* 33 | project uses *Docstrings* 34 | ([PEP257](https://www.python.org/dev/peps/pep-0257/) 35 | to document the code of each class or function. The documentation and comments 36 | must be written in English. Wherever possible, the *Examples* section should 37 | illustrate the basic usage of the code. The examples should also cover special 38 | cases, which might reveal unexpected behavior or major bugs. 39 | 40 | ### Testing 41 | 42 | To test the software please take a look at the [tests](pyoints/tests) 43 | directory. Currently doctests are used to test the functionality of a majority 44 | of *Pyoints* classes and functions. You can run the file 45 | ``tests/test_pyoints.py`` to run the doctests. After adding a new file, please 46 | add a *doctest* reference to ``tests/test_pyoints.py``. To increase the quality 47 | of our code we encourage you to write additional tests. 48 | 49 | 50 | ## Software recommendations 51 | 52 | The following Python packages were used for software development, testing and 53 | documentation. 54 | 55 | 56 | ### autopep8 57 | 58 | Hideo Hattori 59 | * [PyPI](https://pypi.org/project/autopep8/) 60 | * [homepage](https://github.com/hhatto/autopep8) 61 | * [MIT compatible license](https://github.com/matplotlib/matplotlib/blob/master/LICENSE/LICENSE) 62 | 63 | 64 | ### CloudCompare 65 | 66 | Daniel Girardeau-Montaut 67 | * [homepage](https://www.danielgm.net/cc/) 68 | * [GPL v2](https://github.com/CloudCompare/CloudCompare/blob/master/license.txt) 69 | 70 | 71 | ### matplotlib 72 | 73 | John D. Hunter, Michael Droettboom 74 | * [PyPI](https://pypi.org/project/matplotlib/) 75 | * [homepage](https://matplotlib.org/) 76 | * [BSD compatible license](https://github.com/matplotlib/matplotlib/blob/master/LICENSE/LICENSE) 77 | 78 | 79 | ### pycodestyle 80 | 81 | Ian Lee 82 | * [PyPI](https://pypi.org/project/pycodestyle/) 83 | * [homepage](https://pycodestyle.readthedocs.io/en/latest/) 84 | * [Expat license](https://pycodestyle.readthedocs.io/en/latest/index.html#license) 85 | 86 | 87 | ### Sphinx 88 | 89 | Georg Brandl 90 | * [PyPI](https://pypi.org/project/Sphinx/) 91 | * [homepage](http://www.sphinx-doc.org/en/master/) 92 | * [3-Clause BSD license](https://github.com/sphinx-doc/sphinx) 93 | 94 | 95 | ### sphinxcontrib-napoleon 96 | 97 | Rob Ruana 98 | * [PyPI](https://pypi.org/project/sphinxcontrib-napoleon/) 99 | * [homepage](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/) 100 | * [2-Clause BSD license](https://github.com/sphinx-contrib/napoleon/blob/master/LICENSE) 101 | 102 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include requirements.txt 3 | include pyoints/examples/data/* 4 | include pyoints/examples/output/.gitignore -------------------------------------------------------------------------------- /conda/bld.bat: -------------------------------------------------------------------------------- 1 | "%PYTHON%" -m pip install --upgrade pip 2 | "%PYTHON%" -m pip install . -vvv 3 | if errorlevel 1 exit 1 -------------------------------------------------------------------------------- /conda/build.sh: -------------------------------------------------------------------------------- 1 | $PYTHON -m pip install --upgrade pip 2 | $PYTHON -m pip install . -vvv -------------------------------------------------------------------------------- /conda/conda_build_config.yaml: -------------------------------------------------------------------------------- 1 | python: 2 | - 3.5 3 | - 3.6 4 | - 3.7 -------------------------------------------------------------------------------- /conda/meta.yaml: -------------------------------------------------------------------------------- 1 | {% set name = "pyoints" %} 2 | {% set data = load_setup_py_data() %} 3 | 4 | 5 | package: 6 | name: {{ name }} 7 | version: {{ data.get('version') }} 8 | 9 | source: 10 | git_url: "../.git" 11 | 12 | build: 13 | #number: 0 14 | skip: True # [not py3k] 15 | 16 | requirements: 17 | host: 18 | - python {{ python }} 19 | - pip 20 | - gdal # pygdal is not able to detect gdal version 21 | - numpy>=1.15 22 | run: 23 | - python {{ python }} 24 | - {{ pin_compatible('gdal') }} 25 | - {{ pin_compatible('numpy') }} 26 | - pyproj # pyproj via pip fails for Windows 27 | - rtree # rtree via pip fails for Windows 28 | 29 | test: 30 | imports: 31 | - numpy 32 | - osgeo 33 | - cv2 34 | - laspy 35 | - plyfile 36 | - pyproj 37 | - rtree 38 | - cylinder_fitting 39 | - pyoints 40 | - pyoints.examples 41 | - pyoints.grid 42 | - pyoints.registration 43 | - pyoints.storage 44 | 45 | about: 46 | home: https://laempy.github.io/pyoints/modules.html 47 | license: GNU General Public v3 or later (GPLv3+) 48 | license_family: GPL3 49 | license_file: ../LICENSE 50 | summary: A Python package for point cloud, voxel and raster processing. 51 | doc_url: https://laempy.github.io/pyoints/pyoints.html 52 | dev_url: https://github.com/laempy/pyoints 53 | 54 | extra: 55 | recipe-maintainers: 56 | - laempy -------------------------------------------------------------------------------- /dev_requirements.txt: -------------------------------------------------------------------------------- 1 | pip 2 | setuptools 3 | wheel 4 | twine 5 | autopep8 6 | pycodestyle 7 | matplotlib 8 | ipykernel 9 | cloudpickle 10 | m2r 11 | Sphinx 12 | sphinxcontrib-napoleon 13 | nbsphinx -------------------------------------------------------------------------------- /docs/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: 714106d4c09c0078c9135c87e983a4ee 4 | tags: 645f666f9bcd5a90fca523b33c5a78b7 5 | -------------------------------------------------------------------------------- /docs/.doctrees/contributing.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/contributing.doctree -------------------------------------------------------------------------------- /docs/.doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/.doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/index.doctree -------------------------------------------------------------------------------- /docs/.doctrees/license.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/license.doctree -------------------------------------------------------------------------------- /docs/.doctrees/modules.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/modules.doctree -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_10_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_10_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_15_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_15_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_19_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_19_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_27_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_27_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_30_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_30_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_33_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_33_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_39_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_39_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_42_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_42_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_45_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_45_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_50_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_50_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_53_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_53_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_56_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_56_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_59_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_59_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_63_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_63_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_66_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_66_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_69_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_69_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_72_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_72_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_getting_started_75_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_getting_started_75_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_icp_12_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_icp_12_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_icp_17_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_icp_17_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_icp_25_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_icp_25_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_icp_26_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_icp_26_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_icp_31_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_icp_31_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_icp_36_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_icp_36_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_icp_37_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_icp_37_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_stem_detection_14_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_stem_detection_14_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_stem_detection_26_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_stem_detection_26_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_stem_detection_31_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_stem_detection_31_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_stem_detection_34_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_stem_detection_34_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_stem_detection_37_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_stem_detection_37_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_stem_detection_44_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_stem_detection_44_0.png -------------------------------------------------------------------------------- /docs/.doctrees/nbsphinx/tutorials_stem_detection_49_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/nbsphinx/tutorials_stem_detection_49_0.png -------------------------------------------------------------------------------- /docs/.doctrees/pyoints.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/pyoints.doctree -------------------------------------------------------------------------------- /docs/.doctrees/pyoints.examples.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/pyoints.examples.doctree -------------------------------------------------------------------------------- /docs/.doctrees/pyoints.examples.file_examples.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/pyoints.examples.file_examples.doctree -------------------------------------------------------------------------------- /docs/.doctrees/pyoints.grid.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/pyoints.grid.doctree -------------------------------------------------------------------------------- /docs/.doctrees/pyoints.registration.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/pyoints.registration.doctree -------------------------------------------------------------------------------- /docs/.doctrees/pyoints.storage.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/pyoints.storage.doctree -------------------------------------------------------------------------------- /docs/.doctrees/readme.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/readme.doctree -------------------------------------------------------------------------------- /docs/.doctrees/tutorials/getting_started.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/tutorials/getting_started.doctree -------------------------------------------------------------------------------- /docs/.doctrees/tutorials/icp.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/tutorials/icp.doctree -------------------------------------------------------------------------------- /docs/.doctrees/tutorials/stem_detection.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.doctrees/tutorials/stem_detection.doctree -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/.nojekyll -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_10_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_10_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_15_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_15_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_19_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_19_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_27_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_27_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_30_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_30_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_33_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_33_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_39_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_39_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_42_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_42_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_45_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_45_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_50_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_50_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_53_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_53_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_56_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_56_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_59_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_59_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_63_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_63_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_66_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_66_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_69_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_69_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_72_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_72_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_getting_started_75_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_getting_started_75_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_icp_12_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_icp_12_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_icp_17_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_icp_17_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_icp_25_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_icp_25_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_icp_26_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_icp_26_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_icp_31_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_icp_31_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_icp_36_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_icp_36_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_icp_37_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_icp_37_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_stem_detection_14_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_stem_detection_14_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_stem_detection_26_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_stem_detection_26_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_stem_detection_31_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_stem_detection_31_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_stem_detection_34_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_stem_detection_34_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_stem_detection_37_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_stem_detection_37_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_stem_detection_44_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_stem_detection_44_0.png -------------------------------------------------------------------------------- /docs/_images/tutorials_stem_detection_49_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_images/tutorials_stem_detection_49_0.png -------------------------------------------------------------------------------- /docs/_modules/index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | Overview: module code — Pyoints 0.2.0 documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 33 | 34 |
35 |
36 | 79 |
80 | 99 |
100 |
101 | 113 | 117 | 118 | -------------------------------------------------------------------------------- /docs/_sources/contributing.rst.txt: -------------------------------------------------------------------------------- 1 | .. mdinclude:: ../CONTRIBUTING.md -------------------------------------------------------------------------------- /docs/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | Pyoints Documentation 2 | ===================== 3 | 4 | 5 | ******** 6 | Overview 7 | ******** 8 | 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | readme 14 | contributing 15 | 16 | .. toctree:: 17 | :titlesonly: 18 | 19 | license 20 | 21 | 22 | ********* 23 | Tutorials 24 | ********* 25 | 26 | .. toctree:: 27 | :maxdepth: 2 28 | 29 | tutorials/getting_started.ipynb 30 | tutorials/stem_detection.ipynb 31 | tutorials/icp.ipynb 32 | 33 | 34 | .. csv-table:: Downloads 35 | :header: "Topic", "Jupiter Notebook", "Python Script" 36 | :widths: 20, 20, 20 37 | 38 | "Getting started", :download:`getting_started.ipynb `, :download:`getting_started.py ` 39 | "ICP", :download:`icp.ipynb `, :download:`icp.py ` 40 | "Stem detection", :download:`stem_detection.ipynb `, :download:`stem_detection.py ` 41 | 42 | 43 | *** 44 | API 45 | *** 46 | 47 | .. toctree:: 48 | :maxdepth: 4 49 | 50 | modules 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /docs/_sources/license.rst.txt: -------------------------------------------------------------------------------- 1 | LICENSE 2 | ======= 3 | 4 | .. mdinclude:: ../LICENSE -------------------------------------------------------------------------------- /docs/_sources/modules.rst.txt: -------------------------------------------------------------------------------- 1 | pyoints 2 | ======= 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | pyoints 8 | -------------------------------------------------------------------------------- /docs/_sources/pyoints.examples.file_examples.rst.txt: -------------------------------------------------------------------------------- 1 | pyoints.examples.file\_examples package 2 | ======================================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyoints.examples.file\_examples.csv\_example module 8 | --------------------------------------------------- 9 | 10 | .. automodule:: pyoints.examples.file_examples.csv_example 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyoints.examples.file\_examples.dump\_example module 16 | ---------------------------------------------------- 17 | 18 | .. automodule:: pyoints.examples.file_examples.dump_example 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pyoints.examples.file\_examples.las\_example module 24 | --------------------------------------------------- 25 | 26 | .. automodule:: pyoints.examples.file_examples.las_example 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | pyoints.examples.file\_examples.ply\_example module 32 | --------------------------------------------------- 33 | 34 | .. automodule:: pyoints.examples.file_examples.ply_example 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | pyoints.examples.file\_examples.raster\_example module 40 | ------------------------------------------------------ 41 | 42 | .. automodule:: pyoints.examples.file_examples.raster_example 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | pyoints.examples.file\_examples.structured\_example module 48 | ---------------------------------------------------------- 49 | 50 | .. automodule:: pyoints.examples.file_examples.structured_example 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | 56 | Module contents 57 | --------------- 58 | 59 | .. automodule:: pyoints.examples.file_examples 60 | :members: 61 | :undoc-members: 62 | :show-inheritance: 63 | -------------------------------------------------------------------------------- /docs/_sources/pyoints.examples.rst.txt: -------------------------------------------------------------------------------- 1 | pyoints.examples package 2 | ======================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | pyoints.examples.file_examples 10 | 11 | Submodules 12 | ---------- 13 | 14 | pyoints.examples.base\_example module 15 | ------------------------------------- 16 | 17 | .. automodule:: pyoints.examples.base_example 18 | :members: 19 | :undoc-members: 20 | :show-inheritance: 21 | 22 | pyoints.examples.grid\_example module 23 | ------------------------------------- 24 | 25 | .. automodule:: pyoints.examples.grid_example 26 | :members: 27 | :undoc-members: 28 | :show-inheritance: 29 | 30 | pyoints.examples.stemfilter\_example module 31 | ------------------------------------------- 32 | 33 | .. automodule:: pyoints.examples.stemfilter_example 34 | :members: 35 | :undoc-members: 36 | :show-inheritance: 37 | 38 | 39 | Module contents 40 | --------------- 41 | 42 | .. automodule:: pyoints.examples 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | -------------------------------------------------------------------------------- /docs/_sources/pyoints.grid.rst.txt: -------------------------------------------------------------------------------- 1 | pyoints.grid package 2 | ==================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyoints.grid.grid module 8 | ------------------------ 9 | 10 | .. automodule:: pyoints.grid.grid 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyoints.grid.transformation module 16 | ---------------------------------- 17 | 18 | .. automodule:: pyoints.grid.transformation 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: pyoints.grid 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/_sources/pyoints.registration.rst.txt: -------------------------------------------------------------------------------- 1 | pyoints.registration package 2 | ============================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyoints.registration.icp module 8 | ------------------------------- 9 | 10 | .. automodule:: pyoints.registration.icp 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyoints.registration.registration module 16 | ---------------------------------------- 17 | 18 | .. automodule:: pyoints.registration.registration 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pyoints.registration.rototranslations module 24 | -------------------------------------------- 25 | 26 | .. automodule:: pyoints.registration.rototranslations 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | 32 | Module contents 33 | --------------- 34 | 35 | .. automodule:: pyoints.registration 36 | :members: 37 | :undoc-members: 38 | :show-inheritance: 39 | -------------------------------------------------------------------------------- /docs/_sources/pyoints.rst.txt: -------------------------------------------------------------------------------- 1 | pyoints package 2 | =============== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | pyoints.examples 10 | pyoints.grid 11 | pyoints.registration 12 | pyoints.storage 13 | 14 | Submodules 15 | ---------- 16 | 17 | pyoints.about module 18 | -------------------- 19 | 20 | .. automodule:: pyoints.about 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | 25 | pyoints.assertion module 26 | ------------------------ 27 | 28 | .. automodule:: pyoints.assertion 29 | :members: 30 | :undoc-members: 31 | :show-inheritance: 32 | 33 | pyoints.assign module 34 | --------------------- 35 | 36 | .. automodule:: pyoints.assign 37 | :members: 38 | :undoc-members: 39 | :show-inheritance: 40 | 41 | pyoints.classification module 42 | ----------------------------- 43 | 44 | .. automodule:: pyoints.classification 45 | :members: 46 | :undoc-members: 47 | :show-inheritance: 48 | 49 | pyoints.clustering module 50 | ------------------------- 51 | 52 | .. automodule:: pyoints.clustering 53 | :members: 54 | :undoc-members: 55 | :show-inheritance: 56 | 57 | pyoints.coords module 58 | --------------------- 59 | 60 | .. automodule:: pyoints.coords 61 | :members: 62 | :undoc-members: 63 | :show-inheritance: 64 | 65 | pyoints.distance module 66 | ----------------------- 67 | 68 | .. automodule:: pyoints.distance 69 | :members: 70 | :undoc-members: 71 | :show-inheritance: 72 | 73 | pyoints.extent module 74 | --------------------- 75 | 76 | .. automodule:: pyoints.extent 77 | :members: 78 | :undoc-members: 79 | :show-inheritance: 80 | 81 | pyoints.filters module 82 | ---------------------- 83 | 84 | .. automodule:: pyoints.filters 85 | :members: 86 | :undoc-members: 87 | :show-inheritance: 88 | 89 | pyoints.fit module 90 | ------------------ 91 | 92 | .. automodule:: pyoints.fit 93 | :members: 94 | :undoc-members: 95 | :show-inheritance: 96 | 97 | pyoints.georecords module 98 | ------------------------- 99 | 100 | .. automodule:: pyoints.georecords 101 | :members: 102 | :undoc-members: 103 | :show-inheritance: 104 | 105 | pyoints.indexkd module 106 | ---------------------- 107 | 108 | .. automodule:: pyoints.indexkd 109 | :members: 110 | :undoc-members: 111 | :show-inheritance: 112 | 113 | pyoints.interpolate module 114 | -------------------------- 115 | 116 | .. automodule:: pyoints.interpolate 117 | :members: 118 | :undoc-members: 119 | :show-inheritance: 120 | 121 | pyoints.misc module 122 | ------------------- 123 | 124 | .. automodule:: pyoints.misc 125 | :members: 126 | :undoc-members: 127 | :show-inheritance: 128 | 129 | pyoints.normals module 130 | ---------------------- 131 | 132 | .. automodule:: pyoints.normals 133 | :members: 134 | :undoc-members: 135 | :show-inheritance: 136 | 137 | pyoints.nptools module 138 | ---------------------- 139 | 140 | .. automodule:: pyoints.nptools 141 | :members: 142 | :undoc-members: 143 | :show-inheritance: 144 | 145 | pyoints.polar module 146 | -------------------- 147 | 148 | .. automodule:: pyoints.polar 149 | :members: 150 | :undoc-members: 151 | :show-inheritance: 152 | 153 | pyoints.projection module 154 | ------------------------- 155 | 156 | .. automodule:: pyoints.projection 157 | :members: 158 | :undoc-members: 159 | :show-inheritance: 160 | 161 | pyoints.smoothing module 162 | ------------------------ 163 | 164 | .. automodule:: pyoints.smoothing 165 | :members: 166 | :undoc-members: 167 | :show-inheritance: 168 | 169 | pyoints.surface module 170 | ---------------------- 171 | 172 | .. automodule:: pyoints.surface 173 | :members: 174 | :undoc-members: 175 | :show-inheritance: 176 | 177 | pyoints.transformation module 178 | ----------------------------- 179 | 180 | .. automodule:: pyoints.transformation 181 | :members: 182 | :undoc-members: 183 | :show-inheritance: 184 | 185 | pyoints.vector module 186 | --------------------- 187 | 188 | .. automodule:: pyoints.vector 189 | :members: 190 | :undoc-members: 191 | :show-inheritance: 192 | 193 | 194 | Module contents 195 | --------------- 196 | 197 | .. automodule:: pyoints 198 | :members: 199 | :undoc-members: 200 | :show-inheritance: 201 | -------------------------------------------------------------------------------- /docs/_sources/pyoints.storage.rst.txt: -------------------------------------------------------------------------------- 1 | pyoints.storage package 2 | ======================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyoints.storage.BaseGeoHandler module 8 | ------------------------------------- 9 | 10 | .. automodule:: pyoints.storage.BaseGeoHandler 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyoints.storage.CsvHandler module 16 | --------------------------------- 17 | 18 | .. automodule:: pyoints.storage.CsvHandler 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pyoints.storage.DumpHandler module 24 | ---------------------------------- 25 | 26 | .. automodule:: pyoints.storage.DumpHandler 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | pyoints.storage.LasHandler module 32 | --------------------------------- 33 | 34 | .. automodule:: pyoints.storage.LasHandler 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | pyoints.storage.PlyHandler module 40 | --------------------------------- 41 | 42 | .. automodule:: pyoints.storage.PlyHandler 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | pyoints.storage.RasterHandler module 48 | ------------------------------------ 49 | 50 | .. automodule:: pyoints.storage.RasterHandler 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | pyoints.storage.dtype\_converters module 56 | ---------------------------------------- 57 | 58 | .. automodule:: pyoints.storage.dtype_converters 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | pyoints.storage.misc module 64 | --------------------------- 65 | 66 | .. automodule:: pyoints.storage.misc 67 | :members: 68 | :undoc-members: 69 | :show-inheritance: 70 | 71 | pyoints.storage.structured module 72 | --------------------------------- 73 | 74 | .. automodule:: pyoints.storage.structured 75 | :members: 76 | :undoc-members: 77 | :show-inheritance: 78 | 79 | 80 | Module contents 81 | --------------- 82 | 83 | .. automodule:: pyoints.storage 84 | :members: 85 | :undoc-members: 86 | :show-inheritance: 87 | -------------------------------------------------------------------------------- /docs/_sources/readme.rst.txt: -------------------------------------------------------------------------------- 1 | .. mdinclude:: ../README.md -------------------------------------------------------------------------------- /docs/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_static/ajax-loader.gif -------------------------------------------------------------------------------- /docs/_static/classic.css: -------------------------------------------------------------------------------- 1 | /* 2 | * classic.css_t 3 | * ~~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- classic theme. 6 | * 7 | * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: sans-serif; 18 | font-size: 100%; 19 | background-color: #11303d; 20 | color: #000; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.document { 26 | background-color: #1c4e63; 27 | } 28 | 29 | div.documentwrapper { 30 | float: left; 31 | width: 100%; 32 | } 33 | 34 | div.bodywrapper { 35 | margin: 0 0 0 230px; 36 | } 37 | 38 | div.body { 39 | background-color: #ffffff; 40 | color: #000000; 41 | padding: 0 20px 30px 20px; 42 | } 43 | 44 | div.footer { 45 | color: #ffffff; 46 | width: 100%; 47 | padding: 9px 0 9px 0; 48 | text-align: center; 49 | font-size: 75%; 50 | } 51 | 52 | div.footer a { 53 | color: #ffffff; 54 | text-decoration: underline; 55 | } 56 | 57 | div.related { 58 | background-color: #133f52; 59 | line-height: 30px; 60 | color: #ffffff; 61 | } 62 | 63 | div.related a { 64 | color: #ffffff; 65 | } 66 | 67 | div.sphinxsidebar { 68 | } 69 | 70 | div.sphinxsidebar h3 { 71 | font-family: 'Trebuchet MS', sans-serif; 72 | color: #ffffff; 73 | font-size: 1.4em; 74 | font-weight: normal; 75 | margin: 0; 76 | padding: 0; 77 | } 78 | 79 | div.sphinxsidebar h3 a { 80 | color: #ffffff; 81 | } 82 | 83 | div.sphinxsidebar h4 { 84 | font-family: 'Trebuchet MS', sans-serif; 85 | color: #ffffff; 86 | font-size: 1.3em; 87 | font-weight: normal; 88 | margin: 5px 0 0 0; 89 | padding: 0; 90 | } 91 | 92 | div.sphinxsidebar p { 93 | color: #ffffff; 94 | } 95 | 96 | div.sphinxsidebar p.topless { 97 | margin: 5px 10px 10px 10px; 98 | } 99 | 100 | div.sphinxsidebar ul { 101 | margin: 10px; 102 | padding: 0; 103 | color: #ffffff; 104 | } 105 | 106 | div.sphinxsidebar a { 107 | color: #98dbcc; 108 | } 109 | 110 | div.sphinxsidebar input { 111 | border: 1px solid #98dbcc; 112 | font-family: sans-serif; 113 | font-size: 1em; 114 | } 115 | 116 | 117 | 118 | /* -- hyperlink styles ------------------------------------------------------ */ 119 | 120 | a { 121 | color: #355f7c; 122 | text-decoration: none; 123 | } 124 | 125 | a:visited { 126 | color: #355f7c; 127 | text-decoration: none; 128 | } 129 | 130 | a:hover { 131 | text-decoration: underline; 132 | } 133 | 134 | 135 | 136 | /* -- body styles ----------------------------------------------------------- */ 137 | 138 | div.body h1, 139 | div.body h2, 140 | div.body h3, 141 | div.body h4, 142 | div.body h5, 143 | div.body h6 { 144 | font-family: 'Trebuchet MS', sans-serif; 145 | background-color: #f2f2f2; 146 | font-weight: normal; 147 | color: #20435c; 148 | border-bottom: 1px solid #ccc; 149 | margin: 20px -20px 10px -20px; 150 | padding: 3px 0 3px 10px; 151 | } 152 | 153 | div.body h1 { margin-top: 0; font-size: 200%; } 154 | div.body h2 { font-size: 160%; } 155 | div.body h3 { font-size: 140%; } 156 | div.body h4 { font-size: 120%; } 157 | div.body h5 { font-size: 110%; } 158 | div.body h6 { font-size: 100%; } 159 | 160 | a.headerlink { 161 | color: #c60f0f; 162 | font-size: 0.8em; 163 | padding: 0 4px 0 4px; 164 | text-decoration: none; 165 | } 166 | 167 | a.headerlink:hover { 168 | background-color: #c60f0f; 169 | color: white; 170 | } 171 | 172 | div.body p, div.body dd, div.body li, div.body blockquote { 173 | text-align: justify; 174 | line-height: 130%; 175 | } 176 | 177 | div.admonition p.admonition-title + p { 178 | display: inline; 179 | } 180 | 181 | div.admonition p { 182 | margin-bottom: 5px; 183 | } 184 | 185 | div.admonition pre { 186 | margin-bottom: 5px; 187 | } 188 | 189 | div.admonition ul, div.admonition ol { 190 | margin-bottom: 5px; 191 | } 192 | 193 | div.note { 194 | background-color: #eee; 195 | border: 1px solid #ccc; 196 | } 197 | 198 | div.seealso { 199 | background-color: #ffc; 200 | border: 1px solid #ff6; 201 | } 202 | 203 | div.topic { 204 | background-color: #eee; 205 | } 206 | 207 | div.warning { 208 | background-color: #ffe4e4; 209 | border: 1px solid #f66; 210 | } 211 | 212 | p.admonition-title { 213 | display: inline; 214 | } 215 | 216 | p.admonition-title:after { 217 | content: ":"; 218 | } 219 | 220 | pre { 221 | padding: 5px; 222 | background-color: #eeffcc; 223 | color: #333333; 224 | line-height: 120%; 225 | border: 1px solid #ac9; 226 | border-left: none; 227 | border-right: none; 228 | } 229 | 230 | code { 231 | background-color: #ecf0f3; 232 | padding: 0 1px 0 1px; 233 | font-size: 0.95em; 234 | } 235 | 236 | th { 237 | background-color: #ede; 238 | } 239 | 240 | .warning code { 241 | background: #efc2c2; 242 | } 243 | 244 | .note code { 245 | background: #d6d6d6; 246 | } 247 | 248 | .viewcode-back { 249 | font-family: sans-serif; 250 | } 251 | 252 | div.viewcode-block:target { 253 | background-color: #f4debf; 254 | border-top: 1px solid #ac9; 255 | border-bottom: 1px solid #ac9; 256 | } 257 | 258 | div.code-block-caption { 259 | color: #efefef; 260 | background-color: #1c4e63; 261 | } -------------------------------------------------------------------------------- /docs/_static/comment-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_static/comment-bright.png -------------------------------------------------------------------------------- /docs/_static/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_static/comment-close.png -------------------------------------------------------------------------------- /docs/_static/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_static/comment.png -------------------------------------------------------------------------------- /docs/_static/down-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_static/down-pressed.png -------------------------------------------------------------------------------- /docs/_static/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_static/down.png -------------------------------------------------------------------------------- /docs/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_static/file.png -------------------------------------------------------------------------------- /docs/_static/logo_pyoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_static/logo_pyoints.png -------------------------------------------------------------------------------- /docs/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_static/minus.png -------------------------------------------------------------------------------- /docs/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/docs/_static/plus.png -------------------------------------------------------------------------------- /docs/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .ch { color: #408090; font-style: italic } /* Comment.Hashbang */ 8 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 9 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 10 | .highlight .cpf { color: #408090; font-style: italic } /* Comment.PreprocFile */ 11 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 12 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 13 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 14 | .highlight .ge { font-style: italic } /* Generic.Emph */ 15 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 16 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 17 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 18 | .highlight .go { color: #333333 } /* Generic.Output */ 19 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 20 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 21 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 22 | .highlight .gt { color: #0044DD } /* Generic.Traceback */ 23 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 24 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 25 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 26 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 27 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 28 | .highlight .kt { color: #902000 } /* Keyword.Type */ 29 | .highlight .m { color: #208050 } /* Literal.Number */ 30 | .highlight .s { color: #4070a0 } /* Literal.String */ 31 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 32 | .highlight .nb { color: #007020 } /* Name.Builtin */ 33 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 34 | .highlight .no { color: #60add5 } /* Name.Constant */ 35 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 36 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 37 | .highlight .ne { color: #007020 } /* Name.Exception */ 38 | .highlight .nf { color: #06287e } /* Name.Function */ 39 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 40 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 41 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 42 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 43 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 44 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 45 | .highlight .mb { color: #208050 } /* Literal.Number.Bin */ 46 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 47 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 48 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 49 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 50 | .highlight .sa { color: #4070a0 } /* Literal.String.Affix */ 51 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 52 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 53 | .highlight .dl { color: #4070a0 } /* Literal.String.Delimiter */ 54 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 55 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 56 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 57 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 58 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 59 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 60 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 61 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 62 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 63 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 64 | .highlight .fm { color: #06287e } /* Name.Function.Magic */ 65 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 66 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 67 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 68 | .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ 69 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_static/sidebar.js: -------------------------------------------------------------------------------- 1 | /* 2 | * sidebar.js 3 | * ~~~~~~~~~~ 4 | * 5 | * This script makes the Sphinx sidebar collapsible. 6 | * 7 | * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds 8 | * in .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton 9 | * used to collapse and expand the sidebar. 10 | * 11 | * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden 12 | * and the width of the sidebar and the margin-left of the document 13 | * are decreased. When the sidebar is expanded the opposite happens. 14 | * This script saves a per-browser/per-session cookie used to 15 | * remember the position of the sidebar among the pages. 16 | * Once the browser is closed the cookie is deleted and the position 17 | * reset to the default (expanded). 18 | * 19 | * :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. 20 | * :license: BSD, see LICENSE for details. 21 | * 22 | */ 23 | 24 | $(function() { 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | // global elements used by the functions. 34 | // the 'sidebarbutton' element is defined as global after its 35 | // creation, in the add_sidebar_button function 36 | var bodywrapper = $('.bodywrapper'); 37 | var sidebar = $('.sphinxsidebar'); 38 | var sidebarwrapper = $('.sphinxsidebarwrapper'); 39 | 40 | // for some reason, the document has no sidebar; do not run into errors 41 | if (!sidebar.length) return; 42 | 43 | // original margin-left of the bodywrapper and width of the sidebar 44 | // with the sidebar expanded 45 | var bw_margin_expanded = bodywrapper.css('margin-left'); 46 | var ssb_width_expanded = sidebar.width(); 47 | 48 | // margin-left of the bodywrapper and width of the sidebar 49 | // with the sidebar collapsed 50 | var bw_margin_collapsed = '.8em'; 51 | var ssb_width_collapsed = '.8em'; 52 | 53 | // colors used by the current theme 54 | var dark_color = $('.related').css('background-color'); 55 | var light_color = $('.document').css('background-color'); 56 | 57 | function sidebar_is_collapsed() { 58 | return sidebarwrapper.is(':not(:visible)'); 59 | } 60 | 61 | function toggle_sidebar() { 62 | if (sidebar_is_collapsed()) 63 | expand_sidebar(); 64 | else 65 | collapse_sidebar(); 66 | } 67 | 68 | function collapse_sidebar() { 69 | sidebarwrapper.hide(); 70 | sidebar.css('width', ssb_width_collapsed); 71 | bodywrapper.css('margin-left', bw_margin_collapsed); 72 | sidebarbutton.css({ 73 | 'margin-left': '0', 74 | 'height': bodywrapper.height() 75 | }); 76 | sidebarbutton.find('span').text('»'); 77 | sidebarbutton.attr('title', _('Expand sidebar')); 78 | document.cookie = 'sidebar=collapsed'; 79 | } 80 | 81 | function expand_sidebar() { 82 | bodywrapper.css('margin-left', bw_margin_expanded); 83 | sidebar.css('width', ssb_width_expanded); 84 | sidebarwrapper.show(); 85 | sidebarbutton.css({ 86 | 'margin-left': ssb_width_expanded-12, 87 | 'height': bodywrapper.height() 88 | }); 89 | sidebarbutton.find('span').text('«'); 90 | sidebarbutton.attr('title', _('Collapse sidebar')); 91 | document.cookie = 'sidebar=expanded'; 92 | } 93 | 94 | function add_sidebar_button() { 95 | sidebarwrapper.css({ 96 | 'float': 'left', 97 | 'margin-right': '0', 98 | 'width': ssb_width_expanded - 28 99 | }); 100 | // create the button 101 | sidebar.append( 102 | '
«
' 103 | ); 104 | var sidebarbutton = $('#sidebarbutton'); 105 | light_color = sidebarbutton.css('background-color'); 106 | // find the height of the viewport to center the '<<' in the page 107 | var viewport_height; 108 | if (window.innerHeight) 109 | viewport_height = window.innerHeight; 110 | else 111 | viewport_height = $(window).height(); 112 | sidebarbutton.find('span').css({ 113 | 'display': 'block', 114 | 'margin-top': (viewport_height - sidebar.position().top - 20) / 2 115 | }); 116 | 117 | sidebarbutton.click(toggle_sidebar); 118 | sidebarbutton.attr('title', _('Collapse sidebar')); 119 | sidebarbutton.css({ 120 | 'color': '#FFFFFF', 121 | 'border-left': '1px solid ' + dark_color, 122 | 'font-size': '1.2em', 123 | 'cursor': 'pointer', 124 | 'height': bodywrapper.height(), 125 | 'padding-top': '1px', 126 | 'margin-left': ssb_width_expanded - 12 127 | }); 128 | 129 | sidebarbutton.hover( 130 | function () { 131 | $(this).css('background-color', dark_color); 132 | }, 133 | function () { 134 | $(this).css('background-color', light_color); 135 | } 136 | ); 137 | } 138 | 139 | function set_position_from_cookie() { 140 | if (!document.cookie) 141 | return; 142 | var items = document.cookie.split(';'); 143 | for(var k=0; k 4 | 5 | 6 | 7 | 8 | 9 | Search — Pyoints 0.2.0 documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 42 | 43 |
44 |
45 |
46 |
47 | 48 |

Search

49 |
50 | 51 |

52 | Please activate JavaScript to enable the search 53 | functionality. 54 |

55 |
56 |

57 | From here you can search these documents. Enter your search 58 | words into the box below and click "search". Note that the search 59 | function will automatically search for all of the words. Pages 60 | containing fewer words won't appear in the result list. 61 |

62 |
63 | 64 | 65 | 66 |
67 | 68 |
69 | 70 |
71 | 72 |
73 |
74 |
75 | 82 |
83 |
84 | 96 | 100 | 101 | -------------------------------------------------------------------------------- /figures/logo_pyoints.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/figures/logo_pyoints.odg -------------------------------------------------------------------------------- /figures/logo_pyoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/figures/logo_pyoints.png -------------------------------------------------------------------------------- /paper/paper.bib: -------------------------------------------------------------------------------- 1 | @online{Pyoints_GitHub, 2 | author = {Sebastian Lamprecht}, 3 | title = {Pyoints}, 4 | year = {2019}, 5 | doi = {10.5281/zenodo.2557574} 6 | } 7 | 8 | @online{Pyoints_docs, 9 | author = {Sebastian Lamprecht}, 10 | title = {Pyoints Documentation}, 11 | year = {2019}, 12 | url = {https://laempy.github.io/pyoints/}, 13 | urldate = {2019-04-01} 14 | } 15 | 16 | @manual{GDAL, 17 | title = {{GDAL/OGR} Geospatial Data Abstraction software Library}, 18 | author = {{GDAL/OGR contributors}}, 19 | organization = {Open Source Geospatial Foundation}, 20 | year = {2018}, 21 | url = {http://gdal.org}, 22 | } 23 | 24 | @Article{opencv_library, 25 | author = {Bradski, G.}, 26 | title = {{The OpenCV Library}}, 27 | journal = {Dr. Dobb's Journal of Software Tools}, 28 | year = {2000}, 29 | url = {https://opencv.org}, 30 | citeulike-article-id = {2236121}, 31 | keywords = {bibtex-import}, 32 | posted-at = {2008-01-15 19:21:54}, 33 | } 34 | 35 | @article{PCL, 36 | author = {Rusu, Radu Bogdan and Cousins, Steve}, 37 | title = {{3D is here: Point Cloud Library (PCL)}}, 38 | journal = {IEEE International Conference on Robotics and Automation}, 39 | pages = {1--4}, 40 | year = {2011}, 41 | month = {5}, 42 | issn = {1050-4729}, 43 | publisher = {IEEE}, 44 | address = {Shanghai, China}, 45 | doi = {10.1109/ICRA.2011.5980567} 46 | } 47 | 48 | @article{Open3D, 49 | author = {Qian-Yi Zhou and Jaesik Park and Vladlen Koltun}, 50 | title = {{Open3D}: {A} Modern Library for {3D} Data Processing}, 51 | journal = {arXiv:1801.09847}, 52 | year = {2018}, 53 | } 54 | 55 | @manual{PDAL, 56 | title = {{PDAL}: The Point Data Abstraction Library}, 57 | author = {{PDAL contributors}}, 58 | year = {2018}, 59 | url = {https://pdal.io/}, 60 | } 61 | 62 | @Online{PANTHEON, 63 | author = {{PANTHEON consortium}}, 64 | title = {Precision Farming of Hazelnut Orchards}, 65 | year = {2018}, 66 | url = {http://www.project-pantheon.eu/}, 67 | urldate = {2018-08-10}, 68 | } 69 | 70 | 71 | @article{Lamprecht_2017a, 72 | author = {Lamprecht, Sebastian and Hill, Andreas and Stoffels, Johannes and Udelhoven, Thomas}, 73 | title = {A {Machine Learning Method} for {Co-Registration} and {Individual Tree Matching} of {Forest Inventory} and {Airborne Laser Scanning Data}}, 74 | journal = {Remote Sensing}, 75 | year = {2017}, 76 | language = {en}, 77 | volume = {9}, 78 | number = {5}, 79 | month = {5}, 80 | pages = {505}, 81 | doi = {10.3390/rs9050505}, 82 | url = {http://www.mdpi.com/2072-4292/9/5/505}, 83 | urldate = {2017-05-22}, 84 | abstract = {Determining the exact position of a forest inventory plot---and hence the position of the sampled trees---is often hampered by a poor Global Navigation Satellite System (GNSS) signal quality beneath the forest canopy. Inaccurate geo-references hamper the performance of models that aim to retrieve useful information from spatially high remote sensing data (e.g., species classification or timber volume estimation). This restriction is even more severe on the level of individual trees. The objective of this study was to develop a post-processing strategy to improve the positional accuracy of GNSS-measured sample-plot centers and to develop a method to automatically match trees within a terrestrial sample plot to aerial detected trees. We propose a new method which uses a random forest classifier to estimate the matching probability of each terrestrial-reference and aerial detected tree pair, which gives the opportunity to assess the reliability of the results. We investigated 133 sample plots of the Third German National Forest Inventory (BWI, 2011--2012) within the German federal state of Rhineland-Palatinate. For training and objective validation, synthetic forest stands have been modeled using the Waldplaner 2.0 software. Our method has achieved an overall accuracy of 82.7\% for co-registration and 89.1\% for tree matching. With our method, 60\% of the investigated plots could be successfully relocated. The probabilities provided by the algorithm are an objective indicator of the reliability of a specific result which could be incorporated into quantitative models to increase the performance of forest attribute estimations.}, 85 | copyright = {http://creativecommons.org/licenses/by/3.0/}, 86 | file = {:CoRegistration/Lamprecht_2017a.pdf:PDF}, 87 | groups = {CoRegistration}, 88 | keywords = {co-registration, Forest inventory, individual tree detection, machine-learning, point set registration, tree matching}, 89 | owner = {sebastian}, 90 | timestamp = {2017.05.22}, 91 | } 92 | 93 | @article{Lamprecht_2015a, 94 | author = {Lamprecht, Sebastian and Stoffels, Johannes and Dotzler, Sandra and Ha{\ss}, Erik and Udelhoven, Thomas}, 95 | title = {aTrunk---An ALS-Based Trunk Detection Algorithm}, 96 | journal = {Remote Sensing}, 97 | year = {2015}, 98 | volume = {7}, 99 | number = {8}, 100 | pages = {9975}, 101 | issn = {2072-4292}, 102 | doi = {10.3390/rs70809975}, 103 | url = {http://www.mdpi.com/2072-4292/7/8/9975}, 104 | file = {:AlsTreeModelling/Lamprecht_2015a.pdf:PDF}, 105 | groups = {AlsTreeModelling}, 106 | owner = {sebastian}, 107 | timestamp = {2015.12.07}, 108 | } -------------------------------------------------------------------------------- /paper/paper.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Pyoints: A Python package for point cloud, voxel and raster processing.' 3 | tags: 4 | - Python 5 | - geoinformatics 6 | - remote sensing 7 | - point cloud analysis 8 | - raster analysis 9 | authors: 10 | - name: Sebastian Lamprecht 11 | orcid: 0000-0002-8963-2762 12 | affiliation: 1 13 | affiliations: 14 | - name: Trier University 15 | index: 1 16 | date: 25 September 2018 17 | bibliography: paper.bib 18 | --- 19 | 20 | 21 | # Summary 22 | 23 | The evolution of automated systems like autonomous robots and unmanned aerial 24 | vehicles leads to manifold opportunities in science, agriculture and industry. 25 | Remote sensing devices, like laser scanners and multi-spectral cameras, can be 26 | combined with sensor networks to all-embracingly monitor a research object. 27 | 28 | The analysis of such big data is based on geoinformatics and 29 | remote sensing techniques. Today, next to physically driven approaches, machine learning 30 | techniques are often used to extract relevant thematical information from the data sets. 31 | Analysis requires a fusion of the data sets, which is made difficult conceptually 32 | and technically by different data dimensions, data structures, and various 33 | spatial, spectral, and temporal resolutions. 34 | 35 | Today, various software to deal with these different data sources is available. 36 | Software like GDAL [@GDAL] and OpenCV [@opencv_library] are intended for image 37 | processing. Libraries, like PCL [@PCL], Open3D [@Open3D] and PDAL [@PDAL] focus 38 | on 3D point cloud processing. Each of these software packages provide an API 39 | specially designed to solve the problems of their field efficiently. When 40 | developing algorithms for automated processing of various types of input data, 41 | the differing APIs and programming languages of these software packages become 42 | a drawback. To support fast algorithm development and a short familiarization, 43 | a unified API would be desirable. 44 | 45 | *Pyoints* is a python package to conveniently process and analyze point 46 | cloud data, voxels, and raster images. It is intended to be used to support 47 | the development of advanced algorithms for geo-data processing. 48 | 49 | The fundamental idea of *Pyoints* is to overcome the conceptual distinction 50 | between point clouds, voxel spaces, and rasters to simplify data analysis 51 | and data fusion of variously structured data. Based on the assumption that any 52 | geo-object can be represented by a point, a data structure has been designed 53 | that provides a unified API for points, voxels, and rasters. Each data 54 | structure maintains its characteristic features, to allow for intuitive use, 55 | but all data is also considered as a two or three dimensional point cloud, 56 | providing spatial indices that are required in many applications to speed up 57 | spatial neighborhood queries. 58 | 59 | During development, great emphasis was put on designing a powerful but simple 60 | API while also providing solutions for most common problems. *Pyoints* 61 | implements fundamental functions and some advanced algorithms for point cloud, 62 | voxel, and raster data processing, like coordinate transformation, vector 63 | algebra, point filters, and interpolation. *Pyoints* also provides a unified 64 | API for loading and saving commonly used geo-data formats. 65 | 66 | *Pyoints* was designed to support research activities and algorithm 67 | development in the field of geoinformatics and remote sensing. Early versions of 68 | the software have been used for some pre-studies at Trier University 69 | [@Lamprecht_2015a; @Lamprecht_2017a]. *Pyoints* is also used in the PANTHEON project [@PANTHEON] 70 | to monitor hazelnut orchards. 71 | 72 | The source code of *Pyoints* is on GitHub [@Pyoints_GitHub]. The 73 | documentation can be found on GitHub Pages [@Pyoints_docs]. 74 | 75 | 76 | # Acknowledgements 77 | 78 | This work has been supported by the European Commission under the grant 79 | agreement number 774571 Project PANTHEON. 80 | 81 | 82 | # References 83 | -------------------------------------------------------------------------------- /pyoints/__init__.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Pyoints: A Python package for point cloud, voxel and raster processing.""" 20 | 21 | from .about import * 22 | from .indexkd import IndexKD 23 | from .coords import Coords 24 | from .extent import Extent 25 | from .projection import Proj 26 | from .grid import Grid 27 | from .surface import Surface 28 | from .georecords import ( 29 | GeoRecords, 30 | LasRecords, 31 | ) 32 | from . import ( 33 | assertion, 34 | assign, 35 | classification, 36 | clustering, 37 | coords, 38 | distance, 39 | examples, 40 | extent, 41 | filters, 42 | fit, 43 | georecords, 44 | grid, 45 | indexkd, 46 | interpolate, 47 | misc, 48 | normals, 49 | nptools, 50 | polar, 51 | projection, 52 | registration, 53 | smoothing, 54 | storage, 55 | surface, 56 | transformation, 57 | vector, 58 | ) 59 | from .misc import print_rounded 60 | -------------------------------------------------------------------------------- /pyoints/about.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | 20 | __all__ = [ 21 | "__title__", 22 | "__summary__", 23 | "__uri__", 24 | "__version__", 25 | "__author__", 26 | "__email__", 27 | "__license__", 28 | "__copyright__", 29 | ] 30 | 31 | __version__ = '0.2.0' 32 | 33 | __title__ = "Pyoints" 34 | __summary__ = "A Python package for point cloud, voxel and raster processing." 35 | __uri__ = "https://github.com/laempy/pyoints" 36 | 37 | __author__ = "Sebastian Lamprecht" 38 | __email__ = "lamprecht@uni-trier.de" 39 | 40 | __license__ = "GPLv3+" 41 | __copyright__ = "2019, %s" % __author__ 42 | 43 | 44 | def version(): 45 | """Get the version of pyoints. 46 | 47 | Returns 48 | ------- 49 | str 50 | Version specification. 51 | 52 | """ 53 | return __version__ 54 | -------------------------------------------------------------------------------- /pyoints/assign.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Module to find pairs of points""" 20 | 21 | import numpy as np 22 | 23 | from . import ( 24 | assertion, 25 | transformation, 26 | IndexKD, 27 | ) 28 | 29 | from .misc import print_rounded 30 | 31 | 32 | class Matcher: 33 | """Base class to simplify point matching. Points of a reference point set 34 | `A` are assigned to points of a point set `B`. 35 | 36 | Parameters 37 | ---------- 38 | A : array_like(Number, shape=(n, k)) 39 | Represents `n` points of `k` dimensions. These points are used as a 40 | reference point set. 41 | radii : array_like(Number, shape=(k)) 42 | Defines the sphere within the points can get assigned. 43 | 44 | """ 45 | 46 | def __init__(self, coords, radii): 47 | coords = assertion.ensure_coords(coords) 48 | radii = assertion.ensure_numvector(radii, length=coords.shape[1]) 49 | 50 | S = transformation.s_matrix(1.0 / radii) 51 | self.rIndexKD = IndexKD(coords, S) 52 | 53 | def __call__(coords): 54 | """Find matching points. 55 | 56 | Parameters 57 | ---------- 58 | B : array_like(Number, shape=(n, k)) 59 | Represents `n` points of `k` dimensions. These points are assigned 60 | to the previously defined reference coordinates. 61 | 62 | Returns 63 | ------- 64 | pairs : np.ndarray(int, shape=(m, 2)) 65 | Indices of assigned points. For two point sets `A`, `B` and each 66 | row `(a, b)` in `pairs` `A[a, :]` is assigned to `B[b, :]` 67 | 68 | """ 69 | raise NotImplementedError() 70 | 71 | 72 | class PairMatcher(Matcher): 73 | """Find unique pairs of points. A point `a` of point set `A` is assigned 74 | to its closest point `b` of point set `B` if `a` is also the nearest 75 | neighbour to `b`. Thus, duplicate assignment is not possible. 76 | 77 | See Also 78 | -------- 79 | Matcher 80 | 81 | Examples 82 | -------- 83 | 84 | Create point sets. 85 | 86 | >>> A = np.array([(0, 0), (0, 0.1), (1, 1), (1, 0), (0.5, 0.5), (-1, -2)]) 87 | >>> B = np.array([(0.4, 0.4), (0.2, 0), (0.1, 1.2), (2, 1), (-1.1, -1.2)]) 88 | 89 | Match points. 90 | 91 | >>> matcher = PairMatcher(A, [0.3, 0.2]) 92 | >>> pairs = matcher(B) 93 | >>> print_rounded(pairs) 94 | [[4 0] 95 | [0 1]] 96 | >>> print_rounded(A[pairs[:, 0], :] - B[pairs[:, 1], :]) 97 | [[ 0.1 0.1] 98 | [-0.2 0. ]] 99 | 100 | """ 101 | 102 | def __init__(self, coords, radii): 103 | coords = assertion.ensure_coords(coords) 104 | radii = assertion.ensure_numvector(radii, length=coords.shape[1]) 105 | 106 | S = transformation.s_matrix(1.0 / radii) 107 | self.rIndexKD = IndexKD(coords, S) 108 | 109 | def __call__(self, coords): 110 | mIndexKD = IndexKD(coords, self.rIndexKD.t) 111 | rIndexKD = self.rIndexKD 112 | 113 | rDists, rIds = rIndexKD.knn( 114 | mIndexKD.coords, k=1, distance_upper_bound=1) 115 | 116 | mDists, mIds = mIndexKD.knn( 117 | rIndexKD.coords, k=1, distance_upper_bound=1) 118 | 119 | pairs = [] 120 | for rId in range(len(rIds)): 121 | if rDists[rId] <= 1: 122 | if rId == mIds[rIds[rId]]: 123 | pairs.append((rIds[rId], rId)) 124 | 125 | return np.array(pairs, dtype=int) 126 | 127 | 128 | class SphereMatcher(Matcher): 129 | """Find pairs of points. Each point is assigned to all the points 130 | within a previously defined sphere. Duplicate assignments are possible. 131 | 132 | See Also 133 | -------- 134 | Matcher 135 | 136 | Examples 137 | -------- 138 | 139 | Create point sets. 140 | 141 | >>> A = np.array([(0, 0), (0, 0.1), (1, 1), (1, 0), (0.5, 0.5), (-1, -2)]) 142 | >>> B = np.array([(0.4, 0.4), (0.2, 0), (0.1, 1.2), (2, 1), (-1.1, -1.2)]) 143 | 144 | Match points. 145 | 146 | >>> matcher = SphereMatcher(A, [0.3, 0.2]) 147 | >>> pairs = matcher(B) 148 | >>> print_rounded(pairs) 149 | [[4 0] 150 | [0 1] 151 | [1 1]] 152 | >>> print_rounded(A[pairs[:, 0], :] - B[pairs[:, 1], :]) 153 | [[ 0.1 0.1] 154 | [-0.2 0. ] 155 | [-0.2 0.1]] 156 | 157 | """ 158 | 159 | def __call__(self, coords): 160 | mIndexKD = IndexKD(coords, self.rIndexKD.t) 161 | rIndexKD = self.rIndexKD 162 | 163 | pairs = [] 164 | ball_gen = rIndexKD.ball_iter(mIndexKD.coords, 1) 165 | for mId, rIds in enumerate(ball_gen): 166 | for rId in rIds: 167 | pairs.append((rId, mId)) 168 | 169 | return np.array(pairs, dtype=int) 170 | 171 | 172 | class KnnMatcher(Matcher): 173 | """Find pairs of points. Each point is assigned to `k` closest points 174 | within a predefined sphere. Duplicate assignents are possible. 175 | 176 | See Also 177 | -------- 178 | Matcher 179 | 180 | Examples 181 | -------- 182 | 183 | Create coordinates and matcher. 184 | 185 | >>> A = np.array([(0, 0), (0, 0.1), (1, 1), (1, 0), (0.5, 0.5), (-1, -2)]) 186 | >>> B = np.array([(0.4, 0.4), (0.2, 0), (0.1, 1.2), (2, 1), (-1.1, -1.2)]) 187 | >>> matcher = KnnMatcher(A, [0.5, 0.5]) 188 | 189 | Try to assign one neighbour. 190 | 191 | >>> pairs = matcher(B) 192 | >>> print_rounded(pairs) 193 | [[4 0] 194 | [0 1]] 195 | 196 | Try to assign two neighbours. 197 | 198 | >>> pairs = matcher(B, k=2) 199 | >>> print_rounded(pairs) 200 | [[4 0] 201 | [0 1] 202 | [1 1]] 203 | 204 | """ 205 | 206 | def __call__(self, coords, k=1): 207 | """Assign `k` closest points. 208 | 209 | Parameters 210 | ---------- 211 | k : optional, int 212 | Number of neighbours to assign. 213 | 214 | See Also 215 | -------- 216 | Matcher 217 | 218 | """ 219 | if not (isinstance(k, int) and k > 0): 220 | raise ValueError("'k' needs to be an integer greater zero") 221 | mIndexKD = IndexKD(coords, self.rIndexKD.t) 222 | rIndexKD = self.rIndexKD 223 | 224 | pairs = [] 225 | mCoords = mIndexKD.coords 226 | ball_gen = rIndexKD.knn_iter(mCoords, k, distance_upper_bound=1) 227 | for mId, (dists, rIds) in enumerate(ball_gen): 228 | if k == 1: 229 | dists = [dists] 230 | rIds = [rIds] 231 | for dist, rId in zip(dists, rIds): 232 | if dist <= 1: 233 | pairs.append((rId, mId)) 234 | 235 | return np.array(pairs, dtype=int) 236 | -------------------------------------------------------------------------------- /pyoints/distance.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Various distance metrics. 20 | """ 21 | 22 | import numpy as np 23 | 24 | from . import ( 25 | assertion, 26 | ) 27 | from .misc import print_rounded 28 | 29 | 30 | def norm(coords): 31 | """Normalization of coordinates. 32 | 33 | Parameters 34 | ---------- 35 | coords : array_like(Number, shape=(k)) or array_like(Number, shape=(n, k)) 36 | Represents `n` points or a single point of `k` dimensions. 37 | 38 | Returns 39 | ------- 40 | array_like(shape=(n, )) 41 | Normed values. 42 | 43 | See Also 44 | -------- 45 | snorm 46 | 47 | Examples 48 | -------- 49 | 50 | >>> coords = [(3, 4), (0, 1), (4, 3), (0, 0), (8, 6)] 51 | >>> print_rounded(norm(coords)) 52 | [ 5. 1. 5. 0. 10.] 53 | 54 | """ 55 | return np.sqrt(snorm(coords)) 56 | 57 | 58 | def snorm(coords): 59 | """Squared normalization of coordinates. 60 | 61 | Parameters 62 | ---------- 63 | coords: array_like(Number, shape=(k)) or array_like(Number, shape=(n, k)) 64 | Represents `n` points or a single point of `k` dimensions. 65 | 66 | Returns 67 | ------- 68 | Number or array_like(Number, shape=(n)) 69 | Squared normed values. 70 | 71 | See Also 72 | -------- 73 | norm 74 | 75 | Examples 76 | -------- 77 | 78 | >>> coords = [(3, 4), (0, 1), (4, 3), (0, 0), (8, 6)] 79 | >>> print_rounded(snorm(coords)) 80 | [ 25 1 25 0 100] 81 | 82 | """ 83 | coords = assertion.ensure_numarray(coords) 84 | if len(coords.shape) == 1: 85 | res = (coords * coords).sum() 86 | else: 87 | res = (coords * coords).sum(1) 88 | return res 89 | 90 | 91 | def dist(p, coords): 92 | """Calculates the distances between points. 93 | 94 | Parameters 95 | ---------- 96 | p : array_like(Number, shape=(n, k)) or array_like(Number, shape=(k)) 97 | Represents `n` points or a single point of `k` dimensions. 98 | coords : array_like(Number, shape=(n, k)) 99 | Represents `n` points of `k` dimensions. 100 | 101 | Returns 102 | ------- 103 | Number or array_like(Number, shape=(n)) 104 | Normed values. 105 | 106 | See Also 107 | -------- 108 | sdist 109 | 110 | Examples 111 | -------- 112 | 113 | Point to points distance. 114 | 115 | >>> p = (1, 2) 116 | >>> coords = [(2, 2), (1, 1), (1, 2), (9, 8)] 117 | >>> print_rounded(dist(p, coords)) 118 | [ 1. 1. 0. 10.] 119 | 120 | Points to points distance. 121 | 122 | >>> A = [(2, 2), (1, 1), (1, 2)] 123 | >>> B = [(4, 2), (2, 1), (9, 8)] 124 | >>> print_rounded(dist(A, B)) 125 | [ 2. 1. 10.] 126 | 127 | """ 128 | return np.sqrt(sdist(p, coords)) 129 | 130 | 131 | def sdist(p, coords): 132 | """Calculates the squared distances between points. 133 | 134 | Parameters 135 | ---------- 136 | p : array_like(Number, shape=(n, k)) or array_like(Number, shape=(k)) 137 | Represents `n` points or a single point of `k` dimensions. 138 | coords : array_like(Number, shape=(n, k)) 139 | Represents `n` points of `k` dimensions. 140 | 141 | Returns 142 | ------- 143 | Number or array_like(Number, shape=(n)) 144 | Squared distances between the points. 145 | 146 | See Also 147 | -------- 148 | dist 149 | 150 | Examples 151 | -------- 152 | 153 | Squared point to points distance. 154 | 155 | >>> p = (1, 2) 156 | >>> coords = [(2, 4), (1, 1), (1, 2), (9, 8)] 157 | >>> print_rounded(sdist(p, coords)) 158 | [ 5 1 0 100] 159 | 160 | Squared points to points distance. 161 | 162 | >>> A = [(2, 2), (1, 1), (1, 2)] 163 | >>> B = [(4, 2), (2, 1), (9, 8)] 164 | >>> print_rounded(sdist(A, B)) 165 | [ 4 1 100] 166 | 167 | """ 168 | p = assertion.ensure_numarray(p) 169 | coords = assertion.ensure_numarray(coords) 170 | if not p.shape == coords.shape: 171 | if not (len(coords.shape) == 2 and p.shape[0] == coords.shape[1]): 172 | m = "Dimensions %s and %s do not match" 173 | raise ValueError(m % (str(p.shape), str(coords.shape))) 174 | 175 | return snorm(coords - p) 176 | 177 | 178 | def rmse(A, B=None): 179 | """Calculates the Root Mean Squared Error of corresponding data sets. 180 | 181 | Parameters 182 | ---------- 183 | A, B : array_like(Number, shape=(n, k)) 184 | Represent `n` points or a single point of `k` dimensions. 185 | 186 | Returns 187 | ------- 188 | Number 189 | Root Mean Squared Error. 190 | 191 | 192 | Examples 193 | -------- 194 | 195 | >>> A = [(2, 2), (1, 1), (1, 2)] 196 | >>> B = [(2.2, 2), (0.9, 1.1), (1, 2.1)] 197 | >>> print_rounded(rmse(A, B)) 198 | 0.15 199 | 200 | """ 201 | if B is None: 202 | d = snorm(A) 203 | else: 204 | d = sdist(A, B) 205 | return np.sqrt(np.mean(d)) 206 | 207 | 208 | def idw(dists, p=2): 209 | """Calculates the weights for Inverse Distance Weighting method. 210 | 211 | Parameters 212 | ---------- 213 | dists : Number or array_like(Number, shape=(n)) 214 | Represent `n` distance values. 215 | p : optional, Number 216 | Weighting power. 217 | 218 | Returns 219 | ------- 220 | Number or array_like(Number, shape=(n)) 221 | Weights according to Inverse Distance Weighting. 222 | 223 | Examples 224 | -------- 225 | 226 | >>> dists = [0, 1, 4] 227 | 228 | >>> print_rounded(idw(dists)) 229 | [ 1. 0.25 0.04] 230 | 231 | >>> print_rounded(idw(dists, p=1)) 232 | [ 1. 0.5 0.2] 233 | 234 | """ 235 | dists = assertion.ensure_numvector(dists) 236 | return 1.0 / (1 + dists)**p 237 | -------------------------------------------------------------------------------- /pyoints/examples/__init__.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Learn how to use Pyoints as a powerful tool.""" 20 | from . import ( 21 | file_examples, 22 | base_example, 23 | grid_example, 24 | stemfilter_example, 25 | ) 26 | -------------------------------------------------------------------------------- /pyoints/examples/base_example.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """In this example we learn the basics of point cloud processing using Pyoints. 20 | 21 | 22 | We begin with loading the required modules. 23 | 24 | >>> import os 25 | >>> import numpy as np 26 | 27 | >>> from pyoints import ( 28 | ... storage, 29 | ... transformation, 30 | ... IndexKD, 31 | ... ) 32 | >>> from pyoints.misc import print_rounded 33 | 34 | 35 | Then we define input and output paths. 36 | 37 | >>> inpath = os.path.join( 38 | ... os.path.dirname(os.path.abspath(__file__)), 'data') 39 | >>> outpath = os.path.join( 40 | ... os.path.dirname(os.path.abspath(__file__)), 'output') 41 | 42 | 43 | We select an input LAS point cloud. 44 | 45 | >>> infile = os.path.join(inpath, 'forest.las') 46 | >>> lasReader = storage.LasReader(infile) 47 | 48 | 49 | We get the origin of the point cloud. 50 | 51 | >>> print_rounded(lasReader.t.origin, 2) 52 | [ 364187.98 5509577.71 -1.58] 53 | >>> print(lasReader.date.year) 54 | 2018 55 | 56 | Then, we get the projection of the point cloud... 57 | 58 | >>> print(lasReader.proj.proj4) 59 | +proj=utm +zone=32 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs 60 | 61 | 62 | ... and the number of points. 63 | 64 | >>> print(len(lasReader)) 65 | 482981 66 | 67 | 68 | We get the spatial extent in 3D. 69 | 70 | >>> print_rounded(lasReader.extent, 2) 71 | [ 364187.98 5509577.71 -1.58 364194.95 5509584.71 28.78] 72 | 73 | 74 | Now, we load the point cloud data from the disc. We receive an instance 75 | of `GeoRecords`, which is an extension of a numpy record array. 76 | 77 | >>> las = lasReader.load() 78 | 79 | >>> print(las.shape) 80 | (482981,) 81 | >>> print(las.date.year) 82 | 2018 83 | 84 | We get some information on the attributes of the points... 85 | 86 | >>> print(sorted(las.dtype.descr)) 87 | [('classification', '|u1'), ('coords', '>> print_rounded(las[0:10].intensity) 90 | [216 213 214 199 214 183 198 209 200 199] 91 | 92 | >>> print_rounded(np.unique(las.classification)) 93 | [2 5] 94 | 95 | 96 | ... and take a look at the extent in 2D and 3D. 97 | 98 | >>> print_rounded(las.extent(2), 2) 99 | [ 364187.98 5509577.71 364194.95 5509584.71] 100 | >>> print_rounded(las.extent(3), 2) 101 | [ 364187.98 5509577.71 -1.58 364194.95 5509584.71 28.78] 102 | 103 | 104 | Now we take a closer look at the spatial index. We begin with selecting all 105 | points close to the corners of the point cloud's extent. 106 | 107 | >>> radius = 1.0 108 | >>> corners = las.extent().corners 109 | >>> print_rounded(corners, 1) 110 | [[ 364188. 5509577.7 -1.6] 111 | [ 364194.9 5509577.7 -1.6] 112 | [ 364194.9 5509584.7 -1.6] 113 | ..., 114 | [ 364194.9 5509584.7 28.8] 115 | [ 364194.9 5509577.7 28.8] 116 | [ 364188. 5509577.7 28.8]] 117 | 118 | But before we select the points, we count the number of neighbors within the 119 | radius. 120 | 121 | >>> count = las.indexKD().ball_count(radius, coords=corners) 122 | >>> print_rounded(count) 123 | [2502 1984 4027 475 0 0 0 0] 124 | 125 | 126 | OK, now we actually select the points. 127 | 128 | >>> n_ids = las.indexKD().ball(corners, radius) 129 | >>> print(len(n_ids)) 130 | 8 131 | 132 | 133 | For each point we receive a list of indices. So we concatenate them to save 134 | the resulting subset as a point cloud. 135 | 136 | >>> n_ids = np.concatenate(n_ids).astype(int) 137 | >>> print(len(n_ids)) 138 | 8988 139 | 140 | >>> outfile = os.path.join(outpath, 'base_ball.las') 141 | >>> storage.writeLas(las[n_ids], outfile) 142 | 143 | 144 | We can also select the `k` nearest neighbors. 145 | 146 | >>> dists, n_ids = las.indexKD().knn(corners, k=2) 147 | 148 | We receive a matrix of distances and a matrix of indices. 149 | 150 | >>> print_rounded(dists, 2) 151 | [[ 0.3 0.3 ] 152 | [ 0.02 0.04] 153 | [ 0.49 0.49] 154 | [ 0.62 0.62] 155 | [ 3.95 3.97] 156 | [ 5.71 5.73] 157 | [ 1.26 1.27] 158 | [ 1.65 1.66]] 159 | >>> print_rounded(n_ids) 160 | [[ 6 16742] 161 | [ 92767 92763] 162 | [320695 321128] 163 | [206255 206239] 164 | [440696 440687] 165 | [400070 400050] 166 | [400369 400340] 167 | [365239 365240]] 168 | 169 | 170 | Again, we save the resulting subset. 171 | 172 | >>> n_ids = n_ids.flatten() 173 | >>> len(n_ids) 174 | 16 175 | 176 | >>> outfile = os.path.join(outpath, 'base_knn.las') 177 | >>> storage.writeLas(las[n_ids], outfile) 178 | 179 | 180 | If we need to select points in the shape of an ellipsoid, we can also create a 181 | scaled spatial index. Doing so, each coordinate axis is scaled individually. 182 | 183 | >>> T = transformation.s_matrix([1.5, 0.9, 0.5]) 184 | >>> indexKD = IndexKD(las.coords, T=T) 185 | 186 | 187 | Again, we select neighboring points using the `ball` query. But here, we need 188 | to scale the input coordinates beforehand. 189 | 190 | >>> s_corners = T.to_local(corners) 191 | >>> print_rounded(s_corners, 1) 192 | [[ 546282. 4958619.9 -0.8] 193 | [ 546292.4 4958619.9 -0.8] 194 | [ 546292.4 4958626.2 -0.8] 195 | ..., 196 | [ 546292.4 4958626.2 14.4] 197 | [ 546292.4 4958619.9 14.4] 198 | [ 546282. 4958619.9 14.4]] 199 | 200 | Finally, we apply the query and save the subset. 201 | 202 | >>> n_ids = indexKD.ball(s_corners, radius) 203 | >>> n_ids = np.concatenate(n_ids).astype(int) 204 | >>> print(len(n_ids)) 205 | 8520 206 | 207 | >>> outfile = os.path.join(outpath, 'base_ellipsoid.las') 208 | >>> storage.writeLas(las[n_ids], outfile) 209 | 210 | """ 211 | -------------------------------------------------------------------------------- /pyoints/examples/data/forest.las: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/pyoints/examples/data/forest.las -------------------------------------------------------------------------------- /pyoints/examples/data/logo_pyoints.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/pyoints/examples/data/logo_pyoints.jpg -------------------------------------------------------------------------------- /pyoints/examples/file_examples/__init__.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Examples to explain how files can be saved and loaded.""" 20 | from . import ( 21 | csv_example, 22 | dump_example, 23 | las_example, 24 | ply_example, 25 | raster_example, 26 | structured_example 27 | ) 28 | -------------------------------------------------------------------------------- /pyoints/examples/file_examples/csv_example.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Learn how to save and load .csv-files. 20 | 21 | >>> import os 22 | >>> from pyoints import storage 23 | 24 | Create output path. 25 | 26 | >>> outpath = os.path.join( 27 | ... os.path.dirname(os.path.abspath(__file__)), '..', 'output') 28 | 29 | Create GeoRecords from scratch. 30 | 31 | >>> geoRecords = storage.misc.create_random_GeoRecords( 32 | ... center=[332592.88, 5513244.80, 120], epsg=25832) 33 | 34 | >>> print(geoRecords.shape) 35 | (1000,) 36 | >>> print(sorted(geoRecords.dtype.names)) 37 | ['classification', 'coords', 'intensity', 'keypoint', 'synthetic', 'values', 'withheld'] 38 | 39 | Save as a .csv-file. 40 | 41 | >>> outfile = os.path.join(outpath, 'test.csv') 42 | >>> storage.writeCsv(geoRecords, outfile) 43 | 44 | Load the .csv-file again and check the characteristics. 45 | 46 | >>> geoRecords = storage.loadCsv(outfile, header=True) 47 | >>> print(geoRecords.shape) 48 | (1000,) 49 | >>> print(sorted(geoRecords.dtype.names)) 50 | ['classification', 'coords', 'index', 'intensity', 'keypoint', 'synthetic', 'values', 'withheld'] 51 | 52 | """ 53 | -------------------------------------------------------------------------------- /pyoints/examples/file_examples/dump_example.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Learn how to save and load DUMP-files. 20 | 21 | >>> import os 22 | >>> import numpy as np 23 | >>> from pyoints import storage 24 | 25 | Create an output path. 26 | 27 | >>> outpath = os.path.join( 28 | ... os.path.dirname(os.path.abspath(__file__)), '..', 'output') 29 | 30 | Create GeoRecords from scratch. 31 | 32 | >>> geoRecords = storage.misc.create_random_GeoRecords( 33 | ... center=[332592.88, 5513244.80, 120], epsg=25832) 34 | >>> print(geoRecords.shape) 35 | (1000,) 36 | >>> print(sorted(geoRecords.dtype.names)) 37 | ['classification', 'coords', 'intensity', 'keypoint', 'synthetic', 'values', 'withheld'] 38 | 39 | >>> print(hasattr(geoRecords, 'proj')) 40 | True 41 | 42 | Save as a DUMP-file. 43 | 44 | >>> outfile = os.path.join(outpath, 'test.pydump') 45 | >>> storage.writeDump(geoRecords, outfile) 46 | 47 | 48 | Load the DUMP-file again and check the characteristics. 49 | 50 | >>> dumpReader = storage.DumpReader(outfile) 51 | >>> geoRecords = dumpReader.load() 52 | 53 | >>> print(geoRecords.shape) 54 | (1000,) 55 | >>> print(sorted(geoRecords.dtype.names)) 56 | ['classification', 'coords', 'intensity', 'keypoint', 'synthetic', 'values', 'withheld'] 57 | 58 | >>> print(hasattr(geoRecords, 'proj')) 59 | True 60 | 61 | Working with DUMP-strings. 62 | 63 | >>> dumpstr = storage.dumpstring_from_object(geoRecords) 64 | >>> print(isinstance(dumpstr, str)) 65 | True 66 | 67 | >>> geoRecords = storage.dumpstring_to_object(dumpstr) 68 | >>> print(hasattr(geoRecords, 'proj')) 69 | True 70 | 71 | """ 72 | -------------------------------------------------------------------------------- /pyoints/examples/file_examples/las_example.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Learn how to save and load LAS-files. 20 | 21 | >>> import os 22 | >>> import numpy as np 23 | >>> from datetime import datetime 24 | >>> from pyoints import storage 25 | 26 | Create an output path. 27 | 28 | >>> outpath = os.path.join( 29 | ... os.path.dirname(os.path.abspath(__file__)), '..', 'output') 30 | 31 | Create GeoRecords from scratch. 32 | 33 | >>> geoRecords = storage.misc.create_random_GeoRecords( 34 | ... center=[332592.88, 5513244.80, 120], epsg=25832) 35 | >>> print(geoRecords.shape) 36 | (1000,) 37 | >>> print(sorted(geoRecords.dtype.names)) 38 | ['classification', 'coords', 'intensity', 'keypoint', 'synthetic', 'values', 'withheld'] 39 | 40 | Set the capturing date. 41 | 42 | >>> print(geoRecords.date) 43 | None 44 | >>> geoRecords.date = datetime(year=2019, month=3, day=4) 45 | >>> print(geoRecords.date.year) 46 | 2019 47 | 48 | Save as LAS-file. 49 | 50 | >>> outfile = os.path.join(outpath, 'test.las') 51 | >>> storage.writeLas(geoRecords, outfile) 52 | 53 | Load LAS-file again and check the characteristics. 54 | 55 | >>> lasReader = storage.LasReader(outfile) 56 | >>> print(len(lasReader)) 57 | 1000 58 | >>> print(lasReader.date.month) 59 | 3 60 | 61 | >>> las = lasReader.load() 62 | >>> print(las.shape) 63 | (1000,) 64 | >>> print(sorted(las.dtype.descr)) 65 | [('classification', '|u1'), ('coords', '>> np.all(geoRecords.classification == las.classification) 70 | True 71 | >>> np.all(geoRecords.synthetic == las.synthetic) 72 | True 73 | >>> np.all(geoRecords.keypoint == las.keypoint) 74 | True 75 | >>> np.all(geoRecords.withheld == las.withheld) 76 | True 77 | >>> np.all(geoRecords.intensity == las.intensity) 78 | True 79 | >>> print(geoRecords.date.day) 80 | 4 81 | 82 | """ 83 | -------------------------------------------------------------------------------- /pyoints/examples/file_examples/ply_example.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Learn how to save and load .ply-files. 20 | 21 | >>> import os 22 | >>> from pyoints import storage 23 | 24 | Create an output path. 25 | 26 | >>> outpath = os.path.join( 27 | ... os.path.dirname(os.path.abspath(__file__)), '..', 'output') 28 | 29 | Create GeoRecords from scratch. 30 | 31 | >>> geoRecords = storage.misc.create_random_GeoRecords( 32 | ... center=[332592.88, 5513244.80, 120], epsg=25832) 33 | >>> print(geoRecords.shape) 34 | (1000,) 35 | >>> print(sorted(geoRecords.dtype.names)) 36 | ['classification', 'coords', 'intensity', 'keypoint', 'synthetic', 'values', 'withheld'] 37 | 38 | 39 | Save as .ply-file. 40 | 41 | >>> outfile = os.path.join(outpath, 'test.ply') 42 | >>> storage.writePly(geoRecords, outfile) 43 | 44 | Load .ply-file again and check the characteristics. 45 | 46 | >>> geoRecords = storage.loadPly(outfile) 47 | >>> print(geoRecords.shape) 48 | (1000,) 49 | >>> print(sorted(geoRecords.dtype.names)) 50 | ['classification', 'coords', 'intensity', 'keypoint', 'synthetic', 'values', 'withheld'] 51 | 52 | """ 53 | -------------------------------------------------------------------------------- /pyoints/examples/file_examples/raster_example.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Learn how to save and load image files. 20 | 21 | >>> import os 22 | >>> from pyoints import ( 23 | ... storage, 24 | ... transformation, 25 | ... projection, 26 | ... ) 27 | >>> from pyoints.misc import print_rounded 28 | 29 | 30 | Create input and output path. 31 | 32 | >>> inpath = os.path.join( 33 | ... os.path.dirname(os.path.abspath(__file__)), '..', 'data') 34 | >>> outpath = os.path.join( 35 | ... os.path.dirname(os.path.abspath(__file__)), '..', 'output') 36 | 37 | Load an image file. 38 | 39 | >>> infile = os.path.join(inpath, 'logo_pyoints.jpg') 40 | >>> proj = projection.Proj.from_epsg(32632) 41 | >>> rasterHandler = storage.RasterReader(infile, proj=proj) 42 | >>> raster = rasterHandler.load() 43 | 44 | >>> print_rounded(raster.shape) 45 | (96, 250) 46 | >>> print(sorted(raster.dtype.names)) 47 | ['bands', 'coords'] 48 | 49 | Apply a transformation to the matrix to get a propper spatial reference. 50 | 51 | >>> T = transformation.matrix( 52 | ... t=[332575, 5513229], s=[0.5, -0.5], r=0.1, order='srt') 53 | >>> raster = raster.transform(T) 54 | 55 | Save the image as a tif-file. You might like to check the spatial reference of 56 | the output image using a Geographic Information System (GIS). 57 | 58 | >>> outfile = os.path.join(outpath, 'test.tif') 59 | >>> storage.writeRaster(raster, outfile) 60 | 61 | Load image again and check characteristics. 62 | 63 | >>> rasterHandler = storage.RasterReader(outfile, proj=projection.Proj()) 64 | >>> print_rounded(rasterHandler.t.origin) 65 | [ 332575. 5513229.] 66 | 67 | 68 | >>> raster = rasterHandler.load() 69 | 70 | >>> print_rounded(raster.t.origin) 71 | [ 332575. 5513229.] 72 | >>> print_rounded(raster.shape) 73 | (96, 250) 74 | >>> print(sorted(raster.dtype.names)) 75 | ['bands', 'coords'] 76 | 77 | """ 78 | -------------------------------------------------------------------------------- /pyoints/examples/file_examples/structured_example.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Learn how to save and load structured files. 20 | 21 | >>> import os 22 | >>> import numpy as np 23 | >>> from pyoints import storage 24 | 25 | Create output path. 26 | 27 | >>> outpath = os.path.join( 28 | ... os.path.dirname(os.path.abspath(__file__)), '..', 'output') 29 | 30 | Create structured data from scratch. 31 | 32 | >>> data = { 33 | ... 'my_text': 'Some text', 34 | ... 'my_integer': 4, 35 | ... 'my_list': [0, 1, 2, 2], 36 | ... 'my_bool': False, 37 | ... 'my_ndarray': np.array([1, 4, 5]), 38 | ... 'nested': { 39 | ... 'my_text': 'Nested text.', 40 | ... 'my_float': 3.5 41 | ... }, 42 | ... 'my_recarray': np.array( 43 | ... [(1, 'text 1'), (6, 'text 2'), (2, 'text 3')], 44 | ... dtype=[('A', int), ('B', object)] 45 | ... ).view(np.recarray) 46 | ... } 47 | >>> print(data['my_text']) 48 | Some text 49 | >>> print(data['nested']['my_text']) 50 | Nested text. 51 | >>> print(type(data['my_ndarray'])) 52 | 53 | >>> print(type(data['my_recarray'])) 54 | 55 | 56 | Save as a .json-file. 57 | 58 | >>> outfile = os.path.join(outpath, 'test.json') 59 | >>> storage.writeJson(data, outfile) 60 | 61 | Load the a .json-file again. Be carefull, since some data types might have been 62 | changed. 63 | 64 | >>> data = storage.loadJson(outfile) 65 | >>> print(data['my_text']) 66 | Some text 67 | >>> print(data['nested']['my_float']) 68 | 3.5 69 | >>> print(type(data['my_ndarray'])) 70 | 71 | >>> print(type(data['my_recarray'])) 72 | 73 | 74 | """ 75 | -------------------------------------------------------------------------------- /pyoints/examples/grid_example.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """In this example we load a point cloud and convert it to rasters and voxels. 20 | 21 | 22 | >>> import os 23 | >>> import numpy as np 24 | 25 | >>> from pyoints import ( 26 | ... storage, 27 | ... projection, 28 | ... transformation, 29 | ... grid, 30 | ... Grid, 31 | ... filters, 32 | ... Surface, 33 | ... ) 34 | >>> from pyoints.misc import print_rounded 35 | 36 | 37 | First, we define input and output paths. 38 | 39 | >>> inpath = os.path.join( 40 | ... os.path.dirname(os.path.abspath(__file__)), 'data') 41 | >>> outpath = os.path.join( 42 | ... os.path.dirname(os.path.abspath(__file__)), 'output') 43 | 44 | 45 | Then, we select an input LAS point cloud. 46 | 47 | >>> infile = os.path.join(inpath, 'forest.las') 48 | >>> lasReader = storage.LasReader(infile) 49 | >>> las = lasReader.load() 50 | 51 | 52 | Now, let's convert the point cloud to a raster. All points within a raster cell 53 | are grouped to an individual point cloud. 54 | 55 | >>> T = transformation.matrix(t=las.t.origin[:2], s=[1.0, 2.0]) 56 | >>> raster = grid.voxelize(las, T) 57 | 58 | 59 | Let's inspect the properties of the raster. 60 | 61 | >>> print(raster.shape) 62 | (4, 7) 63 | >>> print(raster.dtype) 64 | object 65 | >>> print(len(raster[0, 0])) 66 | 11473 67 | >>> print(sorted(raster[0, 0].dtype.descr)) 68 | [('classification', '|u1'), ('coords', '>> print(sorted(raster[0, 0].dtype.names)) 70 | ['classification', 'coords', 'intensity'] 71 | >>> print(raster[0, 0].dtype.type) 72 | 73 | >>> print(raster[0, 0].dtype.flags) 74 | 16 75 | >>> print(raster[0, 0].dtype.str) 76 | |V26 77 | 78 | 79 | We can save the points of specific cells individually. 80 | 81 | >>> outfile = os.path.join(outpath, 'grid_cell.las') 82 | >>> storage.writeLas(raster[2, 1], outfile) 83 | 84 | 85 | We create a new raster, aggregating the point cloud data in a more specific 86 | manner. 87 | 88 | >>> T = transformation.matrix(t=las.t.origin[:2], s=[0.3, 0.3]) 89 | >>> def aggregate_function(ids): 90 | ... z = las.coords[ids, 2] 91 | ... n = len(ids) 92 | ... z_min = z.min() if n > 0 else 0 93 | ... z_mean = z.mean() if n > 0 else 0 94 | ... z_max = z.max() if n > 0 else 0 95 | ... return (n, [z_min, z_mean, z_max]) 96 | 97 | >>> dtype=[('cell_count', int), ('z', float, 3)] 98 | >>> raster = grid.voxelize(las, T, agg_func=aggregate_function, dtype=dtype) 99 | >>> raster = Grid(las.proj, raster, T) 100 | 101 | >>> print(raster.shape) 102 | (24, 24) 103 | >>> print(sorted(raster.dtype.names)) 104 | ['cell_count', 'coords', 'z'] 105 | 106 | 107 | We save the fields as individual raster images. 108 | 109 | >>> outfile = os.path.join(outpath, 'grid_count.tif') 110 | >>> storage.writeRaster(raster, outfile, field='cell_count') 111 | 112 | >>> outfile = os.path.join(outpath, 'grid_z.tif') 113 | >>> storage.writeRaster(raster, outfile, field='z') 114 | 115 | 116 | Test, if the projection has been set. 117 | 118 | >>> handler = storage.RasterReader(outfile) 119 | >>> raster.proj.wkt == handler.proj.wkt 120 | True 121 | 122 | Now, let's create a three dimensional voxel space. 123 | 124 | >>> T = transformation.matrix(t=las.t.origin, s=[0.4, 0.4, 0.5]) 125 | >>> def aggregate_function(ids): 126 | ... intensity = las.intensity[ids] 127 | ... n = len(ids) 128 | ... intensity = intensity.mean() if n > 0 else 0 129 | ... return (n, intensity) 130 | 131 | >>> dtype=[('cell_count', int), ('intensity', int)] 132 | 133 | >>> voxels = grid.voxelize(las, T, agg_func=aggregate_function, dtype=dtype) 134 | >>> voxels = Grid(las.proj, voxels, T) 135 | 136 | >>> print_rounded(voxels.shape) 137 | (61, 18, 18) 138 | >>> print(sorted(voxels.dtype.names)) 139 | ['cell_count', 'coords', 'intensity'] 140 | 141 | 142 | Finally, we save only the non-empty voxel cells. 143 | 144 | >>> outfile = os.path.join(outpath, 'grid_voxels.las') 145 | >>> storage.writeLas(voxels[voxels.cell_count > 0], outfile) 146 | 147 | """ 148 | -------------------------------------------------------------------------------- /pyoints/examples/output/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /pyoints/examples/stemfilter_example.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """In this example, we try to detect stems in a forest using a point cloud 20 | of terrestrial laser scans. A couple of .las-files will be generated, which 21 | should be inspected with software like CloudCompare. 22 | 23 | 24 | Let's begin with loading the required modules. 25 | 26 | >>> import os 27 | >>> import numpy as np 28 | 29 | >>> from pyoints.interpolate import KnnInterpolator 30 | >>> from pyoints import ( 31 | ... storage, 32 | ... grid, 33 | ... transformation, 34 | ... filters, 35 | ... clustering, 36 | ... classification, 37 | ... vector, 38 | ... GeoRecords, 39 | ... interpolate, 40 | ... ) 41 | >>> from pyoints.misc import print_rounded 42 | 43 | 44 | First, we define an input and an output path. 45 | 46 | >>> inpath = os.path.join( 47 | ... os.path.dirname(os.path.abspath(__file__)), 'data') 48 | >>> outpath = os.path.join( 49 | ... os.path.dirname(os.path.abspath(__file__)), 'output') 50 | 51 | 52 | Thereafter, we load an input LAS point cloud. 53 | 54 | >>> infile = os.path.join(inpath, 'forest.las') 55 | >>> lasReader = storage.LasReader(infile) 56 | >>> las = lasReader.load() 57 | >>> print(len(las)) 58 | 482981 59 | 60 | 61 | The basic idea of the algorithm is to first derive a digital elevation model to 62 | calculate the height above ground. We simply rasterize the point cloud by 63 | deriving the lowest z-coordinate of each cell. 64 | 65 | >>> T = transformation.matrix(t=las.t.origin[:2], s=[0.8, 0.8]) 66 | >>> def aggregate_function(ids): 67 | ... return las.coords[ids, 2].min() if len(ids) > 0 else np.nan 68 | >>> dtype = [('z', float)] 69 | >>> dem_grid = grid.voxelize(las, T, agg_func=aggregate_function, dtype=dtype) 70 | >>> print_rounded(dem_grid.shape) 71 | (9, 9) 72 | 73 | 74 | We save the DEM as a .tif-image. 75 | 76 | >>> outfile = os.path.join(outpath, 'stemfilter_dem.tif') 77 | >>> storage.writeRaster(dem_grid, outfile, field='z') 78 | 79 | 80 | We create a surface interpolator. 81 | 82 | >>> dem = KnnInterpolator(dem_grid.records().coords, dem_grid.records().z) 83 | 84 | 85 | For the stem detection, we will focus on points with height above ground 86 | greater 0.5 m. 87 | 88 | >>> height = las.coords[:, 2] - dem(las.coords) 89 | >>> s_ids = np.where(height > 0.5)[0] 90 | >>> print(len(s_ids)) 91 | 251409 92 | 93 | 94 | We filter the point cloud using a small filter radius. Only a subset of points 95 | with a point distance of at least 10 cm is kept. 96 | 97 | >>> f_ids = list(filters.ball(las.indexKD(), 0.1, order=s_ids)) 98 | >>> las = las[f_ids] 99 | >>> print(len(las)) 100 | 11181 101 | 102 | >>> outfile = os.path.join(outpath, 'stemfilter_ball_10.las') 103 | >>> storage.writeLas(las, outfile) 104 | 105 | 106 | We only keep points with a lot of neighbors to reduce noise. 107 | 108 | >>> count = las.indexKD().ball_count(0.3) 109 | >>> mask = count > 10 110 | >>> las = las[mask] 111 | >>> print(len(las)) 112 | 8154 113 | 114 | >>> outfile = os.path.join(outpath, 'stemfilter_denoised.las') 115 | >>> storage.writeLas(las, outfile) 116 | 117 | 118 | Now, we will filter with a radius of 1 m. This results in a point cloud 119 | with point distances of at least 1 m. Here, points associated with stems are 120 | arranged in straight lines. 121 | 122 | >>> f_ids = list(filters.ball(las.indexKD(), 1.0)) 123 | >>> las = las[f_ids] 124 | >>> print(len(las)) 125 | 189 126 | 127 | >>> outfile = os.path.join(outpath, 'stemfilter_ball_100.las') 128 | >>> storage.writeLas(las, outfile) 129 | 130 | For dense point clouds, the filtering technique results in point distances 131 | between 1 m and 2 m. Thus, we can assume that linear arranged points should 132 | have 2 to 3 neighboring points within a radius of 1.5 m. 133 | 134 | >>> count = las.indexKD().ball_count(1.5) 135 | >>> mask = np.all((count >= 2, count <= 3), axis=0) 136 | >>> las = las[mask] 137 | >>> print(len(las)) 138 | 84 139 | 140 | >>> outfile = os.path.join(outpath, 'stemfilter_linear.las') 141 | >>> storage.writeLas(las, outfile) 142 | 143 | 144 | Now, the stems are clearly visible in the point cloud. Thus, we can detect the 145 | stems by clustering the points. 146 | 147 | >>> cluster_indices = clustering.dbscan(las.indexKD(), 2, epsilon=1.5) 148 | 149 | >>> print(len(cluster_indices)) 150 | 84 151 | >>> print_rounded(np.unique(cluster_indices)) 152 | [-1 0 1 2 3 4 5] 153 | 154 | 155 | In the next step, we remove small clusters and unassigned points. 156 | 157 | >>> cluster_dict = classification.classes_to_dict(cluster_indices, min_size=5) 158 | >>> cluster_indices = classification.dict_to_classes(cluster_dict, len(las)) 159 | 160 | >>> print_rounded(sorted(cluster_dict.keys())) 161 | [0 1 3 5] 162 | 163 | 164 | We add an additional field to the point cloud to store the tree number. 165 | 166 | >>> las = las.add_fields([('tree_id', int)], data=[cluster_indices]) 167 | 168 | >>> outfile = os.path.join(outpath, 'stemfilter_stems.las') 169 | >>> storage.writeLas(las, outfile) 170 | 171 | 172 | Now, we can fit a vector to each stem. You should take a close look at the 173 | characteristics of the `Vector` object. 174 | 175 | >>> stems = {} 176 | >>> for tree_id in cluster_dict: 177 | ... coords = las[cluster_dict[tree_id]].coords 178 | ... stem = vector.Vector.from_coords(coords) 179 | ... stems[tree_id] = stem 180 | 181 | 182 | Finally, we determinate the tree root coordinates using the previously derived 183 | digital elevation model. 184 | 185 | >>> roots = [] 186 | >>> for tree_id in stems: 187 | ... stem = stems[tree_id] 188 | ... coord = vector.vector_surface_intersection(stem, dem) 189 | ... roots.append((tree_id, coord)) 190 | 191 | >>> dtype = [('tree_id', int), ('coords', float, 3)] 192 | >>> roots = np.array(roots, dtype=dtype).view(np.recarray) 193 | >>> roots = GeoRecords(las.proj, roots) 194 | 195 | >>> outfile = os.path.join(outpath, 'stemfilter_roots.las') 196 | >>> storage.writeLas(roots, outfile) 197 | 198 | """ 199 | -------------------------------------------------------------------------------- /pyoints/fit.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Fits shapes or functions to points. 20 | """ 21 | 22 | import numpy as np 23 | import cylinder_fitting 24 | 25 | from . import ( 26 | transformation, 27 | assertion, 28 | vector, 29 | ) 30 | from .misc import print_rounded 31 | 32 | 33 | def fit_sphere(coords, weights=1.0): 34 | """Least square fitting of a sphere to a set of points. 35 | 36 | Parameters 37 | ---------- 38 | coords : array_like(Number, shape=(n, k)) 39 | Represents n data points of `k` dimensions. 40 | weights : (k+1,k+1), array_like 41 | Transformation matrix. 42 | 43 | Returns 44 | ------- 45 | center : np.ndarray(Number, shape=(k)) 46 | Center of the sphere. 47 | r : Number 48 | Radius of the sphere. 49 | 50 | Notes 51 | ----- 52 | Idea taken from [1]. 53 | 54 | References 55 | ---------- 56 | 57 | [1] A. Bruenner (2001): URL 58 | http://www.arndt-bruenner.de/mathe/scripts/kreis3p.htm 59 | 60 | Examples 61 | -------- 62 | 63 | Draw points on a half circle with radius 5 and center (2, 4) and try to 64 | determine the circle parameters. 65 | 66 | >>> x = np.arange(-1, 1, 0.1) 67 | >>> y = np.sqrt(5**2 - x**2) 68 | >>> coords = np.array([x,y]).T + [2,4] 69 | >>> center, r, residuals = fit_sphere(coords) 70 | >>> print_rounded(center) 71 | [ 2. 4.] 72 | >>> print_rounded(r) 73 | 5.0 74 | 75 | """ 76 | coords = assertion.ensure_coords(coords) 77 | dim = coords.shape[1] 78 | 79 | if not assertion.isnumeric(weights): 80 | weights = assertion.ensure_numvector(weights, length=dim) 81 | 82 | # mean-centering to avoid overflow errors 83 | c = coords.mean(0) 84 | cCoords = coords - c 85 | 86 | # create matrices 87 | A = transformation.homogenious(cCoords, value=1) 88 | B = (cCoords**2).sum(1) 89 | 90 | A = (A.T * weights).T 91 | B = B * weights 92 | 93 | # solve equation system 94 | p, residuals, rank, s = np.linalg.lstsq(A, B, rcond=-1) 95 | 96 | bCenter = 0.5 * p[:dim] 97 | r = np.sqrt((bCenter**2).sum() + p[dim]) 98 | center = bCenter + c 99 | 100 | return center, r, residuals 101 | 102 | 103 | def fit_cylinder(coords, vec=None): 104 | """Fits a cylinder to points. 105 | 106 | Parameters 107 | ---------- 108 | coords : array_like(Number, shape=(n, k)) 109 | Represents n data points of `k` dimensions. 110 | vec : optional, array_like(Number, shape(k)) 111 | Estimated orientation of the cylinder axis. 112 | 113 | Returns 114 | ------- 115 | vec: vector.Vector 116 | Orientaton vector. 117 | r : Number 118 | Radius of the cylinder. 119 | resid : Number 120 | Remaining residuals. 121 | 122 | Examples 123 | -------- 124 | 125 | Prepare roto-translated half cylinder. 126 | 127 | >>> r = 2.5 128 | >>> x = np.arange(-1, 0, 0.01) * r 129 | >>> y = np.sqrt(r**2 - x**2) 130 | >>> y[::2] = - y[::2] 131 | >>> z = np.repeat(5, len(x)) 132 | >>> z[::2] = -5 133 | >>> T = transformation.matrix(t=[10, 20, 30], r=[0.3, 0.2, 0.0]) 134 | >>> coords = transformation.transform(np.array([x, y, z]).T, T) 135 | 136 | Get cylinder. 137 | 138 | >>> vec, r, residuals = fit_cylinder(coords, vec=[0, 0, 1]) 139 | 140 | >>> print_rounded(r) 141 | 2.5 142 | >>> print_rounded(vec.origin) 143 | [ 10. 20. 30.] 144 | 145 | Check distances to vector. 146 | 147 | >>> dists = vec.distance(coords) 148 | >>> print_rounded([np.min(dists), np.max(dists)]) 149 | [ 2.5 2.5] 150 | 151 | """ 152 | coords = assertion.ensure_coords(coords, dim=3) 153 | 154 | # set estimated direction 155 | if vec is not None: 156 | vec = assertion.ensure_numvector(vec, length=3) 157 | phi, theta = vector.direction(vec) 158 | guess_angles = [(phi, theta)] 159 | else: 160 | guess_angles = None 161 | 162 | # fit cylinder 163 | vec, origin, r, residuals = cylinder_fitting.fit( 164 | coords, 165 | guess_angles=guess_angles 166 | ) 167 | v = vector.Vector(origin, vec) 168 | 169 | return v, r, residuals 170 | -------------------------------------------------------------------------------- /pyoints/grid/__init__.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Handling of grid objects like image rasters or voxels.""" 20 | 21 | from .grid import * 22 | from .transformation import * 23 | -------------------------------------------------------------------------------- /pyoints/misc.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Some random functions, which ease development. 20 | """ 21 | 22 | import time 23 | import sys 24 | import pkg_resources 25 | import numpy as np 26 | from numbers import Number 27 | 28 | 29 | def tic(): 30 | global startTime_for_tictoc 31 | startTime_for_tictoc = time.time() 32 | 33 | 34 | def toc(): 35 | if 'startTime_for_tictoc' in globals(): 36 | print("Elapsed time is " + str(time.time() - 37 | startTime_for_tictoc) + " seconds.") 38 | return time.time() - startTime_for_tictoc 39 | else: 40 | print("Toc: start time not set") 41 | return None 42 | 43 | 44 | def list_licences(requirements_file): 45 | with open(requirements_file) as f: 46 | package_list = f.readlines() 47 | package_list = [pkgname.strip() for pkgname in package_list] 48 | for pkgname in package_list: 49 | try: 50 | pkg = pkg_resources.require(pkgname)[0] 51 | except BaseException: 52 | print("package '%s' not found" % pkgname) 53 | continue 54 | 55 | try: 56 | lines = pkg.get_metadata_lines('METADATA') 57 | except BaseException: 58 | lines = pkg.get_metadata_lines('PKG-INFO') 59 | 60 | for line in lines: 61 | if line.startswith('License:'): 62 | m = "%s : %s" % (str(pkg), line[9:]) 63 | print(m) 64 | 65 | 66 | def sizeof_fmt(num, suffix='B'): 67 | """ 68 | 69 | Notes 70 | ----- 71 | Taken form [1]. Originally posted by [2]. 72 | 73 | References 74 | ---------- 75 | [1] jan-glx (2018), https://stackoverflow.com/questions/24455615/python-how-to-display-size-of-all-variables, 76 | (acessed: 2018-08-16) 77 | [2] Fred Cirera, https://stackoverflow.com/a/1094933/1870254 78 | (acessed: 2018-08-16) 79 | 80 | """ 81 | for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: 82 | if abs(num) < 1024.0: 83 | return "%3.1f %s%s" % (num, unit, suffix) 84 | num /= 1024.0 85 | return "%.1f %s%s" % (num, 'Yi', suffix) 86 | 87 | 88 | def get_size(obj, seen=None): 89 | """Recursively finds size of objects""" 90 | size = sys.getsizeof(obj) 91 | 92 | if seen is None: 93 | seen = set() 94 | obj_id = id(obj) 95 | if obj_id in seen: 96 | return 0 97 | # Important mark as seen *before* entering recursion to gracefully handle 98 | # self-referential objects 99 | seen.add(obj_id) 100 | if isinstance(obj, (str, int, float)): 101 | pass 102 | elif isinstance(obj, np.ndarray): 103 | size += obj.nbytes 104 | elif isinstance(obj, dict): 105 | size += sum([get_size(v, seen) for v in obj.values()]) 106 | size += sum([get_size(k, seen) for k in obj.keys()]) 107 | elif hasattr(obj, '__dict__'): 108 | size += get_size(obj.__dict__, seen) 109 | elif (hasattr(obj, '__iter__') and 110 | not isinstance(obj, (str, bytes, bytearray))): 111 | size += sum([get_size(i, seen) for i in obj]) 112 | 113 | return size 114 | 115 | 116 | def print_object_size(obj): 117 | """Get the size of cached objects. 118 | 119 | Parameters 120 | ---------- 121 | obj : object 122 | Object to determine size from. 123 | 124 | """ 125 | print(sizeof_fmt(get_size(obj))) 126 | 127 | 128 | def print_rounded(values, decimals=2): 129 | """Prints rounded values. 130 | 131 | Parameters 132 | ---------- 133 | values : Number or array_like(Number) 134 | Values to display. 135 | decimals : optional, int 136 | Number of decimals passed to 'numpy.round'. 137 | 138 | Notes 139 | ----- 140 | Sets negative values close to zero to zero. 141 | 142 | """ 143 | if isinstance(values, Number): 144 | rounded = np.round(values, decimals=decimals) 145 | if np.isclose(rounded, 0): 146 | rounded = rounded * 0 147 | elif isinstance(values, (np.ndarray, list, tuple)): 148 | rounded = np.round(values, decimals=decimals) 149 | rounded[np.isclose(rounded, 0)] = 0 150 | else: 151 | raise ValueError("Data type '%s' not supported" % type(values)) 152 | 153 | if isinstance(values, tuple): 154 | rounded = tuple(rounded) 155 | 156 | np.set_printoptions( 157 | linewidth=75, 158 | precision=decimals, 159 | suppress=True, 160 | threshold=20, 161 | sign=' ', 162 | floatmode='unique', 163 | legacy='1.13' 164 | ) 165 | print(rounded) 166 | -------------------------------------------------------------------------------- /pyoints/polar.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Handling of polar coordinates. 20 | """ 21 | 22 | import numpy as np 23 | 24 | from .import ( 25 | distance, 26 | assertion, 27 | ) 28 | from .misc import print_rounded 29 | 30 | 31 | def coords_to_polar(coords): 32 | """Converts Cartesian coordinates to polar coordinates. 33 | 34 | Parameters 35 | ---------- 36 | coords : array_like(Number, shape=(n, k)) 37 | Represents `n` data points of `k` dimensions in a Cartesian coordinate 38 | system. 39 | 40 | Returns 41 | ------- 42 | pcoords : array_like(Number, shape=(n, k)) 43 | Represents `n` data points of `k` dimensions in a polar coordinate 44 | system. First column represents the distance to the origin of the 45 | coordinate system. All other columns represent the corresponding 46 | axes angles. 47 | 48 | See Also 49 | -------- 50 | polar_to_coords 51 | 52 | Examples 53 | -------- 54 | 55 | 2D coordinates. 56 | 57 | >>> coords = [(0, 0), (0, 1), (1, 0), (1, 1), (-1, 1), (2, -5)] 58 | >>> pcoords = coords_to_polar(coords) 59 | >>> print_rounded(pcoords, 3) 60 | [[ 0. 0. ] 61 | [ 1. 1.571] 62 | [ 1. 0. ] 63 | [ 1.414 0.785] 64 | [ 1.414 2.356] 65 | [ 5.385 -1.19 ]] 66 | 67 | 3D coordinates. 68 | 69 | >>> coords = [(0, 0, 0), (1, 1, 0), (-1, -1, -1), (2, -5, 9)] 70 | >>> pcoords = coords_to_polar(coords) 71 | >>> print_rounded(pcoords, 3) 72 | [[ 0. 0. 0. ] 73 | [ 1.414 0.785 1.571] 74 | [ 1.732 -2.356 2.186] 75 | [ 10.488 -1.19 0.539]] 76 | 77 | """ 78 | coords = assertion.ensure_coords(coords) 79 | 80 | dim = coords.shape[1] 81 | d = distance.norm(coords) 82 | if dim == 2: 83 | x = coords[:, 0] 84 | y = coords[:, 1] 85 | a = np.arctan2(y, x) 86 | return assertion.ensure_polar([d, a], by_col=True) 87 | elif dim == 3: 88 | x = coords[:, 0] 89 | y = coords[:, 1] 90 | z = coords[:, 2] 91 | 92 | # avoid nan 93 | omega = np.zeros(len(d)) 94 | mask = d > 0 95 | omega[mask] = np.arccos(z[mask] / d[mask]) 96 | 97 | phi = np.arctan2(y, x) 98 | return assertion.ensure_polar([d, phi, omega], by_col=True) 99 | else: 100 | raise ValueError('%i dimensions are not supported yet.' % dim) 101 | 102 | 103 | def polar_to_coords(pcoords): 104 | """Converts polar coordinates to Cartesian coordinates. 105 | 106 | Parameters 107 | ---------- 108 | pcoords : array_like(Number, shape=(n, k)) 109 | Represents `n` data points of `k` dimensions in a polar coordinate 110 | system. First column represents the distance to the origin of the 111 | coordinate system. All other columns represent the corresponding 112 | axes angles. 113 | 114 | Returns 115 | ------- 116 | coords : array_like(Number, shape=(n, k)) 117 | Represents `n` data points of `k` dimensions in a Cartesian coordinate 118 | system. 119 | 120 | See Also 121 | -------- 122 | coords_to_polar 123 | 124 | Examples 125 | -------- 126 | 127 | 2D coordinates. 128 | 129 | >>> pcoords = [(0, 0), (3, 0), (3, np.pi), (4, -0.5*np.pi), (1, 0.5)] 130 | >>> coords = polar_to_coords(pcoords) 131 | >>> print_rounded(coords, 3) 132 | [[ 0. 0. ] 133 | [ 3. 0. ] 134 | [-3. 0. ] 135 | [ 0. -4. ] 136 | [ 0.878 0.479]] 137 | 138 | 3D coordinates. 139 | 140 | >>> pcoords = [(0, 0, 0), (2, 0, 0),(4, 0, np.pi), (4, 0.5*np.pi, 0.5)] 141 | >>> coords = polar_to_coords(pcoords) 142 | >>> print_rounded(coords, 3) 143 | [[ 0. 0. 0. ] 144 | [ 0. 0. 2. ] 145 | [ 0. 0. -4. ] 146 | [ 0. 1.918 3.51 ]] 147 | 148 | """ 149 | pcoords = assertion.ensure_polar(pcoords) 150 | 151 | dim = pcoords.shape[1] 152 | d = pcoords[:, 0] 153 | if dim == 2: 154 | a = pcoords[:, 1] 155 | x = d * np.cos(a) 156 | y = d * np.sin(a) 157 | return assertion.ensure_coords([x, y], by_col=True) 158 | elif dim == 3: 159 | phi = pcoords[:, 1] 160 | omega = pcoords[:, 2] 161 | x = d * np.sin(omega) * np.cos(phi) 162 | y = d * np.sin(omega) * np.sin(phi) 163 | z = d * np.cos(omega) 164 | return assertion.ensure_coords([x, y, z], by_col=True) 165 | else: 166 | raise ValueError('%i dimensions are not supported yet' % dim) 167 | -------------------------------------------------------------------------------- /pyoints/registration/__init__.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Alignment of point clouds.""" 20 | from .registration import * 21 | from .rototranslations import find_rototranslations 22 | from .icp import ICP 23 | -------------------------------------------------------------------------------- /pyoints/registration/registration.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Registration or alignment of point sets. 20 | """ 21 | 22 | import numpy as np 23 | 24 | from .. import ( 25 | assertion, 26 | transformation, 27 | ) 28 | from ..misc import print_rounded 29 | 30 | 31 | def find_transformation(A, B): 32 | """Finds the optimal (non-rigid) transformation matrix `M` between two 33 | point sets. Each point of point set `A` is associated with exactly one 34 | point in point set `B`. 35 | 36 | Parameters 37 | ---------- 38 | A : array_like(Number, shape=(n, k)) 39 | Array representing n reference points with k dimensions. 40 | B : array_like(Number, shape=(n, k)) 41 | Array representing n points with k dimensions. 42 | 43 | Returns 44 | ------- 45 | M : np.matrix(Number, shape=(k+1, k+1)) 46 | Tranformation matrix which maps `B` to `A` with A = `B * M.T`. 47 | 48 | See Also 49 | -------- 50 | find_rototranslation 51 | 52 | """ 53 | b = transformation.homogenious(A) 54 | mA = transformation.homogenious(B) 55 | 56 | if not b.shape == mA.shape: 57 | raise ValueError('dimensions do not match') 58 | 59 | x = np.linalg.lstsq(mA, b, rcond=None)[0] 60 | M = x.T 61 | 62 | return M 63 | 64 | 65 | def find_rototranslation(A, B): 66 | """Finds the optimal roto-translation matrix `M` between two point sets. 67 | Each point of point set `A` is associated with exactly one point in point 68 | set `B`. 69 | 70 | Parameters 71 | ---------- 72 | A,B : array_like(Number, shape=(n, k)) 73 | Arrays representing `n` corresponding points with `k` dimensions. 74 | 75 | Returns 76 | ------- 77 | M : numpy.matrix(float, shape=(k+1, k+1)) 78 | Roto-translation matrix to map `B` to `A` with `A = B * M.T`. 79 | 80 | Notes 81 | ----- 82 | Implements the registration algorithm of Besl and McKay (1992) [1]. The 83 | idea has been taken from Nghia Ho (2013) [2]. Code of [2] has been 84 | generalized to `k` dimensional space. 85 | 86 | References 87 | ---------- 88 | 89 | [1] P. J. Besl and N. D. McKay (1992): "A Method for Registration of 3-D 90 | Shapes", IEEE Transactions on Pattern Analysis and Machine Intelligence, 91 | Institute of Electrical and Electronics Engineers (IEEE), vol. 14, 92 | pp. 239-256. 93 | 94 | [2] Nghia Ho (2013): "Finding optimal rotation and translation between 95 | corresponding 3D points", URL http:\/\/nghiaho.com/\?page\_id=671. 96 | 97 | [3] Nghia Ho (2013): "Finding optimal rotation and translation between 98 | corresponding 3D points", URL 99 | http:\/\/nghiaho.com/uploads/code/rigid\_transform\_3D.py\_. 100 | 101 | Examples 102 | -------- 103 | 104 | Creates similar, but shifted and rotated point sets. 105 | 106 | >>> A = np.array([[0, 0], [0, 1], [1, 1], [1, 0]]) 107 | >>> B = transformation.transform(A, transformation.matrix(t=[3, 5], r=0.3)) 108 | 109 | Finds roto-translation. 110 | 111 | >>> M = find_rototranslation(A, B) 112 | 113 | >>> C = transformation.transform(B, M, inverse=False) 114 | >>> print_rounded(C, 2) 115 | [[ 0. 0.] 116 | [ 0. 1.] 117 | [ 1. 1.] 118 | [ 1. 0.]] 119 | 120 | """ 121 | A = assertion.ensure_coords(A) 122 | B = assertion.ensure_coords(B) 123 | 124 | if not A.shape == B.shape: 125 | raise ValueError("coordinate dimensions do not match") 126 | 127 | cA = A.mean(0) 128 | cB = B.mean(0) 129 | mA = transformation.homogenious(A - cA, value=0) 130 | mB = transformation.homogenious(B - cB, value=0) 131 | 132 | # Find rotation matrix 133 | H = mA.T @ mB 134 | U, S, V = np.linalg.svd(H) 135 | R = U @ V 136 | 137 | # reflection case 138 | if np.linalg.det(R) < 0: 139 | R[-1, :] = -R[-1, :] 140 | # TODO test 141 | 142 | # Create transformation matrix 143 | T1 = transformation.t_matrix(cA) 144 | T2 = transformation.t_matrix(-cB) 145 | M = T1 @ R @ T2 146 | 147 | return transformation.LocalSystem(M) 148 | -------------------------------------------------------------------------------- /pyoints/smoothing.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Collection of algorithms to smooth point clouds. 20 | """ 21 | 22 | import numpy as np 23 | 24 | from .indexkd import IndexKD 25 | from . import ( 26 | assertion, 27 | ) 28 | from .misc import print_rounded 29 | 30 | 31 | def mean_ball( 32 | coords, r, 33 | num_iter=1, 34 | update_pairs=False, 35 | f=lambda coord, ncoords: ncoords.mean(0)): 36 | """Smoothing of spatial structures by iterative averaging the coordinates 37 | of neighboured points. 38 | 39 | Parameters 40 | ---------- 41 | coords : array_like(Number, shape=(n, k)) 42 | Array representing `n` points of `k` dimensions. 43 | r : Number 44 | Maximum distance to nearby points used to average the coordinates. 45 | num_iter : optional, positive int 46 | Number of iterations. 47 | update_pairs : optional, bool 48 | Specifies weather or not point pairs are updated on each iteration. 49 | f : callable 50 | Aggregate function used for smoothing. It receives the original point 51 | coordinate and the coordinates of neighboured points as an argument 52 | and returns a smoothed coordinate. 53 | 54 | See Also 55 | -------- 56 | mean_knn 57 | 58 | Examples 59 | -------- 60 | 61 | Create a three dimensional irregular surface of points. 62 | 63 | >>> coords = np.ones((100, 3), dtype=float) 64 | >>> coords[:, 0:2] = np.vstack(np.mgrid[0:10, 0:10].T) 65 | >>> coords[:, 2] = np.tile([1.05, 0.95], 50) 66 | 67 | Get value range in each coordinate dimension. 68 | 69 | >>> print_rounded(np.ptp(coords, axis=0)) 70 | [ 9. 9. 0.1] 71 | 72 | Smooth coordinates to get a more regular surface. But the first two 73 | coordinate dimensions are affected, too. 74 | 75 | >>> scoords = mean_ball(coords, 1.5) 76 | >>> print_rounded(np.ptp(scoords, axis=0), 3) 77 | [ 8. 8. 0.033] 78 | 79 | Modify the aggregation function to smooth the third coordinate axis only. 80 | 81 | >>> def aggregate_function(coord, ncoords): 82 | ... coord[2] = ncoords[:, 2].mean(0) 83 | ... return coord 84 | >>> scoords = mean_ball(coords, 1.5, f=aggregate_function) 85 | >>> print_rounded(np.ptp(scoords, axis=0), 3) 86 | [ 9. 9. 0.026] 87 | 88 | Increase number of iterations to get a smoother result. 89 | 90 | >>> scoords = mean_ball(coords, 1.5, num_iter=3, f=aggregate_function) 91 | >>> print_rounded(np.ptp(scoords, axis=0), 3) 92 | [ 9. 9. 0.01] 93 | 94 | """ 95 | coords = assertion.ensure_coords(coords) 96 | if not assertion.isnumeric(r): 97 | raise TypeError("'r' needs to a number") 98 | if not (isinstance(num_iter, int) and num_iter > 0): 99 | raise ValueError("'num_iter' needs to be an integer greater zero") 100 | if not isinstance(update_pairs, bool): 101 | raise TypeError("'update_pairs' needs to be boolean") 102 | 103 | ids = None 104 | mCoords = np.copy(coords) 105 | for _ in range(num_iter): 106 | 107 | if ids is None or update_pairs: 108 | indexKD = IndexKD(mCoords) 109 | ids = indexKD.ball(indexKD.coords, r) 110 | 111 | # averaging 112 | mCoords = np.array([ 113 | f(mCoords[i, :], mCoords[nIds, :]) for i, nIds in enumerate(ids) 114 | ]) 115 | 116 | return mCoords 117 | 118 | 119 | def mean_knn( 120 | coords, 121 | k, 122 | num_iter=1, 123 | update_pairs=False, 124 | f=lambda coord, ncoords: ncoords.mean(0)): 125 | """Smoothing of spatial structures by averaging neighboured point 126 | coordinates. 127 | 128 | Parameters 129 | ---------- 130 | coords : array_like(Number, shape=(n, l)) 131 | Array representing `n` points with `l` dimensions. 132 | k : float 133 | Number of nearest points used to average the coordinates. 134 | num_iter : optional, int 135 | Number of iterations. 136 | update_pairs : optional, bool 137 | Specifies weather or not point pairs are updated on each iteration. 138 | f : callable 139 | Aggregate function used for smoothing. It receives the original point 140 | coordinate and the coordinates of neighboured points as an argument 141 | and returns a smoothed coordinate. 142 | 143 | See Also 144 | -------- 145 | mean_ball 146 | 147 | Examples 148 | -------- 149 | 150 | Create a three dimensional irregular surface of points. 151 | 152 | >>> coords = np.ones((100, 3), dtype=float) 153 | >>> coords[:, 0:2] = np.vstack(np.mgrid[0:10, 0:10].T) 154 | >>> coords[:, 2] = np.tile([1.05, 0.95], 50) 155 | 156 | Get value range in each coordinate dimension. 157 | 158 | >>> print_rounded(np.ptp(coords, axis=0)) 159 | [ 9. 9. 0.1] 160 | 161 | Smooth coordinates to get a more regular surface. But the first two 162 | coordinate dimensions are affected, too. 163 | 164 | >>> scoords = mean_knn(coords, 5) 165 | >>> print_rounded(np.ptp(scoords, axis=0), 3) 166 | [ 8.2 8.2 0.02] 167 | 168 | Modify the aggregation function to smooth the third coordinate axis only. 169 | 170 | >>> def aggregate_function(coord, ncoords): 171 | ... coord[2] = ncoords[:, 2].mean(0) 172 | ... return coord 173 | >>> scoords = mean_knn(coords, 5, f=aggregate_function) 174 | >>> print_rounded(np.ptp(scoords, axis=0), 3) 175 | [ 9. 9. 0.033] 176 | 177 | """ 178 | coords = assertion.ensure_coords(coords) 179 | if not (isinstance(k, int) and k > 0): 180 | raise ValueError("'k' needs to be an integer greater zero") 181 | if not (isinstance(num_iter, int) and num_iter > 0): 182 | raise ValueError("'num_iter' needs to be an integer greater zero") 183 | if not isinstance(update_pairs, bool): 184 | raise TypeError("'update_pairs' needs to be boolean") 185 | 186 | ids = None 187 | mCoords = np.copy(coords) 188 | for _ in range(num_iter): 189 | 190 | if ids is None or update_pairs: 191 | indexKD = IndexKD(mCoords) 192 | ids = indexKD.knn(indexKD.coords, k=k)[1] 193 | 194 | # averaging 195 | mCoords = np.array([ 196 | f(mCoords[i, :], mCoords[nIds, :]) for i, nIds in enumerate(ids) 197 | ]) 198 | 199 | return mCoords 200 | -------------------------------------------------------------------------------- /pyoints/storage/BaseGeoHandler.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Basic handling of spatial files. 20 | """ 21 | 22 | import os 23 | from .. import ( 24 | assertion, 25 | projection 26 | ) 27 | import warnings 28 | from datetime import datetime 29 | 30 | 31 | class GeoFile: 32 | """Interface to read files containing spatial information. 33 | 34 | Parameters 35 | ---------- 36 | infile : String 37 | File to be read. It should contain data to be interpreted as points of 38 | `k` dimensions. 39 | directory : bool 40 | Indicates if the file is a composite of several files stored in a 41 | directory. 42 | 43 | Properties 44 | ---------- 45 | t : np.matrix(Number, shape=(k+1, k+1)) 46 | Transformation matrix to transform the `k`-dimensional points. Usually 47 | this matrix defines the origin of a local coordinate system. 48 | proj : Proj 49 | Coordinate projection system. 50 | extent : Extent(Number, shape=(2 * k)) 51 | Defines the spatial extent of the points. 52 | corners : np.ndarray(Number, shape=(2**k, k)) 53 | Corners of the extent. 54 | date : datetime 55 | Date of capture. 56 | 57 | See Also 58 | -------- 59 | Proj, Extent 60 | 61 | """ 62 | 63 | def __init__(self, infile, directory=False): 64 | if directory: 65 | if not os.path.isdir(infile): 66 | raise IOError('directory "%s" not found' % infile) 67 | elif not os.path.isfile(infile): 68 | raise IOError('file "%s" not found' % infile) 69 | 70 | self.file_name, self.extension = os.path.splitext( 71 | os.path.basename(infile)) 72 | self.extension = self.extension[1:] 73 | self.path = os.path.dirname(infile) 74 | self.file = os.path.abspath(infile) 75 | 76 | @property 77 | def t(self): 78 | return self._t 79 | 80 | @t.setter 81 | def t(self, t): 82 | t = assertion.ensure_tmatrix(t) 83 | self._t = t 84 | 85 | @property 86 | def date(self): 87 | return self._date 88 | 89 | @date.setter 90 | def date(self, date): 91 | if (date is not None) and (not isinstance(date, datetime)): 92 | m = "'date' needs to be of type 'datetime', got %s" % type(date) 93 | raise TypeError(m) 94 | self._date = date 95 | 96 | @property 97 | def proj(self): 98 | return self._proj 99 | 100 | @proj.setter 101 | def proj(self, proj): 102 | if proj is None: 103 | proj = projection.Proj() 104 | warnings.warn("'proj' not set, so I assume '%s'" % proj.proj4) 105 | elif not isinstance(proj, projection.Proj): 106 | m = "'proj' needs to be of type 'Proj', got %s" % type(proj) 107 | raise TypeError(m) 108 | self._proj = proj 109 | 110 | @property 111 | def extent(self): 112 | raise NotImplementedError() 113 | 114 | @property 115 | def corners(self): 116 | raise NotImplementedError() 117 | 118 | def __len__(): 119 | """Return the number of points. 120 | 121 | Returns 122 | ------- 123 | positive int 124 | Number of objects within the file. 125 | 126 | """ 127 | raise NotImplementedError() 128 | 129 | def load(self, extent=None): 130 | """Load data on demand. 131 | 132 | Parameters 133 | ---------- 134 | extent : optional, array_like(Number, shape=(2*self.dim)) 135 | Defines in which volume or area points shall be loaded. 136 | 137 | Returns 138 | ------- 139 | GeoRecords 140 | Desired geo-data of the file. 141 | 142 | """ 143 | raise NotImplementedError() 144 | 145 | def clean_cache(self): 146 | """Cleans all cached data. 147 | """ 148 | raise NotImplementedError() 149 | -------------------------------------------------------------------------------- /pyoints/storage/CsvHandler.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | import os 20 | import pandas 21 | import numpy as np 22 | 23 | from .. import nptools 24 | 25 | 26 | def loadCsv( 27 | infile, 28 | sep=",", 29 | multicol_sep=".", 30 | dtype=None, 31 | header=True, 32 | ignore='# ', 33 | **kwargs): 34 | """Simplified loading of .csv files. 35 | 36 | Parameters 37 | ---------- 38 | infile : String 39 | File to be read. 40 | sep : optional, Character 41 | Character separating the columns. 42 | multicol_sep : optional, Character 43 | Indicates how the column index of multi-column are separated form the 44 | column name. 45 | dtype : np.dtype 46 | Desired data type of the output numpy record array. 47 | header : bool 48 | Indicates 49 | *\\*kwargs : optional 50 | Arguments passed to `pandas.read_csv`. 51 | 52 | Returns 53 | ------- 54 | np.recarary 55 | Loaded data. 56 | 57 | See Also 58 | -------- 59 | writeCsv, pandas.read_csv 60 | 61 | """ 62 | if not os.path.isfile(infile): 63 | raise IOError('file "%s" not found' % infile) 64 | if dtype is None and not header: 65 | raise ValueError("please specify a header or data types") 66 | if not isinstance(header, bool): 67 | raise TypeError("'header' needs to be boolean") 68 | 69 | # specify meta data 70 | if dtype is not None: 71 | dtype = np.dtype(dtype) 72 | flat_names, flat_types = _flatten_dype(dtype, sep=multicol_sep) 73 | pd_header = 0 if header else None 74 | else: 75 | flat_types = None 76 | flat_names = None 77 | pd_header = 0 if header else 1 78 | 79 | if header and flat_names is None: 80 | with open(infile, 'r') as f: 81 | line = f.readline().replace(os.linesep, '').replace(ignore, '') 82 | flat_names = line.replace('\n', '').split(sep) 83 | 84 | # laod using pandas 85 | df = pandas.read_csv( 86 | infile, 87 | sep=sep, 88 | dtype=flat_types, 89 | names=flat_names, 90 | header=pd_header, 91 | skiprows=0, 92 | skip_blank_lines=False, 93 | **kwargs 94 | ) 95 | 96 | if dtype is None: 97 | # collect nested attributes automatically 98 | records = df.to_records() 99 | 100 | # collect information on multi-columns 101 | shape_dict = {} 102 | for name in records.dtype.names: 103 | v = name.split(multicol_sep) 104 | dt = records.dtype[name] 105 | if len(v) > 1: 106 | name = v[0] 107 | if name not in shape_dict: 108 | shape_dict[name] = int(v[1]) 109 | else: 110 | i = int(v[1]) 111 | if i < 0: 112 | raise ValueError("multi-columns need to start with 1") 113 | shape_dict[name] = max(shape_dict[name], i) 114 | 115 | if len(shape_dict) > 0: 116 | 117 | # collect multicolumns 118 | data_dict = {} 119 | for name in records.dtype.names: 120 | v = name.split(multicol_sep) 121 | if len(v) > 1: 122 | key = v[0] 123 | if key not in data_dict: 124 | shape = (len(records), shape_dict[key]) 125 | dt = records.dtype[name] 126 | data_dict[key] = np.empty(shape, dtype=dt) 127 | i = int(v[1]) - 1 128 | data_dict[key][:, i] = records[name] 129 | else: 130 | data_dict[name] = records[name] 131 | records = nptools.recarray(data_dict) 132 | 133 | else: 134 | data_dict = {} 135 | i = 0 136 | for key in dtype.names: 137 | dt = dtype[key] 138 | if len(dt.shape) > 0: 139 | data_dict[key] = np.array(df.iloc[:, i:i + dt.shape[0]]) 140 | i = i + dt.shape[0] 141 | else: 142 | data_dict[key] = np.array(df.iloc[:, i], dtype=dt) 143 | i = i + 1 144 | records = nptools.recarray(data_dict, dtype=dtype) 145 | 146 | return records 147 | 148 | 149 | def writeCsv(data, outfile, sep=",", multicol_sep=".", **kwargs): 150 | """Write an array to a csv-file. 151 | 152 | Parameters 153 | ---------- 154 | data : array_like 155 | Data to store. 156 | outfile : string 157 | File to write the data to. 158 | sep : optional, Character 159 | Desired field separator. 160 | multicol_sep : optional, Character 161 | Indicates how the column index of multi-column shall be separated form 162 | the column name. For example, the column names 'normal.1', 'normal.2' 163 | indicate a two dimensional attribute 'normal'. 164 | \*\*kwargs : optional 165 | Arguments passed to np.save_txt` 166 | 167 | See Also 168 | -------- 169 | loadCsv, np.save_txt 170 | 171 | Notes 172 | ----- 173 | Limited type validation. 174 | 175 | """ 176 | if not isinstance(data, (np.recarray, np.ndarray)): 177 | raise ValueError("'data' needs to be an numpy (record) array") 178 | if not os.access(os.path.dirname(outfile), os.W_OK): 179 | raise IOError('File %s is not writable' % outfile) 180 | 181 | # set column names 182 | names = _flatten_dype(data.dtype, sep=multicol_sep)[0] 183 | 184 | # unnest columns 185 | data = nptools.unnest(data, deep=True) 186 | data = list(zip(*data)) 187 | 188 | header = sep.join(names) 189 | 190 | np.savetxt( 191 | outfile, 192 | data, 193 | fmt="%s", 194 | delimiter=sep, 195 | header=header, 196 | *kwargs 197 | ) 198 | 199 | 200 | def _flatten_dype(dtype, sep='.'): 201 | # Helper function to get multi-column names. 202 | dtype = np.dtype(dtype) 203 | names = [] 204 | types = [] 205 | for name in dtype.names: 206 | dt = dtype[name] 207 | if len(dt.shape) > 0: 208 | for i in range(dt.shape[0]): 209 | flat_name = "%s%s%i" % (name, sep, i + 1) 210 | names.append(flat_name) 211 | types.append((flat_name, dt.subdtype[0].str)) 212 | else: 213 | names.append(name) 214 | types.append((name, dt.str)) 215 | return names, types 216 | -------------------------------------------------------------------------------- /pyoints/storage/DumpHandler.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | import codecs 20 | import dill as pickle 21 | 22 | from .BaseGeoHandler import GeoFile 23 | 24 | 25 | class DumpReader(GeoFile): 26 | """Class to read GeoRecords from python dump files. 27 | 28 | See Also 29 | -------- 30 | GeoFile 31 | 32 | """ 33 | 34 | def __init__(self, filename): 35 | GeoFile.__init__(self, filename) 36 | 37 | @property 38 | def proj(self): 39 | return self.load().proj4 40 | 41 | @property 42 | def corners(self): 43 | return self.extent.corners 44 | 45 | @property 46 | def extent(self): 47 | return self.load().extent() 48 | 49 | @property 50 | def t(self): 51 | return self.load().t 52 | 53 | def load(self): 54 | if not hasattr(self, '_records'): 55 | self._data = loadDump(self.file) 56 | return self._data 57 | 58 | def clean_cache(self): 59 | del self._data 60 | 61 | 62 | def loadDump(filename): 63 | """Loads a dump file. 64 | 65 | Parameters 66 | ---------- 67 | filename : String 68 | Dump file to load. 69 | 70 | Returns 71 | ------- 72 | object 73 | 74 | """ 75 | with open(filename, 'rb') as f: 76 | return pickle.load(f, pickle.HIGHEST_PROTOCOL) 77 | 78 | 79 | def writeDump(obj, filename): 80 | """Dump an object to a file. 81 | 82 | Parameters 83 | ---------- 84 | obj : object 85 | Object to dump 86 | outfile : String 87 | File to dump object to. 88 | 89 | """ 90 | with open(filename, 'wb') as f: 91 | pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL) 92 | 93 | 94 | def dumpstring_to_object(pickled): 95 | """Converts a dump string to an object. 96 | 97 | Parameters 98 | ---------- 99 | string: string 100 | Dump string 101 | 102 | Returns 103 | ------- 104 | object 105 | Loaded object. 106 | 107 | """ 108 | if pickled is None: 109 | return None 110 | unpickled = pickle.loads(codecs.decode(pickled.encode(), "base64")) 111 | return unpickled 112 | 113 | 114 | def dumpstring_from_object(data): 115 | """Converts an object to a dump string. 116 | 117 | Parameters 118 | ---------- 119 | string: string 120 | Dump string 121 | 122 | Returns 123 | ------- 124 | string 125 | Dump string. 126 | 127 | """ 128 | dump = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) 129 | pickled = codecs.encode(dump, "base64").decode() 130 | return pickled 131 | -------------------------------------------------------------------------------- /pyoints/storage/PlyHandler.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Handling of .ply-files. 20 | """ 21 | 22 | import os 23 | import plyfile 24 | import numpy as np 25 | 26 | from ..georecords import LasRecords 27 | from ..projection import Proj 28 | 29 | from ..misc import * 30 | import warnings 31 | 32 | 33 | def loadPly(infile, proj=Proj()): 34 | """Loads a .ply file. 35 | 36 | Parameters 37 | ---------- 38 | infile : String 39 | PLY-file to be read. 40 | 41 | Returns 42 | ------- 43 | np.recarray 44 | Loaded data. 45 | 46 | See Also 47 | -------- 48 | writePly 49 | 50 | """ 51 | if not os.path.isfile(infile): 52 | raise IOError('file "%s" not found' % infile) 53 | 54 | with warnings.catch_warnings(): 55 | # ignore UserWarning 56 | warnings.filterwarnings("ignore", category=UserWarning) 57 | plydata = plyfile.PlyData.read(infile) 58 | 59 | records = plydata['vertex'].data.view(np.recarray) 60 | 61 | # rename fields 62 | dtypes = [('coords', records.x.dtype, 3)] 63 | fields = [records.dtype.descr[i] for i in range(3, len(records.dtype))] 64 | dtypes.extend(fields) 65 | dtypes = np.dtype(dtypes) 66 | 67 | # change to propper names 68 | names = [] 69 | for name in dtypes.names: 70 | names.append(name.replace('scalar_', '')) 71 | dtypes.names = names 72 | records = records.view(dtypes).copy() 73 | 74 | return LasRecords(proj, records) 75 | 76 | 77 | def writePly(rec, outfile): 78 | """Saves data to a .ply file. 79 | 80 | Parameters 81 | ---------- 82 | rec : np.recarray 83 | Numpy record array to save. 84 | outfile : String 85 | Desired output .ply file . 86 | 87 | See Also 88 | -------- 89 | loadPly 90 | 91 | """ 92 | if not isinstance(rec, np.recarray): 93 | raise TypeError("'records' needs to be a numpy record array") 94 | 95 | # create view 96 | dtypes = [] 97 | for i, name in enumerate(rec.dtype.names): 98 | if name == 'coords': 99 | dtypes.extend([('x', float), ('y', float), ('z', float)]) 100 | else: 101 | dtypes.append(rec.dtype.descr[i]) 102 | rec = rec.view(dtypes) 103 | 104 | dtypes = [] 105 | for i, name in enumerate(rec.dtype.names): 106 | desc = list(rec.dtype.descr[i]) 107 | 108 | # change datatype if required (bug in plyfile?) 109 | if desc[1] == '. 18 | # END OF LICENSE NOTE 19 | """Loading and saving of files.""" 20 | from .BaseGeoHandler import * 21 | from .LasHandler import * 22 | from .RasterHandler import * 23 | from .CsvHandler import * 24 | from .DumpHandler import * 25 | from .PlyHandler import * 26 | from .structured import * 27 | from . import dtype_converters 28 | from . import misc 29 | -------------------------------------------------------------------------------- /pyoints/storage/dtype_converters.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Helper functions for data type conersion. 20 | """ 21 | 22 | import numpy as np 23 | from osgeo import gdal 24 | 25 | # Laspy 26 | ######## 27 | 28 | """ 29 | # see https://pythonhosted.org/laspy/tut_part_3.html 30 | 0 Raw Extra Bytes Value of "options" 31 | 1 unsigned char 1 byte 32 | 2 Char 1 byte 33 | 3 unsigned short 2 bytes 34 | 4 Short 2 bytes 35 | 5 unsigned long 4 bytes 36 | 6 Long 4 bytes 37 | 7 unsigned long long 8 bytes 38 | 8 long long 8 bytes 39 | 9 Float 4 bytes 40 | 10 Double 8 bytes 41 | 11 unsigned char[2] 2 byte 42 | 12 char[2] 2 byte 43 | 13 unsigned short[2] 4 bytes 44 | 14 short[2] 4 bytes 45 | 15 unsigned long[2] 8 bytes 46 | 16 long[2] 8 bytes 47 | 17 unsigned long long[2] 16 bytes 48 | 18 long long[2] 16 bytes 49 | 19 float[2] 8 bytes 50 | 20 double[2] 16 bytes 51 | 21 unsigned char[3] 3 byte 52 | 22 char[3] 3 byte 53 | 23 unsigned short[3] 6 bytes 54 | 24 short[3] 6 bytes 55 | 25 unsigned long[3] 12 bytes 56 | 26 long[3] 12 bytes 57 | 27 unsigned long long[3] 24 bytes 58 | 28 long long[3] 24 bytes 59 | 29 float[3] 12 bytes 60 | 30 double[3] 24 bytes 61 | """ 62 | 63 | LASPY_TYPE_MAP = [ 64 | (1, ['|u1']), 65 | (2, ['|S1']), 66 | (3, [' 0: 80 | type_id = key + len(LASPY_TYPE_MAP) * (dim - 1) 81 | LASPY_TO_NUMPY_TYPE[type_id] = (t[0], dim) 82 | 83 | NUMPY_TO_LASPY_TYPE = {} 84 | for dim in range(1, 4): 85 | NUMPY_TO_LASPY_TYPE[dim] = {} 86 | for t, p in LASPY_TYPE_MAP: 87 | type_id = t + len(LASPY_TYPE_MAP) * (dim - 1) 88 | for key in p: 89 | NUMPY_TO_LASPY_TYPE[dim][key] = type_id 90 | 91 | 92 | def numpy_to_laspy_dtype(dtype): 93 | """Converts a numpy data type to a laspy data type. 94 | 95 | Parameters 96 | ---------- 97 | dtype : np.dtype 98 | Numpy data type to convert. 99 | 100 | Returns 101 | ------- 102 | int 103 | Laspy data type id. 104 | 105 | Examples 106 | -------- 107 | 108 | >>> dtype = np.dtype(np.int32) 109 | >>> print(numpy_to_laspy_dtype(dtype)) 110 | 6 111 | 112 | """ 113 | dtype = np.dtype(dtype) 114 | if dtype.subdtype is None: 115 | dt = dtype 116 | type_name = dt.str 117 | type_dim = dt.shape[0] if len(dt.shape) > 0 else 1 118 | else: 119 | dt = dtype.subdtype 120 | type_name = dt[0].str 121 | type_dim = dt[1][0] if len(dt[1]) > 0 else 1 122 | if type_dim not in NUMPY_TO_LASPY_TYPE.keys(): 123 | return None 124 | if type_name not in NUMPY_TO_LASPY_TYPE[type_dim].keys(): 125 | return None 126 | return NUMPY_TO_LASPY_TYPE[type_dim][type_name] 127 | 128 | 129 | # GDAL 130 | ####### 131 | 132 | NUMPY_TO_GDAL_TYPE = { 133 | '|u1': gdal.GDT_Byte, 134 | '|i1': gdal.GDT_Byte, 135 | '>> dtype = np.dtype(np.int32) 166 | >>> print(numpy_to_gdal_dtype(dtype)) 167 | 5 168 | 169 | """ 170 | dtype = np.dtype(dtype) 171 | key = dtype.str 172 | if key not in NUMPY_TO_GDAL_TYPE: 173 | raise ValueError("data type '%s' not found" % key) 174 | return NUMPY_TO_GDAL_TYPE[key] 175 | -------------------------------------------------------------------------------- /pyoints/storage/misc.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | #!/usr/bin/env python3 20 | # -*- coding: utf-8 -*- 21 | """Some random functions, which ease development. 22 | """ 23 | 24 | import numpy as np 25 | 26 | from .. import projection 27 | from ..georecords import GeoRecords 28 | 29 | 30 | def create_random_GeoRecords(center=None, epsg=25832, dim=3, n=1000, scale=10): 31 | # Create GeoRecords from scratch (for examples) 32 | dtype = [ 33 | ('coords', np.float, dim), 34 | ('intensity', np.uint), 35 | ('classification', np.uint), 36 | ('values', np.float), 37 | ('keypoint', np.bool), 38 | ('synthetic', np.bool), 39 | ('withheld', np.bool) 40 | ] 41 | records = np.recarray(n, dtype=dtype) 42 | 43 | records['coords'] = np.random.rand(n, dim) * scale 44 | records['intensity'] = np.random.rand(n) * 255 45 | records['classification'] = 2 46 | records['classification'][records.coords[:, 2] > 0.1 * scale] = 3 47 | records['classification'][records.coords[:, 2] > 0.3 * scale] = 4 48 | records['classification'][records.coords[:, 2] > 0.5 * scale] = 5 49 | records['synthetic'][:4] = False 50 | records['synthetic'][1] = True 51 | records['keypoint'][:4] = False 52 | records['keypoint'][2] = True 53 | records['withheld'][:4] = False 54 | records['withheld'][3] = True 55 | 56 | records['values'] = np.arange(n) 57 | 58 | if center is not None: 59 | records['coords'] = records['coords'] + center 60 | 61 | proj = projection.Proj.from_epsg(epsg) 62 | 63 | return GeoRecords(proj, records) 64 | -------------------------------------------------------------------------------- /pyoints/storage/structured.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Reading and writing of common structured file types. 20 | """ 21 | import os 22 | import json 23 | 24 | from .. import assertion 25 | 26 | 27 | def loadJson(infile): 28 | """Loads a JSON file from disc. 29 | 30 | Parameters 31 | ---------- 32 | infile : str 33 | Input file. 34 | 35 | Returns 36 | ------- 37 | dict 38 | File data. 39 | 40 | """ 41 | if not os.path.isfile(infile): 42 | raise IOError('file "%s" not found' % infile) 43 | with open(infile, 'r') as f: 44 | js = json.load(f) 45 | return js 46 | 47 | 48 | def writeJson(data, outfile): 49 | """Writes a JSON file to disk. 50 | 51 | Parameters 52 | ---------- 53 | data : dict 54 | JSON compatible data to store. 55 | outfile : str 56 | Output JSON file. 57 | 58 | """ 59 | data = assertion.ensure_json(data) 60 | with open(outfile, 'w') as f: 61 | json.dump(data, f, indent=4) 62 | -------------------------------------------------------------------------------- /pyoints/surface.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Create surface models. 20 | """ 21 | 22 | from . import interpolate 23 | from . import assertion 24 | 25 | 26 | class Surface: 27 | """Creates a surface model based on representative points. 28 | 29 | Parameters 30 | ---------- 31 | coords : array_like(Number, shape=(n, k)) 32 | Represents `n` data points of `k` dimensions representing a surface. 33 | method : optional, Interpolator 34 | Interpolation method to use. 35 | \*\*kwargs : optional 36 | Arguments passed to the interpolation `method`. 37 | 38 | See Also 39 | -------- 40 | pyoints.interpolate.Interpolator 41 | 42 | Examples 43 | -------- 44 | 45 | >>> method = interpolate.LinearInterpolator 46 | >>> surface = Surface([(0, 0, 0), (0, 2, 0), (2, 1, 4)], method=method) 47 | 48 | >>> print(surface([(1, 1)])) 49 | 2.0 50 | >>> print(surface([(1, 1), (0.5, 1)])) 51 | [ 2. 1.] 52 | >>> print(surface([(1, 1, 2), (0.5, 1, 9)])) 53 | [ 2. 1.] 54 | 55 | """ 56 | 57 | def __init__(self, coords, method=interpolate.KnnInterpolator, **kwargs): 58 | coords = assertion.ensure_coords(coords) 59 | self._interpolator = method(coords[:, :-1], coords[:, -1], **kwargs) 60 | 61 | def __call__(self, coords): 62 | return self._interpolator(coords) 63 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | #numpy>=1.15 2 | #gdal 3 | #rtree 4 | #pyproj 5 | laspy 6 | scipy 7 | scikit-learn 8 | dill 9 | pandas 10 | opencv-python 11 | plyfile 12 | networkx 13 | cylinder_fitting -------------------------------------------------------------------------------- /scripts/autopep8.sh: -------------------------------------------------------------------------------- 1 | # requires python-autopep8 2 | SCRIPT_PATH=$(dirname $(realpath -s $0)) 3 | cd $SCRIPT_PATH 4 | autopep8 -r -i -a -a --experimental -v -v ../pyoints 5 | autopep8 -r -i -a -a --experimental -v -v ../tests 6 | -------------------------------------------------------------------------------- /scripts/conda_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SCRIPT_PATH=$(dirname $(realpath -s $0)) 3 | 4 | # build conda package 5 | cd ${SCRIPT_PATH}/../conda 6 | conda build . 7 | 8 | # In case of failure 9 | ##################### 10 | #conda build purge-all 11 | #conda clean -t 12 | #conda clean -p 13 | #conda clean -a 14 | 15 | 16 | # Testing the Environment 17 | ########################## 18 | #conda env remove -y -n pyoints_test 19 | #conda create -y -n pyoints_test -c $(conda build . --output) pyoints 20 | #conda create -n pyoints_env /path/to/builded/package/pyoints-*-py*_*.tar.bz2 --use-local 21 | 22 | 23 | # upload new package 24 | #anaconda login 25 | #anaconda upload $(conda build . --output) 26 | 27 | # Windows 28 | ############# 29 | #conda build . --debug 1> conda.log 2>&1 -------------------------------------------------------------------------------- /scripts/generateDocs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT_PATH=$(dirname $(realpath -s $0)) 4 | SOURCE_PATH='../pyoints' 5 | TUTORIALS_PATH='../tutorials' 6 | OUT_PATH='../docs' 7 | COMPILE_PATH='../sphinx-build' 8 | SPHINX_PATH='../sphinx' 9 | 10 | cd $SCRIPT_PATH 11 | 12 | if [ -d "$OUT_PATH" ]; then 13 | rm -r "$OUT_PATH" 14 | fi 15 | if [ -d "$COMPILE_PATH" ]; then 16 | rm -r "$COMPILE_PATH" 17 | fi 18 | 19 | jupyter nbconvert --to script "$TUTORIALS_PATH/*.ipynb" 20 | 21 | cp -r "$SPHINX_PATH" "$COMPILE_PATH" 22 | cp -r "$TUTORIALS_PATH" "$COMPILE_PATH" 23 | 24 | sphinx-apidoc -f -o "$COMPILE_PATH" "$SOURCE_PATH" 25 | python3 -m sphinx "$COMPILE_PATH" "$OUT_PATH" 26 | 27 | touch "$OUT_PATH/.nojekyll" 28 | -------------------------------------------------------------------------------- /scripts/templates/LICENSE_NOTES.txt: -------------------------------------------------------------------------------- 1 | This file is part of Pyoints. 2 | Copyright (c) 2018, Sebastian Lamprecht, Trier University, 3 | lamprecht@uni-trier.de 4 | 5 | Pyoints is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | Pyoints is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with Pyoints. If not, see . 17 | -------------------------------------------------------------------------------- /scripts/updateLicense.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SCRIPT_PATH=$(dirname $(realpath -s $0)) 3 | cd $SCRIPT_PATH 4 | 5 | LICENSE_NOTES_FILE='./templates/LICENSE_NOTES.txt' 6 | LICENSE_FILE='./templates/GPL_LICENSE.txt' 7 | 8 | OPENING_PATTERN='# BEGIN OF LICENSE NOTE' 9 | CLOSING_PATTERN='# END OF LICENSE NOTE' 10 | 11 | FILE_PATHS='../pyoints ../tests' 12 | LICENSE='../LICENSE' 13 | 14 | USAGE="Usage to update|insert|remove license: updateLicense.sh -u|-i|-r" 15 | 16 | 17 | FILES=$(find $FILE_PATHS -type f -name "*.py") 18 | 19 | 20 | function get_opening_pattern_line(){ 21 | file=$1 22 | echo $(grep -m 1 -n "$OPENING_PATTERN" $file | grep -Eo '^[^:]+') 23 | } 24 | 25 | function get_closing_pattern_line(){ 26 | file=$1 27 | echo $(grep -m 1 -n "$CLOSING_PATTERN" $file | grep -Eo '^[^:]+') 28 | } 29 | 30 | 31 | function update_license_notes(){ 32 | file=$1 33 | outfile=$2 34 | if [ ! -f $file ]; then 35 | echo "file $file does not exist" 36 | exit 1 37 | fi 38 | 39 | echo "update license notes $outfile" 40 | 41 | begin_line=$(get_opening_pattern_line $file) 42 | if [ "$begin_line" = "" ]; then 43 | echo "opening pattern not found" 44 | exit 1 45 | fi 46 | 47 | end_line=$(get_closing_pattern_line $file) 48 | if [ "$end_line" = "" ]; then 49 | echo "closing pattern not found" 50 | exit 1 51 | fi 52 | 53 | local IFS= 54 | heading_lines=$(head -n ${begin_line} < $file) 55 | tailing_lines=$(sed -n ${end_line},'$p' < $file) 56 | 57 | echo $heading_lines > $outfile 58 | while read line; do 59 | if [[ ! -z $line ]]; then 60 | echo "# $line" >> $outfile 61 | else 62 | echo "#" >> $outfile 63 | fi 64 | done <$LICENSE_NOTES_FILE 65 | echo $tailing_lines >> $outfile 66 | } 67 | 68 | function insert_license_notes(){ 69 | file=$1 70 | outfile=$2 71 | if [ ! -f "$file" ]; then 72 | echo "file $file does not exist" 73 | exit 1 74 | fi 75 | 76 | echo "insert license notes $outfile" 77 | 78 | local IFS= 79 | text=$(cat $file) 80 | echo "$OPENING_PATTERN" > $outfile 81 | echo "$CLOSING_PATTERN" >> $outfile 82 | echo "$text" >> $outfile 83 | 84 | update_license_notes $outfile $outfile 85 | } 86 | 87 | function remove_license_notes(){ 88 | file=$1 89 | outfile=$2 90 | if [ ! -f $file ]; then 91 | echo "file $file does not exist" 92 | exit 1 93 | fi 94 | 95 | echo "remove license notes $outfile" 96 | 97 | begin_line=$(get_opening_pattern_line $file) 98 | end_line=$(get_closing_pattern_line $file) 99 | 100 | if [ "$begin_line" = "" ] || [ "$end_line" = "" ]; then 101 | echo "nothing to remove" 102 | else 103 | let "begin_line=$begin_line-1" 104 | let "end_line=$end_line+1" 105 | 106 | local IFS= 107 | 108 | heading_lines=$(head -n ${begin_line} < $file) 109 | tailing_lines=$(sed -n ${end_line},'$p' < $file) 110 | 111 | > $outfile 112 | if [ ! $begin_line = "0" ]; then 113 | echo $heading_lines >> $outfile 114 | fi 115 | if [ ! $tailing_lines = "" ]; then 116 | echo $tailing_lines >> $outfile 117 | fi 118 | 119 | fi 120 | 121 | } 122 | 123 | function create_license_file(){ 124 | file=$1 125 | cp $LICENSE_NOTES_FILE $file 126 | echo >> $file 127 | echo >> $file 128 | echo "$(cat $LICENSE_FILE)" >> $file 129 | } 130 | 131 | 132 | if [[ $1 == "" ]]; then 133 | echo $USAGE 134 | exit 0 135 | fi 136 | while getopts uir option; do 137 | case "${option}" in 138 | u) 139 | # update license notes 140 | echo 'update license' 141 | 142 | create_license_file $LICENSE 143 | 144 | for file in $FILES; do 145 | update_license_notes $file $file 146 | done 147 | exit 0 148 | ;; 149 | i) 150 | create_license_file $LICENSE 151 | 152 | echo 'insert license note' 153 | for file in $FILES; do 154 | insert_license_notes $file $file 155 | done 156 | exit 0 157 | ;; 158 | r) 159 | echo 'remove license note' 160 | 161 | if [ -f $LICENSE ]; then 162 | rm $LICENSE 163 | fi 164 | 165 | for file in $FILES; do 166 | remove_license_notes $file $file 167 | done 168 | exit 0 169 | ;; 170 | *) 171 | echo $USAGE 172 | exit 0 173 | ;; 174 | esac 175 | done 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import setuptools 3 | 4 | # get path of script 5 | script_path = os.path.dirname(os.path.abspath(__file__)) 6 | 7 | # get long description from README 8 | with open(os.path.join(script_path, "README.md"), "r") as f: 9 | long_description = f.read() 10 | 11 | # get requirements 12 | with open(os.path.join(script_path, 'requirements.txt'), "r") as f: 13 | install_requires = [] 14 | for line in f: 15 | pkgname = line.partition('#')[0].rstrip() 16 | if pkgname is not '': 17 | install_requires.append(pkgname) 18 | 19 | # get package meta data 20 | with open(os.path.join(script_path, 'pyoints', 'about.py')) as f: 21 | about = {} 22 | exec(f.read(), about) 23 | 24 | 25 | setuptools.setup( 26 | name=about['__title__'], 27 | version=about['__version__'], 28 | author=about['__author__'], 29 | author_email=about['__email__'], 30 | description=about['__summary__'], 31 | long_description=long_description, 32 | long_description_content_type="text/markdown", 33 | url=about['__uri__'], 34 | project_urls={ 35 | 'Documentation': 'https://laempy.github.io/pyoints', 36 | 'GitHub': 'https://github.com/laempy/pyoints', 37 | }, 38 | packages=setuptools.find_packages(), 39 | install_requires=install_requires, 40 | classifiers=( 41 | "Intended Audience :: Science/Research", 42 | "Topic :: Scientific/Engineering :: GIS", 43 | "Topic :: Scientific/Engineering :: Information Analysis", 44 | "Programming Language :: Python :: 3", 45 | "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", 46 | "Operating System :: OS Independent", 47 | "Development Status :: 5 - Production/Stable", 48 | ), 49 | python_requires='>=3.5', 50 | license=about['__license__'], 51 | include_package_data=True, 52 | ) 53 | -------------------------------------------------------------------------------- /sphinx/.gitignore: -------------------------------------------------------------------------------- 1 | doctrees 2 | _static 3 | !conf.py 4 | !Makefile 5 | !make.bat 6 | -------------------------------------------------------------------------------- /sphinx/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = Pyoints 8 | SOURCEDIR = . 9 | BUILDDIR = . 10 | 11 | 12 | # Put it first so that "make" without argument is like "make help". 13 | help: 14 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 15 | 16 | .PHONY: help Makefile 17 | 18 | # Catch-all target: route all unknown targets to Sphinx using the new 19 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 20 | %: Makefile 21 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 22 | -------------------------------------------------------------------------------- /sphinx/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | import os 16 | import sys 17 | sys.path.insert(0, os.path.abspath('../')) 18 | script_path = os.path.dirname(os.path.abspath(__file__)) 19 | 20 | # -- Project information ----------------------------------------------------- 21 | 22 | # get package meta data 23 | with open(os.path.join(script_path, '..', 'pyoints', 'about.py')) as f: 24 | about = {} 25 | exec(f.read(), about) 26 | 27 | project = u'%s' % about['__title__'] 28 | copyright = u'%s' % about['__copyright__'] 29 | author = u'%s' % about['__author__'] 30 | 31 | # The short X.Y version 32 | version = u'%s' % about['__version__'] 33 | # The full version, including alpha/beta/rc tags 34 | release = u'%s' % about['__version__'] 35 | 36 | 37 | # -- General configuration --------------------------------------------------- 38 | 39 | # If your documentation needs a minimal Sphinx version, state it here. 40 | # 41 | # needs_sphinx = '1.0' 42 | 43 | # Add any Sphinx extension module names here, as strings. They can be 44 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 45 | # ones. 46 | extensions = [ 47 | 'sphinx.ext.autodoc', 48 | 'sphinx.ext.viewcode', 49 | #'sphinx.ext.todo', 50 | 'sphinx.ext.napoleon', 51 | 'sphinx.ext.coverage', 52 | #'sphinx.ext.autosummary', 53 | 'nbsphinx', 54 | 'sphinx.ext.mathjax', 55 | 'm2r', 56 | ] 57 | 58 | # Add any paths that contain templates here, relative to this directory. 59 | templates_path = ['_templates'] 60 | 61 | # The suffix(es) of source filenames. 62 | # You can specify multiple suffix as a list of string: 63 | # 64 | # source_suffix = ['.rst', '.md'] 65 | source_suffix = '.rst' 66 | 67 | # The master toctree document. 68 | master_doc = 'index' 69 | 70 | # The language for content autogenerated by Sphinx. Refer to documentation 71 | # for a list of supported languages. 72 | # 73 | # This is also used if you do content translation via gettext catalogs. 74 | # Usually you set "language" from the command line for these cases. 75 | language = None 76 | 77 | # List of patterns, relative to source directory, that match files and 78 | # directories to ignore when looking for source files. 79 | # This pattern also affects html_static_path and html_extra_path . 80 | exclude_patterns = [ 81 | #u'_build', 82 | 'Thumbs.db', 83 | '.DS_Store', 84 | '_build', 85 | '**.ipynb_checkpoints' 86 | ] 87 | 88 | # The name of the Pygments (syntax highlighting) style to use. 89 | pygments_style = 'sphinx' 90 | 91 | 92 | # -- Options for HTML output ------------------------------------------------- 93 | 94 | # The theme to use for HTML and HTML Help pages. See the documentation for 95 | # a list of builtin themes. 96 | # 97 | #html_theme = 'alabaster' 98 | html_theme = 'classic' 99 | 100 | # Theme options are theme-specific and customize the look and feel of a theme 101 | # further. For a list of options available for each theme, see the 102 | # documentation. 103 | # 104 | # html_theme_options = {} 105 | 106 | # Add any paths that contain custom static files (such as style sheets) here, 107 | # relative to this directory. They are copied after the builtin static files, 108 | # so a file named "default.css" will overwrite the builtin "default.css". 109 | html_static_path = [] 110 | 111 | # Custom sidebar templates, must be a dictionary that maps document names 112 | # to template names. 113 | # 114 | # The default sidebars (for documents that don't match any pattern) are 115 | # defined by theme itself. Builtin themes are using these templates by 116 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 117 | # 'searchbox.html']``. 118 | # 119 | # html_sidebars = {} 120 | 121 | 122 | # -- Options for HTMLHelp output --------------------------------------------- 123 | 124 | # Output file base name for HTML help builder. 125 | htmlhelp_basename = '%s_doc' % about['__title__'] 126 | 127 | 128 | # -- Options for LaTeX output ------------------------------------------------ 129 | 130 | latex_elements = { 131 | # The paper size ('letterpaper' or 'a4paper'). 132 | # 133 | # 'papersize': 'letterpaper', 134 | 135 | # The font size ('10pt', '11pt' or '12pt'). 136 | # 137 | # 'pointsize': '10pt', 138 | 139 | # Additional stuff for the LaTeX preamble. 140 | # 141 | # 'preamble': '', 142 | 143 | # Latex figure (float) alignment 144 | # 145 | # 'figure_align': 'htbp', 146 | } 147 | 148 | # Grouping the document tree into LaTeX files. List of tuples 149 | # (source start file, target name, title, 150 | # author, documentclass [howto, manual, or own class]). 151 | latex_documents = [ 152 | ( 153 | master_doc, 154 | '%s.tex', about['__title__'], 155 | u'%s Documentation' % about['__title__'], 156 | u'%s' % about['__author__'], 157 | 'manual' 158 | ), 159 | ] 160 | 161 | 162 | # -- Options for manual page output ------------------------------------------ 163 | 164 | # One entry per manual page. List of tuples 165 | # (source start file, name, description, authors, manual section). 166 | man_pages = [ 167 | ( 168 | master_doc, 169 | '%s', about['__title__'], 170 | u'%s Documentation' % about['__title__'], 171 | [author], 172 | 1 173 | ) 174 | ] 175 | 176 | 177 | # -- Options for Texinfo output ---------------------------------------------- 178 | 179 | # Grouping the document tree into Texinfo files. List of tuples 180 | # (source start file, target name, title, author, 181 | # dir menu entry, description, category) 182 | texinfo_documents = [ 183 | ( 184 | master_doc, 185 | about['__title__'], 186 | u'Pyoints Documentation', 187 | author, 188 | about['__title__'], 189 | about['__summary__'], 190 | 'Miscellaneous' 191 | ), 192 | ] 193 | 194 | 195 | html_logo = os.path.join(script_path, '..', 'figures', 'logo_pyoints.png') 196 | -------------------------------------------------------------------------------- /sphinx/contributing.rst: -------------------------------------------------------------------------------- 1 | .. mdinclude:: ../CONTRIBUTING.md -------------------------------------------------------------------------------- /sphinx/index.rst: -------------------------------------------------------------------------------- 1 | Pyoints Documentation 2 | ===================== 3 | 4 | 5 | ******** 6 | Overview 7 | ******** 8 | 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | 13 | readme 14 | contributing 15 | 16 | .. toctree:: 17 | :titlesonly: 18 | 19 | license 20 | 21 | 22 | ********* 23 | Tutorials 24 | ********* 25 | 26 | .. toctree:: 27 | :maxdepth: 2 28 | 29 | tutorials/getting_started.ipynb 30 | tutorials/stem_detection.ipynb 31 | tutorials/icp.ipynb 32 | 33 | 34 | .. csv-table:: Downloads 35 | :header: "Topic", "Jupiter Notebook", "Python Script" 36 | :widths: 20, 20, 20 37 | 38 | "Getting started", :download:`getting_started.ipynb `, :download:`getting_started.py ` 39 | "ICP", :download:`icp.ipynb `, :download:`icp.py ` 40 | "Stem detection", :download:`stem_detection.ipynb `, :download:`stem_detection.py ` 41 | 42 | 43 | *** 44 | API 45 | *** 46 | 47 | .. toctree:: 48 | :maxdepth: 4 49 | 50 | modules 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /sphinx/license.rst: -------------------------------------------------------------------------------- 1 | LICENSE 2 | ======= 3 | 4 | .. mdinclude:: ../LICENSE -------------------------------------------------------------------------------- /sphinx/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=PoYnts 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /sphinx/readme.rst: -------------------------------------------------------------------------------- 1 | .. mdinclude:: ../README.md -------------------------------------------------------------------------------- /tests/test_pyoints.py: -------------------------------------------------------------------------------- 1 | # BEGIN OF LICENSE NOTE 2 | # This file is part of Pyoints. 3 | # Copyright (c) 2018, Sebastian Lamprecht, Trier University, 4 | # lamprecht@uni-trier.de 5 | # 6 | # Pyoints is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # Pyoints is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with Pyoints. If not, see . 18 | # END OF LICENSE NOTE 19 | """Run all tests of Pyoints. 20 | """ 21 | 22 | import unittest 23 | import doctest 24 | import pkgutil 25 | 26 | import pyoints 27 | 28 | 29 | def get_tests(root_package): 30 | """Collect all doctests within a package. 31 | 32 | Parameters 33 | ---------- 34 | root_package : package 35 | Root package containing docstrings to tests. 36 | 37 | Returns 38 | ------- 39 | list of doctest.DocTestSuite 40 | Docstring test suites. 41 | 42 | """ 43 | # https://stackoverflow.com/questions/1615406/configure-django-to-find-all-doctests-in-all-modules 44 | suites = [] 45 | if hasattr(root_package, '__path__'): 46 | modules = pkgutil.walk_packages( 47 | root_package.__path__, 48 | root_package.__name__ + '.') 49 | for _, module_name, _ in modules: 50 | try: 51 | suite = doctest.DocTestSuite(module_name) 52 | except ValueError: 53 | # Presumably a "no docstrings" error. That's OK. 54 | pass 55 | else: 56 | suites.append(suite) 57 | else: 58 | suites.append(doctest.DocTestSuite(root_package)) 59 | return suites 60 | 61 | 62 | def load_tests(loader, tests, ignore): 63 | tests.addTests(get_tests(pyoints.storage)) 64 | tests.addTests(get_tests(pyoints.assertion)) 65 | tests.addTests(get_tests(pyoints.assign)) 66 | tests.addTests(get_tests(pyoints.classification)) 67 | tests.addTests(get_tests(pyoints.clustering)) 68 | tests.addTests(get_tests(pyoints.coords)) 69 | tests.addTests(get_tests(pyoints.distance)) 70 | tests.addTests(get_tests(pyoints.extent)) 71 | tests.addTests(get_tests(pyoints.filters)) 72 | tests.addTests(get_tests(pyoints.fit)) 73 | tests.addTests(get_tests(pyoints.georecords)) 74 | tests.addTests(get_tests(pyoints.grid)) 75 | tests.addTests(get_tests(pyoints.indexkd)) 76 | tests.addTests(get_tests(pyoints.interpolate)) 77 | tests.addTests(get_tests(pyoints.misc)) 78 | tests.addTests(get_tests(pyoints.normals)) 79 | tests.addTests(get_tests(pyoints.nptools)) 80 | tests.addTests(get_tests(pyoints.polar)) 81 | tests.addTests(get_tests(pyoints.projection)) 82 | tests.addTests(get_tests(pyoints.registration)) 83 | tests.addTests(get_tests(pyoints.smoothing)) 84 | tests.addTests(get_tests(pyoints.surface)) 85 | tests.addTests(get_tests(pyoints.transformation)) 86 | tests.addTests(get_tests(pyoints.vector)) 87 | 88 | tests.addTests(get_tests(pyoints.examples.file_examples)) 89 | tests.addTests(get_tests(pyoints.examples.base_example)) 90 | tests.addTests(get_tests(pyoints.examples.grid_example)) 91 | tests.addTests(get_tests(pyoints.examples.stemfilter_example)) 92 | 93 | return tests 94 | 95 | 96 | class test_pyoints(unittest.TestCase): 97 | 98 | def test_about(self): 99 | self.assertTrue(hasattr(pyoints, '__version__')) 100 | self.assertTrue(hasattr(pyoints, '__title__')) 101 | self.assertTrue(hasattr(pyoints, '__summary__')) 102 | self.assertTrue(hasattr(pyoints, '__uri__')) 103 | self.assertTrue(hasattr(pyoints, '__author__')) 104 | self.assertTrue(hasattr(pyoints, '__email__')) 105 | self.assertTrue(hasattr(pyoints, '__license__')) 106 | self.assertTrue(hasattr(pyoints, '__copyright__')) 107 | 108 | 109 | print('Run doctests for Pyoints %s' % pyoints.__version__) 110 | unittest.main() 111 | -------------------------------------------------------------------------------- /tutorials/.gitignore: -------------------------------------------------------------------------------- 1 | # Generic files to ignore 2 | *.py 3 | -------------------------------------------------------------------------------- /tutorials/bun000_binary.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/tutorials/bun000_binary.ply -------------------------------------------------------------------------------- /tutorials/bun045_binary.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/tutorials/bun045_binary.ply -------------------------------------------------------------------------------- /tutorials/bun090_binary.ply: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/tutorials/bun090_binary.ply -------------------------------------------------------------------------------- /tutorials/forest.las: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laempy/pyoints/be574c9343e8dec789dada554117088cd86ec17d/tutorials/forest.las --------------------------------------------------------------------------------