Skip to content

Commit

Permalink
docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
Sakharov committed Sep 13, 2023
1 parent 704c268 commit 0820f32
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 18 deletions.
45 changes: 35 additions & 10 deletions aeronet_vector/aeronet_vector/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .utils import utm_zone, CRS_LATLON
import shapely
import numpy as np
from typing import Callable


class Feature:
Expand Down Expand Up @@ -37,14 +38,13 @@ def _valid(self, shape):
shape = shape.buffer(0)
return shape

def apply(self, func, inplace=False):
def apply(self, func: Callable, inplace: bool = False):
"""Applies function geometry
Args:
func (Callable): function to apply
inplace (bool): if True modifies Feature inplace, else returns new Feature
Returns:
new Feature if inplace, else None
"""
new Feature if inplace, else None"""
if inplace:
self._geometry = func(self._geometry)
else:
Expand Down Expand Up @@ -76,7 +76,7 @@ def IoU(self, other):
return self._geometry.intersection(other._geometry).area / self._geometry.union(other._geometry).area

def as_geojson(self, hold_crs=False):
""" Return Feature as GeoJSON formatted dict
""" Returns Feature as GeoJSON formatted dict
Args:
hold_crs (bool): serialize with current projection, that could be not ESPG:4326 (which is standards violation)
Returns:
Expand Down Expand Up @@ -117,15 +117,29 @@ def as_geojson(self, hold_crs=False):
def geojson(self):
return self.as_geojson()

def reproject(self, dst_crs):
def reproject(self, dst_crs, inplace: bool = False):
"""Reprojects Feature to dst_crs
Args:
dst_crs (str or CRS): crs to reproject to
inplace (bool): if True modifies Feature inplace, else returns new Feature
Returns:
new Feature if inplace, else None"""
new_geometry = transform_geom(
src_crs=self.crs,
dst_crs=dst_crs,
geom=self.geometry,
)
return Feature(new_geometry, properties=self.properties, crs=dst_crs)
if inplace:
self._geometry = new_geometry
else:
return Feature(new_geometry, properties=self.properties, crs=dst_crs)

def reproject_to_utm(self):
"""Alias of `reproject` method with automatic Band utm zone determining
The utm zone is determined according to the center of the bounding box of the collection.
Does not suit to large area geometry, that would not fit into one zone (about 6 dergees in longitude)
Returns:
new Feature"""
lon1, lat1, lon2, lat2 = self.shape.bounds
# todo: BUG?? handle non-latlon CRS!
dst_crs = utm_zone((lat1 + lat2)/2, (lon1 + lon2)/2)
Expand All @@ -135,15 +149,26 @@ def copy(self):
"""Returns a copy of feature"""
return Feature(shape(self.geometry), {k: v for k, v in self.properties.items()}, self.crs)

def simplify(self, tolerance, inplace=True):
"""Simplifies geometry with Douglas-Pecker"""
def simplify(self, tolerance: float, inplace: bool = True):
"""Simplifies geometry with Douglas-Pecker
Args:
tolerance (float): simplification tolerance
inplace (bool): if True modifies Feature inplace, else returns new Feature
Returns:
new Feature if inplace, else None"""
if inplace:
self._geometry = self._geometry.simplify(tolerance)
else:
return self.copy().simplify(tolerance, inplace=True)

def cast_property_to(self, key, new_type, inplace=True):
"""Casts property to new type inplace (e.g. str to int)"""
def cast_property_to(self, key: str, new_type: type, inplace: bool = True):
"""Casts property to new type (e.g. str to int)
Args:
key (str): key of modified property
new_type (bool): type to cast to
inplace (bool): if True modifies Feature inplace, else returns new Feature
Returns:
new Feature if inplace, else None"""
if inplace:
self.properties[key] = new_type(self.properties.get(key))
else:
Expand Down
60 changes: 52 additions & 8 deletions aeronet_vector/aeronet_vector/featurecollection.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from rasterio.errors import CRSError
from .feature import Feature
from .utils import utm_zone, CRS_LATLON
from typing import Callable


class FeatureCollection:
Expand Down Expand Up @@ -47,7 +48,7 @@ def _valid(self, features):
valid_features.append(f)
return valid_features

def apply(self, func, inplace=False):
def apply(self, func: Callable, inplace: bool = True):
"""Applies function to collection geometries
Args:
func (Callable): function to apply
Expand All @@ -61,27 +62,57 @@ def apply(self, func, inplace=False):
else:
return FeatureCollection([f.apply(func) for f in self.features], crs=self.crs)

def filter(self, func, inplace=True):
return FeatureCollection(filter(func, self.features), crs=self.crs)
def filter(self, func: Callable, inplace: bool = True):
"""Filters collection according to func
Args:
func (Callable): filtering function
inplace (bool): if True modifies collection inplace, else returns new collection
Returns:
new FeatureCollection if inplace, else None
"""
if inplace:
self.features = list(filter(func, self.features))
else:
return FeatureCollection(filter(func, self.features), crs=self.crs)

def sort(self, key, reverse=False):
def sort(self, key: Callable, reverse: bool = False):
"""Sorts collection inplace
Args:
key (Callable): sorting function
reverse (bool): if True, ascending sorting order, else descending"""
self.features.sort(key=key, reverse=reverse)

def extend(self, fc):
"""Extends collection with another collection (inplace)
Args:
fc (FeatureCollection): collection to extend with"""
for i, f in enumerate(fc):
self.index.add(i + len(self), f.bounds)
self.features.extend(fc.features)

def append(self, feature):
"""Appends feature to the collection (inplace)
Args:
feature (Feature): Feature to append"""
self.index.add(len(self), feature.bounds)
self.features.append(feature)

def bounds_intersection(self, feature):
"""Returns subset of collection features, which bounding boxes intersects with given feature bbox
Args:
feature (Feature): Feature to check intersection with
Returns:
FeatureCollection"""
idx = self.index.intersection(feature.bounds)
features = [self.features[i] for i in idx]
return FeatureCollection(features, self.crs)

def intersection(self, feature):
"""Returns subset of collection features, which intersects with given feature
Args:
feature (Feature): Feature to check intersection with
Returns:
FeatureCollection"""
proposed_features = self.bounds_intersection(feature)
features = []
for pf in proposed_features:
Expand Down Expand Up @@ -237,23 +268,36 @@ def reproject_to_utm(self):
Alias of `reproject` method with automatic Band utm zone determining
The utm zone is determined according to the center of the bounding box of the collection.
Does not suit to large area geometry, that would not fit into one zone (about 6 dergees in longitude)
Returns:
new reprojected FeatureCollection
"""
return self.reproject(dst_crs='utm')

def copy(self):
"""Returns a copy of collection"""
return FeatureCollection((f.copy() for f in self.features), crs=self.crs)

def simplify(self, tolerance, inplace=True):
"""Simplifies all features with Douglas-Pecker"""
def simplify(self, tolerance: float, inplace: bool = True):
"""Simplifies geometries with Douglas-Pecker
Args:
tolerance (float): simplification tolerance
inplace (bool): if True modifies Feature inplace, else returns new Feature
Returns:
FeatureCollection if inplace, else None"""
if inplace:
for f in self.features:
f.simplify(tolerance, inplace=True)
else:
return self.copy().simplify(tolerance, inplace=True)

def cast_property_to(self, key, new_type, inplace=True):
"""Casts property to new type (e.g. str to int)"""
def cast_property_to(self, key: str, new_type: type, inplace: bool = True):
"""Casts property to new type (e.g. str to int)
Args:
key (str): key of modified property
new_type (bool): type to cast to
inplace (bool): if True modifies Feature inplace, else returns new Feature
Returns:
FeatureCollection if inplace, else None"""
if inplace:
for f in self.features:
f.cast_property_to(key, new_type, inplace=True)
Expand Down

0 comments on commit 0820f32

Please sign in to comment.