Example #1
0
    def crowns_to_polys_raster(self):
        ''' Converts tree crown raster to individual polygons and stores them
        in the tree dataframe
        '''
        polys = []
        for feature in rioshapes(self.crowns, mask=self.crowns.astype(bool)):

            # Convert pixel coordinates to lon/lat
            edges = feature[0]['coordinates'][0].copy()
            for i in range(len(edges)):
                edges[i] = self._to_lonlat(*edges[i], self.resolution)

            # poly_smooth = self.smooth_poly(Polygon(edges), s=None, k=9)
            polys.append(Polygon(edges))
        self.trees.crown_poly_raster = polys
Example #2
0
    def crowns_to_polys_smooth(self, store_las=True):
        """ Smooth crown polygons using Dalponte & Coomes (2016) approach:
        Builds a convex hull around first return points (which lie within the
        rasterized crowns).
        Optionally, the trees in the LiDAR point cloud are classified based on
        the generated convex hull.

        Parameters
        ----------
        store_las :    bool
                       set to True if LiDAR point clouds shopuld be classified
                       and stored externally
        """
        print('Converting LAS point cloud to shapely points')
        geometry = [Point(xy) for xy in zip(self.las.x, self.las.y)]
        lidar_geodf = gpd.GeoDataFrame(self.las,
                                       crs=f'epsg:{self.epsg}',
                                       geometry=geometry)

        print('Converting raster crowns to shapely polygons')
        polys = []
        for feature in rioshapes(self.crowns, mask=self.crowns.astype(bool)):
            edges = np.array(list(zip(*feature[0]['coordinates'][0])))
            edges = np.array(
                self._to_lonlat(edges[0], edges[1], self.resolution)).T
            polys.append(Polygon(edges))
        crown_geodf = gpd.GeoDataFrame(pd.DataFrame(np.arange(len(
            self.trees))),
                                       crs=f'epsg:{self.epsg}',
                                       geometry=polys)

        print('Attach LiDAR points to corresponding crowns')
        lidar_in_crowns = gpd.sjoin(lidar_geodf,
                                    crown_geodf,
                                    op='within',
                                    how="inner")

        lidar_tree_class = np.zeros(lidar_in_crowns['index_right'].size)
        lidar_tree_mask = np.zeros(lidar_in_crowns['index_right'].size,
                                   dtype=bool)

        print('Create convex hull around first return points')
        polys = []
        for tidx in range(len(self.trees)):
            bool_indices = lidar_in_crowns['index_right'] == tidx
            lidar_tree_class[bool_indices] = tidx
            points = lidar_in_crowns[bool_indices]
            # check that not all values are the same
            if len(points.z) > 1 and not np.allclose(points.z,
                                                     points.iloc[0].z):
                points = points[points.z >= threshold_otsu(points.z)]
                points = points[points.return_num == 1]
            hull = points.unary_union.convex_hull
            polys.append(hull)
            lidar_tree_mask[bool_indices] = \
                lidar_in_crowns[bool_indices].within(hull)
        self.trees.crown_poly_smooth = polys

        if store_las:
            print('Classifying point cloud')
            lidar_in_crowns = lidar_in_crowns[lidar_tree_mask]
            lidar_tree_class = lidar_tree_class[lidar_tree_mask]
            header = laspy.header.Header()
            self.outpath.mkdir(parents=True, exist_ok=True)
            outfile = laspy.file.File(self.outpath / "trees.las",
                                      mode="w",
                                      header=header)
            xmin = np.floor(np.min(lidar_in_crowns.x))
            ymin = np.floor(np.min(lidar_in_crowns.y))
            zmin = np.floor(np.min(lidar_in_crowns.z))
            outfile.header.offset = [xmin, ymin, zmin]
            outfile.header.scale = [0.001, 0.001, 0.001]
            outfile.x = lidar_in_crowns.x
            outfile.y = lidar_in_crowns.y
            outfile.z = lidar_in_crowns.z
            outfile.intensity = lidar_tree_class
            outfile.close()

        self.lidar_in_crowns = lidar_in_crowns