diff --git a/podpac/algorithm.py b/podpac/algorithm.py
index 54e3d3a6..ec8be6a0 100644
--- a/podpac/algorithm.py
+++ b/podpac/algorithm.py
@@ -28,4 +28,4 @@ from podpac.core.algorithm.coord_select import (
     YearSubstituteCoordinates,
     TransformTimeUnits,
 )
-from podpac.core.algorithm.signal import Convolution, SpatialConvolution, TimeConvolution
+from podpac.core.algorithm.signal import Convolution
diff --git a/podpac/core/algorithm/signal.py b/podpac/core/algorithm/signal.py
index a5e10a61..e09f2bca 100644
--- a/podpac/core/algorithm/signal.py
+++ b/podpac/core/algorithm/signal.py
@@ -63,12 +63,15 @@ class Convolution(UnaryAlgorithm):
     ----------
     source : podpac.Node
         Source node on which convolution will be performed. 
-    kernel : np.ndarray
-        The convolution kernel
-    kernel_ndim : int
-        Number of dimensions of the kernel
+    kernel : np.ndarray, optional
+        The convolution kernel. This kernel must have the same number of dimensions as the source data outputs
+    kernel_dims : list, optional
+        A list of the dimensions for the kernel axes. The dimensions in this list must match the 
+        coordinates in the source, or contain additional dimensions, and the order does not need to match.
+        Any extra dimensions are summed out. 
     kernel_type : str, optional
-        If kernel is not defined, kernel_type will create a kernel based on the inputs. 
+        If kernel is not defined, kernel_type will create a kernel based on the inputs, and it will have the
+        same number of axes as kernel_ndim.
         The format for the created  kernels is '<kernel_type>, <kernel_size>, <kernel_params>'.
         Any kernel defined in `scipy.signal` as well as `mean` can be used. For example:
         kernel_type = 'mean, 8' or kernel_type = 'gaussian,16,8' are both valid. 
@@ -76,21 +79,29 @@ class Convolution(UnaryAlgorithm):
     """
 
     kernel = ArrayTrait(dtype=float).tag(attr=True)
+    kernel_dims = tl.List().tag(attr=True)
 
-    def _first_init(self, kernel=None, kernel_type=None, kernel_ndim=None, **kwargs):
-        if kernel is not None:
-            if kernel_type is not None:
-                raise TypeError("Convolution expected 'kernel' or 'kernel_type', not both")
+    def _first_init(self, kernel=None, kernel_dims=None, kernel_type=None, kernel_ndim=None, **kwargs):
+        if kernel_dims is None:
+            raise TypeError("Convolution expected 'kernel_dims' to be specified when giving a 'kernel' array")
+
+        if kernel is not None and kernel_type is not None:
+            raise TypeError("Convolution expected 'kernel' or 'kernel_type', not both")
 
         if kernel is None:
             if kernel_type is None:
                 raise TypeError("Convolution requires 'kernel' array or 'kernel_type' string")
-            if kernel_ndim is None:
-                raise TypeError("Convolution requires 'kernel_ndim' when supplying a 'kernel_type' string")
+            kernel = self._make_kernel(kernel_type, len(kernel_dims))
 
-            kernel = self._make_kernel(kernel_type, kernel_ndim)
+        if len(kernel_dims) != len(np.array(kernel).shape):
+            raise TypeError(
+                "The kernel_dims should contain the same number of dimensions as the number of axes in 'kernel', but len(kernel_dims) {} != len(kernel.shape) {}".format(
+                    len(kernel_dims), len(np.array(kernel).shape)
+                )
+            )
 
         kwargs["kernel"] = kernel
+        kwargs["kernel_dims"] = kernel_dims
         return super(Convolution, self)._first_init(**kwargs)
 
     @common_doc(COMMON_DOC)
@@ -109,21 +120,20 @@ class Convolution(UnaryAlgorithm):
         -------
         {eval_return}
         """
-        # This should be aligned with coordinates' dimension order
         # The size of this kernel is used to figure out the expanded size
         full_kernel = self._get_full_kernel(coordinates)
 
-        if full_kernel.ndim != coordinates.ndim:
-            raise ValueError(
-                "Cannot evaluate coordinates, kernel and coordinates ndims mismatch (%d != %d)"
-                % (full_kernel.ndim, coordinates.ndim)
-            )
-
         # expand the coordinates
+        # The next line effectively drops extra coordinates, so we have to add those later in case the
+        # source is some sort of reduction Node.
+        kernel_dims = [kd for kd in coordinates.dims if kd in self.kernel_dims]
+        missing_dims = [kd for kd in coordinates.dims if kd not in self.kernel_dims]
+
         exp_coords = []
         exp_slice = []
-        for dim, s in zip(coordinates.dims, full_kernel.shape):
+        for dim in kernel_dims:
             coord = coordinates[dim]
+            s = full_kernel.shape[self.kernel_dims.index(dim)]
             if s == 1 or not isinstance(coord, UniformCoordinates1d):
                 exp_coords.append(coord)
                 exp_slice.append(slice(None))
@@ -142,6 +152,12 @@ class Convolution(UnaryAlgorithm):
                 )
             )
             exp_slice.append(slice(-s_start, -s_end))
+
+        # Add missing dims back in -- this is needed in case the source is a reduce node.
+        exp_coords += [coordinates[d] for d in missing_dims]
+        # exp_slice += [slice(None) for d in missing_dims]
+
+        # Create expanded coordinates
         exp_slice = tuple(exp_slice)
         expanded_coordinates = Coordinates(exp_coords, crs=coordinates.crs, validate_crs=False)
 
@@ -151,29 +167,41 @@ class Convolution(UnaryAlgorithm):
         # evaluate source using expanded coordinates, convolve, and then slice out original coordinates
         source = self.source.eval(expanded_coordinates)
 
-        # The source may not contain all of the same dimensions as the requested coordinates, which means
-        # The kernel could be too big. So, let's detect this and fix the problem.
-        extra_dims = [d for d in expanded_coordinates.dims if d not in source.dims]
-        if extra_dims:
-            coordinates = coordinates.drop(extra_dims)
+        # Check dimensions
+        if any([d not in kernel_dims for d in source.dims if d != "output"]):
+            raise ValueError(
+                "Kernel dims must contain all of the dimensions in source but not all of {} is in kernel_dims={}".format(
+                    source.dims, kernel_dims
+                )
+            )
 
-            extra_dims_ind = [expanded_coordinates.dims.index(d) for d in extra_dims]
-            for ind in extra_dims_ind:
-                full_kernel = full_kernel.sum(axis=ind)
-                exp_list = list(exp_slice)
-                exp_list.pop(ind)
-                exp_slice = tuple(exp_list)
+        full_kernel = self._get_full_kernel(coordinates)
+        kernel_dims = self.kernel_dims
+        sum_dims = [d for d in kernel_dims if d not in source.dims]
+        # Sum out the extra dims
+        full_kernel = full_kernel.sum(axis=tuple([kernel_dims.index(d) for d in sum_dims]))
+        kernel_dims = [d for d in kernel_dims if d in source.dims]
+
+        # Put the kernel axes in the correct order
+        # The (if d in kernel_dims) takes care of "output", which can be optionally present
+        full_kernel = full_kernel.transpose([kernel_dims.index(d) for d in source.dims if (d in kernel_dims)])
 
         if np.any(np.isnan(source)):
             method = "direct"
         else:
             method = "auto"
 
-        if "output" not in source.dims:
+        if ("output" not in source.dims) or ("output" in source.dims and "output" in kernel_dims):
             result = scipy.signal.convolve(source, full_kernel, mode="same", method=method)
         else:
             # source with multiple outputs
-            result = np.array([scipy.signal.convolve(src, full_kernel, mode="same", method=method) for src in source])
+            result = np.stack(
+                [
+                    scipy.signal.convolve(source.sel(output=output), full_kernel, mode="same", method=method)
+                    for output in source.coords["output"]
+                ],
+                axis=source.dims.index("output"),
+            )
         result = result[exp_slice]
 
         if output is None:
@@ -203,69 +231,3 @@ class Convolution(UnaryAlgorithm):
         """{full_kernel}
         """
         return self.kernel
-
-
-class TimeConvolution(Convolution):
-    """Compute a temporal convolution over a source node.
-    
-    Attributes
-    ----------
-    kernel_ndim : int
-        Value is 1. Should not be modified.
-    """
-
-    kernel = ArrayTrait(dtype=float, ndim=1).tag(attr=True)
-
-    def _first_init(self, kernel=None, kernel_type=None, kernel_ndim=1, **kwargs):
-        return super(TimeConvolution, self)._first_init(
-            kernel=kernel, kernel_type=kernel_type, kernel_ndim=kernel_ndim, **kwargs
-        )
-
-    def _get_full_kernel(self, coordinates):
-        """{full_kernel}
-        
-        Raises
-        ------
-        ValueError
-            If source data doesn't have time dimension.
-        """
-        if "time" not in coordinates.dims:
-            raise ValueError("cannot compute time convolution with time-independent coordinates")
-        if "lat" not in coordinates.dims and "lon" not in coordinates.dims:
-            return self.kernel
-
-        kernel = np.array([[self.kernel]])
-        kernel = xr.DataArray(kernel, dims=("lat", "lon", "time"))
-        kernel = kernel.transpose(*coordinates.dims)
-        return kernel.data
-
-
-class SpatialConvolution(Convolution):
-    """Compute a lat-lon convolution over a source node.
-    
-    Attributes
-    ----------
-    kernel_ndim : int
-        Value is 2. Should not be modified.
-    """
-
-    kernel = ArrayTrait(dtype=float, ndim=2).tag(attr=True)
-
-    def _first_init(self, kernel=None, kernel_type=None, kernel_ndim=2, **kwargs):
-        return super(SpatialConvolution, self)._first_init(
-            kernel=kernel, kernel_type=kernel_type, kernel_ndim=kernel_ndim, **kwargs
-        )
-
-    def _get_full_kernel(self, coordinates):
-        """{full_kernel}
-        """
-        if "lat" not in coordinates.dims or "lon" not in coordinates.dims:
-            raise ValueError("cannot compute spatial convolution with coordinate dims %s" % coordinates.dims)
-        if "time" not in coordinates.dims:
-            return self.kernel
-
-        kernel = np.array([self.kernel]).T
-        kernel = xr.DataArray(kernel, dims=("lat", "lon", "time"))
-        kernel = kernel.transpose(*coordinates.dims)
-
-        return kernel.data
diff --git a/setup.cfg b/setup.cfg
index c395494a..a7643c55 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
 [tool:pytest]
-addopts = -v --color=yes -m "not integration"
+#addopts = -v --color=yes -m "not integration"
 testpaths = podpac
 filterwarnings =
   ignore::FutureWarning:traitlets

Algorithm Convolution node kernel should be a labelled array
**Description**
Right now the kernel for the convolution algorithm nodes is very difficult to use, and the results depend on the order of the coords used to evaluate the Node. 

**Steps to Reproduce**
```
lat = [0, 1, 2, 3]
lon = [0, 1, 2]
coords = podpac.Coordinates([lat, lon], ['lat', 'lon'])
coords2 = coords.transpose('lon', 'lat')
source = podpac.data.Array(source=-np.arange(12).reshape(4, 3)**2, coordinates=coords, force_eval=True)
conv = podpac.algorithm.Convolution(source=source, kernel=[[-1, -1, 1]], force_eval=True)
assert np.all(conv.eval(coords) == conv.eval(coords2))
```

**Expected Behavior**
I'd expect the results to not have to depend on the order of the input coordinates

**Observed Behavior**
The results DO depend on the input coordinates


diff --git a/podpac/core/algorithm/test/test_signal.py b/podpac/core/algorithm/test/test_signal.py
index 38362067..6fe1a679 100644
--- a/podpac/core/algorithm/test/test_signal.py
+++ b/podpac/core/algorithm/test/test_signal.py
@@ -10,37 +10,46 @@ import podpac
 from podpac import Coordinates, clinspace, crange
 from podpac.algorithm import Arange
 from podpac.data import Array
-from podpac.core.algorithm.signal import Convolution, TimeConvolution, SpatialConvolution
+from podpac.core.algorithm.signal import Convolution
 
 
 class TestConvolution(object):
     def test_init_kernel(self):
-        node = Convolution(source=Arange(), kernel=[1, 2, 1])
+        node = Convolution(source=Arange(), kernel=[1, 2, 1], kernel_dims=["lat"])
         assert_equal(node.kernel, [1, 2, 1])
 
-        node = Convolution(source=Arange(), kernel_type="mean, 5", kernel_ndim=2)
+        node = Convolution(source=Arange(), kernel_type="mean, 5", kernel_dims=["lat", "lon"])
         assert node.kernel.shape == (5, 5)
         assert np.all(node.kernel == 0.04)
 
-        node = Convolution(source=Arange(), kernel_type="mean, 5", kernel_ndim=3)
+        node = Convolution(source=Arange(), kernel_type="mean, 5", kernel_dims=["lat", "lon", "time"])
         assert node.kernel.shape == (5, 5, 5)
         assert np.all(node.kernel == 0.008)
 
-        node = Convolution(source=Arange(), kernel_type="gaussian, 3, 1", kernel_ndim=2)
+        node = Convolution(source=Arange(), kernel_type="gaussian, 3, 1", kernel_dims=["lat", "lon"])
         assert node.kernel.shape == (3, 3)
 
         # kernel and kernel_type invalid
         with pytest.raises(TypeError, match="Convolution expected 'kernel' or 'kernel_type', not both"):
-            Convolution(source=Arange(), kernel=[1, 2, 1], kernel_type="mean, 5")
+            Convolution(source=Arange(), kernel=[1, 2, 1], kernel_type="mean, 5", kernel_dims=["lat", "lon"])
 
         # kernel or kernel_type required
         with pytest.raises(TypeError, match="Convolution requires 'kernel' array or 'kernel_type' string"):
-            Convolution(source=Arange())
+            Convolution(source=Arange(), kernel_dims=["lat", "lon"])
 
-        # ndim required
-        with pytest.raises(TypeError, match="Convolution requires 'kernel_ndim'"):
+        # kernel_dims required
+        with pytest.raises(
+            TypeError, match="Convolution expected 'kernel_dims' to be specified when giving a 'kernel' array"
+        ):
             Convolution(source=Arange(), kernel_type="mean, 5")
 
+        # kernel_dims correct number of entries
+        with pytest.raises(
+            TypeError,
+            match="The kernel_dims should contain the same number of dimensions as the number of axes in 'kernel', but ",
+        ):
+            Convolution(source=Arange(), kernel=[[[1, 2]]], kernel_dims=["lat"])
+
     def test_eval(self):
         lat = clinspace(45, 66, 30, name="lat")
         lon = clinspace(-80, 70, 40, name="lon")
@@ -50,17 +59,24 @@ class TestConvolution(object):
         kernel2d = [[1, 2, 1]]
         kernel3d = [[[1, 2, 1]]]
 
-        node1d = Convolution(source=Arange(), kernel=kernel1d)
-        node2d = Convolution(source=Arange(), kernel=kernel2d)
-        node3d = Convolution(source=Arange(), kernel=kernel3d)
+        node1d = Convolution(source=Arange(), kernel=kernel1d, kernel_dims=["time"])
+        node2d = Convolution(source=Arange(), kernel=kernel2d, kernel_dims=["lat", "lon"])
+        node3d = Convolution(source=Arange(), kernel=kernel3d, kernel_dims=["lon", "lat", "time"])
 
         o = node1d.eval(Coordinates([time]))
         o = node2d.eval(Coordinates([lat, lon]))
         o = node3d.eval(Coordinates([lat, lon, time]))
 
-        with pytest.raises(ValueError, match="Cannot evaluate coordinates, kernel and coordinates ndims mismatch"):
+        with pytest.raises(
+            ValueError, match="Kernel dims must contain all of the dimensions in source but not all of "
+        ):
             node2d.eval(Coordinates([lat, lon, time]))
 
+        with pytest.raises(
+            ValueError, match="Kernel dims must contain all of the dimensions in source but not all of "
+        ):
+            node2d.eval(Coordinates([lat, time]))
+
     def test_eval_multiple_outputs(self):
 
         lat = clinspace(45, 66, 30, name="lat")
@@ -68,8 +84,18 @@ class TestConvolution(object):
         kernel = [[1, 2, 1]]
         coords = Coordinates([lat, lon])
         multi = Array(source=np.random.random(coords.shape + (2,)), coordinates=coords, outputs=["a", "b"])
-        node = Convolution(source=multi, kernel=kernel)
-        o = node.eval(Coordinates([lat, lon]))
+        node = Convolution(source=multi, kernel=kernel, kernel_dims=["lat", "lon"])
+        o1 = node.eval(Coordinates([lat, lon]))
+
+        kernel = [[[1, 2]]]
+        coords = Coordinates([lat, lon])
+        multi = Array(source=np.random.random(coords.shape + (2,)), coordinates=coords, outputs=["a", "b"])
+        node1 = Convolution(source=multi, kernel=kernel, kernel_dims=["lat", "lon", "output"], force_eval=True)
+        node2 = Convolution(source=multi, kernel=kernel[0], kernel_dims=["lat", "lon"], force_eval=True)
+        o1 = node1.eval(Coordinates([lat, lon]))
+        o2 = node2.eval(Coordinates([lat, lon]))
+
+        assert np.any(o2.data != o1.data)
 
     def test_eval_nan(self):
         lat = clinspace(45, 66, 30, name="lat")
@@ -79,7 +105,7 @@ class TestConvolution(object):
         data = np.ones(coords.shape)
         data[10, 10] = np.nan
         source = Array(source=data, coordinates=coords)
-        node = Convolution(source=source, kernel=[[1, 2, 1]])
+        node = Convolution(source=source, kernel=[[1, 2, 1]], kernel_dims=["lat", "lon"])
 
         o = node.eval(coords[8:12, 7:13])
 
@@ -88,7 +114,7 @@ class TestConvolution(object):
         lon = clinspace(-80, 70, 40, name="lon")
         coords = Coordinates([lat, lon])
 
-        node = Convolution(source=Arange(), kernel=[[1, 2, 1]], kernel_ndim=2)
+        node = Convolution(source=Arange(), kernel=[[1, 2, 1]], kernel_dims=["lat", "lon"])
 
         a = node.create_output_array(coords)
         o = node.eval(coords, output=a)
@@ -103,7 +129,7 @@ class TestConvolution(object):
 
             # normal version
             a = Arange()
-            node = Convolution(source=a, kernel=[[1, 2, 1]], kernel_ndim=2)
+            node = Convolution(source=a, kernel=[[1, 2, 1]], kernel_dims=["lat", "lon"])
             node.eval(coords)
 
             assert node.source is a
@@ -112,7 +138,7 @@ class TestConvolution(object):
             podpac.settings["DEBUG"] = True
 
             a = Arange()
-            node = Convolution(source=a, kernel=[[1, 2, 1]], kernel_ndim=2)
+            node = Convolution(source=a, kernel=[[1, 2, 1]], kernel_dims=["lat", "lon"])
             node.eval(coords)
 
             assert node.source is not a
@@ -121,82 +147,26 @@ class TestConvolution(object):
             assert node.source._requested_coordinates != coords
             assert a._requested_coordinates is None
 
+    def test_extra_kernel_dims(self):
+        lat = clinspace(45, 66, 8, name="lat")
+        lon = clinspace(-80, 70, 16, name="lon")
+        coords = Coordinates([lat, lon])
 
-class TestSpatialConvolution(object):
-    def test_init_kernel(self):
-        node = SpatialConvolution(source=Arange(), kernel=[[1, 2, 1]])
-        assert_equal(node.kernel, [[1, 2, 1]])
-
-        node = SpatialConvolution(source=Arange(), kernel_type="mean, 5")
-        assert node.kernel.shape == (5, 5)
-        assert np.all(node.kernel == 0.04)
-
-        node = SpatialConvolution(source=Arange(), kernel_type="gaussian, 3, 1")
-        assert node.kernel.shape == (3, 3)
-
-        # kernel and kernel_type invalid
-        with pytest.raises(TypeError, match="Convolution expected 'kernel' or 'kernel_type', not both"):
-            SpatialConvolution(source=Arange(), kernel=[[1, 2, 1]], kernel_type="mean, 5")
-
-        # kernel or kernel_type required
-        with pytest.raises(TypeError, match="Convolution requires 'kernel' array or 'kernel_type' string"):
-            SpatialConvolution(source=Arange())
-
-        # kernel ndim must be 2
-        with pytest.raises(tl.TraitError):
-            SpatialConvolution(source=Arange(), kernel=[1, 2, 1])
-
-        with pytest.raises(tl.TraitError):
-            SpatialConvolution(source=Arange(), kernel=[[[1, 2, 1]]])
-
-    def test_eval(self):
-        lat = clinspace(45, 66, 30, name="lat")
-        lon = clinspace(-80, 70, 40, name="lon")
-        time = crange("2017-09-01", "2017-10-31", "1,D", name="time")
-
-        node = SpatialConvolution(source=Arange(), kernel=[[1, 2, 1]])
-        o = node.eval(Coordinates([lat, lon]))
-        o = node.eval(Coordinates([lat, lon, time]))
-
-        with pytest.raises(ValueError, match="cannot compute spatial convolution with coordinate dims"):
-            node.eval(Coordinates([time]))
-
-
-class TestTimeConvolution(object):
-    def test_init_kernel(self):
-        node = TimeConvolution(source=Arange(), kernel=[1, 2, 1])
-        assert_equal(node.kernel, [1, 2, 1])
-
-        node = TimeConvolution(source=Arange(), kernel_type="mean, 5")
-        assert node.kernel.shape == (5,)
-        assert np.all(node.kernel == 0.2)
-
-        node = TimeConvolution(source=Arange(), kernel_type="gaussian, 3, 1")
-        assert node.kernel.shape == (3,)
-
-        # kernel and kernel_type invalid
-        with pytest.raises(TypeError, match="Convolution expected 'kernel' or 'kernel_type', not both"):
-            TimeConvolution(source=Arange(), kernel=[1, 2, 1], kernel_type="mean, 5")
-
-        # kernel or kernel_type required
-        with pytest.raises(TypeError, match="Convolution requires 'kernel' array or 'kernel_type' string"):
-            TimeConvolution(source=Arange())
-
-        # kernel ndim must be 2
-        with pytest.raises(tl.TraitError):
-            TimeConvolution(source=Arange(), kernel=[[1, 2, 1]])
-
-        with pytest.raises(tl.TraitError):
-            TimeConvolution(source=Arange(), kernel=[[[1, 2, 1]]])
+        node = Convolution(source=Arange(), kernel=[[[1, 2, 1]]], kernel_dims=["time", "lat", "lon"])
+        o = node.eval(coords)
 
-    def test_eval(self):
-        lat = clinspace(45, 66, 30, name="lat")
-        lon = clinspace(-80, 70, 40, name="lon")
-        time = crange("2017-09-01", "2017-10-31", "1,D", name="time")
+    def test_coords_order(self):
+        lat = clinspace(-0.25, 1.25, 7, name="lat")
+        lon = clinspace(-0.125, 1.125, 11, name="lon")
+        coords = Coordinates([lat, lon])
 
-        node = TimeConvolution(source=Arange(), kernel=[1, 2, 1])
-        o = node.eval(Coordinates([time]))
-        o = node.eval(Coordinates([lat, lon, time]))
+        lat = clinspace(0, 1, 5, name="lat")
+        lon = clinspace(0, 1, 9, name="lon")
+        coords1 = Coordinates([lat, lon])
+        coords2 = Coordinates([lon, lat])
 
-        with pytest.raises(ValueError, match="cannot compute time convolution with time-independent coordinates"):
-            node.eval(Coordinates([lat, lon]))
+        source = Array(source=np.random.random(coords.shape), coordinates=coords)
+        node = Convolution(source=source, kernel=[[-1, 2, -1]], kernel_dims=["lat", "lon"], force_eval=True)
+        o1 = node.eval(coords1)
+        o2 = node.eval(coords2)
+        assert np.all(o2.data == o1.data.T)
diff --git a/podpac/core/test/test_node.py b/podpac/core/test/test_node.py
index c9c426b4..cbdf04a6 100644
--- a/podpac/core/test/test_node.py
+++ b/podpac/core/test/test_node.py
@@ -1037,9 +1037,9 @@ class TestUserDefinition(object):
                 "node": "algorithm.Arange"
             },
             "mean": {
-                "node": "algorithm.SpatialConvolution",
+                "node": "algorithm.Convolution",
                 "lookup_attrs": {"source": "a"},
-                "attrs": {"kernel_type": "mean,3"}
+                "attrs": {"kernel_type": "mean,3", "kernel_dims": ["lat", "lon"]}
             },
             "c": {
                 "node": "algorithm.Arithmetic",

pip install -e .[devall]
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gdal-bin libgdal-dev
3.7
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
affine==2.4.0
aiofiles==22.1.0
aiosqlite==0.19.0
alabaster==0.7.13
anyio==3.7.1
appdirs==1.4.4
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
arrow==1.2.3
asciitree==0.3.3
astroid==2.15.8
attrs==24.2.0
awscli==1.31.13
Babel==2.14.0
backcall==0.2.0
beautifulsoup4==4.13.3
black==23.3.0
bleach==6.0.0
boto3==1.33.13
botocore==1.33.13
branca==0.8.1
cached-property==1.5.2
certifi @ file:///croot/certifi_1671487769961/work/certifi
cffi==1.15.1
cfgv==3.3.1
charset-normalizer==3.4.1
click==8.1.8
click-plugins==1.1.1
cligj==0.7.2
cloudpickle==2.2.1
colorama==0.4.4
comm==0.1.4
commonmark==0.9.1
coverage==6.5.0
coveralls==3.3.1
cycler==0.11.0
dask==2022.2.0
debugpy==1.7.0
decorator==5.1.1
defusedxml==0.7.1
dill==0.3.7
distlib==0.3.9
docopt==0.6.2
docutils==0.16
entrypoints==0.4
exceptiongroup==1.2.2
fasteners==0.19
fastjsonschema==2.21.1
filelock==3.12.2
fonttools==4.38.0
fqdn==1.5.1
fsspec==2023.1.0
h5py==3.8.0
identify==2.5.24
idna==3.10
imagesize==1.4.1
importlib-metadata==6.7.0
importlib-resources==5.12.0
iniconfig==2.0.0
intake==0.6.6
ipykernel==6.16.2
ipyleaflet==0.18.2
ipympl==0.9.3
ipython==7.34.0
ipython-genutils==0.2.0
ipywidgets==8.1.5
isoduration==20.11.0
isort==5.11.5
jedi==0.19.2
Jinja2==3.1.6
jmespath==1.0.1
json5==0.9.16
jsonpointer==3.0.0
jsonschema==4.17.3
jupyter-client==7.2.0
jupyter-events==0.6.3
jupyter-server==1.24.0
jupyter-ydoc==0.2.5
jupyter_core==4.12.0
jupyter_server_fileid==0.9.3
jupyter_server_ydoc==0.8.0
jupyterlab==3.6.8
jupyterlab-pygments==0.2.2
jupyterlab_server==2.24.0
jupyterlab_widgets==3.0.13
kiwisolver==1.4.5
lazy-import==0.2.2
lazy-object-proxy==1.9.0
livereload==2.7.1
locket==1.0.0
lxml==5.3.1
MarkupSafe==2.1.5
matplotlib==3.5.3
matplotlib-inline==0.1.6
mccabe==0.7.0
mistune==3.0.2
msgpack==1.0.5
mypy-extensions==1.0.0
nbclassic==1.2.0
nbclient==0.7.4
nbconvert==7.6.0
nbformat==5.8.0
nest-asyncio==1.6.0
nodeenv==1.9.1
nodejs==0.1.1
notebook==6.5.7
notebook_shim==0.2.4
numcodecs==0.10.2
numexpr==2.8.6
numpy==1.21.6
optional-django==0.1.0
packaging==24.0
pandas==1.3.5
pandocfilters==1.5.1
parso==0.8.4
partd==1.4.1
pathspec==0.11.2
pexpect==4.9.0
pickleshare==0.7.5
Pillow==9.5.0
Pint==0.18
pkgutil_resolve_name==1.3.10
platformdirs==4.0.0
pluggy==1.2.0
-e git+https://github.com/creare-com/podpac.git@2a1677dcd5af2e00cd151da3c79ece5cfd43bee4#egg=podpac
pre-commit==2.21.0
prometheus-client==0.17.1
prompt_toolkit==3.0.48
psutil==7.0.0
ptyprocess==0.7.0
py==1.11.0
pyasn1==0.5.1
pycparser==2.21
pydap==3.4.1
Pygments==2.17.2
pylint==2.17.7
pyparsing==3.1.4
pyproj==3.2.1
pyrsistent==0.19.3
pytest==7.4.4
pytest-cov==4.1.0
pytest-html==3.2.0
pytest-metadata==3.0.0
pytest-remotedata==0.4.1
python-dateutil==2.7.5
python-json-logger==3.0.1
pytz==2025.2
PyYAML==6.0.1
pyzmq==26.2.1
rasterio==1.2.10
recommonmark==0.7.1
requests==2.31.0
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rsa==4.7.2
s3fs==0.4.2
s3transfer==0.8.2
sat-search==0.3.0
sat-stac==0.4.1
scipy==1.7.3
Send2Trash==1.8.3
six==1.17.0
sniffio==1.3.1
snowballstemmer==2.2.0
snuggs==1.4.7
soupsieve==2.4.1
Sphinx==2.4.5
sphinx-autobuild==2021.3.14
sphinx-rtd-theme==1.3.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
terminado==0.17.1
tinycss2==1.2.1
tomli==2.0.1
tomlkit==0.12.5
toolz==0.12.1
tornado==6.2
traitlets==5.9.0
traittypes==0.2.1
typed-ast==1.5.5
typing_extensions==4.7.1
uri-template==1.3.0
urllib3==1.26.20
virtualenv==20.26.6
wcwidth==0.2.13
webcolors==1.13
webencodings==0.5.1
WebOb==1.8.9
websocket-client==1.6.1
widgetsnbextension==4.0.13
wrapt==1.16.0
xarray==0.20.2
xyzservices==2023.2.0
y-py==0.6.2
ypy-websocket==0.8.4
zarr==2.12.0
zipp==3.15.0

name: podpac
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - affine==2.4.0
      - aiofiles==22.1.0
      - aiosqlite==0.19.0
      - alabaster==0.7.13
      - anyio==3.7.1
      - appdirs==1.4.4
      - argon2-cffi==23.1.0
      - argon2-cffi-bindings==21.2.0
      - arrow==1.2.3
      - asciitree==0.3.3
      - astroid==2.15.8
      - attrs==24.2.0
      - awscli==1.31.13
      - babel==2.14.0
      - backcall==0.2.0
      - beautifulsoup4==4.13.3
      - black==23.3.0
      - bleach==6.0.0
      - boto3==1.33.13
      - botocore==1.33.13
      - branca==0.8.1
      - cached-property==1.5.2
      - cffi==1.15.1
      - cfgv==3.3.1
      - charset-normalizer==3.4.1
      - click==8.1.8
      - click-plugins==1.1.1
      - cligj==0.7.2
      - cloudpickle==2.2.1
      - colorama==0.4.4
      - comm==0.1.4
      - commonmark==0.9.1
      - coverage==6.5.0
      - coveralls==3.3.1
      - cycler==0.11.0
      - dask==2022.2.0
      - debugpy==1.7.0
      - decorator==5.1.1
      - defusedxml==0.7.1
      - dill==0.3.7
      - distlib==0.3.9
      - docopt==0.6.2
      - docutils==0.16
      - entrypoints==0.4
      - exceptiongroup==1.2.2
      - fasteners==0.19
      - fastjsonschema==2.21.1
      - filelock==3.12.2
      - fonttools==4.38.0
      - fqdn==1.5.1
      - fsspec==2023.1.0
      - h5py==3.8.0
      - identify==2.5.24
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==6.7.0
      - importlib-resources==5.12.0
      - iniconfig==2.0.0
      - intake==0.6.6
      - ipykernel==6.16.2
      - ipyleaflet==0.18.2
      - ipympl==0.9.3
      - ipython==7.34.0
      - ipython-genutils==0.2.0
      - ipywidgets==8.1.5
      - isoduration==20.11.0
      - isort==5.11.5
      - jedi==0.19.2
      - jinja2==3.1.6
      - jmespath==1.0.1
      - json5==0.9.16
      - jsonpointer==3.0.0
      - jsonschema==4.17.3
      - jupyter-client==7.2.0
      - jupyter-core==4.12.0
      - jupyter-events==0.6.3
      - jupyter-server==1.24.0
      - jupyter-server-fileid==0.9.3
      - jupyter-server-ydoc==0.8.0
      - jupyter-ydoc==0.2.5
      - jupyterlab==3.6.8
      - jupyterlab-pygments==0.2.2
      - jupyterlab-server==2.24.0
      - jupyterlab-widgets==3.0.13
      - kiwisolver==1.4.5
      - lazy-import==0.2.2
      - lazy-object-proxy==1.9.0
      - livereload==2.7.1
      - locket==1.0.0
      - lxml==5.3.1
      - markupsafe==2.1.5
      - matplotlib==3.5.3
      - matplotlib-inline==0.1.6
      - mccabe==0.7.0
      - mistune==3.0.2
      - msgpack==1.0.5
      - mypy-extensions==1.0.0
      - nbclassic==1.2.0
      - nbclient==0.7.4
      - nbconvert==7.6.0
      - nbformat==5.8.0
      - nest-asyncio==1.6.0
      - nodeenv==1.9.1
      - nodejs==0.1.1
      - notebook==6.5.7
      - notebook-shim==0.2.4
      - numcodecs==0.10.2
      - numexpr==2.8.6
      - numpy==1.21.6
      - optional-django==0.1.0
      - packaging==24.0
      - pandas==1.3.5
      - pandocfilters==1.5.1
      - parso==0.8.4
      - partd==1.4.1
      - pathspec==0.11.2
      - pexpect==4.9.0
      - pickleshare==0.7.5
      - pillow==9.5.0
      - pint==0.18
      - pkgutil-resolve-name==1.3.10
      - platformdirs==4.0.0
      - pluggy==1.2.0
      - podpac==2.1.0+54.g2a1677dc
      - pre-commit==2.21.0
      - prometheus-client==0.17.1
      - prompt-toolkit==3.0.48
      - psutil==7.0.0
      - ptyprocess==0.7.0
      - py==1.11.0
      - pyasn1==0.5.1
      - pycparser==2.21
      - pydap==3.4.1
      - pygments==2.17.2
      - pylint==2.17.7
      - pyparsing==3.1.4
      - pyproj==3.2.1
      - pyrsistent==0.19.3
      - pytest==7.4.4
      - pytest-cov==4.1.0
      - pytest-html==3.2.0
      - pytest-metadata==3.0.0
      - pytest-remotedata==0.4.1
      - python-dateutil==2.7.5
      - python-json-logger==3.0.1
      - pytz==2025.2
      - pyyaml==6.0.1
      - pyzmq==26.2.1
      - rasterio==1.2.10
      - recommonmark==0.7.1
      - requests==2.31.0
      - rfc3339-validator==0.1.4
      - rfc3986-validator==0.1.1
      - rsa==4.7.2
      - s3fs==0.4.2
      - s3transfer==0.8.2
      - sat-search==0.3.0
      - sat-stac==0.4.1
      - scipy==1.7.3
      - send2trash==1.8.3
      - six==1.17.0
      - sniffio==1.3.1
      - snowballstemmer==2.2.0
      - snuggs==1.4.7
      - soupsieve==2.4.1
      - sphinx==2.4.5
      - sphinx-autobuild==2021.3.14
      - sphinx-rtd-theme==1.3.0
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - terminado==0.17.1
      - tinycss2==1.2.1
      - tomli==2.0.1
      - tomlkit==0.12.5
      - toolz==0.12.1
      - tornado==6.2
      - traitlets==5.9.0
      - traittypes==0.2.1
      - typed-ast==1.5.5
      - typing-extensions==4.7.1
      - uri-template==1.3.0
      - urllib3==1.26.20
      - virtualenv==20.26.6
      - wcwidth==0.2.13
      - webcolors==1.13
      - webencodings==0.5.1
      - webob==1.8.9
      - websocket-client==1.6.1
      - widgetsnbextension==4.0.13
      - wrapt==1.16.0
      - xarray==0.20.2
      - xyzservices==2023.2.0
      - y-py==0.6.2
      - ypy-websocket==0.8.4
      - zarr==2.12.0
      - zipp==3.15.0
prefix: /opt/conda/envs/podpac

podpac/core/algorithm/test/test_signal.py::TestConvolution::test_init_kernel podpac/core/algorithm/test/test_signal.py::TestConvolution::test_eval_nan podpac/core/test/test_node.py::tests_node_integration
podpac/core/algorithm/test/test_signal.py::TestConvolution::test_eval podpac/core/algorithm/test/test_signal.py::TestConvolution::test_eval_multiple_outputs podpac/core/algorithm/test/test_signal.py::TestConvolution::test_eval_with_output_argument podpac/core/algorithm/test/test_signal.py::TestConvolution::test_debuggable_source podpac/core/algorithm/test/test_signal.py::TestConvolution::test_extra_kernel_dims podpac/core/algorithm/test/test_signal.py::TestConvolution::test_coords_order podpac/core/test/test_node.py::TestNode::test_cache_ctrl podpac/core/test/test_node.py::TestNode::test_eval_group podpac/core/test/test_node.py::TestCreateOutputArray::test_create_output_array_default podpac/core/test/test_node.py::TestCreateOutputArray::test_create_output_array_data podpac/core/test/test_node.py::TestCreateOutputArray::test_create_output_array_dtype podpac/core/test/test_node.py::TestCreateOutputArray::test_create_output_array_units podpac/core/test/test_node.py::TestCreateOutputArray::test_create_output_array_crs podpac/core/test/test_node.py::TestNodeEval::test_extract_output podpac/core/test/test_node.py::TestUserDefinition::test_debuggable podpac/core/test/test_node.py::TestNoCacheMixin::test_customizable podpac/core/test/test_node.py::TestDiskCacheMixin::test_customizable
podpac/core/test/test_node.py::TestNode::test_style podpac/core/test/test_node.py::TestNode::test_units podpac/core/test/test_node.py::TestNode::test_outputs podpac/core/test/test_node.py::TestNode::test_output podpac/core/test/test_node.py::TestNode::test_cache_output podpac/core/test/test_node.py::TestNode::test_tagged_attr_readonly podpac/core/test/test_node.py::TestNode::test_trait_is_defined podpac/core/test/test_node.py::TestNode::test_init podpac/core/test/test_node.py::TestNode::test_attrs podpac/core/test/test_node.py::TestNode::test_repr podpac/core/test/test_node.py::TestNode::test_str podpac/core/test/test_node.py::TestNode::test_eval_not_implemented podpac/core/test/test_node.py::TestNode::test_find_coordinates_not_implemented podpac/core/test/test_node.py::TestUserDefinition::test_empty podpac/core/test/test_node.py::TestUserDefinition::test_no_node podpac/core/test/test_node.py::TestUserDefinition::test_invalid_node podpac/core/test/test_node.py::TestUserDefinition::test_inputs podpac/core/test/test_node.py::TestUserDefinition::test_lookup_attrs podpac/core/test/test_node.py::TestUserDefinition::test_invalid_property podpac/core/test/test_node.py::TestUserDefinition::test_plugin podpac/core/test/test_node.py::TestUserDefinition::test_from_definition_version_warning podpac/core/test/test_node.py::TestNoCacheMixin::test_default_no_cache podpac/core/test/test_node.py::TestDiskCacheMixin::test_default_disk_cache
podpac/core/algorithm/signal.py setup.cfg podpac/algorithm.py
podpac/core/algorithm/signal.py setup.cfg podpac/algorithm.py