def update_nearest_points( points_with_mismatches: ndarray, in_latlons: ndarray, out_latlons: ndarray, indexes: ndarray, distances: ndarray, surface_type_mask: ndarray, in_classified: ndarray, out_classified: ndarray, ) -> Tuple[ndarray, ndarray, ndarray]: """ Update nearest source points and distances/surface_type to take into account surface type of nearby points. Args: points_with_mismatches: Selected target points which will use Inverse Distance Weighting (idw) approach. These points will be processed by this function. in_latlons: Source points's latitude-longitudes. out_latlons: Target points's latitude-longitudes. indexes: Source grid point indexes for each target grid point. distances: Distance from each target grid point to its source grid points. surface_type_mask: Boolean true if source point type matches target point type. in_classified: Land/sea type for source grid points (land -> True). out_classified: Land/sea type for target grid points (land -> True). Returns: - Updated indexes - source grid point number for all target grid points. - Updated distances - array from each target grid point to its source grid points. - Updated surface_type_mask - matching info between source/target point types. """ # Gather output points with mismatched surface type and find four nearest input # points via KDtree out_latlons_with_mismatches = out_latlons[points_with_mismatches] k_nearest = 4 distances_updates, indexes_updates = nearest_input_pts( in_latlons, out_latlons_with_mismatches, k_nearest) # Calculate update to surface classification at mismatched points out_classified_with_mismatches = out_classified[points_with_mismatches] surface_type_mask_updates = similar_surface_classify( in_classified, out_classified_with_mismatches, indexes_updates) # Apply updates to indexes, distances and surface type mask indexes[points_with_mismatches] = indexes_updates distances[points_with_mismatches] = distances_updates surface_type_mask[points_with_mismatches] = surface_type_mask_updates return indexes, distances, surface_type_mask
def lakes_islands( lake_island_indexes: ndarray, indexes: ndarray, surface_type_mask: ndarray, in_latlons: ndarray, out_latlons: ndarray, in_classified: ndarray, out_classified: ndarray, vicinity: float, ) -> Tuple[ndarray, ndarray]: """ Updating source points and weighting for 4-unmatching-source-point cases - water surrounded by land or land surrounded by water. This function searches nearest 8 points to check if any matching point exists. Note that a similar function can be found in bilinear.py for bilinear regridding rather than nearest neighbour regridding. Args: lake_island_indexes: Indexes of points which are lakes/islands surrounded by mismatched surface type. These points will be processed by this function. in_latlons: Source points's latitude-longitudes. out_latlons: Target points's latitude-longitudes. surface_type_mask: Boolean true if source point type matches target point type. indexes: Source grid point indexes for each target grid point. in_classified: Land/sea type for source grid points (land -> True). out_classified: Land/sea type for target grid points (land -> True). vicinity: Radius of vicinity to search for a matching surface type, in metres. Returns: - Updated indexes - source grid point number for all target grid points. - Updated surface_type_mask - matching info between source/target point types. """ out_latlons_updates = out_latlons[lake_island_indexes] # Consider a larger area of 8 nearest points to look for more distant same # surface type input points. # more than 8 points are within searching limits not considered here k_nearest = 8 distances_updates, indexes_updates = nearest_input_pts( in_latlons, out_latlons_updates, k_nearest) # Update output surface classification and surface type mask out_classified_updates = out_classified[lake_island_indexes] surface_type_mask_updates = similar_surface_classify( in_classified, out_classified_updates, indexes_updates) # Where distance is outside specified vicinity, set surface type to be mismatched # so that it will not be used, update surface type mask again distance_not_in_vicinity = distances_updates > vicinity surface_type_mask_updates = np.where(distance_not_in_vicinity, False, surface_type_mask_updates) count_matching_surface = np.count_nonzero(surface_type_mask_updates, axis=1) points_with_no_match = (np.where(count_matching_surface == 0))[0] if points_with_no_match.shape[0] > 0: # No improved input point has been found with the increase to 8 nearest points # Take the original nearest point, disregard the surface type no_match_indexes = lake_island_indexes[points_with_no_match] surface_type_mask[no_match_indexes, :] = True # From the expansion to 8 nearby input points, a same surface type input has been found # Update the index and surface type mask to use the newly found same surface type input point points_with_match = (np.where(count_matching_surface > 0))[0] count_of_points_with_match = points_with_match.shape[0] for point_idx in range(count_of_points_with_match): # Reset all input surface types to mismatched match_indexes = lake_island_indexes[points_with_match[point_idx]] surface_type_mask[match_indexes, :] = False # Loop through 8 nearest points found for i in range(k_nearest): # Look for an input point with same surface type as output if surface_type_mask_updates[points_with_match[point_idx], i]: # When found, update the indexes and surface mask to use that improved input point indexes[match_indexes, 0] = indexes_updates[points_with_match[point_idx], i] surface_type_mask[match_indexes, 0] = True break return indexes, surface_type_mask
def lakes_islands( lake_island_indexes: ndarray, weights: ndarray, indexes: ndarray, surface_type_mask: ndarray, in_latlons: ndarray, out_latlons: ndarray, in_classified: ndarray, out_classified: ndarray, in_lons_size: int, vicinity: float, lat_spacing: float, lon_spacing: float, ) -> Tuple[ndarray, ndarray, ndarray]: """ Updating source points and weighting for 4-false-source-point cases. These are lakes (water surrounded by land) and islands (land surrounded by water). Note that a similar function can be found in nearest.py for nearest neighbour regridding rather than bilinear regridding. Args: lake_island_indexes: Selected target points which have 4 false source points. in_latlons: Source points's latitude-longitudes. out_latlons: Target points's latitude-longitudes. surface_type_mask: Numpy ndarray of bool, true if source point type matches target point type. indexes: Array of source grid point number for all target grid points. weights: Array of source grid point weighting for all target grid points. in_classified: Land_sea type for source grid points (land ->True). out_classified: Land_sea type for terget grid points (land ->True). in_lons_size: Source grid's longitude dimension. vicinity: Radius of specified searching domain, in meter. lat_spacing: Input grid latitude spacing, in degree. lon_spacing: Input grid longitude spacing, in degree. Returns: - Updated weights - source point weighting for all target grid points. - Updated indexes - source grid point number for all target grid points. - Updated surface_type_mask - matching info between source/target point types. """ # increase 4 points to 8 points out_latlons_updates = out_latlons[lake_island_indexes] # Consider a larger area of 8 nearest points to look for more distant same # surface type input points # more than 8 points are within searching limits not considered here k_nearest = 8 distances_updates, indexes_updates = nearest_input_pts( in_latlons, out_latlons_updates, k_nearest) # Update output surface classification and surface type mask out_classified_updates = out_classified[lake_island_indexes] surface_type_mask_updates = similar_surface_classify( in_classified, out_classified_updates, indexes_updates) # Where distance is outside specified vicinity, set surface type to be mismatched # so that it will not be used, update surface type mask again distance_not_in_vicinity = distances_updates > vicinity surface_type_mask_updates = np.where(distance_not_in_vicinity, False, surface_type_mask_updates) count_matching_surface = np.count_nonzero(surface_type_mask_updates, axis=1) points_with_no_match = np.where(count_matching_surface == 0)[0] # If the expanded search area hasn't found any same surface type matches anywhere # just ignore surface type, use normal bilinear approach if points_with_no_match.shape[0] > 0: # revert back to the basic bilinear weights, indexes unchanged no_match_indexes = lake_island_indexes[points_with_no_match] weights[no_match_indexes] = basic_weights( no_match_indexes, indexes, out_latlons, in_latlons, lat_spacing, lon_spacing, ) points_with_match = np.where(count_matching_surface > 0)[0] count_of_points_with_match = points_with_match.shape[0] # if no further processing can be done, return early if count_of_points_with_match == 0: return weights, indexes, surface_type_mask # Where a same surface type match has been found among the 8 nearest inputs, apply # inverse distance weighting with those matched points new_distances = np.zeros([count_of_points_with_match, NUM_NEIGHBOURS]) for point_idx in range(points_with_match.shape[0]): match_indexes = lake_island_indexes[points_with_match[point_idx]] # Reset all input weight and surface type to mismatched weights[match_indexes, :] = 0.0 surface_type_mask[match_indexes, :] = False # Loop through 8 nearest points found good_count = 0 for i in range(k_nearest): # Look for an input point with same surface type as output if surface_type_mask_updates[points_with_match[point_idx], i]: # When found, update the indexes and surface mask to use that improved input point indexes[match_indexes, good_count] = indexes_updates[ points_with_match[point_idx], i] surface_type_mask[match_indexes, good_count] = True # other mask =false new_distances[point_idx, good_count] = distances_updates[ points_with_match[point_idx], i] good_count += 1 # Use a maximum of four same surface type input points # This is kind of like how bilinear uses four nearby input points if good_count == 4: break lake_island_with_match = lake_island_indexes[points_with_match] # Similar to inverse_distance_weighting in idw.py not_mask = np.logical_not(surface_type_mask[lake_island_with_match]) masked_distances = np.where(not_mask, np.float32(np.inf), new_distances) masked_distances += np.finfo(np.float32).eps inv_distances = 1.0 / masked_distances # add power 1.80 for inverse diatance weight inv_distances_power = np.power(inv_distances, OPTIMUM_IDW_POWER) inv_distances_sum = np.sum(inv_distances_power, axis=1) inv_distances_sum = 1.0 / inv_distances_sum weights_idw = inv_distances_power * inv_distances_sum.reshape(-1, 1) weights[lake_island_with_match] = weights_idw return weights, indexes, surface_type_mask