Skip to content
This repository was archived by the owner on May 23, 2024. It is now read-only.

Commit 3c9e577

Browse files
committed
Add authorization support for NASA LPDAAC HTTP servers (fix #4); removing the locals() (fix #1); fix for Google Earth Engine support (fix #5); add more information about GEE service accounts (fix #3)
1 parent bbaa2bf commit 3c9e577

File tree

4 files changed

+59
-18
lines changed

4 files changed

+59
-18
lines changed

Readme.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Please make sure that the command-line tools are within the System path. Otherwi
2828

2929
*Further requirements:*
3030
For MODIS HDF data processing, HDF4 image format has to be available for gdal command-line tools.
31+
Using the original data source for MODIS Land Products (LPDAAC), user login information from NASA Earthdata are required (see item 'userPwd' in task dictionary).
3132

3233
Usage
3334
-----
@@ -43,7 +44,8 @@ Usage
4344
'format': 'HDF4Image',
4445
'EPSG': None,
4546
'resample': None,
46-
'source': 'LPDAAC'
47+
'source': 'LPDAAC',
48+
'userPwd': 'username:password'
4749
})
4850
output = ingest.start()
4951

@@ -66,17 +68,20 @@ Available datasets
6668
* MODIS Thermal Anomalies & Fire: MCD45A1
6769

6870
Further information to the datasets can be found at the following sites:
69-
* NASA LPDAAC: http://lpdaac.usgs.gov/products/modis_products_table
71+
* NASA LPDAAC: https://lpdaac.usgs.gov/dataset_discovery/modis/modis_products_table
7072
* NSIDC DAAC: http://nsidc.org/data/modis/data_summaries/index.html
7173

7274
Several MODIS and especially Landsat datasets are also available using Google Earth Engine.
7375

7476
Google Earth Engine
7577
-------------------
7678

77-
If you have an account for the Google Earth Engine (https://earthengine.google.org), you can also use the Python bindings to have access to specific MODIS products and Landsat datasets.
79+
If you have an account for the Google Earth Engine (https://earthengine.google.org), you can also use the Python bindings to have access to specific MODIS products and Landsat datasets. To install the earthengine Python API see the following documentation:
80+
https://developers.google.com/earth-engine/python_install
81+
pip install earthengine-api
7882

79-
Please set the variables MY_SERVICE_ACCOUNT and MY_PRIVATE_KEY_FILE in gee_init.py to your service account username and the private key file.
83+
Please set the variables MY_SERVICE_ACCOUNT and MY_PRIVATE_KEY_FILE in gee_init.py to your service account username and the private key file. Therefore you need a service account that needs to be whitelisted first by the Google Earth Engine team. Please see the following documentation for more information:
84+
https://developers.google.com/earth-engine/service_account
8085

8186
*Examples:*
8287

pyEOM/datasets/MODIS.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,26 @@ def TestDatasetLPDAAC():
7979
dataset.getProduct('MOD13Q1')
8080
dataset.source.download('', ['h12v08'])
8181

82+
class LPDAACHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
83+
def http_error_302(self, req, fp, code, msg, headers):
84+
return urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
8285

8386
class LPDAAC(HTTPSource):
8487
url = 'http://e4ftl01.cr.usgs.gov'
88+
userpwd = None
89+
listDirectoriesIndex = 3
90+
91+
def setUserPwd(self, login):
92+
self.userpwd = login
93+
from base64 import b64encode
94+
userAndPass = b64encode(str.encode(self.userpwd)).decode("ascii")
95+
self.http_header = {'Authorization': 'Basic %s' % userAndPass}
96+
cookieprocessor = urllib2.HTTPCookieProcessor()
97+
opener = urllib2.build_opener(LPDAACHTTPRedirectHandler, cookieprocessor)
98+
urllib2.install_opener(opener)
99+
100+
def __init__(self):
101+
super(LPDAAC, self).__init__(self.url)
85102

86103

87104
class NSIDC(FTPSource):
@@ -119,7 +136,7 @@ def addTask(self, dataset, geom, publishPath, start=None, end=None, qualityValue
119136
def __init__(self, task, dataset, source):
120137
LOGGER.debug('LPDAAC init')
121138
self.dataset = dataset
122-
self.source = locals()[source]()
139+
self.source = source
123140
if 'start' in task != "":
124141
self.taskStart = datetime.strptime(task['start'], '%Y-%m-%d')
125142
if 'end' in task != "":

pyEOM/sources.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
__author__ = 'Jonas Eberle <jonas.eberle@eberle-mail.de>'
22

33
import pyEOM.gee_init as gee
4-
import ee
4+
try:
5+
import ee
6+
from ee import oauth
7+
except:
8+
pass
59
import sys
610
import ogr
711

@@ -23,7 +27,10 @@ def __init__(self, username=None, password=None):
2327

2428
def connect(self, username=None, password=None):
2529
if gee.MY_SERVICE_ACCOUNT != '' and gee.MY_PRIVATE_KEY_FILE != '':
26-
ee.Initialize(ee.ServiceAccountCredentials(gee.MY_SERVICE_ACCOUNT, gee.MY_PRIVATE_KEY_FILE))
30+
#ee.ServiceAccountCredentials(gee.MY_SERVICE_ACCOUNT, gee.MY_PRIVATE_KEY_FILE)
31+
from oauth2client.service_account import ServiceAccountCredentials
32+
authorization = ServiceAccountCredentials.from_p12_keyfile(gee.MY_SERVICE_ACCOUNT, gee.MY_PRIVATE_KEY_FILE, scopes=oauth.SCOPE)
33+
ee.Initialize(authorization)
2734
elif username != None and password != None:
2835
pass
2936
else:
@@ -111,7 +118,7 @@ def listScenes(self, dataset, eeGeometry, start, end):
111118
return images
112119

113120

114-
import urllib
121+
import urllib, urllib2
115122
from bs4 import BeautifulSoup
116123
import re
117124
import os
@@ -136,6 +143,8 @@ class HTTPSource(object):
136143
directories = []
137144
filesObj = None
138145
files = []
146+
listDirectoriesIndex = 1
147+
http_header = {}
139148

140149
def __init__(self, url):
141150
self.url = url
@@ -147,17 +156,17 @@ def listDirectories(self, directory):
147156
self.path = directory
148157
LOGGER.info('Retrieving url: '+self.url+directory)
149158
html = urllib.urlopen(self.url+directory).read()
150-
self.directoryObj = BeautifulSoup(html)
159+
self.directoryObj = BeautifulSoup(html, "lxml")
151160
directories = self.directoryObj.findAll(href=re.compile('/'))
152-
directories = directories[1:]
161+
directories = directories[self.listDirectoriesIndex:]
153162
for dir in directories:
154163
self.directories.append(dir.string[0:-1])
155164
return self.directories
156165

157166
def listFiles(self, directory):
158167
self.path = directory
159168
html = urllib.urlopen(self.url+directory).read()
160-
self.filesObj = BeautifulSoup(html)
169+
self.filesObj = BeautifulSoup(html, "lxml")
161170

162171
def filterFiles(self, regexp, urls=True):
163172
self.files = self.filesObj.findAll(text=re.compile(regexp))
@@ -169,8 +178,13 @@ def downloadFile(self, url, path):
169178
LOGGER.info('Download: '+url)
170179
filepath = path+'/'+url.split('/')[-1]
171180
if not os.path.isfile(filepath):
172-
download = urllib.urlretrieve(url, filepath)
173-
return download[0]
181+
#download = urllib.urlretrieve(url, filepath)
182+
filSave = open(filepath, "wb")
183+
req = urllib2.Request(url, headers=self.http_header)
184+
http = urllib2.urlopen(req)
185+
filSave.write(http.read())
186+
filSave.close()
187+
return filepath
174188
else:
175189
return filepath
176190

pyEOM/tasks.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pyEOM import log
66
import os
77
import sys
8+
import importlib
89
import ConfigParser
910

1011
LOGGER = log.LOGGER
@@ -102,11 +103,12 @@ def start(self):
102103
LOGGER.debug('Dataset: '+dataset[0])
103104
LOGGER.debug('Product: '+dataset[1])
104105
if "MODIS" == dataset[0]:
105-
from datasets.predefined.MODIS import *
106-
if dataset[1] not in locals():
107-
raise Exception('Dataset Class is not available!')
106+
try:
107+
dataset = getattr(importlib.import_module('pyEOM.datasets.predefined.MODIS.'+dataset[1]), 'Dataset')
108+
except Exception as e:
109+
raise Exception('Dataset Class is not available: '+str(e))
108110
sys.exit(1)
109-
self.processing['dataset'] = locals()[dataset[1]].Dataset()
111+
self.processing['dataset'] = dataset()
110112

111113
if len(self.processing['dataset'].sources) == 0:
112114
raise Exception('No sources available!')
@@ -127,7 +129,10 @@ def start(self):
127129
try:
128130
LOGGER.debug('Try to get source class: '+source)
129131
if source == 'LPDAAC' or source == 'NSIDC':
130-
self.processing['source'] = getattr(MODIS, 'MODISHDF')(self.task, self.processing['dataset'], source)
132+
sourceObj = getattr(MODIS, source)()
133+
if source == 'LPDAAC':
134+
sourceObj.setUserPwd(self.task['userPwd'])
135+
self.processing['source'] = getattr(MODIS, 'MODISHDF')(self.task, self.processing['dataset'], sourceObj)
131136
else:
132137
self.processing['source'] = getattr(MODIS, source)(self.task, self.processing['dataset'])
133138
except NameError, e:

0 commit comments

Comments
 (0)