Skip to content

Commit 49657b2

Browse files
authored
feat: filter per geometry Type (#484)
2 parents 419f6d6 + e143d77 commit 49657b2

File tree

6 files changed

+345
-8
lines changed

6 files changed

+345
-8
lines changed

geetools/ee_feature_collection.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,3 +796,73 @@ def areaSort(self, ascending: bool = True) -> ee.FeatureCollection:
796796
# sort by area and remove the property from the output
797797
properties = fc.first().propertyNames().remove(name)
798798
return fc.sort(name, ascending).map(lambda feat: feat.select(properties))
799+
800+
def filterGeometryType(self, type_: str | ee.String) -> ee.FeatureCollection:
801+
"""Filter the collection by geometry type.
802+
803+
Only keep in the Final featureCollection the Feature with the specified geometry type.
804+
Can be combined with ``breakGeometries`` to filter out multi geometries.
805+
806+
Args:
807+
type_: The geometry type to filter on. Must be one of the `GEE compatible types <https://developers.google.com/earth-engine/guides/geometries>`__
808+
809+
Returns:
810+
The filtered collection
811+
812+
Examples:
813+
.. jupyter-execute::
814+
815+
import ee, geetools
816+
from geetools.utils import initialize_documentation
817+
818+
initialize_documentation()
819+
820+
geoms = [ee.Geometry.Point([0, 0]), ee.Geometry.Point([0, 0]).buffer(1)]
821+
fc = ee.FeatureCollection([ee.Feature(g, {"test": "test"}) for g in geoms])
822+
fc = fc.geetools.breakGeometries().geetools.filterGeometryType("Point")
823+
fc.aggregate_array("test").getInfo()
824+
"""
825+
# extract the properties of the features before filtering
826+
# and create an extra column with the geometry type
827+
type_, name = ee.String(type_), "__geetools_type__"
828+
properties = self._obj.first().propertyNames()
829+
fc = self._obj.map(lambda feat: feat.set(name, feat.geometry().type()))
830+
831+
# filter the collection and remove the extra column
832+
fc = fc.filter(ee.Filter.eq(name, type_)).select(properties)
833+
834+
return ee.FeatureCollection(fc)
835+
836+
def breakGeometries(self) -> ee.FeatureCollection:
837+
"""Break every feature using geometries into it's constituents.
838+
839+
Each Geometry that is using a multi parts geometry type will be duplicated
840+
into multiple features with each one carrying one of the constituent of the multiPolygon.
841+
842+
Returns:
843+
ee.FeatureCollection: The collection with the broken down geometries
844+
845+
Examples:
846+
.. code-block:: python
847+
848+
import ee, geetools
849+
from geetools.utils import initialize_documentation
850+
851+
initialize_documentation()
852+
853+
multipoint = ee.Geometry.MultiPoint([ee.Geometry.Point([0, 0]), ee.Geometry.Point([1, 0])])
854+
fc = ee.FeatureCollection([ee.Feature(multipoint, {"test": "test"})])
855+
fc = fc.geetools.breakGeometries()
856+
fc.aggregate_array("test").getInfo()
857+
"""
858+
# helper function to break the geometry of a feature
859+
def split(feat):
860+
feat = ee.Feature(feat)
861+
geometries = feat.geometry().geometries()
862+
return geometries.map(lambda g: ee.Feature(ee.Geometry(g), feat.toDictionary()))
863+
864+
# apply the function to the collection and flatten the list as each feature
865+
# can have multiple geometries (thus list of list) and we want a single list
866+
list = self._obj.toList(self._obj.size()).map(split).flatten()
867+
868+
return ee.FeatureCollection(list)

tests/test_FeatureCollection.py

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -363,11 +363,37 @@ def gdfZ(self):
363363
}
364364
return gpd.GeoDataFrame.from_features(data["features"])
365365

366-
class TestAreaSort:
367-
"""Test the ``areaSort`` method."""
368-
369-
def test_area_sort(self, ee_list_regression):
370-
fc = ee.FeatureCollection("FAO/GAUL/2015/level0").limit(5)
371-
fc = fc.geetools.areaSort()
372-
property = fc.aggregate_array("ADM0_NAME")
373-
ee_list_regression.check(property)
366+
367+
class TestAreaSort:
368+
"""Test the ``areaSort`` method."""
369+
370+
def test_area_sort(self, ee_list_regression):
371+
fc = ee.FeatureCollection("FAO/GAUL/2015/level0").limit(5)
372+
fc = fc.geetools.areaSort()
373+
property = fc.aggregate_array("ADM0_NAME")
374+
ee_list_regression.check(property)
375+
376+
377+
class TestFilterGeometryType:
378+
"""Test the ``filterGeometryType`` method."""
379+
380+
def test_filter_geometry_type(self, ee_feature_collection_regression):
381+
geometries = [ee.Geometry.Point([0, 0]), ee.Geometry.Point([0, 0]).buffer(1)]
382+
fc = ee.FeatureCollection([ee.Feature(g, {"test": "test"}) for g in geometries])
383+
fc = fc.geetools.filterGeometryType("Point")
384+
ee_feature_collection_regression.check(fc)
385+
386+
387+
class TestBreakGeometries:
388+
"""Test the ``breakGeometries`` method."""
389+
390+
def test_break_geometries(self, fc, ee_feature_collection_regression):
391+
fc = fc.geetools.breakGeometries()
392+
ee_feature_collection_regression.check(fc)
393+
394+
@pytest.fixture
395+
def fc(self):
396+
point0 = ee.Geometry.Point([0, 0])
397+
point1 = ee.Geometry.Point([1, 0])
398+
multipoint = ee.Geometry.MultiPoint([point0, point1])
399+
return ee.FeatureCollection([ee.Feature(multipoint, {"test": "test"})])
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
result: '0'
2+
values:
3+
'0':
4+
functionInvocationValue:
5+
arguments:
6+
features:
7+
functionInvocationValue:
8+
arguments:
9+
list:
10+
functionInvocationValue:
11+
arguments:
12+
baseAlgorithm:
13+
functionDefinitionValue:
14+
argumentNames:
15+
- _MAPPING_VAR_1_0
16+
body: '1'
17+
dropNulls:
18+
constantValue: false
19+
list:
20+
functionInvocationValue:
21+
arguments:
22+
collection:
23+
valueReference: '3'
24+
count:
25+
functionInvocationValue:
26+
arguments:
27+
collection:
28+
valueReference: '3'
29+
functionName: Collection.size
30+
functionName: Collection.toList
31+
functionName: List.map
32+
functionName: List.flatten
33+
functionName: Collection
34+
'1':
35+
functionInvocationValue:
36+
arguments:
37+
baseAlgorithm:
38+
functionDefinitionValue:
39+
argumentNames:
40+
- _MAPPING_VAR_0_0
41+
body: '2'
42+
dropNulls:
43+
constantValue: false
44+
list:
45+
functionInvocationValue:
46+
arguments:
47+
geometry:
48+
functionInvocationValue:
49+
arguments:
50+
feature:
51+
argumentReference: _MAPPING_VAR_1_0
52+
functionName: Feature.geometry
53+
functionName: Geometry.geometries
54+
functionName: List.map
55+
'2':
56+
functionInvocationValue:
57+
arguments:
58+
geometry:
59+
argumentReference: _MAPPING_VAR_0_0
60+
metadata:
61+
functionInvocationValue:
62+
arguments:
63+
element:
64+
argumentReference: _MAPPING_VAR_1_0
65+
functionName: Element.toDictionary
66+
functionName: Feature
67+
'3':
68+
functionInvocationValue:
69+
arguments:
70+
features:
71+
arrayValue:
72+
values:
73+
- functionInvocationValue:
74+
arguments:
75+
geometry:
76+
functionInvocationValue:
77+
arguments:
78+
coordinates:
79+
arrayValue:
80+
values:
81+
- functionInvocationValue:
82+
arguments:
83+
coordinates:
84+
constantValue:
85+
- 0
86+
- 0
87+
functionName: GeometryConstructors.Point
88+
- functionInvocationValue:
89+
arguments:
90+
coordinates:
91+
constantValue:
92+
- 1
93+
- 0
94+
functionName: GeometryConstructors.Point
95+
functionName: GeometryConstructors.MultiPoint
96+
metadata:
97+
constantValue:
98+
test: test
99+
functionName: Feature
100+
functionName: Collection
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
result: '0'
2+
values:
3+
'0':
4+
functionInvocationValue:
5+
arguments:
6+
baseAlgorithm:
7+
functionDefinitionValue:
8+
argumentNames:
9+
- _MAPPING_VAR_0_0
10+
body: '1'
11+
collection:
12+
functionInvocationValue:
13+
arguments:
14+
collection:
15+
functionInvocationValue:
16+
arguments:
17+
baseAlgorithm:
18+
functionDefinitionValue:
19+
argumentNames:
20+
- _MAPPING_VAR_0_0
21+
body: '5'
22+
collection:
23+
valueReference: '2'
24+
functionName: Collection.map
25+
filter:
26+
functionInvocationValue:
27+
arguments:
28+
leftField:
29+
valueReference: '6'
30+
rightValue:
31+
constantValue: Point
32+
functionName: Filter.equals
33+
functionName: Collection.filter
34+
functionName: Collection.map
35+
'1':
36+
functionInvocationValue:
37+
arguments:
38+
input:
39+
argumentReference: _MAPPING_VAR_0_0
40+
propertySelectors:
41+
functionInvocationValue:
42+
arguments:
43+
element:
44+
functionInvocationValue:
45+
arguments:
46+
collection:
47+
valueReference: '2'
48+
functionName: Collection.first
49+
functionName: Element.propertyNames
50+
retainGeometry:
51+
constantValue: true
52+
functionName: Feature.select
53+
'2':
54+
functionInvocationValue:
55+
arguments:
56+
features:
57+
arrayValue:
58+
values:
59+
- functionInvocationValue:
60+
arguments:
61+
geometry:
62+
valueReference: '3'
63+
metadata:
64+
valueReference: '4'
65+
functionName: Feature
66+
- functionInvocationValue:
67+
arguments:
68+
geometry:
69+
functionInvocationValue:
70+
arguments:
71+
distance:
72+
constantValue: 1
73+
geometry:
74+
valueReference: '3'
75+
functionName: Geometry.buffer
76+
metadata:
77+
valueReference: '4'
78+
functionName: Feature
79+
functionName: Collection
80+
'3':
81+
functionInvocationValue:
82+
arguments:
83+
coordinates:
84+
constantValue:
85+
- 0
86+
- 0
87+
functionName: GeometryConstructors.Point
88+
'4':
89+
constantValue:
90+
test: test
91+
'5':
92+
functionInvocationValue:
93+
arguments:
94+
key:
95+
valueReference: '6'
96+
object:
97+
argumentReference: _MAPPING_VAR_0_0
98+
value:
99+
functionInvocationValue:
100+
arguments:
101+
geometry:
102+
functionInvocationValue:
103+
arguments:
104+
feature:
105+
argumentReference: _MAPPING_VAR_0_0
106+
functionName: Feature.geometry
107+
functionName: Geometry.type
108+
functionName: Element.set
109+
'6':
110+
constantValue: __geetools_type__
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
features:
2+
- geometry:
3+
coordinates:
4+
- 0.0
5+
- 0.0
6+
type: Point
7+
id: '0'
8+
properties:
9+
test: test
10+
type: Feature
11+
- geometry:
12+
coordinates:
13+
- 1.0
14+
- 0.0
15+
type: Point
16+
id: '1'
17+
properties:
18+
test: test
19+
type: Feature
20+
type: FeatureCollection
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
features:
2+
- geometry:
3+
coordinates:
4+
- 0.0
5+
- 0.0
6+
type: Point
7+
id: '0'
8+
properties:
9+
test: test
10+
type: Feature
11+
type: FeatureCollection

0 commit comments

Comments
 (0)