Example #1
0
def test_decode_non_standard_calendar_inside_timestamp_range(
        calendar):
    cftime = _import_cftime()
    units = 'days since 0001-01-01'
    times = pd.date_range('2001-04-01-00', end='2001-04-30-23',
                          freq='H')
    non_standard_time = cftime.date2num(
        times.to_pydatetime(), units, calendar=calendar)

    if cftime.__name__ == 'cftime':
        expected = cftime.num2date(
            non_standard_time, units, calendar=calendar,
            only_use_cftime_datetimes=True)
    else:
        expected = cftime.num2date(non_standard_time, units,
                                   calendar=calendar)

    expected_dtype = np.dtype('O')

    actual = coding.times.decode_cf_datetime(
        non_standard_time, units, calendar=calendar)
    assert actual.dtype == expected_dtype
    abs_diff = abs(actual - expected)
    # once we no longer support versions of netCDF4 older than 1.1.5,
    # we could do this check with near microsecond accuracy:
    # https://github.com/Unidata/netcdf4-python/issues/355
    assert (abs_diff <= np.timedelta64(1, 's')).all()
Example #2
0
def _decode_datetime_with_cftime(num_dates, units, calendar,
                                 enable_cftimeindex):
    cftime = _import_cftime()
    if enable_cftimeindex:
        _require_standalone_cftime()
        dates = np.asarray(cftime.num2date(num_dates, units, calendar,
                                           only_use_cftime_datetimes=True))
    else:
        dates = np.asarray(cftime.num2date(num_dates, units, calendar))

    if (dates[np.nanargmin(num_dates)].year < 1678 or
            dates[np.nanargmax(num_dates)].year >= 2262):
        if not enable_cftimeindex or calendar in _STANDARD_CALENDARS:
            warnings.warn(
                'Unable to decode time axis into full '
                'numpy.datetime64 objects, continuing using dummy '
                'cftime.datetime objects instead, reason: dates out '
                'of range', SerializationWarning, stacklevel=3)
    else:
        if enable_cftimeindex:
            if calendar in _STANDARD_CALENDARS:
                dates = cftime_to_nptime(dates)
        else:
            try:
                dates = cftime_to_nptime(dates)
            except ValueError as e:
                warnings.warn(
                    'Unable to decode time axis into full '
                    'numpy.datetime64 objects, continuing using '
                    'dummy cftime.datetime objects instead, reason:'
                    '{0}'.format(e), SerializationWarning, stacklevel=3)
    return dates
Example #3
0
def test_decode_dates_outside_timestamp_range(
        calendar, enable_cftimeindex):
    from datetime import datetime

    if enable_cftimeindex:
        pytest.importorskip('cftime')

    cftime = _import_cftime()

    units = 'days since 0001-01-01'
    times = [datetime(1, 4, 1, h) for h in range(1, 5)]
    noleap_time = cftime.date2num(times, units, calendar=calendar)
    if enable_cftimeindex:
        expected = cftime.num2date(noleap_time, units, calendar=calendar,
                                   only_use_cftime_datetimes=True)
    else:
        expected = cftime.num2date(noleap_time, units, calendar=calendar)
    expected_date_type = type(expected[0])

    with warnings.catch_warnings():
        warnings.filterwarnings('ignore', 'Unable to decode time axis')
        actual = coding.times.decode_cf_datetime(
            noleap_time, units, calendar=calendar,
            enable_cftimeindex=enable_cftimeindex)
    assert all(isinstance(value, expected_date_type) for value in actual)
    abs_diff = abs(actual - expected)
    # once we no longer support versions of netCDF4 older than 1.1.5,
    # we could do this check with near microsecond accuracy:
    # https://github.com/Unidata/netcdf4-python/issues/355
    assert (abs_diff <= np.timedelta64(1, 's')).all()
Example #4
0
def _decode_datetime_with_cftime(num_dates, units, calendar):
    cftime = _import_cftime()

    if cftime.__name__ == 'cftime':
        return np.asarray(cftime.num2date(num_dates, units, calendar,
                                          only_use_cftime_datetimes=True))
    else:
        # Must be using num2date from an old version of netCDF4 which
        # does not have the only_use_cftime_datetimes option.
        return np.asarray(cftime.num2date(num_dates, units, calendar))
Example #5
0
def test_decode_non_standard_calendar_inside_timestamp_range(
        calendar, enable_cftimeindex):
    if enable_cftimeindex:
        pytest.importorskip('cftime')

    cftime = _import_cftime()
    units = 'days since 0001-01-01'
    times = pd.date_range('2001-04-01-00', end='2001-04-30-23',
                          freq='H')
    noleap_time = cftime.date2num(times.to_pydatetime(), units,
                                  calendar=calendar)
    if enable_cftimeindex:
        expected = cftime.num2date(noleap_time, units, calendar=calendar)
        expected_dtype = np.dtype('O')
    else:
        expected = times.values
        expected_dtype = np.dtype('M8[ns]')

    with warnings.catch_warnings():
        warnings.filterwarnings('ignore', 'Unable to decode time axis')
        actual = coding.times.decode_cf_datetime(
            noleap_time, units, calendar=calendar,
            enable_cftimeindex=enable_cftimeindex)
    assert actual.dtype == expected_dtype
    abs_diff = abs(actual - expected)
    # once we no longer support versions of netCDF4 older than 1.1.5,
    # we could do this check with near microsecond accuracy:
    # https://github.com/Unidata/netcdf4-python/issues/355
    assert (abs_diff <= np.timedelta64(1, 's')).all()
Example #6
0
def test_decode_non_standard_calendar_single_element_fallback(
        calendar, enable_cftimeindex):
    if enable_cftimeindex:
        pytest.importorskip('cftime')

    cftime = _import_cftime()

    units = 'days since 0001-01-01'
    try:
        dt = cftime.netcdftime.datetime(2001, 2, 29)
    except AttributeError:
        # Must be using standalone netcdftime library
        dt = cftime.datetime(2001, 2, 29)

    num_time = cftime.date2num(dt, units, calendar)
    if enable_cftimeindex:
        actual = coding.times.decode_cf_datetime(
            num_time, units, calendar=calendar,
            enable_cftimeindex=enable_cftimeindex)
    else:
        with pytest.warns(SerializationWarning,
                          match='Unable to decode time axis'):
            actual = coding.times.decode_cf_datetime(
                num_time, units, calendar=calendar,
                enable_cftimeindex=enable_cftimeindex)

    expected = np.asarray(cftime.num2date(num_time, units, calendar))
    assert actual.dtype == np.dtype('O')
    assert expected == actual
Example #7
0
def test_decode_non_standard_calendar_fallback(
        calendar, enable_cftimeindex):
    if enable_cftimeindex:
        pytest.importorskip('cftime')

    cftime = _import_cftime()
    # ensure leap year doesn't matter
    for year in [2010, 2011, 2012, 2013, 2014]:
        units = 'days since {0}-01-01'.format(year)
        num_times = np.arange(100)
        expected = cftime.num2date(num_times, units, calendar)

        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')
            actual = coding.times.decode_cf_datetime(
                num_times, units, calendar=calendar,
                enable_cftimeindex=enable_cftimeindex)
            if enable_cftimeindex:
                assert len(w) == 0
            else:
                assert len(w) == 1
                assert 'Unable to decode time axis' in str(w[0].message)

        assert actual.dtype == np.dtype('O')
        assert_array_equal(actual, expected)
Example #8
0
def test_decode_single_element_outside_timestamp_range(
        calendar):
    cftime = _import_cftime()
    units = 'days since 0001-01-01'
    for days in [1, 1470376]:
        for num_time in [days, [days], [[days]]]:
            with warnings.catch_warnings():
                warnings.filterwarnings('ignore',
                                        'Unable to decode time axis')
                actual = coding.times.decode_cf_datetime(
                    num_time, units, calendar=calendar)

            if cftime.__name__ == 'cftime':
                expected = cftime.num2date(days, units, calendar,
                                           only_use_cftime_datetimes=True)
            else:
                expected = cftime.num2date(days, units, calendar)

            assert isinstance(actual.item(), type(expected))
Example #9
0
def test_decode_multidim_time_outside_timestamp_range(
        calendar, enable_cftimeindex):
    from datetime import datetime

    if enable_cftimeindex:
        pytest.importorskip('cftime')

    cftime = _import_cftime()

    units = 'days since 0001-01-01'
    times1 = [datetime(1, 4, day) for day in range(1, 6)]
    times2 = [datetime(1, 5, day) for day in range(1, 6)]
    noleap_time1 = cftime.date2num(times1, units, calendar=calendar)
    noleap_time2 = cftime.date2num(times2, units, calendar=calendar)
    mdim_time = np.empty((len(noleap_time1), 2), )
    mdim_time[:, 0] = noleap_time1
    mdim_time[:, 1] = noleap_time2

    if enable_cftimeindex:
        expected1 = cftime.num2date(noleap_time1, units, calendar,
                                    only_use_cftime_datetimes=True)
        expected2 = cftime.num2date(noleap_time2, units, calendar,
                                    only_use_cftime_datetimes=True)
    else:
        expected1 = cftime.num2date(noleap_time1, units, calendar)
        expected2 = cftime.num2date(noleap_time2, units, calendar)

    with warnings.catch_warnings():
        warnings.filterwarnings('ignore', 'Unable to decode time axis')
        actual = coding.times.decode_cf_datetime(
            mdim_time, units, calendar=calendar,
            enable_cftimeindex=enable_cftimeindex)

    assert actual.dtype == np.dtype('O')

    abs_diff1 = abs(actual[:, 0] - expected1)
    abs_diff2 = abs(actual[:, 1] - expected2)
    # once we no longer support versions of netCDF4 older than 1.1.5,
    # we could do this check with near microsecond accuracy:
    # https://github.com/Unidata/netcdf4-python/issues/355
    assert (abs_diff1 <= np.timedelta64(1, 's')).all()
    assert (abs_diff2 <= np.timedelta64(1, 's')).all()
Example #10
0
def _generate_linear_range(start, end, periods):
    """Generate an equally-spaced sequence of cftime.datetime objects between
    and including two dates (whose length equals the number of periods)."""
    import cftime

    total_seconds = (end - start).total_seconds()
    values = np.linspace(0., total_seconds, periods, endpoint=True)
    units = 'seconds since {}'.format(format_cftime_datetime(start))
    calendar = start.calendar
    return cftime.num2date(values, units=units, calendar=calendar,
                           only_use_cftime_datetimes=True)
Example #11
0
def test_cf_datetime(num_dates, units, calendar):
    cftime = _import_cftime()
    if cftime.__name__ == "cftime":
        expected = cftime.num2date(num_dates,
                                   units,
                                   calendar,
                                   only_use_cftime_datetimes=True)
    else:
        expected = cftime.num2date(num_dates, units, calendar)
    min_y = np.ravel(np.atleast_1d(expected))[np.nanargmin(num_dates)].year
    max_y = np.ravel(np.atleast_1d(expected))[np.nanargmax(num_dates)].year
    if min_y >= 1678 and max_y < 2262:
        expected = cftime_to_nptime(expected)

    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", "Unable to decode time axis")
        actual = coding.times.decode_cf_datetime(num_dates, units, calendar)

    abs_diff = np.asarray(abs(actual - expected)).ravel()
    abs_diff = pd.to_timedelta(abs_diff.tolist()).to_numpy()

    # once we no longer support versions of netCDF4 older than 1.1.5,
    # we could do this check with near microsecond accuracy:
    # https://github.com/Unidata/netcdf4-python/issues/355
    assert (abs_diff <= np.timedelta64(1, "s")).all()
    encoded, _, _ = coding.times.encode_cf_datetime(actual, units, calendar)
    if "1-1-1" not in units:
        # pandas parses this date very strangely, so the original
        # units/encoding cannot be preserved in this case:
        # (Pdb) pd.to_datetime('1-1-1 00:00:0.0')
        # Timestamp('2001-01-01 00:00:00')
        assert_array_equal(num_dates, np.around(encoded, 1))
        if hasattr(num_dates,
                   "ndim") and num_dates.ndim == 1 and "1000" not in units:
            # verify that wrapping with a pandas.Index works
            # note that it *does not* currently work to even put
            # non-datetime64 compatible dates into a pandas.Index
            encoded, _, _ = coding.times.encode_cf_datetime(
                pd.Index(actual), units, calendar)
            assert_array_equal(num_dates, np.around(encoded, 1))
def test_decode_nonstandard_calendar_multidim_time_inside_timestamp_range(
        calendar):
    import cftime

    units = "days since 0001-01-01"
    times1 = pd.date_range("2001-04-01", end="2001-04-05", freq="D")
    times2 = pd.date_range("2001-05-01", end="2001-05-05", freq="D")
    time1 = cftime.date2num(times1.to_pydatetime(), units, calendar=calendar)
    time2 = cftime.date2num(times2.to_pydatetime(), units, calendar=calendar)
    mdim_time = np.empty((len(time1), 2))
    mdim_time[:, 0] = time1
    mdim_time[:, 1] = time2

    if cftime.__name__ == "cftime":
        expected1 = cftime.num2date(time1,
                                    units,
                                    calendar,
                                    only_use_cftime_datetimes=True)
        expected2 = cftime.num2date(time2,
                                    units,
                                    calendar,
                                    only_use_cftime_datetimes=True)
    else:
        expected1 = cftime.num2date(time1, units, calendar)
        expected2 = cftime.num2date(time2, units, calendar)

    expected_dtype = np.dtype("O")

    actual = coding.times.decode_cf_datetime(mdim_time,
                                             units,
                                             calendar=calendar)

    assert actual.dtype == expected_dtype
    abs_diff1 = abs(actual[:, 0] - expected1)
    abs_diff2 = abs(actual[:, 1] - expected2)
    # once we no longer support versions of netCDF4 older than 1.1.5,
    # we could do this check with near microsecond accuracy:
    # https://github.com/Unidata/netcdf4-python/issues/355
    assert (abs_diff1 <= np.timedelta64(1, "s")).all()
    assert (abs_diff2 <= np.timedelta64(1, "s")).all()
Example #13
0
def test_cf_datetime(num_dates, units, calendar):
    cftime = _import_cftime()
    if cftime.__name__ == 'cftime':
        expected = cftime.num2date(num_dates, units, calendar,
                                   only_use_cftime_datetimes=True)
    else:
        expected = cftime.num2date(num_dates, units, calendar)
    min_y = np.ravel(np.atleast_1d(expected))[np.nanargmin(num_dates)].year
    max_y = np.ravel(np.atleast_1d(expected))[np.nanargmax(num_dates)].year
    if min_y >= 1678 and max_y < 2262:
        expected = cftime_to_nptime(expected)

    with warnings.catch_warnings():
        warnings.filterwarnings('ignore',
                                'Unable to decode time axis')
        actual = coding.times.decode_cf_datetime(num_dates, units,
                                                 calendar)

    abs_diff = np.atleast_1d(abs(actual - expected)).astype(np.timedelta64)
    # once we no longer support versions of netCDF4 older than 1.1.5,
    # we could do this check with near microsecond accuracy:
    # https://github.com/Unidata/netcdf4-python/issues/355
    assert (abs_diff <= np.timedelta64(1, 's')).all()
    encoded, _, _ = coding.times.encode_cf_datetime(actual, units,
                                                    calendar)
    if '1-1-1' not in units:
        # pandas parses this date very strangely, so the original
        # units/encoding cannot be preserved in this case:
        # (Pdb) pd.to_datetime('1-1-1 00:00:0.0')
        # Timestamp('2001-01-01 00:00:00')
        assert_array_equal(num_dates, np.around(encoded, 1))
        if (hasattr(num_dates, 'ndim') and num_dates.ndim == 1 and
                '1000' not in units):
            # verify that wrapping with a pandas.Index works
            # note that it *does not* currently work to even put
            # non-datetime64 compatible dates into a pandas.Index
            encoded, _, _ = coding.times.encode_cf_datetime(
                pd.Index(actual), units, calendar)
            assert_array_equal(num_dates, np.around(encoded, 1))
Example #14
0
def test_decode_360_day_calendar():
    cftime = _import_cftime()
    calendar = '360_day'
    # ensure leap year doesn't matter
    for year in [2010, 2011, 2012, 2013, 2014]:
        units = 'days since {0}-01-01'.format(year)
        num_times = np.arange(100)

        if cftime.__name__ == 'cftime':
            expected = cftime.num2date(num_times, units, calendar,
                                       only_use_cftime_datetimes=True)
        else:
            expected = cftime.num2date(num_times, units, calendar)

        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')
            actual = coding.times.decode_cf_datetime(
                num_times, units, calendar=calendar)
            assert len(w) == 0

        assert actual.dtype == np.dtype('O')
        assert_array_equal(actual, expected)
Example #15
0
def test_decode_non_standard_calendar_single_element(
        calendar):
    cftime = _import_cftime()
    units = 'days since 0001-01-01'

    try:
        dt = cftime.netcdftime.datetime(2001, 2, 29)
    except AttributeError:
        # Must be using the standalone cftime library
        dt = cftime.datetime(2001, 2, 29)

    num_time = cftime.date2num(dt, units, calendar)
    actual = coding.times.decode_cf_datetime(
        num_time, units, calendar=calendar)

    if cftime.__name__ == 'cftime':
        expected = np.asarray(cftime.num2date(
            num_time, units, calendar, only_use_cftime_datetimes=True))
    else:
        expected = np.asarray(cftime.num2date(num_time, units, calendar))
    assert actual.dtype == np.dtype('O')
    assert expected == actual
Example #16
0
def test_use_cftime_true(calendar, units_year):
    from cftime import num2date

    numerical_dates = [0, 1]
    units = f"days since {units_year}-01-01"
    expected = num2date(
        numerical_dates, units, calendar, only_use_cftime_datetimes=True
    )

    with pytest.warns(None) as record:
        result = decode_cf_datetime(numerical_dates, units, calendar, use_cftime=True)
        np.testing.assert_array_equal(result, expected)
        assert not record
 def validate_array_values(name, array):
     if name == "time":
         for i, s in enumerate(states):
             value = cftime.num2date(
                 array[i],
                 units="seconds since 2010-01-01 00:00:00",
                 calendar=calendar,
             )
             assert value == s["time"]
     else:
         for i, s in enumerate(states):
             numpy.testing.assert_array_equal(array[i, 0, :],
                                              s[name].view[:])
Example #18
0
def test_decode_non_standard_calendar_single_element(
        calendar):
    cftime = _import_cftime()
    units = 'days since 0001-01-01'

    try:
        dt = cftime.netcdftime.datetime(2001, 2, 29)
    except AttributeError:
        # Must be using the standalone cftime library
        dt = cftime.datetime(2001, 2, 29)

    num_time = cftime.date2num(dt, units, calendar)
    actual = coding.times.decode_cf_datetime(
        num_time, units, calendar=calendar)

    if cftime.__name__ == 'cftime':
        expected = np.asarray(cftime.num2date(
            num_time, units, calendar, only_use_cftime_datetimes=True))
    else:
        expected = np.asarray(cftime.num2date(num_time, units, calendar))
    assert actual.dtype == np.dtype('O')
    assert expected == actual
Example #19
0
def test_decode_360_day_calendar():
    cftime = _import_cftime()
    calendar = '360_day'
    # ensure leap year doesn't matter
    for year in [2010, 2011, 2012, 2013, 2014]:
        units = 'days since {0}-01-01'.format(year)
        num_times = np.arange(100)

        if cftime.__name__ == 'cftime':
            expected = cftime.num2date(num_times, units, calendar,
                                       only_use_cftime_datetimes=True)
        else:
            expected = cftime.num2date(num_times, units, calendar)

        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')
            actual = coding.times.decode_cf_datetime(
                num_times, units, calendar=calendar)
            assert len(w) == 0

        assert actual.dtype == np.dtype('O')
        assert_array_equal(actual, expected)
Example #20
0
def test_use_cftime_default_non_standard_calendar(calendar, units_year):
    from cftime import num2date

    numerical_dates = [0, 1]
    units = "days since {}-01-01".format(units_year)
    expected = num2date(
        numerical_dates, units, calendar, only_use_cftime_datetimes=True
    )

    with pytest.warns(None) as record:
        result = decode_cf_datetime(numerical_dates, units, calendar)
        np.testing.assert_array_equal(result, expected)
        assert not record
Example #21
0
def test_format_parse_datetime():

    dates = [
        cftime.num2date(t, units="days since 01-01-01", calendar="noleap")
        for t in times
    ]
    assert format_datetime(dates[0]) == "0001-01-01 00:00:00"
    assert format_datetime(dates[-1]) == "0005-12-01 00:00:00"

    for d in dates:
        assert parse_datetime(format_datetime(d), "noleap") == d

    dates = [
        cftime.num2date(t,
                        units="days since 01-01-01",
                        calendar="proleptic_gregorian") for t in times
    ]
    assert format_datetime(dates[0]) == "0001-01-01 00:00:00"
    assert format_datetime(dates[-1]) == "0005-11-30 00:00:00"

    for d in dates:
        assert parse_datetime(format_datetime(d), "proleptic_gregorian") == d
Example #22
0
def _generate_linear_range(start, end, periods):
    """Generate an equally-spaced sequence of cftime.datetime objects between
    and including two dates (whose length equals the number of periods)."""
    if cftime is None:
        raise ModuleNotFoundError("No module named 'cftime'")

    total_seconds = (end - start).total_seconds()
    values = np.linspace(0.0, total_seconds, periods, endpoint=True)
    units = f"seconds since {format_cftime_datetime(start)}"
    calendar = start.calendar
    return cftime.num2date(
        values, units=units, calendar=calendar, only_use_cftime_datetimes=True
    )
Example #23
0
def test_decode_nonstandard_calendar_multidim_time_inside_timestamp_range(
        calendar, enable_cftimeindex):
    if enable_cftimeindex:
        pytest.importorskip('cftime')

    cftime = _import_cftime()

    units = 'days since 0001-01-01'
    times1 = pd.date_range('2001-04-01', end='2001-04-05', freq='D')
    times2 = pd.date_range('2001-05-01', end='2001-05-05', freq='D')
    noleap_time1 = cftime.date2num(times1.to_pydatetime(),
                                   units, calendar=calendar)
    noleap_time2 = cftime.date2num(times2.to_pydatetime(),
                                   units, calendar=calendar)
    mdim_time = np.empty((len(noleap_time1), 2), )
    mdim_time[:, 0] = noleap_time1
    mdim_time[:, 1] = noleap_time2

    if enable_cftimeindex:
        expected1 = cftime.num2date(noleap_time1, units, calendar)
        expected2 = cftime.num2date(noleap_time2, units, calendar)
        expected_dtype = np.dtype('O')
    else:
        expected1 = times1.values
        expected2 = times2.values
        expected_dtype = np.dtype('M8[ns]')

    actual = coding.times.decode_cf_datetime(
        mdim_time, units, calendar=calendar,
        enable_cftimeindex=enable_cftimeindex)

    assert actual.dtype == expected_dtype
    abs_diff1 = abs(actual[:, 0] - expected1)
    abs_diff2 = abs(actual[:, 1] - expected2)
    # once we no longer support versions of netCDF4 older than 1.1.5,
    # we could do this check with near microsecond accuracy:
    # https://github.com/Unidata/netcdf4-python/issues/355
    assert (abs_diff1 <= np.timedelta64(1, 's')).all()
    assert (abs_diff2 <= np.timedelta64(1, 's')).all()
Example #24
0
def test_decode_nonstandard_calendar_multidim_time_inside_timestamp_range(
        calendar, enable_cftimeindex):
    if enable_cftimeindex:
        pytest.importorskip('cftime')

    cftime = _import_cftime()

    units = 'days since 0001-01-01'
    times1 = pd.date_range('2001-04-01', end='2001-04-05', freq='D')
    times2 = pd.date_range('2001-05-01', end='2001-05-05', freq='D')
    noleap_time1 = cftime.date2num(times1.to_pydatetime(),
                                   units, calendar=calendar)
    noleap_time2 = cftime.date2num(times2.to_pydatetime(),
                                   units, calendar=calendar)
    mdim_time = np.empty((len(noleap_time1), 2), )
    mdim_time[:, 0] = noleap_time1
    mdim_time[:, 1] = noleap_time2

    if enable_cftimeindex:
        expected1 = cftime.num2date(noleap_time1, units, calendar)
        expected2 = cftime.num2date(noleap_time2, units, calendar)
        expected_dtype = np.dtype('O')
    else:
        expected1 = times1.values
        expected2 = times2.values
        expected_dtype = np.dtype('M8[ns]')

    actual = coding.times.decode_cf_datetime(
        mdim_time, units, calendar=calendar,
        enable_cftimeindex=enable_cftimeindex)

    assert actual.dtype == expected_dtype
    abs_diff1 = abs(actual[:, 0] - expected1)
    abs_diff2 = abs(actual[:, 1] - expected2)
    # once we no longer support versions of netCDF4 older than 1.1.5,
    # we could do this check with near microsecond accuracy:
    # https://github.com/Unidata/netcdf4-python/issues/355
    assert (abs_diff1 <= np.timedelta64(1, 's')).all()
    assert (abs_diff2 <= np.timedelta64(1, 's')).all()
Example #25
0
def _generate_linear_range(start, end, periods):
    """Generate an equally-spaced sequence of cftime.datetime objects between
    and including two dates (whose length equals the number of periods)."""
    import cftime

    total_seconds = (end - start).total_seconds()
    values = np.linspace(0.0, total_seconds, periods, endpoint=True)
    units = "seconds since {}".format(format_cftime_datetime(start))
    calendar = start.calendar
    return cftime.num2date(values,
                           units=units,
                           calendar=calendar,
                           only_use_cftime_datetimes=True)
Example #26
0
    def _defineGeneralHeader(self, header_items=None):
        """
        Defines known header items and overwrites any with header_items 
        key/value pairs.
        """
        if header_items == None:
            header_items = {}

        warning_message = "Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file."

        # Check if DATE field previously known in NASA Ames file
        time_now = [
            int(i) for i in time.strftime("%Y %m %d",
                                          time.localtime(time.time())).split()
        ]

        if not "RDATE" in self.na_dict:
            self.na_dict["RDATE"] = time_now

        if xarray_utils.is_time(self.ax0):
            # Get first date in list
            try:
                units = self.ax0.encoding["units"]
                first_day = self.na_dict["X"][0]

                # Cope with "X" being a list or list of lists (for different FFIs)
                while hasattr(first_day, "__len__"):
                    first_day = first_day[0]

                self.na_dict["DATE"] = \
                    [getattr(cftime.num2date(first_day, units), attr) for attr in ('year', 'month', 'day')]
            except Exception:
                msg = warning_message
                log.info(msg)
                self.output_message.append(msg)
                self.na_dict["DATE"] = [999] * 3

        else:
            if not "DATE" in self.na_dict:
                msg = warning_message
                log.info(msg)
                self.output_message.append(msg)
                self.na_dict["DATE"] = [999] * 3
            else:
                pass  # i.e. use existing DATE

        self.na_dict["IVOL"] = 1
        self.na_dict["NVOL"] = 1

        for key in header_items.keys():
            self.na_dict[key] = header_items[key]
Example #27
0
def compute_mon_climatology(dsm):
    '''Compute a monthly climatology'''

    tb_name, tb_dim = time_bound_var(dsm)

    grid_vars = [v for v in dsm.variables if 'time' not in dsm[v].dims]
    variables = [
        v for v in dsm.variables
        if 'time' in dsm[v].dims and v not in ['time', tb_name]
    ]

    # save attrs
    attrs = {v: dsm[v].attrs for v in dsm.variables}
    encoding = {
        v: {
            key: val
            for key, val in dsm[v].encoding.items()
            if key in ['dtype', '_FillValue', 'missing_value']
        }
        for v in dsm.variables
    }

    #-- compute time variable
    date = cftime.num2date(dsm[tb_name].mean(tb_dim),
                           units=dsm.time.attrs['units'],
                           calendar=dsm.time.attrs['calendar'])
    dsm.time.values = date
    if len(date) % 12 != 0:
        raise ValueError('Time axis not evenly divisible by 12!')

    #-- compute climatology
    ds = dsm.drop(grid_vars).groupby('time.month').mean('time').rename(
        {'month': 'time'})

    #-- put grid_vars back
    ds = xr.merge(
        (ds, dsm.drop([v for v in dsm.variables if v not in grid_vars])))

    attrs['time'] = {'long_name': 'Month', 'units': 'month'}
    del encoding['time']

    # put the attributes back
    for v in ds.variables:
        ds[v].attrs = attrs[v]

    # put the encoding back
    for v in ds.variables:
        if v in encoding:
            ds[v].encoding = encoding[v]

    return ds
Example #28
0
def test_decode_multidim_time_outside_timestamp_range(calendar):
    from datetime import datetime

    import cftime

    units = "days since 0001-01-01"
    times1 = [datetime(1, 4, day) for day in range(1, 6)]
    times2 = [datetime(1, 5, day) for day in range(1, 6)]
    time1 = cftime.date2num(times1, units, calendar=calendar)
    time2 = cftime.date2num(times2, units, calendar=calendar)
    mdim_time = np.empty((len(time1), 2))
    mdim_time[:, 0] = time1
    mdim_time[:, 1] = time2

    expected1 = cftime.num2date(time1,
                                units,
                                calendar,
                                only_use_cftime_datetimes=True)
    expected2 = cftime.num2date(time2,
                                units,
                                calendar,
                                only_use_cftime_datetimes=True)

    with warnings.catch_warnings():
        warnings.filterwarnings("ignore", "Unable to decode time axis")
        actual = coding.times.decode_cf_datetime(mdim_time,
                                                 units,
                                                 calendar=calendar)

    assert actual.dtype == np.dtype("O")

    abs_diff1 = abs(actual[:, 0] - expected1)
    abs_diff2 = abs(actual[:, 1] - expected2)
    # once we no longer support versions of netCDF4 older than 1.1.5,
    # we could do this check with near microsecond accuracy:
    # https://github.com/Unidata/netcdf4-python/issues/355
    assert (abs_diff1 <= np.timedelta64(1, "s")).all()
    assert (abs_diff2 <= np.timedelta64(1, "s")).all()
Example #29
0
def _decode_datetime_with_cftime(num_dates, units, calendar,
                                 enable_cftimeindex):
    cftime = _import_cftime()
    if enable_cftimeindex:
        _require_standalone_cftime()
        dates = np.asarray(
            cftime.num2date(num_dates,
                            units,
                            calendar,
                            only_use_cftime_datetimes=True))
    else:
        dates = np.asarray(cftime.num2date(num_dates, units, calendar))

    if (dates[np.nanargmin(num_dates)].year < 1678
            or dates[np.nanargmax(num_dates)].year >= 2262):
        if not enable_cftimeindex or calendar in _STANDARD_CALENDARS:
            warnings.warn(
                'Unable to decode time axis into full '
                'numpy.datetime64 objects, continuing using dummy '
                'cftime.datetime objects instead, reason: dates out '
                'of range',
                SerializationWarning,
                stacklevel=3)
    else:
        if enable_cftimeindex:
            if calendar in _STANDARD_CALENDARS:
                dates = cftime_to_nptime(dates)
        else:
            try:
                dates = cftime_to_nptime(dates)
            except ValueError as e:
                warnings.warn('Unable to decode time axis into full '
                              'numpy.datetime64 objects, continuing using '
                              'dummy cftime.datetime objects instead, reason:'
                              '{0}'.format(e),
                              SerializationWarning,
                              stacklevel=3)
    return dates
Example #30
0
def _decode_datetime_with_cftime(num_dates, units, calendar):
    cftime = _import_cftime()

    if cftime.__name__ == 'cftime':
        dates = np.asarray(cftime.num2date(num_dates, units, calendar,
                                           only_use_cftime_datetimes=True))
    else:
        # Must be using num2date from an old version of netCDF4 which
        # does not have the only_use_cftime_datetimes option.
        dates = np.asarray(cftime.num2date(num_dates, units, calendar))

    if (dates[np.nanargmin(num_dates)].year < 1678 or
            dates[np.nanargmax(num_dates)].year >= 2262):
        if calendar in _STANDARD_CALENDARS:
            warnings.warn(
                'Unable to decode time axis into full '
                'numpy.datetime64 objects, continuing using dummy '
                'cftime.datetime objects instead, reason: dates out '
                'of range', SerializationWarning, stacklevel=3)
    else:
        if calendar in _STANDARD_CALENDARS:
            dates = cftime_to_nptime(dates)
    return dates
Example #31
0
def test_use_cftime_default_standard_calendar_out_of_range(
        calendar, units_year):
    from cftime import num2date

    numerical_dates = [0, 1]
    units = f"days since {units_year}-01-01"
    expected = num2date(numerical_dates,
                        units,
                        calendar,
                        only_use_cftime_datetimes=True)

    with pytest.warns(SerializationWarning):
        result = decode_cf_datetime(numerical_dates, units, calendar)
        np.testing.assert_array_equal(result, expected)
Example #32
0
def get_ncdates(nc, tvar='time'):
    """ Return dates from netcdf time coordinate """
    t = nc.variables[tvar]
    dts = cftime.num2date(t[:], t.units, calendar=t.calendar)
    # Matplotlib does not support the real_datetime format returned by cftime. Furthermore,
    # it is not possible to create datetime.datetime directly (i.e. using cftime.num2pydate)
    # for certain calendars (e.g. Gregorian). The nc-time-axis package extends matplotlib
    # to work directly with cftime objects. However, to avoid this additional dependency,
    # we manually create the python datetime objects from cftime objects.
    pydts = np.array([
        datetime.datetime(dt.year, dt.month, dt.day, dt.hour, dt.minute,
                          dt.second) for dt in dts
    ])
    return pydts
Example #33
0
def _decode_datetime_with_cftime(num_dates, units, calendar):
    cftime = _import_cftime()

    if cftime.__name__ == 'cftime':
        dates = np.asarray(cftime.num2date(num_dates, units, calendar,
                                           only_use_cftime_datetimes=True))
    else:
        # Must be using num2date from an old version of netCDF4 which
        # does not have the only_use_cftime_datetimes option.
        dates = np.asarray(cftime.num2date(num_dates, units, calendar))

    if (dates[np.nanargmin(num_dates)].year < 1678 or
            dates[np.nanargmax(num_dates)].year >= 2262):
        if calendar in _STANDARD_CALENDARS:
            warnings.warn(
                'Unable to decode time axis into full '
                'numpy.datetime64 objects, continuing using dummy '
                'cftime.datetime objects instead, reason: dates out '
                'of range', SerializationWarning, stacklevel=3)
    else:
        if calendar in _STANDARD_CALENDARS:
            dates = cftime_to_nptime(dates)
    return dates
Example #34
0
def test_use_cftime_default_non_standard_calendar(calendar,
                                                  units_year) -> None:
    from cftime import num2date

    numerical_dates = [0, 1]
    units = f"days since {units_year}-01-01"
    expected = num2date(numerical_dates,
                        units,
                        calendar,
                        only_use_cftime_datetimes=True)

    with assert_no_warnings():
        result = decode_cf_datetime(numerical_dates, units, calendar)
        np.testing.assert_array_equal(result, expected)
Example #35
0
def test_calendar_dask_cftime() -> None:
    from cftime import num2date

    # 3D lazy dask
    data = xr.DataArray(
        num2date(
            np.random.randint(1, 1000000, size=(4, 5, 6)),
            "hours since 1970-01-01T00:00",
            calendar="noleap",
        ),
        dims=("x", "y", "z"),
    ).chunk()
    with raise_if_dask_computes(max_computes=2):
        assert data.dt.calendar == "noleap"
Example #36
0
def test_decode_non_standard_calendar_single_element(calendar):
    import cftime

    units = "days since 0001-01-01"

    dt = cftime.datetime(2001, 2, 29)

    num_time = cftime.date2num(dt, units, calendar)
    actual = coding.times.decode_cf_datetime(num_time, units, calendar=calendar)

    expected = np.asarray(
        cftime.num2date(num_time, units, calendar, only_use_cftime_datetimes=True)
    )
    assert actual.dtype == np.dtype("O")
    assert expected == actual
Example #37
0
def test_decode_non_standard_calendar_inside_timestamp_range(calendar):
    cftime = _import_cftime()
    units = "days since 0001-01-01"
    times = pd.date_range("2001-04-01-00", end="2001-04-30-23", freq="H")
    non_standard_time = cftime.date2num(times.to_pydatetime(), units, calendar=calendar)

    if cftime.__name__ == "cftime":
        expected = cftime.num2date(
            non_standard_time, units, calendar=calendar, only_use_cftime_datetimes=True
        )
    else:
        expected = cftime.num2date(non_standard_time, units, calendar=calendar)

    expected_dtype = np.dtype("O")

    actual = coding.times.decode_cf_datetime(
        non_standard_time, units, calendar=calendar
    )
    assert actual.dtype == expected_dtype
    abs_diff = abs(actual - expected)
    # once we no longer support versions of netCDF4 older than 1.1.5,
    # we could do this check with near microsecond accuracy:
    # https://github.com/Unidata/netcdf4-python/issues/355
    assert (abs_diff <= np.timedelta64(1, "s")).all()
Example #38
0
def _convert_datetime(
    datetime: Union[pydt.datetime, cftime.datetime],
    new_doy: Optional[Union[float, int]] = None,
    calendar: str = "default",
) -> Union[cftime.datetime, pydt.datetime, float]:
    """Convert a datetime object to another calendar.

    Nanosecond information are lost as cftime.datetime doesn't support them.

    Parameters
    ----------
    datetime: Union[datetime.datetime, cftime.datetime]
      A datetime object to convert.
    new_doy:  Optional[Union[float, int]]
      Allows for redefining the day of year (thus ignoring month and day information from the source datetime).
      -1 is understood as a nan.
    calendar: str
      The target calendar

    Returns
    -------
    Union[cftime.datetime, datetime.datetime, np.nan]
      A datetime object of the target calendar with the same year, month, day and time
      as the source (month and day according to `new_doy` if given).
      If the month and day doesn't exist in the target calendar, returns np.nan. (Ex. 02-29 in "noleap")
    """
    if new_doy in [np.nan, -1]:
        return np.nan
    if new_doy is not None:
        new_date = cftime.num2date(
            new_doy - 1,
            f"days since {datetime.year}-01-01",
            calendar=calendar if calendar != "default" else "standard",
        )
    else:
        new_date = datetime
    try:
        return datetime_classes[calendar](
            datetime.year,
            new_date.month,
            new_date.day,
            datetime.hour,
            datetime.minute,
            datetime.second,
            datetime.microsecond,
        )
    except ValueError:
        return np.nan
Example #39
0
def test_decode_single_element_outside_timestamp_range(calendar):
    import cftime

    units = "days since 0001-01-01"
    for days in [1, 1470376]:
        for num_time in [days, [days], [[days]]]:
            with warnings.catch_warnings():
                warnings.filterwarnings("ignore", "Unable to decode time axis")
                actual = coding.times.decode_cf_datetime(
                    num_time, units, calendar=calendar
                )

            expected = cftime.num2date(
                days, units, calendar, only_use_cftime_datetimes=True
            )
            assert isinstance(actual.item(), type(expected))
Example #40
0
 def restore_dataset(self, ds):
     """Return the original time variable.
     """
     if not self._time_computed:
         raise ValueError("time was not computed; cannot restore dataset")
     time_values = ds[self.orig_time_coord_name].values
     if self.orig_time_coord_decoded:
         time_values = xr.CFTimeIndex(
             cftime.num2date(
                 time_values,
                 units=self.time_attrs["units"],
                 calendar=self.time_attrs["calendar"],
             ))
     ds[self.time_coord_name].values = time_values
     ds = ds.drop(self.orig_time_coord_name)
     return ds
Example #41
0
def default_freq(**indexer) -> str:
    """Return the default frequency."""
    freq = "AS-JAN"
    if indexer:
        group, value = indexer.popitem()
        if group == "season":
            month = 12  # The "season" scheme is based on AS-DEC
        elif group == "month":
            month = np.take(value, 0)
        elif group == "doy_bounds":
            month = cftime.num2date(value[0] - 1,
                                    "days since 2004-01-01").month
        elif group == "date_bounds":
            month = int(value[0][:2])
        freq = "AS-" + _MONTH_ABBREVIATIONS[month]
    return freq
Example #42
0
def test_decode_single_element_outside_timestamp_range(
        calendar, enable_cftimeindex):
    if enable_cftimeindex:
        pytest.importorskip('cftime')

    cftime = _import_cftime()
    units = 'days since 0001-01-01'
    for days in [1, 1470376]:
        for num_time in [days, [days], [[days]]]:
            with warnings.catch_warnings():
                warnings.filterwarnings('ignore',
                                        'Unable to decode time axis')
                actual = coding.times.decode_cf_datetime(
                    num_time, units, calendar=calendar,
                    enable_cftimeindex=enable_cftimeindex)
            expected = cftime.num2date(days, units, calendar)
            assert isinstance(actual.item(), type(expected))
Example #43
0
    def load(self):
        fnc = netCDF4.Dataset(self.file_path)
        variables = {}
        for var_name, var_name_nc in cdf_variable_name_dict.items():
            variables[var_name] = np.array(fnc[var_name_nc]).reshape((fnc[var_name_nc].shape[0], 1))

        time_units = fnc['UNIX_TIME'].units

        variables['DATETIME'] = cftime.num2date(variables['UNIX_TIME'].flatten(),
                                                units=time_units,
                                                only_use_cftime_datetimes=False,
                                                only_use_python_datetimes=True)

        variables['DATETIME'] = np.reshape(variables['DATETIME'], (fnc['UNIX_TIME'].shape[0], 1))
        self.variables = variables
        self.done = True
        fnc.close()
Example #44
0
def time_year_plus_frac(ds, time_name):
    """return time variable, as year plus fraction of year"""

    # this is straightforward if time has units='days since 0000-01-01' and calendar='noleap'
    # so convert specification of time to that representation

    # get time values as an np.ndarray of cftime objects
    if np.dtype(ds[time_name]) == np.dtype('O'):
        tvals_cftime = ds[time_name].values
    else:
        tvals_cftime = cftime.num2date(
            ds[time_name].values, ds[time_name].attrs['units'], ds[time_name].attrs['calendar'])

    # convert cftime objects to representation mentioned above
    tvals_days = cftime.date2num(tvals_cftime, 'days since 0000-01-01', calendar='noleap')

    return tvals_days / 365.0
Example #45
0
def time_index_to_datetime(timestamps, time_units: str):

    if isinstance(timestamps, np.ndarray):
        timestamps = timestamps.tolist()

    if not isinstance(timestamps, list):
        timestamps = [timestamps]

    result = [
        cftime.num2date(timestamp, time_units).replace(tzinfo=pytz.UTC)
        for timestamp in timestamps
    ]

    if isinstance(result[0], list):
        return list(itertools.chain(*result))

    return result
Example #46
0
def test_decode_single_element_outside_timestamp_range(
        calendar, enable_cftimeindex):
    if enable_cftimeindex:
        pytest.importorskip('cftime')

    cftime = _import_cftime()
    units = 'days since 0001-01-01'
    for days in [1, 1470376]:
        for num_time in [days, [days], [[days]]]:
            with warnings.catch_warnings():
                warnings.filterwarnings('ignore',
                                        'Unable to decode time axis')
                actual = coding.times.decode_cf_datetime(
                    num_time, units, calendar=calendar,
                    enable_cftimeindex=enable_cftimeindex)
            expected = cftime.num2date(days, units, calendar)
            assert isinstance(actual.item(), type(expected))
Example #47
0
def test_cf_datetime(num_dates, units, calendar):
    cftime = _import_cftime()
    expected = _ensure_naive_tz(
        cftime.num2date(num_dates, units, calendar))
    with warnings.catch_warnings():
        warnings.filterwarnings('ignore',
                                'Unable to decode time axis')
        actual = coding.times.decode_cf_datetime(num_dates, units,
                                                 calendar)
    if (isinstance(actual, np.ndarray) and
            np.issubdtype(actual.dtype, np.datetime64)):
        # self.assertEqual(actual.dtype.kind, 'M')
        # For some reason, numpy 1.8 does not compare ns precision
        # datetime64 arrays as equal to arrays of datetime objects,
        # but it works for us precision. Thus, convert to us
        # precision for the actual array equal comparison...
        actual_cmp = actual.astype('M8[us]')
    else:
        actual_cmp = actual
    assert_array_equal(expected, actual_cmp)
    encoded, _, _ = coding.times.encode_cf_datetime(actual, units,
                                                    calendar)
    if '1-1-1' not in units:
        # pandas parses this date very strangely, so the original
        # units/encoding cannot be preserved in this case:
        # (Pdb) pd.to_datetime('1-1-1 00:00:0.0')
        # Timestamp('2001-01-01 00:00:00')
        assert_array_equal(num_dates, np.around(encoded, 1))
        if (hasattr(num_dates, 'ndim') and num_dates.ndim == 1 and
                '1000' not in units):
            # verify that wrapping with a pandas.Index works
            # note that it *does not* currently work to even put
            # non-datetime64 compatible dates into a pandas.Index
            encoded, _, _ = coding.times.encode_cf_datetime(
                pd.Index(actual), units, calendar)
            assert_array_equal(num_dates, np.around(encoded, 1))
Example #48
0
epsg = options.epsg
extract_type = options.extract_type
level = options.level
shp_filename = options.out_file
ts_fieldname = "timestamp"
dst_fieldname = options.dst_fieldname
step = options.step

nc = NC(filename, "r")
xdim, ydim, zdim, tdim = ppt.get_dims(nc)

if tdim:
    time = nc.variables[tdim]
    time_units = time.units
    time_calendar = time.calendar
    timestamps = cftime.num2date(time[:], time_units, time_calendar)
    has_time = True
else:
    tdim = None
nc.close()

src_ds = gdal.Open("NETCDF:{}:{}".format(filename, dst_fieldname))

# Get Memory Driver
mem_driver = ogr.GetDriverByName("Memory")
mem_ds = mem_driver.CreateDataSource("memory_layer")

# Get SHP Driver
shp_driver = ogr.GetDriverByName("ESRI Shapefile")
shp_filename = validateShapePath(shp_filename)
if os.path.exists(shp_filename):
Example #49
0
        # Bounding box for our catchment
        BB = dict(lon=[143, 150], lat=[-37, -33])

        (latidx,) = logical_and(lat >= BB["lat"][0], lat < BB["lat"][1]).nonzero()
        (lonidx,) = logical_and(lon >= BB["lon"][0], lon < BB["lon"][1]).nonzero()

        print(lonidx)
        print(latidx)
        print(lat[latidx])
        print(lon[lonidx])
        # get rid of the non used lat/lon now
        lat = lat[latidx]
        lon = lon[lonidx]
        # Now get the time for the x-axis
        time = ncdataset.variables["time"]
        timeObj = cftime.num2date(time[:], units=time.units, calendar=time.calendar)

        # Now determine area P for each timestep and display in a graph
        # first  the mean per area lat, next average those also
        # Multiply with timestep in seconds to get mm

        # unfortunateley Tair also has  heigh dimension and Precip not
        if mapstackname == "P":
            p_select = (
                ncdata[:, latidx.min() : latidx.max(), lonidx.min() : lonidx.max()]
                * 86400
            )
        if mapstackname == "TEMP":
            p_select = (
                ncdata[:, 0, latidx.min() : latidx.max(), lonidx.min() : lonidx.max()]
                - 273.15
Example #50
0
def write_netcdf_timeseries(
    srcFolder,
    srcPrefix,
    trgFile,
    trgVar,
    trgUnits,
    trgName,
    timeList,
    metadata,
    logger,
    clone,
    maxbuf=600,
    Format="NETCDF4",
    zlib=True,
    least_significant_digit=None,
    startidx=0,
    EPSG="EPSG:4326",
    FillVal=1e31,
):
    """
    Write pcraster mapstack to netcdf file. Taken from GLOFRIS_Utils.py
    
    - srcFolder - Folder with pcraster mapstack
    - srcPrefix - name of the mapstack
    - trgFile - target netcdf file
    - tgrVar - variable in nc file
    - trgUnits - units for the netcdf file
    - timeLists - list of times
    - metadata - dict with metedata for var

    Optional argumenrs
    - maxbuf = 600: number of timesteps to buffer before writing
    
    """
    complevel = 9

    # if necessary, make trgPrefix maximum of 8 characters
    if len(srcPrefix) > 8:
        srcPrefix = srcPrefix[0:8]
    # Open target netCDF file
    nc_trg = nc4.Dataset(trgFile, "a", format=Format)
    # read time axis and convert to time objects
    logger.debug("Creating time object..")
    time = nc_trg.variables["time"]
    nc_trg.set_fill_off()
    timeObj = cftime.num2date(time[:], units=time.units, calendar=time.calendar)

    try:
        nc_var = nc_trg.variables[trgVar]
    except:
        # prepare the variable

        if EPSG.lower() == "epsg:4326":
            nc_var = nc_trg.createVariable(
                trgVar,
                "f4",
                ("time", "lat", "lon"),
                fill_value=FillVal,
                zlib=zlib,
                complevel=complevel,
                least_significant_digit=least_significant_digit,
            )
            nc_var.coordinates = "lat lon"
        else:
            nc_var = nc_trg.createVariable(
                trgVar,
                "f4",
                ("time", "y", "x"),
                fill_value=FillVal,
                zlib=zlib,
                complevel=complevel,
                least_significant_digit=least_significant_digit,
            )
            nc_var.coordinates = "lat lon"
            nc_var.grid_mapping = "crs"

        nc_var.units = trgUnits
        nc_var.standard_name = trgName
        # print metadata
        for attr in metadata:
            # print metadata[attr]
            nc_var.setncattr(attr, metadata[attr])

    nc_Fill = nc_var._FillValue
    # Create a buffer of a number of timesteps to speed-up writing
    bufsize = minimum(len(timeList), maxbuf)

    if len(shape(nc_trg.variables["lat"])) == 2:
        latlen = shape(nc_trg.variables["lat"])[0]
        lonlen = shape(nc_trg.variables["lon"])[1]
    else:
        latlen = len(nc_trg.variables["lat"])
        lonlen = len(nc_trg.variables["lon"])

    timestepbuffer = np.zeros((bufsize, latlen, lonlen))

    # now loop over all time steps, check the date and write valid dates to a list, write time series to PCRaster maps
    for nn, curTime in enumerate(timeList):
        logger.debug("Adding time: " + str(curTime))
        idx = int(where(timeObj == curTime)[0])
        count = nn + startidx

        below_thousand = count % 1000
        above_thousand = count / 1000
        # read the file of interest
        pcraster_file = str(srcPrefix + "%0" + str(8 - len(srcPrefix)) + ".f.%03.f") % (
            above_thousand,
            below_thousand,
        )
        pcraster_path = os.path.join(srcFolder, pcraster_file)
        # write grid to PCRaster file
        logger.debug("processing map: " + pcraster_file)
        # x, y, data, FillVal = readMap(pcraster_path, 'PCRaster',logger)
        x, y, data, FFillVal = _readMap(pcraster_path, "PCRaster", logger)
        logger.debug("Setting fillval...")

        data[data == FFillVal] = float(nc_Fill)
        data[isinf(data)] = float(nc_Fill)
        data[isnan(data)] = float(nc_Fill)
        data[clone <= -999] = float(nc_Fill)
        data[clone == FFillVal] = float(nc_Fill)

        buffreset = (idx + 1) % maxbuf
        bufpos = (idx) % maxbuf
        logger.debug("Adding data to array...")
        timestepbuffer[bufpos, :, :] = data

        logger.debug(
            "index: "
            + str(idx - bufpos)
            + " index: "
            + str(idx)
            + "bufpos: "
            + str(bufpos)
            + "idx: "
            + str(idx)
        )
        if buffreset == 0 or idx == bufsize - 1 or nn + 1 == len(timeList):
            logger.info(
                "Writing buffer to file at: "
                + str(curTime)
                + " "
                + str(int(bufpos) + 1)
                + " timesteps"
            )
            nc_var[idx - bufpos : idx + 1, :, :] = timestepbuffer[0 : bufpos + 1, :, :]
            nc_trg.sync()

    # nc_trg.sync()
    nc_trg.close()
Example #51
0
def main():
    ### Read input arguments #####
    parser = OptionParser()
    usage = "usage: %prog [options]"
    parser = OptionParser(usage=usage)
    parser.add_option(
        "-q",
        "--quiet",
        dest="verbose",
        default=True,
        action="store_false",
        help="do not print status messages to stdout",
    )
    parser.add_option(
        "-i",
        "--ini",
        dest="inifile",
        default="hand_contour_inun.ini",
        nargs=1,
        help="ini configuration file",
    )
    parser.add_option(
        "-f",
        "--flood_map",
        nargs=1,
        dest="flood_map",
        help="Flood map file (NetCDF point time series file",
    )
    parser.add_option(
        "-v",
        "--flood_variable",
        nargs=1,
        dest="flood_variable",
        default="water_level",
        help="variable name of flood water level",
    )
    parser.add_option(
        "-b",
        "--bankfull_map",
        dest="bankfull_map",
        default="",
        help="Map containing bank full level (is subtracted from flood map, in NetCDF)",
    )
    parser.add_option(
        "-c",
        "--catchment",
        dest="catchment_strahler",
        default=7,
        type="int",
        help="Strahler order threshold >= are selected as catchment boundaries",
    )
    parser.add_option(
        "-t",
        "--time",
        dest="time",
        default="",
        help="time in YYYYMMDDHHMMSS, overrides time in NetCDF input if set",
    )
    # parser.add_option('-s', '--hand_strahler',
    #                   dest='hand_strahler', default=7, type='int',
    #                   help='Strahler order threshold >= selected as riverine')
    parser.add_option(
        "-m",
        "--max_strahler",
        dest="max_strahler",
        default=1000,
        type="int",
        help="Maximum Strahler order to loop over",
    )
    parser.add_option(
        "-d", "--destination", dest="dest_path", default="inun", help="Destination path"
    )
    parser.add_option(
        "-H",
        "--hand_file_prefix",
        dest="hand_file_prefix",
        default="",
        help="optional HAND file prefix of already generated HAND files",
    )
    parser.add_option(
        "-n",
        "--neg_HAND",
        dest="neg_HAND",
        default=0,
        type="int",
        help="if set to 1, allow for negative HAND values in HAND maps",
    )
    (options, args) = parser.parse_args()

    if not os.path.exists(options.inifile):
        print("path to ini file cannot be found")
        sys.exit(1)
    options.dest_path = os.path.abspath(options.dest_path)

    if not (os.path.isdir(options.dest_path)):
        os.makedirs(options.dest_path)

    # set up the logger
    flood_name = os.path.split(options.flood_map)[1].split(".")[0]
    # case_name = 'inun_{:s}_hand_{:02d}_catch_{:02d}'.format(flood_name, options.hand_strahler, options.catchment_strahler)
    case_name = "inun_{:s}_catch_{:02d}".format(flood_name, options.catchment_strahler)
    logfilename = os.path.join(options.dest_path, "hand_contour_inun.log")
    logger, ch = inun_lib.setlogger(logfilename, "HAND_INUN", options.verbose)
    logger.info("$Id: $")
    logger.info("Flood map: {:s}".format(options.flood_map))
    logger.info("Bank full map: {:s}".format(options.bankfull_map))
    logger.info("Destination path: {:s}".format(options.dest_path))
    # read out ini file
    ### READ CONFIG FILE
    # open config-file
    config = inun_lib.open_conf(options.inifile)

    # read settings
    options.dem_file = inun_lib.configget(config, "HighResMaps", "dem_file", True)
    options.ldd_file = inun_lib.configget(config, "HighResMaps", "ldd_file", True)
    options.stream_file = inun_lib.configget(config, "HighResMaps", "stream_file", True)
    options.riv_length_fact_file = inun_lib.configget(
        config, "wflowResMaps", "riv_length_fact_file", True
    )
    options.ldd_wflow = inun_lib.configget(config, "wflowResMaps", "ldd_wflow", True)
    options.riv_width_file = inun_lib.configget(
        config, "wflowResMaps", "riv_width_file", True
    )
    options.file_format = inun_lib.configget(
        config, "file_settings", "file_format", 0, datatype="int"
    )
    options.out_format = inun_lib.configget(
        config, "file_settings", "out_format", 0, datatype="int"
    )
    options.latlon = inun_lib.configget(
        config, "file_settings", "latlon", 0, datatype="int"
    )
    options.x_tile = inun_lib.configget(
        config, "tiling", "x_tile", 10000, datatype="int"
    )
    options.y_tile = inun_lib.configget(
        config, "tiling", "y_tile", 10000, datatype="int"
    )
    options.x_overlap = inun_lib.configget(
        config, "tiling", "x_overlap", 1000, datatype="int"
    )
    options.y_overlap = inun_lib.configget(
        config, "tiling", "y_overlap", 1000, datatype="int"
    )
    options.iterations = inun_lib.configget(
        config, "inundation", "iterations", 20, datatype="int"
    )
    options.initial_level = inun_lib.configget(
        config, "inundation", "initial_level", 32.0, datatype="float"
    )
    options.flood_volume_type = inun_lib.configget(
        config, "inundation", "flood_volume_type", 0, datatype="int"
    )

    # options.area_multiplier = inun_lib.configget(config, 'inundation',
    #                               'area_multiplier', 1., datatype='float')
    logger.info("DEM file: {:s}".format(options.dem_file))
    logger.info("LDD file: {:s}".format(options.ldd_file))
    logger.info("streamfile: {:s}".format(options.stream_file))
    logger.info("Columns per tile: {:d}".format(options.x_tile))
    logger.info("Rows per tile: {:d}".format(options.y_tile))
    logger.info("Columns overlap: {:d}".format(options.x_overlap))
    logger.info("Rows overlap: {:d}".format(options.y_overlap))
    metadata_global = {}
    # add metadata from the section [metadata]
    meta_keys = config.options("metadata_global")
    for key in meta_keys:
        metadata_global[key] = config.get("metadata_global", key)
    # add a number of metadata variables that are mandatory
    metadata_global["config_file"] = os.path.abspath(options.inifile)
    metadata_var = {}
    metadata_var["units"] = "m"
    metadata_var["standard_name"] = "water_surface_height_above_reference_datum"
    metadata_var["long_name"] = "flooding"
    metadata_var[
        "comment"
    ] = "water_surface_reference_datum_altitude is given in file {:s}".format(
        options.dem_file
    )
    if not os.path.exists(options.dem_file):
        logger.error("path to dem file {:s} cannot be found".format(options.dem_file))
        sys.exit(1)
    if not os.path.exists(options.ldd_file):
        logger.error("path to ldd file {:s} cannot be found".format(options.ldd_file))
        sys.exit(1)

    # Read extent from a GDAL compatible file
    try:
        extent = inun_lib.get_gdal_extent(options.dem_file)
    except:
        msg = "Input file {:s} not a gdal compatible file".format(options.dem_file)
        inun_lib.close_with_error(logger, ch, msg)
        sys.exit(1)

    try:
        x, y = inun_lib.get_gdal_axes(options.dem_file, logging=logger)
        srs = inun_lib.get_gdal_projection(options.dem_file, logging=logger)
    except:
        msg = "Input file {:s} not a gdal compatible file".format(options.dem_file)
        inun_lib.close_with_error(logger, ch, msg)
        sys.exit(1)

    # read history from flood file
    if options.file_format == 0:
        a = nc.Dataset(options.flood_map, "r")
        metadata_global[
            "history"
        ] = "Created by: $Id: $, boundary conditions from {:s},\nhistory: {:s}".format(
            os.path.abspath(options.flood_map), a.history
        )
        a.close()
    else:
        metadata_global[
            "history"
        ] = "Created by: $Id: $, boundary conditions from {:s},\nhistory: {:s}".format(
            os.path.abspath(options.flood_map), "PCRaster file, no history"
        )

    # first write subcatch maps and hand maps
    ############### TODO ######
    # setup a HAND file for each strahler order

    max_s = inun_lib.define_max_strahler(options.stream_file, logging=logger)
    stream_max = np.minimum(max_s, options.max_strahler)

    for hand_strahler in range(options.catchment_strahler, stream_max + 1, 1):
        dem_name = os.path.split(options.dem_file)[1].split(".")[0]
        if os.path.isfile(
            "{:s}_{:02d}.tif".format(options.hand_file_prefix, hand_strahler)
        ):
            hand_file = "{:s}_{:02d}.tif".format(
                options.hand_file_prefix, hand_strahler
            )
        else:
            logger.info(
                "No HAND files with HAND prefix were found, checking {:s}_hand_strahler_{:02d}.tif".format(
                    dem_name, hand_strahler
                )
            )
            hand_file = os.path.join(
                options.dest_path,
                "{:s}_hand_strahler_{:02d}.tif".format(dem_name, hand_strahler),
            )
        if not (os.path.isfile(hand_file)):
            # hand file does not exist yet! Generate it, otherwise skip!
            logger.info(
                "HAND file {:s} not found, start setting up...please wait...".format(
                    hand_file
                )
            )
            hand_file_tmp = os.path.join(
                options.dest_path,
                "{:s}_hand_strahler_{:02d}.tif.tmp".format(dem_name, hand_strahler),
            )
            ds_hand, band_hand = inun_lib.prepare_gdal(
                hand_file_tmp, x, y, logging=logger, srs=srs
            )
            # band_hand = ds_hand.GetRasterBand(1)

            # Open terrain data for reading
            ds_dem, rasterband_dem = inun_lib.get_gdal_rasterband(options.dem_file)
            ds_ldd, rasterband_ldd = inun_lib.get_gdal_rasterband(options.ldd_file)
            ds_stream, rasterband_stream = inun_lib.get_gdal_rasterband(
                options.stream_file
            )
            n = 0
            for x_loop in range(0, len(x), options.x_tile):
                x_start = np.maximum(x_loop, 0)
                x_end = np.minimum(x_loop + options.x_tile, len(x))
                # determine actual overlap for cutting
                for y_loop in range(0, len(y), options.y_tile):
                    x_overlap_min = x_start - np.maximum(x_start - options.x_overlap, 0)
                    x_overlap_max = (
                        np.minimum(x_end + options.x_overlap, len(x)) - x_end
                    )
                    n += 1
                    # print('tile {:001d}:'.format(n))
                    y_start = np.maximum(y_loop, 0)
                    y_end = np.minimum(y_loop + options.y_tile, len(y))
                    y_overlap_min = y_start - np.maximum(y_start - options.y_overlap, 0)
                    y_overlap_max = (
                        np.minimum(y_end + options.y_overlap, len(y)) - y_end
                    )
                    # cut out DEM
                    logger.debug(
                        "Computing HAND for xmin: {:d} xmax: {:d} ymin {:d} ymax {:d}".format(
                            x_start, x_end, y_start, y_end
                        )
                    )
                    terrain = rasterband_dem.ReadAsArray(
                        float(x_start - x_overlap_min),
                        float(y_start - y_overlap_min),
                        int((x_end + x_overlap_max) - (x_start - x_overlap_min)),
                        int((y_end + y_overlap_max) - (y_start - y_overlap_min)),
                    )

                    drainage = rasterband_ldd.ReadAsArray(
                        float(x_start - x_overlap_min),
                        float(y_start - y_overlap_min),
                        int((x_end + x_overlap_max) - (x_start - x_overlap_min)),
                        int((y_end + y_overlap_max) - (y_start - y_overlap_min)),
                    )
                    stream = rasterband_stream.ReadAsArray(
                        float(x_start - x_overlap_min),
                        float(y_start - y_overlap_min),
                        int((x_end + x_overlap_max) - (x_start - x_overlap_min)),
                        int((y_end + y_overlap_max) - (y_start - y_overlap_min)),
                    )
                    # write to temporary file
                    terrain_temp_file = os.path.join(
                        options.dest_path, "terrain_temp.map"
                    )
                    drainage_temp_file = os.path.join(
                        options.dest_path, "drainage_temp.map"
                    )
                    stream_temp_file = os.path.join(
                        options.dest_path, "stream_temp.map"
                    )
                    if rasterband_dem.GetNoDataValue() is not None:
                        inun_lib.gdal_writemap(
                            terrain_temp_file,
                            "PCRaster",
                            np.arange(0, terrain.shape[1]),
                            np.arange(0, terrain.shape[0]),
                            terrain,
                            rasterband_dem.GetNoDataValue(),
                            gdal_type=gdal.GDT_Float32,
                            logging=logger,
                        )
                    else:
                        # in case no nodata value is found
                        logger.warning(
                            "No nodata value found in {:s}. assuming -9999".format(
                                options.dem_file
                            )
                        )
                        inun_lib.gdal_writemap(
                            terrain_temp_file,
                            "PCRaster",
                            np.arange(0, terrain.shape[1]),
                            np.arange(0, terrain.shape[0]),
                            terrain,
                            -9999.0,
                            gdal_type=gdal.GDT_Float32,
                            logging=logger,
                        )

                    inun_lib.gdal_writemap(
                        drainage_temp_file,
                        "PCRaster",
                        np.arange(0, terrain.shape[1]),
                        np.arange(0, terrain.shape[0]),
                        drainage,
                        rasterband_ldd.GetNoDataValue(),
                        gdal_type=gdal.GDT_Int32,
                        logging=logger,
                    )
                    inun_lib.gdal_writemap(
                        stream_temp_file,
                        "PCRaster",
                        np.arange(0, terrain.shape[1]),
                        np.arange(0, terrain.shape[0]),
                        stream,
                        rasterband_ldd.GetNoDataValue(),
                        gdal_type=gdal.GDT_Int32,
                        logging=logger,
                    )
                    # read as pcr objects
                    pcr.setclone(terrain_temp_file)
                    terrain_pcr = pcr.readmap(terrain_temp_file)
                    drainage_pcr = pcr.lddrepair(
                        pcr.ldd(pcr.readmap(drainage_temp_file))
                    )  # convert to ldd type map
                    stream_pcr = pcr.scalar(
                        pcr.readmap(stream_temp_file)
                    )  # convert to ldd type map

                    # check if the highest stream order of the tile is below the hand_strahler
                    # if the highest stream order of the tile is smaller than hand_strahler, than DEM values are taken instead of HAND values.
                    max_stream_tile = inun_lib.define_max_strahler(
                        stream_temp_file, logging=logger
                    )
                    if max_stream_tile < hand_strahler:
                        hand_pcr = terrain_pcr
                        logger.info(
                            "For this tile, DEM values are used instead of HAND because there is no stream order larger than {:02d}".format(
                                hand_strahler
                            )
                        )
                    else:
                        # compute streams
                        stream_ge, subcatch = inun_lib.subcatch_stream(
                            drainage_pcr, hand_strahler, stream=stream_pcr
                        )  # generate streams
                        # compute basins
                        stream_ge_dummy, subcatch = inun_lib.subcatch_stream(
                            drainage_pcr, options.catchment_strahler, stream=stream_pcr
                        )  # generate streams
                        basin = pcr.boolean(subcatch)
                        hand_pcr, dist_pcr = inun_lib.derive_HAND(
                            terrain_pcr,
                            drainage_pcr,
                            3000,
                            rivers=pcr.boolean(stream_ge),
                            basin=basin,
                            neg_HAND=options.neg_HAND,
                        )
                    # convert to numpy
                    hand = pcr.pcr2numpy(hand_pcr, -9999.0)
                    # cut relevant part
                    if y_overlap_max == 0:
                        y_overlap_max = -hand.shape[0]
                    if x_overlap_max == 0:
                        x_overlap_max = -hand.shape[1]
                    hand_cut = hand[
                        0 + y_overlap_min : -y_overlap_max,
                        0 + x_overlap_min : -x_overlap_max,
                    ]

                    band_hand.WriteArray(hand_cut, float(x_start), float(y_start))
                    os.unlink(terrain_temp_file)
                    os.unlink(drainage_temp_file)
                    os.unlink(stream_temp_file)
                    band_hand.FlushCache()
            ds_dem = None
            ds_ldd = None
            ds_stream = None
            band_hand.SetNoDataValue(-9999.0)
            ds_hand = None
            logger.info("Finalizing {:s}".format(hand_file))
            # rename temporary file to final hand file
            os.rename(hand_file_tmp, hand_file)
        else:
            logger.info("HAND file {:s} already exists...skipping...".format(hand_file))

    #####################################################################################
    #  HAND file has now been prepared, moving to flood mapping part                    #
    #####################################################################################
    # set the clone
    pcr.setclone(options.ldd_wflow)
    # read wflow ldd as pcraster object
    ldd_pcr = pcr.readmap(options.ldd_wflow)
    xax, yax, riv_width, fill_value = inun_lib.gdal_readmap(
        options.riv_width_file, "GTiff", logging=logger
    )

    # determine cell length in meters using ldd_pcr as clone (if latlon=True, values are converted to m2
    x_res, y_res, reallength_wflow = pcrut.detRealCellLength(
        pcr.scalar(ldd_pcr), not (bool(options.latlon))
    )
    cell_surface_wflow = pcr.pcr2numpy(x_res * y_res, 0)

    if options.flood_volume_type == 0:
        # load the staticmaps needed to estimate volumes across all
        # xax, yax, riv_length, fill_value = inun_lib.gdal_readmap(options.riv_length_file, 'GTiff', logging=logger)
        # riv_length = np.ma.masked_where(riv_length==fill_value, riv_length)
        xax, yax, riv_width, fill_value = inun_lib.gdal_readmap(
            options.riv_width_file, "GTiff", logging=logger
        )
        riv_width[riv_width == fill_value] = 0

        # read river length factor file (multiplier)
        xax, yax, riv_length_fact, fill_value = inun_lib.gdal_readmap(
            options.riv_length_fact_file, "GTiff", logging=logger
        )
        riv_length_fact = np.ma.masked_where(
            riv_length_fact == fill_value, riv_length_fact
        )
        drain_length = wflow_lib.detdrainlength(ldd_pcr, x_res, y_res)

        # compute river length in each cell
        riv_length = pcr.pcr2numpy(drain_length, 0) * riv_length_fact
        # riv_length_pcr = pcr.numpy2pcr(pcr.Scalar, riv_length, 0)

    flood_folder = os.path.join(options.dest_path, case_name)
    flood_vol_map = os.path.join(
        flood_folder,
        "{:s}_vol.tif".format(os.path.split(options.flood_map)[1].split(".")[0]),
    )
    if not (os.path.isdir(flood_folder)):
        os.makedirs(flood_folder)
    if options.out_format == 0:
        inun_file_tmp = os.path.join(flood_folder, "{:s}.tif.tmp".format(case_name))
        inun_file = os.path.join(flood_folder, "{:s}.tif".format(case_name))
    else:
        inun_file_tmp = os.path.join(flood_folder, "{:s}.nc.tmp".format(case_name))
        inun_file = os.path.join(flood_folder, "{:s}.nc".format(case_name))

    hand_temp_file = os.path.join(flood_folder, "hand_temp.map")
    drainage_temp_file = os.path.join(flood_folder, "drainage_temp.map")
    stream_temp_file = os.path.join(flood_folder, "stream_temp.map")
    flood_vol_temp_file = os.path.join(flood_folder, "flood_warp_temp.tif")
    # load the data with river levels and compute the volumes
    if options.file_format == 0:
        # assume we need the maximum value in a NetCDF time series grid
        logger.info("Reading flood from {:s} NetCDF file".format(options.flood_map))
        a = nc.Dataset(options.flood_map, "r")
        if options.latlon == 0:
            xax = a.variables["x"][:]
            yax = a.variables["y"][:]
        else:
            try:
                xax = a.variables["lon"][:]
                yax = a.variables["lat"][:]
            except:
                xax = a.variables["x"][:]
                yax = a.variables["y"][:]
        if options.time == "":
            time_list = cftime.num2date(
                a.variables["time"][:],
                units=a.variables["time"].units,
                calendar=a.variables["time"].calendar,
            )
            time = [time_list[len(time_list) // 2]]
        else:
            time = [dt.datetime.strptime(options.time, "%Y%m%d%H%M%S")]

        flood_series = a.variables[options.flood_variable][:]
        flood_data = flood_series.max(axis=0)
        if np.ma.is_masked(flood_data):
            flood = flood_data.data
            flood[flood_data.mask] = 0
        if yax[-1] > yax[0]:
            yax = np.flipud(yax)
            flood = np.flipud(flood)
        a.close()
    elif options.file_format == 1:
        logger.info("Reading flood from {:s} PCRaster file".format(options.flood_map))
        xax, yax, flood, flood_fill_value = inun_lib.gdal_readmap(
            options.flood_map, "PCRaster", logging=logger
        )
        flood = np.ma.masked_equal(flood, flood_fill_value)
        if options.time == "":
            options.time = "20000101000000"
        time = [dt.datetime.strptime(options.time, "%Y%m%d%H%M%S")]

        flood[flood == flood_fill_value] = 0.0
    # load the bankfull depths
    if options.bankfull_map == "":
        bankfull = np.zeros(flood.shape)
    else:
        if options.file_format == 0:
            logger.info(
                "Reading bankfull from {:s} NetCDF file".format(options.bankfull_map)
            )
            a = nc.Dataset(options.bankfull_map, "r")
            xax = a.variables["x"][:]
            yax = a.variables["y"][:]
            #            xax = a.variables['lon'][:]
            #            yax = a.variables['lat'][:]

            bankfull_series = a.variables[options.flood_variable][:]
            bankfull_data = bankfull_series.max(axis=0)
            if np.ma.is_masked(bankfull_data):
                bankfull = bankfull_data.data
                bankfull[bankfull_data.mask] = 0
            if yax[-1] > yax[0]:
                yax = np.flipud(yax)
                bankfull = np.flipud(bankfull)
            a.close()
        elif options.file_format == 1:
            logger.info(
                "Reading bankfull from {:s} PCRaster file".format(options.bankfull_map)
            )
            xax, yax, bankfull, bankfull_fill_value = inun_lib.gdal_readmap(
                options.bankfull_map, "PCRaster", logging=logger
            )
            bankfull = np.ma.masked_equal(bankfull, bankfull_fill_value)
    #     flood = bankfull*2
    # res_x = 2000
    # res_y = 2000
    # subtract the bankfull water level to get flood levels (above bankfull)
    flood_vol = np.maximum(flood - bankfull, 0)
    if options.flood_volume_type == 0:
        flood_vol_m = (
            riv_length * riv_width * flood_vol / cell_surface_wflow
        )  # volume expressed in meters water disc
        flood_vol_m_pcr = pcr.numpy2pcr(pcr.Scalar, flood_vol_m, 0)
    else:
        flood_vol_m = flood_vol / cell_surface_wflow
    flood_vol_m_data = flood_vol_m.data
    flood_vol_m_data[flood_vol_m.mask] = -999.0
    logger.info("Saving water layer map to {:s}".format(flood_vol_map))
    # write to a tiff file
    inun_lib.gdal_writemap(
        flood_vol_map,
        "GTiff",
        xax,
        yax,
        np.maximum(flood_vol_m_data, 0),
        -999.0,
        logging=logger,
    )
    # this is placed later in the hand loop
    # ds_hand, rasterband_hand = inun_lib.get_gdal_rasterband(hand_file)
    ds_ldd, rasterband_ldd = inun_lib.get_gdal_rasterband(options.ldd_file)
    ds_stream, rasterband_stream = inun_lib.get_gdal_rasterband(options.stream_file)

    logger.info("Preparing flood map in {:s} ...please wait...".format(inun_file))
    if options.out_format == 0:
        ds_inun, band_inun = inun_lib.prepare_gdal(
            inun_file_tmp, x, y, logging=logger, srs=srs
        )
        # band_inun = ds_inun.GetRasterBand(1)
    else:
        ds_inun, band_inun = inun_lib.prepare_nc(
            inun_file_tmp,
            time,
            x,
            np.flipud(y),
            metadata=metadata_global,
            metadata_var=metadata_var,
            logging=logger,
        )
    # loop over all the tiles
    n = 0
    for x_loop in range(0, len(x), options.x_tile):
        x_start = np.maximum(x_loop, 0)
        x_end = np.minimum(x_loop + options.x_tile, len(x))
        # determine actual overlap for cutting
        for y_loop in range(0, len(y), options.y_tile):
            x_overlap_min = x_start - np.maximum(x_start - options.x_overlap, 0)
            x_overlap_max = np.minimum(x_end + options.x_overlap, len(x)) - x_end
            n += 1
            # print('tile {:001d}:'.format(n))
            y_start = np.maximum(y_loop, 0)
            y_end = np.minimum(y_loop + options.y_tile, len(y))
            y_overlap_min = y_start - np.maximum(y_start - options.y_overlap, 0)
            y_overlap_max = np.minimum(y_end + options.y_overlap, len(y)) - y_end
            x_tile_ax = x[x_start - x_overlap_min : x_end + x_overlap_max]
            y_tile_ax = y[y_start - y_overlap_min : y_end + y_overlap_max]
            # cut out DEM
            logger.debug(
                "handling xmin: {:d} xmax: {:d} ymin {:d} ymax {:d}".format(
                    x_start, x_end, y_start, y_end
                )
            )

            drainage = rasterband_ldd.ReadAsArray(
                float(x_start - x_overlap_min),
                float(y_start - y_overlap_min),
                int((x_end + x_overlap_max) - (x_start - x_overlap_min)),
                int((y_end + y_overlap_max) - (y_start - y_overlap_min)),
            )
            stream = rasterband_stream.ReadAsArray(
                float(x_start - x_overlap_min),
                float(y_start - y_overlap_min),
                int((x_end + x_overlap_max) - (x_start - x_overlap_min)),
                int((y_end + y_overlap_max) - (y_start - y_overlap_min)),
            )

            # stream_max = np.minimum(stream.max(), options.max_strahler)

            inun_lib.gdal_writemap(
                drainage_temp_file,
                "PCRaster",
                x_tile_ax,
                y_tile_ax,
                drainage,
                rasterband_ldd.GetNoDataValue(),
                gdal_type=gdal.GDT_Int32,
                logging=logger,
            )
            inun_lib.gdal_writemap(
                stream_temp_file,
                "PCRaster",
                x_tile_ax,
                y_tile_ax,
                stream,
                rasterband_stream.GetNoDataValue(),
                gdal_type=gdal.GDT_Int32,
                logging=logger,
            )

            # read as pcr objects
            pcr.setclone(stream_temp_file)
            drainage_pcr = pcr.lddrepair(
                pcr.ldd(pcr.readmap(drainage_temp_file))
            )  # convert to ldd type map
            stream_pcr = pcr.scalar(
                pcr.readmap(stream_temp_file)
            )  # convert to ldd type map

            # warp of flood volume to inundation resolution
            inun_lib.gdal_warp(
                flood_vol_map,
                stream_temp_file,
                flood_vol_temp_file,
                gdal_interp=gdalconst.GRA_NearestNeighbour,
            )  # ,
            x_tile_ax, y_tile_ax, flood_meter, fill_value = inun_lib.gdal_readmap(
                flood_vol_temp_file, "GTiff", logging=logger
            )
            # make sure that the option unittrue is on !! (if unitcell was is used in another function)
            x_res_tile, y_res_tile, reallength = pcrut.detRealCellLength(
                pcr.scalar(stream_pcr), not (bool(options.latlon))
            )
            cell_surface_tile = pcr.pcr2numpy(x_res_tile * y_res_tile, 0)

            # convert meter depth to volume [m3]
            flood_vol = pcr.numpy2pcr(
                pcr.Scalar, flood_meter * cell_surface_tile, fill_value
            )

            # first prepare a basin map, belonging to the lowest order we are looking at
            inundation_pcr = pcr.scalar(stream_pcr) * 0
            for hand_strahler in range(options.catchment_strahler, stream_max + 1, 1):
                # hand_temp_file = os.path.join(flood_folder, 'hand_temp.map')
                if os.path.isfile(
                    os.path.join(
                        options.dest_path,
                        "{:s}_hand_strahler_{:02d}.tif".format(dem_name, hand_strahler),
                    )
                ):
                    hand_file = os.path.join(
                        options.dest_path,
                        "{:s}_hand_strahler_{:02d}.tif".format(dem_name, hand_strahler),
                    )
                else:
                    hand_file = "{:s}_{:02d}.tif".format(
                        options.hand_file_prefix, hand_strahler
                    )
                ds_hand, rasterband_hand = inun_lib.get_gdal_rasterband(hand_file)
                hand = rasterband_hand.ReadAsArray(
                    float(x_start - x_overlap_min),
                    float(y_start - y_overlap_min),
                    int((x_end + x_overlap_max) - (x_start - x_overlap_min)),
                    int((y_end + y_overlap_max) - (y_start - y_overlap_min)),
                )
                print(
                    (
                        "len x-ax: {:d} len y-ax {:d} x-shape {:d} y-shape {:d}".format(
                            len(x_tile_ax), len(y_tile_ax), hand.shape[1], hand.shape[0]
                        )
                    )
                )

                inun_lib.gdal_writemap(
                    hand_temp_file,
                    "PCRaster",
                    x_tile_ax,
                    y_tile_ax,
                    hand,
                    rasterband_hand.GetNoDataValue(),
                    gdal_type=gdal.GDT_Float32,
                    logging=logger,
                )

                hand_pcr = pcr.readmap(hand_temp_file)

                stream_ge_hand, subcatch_hand = inun_lib.subcatch_stream(
                    drainage_pcr, options.catchment_strahler, stream=stream_pcr
                )
                # stream_ge_hand, subcatch_hand = inun_lib.subcatch_stream(drainage_pcr, hand_strahler, stream=stream_pcr)
                stream_ge, subcatch = inun_lib.subcatch_stream(
                    drainage_pcr,
                    options.catchment_strahler,
                    stream=stream_pcr,
                    basin=pcr.boolean(pcr.cover(subcatch_hand, 0)),
                    assign_existing=True,
                    min_strahler=hand_strahler,
                    max_strahler=hand_strahler,
                )  # generate subcatchments, only within basin for HAND
                flood_vol_strahler = pcr.ifthenelse(
                    pcr.boolean(pcr.cover(subcatch, 0)), flood_vol, 0
                )  # mask the flood volume map with the created subcatch map for strahler order = hand_strahler

                inundation_pcr_step = inun_lib.volume_spread(
                    drainage_pcr,
                    hand_pcr,
                    pcr.subcatchment(
                        drainage_pcr, subcatch
                    ),  # to make sure backwater effects can occur from higher order rivers to lower order rivers
                    flood_vol_strahler,
                    volume_thres=0.0,
                    iterations=options.iterations,
                    cell_surface=pcr.numpy2pcr(pcr.Scalar, cell_surface_tile, -9999),
                    logging=logger,
                    order=hand_strahler,
                    neg_HAND=options.neg_HAND,
                )  # 1166400000.
                # use maximum value of inundation_pcr_step and new inundation for higher strahler order
                inundation_pcr = pcr.max(inundation_pcr, inundation_pcr_step)
            inundation = pcr.pcr2numpy(inundation_pcr, -9999.0)
            # cut relevant part
            if y_overlap_max == 0:
                y_overlap_max = -inundation.shape[0]
            if x_overlap_max == 0:
                x_overlap_max = -inundation.shape[1]
            inundation_cut = inundation[
                0 + y_overlap_min : -y_overlap_max, 0 + x_overlap_min : -x_overlap_max
            ]
            # inundation_cut
            if options.out_format == 0:
                band_inun.WriteArray(inundation_cut, float(x_start), float(y_start))
                band_inun.FlushCache()
            else:
                # with netCDF, data is up-side-down.
                inun_lib.write_tile_nc(band_inun, inundation_cut, x_start, y_start)
            # clean up
            os.unlink(flood_vol_temp_file)
            os.unlink(drainage_temp_file)
            os.unlink(hand_temp_file)
            os.unlink(
                stream_temp_file
            )  # also remove temp stream file from output folder

            # if n == 35:
            #     band_inun.SetNoDataValue(-9999.)
            #     ds_inun = None
            #     sys.exit(0)
    # os.unlink(flood_vol_map)

    logger.info("Finalizing {:s}".format(inun_file))
    # add the metadata to the file and band
    # band_inun.SetNoDataValue(-9999.)
    # ds_inun.SetMetadata(metadata_global)
    # band_inun.SetMetadata(metadata_var)
    if options.out_format == 0:
        ds_inun = None
        ds_hand = None
    else:
        ds_inun.close()

    ds_ldd = None
    # rename temporary file to final hand file
    if os.path.isfile(inun_file):
        # remove an old result if available
        os.unlink(inun_file)
    os.rename(inun_file_tmp, inun_file)

    logger.info("Done! Thank you for using hand_contour_inun.py")
    logger, ch = inun_lib.closeLogger(logger, ch)
    del logger, ch
    sys.exit(0)
Example #52
0
    def __init__(self, netcdffile, logging, vars=[]):
        """
        First try to setup a class read netcdf files
        (converted with pcr2netcdf.py)

        netcdffile: file to read the forcing data from
        logging: python logging object
        vars: list of variables to get from file
        """

        self.fname = netcdffile
        if os.path.exists(netcdffile):
            self.dataset = netCDF4.Dataset(netcdffile, mode="r")
        else:
            msg = os.path.abspath(netcdffile) + " not found!"
            logging.error(msg)
            raise ValueError(msg)

        logging.info("Reading state input from netCDF file: " + netcdffile)
        self.alldat = {}
        a = pcr.pcr2numpy(pcr.cover(0.0), 0.0).flatten()
        # Determine steps to load in mem based on estimated memory usage
        floatspermb = 1048576 / 4
        maxmb = 40
        self.maxsteps = maxmb * len(a) / floatspermb + 1
        self.fstep = 0
        self.lstep = self.fstep + self.maxsteps

        self.datetime = self.dataset.variables["time"][:]
        if hasattr(self.dataset.variables["time"], "units"):
            self.timeunits = self.dataset.variables["time"].units
        else:
            self.timeunits = "Seconds since 1970-01-01 00:00:00"
        if hasattr(self.dataset.variables["time"], "calendar"):
            self.calendar = self.dataset.variables["time"].calendar
        else:
            self.calendar = "gregorian"
        self.datetimelist = cftime.num2date(
            self.datetime, self.timeunits, calendar=self.calendar
        )

        try:
            self.x = self.dataset.variables["x"][:]
        except:
            self.x = self.dataset.variables["lon"][:]

        # Now check Y values to see if we must flip the data
        try:
            self.y = self.dataset.variables["y"][:]
        except:
            self.y = self.dataset.variables["lat"][:]

        # test if 1D or 2D array
        if len(self.y.shape) == 1:
            if self.y[0] > self.y[-1]:
                self.flip = False
            else:
                self.flip = True
        else:  # not sure if this works
            self.y = self.y[:][0]
            if self.y[0] > self.y[-1]:
                self.flip = False
            else:
                self.flip = True

        x = pcr.pcr2numpy(pcr.xcoordinate(pcr.boolean(pcr.cover(1.0))), np.nan)[0, :]
        y = pcr.pcr2numpy(pcr.ycoordinate(pcr.boolean(pcr.cover(1.0))), np.nan)[:, 0]

        # Get average cell size
        acc = (
            np.diff(x).mean() * 0.25
        )  # non-exact match needed becuase of possible rounding problems
        if self.flip:
            (self.latidx,) = np.logical_and(
                self.y[::-1] + acc >= y.min(), self.y[::-1] <= y.max() + acc
            ).nonzero()
            (self.lonidx,) = np.logical_and(
                self.x + acc >= x.min(), self.x <= x.max() + acc
            ).nonzero()
        else:
            (self.latidx,) = np.logical_and(
                self.y + acc >= y.min(), self.y <= y.max() + acc
            ).nonzero()
            (self.lonidx,) = np.logical_and(
                self.x + acc >= x.min(), self.x <= x.max() + acc
            ).nonzero()

        if len(self.lonidx) != len(x):
            logging.error("error in determining X coordinates in netcdf...")
            logging.error("model expects: " + str(x.min()) + " to " + str(x.max()))
            logging.error(
                "got coordinates  netcdf: "
                + str(self.x.min())
                + " to "
                + str(self.x.max())
            )
            logging.error(
                "got len from  netcdf x: "
                + str(len(x))
                + " expected "
                + str(len(self.lonidx))
            )
            raise ValueError("X coordinates in netcdf do not match model")

        if len(self.latidx) != len(y):
            logging.error("error in determining Y coordinates in netcdf...")
            logging.error("model expects: " + str(y.min()) + " to " + str(y.max()))
            logging.error(
                "got from  netcdf: " + str(self.y.min()) + " to " + str(self.y.max())
            )
            logging.error(
                "got len from  netcdf y: "
                + str(len(y))
                + " expected "
                + str(len(self.latidx))
            )
            raise ValueError("Y coordinates in netcdf do not match model")

        for var in vars:
            try:
                self.alldat[var] = self.dataset.variables[var][
                    self.fstep : self.maxsteps
                ]
            except:
                self.alldat.pop(var, None)
                logging.warning(
                    "Variable " + var + " not found in netcdf file: " + netcdffile
                )
Example #53
0
def times(calendar):
    import cftime

    return cftime.num2date(
        np.arange(_NT), units='hours since 2000-01-01', calendar=calendar,
        only_use_cftime_datetimes=True)