diff --git a/.gitignore b/.gitignore index f7f7224..9768c6e 100644 --- a/.gitignore +++ b/.gitignore @@ -20,20 +20,9 @@ changelog_0.2.1.md pyfortest/temp.* /.idea /.vs -/docs/api/setup.rst -/docs/api/pyfortest.test_pyfor.rst -/docs/api/pyfortest.test_collection.rst -/docs/api/pyfortest.rst -/docs/html/.doctrees/advanced -/docs/html/.doctrees/api -/docs/html/.doctrees/topics -/docs/html/.doctrees -/docs/html/api/pyfortest.html -/docs/html/api/pyfortest.test_collection.html -/docs/html/api/pyfortest.test_pyfor.html -/docs/html/api/setup.html -/docs/html/_sources -/docs/html/_static -/pyfortest/__pycache__ -/__pycache__ +/environment.yml~ +/CHANGELOG.md~ +/README.md~ +/.environment.yml.un~ +/.CHANGELOG.md.un~ /temp_tif.tif diff --git a/README.md b/README.md index 791590b..74c21fc 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,11 @@ memory optimized API for managing large collections of tiles. ## Release Status -Current Release: 0.3.5 +Current Release: 0.3.6 -Release Date: September 9, 2019 +Release Date: [] -Release Status: 0.3.5 is an adolescent LiDAR data processing package adequate for single tile processing and large acqusitions. +Release Status: 0.3.6 is an adolescent LiDAR data processing package adequate for single tile processing and large acqusitions. ## What Does pyfor Do? diff --git a/pyfor.pyproj b/pyfor.pyproj new file mode 100644 index 0000000..67de259 --- /dev/null +++ b/pyfor.pyproj @@ -0,0 +1,128 @@ + + + + Debug + 2.0 + {aa3e2147-3c18-4aa6-9a18-4dd5b1504264} + + setup.py + + . + . + {888888a0-9f3d-457c-b088-3a5042f75d52} + Standard Python launcher + CondaEnv|CondaEnv|pyfor_env + unittest + test_*.py + pyfortest + + + + + 10.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pyfor/cloud.py b/pyfor/cloud.py index a05e02c..c5b7503 100644 --- a/pyfor/cloud.py +++ b/pyfor/cloud.py @@ -115,6 +115,19 @@ def __init__(self, path): self.normalized = None self.crs = None + @classmethod + def from_pdal(cls, ins): + """ + Converts a PDAL `ins` argument from a PDAL `filters.python` into a `Cloud` object. + + :param ins: The `ins` argument from PDAL. + """ + + df = pd.DataFrame(ins) + df = df.rename(columns={"X": "x", "Y": "y", "Z":"z", "ReturnNumber": "return_num"}) + cloud_data = pyfor.cloud.CloudData(df, header=None) + return cls(cloud_data) + def _get_las_points(self, las): """ Reads points into pandas dataframe. diff --git a/pyfor/ground_filter.py b/pyfor/ground_filter.py index 4ae9373..e169510 100644 --- a/pyfor/ground_filter.py +++ b/pyfor/ground_filter.py @@ -200,6 +200,9 @@ def _filter(self, grid): """ Runs the actual ground filter. Generally used as an internal function that is called by user functions (.bem, .classify, .ground_points). + + :param grid: A `pyfor.rasterizer.Grid` object. + :return: A `pandas.DataFrame` of filtered points. """ np.seterr(divide='ignore', invalid='ignore') @@ -256,7 +259,9 @@ def bem(self, cloud, cell_size): def classify(self, cloud, ground_int=2): """ Sets the classification of the original input cloud points to ground (default 2 as per las specification). This - performs the adjustment of the input `Cloud` object **in place**. Only implemented for `.las` files. + performs the adjustment of the input `Cloud` object **in place**. Only implemented for `.las` files. Additionally, + the original ground classification is preserved if it exists, so this will only add additional ground points for + already classified point clouds. :param cloud: A cloud object. :param ground_int: The integer to set classified points to, the default is 2 in the las specification for ground @@ -265,8 +270,8 @@ def classify(self, cloud, ground_int=2): if cloud.extension == '.las': grid = cloud.grid(self.cell_size) - self._filter(grid) - grid.cloud.data.points["classification"][grid.cloud.data.points['v_i'] <= self.g + self.w] = ground_int + filtered_point_ids = self._filter(grid).index + grid.cloud.data.points["classification"][filtered_point_ids] = ground_int else: print("This is only implemented for .las files.") diff --git a/pyfortest/__pycache__/__init__.cpython-36.pyc b/pyfortest/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..c92c28a Binary files /dev/null and b/pyfortest/__pycache__/__init__.cpython-36.pyc differ diff --git a/pyfortest/__pycache__/test_collection.cpython-36.pyc b/pyfortest/__pycache__/test_collection.cpython-36.pyc new file mode 100644 index 0000000..a770499 Binary files /dev/null and b/pyfortest/__pycache__/test_collection.cpython-36.pyc differ diff --git a/pyfortest/__pycache__/test_pyfor.cpython-36.pyc b/pyfortest/__pycache__/test_pyfor.cpython-36.pyc new file mode 100644 index 0000000..f9d500a Binary files /dev/null and b/pyfortest/__pycache__/test_pyfor.cpython-36.pyc differ diff --git a/pyfortest/test_pyfor.py b/pyfortest/test_pyfor.py index b8c5f1a..6bf0b2f 100644 --- a/pyfortest/test_pyfor.py +++ b/pyfortest/test_pyfor.py @@ -384,6 +384,10 @@ def setUp(self): def test_filter(self): self.test_kp_filter._filter(self.test_cloud.grid(self.test_kp_filter.cell_size)) + def test_classify(self): + self.test_kp_filter.classify(self.test_cloud) + self.assertEqual(133410, np.sum(self.test_cloud.data.points["classification"] == 2)) + class Zhang2003TestCase(unittest.TestCase): def setUp(self): self.test_cloud = cloud.Cloud(test_las) diff --git a/test_environment.yml b/test_environment.yml new file mode 100644 index 0000000..8cae8e4 --- /dev/null +++ b/test_environment.yml @@ -0,0 +1,27 @@ +name: pyfor_env + +dependencies: + - python=3.7 + - gdal + - rasterio + - conda-forge::pyproj + - numpy + - pandas + - scipy + - scikit-image + - scikit-learn + - pyqtgraph + - matplotlib + - pyopengl + - numba + - libkml + - geopandas + - joblib + - poppler + - conda-forge::lastools + - pip: + - laspy + - laxpy + - sphinx_rtd_theme + - python-coveralls + - plyfile