Esempio n. 1
0
def create_speed_hills(ks, us, sks, sus, **kwargs):
    """
    :type ks: numpy.ndarray
    :type us: numpy.ndarray
    :type sks: numpy.ndarray
    :type sus: numpy.ndarray
    :rtype: list[pyticas_ncrtes.core.etypes.Hill]
    """
    from pyticas_ncrtes.core.etypes import Hill
    kt = kwargs.get('kt', DEFAULT_KT)
    f_merge = kwargs.get('merge', True)
    lsw = setting.SW_30MIN
    lsus = data_util.smooth(us, lsw)
    lsks = data_util.smooth(ks, lsw)

    lsus_minmax_idxs = np.diff(np.sign(np.diff(lsus))).nonzero()[0].tolist()
    lsus_over_kt = np.where(sks > kt)
    lsus_under_kt = np.where(sks <= kt)

    high_k_minmax_idxs = np.intersect1d(lsus_minmax_idxs, lsus_over_kt[0])
    sus_minmax_idxs = np.diff(np.sign(np.diff(sus))).nonzero()[0].tolist()
    low_k_minmax_idxs = np.intersect1d(sus_minmax_idxs, lsus_under_kt[0])

    minmax_idxs = [0] + np.concatenate([low_k_minmax_idxs, high_k_minmax_idxs
                                        ]).tolist() + [len(us) - 1]
    minmax_idxs = np.sort(np.unique(minmax_idxs))

    # remove same trend (decrease or increase)
    tmp = [minmax_idxs[0]]
    for midx in range(1, len(minmax_idxs)):
        if minmax_idxs[midx] - minmax_idxs[midx - 1] <= 2:
            continue
        tmp.append(minmax_idxs[midx])
    minmax_idxs = tmp

    cycles = []
    for idx in range(1, len(minmax_idxs)):
        cycles.append([minmax_idxs[idx - 1], minmax_idxs[idx]])

    if f_merge:
        cycles = merge_cycles(cycles, lsks, lsus)

    hills = []
    for idx, (sidx, eidx) in enumerate(cycles):
        hills.append(
            Hill(idx, sidx, eidx, ks[sidx:eidx], us[sidx:eidx], sks[sidx:eidx],
                 sus[sidx:eidx]))

    return hills
def _smoothing_data(edata):
    """
    :type edata: pyticas_ncrtes.core.etypes.ESTData
    """
    sw = setting.SW_2HOURS
    ssu, ssk = 5, 3

    sus = data_util.smooth(edata.us, sw)
    sks = data_util.smooth(edata.ks, sw)

    qus = data_util.stepping(sus, ssu)
    qks = data_util.stepping(sks, ssk)

    edata.sus = sus
    edata.sks = sks
    edata.qus = np.array(qus)
    edata.qks = np.array(qks)
Esempio n. 3
0
def _sist(edata):
    """ Speed Reduction Start Time

    :param edata:
    :type edata: pyticas_ncrtes.core.etypes.ESTData
    """
    # edata.ncrt = 203
    # faverolles 1/14/2020: set to 45mph default from 30
    CONGESTED_SPEED = 45
    intv30min = setting.INTV30MIN
    intv1h = setting.INTV1HOUR
    # ncrt, qus, sus = edata.ncrt, edata.qus, edata.sus
    qus = edata.qus
    ncrt, sus = edata.ncrt, data_util.smooth(edata.us, setting.SW_1HOUR)
    if not ncrt:
        return

    if sus[ncrt] < CONGESTED_SPEED:
        return

    sist = edata.lst

    # move backward
    for idx in range(ncrt, 0, -1):
        if qus[idx] - qus[idx - 1] >= -1:
            sist = idx
        else:
            break

    # move forward
    for idx in range(sist, ncrt):
        if qus[idx + 1] - qus[idx] >= 1:
            break
        else:
            sist = idx

    # find minimum speed
    sidx, eidx = max(0, sist - intv30min), min(sist + intv30min, len(qus) - 1)
    edata.sist = np.argmin(sus[sidx:eidx]).item() + sidx

    # minimum speed between sist candidate and ncrt
    if edata.sist and edata.ncrt and edata.ncrt - edata.sist > intv30min:
        edata.sist = np.argmin(sus[edata.sist:edata.ncrt]).item() + edata.sist

    # sist cannot be early than lst
    if edata.lst and edata.sist:
        edata.sist = max(edata.lst, edata.sist)

    # sist cannot be later than ncrt
    if edata.sist >= edata.ncrt:
        edata.sist = None
Esempio n. 4
0
def _adjust_with_speed(edata, target_idx):
    """

    :type edata: pyticas_ncrtes.core.etypes.ESTData
    :type target_idx: int
    :rtype: int
    """
    logger = getLogger(__name__)

    sdata = data_util.smooth(edata.us, setting.SW_30MIN)
    qdata = edata.qus  #data_util.stepping(sdata, 2)

    stick = None
    qu = qdata[target_idx]
    for idx in range(target_idx, 0, -1):
        cu = qdata[idx]
        if cu != qu:
            stick = idx
            break

    if not stick:
        return target_idx

    _stick = stick
    for idx in range(stick, 0, -1):
        if sdata[idx] > qu:
            _stick = idx + 1
        else:
            break

    stick = _stick

    # for tidx in range(stick, stick+setting.INTV15MIN):
    #     if sdata[tidx] >= qu:
    #         return tidx + 1

    # never reaches to here
    return stick
Esempio n. 5
0
def _write(num, edata, output_path, prefix=''):
    """

    :type num: int
    :type edata: pyticas_ncrtes.core.etypes.ESTData
    :type output_path: str
    :type prefix: str
    """
    logger = getLogger(__name__)
    if edata is None or not edata.is_loaded:
        logger.debug('  - Data is not loaded : %s' %
                     edata.target_station.station_id if edata else 'N/A')
        return

    sw = setting.SW_3HOURS
    sks, sus = data_util.smooth(edata.ks, sw), data_util.smooth(edata.us, sw)

    hills = hill_helper.create_uk_hills(edata.ks, edata.us, sks, sus)
    ecs = ['#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#0000FF', '#9400D3']

    long_title = '%s (%s[prj_id=%s, s_limit=%d, label=%s], %s)' % (
        edata.snow_event.snow_period.get_date_string(),
        edata.target_station.station_id, edata.snow_route.id
        if edata.snow_route else 'N/A', edata.target_station.s_limit,
        edata.target_station.label, edata.target_station.corridor.name)

    title = '%s (%s, %s)' % (edata.snow_event.snow_period.get_date_string(),
                             edata.target_station.station_id,
                             edata.target_station.corridor.name)

    if REPORT_MODE:
        fig = plt.figure(figsize=(16, 9), dpi=100, facecolor='white')
    else:
        fig = plt.figure(dpi=100, facecolor='white')
    ax1 = plt.subplot(211)
    ax2 = plt.subplot(223)
    ax3 = plt.subplot(224)

    ax1.axhline(y=edata.target_station.s_limit, c='b')
    if edata.wn_ffs:
        ax1.axhline(y=edata.wn_ffs, c='r')

    #ax1.plot(edata.us, marker='', label='Speed', c='#3794D5')
    ax1.plot(edata.ks, marker='', label='Density', c='#ADE2CD')
    # if edata.wn_avg_us is not None:
    #     ax1.plot(edata.wn_avg_us, c='#8C65C5', label='WN Avg. Speed')

    # ax1.plot(edata.qus, c='#746F69', label='Q-Us')

    if edata.normal_func:
        nt_us = edata.normal_func.nighttime_func.speeds(
            edata.snow_event.data_period.get_timeline(as_datetime=True))
    else:
        nt_us = [None] * edata.n_data

    ax1.plot(nt_us, c='k', label='Nighttime Speed')

    #nt_ks = edata.normal_func.nighttime_func.densities(edata.snow_event.data_period.get_timeline(as_datetime=True))
    #ax1.plot(nt_ks, c='k', label='Nighttime Density')

    # snow start and end time
    sstime = edata.snow_event.time_to_index(edata.snow_event.snow_start_time)
    setime = edata.snow_event.time_to_index(edata.snow_event.snow_end_time)
    ax1.axvline(x=sstime, c='#948D90')  # grey vertical line
    ax1.axvline(x=setime, c='#948D90')

    # draw chart using UK-Hills
    n_data = len(edata.us)
    ecs = ['#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#0000FF', '#9400D3']
    markers = ['o', 'x', 'd', '^', '<', '>', 'v', 's', '*', '+']
    ls = '-'
    used = []
    for gidx, hill in enumerate(hills):
        lw = 1
        c = ecs[gidx % len(ecs)]
        marker = '|'
        for midx in range(len(markers)):
            marker_stick = '%s-%s' % (c, markers[midx])
            if marker_stick not in used:
                marker = markers[midx]
                used.append(marker_stick)
                break

        sidx, eidx = hill.sidx, hill.eidx
        _sidx, _eidx = sidx, eidx
        ax2.plot(edata.sks[_sidx:_eidx + 1],
                 edata.sus[_sidx:_eidx + 1] * edata.sks[_sidx:_eidx + 1],
                 c=c,
                 marker=marker,
                 ms=4,
                 zorder=2)
        ax3.plot(edata.sks[_sidx:_eidx + 1],
                 edata.sus[_sidx:_eidx + 1],
                 c=c,
                 marker=marker,
                 ms=4,
                 zorder=2)

        _ks, _us = [None] * len(edata.ks), [None] * len(edata.us)
        for idx in range(sidx, eidx + 1):
            if idx >= n_data - 1:
                break
            # _ks[idx] = edata.merged_sks[idx]
            # _us[idx] = edata.merged_sus[idx]

            _ks[idx] = edata.ks[idx]
            _us[idx] = edata.us[idx]

        ax1.plot(_us, lw=lw, ls=ls, c=c)

    # Normal and Wet-Normal Avg UK
    _recv_ks = np.array(list(range(5, 120)))

    if edata.normal_func:
        uk_function = edata.normal_func.daytime_func.get_uk_function()
        if uk_function and uk_function.is_valid():
            ax2.plot(_recv_ks,
                     np.array(uk_function.speeds(_recv_ks)) * _recv_ks,
                     ls=':',
                     lw=2,
                     label='Normal Avg. QK')
            # ax2.plot(_recv_ks, uk_function.speeds(_recv_ks), ls=':', lw=2, label='Normal Avg. UK')
            ax3.plot(_recv_ks,
                     uk_function.speeds(_recv_ks),
                     ls=':',
                     lw=2,
                     label='Normal Avg. UK')

            if uk_function._wn_uk:
                ax2.plot(_recv_ks,
                         np.array(uk_function.wet_normal_speeds(_recv_ks)) *
                         _recv_ks,
                         ls=':',
                         lw=2,
                         label='Wet-Normal QK')
                # ax2.plot(_recv_ks, uk_function.wet_normal_speeds(_recv_ks, edata.wn_ffs, edata.k_at_wn_ffs), ls=':', lw=2, label='Wet-Normal UK')
                ax3.plot(_recv_ks,
                         uk_function.wet_normal_speeds(_recv_ks),
                         ls=':',
                         lw=2,
                         label='Wet-Normal UK')

    # ax2.scatter(edata.ks, edata.us, c='#5D63FF', marker='.')
    # ax3.scatter(edata.sks, edata.sus, c='#5D63FF', marker='.')

    ms = [14, 13, 12, 11, 10, 8, 7]
    us = data_util.smooth(edata.us, 15)

    # srst
    if edata.srst is not None:
        data = np.array([None] * len(us))
        data[edata.srst] = us[edata.srst]
        ax1.plot(data, marker='o', ms=ms[0], c='#4ED8FF',
                 label='SRST')  # light blue circle

    # lst
    if edata.lst is not None:
        data = np.array([None] * len(us))
        data[edata.lst] = us[edata.lst]
        ax1.plot(data, marker='o', ms=ms[1], c='#FFAD2A',
                 label='LST')  # orange circle

    # sist
    if edata.sist is not None:
        data = np.array([None] * len(us))
        data[edata.sist] = us[edata.sist]
        ax1.plot(data, marker='o', ms=ms[2], c='#68BD20',
                 label='SIST')  # orange green

    # # rst
    # if edata.rst is not None:
    #     data = np.array([None] * len(us))
    #     data[edata.rst] = us[edata.rst]
    #     ax1.plot(data, marker='o', ms=ms[2], c='#68BD20', label='RST')  # green

    # ncrt
    if edata.ncrt is not None:
        data = np.array([None] * len(us))
        data[edata.ncrt] = us[edata.ncrt]
        # ax1.plot(data, marker='o', ms=ms[3], c='#FF0512', label='NCRT (%d)' % edata.ncrt_type)  # navy blue
        ax1.plot(data, marker='o', ms=ms[3], c='#FF0512',
                 label='NCRT')  # navy blue

    # pst
    if edata.pst:
        data = np.array([None] * len(us))
        data[edata.pst] = us[edata.pst]
        ax1.plot(data, marker='o', ms=ms[4], c='#9535CC',
                 label='PST')  # purple diamod

    # sbpst
    # if edata.sbpst:
    #     data = np.array([None] * len(us))
    #     data[edata.sbpst] = us[edata.sbpst]
    #     ax1.plot(data, marker='p', ms=ms[4], c='#bdb76b', label='BPST')  # DarkKhaki
    #
    # # sapst
    # if edata.sapst:
    #     data = np.array([None] * len(us))
    #     data[edata.sapst] = us[edata.sapst]
    #     ax1.plot(data, marker='p', ms=ms[4], c='#cdad00', label='APST')  # gold3

    # nfrt
    if edata.nfrt:
        data = np.array([None] * len(us))
        data[edata.nfrt] = us[edata.nfrt]
        ax1.plot(data, marker='*', ms=ms[4], c='#0072CC',
                 label='NFRT')  # light blue

    # # stable speed area around pst
    # if not edata.ncrt and (edata.sbpst or edata.sapst):
    #     data = np.array([None] * len(us))
    #     if edata.sbpst:
    #         data[edata.sbpst] = us[edata.sbpst]
    #         ax1.plot(data, marker='p', ms=ms[4], c='#FF00F8', label='NCRT-C')  # light-pink pentagon
    #     if edata.sapst and not edata.ncrt:
    #         data[edata.sapst] = us[edata.sapst]
    #         ax1.plot(data, marker='p', ms=ms[4], c='#FF00F8', label='')  # light-pink pentagon

    # snowday-ffs
    # if edata.snowday_ffs:
    #     ax1.axhline(y=edata.snowday_ffs, c='#33CC0A')
    # if edata.nfrt:
    #     data = np.array([None] * len(us))
    #     data[edata.nfrt] = us[edata.nfrt]
    #     ax1.plot(data, marker='*', ms=ms[5], c='#CC00B8', label='SnowDay-FFS')  # navy blue

    # reported time
    for rp in edata.rps:
        data = np.array([None] * len(us))
        data[rp] = us[rp]
        ax1.plot(data, marker='^', ms=ms[6], c='#FF0512',
                 label='RBRT')  # red circle

    box = ax1.get_position()
    ax1.set_position([box.x0, box.y0, box.width * 0.9, box.height])
    ax1.legend(loc='center left',
               bbox_to_anchor=(1, 0.5),
               numpoints=1,
               prop={'size': 12})

    ulist = np.array([90, 100, 110, 120, 150, 200, 300])
    klist = np.array([80, 100, 150, 200, 250, 300, 350, 400, 450, 500, 600])
    qlist = np.array([0, 2000, 3000, 3500, 4000, 4500, 5000, 6000, 7000])
    _maxq = max(edata.sks * edata.sus)
    _maxk = max(edata.sks)
    _maxu = max(edata.sus)
    maxq = qlist[np.where(qlist > _maxq)][0]
    maxu = ulist[np.where(ulist > _maxu)][0]
    maxk = klist[np.where(klist > _maxk)][0]

    ax1.set_title(long_title)
    ax2.set_title(title + ' - Smoothed Q-K')
    ax3.set_title(title + ' - Smoothed U-K')
    ax1.set_ylabel('speed, density')
    ax1.set_xlabel('time')
    ax1.set_ylim(ymin=0, ymax=maxu)
    ax1.set_xlim(xmin=0, xmax=len(edata.sus))
    # ax2.set_ylabel('speed')
    ax2.set_ylabel('flow')
    ax2.set_xlabel('density')
    # ax2.set_ylim(ymin=0, ymax=100)

    ax2.set_ylim(ymin=0, ymax=maxq)
    ax2.set_xlim(xmin=0, xmax=maxk)
    ax3.set_ylim(ymin=0, ymax=maxu)
    ax3.set_xlim(xmin=0, xmax=maxk)
    ax3.set_ylabel('speed')
    ax3.set_xlabel('density')

    ax1.grid()
    ax2.grid()
    ax3.grid()
    ax2.legend(prop={'size': 12})
    ax3.legend(prop={'size': 12})

    if not REPORT_MODE:
        plt.show()
    else:
        import datetime
        timeline = edata.snow_event.data_period.get_timeline(as_datetime=True)
        timeline = [
            t - datetime.timedelta(seconds=setting.DATA_INTERVAL)
            for t in timeline
        ]

        ntimes = [
            t.strftime('%H:%M') for idx, t in enumerate(timeline)
            if idx % (2 * 3600 / setting.DATA_INTERVAL) == 0
        ]
        loc_times = [
            idx for idx, t in enumerate(timeline)
            if idx % (2 * 3600 / setting.DATA_INTERVAL) == 0
        ]
        ax1.set_xticks(loc_times)
        ax1.set_xticklabels(ntimes, rotation=90)

        plt.tight_layout()
        postfix = ' (rp)' if edata.reported_events else ''
        file_path = os.path.join(
            output_path, '(%03d) %s%s%s.png' %
            (num, prefix, edata.target_station.station_id, postfix))
        fig.savefig(file_path, dpi=100, bbox_inches='tight')

    plt.close(fig)

    if 0:
        wb = xlsxwriter.Workbook(
            os.path.join(
                ncrtes.get_infra().get_path('tmp', create=True),
                '%s %s (night).xlsx' %
                (edata.snow_event.snow_period.get_date_string(),
                 edata.target_station.station_id)))
        ws = wb.add_worksheet('night-data')
        prd = edata.snow_event.data_period.clone()
        prd.interval = setting.DATA_INTERVAL

        ws.write_column(0, 0, ['time'] + prd.get_timeline(as_datetime=False))
        ws.write_column(0, 1, ['speed'] + edata.us.tolist())
        ws.write_column(0, 2,
                        ['nighttime average speed'] + edata.night_us.tolist())
        ws.write_column(0, 3, ['smoothed speed'] + edata.sus.tolist())
        # ws.write_column(0, 4, ['smoothed speed (2h)'] + edata.lsus.tolist())
        # ws.write_column(0, 5, ['smoothed speed (1h)'] + edata.llsus.tolist())
        ws.write_column(0, 6,
                        ['nighttime ratio'] + edata.night_ratios.tolist())

        wb.close()
Esempio n. 6
0
def _write_chart_and_data(ddata, uk_origin, Kl, seg_func):
    if not DEBUG_MODE:
        return

    ecs = ['#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#0000FF', '#9400D3']
    cnt = 0

    import matplotlib.pyplot as plt
    plt.figure()

    _us_origin, _ks_origin = data_util.dict2sorted_list(uk_origin)
    ks_origin = np.array(_ks_origin)
    us_origin = np.array(_us_origin)

    plt.scatter(ks_origin, us_origin, marker='x', color='#999999')

    for func in seg_func.funcs:

        # print('> ', func.x1, func.x2, func.line_func, type(func))
        isinstance(func, etypes.FitFunction)
        uf = func.get_function()
        lus = []
        lks = []
        for _k in range(int(func.x1 * 10), int(func.x2 * 10)):
            k = _k / 10.0
            if k > 140:
                break
            lks.append(k)
            lus.append(uf(k))

        c = ecs[cnt % len(ecs)]
        cnt += 1
        # plt.scatter(lks, lus, marker='^', color=c)

    _ks, _us = [], []
    for k in range(10, 200):
        _ks.append(k)
        _us.append(seg_func.speed(k))

    plt.plot(_ks, data_util.smooth(_us, 11), c='b', lw=2)

    plt.xlim(xmin=0, xmax=160)
    plt.ylim(ymin=0, ymax=120)
    plt.title('%s (Kt=%.2f, SL=%s, label=%s)' %
              (ddata.station.station_id, Kl, ddata.station.s_limit,
               ddata.station.label))
    plt.grid()
    periods = sorted(ddata.periods + ddata.not_congested_periods,
                     key=lambda prd: prd.start_date)
    ffpath = _output_path(
        'normal_function', '(%d-%d) %s.png' %
        (periods[0].start_date.year, periods[-1].end_date.year,
         ddata.station.station_id))
    plt.tight_layout()
    plt.savefig(ffpath, dpi=100, bbox_inches='tight')
    # plt.show()
    plt.close()

    import xlsxwriter
    import os
    from pyticas_ncrtes import ncrtes
    infra = ncrtes.get_infra()
    output_dir = infra.get_path('ncrtes/normal-data-set', create=True)
    data_file = os.path.join(
        output_dir, '(%d-%d) %s (func).xlsx' %
        (periods[0].start_date.year, periods[-1].end_date.year,
         ddata.station.station_id))
    wb = xlsxwriter.Workbook(data_file)
    ws = wb.add_worksheet('uk')
    ws.write_column(0, 0, ['k'] + _ks)
    ws.write_column(0, 1, ['u'] + _us)
    wb.close()
Esempio n. 7
0
def _segmented_function(ddata, uk, uk_origin):
    """ make U-K functions as a group of segmented functions (linear functions + log function)

    :type ddata: pyticas_ncrtes.core.etypes.DaytimeData
    :type uk: dict[float, float]
    :type uk_origin: dict[float, float]
    :rtype: etypes.SegmentedFunction
    """
    if uk is None or not any(uk):
        return etypes.SegmentedFunction([], None, None, None)

    # estimate FFS
    smoothing_window_size = 5
    ffs, Kf = ncr_ffs.estimate(uk, smoothing_window_size=smoothing_window_size)

    if ffs < 0:
        return etypes.SegmentedFunction([], None, None, None)

    filtered_uk = uk
    filtered_us, filtered_ks = data_util.dict2sorted_list(filtered_uk)
    filtered_target_uk = filtered_uk
    filtered_target_ks = np.array(filtered_ks)
    filtered_target_us = np.array(filtered_us)

    Kt = max(filtered_target_ks[np.where(filtered_target_us >= ffs)])

    # max_k with FFS
    margink = 1

    wh_around_maxk = np.where((filtered_target_ks <= Kt + margink)
                              & (filtered_target_ks >= Kt - margink))
    median_u_at_kt = np.median(filtered_target_us[wh_around_maxk])

    # prepare avg u-k data
    _ks, _us = [], []
    for _k in range(10, 100):
        avg, stddev, arounds = data_util.avg_y_of_around_x(
            filtered_target_ks, filtered_target_us, _k, 2)
        if avg:
            _ks.append(_k)
            _us.append(avg)
    _ks = np.array(_ks)
    _us = np.array(_us)

    # smoothing
    _sus = data_util.smooth(_us, 11)
    _sus2 = data_util.smooth(_us, 21)

    # find k-range for calibration of log function
    u_under_limit = min(ddata.station.s_limit - 5, 40)
    k_at_lowu = _ks[np.where(_us < u_under_limit)[0][0]]
    wh = np.where((_ks <= k_at_lowu) & (_ks >= Kt))
    _tks, _tus = _ks[wh], _sus[wh]

    if any(_tks):
        vk, vu, d = _find_vertex(_tks, _tus)
    else:
        vk, vu, d = -1, -1, -1

    # set Kth
    if (d < 1 or (vu > 0 and median_u_at_kt - vu > 10)):
        kth, uth = Kt, median_u_at_kt
    else:
        kth, uth = vk, vu

    # log-function calibration
    logfunc = _after_kt(ddata.station, filtered_target_uk, kth)

    # find Kt from average u-k data and calibrated function
    # print('kth=', kth)
    kth2 = max(Kt, vk)

    Kl = _find_func_matching_point(logfunc, kth, kth2, _ks, _sus)
    for _ in range(10):
        kth2 = kth2 + 5
        Kl = _find_func_matching_point(logfunc, kth, kth2, _ks, _sus)
        if Kl is not None:
            break

    # result segmented functions ([0] = FFS function)
    lfunc = etypes.LineFunction((0, ffs), 0, Kf)
    funcs = [lfunc]

    # K_vertex
    if not Kl:
        Kl = Kt + 5
    Kv = vk if vk < Kl else -1

    if Kt < Kv and Kv - Kt > 2:
        # funcs[1] = FFS to Kt
        u_at_kt, _, _ = data_util.avg_y_of_around_x(filtered_target_ks,
                                                    filtered_target_us, Kt, 2)
        popts = _line_func(lfunc.x2, lfunc.get_speed(lfunc.x2), Kt, u_at_kt)
        lfunc = etypes.LineFunction(popts, lfunc.x2, Kt)
        funcs.append(lfunc)

        # funcs[2] = FFS to Kv
        u_at_kv, _, _ = data_util.avg_y_of_around_x(filtered_target_ks,
                                                    filtered_target_us, Kv, 2)
        popts = _line_func(Kt, u_at_kt, Kv, u_at_kv)
        lfunc = etypes.LineFunction(popts, lfunc.x2, Kv)
        funcs.append(lfunc)
    else:
        # funcs[2] = FFS to Kt
        u_at_kt, _, _ = data_util.avg_y_of_around_x(filtered_target_ks,
                                                    filtered_target_us, Kt, 2)
        popts = _line_func(lfunc.x2, lfunc.get_speed(lfunc.x2), Kt, u_at_kt)
        lfunc = etypes.LineFunction(popts, lfunc.x2, Kt)
        funcs.append(lfunc)

    # funcs[3] = Kv to Kl
    u_at_kl = logfunc.get_speed(Kl)
    popts = _line_func(lfunc.x2, lfunc.get_speed(lfunc.x2), Kl, u_at_kl)
    lfunc = etypes.LineFunction(popts, lfunc.x2, Kl)
    funcs.append(lfunc)

    # func[4] = logfunc
    logfunc.x1 = Kl
    logfunc.x2 = 9999
    funcs.append(logfunc)

    seg_func1 = etypes.SegmentedFunction(funcs, Kf, Kt, ffs)

    funcs2 = list(funcs[:2])
    logfunc2 = _after_kt(ddata.station, filtered_target_uk, Kt)

    Kl2 = _find_func_matching_point(logfunc2, Kt, kth2, _ks, _sus)
    for _ in range(10):
        kth2 = kth2 + 5
        Kl2 = _find_func_matching_point(logfunc2, Kt, kth2, _ks, _sus)
        if Kl2 is not None:
            break

    if not Kl2:
        Kl2 = Kt + 5

    lfunc = funcs2[-1]
    u_at_kl2 = logfunc2.get_speed(Kl)
    popts = _line_func(lfunc.x2, lfunc.get_speed(lfunc.x2), Kl2, u_at_kl2)
    lfunc = etypes.LineFunction(popts, lfunc.x2, Kl2)
    funcs2.append(lfunc)

    # func[4] = logfunc
    logfunc2.x1 = Kl2
    logfunc2.x2 = 9999
    funcs2.append(logfunc2)

    seg_func2 = etypes.SegmentedFunction(funcs2, Kf, Kt, ffs)

    checked1, max_diff1 = _check_seg_func(seg_func1, _ks, _sus2)
    checked2, max_diff2 = _check_seg_func(seg_func2, _ks, _sus2)

    if not checked1 and not checked2:
        print('! fail to calibrate function')
        return etypes.SegmentedFunction([], Kf, Kt, ffs)

    seg_func = None

    if not checked1 or not checked2:
        seg_func = seg_func1 if checked1 else seg_func2
    else:
        seg_func = seg_func1 if max_diff1 < max_diff2 else seg_func2

    if seg_func and seg_func.is_valid():
        _write_chart_and_data(ddata, uk_origin, Kl, seg_func)

    return seg_func
def _collect_data_a_day(target_station, prd, dc):
    """ returns k,u data during speed recovery/reduction section on a day

    :type target_station: pyticas.ttypes.RNodeObject
    :type prd: pyticas.ttypes.Period
    :type dc: function
    :rtype: (dict[float, float], dict[float, float], dict[float, float],
             list[float], list[float],
             pyticas.ttypes.RNodeData, pyticas.ttypes.RNodeData,
             list[pyticas_ncrtes.hill.hill.Hill],
             bool)
    """
    global wb

    logger = getLogger(__name__)
    _us, _ks, _qs = _station_data(target_station, prd, dc)

    if not _us:
        return None, None, None, None, None, None, None, None, False

    sw = SW_1HOUR
    us = np.array(_us.data)
    ks = np.array(_ks.data)
    qs = np.array(_qs.data)

    sks = data_util.smooth(ks, sw)
    sus = data_util.smooth(us, sw)
    sqs = data_util.smooth(qs, sw)

    if not any(sus):
        return None, None, None, None, None, None, None, None, False

    recovery_uk, reduction_uk, all_uk = {}, {}, {}
    dranges = _data_collecting_interval(target_station, ks, us, qs, sks, sus, sqs)

    is_not_congested = False
    if not dranges:
        minu_idx = np.argmin(sus).item()
        # toidx = min(minu_idx + setting.INTV2HOUR, len(sus)-1)

        toidx = None
        maxu = sus[minu_idx]
        for idx in range(minu_idx, len(sus)-1):
            if sus[idx] > sus[idx+1] and maxu - sus[idx+1] > 5:
                toidx = idx
                break
            maxu = sus[idx] if sus[idx] > maxu else maxu
        if not toidx:
            toidx = min(minu_idx + setting.INTV1HOUR, len(sus)-1)

        toidx = min(toidx, minu_idx + setting.INTV2HOUR)

        #print(target_station.station_id, prd.get_date_string(), '-> not congested : ', (minu_idx, toidx))

        if toidx < len(sus)-1 or sus[toidx] > target_station.s_limit:
            dranges = [(minu_idx, toidx)]
        is_not_congested = True

    if not dranges:
        return None, None, None, None, None, None, None, None, False

    ###########################

    ws = wb.add_worksheet(prd.get_date_string())
    ws.write_row(0, 0, [' ', 'k', 'u', 'q', 'sk', 'su', 'sq'])
    times = list(range(len(ks)))
    ws.write_column(1, 0, times)
    ws.write_column(1, 1, ks)
    ws.write_column(1, 2, us)
    ws.write_column(1, 3, qs)
    ws.write_column(1, 4, sks)
    ws.write_column(1, 5, sus)
    ws.write_column(1, 6, sqs)

    col = 8
    for (sidx, eidx) in dranges:
        ws.write_column(0, col, ['ks(%d-%d)' % (sidx, eidx)] + ks[sidx:eidx+1].tolist())
        ws.write_column(0, col+1, ['us(%d-%d)' % (sidx, eidx)] + us[sidx:eidx+1].tolist())
        col += 3


    ##############################
    sks2, sus2 = data_util.smooth(ks, 11), data_util.smooth(us, 11)
    # sks2, sus2 = ks, us
    already = []
    for (sidx, eidx) in dranges:
        # print(target_station.station_id, prd.get_date_string(), (sidx, eidx))
        for idx in range(sidx, eidx+1):
            if idx in already:
                continue

            _tk = (sks2[idx] if sks2[idx] not in recovery_uk
                   else _unique_key(sks2[idx], list(recovery_uk.keys())))
            recovery_uk[_tk] = sus2[idx]
            reduction_uk[_tk] = sus2[idx]
            all_uk[_tk] = sus2[idx]

            # _tk = (ks[idx] if ks[idx] not in recovery_uk
            #        else _unique_key(ks[idx], list(recovery_uk.keys())))
            # recovery_uk[_tk] = us[idx]
            # reduction_uk[_tk] = us[idx]
            # all_uk[_tk] = us[idx]

            already.append(idx)


    if SHOW_GRAPH:
        fig = plt.figure(facecolor='white', figsize=(16, 8))
        ax1 = plt.subplot(121)
        ax2 = ax1.twinx()
        ax3 = plt.subplot(122)
        ax1.plot(qs, label='qs')
        ax1.plot(sqs, label='sqs')
        ax2.plot(us, c='#EA8BD6', label='us')
        ax2.plot(sus, c='#CC3137', label='sus')
        ax2.plot(sks, c='#1DCC2E', label='sks')

        for (sidx, eidx) in dranges:
            ax1.axvline(x=sidx, c='g', label='start')
            ax1.axvline(x=eidx, c='r', label='end')

        ecs = ['#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#0000FF', '#9400D3']
        markers = ['o', 'x', 'd', '^', '<', '>', 'v', 's', '*', '+']
        UK.append(recovery_uk)
        used = []
        for idx, _uk in enumerate(UK):
            c = ecs[idx % len(ecs)]
            marker = '|'
            for midx in range(len(markers)):
                marker_stick = '%s-%s' % (c, markers[midx])
                if marker_stick not in used:
                    marker = markers[midx]
                    used.append(marker_stick)
                    break
            _ks = list(_uk.keys())
            _us = [ _uk[_k] for _k in _ks]
            ax3.scatter(_ks, _us, c=c, marker=marker)


        # ax1.axvline(x=maxq_idx, c='g', label='maxq')
        # ax1.axvline(x=minu_idx, c='r', label='minu')
        # if recovered_idx:
        #     ax1.axvline(x=recovered_idx, c='b', label='recovered')
        ax3.set_xlim(xmin=0, xmax=80)
        ax3.set_ylim(ymin=0, ymax=90)

        plt.suptitle('%s (s_limit=%s, period=%s)' % (
            target_station.station_id, target_station.s_limit, prd.get_period_string()))
        ax1.legend()
        plt.grid()

        mng = plt.get_current_fig_manager()
        mng.window.state('zoomed')
        plt.show()
        plt.close(fig)

    return all_uk, recovery_uk, reduction_uk, us, ks, _us, _ks, [], is_not_congested