def project_xy_to_latlng(polygon_object_xy_metres, projection_object, false_easting_metres=0, false_northing_metres=0.): """Converts polygon from x-y to lat-long coordinates. :param polygon_object_xy_metres: `shapely.geometry.Polygon` object with vertices in x-y coordinates. :param projection_object: `pyproj.Proj` object. Will be used to convert coordinates. :param false_easting_metres: False easting (will be subtracted from all x- coordinates before converting). :param false_northing_metres: False northing (will be subtracted from all y- coordinates before converting). :return: polygon_object_latlng: `shapely.geometry.Polygon` object with vertices in lat-long coordinates. """ vertex_dict = polygon_object_to_vertex_arrays(polygon_object_xy_metres) vertex_dict[EXTERIOR_Y_COLUMN], vertex_dict[EXTERIOR_X_COLUMN] = ( projections.project_xy_to_latlng( vertex_dict[EXTERIOR_X_COLUMN], vertex_dict[EXTERIOR_Y_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_Y_COLUMN][i], vertex_dict[HOLE_X_COLUMN][i] = ( projections.project_xy_to_latlng( vertex_dict[HOLE_X_COLUMN][i], vertex_dict[HOLE_Y_COLUMN][i], projection_object=projection_object, false_easting_metres=false_easting_metres, false_northing_metres=false_northing_metres)) if num_holes == 0: polygon_object_latlng = vertex_arrays_to_polygon_object( vertex_dict[EXTERIOR_X_COLUMN], vertex_dict[EXTERIOR_Y_COLUMN]) else: polygon_object_latlng = 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_latlng
def project_xy_to_latlng(x_coords_metres, y_coords_metres, projection_object=None, model_name=None, grid_id=None): """Converts from x-y coordinates (under model projection) to lat-long. P = number of points to convert :param x_coords_metres: length-P numpy array of x-coordinates. :param y_coords_metres: length-P numpy array of y-coordinates. 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: latitudes_deg: length-P numpy array of latitudes (deg N). :return: longitudes_deg: length-P numpy array of longitudes (deg E). """ 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_xy_to_latlng( x_coords_metres, y_coords_metres, projection_object=projection_object, false_easting_metres=false_easting_metres, false_northing_metres=false_northing_metres)
def test_project_xy_to_latlng(self): """Ensures that project_xy_to_latlng 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_xy_to_latlng( X_COORDS_METRES, Y_COORDS_METRES, projection_object=projection_object, false_easting_metres=FALSE_EASTING_METRES, false_northing_metres=FALSE_NORTHING_METRES)
def project_xy_to_latlng(x_coords_metres, y_coords_metres, projection_object=None, model_name=None, grid_name=None): """Converts points from x-y (model) to lat-long coordinates. P = number of points :param x_coords_metres: length-P numpy array of x-coordinates. :param y_coords_metres: length-P numpy array of x-coordinates. :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: latitudes_deg: length-P numpy array of latitudes (deg N). :return: longitudes_deg: length-P numpy array of longitudes (deg E). """ 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_xy_to_latlng( x_coords_metres=x_coords_metres, y_coords_metres=y_coords_metres, projection_object=projection_object, false_easting_metres=false_easting_metres, false_northing_metres=false_northing_metres)
def test_project_xy_to_latlng(self): """Ensures that project_xy_to_latlng 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_xy_to_latlng( x_coords_metres=X_COORDS_METRES, y_coords_metres=Y_COORDS_METRES, projection_object=projection_object, false_easting_metres=FALSE_EASTING_METRES, false_northing_metres=FALSE_NORTHING_METRES)
def _get_basemap(grid_metadata_dict): """Creates basemap. M = number of rows in grid M = number of columns in grid :param grid_metadata_dict: Dictionary returned by `grids.read_equidistant_metafile`. :return: basemap_object: Basemap handle (instance of `mpl_toolkits.basemap.Basemap`). :return: basemap_x_matrix_metres: M-by-N numpy array of x-coordinates under Basemap projection (different than pyproj projection). :return: basemap_y_matrix_metres: Same but for y-coordinates. """ x_matrix_metres, y_matrix_metres = grids.xy_vectors_to_matrices( x_unique_metres=grid_metadata_dict[grids.X_COORDS_KEY], y_unique_metres=grid_metadata_dict[grids.Y_COORDS_KEY]) projection_object = grid_metadata_dict[grids.PROJECTION_KEY] latitude_matrix_deg, longitude_matrix_deg = ( projections.project_xy_to_latlng(x_coords_metres=x_matrix_metres, y_coords_metres=y_matrix_metres, projection_object=projection_object)) standard_latitudes_deg, central_longitude_deg = _get_lcc_params( projection_object) basemap_object = Basemap(projection='lcc', lat_1=standard_latitudes_deg[0], lat_2=standard_latitudes_deg[1], lon_0=central_longitude_deg, rsphere=projections.DEFAULT_EARTH_RADIUS_METRES, ellps=projections.SPHERE_NAME, resolution=RESOLUTION_STRING, llcrnrx=x_matrix_metres[0, 0], llcrnry=y_matrix_metres[0, 0], urcrnrx=x_matrix_metres[-1, -1], urcrnry=y_matrix_metres[-1, -1]) basemap_x_matrix_metres, basemap_y_matrix_metres = basemap_object( longitude_matrix_deg, latitude_matrix_deg) return basemap_object, basemap_x_matrix_metres, basemap_y_matrix_metres
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))
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
def _run(top_gridrad_dir_name, first_spc_date_string, last_spc_date_string, colour_map_name, grid_spacing_metres, output_file_name): """Plots GridRad domains. This is effectively the main method. :param top_gridrad_dir_name: See documentation at top of file. :param first_spc_date_string: Same. :param last_spc_date_string: Same. :param colour_map_name: Same. :param grid_spacing_metres: Same. :param output_file_name: Same. """ colour_map_object = pyplot.get_cmap(colour_map_name) file_system_utils.mkdir_recursive_if_necessary(file_name=output_file_name) first_time_unix_sec = time_conversion.get_start_of_spc_date( first_spc_date_string) last_time_unix_sec = time_conversion.get_end_of_spc_date( last_spc_date_string) valid_times_unix_sec = time_periods.range_and_interval_to_list( start_time_unix_sec=first_time_unix_sec, end_time_unix_sec=last_time_unix_sec, time_interval_sec=TIME_INTERVAL_SEC, include_endpoint=True) valid_spc_date_strings = [ time_conversion.time_to_spc_date_string(t) for t in valid_times_unix_sec ] domain_min_latitudes_deg = [] domain_max_latitudes_deg = [] domain_min_longitudes_deg = [] domain_max_longitudes_deg = [] prev_domain_limits_deg = numpy.full(4, numpy.nan) prev_spc_date_string = 'foo' num_times = len(valid_times_unix_sec) for i in range(num_times): this_gridrad_file_name = gridrad_io.find_file( unix_time_sec=valid_times_unix_sec[i], top_directory_name=top_gridrad_dir_name, raise_error_if_missing=False) if not os.path.isfile(this_gridrad_file_name): continue these_domain_limits_deg = _get_domain_one_file(this_gridrad_file_name) same_domain = (valid_spc_date_strings[i] == prev_spc_date_string and numpy.allclose(these_domain_limits_deg, prev_domain_limits_deg, TOLERANCE)) if same_domain: continue prev_domain_limits_deg = these_domain_limits_deg + 0. prev_spc_date_string = valid_spc_date_strings[i] domain_min_latitudes_deg.append(these_domain_limits_deg[0]) domain_max_latitudes_deg.append(these_domain_limits_deg[1]) domain_min_longitudes_deg.append(these_domain_limits_deg[2]) domain_max_longitudes_deg.append(these_domain_limits_deg[3]) print(SEPARATOR_STRING) domain_min_latitudes_deg = numpy.array(domain_min_latitudes_deg) domain_max_latitudes_deg = numpy.array(domain_max_latitudes_deg) domain_min_longitudes_deg = numpy.array(domain_min_longitudes_deg) domain_max_longitudes_deg = numpy.array(domain_max_longitudes_deg) num_domains = len(domain_min_latitudes_deg) grid_metadata_dict = grids.create_equidistant_grid( min_latitude_deg=OVERALL_MIN_LATITUDE_DEG, max_latitude_deg=OVERALL_MAX_LATITUDE_DEG, min_longitude_deg=OVERALL_MIN_LONGITUDE_DEG, max_longitude_deg=OVERALL_MAX_LONGITUDE_DEG, x_spacing_metres=grid_spacing_metres, y_spacing_metres=grid_spacing_metres, azimuthal=False) unique_x_coords_metres = grid_metadata_dict[grids.X_COORDS_KEY] unique_y_coords_metres = grid_metadata_dict[grids.Y_COORDS_KEY] projection_object = grid_metadata_dict[grids.PROJECTION_KEY] x_coord_matrix_metres, y_coord_matrix_metres = grids.xy_vectors_to_matrices( x_unique_metres=unique_x_coords_metres, y_unique_metres=unique_y_coords_metres) latitude_matrix_deg, longitude_matrix_deg = ( projections.project_xy_to_latlng(x_coords_metres=x_coord_matrix_metres, y_coords_metres=y_coord_matrix_metres, projection_object=projection_object)) num_grid_rows = latitude_matrix_deg.shape[0] num_grid_columns = latitude_matrix_deg.shape[1] num_days_matrix = numpy.full((num_grid_rows, num_grid_columns), 0) for i in range(num_domains): if numpy.mod(i, 10) == 0: print('Have found grid points in {0:d} of {1:d} domains...'.format( i, num_domains)) this_lat_flag_matrix = numpy.logical_and( latitude_matrix_deg >= domain_min_latitudes_deg[i], latitude_matrix_deg <= domain_max_latitudes_deg[i]) this_lng_flag_matrix = numpy.logical_and( longitude_matrix_deg >= domain_min_longitudes_deg[i], longitude_matrix_deg <= domain_max_longitudes_deg[i]) num_days_matrix += numpy.logical_and(this_lat_flag_matrix, this_lng_flag_matrix).astype(int) print(SEPARATOR_STRING) figure_object, axes_object = _plot_data( num_days_matrix=num_days_matrix, grid_metadata_dict=grid_metadata_dict, colour_map_object=colour_map_object) plotting_utils.label_axes(axes_object=axes_object, label_string='(c)') print('Saving figure to: "{0:s}"...'.format(output_file_name)) figure_object.savefig(output_file_name, dpi=FIGURE_RESOLUTION_DPI, pad_inches=0, bbox_inches='tight') pyplot.close(figure_object)
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 _run(input_shapefile_name, output_pickle_file_name): """Converts SPC convective outlook to nicer file format. This is effectively the main method. :param input_shapefile_name: See documentation at top of file. :param output_pickle_file_name: Same. """ projection_object = projections.init_lcc_projection( standard_latitudes_deg=STANDARD_LATITUDES_DEG, central_longitude_deg=CENTRAL_LONGITUDE_DEG, ellipsoid_name=ELLIPSOID_NAME) print('Reading data from: "{0:s}"...'.format(input_shapefile_name)) shapefile_handle = shapefile.Reader(input_shapefile_name) list_of_polygon_objects_latlng = [] risk_type_strings = [] for this_record_object in shapefile_handle.iterShapeRecords(): # print this_record_object.record this_risk_type_enum = this_record_object.record[RISK_TYPE_INDEX] try: this_risk_type_string = RISK_TYPE_ENUM_TO_STRING[ this_risk_type_enum] except KeyError: continue these_xy_tuples = this_record_object.shape.points this_num_vertices = len(these_xy_tuples) these_x_coords_metres = numpy.array( [these_xy_tuples[k][0] for k in range(this_num_vertices)]) these_y_coords_metres = numpy.array( [these_xy_tuples[k][1] for k in range(this_num_vertices)]) 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)) this_polygon_object_latlng = (polygons.vertex_arrays_to_polygon_object( exterior_x_coords=these_longitudes_deg, exterior_y_coords=these_latitudes_deg)) risk_type_strings.append(this_risk_type_string) list_of_polygon_objects_latlng.append(this_polygon_object_latlng) outlook_dict = { RISK_TYPE_COLUMN: risk_type_strings, POLYGON_COLUMN: list_of_polygon_objects_latlng } outlook_table = pandas.DataFrame.from_dict(outlook_dict) # print(outlook_table) print('Writing outlook polygons to file: "{0:s}"...'.format( output_pickle_file_name)) file_system_utils.mkdir_recursive_if_necessary( file_name=output_pickle_file_name) pickle_file_handle = open(output_pickle_file_name, 'wb') pickle.dump(outlook_table, pickle_file_handle) pickle_file_handle.close()