diff --git a/python/ee/feature.py b/python/ee/feature.py index b278aefbf..600598ce7 100644 --- a/python/ee/feature.py +++ b/python/ee/feature.py @@ -230,6 +230,64 @@ def centroid( self.name() + '.centroid', self, maxError, proj ) + def closestPoint( + self, + right: _arg_types.Element, + # pylint: disable-next=invalid-name + maxError: Optional[_arg_types.ErrorMargin] = None, + proj: Optional[_arg_types.Projection] = None, + ) -> computedobject.ComputedObject: + """Returns the point on the right input that is nearest to the left input. + + If either input is empty, null is returned. If both inputs are unbounded, an + arbitrary point is returned. If one input is unbounded, an arbitrary point + in the bounded input is returned. + + Args: + right: The feature containing the geometry used as the right operand of + the operation. + maxError: The maximum amount of error tolerated when performing any + necessary reprojection. + proj: The projection in which to perform the operation. If not specified, + the operation will be performed in a spherical coordinate system, and + linear distances will be in meters on the sphere. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.closestPoint', self, right, maxError, proj + ) + + def closestPoints( + self, + right: _arg_types.Element, + # pylint: disable-next=invalid-name + maxError: Optional[_arg_types.ErrorMargin] = None, + proj: Optional[_arg_types.Projection] = None, + ) -> computedobject.ComputedObject: + """Returns the points on the right input that are nearest to the left input. + + Returns a dictionary containing up to two entries representing a point on + each input feature's geometry that is closest to the geometry of the other + input. If either geometry is empty, an empty dictionary is returned. If both + geometries are unbounded, the dictionary has an arbitrary point for both + 'left' and 'right'. If one geometry is unbounded, the dictionary has an + arbitrary point contained in the bounded geometry for both 'left' and + 'right'. + + Args: + right: The feature containing the geometry used as the right operand of + the operation. + maxError: The maximum amount of error tolerated when performing any + necessary reprojection. + proj: The projection in which to perform the operation. If not specified, + the operation will be performed in a spherical coordinate system, and + linear distances will be in meters on the sphere. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.closestPoints', self, right, maxError, proj + ) + def containedIn( self, right: _arg_types.Any, diff --git a/python/ee/geometry.py b/python/ee/geometry.py index 7c8bf6a8b..41c885658 100644 --- a/python/ee/geometry.py +++ b/python/ee/geometry.py @@ -975,6 +975,67 @@ def centroid( self.name() + '.centroid', self, maxError, proj ) + def closestPoint( + self, + right: _arg_types.Geometry, + # pylint: disable-next=invalid-name + maxError: Optional[_arg_types.ErrorMargin] = None, + proj: Optional[_arg_types.Projection] = None, + ) -> computedobject.ComputedObject: + """Returns the point on the right input that is nearest to the left input. + + If either input is empty, null is returned. If both inputs are unbounded, an + arbitrary point is returned. If one input is unbounded, an arbitrary point + in the bounded input is returned. + + Args: + right: The geometry used as the right operand of the operation. + maxError: The maximum amount of error tolerated when performing any + necessary reprojection. + proj: The projection in which to perform the operation. If not specified, + the operation will be performed in a spherical coordinate system, and + linear distances will be in meters on the sphere. + + Returns: + An ee.Object. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.closestPoint', self, right, maxError, proj + ) + + def closestPoints( + self, + right: _arg_types.Geometry, + # pylint: disable-next=invalid-name + maxError: Optional[_arg_types.ErrorMargin] = None, + proj: Optional[_arg_types.Projection] = None, + ) -> computedobject.ComputedObject: + """Returns the points on the right input that are nearest to the left input. + + Returns a dictionary containing up to two entries representing a point on + each input geometry that is closest to the other input geometry. If either + geometry is empty, an empty dictionary is returned. If both geometries are + unbounded, the dictionary has an arbitrary point for both 'left' and + 'right'. If one geometry is unbounded, the dictionary has an arbitrary point + contained in the bounded geometry for both 'left' and 'right'. + + Args: + right: The geometry used as the right operand of the operation. + maxError: The maximum amount of error tolerated when performing any + necessary reprojection. + proj: The projection in which to perform the operation. If not specified, + the operation will be performed in a spherical coordinate system, and + linear distances will be in meters on the sphere. + + Returns: + An ee.Object. + """ + + return apifunction.ApiFunction.call_( + self.name() + '.closestPoints', self, right, maxError, proj + ) + def containedIn( self, right: _arg_types.Geometry, diff --git a/python/ee/tests/feature_test.py b/python/ee/tests/feature_test.py index 34b6d9871..5c01994a5 100644 --- a/python/ee/tests/feature_test.py +++ b/python/ee/tests/feature_test.py @@ -215,6 +215,52 @@ def test_centroid(self): result = json.loads(expression.serialize()) self.assertEqual(expect, result) + def test_closest_point(self): + right = ee.Feature(None, {'a': 'b'}) + max_error = 10 + proj = EPSG_4326 + expect = make_expression_graph({ + 'arguments': { + 'left': FEATURE_NONE_GRAPH, + 'right': FEATURE_A_GRAPH, + 'maxError': MAX_ERROR_GRAPH, + 'proj': PROJ_GRAPH, + }, + 'functionName': 'Feature.closestPoint', + }) + expression = ee.Feature(None).closestPoint(right, max_error, proj) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Feature(None).closestPoint( + right=right, maxError=max_error, proj=proj + ) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + def test_closest_points(self): + right = ee.Feature(None, {'a': 'b'}) + max_error = 10 + proj = EPSG_4326 + expect = make_expression_graph({ + 'arguments': { + 'left': FEATURE_NONE_GRAPH, + 'right': FEATURE_A_GRAPH, + 'maxError': MAX_ERROR_GRAPH, + 'proj': PROJ_GRAPH, + }, + 'functionName': 'Feature.closestPoints', + }) + expression = ee.Feature(None).closestPoints(right, max_error, proj) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + + expression = ee.Feature(None).closestPoints( + right=right, maxError=max_error, proj=proj + ) + result = json.loads(expression.serialize()) + self.assertEqual(expect, result) + def test_contained_in(self): expect = right_maxerror_proj('containedIn') diff --git a/python/ee/tests/geometry_point_test.py b/python/ee/tests/geometry_point_test.py index 8d0e87595..d01116b8c 100644 --- a/python/ee/tests/geometry_point_test.py +++ b/python/ee/tests/geometry_point_test.py @@ -131,6 +131,40 @@ def test_centroid(self): ) self.assertEqual(actual, expect) + def test_closest_point(self): + expect = make_expression_graph_geom( + 'closestPoint', + {'left': POINT, 'right': POINT2, 'maxError': MAX_ERROR, 'proj': PROJ}, + ) + actual = json.loads( + self.point.closestPoint(self.point2, MAX_ERROR_VAL, EPSG).serialize() + ) + self.assertEqual(actual, expect) + + actual = json.loads( + self.point.closestPoint( + right=self.point2, maxError=MAX_ERROR_VAL, proj=EPSG + ).serialize() + ) + self.assertEqual(actual, expect) + + def test_closest_points(self): + expect = make_expression_graph_geom( + 'closestPoints', + {'left': POINT, 'right': POINT2, 'maxError': MAX_ERROR, 'proj': PROJ}, + ) + actual = json.loads( + self.point.closestPoints(self.point2, MAX_ERROR_VAL, EPSG).serialize() + ) + self.assertEqual(actual, expect) + + actual = json.loads( + self.point.closestPoints( + right=self.point2, maxError=MAX_ERROR_VAL, proj=EPSG + ).serialize() + ) + self.assertEqual(actual, expect) + def test_contained_in(self): expect = make_expression_graph_geom( 'containedIn',