예제 #1
0
    def setUpClass(cls):
        super().setUpClass()

        with open(os.path.join(cls.INPUT_FOLDER, "test_fis_results.txt"), 'r') as file:
            results = [ast.literal_eval(line.strip()) for line in file]

        bbox = BBox([14.00, 45.00, 14.03, 45.03], crs=CRS.WGS84)
        geometry1 = Geometry(Polygon([(465888.877326859, 5079639.436138632),
                                      (465885.3413983975, 5079641.524618266),
                                      (465882.9542217017, 5079647.166043535),
                                      (465888.8780175466, 5079668.703676634),
                                      (465888.877326859, 5079639.436138632)]),
                             CRS(32633))
        geometry2 = Geometry('POLYGON((-5.13 48, -5.23 48.09, -5.13 48.17, -5.03 48.08, -5.13 48))', CRS.WGS84)

        cls.test_cases = [
            cls.FisTestCase('geometry',
                            FisRequest(layer='TRUE-COLOR-S2-L1C',
                                       geometry_list=[geometry1],
                                       time=('2017-1-1', '2017-2-1'),
                                       resolution="50m",
                                       histogram_type=HistogramType.STREAMING,
                                       bins=5),
                            raw_result=results[0],
                            result_length=1),
            cls.FisTestCase('bbox',
                            FisRequest(layer='BANDS-S2-L1C',
                                       geometry_list=[bbox],
                                       time='2017-1-1',
                                       resolution="50m",
                                       maxcc=0.2,
                                       custom_url_params={
                                           CustomUrlParam.ATMFILTER: "ATMCOR",
                                           CustomUrlParam.DOWNSAMPLING: "BICUBIC",
                                           CustomUrlParam.UPSAMPLING: "BICUBIC"}
                                       ),
                            raw_result=results[1],
                            result_length=1),
            cls.FisTestCase('list',
                            FisRequest(data_source=DataSource.LANDSAT8,
                                       layer='BANDS-L8',
                                       geometry_list=[bbox, geometry1],
                                       time=('2017-1-1', '2017-1-10'),
                                       resolution="100m",
                                       bins=32, data_folder=cls.OUTPUT_FOLDER),
                            raw_result=results[2], result_length=2,
                            save_data=True),
            cls.FisTestCase('Polygon in WGS84',
                            FisRequest(layer='TRUE-COLOR-S2-L1C',
                                       geometry_list=[geometry2],
                                       time=('2017-10-1', '2017-10-2'),
                                       resolution="60m",
                                       bins=11, histogram_type=HistogramType.EQUALFREQUENCY),
                            raw_result=results[3], result_length=1),
        ]

        for test_case in cls.test_cases:
            test_case.collect_data()
    def get_sampling_geometry(self, tile_info):
        tile_geometry = Geometry(tile_info['geometry'],
                                 crs=self.area_of_interest.crs)

        utm_crs = ShOgcSampling.get_crs(tile_info)
        tile_geometry = tile_geometry.transform(utm_crs)
        utm_aoi_geometry = self.area_of_interest.transform(utm_crs)

        # Warning: This intersection can be too small for sampling, that is why __next__ method is retrying the process
        return Geometry(
            tile_geometry.geometry.intersection(utm_aoi_geometry.geometry),
            utm_crs)
예제 #3
0
def plot_batch_splitter(splitter: BatchSplitter):
    """ Plots tiles and area geometry from a splitter class
    """
    gdf = gpd.GeoDataFrame(dict(
        status=[info['status'] for info in splitter.get_info_list()],
        geometry=splitter.get_bbox_list()),
                           crs=CRS.WGS84.pyproj_crs())
    ax = gdf.plot(column='status', legend=True, figsize=(10, 10))

    geometry = Geometry(splitter.get_area_shape(), splitter.crs)
    geometry = geometry.transform(CRS.WGS84)
    area_series = gpd.GeoSeries([geometry.geometry],
                                crs=geometry.crs.pyproj_crs())
    area_series.plot(ax=ax, facecolor='none', edgecolor='black')
예제 #4
0
    def execute(self, eopatch=None, *, bbox=None, time_interval=None):
        """Execute method that adds new Meteoblue data into an EOPatch

        :param eopatch: An EOPatch in which data will be added. If not provided a new EOPatch will be created.
        :type eopatch: EOPatch or None
        :param bbox: A bounding box of a request. Should be provided if eopatch parameter is not provided.
        :type bbox: BBox or None
        :param time_interval: An interval for which data should be downloaded. If not provided then timestamps from
            provided eopatch will be used.
        :type time_interval: (dt.datetime, dt.datetime) or (str, str) or None
        """
        eopatch = eopatch or EOPatch()
        eopatch.bbox = self._prepare_bbox(eopatch, bbox)
        time_intervals = self._prepare_time_intervals(eopatch, time_interval)

        bbox = eopatch.bbox
        geometry = Geometry(bbox.geometry, bbox.crs).transform(CRS.WGS84)
        geojson = shapely.geometry.mapping(geometry.geometry)
        query = {
            "units": self.units,
            "geometry": geojson,
            "format": "protobuf",
            "timeIntervals": time_intervals,
            "queries": [self.query],
        }
        result_data, result_timestamp = self._get_data(query)

        if not eopatch.timestamp and result_timestamp:
            eopatch.timestamp = result_timestamp

        eopatch[self.feature] = result_data
        return eopatch
예제 #5
0
def test_search_geometry_and_iterator_methods(catalog):
    """ Tests search with a geometry and test methods of CatalogSearchIterator
    """
    search_geometry = Geometry(TEST_BBOX.geometry, crs=TEST_BBOX.crs)

    search_iterator = catalog.search(collection=DataCollection.SENTINEL2_L1C,
                                     time=('2021-01-01', '2021-01-5'),
                                     geometry=search_geometry,
                                     query={'eo:cloud_cover': {
                                         'lt': 40
                                     }})
    results = list(search_iterator)

    assert len(results) == 1
    assert search_iterator.get_timestamps() == [
        dt.datetime(2021, 1, 3, 7, 14, 7, tzinfo=dateutil.tz.tzutc())
    ]
    assert search_iterator.get_ids() == [
        'S2A_MSIL1C_20210103T071211_N0209_R020_T38LPH_20210103T083459'
    ]

    geometries = search_iterator.get_geometries()
    assert len(geometries) == 1
    assert isinstance(geometries[0], Geometry)
    assert geometries[0].geometry.intersects(search_geometry.geometry)
예제 #6
0
    def setUpClass(cls):
        super().setUpClass()

        polygon = shapely.geometry.Polygon([(465888.8773268595, 5079639.43613863),
                                            (465885.3413983975, 5079641.52461826),
                                            (465882.9542217017, 5079647.16604353),
                                            (465888.8780175466, 5079668.70367663),
                                            (465888.877326859, 5079639.436138632)])
        cls.wkt_string = 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), ' \
                         '(30 20, 20 15, 20 25, 30 20)))'
        cls.geometry1 = Geometry(polygon, CRS(32633))
        cls.geometry2 = Geometry(cls.wkt_string, CRS.WGS84)
        cls.bbox = BBox(bbox=[14.00, 45.00, 14.03, 45.03], crs=CRS.WGS84)
        cls.bbox_collection = BBoxCollection([cls.bbox, BBox('46,13,47,20', CRS.WGS84)])

        cls.geometry_list = [cls.geometry1, cls.geometry2, cls.bbox, cls.bbox_collection]
예제 #7
0
    def test_wkt(self):
        for geometry in [self.geometry1, self.geometry2]:
            self.assertEqual(
                geometry, Geometry(geometry.wkt, geometry.crs),
                'Transforming geometry to geojson and back should preserve it')

        self.assertEqual(self.geometry2.wkt, self.wkt_string,
                         'New WKT string doesnt match the original')
예제 #8
0
    def geometry(self):
        """ Helper function to return a WKT polygon from a Geopedia geometry

        Given a geometry field from a Geopedia table, return a WKT polygon in POP_WEB CRS

        :param geometry: Dictionary describing a Geopedia geometry feature
        :return: WKT polygon in popular web-mercator
        """
        geometry_payload = self.payload['geometry']
        return Geometry(
            shapely.geometry.shape(geometry_payload),
            crs=geometry_payload['crs']['properties']['name'].split(':')[-1])
예제 #9
0
 def cloud_map(self):
     """
     Download cloud map from API.
     """
     if not isinstance(self._cloud_map, pd.DataFrame):
         geometry = Geometry(Polygon(self.external_polygon), CRS.WGS84)
         request = get_request(
             self,
             evalscript=evalscript_cloud_mask,
             geometry=geometry
         )
         self._cloud_map = pd.DataFrame(request.get_data()[0])
     return self._cloud_map
예제 #10
0
    def _decode(self, file, path):
        """Loads from a file and decodes content."""
        file_format = MimeType(fs.path.splitext(path)[1].strip("."))

        if file_format is MimeType.NPY:
            return np.load(file)

        if file_format is MimeType.GPKG:
            dataframe = gpd.read_file(file)

            if dataframe.crs is not None:
                # Trying to preserve a standard CRS and passing otherwise
                try:
                    with warnings.catch_warnings():
                        warnings.simplefilter("ignore", category=SHUserWarning)
                        dataframe.crs = CRS(dataframe.crs).pyproj_crs()
                except ValueError:
                    pass

            if "TIMESTAMP" in dataframe:
                dataframe.TIMESTAMP = pd.to_datetime(dataframe.TIMESTAMP)

            return dataframe

        if file_format in [MimeType.JSON, MimeType.GEOJSON]:
            json_data = json.load(file)

            if self.feature_type is FeatureType.BBOX:
                return Geometry.from_geojson(json_data).bbox

            return json_data

        if file_format is MimeType.PICKLE:
            warnings.warn(
                f"File {self.path} with data of type {self.feature_type} is in pickle format which is deprecated "
                "since eo-learn version 1.0. Please re-save this EOPatch with the new eo-learn version to "
                "update the format. In newer versions this backward compatibility will be removed.",
                EODeprecationWarning,
            )

            data = pickle.load(file)

            # There seems to be an issue in geopandas==0.8.1 where unpickling GeoDataFrames, which were saved with an
            # old geopandas version, loads geometry column into a pandas.Series instead geopandas.GeoSeries. Because
            # of that it is missing a crs attribute which is only attached to the entire GeoDataFrame
            if isinstance(data, GeoDataFrame) and not isinstance(
                    data.geometry, GeoSeries):
                data = data.set_geometry("geometry")

            return data
        raise ValueError(f"Unsupported data type for file {path}.")
예제 #11
0
    def test_bbox_geometry(self):
        """ Test intersection between bbox and geometry
        """
        evalscript = """
            //VERSION=3

            function setup() {
                return {
                    input: [{
                        bands: ["B02", "B03", "B04"],
                        units: "REFLECTANCE"
                    }],
                    output: {
                        bands: 3
                    }
                };
            }

            function evaluatePixel(sample) {
                return [2.5 * sample.B04, 2.5 * sample.B03, 2.5 * sample.B02];
            }
        """

        delta = 0.2
        bbox = BBox(bbox=[46.16, -16.15, 46.51, -15.58], crs=CRS.WGS84)
        geometry = Geometry(bbox.geometry, crs=bbox.crs)
        bbox_translated = BBox(bbox=[
            46.16 + delta, -16.15 + delta, 46.51 + delta, -15.58 + delta
        ],
                               crs=CRS.WGS84)

        request = SentinelHubRequest(
            evalscript=evalscript,
            input_data=[
                SentinelHubRequest.input_data(
                    data_collection=DataCollection.SENTINEL2_L1C,
                    time_interval=('2017-12-15T07:12:03',
                                   '2017-12-15T07:12:04'),
                    maxcc=0.8)
            ],
            responses=[
                SentinelHubRequest.output_response('default', MimeType.JPG)
            ],
            geometry=geometry,
            bbox=bbox_translated,
            size=(512, 856))

        img = request.get_data(max_threads=3)[0]

        self.assertEqual(img.shape, (856, 512, 3))
        self.test_numpy_data(img, exp_min=0, exp_max=255, exp_mean=22.625)
예제 #12
0
def simplify_geometry(aoi: Geometry,
                      tolerance: float = 0.004,
                      max_count: int = 1500) -> Geometry:
    """
    Simplify input geometry such that number of vertices can be processed by batch process API
    """
    vertex_count = get_number_of_vertices(aoi)

    LOGGER.info(f'Number of vertices of original geometry: {vertex_count}')

    if vertex_count > max_count:
        geometry = aoi.geometry.simplify(tolerance, preserve_topology=True)
        aoi = Geometry(geometry, crs=aoi.crs)

    LOGGER.info(
        f'Number of vertices after simplification: {get_number_of_vertices(aoi)}'
    )

    return aoi
예제 #13
0
def create_batch_request(
        config: DownloadConfig,
        output_responses: List[dict],
        description: str = 'Batch request') -> SentinelHubBatch:
    """ Helper function that creates a SH batch request
    """
    LOGGER.info('Read and simplify AOI geometry')
    aoi_gdf = gpd.read_file(config.aoi_filename)
    assert aoi_gdf.crs.name == 'WGS 84'
    aoi = Geometry(aoi_gdf.geometry.values[0], crs=CRS.WGS84)
    aoi = simplify_geometry(aoi)

    LOGGER.info('\nSet up SH and AWS credentials')
    _ = set_sh_config(config)

    LOGGER.info('\nThis evalscript is executed')
    evalscript = load_evalscript()

    sentinelhub_request = SentinelHubRequest(
        evalscript=evalscript,
        input_data=[
            SentinelHubRequest.input_data(
                data_collection=config.data_collection,
                time_interval=config.time_interval,
                maxcc=config.maxcc,
                mosaicking_order=config.mosaicking_order)
        ],
        responses=output_responses,
        geometry=aoi)

    batch_request = SentinelHubBatch.create(
        sentinelhub_request,
        tiling_grid=SentinelHubBatch.tiling_grid(**config.grid_definition),
        output=SentinelHubBatch.output(
            default_tile_path=
            f's3://{config.bucket_name}/{config.tiles_path}/<tileName>/<outputId>.<format>'
        ),
        description=description)

    return batch_request
예제 #14
0
for idx, (image, time) in enumerate(zip(images, wcs_request.get_dates())):
    axs.flat[idx].imshow(image)
    axs.flat[idx].set_title(time.date().isoformat())
fig.tight_layout()
"""
Comparing Histograms: FIS can also provide histograms of calues split into a specified number of binds which will allows
us to analyse the distribution of values without having to download entire images.

Compare NDVI value distributions of the following bboxes and polygons. We will divide values into 20 equally-sized bins
and use Landsat 8 data. For simplification, we select a time interval for which there is only one acquisition available.
"""
bbox1 = BBox([46.16, -16.15, 46.51, -15.58], CRS.WGS84)
bbox2 = BBox((1292344.0, 5195920.0, 1310615.0, 5214191.0), CRS.POP_WEB)

geometry1 = Geometry(
    Polygon([(-5.13, 48), (-5.23, 48.09), (-5.13, 48.17), (-5.03, 48.08),
             (-5.13, 48)]), CRS.WGS84)
geometry2 = Geometry(
    Polygon([(1292344.0, 5205055.5), (1301479.5, 5195920.0),
             (1310615.0, 5205055.5), (1301479.5, 5214191.0),
             (1292344.0, 5205055.5)]), CRS.POP_WEB)

print(list(HistogramType))

ndvi_script = 'return [(B05 - B04) / (B05 + B04)]'

histogram_request = FisRequest(
    data_collection=DataCollection.LANDSAT8,
    layer='TRUE-COLOR-L8',
    geometry_list=[bbox1, bbox2, geometry1, geometry2],
    time=('2018-06-10', '2018-06-15'),
예제 #15
0

# 1. Create a batch request
# 1.1 Define a Processing API request
CLIENT_ID = 'ac45629f-3b23-4a8d-a579-0a30bbfbaa0e'
CLIENT_SECRET = 'd,w~E#IpJJ6eTfEUd$A*q<bU5hNivI!jANtt<7WP'
config = SHConfig()
if CLIENT_ID and CLIENT_SECRET:
    config.sh_client_id = CLIENT_ID
    config.sh_client_secret = CLIENT_SECRET

SHAPE_PATH = os.path.join('.', 'data', 'california_crop_fields.geojson')
area_gdf = gpd.read_file(SHAPE_PATH)

# Geometry of entire area
full_geometry = Geometry(area_gdf.geometry.values[0], crs=CRS.WGS84)
# BBox of a test sub-area
test_bbox = Geometry(area_gdf.geometry.values[1], crs=CRS.WGS84).bbox

area_gdf.plot(column='name')

# Check a true-colour satellite image of the entire area
evalscript_true_colour = """
    //VERSION=3
    function setup() {
        return {
            input: [{ 
                bands: ["B02", "B03", "B04"] 
            }],
            output: { bands: 3 }
        }
예제 #16
0
 def test_geojson(self):
     for geometry in [self.geometry1, self.geometry2]:
         self.assertEqual(geometry, Geometry(geometry.geojson, geometry.crs),
                          'Transforming geometry to geojson and back should preserve it')
예제 #17
0
    def test_multipart_geometry(self):
        evalscript = """
            //VERSION=3

            function setup() {
            return {
                input: ["B02", "B03", "B04", "dataMask"],
                output: { id:"default", bands: 3}
            }
            }

            function updateOutputMetadata(scenes, inputMetadata, outputMetadata) {
            var sum = (r_count + g_count + b_count);
            var r_rat = r_count / sum;
            var g_rat = g_count / sum;
            var b_rat = b_count / sum;
            outputMetadata.userData = { "rgb_ratios":  [r_rat, g_rat, b_rat] }
            }

            var r_count = 0;
            var g_count = 0;
            var b_count = 0;

            function evaluatePixel(sample) {

                b_count += sample.B02;
                g_count += sample.B03;
                r_count += sample.B04;

                if (sample.dataMask == 0) {
                    return [1,1,1];
                }

                return [sample.B04*2.5, sample.B03*2.5, sample.B02*2.5];
            }
        """

        points = [(3983945.16471594, 4455475.78793186),
                  (3983888.00256275, 4455757.47439827),
                  (3983881.86585896, 4455756.21837259),
                  (3983806.23113651, 4456128.92147268),
                  (3983795.43856837, 4456181.52922753),
                  (3983782.49665288, 4456243.16761979),
                  (3983769.24786918, 4456304.74059236),
                  (3983755.69254332, 4456366.24660331),
                  (3983741.83100914, 4456427.68411298),
                  (3983731.89973217, 4456470.84795843),
                  (3983633.33670483, 4456895.81023678),
                  (3983639.43692276, 4456897.23726002),
                  (3983537.58701916, 4457336.35298979),
                  (3983531.486563, 4457334.92584801),
                  (3983451.81567033, 4457678.40439185),
                  (3983444.09684707, 4457713.91738361),
                  (3983433.21703553, 4457774.95105094),
                  (3983425.2423303, 4457836.4359853),
                  (3983420.19086095, 4457898.23280495),
                  (3983418.07413054, 4457960.2014162),
                  (3983418.89698951, 4458022.20133071),
                  (3983422.65762374, 4458084.09198476),
                  (3983429.05535825, 4458143.30899922),
                  (3983435.27377231, 4458142.55298424),
                  (3983439.97457434, 4458175.43769638),
                  (3983450.97468474, 4458236.14788553),
                  (3983466.88315168, 4458303.87476693),
                  (3983460.83517165, 4458305.51224157),
                  (3983466.80900589, 4458327.76588705),
                  (3983484.9991527, 4458387.02138291),
                  (3983505.97749719, 4458445.340412),
                  (3983538.67409472, 4458522.43435024),
                  (3983584.70089337, 4458635.18822735),
                  (3983780.40768297, 4459048.67824218),
                  (3983801.72985096, 4459096.84527808),
                  (3983883.42859759, 4459278.64097453),
                  (3984316.01202946, 4460214.51826613),
                  (3984398.97672295, 4460080.53793049),
                  (3984534.50220822, 4459799.86484374),
                  (3984577.77550522, 4459774.02321167),
                  (3984491.40157364, 4459687.94895666),
                  (3984776.22996932, 4459142.13379129),
                  (3984819.68594039, 4459029.12887873),
                  (3984907.71921624, 4458981.665405),
                  (3984888.9490588, 4458770.02890185),
                  (3985209.2168573, 4458503.41559024),
                  (3985821.45298221, 4458006.99923219),
                  (3985788.76207523, 4457880.30735337),
                  (3985793.50611539, 4457877.12247581),
                  (3985784.68739608, 4457859.48509427),
                  (3985732.13693102, 4457697.05635426),
                  (3985820.89433686, 4457656.86419316),
                  (3985677.94930497, 4457315.34906349),
                  (3985611.18897298, 4457337.80151946),
                  (3985327.61285454, 4457451.86990929),
                  (3985146.68294768, 4456972.64460213),
                  (3985446.37981687, 4456852.84034971),
                  (3985488.11295695, 4456837.9565739),
                  (3985384.27368677, 4456550.32595766),
                  (3985005.77351172, 4455718.96868536),
                  (3984372.83691021, 4455665.6888113),
                  (3984231.62160324, 4455623.03272949),
                  (3984096.30921154, 4455487.68759209),
                  (3983945.16471594, 4455475.78793186)]

        sgeo = Polygon(points)
        crs = CRS('epsg:3857')
        geo = Geometry(sgeo, crs=crs)
        bbox = BBox(sgeo.bounds, crs=crs)
        size = bbox_to_dimensions(bbox, 10)

        request = SentinelHubRequest(
            evalscript=evalscript,
            input_data=[
                SentinelHubRequest.input_data(
                    data_source=DataSource.SENTINEL2_L1C,
                    time_interval=('2017-11-15T07:12:03',
                                   '2017-12-15T07:12:04'),
                )
            ],
            responses=[
                SentinelHubRequest.output_response('default', MimeType.TIFF),
                SentinelHubRequest.output_response('userdata', MimeType.JSON)
            ],
            bbox=bbox,
            geometry=geo,
            size=size)

        tar = request.get_data(max_threads=3)[0]

        img = tar['default.tif']

        self.assertEqual(img.shape, (382, 181, 3))
        self.test_numpy_data(img, exp_min=25, exp_max=255, exp_mean=144.89)

        json_data = tar['userdata.json']

        self.assertTrue('rgb_ratios' in json_data)

        expected_ratios = [
            0.29098381560041126, 0.3227735909047216, 0.3862425934948671
        ]
        self.assertTrue(np.allclose(json_data['rgb_ratios'], expected_ratios))
예제 #18
0
class WMSRequest:
    # ************** True color (PNG) on a specific date ************** #
    bbox = [
        6887893.492833803, 5009377.085697314, 7200979.560689886,
        5322463.153553395
    ]
    # bbox = [46.16, -16.15, 46.51, -15.58]
    _bbox = BBox(bbox=bbox, crs=CRS.POP_WEB)
    geometry = Geometry(
        Polygon([(6887893.0, 5422463.0), (7200979.0, 5009377.0),
                 (7210979, 5422463.0), (7200979.0, 5209377.0),
                 (6887893.0, 5422463.0)]), CRS.POP_WEB)

    def request1(self):
        wms_request = FisRequest(
            layer='TRUE_COLOR',
            geometry_list=[self._bbox, self.geometry],
            # bbox=self._bbox,
            custom_url_params={CustomUrlParam.SHOWLOGO: False},
            width=512,
            height=512,
            config=config)
        img = wms_request.get_data()
        # print(len(img))
        # print(type(img))
        # print(img)
        plot_image(img[-1])

    # ************** True color of the latest acquisition ************** #
    def request2(self):
        wms_true_color_request = WmsRequest(layer='TRUE-COLOR',
                                            bbox=betsiboka_bbox,
                                            time='latest',
                                            width=512,
                                            height=856,
                                            config=config)
        wms_true_color_img = wms_true_color_request.get_data()
        plot_image(wms_true_color_img[-1])
        print(
            'The latest Sentinel-2 image of this area was taken on {}.'.format(
                wms_true_color_request.get_dates()[-1]))

    # ************** True color of the multiple acquisitions in certain time window ************** #
    def request3(self):
        wms_true_color_request = WmsRequest(layer='TRUE-COLOR',
                                            bbox=betsiboka_bbox,
                                            time=('2017-12-01', '2017-12-31'),
                                            width=512,
                                            height=856,
                                            config=config)
        wms_true_color_img = wms_true_color_request.get_data()
        print('There are %d Sentinel-2 images available for December 2017.' %
              len(wms_true_color_img))
        plot_image(wms_true_color_img[2])

        print('These %d images were taken on the following dates:' %
              len(wms_true_color_img))
        for index, date in enumerate(wms_true_color_request.get_dates()):
            print(' - image %d was taken on %s' % (index, date))

    # ************** True color of the multiple acquisitions in certain time window with cloud coverage less than 30% ************** #
    def request4(self):
        wms_true_color_request = WmsRequest(layer='TRUE-COLOR',
                                            bbox=betsiboka_bbox,
                                            time=('2017-12-01', '2017-12-31'),
                                            width=512,
                                            height=856,
                                            maxcc=0.3,
                                            config=config)
        wms_true_color_img = wms_true_color_request.get_data()
        print(
            'There are %d Sentinel-2 images available for December 2017 with cloud coverage less '
            'than %1.0f%%.' %
            (len(wms_true_color_img), wms_true_color_request.maxcc * 100.0))
        plot_image(wms_true_color_img[-1])
        print('These %d images were taken on the following dates:' %
              len(wms_true_color_img))

        for index, date in enumerate(wms_true_color_request.get_dates()):
            print(' - image %d was taken on %s' % (index, date))

    # ************** All Sentinel-2’s raw band values ************** #
    def request5(self):
        wms_bands_request = WmsRequest(layer='BANDS-S2-L1C',
                                       bbox=betsiboka_bbox,
                                       time='2017-12-15',
                                       width=512,
                                       height=856,
                                       image_format=MimeType.TIFF_d32f,
                                       config=config)

        wms_bands_img = wms_bands_request.get_data()
        print("Shape:", wms_bands_img[-1][:, :, 12].shape)
        plot_image(wms_bands_img[-1][:, :, 12])
        plot_image(wms_bands_img[-1][:, :, [3, 2, 1]], 2.5)

    # ************** All Sentinel-2’s raw band values ************** #
    def request6(self):
        wms_bands_request = WmsRequest(data_folder='test_dir',
                                       layer='BANDS-S2-L1C',
                                       bbox=betsiboka_bbox,
                                       time='2017-12-15',
                                       width=512,
                                       height=856,
                                       image_format=MimeType.TIFF_d32f,
                                       config=config)
        wms_bands_img = wms_bands_request.get_data(save_data=True)
        import os

        for folder, _, filenames in os.walk(wms_bands_request.data_folder):
            for filename in filenames:
                print(os.path.join(folder, filename))

        wms_bands_request_from_disk = WmsRequest(
            data_folder='test_dir',
            layer='BANDS-S2-L1C',
            bbox=betsiboka_bbox,
            time='2017-12-15',
            width=512,
            height=856,
            image_format=MimeType.TIFF_d32f,
            config=config)
        wms_bands_img_from_disk = wms_bands_request_from_disk.get_data()
        if np.array_equal(wms_bands_img[-1], wms_bands_img_from_disk[-1]):
            print('Arrays are equal.')
        else:
            print('Arrays are different.')
        wms_bands_img_redownload = wms_bands_request_from_disk.get_data(
            redownload=True)

    # ************** Save downloaded data directly to disk ************** #
    def request7(self):
        wms_true_color_request = WmsRequest(data_folder='test_dir_tiff',
                                            layer='TRUE-COLOR',
                                            bbox=betsiboka_bbox,
                                            time=('2017-12-01', '2017-12-31'),
                                            width=512,
                                            height=856,
                                            image_format=MimeType.TIFF,
                                            config=config)
        wms_true_color_request.save_data()
        os.listdir(wms_true_color_request.data_folder)

    # ************** Merging two or more download requests into one ************** #
    def request8(self):
        print("asdasd")
        betsiboka_bbox_large = BBox([45.88, -16.12, 47.29, -15.45],
                                    crs=CRS.WGS84)

        wms_true_color_request = WmsRequest(layer='AGRICULTURE',
                                            bbox=betsiboka_bbox_large,
                                            time='2015-12-01',
                                            width=960,
                                            image_format=MimeType.PNG,
                                            config=config)

        wms_true_color_img = wms_true_color_request.get_data()
        plot_image(wms_true_color_img[0])
        plot_image(wms_true_color_img[1])

        wms_true_color_request_with_deltat = WmsRequest(
            layer='AGRICULTURE',
            bbox=betsiboka_bbox_large,
            time='2015-12-01',
            width=960,
            image_format=MimeType.PNG,
            time_difference=datetime.timedelta(hours=2),
            config=config)

        wms_true_color_img = wms_true_color_request_with_deltat.get_data()
        print('These %d images were taken on the following dates:' %
              len(wms_true_color_img))
        for index, date in enumerate(
                wms_true_color_request_with_deltat.get_dates()):
            print(' - image %d was taken on %s' % (index, date))
        plot_image(wms_true_color_img[-1])
 def get_sampling_geometry(tile_info):
     return Geometry(shapely.geometry.shape(tile_info['coverGeometry']),
                     crs=ShIndexSampling.get_crs(tile_info))