Esempio n. 1
0
    def __getitem__(self, index):
        # fallback for non-slices
        if not isinstance(index, slice):
            return ArrayCoordinates1d(self.coordinates, **self.properties)[index]

        # start, stop, step
        if index.start is None:
            start = self.start
        elif index.start >= 0:
            start = add_coord(self.start, self.step * min(index.start, self.size - 1))
        else:
            start = add_coord(self.start, self.step * max(0, self.size + index.start))

        if index.stop is None:
            stop = self.stop
        elif index.stop >= 0:
            stop = add_coord(self.start, self.step * (min(index.stop, self.size) - 1))
        else:
            stop = add_coord(self.start, self.step * max(0, self.size + index.stop - 1))

        if index.step is None:
            step = self.step
        else:
            step = index.step * self.step
            if index.step < 0:
                start, stop = stop, start

        # empty slice
        if start > stop and step > 0:
            return ArrayCoordinates1d([], **self.properties)
        return UniformCoordinates1d(start, stop, step, **self.properties)
Esempio n. 2
0
    def __getitem__(self, index):
        if isinstance(index, slice):
            # start, stop, step
            if index.start is None:
                start = self.start
            elif index.start >= 0:
                start = add_coord(self.start,
                                  self.step * min(index.start, self.size - 1))
            else:
                start = add_coord(self.start,
                                  self.step * max(0, self.size + index.start))

            if index.stop is None:
                stop = self.stop
            elif index.stop >= 0:
                stop = add_coord(self.start,
                                 self.step * (min(index.stop, self.size) - 1))
            else:
                stop = add_coord(
                    self.start, self.step * max(0, self.size + index.stop - 1))

            if index.step is None:
                step = self.step
            else:
                step = index.step * self.step
                if index.step < 0:
                    start, stop = stop, start

            # properties and segment_lengths
            kwargs = self.properties

            if self.ctype != 'point':
                if isinstance(self.segment_lengths, np.ndarray):
                    kwargs['segment_lengths'] = self.segment_lengths[index]
                elif self.segment_lengths != step:
                    kwargs['segment_lengths'] = self.segment_lengths

            # reroute empty slices to the else clause
            if start > stop and step > 0:
                return self[[]]

            return UniformCoordinates1d(start, stop, step, **kwargs)

        else:
            # coordinates
            coords = self.coordinates[index]

            # properties and segment_lengths
            kwargs = self.properties

            if self.ctype != 'point':
                if isinstance(self.segment_lengths, np.ndarray):
                    kwargs['segment_lengths'] = self.segment_lengths[index]
                else:
                    kwargs['segment_lengths'] = self.segment_lengths

            kwargs['ctype'] = self.ctype

            return ArrayCoordinates1d(coords, **kwargs)
Esempio n. 3
0
    def get_area_bounds(self, boundary):
        """
        Get low and high coordinate area bounds.

        Arguments
        ---------
        boundary : float, timedelta, array, None
            Boundary offsets in this dimension.

            * For a centered uniform boundary (same for every coordinate), use a single positive float or timedelta
                offset. This represents the "total segment length" / 2.
            * For a uniform boundary (segment or polygon same for every coordinate), use an array of float or
                timedelta offsets
            * For a fully specified boundary, use an array of boundary arrays (2-D array, N_coords x boundary spec),
                 one per coordinate. The boundary_spec can be a single number, two numbers, or an array of numbers.
            * For point coordinates, use None.

        Returns
        -------
        low: float, np.datetime64
            low area bound
        high: float, np.datetime64
            high area bound
        """

        # point coordinates
        if boundary is None:
            return self.bounds

        # empty coordinates
        if self.size == 0:
            return self.bounds

        if np.array(boundary).ndim == 0:
            # shortcut for uniform centered boundary
            boundary = make_coord_delta(boundary)
            lo_offset = -boundary
            hi_offset = boundary
        elif np.array(boundary).ndim == 1:
            # uniform boundary polygon
            boundary = make_coord_delta_array(boundary)
            lo_offset = min(boundary)
            hi_offset = max(boundary)
        else:
            L, H = self.argbounds
            lo_offset = min(make_coord_delta_array(boundary[L]))
            hi_offset = max(make_coord_delta_array(boundary[H]))

        lo, hi = self.bounds
        lo = add_coord(lo, lo_offset)
        hi = add_coord(hi, hi_offset)

        return lo, hi
Esempio n. 4
0
    def coordinates(self):
        """:array, read-only: Coordinate values. """

        coordinates = add_coord(self.start,
                                np.arange(0, self.size) * self.step)
        coordinates.setflags(write=False)
        return coordinates
Esempio n. 5
0
    def bounds(self):
        """ Low and high coordinate bounds. """

        lo = self.start
        hi = add_coord(self.start, self.step * (self.size - 1))
        if self.is_descending:
            lo, hi = hi, lo
        return lo, hi
Esempio n. 6
0
    def bounds(self):
        """ Low and high coordinate bounds. """

        lo = self.start
        hi = add_coord(self.start, self.step * (self.size - 1))
        if self.is_descending:
            lo, hi = hi, lo

        # read-only array with the correct dtype
        bounds = np.array([lo, hi], dtype=self.dtype)
        bounds.setflags(write=False)
        return bounds
Esempio n. 7
0
    def area_bounds(self):
        """
        Low and high coordinate area bounds.

        When ctype != 'point', this includes the portions of the segments beyond the coordinate bounds.
        """

        # point ctypes, just use bounds
        if self.ctype == 'point':
            return self.bounds

        # empty coordinates [np.nan, np.nan]
        if self.size == 0:
            return self.bounds

        # segment ctypes, calculated
        L, H = self.argbounds
        lo, hi = self.bounds

        if not isinstance(self.segment_lengths, np.ndarray):
            lo_length = hi_length = self.segment_lengths  # uniform segment_lengths
        else:
            lo_length, hi_length = self.segment_lengths[
                L], self.segment_lengths[H]

        if self.ctype == 'left':
            hi = add_coord(hi, hi_length)
        elif self.ctype == 'right':
            lo = add_coord(lo, -lo_length)
        elif self.ctype == 'midpoint':
            lo = add_coord(lo, -divide_delta(lo_length, 2.0))
            hi = add_coord(hi, divide_delta(hi_length, 2.0))

        # read-only array with the correct dtype
        area_bounds = np.array([lo, hi], dtype=self.dtype)
        area_bounds.setflags(write=False)
        return area_bounds
Esempio n. 8
0
    def __getitem__(self, index):
        # fallback for non-slices
        if not isinstance(index, slice):
            # The following 3 lines is copied from ArrayCoordinates1d.__getitem__
            if self.ndim == 1 and np.ndim(index) > 1 and np.array(index).dtype == int:
                index = np.array(index).flatten().tolist()
            return ArrayCoordinates1d(self.coordinates[index], **self.properties)

        # start, stop, step
        if index.start is None:
            start = self.start
        elif index.start >= 0:
            start = add_coord(self.start, self.step * min(index.start, self.size - 1))
        else:
            start = add_coord(self.start, self.step * max(0, self.size + index.start))

        if index.stop is None:
            stop = self.stop
        elif index.stop >= 0:
            stop = add_coord(self.start, self.step * (min(index.stop, self.size) - 1))
        else:
            stop = add_coord(self.start, self.step * max(0, self.size + index.stop - 1))

        if index.step is None:
            step = self.step
        else:
            step = index.step * self.step
            if index.step < 0:
                start, stop = stop, start

        # empty slice
        if ((start > stop) and np.array(step).astype(float) > 0) or (
            (start < stop) and np.array(step).astype(float) < 0
        ):
            return ArrayCoordinates1d([], **self.properties)
        return UniformCoordinates1d(start, stop, step, **self.properties)
Esempio n. 9
0
    def __init__(self, start, stop, step=None, size=None, name=None, fix_stop_val=False):
        """
        Create uniformly-spaced 1d coordinates from a `start`, `stop`, and `step` or `size`.

        Parameters
        ----------
        start : float or datetime64
            Start coordinate.
        stop : float or datetime64
            Stop coordinate.
        step : float or timedelta64
            Signed, nonzero step between coordinates (either step or size required).
        size : int
            Number of coordinates (either step or size required).
        name : str, optional
            Dimension name, one of 'lat', 'lon', 'time', or 'alt'.
        fix_stop_val : bool, optional
            Default is False. If True, the constructor will modify the step to be consistent
            instead of the stop value. Otherwise, the stop value *may* be modified to ensure that
            stop = start + step * size

        Notes
        ------
        When the user specifies fix_stop_val, then `stop` will always be exact as specified by the user.

        For floating point coordinates, the specified `step` my be changed internally to satisfy floating point consistency.
        That is, for consistency `step = (stop - start)  / (size - 1)`
        """

        if step is not None and size is not None:
            raise TypeError("only one of 'step' and 'size' is allowed")
        elif step is None and size is None:
            raise TypeError("'step' or 'size' is required")

        # validate and set start, stop, and step
        start = make_coord_value(start)
        stop = make_coord_value(stop)

        if step is not None:
            step = make_coord_delta(step)
        elif isinstance(size, (int, np.compat.long, np.integer)) and not isinstance(size, np.timedelta64):
            step = divide_delta(stop - start, size - 1)
        else:
            raise TypeError("size must be an integer, not '%s'" % type(size))

        if isinstance(start, float) and isinstance(stop, float) and isinstance(step, float):
            fstep = step
        elif isinstance(start, np.datetime64) and isinstance(stop, np.datetime64) and isinstance(step, np.timedelta64):
            fstep = step.astype(float)
        else:
            raise TypeError(
                "UniformCoordinates1d mismatching types (start '%s', stop '%s', step '%s')."
                % (type(start), type(stop), type(step))
            )

        if fstep == 0:
            raise ValueError("Uniformcoordinates1d step cannot be zero")

        if fstep <= 0 and start < stop:
            raise ValueError("UniformCoordinates1d step must be greater than zero if start < stop.")

        if fstep >= 0 and start > stop:
            raise ValueError("UniformCoordinates1d step must be less than zero if start > stop.")

        self.set_trait("start", start)
        self.set_trait("stop", stop)
        self.set_trait("step", step)

        if not fix_stop_val:  # Need to make sure that 'stop' is consistent with self.coordinates[-1]
            self.set_trait("stop", add_coord(self.start, (self.size - 1) * self.step))

        # Make sure step is floating-point error consistent in all cases
        # This is only needed when the type is float
        if fstep == step and self.size > 1:
            step = divide_delta(self.stop - self.start, self.size - 1)
            self.set_trait("step", step)

        # set common properties
        super(UniformCoordinates1d, self).__init__(name=name)
Esempio n. 10
0
    def coordinates(self):
        """:array, read-only: Coordinate values. """

        coordinates = add_coord(self.start, np.arange(0, self.size) * self.step)
        # coordinates.setflags(write=False)  # This breaks the 002-open-point-file example
        return coordinates
Esempio n. 11
0
def test_add_coord():
    # numbers
    assert add_coord(5, 1) == 6
    assert add_coord(5, -1) == 4
    assert np.allclose(add_coord(5, np.array([-1, 1])), [4, 6])

    # simple timedeltas
    td64 = np.timedelta64
    dt64 = np.datetime64
    assert add_coord(dt64('2018-01-30'), td64(1, 'D')) == dt64('2018-01-31')
    assert add_coord(dt64('2018-01-30'), td64(2, 'D')) == dt64('2018-02-01')
    assert add_coord(dt64('2018-01-30'), td64(-1, 'D')) == dt64('2018-01-29')
    assert add_coord(dt64('2018-01-01'), td64(-1, 'D')) == dt64('2017-12-31')
    assert np.all(
        add_coord(dt64('2018-01-30'), np.array([td64(1, 'D'),
                                                td64(2, 'D')])) ==
        np.array([dt64('2018-01-31'), dt64('2018-02-01')]))

    # year timedeltas
    assert add_coord(dt64('2018-01-01'), td64(1, 'Y')) == dt64('2019-01-01')
    assert add_coord(dt64('2018-01-01'), td64(-1, 'Y')) == dt64('2017-01-01')
    assert add_coord(dt64('2020-02-29'), td64(1, 'Y')) == dt64('2021-02-28')

    # month timedeltas
    assert add_coord(dt64('2018-01-01'), td64(1, 'M')) == dt64('2018-02-01')
    assert add_coord(dt64('2018-01-01'), td64(-1, 'M')) == dt64('2017-12-01')
    assert add_coord(dt64('2018-01-01'), td64(24, 'M')) == dt64('2020-01-01')
    assert add_coord(dt64('2018-01-31'), td64(1, 'M')) == dt64('2018-02-28')
    assert add_coord(dt64('2018-01-31'), td64(2, 'M')) == dt64('2018-03-31')
    assert add_coord(dt64('2018-01-31'), td64(3, 'M')) == dt64('2018-04-30')
    assert add_coord(dt64('2020-01-31'), td64(1, 'M')) == dt64('2020-02-29')

    # type error
    with pytest.raises(TypeError):
        add_coord(25.0, dt64('2020-01-31'))

    # this base case is generally not encountered
    from podpac.core.coordinates.utils import _add_nominal_timedelta
    assert _add_nominal_timedelta(dt64('2018-01-30'),
                                  td64(1, 'D')) == dt64('2018-01-31')