def project_latlng_to_xy(polygon_object_latlng,
                         projection_object=None,
                         false_easting_metres=0,
                         false_northing_metres=0.):
    """Converts polygon from lat-long to x-y coordinates.

    :param polygon_object_latlng: `shapely.geometry.Polygon` object with
        vertices in lat-long coordinates.
    :param projection_object: `pyproj.Proj` object.  If None, this method will
        create an azimuthal equidistant projection centered at the polygon
        centroid.
    :param false_easting_metres: False easting (will be added to all x-
        coordinates).
    :param false_northing_metres: False northing (will be added to all y-
        coordinates).
    :return: polygon_object_xy_metres: `shapely.geometry.Polygon` object with
        vertices in x-y coordinates.
    :return: projection_object: `pyproj.Proj` object.  If input was defined,
        this is simply the input object.  If input was None, this is the object
        created on the fly.
    """

    if projection_object is None:
        centroid_object_latlng = polygon_object_latlng.centroid
        projection_object = projections.init_azimuthal_equidistant_projection(
            centroid_object_latlng.y, centroid_object_latlng.x)
        false_easting_metres = 0.
        false_northing_metres = 0.

    vertex_dict = polygon_object_to_vertex_arrays(polygon_object_latlng)
    vertex_dict[EXTERIOR_X_COLUMN], vertex_dict[EXTERIOR_Y_COLUMN] = (
        projections.project_latlng_to_xy(
            vertex_dict[EXTERIOR_Y_COLUMN],
            vertex_dict[EXTERIOR_X_COLUMN],
            projection_object=projection_object,
            false_easting_metres=false_easting_metres,
            false_northing_metres=false_northing_metres))

    num_holes = len(vertex_dict[HOLE_X_COLUMN])
    for i in range(num_holes):
        vertex_dict[HOLE_X_COLUMN][i], vertex_dict[HOLE_Y_COLUMN][i] = (
            projections.project_latlng_to_xy(
                vertex_dict[HOLE_Y_COLUMN][i],
                vertex_dict[HOLE_X_COLUMN][i],
                projection_object=projection_object,
                false_easting_metres=false_easting_metres,
                false_northing_metres=false_northing_metres))

    if num_holes == 0:
        polygon_object_xy = vertex_arrays_to_polygon_object(
            vertex_dict[EXTERIOR_X_COLUMN], vertex_dict[EXTERIOR_Y_COLUMN])
    else:
        polygon_object_xy = vertex_arrays_to_polygon_object(
            vertex_dict[EXTERIOR_X_COLUMN],
            vertex_dict[EXTERIOR_Y_COLUMN],
            hole_x_coords_list=vertex_dict[HOLE_X_COLUMN],
            hole_y_coords_list=vertex_dict[HOLE_Y_COLUMN])

    return polygon_object_xy, projection_object
def _project_storms_latlng_to_xy(storm_object_table, projection_object):
    """Projects storm objects from lat-long to x-y coordinates.

    This method projects both centroids and outlines.

    V = number of vertices in a given storm object

    :param storm_object_table: pandas DataFrame created by _read_storm_tracks.
    :param projection_object: Instance of `pyproj.Proj`, created by
        _init_azimuthal_equidistant_projection.
    :return: storm_object_table: Same as input, but with additional columns
        listed below.
    storm_object_table.centroid_x_metres: x-coordinate of centroid.
    storm_object_table.centroid_y_metres: y-coordinate of centroid.
    storm_object_table.vertices_x_metres: length-V numpy array with x-
        coordinates of vertices.
    storm_object_table.vertices_y_metres: length-V numpy array with y-
        coordinates of vertices.
    """

    (centroids_x_metres,
     centroids_y_metres) = projections.project_latlng_to_xy(
         storm_object_table[tracking_io.CENTROID_LAT_COLUMN].values,
         storm_object_table[tracking_io.CENTROID_LNG_COLUMN].values,
         projection_object=projection_object)

    nested_array = storm_object_table[[
        tracking_io.STORM_ID_COLUMN, tracking_io.STORM_ID_COLUMN
    ]].values.tolist()
    argument_dict = {
        CENTROID_X_COLUMN: centroids_x_metres,
        CENTROID_Y_COLUMN: centroids_y_metres,
        VERTICES_X_COLUMN: nested_array,
        VERTICES_Y_COLUMN: nested_array
    }
    storm_object_table = storm_object_table.assign(**argument_dict)

    num_storm_objects = len(storm_object_table.index)
    for i in range(num_storm_objects):
        this_vertex_dict_latlng = (polygons.polygon_object_to_vertex_arrays(
            storm_object_table[
                tracking_io.POLYGON_OBJECT_LATLNG_COLUMN].values[i]))

        (storm_object_table[VERTICES_X_COLUMN].values[i],
         storm_object_table[VERTICES_Y_COLUMN].values[i]) = (
             projections.project_latlng_to_xy(
                 this_vertex_dict_latlng[polygons.EXTERIOR_Y_COLUMN],
                 this_vertex_dict_latlng[polygons.EXTERIOR_X_COLUMN],
                 projection_object=projection_object))

    return storm_object_table
def project_latlng_to_xy(latitudes_deg,
                         longitudes_deg,
                         projection_object=None,
                         model_name=None,
                         grid_name=None):
    """Converts points from lat-long to x-y (model) coordinates.

    P = number of points

    :param latitudes_deg: length-P numpy array of latitudes (deg N).
    :param longitudes_deg: length-P numpy array of longitudes (deg E).
    :param projection_object: Instance of `pyproj.Proj`, specifying the
        projection.  If None, will create projection object on the fly.
    :param model_name: See doc for `check_grid_name`.
    :param grid_name: Same.
    :return: x_coords_metres: length-P numpy array of x-coordinates.
    :return: y_coords_metres: length-P numpy array of x-coordinates.
    """

    false_easting_metres, false_northing_metres = (
        get_false_easting_and_northing(model_name=model_name,
                                       grid_name=grid_name))

    if projection_object is None:
        projection_object = init_projection(model_name)

    return projections.project_latlng_to_xy(
        latitudes_deg=latitudes_deg,
        longitudes_deg=longitudes_deg,
        projection_object=projection_object,
        false_easting_metres=false_easting_metres,
        false_northing_metres=false_northing_metres)
示例#4
0
def project_latlng_to_xy(latitudes_deg,
                         longitudes_deg,
                         projection_object=None,
                         model_name=None,
                         grid_id=None):
    """Converts from lat-long to x-y coordinates (under model projection).

    P = number of points to convert

    :param latitudes_deg: length-P numpy array of latitudes (deg N).
    :param longitudes_deg: length-P numpy array of longitudes (deg E).
    :param projection_object: Projection object created by
        init_model_projection.  If projection_object = None, it will be created
        on the fly, based on args `model_name` and `grid_id`.
    :param model_name: Name of model.
    :param grid_id: ID for model grid.
    :return: x_coords_metres: length-P numpy array of x-coordinates.
    :return: y_coords_metres: length-P numpy array of y-coordinates.
    """

    false_easting_metres, false_northing_metres = (
        get_false_easting_and_northing(model_name, grid_id))
    if projection_object is None:
        projection_object = init_model_projection(model_name)

    return projections.project_latlng_to_xy(
        latitudes_deg,
        longitudes_deg,
        projection_object=projection_object,
        false_easting_metres=false_easting_metres,
        false_northing_metres=false_northing_metres)
def _get_projection_offsets(
        basemap_object, pyproj_object, test_latitudes_deg, test_longitudes_deg):
    """Finds offsets between basemap and pyproj projections.

    P = number of points used to find offsets

    :param basemap_object: Instance of `mpl_toolkits.basemap.Basemap`.
    :param pyproj_object: Instance of `pyproj.Proj`.  The two objects should
        encode the same projection, just with different false easting/northing.
    :param test_latitudes_deg: length-P numpy array of latitudes (deg N).
    :param test_longitudes_deg: length-P numpy array of longitudes (deg E).
    :return: x_offset_metres: x-offset (basemap minus pyproj).
    :return: y_offset_metres: y-offset (basemap minus pyproj).
    """

    pyproj_x_coords_metres, pyproj_y_coords_metres = (
        projections.project_latlng_to_xy(
            latitudes_deg=test_latitudes_deg,
            longitudes_deg=test_longitudes_deg, projection_object=pyproj_object)
    )

    basemap_x_coords_metres, basemap_y_coords_metres = basemap_object(
        test_longitudes_deg, test_latitudes_deg)

    x_offset_metres = numpy.mean(
        basemap_x_coords_metres - pyproj_x_coords_metres
    )
    y_offset_metres = numpy.mean(
        basemap_y_coords_metres - pyproj_y_coords_metres
    )

    return x_offset_metres, y_offset_metres
示例#6
0
    def test_project_latlng_to_xy(self):
        """Ensures that project_latlng_to_xy does not crash.

        This is an integration test, not a unit test, because it requires
        init_lambert_conformal_projection to create the projection object.
        """

        projection_object = projections.init_lambert_conformal_projection(
            STANDARD_LATITUDES_DEG, CENTRAL_LONGITUDE_DEG)

        projections.project_latlng_to_xy(
            LATITUDES_DEG,
            LONGITUDES_DEG,
            projection_object=projection_object,
            false_easting_metres=FALSE_EASTING_METRES,
            false_northing_metres=FALSE_NORTHING_METRES)
示例#7
0
def _project_polygon_latlng_to_xy(polygon_object_latlng,
                                  centroid_latitude_deg=None,
                                  centroid_longitude_deg=None):
    """Projects polygon from lat-long to x-y coordinates.

    :param polygon_object_latlng: Instance of `shapely.geometry.Polygon`, where
        x-coordinates are actually longitudes and y-coordinates are actually
        latitudes.
    :param centroid_latitude_deg: Latitude (deg N) at polygon centroid.
    :param centroid_longitude_deg: Longitude (deg E) at polygon centroid.
    :return: polygon_object_xy: Instance of `shapely.geometry.Polygon`, where x-
        and y-coordinates are in metres.
    """

    projection_object = projections.init_lambert_conformal_projection(
        standard_latitudes_deg=numpy.array(
            [centroid_latitude_deg, centroid_latitude_deg]),
        central_longitude_deg=centroid_longitude_deg)

    vertex_latitudes_deg = numpy.asarray(polygon_object_latlng.exterior.xy[1])
    vertex_longitudes_deg = numpy.asarray(polygon_object_latlng.exterior.xy[0])
    vertex_x_metres, vertex_y_metres = projections.project_latlng_to_xy(
        vertex_latitudes_deg,
        vertex_longitudes_deg,
        projection_object=projection_object,
        false_easting_metres=0.,
        false_northing_metres=0.)

    return polygons.vertex_arrays_to_polygon_object(vertex_x_metres,
                                                    vertex_y_metres)
示例#8
0
    def test_project_latlng_to_xy(self):
        """Ensures that project_latlng_to_xy does not crash.

        This is an integration test, not a unit test, because it requires
        init_lcc_projection to create the projection object.
        """

        projection_object = projections.init_lcc_projection(
            standard_latitudes_deg=STANDARD_LATITUDES_DEG,
            central_longitude_deg=CENTRAL_LONGITUDE_DEG)

        projections.project_latlng_to_xy(
            latitudes_deg=LATITUDES_DEG,
            longitudes_deg=LONGITUDES_DEG,
            projection_object=projection_object,
            false_easting_metres=FALSE_EASTING_METRES,
            false_northing_metres=FALSE_NORTHING_METRES)
def _project_winds_latlng_to_xy(wind_table, projection_object):
    """Projects wind observations from lat-long to x-y coordinates.

    :param wind_table: pandas DataFrame created by _read_wind_observations.
    :param projection_object: Instance of `pyproj.Proj`, created by
        _init_azimuthal_equidistant_projection.
    :return: wind_table: Same as input, but with additional columns listed
        below.
    wind_table.x_coord_metres: x-coordinate.
    wind_table.y_coord_metres: y-coordinate.
    """

    x_coords_metres, y_coords_metres = projections.project_latlng_to_xy(
        wind_table[raw_wind_io.LATITUDE_COLUMN].values,
        wind_table[raw_wind_io.LONGITUDE_COLUMN].values,
        projection_object=projection_object)

    argument_dict = {
        WIND_X_COLUMN: x_coords_metres,
        WIND_Y_COLUMN: y_coords_metres
    }
    return wind_table.assign(**argument_dict)
示例#10
0
    def test_project_both_ways(self):
        """Ensures that the two projection methods are inverses.

        This is an integration test, not a unit test, because it calls both
        projection methods.  Also, it requires init_lcc_projection
        to create the projection object.
        """

        projection_object = projections.init_lcc_projection(
            standard_latitudes_deg=STANDARD_LATITUDES_DEG,
            central_longitude_deg=CENTRAL_LONGITUDE_DEG)

        these_x_coords_metres, these_y_coords_metres = (
            projections.project_latlng_to_xy(
                latitudes_deg=LATITUDES_DEG,
                longitudes_deg=LONGITUDES_DEG,
                projection_object=projection_object,
                false_easting_metres=FALSE_EASTING_METRES,
                false_northing_metres=FALSE_NORTHING_METRES))

        these_latitudes_deg, these_longitudes_deg = (
            projections.project_xy_to_latlng(
                x_coords_metres=these_x_coords_metres,
                y_coords_metres=these_y_coords_metres,
                projection_object=projection_object,
                false_easting_metres=FALSE_EASTING_METRES,
                false_northing_metres=FALSE_NORTHING_METRES))

        self.assertTrue(
            numpy.allclose(these_latitudes_deg,
                           LATITUDES_DEG,
                           atol=TOLERANCE,
                           equal_nan=True))
        self.assertTrue(
            numpy.allclose(these_longitudes_deg,
                           LONGITUDES_DEG,
                           atol=TOLERANCE,
                           equal_nan=True))
示例#11
0
    temporal_tracking.LATITUDES_KEY: LOCAL_MAX_LATITUDES_DEG,
    temporal_tracking.LONGITUDES_KEY: LOCAL_MAX_LONGITUDES_DEG,
    echo_top_tracking.MAX_VALUES_KEY: LOCAL_MAX_VALUES
}

# The following constants are used to test _remove_redundant_local_maxima.
SMALL_INTERMAX_DISTANCE_METRES = 1000.
LARGE_INTERMAX_DISTANCE_METRES = 10000.

PROJECTION_OBJECT = projections.init_azimuthal_equidistant_projection(
    central_latitude_deg=35., central_longitude_deg=95.)

LOCAL_MAX_X_COORDS_METRES, LOCAL_MAX_Y_COORDS_METRES = (
    projections.project_latlng_to_xy(LOCAL_MAX_LATITUDES_DEG,
                                     LOCAL_MAX_LONGITUDES_DEG,
                                     projection_object=PROJECTION_OBJECT,
                                     false_easting_metres=0.,
                                     false_northing_metres=0.))

LOCAL_MAX_DICT_SMALL_DISTANCE = {
    temporal_tracking.LATITUDES_KEY: LOCAL_MAX_LATITUDES_DEG,
    temporal_tracking.LONGITUDES_KEY: LOCAL_MAX_LONGITUDES_DEG,
    echo_top_tracking.MAX_VALUES_KEY: LOCAL_MAX_VALUES,
    temporal_tracking.X_COORDS_KEY: LOCAL_MAX_X_COORDS_METRES,
    temporal_tracking.Y_COORDS_KEY: LOCAL_MAX_Y_COORDS_METRES
}

LOCAL_MAX_DICT_LARGE_DISTANCE = {
    temporal_tracking.LATITUDES_KEY: LOCAL_MAX_LATITUDES_DEG[:-1],
    temporal_tracking.LONGITUDES_KEY: LOCAL_MAX_LONGITUDES_DEG[:-1],
    echo_top_tracking.MAX_VALUES_KEY: LOCAL_MAX_VALUES[:-1],
def _run(input_file_name, top_tracking_dir_name, min_latitude_deg,
         max_latitude_deg, min_longitude_deg, max_longitude_deg,
         grid_spacing_metres, output_dir_name):
    """Subsets ungridded predictions by space.

    This is effectively the main method.

    :param input_file_name: See documentation at top of file.
    :param top_tracking_dir_name: Same.
    :param min_latitude_deg: Same.
    :param max_latitude_deg: Same.
    :param min_longitude_deg: Same.
    :param max_longitude_deg: Same.
    :param grid_spacing_metres: Same.
    :param output_dir_name: Same.
    """

    equidistant_grid_dict = grids.create_equidistant_grid(
        min_latitude_deg=min_latitude_deg, max_latitude_deg=max_latitude_deg,
        min_longitude_deg=min_longitude_deg,
        max_longitude_deg=max_longitude_deg,
        x_spacing_metres=grid_spacing_metres,
        y_spacing_metres=grid_spacing_metres, azimuthal=False)

    grid_metafile_name = grids.find_equidistant_metafile(
        directory_name=output_dir_name, raise_error_if_missing=False)

    print('Writing metadata for equidistant grid to: "{0:s}"...'.format(
        grid_metafile_name
    ))

    grids.write_equidistant_metafile(grid_dict=equidistant_grid_dict,
                                     pickle_file_name=grid_metafile_name)

    grid_point_x_coords_metres = equidistant_grid_dict[grids.X_COORDS_KEY]
    grid_point_y_coords_metres = equidistant_grid_dict[grids.Y_COORDS_KEY]
    projection_object = equidistant_grid_dict[grids.PROJECTION_KEY]

    grid_edge_x_coords_metres = numpy.append(
        grid_point_x_coords_metres - 0.5 * grid_spacing_metres,
        grid_point_x_coords_metres[-1] + 0.5 * grid_spacing_metres
    )
    grid_edge_y_coords_metres = numpy.append(
        grid_point_y_coords_metres - 0.5 * grid_spacing_metres,
        grid_point_y_coords_metres[-1] + 0.5 * grid_spacing_metres
    )

    print('Reading input data from: "{0:s}"...'.format(input_file_name))
    prediction_dict = prediction_io.read_ungridded_predictions(input_file_name)
    print(SEPARATOR_STRING)

    full_id_strings = prediction_dict[prediction_io.STORM_IDS_KEY]
    storm_times_unix_sec = prediction_dict[prediction_io.STORM_TIMES_KEY]
    unique_storm_times_unix_sec = numpy.unique(storm_times_unix_sec)

    num_storm_objects = len(storm_times_unix_sec)
    storm_latitudes_deg = numpy.full(num_storm_objects, numpy.nan)
    storm_longitudes_deg = numpy.full(num_storm_objects, numpy.nan)

    for this_time_unix_sec in unique_storm_times_unix_sec:
        these_indices = numpy.where(
            storm_times_unix_sec == this_time_unix_sec
        )[0]
        these_full_id_strings = [full_id_strings[k] for k in these_indices]

        (storm_latitudes_deg[these_indices],
         storm_longitudes_deg[these_indices]
        ) = _read_storm_locations_one_time(
            top_tracking_dir_name=top_tracking_dir_name,
            valid_time_unix_sec=this_time_unix_sec,
            desired_full_id_strings=these_full_id_strings)

    print(SEPARATOR_STRING)

    storm_x_coords_metres, storm_y_coords_metres = (
        projections.project_latlng_to_xy(
            latitudes_deg=storm_latitudes_deg,
            longitudes_deg=storm_longitudes_deg,
            projection_object=projection_object)
    )

    num_grid_rows = len(grid_point_y_coords_metres)
    num_grid_columns = len(grid_point_x_coords_metres)

    for i in range(num_grid_rows):
        for j in range(num_grid_columns):
            these_indices = grids.find_events_in_grid_cell(
                event_x_coords_metres=storm_x_coords_metres,
                event_y_coords_metres=storm_y_coords_metres,
                grid_edge_x_coords_metres=grid_edge_x_coords_metres,
                grid_edge_y_coords_metres=grid_edge_y_coords_metres,
                row_index=i, column_index=j, verbose=True)

            if len(these_indices) == 0:
                continue

            this_prediction_dict = prediction_io.subset_ungridded_predictions(
                prediction_dict=prediction_dict,
                desired_storm_indices=these_indices)

            this_output_file_name = prediction_io.find_ungridded_file(
                directory_name=output_dir_name, grid_row=i, grid_column=j,
                raise_error_if_missing=False)

            print('Writing subset to: "{0:s}"...'.format(this_output_file_name))

            prediction_io.write_ungridded_predictions(
                netcdf_file_name=this_output_file_name,
                class_probability_matrix=this_prediction_dict[
                    prediction_io.PROBABILITY_MATRIX_KEY],
                storm_ids=this_prediction_dict[prediction_io.STORM_IDS_KEY],
                storm_times_unix_sec=this_prediction_dict[
                    prediction_io.STORM_TIMES_KEY],
                observed_labels=this_prediction_dict[
                    prediction_io.OBSERVED_LABELS_KEY],
                target_name=this_prediction_dict[prediction_io.TARGET_NAME_KEY],
                model_file_name=this_prediction_dict[
                    prediction_io.MODEL_FILE_KEY]
            )

            print('\n')
def create_distance_buffers(storm_object_table, min_distances_metres,
                            max_distances_metres):
    """Creates one or more distance buffers around each storm object.

    K = number of buffers

    :param storm_object_table: pandas DataFrame with the following columns.
        Each row is one storm object.
    storm_object_table.centroid_latitude_deg: Latitude (deg N) of storm-object
        centroid.
    storm_object_table.centroid_longitude_deg: Longitude (deg E) of storm-object
        centroid.
    storm_object_table.polygon_object_latlng_deg: Instance of
        `shapely.geometry.Polygon`, with x-coords in longitude (deg E) and
        y-coords in latitude (deg N).

    :param min_distances_metres: length-K numpy array of minimum distances.  If
        the storm object is inside the [k]th buffer -- i.e., the [k]th buffer
        has no minimum distance -- then min_distances_metres[k] should be NaN.
    :param max_distances_metres: length-K numpy array of max distances.
    :return: storm_object_table: Same as input but with K additional columns
        (one per distance buffer).  Column names are generated by
        `buffer_to_column_name`, and each value in these columns is a
        `shapely.geometry.Polygon` object, with x-coords in longitude (deg E) and
        y-coords in latitude (deg N).
    """

    num_buffers = len(min_distances_metres)
    these_expected_dim = numpy.array([num_buffers], dtype=int)
    error_checking.assert_is_numpy_array(max_distances_metres,
                                         exact_dimensions=these_expected_dim)

    global_centroid_lat_deg, global_centroid_lng_deg = (
        geodetic_utils.get_latlng_centroid(
            latitudes_deg=storm_object_table[CENTROID_LATITUDE_COLUMN].values,
            longitudes_deg=storm_object_table[CENTROID_LONGITUDE_COLUMN].values
        ))

    projection_object = projections.init_azimuthal_equidistant_projection(
        central_latitude_deg=global_centroid_lat_deg,
        central_longitude_deg=global_centroid_lng_deg)

    num_storm_objects = len(storm_object_table.index)
    object_array = numpy.full(num_storm_objects, numpy.nan, dtype=object)
    buffer_column_names = [''] * num_buffers

    for j in range(num_buffers):
        buffer_column_names[j] = buffer_to_column_name(
            min_distance_metres=min_distances_metres[j],
            max_distance_metres=max_distances_metres[j])

        storm_object_table = storm_object_table.assign(
            **{buffer_column_names[j]: object_array})

    for i in range(num_storm_objects):
        this_orig_vertex_dict_latlng_deg = (
            polygons.polygon_object_to_vertex_arrays(
                storm_object_table[LATLNG_POLYGON_COLUMN].values[i]))

        these_orig_x_metres, these_orig_y_metres = (
            projections.project_latlng_to_xy(
                latitudes_deg=this_orig_vertex_dict_latlng_deg[
                    polygons.EXTERIOR_Y_COLUMN],
                longitudes_deg=this_orig_vertex_dict_latlng_deg[
                    polygons.EXTERIOR_X_COLUMN],
                projection_object=projection_object))

        for j in range(num_buffers):
            this_buffer_poly_object_xy_metres = polygons.buffer_simple_polygon(
                vertex_x_metres=these_orig_x_metres,
                vertex_y_metres=these_orig_y_metres,
                min_buffer_dist_metres=min_distances_metres[j],
                max_buffer_dist_metres=max_distances_metres[j])

            this_buffer_vertex_dict = polygons.polygon_object_to_vertex_arrays(
                this_buffer_poly_object_xy_metres)

            (this_buffer_vertex_dict[polygons.EXTERIOR_Y_COLUMN],
             this_buffer_vertex_dict[polygons.EXTERIOR_X_COLUMN]
             ) = projections.project_xy_to_latlng(
                 x_coords_metres=this_buffer_vertex_dict[
                     polygons.EXTERIOR_X_COLUMN],
                 y_coords_metres=this_buffer_vertex_dict[
                     polygons.EXTERIOR_Y_COLUMN],
                 projection_object=projection_object)

            this_num_holes = len(
                this_buffer_vertex_dict[polygons.HOLE_X_COLUMN])

            for k in range(this_num_holes):
                (this_buffer_vertex_dict[polygons.HOLE_Y_COLUMN][k],
                 this_buffer_vertex_dict[polygons.HOLE_X_COLUMN][k]
                 ) = projections.project_xy_to_latlng(
                     x_coords_metres=this_buffer_vertex_dict[
                         polygons.HOLE_X_COLUMN][k],
                     y_coords_metres=this_buffer_vertex_dict[
                         polygons.HOLE_Y_COLUMN][k],
                     projection_object=projection_object)

            this_buffer_poly_object_latlng_deg = (
                polygons.vertex_arrays_to_polygon_object(
                    exterior_x_coords=this_buffer_vertex_dict[
                        polygons.EXTERIOR_X_COLUMN],
                    exterior_y_coords=this_buffer_vertex_dict[
                        polygons.EXTERIOR_Y_COLUMN],
                    hole_x_coords_list=this_buffer_vertex_dict[
                        polygons.HOLE_X_COLUMN],
                    hole_y_coords_list=this_buffer_vertex_dict[
                        polygons.HOLE_Y_COLUMN]))

            storm_object_table[buffer_column_names[j]].values[i] = (
                this_buffer_poly_object_latlng_deg)

    return storm_object_table
示例#14
0
def get_latlng_grid_points_in_radius(
        test_latitude_deg, test_longitude_deg, effective_radius_metres,
        grid_point_latitudes_deg=None, grid_point_longitudes_deg=None,
        grid_point_dict=None):
    """Finds lat-long grid points within radius of test point.

    One of the following sets of input args must be specified:

    - grid_point_latitudes_deg and grid_point_longitudes_deg
    - grid_point_dict

    M = number of rows (unique grid-point latitudes)
    N = number of columns (unique grid-point longitudes)
    K = number of grid points within radius of test point

    :param test_latitude_deg: Latitude (deg N) of test point.
    :param test_longitude_deg: Longitude (deg E) of test point.
    :param effective_radius_metres: Effective radius (will find all grid points
        within this radius of test point).
    :param grid_point_latitudes_deg: length-M numpy array with latitudes (deg N)
        of grid points.
    :param grid_point_longitudes_deg: length-N numpy array with longitudes
        (deg E) of grid points.
    :param grid_point_dict: Dictionary created by a previous run of this method
        (see output documentation).
    :return: rows_in_radius: length-K numpy array with row indices of grid
        points near test point.
    :return: columns_in_radius: Same but for columns.
    :return: grid_point_dict: Dictionary with the following keys.
    grid_point_dict['grid_point_x_matrix_metres']: M-by-N numpy array with
        x-coordinates of grid points.
    grid_point_dict['grid_point_y_matrix_metres']: M-by-N numpy array with
        y-coordinates of grid points.
    grid_point_dict['projection_object']: Instance of `pyproj.Proj`, which can
        be used to convert future test points from lat-long to x-y coordinates.
    """

    if grid_point_dict is None:
        (grid_point_lat_matrix_deg, grid_point_lng_matrix_deg
        ) = latlng_vectors_to_matrices(
            unique_latitudes_deg=grid_point_latitudes_deg,
            unique_longitudes_deg=grid_point_longitudes_deg)

        projection_object = projections.init_azimuthal_equidistant_projection(
            central_latitude_deg=numpy.mean(grid_point_latitudes_deg),
            central_longitude_deg=numpy.mean(grid_point_longitudes_deg))

        (grid_point_x_matrix_metres, grid_point_y_matrix_metres
        ) = projections.project_latlng_to_xy(
            latitudes_deg=grid_point_lat_matrix_deg,
            longitudes_deg=grid_point_lng_matrix_deg,
            projection_object=projection_object)

        grid_point_dict = {
            GRID_POINT_X_MATRIX_KEY: grid_point_x_matrix_metres,
            GRID_POINT_Y_MATRIX_KEY: grid_point_y_matrix_metres,
            PROJECTION_OBJECT_KEY: projection_object
        }

    error_checking.assert_is_valid_latitude(test_latitude_deg)
    error_checking.assert_is_geq(effective_radius_metres, 0.)
    test_longitude_deg = lng_conversion.convert_lng_positive_in_west(
        longitudes_deg=numpy.array([test_longitude_deg]), allow_nan=False)[0]

    (test_x_coords_metres, test_y_coords_metres
    ) = projections.project_latlng_to_xy(
        latitudes_deg=numpy.array([test_latitude_deg]),
        longitudes_deg=numpy.array([test_longitude_deg]),
        projection_object=grid_point_dict[PROJECTION_OBJECT_KEY])
    test_x_coord_metres = test_x_coords_metres[0]
    test_y_coord_metres = test_y_coords_metres[0]

    valid_x_flags = numpy.absolute(
        grid_point_dict[GRID_POINT_X_MATRIX_KEY] - test_x_coord_metres
    ) <= effective_radius_metres
    valid_y_flags = numpy.absolute(
        grid_point_dict[GRID_POINT_Y_MATRIX_KEY] - test_y_coord_metres
    ) <= effective_radius_metres
    rows_to_try, columns_to_try = numpy.where(numpy.logical_and(
        valid_x_flags, valid_y_flags))

    distances_to_try_metres = numpy.sqrt(
        (grid_point_dict[GRID_POINT_X_MATRIX_KEY][rows_to_try, columns_to_try] -
         test_x_coord_metres) ** 2 +
        (grid_point_dict[GRID_POINT_Y_MATRIX_KEY][rows_to_try, columns_to_try] -
         test_y_coord_metres) ** 2)
    valid_indices = numpy.where(
        distances_to_try_metres <= effective_radius_metres)[0]

    return (rows_to_try[valid_indices], columns_to_try[valid_indices],
            grid_point_dict)
示例#15
0
def create_equidistant_grid(min_latitude_deg,
                            max_latitude_deg,
                            min_longitude_deg,
                            max_longitude_deg,
                            x_spacing_metres,
                            y_spacing_metres,
                            azimuthal=True):
    """Creates equidistant grid.

    M = number of rows
    N = number of columns

    :param min_latitude_deg: Minimum latitude (deg N) in grid.
    :param max_latitude_deg: Max latitude (deg N) in grid.
    :param min_longitude_deg: Minimum longitude (deg E) in grid.
    :param max_longitude_deg: Max longitude (deg E) in grid.
    :param x_spacing_metres: Spacing between grid points in adjacent columns.
    :param y_spacing_metres: Spacing between grid points in adjacent rows.
    :param azimuthal: Boolean flag.  If True, will create azimuthal equidistant
        grid.  If False, will create Lambert conformal grid.
    :return: grid_dict: Dictionary with the following keys.
    grid_dict['grid_point_x_coords_metres']: length-N numpy array with unique
        x-coordinates at grid points.
    grid_dict['grid_point_y_coords_metres']: length-M numpy array with unique
        y-coordinates at grid points.
    grid_dict['projection_object']: Instance of `pyproj.Proj` (used to convert
        between lat-long coordinates and the x-y coordinates of the grid).
    """

    # Check input args.
    error_checking.assert_is_valid_latitude(min_latitude_deg)
    error_checking.assert_is_valid_latitude(max_latitude_deg)
    error_checking.assert_is_greater(max_latitude_deg, min_latitude_deg)
    error_checking.assert_is_greater(x_spacing_metres, 0.)
    error_checking.assert_is_greater(y_spacing_metres, 0.)
    error_checking.assert_is_boolean(azimuthal)

    min_longitude_deg = lng_conversion.convert_lng_negative_in_west(
        min_longitude_deg, allow_nan=False)
    max_longitude_deg = lng_conversion.convert_lng_negative_in_west(
        max_longitude_deg, allow_nan=False)
    error_checking.assert_is_greater(max_longitude_deg, min_longitude_deg)

    # Create lat-long grid.
    num_grid_rows = 1 + int(
        numpy.round((max_latitude_deg - min_latitude_deg) /
                    DUMMY_LATITUDE_SPACING_DEG))
    num_grid_columns = 1 + int(
        numpy.round((max_longitude_deg - min_longitude_deg) /
                    DUMMY_LONGITUDE_SPACING_DEG))

    unique_latitudes_deg, unique_longitudes_deg = get_latlng_grid_points(
        min_latitude_deg=min_latitude_deg,
        min_longitude_deg=min_longitude_deg,
        lat_spacing_deg=DUMMY_LATITUDE_SPACING_DEG,
        lng_spacing_deg=DUMMY_LONGITUDE_SPACING_DEG,
        num_rows=num_grid_rows,
        num_columns=num_grid_columns)

    latitude_matrix_deg, longitude_matrix_deg = latlng_vectors_to_matrices(
        unique_latitudes_deg=unique_latitudes_deg,
        unique_longitudes_deg=unique_longitudes_deg)

    # Create projection.
    central_latitude_deg = 0.5 * (min_latitude_deg + max_latitude_deg)
    central_longitude_deg = 0.5 * (min_longitude_deg + max_longitude_deg)

    if azimuthal:
        projection_object = projections.init_azimuthal_equidistant_projection(
            central_latitude_deg=central_latitude_deg,
            central_longitude_deg=central_longitude_deg)
    else:
        projection_object = projections.init_lcc_projection(
            standard_latitudes_deg=numpy.full(2, central_latitude_deg),
            central_longitude_deg=central_longitude_deg)

    # Convert lat-long grid to preliminary x-y grid.
    prelim_x_matrix_metres, prelim_y_matrix_metres = (
        projections.project_latlng_to_xy(latitudes_deg=latitude_matrix_deg,
                                         longitudes_deg=longitude_matrix_deg,
                                         projection_object=projection_object))

    # Find corners of preliminary x-y grid.
    x_min_metres = numpy.min(prelim_x_matrix_metres)
    x_max_metres = numpy.max(prelim_x_matrix_metres)
    y_min_metres = numpy.min(prelim_y_matrix_metres)
    y_max_metres = numpy.max(prelim_y_matrix_metres)

    # Find corners of final x-y grid.
    x_min_metres = number_rounding.floor_to_nearest(x_min_metres,
                                                    x_spacing_metres)
    x_max_metres = number_rounding.ceiling_to_nearest(x_max_metres,
                                                      x_spacing_metres)
    y_min_metres = number_rounding.floor_to_nearest(y_min_metres,
                                                    y_spacing_metres)
    y_max_metres = number_rounding.ceiling_to_nearest(y_max_metres,
                                                      y_spacing_metres)

    # Create final x-y grid.
    num_grid_rows = 1 + int(
        numpy.round((y_max_metres - y_min_metres) / y_spacing_metres))
    num_grid_columns = 1 + int(
        numpy.round((x_max_metres - x_min_metres) / x_spacing_metres))

    unique_x_coords_metres, unique_y_coords_metres = get_xy_grid_points(
        x_min_metres=x_min_metres,
        y_min_metres=y_min_metres,
        x_spacing_metres=x_spacing_metres,
        y_spacing_metres=y_spacing_metres,
        num_rows=num_grid_rows,
        num_columns=num_grid_columns)

    return {
        X_COORDS_KEY: unique_x_coords_metres,
        Y_COORDS_KEY: unique_y_coords_metres,
        PROJECTION_KEY: projection_object
    }
def _shuffle_data_with_smart_io(storm_object_table=None,
                                file_dict=None,
                                working_spc_date_unix_sec=None,
                                read_from_intermediate=None):
    """Shuffles data with smart IO.

    Specifically, this method ensures that only SPC dates (k - 1)...(k + 1) are
    in memory, where k is the date currently being worked on.

    :param storm_object_table: pandas DataFrame with columns documented in
        _write_intermediate_results.
    :param file_dict: See documentation for find_files_for_smart_io..
    :param working_spc_date_unix_sec: Next SPC date to work on.
    :param read_from_intermediate: Boolean flag.  If True, will read from
        intermediate files.  If False, will read from input files.
    :return: storm_object_table: pandas DataFrame with columns documented in
        _write_intermediate_results.
    """

    working_spc_date_index = numpy.where(
        file_dict[SPC_DATES_KEY] == working_spc_date_unix_sec)[0][0]
    num_spc_dates = len(file_dict[SPC_DATES_KEY])

    if working_spc_date_index == 0:
        read_spc_date_indices = numpy.array([0, 1], dtype=int)
        write_spc_date_indices = numpy.array(
            [num_spc_dates - 2, num_spc_dates - 1], dtype=int)
        clear_table = True

    elif working_spc_date_index == num_spc_dates - 1:
        read_spc_date_indices = numpy.array([], dtype=int)
        write_spc_date_indices = numpy.array([num_spc_dates - 3], dtype=int)
        clear_table = False

    else:
        read_spc_date_indices = numpy.array([working_spc_date_index + 1],
                                            dtype=int)
        write_spc_date_indices = numpy.array([working_spc_date_index - 2],
                                             dtype=int)
        clear_table = False

    read_spc_date_indices = read_spc_date_indices[read_spc_date_indices >= 0]
    read_spc_date_indices = read_spc_date_indices[
        read_spc_date_indices < num_spc_dates]
    write_spc_date_indices = write_spc_date_indices[
        write_spc_date_indices >= 0]
    write_spc_date_indices = write_spc_date_indices[
        write_spc_date_indices < num_spc_dates]

    if storm_object_table is not None:
        for this_index in write_spc_date_indices:
            this_spc_date_unix_sec = file_dict[SPC_DATES_KEY][this_index]
            this_spc_date_string = time_conversion.time_to_spc_date_string(
                this_spc_date_unix_sec)

            this_spc_date_indices = numpy.where(
                storm_object_table[tracking_io.SPC_DATE_COLUMN].values ==
                this_spc_date_unix_sec)[0]

            this_temp_file_name = file_dict[TEMP_FILE_NAMES_KEY][this_index]
            print('Writing intermediate data for ' + this_spc_date_string +
                  ': ' + this_temp_file_name + '...')

            _write_intermediate_results(
                storm_object_table.iloc[this_spc_date_indices],
                this_temp_file_name)
            storm_object_table.drop(
                storm_object_table.index[this_spc_date_indices],
                axis=0,
                inplace=True)

    if clear_table:
        storm_object_table = None

    for this_index in read_spc_date_indices:
        this_spc_date_unix_sec = file_dict[SPC_DATES_KEY][this_index]
        this_spc_date_string = time_conversion.time_to_spc_date_string(
            this_spc_date_unix_sec)

        if read_from_intermediate:
            this_temp_file_name = file_dict[TEMP_FILE_NAMES_KEY][this_index]
            print('Reading intermediate data for ' + this_spc_date_string +
                  ': ' + this_temp_file_name + '...')

            this_storm_object_table = _read_intermediate_results(
                this_temp_file_name)

        else:
            this_storm_object_table = best_tracks.read_input_storm_objects(
                file_dict[INPUT_FILE_NAMES_KEY][this_index],
                keep_spc_date=True)

            these_centroid_x_metres, these_centroid_y_metres = (
                projections.project_latlng_to_xy(
                    this_storm_object_table[
                        tracking_io.CENTROID_LAT_COLUMN].values,
                    this_storm_object_table[
                        tracking_io.CENTROID_LNG_COLUMN].values,
                    projection_object=PROJECTION_OBJECT,
                    false_easting_metres=0.,
                    false_northing_metres=0.))

            argument_dict = {
                best_tracks.CENTROID_X_COLUMN: these_centroid_x_metres,
                best_tracks.CENTROID_Y_COLUMN: these_centroid_y_metres
            }
            this_storm_object_table = this_storm_object_table.assign(
                **argument_dict)
            this_storm_object_table.drop([
                tracking_io.CENTROID_LAT_COLUMN,
                tracking_io.CENTROID_LNG_COLUMN
            ],
                                         axis=1,
                                         inplace=True)

        if storm_object_table is None:
            storm_object_table = copy.deepcopy(this_storm_object_table)
        else:
            this_storm_object_table, _ = this_storm_object_table.align(
                storm_object_table, axis=1)
            storm_object_table = pandas.concat(
                [storm_object_table, this_storm_object_table],
                axis=0,
                ignore_index=True)

    return storm_object_table
    num_spc_dates = len(spc_date_strings)
    storm_object_table_by_spc_date = [None] * num_spc_dates

    grid_point_lat_, grid_point_long = utils.get_latlng_grid_points(min_latitude_deg=MIN_LAT_DEG, min_longitude_deg=MIN_LONG_DEG, 
                                                     lat_spacing_deg= LATITUDE_SPACING_DEG, lng_spacing_deg=LONGITUDE_SPACING_DEG,
                                                        num_rows=NUM_ROWS, num_columns=NUM_COLUMNS)
    grid_point_lat = numpy.flip(grid_point_lat_,-1)
    lats, lons = latlng_vectors_to_matrices(grid_point_lat, grid_point_long)
    central_latitude_deg = numpy.mean(numpy.array([MIN_LAT_DEG, MAX_LAT_DEG]))
    central_longitude_deg = numpy.mean(numpy.array([MIN_LONG_DEG, MAX_LONG_DEG]))
     
    projection_object = projections.init_azimuthal_equidistant_projection(central_latitude_deg=central_latitude_deg,
                                                                          central_longitude_deg=central_longitude_deg)
    # Project lat-long grid points to x-y. 
    (grid_point_x_matrix_metres, grid_point_y_matrix_metres) = projections.project_latlng_to_xy(
                                                                                latitudes_deg=lats,
                                                                                longitudes_deg=lons,
                                                                                projection_object=projection_object)    
    
    x_min_metres = numpy.min(grid_point_x_matrix_metres)
    x_max_metres = numpy.max(grid_point_x_matrix_metres)
    y_min_metres = numpy.min(grid_point_y_matrix_metres)
    y_max_metres = numpy.max(grid_point_y_matrix_metres)
    
    # Round corners to nearest 10 km.  These will become the corners of the actual
    # x-y grid.
    x_min_metres = number_rounding.floor_to_nearest(x_min_metres, X_SPACING_METRES)
    x_max_metres = number_rounding.ceiling_to_nearest(x_max_metres, X_SPACING_METRES)
    y_min_metres = number_rounding.floor_to_nearest(y_min_metres, Y_SPACING_METRES)
    y_max_metres = number_rounding.ceiling_to_nearest(y_max_metres, Y_SPACING_METRES)

    num_grid_rows = 1 + int(numpy.round((y_max_metres - y_min_metres) / Y_SPACING_METRES))
示例#18
0
def make_buffers_around_polygons(storm_object_table,
                                 min_buffer_dists_metres=None,
                                 max_buffer_dists_metres=None):
    """Creates one or more buffers around each storm polygon.

    N = number of buffers
    V = number of vertices in a given polygon

    :param storm_object_table: pandas DataFrame with the following columns.
    storm_object_table.storm_id: String ID for storm cell.
    storm_object_table.polygon_object_latlng: Instance of
        `shapely.geometry.Polygon`, with vertices in lat-long coordinates.
    :param min_buffer_dists_metres: length-N numpy array of minimum buffer
        distances.  If min_buffer_dists_metres[i] is NaN, the [i]th buffer
        includes the original polygon.  If min_buffer_dists_metres[i] is
        defined, the [i]th buffer is a "nested" buffer, not including the
        original polygon.
    :param max_buffer_dists_metres: length-N numpy array of maximum buffer
        distances.  Must be all real numbers (no NaN).
    :return: storm_object_table: Same as input, but with N extra columns.
    storm_object_table.polygon_object_latlng_buffer_<D>m: Instance of
        `shapely.geometry.Polygon` for D-metre buffer around storm.
    storm_object_table.polygon_object_latlng_buffer_<d>_<D>m: Instance of
        `shapely.geometry.Polygon` for d-to-D-metre buffer around storm.
    """

    error_checking.assert_is_geq_numpy_array(min_buffer_dists_metres,
                                             0.,
                                             allow_nan=True)
    error_checking.assert_is_numpy_array(min_buffer_dists_metres,
                                         num_dimensions=1)

    num_buffers = len(min_buffer_dists_metres)
    error_checking.assert_is_geq_numpy_array(max_buffer_dists_metres,
                                             0.,
                                             allow_nan=False)
    error_checking.assert_is_numpy_array(max_buffer_dists_metres,
                                         exact_dimensions=numpy.array(
                                             [num_buffers]))

    for j in range(num_buffers):
        if numpy.isnan(min_buffer_dists_metres[j]):
            continue
        error_checking.assert_is_greater(max_buffer_dists_metres[j],
                                         min_buffer_dists_metres[j],
                                         allow_nan=False)

    num_storm_objects = len(storm_object_table.index)
    centroid_latitudes_deg = numpy.full(num_storm_objects, numpy.nan)
    centroid_longitudes_deg = numpy.full(num_storm_objects, numpy.nan)

    for i in range(num_storm_objects):
        this_centroid_object = storm_object_table[
            POLYGON_OBJECT_LATLNG_COLUMN].values[0].centroid
        centroid_latitudes_deg[i] = this_centroid_object.y
        centroid_longitudes_deg[i] = this_centroid_object.x

    global_centroid_lat_deg, global_centroid_lng_deg = (
        polygons.get_latlng_centroid(centroid_latitudes_deg,
                                     centroid_longitudes_deg))
    projection_object = projections.init_azimuthal_equidistant_projection(
        global_centroid_lat_deg, global_centroid_lng_deg)

    object_array = numpy.full(num_storm_objects, numpy.nan, dtype=object)
    argument_dict = {}
    buffer_column_names = [''] * num_buffers

    for j in range(num_buffers):
        buffer_column_names[j] = distance_buffer_to_column_name(
            min_buffer_dists_metres[j], max_buffer_dists_metres[j])
        argument_dict.update({buffer_column_names[j]: object_array})
    storm_object_table = storm_object_table.assign(**argument_dict)

    for i in range(num_storm_objects):
        orig_vertex_dict_latlng = polygons.polygon_object_to_vertex_arrays(
            storm_object_table[POLYGON_OBJECT_LATLNG_COLUMN].values[i])

        (orig_vertex_x_metres,
         orig_vertex_y_metres) = projections.project_latlng_to_xy(
             orig_vertex_dict_latlng[polygons.EXTERIOR_Y_COLUMN],
             orig_vertex_dict_latlng[polygons.EXTERIOR_X_COLUMN],
             projection_object=projection_object)

        for j in range(num_buffers):
            buffer_polygon_object_xy = polygons.buffer_simple_polygon(
                orig_vertex_x_metres,
                orig_vertex_y_metres,
                min_buffer_dist_metres=min_buffer_dists_metres[j],
                max_buffer_dist_metres=max_buffer_dists_metres[j])

            buffer_vertex_dict = polygons.polygon_object_to_vertex_arrays(
                buffer_polygon_object_xy)

            (buffer_vertex_dict[polygons.EXTERIOR_Y_COLUMN],
             buffer_vertex_dict[polygons.EXTERIOR_X_COLUMN]) = (
                 projections.project_xy_to_latlng(
                     buffer_vertex_dict[polygons.EXTERIOR_X_COLUMN],
                     buffer_vertex_dict[polygons.EXTERIOR_Y_COLUMN],
                     projection_object=projection_object))

            this_num_holes = len(buffer_vertex_dict[polygons.HOLE_X_COLUMN])
            for k in range(this_num_holes):
                (buffer_vertex_dict[polygons.HOLE_Y_COLUMN][k],
                 buffer_vertex_dict[polygons.HOLE_X_COLUMN][k]) = (
                     projections.project_xy_to_latlng(
                         buffer_vertex_dict[polygons.HOLE_X_COLUMN][k],
                         buffer_vertex_dict[polygons.HOLE_Y_COLUMN][k],
                         projection_object=projection_object))

            buffer_polygon_object_latlng = (
                polygons.vertex_arrays_to_polygon_object(
                    buffer_vertex_dict[polygons.EXTERIOR_X_COLUMN],
                    buffer_vertex_dict[polygons.EXTERIOR_Y_COLUMN],
                    hole_x_coords_list=buffer_vertex_dict[
                        polygons.HOLE_X_COLUMN],
                    hole_y_coords_list=buffer_vertex_dict[
                        polygons.HOLE_Y_COLUMN]))

            storm_object_table[buffer_column_names[j]].values[
                i] = buffer_polygon_object_latlng

    return storm_object_table
def make_buffers_around_storm_objects(
        storm_object_table, min_distances_metres, max_distances_metres):
    """Creates one or more distance buffers around each storm object.

    N = number of storm objects
    B = number of buffers around each storm object
    V = number of vertices in a given buffer

    :param storm_object_table: N-row pandas DataFrame with the following
        columns.
    storm_object_table.storm_id: String ID for storm cell.
    storm_object_table.polygon_object_latlng: Instance of
        `shapely.geometry.Polygon`, containing vertices of storm object in
        lat-long coordinates.

    :param min_distances_metres: length-B numpy array of minimum buffer
        distances.  If min_distances_metres[i] is NaN, the storm object is
        included in the [i]th buffer, so the [i]th buffer is inclusive.  If
        min_distances_metres[i] is a real number, the storm object is *not*
        included in the [i]th buffer, so the [i]th buffer is exclusive.
    :param max_distances_metres: length-B numpy array of maximum buffer
        distances.  Must be all real numbers (no NaN).
    :return: storm_object_table: Same as input, but with B additional columns.
        Each additional column (listed below) contains a
        `shapely.geometry.Polygon` instance for each storm object.  Each
        `shapely.geometry.Polygon` instance contains the lat-long vertices of
        one distance buffer around one storm object.
    storm_object_table.polygon_object_latlng_buffer_<D>m: For an inclusive
        buffer of D metres around the storm.
    storm_object_table.polygon_object_latlng_buffer_<d>_<D>m: For an exclusive
        buffer of d...D metres around the storm.
    """

    error_checking.assert_is_geq_numpy_array(
        min_distances_metres, 0., allow_nan=True)
    error_checking.assert_is_numpy_array(
        min_distances_metres, num_dimensions=1)

    num_buffers = len(min_distances_metres)
    error_checking.assert_is_geq_numpy_array(
        max_distances_metres, 0., allow_nan=False)
    error_checking.assert_is_numpy_array(
        max_distances_metres, exact_dimensions=numpy.array([num_buffers]))

    for j in range(num_buffers):
        if numpy.isnan(min_distances_metres[j]):
            continue
        error_checking.assert_is_greater(
            max_distances_metres[j], min_distances_metres[j],
            allow_nan=False)

    num_storm_objects = len(storm_object_table.index)
    centroid_latitudes_deg = numpy.full(num_storm_objects, numpy.nan)
    centroid_longitudes_deg = numpy.full(num_storm_objects, numpy.nan)

    for i in range(num_storm_objects):
        this_centroid_object = storm_object_table[
            POLYGON_OBJECT_LATLNG_COLUMN].values[0].centroid
        centroid_latitudes_deg[i] = this_centroid_object.y
        centroid_longitudes_deg[i] = this_centroid_object.x

    (global_centroid_lat_deg, global_centroid_lng_deg
    ) = geodetic_utils.get_latlng_centroid(
        latitudes_deg=centroid_latitudes_deg,
        longitudes_deg=centroid_longitudes_deg)
    projection_object = projections.init_azimuthal_equidistant_projection(
        global_centroid_lat_deg, global_centroid_lng_deg)

    object_array = numpy.full(num_storm_objects, numpy.nan, dtype=object)
    argument_dict = {}
    buffer_column_names = [''] * num_buffers

    for j in range(num_buffers):
        buffer_column_names[j] = distance_buffer_to_column_name(
            min_distances_metres[j], max_distances_metres[j])
        argument_dict.update({buffer_column_names[j]: object_array})
    storm_object_table = storm_object_table.assign(**argument_dict)

    for i in range(num_storm_objects):
        orig_vertex_dict_latlng = polygons.polygon_object_to_vertex_arrays(
            storm_object_table[POLYGON_OBJECT_LATLNG_COLUMN].values[i])

        (orig_vertex_x_metres,
         orig_vertex_y_metres) = projections.project_latlng_to_xy(
             orig_vertex_dict_latlng[polygons.EXTERIOR_Y_COLUMN],
             orig_vertex_dict_latlng[polygons.EXTERIOR_X_COLUMN],
             projection_object=projection_object)

        for j in range(num_buffers):
            buffer_polygon_object_xy = polygons.buffer_simple_polygon(
                orig_vertex_x_metres, orig_vertex_y_metres,
                min_buffer_dist_metres=min_distances_metres[j],
                max_buffer_dist_metres=max_distances_metres[j])

            buffer_vertex_dict = polygons.polygon_object_to_vertex_arrays(
                buffer_polygon_object_xy)

            (buffer_vertex_dict[polygons.EXTERIOR_Y_COLUMN],
             buffer_vertex_dict[polygons.EXTERIOR_X_COLUMN]) = (
                 projections.project_xy_to_latlng(
                     buffer_vertex_dict[polygons.EXTERIOR_X_COLUMN],
                     buffer_vertex_dict[polygons.EXTERIOR_Y_COLUMN],
                     projection_object=projection_object))

            this_num_holes = len(buffer_vertex_dict[polygons.HOLE_X_COLUMN])
            for k in range(this_num_holes):
                (buffer_vertex_dict[polygons.HOLE_Y_COLUMN][k],
                 buffer_vertex_dict[polygons.HOLE_X_COLUMN][k]) = (
                     projections.project_xy_to_latlng(
                         buffer_vertex_dict[polygons.HOLE_X_COLUMN][k],
                         buffer_vertex_dict[polygons.HOLE_Y_COLUMN][k],
                         projection_object=projection_object))

            buffer_polygon_object_latlng = (
                polygons.vertex_arrays_to_polygon_object(
                    buffer_vertex_dict[polygons.EXTERIOR_X_COLUMN],
                    buffer_vertex_dict[polygons.EXTERIOR_Y_COLUMN],
                    hole_x_coords_list=
                    buffer_vertex_dict[polygons.HOLE_X_COLUMN],
                    hole_y_coords_list=
                    buffer_vertex_dict[polygons.HOLE_Y_COLUMN]))

            storm_object_table[buffer_column_names[j]].values[
                i] = buffer_polygon_object_latlng

    return storm_object_table
def _match_locations_one_time(source_object_table, target_object_table,
                              max_distance_metres):
    """Matches storm locations at one time.

    :param source_object_table: pandas DataFrame, where each row is a storm
        object in the source dataset.  See `storm_tracking_io.write_file` for a
        list of expected columns.
    :param target_object_table: Same but for target dataset.
    :param max_distance_metres: Max distance for matching.
    :return: source_to_target_dict: Dictionary, where each key is a tuple with
        (source ID, source time) and each value is a list with [target ID,
        target time].  The IDs are strings, and the times are Unix seconds
        (integers).  For source objects with no match in the target dataset, the
        corresponding value is None (rather than a list).
    """

    # TODO(thunderhoser): Maybe use polygons here?

    num_source_objects = len(source_object_table.index)
    source_to_target_dict = dict()

    if num_source_objects == 0:
        return source_to_target_dict

    for i in range(num_source_objects):
        this_key = (
            source_object_table[tracking_utils.FULL_ID_COLUMN].values[i],
            source_object_table[tracking_utils.VALID_TIME_COLUMN].values[i])

        source_to_target_dict[this_key] = None

    num_target_objects = len(target_object_table.index)
    if num_target_objects == 0:
        return source_to_target_dict

    # Create equidistant projection.
    all_latitudes_deg = numpy.concatenate(
        (source_object_table[tracking_utils.CENTROID_LATITUDE_COLUMN].values,
         target_object_table[tracking_utils.CENTROID_LATITUDE_COLUMN].values))
    all_longitudes_deg = numpy.concatenate(
        (source_object_table[tracking_utils.CENTROID_LONGITUDE_COLUMN].values,
         target_object_table[tracking_utils.CENTROID_LONGITUDE_COLUMN].values))
    projection_object = projections.init_azimuthal_equidistant_projection(
        central_latitude_deg=numpy.mean(all_latitudes_deg),
        central_longitude_deg=numpy.mean(all_longitudes_deg))

    # Project storm centers from lat-long to x-y.
    source_x_coords_metres, source_y_coords_metres = (
        projections.project_latlng_to_xy(
            latitudes_deg=source_object_table[
                tracking_utils.CENTROID_LATITUDE_COLUMN].values,
            longitudes_deg=source_object_table[
                tracking_utils.CENTROID_LONGITUDE_COLUMN].values,
            projection_object=projection_object))

    target_x_coords_metres, target_y_coords_metres = (
        projections.project_latlng_to_xy(
            latitudes_deg=target_object_table[
                tracking_utils.CENTROID_LATITUDE_COLUMN].values,
            longitudes_deg=target_object_table[
                tracking_utils.CENTROID_LONGITUDE_COLUMN].values,
            projection_object=projection_object))

    # Find nearest target object to each source object.
    source_coord_matrix = numpy.transpose(
        numpy.vstack((source_x_coords_metres, source_y_coords_metres)))
    target_coord_matrix = numpy.transpose(
        numpy.vstack((target_x_coords_metres, target_y_coords_metres)))

    distance_matrix_metres2 = euclidean_distances(X=source_coord_matrix,
                                                  Y=target_coord_matrix,
                                                  squared=True)

    nearest_target_indices = numpy.argmin(distance_matrix_metres2, axis=1)
    source_indices = numpy.linspace(0,
                                    num_source_objects - 1,
                                    num=num_source_objects,
                                    dtype=int)

    min_distances_metres2 = distance_matrix_metres2[source_indices,
                                                    nearest_target_indices]
    bad_subindices = numpy.where(
        min_distances_metres2 > max_distance_metres**2)[0]
    nearest_target_indices[bad_subindices] = -1

    # Print results to command window.
    num_matched_source_objects = numpy.sum(nearest_target_indices >= 0)
    source_time_string = time_conversion.unix_sec_to_string(
        source_object_table[tracking_utils.VALID_TIME_COLUMN].values[0],
        TIME_FORMAT)

    print('Matched {0:d} of {1:d} source objects at {2:s}.'.format(
        num_matched_source_objects, num_source_objects, source_time_string))

    # Fill dictionary.
    for i in range(num_source_objects):
        this_key = (
            source_object_table[tracking_utils.FULL_ID_COLUMN].values[i],
            source_object_table[tracking_utils.VALID_TIME_COLUMN].values[i])

        j = nearest_target_indices[i]
        if j == -1:
            continue

        source_to_target_dict[this_key] = [
            target_object_table[tracking_utils.FULL_ID_COLUMN].values[j],
            target_object_table[tracking_utils.VALID_TIME_COLUMN].values[j]
        ]

    return source_to_target_dict
示例#21
0
def _get_grid_point_coords(model_name,
                           first_row_in_full_grid,
                           last_row_in_full_grid,
                           first_column_in_full_grid,
                           last_column_in_full_grid,
                           grid_id=None,
                           basemap_object=None):
    """Returns x-y and lat-long coords for a subgrid of the full model grid.

    This method generates different x-y coordinates than
    `nwp_model_utils.get_xy_grid_point_matrices`, because (like
    `mpl_toolkits.basemap.Basemap`) this method sets false easting = false
    northing = 0 metres.

    :param model_name: Name of NWP model (must be accepted by
        `nwp_model_utils.check_grid_name`).
    :param first_row_in_full_grid: Row 0 in the subgrid is row
        `first_row_in_full_grid` in the full grid.
    :param last_row_in_full_grid: Last row in the subgrid is row
        `last_row_in_full_grid` in the full grid.  If you want last row in the
        subgrid to equal last row in the full grid, make this -1.
    :param first_column_in_full_grid: Column 0 in the subgrid is column
        `first_column_in_full_grid` in the full grid.
    :param last_column_in_full_grid: Last column in the subgrid is column
        `last_column_in_full_grid` in the full grid.  If you want last column in
        the subgrid to equal last column in the full grid, make this -1.
    :param grid_id: Grid for NWP model (must be accepted by
        `nwp_model_utils.check_grid_name`).
    :param basemap_object: Instance of `mpl_toolkits.basemap.Basemap` for the
        given NWP model.  If you don't have one, no big deal -- leave this
        argument empty.
    :return: coordinate_dict: Dictionary with the following keys.
    coordinate_dict['grid_point_x_matrix_metres']: M-by-N numpy array of
        x-coordinates.
    coordinate_dict['grid_point_y_matrix_metres']: M-by-N numpy array of
        y-coordinates.
    coordinate_dict['grid_point_lat_matrix_deg']: M-by-N numpy array of
        latitudes (deg N).
    coordinate_dict['grid_point_lng_matrix_deg']: M-by-N numpy array of
        longitudes (deg E).
    """

    num_rows_in_full_grid, num_columns_in_full_grid = (
        nwp_model_utils.get_grid_dimensions(model_name=model_name,
                                            grid_name=grid_id))

    error_checking.assert_is_integer(first_row_in_full_grid)
    error_checking.assert_is_geq(first_row_in_full_grid, 0)
    error_checking.assert_is_integer(last_row_in_full_grid)
    if last_row_in_full_grid < 0:
        last_row_in_full_grid += num_rows_in_full_grid

    error_checking.assert_is_greater(last_row_in_full_grid,
                                     first_row_in_full_grid)
    error_checking.assert_is_less_than(last_row_in_full_grid,
                                       num_rows_in_full_grid)

    error_checking.assert_is_integer(first_column_in_full_grid)
    error_checking.assert_is_geq(first_column_in_full_grid, 0)
    error_checking.assert_is_integer(last_column_in_full_grid)
    if last_column_in_full_grid < 0:
        last_column_in_full_grid += num_columns_in_full_grid

    error_checking.assert_is_greater(last_column_in_full_grid,
                                     first_column_in_full_grid)
    error_checking.assert_is_less_than(last_column_in_full_grid,
                                       num_columns_in_full_grid)

    grid_point_lat_matrix_deg, grid_point_lng_matrix_deg = (
        nwp_model_utils.get_latlng_grid_point_matrices(model_name=model_name,
                                                       grid_name=grid_id))

    grid_point_lat_matrix_deg = grid_point_lat_matrix_deg[
        first_row_in_full_grid:(last_row_in_full_grid + 1),
        first_column_in_full_grid:(last_column_in_full_grid + 1)]

    grid_point_lng_matrix_deg = grid_point_lng_matrix_deg[
        first_row_in_full_grid:(last_row_in_full_grid + 1),
        first_column_in_full_grid:(last_column_in_full_grid + 1)]

    if basemap_object is None:
        standard_latitudes_deg, central_longitude_deg = (
            nwp_model_utils.get_projection_params(model_name))

        projection_object = projections.init_lcc_projection(
            standard_latitudes_deg=standard_latitudes_deg,
            central_longitude_deg=central_longitude_deg)

        grid_point_x_matrix_metres, grid_point_y_matrix_metres = (
            projections.project_latlng_to_xy(
                latitudes_deg=grid_point_lat_matrix_deg,
                longitudes_deg=grid_point_lng_matrix_deg,
                projection_object=projection_object,
                false_northing_metres=0.,
                false_easting_metres=0.))
    else:
        grid_point_x_matrix_metres, grid_point_y_matrix_metres = basemap_object(
            grid_point_lng_matrix_deg, grid_point_lat_matrix_deg)

    return {
        X_COORD_MATRIX_KEY: grid_point_x_matrix_metres,
        Y_COORD_MATRIX_KEY: grid_point_y_matrix_metres,
        LATITUDE_MATRIX_KEY: grid_point_lat_matrix_deg,
        LONGITUDE_MATRIX_KEY: grid_point_lng_matrix_deg,
    }
示例#22
0
def _run(prediction_file_name, top_tracking_dir_name, prob_threshold,
         grid_spacing_metres, output_dir_name):
    """Plots spatial distribution of false alarms.

    This is effectively the main method.

    :param prediction_file_name: See documentation at top of file.
    :param top_tracking_dir_name: Same.
    :param prob_threshold: Same.
    :param grid_spacing_metres: Same.
    :param output_dir_name: Same.
    """

    # Process input args.
    file_system_utils.mkdir_recursive_if_necessary(
        directory_name=output_dir_name)
    error_checking.assert_is_greater(prob_threshold, 0.)
    error_checking.assert_is_less_than(prob_threshold, 1.)

    grid_metadata_dict = grids.create_equidistant_grid(
        min_latitude_deg=MIN_LATITUDE_DEG,
        max_latitude_deg=MAX_LATITUDE_DEG,
        min_longitude_deg=MIN_LONGITUDE_DEG,
        max_longitude_deg=MAX_LONGITUDE_DEG,
        x_spacing_metres=grid_spacing_metres,
        y_spacing_metres=grid_spacing_metres,
        azimuthal=False)

    # Read predictions and find positive forecasts and false alarms.
    print('Reading predictions from: "{0:s}"...'.format(prediction_file_name))
    prediction_dict = prediction_io.read_ungridded_predictions(
        prediction_file_name)

    observed_labels = prediction_dict[prediction_io.OBSERVED_LABELS_KEY]
    forecast_labels = (
        prediction_dict[prediction_io.PROBABILITY_MATRIX_KEY][:, -1] >=
        prob_threshold).astype(int)

    pos_forecast_indices = numpy.where(forecast_labels == 1)[0]
    false_alarm_indices = numpy.where(
        numpy.logical_and(observed_labels == 0, forecast_labels == 1))[0]

    num_examples = len(observed_labels)
    num_positive_forecasts = len(pos_forecast_indices)
    num_false_alarms = len(false_alarm_indices)

    print(('Probability threshold = {0:.3f} ... number of examples, positive '
           'forecasts, false alarms = {1:d}, {2:d}, {3:d}').format(
               prob_threshold, num_examples, num_positive_forecasts,
               num_false_alarms))

    # Find and read tracking files.
    pos_forecast_id_strings = [
        prediction_dict[prediction_io.STORM_IDS_KEY][k]
        for k in pos_forecast_indices
    ]
    pos_forecast_times_unix_sec = (
        prediction_dict[prediction_io.STORM_TIMES_KEY][pos_forecast_indices])

    file_times_unix_sec = numpy.unique(pos_forecast_times_unix_sec)
    num_files = len(file_times_unix_sec)
    storm_object_tables = [None] * num_files

    print(SEPARATOR_STRING)

    for i in range(num_files):
        this_tracking_file_name = tracking_io.find_file(
            top_tracking_dir_name=top_tracking_dir_name,
            tracking_scale_metres2=DUMMY_TRACKING_SCALE_METRES2,
            source_name=tracking_utils.SEGMOTION_NAME,
            valid_time_unix_sec=file_times_unix_sec[i],
            spc_date_string=time_conversion.time_to_spc_date_string(
                file_times_unix_sec[i]),
            raise_error_if_missing=True)

        print('Reading data from: "{0:s}"...'.format(this_tracking_file_name))
        this_table = tracking_io.read_file(this_tracking_file_name)
        storm_object_tables[i] = this_table.loc[this_table[
            tracking_utils.FULL_ID_COLUMN].isin(pos_forecast_id_strings)]

        if i == 0:
            continue

        storm_object_tables[i] = storm_object_tables[i].align(
            storm_object_tables[0], axis=1)[0]

    storm_object_table = pandas.concat(storm_object_tables,
                                       axis=0,
                                       ignore_index=True)
    print(SEPARATOR_STRING)

    # Find latitudes and longitudes of false alarms.
    all_id_strings = (
        storm_object_table[tracking_utils.FULL_ID_COLUMN].values.tolist())
    all_times_unix_sec = (
        storm_object_table[tracking_utils.VALID_TIME_COLUMN].values)
    good_indices = tracking_utils.find_storm_objects(
        all_id_strings=all_id_strings,
        all_times_unix_sec=all_times_unix_sec,
        id_strings_to_keep=pos_forecast_id_strings,
        times_to_keep_unix_sec=pos_forecast_times_unix_sec,
        allow_missing=False)

    pos_forecast_latitudes_deg = storm_object_table[
        tracking_utils.CENTROID_LATITUDE_COLUMN].values[good_indices]

    pos_forecast_longitudes_deg = storm_object_table[
        tracking_utils.CENTROID_LONGITUDE_COLUMN].values[good_indices]

    false_alarm_id_strings = [
        prediction_dict[prediction_io.STORM_IDS_KEY][k]
        for k in false_alarm_indices
    ]
    false_alarm_times_unix_sec = (
        prediction_dict[prediction_io.STORM_TIMES_KEY][false_alarm_indices])
    good_indices = tracking_utils.find_storm_objects(
        all_id_strings=all_id_strings,
        all_times_unix_sec=all_times_unix_sec,
        id_strings_to_keep=false_alarm_id_strings,
        times_to_keep_unix_sec=false_alarm_times_unix_sec,
        allow_missing=False)

    false_alarm_latitudes_deg = storm_object_table[
        tracking_utils.CENTROID_LATITUDE_COLUMN].values[good_indices]

    false_alarm_longitudes_deg = storm_object_table[
        tracking_utils.CENTROID_LONGITUDE_COLUMN].values[good_indices]

    pos_forecast_x_coords_metres, pos_forecast_y_coords_metres = (
        projections.project_latlng_to_xy(
            latitudes_deg=pos_forecast_latitudes_deg,
            longitudes_deg=pos_forecast_longitudes_deg,
            projection_object=grid_metadata_dict[grids.PROJECTION_KEY]))

    num_pos_forecasts_matrix = grids.count_events_on_equidistant_grid(
        event_x_coords_metres=pos_forecast_x_coords_metres,
        event_y_coords_metres=pos_forecast_y_coords_metres,
        grid_point_x_coords_metres=grid_metadata_dict[grids.X_COORDS_KEY],
        grid_point_y_coords_metres=grid_metadata_dict[grids.Y_COORDS_KEY])[0]
    print(SEPARATOR_STRING)

    false_alarm_x_coords_metres, false_alarm_y_coords_metres = (
        projections.project_latlng_to_xy(
            latitudes_deg=false_alarm_latitudes_deg,
            longitudes_deg=false_alarm_longitudes_deg,
            projection_object=grid_metadata_dict[grids.PROJECTION_KEY]))

    num_false_alarms_matrix = grids.count_events_on_equidistant_grid(
        event_x_coords_metres=false_alarm_x_coords_metres,
        event_y_coords_metres=false_alarm_y_coords_metres,
        grid_point_x_coords_metres=grid_metadata_dict[grids.X_COORDS_KEY],
        grid_point_y_coords_metres=grid_metadata_dict[grids.Y_COORDS_KEY])[0]
    print(SEPARATOR_STRING)

    num_pos_forecasts_matrix = num_pos_forecasts_matrix.astype(float)
    num_pos_forecasts_matrix[num_pos_forecasts_matrix == 0] = numpy.nan
    num_false_alarms_matrix = num_false_alarms_matrix.astype(float)
    num_false_alarms_matrix[num_false_alarms_matrix == 0] = numpy.nan
    far_matrix = num_false_alarms_matrix / num_pos_forecasts_matrix

    this_max_value = numpy.nanpercentile(num_false_alarms_matrix,
                                         MAX_COUNT_PERCENTILE_TO_PLOT)
    if this_max_value < 10:
        this_max_value = numpy.nanmax(num_false_alarms_matrix)

    figure_object = plotter._plot_one_value(
        data_matrix=num_false_alarms_matrix,
        grid_metadata_dict=grid_metadata_dict,
        colour_map_object=CMAP_OBJECT_FOR_COUNTS,
        min_colour_value=0,
        max_colour_value=this_max_value,
        plot_cbar_min_arrow=False,
        plot_cbar_max_arrow=True)[0]

    num_false_alarms_file_name = '{0:s}/num_false_alarms.jpg'.format(
        output_dir_name)

    print('Saving figure to: "{0:s}"...'.format(num_false_alarms_file_name))
    figure_object.savefig(num_false_alarms_file_name,
                          dpi=FIGURE_RESOLUTION_DPI,
                          pad_inches=0,
                          bbox_inches='tight')
    pyplot.close(figure_object)

    this_max_value = numpy.nanpercentile(num_pos_forecasts_matrix,
                                         MAX_COUNT_PERCENTILE_TO_PLOT)
    if this_max_value < 10:
        this_max_value = numpy.nanmax(num_pos_forecasts_matrix)

    figure_object = plotter._plot_one_value(
        data_matrix=num_pos_forecasts_matrix,
        grid_metadata_dict=grid_metadata_dict,
        colour_map_object=CMAP_OBJECT_FOR_COUNTS,
        min_colour_value=0,
        max_colour_value=this_max_value,
        plot_cbar_min_arrow=False,
        plot_cbar_max_arrow=True)[0]

    num_pos_forecasts_file_name = '{0:s}/num_positive_forecasts.jpg'.format(
        output_dir_name)

    print('Saving figure to: "{0:s}"...'.format(num_pos_forecasts_file_name))
    figure_object.savefig(num_pos_forecasts_file_name,
                          dpi=FIGURE_RESOLUTION_DPI,
                          pad_inches=0,
                          bbox_inches='tight')
    pyplot.close(figure_object)

    this_max_value = numpy.nanpercentile(far_matrix,
                                         MAX_FAR_PERCENTILE_TO_PLOT)
    this_min_value = numpy.nanpercentile(far_matrix,
                                         100. - MAX_FAR_PERCENTILE_TO_PLOT)

    figure_object = plotter._plot_one_value(
        data_matrix=far_matrix,
        grid_metadata_dict=grid_metadata_dict,
        colour_map_object=CMAP_OBJECT_FOR_FAR,
        min_colour_value=this_min_value,
        max_colour_value=this_max_value,
        plot_cbar_min_arrow=this_min_value > 0.,
        plot_cbar_max_arrow=this_max_value < 1.)[0]

    far_file_name = '{0:s}/false_alarm_ratio.jpg'.format(output_dir_name)

    print('Saving figure to: "{0:s}"...'.format(far_file_name))
    figure_object.savefig(far_file_name,
                          dpi=FIGURE_RESOLUTION_DPI,
                          pad_inches=0,
                          bbox_inches='tight')
    pyplot.close(figure_object)
示例#23
0
def get_xy_grid_point_matrices(first_row_in_narr_grid,
                               last_row_in_narr_grid,
                               first_column_in_narr_grid,
                               last_column_in_narr_grid,
                               basemap_object=None):
    """Returns coordinate matrices for a contiguous subset of the NARR grid.

    However, this subset need not be *strictly* a subset.  In other words, the
    "subset" could be the full NARR grid.

    This method generates different x- and y-coordinates than
    `nwp_model_utils.get_xy_grid_point_matrices`, because (like
    `mpl_toolkits.basemap.Basemap`) this method assumes that false easting and
    northing are zero.

    :param first_row_in_narr_grid: Row 0 in the subgrid is row
        `first_row_in_narr_grid` in the full NARR grid.
    :param last_row_in_narr_grid: Last row (index -1) in the subgrid is row
        `last_row_in_narr_grid` in the full NARR grid.
    :param first_column_in_narr_grid: Column 0 in the subgrid is row
        `first_column_in_narr_grid` in the full NARR grid.
    :param last_column_in_narr_grid: Last column (index -1) in the subgrid is
        row `last_column_in_narr_grid` in the full NARR grid.
    :param basemap_object: Instance of `mpl_toolkits.basemap.Basemap` created
        for the NARR grid.  If you don't have one, no big deal -- leave this
        argument empty.
    :return: grid_point_x_matrix_metres: M-by-N numpy array of x-coordinates.
    :return: grid_point_y_matrix_metres: M-by-N numpy array of y-coordinates.
    """

    error_checking.assert_is_integer(first_row_in_narr_grid)
    error_checking.assert_is_geq(first_row_in_narr_grid, 0)
    error_checking.assert_is_integer(last_row_in_narr_grid)
    error_checking.assert_is_greater(last_row_in_narr_grid,
                                     first_row_in_narr_grid)
    error_checking.assert_is_less_than(last_row_in_narr_grid,
                                       NUM_ROWS_IN_NARR_GRID)

    error_checking.assert_is_integer(first_column_in_narr_grid)
    error_checking.assert_is_geq(first_column_in_narr_grid, 0)
    error_checking.assert_is_integer(last_column_in_narr_grid)
    error_checking.assert_is_greater(last_column_in_narr_grid,
                                     first_column_in_narr_grid)
    error_checking.assert_is_less_than(last_column_in_narr_grid,
                                       NUM_COLUMNS_IN_NARR_GRID)

    latitude_matrix_deg, longitude_matrix_deg = (
        nwp_model_utils.get_latlng_grid_point_matrices(
            model_name=nwp_model_utils.NARR_MODEL_NAME))

    latitude_matrix_deg = latitude_matrix_deg[first_row_in_narr_grid:(
        last_row_in_narr_grid +
        1), first_column_in_narr_grid:(last_column_in_narr_grid + 1)]
    longitude_matrix_deg = longitude_matrix_deg[first_row_in_narr_grid:(
        last_row_in_narr_grid +
        1), first_column_in_narr_grid:(last_column_in_narr_grid + 1)]

    if basemap_object is None:
        standard_latitudes_deg, central_longitude_deg = (
            nwp_model_utils.get_projection_params(
                nwp_model_utils.NARR_MODEL_NAME))
        projection_object = projections.init_lambert_conformal_projection(
            standard_latitudes_deg=standard_latitudes_deg,
            central_longitude_deg=central_longitude_deg)

        grid_point_x_matrix_metres, grid_point_y_matrix_metres = (
            projections.project_latlng_to_xy(
                latitude_matrix_deg,
                longitude_matrix_deg,
                projection_object=projection_object,
                false_northing_metres=0.,
                false_easting_metres=0.))

    else:
        grid_point_x_matrix_metres, grid_point_y_matrix_metres = (
            basemap_object(longitude_matrix_deg, latitude_matrix_deg))

    return grid_point_x_matrix_metres, grid_point_y_matrix_metres
示例#24
0
def evaluate_tracks(storm_object_table, top_myrorss_dir_name,
                    radar_field_name):
    """Evaluates a set of storm tracks, following Lakshmanan and Smith (2010).

    T = number of storm tracks

    :param storm_object_table: pandas DataFrame with storm objects.  Should
        contain columns listed in `storm_tracking_io.write_file`.
    :param top_myrorss_dir_name: Name of top-level directory with MYRORSS data.
        Files therein will be found by `myrorss_and_mrms_io.find_raw_file` and
        read by `myrorss_and_mrms_io.read_data_from_sparse_grid_file`.
    :param radar_field_name: Name of radar field to use in computing mismatch
        error.  Must be accepted by `radar_utils.check_field_name`.
    :return: evaluation_dict: Dictionary with the following keys.
    evaluation_dict['track_durations_sec']: length-T numpy array of storm
        durations.
    evaluation_dict['track_linearity_errors_metres']: length-T numpy array of
        linearity errors.  The "linearity error" for one track is the RMSE
        (root mean square error) of Theil-Sen estimates over all time steps.
    evaluation_dict['track_mismatch_errors']: length-T numpy array of mismatch
        errors.  The "mismatch error" is the standard deviation of X over all
        time steps, where X = median value of `radar_field_name` inside the
        storm.
    evaluation_dict['mean_linearity_error_metres']: Mean linearity error.  This
        is the mean of trackwise linearity errors for tracks with duration >=
        median duration.
    evaluation_dict['mean_mismatch_error']: Mean mismatch error.  This is the
        mean of trackwise mismatch errors for tracks with duration >= median
        duration.
    evaluation_dict['radar_field_name']: Same as input (metadata).
    """

    # Add x-y coordinates.
    projection_object = projections.init_azimuthal_equidistant_projection(
        central_latitude_deg=echo_top_tracking.CENTRAL_PROJ_LATITUDE_DEG,
        central_longitude_deg=echo_top_tracking.CENTRAL_PROJ_LONGITUDE_DEG)

    x_coords_metres, y_coords_metres = projections.project_latlng_to_xy(
        latitudes_deg=storm_object_table[
            tracking_utils.CENTROID_LATITUDE_COLUMN].values,
        longitudes_deg=storm_object_table[
            tracking_utils.CENTROID_LONGITUDE_COLUMN].values,
        projection_object=projection_object,
        false_easting_metres=0.,
        false_northing_metres=0.)

    storm_object_table = storm_object_table.assign(
        **{
            tracking_utils.CENTROID_X_COLUMN: x_coords_metres,
            tracking_utils.CENTROID_Y_COLUMN: y_coords_metres
        })

    # Convert list of storm objects to list of tracks.
    storm_track_table = tracking_utils.storm_objects_to_tracks(
        storm_object_table)

    # Fit Theil-Sen model to each track.
    print(SEPARATOR_STRING)
    storm_track_table = _fit_theil_sen_many_tracks(storm_track_table)
    print(SEPARATOR_STRING)

    # Compute storm durations.
    num_tracks = len(storm_track_table.index)
    track_durations_sec = numpy.full(num_tracks, -1, dtype=int)

    for i in range(num_tracks):
        this_start_time_unix_sec = numpy.min(
            storm_track_table[tracking_utils.TRACK_TIMES_COLUMN].values[i])
        this_end_time_unix_sec = numpy.max(
            storm_track_table[tracking_utils.TRACK_TIMES_COLUMN].values[i])
        track_durations_sec[i] = (this_end_time_unix_sec -
                                  this_start_time_unix_sec)

    for this_percentile_level in DURATION_PERCENTILE_LEVELS:
        this_percentile_seconds = numpy.percentile(track_durations_sec,
                                                   this_percentile_level)

        print('{0:d}th percentile of track durations = {1:.1f} seconds'.format(
            int(numpy.round(this_percentile_level)), this_percentile_seconds))

    print('\n')

    for this_percentile_level in DURATION_PERCENTILE_LEVELS:
        this_percentile_seconds = numpy.percentile(
            track_durations_sec[track_durations_sec != 0],
            this_percentile_level)

        print(
            ('{0:d}th percentile of non-zero track durations = {1:.1f} seconds'
             ).format(int(numpy.round(this_percentile_level)),
                      this_percentile_seconds))

    print(SEPARATOR_STRING)

    median_duration_sec = numpy.median(
        track_durations_sec[track_durations_sec != 0])
    long_track_flags = track_durations_sec >= median_duration_sec
    long_track_indices = numpy.where(long_track_flags)[0]

    # Compute linearity error for each track.
    track_linearity_errors_metres = numpy.full(num_tracks, numpy.nan)

    for i in range(num_tracks):
        if numpy.mod(i, 50) == 0:
            print(('Have computed linearity error for {0:d} of {1:d} tracks...'
                   ).format(i, num_tracks))

        track_linearity_errors_metres[i] = _get_mean_ts_error_one_track(
            storm_track_table=storm_track_table, row_index=i)

    print('Have computed linearity error for all {0:d} tracks!'.format(
        num_tracks))

    good_indices = numpy.where(
        numpy.logical_and(
            long_track_flags,
            track_linearity_errors_metres <= MAX_LINEARITY_ERROR_METRES))[0]

    mean_linearity_error_metres = numpy.mean(
        track_linearity_errors_metres[good_indices])

    print('Mean linearity error = {0:.1f} metres'.format(
        mean_linearity_error_metres))
    print(SEPARATOR_STRING)

    # Compute mismatch error for each track.
    radar_statistic_table = (
        radar_stats.get_storm_based_radar_stats_myrorss_or_mrms(
            storm_object_table=storm_object_table,
            top_radar_dir_name=top_myrorss_dir_name,
            radar_metadata_dict_for_tracking=None,
            statistic_names=[],
            percentile_levels=numpy.array([50.]),
            radar_field_names=[radar_field_name],
            radar_source=radar_utils.MYRORSS_SOURCE_ID))

    print(SEPARATOR_STRING)

    radar_height_m_asl = radar_utils.get_valid_heights(
        data_source=radar_utils.MYRORSS_SOURCE_ID,
        field_name=radar_field_name)[0]

    median_column_name = radar_stats.radar_field_and_percentile_to_column_name(
        radar_field_name=radar_field_name,
        radar_height_m_asl=radar_height_m_asl,
        percentile_level=50.)

    median_by_storm_object = radar_statistic_table[median_column_name]
    track_mismatch_errors = numpy.full(num_tracks, numpy.nan)

    for i in range(num_tracks):
        these_object_indices = storm_track_table[
            tracking_utils.OBJECT_INDICES_COLUMN].values[i]

        if len(these_object_indices) < 2:
            continue

        track_mismatch_errors[i] = numpy.std(
            median_by_storm_object[these_object_indices], ddof=1)

    mean_mismatch_error = numpy.nanmean(
        track_mismatch_errors[long_track_indices])

    print('Mean mismatch error for "{0:s}" = {1:.4e}'.format(
        radar_field_name, mean_mismatch_error))

    return {
        DURATIONS_KEY: track_durations_sec,
        LINEARITY_ERRORS_KEY: track_linearity_errors_metres,
        MISMATCH_ERRORS_KEY: track_mismatch_errors,
        MEAN_LINEARITY_ERROR_KEY: mean_linearity_error_metres,
        MEAN_MISMATCH_ERROR_KEY: mean_mismatch_error,
        RADAR_FIELD_KEY: radar_field_name
    }