better-satcen-00002 data pipeline results (Sentinel-1 Multi-temporal SLC stack and Multi-temporal Coherence stack data pipeline): loading from pickle file and exploiting

This Notebook shows how to: * reload bbetter-satcen-00002 data pipeline results previously stored in a pickle file * Create a new dataframe with data values cropped over the area of interest * Plot an RGB * Create a geotif

Import needed modules

In [1]:
import pandas as pd
from geopandas import GeoDataFrame
import gdal
import numpy as np
from shapely.geometry import Point
from shapely.geometry import Polygon
import matplotlib
import matplotlib.pyplot as plt
from PIL import Image
%matplotlib inline
from shapely.wkt import loads
from shapely.geometry import box
from urlparse import urlparse
import requests

Read picke data file

In [2]:
pickle_filename = 'better-satcen-00002_validation_NW_Niger.pkl'
In [3]:
results = GeoDataFrame(pd.read_pickle(pickle_filename))
In [4]:
results.head()
Out[4]:
enclosure enddate identifier self startdate title wkt
0 https://store.terradue.com/better-satcen-00002... 2018-09-22 05:19:19.147 0ecaac621b804f8362ca89b13025f218424aed16 https://catalog.terradue.com//better-satcen-00... 2018-09-22 05:19:03.036 S1B_S1_SLC__1SDV_20180910T051902_20180910T0519... POLYGON((10.6534444444444 12.1762222222222
1 https://store.terradue.com/better-satcen-00002... 2018-09-22 05:19:19.147 1c04a6d1f769d6d6c1842b5f833e2bcf83a9e9bf https://catalog.terradue.com//better-satcen-00... 2018-09-22 05:19:03.036 SLC_S1B_S1_SLC__1SDV_20180910T051902_20180910T... POLYGON((10.6534444444444 12.1762222222222
2 https://store.terradue.com/better-satcen-00002... 2018-09-22 05:19:19.147 31bba41907a285824f472b2569cd2ecfdda7c5a7 https://catalog.terradue.com//better-satcen-00... 2018-09-22 05:19:03.036 MTC_S1B_S1_SLC__1SDV_20180910T051902_20180910T... POLYGON((10.6534444444444 12.1762222222222
3 https://store.terradue.com/better-satcen-00002... 2018-09-22 05:19:19.147 519f895505caadd1d45a156b38627ef3b2260027 https://catalog.terradue.com//better-satcen-00... 2018-09-22 05:19:03.036 SLC_S1B_S1_SLC__1SDV_20180910T051902_20180910T... POLYGON((10.6534444444444 12.1762222222222
4 https://store.terradue.com/better-satcen-00002... 2018-09-22 05:19:19.147 5d629460a65b06ea9818970d0d1af340d19bbd54 https://catalog.terradue.com//better-satcen-00... 2018-09-22 05:19:03.036 SLC_S1B_S1_SLC__1SDV_20180910T051902_20180910T... POLYGON((10.6534444444444 12.1762222222222

Credentials for ellip platform access

  • Provide here your ellip username and api key for platform access
In [5]:
import getpass

user = 'msagona'#getpass.getpass("Enter you ellip username:")
api_key = 'AKCp2V6wov45FKVuto1g558a3QKv5ZX4aD4vieCoZP8PtBU2TSFF2pBx57grLHp6zJuTY67S1'#getpass.getpass("Enter the API key:")

Define filter parameters

Time of interest

In [6]:
#mydates = pd.to_datetime(['2018-10-01 17:22:23.7090000', '2018-09-22 05:19:03.036000', '2018-09-10 05:19:02.7223870Z', '2018-08-29 05:19:02.107153', '2018-07-12 05:18:59.285207', '2018-07-09 17:22:19.676794', '2018-06-27 17:22:18.9696100Z', '2018-06-18 05:18:58.0635280Z', '2018-06-06 05:18:57.166982'])
#'2018-09-22 05:19:03.036000', '2018-08-29 05:19:02.107153',
mydates = pd.to_datetime(['2018-06-27 17:22:18.9696100Z', '2018-06-18 05:18:58.0635280Z', '2018-06-06 05:18:57.166982'])

Area of interest

  • The user can choose to define an AOI selecting a Point and a buffer size used to build a squared polygon around that point
In [7]:
point_of_interest = Point(9.838, 13.87)
In [8]:
buffer_size = 0.5
In [9]:
aoi_wkt1 = box(*point_of_interest.buffer(buffer_size).bounds)
In [10]:
aoi_wkt1.wkt
Out[10]:
'POLYGON ((10.338 13.37, 10.338 14.37, 9.337999999999999 14.37, 9.337999999999999 13.37, 10.338 13.37))'
  • Or use the reference AOI (in this case the NW Niger area)
In [11]:
geom = 'MULTIPOLYGON (((10.65344444444444 12.17622222222222, 10.65344444444444 15.48261111111111, 8.398666666666667 15.48261111111111, 8.398666666666667 12.17622222222222, 10.65344444444444 12.17622222222222)), ((15.42594444444445 18.89533333333333, 15.42594444444445 23.45238888888889, 12.93755555555556 23.45238888888889, 12.93755555555556 18.89533333333333, 15.42594444444445 18.89533333333333)), ((6.452166666666667 18.3855, 6.452166666666667 20.24616666666667, 5.364944444444444 20.24616666666667, 5.364944444444444 18.3855, 6.452166666666667 18.3855)))'

aoi_wkt2 = loads(geom)[0]
#aoi_wkt2 = loads('POLYGON((9.267 14.695,10.392 14.69,10.393 13.186,9.27 13.188,9.267 14.695))')
In [12]:
aoi_wkt2.wkt
Out[12]:
'POLYGON ((10.65344444444444 12.17622222222222, 10.65344444444444 15.48261111111111, 8.398666666666667 15.48261111111111, 8.398666666666667 12.17622222222222, 10.65344444444444 12.17622222222222))'

Create a new dataframe with data values

  • Get the subframe metadata values related to selected bands and add the related data extended info: (cropped) data values and band name.

Define auxiliary methods to create new dataframe

  • The first method gets download reference, bands numbers, cropping flag, AOI cropping area and returns the related bands data array with related original geospatial infos
  • If crop = False the original bands extent is returned
In [13]:
def get_cropped_image_as_array(url, crop, bbox):

    #[-projwin ulx uly lrx lry]
    ulx, uly, lrx, lry = bbox[0], bbox[3], bbox[2], bbox[1]

    output = '/vsimem/clip.tif'
    gdal.UseExceptions()

    ds = gdal.Open('/vsicurl/%s' % url)

    if crop==True:
        ds = gdal.Translate(output, ds, projWin = [ulx, uly, lrx, lry], projWinSRS = 'EPSG:4326')
        ds = None
    else:
        ds = gdal.Translate(output, ds)
        ds=None

    ds = gdal.Open('/vsimem/clip.tif')
    band = ds.GetRasterBand(1)

    w = band.XSize
    h = band.YSize

    data = band.ReadAsArray(0, 0, w, h).astype(np.float)

    data[data==-9999] = np.nan

    ds = None
    band = None

    return data
  • The second one selects the data to be managed (only the actual results related to the filtered metadata) and returns a new GeoDataFrame containing the requested bands extended info with the original metadata set
In [14]:
def get_info(row, user, api_key, crop=True, bbox=[]):

    extended_info = dict()

    # add the band info
    extended_info['band'] = '_'.join(row['title'].rsplit('_',4)[1:])
    print 'Managing %s - band %s' %(row['title'].rsplit('_',4)[0], extended_info['band'])

    # add the data for the AOI
    parsed_url = urlparse(row['enclosure'])

    url = '%s://%s:%s@%s/api%s' % (list(parsed_url)[0], user, api_key, list(parsed_url)[1], list(parsed_url)[2])

    extended_info['aoi_data'] = get_cropped_image_as_array(url, crop, bbox)

    # create the series
    series = pd.Series(extended_info)

    return series
  • Select from metadataframe a subframe containing the coherence geotiff results related to the selected datetimes of interest
In [15]:
#mydate_results = results[((results.apply(lambda row: 'MTC_' in row['title'], axis=1)) |
#                          (results.apply(lambda row: 'SLC_' in row['title'], axis=1))) &
#                          (results.apply(lambda row: '.tif' in row['enclosure'], axis=1)) &
#                          (results.apply(lambda row: row['startdate'] == mydates[-1] , axis=1))]

mydate_results = results[(results.apply(lambda row: 'coh_' in row['title'], axis=1)) &
                         (results.apply(lambda row: row['startdate'] in mydates , axis=1))]

In [16]:
mydate_results
Out[16]:
enclosure enddate identifier self startdate title wkt
33 https://store.terradue.com/better-satcen-00002... 2018-06-27 17:22:35.073582 b18961af52c6fad3213feb5bb9d54094e4af230c https://catalog.terradue.com//better-satcen-00... 2018-06-27 17:22:18.969610 MTC_S1B_S1_SLC__1SDV_20180615T172218_20180615T... POLYGON((10.6534444444444 12.1762222222222
44 https://store.terradue.com/better-satcen-00002... 2018-06-18 05:19:14.166966 bce1a34e4a7a0f4feb35f468c96ea81c50c07964 https://catalog.terradue.com//better-satcen-00... 2018-06-18 05:18:58.063528 MTC_S1B_S1_SLC__1SDV_20180606T051857_20180606T... POLYGON((10.6534444444444 12.1762222222222
56 https://store.terradue.com/better-satcen-00002... 2018-06-06 05:19:13.262935 668a1aeeae2043b829312a825f82c75db804fbac https://catalog.terradue.com//better-satcen-00... 2018-06-06 05:18:57.166982 MTC_S1B_S1_SLC__1SDV_20180525T051856_20180525T... POLYGON((10.6534444444444 12.1762222222222

*PARAMETERS*

  • crop = True|False

if crop=False the bbox can be omitted

In [17]:
aoi_wkt = aoi_wkt1
#aoi_wkt = aoi_wkt2

mydate_results = mydate_results.merge(mydate_results.apply(lambda row: get_info(row, user, api_key, True, list(aoi_wkt.bounds)), axis=1), left_index=True, right_index=True)

Managing MTC_S1B_S1_SLC__1SDV_20180615T172218_20180615T172234_011387_014E95_4DF7_S1B_S1_SLC__1SDV_20180627T172218_20180627T172235_011562_01540A_C8D5 - band coh_VV_15Jun2018_27Jun2018
Managing MTC_S1B_S1_SLC__1SDV_20180606T051857_20180606T051913_011248_014A49_D5FE_S1B_S1_SLC__1SDV_20180618T051858_20180618T051914_011423_014FAE_9A00 - band coh_VV_06Jun2018_18Jun2018
Managing MTC_S1B_S1_SLC__1SDV_20180525T051856_20180525T051912_011073_0144B9_1A53_S1B_S1_SLC__1SDV_20180606T051857_20180606T051913_011248_014A49_D5FE - band coh_VV_25May2018_06Jun2018
In [18]:
mydate_results
Out[18]:
enclosure enddate identifier self startdate title wkt aoi_data band
33 https://store.terradue.com/better-satcen-00002... 2018-06-27 17:22:35.073582 b18961af52c6fad3213feb5bb9d54094e4af230c https://catalog.terradue.com//better-satcen-00... 2018-06-27 17:22:18.969610 MTC_S1B_S1_SLC__1SDV_20180615T172218_20180615T... POLYGON((10.6534444444444 12.1762222222222 [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,... coh_VV_15Jun2018_27Jun2018
44 https://store.terradue.com/better-satcen-00002... 2018-06-18 05:19:14.166966 bce1a34e4a7a0f4feb35f468c96ea81c50c07964 https://catalog.terradue.com//better-satcen-00... 2018-06-18 05:18:58.063528 MTC_S1B_S1_SLC__1SDV_20180606T051857_20180606T... POLYGON((10.6534444444444 12.1762222222222 [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,... coh_VV_06Jun2018_18Jun2018
56 https://store.terradue.com/better-satcen-00002... 2018-06-06 05:19:13.262935 668a1aeeae2043b829312a825f82c75db804fbac https://catalog.terradue.com//better-satcen-00... 2018-06-06 05:18:57.166982 MTC_S1B_S1_SLC__1SDV_20180525T051856_20180525T... POLYGON((10.6534444444444 12.1762222222222 [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,... coh_VV_25May2018_06Jun2018
  • Show data bands name
In [19]:
print list(mydate_results.band)

['coh_VV_15Jun2018_27Jun2018', 'coh_VV_06Jun2018_18Jun2018', 'coh_VV_25May2018_06Jun2018']

Plotting data and exporting geotif

Plot data

Choose bands to be plotted

In [20]:
bands = None

bands =  mydate_results['aoi_data'].values
titles = mydate_results['band'].values

numbands = len(bands)

for i in range(numbands):
    bmin = bands[i].min()
    bmax = bands[i].max()
    if bmin != bmax:
        bands[i] = (bands[i] - bmin)/(bmax - bmin) * 255
    else:
        print 'WARNING bmin %s = bmax %s (%i)!' %(bmin, bmax,i)

  • Plot the bands separately
In [21]:
fig = plt.figure(figsize=(20,20))


for i in range(numbands):
    a = fig.add_subplot(3, 4, i+1)
    #
    imgplot = plt.imshow(bands[i].astype(np.uint8),
                         cmap='gray')
    a.set_title(titles[i])

plt.tight_layout()
fig = plt.gcf()
plt.show()

fig.clf()
plt.close()



MemoryErrorTraceback (most recent call last)
/opt/anaconda/lib/python2.7/site-packages/IPython/core/formatters.pyc in __call__(self, obj)
    305                 pass
    306             else:
--> 307                 return printer(obj)
    308             # Finally look for special method names
    309             method = get_real_method(obj, self.print_method)

/opt/anaconda/lib/python2.7/site-packages/IPython/core/pylabtools.pyc in <lambda>(fig)
    225
    226     if 'png' in formats:
--> 227         png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
    228     if 'retina' in formats or 'png2x' in formats:
    229         png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))

/opt/anaconda/lib/python2.7/site-packages/IPython/core/pylabtools.pyc in print_figure(fig, fmt, bbox_inches, **kwargs)
    117
    118     bytes_io = BytesIO()
--> 119     fig.canvas.print_figure(bytes_io, **kw)
    120     data = bytes_io.getvalue()
    121     if fmt == 'svg':

/opt/anaconda/lib/python2.7/site-packages/matplotlib/backend_bases.pyc in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, **kwargs)
   2198                     orientation=orientation,
   2199                     dryrun=True,
-> 2200                     **kwargs)
   2201                 renderer = self.figure._cachedRenderer
   2202                 bbox_inches = self.figure.get_tightbbox(renderer)

/opt/anaconda/lib/python2.7/site-packages/matplotlib/backends/backend_agg.pyc in print_png(self, filename_or_obj, *args, **kwargs)
    543
    544     def print_png(self, filename_or_obj, *args, **kwargs):
--> 545         FigureCanvasAgg.draw(self)
    546         renderer = self.get_renderer()
    547         original_dpi = renderer.dpi

/opt/anaconda/lib/python2.7/site-packages/matplotlib/backends/backend_agg.pyc in draw(self)
    462
    463         try:
--> 464             self.figure.draw(self.renderer)
    465         finally:
    466             RendererAgg.lock.release()

/opt/anaconda/lib/python2.7/site-packages/matplotlib/artist.pyc in draw_wrapper(artist, renderer, *args, **kwargs)
     61     def draw_wrapper(artist, renderer, *args, **kwargs):
     62         before(artist, renderer)
---> 63         draw(artist, renderer, *args, **kwargs)
     64         after(artist, renderer)
     65

/opt/anaconda/lib/python2.7/site-packages/matplotlib/figure.pyc in draw(self, renderer)
   1142
   1143             mimage._draw_list_compositing_images(
-> 1144                 renderer, self, dsu, self.suppressComposite)
   1145
   1146             renderer.close_group('figure')

/opt/anaconda/lib/python2.7/site-packages/matplotlib/image.pyc in _draw_list_compositing_images(renderer, parent, dsu, suppress_composite)
    137     if not_composite or not has_images:
    138         for zorder, a in dsu:
--> 139             a.draw(renderer)
    140     else:
    141         # Composite any adjacent images together

/opt/anaconda/lib/python2.7/site-packages/matplotlib/artist.pyc in draw_wrapper(artist, renderer, *args, **kwargs)
     61     def draw_wrapper(artist, renderer, *args, **kwargs):
     62         before(artist, renderer)
---> 63         draw(artist, renderer, *args, **kwargs)
     64         after(artist, renderer)
     65

/opt/anaconda/lib/python2.7/site-packages/matplotlib/axes/_base.pyc in draw(self, renderer, inframe)
   2424             renderer.stop_rasterizing()
   2425
-> 2426         mimage._draw_list_compositing_images(renderer, self, dsu)
   2427
   2428         renderer.close_group('axes')

/opt/anaconda/lib/python2.7/site-packages/matplotlib/image.pyc in _draw_list_compositing_images(renderer, parent, dsu, suppress_composite)
    137     if not_composite or not has_images:
    138         for zorder, a in dsu:
--> 139             a.draw(renderer)
    140     else:
    141         # Composite any adjacent images together

/opt/anaconda/lib/python2.7/site-packages/matplotlib/artist.pyc in draw_wrapper(artist, renderer, *args, **kwargs)
     61     def draw_wrapper(artist, renderer, *args, **kwargs):
     62         before(artist, renderer)
---> 63         draw(artist, renderer, *args, **kwargs)
     64         after(artist, renderer)
     65

/opt/anaconda/lib/python2.7/site-packages/matplotlib/image.pyc in draw(self, renderer, *args, **kwargs)
    541         else:
    542             im, l, b, trans = self.make_image(
--> 543                 renderer, renderer.get_image_magnification())
    544             if im is not None:
    545                 renderer.draw_image(gc, l, b, im)

/opt/anaconda/lib/python2.7/site-packages/matplotlib/image.pyc in make_image(self, renderer, magnification, unsampled)
    768         return self._make_image(
    769             self._A, bbox, transformed_bbox, self.axes.bbox, magnification,
--> 770             unsampled=unsampled)
    771
    772     def _check_unsampled_image(self, renderer):

/opt/anaconda/lib/python2.7/site-packages/matplotlib/image.pyc in _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification, unsampled, round_to_pixel_border)
    371                     # clipping to [0, 1] and we use out-of-bounds
    372                     # values to carry the over/under/bad information
--> 373                     rgba = np.empty((A.shape[0], A.shape[1], 4), dtype=A.dtype)
    374                     rgba[..., 0] = A  # normalized data
    375                     # this is to work around spurious warnings coming

MemoryError:
<matplotlib.figure.Figure at 0x7f0b00af57d0>
  • Select 3 bands and plot them as an RGB
In [25]:
rgbbands=(bands[0],bands[1],bands[2])

rgb_uint8 = np.dstack(rgbbands).astype(np.uint16)

width = 10
height = 10
plt.figure(figsize=(width, height))
img = Image.fromarray(rgb_uint8)
imgplot = plt.imshow(img)


TypeErrorTraceback (most recent call last)
<ipython-input-25-0d1a1fb357d6> in <module>()
      6 height = 10
      7 plt.figure(figsize=(width, height))
----> 8 img = Image.fromarray(rgb_uint8)
      9 imgplot = plt.imshow(img)

/opt/anaconda/lib/python2.7/site-packages/PIL/Image.pyc in fromarray(obj, mode)
   2166         except KeyError:
   2167             # print typekey
-> 2168             raise TypeError("Cannot handle this data type")
   2169     else:
   2170         rawmode = mode

TypeError: Cannot handle this data type
<matplotlib.figure.Figure at 0x7ff96bd22690>

Export the Geotiff

In [22]:
band_number = 3
cols = myGDF['xsize'].values[0]
rows = myGDF['ysize'].values[0]

print (cols,rows)

geo_transform = myGDF['geo_transform'].values[0]
projection = myGDF['projection'].values[0]

drv = gdal.GetDriverByName('GTiff')

ds = drv.Create('export.tif', cols, rows, band_number, gdal.GDT_Float32)

ds.SetGeoTransform(geo_transform)
ds.SetProjection(projection)

ds.GetRasterBand(1).WriteArray(rgbbands[0], 0, 0)
ds.GetRasterBand(2).WriteArray(rgbbands[1], 0, 0)
ds.GetRasterBand(3).WriteArray(rgbbands[2], 0, 0)
ds.FlushCache()
(1241, 1567)

Download functionalities

Download a product

  • Define the download function
In [23]:
def get_product(url, dest, api_key):

    request_headers = {'X-JFrog-Art-Api': api_key}

    r = requests.get(url, headers=request_headers)

    open(dest, 'wb').write(r.content)

    return r.status_code
  • Get the reference download endpoint for the product related to the first date
In [24]:
enclosure = myGDF[(myGDF['startdate'] == mydates[0])]['enclosure'].values[0]

enclosure
Out[24]:
'https://store.terradue.com/better-wfp-00005/_results/workflows/ec_better_ewf_wfp_01_02_03_ewf_wfp_01_02_03_0_5/run/0fe626d4-19bf-11e9-adb5-0242ac11000f/0001333-181221095105003-oozie-oozi-W/ff52d47e-a750-4368-a476-55a197fa1980/S1A_IW_GRDH_1SDV_20170929T132959_20170929T133024_018591_01F571_F9F4_Sigma0_all_bands.tif'
In [25]:
output_name = myGDF[(myGDF['startdate'] == mydates[0])]['title'].values[0]

output_name
Out[25]:
'S1A_IW_GRDH_1SDV_20170929T132959_20170929T133024_018591_01F571_F9F4_Sigma0_all_bands.tif'
In [26]:
get_product(enclosure,
            output_name,
            api_key)
Out[26]:
200

Bulk Download

  • Define the bulk download function
In [27]:
def get_product_bulk(row, api_key):

    return get_product(row['enclosure'],
                       row['title'],
                       api_key)
  • Download all the products related to the chosen dates
In [28]:
myGDF.apply(lambda row: get_product_bulk(row, api_key), axis=1)
Out[28]:
0     200
1     200
2     200
3     200
4     200
5     200
6     200
7     200
8     200
9     200
10    200
11    200
dtype: int64