Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Node get_bounds method. #486

Merged
merged 4 commits into from
Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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