A python package that extends Google Earth Engine
--- **GitHub**: [https://github.com/davemlz/eemont](https://github.com/davemlz/eemont) **Documentation**: [https://eemont.readthedocs.io/](https://eemont.readthedocs.io/) **PyPI**: [https://pypi.org/project/eemont/](https://pypi.org/project/eemont/) **Conda-forge**: [https://anaconda.org/conda-forge/eemont](https://anaconda.org/conda-forge/eemont) **Tutorials**: [https://github.com/davemlz/eemont/tree/master/docs/tutorials](https://github.com/davemlz/eemont/tree/master/docs/tutorials) **Paper**: [https://joss.theoj.org/papers/10.21105/joss.03168](https://joss.theoj.org/papers/10.21105/joss.03168) --- ## Overview [Google Earth Engine](https://earthengine.google.com/) is a cloud-based service for geospatial processing of vector and raster data. The Earth Engine platform has a [JavaScript and a Python API](https://developers.google.com/earth-engine/guides) with different methods to process geospatial objects. Google Earth Engine also provides a [HUGE PETABYTE-SCALE CATALOG](https://developers.google.com/earth-engine/datasets/) of raster and vector data that users can process online (e.g. Landsat Missions Image Collections, Sentinel Missions Image Collections, MODIS Products Image Collections, World Database of Protected Areas, etc.). The eemont package extends the [Google Earth Engine Python API](https://developers.google.com/earth-engine/guides/python_install) with pre-processing and processing tools for the most used satellite platforms by adding utility methods for different [Earth Engine Objects](https://developers.google.com/earth-engine/guides/objects_methods_overview) that are friendly with the Python method chaining. ## Google Earth Engine Community: Developer Resources The eemont Python package can be found in the [Earth Engine Community: Developer Resources](https://developers.google.com/earth-engine/tutorials/community/developer-resources) together with other awesome resources such as [geemap](https://geemap.org/) and [rgee](https://github.com/r-spatial/rgee). ## How does it work? The eemont python package extends the following Earth Engine classes: - [ee.Feature](https://developers.google.com/earth-engine/guides/features) - [ee.FeatureCollection](http://developers.google.com/earth-engine/guides/feature_collections) - [ee.Geometry](https://developers.google.com/earth-engine/guides/geometries) - [ee.Image](https://developers.google.com/earth-engine/guides/image_overview) - [ee.ImageCollection](https://developers.google.com/earth-engine/guides/ic_creating) - [ee.List](https://developers.google.com/earth-engine/guides/objects_methods_overview) - [ee.Number](https://developers.google.com/earth-engine/guides/objects_methods_overview) New utility methods and constructors are added to above-mentioned classes in order to create a more fluid code by being friendly with the Python method chaining. These methods are mandatory for some pre-processing and processing tasks (e.g. clouds masking, shadows masking, image scaling, spectral indices computation, etc.), and they are presented as simple functions that give researchers, students and analysts the chance to analyze data with far fewer lines of code. Look at this simple example where a [Sentinel-2 Surface Reflectance Image Collection](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR) is pre-processed and processed in just one step: ```python import ee, eemont ee.Authenticate() ee.Initialize() point = ee.Geometry.PointFromQuery( 'Cali, Colombia', user_agent = 'eemont-example' ) # Extended constructor S2 = (ee.ImageCollection('COPERNICUS/S2_SR') .filterBounds(point) .closest('2020-10-15') # Extended (pre-processing) .maskClouds(prob = 70) # Extended (pre-processing) .scaleAndOffset() # Extended (pre-processing) .spectralIndices(['NDVI','NDWI','BAIS2'])) # Extended (processing) ``` And just like that, the collection was pre-processed, processed and ready to be analyzed! ## Installation Install the latest version from PyPI: ``` pip install eemont ``` Upgrade `eemont` by running: ``` pip install -U eemont ``` Install the latest version from conda-forge: ``` conda install -c conda-forge eemont ``` Install the latest dev version from GitHub by running: ``` pip install git+https://github.com/davemlz/eemont ``` ## Features Let's see some of the main features of eemont and how simple they are compared to the GEE Python API original methods: ### Overloaded Operators The following operators are overloaded: +, -, \*\, /, //, %, \**\ , <<, >>, &, \|\, <, <=, ==, !=, >, >=, -, ~. (and you can avoid the `ee.Image.expression()` method!)| GEE Python API | eemont-style |
|---|---|
| ``` python ds = 'COPERNICUS/S2_SR' S2 = (ee.ImageCollection(ds) .first()) def scaleImage(img): scaling = img.select('B.*') x = scaling.multiply(0.0001) scaling = img.select(['AOT','WVP']) scaling = scaling.multiply(0.001) x = x.addBands(scaling) notScaling = img.select([ 'SCL', 'TCI.*', 'MSK.*', 'QA.*' ])) return x.addBands(notScaling) S2 = scaleImage(S2) exp = '2.5*(N-R)/(N+(6*R)-(7.5*B)+1)' imgDict = { 'N': S2.select('B8'), 'R': S2.select('B4'), 'B': S2.select('B2') } EVI = S2.expression(exp,imgDict) ``` | ``` python ds = 'COPERNICUS/S2_SR' S2 = (ee.ImageCollection(ds) .first() .scale()) N = S2.select('B8') R = S2.select('B4') B = S2.select('B2') EVI = 2.5*(N-R)/(N+(6*R)-(7.5*B)+1) ``` |
| GEE Python API | eemont-style |
|---|---|
| ``` python ds = 'LANDSAT/LC08/C01/T1_SR' def maskCloudsShadows(img): c = (1 << 3) s = (1 << 5) qa = 'pixel_qa' qa = img.select(qa) cm = qa.bitwiseAnd(c).eq(0) sm = qa.bitwiseAnd(s).eq(0) mask = cm.And(sm) return img.updateMask(mask) (ee.ImageCollection(ds) .map(maskCloudsShadows)) ``` | ``` python ds = 'LANDSAT/LC08/C01/T1_SR' (ee.ImageCollection(ds) .maskClouds()) ``` |
| GEE Python API | eemont-style |
|---|---|
| ``` python def scaleBands(img): scaling = img.select([ 'NDVI', 'EVI', 'sur.*' ]) x = scaling.multiply(0.0001) scaling = img.select('.*th') scaling = scaling.multiply(0.01) x = x.addBands(scaling) notScaling = img.select([ 'DetailedQA', 'DayOfYear', 'SummaryQA' ]) return x.addBands(notScaling) ds = 'MODIS/006/MOD13Q1' (ee.ImageCollection(ds) .map(scaleBands)) ``` | ``` python ds = 'MODIS/006/MOD13Q1' (ee.ImageCollection(ds) .scaleAndOffset()) ``` |
| GEE Python API | eemont-style |
|---|---|
| ``` python ds = 'LANDSAT/LC08/C01/T1_SR' def maskCloudsShadows(img): c = (1 << 3) s = (1 << 5) qa = 'pixel_qa' qa = img.select(qa) cm = qa.bitwiseAnd(c).eq(0) sm = qa.bitwiseAnd(s).eq(0) mask = cm.And(sm) return img.updateMask(mask) def scaleBands(img): scaling = img.select('B[1-7]') x = scaling.multiply(0.0001) scaling = img.select([ 'B10','B11' ]) scaling = scaling.multiply(0.1) x = x.addBands(scaling) notScaling = img.select([ 'sr_aerosol', 'pixel_qa', 'radsat_qa' ]) return x.addBands(notScaling) (ee.ImageCollection(ds) .map(maskCloudsShadows) .map(scaleBands)) ``` | ``` python ds = 'LANDSAT/LC08/C01/T1_SR' (ee.ImageCollection(ds) .preprocess()) ``` |
| GEE Python API | eemont-style |
|---|---|
| ``` python ds = 'LANDSAT/LC08/C01/T1_SR' def scaleImage(img): scaling = img.select('B[1-7]') x = scaling.multiply(0.0001) scaling = img.select(['B10','B11']) scaling = scaling.multiply(0.1) x = x.addBands(scaling) notScaling = img.select([ 'sr_aerosol', 'pixel_qa', 'radsat_qa' ])) return x.addBands(notScaling) def addIndices(img): x = ['B5','B4'] a = img.normalizedDifference(x) a = a.rename('NDVI') x = ['B5','B3'] b = img.normalizedDifference(x) b = b.rename('GNDVI') x = ['B3','B6'] c = img.normalizedDifference(x) c = b.rename('NDSI') return img.addBands([a,b,c]) (ee.ImageCollection(ds) .map(scaleImage) .map(addIndices)) ``` | ``` python ds = 'LANDSAT/LC08/C01/T1_SR' (ee.ImageCollection(ds) .scaleAndOffset() .spectralIndices([ 'NDVI', 'GNDVI', 'NDSI']) ) ``` |
| GEE Python API | eemont-style |
|---|---|
| ``` python ds = 'COPERNICUS/S5P/OFFL/L3_NO2' xy = [-76.21, 3.45] poi = ee.Geometry.Point(xy) date = ee.Date('2020-10-15') date = date.millis() def setTimeDelta(img): prop = 'system:time_start' prop = img.get(prop) prop = ee.Number(prop) delta = prop.subtract(date) delta = delta.abs() return img.set( 'dateDist', delta) (ee.ImageCollection(ds) .filterBounds(poi) .map(setTimeDelta) .sort('dateDist') .first()) ``` | ``` python ds = 'COPERNICUS/S5P/OFFL/L3_NO2' xy = [-76.21, 3.45] poi = ee.Geometry.Point(xy) (ee.ImageCollection(ds) .filterBounds(poi) .closest('2020-10-15')) ``` |
| JS (Code Editor) | Python (eemont) | R (rgee+) |
|---|---|---|
| ``` javascript var usr = 'users/sofiaermida/' var rep = 'landsat_smw_lst:' var fld = 'modules/' var fle = 'Landsat_LST.js' var pth = usr+rep+fld+fle var mod = require(pth) var LST = mod.collection( ee.Geometry.Rectangle([ -8.91, 40.0, -8.3, 40.4 ]), 'L8', '2018-05-15', '2018-05-31', true ) ``` | ``` python import ee, eemont ee.Initialize() usr = 'users/sofiaermida/' rep = 'landsat_smw_lst:' fld = 'modules/' fle = 'Landsat_LST.js' pth = usr+rep+fld+fle ee.install(pth) mod = ee.require(pth) LST = mod.collection( ee.Geometry.Rectangle([ -8.91, 40.0, -8.3, 40.4 ]), 'L8', '2018-05-15', '2018-05-31', True ) ``` | ``` r library(rgee) library(rgeeExtra) ee_Initialize() usr <- 'users/sofiaermida/' rep <- 'landsat_smw_lst:' fld <- 'modules/' fle <- 'Landsat_LST.js' pth <- paste0(usr,rep,fld,fle) mod <- ee$require(pth) LST = mod$collection( ee$Geometry$Rectangle(c( -8.91, 40.0, -8.3, 40.4 )), 'L8', '2018-05-15', '2018-05-31', TRUE ) ``` |