def test_contour_area_projected(square_verts):
    lon0, lat0 = 0, 0
    dlon, dlat = 1, 1
    verts_proj = rclv.project_vertices(square_verts, lon0, lat0, dlon, dlat)
    region_area, hull_area, convex_def = rclv.contour_area(verts_proj)

    square_area_equator = 123.64311711e8

    np.testing.assert_allclose(region_area, square_area_equator)
    np.testing.assert_allclose(hull_area, square_area_equator)
    np.testing.assert_allclose(convex_def, 0.0)
Exemple #2
0
def test_contour_around_maximum(sample_data_and_maximum):
    psi, ji, psi_max = sample_data_and_maximum

    # we should get an error if the contour intersects the domain boundary
    with pytest.raises(ValueError):
        _ = rclv.find_contour_around_maximum(psi, ji, psi_max + 0.1)

    con, region_data, border_i, border_j = rclv.find_contour_around_maximum(
        psi, ji, psi_max / 2)

    # region data should be normalized to have the center point 0
    assert region_data[border_i[1], border_j[0]] == 0.0
    assert region_data.shape == (sum(border_j) + 1, sum(border_j) + 1)

    # the contour should be closed
    assert rclv.is_contour_closed(con)

    # check size against reference solution
    region_area, hull_area, convex_def = rclv.contour_area(con)
    np.testing.assert_allclose(region_area, 575.02954788959767)
    np.testing.assert_allclose(hull_area, 575.0296629815823)
    assert convex_def == (hull_area - region_area) / region_area
Exemple #3
0
def test_contour_area(square_verts):
    region_area, hull_area, convex_def = rclv.contour_area(square_verts)
    assert region_area == 1.0
    assert hull_area == 1.0
    assert convex_def == 0.0
def filtering(track, year):
    print("----------- year " + str(year) + " START -----------")

    track_iyr = track[track.year == year]
    reserve = []
    plot = False

    # -----------------------------------
    # loop through each LPS track
    for iLPS in track_iyr.index.unique():
        # -----------------------------------
        # print the progress of filtering
        if iLPS % 200 == 0:
            print("year: "+str(year)+"       |    progress:"\
             +str( np.around(100*(iLPS-track_iyr.index.unique()[0])/len(track_iyr.index.unique())) ) )

        # -----------------------------------
        # genesis and terminate location
        track_lon_b, track_lat_b, track_yr, track_mm, track_dd, track_hh, j, k, l = track.loc[
            iLPS].values[0]
        track_lon_e, track_lat_e, track_yr, track_mm, track_dd, track_hh, j, k, l = track.loc[
            iLPS].values[len(track.loc[iLPS].datetime) - 1]

        # to the west of 100E
        if (track_lon_b < 100) | (track_lat_b < 18): continue
        # form over land
        landsea = lsm.sel(lat=track_lat_b, lon=track_lon_b, method='nearest')
        if (landsea < 0.7): continue
        # form below surface
        #orography_of_vortex = orography.sel(lat=track_lat_b,lon=track_lon_b,method='nearest')
        #if orography_of_vortex==1:continue

        # if the system formed poleward of the monsoon domain and moves southward (to filter out mid-to-high latitidue systems)
        if (track_lat_b > bdry_lat[find_nearest(
                bdry_lon, track_lon_b)]) & (track_lat_b > track_lat_e):
            continue

        # cyclones must have four time steps equatorward of the northern boundary of the monsoon domain
        monsoon_step = 0
        for it in np.arange(0, len(track.loc[iLPS].datetime), 1):
            track_lon, track_lat, track_yr, track_mm, track_dd, track_hh, j, k, l = track.loc[
                iLPS].values[it]
            if (track_lat <= bdry_lat[find_nearest(bdry_lon, track_lon)]):
                monsoon_step = monsoon_step + 1
        if monsoon_step < 2: continue

        # -----------------------------------
        # four time steps over land
        t_land = 0.0
        for it in np.arange(0, len(track.loc[iLPS].datetime), 1):
            track_lon, track_lat, track_yr, track_mm, track_dd, track_hh, j, k, l = track.loc[
                iLPS].values[it]
            landsea = lsm.sel(lat=track_lat, lon=track_lon, method='nearest')
            if landsea >= 0.7:
                t_land = t_land + 1.0
            else:
                break
        if (t_land < 4): continue

        # path distance: sum of all great-circule distance between track positions >= 1000km
        travel_distance = 0
        for it in np.arange(0, len(track.loc[iLPS].datetime) - 1, 1):
            track_lon, track_lat, track_yr, track_mm, track_dd, track_hh, j, k, l = track.loc[
                iLPS].values[it]
            track_lon0, track_lat0, track_yr0, track_mm0, track_dd0, track_hh0, j, k, l = track.loc[
                iLPS].values[it + 1]
            travel_distance = travel_distance + \
                            distance_on_unit_sphere(track_lat,track_lon,track_lat0,track_lon0)
        if travel_distance < 1000.00: continue

        # -----------------------------------
        # 3) Loop through each time step
        oro = 0
        contour = None

        for it in np.arange(0, len(track.loc[iLPS].datetime), 1):
            if (contour != None): break

            distance = 99999.9
            slp_min = -1000.0
            exp = None

            track_lon, track_lat, track_yr, track_mm, track_dd, track_hh, j, k, l = track.loc[
                iLPS].values[it]

            # ---------------------------------
            # 1) check monsoon domain and orography
            monsoon_domain = monsoon.sel(lat=track_lat,
                                         lon=track_lon,
                                         method='nearest')
            if monsoon_domain == 0:
                continue

            orography_of_vortex = orography.sel(lat=track_lat,
                                                lon=track_lon,
                                                method='nearest')
            if orography_of_vortex == 1:
                oro = oro + 1
                if oro <= (len(track.loc[iLPS].datetime) / 10.0):
                    continue
                else:
                    contour = None
                    break

            # ---------------------------------
            # 2) read in the SLPA data (relative to 21-day running average)
            time_step = str(track_yr) + "-" + str(track_mm).zfill(
                2) + "-" + str(track_dd).zfill(2) + "T" + str(
                    track_hh) + ":00:00"

            file = "/home/yujia/Data/ERA-Interim/6hourly/slp/0.75/r_21d/T63/" + str(
                track_yr) + ".Asia.nc"
            ds = xr.open_dataset(file)

            slp = ds.slp.sel(time=time_step)
            slp = slp[::-1, :] / 100.0
            slp_iday = slp.load().data

            lat = slp.lat
            lon = slp.lon
            lon2d, lat2d = np.meshgrid(lon, lat)

            ilat = np.abs(lat - track_lat).argmin()
            ilon = np.abs(lon - track_lon).argmin()
            loc = [ilat.values, ilon.values]
            threshold = slp_iday[ilat, ilon]

            # --------------------------------------
            # 3) find a contour surrounding the vortex center and SLPA minimum within the contour <=-2hPa
            try:
                exp = list(rclv.find_contour_around_maximum(-slp_iday,loc,-threshold-0.05,\
                            max_footprint=784,periodic=(True,True)))
                area0, hull0, cd0 = rclv.contour_area(exp[0])
                if area0 > 400: continue
            except ValueError as err:
                continue

            # --------------------------------------
            # is_inside2: double check if the vortex center falls within the contour
            is_inside2 = rclv.point_in_contour(exp[0], [exp[5], exp[4]])
            # --------------------------------------
            # xy_min: SLPA local min <= -?1? hPa (can be multiple minimums)
            xy_min = peak_local_max(exp[1],
                                    min_distance=1,
                                    threshold_abs=1,
                                    exclude_border=0,
                                    indices=True)

            # --------------------------------------
            # find the largest slpa minimum within the contour (if multiple SLPA extrame present)
            for i in xy_min:
                is_inside1 = rclv.point_in_contour(exp[0], i)
                if (is_inside1) & (is_inside2) & (slp_min <= exp[1][i[0],
                                                                    i[1]]):
                    slp_min = exp[1][i[0], i[1]]
                    slp_min_loc = i
                    slp_min_lat = lat[ilat - (exp[4] - i[0])].values
                    slp_min_lon = lon[ilon - (exp[5] - i[1])].values
                    distance = distance_on_unit_sphere(track_lat, track_lon,
                                                       slp_min_lat,
                                                       slp_min_lon)

            # --------------------------------------
            # 4) the distance between vortex center and SLPA min must be less than 500km
            if (distance <= 500.0):
                ilat = np.abs(lat - slp_min_lat).argmin()
                ilon = np.abs(lon - slp_min_lon).argmin()
                loc = [ilat.values, ilon.values]
                localmin = slp_iday[ilat, ilon]
                orography_of_slp = orography.sel(lat=slp_min_lat,
                                                 lon=slp_min_lon,
                                                 method='nearest')
                if orography_of_slp == 1: continue

                track.loc[iLPS].intensity.values[it] = -localmin

                # find convex contour -> DEFINE the boundary of cyclones
                upper_bound = -localmin
                low_bound = np.amax([-localmin + threshold, 1.0])

                max_cd = 0.05
                min_area = 0.0
                min_grad = 0.0
                exp = None
                # find convex contour with the largest mean slpa gradient within the contour
                for deltaP in np.arange(low_bound, upper_bound + 0.1, 0.1):
                    try:
                        exp_tmp = list(rclv.find_contour_around_maximum(-slp_iday,loc,-localmin-deltaP,\
                                       max_footprint=400,periodic=(True,True)))

                        area, hull, cd = rclv.contour_area(exp_tmp[0])
                        xy_min = np.amax(exp_tmp[1])

                        r_mask = np.zeros_like(exp_tmp[1], dtype='bool')
                        r_mask[np.round(exp_tmp[0][:, 0]).astype('int'),
                               np.round(exp_tmp[0][:, 1]).astype('int')] = 1
                        r_mask = ndimage.binary_fill_holes(r_mask)
                        r_mask = ~r_mask
                        exp_tmp[1][r_mask] = None

                        gradient = np.gradient(exp_tmp[1])
                        mag_grad = np.sqrt(gradient[0]**2 + gradient[1]**2)
                        ave_grad = np.nanmean(mag_grad)

                        if (cd > max_cd) & (exp != None):
                            track.loc[iLPS].intensity.values[it] = deltaP
                            break
                        if (-localmin == xy_min) & (cd <= max_cd) & (
                                area >= min_area) & (ave_grad >= min_grad):
                            exp = exp_tmp
                            min_grad = ave_grad
                            track.loc[iLPS].intensity.values[it] = ave_grad
                        #if (-localmin==xy_min)&(cd<=max_cd)&(area>=min_area):
                        #    track.loc[iLPS].intensity.values[it] = deltaP

                    except ValueError as err:
                        continue

                # --------------------------------------
                # 5) if find a contour satisfying the criteria -> compute the averaged gradient within the convex contour
                if exp != None:
                    area, hull, cd = rclv.contour_area(exp[0])

                    r_mask = np.zeros_like(exp[1], dtype='bool')
                    r_mask[np.round(exp[0][:, 0]).astype('int'),
                           np.round(exp[0][:, 1]).astype('int')] = 1
                    r_mask = ndimage.binary_fill_holes(r_mask)

                    # --------------------------------------
                    r_mask = ~r_mask
                    exp[1][r_mask] = None
                    gradient = np.gradient(exp[1])
                    mag_grad = np.sqrt(gradient[0]**2 + gradient[1]**2)
                    ave_grad = np.nanmean(mag_grad)

                    # --------------------------------------
                    # 6) averaged gradient >= 2hPa/6degree (0.25)
                    if (ave_grad >= 0.30):
                        contour = exp

                        if plot:
                            area, hull, cd = rclv.contour_area(contour[0])

                            fig, ax = plt.subplots(figsize=(8, 3), ncols=2)
                            plot = ax[0].pcolormesh(contour[1],
                                                    cmap='Spectral')
                            ax[0].plot(contour[5],
                                       contour[4],
                                       marker="X",
                                       color="red",
                                       markersize=20.0)
                            ax[0].plot(contour[0][:, 1],
                                       contour[0][:, 0],
                                       'r',
                                       linewidth=1.5)

                            ax[1].pcolormesh(mag_grad, cmap='viridis')
                            ax[1].plot(contour[0][:, 1],
                                       contour[0][:, 0],
                                       'r',
                                       linewidth=1.5)
                            ax[1].plot(contour[5],
                                       contour[4],
                                       marker="X",
                                       color="red",
                                       markersize=20.0)

        # ------------------------------------------
        if (contour != None):
            reserve.append([iLPS])

            track.loc[iLPS].closed = 1

            if plot:
                fig, ax = plt.subplots(
                    figsize=(4, 3),
                    subplot_kw={
                        'projection': ccrs.PlateCarree(central_longitude=150)
                    })
                extent = [55, 185, -5, 55]
                lat = track.loc[iLPS].lat.copy()
                lon = track.loc[iLPS].lon.copy()
                if lon.max() - lon.min() > 180:
                    transform = np.where(lon > 180)[0]
                    lon.iloc[transform] = lon.iloc[transform] - 360
                    track_istorm = sgeom.LineString(zip(
                        lon.values, lat.values))
                    ax.add_geometries([track_istorm],
                                      ccrs.PlateCarree(),
                                      facecolor='none',
                                      edgecolor='r',
                                      linewidth=1.5)
                else:
                    track_istorm = sgeom.LineString(zip(
                        lon.values, lat.values))
                    ax.add_geometries([track_istorm],
                                      ccrs.PlateCarree(),
                                      facecolor='none',
                                      edgecolor='r',
                                      linewidth=1.5)

                    ax.coastlines()
                    ax.stock_img()
                    ax.set_extent(extent)
                    ax.set_title(str(iLPS))

    print("----------- year " + str(year) + " END -----------")
    return reserve