update from GitHub.com

master
Kilian Vos 6 years ago
parent 75d449edaf
commit 8c2ba23cff

@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>NARRA</name>
<Style id="poly-000000-1200-77-nodesc-normal">
<LineStyle>
<color>ff000000</color>
<width>1.2</width>
</LineStyle>
<PolyStyle>
<color>4d000000</color>
<fill>1</fill>
<outline>1</outline>
</PolyStyle>
<BalloonStyle>
<text><![CDATA[<h3>$[name]</h3>]]></text>
</BalloonStyle>
</Style>
<Style id="poly-000000-1200-77-nodesc-highlight">
<LineStyle>
<color>ff000000</color>
<width>1.8</width>
</LineStyle>
<PolyStyle>
<color>4d000000</color>
<fill>1</fill>
<outline>1</outline>
</PolyStyle>
<BalloonStyle>
<text><![CDATA[<h3>$[name]</h3>]]></text>
</BalloonStyle>
</Style>
<StyleMap id="poly-000000-1200-77-nodesc">
<Pair>
<key>normal</key>
<styleUrl>#poly-000000-1200-77-nodesc-normal</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#poly-000000-1200-77-nodesc-highlight</styleUrl>
</Pair>
</StyleMap>
<Placemark>
<name>Polygon 1</name>
<styleUrl>#poly-000000-1200-77-nodesc</styleUrl>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<tessellate>1</tessellate>
<coordinates>
151.2957545,-33.7012561,0
151.297557,-33.7388075,0
151.312234,-33.7390216,0
151.311204,-33.701399,0
151.2957545,-33.7012561,0
</coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
</Document>
</kml>

@ -9,7 +9,7 @@ The underlying approach and application of the CoastSat toolkit are described in
*Vos K., Splinter K.D., Harley M.D., Simmons J.A., Turner I.L. (submitted). CoastSat: a Google Earth Engine-enabled Python toolkit to extract shorelines from publicly available satellite imagery, Environmental Modelling and Software*. *Vos K., Splinter K.D., Harley M.D., Simmons J.A., Turner I.L. (submitted). CoastSat: a Google Earth Engine-enabled Python toolkit to extract shorelines from publicly available satellite imagery, Environmental Modelling and Software*.
There are two main steps: There are two main steps:
- assisted retrieval from from Google Earth Engine of all avaiable satellite images spanning the user-defined region of interest and time period - assisted retrieval from Google Earth Engine of all avaiable satellite images spanning the user-defined region of interest and time period
- automated extraction of shorelines from all the selected images using a sub-pixel resolution technique - automated extraction of shorelines from all the selected images using a sub-pixel resolution technique
@ -105,7 +105,7 @@ jupyter notebook
``` ```
A web browser window will open. Point to the directory where you downloaded/cloned this repository and click on `example_jupyter.ipynb`. A web browser window will open. Point to the directory where you downloaded/cloned this repository and click on `example_jupyter.ipynb`.
The following sections guide the reader through the different functionalities of CoastSat with an example at Narrabeen-Collaroy beach (Australia). If you prefer to use Spyder or PyCharm or other integrated development environments (IDEs), a Python script is also included `main.py` in the repository. The following sections guide the reader through the different functionalities of CoastSat with an example at Narrabeen-Collaroy beach (Australia). If you prefer to use Spyder or PyCharm or other integrated development environments (IDEs), a Python script `main.py` is also included in the repository. If using `main.py` on Spyder, make sure that the Graphics Backend is set to **Automatic** and not **Inline** (as this mode doesn't allow to interact with the figures). To change this setting go under Preferences>IPython console>Graphics.
To run a Jupyter Notebook, place your cursor inside one of the code sections and then clikc on the 'run' button up in the top menu to run that section and progress forward (as shown in the animation below). To run a Jupyter Notebook, place your cursor inside one of the code sections and then clikc on the 'run' button up in the top menu to run that section and progress forward (as shown in the animation below).
@ -129,16 +129,16 @@ It is now time to map the sandy shorelines!
The following user-defined settings are required: The following user-defined settings are required:
- `cloud_thresh`: threshold on maximum cloud cover that is acceptable on the images (value between 0 and 1 - this may require some initial experimentation) - `cloud_thresh`: threshold on maximum cloud cover that is acceptable on the images (value between 0 and 1 - this may require some initial experimentation)
- `output_epsg`: epsg code defining the spatial reference system of the shoreline coordinates - `output_epsg`: epsg code defining the spatial reference system of the shoreline coordinates. It has to be a cartesion coordinate system (i.e. projected) and not a geographical coordinate system (in latitude and longitude angles).
- `check_detection`: if set to `True` allows the user to quality control each shoreline detection - `check_detection`: if set to `True` allows the user to quality control each shoreline detection
See http://spatialreference.org/ to find the EPSG number corresponding to your local coordinate system. If the user wants to quality control the mapped shorelines and manually validate each detection, the parameter `check_detection` should be set to `True`. See http://spatialreference.org/ to find the EPSG number corresponding to your local coordinate system. If the user wants to quality control the mapped shorelines and manually validate each detection, the parameter `check_detection` should be set to `True`.
In addition, there are extra parameters (`min_beach_size`, `buffer_size`, `min_length_sl`) that can be tuned to optimise the shoreline detection (for Advanced users only). For the moment leave these parameters set to their default values, we will see later how they can be modified. In addition, there are extra parameters (`min_beach_size`, `buffer_size`, `min_length_sl`, `cloud_mask_issue`) that can be tuned to optimise the shoreline detection (for Advanced users only). For the moment leave these parameters set to their default values, we will see later how they can be modified.
An example of settings is provided here: An example of settings is provided here:
![settings](https://user-images.githubusercontent.com/7217258/49565578-ba7f5200-f97b-11e8-9bb4-8d933329b625.PNG) ![settings_v2](https://user-images.githubusercontent.com/7217258/52684207-876bc700-2f99-11e9-9e5c-086f523bcdc2.PNG)
Once all the settings have been defined, the batch shoreline detection can be launched by calling: Once all the settings have been defined, the batch shoreline detection can be launched by calling:
``` ```
@ -162,6 +162,7 @@ As mentioned above, there are some additional parameters that can be modified to
- `min_beach_area`: minimum allowable object area (in metres^2) for the class 'sand'. During the image classification, some features (for example, building roofs) may be incorrectly labelled as sand. To correct this, all the objects classified as sand containing less than a certain number of connected pixels are removed from the sand class. The default value of `min_beach_area` is 4500 m^2, which corresponds to 20 connected pixels of 15 m^2. If you are looking at a very small beach (<20 connected pixels on the images), try decreasing the value of this parameter. - `min_beach_area`: minimum allowable object area (in metres^2) for the class 'sand'. During the image classification, some features (for example, building roofs) may be incorrectly labelled as sand. To correct this, all the objects classified as sand containing less than a certain number of connected pixels are removed from the sand class. The default value of `min_beach_area` is 4500 m^2, which corresponds to 20 connected pixels of 15 m^2. If you are looking at a very small beach (<20 connected pixels on the images), try decreasing the value of this parameter.
- `buffer_size`: radius (in metres) that defines the buffer around sandy pixels that is considered for the shoreline detection. The default value of `buffer_size` is 150 m. This parameter should be increased if you have a very wide (>150 m) surf zone or inter-tidal zone. - `buffer_size`: radius (in metres) that defines the buffer around sandy pixels that is considered for the shoreline detection. The default value of `buffer_size` is 150 m. This parameter should be increased if you have a very wide (>150 m) surf zone or inter-tidal zone.
- `min_length_sl`: minimum length (in metres) of shoreline perimeter to be valid. This can be used to discard small features that are detected but do not correspond to the sand-water shoreline. The default value is 200 m. If the shoreline that you are trying to map is shorter than 200 m, decrease the value of this parameter. - `min_length_sl`: minimum length (in metres) of shoreline perimeter to be valid. This can be used to discard small features that are detected but do not correspond to the sand-water shoreline. The default value is 200 m. If the shoreline that you are trying to map is shorter than 200 m, decrease the value of this parameter.
- `cloud_mask_issue`: the cloud mask algorithm applied to Landsat images by USGS, namely CFMASK, does have difficulties sometimes with very bright features such as beaches or white-water in the ocean. This may result in pixels corresponding to a beach being identified as clouds in the cloud mask (appear as black pixels on your images). If this issue seems to be present in a large proportion of images from your local beach, you can switch this parameter to `True` and CoastSat will remove from the cloud mask the pixels that form very thin linear features (as often these are beaches and not clouds). Only activate this parameter if you observe this very specific cloud mask issue, otherwise leave to the default value of `False`.
#### Reference shoreline #### Reference shoreline
@ -180,13 +181,25 @@ The maximum distance (in metres) allowed from the reference shoreline is defined
### 2.3 Shoreline change analysis ### 2.3 Shoreline change analysis
This section shows how to obtain time-series of shoreline change along shore-normal transects. This section shows how to obtain time-series of shoreline change along shore-normal transects. Each transect is defined by two points, its origin and a second point that defines its orientation. The parameter `transect_length` determines how far (in metres) from the origin the transect will span. There are 3 options to define the coordinates of the transects:
1. The user can interactively draw shore-normal transects along the beach:
The user can draw shore-normal transects by calling:
``` ```
settings['transect_length'] = 500 # defines the length of the transects in metres
transects = SDS_transects.draw_transects(output, settings) transects = SDS_transects.draw_transects(output, settings)
``` ```
2. Load the transect coordinates from a KML file:
```
transects = SDS_transects.load_transects_from_kml('transects.kml')
```
3. Create the transects by manually providing the coordinates of two points:
```
transects = dict([])
transects['Transect 1'] = np.array([[342836, ,6269215], [343315, 6269071]])
transects['Transect 2'] = np.array([[342482, 6268466], [342958, 6268310]])
transects['Transect 3'] = np.array([[342185, 6267650], [342685, 6267641]])
```
**Note:** if you choose option 2 or 3, make sure that the points that you are providing are in the spatial reference system defined by `settings['output_epsg']`.
Once the shore-normal transects have been defined, the intersection between the 2D shorelines and the transects is computed with the following function: Once the shore-normal transects have been defined, the intersection between the 2D shorelines and the transects is computed with the following function:
``` ```
settings['along_dist'] = 25 settings['along_dist'] = 25

@ -543,14 +543,20 @@ def retrieve_images(inputs):
im_epsg.append(int(im_dic['bands'][0]['crs'][5:])) im_epsg.append(int(im_dic['bands'][0]['crs'][5:]))
# Sentinel-2 products don't provide a georeferencing accuracy (RMSE as in Landsat) # Sentinel-2 products don't provide a georeferencing accuracy (RMSE as in Landsat)
# but they have a flag indicating if the geometric quality control was passed or failed # but they have a flag indicating if the geometric quality control was passed or failed
# if passed a value of 1 is stored if faile a value of -1 is stored in the metadata # if passed a value of 1 is stored if failed a value of -1 is stored in the metadata
try: if 'GEOMETRIC_QUALITY_FLAG' in im_dic['properties'].keys():
if im_dic['properties']['GEOMETRIC_QUALITY_FLAG'] == 'PASSED': if im_dic['properties']['GEOMETRIC_QUALITY_FLAG'] == 'PASSED':
acc_georef.append(1) acc_georef.append(1)
else: else:
acc_georef.append(-1) acc_georef.append(-1)
except: elif 'quality_check' in im_dic['properties'].keys():
if im_dic['properties']['quality_check'] == 'PASSED':
acc_georef.append(1)
else:
acc_georef.append(-1) acc_georef.append(-1)
else:
acc_georef.append(-1)
print(i+1, end='..') print(i+1, end='..')
# sort timestamps and georef accuracy (dowloaded images are sorted by date in directory) # sort timestamps and georef accuracy (dowloaded images are sorted by date in directory)
@ -564,7 +570,7 @@ def retrieve_images(inputs):
'epsg':im_epsg_sorted, 'filenames':filenames_sorted} 'epsg':im_epsg_sorted, 'filenames':filenames_sorted}
print('\nFinished with ' + satname) print('\nFinished with ' + satname)
# merge overlapping images (only if polygon is at the edge of an image) # merge overlapping images (necessary only if the polygon is at the boundary of an image)
if 'S2' in metadata.keys(): if 'S2' in metadata.keys():
metadata = merge_overlapping_images(metadata,inputs) metadata = merge_overlapping_images(metadata,inputs)
@ -643,7 +649,7 @@ def merge_overlapping_images(metadata,inputs):
fn_im.append([os.path.join(filepath, 'S2', '10m', filenames[pair[index]]), fn_im.append([os.path.join(filepath, 'S2', '10m', filenames[pair[index]]),
os.path.join(filepath, 'S2', '20m', filenames[pair[index]].replace('10m','20m')), os.path.join(filepath, 'S2', '20m', filenames[pair[index]].replace('10m','20m')),
os.path.join(filepath, 'S2', '60m', filenames[pair[index]].replace('10m','60m'))]) os.path.join(filepath, 'S2', '60m', filenames[pair[index]].replace('10m','60m'))])
im_ms, georef, cloud_mask, im_extra, imQA = SDS_preprocess.preprocess_single(fn_im[index], sat) im_ms, georef, cloud_mask, im_extra, imQA = SDS_preprocess.preprocess_single(fn_im[index], sat, False)
# in Sentinel2 images close to the edge of the image there are some artefacts, # in Sentinel2 images close to the edge of the image there are some artefacts,
# that are squares with constant pixel intensities. They need to be masked in the # that are squares with constant pixel intensities. They need to be masked in the
@ -772,7 +778,7 @@ def remove_cloudy_images(metadata,inputs,cloud_thresh):
# image filename # image filename
fn = SDS_tools.get_filenames(filenames[i],filepath, satname) fn = SDS_tools.get_filenames(filenames[i],filepath, satname)
# preprocess image (cloud mask + pansharpening/downsampling) # preprocess image (cloud mask + pansharpening/downsampling)
im_ms, georef, cloud_mask, im_extra, imQA = SDS_preprocess.preprocess_single(fn, satname) im_ms, georef, cloud_mask, im_extra, imQA = SDS_preprocess.preprocess_single(fn, satname, False)
# calculate cloud cover # calculate cloud cover
cloud_cover = np.divide(sum(sum(cloud_mask.astype(int))), cloud_cover = np.divide(sum(sum(cloud_mask.astype(int))),
(cloud_mask.shape[0]*cloud_mask.shape[1])) (cloud_mask.shape[0]*cloud_mask.shape[1]))

@ -28,7 +28,7 @@ import SDS_tools
np.seterr(all='ignore') # raise/ignore divisions by 0 and nans np.seterr(all='ignore') # raise/ignore divisions by 0 and nans
def create_cloud_mask(im_qa, satname): def create_cloud_mask(im_qa, satname, cloud_mask_issue):
""" """
Creates a cloud mask using the information contained in the QA band. Creates a cloud mask using the information contained in the QA band.
@ -40,6 +40,8 @@ def create_cloud_mask(im_qa, satname):
Image containing the QA band Image containing the QA band
satname: string satname: string
short name for the satellite (L5, L7, L8 or S2) short name for the satellite (L5, L7, L8 or S2)
cloud_mask_issue: boolean
True if there is an issue with the cloud mask and sand pixels are being masked on the images
Returns: Returns:
----------- -----------
@ -58,9 +60,15 @@ def create_cloud_mask(im_qa, satname):
# find which pixels have bits corresponding to cloud values # find which pixels have bits corresponding to cloud values
cloud_mask = np.isin(im_qa, cloud_values) cloud_mask = np.isin(im_qa, cloud_values)
# remove isolated cloud pixels (there are some in the swash zone and they are not clouds) # remove cloud pixels that form very thin features. These are beach or swash pixels that are
# erroneously identified as clouds by the CFMASK algorithm applied to the images by the USGS.
if sum(sum(cloud_mask)) > 0 and sum(sum(~cloud_mask)) > 0: if sum(sum(cloud_mask)) > 0 and sum(sum(~cloud_mask)) > 0:
morphology.remove_small_objects(cloud_mask, min_size=10, connectivity=1, in_place=True) morphology.remove_small_objects(cloud_mask, min_size=10, connectivity=1, in_place=True)
if cloud_mask_issue:
elem = morphology.square(3) # use a square of width 3 pixels
cloud_mask = morphology.binary_opening(cloud_mask,elem) # perform image opening
# remove objects with less than 25 connected pixels
morphology.remove_small_objects(cloud_mask, min_size=25, connectivity=1, in_place=True)
return cloud_mask return cloud_mask
@ -209,7 +217,7 @@ def rescale_image_intensity(im, cloud_mask, prob_high):
return im_adj return im_adj
def preprocess_single(fn, satname): def preprocess_single(fn, satname, cloud_mask_issue):
""" """
Reads the image and outputs the pansharpened/down-sampled multispectral bands, the Reads the image and outputs the pansharpened/down-sampled multispectral bands, the
georeferencing vector of the image (coordinates of the upper left pixel), the cloud mask and georeferencing vector of the image (coordinates of the upper left pixel), the cloud mask and
@ -226,6 +234,8 @@ def preprocess_single(fn, satname):
resolution (30m and 15m for Landsat 7-8, 10m, 20m, 60m for Sentinel-2) resolution (30m and 15m for Landsat 7-8, 10m, 20m, 60m for Sentinel-2)
satname: str satname: str
name of the satellite mission (e.g., 'L5') name of the satellite mission (e.g., 'L5')
cloud_mask_issue: boolean
True if there is an issue with the cloud mask and sand pixels are being masked on the images
Returns: Returns:
----------- -----------
@ -262,7 +272,7 @@ def preprocess_single(fn, satname):
# create cloud mask # create cloud mask
im_qa = im_ms[:,:,5] im_qa = im_ms[:,:,5]
im_ms = im_ms[:,:,:-1] im_ms = im_ms[:,:,:-1]
cloud_mask = create_cloud_mask(im_qa, satname) cloud_mask = create_cloud_mask(im_qa, satname, cloud_mask_issue)
# resize the image using bilinear interpolation (order 1) # resize the image using bilinear interpolation (order 1)
im_ms = transform.resize(im_ms,(nrows, ncols), order=1, preserve_range=True, im_ms = transform.resize(im_ms,(nrows, ncols), order=1, preserve_range=True,
@ -314,7 +324,7 @@ def preprocess_single(fn, satname):
# create cloud mask # create cloud mask
im_qa = im_ms[:,:,5] im_qa = im_ms[:,:,5]
cloud_mask = create_cloud_mask(im_qa, satname) cloud_mask = create_cloud_mask(im_qa, satname, cloud_mask_issue)
# resize the image using bilinear interpolation (order 1) # resize the image using bilinear interpolation (order 1)
im_ms = im_ms[:,:,:5] im_ms = im_ms[:,:,:5]
@ -374,7 +384,7 @@ def preprocess_single(fn, satname):
# create cloud mask # create cloud mask
im_qa = im_ms[:,:,5] im_qa = im_ms[:,:,5]
cloud_mask = create_cloud_mask(im_qa, satname) cloud_mask = create_cloud_mask(im_qa, satname, cloud_mask_issue)
# resize the image using bilinear interpolation (order 1) # resize the image using bilinear interpolation (order 1)
im_ms = im_ms[:,:,:5] im_ms = im_ms[:,:,:5]
@ -456,7 +466,7 @@ def preprocess_single(fn, satname):
bands = [data.GetRasterBand(k + 1).ReadAsArray() for k in range(data.RasterCount)] bands = [data.GetRasterBand(k + 1).ReadAsArray() for k in range(data.RasterCount)]
im60 = np.stack(bands, 2) im60 = np.stack(bands, 2)
imQA = im60[:,:,0] imQA = im60[:,:,0]
cloud_mask = create_cloud_mask(imQA, satname) cloud_mask = create_cloud_mask(imQA, satname, cloud_mask_issue)
# resize the cloud mask using nearest neighbour interpolation (order 0) # resize the cloud mask using nearest neighbour interpolation (order 0)
cloud_mask = transform.resize(cloud_mask,(nrows, ncols), order=0, preserve_range=True, cloud_mask = transform.resize(cloud_mask,(nrows, ncols), order=0, preserve_range=True,
mode='constant') mode='constant')
@ -551,10 +561,12 @@ def save_jpg(metadata, settings):
contains all the information about the satellite images that were downloaded contains all the information about the satellite images that were downloaded
settings: dict settings: dict
contains the following fields: contains the following fields:
'cloud_thresh': float cloud_thresh: float
value between 0 and 1 indicating the maximum cloud fraction in the image that is accepted value between 0 and 1 indicating the maximum cloud fraction in the image that is accepted
'sitename': string sitename: string
name of the site (also name of the folder where the images are stored) name of the site (also name of the folder where the images are stored)
cloud_mask_issue: boolean
True if there is an issue with the cloud mask and sand pixels are being masked on the images
Returns: Returns:
----------- -----------
@ -582,7 +594,7 @@ def save_jpg(metadata, settings):
# image filename # image filename
fn = SDS_tools.get_filenames(filenames[i],filepath, satname) fn = SDS_tools.get_filenames(filenames[i],filepath, satname)
# read and preprocess image # read and preprocess image
im_ms, georef, cloud_mask, im_extra, imQA = preprocess_single(fn, satname) im_ms, georef, cloud_mask, im_extra, imQA = preprocess_single(fn, satname, settings['cloud_mask_issue'])
# calculate cloud cover # calculate cloud cover
cloud_cover = np.divide(sum(sum(cloud_mask.astype(int))), cloud_cover = np.divide(sum(sum(cloud_mask.astype(int))),
(cloud_mask.shape[0]*cloud_mask.shape[1])) (cloud_mask.shape[0]*cloud_mask.shape[1]))
@ -593,6 +605,10 @@ def save_jpg(metadata, settings):
date = filenames[i][:10] date = filenames[i][:10]
create_jpg(im_ms, cloud_mask, date, satname, filepath_jpg) create_jpg(im_ms, cloud_mask, date, satname, filepath_jpg)
# print the location where the images have been saved
print('Satellite images saved as .jpg in ' + os.path.join(os.getcwd(), 'data', sitename,
'jpg_files', 'preprocessed'))
def get_reference_sl_manual(metadata, settings): def get_reference_sl_manual(metadata, settings):
""" """
Allows the user to manually digitize a reference shoreline that is used seed the shoreline Allows the user to manually digitize a reference shoreline that is used seed the shoreline
@ -650,14 +666,14 @@ def get_reference_sl_manual(metadata, settings):
filepath = SDS_tools.get_filepath(settings['inputs'],satname) filepath = SDS_tools.get_filepath(settings['inputs'],satname)
filenames = metadata[satname]['filenames'] filenames = metadata[satname]['filenames']
else: else:
print('You cannot digitize the shoreline on L7 images, add another L8, S2 or L5 to your dataset.') raise Exception('You cannot digitize the shoreline on L7 images, add another L8, S2 or L5 to your dataset.')
# loop trhough the images # loop trhough the images
for i in range(len(filenames)): for i in range(len(filenames)):
# read image # read image
fn = SDS_tools.get_filenames(filenames[i],filepath, satname) fn = SDS_tools.get_filenames(filenames[i],filepath, satname)
im_ms, georef, cloud_mask, im_extra, imQA = preprocess_single(fn, satname) im_ms, georef, cloud_mask, im_extra, imQA = preprocess_single(fn, satname, settings['cloud_mask_issue'])
# calculate cloud cover # calculate cloud cover
cloud_cover = np.divide(sum(sum(cloud_mask.astype(int))), cloud_cover = np.divide(sum(sum(cloud_mask.astype(int))),
(cloud_mask.shape[0]*cloud_mask.shape[1])) (cloud_mask.shape[0]*cloud_mask.shape[1]))
@ -684,7 +700,7 @@ def get_reference_sl_manual(metadata, settings):
mng = plt.get_current_fig_manager() mng = plt.get_current_fig_manager()
mng.window.showMaximized() mng.window.showMaximized()
# let user click on the image once # let user click on the image once
pt_input = ginput(n=1, timeout=1000000, show_clicks=True) pt_input = ginput(n=1, timeout=1e9, show_clicks=False)
pt_input = np.array(pt_input) pt_input = np.array(pt_input)
# if clicks next to <skip>, show another image # if clicks next to <skip>, show another image
if pt_input[0][0] > im_ms.shape[1]/2: if pt_input[0][0] > im_ms.shape[1]/2:
@ -694,102 +710,78 @@ def get_reference_sl_manual(metadata, settings):
# remove keep and skip buttons # remove keep and skip buttons
keep_button.set_visible(False) keep_button.set_visible(False)
skip_button.set_visible(False) skip_button.set_visible(False)
# create two new buttons
add_button = plt.text(0, 0.9, 'add', size=16, ha="left", va="top",
transform=plt.gca().transAxes,
bbox=dict(boxstyle="square", ec='k',fc='w'))
end_button = plt.text(1, 0.9, 'end', size=16, ha="right", va="top",
transform=plt.gca().transAxes,
bbox=dict(boxstyle="square", ec='k',fc='w'))
# add multiple reference shorelines (until user clicks on <end> button)
pts_sl = np.expand_dims(np.array([np.nan, np.nan]),axis=0)
while 1:
add_button.set_visible(False)
end_button.set_visible(False)
# update title (instructions) # update title (instructions)
plt.title('Click points along the shoreline every ~500 m.\n' + plt.title('Click points along the shoreline (enough points to capture the beach curvature).\n' +
'Start at one end of the beach.\n' + 'When finished digitizing, click <ENTER>', 'Start at one end of the beach.\n' + 'When finished digitizing, click <ENTER>',
fontsize=14) fontsize=14)
plt.draw() plt.draw()
# let user click on the shoreline # let user click on the shoreline
pts = ginput(n=50000, timeout=1e9, show_clicks=True) pts = ginput(n=50000, timeout=1e9, show_clicks=True)
pts_pix = np.array(pts) pts_pix = np.array(pts)
# convert pixel coordinates to world coordinates
# interpolate between points and show the output to the user pts_world = SDS_tools.convert_pix2world(pts_pix[:,[1,0]], georef)
pts_pix_interp = np.expand_dims(np.array([np.nan, np.nan]),axis=0) # interpolate between points clicked by the user (1m resolution)
for k in range(len(pts_pix)-1): pts_world_interp = np.expand_dims(np.array([np.nan, np.nan]),axis=0)
if pts_pix[k,0] < pts_pix[k+1,0]: for k in range(len(pts_world)-1):
x = pts_pix[[k,k+1],0] pt_dist = np.linalg.norm(pts_world[k,:]-pts_world[k+1,:])
y = pts_pix[[k,k+1],1] xvals = np.arange(0,pt_dist)
else: yvals = np.zeros(len(xvals))
x = pts_pix[[k+1,k],0] pt_coords = np.zeros((len(xvals),2))
y = pts_pix[[k+1,k],1] pt_coords[:,0] = xvals
xvals = np.linspace(x[0],x[1],50) pt_coords[:,1] = yvals
yinterp = np.interp(xvals,x,y) phi = 0
pts_pix_interp = np.append(pts_pix_interp, deltax = pts_world[k+1,0] - pts_world[k,0]
np.transpose(np.array([xvals,yinterp])), axis=0) deltay = pts_world[k+1,1] - pts_world[k,1]
pts_pix_interp = np.delete(pts_pix_interp,0,axis=0) phi = np.pi/2 - np.math.atan2(deltax, deltay)
plt.plot(pts_pix_interp[:,0], pts_pix_interp[:,1], 'r.', markersize=3) tf = transform.EuclideanTransform(rotation=phi, translation=pts_world[k,:])
plt.title('Saving reference shoreline as ' + sitename + '_reference_shoreline.pkl ...') pts_world_interp = np.append(pts_world_interp,tf(pt_coords), axis=0)
pts_world_interp = np.delete(pts_world_interp,0,axis=0)
# convert to pixel coordinates and plot
pts_pix_interp = SDS_tools.convert_world2pix(pts_world_interp, georef)
pts_sl = np.append(pts_sl, pts_world_interp, axis=0)
plt.plot(pts_pix_interp[:,0], pts_pix_interp[:,1], 'r--')
plt.plot(pts_pix_interp[0,0], pts_pix_interp[0,1],'ko')
plt.plot(pts_pix_interp[-1,0], pts_pix_interp[-1,1],'ko')
# update title and buttons
add_button.set_visible(True)
end_button.set_visible(True)
plt.title('click <add> to digitize another shoreline or <end> to finish and save the shoreline(s)',
fontsize=14)
plt.draw() plt.draw()
ginput(n=1, timeout=5, show_clicks=True) pt_input = ginput(n=1, timeout=1e9, show_clicks=False)
pt_input = np.array(pt_input)
# if user clicks on <end>, save the points and break the loop
if pt_input[0][0] > im_ms.shape[1]/2:
add_button.set_visible(False)
end_button.set_visible(False)
plt.title('Reference shoreline saved as ' + sitename + '_reference_shoreline.pkl')
plt.draw()
ginput(n=1, timeout=5, show_clicks=False)
plt.close() plt.close()
break
pts_sl = np.delete(pts_sl,0,axis=0)
# convert world coordinates to user-defined coordinates
# convert image coordinates to world coordinates
pts_world = SDS_tools.convert_pix2world(pts_pix_interp[:,[1,0]], georef)
image_epsg = metadata[satname]['epsg'][i] image_epsg = metadata[satname]['epsg'][i]
pts_coords = SDS_tools.convert_epsg(pts_world, image_epsg, settings['output_epsg']) pts_coords = SDS_tools.convert_epsg(pts_sl, image_epsg, settings['output_epsg'])
# save the reference shoreline # save the reference shoreline
filepath = os.path.join(os.getcwd(), 'data', sitename) filepath = os.path.join(os.getcwd(), 'data', sitename)
with open(os.path.join(filepath, sitename + '_reference_shoreline.pkl'), 'wb') as f: with open(os.path.join(filepath, sitename + '_reference_shoreline.pkl'), 'wb') as f:
pickle.dump(pts_coords, f) pickle.dump(pts_coords, f)
print('Reference shoreline has been saved') print('Reference shoreline has been saved in ' + filepath)
break break
return pts_coords return pts_coords
def get_reference_sl_Australia(settings):
"""
Automatically finds a reference shoreline from a high resolution coastline of Australia
(Smartline from Geoscience Australia). It finds the points of the national coastline vector
that are situated inside the area of interest (polygon).
KV WRL 2018
Arguments:
-----------
settings: dict
contains the following fields:
'cloud_thresh': float
value between 0 and 1 indicating the maximum cloud fraction in the image that is accepted
'sitename': string
name of the site (also name of the folder where the images are stored)
'output_epsg': int
epsg code of the desired spatial reference system
Returns:
-----------
ref_sl: np.array
coordinates of the reference shoreline found in the shapefile
"""
# load high-resolution shoreline of Australia
filename = os.path.join(os.getcwd(), 'data', 'shoreline_Australia.pkl')
with open(filename, 'rb') as f:
sl = pickle.load(f)
# spatial reference system of this shoreline
sl_epsg = 4283 # GDA94 geographic
# only select the points that sit inside the area of interest (polygon)
polygon = settings['inputs']['polygon']
# spatial reference system of the polygon (latitudes and longitudes)
polygon_epsg = 4326 # WGS84 geographic
polygon = SDS_tools.convert_epsg(np.array(polygon[0]), polygon_epsg, sl_epsg)[:,:-1]
# use matplotlib function Path
path = mpltPath.Path(polygon)
sl_inside = sl[np.where(path.contains_points(sl))]
# convert to desired output coordinate system
ref_sl = SDS_tools.convert_epsg(sl_inside, sl_epsg, settings['output_epsg'])[:,:-1]
# make a figure for quality control
plt.figure()
plt.axis('equal')
plt.xlabel('Eastings [m]')
plt.ylabel('Northings [m]')
plt.plot(ref_sl[:,0], ref_sl[:,1], 'r.')
polygon = SDS_tools.convert_epsg(polygon, sl_epsg, settings['output_epsg'])[:,:-1]
plt.plot(polygon[:,0], polygon[:,1], 'k-')
return ref_sl

@ -164,36 +164,14 @@ def classify_image_NN(im_ms, im_extra, cloud_mask, min_beach_area, satname):
""" """
if satname == 'L5': if satname == 'S2':
# load classifier (without panchromatic band)
clf = joblib.load(os.path.join(os.getcwd(), 'classifiers', 'NN_4classes_nopan.pkl'))
# calculate features
n_features = 9
im_features = np.zeros((im_ms.shape[0], im_ms.shape[1], n_features))
im_features[:,:,[0,1,2,3,4]] = im_ms
im_features[:,:,5] = nd_index(im_ms[:,:,3], im_ms[:,:,1], cloud_mask) # (NIR-G)
im_features[:,:,6] = nd_index(im_ms[:,:,3], im_ms[:,:,2], cloud_mask) # ND(NIR-R)
im_features[:,:,7] = nd_index(im_ms[:,:,0], im_ms[:,:,2], cloud_mask) # ND(B-R)
im_features[:,:,8] = nd_index(im_ms[:,:,4], im_ms[:,:,1], cloud_mask) # ND(SWIR-G)
vec_features = im_features.reshape((im_ms.shape[0] * im_ms.shape[1], n_features))
elif satname in ['L7','L8']:
# load classifier (with panchromatic band)
clf = joblib.load(os.path.join(os.getcwd(), 'classifiers', 'NN_4classes_withpan.pkl'))
# calculate features
n_features = 10
im_features = np.zeros((im_ms.shape[0], im_ms.shape[1], n_features))
im_features[:,:,[0,1,2,3,4]] = im_ms
im_features[:,:,5] = im_extra
im_features[:,:,6] = nd_index(im_ms[:,:,3], im_ms[:,:,1], cloud_mask) # (NIR-G)
im_features[:,:,7] = nd_index(im_ms[:,:,3], im_ms[:,:,2], cloud_mask) # ND(NIR-R)
im_features[:,:,8] = nd_index(im_ms[:,:,0], im_ms[:,:,2], cloud_mask) # ND(B-R)
im_features[:,:,9] = nd_index(im_ms[:,:,4], im_ms[:,:,1], cloud_mask) # ND(SWIR-G)
vec_features = im_features.reshape((im_ms.shape[0] * im_ms.shape[1], n_features))
elif satname == 'S2':
# load classifier (special classifier for Sentinel-2 images) # load classifier (special classifier for Sentinel-2 images)
clf = joblib.load(os.path.join(os.getcwd(), 'classifiers', 'NN_4classes_S2.pkl')) clf = joblib.load(os.path.join(os.getcwd(), 'classifiers', 'NN_4classes_S2.pkl'))
else:
# load classifier (special classifier for Landsat images)
clf = joblib.load(os.path.join(os.getcwd(), 'classifiers', 'NN_4classes_Landsat.pkl'))
# calculate features # calculate features
vec_features = calculate_features(im_ms, cloud_mask, np.ones(cloud_mask.shape).astype(bool)) vec_features = calculate_features(im_ms, cloud_mask, np.ones(cloud_mask.shape).astype(bool))
vec_features[np.isnan(vec_features)] = 1e-9 # NaN values are create when std is too close to 0 vec_features[np.isnan(vec_features)] = 1e-9 # NaN values are create when std is too close to 0
@ -269,7 +247,7 @@ def find_wl_contours1(im_ndwi, cloud_mask):
return contours return contours
def find_wl_contours2(im_ms, im_labels, cloud_mask, buffer_size): def find_wl_contours2(im_ms, im_labels, cloud_mask, buffer_size, is_reference_sl):
""" """
New robust method for extracting shorelines. Incorporates the classification component to New robust method for extracting shorelines. Incorporates the classification component to
refine the treshold and make it specific to the sand/water interface. refine the treshold and make it specific to the sand/water interface.
@ -287,6 +265,8 @@ def find_wl_contours2(im_ms, im_labels, cloud_mask, buffer_size):
buffer_size: int buffer_size: int
size of the buffer around the sandy beach over which the pixels are considered in the size of the buffer around the sandy beach over which the pixels are considered in the
thresholding algorithm. thresholding algorithm.
is_reference_sl: boolean
True if there is a reference shoreline, False otherwise
Returns: ----------- Returns: -----------
contours_wi: list of np.arrays contours_wi: list of np.arrays
@ -339,6 +319,11 @@ def find_wl_contours2(im_ms, im_labels, cloud_mask, buffer_size):
im_wi_buffer[~im_buffer] = np.nan im_wi_buffer[~im_buffer] = np.nan
im_mwi_buffer = np.copy(im_mwi) im_mwi_buffer = np.copy(im_mwi)
im_mwi_buffer[~im_buffer] = np.nan im_mwi_buffer[~im_buffer] = np.nan
if is_reference_sl: # if there is a reference_shoreline map the shoreline on the entire image
contours_wi = measure.find_contours(im_wi, t_wi)
contours_mwi = measure.find_contours(im_mwi, t_mwi)
else: # otherwise only map the shoreline along the sandy pixels
contours_wi = measure.find_contours(im_wi_buffer, t_wi) contours_wi = measure.find_contours(im_wi_buffer, t_wi)
contours_mwi = measure.find_contours(im_mwi_buffer, t_mwi) contours_mwi = measure.find_contours(im_mwi_buffer, t_mwi)
@ -585,19 +570,23 @@ def extract_shorelines(metadata, settings):
metadata: dict metadata: dict
contains all the information about the satellite images that were downloaded contains all the information about the satellite images that were downloaded
inputs: dict settings: dict
contains the following fields: contains the following fields:
sitename: str sitename: str
String containig the name of the site String containig the name of the site
polygon: list cloud_mask_issue: boolean
polygon containing the lon/lat coordinates to be extracted True if there is an issue with the cloud mask and sand pixels are being masked on the images
longitudes in the first column and latitudes in the second column buffer_size: int
dates: list of str size of the buffer (m) around the sandy beach over which the pixels are considered in the
list that contains 2 strings with the initial and final dates in format thresholding algorithm
'yyyy-mm-dd' e.g. ['1987-01-01', '2018-01-01'] min_beach_area: int
sat_list: list of str minimum allowable object area (in metres^2) for the class 'sand'
list that contains the names of the satellite missions to include cloud_thresh: float
e.g. ['L5', 'L7', 'L8', 'S2'] value between 0 and 1 defining the maximum percentage of cloud cover allowed in the images
output_epsg: int
output spatial reference system as EPSG code
check_detection: boolean
True to show each invidual detection and let the user validate the mapped shoreline
Returns: Returns:
----------- -----------
@ -646,7 +635,7 @@ def extract_shorelines(metadata, settings):
# get image filename # get image filename
fn = SDS_tools.get_filenames(filenames[i],filepath, satname) fn = SDS_tools.get_filenames(filenames[i],filepath, satname)
# preprocess image (cloud mask + pansharpening/downsampling) # preprocess image (cloud mask + pansharpening/downsampling)
im_ms, georef, cloud_mask, im_extra, imQA = SDS_preprocess.preprocess_single(fn, satname) im_ms, georef, cloud_mask, im_extra, imQA = SDS_preprocess.preprocess_single(fn, satname, settings['cloud_mask_issue'])
# get image spatial reference system (epsg code) from metadata dict # get image spatial reference system (epsg code) from metadata dict
image_epsg = metadata[satname]['epsg'][i] image_epsg = metadata[satname]['epsg'][i]
# calculate cloud cover # calculate cloud cover
@ -671,8 +660,9 @@ def extract_shorelines(metadata, settings):
contours_mwi = find_wl_contours1(im_mndwi, cloud_mask) contours_mwi = find_wl_contours1(im_mndwi, cloud_mask)
else: else:
# use classification to refine threshold and extract sand/water interface # use classification to refine threshold and extract sand/water interface
is_reference_sl = 'reference_shoreline' in settings.keys()
contours_wi, contours_mwi = find_wl_contours2(im_ms, im_labels, contours_wi, contours_mwi = find_wl_contours2(im_ms, im_labels,
cloud_mask, buffer_size_pixels) cloud_mask, buffer_size_pixels, is_reference_sl)
except: except:
continue continue

@ -14,6 +14,8 @@ import skimage.transform as transform
from pylab import ginput from pylab import ginput
import pickle import pickle
import simplekml import simplekml
import json
from osgeo import ogr
def find_indices(lst, condition): def find_indices(lst, condition):
"imitation of MATLAB find function" "imitation of MATLAB find function"
@ -106,7 +108,11 @@ def draw_transects(output, settings):
origin = pts[0] origin = pts[0]
except: except:
fig1.gca().set_title('Transect locations', fontsize=16) fig1.gca().set_title('Transect locations', fontsize=16)
fig1.savefig(os.path.join(filepath, sitename + 'transects.jpg'), dpi=200) fig1.savefig(os.path.join(filepath, 'jpg_files', sitename + '_transect_locations.jpg'), dpi=200)
plt.title('Transects saved as ' + sitename + '_transects.pkl and ' + sitename + '_transects.kml ')
plt.draw()
ginput(n=1, timeout=5, show_clicks=True)
plt.close(fig1)
break break
counter = counter + 1 counter = counter + 1
# create the transect using the origin, orientation and length # create the transect using the origin, orientation and length
@ -134,6 +140,50 @@ def draw_transects(output, settings):
newline.coords = transects[key] newline.coords = transects[key]
newline.description = 'user-defined cross-shore transect' newline.description = 'user-defined cross-shore transect'
kml.save(os.path.join(filepath, sitename + '_transects.kml')) kml.save(os.path.join(filepath, sitename + '_transects.kml'))
print('Transect locations saved in ' + filepath)
return transects
def load_transects_from_kml(filename):
"""
Reads transect coordinates from a KML file.
Arguments:
-----------
filename: str
contains the path and filename of the KML file to be loaded
Returns:
-----------
transects: dict
contains the X and Y coordinates of each transect.
"""
# set driver
drv = ogr.GetDriverByName('KML')
# read file
file = drv.Open(filename)
layer = file.GetLayer()
feature = layer.GetNextFeature()
# initialise transects dictionnary
transects = dict([])
while feature:
f_dict = json.loads(feature.ExportToJson())
# raise an exception if the KML file contains other features that LineString geometries
if not f_dict['geometry']['type'] == 'LineString':
raise Exception('The KML file you provided does not contain LineString geometries. Modify your KML file and try again.')
# store the name of the feature and coordinates in the transects dictionnary
else:
name = f_dict['properties']['Name']
coords = np.array(f_dict['geometry']['coordinates'])[:,:-1]
transects[name] = coords
feature = layer.GetNextFeature()
print('%d transects have been loaded' % len(transects.keys()))
return transects return transects
@ -146,6 +196,9 @@ def compute_intersection(output, transects, settings):
----------- -----------
output: dict output: dict
contains the extracted shorelines and corresponding dates. contains the extracted shorelines and corresponding dates.
transects: dict
contains the X and Y coordinates of the transects (first and last point needed for each
transect).
settings: dict settings: dict
contains parameters defining : contains parameters defining :
along_dist: alongshore distance to caluclate the intersection (median of points along_dist: alongshore distance to caluclate the intersection (median of points

Binary file not shown.

@ -24,6 +24,7 @@
"outputs": [], "outputs": [],
"source": [ "source": [
"import os\n", "import os\n",
"import numpy as np\n",
"import pickle\n", "import pickle\n",
"import warnings\n", "import warnings\n",
"warnings.filterwarnings(\"ignore\")\n", "warnings.filterwarnings(\"ignore\")\n",
@ -125,6 +126,7 @@
" 'min_beach_area': 4500, # minimum area (in metres^2) for an object to be labelled as a beach\n", " 'min_beach_area': 4500, # minimum area (in metres^2) for an object to be labelled as a beach\n",
" 'buffer_size': 150, # radius (in metres) of the buffer around sandy pixels considered in the shoreline detection\n", " 'buffer_size': 150, # radius (in metres) of the buffer around sandy pixels considered in the shoreline detection\n",
" 'min_length_sl': 200, # minimum length (in metres) of shoreline perimeter to be valid\n", " 'min_length_sl': 200, # minimum length (in metres) of shoreline perimeter to be valid\n",
" 'cloud_mask_issue': False, # switch this parameter to True if sand pixels are masked (in black) on many images \n",
"}" "}"
] ]
}, },
@ -218,7 +220,7 @@
"source": [ "source": [
"## 4. Shoreline analysis\n", "## 4. Shoreline analysis\n",
"\n", "\n",
"Shows how to plot the mapped shorelines and draw shore-normal transects and compute time-series of cross-shore distance along the transects." "In this section we show how to compute time-series of cross-shore distance along user-defined shore-normal transects."
] ]
}, },
{ {
@ -243,7 +245,25 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Create shore-normal transects along the beach" "The shore-normal transects are defined by two points, the origin of the transect and a second point that defines its orientaion. The parameter *transect_length* determines how far from the origin the transects span."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"settings['transect_length'] = 500 # defines the length of the transects in metres"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are 3 options to define the coordinates of the shore-normal transects:\n",
"\n",
"**Option 1**: the user can interactively draw the shore-normal transects along the beach by calling:"
] ]
}, },
{ {
@ -253,7 +273,6 @@
"outputs": [], "outputs": [],
"source": [ "source": [
"%matplotlib qt\n", "%matplotlib qt\n",
"settings['transect_length'] = 500 # defines the length of the transects in metres\n",
"transects = SDS_transects.draw_transects(output, settings)" "transects = SDS_transects.draw_transects(output, settings)"
] ]
}, },
@ -261,7 +280,43 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"Intersect the transects with the 2D shorelines to obtain time-series of cross-shore distance" "**Option 2**: the user can load the transect coordinates (make sure the spatial reference system is the same as defined by *output_epsg* previously) from a .kml file by calling:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"kml_file = 'NARRA_transects.kml'\n",
"transects = SDS_transects.load_transects_from_kml(kml_file)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Option 3**: manually provide the coordinates of the transects as shown in the example below:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"transects = dict([])\n",
"transects['Transect 1'] = np.array([[342836, 6269215], [343315, 6269071]])\n",
"transects['Transect 2'] = np.array([[342482, 6268466], [342958, 6268310]])\n",
"transects['Transect 3'] = np.array([[342185, 6267650], [342685, 6267641]])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, intersect the transects with the 2D shorelines to obtain time-series of cross-shore distance"
] ]
}, },
{ {

@ -8,14 +8,15 @@
# load modules # load modules
import os import os
import numpy as np
import pickle import pickle
import warnings import warnings
warnings.filterwarnings("ignore") warnings.filterwarnings("ignore")
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import SDS_download, SDS_preprocess, SDS_shoreline, SDS_tools, SDS_transects import SDS_download, SDS_preprocess, SDS_shoreline, SDS_tools, SDS_transects
# region of interest (longitude, latitude), can also be loaded from a .kml polygon # region of interest (longitude, latitude in WGS84), can be loaded from a .kml polygon
polygon = SDS_tools.coords_from_kml('NARRA.kml') polygon = SDS_tools.coords_from_kml('NARRA_polygon.kml')
#polygon = [[[151.301454, -33.700754], #polygon = [[[151.301454, -33.700754],
# [151.311453, -33.702075], # [151.311453, -33.702075],
# [151.307237, -33.739761], # [151.307237, -33.739761],
@ -23,7 +24,7 @@ polygon = SDS_tools.coords_from_kml('NARRA.kml')
# [151.301454, -33.700754]]] # [151.301454, -33.700754]]]
# date range # date range
dates = ['2017-12-01', '2018-06-01'] dates = ['2017-12-01', '2018-02-01']
# satellite missions # satellite missions
sat_list = ['L8','S2'] sat_list = ['L8','S2']
@ -42,12 +43,12 @@ inputs = {
#%% 2. Retrieve images #%% 2. Retrieve images
# retrieve satellite images from GEE # retrieve satellite images from GEE
#metadata = SDS_download.retrieve_images(inputs) metadata = SDS_download.retrieve_images(inputs)
# if you have already downloaded the images, just load the metadata file # if you have already downloaded the images, just load the metadata file
filepath = os.path.join(os.getcwd(), 'data', sitename) #filepath = os.path.join(os.getcwd(), 'data', sitename)
with open(os.path.join(filepath, sitename + '_metadata' + '.pkl'), 'rb') as f: #with open(os.path.join(filepath, sitename + '_metadata' + '.pkl'), 'rb') as f:
metadata = pickle.load(f) # metadata = pickle.load(f)
#%% 3. Batch shoreline detection #%% 3. Batch shoreline detection
@ -66,10 +67,11 @@ settings = {
'min_beach_area': 4500, # minimum area (in metres^2) for an object to be labelled as a beach 'min_beach_area': 4500, # minimum area (in metres^2) for an object to be labelled as a beach
'buffer_size': 150, # radius (in metres) of the buffer around sandy pixels considered in the shoreline detection 'buffer_size': 150, # radius (in metres) of the buffer around sandy pixels considered in the shoreline detection
'min_length_sl': 200, # minimum length (in metres) of shoreline perimeter to be valid 'min_length_sl': 200, # minimum length (in metres) of shoreline perimeter to be valid
'cloud_mask_issue': False, # switch this parameter to True if sand pixels are masked (in black) on many images
} }
# [OPTIONAL] preprocess images (cloud masking, pansharpening/down-sampling) # [OPTIONAL] preprocess images (cloud masking, pansharpening/down-sampling)
#SDS_preprocess.save_jpg(metadata, settings) SDS_preprocess.save_jpg(metadata, settings)
# [OPTIONAL] create a reference shoreline (helps to identify outliers and false detections) # [OPTIONAL] create a reference shoreline (helps to identify outliers and false detections)
settings['reference_shoreline'] = SDS_preprocess.get_reference_sl_manual(metadata, settings) settings['reference_shoreline'] = SDS_preprocess.get_reference_sl_manual(metadata, settings)
@ -96,22 +98,40 @@ fig.set_size_inches([15.76, 8.52])
#%% 4. Shoreline analysis #%% 4. Shoreline analysis
# if you have already mapped the shorelines, just load them # if you have already mapped the shorelines, load the output.pkl file
filepath = os.path.join(os.getcwd(), 'data', sitename) filepath = os.path.join(os.getcwd(), 'data', sitename)
with open(os.path.join(filepath, sitename + '_output' + '.pkl'), 'rb') as f: with open(os.path.join(filepath, sitename + '_output' + '.pkl'), 'rb') as f:
output = pickle.load(f) output = pickle.load(f)
# create shore-normal transects along the beach # now we have to define cross-shore transects over which to quantify the shoreline changes
# each transect is defined by two points, its origin and a second point that defines its orientation
# the parameter transect length determines how far from the origin the transect will span
settings['transect_length'] = 500 settings['transect_length'] = 500
# there are 3 options to create the transects:
# - option 1: draw the shore-normal transects along the beach
# - option 2: load the transect coordinates from a .kml file
# - option 3: create the transects manually by providing the coordinates
# option 1: draw origin of transect first and then a second point to define the orientation
transects = SDS_transects.draw_transects(output, settings) transects = SDS_transects.draw_transects(output, settings)
# option 2: load the transects from a KML file
#kml_file = 'NARRA_transects.kml'
#transects = SDS_transects.load_transects_from_kml(kml_file)
# option 3: create the transects by manually providing the coordinates of two points
#transects = dict([])
#transects['Transect 1'] = np.array([[342836, 6269215], [343315, 6269071]])
#transects['Transect 2'] = np.array([[342482, 6268466], [342958, 6268310]])
#transects['Transect 3'] = np.array([[342185, 6267650], [342685, 6267641]])
# intersect the transects with the 2D shorelines to obtain time-series of cross-shore distance # intersect the transects with the 2D shorelines to obtain time-series of cross-shore distance
settings['along_dist'] = 25 settings['along_dist'] = 25
cross_distance = SDS_transects.compute_intersection(output, transects, settings) cross_distance = SDS_transects.compute_intersection(output, transects, settings)
# plot the time-series # plot the time-series
from matplotlib import gridspec from matplotlib import gridspec
import numpy as np
fig = plt.figure() fig = plt.figure()
gs = gridspec.GridSpec(len(cross_distance),1) gs = gridspec.GridSpec(len(cross_distance),1)
gs.update(left=0.05, right=0.95, bottom=0.05, top=0.95, hspace=0.05) gs.update(left=0.05, right=0.95, bottom=0.05, top=0.95, hspace=0.05)

@ -2,166 +2,214 @@
# $ conda create --name <env> --file <this file> # $ conda create --name <env> --file <this file>
# platform: linux-64 # platform: linux-64
@EXPLICIT @EXPLICIT
https://conda.anaconda.org/conda-forge/linux-64/libgfortran-3.0.0-1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/blas-1.0-mkl.tar.bz2 https://repo.anaconda.com/pkgs/main/linux-64/blas-1.0-mkl.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/ca-certificates-2018.03.07-0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2018.11.29-ha4d7672_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/intel-openmp-2019.1-144.tar.bz2 https://repo.anaconda.com/pkgs/main/linux-64/intel-openmp-2019.1-144.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libgcc-ng-8.2.0-hdf63c60_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-7.3.0-hdf63c60_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/libgfortran-3.0.0-1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-7.2.0-hdf63c60_3.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libgfortran-ng-7.3.0-hdf63c60_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.8.0-1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libstdcxx-ng-8.2.0-hdf63c60_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-7.3.0-hdf63c60_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/poppler-data-0.4.9-0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/pandoc-2.6-1.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.6-h470a237_2.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.9-1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/expat-2.2.6-he6710b0_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.6-h14c3975_1002.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/freexl-1.0.5-h470a237_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/expat-2.2.5-hf484d3e_1002.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/geos-3.6.2-heeff764_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/freexl-1.0.5-h14c3975_1002.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/giflib-5.1.4-h470a237_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/geos-3.7.1-hf484d3e_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/gmp-6.1.2-h6c8ec71_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/giflib-5.1.4-h14c3975_1001.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/icu-58.2-h9c2bf20_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-hf484d3e_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/jpeg-9c-h470a237_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/icu-58.2-hf484d3e_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/json-c-0.13.1-h1bed415_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/jpeg-9c-h14c3975_1001.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libffi-3.2.1-hd88cf55_4.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/json-c-0.13.1-h14c3975_1001.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libgcc-7.2.0-h69d50b8_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.2.1-hf484d3e_1005.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libsodium-1.0.16-h1bed415_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.15-h14c3975_1004.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.32.1-h470a237_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.16-h14c3975_1001.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libxcb-1.13-h1bed415_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.32.1-h14c3975_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/mkl-2018.0.3-1.tar.bz2 https://repo.anaconda.com/pkgs/main/linux-64/mkl-2019.1-144.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/ncurses-6.1-he6710b0_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.1-hf484d3e_1002.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/openssl-1.0.2p-h14c3975_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.3-h9ac9557_1001.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/openssl-1.0.2p-h14c3975_1002.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/pcre-8.42-h439df22_0.tar.bz2 https://repo.anaconda.com/pkgs/main/linux-64/pcre-8.42-h439df22_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/pixman-0.34.0-h470a237_3.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/pixman-0.34.0-h14c3975_1003.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/proj4-4.9.3-hc8507d1_7.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/proj4-5.2.0-h14c3975_1001.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h470a237_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h14c3975_1001.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.0.9-h470a237_4.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/tzcode-2018g-h14c3975_1001.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/xorg-renderproto-0.11.1-h470a237_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h14c3975_1002.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/xorg-xextproto-7.3.0-h470a237_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.0.9-h14c3975_1004.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/xorg-xproto-7.0.31-h470a237_7.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.9-h14c3975_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/xz-5.2.4-h14c3975_4.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.2-h14c3975_1007.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/zlib-1.2.11-h7b6447c_3.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/xorg-renderproto-0.11.1-h14c3975_1002.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/boost-cpp-1.68.0-h3a22d5f_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/xorg-xextproto-7.3.0-h14c3975_1002.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/glib-2.56.2-hd408876_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/xorg-xproto-7.0.31-h14c3975_1007.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.13-h951d187_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.4-h14c3975_1001.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/hdf5-1.10.1-h9caa474_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.11-h14c3975_1004.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libedit-3.1.20170329-h6b74fdf_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/boost-cpp-1.68.0-h11c811c_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libpng-1.6.35-hbc83047_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/gettext-0.19.8.1-h9745a5d_1001.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.8.0-h5b517e9_3.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.13-h9a582f1_1002.tar.bz2
https://conda.anaconda.org/anaconda/linux-64/libtiff-4.0.9-he85c1e1_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.10.4-nompi_h11e915b_1105.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libxml2-2.9.8-h26e45fe_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20170329-hf8c457e_1001.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/pandoc-2.2.3.2-0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.36-h84994c4_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/readline-7.0-h7b6447c_5.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.0.10-h648cc4a_1001.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/tk-8.6.8-hbc83047_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.13-h14c3975_1002.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.2-h5d6a6da_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.8-h143f9aa_1005.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.3-h8c8a85c_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/readline-7.0-hf8c457e_1001.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.6.6-h470a237_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.9-h84994c4_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/zeromq-4.2.5-hf484d3e_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.2.2-hac72e42_1001.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.3-h4937e3b_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.2.5-hf484d3e_1006.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/freetype-2.9.1-h94bbf69_1005.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.4.3-h1105359_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/glib-2.56.2-had28632_1001.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/kealib-1.4.10-he7154bc_1002.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/krb5-1.16.3-hc83ff2d_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-h328b03d_1009.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.3.0-hf38bd82_1003.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.26.0-h67949de_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.6.7-h14c3975_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/dbus-1.13.2-h714fa37_1.tar.bz2 https://repo.anaconda.com/pkgs/main/linux-64/dbus-1.13.2-h714fa37_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/freetype-2.8-hab7d2ae_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.13.1-h2176d3f_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/geotiff-1.4.2-h8e81d37_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/gstreamer-1.14.0-hb453b48_1.tar.bz2 https://repo.anaconda.com/pkgs/main/linux-64/gstreamer-1.14.0-hb453b48_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/kealib-1.4.7-h79811e5_5.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.64.0-h01ee5af_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/krb5-1.16.2-hbb41f41_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libpq-10.6-h13b8bad_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-he469717_9.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libspatialite-4.3.0a-hb5ec416_1026.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.3.0-h0e734dc_3.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/python-3.6.7-hd21baee_1001.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.26.0-hb1c47c0_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.3-h14c3975_1004.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.3-h470a237_4.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.10-h14c3975_1002.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.10-h470a237_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxt-1.1.5-h14c3975_1002.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/fontconfig-2.12.6-h49f89f6_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/alabaster-0.7.12-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/gst-plugins-base-1.14.0-hbbd80ab_1.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.62.0-hbdb9355_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/libpq-10.5-he29860b_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libspatialite-4.3.0a-h72746d6_18.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/python-3.6.6-h5001a0f_3.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/asn1crypto-0.24.0-py36_1003.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/asn1crypto-0.24.0-py36_1003.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/backcall-0.1.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/attrs-18.2.0-py_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/backcall-0.1.0-py_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/cachetools-2.1.0-py_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/cachetools-2.1.0-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/cairo-1.14.12-h7636065_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/cairo-1.14.12-h80bd089_1005.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/certifi-2018.10.15-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/certifi-2018.11.29-py36_1000.tar.bz2
https://conda.anaconda.org/anaconda/linux-64/cloudpickle-0.6.1-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/chardet-3.0.4-py36_1003.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/cryptography-vectors-2.3.1-py36_1000.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/cloudpickle-0.7.0-py_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/curl-7.62.0-h74213dd_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/curl-7.64.0-h646f8bb_0.tar.bz2
https://conda.anaconda.org/anaconda/linux-64/dask-core-1.0.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/dask-core-1.1.1-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/decorator-4.3.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/decorator-4.3.2-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/entrypoints-0.2.3-py36_2.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/docutils-0.14-py36_1001.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/entrypoints-0.3-py36_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/gst-plugins-base-1.14.0-hbbd80ab_1.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/httplib2-0.12.0-py36_1000.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/httplib2-0.12.0-py36_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/idna-2.8-py36_1000.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/idna-2.8-py36_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/ipython_genutils-0.2.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/imagesize-1.1.0-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/kiwisolver-1.0.1-py36hf484d3e_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/ipython_genutils-0.2.0-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/markupsafe-1.1.0-py36h7b6447c_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/jeepney-0.4-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/mistune-0.8.4-py36h7b6447c_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.0.1-py36h6bb024c_1002.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/numpy-base-1.15.4-py36h81de0dd_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/lazy-object-proxy-1.3.1-py36h14c3975_1000.tar.bz2
https://conda.anaconda.org/anaconda/linux-64/olefile-0.46-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/markupsafe-1.1.0-py36h14c3975_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/pandocfilters-1.4.2-py36_1.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/mccabe-0.6.1-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/parso-0.3.1-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/mistune-0.8.4-py36h14c3975_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/pickleshare-0.7.5-py36_0.tar.bz2 https://repo.anaconda.com/pkgs/main/linux-64/numpy-base-1.15.4-py36hde5b4d6_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/postgresql-10.5-h66035e0_1.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/olefile-0.46-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/prometheus_client-0.4.2-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.4.2-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/ptyprocess-0.6.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/parso-0.3.3-py_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/pickleshare-0.7.5-py36_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/postgresql-10.6-h66cca7a_1000.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.5.0-py_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/psutil-5.5.0-py36h14c3975_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/ptyprocess-0.6.0-py36_1000.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/pyasn1-0.4.4-py_1.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/pyasn1-0.4.4-py_1.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/pycodestyle-2.5.0-py_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/pycparser-2.19-py_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.19-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/pyparsing-2.3.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/pyflakes-2.1.0-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/pytz-2018.7-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/pyparsing-2.3.1-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/pyzmq-17.1.2-py36h14c3975_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.6.8-py36_1002.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/qt-5.9.5-h7e424d6_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/pytz-2018.9-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/send2trash-1.5.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/pyzmq-17.1.2-py36h6afc9c9_1001.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/qtpy-1.6.0-pyh8a2030e_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/rope-0.10.7-py_1.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/send2trash-1.5.0-py_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/simplejson-3.16.1-py36h470a237_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/simplejson-3.16.1-py36h470a237_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/simplekml-1.3.0-py_1.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/simplekml-1.3.0-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/sip-4.19.8-py36hf484d3e_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/sip-4.18.1-py36hf484d3e_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/six-1.11.0-py36_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/six-1.12.0-py36_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/testpath-0.4.2-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-1.2.1-py_1.tar.bz2
https://conda.anaconda.org/anaconda/linux-64/toolz-0.9.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-websupport-1.1.0-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/tornado-5.1.1-py36h7b6447c_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/testpath-0.4.2-py36_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/wcwidth-0.1.7-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/toolz-0.9.0-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/webencodings-0.5.1-py36_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/tornado-5.1.1-py36h14c3975_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/cffi-1.11.5-py36h5e8e0c9_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/typed-ast-1.3.1-py36h14c3975_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/cycler-0.10.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.1.7-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/jedi-0.13.1-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-py_1.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/libdap4-3.19.1-h8fe5423_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/wrapt-1.11.1-py36h14c3975_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libnetcdf-4.4.1.1-h816af47_8.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/wurlitzer-1.0.2-py36_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/pexpect-4.6.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/babel-2.6.0-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/pillow-5.1.0-py36h3deb7b8_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/cffi-1.11.5-py36h9745a5d_1001.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/poppler-0.65.0-ha54bb34_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/cycler-0.10.0-py_1.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/pyasn1-modules-0.2.1-py_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.9.0.1-py36h14c3975_1001.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/pyqt-5.9.2-py36h751905a_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/gobject-introspection-1.56.1-py36h9e29830_1001.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/python-dateutil-2.7.5-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-1.9.0-he243708_1001.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/jedi-0.13.2-py36_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/libdap4-3.19.1-hd48c02d_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.6.2-hbdf4f91_1001.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/packaging-19.0-py_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/pexpect-4.6.0-py36_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/pillow-5.4.1-py36h00a061d_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/poppler-0.67.0-h2fc8fa2_1002.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/pyasn1-modules-0.2.3-py_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/pyrsistent-0.14.10-py36h14c3975_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.0-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/qt-5.6.3-h8bf5577_3.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/qtawesome-0.5.6-pyh8a2030e_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/rsa-3.4.2-py_1.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/rsa-3.4.2-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/setuptools-40.6.2-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/setuptools-40.8.0-py36_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/terminado-0.8.1-py36_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/terminado-0.8.1-py36_1001.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/traitlets-4.3.2-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/traitlets-4.3.2-py36_1000.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/uritemplate-3.0.0-py_1.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/uritemplate-3.0.0-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/bleach-3.0.2-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/astroid-2.1.0-py36_1000.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/cryptography-2.3.1-py36hdffb7b8_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/atk-2.25.90-hf2eb9ee_1001.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/google-auth-1.6.1-py_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/bleach-3.1.0-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/jinja2-2.10-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/cryptography-2.5-py36hb7f436b_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/jsonschema-2.6.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.36.12-h4f1c04b_1001.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/jupyter_core-4.4.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/google-auth-1.6.2-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/libgdal-2.2.4-hc8d23f9_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/isort-4.3.4-py36_1000.tar.bz2
https://conda.anaconda.org/anaconda/linux-64/networkx-2.2-py36_1.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/jinja2-2.10-py_1.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/oauth2client-4.1.2-py_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/jsonschema-3.0.0a3-py36_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/pygments-2.2.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/jupyter_core-4.4.0-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/wheel-0.32.3-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libgdal-2.4.0-h982c1cc_1002.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/networkx-2.2-py_1.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/oauth2client-4.1.3-py_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/pango-1.40.14-hf0c64fd_1003.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/pygments-2.3.1-py_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.6.0-py36h13b7fb3_1008.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/wheel-0.32.3-py36_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/google-auth-httplib2-0.0.3-py_2.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/google-auth-httplib2-0.0.3-py_2.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/jupyter_client-5.2.3-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.31-h5baeb44_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/nbformat-4.4.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/jupyter_client-5.2.4-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/pip-18.1-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/nbformat-4.4.0-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/prompt_toolkit-2.0.7-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/pip-19.0.2-py36_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/pyopenssl-18.0.0-py36_1000.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/prompt_toolkit-2.0.8-py_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/google-api-python-client-1.7.5-py_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/pylint-2.2.2-py36_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/ipython-7.2.0-py36h39e3cac_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/pyopenssl-19.0.0-py36_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/nbconvert-5.3.1-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/secretstorage-3.1.1-py36_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/earthengine-api-0.1.152-py_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/google-api-python-client-1.7.8-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/ipykernel-5.1.0-py36h39e3cac_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/ipython-7.2.0-py36h24bf2e0_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/jupyter_console-6.0.0-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/keyring-17.1.1-py36_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/notebook-5.7.2-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/nbconvert-5.3.1-py_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/qtconsole-4.4.2-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/urllib3-1.24.1-py36_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/widgetsnbextension-3.4.2-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/earthengine-api-0.1.167-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/ipywidgets-7.4.2-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/ipykernel-5.1.0-py36h24bf2e0_1002.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/jupyter-1.0.0-py36_7.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/requests-2.21.0-py36_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/gdal-2.2.4-py36h637b7d7_1.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/jupyter_console-6.0.0-py_0.tar.bz2
https://conda.anaconda.org/anaconda/linux-64/imageio-2.4.1-py36_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/notebook-5.7.4-py36_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/matplotlib-2.2.2-py36h0e671d2_1.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/qtconsole-4.4.3-py_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/mkl_fft-1.0.6-py36h7dd41cf_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/sphinx-1.8.4-py36_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/mkl_random-1.0.1-py36h4414c95_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/spyder-kernels-0.4.2-py36_0.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/numpy-1.15.4-py36h1d66e8a_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/numpydoc-0.8.0-py_1.tar.bz2
https://conda.anaconda.org/anaconda/linux-64/pywavelets-1.0.1-py36hdd07704_0.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/widgetsnbextension-3.4.2-py36_1000.tar.bz2
https://conda.anaconda.org/anaconda/linux-64/scipy-1.1.0-py36hfa4b5c9_1.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/ipywidgets-7.4.2-py_0.tar.bz2
https://conda.anaconda.org/anaconda/linux-64/scikit-image-0.14.0-py36hf484d3e_1.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/spyder-3.3.3-py36_0.tar.bz2
https://conda.anaconda.org/anaconda/linux-64/scikit-learn-0.20.1-py36h4989274_0.tar.bz2 https://conda.anaconda.org/conda-forge/noarch/jupyter-1.0.0-py_1.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/gdal-2.4.0-py36h1c6dbfb_1002.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/imageio-2.5.0-py36_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.0.2-py36_1002.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.0.2-py36h167e16e_1002.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/mkl_fft-1.0.10-py36h14c3975_1.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/mkl_random-1.0.2-py36h637b7d7_2.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/numpy-1.15.4-py36h7e9f1db_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.0.1-py36h3010b51_1000.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/scipy-1.2.0-py36h7c811a0_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/scikit-image-0.14.2-py36hf484d3e_1.tar.bz2
https://repo.anaconda.com/pkgs/main/linux-64/scikit-learn-0.20.2-py36hd81dba3_0.tar.bz2

Loading…
Cancel
Save