def _find_one_centroid_distance(storm_x_vertices_metres, storm_y_vertices_metres, warning_polygon_object_xy): """Finds distance between one storm object and one warning. V = number of vertices in storm outline :param storm_x_vertices_metres: length-V numpy array of x-coordinates. :param storm_y_vertices_metres: length-V numpy array of y-coordinates. :param warning_polygon_object_xy: Polygon (instance of `shapely.geometry.Polygon`) with x-y coordinates of warning boundary. :return: distance_metres: Distance between storm object and warning (minimum distance to polygon interior over all storm vertices). """ centroid_x_metres = numpy.mean(storm_x_vertices_metres) centroid_y_metres = numpy.mean(storm_y_vertices_metres) pip_flag = polygons.point_in_or_on_polygon( polygon_object=warning_polygon_object_xy, query_x_coordinate=centroid_x_metres, query_y_coordinate=centroid_y_metres) if pip_flag: return 0. point_object = shapely.geometry.Point(centroid_x_metres, centroid_y_metres) return point_object.distance(warning_polygon_object_xy)
def _find_one_polygon_distance(storm_x_vertices_metres, storm_y_vertices_metres, warning_polygon_object_xy): """Finds distance between one storm object and one warning. V = number of vertices in storm outline :param storm_x_vertices_metres: length-V numpy array of x-coordinates. :param storm_y_vertices_metres: length-V numpy array of y-coordinates. :param warning_polygon_object_xy: Polygon (instance of `shapely.geometry.Polygon`) with x-y coordinates of warning boundary. :return: distance_metres: Distance between storm object and warning (minimum distance to polygon interior over all storm vertices). """ num_vertices = len(storm_x_vertices_metres) distance_metres = LARGE_NUMBER for k in range(num_vertices): this_flag = polygons.point_in_or_on_polygon( polygon_object=warning_polygon_object_xy, query_x_coordinate=storm_x_vertices_metres[k], query_y_coordinate=storm_y_vertices_metres[k]) if this_flag: return 0. this_point_object = shapely.geometry.Point(storm_x_vertices_metres[k], storm_y_vertices_metres[k]) this_distance_metres = this_point_object.distance( warning_polygon_object_xy) distance_metres = numpy.minimum(distance_metres, this_distance_metres) return distance_metres
def test_point_in_or_on_polygon_inside(self): """Ensures correct output from point_in_or_on_polygon. In this case, answer = True (point inside polygon). """ this_flag = polygons.point_in_or_on_polygon( POLYGON_OBJECT_EXCL_BUFFER_XY_METRES, query_x_coordinate=X_IN_NESTED_BUFFER, query_y_coordinate=Y_IN_NESTED_BUFFER) self.assertTrue(this_flag)
def test_point_in_or_on_polygon_false(self): """Ensures correct output from point_in_or_on_polygon. In this case, answer = False. """ this_flag = polygons.point_in_or_on_polygon( POLYGON_OBJECT_EXCL_BUFFER_XY_METRES, query_x_coordinate=X_OUTSIDE_NESTED_BUFFER, query_y_coordinate=Y_OUTSIDE_NESTED_BUFFER) self.assertFalse(this_flag)
def _polygons_to_mask_one_panel(polygon_objects_grid_coords, num_grid_rows, num_grid_columns): """Converts list of polygons to binary mask. M = number of rows in grid N = number of columns in grid :param polygon_objects_grid_coords: See doc for `polygons_from_pixel_to_grid_coords`. :param num_grid_rows: Same. :param num_grid_columns: Same. :return: mask_matrix: M-by-N numpy array of Boolean flags. If mask_matrix[i, j] == True, grid point [i, j] is in/on at least one of the polygons. """ mask_matrix = numpy.full((num_grid_rows, num_grid_columns), False, dtype=bool) num_polygons = len(polygon_objects_grid_coords) if num_polygons == 0: return mask_matrix # TODO(thunderhoser): This triple for-loop is probably inefficient. for k in range(num_polygons): these_grid_columns = numpy.array( polygon_objects_grid_coords[k].exterior.xy[0]) error_checking.assert_is_geq_numpy_array(these_grid_columns, -0.5) error_checking.assert_is_leq_numpy_array(these_grid_columns, num_grid_columns - 0.5) these_grid_rows = numpy.array( polygon_objects_grid_coords[k].exterior.xy[1]) error_checking.assert_is_geq_numpy_array(these_grid_rows, -0.5) error_checking.assert_is_leq_numpy_array(these_grid_rows, num_grid_rows - 0.5) for i in range(num_grid_rows): for j in range(num_grid_columns): if mask_matrix[i, j]: continue mask_matrix[i, j] = polygons.point_in_or_on_polygon( polygon_object=polygon_objects_grid_coords[k], query_x_coordinate=j, query_y_coordinate=i) return mask_matrix
def find_points_in_conus(conus_latitudes_deg, conus_longitudes_deg, query_latitudes_deg, query_longitudes_deg, use_shortcuts=True, verbose=False): """Finds points in CONUS. Q = number of query points This method assumes that the domain doesn't wrap around 0 deg E (Greenwich). If you set `use_shortcuts = True`, this method will assume that input coordinates `conus_latitudes_deg` and `conus_longitudes_deg` have been eroded by less than 100 km. :param conus_latitudes_deg: See doc for `_check_boundary`. :param conus_longitudes_deg: Same. :param query_latitudes_deg: length-Q numpy with latitudes (deg N) of query points. :param query_longitudes_deg: length-Q numpy with longitudes (deg E) of query points. :param use_shortcuts: Boolean flag. If True, will use shortcuts to speed up calculation. :param verbose: Boolean flag. If True, will print progress messages to command window. :return: in_conus_flags: length-Q numpy array of Boolean flags. """ conus_longitudes_deg = _check_boundary(latitudes_deg=conus_latitudes_deg, longitudes_deg=conus_longitudes_deg) query_longitudes_deg = _check_boundary(latitudes_deg=query_latitudes_deg, longitudes_deg=query_longitudes_deg) error_checking.assert_is_boolean(use_shortcuts) error_checking.assert_is_boolean(verbose) num_query_points = len(query_latitudes_deg) in_conus_flags = numpy.full(num_query_points, -1, dtype=int) if use_shortcuts: # Use rectangle. latitude_flags = numpy.logical_and( query_latitudes_deg >= SHORTCUT_BOX_LATITUDES_DEG[0], query_latitudes_deg <= SHORTCUT_BOX_LATITUDES_DEG[1]) longitude_flags = numpy.logical_and( query_longitudes_deg >= SHORTCUT_BOX_LONGITUDES_DEG[0], query_longitudes_deg <= SHORTCUT_BOX_LONGITUDES_DEG[1]) in_conus_flags[numpy.logical_and(latitude_flags, longitude_flags)] = 1 # Use simplified eroded boundary. module_dir_name = os.path.dirname(__file__) parent_dir_name = '/'.join(module_dir_name.split('/')[:-1]) inner_boundary_file_name = ( '{0:s}/conus_polygon_100-km-eroded.nc'.format(parent_dir_name)) inner_conus_latitudes_deg, inner_conus_longitudes_deg = ( read_from_netcdf(inner_boundary_file_name)) trial_indices = numpy.where(in_conus_flags == -1)[0] these_flags = find_points_in_conus( conus_latitudes_deg=inner_conus_latitudes_deg, conus_longitudes_deg=inner_conus_longitudes_deg, query_latitudes_deg=query_latitudes_deg[trial_indices], query_longitudes_deg=query_longitudes_deg[trial_indices], use_shortcuts=False) these_indices = trial_indices[numpy.where(these_flags)] in_conus_flags[these_indices] = 1 # Use simplified dilated boundary. module_dir_name = os.path.dirname(__file__) parent_dir_name = '/'.join(module_dir_name.split('/')[:-1]) outer_boundary_file_name = ( '{0:s}/conus_polygon_100-km-dilated.nc'.format(parent_dir_name)) outer_conus_latitudes_deg, outer_conus_longitudes_deg = ( read_from_netcdf(outer_boundary_file_name)) trial_indices = numpy.where(in_conus_flags == -1)[0] these_flags = find_points_in_conus( conus_latitudes_deg=outer_conus_latitudes_deg, conus_longitudes_deg=outer_conus_longitudes_deg, query_latitudes_deg=query_latitudes_deg[trial_indices], query_longitudes_deg=query_longitudes_deg[trial_indices], use_shortcuts=False) these_indices = trial_indices[numpy.where(numpy.invert(these_flags))] in_conus_flags[these_indices] = 0 conus_polygon_object = polygons.vertex_arrays_to_polygon_object( exterior_x_coords=conus_longitudes_deg, exterior_y_coords=conus_latitudes_deg) for i in range(num_query_points): if numpy.mod(i, 1000) == 0 and verbose: print(('Have done point-in-CONUS test for {0:d} of {1:d} points...' ).format(i, num_query_points)) if in_conus_flags[i] != -1: continue in_conus_flags[i] = polygons.point_in_or_on_polygon( polygon_object=conus_polygon_object, query_x_coordinate=query_longitudes_deg[i], query_y_coordinate=query_latitudes_deg[i]) if verbose: print('Have done point-in-CONUS test for all {0:d} points!'.format( num_query_points)) return in_conus_flags.astype(bool)