Skip to content

Commit

Permalink
Merge pull request #486 from creare-com/feature/get_bounds
Browse files Browse the repository at this point in the history
ENH: Node get_bounds method.
  • Loading branch information
jmilloy authored Aug 3, 2021
2 parents 180af3d + 538ed8c commit 7eec446
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 2 deletions.
24 changes: 24 additions & 0 deletions podpac/core/data/datasource.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,30 @@ def find_coordinates(self):

return [self.coordinates]

def get_bounds(self, crs="default"):
"""Get the full available coordinate bounds for the Node.
Arguments
---------
crs : str
Desired CRS for the bounds. Use 'source' to use the native source crs.
If not specified, podpac.settings["DEFAULT_CRS"] is used. Optional.
Returns
-------
bounds : dict
Bounds for each dimension. Keys are dimension names and values are tuples (min, max).
crs : str
The crs for the bounds.
"""

if crs == "default":
crs = settings["DEFAULT_CRS"]
elif crs == "source":
crs = self.coordinates.crs

return self.coordinates.transform(crs).bounds, crs

@common_doc(COMMON_DATA_DOC)
def get_data(self, coordinates, coordinates_index):
"""{get_data}
Expand Down
17 changes: 16 additions & 1 deletion podpac/core/data/ogr.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def extents(self):

def get_source_data(self, bounds={}):
"""
Raise a user-friendly exception when calling get_source_data for this node.
Not available for OGR nodes.
Arguments
---------
Expand All @@ -71,6 +71,21 @@ def get_source_data(self, bounds={}):
"The source data is a vector-based shapefile without a native resolution."
)

def find_coordinates(self):
"""
Not available for OGR nodes.
raises
------
coord_list : list
list of available coordinates (Coordinates objects)
"""

raise AttributeError(
"Cannot get available coordinates for OGR datasources. "
"The source data is a vector-based shapefile without native coordinates."
)

@common_doc(COMMON_NODE_DOC)
def _eval(self, coordinates, output=None, _selector=None):
if "lat" not in coordinates.udims or "lon" not in coordinates.udims:
Expand Down
30 changes: 30 additions & 0 deletions podpac/core/data/test/test_datasource.py
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,36 @@ def test_get_source_data_with_bounds(self):
data = node.get_source_data({"lon": (1.5, 4.5)})
np.testing.assert_array_equal(data, node.source[:, 2:])

def test_get_bounds(self):
node = podpac.data.Array(
source=np.ones((3, 4)),
coordinates=podpac.Coordinates([range(3), range(4)], ["lat", "lon"], crs="EPSG:2193"),
)

with podpac.settings:
podpac.settings["DEFAULT_CRS"] = "EPSG:4326"

# specify crs
bounds, crs = node.get_bounds(crs="EPSG:3857")
assert bounds == {
"lat": (-13291827.558247399, -13291815.707967814),
"lon": (9231489.26794932, 9231497.142754894),
}
assert crs == "EPSG:3857"

# native/source crs
bounds, crs = node.get_bounds(crs="source")
assert bounds == {"lat": (0, 2), "lon": (0, 3)}
assert crs == "EPSG:2193"

# default crs
bounds, crs = node.get_bounds()
assert bounds == {
"lat": (-75.81365382984804, -75.81362774074242),
"lon": (82.92787904584206, 82.92794978642414),
}
assert crs == "EPSG:4326"


class TestDataSourceWithMultipleOutputs(object):
def test_evaluate_no_overlap_with_output_extract_output(self):
Expand Down
21 changes: 20 additions & 1 deletion podpac/core/interpolation/interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from podpac.core.settings import settings
from podpac.core.node import Node
from podpac.core.utils import NodeTrait, common_doc
from podpac.core.utils import NodeTrait, common_doc, cached_property
from podpac.core.units import UnitsDataArray
from podpac.core.coordinates import merge_dims, Coordinates
from podpac.core.interpolation.interpolation_manager import InterpolationManager, InterpolationTrait
Expand Down Expand Up @@ -273,3 +273,22 @@ def find_coordinates(self):
"""

return self.source.find_coordinates()

def get_bounds(self, crs="default"):
"""Get the full available coordinate bounds for the Node.
Arguments
---------
crs : str
Desired CRS for the bounds. Use 'source' to use the native source crs.
If not specified, the default CRS in the podpac settings is used. Optional.
Returns
-------
bounds : dict
Bounds for each dimension. Keys are dimension names and values are tuples (hi, lo).
crs : str
The crs for the bounds.
"""

return self.source.get_bounds(crs=crs)
3 changes: 3 additions & 0 deletions podpac/core/interpolation/test/test_interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ def test_compositor_chain(self):

np.testing.assert_array_equal(o.data, np.concatenate([self.s1.source, self.s2.source], axis=0))

def test_get_bounds(self):
assert self.interp.get_bounds() == self.s1.get_bounds()


class TestInterpolationBehavior(object):
def test_linear_1D_issue411and413(self):
Expand Down
31 changes: 31 additions & 0 deletions podpac/core/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,37 @@ def find_coordinates(self):

raise NotImplementedError

def get_bounds(self, crs="default"):
"""Get the full available coordinate bounds for the Node.
Arguments
---------
crs : str
Desired CRS for the bounds.
If not specified, the default CRS in the podpac settings is used. Optional.
Returns
-------
bounds : dict
Bounds for each dimension. Keys are dimension names and values are tuples (min, max).
crs : str
The CRS for the bounds.
"""

if crs == "default":
crs = podpac.settings["DEFAULT_CRS"]

bounds = {}
for coords in self.find_coordinates():
ct = coords.transform(crs)
for dim, (lo, hi) in ct.bounds.items():
if dim not in bounds:
bounds[dim] = (lo, hi)
else:
bounds[dim] = (min(lo, bounds[dim][0]), max(hi, bounds[dim][1]))

return bounds, crs

@common_doc(COMMON_DOC)
def create_output_array(self, coords, data=np.nan, attrs=None, **kwargs):
"""
Expand Down
26 changes: 26 additions & 0 deletions podpac/core/test/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,32 @@ def test_find_coordinates_not_implemented(self):
with pytest.raises(NotImplementedError):
node.find_coordinates()

def test_get_bounds(self):
class MyNode(Node):
def find_coordinates(self):
return [
podpac.Coordinates([[0, 1, 2], [0, 10, 20]], dims=["lat", "lon"], crs="EPSG:2193"),
podpac.Coordinates([[3, 4], [30, 40]], dims=["lat", "lon"], crs="EPSG:2193"),
]

node = MyNode()

with podpac.settings:
podpac.settings["DEFAULT_CRS"] = "EPSG:4326"

# specify crs
bounds, crs = node.get_bounds(crs="EPSG:2193")
assert bounds == {"lat": (0, 4), "lon": (0, 40)}
assert crs == "EPSG:2193"

# default crs
bounds, crs = node.get_bounds()
assert bounds == {
"lat": (-75.81397534013118, -75.81362774074242),
"lon": (82.92787904584206, 82.9280189659297),
}
assert crs == "EPSG:4326"


class TestCreateOutputArray(object):
def test_create_output_array_default(self):
Expand Down

0 comments on commit 7eec446

Please sign in to comment.