from __future__ import division, unicode_literals, print_function, absolute_import
import numpy as np
import traitlets as tl
from podpac.core.node import NodeException
from podpac.core.units import UnitsDataArray
from podpac.core.utils import common_doc
from podpac.core.compositor.compositor import COMMON_COMPOSITOR_DOC, BaseCompositor
[docs]@common_doc(COMMON_COMPOSITOR_DOC)
class OrderedCompositor(BaseCompositor):
"""Compositor that combines sources based on their order in self.sources.
The sources should generally be interpolated before being composited (i.e. not raw datasources).
Attributes
----------
sources : list
Source nodes, in order of preference. Later sources are only used where earlier sources do not provide data.
source_coordinates : :class:`podpac.Coordinates`
Coordinates that make each source unique. Must the same size as ``sources`` and single-dimensional. Optional.
multithreading : bool, optional
Default is True. If True, will always evaluate the compositor in serial, ignoring any MULTITHREADING settings
"""
multithreading = tl.Bool(False)
[docs] @common_doc(COMMON_COMPOSITOR_DOC)
def composite(self, coordinates, data_arrays, result=None):
"""Composites data_arrays in order that they appear. Once a request contains no nans, the result is returned.
Parameters
----------
coordinates : :class:`podpac.Coordinates`
{requested_coordinates}
data_arrays : generator
Evaluated source data, in the same order as the sources.
result : podpac.UnitsDataArray, optional
{eval_output}
Returns
-------
{eval_return} This composites the sources together until there are no nans or no more sources.
"""
if result is None:
result = self.create_output_array(coordinates)
else:
result[:] = np.nan
mask = UnitsDataArray.create(coordinates, outputs=self.outputs, data=0, dtype=bool)
for data in data_arrays:
if self.outputs is None:
try:
data = data.transpose(*result.dims)
except ValueError:
raise NodeException(
"Cannot evaluate compositor with requested dims %s. "
"The compositor source dims are %s. "
"Specify the compositor 'dims' attribute to ignore extra requested dims."
% (coordinates.dims, data.dims)
)
self._composite(result, data, mask)
else:
for name in data["output"]:
self._composite(result.sel(output=name), data.sel(output=name), mask.sel(output=name))
# stop if the results are full
if np.all(mask):
break
return result
@staticmethod
def _composite(result, data, mask):
source_mask = np.isfinite(data.data)
b = ~mask & source_mask
result.data[b.data] = data.data[b.data]
mask |= source_mask