def _check_polygons(polygon_objects_grid_coords, num_panel_rows, num_panel_columns, panel_row_by_polygon, panel_column_by_polygon): """Error-checks list of polygons. :param polygon_objects_grid_coords: See doc for `polygons_from_pixel_to_grid_coords`. :param num_panel_rows: Same. :param num_panel_columns: Same. :param panel_row_by_polygon: Same. :param panel_column_by_polygon: Same. """ error_checking.assert_is_integer(num_panel_rows) error_checking.assert_is_greater(num_panel_rows, 0) error_checking.assert_is_integer(num_panel_columns) error_checking.assert_is_greater(num_panel_columns, 0) num_polygons = len(polygon_objects_grid_coords) if num_polygons == 0: return error_checking.assert_is_numpy_array(numpy.array( polygon_objects_grid_coords, dtype=object), num_dimensions=1) these_expected_dim = numpy.array([num_polygons], dtype=int) error_checking.assert_is_integer_numpy_array(panel_row_by_polygon) error_checking.assert_is_numpy_array(panel_row_by_polygon, exact_dimensions=these_expected_dim) error_checking.assert_is_geq_numpy_array(panel_row_by_polygon, 0) error_checking.assert_is_less_than_numpy_array(panel_row_by_polygon, num_panel_rows) error_checking.assert_is_integer_numpy_array(panel_column_by_polygon) error_checking.assert_is_numpy_array(panel_column_by_polygon, exact_dimensions=these_expected_dim) error_checking.assert_is_geq_numpy_array(panel_column_by_polygon, 0) error_checking.assert_is_less_than_numpy_array(panel_column_by_polygon, num_panel_columns)
def write_points(output_file_name, grid_row_by_point, grid_column_by_point, panel_row_by_point, panel_column_by_point, full_storm_id_string=None, storm_time_unix_sec=None): """Writes human points of interest for one image to NetCDF file. K = number of points of interest :param output_file_name: Path to output (NetCDF) file. :param grid_row_by_point: length-K numpy array of row indices in data grid (floats). :param grid_column_by_point: length-K numpy array of column indices in data grid (floats). :param panel_row_by_point: length-K numpy array of row indices in panel grid (integers). :param panel_column_by_point: length-K numpy array of column indices in panel grid (integers). :param full_storm_id_string: See doc for `write_polygons`. :param storm_time_unix_sec: Same. """ is_composite = (full_storm_id_string is None and storm_time_unix_sec is None) if is_composite: full_storm_id_string = DUMMY_STORM_ID_STRING storm_time_unix_sec = -1 error_checking.assert_is_string(full_storm_id_string) error_checking.assert_is_integer(storm_time_unix_sec) # error_checking.assert_is_integer(num_grid_rows) # error_checking.assert_is_greater(num_grid_rows, 0) # error_checking.assert_is_integer(num_grid_columns) # error_checking.assert_is_greater(num_grid_columns, 0) error_checking.assert_is_numpy_array(grid_row_by_point, num_dimensions=1) error_checking.assert_is_geq_numpy_array(grid_row_by_point, -0.5) # error_checking.assert_is_leq_numpy_array( # grid_row_by_point, num_grid_rows - 0.5) num_points = len(grid_row_by_point) these_expected_dim = numpy.array([num_points], dtype=int) error_checking.assert_is_numpy_array(grid_column_by_point, exact_dimensions=these_expected_dim) error_checking.assert_is_geq_numpy_array(grid_column_by_point, -0.5) # error_checking.assert_is_leq_numpy_array( # grid_column_by_point, num_grid_columns - 0.5) error_checking.assert_is_numpy_array(panel_row_by_point, exact_dimensions=these_expected_dim) error_checking.assert_is_integer_numpy_array(panel_row_by_point) error_checking.assert_is_geq_numpy_array(panel_row_by_point, 0) error_checking.assert_is_numpy_array(panel_column_by_point, exact_dimensions=these_expected_dim) error_checking.assert_is_integer_numpy_array(panel_column_by_point) error_checking.assert_is_geq_numpy_array(panel_column_by_point, 0) file_system_utils.mkdir_recursive_if_necessary(file_name=output_file_name) dataset_object = netCDF4.Dataset(output_file_name, 'w', format='NETCDF3_64BIT_OFFSET') dataset_object.setncattr(STORM_ID_KEY, full_storm_id_string) dataset_object.setncattr(STORM_TIME_KEY, storm_time_unix_sec) dataset_object.createDimension(POINT_DIMENSION_KEY, num_points) dataset_object.createVariable(GRID_ROW_BY_POINT_KEY, datatype=numpy.float32, dimensions=POINT_DIMENSION_KEY) dataset_object.variables[GRID_ROW_BY_POINT_KEY][:] = grid_row_by_point dataset_object.createVariable(GRID_COLUMN_BY_POINT_KEY, datatype=numpy.float32, dimensions=POINT_DIMENSION_KEY) dataset_object.variables[ GRID_COLUMN_BY_POINT_KEY][:] = grid_column_by_point dataset_object.createVariable(PANEL_ROW_BY_POINT_KEY, datatype=numpy.int32, dimensions=POINT_DIMENSION_KEY) dataset_object.variables[PANEL_ROW_BY_POINT_KEY][:] = panel_row_by_point dataset_object.createVariable(PANEL_COLUMN_BY_POINT_KEY, datatype=numpy.int32, dimensions=POINT_DIMENSION_KEY) dataset_object.variables[ PANEL_COLUMN_BY_POINT_KEY][:] = panel_column_by_point dataset_object.close()
def write_polygons(output_file_name, positive_objects_grid_coords, positive_panel_row_by_polygon, positive_panel_column_by_polygon, positive_mask_matrix, negative_objects_grid_coords=None, negative_panel_row_by_polygon=None, negative_panel_column_by_polygon=None, negative_mask_matrix=None, full_storm_id_string=None, storm_time_unix_sec=None): """Writes human polygons for one image to NetCDF file. P = number of positive regions of interest N = number of negative regions of interest :param output_file_name: Path to output (NetCDF) file. :param positive_objects_grid_coords: length-P list of polygons created by `polygons_from_pixel_to_grid_coords`, containing positive regions of interest. :param positive_panel_row_by_polygon: length-P numpy array of corresponding panel rows (non-negative integers). :param positive_panel_column_by_polygon: length-P numpy array of corresponding panel columns (non-negative integers). :param positive_mask_matrix: Binary mask for positive regions of interest, created by `polygons_to_mask`. :param negative_objects_grid_coords: length-N list of polygons created by `polygons_from_pixel_to_grid_coords`, containing negative regions of interest. :param negative_panel_row_by_polygon: length-N numpy array of corresponding panel rows (non-negative integers). :param negative_panel_column_by_polygon: length-N numpy array of corresponding panel columns (non-negative integers). :param negative_mask_matrix: Binary mask for negative regions of interest, created by `polygons_to_mask`. :param full_storm_id_string: Full storm ID (if polygons were drawn for composite, this should be None). :param storm_time_unix_sec: Valid time (if polygons were drawn for composite, this should be None). """ is_composite = (full_storm_id_string is None and storm_time_unix_sec is None) if is_composite: full_storm_id_string = DUMMY_STORM_ID_STRING storm_time_unix_sec = -1 error_checking.assert_is_string(full_storm_id_string) error_checking.assert_is_integer(storm_time_unix_sec) error_checking.assert_is_boolean_numpy_array(positive_mask_matrix) error_checking.assert_is_numpy_array(positive_mask_matrix, num_dimensions=4) _check_polygons(polygon_objects_grid_coords=positive_objects_grid_coords, num_panel_rows=positive_mask_matrix.shape[0], num_panel_columns=positive_mask_matrix.shape[1], panel_row_by_polygon=positive_panel_row_by_polygon, panel_column_by_polygon=positive_panel_column_by_polygon) if negative_objects_grid_coords is None: negative_objects_grid_coords = [] negative_panel_row_by_polygon = numpy.array([], dtype=int) negative_panel_column_by_polygon = numpy.array([], dtype=int) negative_mask_matrix = numpy.full(positive_mask_matrix.shape, False, dtype=bool) error_checking.assert_is_boolean_numpy_array(negative_mask_matrix) error_checking.assert_is_numpy_array(negative_mask_matrix, exact_dimensions=numpy.array( positive_mask_matrix.shape, dtype=int)) _check_polygons(polygon_objects_grid_coords=negative_objects_grid_coords, num_panel_rows=negative_mask_matrix.shape[0], num_panel_columns=negative_mask_matrix.shape[1], panel_row_by_polygon=negative_panel_row_by_polygon, panel_column_by_polygon=negative_panel_column_by_polygon) (positive_vertex_rows, positive_vertex_columns, these_vertex_to_poly_indices ) = _polygon_list_to_vertex_list(positive_objects_grid_coords) positive_panel_row_by_vertex = numpy.array([ positive_panel_row_by_polygon[k] if k >= 0 else numpy.nan for k in these_vertex_to_poly_indices ]) positive_panel_column_by_vertex = numpy.array([ positive_panel_column_by_polygon[k] if k >= 0 else numpy.nan for k in these_vertex_to_poly_indices ]) if len(positive_vertex_rows) == 0: positive_vertex_rows = numpy.full(1, SENTINEL_VALUE - 1) positive_vertex_columns = numpy.full(1, SENTINEL_VALUE - 1) positive_panel_row_by_vertex = numpy.full(1, -1, dtype=int) positive_panel_column_by_vertex = numpy.full(1, -1, dtype=int) (negative_vertex_rows, negative_vertex_columns, these_vertex_to_poly_indices ) = _polygon_list_to_vertex_list(negative_objects_grid_coords) negative_panel_row_by_vertex = numpy.array([ negative_panel_row_by_polygon[k] if k >= 0 else numpy.nan for k in these_vertex_to_poly_indices ]) negative_panel_column_by_vertex = numpy.array([ negative_panel_column_by_polygon[k] if k >= 0 else numpy.nan for k in these_vertex_to_poly_indices ]) file_system_utils.mkdir_recursive_if_necessary(file_name=output_file_name) dataset_object = netCDF4.Dataset(output_file_name, 'w', format='NETCDF3_64BIT_OFFSET') dataset_object.setncattr(STORM_ID_KEY, full_storm_id_string) dataset_object.setncattr(STORM_TIME_KEY, storm_time_unix_sec) dataset_object.createDimension(PANEL_ROW_DIMENSION_KEY, positive_mask_matrix.shape[0]) dataset_object.createDimension(PANEL_COLUMN_DIMENSION_KEY, positive_mask_matrix.shape[1]) dataset_object.createDimension(GRID_ROW_DIMENSION_KEY, positive_mask_matrix.shape[2]) dataset_object.createDimension(GRID_COLUMN_DIMENSION_KEY, positive_mask_matrix.shape[3]) dataset_object.createDimension(POSITIVE_VERTEX_DIM_KEY, len(positive_vertex_rows)) dataset_object.createDimension(NEGATIVE_VERTEX_DIM_KEY, len(negative_vertex_rows)) dataset_object.createVariable(POSITIVE_VERTEX_ROWS_KEY, datatype=numpy.float32, dimensions=POSITIVE_VERTEX_DIM_KEY) dataset_object.variables[ POSITIVE_VERTEX_ROWS_KEY][:] = positive_vertex_rows dataset_object.createVariable(POSITIVE_VERTEX_COLUMNS_KEY, datatype=numpy.float32, dimensions=POSITIVE_VERTEX_DIM_KEY) dataset_object.variables[POSITIVE_VERTEX_COLUMNS_KEY][:] = ( positive_vertex_columns) dataset_object.createVariable(POSITIVE_PANEL_ROW_BY_VERTEX_KEY, datatype=numpy.int32, dimensions=POSITIVE_VERTEX_DIM_KEY) dataset_object.variables[ POSITIVE_PANEL_ROW_BY_VERTEX_KEY][:] = positive_panel_row_by_vertex dataset_object.createVariable(POSITIVE_PANEL_COLUMN_BY_VERTEX_KEY, datatype=numpy.int32, dimensions=POSITIVE_VERTEX_DIM_KEY) dataset_object.variables[ POSITIVE_PANEL_COLUMN_BY_VERTEX_KEY][:] = positive_panel_column_by_vertex dataset_object.createVariable(NEGATIVE_VERTEX_ROWS_KEY, datatype=numpy.float32, dimensions=NEGATIVE_VERTEX_DIM_KEY) dataset_object.variables[ NEGATIVE_VERTEX_ROWS_KEY][:] = negative_vertex_rows dataset_object.createVariable(NEGATIVE_VERTEX_COLUMNS_KEY, datatype=numpy.float32, dimensions=NEGATIVE_VERTEX_DIM_KEY) dataset_object.variables[NEGATIVE_VERTEX_COLUMNS_KEY][:] = ( negative_vertex_columns) dataset_object.createVariable(NEGATIVE_PANEL_ROW_BY_VERTEX_KEY, datatype=numpy.int32, dimensions=NEGATIVE_VERTEX_DIM_KEY) dataset_object.variables[ NEGATIVE_PANEL_ROW_BY_VERTEX_KEY][:] = negative_panel_row_by_vertex dataset_object.createVariable(NEGATIVE_PANEL_COLUMN_BY_VERTEX_KEY, datatype=numpy.int32, dimensions=NEGATIVE_VERTEX_DIM_KEY) dataset_object.variables[ NEGATIVE_PANEL_COLUMN_BY_VERTEX_KEY][:] = negative_panel_column_by_vertex dataset_object.createVariable( POSITIVE_MASK_MATRIX_KEY, datatype=numpy.int32, dimensions=(PANEL_ROW_DIMENSION_KEY, PANEL_COLUMN_DIMENSION_KEY, GRID_ROW_DIMENSION_KEY, GRID_COLUMN_DIMENSION_KEY)) dataset_object.variables[POSITIVE_MASK_MATRIX_KEY][:] = ( positive_mask_matrix.astype(int)) dataset_object.createVariable( NEGATIVE_MASK_MATRIX_KEY, datatype=numpy.int32, dimensions=(PANEL_ROW_DIMENSION_KEY, PANEL_COLUMN_DIMENSION_KEY, GRID_ROW_DIMENSION_KEY, GRID_COLUMN_DIMENSION_KEY)) dataset_object.variables[NEGATIVE_MASK_MATRIX_KEY][:] = ( negative_mask_matrix.astype(int)) dataset_object.close()
def pixel_columns_to_grid_columns(pixel_column_by_vertex, num_pixel_columns, num_panel_columns, num_grid_columns, assert_same_panel): """Converts pixel columns to grid columns. V = number of vertices in object :param pixel_column_by_vertex: length-V numpy array with column coordinates of vertices in pixel space. :param num_pixel_columns: Total number of pixel columns in image. :param num_panel_columns: Total number of panel columns in image. :param num_grid_columns: Total number of columns in grid (one grid per panel). :param assert_same_panel: Boolean flag. If True, all vertices must be in the same panel. :return: grid_column_by_vertex: length-V numpy array with column coordinates (floats) of vertices in grid space. :return: panel_column_by_vertex: length-V numpy array with column coordinates (integers) of vertices in panel space. """ error_checking.assert_is_integer(num_pixel_columns) error_checking.assert_is_greater(num_pixel_columns, 0) error_checking.assert_is_integer(num_panel_columns) error_checking.assert_is_greater(num_panel_columns, 0) error_checking.assert_is_integer(num_grid_columns) error_checking.assert_is_greater(num_grid_columns, 0) error_checking.assert_is_boolean(assert_same_panel) error_checking.assert_is_numpy_array(pixel_column_by_vertex, num_dimensions=1) pixel_column_by_vertex += 0.5 error_checking.assert_is_geq_numpy_array(pixel_column_by_vertex, 0.) error_checking.assert_is_leq_numpy_array(pixel_column_by_vertex, num_pixel_columns) panel_column_to_first_px_column = {} for j in range(num_panel_columns): panel_column_to_first_px_column[j] = (j * float(num_pixel_columns) / num_panel_columns) panel_column_by_vertex = numpy.floor(pixel_column_by_vertex * float(num_panel_columns) / num_pixel_columns).astype(int) panel_column_by_vertex[panel_column_by_vertex == num_panel_columns] = num_panel_columns - 1 if assert_same_panel and len(numpy.unique(panel_column_by_vertex)) > 1: error_string = ( 'Object is in multiple panels. Panel columns listed below.\n{0:s}' ).format(str(panel_column_by_vertex)) raise ValueError(error_string) num_vertices = len(pixel_column_by_vertex) for i in range(num_vertices): pixel_column_by_vertex[i] = ( pixel_column_by_vertex[i] - panel_column_to_first_px_column[panel_column_by_vertex[i]]) grid_column_by_vertex = -0.5 + (pixel_column_by_vertex * float( num_grid_columns * num_panel_columns) / num_pixel_columns) return grid_column_by_vertex, panel_column_by_vertex
def polygons_to_mask(polygon_objects_grid_coords, num_grid_rows, num_grid_columns, num_panel_rows, num_panel_columns, panel_row_by_polygon, panel_column_by_polygon): """Converts list of polygons to one binary mask for each panel. M = number of rows in grid N = number of columns in grid J = number of panel rows in image K = number of panel columns in image :param polygon_objects_grid_coords: See doc for `polygons_from_pixel_to_grid_coords`. :param num_grid_rows: Same. :param num_grid_columns: Same. :param num_panel_rows: Same. :param num_panel_columns: Same. :param panel_row_by_polygon: Same. :param panel_column_by_polygon: Same. :return: mask_matrix: J-by-K-by-M-by-N numpy array of Boolean flags. If mask_matrix[j, k, m, n] == True, grid point [m, n] in panel [j, k] is in/on at least one of the polygons. """ error_checking.assert_is_integer(num_grid_rows) error_checking.assert_is_greater(num_grid_rows, 0) error_checking.assert_is_integer(num_grid_columns) error_checking.assert_is_greater(num_grid_columns, 0) _check_polygons(polygon_objects_grid_coords=polygon_objects_grid_coords, num_panel_rows=num_panel_rows, num_panel_columns=num_panel_columns, panel_row_by_polygon=panel_row_by_polygon, panel_column_by_polygon=panel_column_by_polygon) mask_matrix = numpy.full( (num_panel_rows, num_panel_columns, num_grid_rows, num_grid_columns), False, dtype=bool) num_polygons = len(polygon_objects_grid_coords) if num_polygons == 0: return mask_matrix panel_coord_matrix = numpy.hstack( (numpy.reshape(panel_row_by_polygon, (num_polygons, 1)), numpy.reshape(panel_column_by_polygon, (num_polygons, 1)))) panel_coord_matrix = numpy.unique(panel_coord_matrix.astype(int), axis=0) for i in range(panel_coord_matrix.shape[0]): this_panel_row = panel_coord_matrix[i, 0] this_panel_column = panel_coord_matrix[i, 1] these_polygon_indices = numpy.where( numpy.logical_and(panel_row_by_polygon == this_panel_row, panel_column_by_polygon == this_panel_column))[0] these_polygon_objects = [ polygon_objects_grid_coords[k] for k in these_polygon_indices ] mask_matrix[this_panel_row, this_panel_column, ...] = (_polygons_to_mask_one_panel( polygon_objects_grid_coords=these_polygon_objects, num_grid_rows=num_grid_rows, num_grid_columns=num_grid_columns)) return mask_matrix
def test_assert_is_integer_none(self): """Checks assert_is_integer when input is None.""" with self.assertRaises(TypeError): error_checking.assert_is_integer(None)
def test_assert_is_integer_true(self): """Checks assert_is_integer when input is integer.""" error_checking.assert_is_integer(SINGLE_INTEGER)
def test_assert_is_integer_nan(self): """Checks assert_is_integer when input is NaN.""" with self.assertRaises(TypeError): error_checking.assert_is_integer(numpy.nan)
def test_assert_is_integer_complex(self): """Checks assert_is_integer when input is complex.""" with self.assertRaises(TypeError): error_checking.assert_is_integer(SINGLE_COMPLEX_NUMBER)
def test_assert_is_integer_boolean(self): """Checks assert_is_integer when input is Boolean.""" with self.assertRaises(TypeError): error_checking.assert_is_integer(SINGLE_BOOLEAN)
def test_assert_is_integer_float(self): """Checks assert_is_integer when input is float.""" with self.assertRaises(TypeError): error_checking.assert_is_integer(SINGLE_FLOAT)
def test_assert_is_integer_too_many_inputs(self): """Checks assert_is_integer when input is array of integers.""" with self.assertRaises(TypeError): error_checking.assert_is_integer(INTEGER_NUMPY_ARRAY)