earthengine-api/python/ee/tests/featurecollection_test.py
2024-05-23 14:50:14 -07:00

400 lines
13 KiB
Python

#!/usr/bin/env python3
"""Test for the ee.featurecollection module."""
import json
from typing import Any, Dict
from unittest import mock
import unittest
import ee
from ee import _cloud_api_utils
from ee import apitestcase
def make_expression_graph(
function_invocation_value: Dict[str, Any],
) -> Dict[str, Any]:
return {
'result': '0',
'values': {'0': {'functionInvocationValue': function_invocation_value}},
}
# ee.FeatureCollection(ee.Feature(None))
FEATURES_ONE = {
'functionInvocationValue': {
'functionName': 'Collection',
'arguments': {
'features': {
'arrayValue': {
'values': [{
'functionInvocationValue': {
'functionName': 'Feature',
'arguments': {},
}
}]
}
}
},
}
}
FEATURES_A = {
'functionInvocationValue': {
'functionName': 'Collection.loadTable',
'arguments': {'tableId': {'constantValue': 'a'}},
}
}
FEATURES_B = {
'functionInvocationValue': {
'functionName': 'Collection.loadTable',
'arguments': {'tableId': {'constantValue': 'b'}},
}
}
class FeatureCollectionTestCase(apitestcase.ApiTestCase):
def test_constructors(self):
"""Verifies that constructors understand valid parameters."""
from_id = ee.FeatureCollection('abcd')
self.assertEqual(
ee.ApiFunction.lookup('Collection.loadTable'), from_id.func)
self.assertEqual({'tableId': 'abcd'}, from_id.args)
from_id_and_geom_column = ee.FeatureCollection('abcd', 'xyz')
self.assertEqual(
ee.ApiFunction.lookup('Collection.loadTable'),
from_id_and_geom_column.func)
self.assertEqual({
'tableId': 'abcd',
'geometryColumn': 'xyz'
}, from_id_and_geom_column.args)
geometry = ee.Geometry.Point(1, 2)
feature = ee.Feature(geometry)
geo_json = {'type': 'FeatureCollection', 'features': [geometry.toGeoJSON()]}
from_geometries = ee.FeatureCollection([geometry])
from_single_geometry = ee.FeatureCollection(geometry)
from_features = ee.FeatureCollection([feature])
from_single_feature = ee.FeatureCollection(feature)
from_geo_json = ee.FeatureCollection(geo_json)
self.assertEqual(from_geometries, from_single_geometry)
self.assertEqual(from_geometries, from_features)
self.assertEqual(from_geometries, from_single_feature)
self.assertEqual(from_geometries, from_geo_json)
self.assertEqual(ee.ApiFunction.lookup('Collection'), from_geometries.func)
self.assertEqual({'features': [feature]}, from_geometries.args)
# Test a computed list object.
l = ee.List([feature]).slice(0)
from_list = ee.FeatureCollection(l)
self.assertEqual({'features': l}, from_list.args)
from_computed_object = ee.FeatureCollection(
ee.ComputedObject(None, {'x': 'y'}))
self.assertEqual({'x': 'y'}, from_computed_object.args)
def test_get_map_id(self):
"""Verifies that getMap() uses Collection.draw to draw."""
collection = ee.FeatureCollection('test5')
mapid = collection.getMapId({'color': 'ABCDEF'})
manual = ee.ApiFunction.call_('Collection.draw', collection, 'ABCDEF')
self.assertEqual('fakeMapId', mapid['mapid'])
self.assertEqual(manual, mapid['image'])
def test_download(self):
"""Verifies that Download ID and URL generation."""
ee.FeatureCollection('test7').getDownloadURL()
self.assertEqual('/table', self.last_table_call['url'])
self.assertEqual(ee.FeatureCollection('test7').serialize(),
self.last_table_call['data']['table'].serialize())
ee.FeatureCollection('test8').getDownloadURL(
'json', 'bar, baz', 'qux')
self.assertEqual(
ee.FeatureCollection('test8').serialize(),
self.last_table_call['data']['table'].serialize())
self.assertEqual('JSON', self.last_table_call['data']['format'])
self.assertEqual('bar, baz', self.last_table_call['data']['selectors'])
self.assertEqual('qux', self.last_table_call['data']['filename'])
self.assertEqual(
ee.FeatureCollection('test7').getDownloadUrl('csv'),
ee.FeatureCollection('test7').getDownloadURL('csv'))
def test_download_table_with_cloud_api(self):
cloud_api_resource = mock.MagicMock()
with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
create_table_response = {'name': 'table_name'}
cloud_api_resource.projects().tables().create().execute.return_value = (
create_table_response)
fc = ee.FeatureCollection([ee.Feature(None, {'foo': 'bar'})])
result = ee.data.getTableDownloadId({
'table': fc, 'selectors': 'foo', 'format': 'CSV',
})
url = ee.data.makeTableDownloadUrl(result)
self.assertDictEqual(result, {'docid': '5', 'token': '6'})
self.assertEqual(url, f'/{_cloud_api_utils.VERSION}/5:getFeatures')
def test_select(self):
def equals(c1, c2):
self.assertEqual(c1.serialize(), c2.serialize())
fc = ee.FeatureCollection(ee.Feature(ee.Geometry.Point(0, 0), {'a': 5}))
equals(fc.select('a'), fc.select(['a']))
equals(fc.select('a', 'b'), fc.select(['a', 'b']))
equals(fc.select('a', 'b', 'c'), fc.select(['a', 'b', 'c']))
equals(fc.select('a', 'b', 'c', 'd'), fc.select(['a', 'b', 'c', 'd']))
equals(fc.select(['a']), fc.select(['a'], None, True))
equals(fc.select(['a'], None, False),
fc.select(propertySelectors=['a'], retainGeometry=False))
def test_init_opt_column(self):
result = ee.FeatureCollection(
args='[{}]', opt_column='a-column'
).serialize()
self.assertIn('"geometryColumn": {"constantValue": "a-column"}', result)
def test_classify(self):
output_name = 'output name'
expect = make_expression_graph({
'arguments': {
'features': FEATURES_ONE,
'classifier': {
'functionInvocationValue': {
'arguments': {},
'functionName': 'Classifier.smileNaiveBayes',
}
},
'outputName': {'constantValue': output_name},
},
'functionName': 'FeatureCollection.classify',
})
classifier = ee.Classifier.smileNaiveBayes()
expression = ee.FeatureCollection(ee.Feature(None)).classify(
classifier, output_name
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
expression = ee.FeatureCollection(ee.Feature(None)).classify(
classifier=classifier, outputName=output_name
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
def test_cluster(self):
output_name = 'output name'
expect = make_expression_graph({
'arguments': {
'features': FEATURES_ONE,
'clusterer': {
'functionInvocationValue': {
'functionName': 'Clusterer.wekaCobweb',
'arguments': {},
}
},
'outputName': {'constantValue': output_name},
},
'functionName': 'FeatureCollection.cluster',
})
clusterer = ee.Clusterer.wekaCobweb()
expression = ee.FeatureCollection(ee.Feature(None)).cluster(
clusterer, output_name
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
expression = ee.FeatureCollection(ee.Feature(None)).cluster(
clusterer=clusterer, outputName=output_name
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
def test_copy_properties(self):
source = ee.FeatureCollection('b')
properties = ['c', 'd']
exclude = ['e', 'f']
expect = make_expression_graph({
'arguments': {
'destination': FEATURES_A,
'source': FEATURES_B,
'properties': {'constantValue': properties},
'exclude': {'constantValue': exclude},
},
# Note this is Element rather than FeatureCollection
'functionName': 'Element.copyProperties',
})
expression = ee.FeatureCollection('a').copyProperties(
source, properties, exclude
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
expression = ee.FeatureCollection('a').copyProperties(
source=source, properties=properties, exclude=exclude
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
def test_inverse_distance(self):
a_range = 2
property_name = 'property name'
mean = 3
std_dev = 4
gamma = 5
reducer = ee.Reducer.max()
expect = make_expression_graph({
'arguments': {
'collection': FEATURES_ONE,
'range': {'constantValue': a_range},
'propertyName': {'constantValue': property_name},
'mean': {'constantValue': mean},
'stdDev': {'constantValue': std_dev},
'gamma': {'constantValue': gamma},
'reducer': {
'functionInvocationValue': {
'functionName': 'Reducer.max',
'arguments': {},
}
},
},
'functionName': 'FeatureCollection.inverseDistance',
})
expression = ee.FeatureCollection(ee.Feature(None)).inverseDistance(
a_range, property_name, mean, std_dev, gamma, reducer
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
expression = ee.FeatureCollection(ee.Feature(None)).inverseDistance(
range=a_range,
propertyName=property_name,
mean=mean,
stdDev=std_dev,
gamma=gamma,
reducer=reducer,
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
def test_kriging(self):
property_name = 'property name'
shape = 'exponential'
range = 1
sill = 2
nugget = 3
max_distance = 4
reducer = ee.Reducer.max()
expect = make_expression_graph({
'arguments': {
'collection': FEATURES_ONE,
'propertyName': {'constantValue': property_name},
'shape': {'constantValue': shape},
'range': {'constantValue': range},
'sill': {'constantValue': sill},
'nugget': {'constantValue': nugget},
'maxDistance': {'constantValue': max_distance},
'reducer': {
'functionInvocationValue': {
'functionName': 'Reducer.max',
'arguments': {},
}
},
},
'functionName': 'FeatureCollection.kriging',
})
expression = ee.FeatureCollection(ee.Feature(None)).kriging(
property_name, shape, range, sill, nugget, max_distance, reducer
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
expression = ee.FeatureCollection(ee.Feature(None)).kriging(
propertyName=property_name,
shape=shape,
range=range,
sill=sill,
nugget=nugget,
maxDistance=max_distance,
reducer=reducer,
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
def test_make_array(self):
properties = ['a', 'b']
name = 'name string'
expect = make_expression_graph({
'arguments': {
'collection': FEATURES_ONE,
'properties': {'constantValue': properties},
'name': {'constantValue': name},
},
'functionName': 'FeatureCollection.makeArray',
})
expression = ee.FeatureCollection(ee.Feature(None)).makeArray(
properties, name
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
expression = ee.FeatureCollection(ee.Feature(None)).makeArray(
properties=properties, name=name
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
def test_random_points(self):
region = ee.Geometry.Point([1, 2])
points = 1
seed = 2
max_error_num = 3
max_error = ee.ErrorMargin(max_error_num)
expect = make_expression_graph({
'arguments': {
'region': {
'functionInvocationValue': {
'functionName': 'GeometryConstructors.Point',
'arguments': {'coordinates': {'constantValue': [1, 2]}},
}
},
'points': {'constantValue': points},
'seed': {'constantValue': seed},
'maxError': {
'functionInvocationValue': {
'functionName': 'ErrorMargin',
'arguments': {'value': {'constantValue': max_error_num}},
}
},
},
'functionName': 'FeatureCollection.randomPoints',
})
expression = ee.FeatureCollection.randomPoints(
region, points, seed, max_error_num
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
expression = ee.FeatureCollection.randomPoints(
region, points, seed, max_error
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
expression = ee.FeatureCollection(ee.Feature(None)).randomPoints(
region=region, points=points, seed=seed, maxError=max_error
)
result = json.loads(expression.serialize())
self.assertEqual(expect, result)
if __name__ == '__main__':
unittest.main()