Skip to content

Commit 14c4ed3

Browse files
author
Scisco
committed
Merge pull request #85 from developmentseed/features
resolves #81 #84
2 parents 9095a1c + 7d028dd commit 14c4ed3

File tree

5 files changed

+79
-42
lines changed

5 files changed

+79
-42
lines changed

landsat/downloader.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,12 @@ def download(self, scenes, bands=None):
4545
List
4646
4747
:returns:
48-
Boolean
48+
(List) includes downloaded scenes as key and source as value (aws or google)
4949
"""
5050

5151
if isinstance(scenes, list):
52+
output = {}
53+
5254
for scene in scenes:
5355
# If bands are provided the image is from 2015 or later use Amazon
5456
if (bands and int(scene[12]) > 4):
@@ -61,14 +63,18 @@ def download(self, scenes, bands=None):
6163
bands_plus.append('MTL')
6264
for band in bands_plus:
6365
self.amazon_s3(scene, band, path)
66+
output[scene] = 'aws'
6467
except RemoteFileDoesntExist:
6568
self.google_storage(scene, self.download_dir)
69+
output[scene] = 'google'
70+
6671
else:
6772
raise Exception('Expected bands list')
6873
else:
6974
self.google_storage(scene, self.download_dir)
75+
output[scene] = 'google'
7076

71-
return True
77+
return output
7278

7379
else:
7480
raise Exception('Expected sceneIDs list')

landsat/image.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import warnings
66
import sys
7-
from os.path import join
7+
from os.path import join, isdir
88
import tarfile
99
import glob
1010
import subprocess
@@ -49,10 +49,14 @@ class Process(VerbosityMixin):
4949
Whether the output should be verbose. Default is False.
5050
:type verbose:
5151
boolean
52+
:param force_unzip:
53+
Whether to force unzip the tar file. Default is False
54+
:type force_unzip:
55+
boolean
5256
5357
"""
5458

55-
def __init__(self, path, bands=None, dst_path=None, verbose=False):
59+
def __init__(self, path, bands=None, dst_path=None, verbose=False, force_unzip=False):
5660

5761
self.projection = {'init': 'epsg:3857'}
5862
self.dst_crs = {'init': u'epsg:3857'}
@@ -71,7 +75,7 @@ def __init__(self, path, bands=None, dst_path=None, verbose=False):
7175
self.scene_path = join(self.src_path, self.scene)
7276

7377
if self._check_if_zipped(path):
74-
self._unzip(join(self.src_path, get_file(path)), join(self.src_path, self.scene), self.scene)
78+
self._unzip(join(self.src_path, get_file(path)), join(self.src_path, self.scene), self.scene, force_unzip)
7579

7680
self.bands_path = []
7781
for band in self.bands:
@@ -287,14 +291,19 @@ def _get_boundaries(self, src):
287291
def _percent_cut(self, color, low, high):
288292
return numpy.percentile(color[numpy.logical_and(color > 0, color < 65535)], (low, high))
289293

290-
def _unzip(self, src, dst, scene):
294+
def _unzip(self, src, dst, scene, force_unzip=False):
291295
""" Unzip tar files """
292296
self.output("Unzipping %s - It might take some time" % scene, normal=True, arrow=True)
293297

294298
try:
295-
tar = tarfile.open(src, 'r')
296-
tar.extractall(path=dst)
297-
tar.close()
299+
# check if file is already unzipped, skip
300+
if isdir(dst) and not force_unzip:
301+
self.output("%s is already unzipped." % scene, normal=True, arrow=True)
302+
return
303+
else:
304+
tar = tarfile.open(src, 'r')
305+
tar.extractall(path=dst)
306+
tar.close()
298307
except tarfile.ReadError:
299308
check_create_folder(dst)
300309
subprocess.check_call(['tar', '-xf', src, '-C', dst])

landsat/landsat.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@
8686
8787
--region URL to S3 region e.g. s3-us-west-2.amazonaws.com
8888
89+
--force-unzip Force unzip tar file
90+
8991
Process:
9092
landsat.py process path [-h] [-b --bands] [-p --pansharpen]
9193
@@ -115,6 +117,8 @@
115117
--bucket Bucket name (required if uploading to s3)
116118
117119
--region URL to S3 region e.g. s3-us-west-2.amazonaws.com
120+
121+
--force-unzip Force unzip tar file
118122
"""
119123

120124

@@ -179,6 +183,7 @@ def args_options():
179183
'as Environment Variables)')
180184
parser_download.add_argument('--bucket', help='Bucket name (required if uploading to s3)')
181185
parser_download.add_argument('--region', help='URL to S3 region e.g. s3-us-west-2.amazonaws.com')
186+
parser_download.add_argument('--force-unzip', help='Force unzip tar file', action='store_true')
182187

183188
parser_process = subparsers.add_parser('process', help='Process Landsat imagery')
184189
parser_process.add_argument('path',
@@ -198,6 +203,7 @@ def args_options():
198203
'as Environment Variables)')
199204
parser_process.add_argument('--bucket', help='Bucket name (required if uploading to s3)')
200205
parser_process.add_argument('--region', help='URL to S3 region e.g. s3-us-west-2.amazonaws.com')
206+
parser_process.add_argument('--force-unzip', help='Force unzip tar file', action='store_true')
201207

202208
return parser
203209

@@ -221,9 +227,11 @@ def main(args):
221227
v = VerbosityMixin()
222228

223229
if args:
230+
224231
if args.subs == 'process':
225232
verbose = True if args.verbose else False
226-
stored = process_image(args.path, args.bands, verbose, args.pansharpen)
233+
force_unzip = True if args.force_unzip else False
234+
stored = process_image(args.path, args.bands, verbose, args.pansharpen, force_unzip)
227235

228236
if args.upload:
229237
u = Uploader(args.key, args.secret, args.region)
@@ -269,18 +277,21 @@ def main(args):
269277
elif args.subs == 'download':
270278
d = Downloader(download_dir=args.dest)
271279
try:
272-
if d.download(args.scenes, convert_to_integer_list(args.bands)):
273-
if args.process:
280+
downloaded = d.download(args.scenes, convert_to_integer_list(args.bands))
281+
282+
if args.process:
283+
force_unzip = True if args.force_unzip else False
284+
for scene, src in downloaded.iteritems():
274285
if args.dest:
275-
path = join(args.dest, args.scenes[0])
286+
path = join(args.dest, scene)
276287
else:
277-
path = join(settings.DOWNLOAD_DIR, args.scenes[0])
288+
path = join(settings.DOWNLOAD_DIR, scene)
278289

279290
# Keep using Google if the image is before 2015
280-
if (int(args.scenes[0][12]) < 5 or not args.bands):
291+
if src == 'google':
281292
path = path + '.tar.bz'
282293

283-
stored = process_image(path, args.bands, False, args.pansharpen)
294+
stored = process_image(path, args.bands, False, args.pansharpen, force_unzip)
284295

285296
if args.upload:
286297
try:
@@ -291,14 +302,16 @@ def main(args):
291302
return ["Connection timeout. Probably the region parameter is incorrect", 1]
292303
u.run(args.bucket, get_file(stored), stored)
293304

294-
return ["The output is stored at %s" % stored]
295-
else:
296-
return ['Download Completed', 0]
305+
v.output("The output is stored at %s" % stored, normal=True, arrow=True)
306+
307+
return ['Image Processing Completed', 0]
308+
else:
309+
return ['Download Completed', 0]
297310
except IncorrectSceneId:
298311
return ['The SceneID provided was incorrect', 1]
299312

300313

301-
def process_image(path, bands=None, verbose=False, pansharpen=False):
314+
def process_image(path, bands=None, verbose=False, pansharpen=False, force_unzip=None):
302315
""" Handles constructing and image process.
303316
304317
:param path:
@@ -323,7 +336,7 @@ def process_image(path, bands=None, verbose=False, pansharpen=False):
323336
"""
324337
try:
325338
bands = convert_to_integer_list(bands)
326-
p = Process(path, bands=bands, verbose=verbose)
339+
p = Process(path, bands=bands, verbose=verbose, force_unzip=force_unzip)
327340
except IOError:
328341
exit("Zip file corrupted", 1)
329342
except FileDoesNotExist as e:

landsat/tests/test_download.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ def setUpClass(cls):
2828
cls.d = Downloader()
2929
cls.temp_folder = mkdtemp()
3030
cls.scene = 'LT81360082013127LGN01'
31+
cls.scene_2 = 'LC82050312014229LGN00'
3132
cls.scene_s3 = 'LC80010092015051LGN00'
33+
cls.scene_s3_2 = 'LC82050312015136LGN00'
3234
cls.scene_size = 59204484
3335

3436
@classmethod
@@ -49,21 +51,19 @@ def assertSize(self, url, path):
4951
def test_download(self, mock_fetch):
5052
mock_fetch.return_value = True
5153

52-
# download one list
54+
# download one scene
5355
self.d.download([self.scene])
54-
self.assertTrue(self.d.download([self.scene]))
56+
self.assertEqual({self.scene: 'google'}, self.d.download([self.scene]))
57+
58+
# download multiple scenes
59+
self.assertEqual({self.scene: 'google', self.scene_2: 'google'}, self.d.download([self.scene, self.scene_2]))
5560

5661
# Test if error is raised when passing scene as string instead of list
5762
self.assertRaises(Exception, self.d.download, self.scene)
5863

59-
# Test if download works when passing scenes as list
60-
self.d.download([self.scene, self.scene])
61-
self.assertTrue(self.d.download([self.scene]))
62-
6364
# Test when passing band list along with sceneID
64-
self.d.download([self.scene_s3], bands=[11])
65-
66-
self.assertTrue(self.d.download([self.scene]))
65+
self.assertEqual({self.scene_s3: 'aws', self.scene_s3_2: 'aws'},
66+
self.d.download([self.scene_s3, self.scene_s3_2], bands=[11]))
6767

6868
# Test whether passing band as string raises an exception
6969
self.assertRaises(Exception, self.d.download, self.scene, 4)

landsat/tests/test_landsat.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -94,45 +94,54 @@ def test_download_incorrect(self):
9494
@mock.patch('landsat.landsat.Downloader.download')
9595
def test_download_process_continuous(self, mock_downloader, mock_process):
9696
"""Test download and process commands together"""
97-
mock_downloader.return_value = True
97+
mock_downloader.return_value = {'LC80010092015051LGN00': 'aws',
98+
'LC80010092014051LGN00': 'aws'}
9899
mock_process.return_value = 'image.TIF'
99100

100-
args = ['download', 'LC80010092015051LGN00', '-b', '432', '-d', self.mock_path, '-p']
101+
args = ['download', 'LC80010092015051LGN00', 'LC80010092014051LGN00', '-b', '432', '-d', self.mock_path, '-p']
101102
output = landsat.main(self.parser.parse_args(args))
102-
mock_downloader.assert_called_with(['LC80010092015051LGN00'], ['4', '3', '2'])
103-
mock_process.assert_called_with('path/to/folder/LC80010092015051LGN00', '432', False, False)
104-
self.assertEquals(output, ["The output is stored at image.TIF"])
103+
mock_downloader.assert_called_with(['LC80010092015051LGN00', 'LC80010092014051LGN00'], ['4', '3', '2'])
104+
mock_process.assert_called_with('path/to/folder/LC80010092014051LGN00', '432', False, False, False)
105+
self.assertEquals(output, ["Image Processing Completed", 0])
106+
107+
# Call with force unzip flag
108+
args = ['download', 'LC80010092015051LGN00', 'LC80010092014051LGN00', '-b', '432', '-d',
109+
self.mock_path, '-p', '--force-unzip']
110+
output = landsat.main(self.parser.parse_args(args))
111+
mock_downloader.assert_called_with(['LC80010092015051LGN00', 'LC80010092014051LGN00'], ['4', '3', '2'])
112+
mock_process.assert_called_with('path/to/folder/LC80010092014051LGN00', '432', False, False, True)
113+
self.assertEquals(output, ["Image Processing Completed", 0])
105114

106115
@mock.patch('landsat.landsat.Uploader')
107116
@mock.patch('landsat.landsat.process_image')
108117
@mock.patch('landsat.landsat.Downloader.download')
109118
def test_download_process_continuous_with_upload(self, mock_downloader, mock_process, mock_upload):
110119
"""Test download and process commands together"""
111-
mock_downloader.return_value = True
120+
mock_downloader.return_value = {'LC80010092015051LGN00': 'aws'}
112121
mock_process.return_value = 'image.TIF'
113122
mock_upload.run.return_value = True
114123

115124
args = ['download', 'LC80010092015051LGN00', '-b', '432', '-d', self.mock_path, '-p',
116125
'-u', '--key', 'somekey', '--secret', 'somesecret', '--bucket', 'mybucket', '--region', 'this']
117126
output = landsat.main(self.parser.parse_args(args))
118127
mock_downloader.assert_called_with(['LC80010092015051LGN00'], ['4', '3', '2'])
119-
mock_process.assert_called_with('path/to/folder/LC80010092015051LGN00', '432', False, False)
128+
mock_process.assert_called_with('path/to/folder/LC80010092015051LGN00', '432', False, False, False)
120129
mock_upload.assert_called_with('somekey', 'somesecret', 'this')
121130
mock_upload.return_value.run.assert_called_with('mybucket', 'image.TIF', 'image.TIF')
122-
self.assertEquals(output, ["The output is stored at image.TIF"])
131+
self.assertEquals(output, ["Image Processing Completed", 0])
123132

124133
@mock.patch('landsat.landsat.process_image')
125134
@mock.patch('landsat.landsat.Downloader.download')
126135
def test_download_process_continuous_with_wrong_args(self, mock_downloader, mock_process):
127136
"""Test download and process commands together"""
128-
mock_downloader.return_value = True
137+
mock_downloader.return_value = {'LC80010092015051LGN00': 'aws'}
129138
mock_process.return_value = 'image.TIF'
130139

131140
args = ['download', 'LC80010092015051LGN00', '-b', '432', '-d', self.mock_path, '-p',
132141
'-u', '--region', 'whatever']
133142
output = landsat.main(self.parser.parse_args(args))
134143
mock_downloader.assert_called_with(['LC80010092015051LGN00'], ['4', '3', '2'])
135-
mock_process.assert_called_with('path/to/folder/LC80010092015051LGN00', '432', False, False)
144+
mock_process.assert_called_with('path/to/folder/LC80010092015051LGN00', '432', False, False, False)
136145
self.assertEquals(output, ['Could not authenticate with AWS', 1])
137146

138147
@mock.patch('landsat.landsat.process_image')
@@ -143,7 +152,7 @@ def test_process_correct(self, mock_process):
143152
args = ['process', 'path/to/folder/LC80010092015051LGN00']
144153
output = landsat.main(self.parser.parse_args(args))
145154

146-
mock_process.assert_called_with('path/to/folder/LC80010092015051LGN00', None, False, False)
155+
mock_process.assert_called_with('path/to/folder/LC80010092015051LGN00', None, False, False, False)
147156
self.assertEquals(output, ["The output is stored at image.TIF"])
148157

149158
@mock.patch('landsat.landsat.process_image')
@@ -154,7 +163,7 @@ def test_process_correct_pansharpen(self, mock_process):
154163
args = ['process', '--pansharpen', 'path/to/folder/LC80010092015051LGN00']
155164
output = landsat.main(self.parser.parse_args(args))
156165

157-
mock_process.assert_called_with('path/to/folder/LC80010092015051LGN00', None, False, True)
166+
mock_process.assert_called_with('path/to/folder/LC80010092015051LGN00', None, False, True, False)
158167
self.assertEquals(output, ["The output is stored at image.TIF"])
159168

160169
def test_process_incorrect(self):

0 commit comments

Comments
 (0)