def get_coordinates(eopatch, feature, crs): """ Creates coordinates for xarray DataArray :param eopatch: eopatch :type eopatch: EOPatch :param feature: feature of eopatch :type feature: (FeatureType, str) :param crs: convert spatial coordinates to crs :type crs: sentinelhub.crs :return: coordinates for xarry DataArray/Dataset :rtype: dict """ features = list(FeatureParser(feature)) feature_type, feature_name = features[0] original_crs = eopatch.bbox.crs if crs and original_crs != crs: bbox = eopatch.bbox.transform(crs) else: bbox = eopatch.bbox data = eopatch[feature_type][feature_name] timestamps = eopatch.timestamp if feature_type in FeatureTypeSet.RASTER_TYPES_4D: return {**_get_temporal_coordinates(timestamps), **_get_spatial_coordinates(bbox, data, feature_type), **_get_depth_coordinates(data=data, feature_name=feature_name)} if feature_type in FeatureTypeSet.RASTER_TYPES_2D: return {**_get_temporal_coordinates(timestamps), **_get_depth_coordinates(data=data, feature_name=feature_name)} if feature_type in FeatureTypeSet.RASTER_TYPES_3D: return {**_get_spatial_coordinates(bbox, data, feature_type), **_get_depth_coordinates(data=data, feature_name=feature_name)} return _get_depth_coordinates(data=data, feature_name=feature_name)
def array_to_dataframe(eopatch, feature, remove_depth=True, crs=None): """ Converts one feature of eopatch to xarray DataArray :param eopatch: eopatch :type eopatch: EOPatch :param feature: feature of eopatch :type feature: (FeatureType, str) :param remove_depth: removes last dimension if it is one :type remove_depth: bool :param crs: converts dimensions to crs :type crs: sentinelhub.crs :return: dataarray :rtype: xarray DataArray """ features = list(FeatureParser(feature)) feature_type, feature_name = features[0] bbox = eopatch.bbox data = eopatch[feature_type][feature_name] if isinstance(data, xr.DataArray): data = data.values dimensions = get_dimensions(feature) coordinates = get_coordinates(eopatch, feature, crs=crs) dataframe = xr.DataArray(data=data, coords=coordinates, dims=dimensions, attrs={'crs': str(bbox.crs), 'feature_type': feature_type, 'feature_name': feature_name}, name=string_to_variable(feature_name)) if remove_depth and dataframe.values.shape[-1] == 1: dataframe = dataframe.squeeze() dataframe = dataframe.drop(feature_name + '_dim') return dataframe
def get_dimensions(feature): """ Returns list of dimensions for xarray DataArray/Dataset :param feature: eopatch feature :type feature: (FeatureType, str) :return: dimensions for xarray DataArray/Dataset :rtype: list(str) """ features = list(FeatureParser(feature)) feature_type, feature_name = features[0] depth = string_to_variable(feature_name, '_dim') if feature_type in FeatureTypeSet.RASTER_TYPES_4D: return ['time', 'y', 'x', depth] if feature_type in FeatureTypeSet.RASTER_TYPES_2D: return ['time', depth] if feature_type in FeatureTypeSet.RASTER_TYPES_3D: return ['y', 'x', depth] return [depth]
def plot(self): """ Plots eopatch :return: plot :rtype: holovies/bokeh """ features = list(FeatureParser(self.feature)) feature_type, feature_name = features[0] if self.pixel and feature_type in FeatureTypeSet.RASTER_TYPES_4D: vis = self.plot_pixel(feature_type, feature_name) elif feature_type in (FeatureType.MASK, *FeatureTypeSet.RASTER_TYPES_3D): vis = self.plot_raster(feature_type, feature_name) elif feature_type is FeatureType.DATA: vis = self.plot_data(feature_name) elif feature_type is FeatureType.VECTOR: vis = self.plot_vector(feature_name) elif feature_type is FeatureType.VECTOR_TIMELESS: vis = self.plot_vector_timeless(feature_name) else: # elif feature_type in (FeatureType.SCALAR, FeatureType.LABEL): vis = self.plot_scalar_label(feature_type, feature_name) return vis.opts(plot=dict(width=PLOT_WIDTH, height=PLOT_HEIGHT))
def __init__(self, class_feature, samples_amount=0.1, valid_mask=None, ignore_labels=None, features=None, weak_classes=None, search_radius=3, samples_per_class=None, seed=None): """ :param class_feature: Feature that contains class labels :type class_feature: (FeatureType, string) or string :param samples_amount: Number of samples taken per patch. If the number is on the interval of [0...1] then that percentage of all points is taken. If the value is 1 all eligible points are taken. :type samples_amount: float :param valid_mask: Feature that defines the area from where samples are taken, if None the whole image is used :type valid_mask: (FeatureType, string), string or None :param ignore_labels: A single item or a list of values that should not be sampled. :type ignore_labels: list of integers or int :param features: Temporal features to include in dataset for each pixel sampled :type features: Input that the FeatureParser class can parse :param samples_per_class: Number of samples per class returned after balancing. If the number is higher than total amount of some classes, then those classes have their points duplicated to reach the desired amount. If the argument is None then limit is set to the size of the number of samples of the smallest class :type samples_per_class: int or None :param seed: Seed for random generator :type seed: int :param weak_classes: Classes that upon finding, also the neighbouring regions will be checked and added if they contain one of the weak classes. Used to enrich the samples :type weak_classes: list of integers or int :param search_radius: How many points in each direction to check for additional weak classes :type search_radius: int """ self.class_feature = next( FeatureParser(class_feature, default_feature_type=FeatureType.MASK_TIMELESS)()) self.samples_amount = samples_amount self.valid_mask = next( FeatureParser(valid_mask, default_feature_type=FeatureType.MASK_TIMELESS) ()) if valid_mask else None self.ignore_labels = ignore_labels if isinstance( ignore_labels, list) else [ignore_labels] self.features = FeatureParser(features, default_feature_type=FeatureType. DATA_TIMELESS) if features else None self.columns = [self.class_feature[1]] + ['patch_identifier', 'x', 'y'] if features: self.columns += [x[1] for x in self.features] self.samples_per_class = samples_per_class self.seed = seed if seed is not None: random.seed(self.seed) self.weak_classes = weak_classes if isinstance( weak_classes, list) else [weak_classes] self.search_radius = search_radius self.sampled_data = [] self.balanced_data = None self.class_distribution = None