├── .gitignore ├── bitreader ├── README.md ├── _tools ├── dictionary ├── collection ├── string ├── number ├── feature ├── geometry ├── date ├── featurecollection ├── list ├── map ├── imagecollection └── image ├── widgets ├── assetmanager ├── assetItems ├── utils └── assetSelector ├── LICENSE ├── labels ├── tools_test ├── mutables ├── composite ├── example_dt ├── tools ├── scores ├── list_algorithms ├── ui ├── closePanel ├── async ├── checkSelect ├── polygon ├── labels ├── labels2 ├── drawingPanel └── ImageSlider ├── indexes ├── decision_tree ├── cloud_masks_test ├── batch ├── helpers_js ├── cloud_masks └── satellite /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /bitreader: -------------------------------------------------------------------------------- 1 | var decodeKey = function(key) { 2 | var items = ee.String(key).split('-') 3 | var start = ee.String(items.get(0)) 4 | var newkey = ee.String(ee.Algorithms.If( 5 | items.size().eq(1), 6 | start.cat('-').cat(start), 7 | key 8 | )) 9 | items = newkey.split('-') 10 | var end = ee.String(items.get(1)) 11 | var s = ee.Number.parse(start) 12 | var e = ee.Number.parse(end) 13 | return ee.List.sequence(s, e) 14 | } 15 | 16 | print(decodeKey('6-5')) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A set of tools to use in Google Earth Engine Code Editor 2 | 3 | Google Earth Engine is a web platform for satellite image processing 4 | (https://earthengine.google.com/). One way to use this platform (probably 5 | the most popular way) is using an online tool called *The Code Editor*, which 6 | let the user access the platform using a scripting language (JavaScript). 7 | 8 | Documentation and examples in the [Wiki Page](https://github.com/fitoprincipe/geetools-code-editor/wiki) 9 | -------------------------------------------------------------------------------- /_tools/dictionary: -------------------------------------------------------------------------------- 1 | var helpersJS = require('users/fitoprincipe/geetools:helpers_js') 2 | 3 | /** DICTIONARIES */ 4 | var dictionary = {}; 5 | 6 | // Extract values from a list of keys 7 | dictionary.extractList = function(dict, list) { 8 | var empty = ee.List([]) 9 | list = ee.List(list) 10 | dict = ee.Dictionary(dict) 11 | var values = ee.List(list.iterate(function(el, first) { 12 | var f = ee.List(first) 13 | var cond = dict.contains(el); 14 | return ee.Algorithms.If(cond, f.add(dict.get(el)), f) 15 | }, empty)) 16 | return values 17 | } 18 | 19 | exports.dictionary = dictionary -------------------------------------------------------------------------------- /_tools/collection: -------------------------------------------------------------------------------- 1 | var helpersJS = require('users/fitoprincipe/geetools:helpers_js') 2 | 3 | /** COLLECTIONS **/ 4 | var collection = {}; 5 | 6 | collection.enumerate = function(col) { 7 | var collist = col.toList(col.size()) 8 | 9 | // first element 10 | var ini = ee.Number(0) 11 | var first_image = ee.Image(collist.get(0)) 12 | var first = ee.List([ini, first_image]) 13 | 14 | var start = ee.List([first]) 15 | var rest = collist.slice(1) 16 | 17 | var list = ee.List(rest.iterate(function(im, s){ 18 | im = ee.Image(im) 19 | s = ee.List(s) 20 | var last = ee.List(s.get(-1)) 21 | var last_index = ee.Number(last.get(0)) 22 | var index = last_index.add(1) 23 | return s.add(ee.List([index, im])) 24 | }, start)) 25 | 26 | return list 27 | } 28 | 29 | exports.collection = collection -------------------------------------------------------------------------------- /_tools/string: -------------------------------------------------------------------------------- 1 | var helpersJS = require('users/fitoprincipe/geetools:helpers_js') 2 | 3 | /** STRINGS */ 4 | var string = {}; 5 | 6 | // format 7 | string.format = function(string, replacement) { 8 | var str = ee.String(string) 9 | var repl = ee.Dictionary(replacement) 10 | var keys = repl.keys() 11 | var values = repl.values().map(function(v){return ee.Algorithms.String(v)}) 12 | var z = keys.zip(values) 13 | 14 | var newstr = z.iterate(function(kv, ini){ 15 | var keyval = ee.List(kv) 16 | ini = ee.String(ini) 17 | 18 | var key = ee.String(keyval.get(0)) 19 | var value = ee.String(keyval.get(1)) 20 | 21 | var pattern = ee.String('{').cat(key).cat(ee.String('}')) 22 | 23 | return ini.replace(pattern, value) 24 | 25 | }, str) 26 | return ee.String(newstr) 27 | } 28 | 29 | exports.string = string 30 | -------------------------------------------------------------------------------- /widgets: -------------------------------------------------------------------------------- 1 | /*** 2 | * Custom Widgets 3 | * Author: Rodrigo E. Principe 4 | * email: fitoprincipe82@gmail.com 5 | * Licence: MIT 6 | ***/ 7 | 8 | var ClosePanel = require('users/fitoprincipe/geetools:ui/closePanel').ClosePanel 9 | exports.ClosePanel = ClosePanel 10 | 11 | var DrawingPanel = require('users/fitoprincipe/geetools:ui/drawingPanel').DrawingPanel 12 | exports.DrawingPanel = DrawingPanel 13 | 14 | var CheckSelect = require('users/fitoprincipe/geetools:ui/checkSelect').CheckSelect 15 | exports.CheckSelect = CheckSelect 16 | 17 | var asyncSelect = require('users/fitoprincipe/geetools:ui/async').asyncSelect 18 | exports.asyncSelect = asyncSelect 19 | 20 | var labelWidget = require('users/fitoprincipe/geetools:ui/labels').labelWidget 21 | exports.labelWidget = labelWidget 22 | 23 | var ImageSlider = require('users/fitoprincipe/geetools:ui/ImageSlider').ImageSlider 24 | exports.ImageSlider = ImageSlider -------------------------------------------------------------------------------- /assetmanager/assetItems: -------------------------------------------------------------------------------- 1 | /** 2 | * Asset Items 3 | * Author: Rodrigo E. Principe 4 | * email: fitoprincipe82@gmail.com 5 | * license: MIT 6 | **/ 7 | var utils = require('users/fitoprincipe/geetools:assetmanager/utils') 8 | 9 | var AssetItem = function(id, type, parent) { 10 | this.id = id 11 | this.type = type 12 | this.parent = parent 13 | 14 | this.user = utils.getRoot(this.id) 15 | this.folder = this.id.replace(this.user+'/', '') 16 | 17 | this.label = function() { 18 | return '('+type.toUpperCase()+') '+this.folder 19 | } 20 | } 21 | exports.AssetItem = AssetItem 22 | 23 | var getItems = function(id) { 24 | var data = ee.data.getList({'id':id}) 25 | var result = [] 26 | for (var i in data) { 27 | var value = data[i] 28 | var item = new AssetItem(value.id, value.type) 29 | result.push({label:item.label(), value:value.id}) 30 | } 31 | return result 32 | } 33 | exports.getItems = getItems -------------------------------------------------------------------------------- /_tools/number: -------------------------------------------------------------------------------- 1 | var helpersJS = require('users/fitoprincipe/geetools:helpers_js') 2 | 3 | /** NUMBER */ 4 | var number = {}; 5 | 6 | // RANDOM NUMBER 7 | number.random = function(start, end, type) { 8 | var s = start || 0 9 | var e = end || 1 10 | var t = type || 'float' 11 | 12 | s = ee.Number(s) 13 | e = ee.Number(e) 14 | 15 | var diff = e.subtract(s) 16 | 17 | var value = ee.Number(Math.random()) 18 | 19 | var result = value.multiply(diff).add(s) 20 | 21 | if (t == 'float') { 22 | return result} 23 | else if (t == 'int') { 24 | return result.toInt() 25 | } 26 | } 27 | 28 | // TRIM DECIMALS 29 | number.trimDecimals = function(places) { 30 | 31 | var factor = ee.Number(10).pow(ee.Number(places).toInt()) 32 | 33 | var wrap = function(number) { 34 | var n = ee.Number(number) 35 | 36 | var floor = n.floor() 37 | var decimals = n.subtract(floor) 38 | var take = decimals.multiply(factor).toInt() 39 | var newdecimals = take.toFloat().divide(factor) 40 | return floor.add(newdecimals).toFloat() 41 | } 42 | return wrap 43 | } 44 | 45 | exports.number = number -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /labels: -------------------------------------------------------------------------------- 1 | var text = require('users/gena/packages:text') 2 | var tools = require('users/fitoprincipe/geetools:tools') 3 | 4 | var rgbToHex = function(R,G,B) { 5 | // https://campushippo.com/lessons/how-to-convert-rgb-colors-to-hexadecimal-with-javascript-78219fdb 6 | var rgbToHex = function (rgb) { 7 | var hex = Number(rgb).toString(16); 8 | if (hex.length < 2) { 9 | hex = "0" + hex; 10 | } 11 | return hex; 12 | }; 13 | 14 | var red = rgbToHex(R); 15 | var green = rgbToHex(G); 16 | var blue = rgbToHex(B); 17 | return red+green+blue; 18 | } 19 | 20 | var labelF = function(feat, column, scale, options) { 21 | var cent = feat.geometry().centroid() 22 | var prop = ee.Algorithms.String(feat.get(column)) 23 | return ee.Image(text.draw(prop, cent, scale, options)) 24 | } 25 | 26 | var label = function(fc, column, scale, options) { 27 | var first = labelF(ee.Feature(fc.first()), column, scale, options) 28 | var result = fc.iterate(function(feat, start) { 29 | feat = ee.Feature(feat) 30 | start = ee.Image(start) 31 | label = labelF(feat, column, scale, options) 32 | return tools.image.cat(start, label) 33 | }, first) 34 | 35 | return ee.Image(result) 36 | } 37 | 38 | exports.labelCollection = label 39 | exports.labelFeature = labelF 40 | exports.rgbToHex = rgbToHex -------------------------------------------------------------------------------- /_tools/feature: -------------------------------------------------------------------------------- 1 | var helpersJS = require('users/fitoprincipe/geetools:helpers_js') 2 | var list = require('users/fitoprincipe/geetools:_tools/list').list 3 | 4 | 5 | /** FEATURE */ 6 | var feature = {}; 7 | 8 | // GET FEATURE PROPERTY 9 | feature.gets = function(feature, properties) { 10 | var list = ee.List(properties) || feature.propertyNames() 11 | var newlist = list.map(function(el){ 12 | return feature.get(el) 13 | }) 14 | return newlist 15 | } 16 | 17 | // AGGREGATE PROPERTIES 18 | feature.reduce = function(options) { 19 | var opt = options || {'reducer': 'mean', 20 | 'properties': ee.Number(0), 21 | 'name': 'reduction'} 22 | 23 | var reduc = opt['reducer'] || 'mean' 24 | var prop = opt['properties'] || ee.Number(0) 25 | var n = opt['name'] || 'reduction' 26 | 27 | var wrap = function(feat) { 28 | var propnames = feat.propertyNames().remove('system:index') 29 | var prop2use = ee.List(ee.Algorithms.If(prop, prop, propnames)) 30 | var properties = list.intersection(prop2use, propnames) 31 | var values = feature.gets(feat, properties) 32 | var casted = values.map(function(el){return ee.Number(el)}) 33 | return feat.set(n, ee.Number(casted.reduce(reduc))) 34 | } 35 | 36 | return wrap 37 | } 38 | 39 | exports.feature = feature -------------------------------------------------------------------------------- /_tools/geometry: -------------------------------------------------------------------------------- 1 | var helpersJS = require('users/fitoprincipe/geetools:helpers_js') 2 | 3 | /** GEOMETRIES */ 4 | var geometry = {}; 5 | 6 | // mask out pixels inside a geometry (return a function for mapping) 7 | geometry.maskInside = function(image, geometry) { 8 | var mask = ee.Image.constant(1).clip(geometry).mask().not() 9 | return image.updateMask(mask) 10 | } 11 | 12 | // paint geometries 13 | geometry.paint = function(geometry, options) { 14 | 15 | var def = { 16 | fillColor: null, 17 | borderColor: 'black', 18 | border: 2, 19 | idFld: null 20 | } 21 | 22 | var opt = helpersJS.get_options(def, options) 23 | 24 | var type = geometry.name() 25 | 26 | // build FeatureCollection depending on geometry type 27 | if (type === 'Geometry') { 28 | geometry = ee.FeatureCollection([ee.Feature(geometry)]) 29 | } else if (type === 'Feature') { 30 | geometry = ee.FeatureCollection([geometry]) 31 | } 32 | 33 | // Fill 34 | if (opt.fillColor !== null) { 35 | var fill = ee.Image().paint(geometry, 1).visualize({palette:[opt.fillColor]}) 36 | } 37 | 38 | // Border 39 | var out = ee.Image().paint(geometry, 1, opt.border).visualize({palette:[opt.borderColor]}) 40 | 41 | // Fill & Border 42 | if ((opt.fillColor !== null) & (opt.borderColor !== null)){ var rgbVector = fill.blend(out)} 43 | 44 | // Only Fill 45 | else if (opt.fillColor !== null){ var rgbVector = fill } 46 | 47 | // Only Border 48 | else { var rgbVector = out } 49 | 50 | // Get id image 51 | if (opt.idFld !== null) { 52 | var idImg = geometry.reduceToImage([opt.idFld], ee.Reducer.first()).rename(opt.idFld) 53 | return rgbVector.addBands(idImg) 54 | } else { 55 | return rgbVector 56 | } 57 | } 58 | 59 | exports.geometry = geometry -------------------------------------------------------------------------------- /tools_test: -------------------------------------------------------------------------------- 1 | var tools = require('users/fitoprincipe/geetools:tools') 2 | 3 | var help = {}; 4 | 5 | var sum_bands = function(add_layers) { 6 | var add = add_layers || false; 7 | 8 | print(tools.help['sum_bands']) 9 | 10 | var ii = tools.dict2image({a:1, b:2, c:4, d:8}) 11 | 12 | var sum = tools.sum_bands()(ii) 13 | var sum2 = tools.sum_bands('a_name')(ii) 14 | var sum3 = tools.sum_bands('a_plus_d', ['a', 'd'])(ii) 15 | var sum4 = tools.sum_bands(null, null, false)(ii) 16 | 17 | print('Original image', ii) 18 | print('All default', sum) 19 | print('Just a name', sum2) 20 | print('Some bands', sum3) 21 | print('Only sum band', sum4) 22 | 23 | if (add) { 24 | Map.addLayer(sum, {},'All default'); 25 | Map.addLayer(sum2, {},'Just a name'); 26 | Map.addLayer(sum3, {},'Some bands'); 27 | Map.addLayer(sum4, {},'Only sum band'); 28 | } 29 | } 30 | 31 | var replace_band = function(add_layers) { 32 | print(tools.help['replace_band']); 33 | 34 | var i = tools.dict2image({'a':1, 'b':2, 'c':3}) 35 | var i2 = tools.dict2image({'d':4}) 36 | 37 | print('replace_band', i); 38 | print('add_band', i2); 39 | 40 | var result = tools.replace_band(i, 'c', i2); 41 | print('replace band "c"', result) 42 | 43 | if (add_layers) { 44 | Map.addLayer(i,{},'replace_band') 45 | Map.addLayer(i2,{},'add_band') 46 | Map.addLayer(result,{},'replace band c') 47 | } 48 | } 49 | 50 | help['sum_bands'] = 'sum_bands(add_layer)\n\n'+ 51 | 'add_layer: whether to add or not the layers to the Map' 52 | 53 | help['replace_band'] = 'replace_band(add_layer)\n\n'+ 54 | 'add_layer: whether to add or not the layers to the Map' 55 | 56 | 57 | exports.sum_bands = sum_bands 58 | exports.replace_band = replace_band 59 | exports.help = help -------------------------------------------------------------------------------- /mutables: -------------------------------------------------------------------------------- 1 | /**************** 2 | * Mutable Objects 3 | * (despite https://groups.google.com/d/msg/google-earth-engine-developers/ZWxQsCiJhQY/x4b-7vKYAwAJ) 4 | * 5 | * Author: Rodrigo E. Principe 6 | * email: fitoprincipe82@gmail.com 7 | * licence: MIT 8 | ****************/ 9 | var tools = require('users/fitoprincipe/geetools:tools') 10 | 11 | var Feature = function(feature) { 12 | this.feature = feature 13 | this.set = function(key, value) { 14 | this.feature = this.feature.set(key, value) 15 | } 16 | this.get = function(property) { 17 | return this.feature.get(property) 18 | } 19 | this.id = this.feature.id() 20 | } 21 | exports.Feature = Feature 22 | 23 | var FeatureCollection = function(collection) { 24 | this.collection = collection 25 | 26 | this.setPosition = function(position, property, value) { 27 | var featlist = this.collection.toList(this.collection.size()) 28 | var feat = ee.Feature(featlist.get(position)) 29 | this.set(feat, property, value) 30 | } 31 | 32 | this.getPosition = function(position, property) { 33 | var featlist = this.collection.toList(this.collection.size()) 34 | var feat = ee.Feature(featlist.get(position)) 35 | return this.get(feat, property) 36 | } 37 | 38 | this.set = function(feature, property, value) { 39 | var fid = feature.id() // string 40 | var newft = this.collection.map(function(feat) { 41 | feat = ee.Feature(feat) 42 | var featid = feat.id() 43 | var cond = fid.compareTo(featid).eq(0) 44 | return ee.Feature(ee.Algorithms.If(cond, 45 | feat.set(property, value), feat)) 46 | }) 47 | this.collection = newft 48 | } 49 | 50 | this.get = function(feature, property) { 51 | var fid = feature.id() // string 52 | var feat = ee.Feature(this.collection.filterMetadata('system:index', 'equals', fid).first()) 53 | return feat.get(property) 54 | } 55 | 56 | } 57 | exports.FeatureCollection = FeatureCollection -------------------------------------------------------------------------------- /_tools/date: -------------------------------------------------------------------------------- 1 | var helpersJS = require('users/fitoprincipe/geetools:helpers_js') 2 | 3 | /** DATES **/ 4 | 5 | var date = {} 6 | 7 | date.fromDOY = function(doy, year) { 8 | var less10 = function(doy) { 9 | doy = doy.toInt() 10 | return ee.String('00').cat(ee.Number(doy).format()) 11 | } 12 | 13 | var less100 = function(doy) { 14 | doy = doy.toInt() 15 | return ee.String('0').cat(ee.Number(doy).format()) 16 | } 17 | 18 | doy = ee.Number(doy).add(1).toInt() 19 | var year_str = ee.Number(year).format() 20 | var doy_str = ee.Algorithms.If( 21 | doy.lt(10), less10(doy), 22 | ee.String(ee.Algorithms.If( 23 | doy.lt(100), less100(doy), doy.format() 24 | )) 25 | ) 26 | var str = ee.String(doy_str).cat(year_str) 27 | 28 | return ee.Date.parse('DDDyyyy', str) 29 | } 30 | 31 | date.isLeap = function(year) { 32 | year = ee.Number(year) 33 | 34 | var divisible4 = year.mod(4).gt(0) 35 | var divisible100 = year.mod(100).gt(0) 36 | var divisible400 = year.mod(400).gt(0) 37 | 38 | var leap = ee.Algorithms.If( 39 | divisible4, 0, 40 | ee.Algorithms.If( 41 | divisible100, 1, 42 | ee.Algorithms.If( 43 | divisible400, 0, 1))) 44 | return ee.Number(leap) 45 | } 46 | 47 | date.makeDateBand = function(image, options) { 48 | 49 | var defaults = { 50 | dateFormat: 'YMMdd', 51 | bandName: 'date' 52 | } 53 | 54 | var opt = helpersJS.get_options(defaults, options) 55 | 56 | var f = ee.String(opt.dateFormat) 57 | // catch string formats for month 58 | var pattern = f.replace('(MMM+)', 'MM') 59 | 60 | var footprint = image.geometry() 61 | 62 | var idate = image.date().format(pattern) 63 | var idate_number = ee.Number.parse(idate) 64 | var date_band = ee.Image.constant(idate_number).rename(opt.bandName) 65 | return ee.Image(ee.Algorithms.If(footprint.isUnbounded(), date_band, 66 | date_band.clip(footprint))) 67 | } 68 | 69 | 70 | exports.date = date -------------------------------------------------------------------------------- /composite: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rodrigo E. Principe 3 | * License: Apache 2.0 4 | * email: fitoprincipe82@gmail.com 5 | 6 | Composite Methods 7 | */ 8 | 9 | var tools = require('users/fitoprincipe/geetools:tools') 10 | 11 | var euclidean_distance = function(i, i2) { 12 | var bandsi = i.bandNames() 13 | var bandsi2 = i2.bandNames() 14 | 15 | var proxy = tools.image.fromList(bandsi, 0) 16 | i = proxy.where(i.gt(0), i) 17 | i2 = proxy.where(i2.gt(0), i2) 18 | 19 | var a = i.subtract(i2) 20 | var b = a.pow(2) 21 | var c = b.reduce('sum') 22 | var d = c.sqrt() 23 | 24 | return d.rename('distance') 25 | } 26 | 27 | var sum_distance = function(i, rest) { 28 | var accum = ee.Image(0).rename('sumdist') 29 | return ee.Image(rest.iterate(function(im, ini) { 30 | ini = ee.Image(ini) 31 | im = ee.Image(im) 32 | var dist = ee.Image(euclidean_distance(i, im)).rename('sumdist') 33 | return ini.add(dist) 34 | }, accum)) 35 | } 36 | 37 | var medoid = function(collection, bands) { 38 | 39 | //collection = collection.map(function(im){return im.unmask()}) 40 | 41 | var first_image = ee.Image(collection.first()) 42 | var ibands = first_image.bandNames() 43 | 44 | bands = bands || ibands 45 | //collection = collection.select(bands) 46 | 47 | var enumerated = tools.imageCollection.enumerateProperty(collection) 48 | var collist = enumerated.toList(enumerated.size()) 49 | 50 | var imlist = ee.List(collist.map(function(im){ 51 | im = ee.Image(im) 52 | var n = ee.Number(im.get('enumeration')) 53 | var filtered = tools.list.removeIndex(collist, n) 54 | .map(function(im){ 55 | im = ee.Image(im) 56 | return im.select(bands) 57 | }) 58 | var dist = sum_distance( 59 | im.select(bands), 60 | filtered 61 | ).multiply(-1) 62 | return im.addBands(dist) 63 | })) 64 | 65 | var medcol = ee.ImageCollection.fromImages(imlist) 66 | return medcol.qualityMosaic('sumdist') 67 | } 68 | 69 | exports.medoid = medoid 70 | -------------------------------------------------------------------------------- /assetmanager/utils: -------------------------------------------------------------------------------- 1 | /** 2 | * Util functions for handling assets 3 | * Author: Rodrigo E. Principe 4 | * email: fitoprincipe82@gmail.com 5 | * license: MIT 6 | **/ 7 | 8 | var helpers = require('users/fitoprincipe/geetools:helpers_js') 9 | 10 | var getRoot = function(id) { 11 | if (id) { 12 | var splitted = id.split('/') 13 | return splitted[0]+'/'+splitted[1] 14 | } else { 15 | return ee.data.getAssetRoots()[0]['id'] 16 | } 17 | } 18 | exports.getRoot = getRoot 19 | 20 | var recrusiveDeleteAsset = function(assetId) { 21 | var info = ee.data.getInfo(assetId) 22 | if (info) { 23 | var ty = info['type'] 24 | if (ty === 'Image' || ty === 'FeatureCollection') { 25 | // setting content to 0 will delete the assetId 26 | var content = 0 27 | } else if (ty === 'Folder' || ty === 'ImageCollection') { 28 | try { 29 | var content = ee.data.getList({'id':assetId}) 30 | } catch(e) { 31 | print(e) 32 | return null 33 | } 34 | } else { 35 | print("Can't handle "+ty+" type yet") 36 | return null 37 | } 38 | 39 | if (content === 0) { 40 | // delete empty colletion and/or folder 41 | ee.data.deleteAsset(assetId) 42 | } else { 43 | for (var i in content) { 44 | var asset = content[i] 45 | var path = asset['id'] 46 | var ty = asset['type'] 47 | if (ty === 'Image') { 48 | // print('deleting {}'.format(path)) 49 | ee.data.deleteAsset(path) 50 | } else { 51 | recrusiveDeleteAsset(path) 52 | } 53 | } 54 | // delete empty collection and/or folder 55 | ee.data.deleteAsset(assetId) 56 | } 57 | } else { 58 | print(assetId+' does not exists or there is another problem') 59 | } 60 | } 61 | exports.recrusiveDeleteAsset = recrusiveDeleteAsset 62 | 63 | //recrusiveDeleteAsset('users/fitoprincipe/TEST/Folder/Collection') -------------------------------------------------------------------------------- /_tools/featurecollection: -------------------------------------------------------------------------------- 1 | var helpersJS = require('users/fitoprincipe/geetools:helpers_js') 2 | 3 | /** FEATURECOLLECTION **/ 4 | var featureCollection = {}; 5 | 6 | // GET UNIQUE PROPERTY VALUES 7 | featureCollection.propertyValues = function(col, property) { 8 | var collist = col.toList(col.size()) 9 | var newlist = collist.map(function(feat){ 10 | return ee.Feature(feat).get(property) 11 | }).removeAll([null]).removeAll([""]) 12 | return newlist 13 | } 14 | 15 | featureCollection.toDictionary = function(col, options) { 16 | var def = { 17 | properties: null, 18 | idField: null 19 | } 20 | var opt = helpersJS.get_options(def, options) 21 | 22 | var idField = opt.idField || 'system:index' 23 | 24 | var newdict = col.iterate(function(feat, ini){ 25 | ini = ee.Dictionary(ini) 26 | var f = ee.Feature(feat) 27 | var id = f.get(idField) 28 | if (opt.idField) { 29 | var props = f.toDictionary(opt.properties).remove([opt.idField]) 30 | } else { 31 | var props = f.toDictionary(opt.properties) 32 | } 33 | return ini.set(id, props) 34 | }, ee.Dictionary({})) 35 | return newdict 36 | } 37 | 38 | featureCollection.enumerateProperty = function(col, name) { 39 | name = name || 'enumeration' 40 | //var collist = col.toList(col.size()) 41 | var enumerated = collection.enumerate(col) 42 | 43 | var featlist = enumerated.map(function(l){ 44 | l = ee.List(l) 45 | var index = ee.Number(l.get(0)) 46 | var element = l.get(1) 47 | return ee.Feature(element).set(name, index) 48 | }) 49 | return ee.FeatureCollection(featlist) 50 | } 51 | 52 | featureCollection.paint = function(table, propertyName, styles) { 53 | var results = [] 54 | 55 | var keys = Object.keys(styles) 56 | for (var i=0; i 0) { 78 | self.sel.setPlaceholder('Select a value') 79 | } else { 80 | self.sel.setPlaceholder('No items') 81 | } 82 | if (value) { 83 | try { 84 | self.sel.setValue(value.getInfo(), trigger) 85 | } catch (e) { 86 | self.sel.setValue(value, trigger) 87 | } 88 | } 89 | } else { 90 | self.sel.setPlaceholder('Error') 91 | print(error) 92 | } 93 | } 94 | return wrap 95 | } 96 | 97 | asyncSelect.prototype.build = function() { 98 | return this.sel 99 | } 100 | 101 | exports.asyncSelect = asyncSelect -------------------------------------------------------------------------------- /ui/checkSelect: -------------------------------------------------------------------------------- 1 | /** 2 | * CheckSelect Widget 3 | * Author: Rodrigo E. Principe 4 | * email: fitoprincipe82@gmail.com 5 | * license: MIT 6 | **/ 7 | 8 | var helpers = require('users/fitoprincipe/geetools:helpers_js') 9 | 10 | var CheckSelect = function(options) { 11 | var def = { 12 | items: [], 13 | checkbox: { 14 | style: { 15 | padding: '8px 0px 8px 0px' 16 | } 17 | }, 18 | select: { 19 | style: { 20 | } 21 | }, 22 | panel: { 23 | style: { 24 | padding: '0px 0px 0px 0px' 25 | }, 26 | layout: ui.Panel.Layout.flow('horizontal') 27 | } 28 | } 29 | this.options = helpers.get_options(def, options) 30 | 31 | this.options.select.items = this.options.items 32 | 33 | this.checkbox = ui.Checkbox(this.options.checkbox) 34 | this.select = ui.Select(this.options.select) 35 | 36 | this.options.panel.widgets = [this.checkbox, this.select] 37 | 38 | this.panel = ui.Panel(this.options.panel) 39 | 40 | this.name = 'CheckSelect' 41 | 42 | // Inherit 43 | this.items = this.select.items() 44 | 45 | // Bindings 46 | this.select.onChange(this.selectCallback()) 47 | this.checkbox.onChange(this.checkCallback()) 48 | } 49 | 50 | // Getters 51 | CheckSelect.prototype.getLabel = function() { 52 | var value = this.select.getValue() 53 | // check if items is a list of objects or not 54 | var firstItem = this.items.get(0) 55 | var selection = null 56 | 57 | if (typeof(firstItem) !== 'object') { 58 | selection = value 59 | } else { 60 | // iterate over each element to find the associated value 61 | this.items.forEach(function(element, index) { 62 | var label = element.label 63 | var val = element.value 64 | if (value === val) {selection = label} 65 | }) 66 | } 67 | return selection 68 | } 69 | 70 | // onSelect 71 | CheckSelect.prototype.selectCallback = function() { 72 | var self = this 73 | var wrap = function(value, widget) { 74 | if (self.onSelectCallback) { 75 | self.onSelectCallback(value, self.checkbox.getValue(), widget, self.checkbox, self) 76 | } 77 | } 78 | return wrap 79 | } 80 | CheckSelect.prototype.onSelect = function(callback) { 81 | this.onSelectCallback = callback 82 | } 83 | 84 | // onCheck 85 | CheckSelect.prototype.checkCallback = function() { 86 | var self = this 87 | var wrap = function(value, widget) { 88 | if (self.onCheckCallback) { 89 | self.onCheckCallback(value, self.select.getValue(), widget, self.select, self) 90 | } 91 | } 92 | return wrap 93 | } 94 | CheckSelect.prototype.onCheck = function(callback) { 95 | this.onCheckCallback = callback 96 | } 97 | 98 | CheckSelect.prototype.build = function() { 99 | return this.panel 100 | } 101 | 102 | CheckSelect.prototype.addTo = function(widget) { 103 | widget.add(this.build()) 104 | this.parent = widget 105 | return widget 106 | } 107 | 108 | CheckSelect.prototype.insertTo = function(widget, position) { 109 | widget.insert(position, this.build()) 110 | this.parent = widget 111 | return widget 112 | } 113 | 114 | exports.CheckSelect = CheckSelect 115 | -------------------------------------------------------------------------------- /_tools/list: -------------------------------------------------------------------------------- 1 | var helpersJS = require('users/fitoprincipe/geetools:helpers_js') 2 | 3 | /** LIST */ 4 | var list = {}; 5 | 6 | // Sequence 7 | list.sequence = function(ini, end, step) { 8 | var step = step || 1 9 | var end = ee.Number(end) 10 | if (step === 0){ 11 | step = 1 12 | } 13 | var amplitude = end.subtract(ini) 14 | var mod = amplitude.mod(step) 15 | var seq = ee.List.sequence(ini, end, step) 16 | var condition = mod.neq(0) 17 | var final = ee.Algorithms.If(condition, seq.add(end), seq) 18 | 19 | return ee.List(final) 20 | } 21 | 22 | // Remove duplicate values 23 | list.removeDuplicates = function(list) { 24 | var newlist = ee.List([]); 25 | list = ee.List(list) 26 | var wrap = function(element, init) { 27 | init = ee.List(init); 28 | var contained = init.contains(element); 29 | 30 | return ee.Algorithms.If(contained, init, init.add(element)); 31 | }; 32 | return ee.List(list.iterate(wrap, newlist)); 33 | } 34 | 35 | // Intersection 36 | list.intersection = function(list1, list2) { 37 | var newlist = ee.List([]) 38 | var wrap = function(element, first) { 39 | first = ee.List(first) 40 | 41 | return ee.Algorithms.If(list2.contains(element), first.add(element), first) 42 | } 43 | return ee.List(list1.iterate(wrap, newlist)) 44 | } 45 | 46 | // Substract two lists 47 | list.difference = function(list1, list2) { 48 | return list1.removeAll(list2).add(list2.removeAll(list1)).flatten() 49 | } 50 | 51 | // Filter a list with a regex value 52 | list.filterRegex = function(list, regex, flags) { 53 | list = ee.List(list) 54 | var fl = flags || null 55 | var i = ee.List([]) 56 | var f = list.iterate(function(el, first){ 57 | var f = ee.List(first) 58 | var e = ee.String(el); 59 | var matched = ee.List(e.match(regex, fl)) 60 | return ee.Algorithms.If(matched.size().gt(0), f.add(el), f) 61 | }, i) 62 | return ee.List(f) 63 | } 64 | 65 | // Remove an element by its index number 66 | list.removeIndex = function(list, index) { 67 | list = ee.List(list) 68 | index = ee.Number(index) 69 | var size = list.size() 70 | 71 | var allowed = function() { 72 | var zerof = function(list) { 73 | return list.slice(1, list.size()) 74 | } 75 | 76 | var rest = function(list, index) { 77 | list = ee.List(list) 78 | index = ee.Number(index) 79 | var last = index.eq(list.size()) 80 | 81 | var lastf = function(list) { 82 | return list.slice(0, list.size().subtract(1)) 83 | } 84 | 85 | var restf = function(list, index) { 86 | list = ee.List(list) 87 | index = ee.Number(index) 88 | var first = list.slice(0, index) 89 | return first.cat(list.slice(index.add(1), list.size())) 90 | } 91 | return ee.List(ee.Algorithms.If(last, lastf(list), restf(list, index))) 92 | } 93 | 94 | return ee.List(ee.Algorithms.If(index, rest(list, index), zerof(list))) 95 | } 96 | 97 | var condition = index.gte(size).or(index.lt(0)) 98 | 99 | return ee.List(ee.Algorithms.If(condition, -1, allowed())) 100 | } 101 | 102 | exports.list = list -------------------------------------------------------------------------------- /ui/polygon: -------------------------------------------------------------------------------- 1 | /*** 2 | * Polygon Widget 3 | * Author: Rodrigo E. Principe 4 | * email: fitoprincipe82@gmail.com 5 | * licence: MIT 6 | ***/ 7 | 8 | var tools = require('users/fitoprincipe/geetools:tools') 9 | var helper = require('users/fitoprincipe/geetools:helpers_js') 10 | 11 | var Polygon = function(options) { 12 | 13 | // Arguments 14 | var def = { 15 | map: Map, 16 | map_callback: null, 17 | map_callback_id: null, 18 | } 19 | this.opt = tools.get_options(def, options) 20 | 21 | this.point_list = [] 22 | this.callback_ID = null 23 | this.drawing = null 24 | 25 | this.started = false 26 | } 27 | 28 | Polygon.prototype.start = function() { 29 | 30 | if (this.started) { 31 | print('There is a drawing already started, please stop it before a new drawing') 32 | return null 33 | } 34 | 35 | this.started = true 36 | 37 | // unbound callback if parsed 38 | if (this.opt.map_callback && this.opt.map_callback_id) { 39 | //print('on start', this.opt.map_callback_id) 40 | this.opt.map.unlisten(this.opt.map_callback_id) 41 | } 42 | 43 | this.point_list = [] 44 | this.uuid = helper.uuid4() 45 | 46 | var fc = ee.FeatureCollection([]) 47 | Map.addLayer(fc, null, this.uuid) 48 | 49 | var app = this 50 | 51 | var callback = function(coords) { 52 | var lon = coords.lon 53 | var lat = coords.lat 54 | var p = [lon, lat] 55 | app.point_list.push(p) 56 | if (app.point_list.length === 1) { 57 | var feat = ee.Feature(ee.Geometry.Point(p)) 58 | } 59 | else if (app.point_list.length === 2) { 60 | var feat = ee.Feature(ee.Geometry.LineString(app.point_list)) 61 | } 62 | else { 63 | var feat = ee.Feature(ee.Geometry.Polygon(app.point_list)) 64 | } 65 | 66 | var newfc = ee.FeatureCollection([feat]) 67 | var layer = tools.map.getLayer(app.uuid) 68 | var newlayer = ui.Map.Layer(newfc, null, app.uuid) 69 | tools.map.replaceLayer(layer, newlayer, app.opt.map) 70 | } 71 | 72 | this.opt.map.style().set('cursor', 'crosshair') 73 | this.callback_ID = this.opt.map.onClick(callback) 74 | } 75 | 76 | Polygon.prototype.stop = function() { 77 | 78 | // unbound drawing callback 79 | this.opt.map.unlisten(this.callback_ID) 80 | 81 | // bound callback again 82 | if (this.opt.map_callback && this.opt.map_callback_id) { 83 | this.opt.map_callback_id = this.opt.map.onClick(this.opt.map_callback) 84 | //print('on stop', this.opt.map_callback_id) 85 | } 86 | 87 | // remove temp layer 88 | tools.map.removeLayerByName(this.uuid, this.opt.map) 89 | 90 | if (this.point_list.length >= 3) { 91 | var polygon = ee.Geometry.Polygon(this.point_list) 92 | //Map.addLayer(polygon, null, this.opt.name) 93 | this.drawing = polygon 94 | } else { 95 | this.drawing = null 96 | print('The Polygon must have a minimum of 3 points') 97 | } 98 | this.opt.map.style().set('cursor', 'hand') 99 | this.started = false 100 | } 101 | 102 | Polygon.prototype.getDrawing = function() { 103 | return this.drawing 104 | } 105 | 106 | exports.Polygon = Polygon 107 | 108 | var test = function() { 109 | var pol = new Polygon() 110 | 111 | var start = ui.Button('Start') 112 | start.onClick(function(){pol.start()}) 113 | 114 | var stop = ui.Button('Stop') 115 | stop.onClick(function(){ 116 | pol.stop() 117 | print(pol.drawing) 118 | }) 119 | 120 | Map.add(start) 121 | Map.add(stop) 122 | } 123 | 124 | //test() -------------------------------------------------------------------------------- /indexes: -------------------------------------------------------------------------------- 1 | /*** 2 | * A Collection of Indexes 3 | * 4 | * Author: Rodrigo E. Principe 5 | * email: fitoprincipe82@gmail.com 6 | * License: MIT 7 | */ 8 | 9 | var help = {} 10 | 11 | var custom_ndvi = function(NIR, RED) { 12 | var wrap = function(img) { 13 | var nir = img.select([NIR]) 14 | var red = img.select([RED]) 15 | var ndvi = img.expression('(NIR-RED)/(NIR+RED)', {NIR: nir, RED:red}).rename('NDVI') 16 | return img.addBands(ndvi) 17 | } 18 | return wrap 19 | } 20 | exports.ndvi = custom_ndvi 21 | 22 | var custom_nbr = function(NIR, SWIR2) { 23 | var wrap = function(img) { 24 | var nir = img.select([NIR]) 25 | var swir2 = img.select([SWIR2]) 26 | var index = img.expression('(NIR-SWIR2)/(NIR+SWIR2)', {NIR: nir, SWIR2:swir2}).rename('NBR') 27 | return img.addBands(index) 28 | } 29 | return wrap 30 | } 31 | exports.nbr = custom_nbr 32 | 33 | var custom_nbr2 = function(SWIR, SWIR2) { 34 | var wrap = function(img) { 35 | var swir = img.select([SWIR]) 36 | var swir2 = img.select([SWIR2]) 37 | var index = img.expression('(SWIR-SWIR2)/(SWIR+SWIR2)', {SWIR: swir, SWIR2:swir2}).rename('NBR2') 38 | return img.addBands(index) 39 | } 40 | return wrap 41 | } 42 | exports.nbr2 = custom_nbr2 43 | 44 | var custom_evi = function(NIR, RED, BLUE) { 45 | var wrap = function(img) { 46 | var nir = img.select([NIR]) 47 | var red = img.select([RED]) 48 | var blue = img.select([BLUE]) 49 | var L = ee.Number(1) 50 | var C1 = ee.Number(6) 51 | var C2 = ee.Number(7.5) 52 | var index = img.expression('(NIR-RED)/(NIR+(C1*RED)-(C2*BLUE)+L)', 53 | {NIR: nir, 54 | RED: red, 55 | BLUE: blue, 56 | L: L, 57 | C1: C1, 58 | C2: C2 59 | }).rename('EVI') 60 | return img.addBands(index) 61 | } 62 | return wrap 63 | } 64 | exports.evi = custom_evi 65 | 66 | var custom_savi = function(NIR, RED, L) { 67 | L = ee.Number(L) 68 | var wrap = function(img) { 69 | var nir = img.select([NIR]) 70 | var red = img.select([RED]) 71 | 72 | var savi = img.expression( 73 | '((N - R)/(N + R + l))*(1 + l)', 74 | {'N': nir, 'R': red, 'l': L}).rename('SAVI') 75 | 76 | return img.addBands(savi) 77 | } 78 | return wrap 79 | } 80 | exports.savi = custom_savi 81 | 82 | var SAVI = { 83 | custom: custom_savi, 84 | sentinel2: function(L) {return custom_savi('B8', 'B4', L)}, 85 | landsat8: function(L) {return custom_savi('B5', 'B4', L)}, 86 | landsat457: function(L) {return custom_savi('B4', 'B3', L)}, 87 | MOD09GQ: function(L) {return custom_savi('sur_refl_b02', 'sur_refl_b01', L)}, 88 | } 89 | 90 | var NDVI = { 91 | custom: custom_ndvi, 92 | sentinel2: custom_ndvi('B8','B4'), 93 | landsat8: custom_ndvi('B5', 'B4'), 94 | landsat457: custom_ndvi('B4', 'B3'), 95 | MOD09GQ: custom_ndvi('sur_refl_b02', 'sur_refl_b01') 96 | } 97 | var NBR = { 98 | custom: custom_nbr, 99 | sentinel2: custom_nbr('B8','B12'), 100 | landsat8: custom_nbr('B5', 'B7'), 101 | landsat457: custom_nbr('B4', 'B7') 102 | } 103 | var NBR2 = { 104 | custom: custom_nbr2, 105 | sentinel2: custom_nbr2('B11','B12'), 106 | landsat8: custom_nbr2('B6', 'B7'), 107 | landsat457: custom_nbr2('B5', 'B7') 108 | } 109 | var EVI = { 110 | custom: custom_evi, 111 | sentinel2: custom_evi('B8','B4', 'B2'), 112 | landsat8: custom_evi('B5', 'B4', 'B2'), 113 | landsat457: custom_evi('B4', 'B3', 'B1') 114 | } 115 | 116 | help['NDVI'] = 'NDVI["custom"] returns the function that has NIR & RED as arguments\n' 117 | +'NDVI["sentinel2"] returns the function for Sentinel 2' 118 | 119 | exports.NDVI = NDVI 120 | exports.NBR = NBR 121 | exports.NBR2 = NBR2 122 | exports.EVI = EVI 123 | exports.SAVI = SAVI 124 | exports.help = help -------------------------------------------------------------------------------- /assetmanager/assetSelector: -------------------------------------------------------------------------------- 1 | /** 2 | * Asset selector widget 3 | * Author: Rodrigo E. Principe 4 | * email: fitoprincipe82@gmail.com 5 | * license: MIT 6 | **/ 7 | 8 | var helpers = require('users/fitoprincipe/geetools:helpers_js') 9 | var widgets = require('users/fitoprincipe/geetools:widgets') 10 | var utils = require('users/fitoprincipe/geetools:assetmanager/utils') 11 | var assetItems = require('users/fitoprincipe/geetools:assetmanager/assetItems') 12 | var CS = widgets.CheckSelect 13 | 14 | var AssetSelector = function(options) { 15 | 16 | var def = { 17 | root: null, 18 | title: true, 19 | panel: { 20 | style: { 21 | padding: '0px', 22 | }, 23 | } 24 | } 25 | this.options = helpers.get_options(def, options) 26 | this.root = this.options.root || utils.getRoot() 27 | 28 | // First (root) selector 29 | this.root_selector = new CS({ 30 | items: assetItems.getItems(this.root) 31 | }) 32 | this.root_selector.onSelect(this.callback()) 33 | 34 | // make proxy callback 35 | this.onCheckCallback = function() {} 36 | 37 | // set position & parent 38 | this.root_selector.position = 0 39 | this.root_selector.container = null 40 | 41 | // global Panel 42 | this.panel = ui.Panel(this.options.panel) 43 | 44 | // make a list of selectors 45 | this.selectors = [this.root_selector] 46 | 47 | // add selectors 48 | this.update() 49 | } 50 | 51 | AssetSelector.prototype.build = function() { 52 | // bind callback 53 | this.root_selector.onCheck(this.onCheckCallback) 54 | 55 | return this.panel 56 | } 57 | 58 | AssetSelector.prototype.addTo = function(widget) { 59 | widget.add(this.build()) 60 | this.parent = widget 61 | return widget 62 | } 63 | 64 | AssetSelector.prototype.insertTo = function(widget, position) { 65 | widget.insert(position, this.build()) 66 | this.parent = widget 67 | return widget 68 | } 69 | 70 | AssetSelector.prototype.getSelected = function() { 71 | // get a list of the selected (checked) assets 72 | var selected = [] 73 | for (var i in this.selectors) { 74 | var object = {} 75 | var select = this.selectors[i] 76 | if (select.checkbox.getValue()) { 77 | var label = select.getLabel() 78 | if (label) { 79 | var id = select.select.getValue() 80 | var type = label.split(' ')[0].slice(1,-1) 81 | object.id = id 82 | object.type = type 83 | selected.push(object) 84 | } 85 | } 86 | } 87 | return selected 88 | } 89 | 90 | AssetSelector.prototype.onCheck = function(callback) { 91 | this.onCheckCallback = callback 92 | } 93 | 94 | AssetSelector.prototype.update = function() { 95 | // Update the list of selectors using the list of selectors 96 | this.panel.clear() 97 | // panel title (user) 98 | if (this.options.title) { 99 | this.title = ui.Label({value:this.root}) 100 | this.panel.add(this.title) 101 | } 102 | // iterate over selectors 103 | for (var i in this.selectors) { 104 | var selector = this.selectors[i] 105 | selector.addTo(this.panel) 106 | } 107 | } 108 | 109 | AssetSelector.prototype.callback = function() { 110 | var self = this 111 | var wrap = function(value, check, select, checkbox, cs) { 112 | 113 | // Remake id and get type 114 | var label = cs.getLabel() 115 | var splitted = label.split(' ') 116 | var type = splitted[0] 117 | type = type.slice(1, type.length-1) 118 | 119 | var id = value 120 | 121 | if (type === 'IMAGE' || type === 'TABLE') { 122 | // clear old selectors 123 | self.selectors = self.selectors.slice(0, cs.position+1) 124 | // add selectors to panel 125 | self.update() 126 | } else { 127 | 128 | // Make new selector 129 | var items = assetItems.getItems(id) 130 | var selector = new CS({ 131 | items:items, 132 | }) 133 | // set properties to selector 134 | selector.position = cs.position + 1 135 | selector.container = cs 136 | // set callbacks 137 | selector.onSelect(self.callback()) 138 | selector.onCheck(self.onCheckCallback) 139 | // add selector to global list 140 | self.selectors[selector.position] = selector 141 | // clear old selectors 142 | self.selectors = self.selectors.slice(0, selector.position+1) 143 | // add selectors to panel 144 | self.update() 145 | } 146 | } 147 | return wrap 148 | } 149 | 150 | exports.AssetSelector = AssetSelector -------------------------------------------------------------------------------- /_tools/map: -------------------------------------------------------------------------------- 1 | var helpersJS = require('users/fitoprincipe/geetools:helpers_js') 2 | var image_module = require('users/fitoprincipe/geetools:_tools/image').image 3 | 4 | /** MAP */ 5 | var map = {}; 6 | 7 | // ADD COLLECTION TO MAP 8 | map.addImageCollection = function(col, options) { 9 | 10 | var def = { 11 | namePattern : '{id}', 12 | datePattern : 'yyyy-MM-dd', 13 | visParams : {min:0, max:1}, 14 | active : false 15 | } 16 | var opt = helpersJS.get_options(def, options) 17 | 18 | var list = col.toList(col.size()) 19 | var i = 0 20 | 21 | while (i >= 0) { 22 | try { 23 | var img = ee.Image(list.get(i)) 24 | var name = image_module.makeName(img, opt.namePattern, opt.datePattern) 25 | Map.addLayer(img, opt.visParams, name.getInfo(), opt.active) 26 | i++ 27 | } catch (error) { 28 | return null 29 | } 30 | } 31 | } 32 | 33 | // Remove Layer 34 | // https://gis.stackexchange.com/questions/291199/google-earth-engine-remove-layer-from-the-layer-manager 35 | /* 36 | map.removeLayerByName = function(name, map) { 37 | var m = map || Map 38 | var layers = m.layers() 39 | // list of layers names 40 | var names = [] 41 | layers.forEach(function(lay) { 42 | var lay_name = lay.getName() 43 | names.push(lay_name) 44 | }) 45 | // get index 46 | var index = names.indexOf(name) 47 | if (index > -1) { 48 | // if name in names 49 | var layer = layers.get(index) 50 | m.remove(layer) 51 | } else { 52 | print('Layer '+name+' not found') 53 | } 54 | } 55 | */ 56 | map.removeLayerByName = function(name, map) { 57 | var m = map || Map 58 | var layers = m.layers().getJsArray() 59 | var removedIndexes = [] 60 | for (var i in layers) { 61 | var lay = layers[i] 62 | var lay_name = lay.getName() 63 | if (lay_name === name) { 64 | m.remove(lay) 65 | removedIndexes.push(Number(i)) 66 | } 67 | } 68 | return removedIndexes 69 | } 70 | 71 | map.removeAllLayersByName = map.removeLayerByName 72 | 73 | map.removeLayer = function(layer, map) { 74 | var m = map || Map 75 | var layers = m.layers().getJsArray() 76 | var removed = [] 77 | for (var i in layers) { 78 | var lay = layers[i] 79 | if (lay === layer) { 80 | m.remove(lay) 81 | removed.push(Number(i)) 82 | } 83 | } 84 | return removed 85 | } 86 | 87 | map.replaceLayer = function(layer, newlayer, map) { 88 | if (layer !== newlayer) { 89 | var m = map || Map 90 | var layers = m.layers() 91 | var layersjs = layers.getJsArray() 92 | var replaced = [] 93 | for (var i in layersjs) { 94 | var lay = layersjs[i] 95 | if (lay === layer) { 96 | var i = layersjs.indexOf(layer) 97 | layers.set(i, newlayer) 98 | replaced.push(Number(i)) 99 | } 100 | } 101 | } 102 | return replaced 103 | } 104 | 105 | map.removeObject = function(eeobject, map) { 106 | // remove every added object in the map 107 | // return a list of the indexes removed 108 | var m = map || Map 109 | var layers = m.layers() 110 | var layersJS = layers.getJsArray() 111 | var removedIndexes = [] 112 | 113 | for (var i in layersJS) { 114 | var layer = layersJS[i] 115 | var object = layer.getEeObject() 116 | if (object === eeobject) { 117 | m.remove(layer) 118 | removedIndexes.push(Number(i)) 119 | } 120 | } 121 | return removedIndexes 122 | } 123 | 124 | map.getLayer = function(name, map) { 125 | var m = map || Map 126 | var layers = m.layers() 127 | var lay = null 128 | var l = layers.length() 129 | for (var i=0; i=0) { 115 | try { 116 | var image = ee.Image(list.get(i)) 117 | var n = image_module.makeName(image, namePattern, properties, datePattern) 118 | var imap = image.getMap(visParams) 119 | var idtok = imap['mapid'] 120 | idtok = idtok.split('/')[3] 121 | var mapid = idtok.split('-')[0] 122 | var token = idtok.split('-')[1] 123 | params.push(n.getInfo()) 124 | params.push(mapid) 125 | params.push(token) 126 | i++ 127 | } catch (error) { 128 | //print(error.message) 129 | break 130 | } 131 | } 132 | var data = params.join(',') 133 | var url = helpersJS.string.format(baseurl, {name: name, data: data}) 134 | return url 135 | } 136 | 137 | imageCollection.outliers = function(collection, options) { 138 | //Compute outliers by: 139 | 140 | //outlier > mean+(sigma*stddev) 141 | //outlier < mean+(sigma*stddev) 142 | 143 | //if `updateMask` is False return the passed collection in which each image 144 | //have new bands (a mask) corresponding to the passed dict and a suffix '_outlier' 145 | //else return the passed collection with the passed bands masked if are 146 | //outliers (the outlier band is not returned). 147 | 148 | //idea from: https://www.kdnuggets.com/2017/02/removing-outliers-standard-deviation-python.html 149 | var def = { 150 | bands: null, 151 | sigma: 2, 152 | updateMask: false 153 | } 154 | var opt = helpersJS.get_options(def, options) 155 | var bands = opt.bands || ee.Image(collection.first()).bandNames() 156 | bands = ee.List(bands) 157 | var forstats = collection.select(bands) 158 | var mean = forstats.mean() 159 | var stddev = forstats.reduce(ee.Reducer.stdDev()) 160 | var imin = mean.subtract(stddev.multiply(opt.sigma)) 161 | var imax = mean.add(stddev.multiply(opt.sigma)) 162 | 163 | var getOutlier = function(im, imin, imax) { 164 | var ismin = im.lt(imin) 165 | var ismax = im.gt(imax) 166 | var outlier = ismin.or(ismax) 167 | return outlier 168 | } 169 | 170 | var overcol = function(im) { 171 | var outs = getOutlier(im.select(bands), imin, imax) 172 | if (opt.updateMask) { 173 | var ibands = im.select(bands) 174 | ibands = ibands.updateMask(outs.not()) 175 | } else { 176 | var ibands = image_module.addSuffix('_outlier')(outs) 177 | } 178 | 179 | return im.addBands({srcImg:ibands, overwrite:true}) 180 | } 181 | 182 | return collection.map(overcol) 183 | } 184 | 185 | exports.imageCollection = imageCollection -------------------------------------------------------------------------------- /ui/drawingPanel: -------------------------------------------------------------------------------- 1 | /*** 2 | * DrawingPanel Widget 3 | * Author: Rodrigo E. Principe 4 | * email: fitoprincipe82@gmail.com 5 | * licence: MIT 6 | * web: https://github.com/fitoprincipe/geetools-code-editor/wiki/DrawingPanel 7 | ***/ 8 | 9 | var tools = require('users/fitoprincipe/geetools:tools') 10 | var helper = require('users/fitoprincipe/geetools:helpers_js') 11 | var Polygon = require('users/fitoprincipe/geetools:ui/polygon').Polygon 12 | 13 | var options = { 14 | 'polygon': Polygon, 15 | } 16 | 17 | var DrawingPanel = function(options) { 18 | 19 | // Parsed arguments 20 | var def = { 21 | width: 300, 22 | height: 100, 23 | border: '1px solid black', 24 | position: 'top-center', 25 | layout: ui.Panel.Layout.flow('vertical'), 26 | drawings: ['polygon'], 27 | map: Map, 28 | map_callback: null, 29 | map_callback_id: null, 30 | title: null, 31 | } 32 | this.opt = tools.get_options(def, options) 33 | 34 | // Width and Height PixelSize 35 | var width = new helper.PixelSize(this.opt.width) 36 | var height = new helper.PixelSize(this.opt.height) 37 | 38 | if (width.number < 150) { 39 | print('Width of the widget must be greater than 150') 40 | return null 41 | } 42 | 43 | // Select drawing Dropdown 44 | this.type_drop = ui.Select({ 45 | items: this.opt.drawings, 46 | value: this.opt.drawings[0], 47 | }) 48 | 49 | // Start drawing Button 50 | this.start_button = ui.Button({ 51 | label: 'start', 52 | style: { 53 | width: 20, 54 | } 55 | }) 56 | 57 | // Stop drawing Button 58 | this.stop_button = ui.Button({ 59 | label: 'stop', 60 | style: { 61 | width: 20, 62 | } 63 | }) 64 | 65 | // Compute pixel size for name textbox 66 | var size_name = width.subtract(100).subtract(20) 67 | 68 | // Name Textbox 69 | this.drawing_name_textbox = ui.Textbox({ 70 | style: { 71 | width: size_name.value(), 72 | } 73 | }) 74 | 75 | // Panel for: name, start, stop 76 | this.add_panel = ui.Panel({ 77 | widgets: [this.drawing_name_textbox, this.start_button, this.stop_button], 78 | layout: ui.Panel.Layout.flow('horizontal') 79 | }) 80 | 81 | // List of widgets 82 | var widgets = [ 83 | this.type_drop, 84 | this.add_panel, 85 | ] 86 | 87 | // Add Title if parsed 88 | if (this.opt.title) { 89 | var title_text = ui.Label(this.opt.title) 90 | widgets = helper.array.insert(widgets, 0, title_text) 91 | } 92 | 93 | // Main Panel 94 | this.panel = ui.Panel({ 95 | widgets: widgets, 96 | style: { 97 | width: this.opt.width, 98 | height: this.opt.height, 99 | position: this.opt.position, 100 | border: this.opt.border 101 | }, 102 | layout: this.opt.layout 103 | }) 104 | 105 | // Scope variables 106 | this.parent = null 107 | this.draw = null 108 | this.drawing_name = null // actual drawing name 109 | this.drawings = {} // Object to store drawings with their names 110 | this.onStopCallback = null 111 | this.onStartCallback = null 112 | this.started = false 113 | this.errors = [] 114 | } 115 | 116 | DrawingPanel.prototype.start = function() { 117 | var app = this 118 | var wrap = function() { 119 | // Check if already started 120 | if (app.started) { 121 | var error = 'There is a drawing already started, please stop it before a new drawing' 122 | app.errors.push(error) 123 | print(error) 124 | return null 125 | } 126 | // Check if there is a name 127 | var name = app.drawing_name_textbox.getValue() 128 | if (!name) { 129 | var error = 'You must provide a name' 130 | app.errors.push(error) 131 | print(error) 132 | return null 133 | } 134 | // set drawing name 135 | app.drawing_name = name 136 | // Bound user onStart callback 137 | if (app.onStartCallback) { 138 | app.onStartCallback(app) 139 | } 140 | // Get type of drawing 141 | var toDraw = app.type_drop.getValue() 142 | var Draw = options[toDraw] 143 | // Before starting a new Drawing, if there is a previous one, get the callback ID 144 | // of the latter 145 | if (app.draw) { 146 | app.opt.map_callback_id = app.draw.opt.map_callback_id 147 | } 148 | // make a new Drawing 149 | app.draw = new Draw({ 150 | map: app.map, 151 | //name: app.drawing_name.getValue(), 152 | map_callback: app.opt.map_callback, 153 | map_callback_id: app.opt.map_callback_id, 154 | }) 155 | app.draw.start() 156 | app.started = true 157 | 158 | } 159 | return wrap 160 | } 161 | 162 | DrawingPanel.prototype.stop = function() { 163 | var app = this 164 | var wrap = function() { 165 | if (app.started) { // If the drawing has started 166 | // Stop drawing 167 | app.draw.stop() 168 | // Get drawing geometry 169 | var drawing = app.draw.getDrawing() 170 | // Get drawing name 171 | var drawing_name = app.drawing_name_textbox.getValue() 172 | // Store drawing 173 | if (drawing) { 174 | //app.drawings[drawing_name] = drawing // I don't know why this is not working... 175 | app.drawings = helper.object.insert(app.drawings, drawing_name, drawing) 176 | // call user onStop callback 177 | } 178 | if (app.onStopCallback) { 179 | try { 180 | app.onStopCallback(app, drawing) 181 | } catch (error) { 182 | print("Something went wrong in 'onStop' callback:\n"+error.message) 183 | } 184 | } 185 | app.started = false 186 | } 187 | else { 188 | return null 189 | } 190 | } 191 | return wrap 192 | } 193 | 194 | DrawingPanel.prototype.onStop = function(callback) { 195 | this.onStopCallback = callback || null 196 | } 197 | 198 | DrawingPanel.prototype.onStart = function(callback) { 199 | this.onStartCallback = callback || null 200 | } 201 | 202 | DrawingPanel.prototype.build = function() { 203 | // bind callbacks 204 | this.start_button.onClick(this.start()) 205 | this.stop_button.onClick(this.stop()) 206 | 207 | return this.panel 208 | } 209 | 210 | DrawingPanel.prototype.addTo = function(widget) { 211 | widget.add(this.build()) 212 | this.parent = widget 213 | return widget 214 | } 215 | 216 | DrawingPanel.prototype.insertTo = function(widget, position) { 217 | widget.insert(position, this.build()) 218 | this.parent = widget 219 | return widget 220 | } 221 | 222 | exports.DrawingPanel = DrawingPanel 223 | 224 | var test = function() { 225 | var panel = new DrawingPanel() 226 | panel.addTo(Map) 227 | 228 | var printResult = function(dp, polygon) { 229 | 230 | print(polygon) 231 | Map.addLayer(polygon) 232 | } 233 | panel.onStop(printResult) 234 | } 235 | 236 | //test() -------------------------------------------------------------------------------- /ui/ImageSlider: -------------------------------------------------------------------------------- 1 | var tools = require('users/fitoprincipe/geetools:tools') 2 | var helpers = require('users/fitoprincipe/geetools:helpers_js') 3 | 4 | var ImageSlider = function(collection, options) { 5 | var def = { 6 | start: null, 7 | end: null, 8 | title: 'ImageSlider', 9 | width: 200, 10 | mosaic: true, 11 | site: null, 12 | map: Map 13 | } 14 | 15 | this.options = helpers.get_options(def, options) 16 | var ordered = collection.sort('system:time_start', true) 17 | this.collection = ordered 18 | 19 | if (!this.options.start) { 20 | var first = ordered.first() 21 | this.options.start = first.date().format().getInfo() 22 | } 23 | if (!this.options.end) { 24 | var last = ee.Image(ordered.toList(ordered.size()).get(-1)) 25 | this.options.end = last.date().format().getInfo() 26 | } 27 | 28 | this.slider = ui.DateSlider(this.options.start, this.options.end) 29 | 30 | this.logger = ui.Label('') 31 | this.logger.style().set('color', 'red') 32 | 33 | this.panel = ui.Panel({ 34 | widgets: [ui.Label(this.options.title), this.logger, this.slider] 35 | }) 36 | 37 | this.setWidth(this.options.width) 38 | this.images = {} 39 | } 40 | 41 | ImageSlider.prototype.build = function() { 42 | return this.panel 43 | } 44 | 45 | ImageSlider.prototype.addTo = function(widget) { 46 | widget.add(this.build()) 47 | this.parent = widget 48 | return widget 49 | } 50 | 51 | ImageSlider.prototype.insertTo = function(widget, position) { 52 | widget.insert(position, this.build()) 53 | this.parent = widget 54 | return widget 55 | } 56 | 57 | ImageSlider.prototype.setWidth = function(width) { 58 | var ps = new helpers.PixelSize(width) 59 | this.panel.style().set('width', ps.value()) 60 | var sliderwidth = ps.subtract(30).value() 61 | this.slider.style().set('width', sliderwidth) 62 | } 63 | 64 | ImageSlider.prototype.getDate = function() { 65 | return this.slider.getValue()[0] 66 | } 67 | 68 | ImageSlider.prototype.getFiltered = function(collection, date, site, strategy, keepCurrent) { 69 | var filtered = collection.filterBounds(site) 70 | date = ee.Date(date) 71 | var self = this 72 | if (strategy === 'exact') { 73 | var start = date 74 | } else if (strategy === 'back') { 75 | if (keepCurrent) { 76 | filtered = filtered.filterDate('1970-01-01', date.advance(1, 'day')) 77 | } else { 78 | filtered = filtered.filterDate('1970-01-01', date) 79 | } 80 | var sorted = filtered.sort('system:time_start', false) 81 | var first = ee.Image(sorted.first()) 82 | var start = first.date() 83 | } else if (strategy === 'forth') { 84 | if (keepCurrent) { 85 | filtered = filtered.filterDate(date, date.advance(1, 'month')) 86 | } else { 87 | filtered = filtered.filterDate(date.advance(1, 'day'), date.advance(1, 'month')) 88 | } 89 | var sorted = filtered.sort('system:time_start', true) 90 | var first = ee.Image(sorted.first()) 91 | var start = first.date() 92 | } else if (strategy === 'closest') { 93 | var image = tools.imageCollection.getClosestTo(filtered, date) 94 | var start = image.date() 95 | } 96 | var end = start.advance(1, 'day') 97 | filtered = filtered.filterDate(start, end) 98 | 99 | return [filtered, start] 100 | } 101 | 102 | ImageSlider.prototype.getImage = function(options) { 103 | // date param must be in milliseconds 104 | 105 | this.logger.setValue('Loading image...') 106 | 107 | var def = { 108 | date: this.getDate(), 109 | site: this.options.site, 110 | mosaic: this.options.mosaic, 111 | strategy: 'exact', 112 | keepCurrent: false 113 | } 114 | var opt = helpers.get_options(def, options) 115 | 116 | var site = opt.site || tools.map.getBounds(this.map) 117 | 118 | var self = this 119 | var proxy = ee.Image(0).selfMask() 120 | 121 | var getImage = function(filt) { 122 | var filtered = filt[0] 123 | var date = filt[1] 124 | 125 | date = ee.Date.fromYMD(date.get('year'), date.get('month'), date.get('day')) 126 | 127 | var size = filtered.size() 128 | if (opt.mosaic) { 129 | var geom = tools.imageCollection.mergeGeometries(filtered) 130 | var image = filtered.mosaic().set('system:time_start', date.millis()).clip(geom) 131 | } else { 132 | var image = filtered.first() 133 | } 134 | image = ee.Image(ee.Algorithms.If(size, image, proxy)) 135 | date.millis().evaluate(function(d){ 136 | self.slider.setValue(d) 137 | self.logger.setValue('Image loaded') 138 | }) 139 | return image 140 | } 141 | 142 | if (this.options.site) { // storte in images dict 143 | if (this.images[opt.date]) { 144 | return this.images[opt.date] 145 | } else { 146 | var filtered = this.getFiltered(this.collection, opt.date, site, opt.strategy, opt.keepCurrent) 147 | var image = getImage(filtered) 148 | this.images[opt.date] = image 149 | } 150 | } else { 151 | var filtered = this.getFiltered(this.collection, opt.date, site, opt.strategy, opt.keepCurrent) 152 | var image = getImage(filtered) 153 | } 154 | return image 155 | } 156 | 157 | ImageSlider.prototype.getForward = function(options) { 158 | var def = { 159 | site: this.options.site, 160 | mosaic: this.options.mosaic, 161 | keepCurrent: false 162 | } 163 | var opt = helpers.get_options(def, options) 164 | 165 | return this.getImage({site: opt.site, mosaic: opt.mosaic, strategy:'forth', keepCurrent: opt.keepCurrent}) 166 | } 167 | 168 | ImageSlider.prototype.getBackward = function(options) { 169 | var def = { 170 | site: this.options.site, 171 | mosaic: this.options.mosaic, 172 | keepCurrent: false 173 | } 174 | var opt = helpers.get_options(def, options) 175 | 176 | return this.getImage({site: opt.site, mosaic: opt.mosaic, strategy:'back', keepCurrent: opt.keepCurrent}) 177 | } 178 | 179 | ImageSlider.prototype.getClosest = function(options) { 180 | var def = { 181 | site: this.options.site, 182 | mosaic: this.options.mosaic, 183 | keepCurrent: false 184 | } 185 | var opt = helpers.get_options(def, options) 186 | 187 | return this.getImage({site: opt.site, mosaic: opt.mosaic, strategy:'closest', keepCurrent: opt.keepCurrent}) 188 | } 189 | 190 | exports.ImageSlider = ImageSlider 191 | 192 | var test = function() { 193 | var col = ee.ImageCollection('COPERNICUS/S2') 194 | var slider = new ImageSlider(col, {start: '2018-01-01', end:'2018-05-01'}) 195 | slider.setWidth(500) 196 | slider.addTo(Map) 197 | var but = ui.Button('closest') 198 | var f = ui.Button('forth') 199 | var b = ui.Button('back') 200 | 201 | but.onClick(function() { 202 | var i = slider.getClosest() 203 | if (i) { 204 | Map.addLayer(i, {bands:['B4', 'B3', 'B2'], min:0, max:5000}, i.date().format().getInfo()) 205 | } else { print('no images') } 206 | }) 207 | f.onClick(function() { 208 | var i = slider.getForward({keepCurrent:true}) 209 | if (i) { 210 | Map.addLayer(i, {bands:['B4', 'B3', 'B2'], min:0, max:5000}, i.date().format().getInfo()) 211 | } else { print('no images') } 212 | }) 213 | b.onClick(function() { 214 | var i = slider.getBackward({keepCurrent:false}) 215 | if (i) { 216 | Map.addLayer(i, {bands:['B4', 'B3', 'B2'], min:0, max:5000}, i.date().format().getInfo()) 217 | } else { print('no images') } 218 | }) 219 | Map.add(but) 220 | Map.add(b) 221 | Map.add(f) 222 | } 223 | 224 | //test() 225 | -------------------------------------------------------------------------------- /batch: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rodrigo E. Principe 3 | * License: Apache 2.0 4 | * email: fitoprincipe82@gmail.com 5 | 6 | Some batch useful tools 7 | */ 8 | 9 | var tools = require('users/fitoprincipe/geetools:tools') 10 | var helpers = require('users/fitoprincipe/geetools:helpers_js') 11 | 12 | var getRegion = function(object, bounds) { 13 | bounds = bounds || false 14 | try { 15 | var name = object.name() 16 | if (name in ['Image', 'Feature', 'ImageCollection', 'FeatureCollection']) { 17 | var geom = object.geometry() 18 | } else { 19 | var geom = object 20 | } 21 | if (bounds) { 22 | geom = geom.bounds() 23 | } 24 | return geom 25 | } catch(err) { 26 | print(err.message) 27 | return object 28 | } 29 | } 30 | 31 | exports.getRegion = getRegion 32 | 33 | /* 34 | var getRegion = function(object) { 35 | try { 36 | var name = object.name() 37 | if (name === 'Image' || name === 'Feature') { 38 | var geom = object.geometry() 39 | } else { 40 | var geom = object 41 | } 42 | var coords = geom.getInfo()['coordinates'] 43 | return helpers.array.toString(coords) 44 | 45 | } catch(err) { 46 | print(err) 47 | return object 48 | } 49 | 50 | } 51 | */ 52 | 53 | // TASK CLASS 54 | var Task = function(taskId, config) { 55 | this.id = taskId 56 | this.config = config 57 | } 58 | 59 | Task.prototype.start = function() { 60 | ee.data.startProcessing(this.id, this.config) 61 | } 62 | 63 | var IMAGE_TYPES = function(img, type) { 64 | var types = { "float":img.toFloat(), 65 | "byte":img.toByte(), 66 | "int":img.toInt(), 67 | "double":img.toDouble(), 68 | "long": img.toLong(), 69 | "short": img.toShort(), 70 | "int8": img.toInt8(), 71 | "int16": img.toInt16(), 72 | "int32": img.toInt32(), 73 | "int64": img.toInt64(), 74 | "uint8": img.toUint8(), 75 | "uint16": img.toUint16(), 76 | "uint32": img.toUint32()} 77 | 78 | return types[type] 79 | } 80 | 81 | var Download = {'ImageCollection': {}, 'Table': {}, 'Image':{}} 82 | 83 | Download.ImageCollection.toAsset = function(collection, assetFolder, options) { 84 | var root = ee.data.getAssetRoots()[0]['id'] 85 | var folder = assetFolder 86 | if (folder !== null && folder !== undefined) { 87 | var assetFolder = root+'/'+folder+'/' 88 | } else { 89 | var assetFolder = root+'/' 90 | } 91 | 92 | var defaults = { 93 | name: null, 94 | scale: 1000, 95 | maxPixels: 1e13, 96 | region: null 97 | } 98 | 99 | var opt = tools.get_options(defaults, options) 100 | var n = collection.size().getInfo(); 101 | 102 | var colList = collection.toList(n); 103 | 104 | for (var i = 0; i < n; i++) { 105 | var img = ee.Image(colList.get(i)); 106 | var id = img.id().getInfo() || 'image_'+i.toString(); 107 | var region = opt.region || img.geometry().bounds().getInfo()["coordinates"]; 108 | var assetId = assetFolder+id 109 | 110 | Export.image.toAsset({ 111 | image: img, 112 | description: id, 113 | assetId: assetId, 114 | region: region, 115 | scale: opt.scale, 116 | maxPixels: opt.maxPixels}) 117 | } 118 | } 119 | 120 | Download.ImageCollection.toDrive = function(collection, folder, options) { 121 | 122 | var defaults = { 123 | scale: 1000, 124 | maxPixels: 1e13, 125 | type: 'float', 126 | region: null, 127 | name: '{id}', 128 | crs: null, 129 | dateFormat: 'yyyy-MM-dd', 130 | async: false 131 | } 132 | var opt = tools.get_options(defaults, options) 133 | var colList = collection.toList(collection.size()); 134 | 135 | var wrap = function(n, img) { 136 | var geom = opt.region || img.geometry() 137 | var region = getRegion(geom) 138 | var imtype = IMAGE_TYPES(img, opt.type) 139 | var description = helpers.string.formatTask(n) 140 | var params = { 141 | image: imtype, 142 | description: description, 143 | folder: folder, 144 | fileNamePrefix: n, 145 | region: region, 146 | scale: opt.scale, 147 | maxPixels: opt.maxPixels 148 | } 149 | if (opt.crs) { 150 | params.crs = opt.crs 151 | } 152 | Export.image.toDrive(params) 153 | } 154 | 155 | var i = 0 156 | while (i >= 0) { 157 | try { 158 | var img = ee.Image(colList.get(i)); 159 | var n = tools.image.makeName(img, opt.name, opt.dateFormat).getInfo() 160 | wrap(n, img) 161 | i++ 162 | } catch (err) { 163 | var msg = err.message 164 | if (msg.slice(0, 36) === 'List.get: List index must be between') { 165 | break 166 | } else { 167 | print(msg) 168 | break 169 | } 170 | } 171 | } 172 | } 173 | 174 | Download.Table.toAsset = function(collection, options) { 175 | var def = { 176 | description: 'myExportTableTask', 177 | assetId: null 178 | } 179 | var opt = tools.get_options(def, options) 180 | 181 | var full_config = { 182 | 'type': 'EXPORT_FEATURES', 183 | 'json': collection.serialize(), 184 | 'description': opt.description, 185 | 'state': 'UNSUBMITTED', 186 | } 187 | full_config.assetId = opt.assetId 188 | var task = new Task(ee.data.newTaskId()[0], full_config) 189 | return task 190 | } 191 | 192 | Download.Table.toDrive = function(collection, options) { 193 | var def = { 194 | description: 'myExportTableTask', 195 | fileNamePrefix: 'myExportTableTask', 196 | folder: null, 197 | fileFormat: 'CSV' 198 | } 199 | var opt = tools.get_options(def, options) 200 | 201 | var full_config = { 202 | 'type': 'EXPORT_FEATURES', 203 | 'json': collection.serialize(), 204 | 'description': opt.description, 205 | 'state': 'UNSUBMITTED', 206 | 'driveFileNamePrefix': opt['fileNamePrefix'], 207 | 'driveFolder': opt['folder'], 208 | 'fileFormat': opt['fileFormat'] 209 | } 210 | var task = new Task(ee.data.newTaskId()[0], full_config) 211 | return task 212 | } 213 | 214 | Download.Image.toAsset = function(image, options) { 215 | var def = { 216 | description: 'myExportImageTask', 217 | assetId: null, 218 | region: image, 219 | scale: image.projection().nominalScale().getInfo() 220 | } 221 | var opt = tools.get_options(def, options) 222 | 223 | var full_config = { 224 | 'type': 'EXPORT_IMAGE', 225 | 'json': image.serialize(), 226 | 'description': opt.description, 227 | 'state': 'UNSUBMITTED', 228 | 'region': getRegion(opt.region), 229 | 'scale': opt.scale 230 | } 231 | full_config.assetId = opt.assetId 232 | var task = new Task(ee.data.newTaskId()[0], full_config) 233 | return task 234 | } 235 | 236 | Download.Image.toDrive = function(image, options) { 237 | var def = { 238 | description: 'myExportImageTask', 239 | fileNamePrefix: 'myExportImageTask', 240 | folder: null, 241 | fileFormat: 'GeoTIFF', 242 | region: image, 243 | scale: image.projection().nominalScale().getInfo() 244 | } 245 | var opt = tools.get_options(def, options) 246 | 247 | var full_config = { 248 | 'type': 'EXPORT_IMAGE', 249 | 'json': image.serialize(), 250 | 'description': opt.description, 251 | 'state': 'UNSUBMITTED', 252 | 'driveFileNamePrefix': opt['fileNamePrefix'], 253 | 'driveFolder': opt['folder'], 254 | 'fileFormat': opt['fileFormat'], 255 | 'scale': opt.scale, 256 | 'region': getRegion(opt.region) 257 | } 258 | var task = new Task(ee.data.newTaskId()[0], full_config) 259 | return task 260 | } 261 | 262 | exports.Download = Download -------------------------------------------------------------------------------- /helpers_js: -------------------------------------------------------------------------------- 1 | /** 2 | * JavaScript Helpers 3 | * 4 | * Author: Rodrigo E. Principe 5 | * email: fitoprincipe82@gmail.com 6 | * license: MIT 7 | **/ 8 | 9 | var PixelSize = function(number) { 10 | if (object.is(number, 'Number')) { 11 | this.number = Number(number) 12 | } else { 13 | this.number = Number(number.replace('px', '')) 14 | } 15 | 16 | this.value = function() { 17 | return this.number.toString()+'px' 18 | } 19 | 20 | this.add = function(another) { 21 | var other = another.number || another 22 | return new PixelSize(this.number + other) 23 | } 24 | 25 | this.multiply = function(another) { 26 | var other = another.number || another 27 | return new PixelSize(this.number * other) 28 | } 29 | 30 | this.divide = function(another) { 31 | var other = another.number || another 32 | return new PixelSize(this.number / other) 33 | } 34 | 35 | this.subtract = function(another) { 36 | var other = another.number || another 37 | return new PixelSize(this.number - other) 38 | } 39 | } 40 | exports.PixelSize = PixelSize 41 | 42 | // UUID4 43 | // from https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript#answer-21963136 44 | var uuid4 = function() { 45 | var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); } 46 | var d0 = Math.random()*0x100000000|0; 47 | var d1 = Math.random()*0x100000000|0; 48 | var d2 = Math.random()*0x100000000|0; 49 | var d3 = Math.random()*0x100000000|0; 50 | return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'-'+ 51 | lut[d1&0xff]+lut[d1>>8&0xff]+'-'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'-'+ 52 | lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'-'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+ 53 | lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff]; 54 | } 55 | exports.uuid4 = uuid4 56 | 57 | // HASH 58 | // from https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript 59 | var hash = function(str) { 60 | var h = 0, i, chr; 61 | if (str.length === 0) return h; 62 | for (i = 0; i < str.length; i++) { 63 | chr = str.charCodeAt(i); 64 | h = ((h << 5) - h) + chr; 65 | h |= 0; // Convert to 32bit integer 66 | } 67 | return h; 68 | } 69 | exports.hash = hash 70 | 71 | // Catch Widget Object Type 72 | var isWidget = function(obj) { 73 | try { 74 | var o = obj.Ab().split('.')[0] 75 | if (o === 'ui') { 76 | n = true 77 | } 78 | else { 79 | n = false 80 | } 81 | } catch(err) { 82 | var n = false 83 | } 84 | return n 85 | } 86 | exports.isWidget = isWidget 87 | 88 | // KEEP IN CASE BROKEN APPS 89 | var get_options_old = function(def, options) { 90 | // fill options dict with default values if not present 91 | 92 | if (options !== undefined) { 93 | var opt = options 94 | 95 | for (var key in def) { 96 | var value = def[key] 97 | if (opt[key] === undefined) {opt[key] = value} 98 | } 99 | } else {var opt = def} 100 | return opt 101 | } 102 | exports.get_options_simple = get_options_old 103 | 104 | 105 | // Get Options 106 | var get_options = function(defaults, options) { 107 | // fill options dict with default values if not present 108 | if (options !== undefined) { 109 | var opt = options 110 | 111 | for (var key in defaults) { 112 | var def_value = defaults[key] 113 | var opt_value = opt[key] 114 | 115 | var def_type = object.getType(def_value) 116 | var opt_type = object.getType(opt_value) 117 | 118 | if (def_type === 'Object' && opt_type === 'Object' && !isWidget(def_value)) { 119 | def_value = get_options(def_value, opt_value) 120 | } 121 | 122 | if (opt[key] === undefined) { 123 | opt[key] = def_value 124 | } 125 | } 126 | } else {var opt = defaults} 127 | return opt 128 | } 129 | exports.get_options = get_options 130 | // 131 | 132 | // Objects 133 | var object = {} 134 | 135 | object.getType = function(obj) { 136 | return Object.prototype.toString.call(obj).slice(8, -1); 137 | } 138 | 139 | object.is = function (obj, type) { 140 | var clas = object.getType(obj) 141 | return obj !== undefined && obj !== null && clas === type; 142 | } 143 | 144 | object.keys = function(obj) { 145 | return Object.keys(obj) 146 | } 147 | 148 | object.values = function(obj) { 149 | var v = [] 150 | for (var key in obj) { 151 | var value = obj[key] 152 | v.push(value) 153 | } 154 | return v 155 | } 156 | 157 | object.remove = function(obj, key) { 158 | var newobj = {} 159 | for (var k in obj) { 160 | var val = obj[k] 161 | if (k !== key) { 162 | newobj[k] = val 163 | } 164 | } 165 | return newobj 166 | } 167 | 168 | object.insert = function(obj, key, value) { 169 | var newobj = {} 170 | for (var k in obj) { 171 | newobj[k] = obj[k] 172 | } 173 | newobj[key] = value 174 | return newobj 175 | } 176 | 177 | exports.object = object 178 | 179 | // Arrays 180 | var array = {} 181 | 182 | array.insert = function(array, index, element) { 183 | var firstPart = array.slice(0, index) 184 | var lastPart = array.slice(index, array.length) 185 | 186 | firstPart.push(element) 187 | for (var i in lastPart) { 188 | var rest = lastPart[i] 189 | firstPart.push(rest) 190 | } 191 | return firstPart 192 | } 193 | 194 | array.toString = function(array) { 195 | var str = array.toString() 196 | return '['+str+']' 197 | } 198 | 199 | array.countArraysInside = function(array) { 200 | // Count the number of arrays inside another array 201 | var arrays = 0 202 | for (var i in array) { 203 | var element = array[i] 204 | if (object.is(element, 'Array')) { 205 | arrays += 1 206 | } 207 | } 208 | return arrays 209 | } 210 | 211 | array.contains = function(array, element) { 212 | var r = false 213 | array.map(function(el){ 214 | if (element === el) {r = true} 215 | }) 216 | return r 217 | } 218 | 219 | var writeableArray = function(array) { 220 | // Make an array writeable 221 | var ini = [] 222 | 223 | for (var i in array) {ini.push(0)} 224 | 225 | for (var i in array) { 226 | var e = array[i] 227 | if (object.is(e, 'Array')) { 228 | ini[i] = writeableArray(e) 229 | } else { 230 | ini[i] = e 231 | } 232 | } 233 | return ini 234 | } 235 | 236 | array.writeableArray = writeableArray 237 | 238 | var arrayToString = function(arr) { 239 | // make sure it is writeable 240 | arr = array.writeableArray(arr) 241 | for (var i in arr) { 242 | var element = arr[i] 243 | if (object.is(element, 'Array')) { 244 | var n = array.countArraysInside(element) 245 | if (n === 0) { 246 | var str = element.toString() 247 | str = '['+str+']' 248 | arr[i] = str 249 | } else { 250 | arr[i] = arrayToString(element) 251 | } 252 | } 253 | } 254 | var str = arr.toString() 255 | str = '['+str+']' 256 | return str 257 | } 258 | 259 | array.toString = arrayToString 260 | 261 | exports.array = array 262 | 263 | // Strings 264 | var string = {} 265 | 266 | string.format = function(str, arr) { 267 | if (typeof(arr) === 'array') { 268 | return str.replace(/{(\d+)}/g, function (match, number) { 269 | return typeof arr[number] != 'undefined' ? arr[number] : match; 270 | })} 271 | else if (typeof(arr) === 'object') { 272 | return str.replace(/{(\w+)}/g, function (match, str) { 273 | return typeof arr[str] != 'undefined' ? arr[str] : match; 274 | }); 275 | }; 276 | } 277 | 278 | string._str = function(str) { 279 | return str.replace(/(\s)/g, function(match, result) { 280 | return '_' 281 | }) 282 | } 283 | 284 | string.formatTask = function(str) { 285 | return str.replace(/(\/)/g, function(match, result) { 286 | return '_' 287 | }) 288 | } 289 | 290 | exports.string = string -------------------------------------------------------------------------------- /_tools/image: -------------------------------------------------------------------------------- 1 | var helpersJS = require('users/fitoprincipe/geetools:helpers_js') 2 | var string_module = require('users/fitoprincipe/geetools:_tools/string').string 3 | 4 | /** IMAGES */ 5 | var image = {}; 6 | 7 | image.cat = function(img1, img2) { 8 | var img1mask = img1.mask().select(0).gt(0).rename('mask') 9 | img1 = img1.addBands(img1mask) 10 | var img2mask = img2.mask().select(0).gt(0).rename('mask') 11 | img2 = img2.addBands(img2mask) 12 | var concat = img2.unmask().where(img1.unmask().select(0).gt(0), img1.unmask()) 13 | var mask = concat.select('mask') 14 | return concat.updateMask(mask).select([0, 1, 2]) 15 | } 16 | 17 | // get value from point 18 | image.getValue = function(image, point, scale) { 19 | var sc = ee.Number(scale).toInt() || 10; 20 | var result = image.reduceRegion(ee.Reducer.first(), point, sc); 21 | return result 22 | } 23 | 24 | // replace one band with another band 25 | image.replaceBand = function(image, replace_band, add_band) { 26 | var band = add_band.select([0]); 27 | var bands = image.bandNames(); 28 | var resto = bands.remove(replace_band); 29 | var img_resto = image.select(resto); 30 | var img_final = img_resto.addBands(band); 31 | return img_final 32 | } 33 | 34 | // SUFFIX TO BAND 35 | image.addSuffix = function(suffix, bands) { 36 | if (bands == undefined) { 37 | var bands = null 38 | } else { 39 | var bands = ee.List(bands) 40 | } 41 | var suf = ee.String(suffix) 42 | var wrap = function(img) { 43 | var allbands = img.bandNames() 44 | var bands_ = ee.List(ee.Algorithms.If(bands, bands, allbands)) 45 | var newbands = bands_.iterate(function(band, first){ 46 | var all = ee.List(first) 47 | return all.replace(band, ee.String(band).cat(suf)) 48 | }, allbands) 49 | newbands = ee.List(newbands) 50 | return img.select(allbands, newbands) 51 | } 52 | return wrap 53 | } 54 | 55 | // PREFIX TO BAND 56 | image.addPrefix = function(prefix, bands) { 57 | if (bands == undefined) { 58 | var bands = null 59 | } else { 60 | var bands = ee.List(bands) 61 | } 62 | var suf = ee.String(prefix) 63 | var wrap = function(img) { 64 | var allbands = img.bandNames() 65 | var bands_ = ee.List(ee.Algorithms.If(bands, bands, allbands)) 66 | var newbands = bands_.iterate(function(band, first){ 67 | var all = ee.List(first) 68 | return all.replace(band, suf.cat(ee.String(band))) 69 | }, allbands) 70 | newbands = ee.List(newbands) 71 | return img.select(allbands, newbands) 72 | } 73 | return wrap 74 | } 75 | 76 | // MAKE AN IMAGE FROM A DICT (Equals to Dictionary.toImage()) 77 | image.fromDict = function(bands) { 78 | // this is the same as doing ee.Dictionary().toImage() 79 | 80 | if (bands === undefined) {var opt={constant:0}} else {var opt=bands} 81 | 82 | var dict = ee.Dictionary(opt) 83 | var bandnames = dict.keys() // ee.List 84 | var bandlist = dict.map(function(key, val){ 85 | var v = ee.Number(val); 86 | var k = ee.String(key) 87 | var f = ee.Image(v).select([0], [k]) 88 | return f 89 | }).values() 90 | bandlist = ee.List(bandlist) 91 | var final = ee.Image(bandlist.iterate(function(img, ini){ 92 | ini = ee.Image(ini) 93 | return ini.addBands(img) 94 | }, ee.Image(0))) 95 | return final.select(bandnames) 96 | } 97 | 98 | // MAKE AN IMAGE FROM A LIST 99 | image.fromList = function(list, value) { 100 | if (value === undefined) {var val = 0} else {var val = value} 101 | var listt = ee.List(list) 102 | 103 | var imglist = listt.map(function(name){ 104 | return ee.Image(val).select([0], [name]) 105 | }) 106 | var final = ee.Image(imglist.iterate(function(img, ini){ 107 | ini = ee.Image(ini) 108 | return ini.addBands(img) 109 | }, ee.Image(0))) 110 | return final.select(listt) 111 | } 112 | 113 | image.removeBands = function(image, bands) { 114 | var bnames = image.bandNames() 115 | var bands = ee.List(bands) 116 | 117 | var inter = list.intersection(bnames, bands) 118 | // var diff = list.subtract(bnames, inter) 119 | var diff = bnames.removeAll(inter) 120 | 121 | return image.select(diff) 122 | } 123 | 124 | // DOY TO DATE 125 | image.doyToDate = function(image, options) { 126 | 127 | var defaults = { 128 | dateFormat: 'yyyyMMdd', 129 | year: image.date().get('year') 130 | } 131 | var opt = helpersJS.get_options(defaults, options) 132 | var year = ee.Number(opt.year) 133 | 134 | var doyband = image.select([0]) 135 | var leap = date.isLeap(year) 136 | var limit = ee.Number(ee.Algorithms.If(leap, 365, 364)) 137 | var alldoys = ee.List.sequence(0, limit) 138 | 139 | var datei = ee.Image(alldoys.iterate(function(doy, i){ 140 | i = ee.Image(i) 141 | doy = ee.Number(doy) 142 | var d = date.fromDOY(doy, year) 143 | var date_band = ee.Image.constant(ee.Number.parse(d.format(opt.dateFormat))) 144 | var condition = i.eq(doy) 145 | return i.where(condition, date_band) 146 | }, doyband)) 147 | 148 | return datei.rename('date') 149 | } 150 | 151 | image.maskCover = function(image, geometry, options) { 152 | var def = { 153 | scale: null, 154 | max_pixels: 1e13 155 | } 156 | 157 | // select first band 158 | image = image.select([0]) 159 | 160 | var opt = helpersJS.get_options(def, options) 161 | var max_pixels = opt.max_pixels 162 | 163 | // get projection 164 | var projection = image.projection() 165 | 166 | var scale = opt.scale || projection.nominalScale() 167 | 168 | // get band name 169 | var band = ee.String(image.bandNames().get(0)) 170 | 171 | // Make an image with all ones 172 | var ones_i = ee.Image.constant(1).reproject(projection).rename(band) 173 | 174 | // Get total number of pixels 175 | var ones = ones_i.reduceRegion({ 176 | reducer: ee.Reducer.count(), 177 | geometry: geometry, 178 | scale: scale, 179 | maxPixels: max_pixels 180 | }).get(band) 181 | 182 | ones = ee.Number(ones) 183 | 184 | // select first band, unmask and get the inverse 185 | image = image.select([0]) 186 | var mask = image.mask() 187 | var mask_not = mask.not() 188 | var image_to_compute = mask.updateMask(mask_not) 189 | 190 | // Get number of zeros in the given mask_image 191 | zeros_in_mask = image_to_compute.reduceRegion({ 192 | reducer: ee.Reducer.count(), 193 | geometry: geometry, 194 | scale: scale, 195 | maxPixels: max_pixels 196 | }).get(band) 197 | var zeros_in_mask = ee.Number(zeros_in_mask) 198 | 199 | // var percentage = tools.number.trim_decimals(zeros_in_mask.divide(ones), 4) 200 | var percentage = zeros_in_mask.divide(ones) 201 | 202 | return percentage.multiply(100) 203 | } 204 | 205 | image.addMultiBands = function(image, imageList) { 206 | 207 | var iteration = function(img, ini) { 208 | ini = ee.Image(ini) 209 | var img = ee.Image(img) 210 | return ini.addBands(img) 211 | } 212 | return ee.List(imageList).iterate(iteration, image) 213 | } 214 | 215 | image.toQGIS = function(image, visParams, name) { 216 | visParams = visParams || {min:0, max:1} 217 | name = name || 'no_name_image' 218 | var baseurl = 'https://str2txt-app.rodrigoprincipe.repl.co/image?name={name}&mapid={mapid}&token={token}' 219 | var data = image.getMap(visParams) 220 | var idtok = data['mapid'] 221 | idtok = idtok.split('/')[3] 222 | var mapid = idtok.split('-')[0] 223 | var token = idtok.split('-')[1] 224 | return helpersJS.string.format(baseurl, {name:name, mapid:mapid, token:token}) 225 | } 226 | 227 | image.makeName = function(image, pattern, date_pattern) { 228 | /* 229 | Make a name with the given pattern. The pattern must contain the 230 | propeties to replace between curly braces. There are 2 special words: 231 | 232 | * 'system_date': replace with the date of the image formatted with 233 | `date_pattern`, which defaults to 'yyyyMMdd' 234 | * 'id' or 'ID': the image id. If None, it'll be replaced with 'id' 235 | 236 | Pattern example (supposing each image has a property called `city`): 237 | 'image from {city} on {system_date}' 238 | */ 239 | date_pattern = date_pattern || null 240 | //properties = properties || null 241 | 242 | var match = ee.String(pattern).match('{.*?}', 'g') 243 | var exclude = ee.List(['system_date', 'id', 'ID']) 244 | var properties = ee.List(match.iterate(function(it, l){ 245 | it = ee.String(it) 246 | var newit = it.slice(1,-1) 247 | l = ee.List(l) 248 | return ee.Algorithms.If(exclude.contains(newit), l, l.add(newit)) 249 | }, ee.List([]))) 250 | 251 | var img = ee.Image(image) 252 | var condition = ee.Number(properties.size()).gt(0) 253 | var props = ee.Dictionary( 254 | ee.Algorithms.If(condition, img.toDictionary(properties), {})) 255 | props = ee.Dictionary(ee.Algorithms.If( 256 | img.id(), 257 | props.set('id', img.id()).set('ID', img.id()), 258 | props)) 259 | props = ee.Dictionary(ee.Algorithms.If( 260 | img.propertyNames().contains('system:time_start'), 261 | props.set('system_date', img.date().format(date_pattern)), 262 | props)) 263 | var name = string_module.format(pattern, props) 264 | 265 | return name 266 | } 267 | 268 | exports.image = image -------------------------------------------------------------------------------- /cloud_masks: -------------------------------------------------------------------------------- 1 | /*** 2 | * Functions to apply cloud mask to different collections 3 | * 4 | * Author: Rodrigo E. Principe 5 | * email: fitoprincipe82@gmail.com 6 | * License: MIT 7 | * Repository: https://github.com/fitoprincipe/geetools-code-editor 8 | */ 9 | 10 | var tools = require('users/fitoprincipe/geetools:tools') 11 | var l_algo = require('users/fitoprincipe/geetools:list_algorithms') 12 | var dt = require('users/fitoprincipe/geetools:decision_tree') 13 | var helpers = require('users/fitoprincipe/geetools:helpers_js') 14 | 15 | var help = {}; 16 | 17 | var compute = function(image, mask_band, bits, options) { 18 | // cast params in case they are not EE objects 19 | var bits_dict = ee.Dictionary(bits) 20 | var opt = ee.List(options) 21 | image = ee.Image(image).select(mask_band) 22 | 23 | var first = ee.Image.constant(0) // init image 24 | 25 | // function for iterate over the options 26 | var for_iterate = function(option, ini) { 27 | var i = ee.Image(ini) // cast ini 28 | 29 | // bits relation dict contains the option? 30 | var cond = bits_dict.contains(option); 31 | 32 | // get the mask for the option 33 | var mask = tools.computeQAbits(ee.List(bits_dict.get(option)).get(0), 34 | ee.List(bits_dict.get(option)).get(1), 35 | option)(image) 36 | return ee.Image(ee.Algorithms.If(cond, 37 | i.or(mask), 38 | i)) 39 | } 40 | 41 | var good_pix = ee.Image(opt.iterate(for_iterate, first)) 42 | 43 | return good_pix.not(); 44 | } 45 | 46 | var sentinel2 = function(options) { 47 | var opt = options || ['opaque', 'cirrus'] 48 | var rel = {opaque: [10, 10] , cirrus:[11, 11]} 49 | var band = 'QA60' 50 | 51 | var wrap = function(img){ 52 | var good_pix = compute(img, band, rel, opt) 53 | return img.updateMask(good_pix) 54 | } 55 | return wrap 56 | } 57 | 58 | var landsatSR = function(options) { 59 | var sr = { 60 | bits: ee.Dictionary( 61 | { 62 | 'cloud': [1,1], 63 | 'shadow': [2,2], 64 | 'adjacent': [3,3], 65 | 'snow': [4,4] 66 | 67 | }), 68 | band: 'sr_cloud_qa'} 69 | 70 | var pix = { 71 | bits: ee.Dictionary( 72 | { 73 | 'cloud': [5,5], 74 | 'shadow': [3,3], 75 | 'snow': [4,4] 76 | }), 77 | band: 'pixel_qa'} 78 | 79 | // Parameters 80 | var opt = options || sr.bits.keys(); 81 | options = ee.List(opt); 82 | 83 | var wrap = function(image) { 84 | var bands = image.bandNames(); 85 | var contains_sr = bands.contains('sr_cloud_qa'); 86 | var good_pix = ee.Image(ee.Algorithms.If(contains_sr, 87 | compute(image, sr.band, sr.bits, opt), 88 | compute(image, pix.band, pix.bits, opt))) 89 | 90 | // var good_pix = compute(image, mask_band, bits, opt) 91 | return image.updateMask(good_pix) 92 | } 93 | return wrap 94 | } 95 | 96 | var landsatTOA = function(options) { 97 | var bits = ee.Dictionary({ 98 | 'cloud': [4, 4], 99 | 'shadow': [8, 8], 100 | 'snow': [10, 10] 101 | }); 102 | var mask_band = 'BQA' 103 | 104 | // Parameters 105 | var opt = options || bits.keys(); 106 | options = ee.List(opt); 107 | 108 | var wrap = function(image) { 109 | var good_pix = compute(image, mask_band, bits, options) 110 | return image.updateMask(good_pix); 111 | } 112 | return wrap 113 | } 114 | 115 | var landsatTOAmask = function(name) { 116 | var bits = ee.Dictionary({'cloud': [4,4], 'shadow': [8,8], 'snow': [10,10]}); 117 | var mask_band = 'BQA'; 118 | 119 | // Parameters 120 | name = name || 'cloud_mask'; 121 | var options = bits.keys(); 122 | 123 | var wrap = function(image) { 124 | var good_pix = compute(image, mask_band, bits, options) 125 | return good_pix.rename(name); 126 | } 127 | return wrap 128 | } 129 | 130 | var modisSR = function(options) { 131 | var bits = ee.Dictionary({ 132 | 'cloud': [0, 0], 133 | 'mix': [1,1], 134 | 'shadow': [2,2], 135 | 'cloud2':[10,10], 136 | 'snow':[12,12] 137 | }); 138 | 139 | var opt = options || bits.keys() 140 | var mask_band = 'state_1km' 141 | 142 | options = ee.List(opt); 143 | var wrap = function(image) { 144 | var good_pix = compute(image, mask_band, bits, opt) 145 | return image.updateMask(good_pix); 146 | } 147 | return wrap 148 | } 149 | 150 | var sclData = ee.Dictionary({ 151 | 'saturated': [1, 0], 152 | 'dark': [2, 0], 153 | 'shadow': [3, 0], 154 | 'vegetation': [4, 1], 155 | 'bare_soil': [5, 1], 156 | 'water': [6, 0], 157 | 'cloud_low': [7, 0], 158 | 'cloud_medium': [8, 0], 159 | 'cloud_high': [9, 0], 160 | 'cirrus': [10, 0], 161 | 'snow': [11, 0] 162 | }) 163 | 164 | var scl = function(image) { 165 | // Decodify the SCL bands and create a mask for each category 166 | 167 | var i = image.select('SCL') 168 | 169 | var data = ee.Dictionary(sclData) 170 | 171 | var wrap = function(name, list) { 172 | list = ee.List(list) 173 | name = ee.String(name) 174 | var band_value = ee.Number(list.get(0)) 175 | var mask = i.eq(band_value).rename(name) 176 | return mask 177 | } 178 | 179 | var newbands = ee.Dictionary(data.map(wrap)) 180 | var images = newbands.values() 181 | var first = ee.Image(images.get(0)) 182 | var rest = images.slice(1) 183 | return ee.Image(tools.image.addMultiBands(first, rest)) 184 | } 185 | 186 | var sclMask = function(options) { 187 | var opt = options || ['saturated', 'dark', 'shadow', 'cloud_low', 188 | 'cloud_medium', 'cloud_high', 'cirrus', 'snow'] 189 | opt = ee.List(opt) 190 | var wrap = function(img) { 191 | var r = opt.iterate(function(name, i){ 192 | i = ee.Image(i) 193 | name = ee.String(name) 194 | var data = ee.List(sclData.get(name)) 195 | var value = ee.Number(data.get(0)) 196 | var direction = ee.Number(data.get(1)) 197 | var band = i.select('SCL') 198 | var mask = ee.Algorithms.If(direction, 199 | band.eq(value), 200 | band.neq(value) 201 | ) 202 | mask = ee.Image(mask) 203 | return i.updateMask(mask) 204 | }, img) 205 | return ee.Image(r) 206 | } 207 | return wrap 208 | } 209 | 210 | var hollstein_S2 = function(options) { 211 | // Taken from André Hollstein et al. 2016 (doi:10.3390/rs8080666) 212 | // http://www.mdpi.com/2072-4292/8/8/666/pdf 213 | 214 | var opt = options || ['cloud', 'snow', 'shadow', 'water', 'cirrus'] 215 | 216 | var difference = function(a, b) { 217 | var wrap = function(img) { 218 | return img.select(a).subtract(img.select(b)) 219 | } 220 | return wrap 221 | } 222 | var ratio = function(a, b) { 223 | var wrap = function(img) { 224 | return img.select(a).divide(img.select(b)) 225 | } 226 | return wrap 227 | } 228 | 229 | var opt_list = ee.List(opt) 230 | 231 | var compute_dt = function(img) { 232 | 233 | //1 234 | var b3 = img.select('B3').lt(3190) 235 | 236 | //2 237 | var b8a = img.select('B8A').lt(1660) 238 | var r511 = ratio('B5', 'B11')(img).lt(4.33) 239 | 240 | //3 241 | var s1110 = difference('B11', 'B10')(img).lt(2550) 242 | var b3_3 = img.select('B3').lt(5250) 243 | var r210 = ratio('B2','B10')(img).lt(14.689) 244 | var s37 = difference('B3', 'B7')(img).lt(270) 245 | 246 | //4 247 | var r15 = ratio('B1', 'B5')(img).lt(1.184) 248 | var s67 = difference('B6', 'B7')(img).lt(-160) 249 | var b1 = img.select('B1').lt(3000) 250 | var r29 = ratio('B2', 'B9')(img).lt(0.788) 251 | var s911 = difference('B9', 'B11')(img).lt(210) 252 | var s911_2 = difference('B9', 'B11')(img).lt(-970) 253 | 254 | var dtf = dt.binary({1:b3, 255 | 21:b8a, 22:r511, 256 | 31:s37, 32:r210, 33:s1110, 34:b3_3, 257 | 41: s911_2, 42:s911, 43:r29, 44:s67, 45:b1, 46:r15 258 | }, 259 | {'shadow-1':[[1,1], [21,1], [31,1], [41,0]], 260 | 'water': [[1,1], [21,1], [31,0], [42,1]], 261 | 'shadow-2':[[1,1], [21,1], [31,0], [42,0]], 262 | 'cirrus-2':[[1,1], [21,0], [32,1], [43,0]], 263 | 'cloud-1': [[1,0], [22,1], [33,1], [44,1]], 264 | 'cirrus-1':[[1,0], [22,1], [33,1], [44,0]], 265 | 'cloud-2': [[1,0], [22,1], [33,0], [45,0]], 266 | 'shadow-3':[[1,0], [22,0], [34,1], [46,0]], 267 | 'snow': [[1,0], [22,0], [34,0]], 268 | }, 'hollstein') 269 | 270 | var results = dtf(img) 271 | 272 | var optlist = ee.List(opt) 273 | 274 | var finalmask = ee.Image(optlist.iterate(function(option, ini){ 275 | ini = ee.Image(ini) 276 | var mask = results.select([option]) 277 | return ini.or(mask) 278 | }, ee.Image.constant(0).select([0], ['hollstein']))) 279 | 280 | return img.addBands(results).updateMask(finalmask.not())//results.select('hollstein')) 281 | } 282 | return compute_dt 283 | } 284 | 285 | var make = { 286 | sentinel2: sentinel2, 287 | landsatSR: landsatSR, 288 | landsatTOA: landsatTOA, 289 | modisSR: modisSR, 290 | hollstein_S2: hollstein_S2 291 | } 292 | 293 | help['sentinel2'] = 'sentinel2(options)\n\n'+ 294 | 'function to mask out clouds of Sentinel 2 images\n'+ 295 | 'options (list): opaque, cirrus' 296 | 297 | help['landsatSR'] = 'landsatSR(options)\n\n'+ 298 | 'function to mask out clouds of Landsat SR images\n'+ 299 | 'collections: LANDSAT/LT04/C01/T1_SR, LANDSAT/LT05/C01/T1_SR,\n'+ 300 | 'LANDSAT/LE07/C01/T1_SR, LANDSAT/LC08/C01/T1_SR\n'+ 301 | 'options (list): cloud, shadow, adjacent (only L8), snow' 302 | 303 | help['landsatTOA'] = 'landsatTOA(options)\n\n'+ 304 | 'function to mask out clouds of Landsat TOA images\n'+ 305 | 'collections: LANDSAT/LT04/C01/T1_TOA, LANDSAT/LT05/C01/T1_TOA,\n'+ 306 | 'LANDSAT/LE07/C01/T1_TOA, LANDSAT/LC08/C01/T1_TOA\n'+ 307 | 'options (list): cloud, shadow, snow' 308 | 309 | help['modisSR'] = 'modis(options)\n\n'+ 310 | 'function to mask out clouds of MODIS images\n'+ 311 | 'collections: MODIS/006/MOD09GA, MODIS/006/MYD09GA\n'+ 312 | 'options (list): cloud, mix, shadow, cloud2, snow' 313 | 314 | help['hollstein_S2'] = 'hollstein_S2(options)\n\n'+ 315 | 'Implementation of the decision tree developed by André Hollstein et al. 2016 (doi:10.3390/rs8080666) (http://www.mdpi.com/2072-4292/8/8/666/pdf)\n'+ 316 | 'options: a list with one or more of "cloud", "water", "cirrus", "snow", "shadow"\n' 317 | 318 | help['make'] = 'Create functions using a key. Example:\n'+ 319 | "var make = require('users/fitoprincipe/geetools:cloud_masks').make\n"+ 320 | 'var s2 = make.sentinel2()\n'+ 321 | 'var LSR = make.landsatSR()' 322 | 323 | exports.help = help 324 | exports.sentinel2 = sentinel2 325 | exports.landsatSR = landsatSR 326 | exports.landsatTOA = landsatTOA 327 | exports.landsatTOAmask = landsatTOAmask 328 | exports.modisSR = modisSR 329 | exports.make = make 330 | exports.hollstein_S2 = hollstein_S2 331 | exports.options = ee.Dictionary(help).keys() 332 | exports.compute = compute 333 | exports.scl = scl 334 | exports.sclMask = sclMask 335 | 336 | var Test = function() { 337 | print('running') 338 | var sat = require('users/fitoprincipe/geetools:satellite') 339 | var s2 = new sat.Sentinel(2, 'SR') 340 | var site = tools.map.getBounds(Map) 341 | var col = s2.collection.filterBounds(site).filterMetadata(s2.cloud_cover, 'less_than', 10) 342 | var first = col.first() 343 | Map.addLayer(first, s2.vis.NSR, 'S2 SR') 344 | } 345 | 346 | //Test() 347 | -------------------------------------------------------------------------------- /satellite: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Rodrigo E. Principe 3 | * License: Apache 2.0 4 | * email: fitoprincipe82@gmail.com 5 | 6 | Easy way to access common satellites (Landsat, Sentinel, MODIS) information 7 | */ 8 | 9 | var indexes = require('users/fitoprincipe/geetools:indexes') 10 | var cld = require('users/fitoprincipe/geetools:cloud_masks') 11 | 12 | var SHORTS = { 13 | 'COPERNICUS/S2': 'S2TOA', 14 | 'COPERNICUS/S2_SR': 'S2SR', 15 | 'LANDSAT/LM01/C01/T1': 'L1T1', 16 | 'LANDSAT/LM02/C01/T1': 'L2T1', 17 | 'LANDSAT/LM03/C01/T1': 'L3T1', 18 | 'LANDSAT/LM04/C01/T1': 'L4MSST1', 19 | 'LANDSAT/LT04/C01/T1_SR': 'L4SRT1', 20 | 'LANDSAT/LT04/C01/T1_TOA': 'L4TOAT1', 21 | 'LANDSAT/LM05/C01/T1': 'L5MSST1', 22 | 'LANDSAT/LT05/C01/T1_SR': 'L5SRT1', 23 | 'LANDSAT/LT05/C01/T1_TOA': 'L5TOAT1', 24 | 'LANDSAT/LM07/C01/T1': 'L7MSST1', 25 | 'LANDSAT/LE07/C01/T1_SR': 'L7SRT1', 26 | 'LANDSAT/LE07/C01/T1_TOA': 'L7TOAT1', 27 | 'LANDSAT/LC08/C01/T1_SR': 'L8SRT1', 28 | 'LANDSAT/LC08/C01/T1_TOA': 'L8TOAT1' 29 | } 30 | 31 | var Landsat = function(number, process, sensor, tier) { 32 | this.number = number; 33 | this.process = process; 34 | this.sensor = sensor; 35 | 36 | // TIER 37 | if (tier === undefined || tier === null) { 38 | this.tier = 1; 39 | } else { 40 | this.tier = tier; 41 | } 42 | 43 | var numbers = [4, 5, 7, 8] 44 | var processes = ['RAW', 'TOA', 'SR'] 45 | var sensors = ['TM', 'MSS', 'ETM', 'OLI'] 46 | var tiers = [1, 2] 47 | 48 | // SENSOR 49 | switch (number) { 50 | case 4: case 5: 51 | if (sensor === undefined || sensor === null) { 52 | this.sensor = 'TM' 53 | } 54 | break 55 | case 7: 56 | this.sensor = 'ETM' 57 | break 58 | case 8: 59 | this.sensor = 'OLI' 60 | break 61 | } 62 | 63 | // DATES 64 | switch (number) { 65 | case 1: 66 | this.start_date = '1972-07-23' 67 | this.end_date = '1978-01-08' 68 | break 69 | case 2: 70 | this.start_date = '1975-01-22' 71 | this.end_date = '1982-02-26' 72 | break 73 | case 3: 74 | this.start_date = '1978-03-5' 75 | this.end_date = '1983-03-31' 76 | break 77 | case 4: 78 | switch (this.sensor) { 79 | case 'TM': 80 | this.start_date = '1982-08-22' 81 | this.end_date = '1993-12-14' 82 | break 83 | case 'MSS': 84 | this.start_date = '1982-07-16' 85 | this.end_date = '1993-12-14' 86 | break 87 | } 88 | case 5: 89 | switch (this.sensor) { 90 | case 'TM': 91 | this.start_date = '1984-01-01' 92 | this.end_date = '2012-05-05' 93 | break 94 | case 'MSS': 95 | this.start_date = '1984-03-01' 96 | this.end_date = '2013-01-31' 97 | break 98 | } 99 | case 7: 100 | this.start_date = '1999-01-01' 101 | this.end_date = null 102 | break 103 | case 8: 104 | this.start_date = '2013-04-11' 105 | this.end_date = null 106 | break 107 | } 108 | 109 | if (numbers.indexOf(number) === -1) { 110 | print('Landsat '+number+' not available') 111 | return null; 112 | } 113 | if (processes.indexOf(process) === -1) { 114 | print('Landsat '+number+' '+process+' not available') 115 | return null; 116 | } 117 | if (sensors.indexOf(sensor) === -1 && sensor !== undefined) { 118 | print('Landsat sensor '+sensor+' not available') 119 | return null; 120 | } 121 | if (tiers.indexOf(this.tier) === -1) { 122 | print('Landsat tier '+this.tier+' not available') 123 | return null; 124 | } 125 | 126 | // IF SENSOR IS NOT AVAILABLE 127 | switch (number) { 128 | case 4: case 5: 129 | if ((this.process === 'SR' || this.process === 'TOA') 130 | && this.sensor !== 'TM') { 131 | print('Lansat '+this.number+' '+this.process+' has no sensor '+this.sensor) 132 | print('Switching to TM..') 133 | this.sensor = 'TM' 134 | } 135 | else if (this.sensor !== 'TM' && this.sensor !== 'MSS') { 136 | print('Lansat '+this.number+' has no sensor '+this.sensor) 137 | print('Switching to TM..') 138 | this.sensor = 'TM' 139 | } 140 | break 141 | case 7: 142 | if (this.sensor !== 'ETM') { 143 | print('Lansat 7 has no sensor '+this.sensor) 144 | print('Switching to ETM..') 145 | this.sensor = 'ETM' 146 | } 147 | break 148 | case 8: 149 | if (this.sensor !== 'OLI') { 150 | print('Lansat 8 has no sensor '+this.sensor) 151 | print('Switching to OLI..') 152 | this.sensor = 'OLI' 153 | } 154 | break 155 | } 156 | 157 | // ID CONSTRUCTOR 158 | var NUMBER = { 159 | 4: {'TM':'LT04', 'MSS':'LM04'}, 160 | 5: {'TM':'LT05', 'MSS':'LM05'}, 161 | 7: {'ETM':'LE07'}, 162 | 8: {'OLI':'LC08'} 163 | } 164 | var TIER = {1: 'T1', 2: 'T2'} 165 | var PROCESS = {'RAW': '', 'SR': '_SR', 'TOA': '_TOA'} 166 | 167 | // MAKE ID 168 | this.id = 'LANDSAT/'+NUMBER[this.number][this.sensor] 169 | +'/C01/'+TIER[this.tier] 170 | +PROCESS[this.process] 171 | 172 | this.collection = ee.ImageCollection(this.id) 173 | 174 | this.shortName = SHORTS[this.id] 175 | 176 | // BANDS by NUMBER 177 | switch (this.number) { 178 | case 4: case 5: case 7: 179 | // by SENSOR 180 | switch (this.sensor) { 181 | case 'TM': case 'ETM': 182 | this.band = { 183 | 'blue':'B1', 'green':'B2', 184 | 'red':'B3', 'nir':'B4', 185 | 'swir':'B5', 'swir2': 'B7' 186 | } 187 | break 188 | case 'MSS': 189 | this.band = { 190 | 'green': 'B1', 'red': 'B2', 191 | 'nir': 'B3', 'nir2': 'B4' 192 | } 193 | break 194 | } 195 | break 196 | case 8: 197 | this.band = { 198 | 'ublue': 'B1', 'blue': 'B2', 'green': 'B3', 'red': 'B4', 199 | 'nir':'B5', 'swir':'B6', 'swir2':'B7', 'thermal':'B10', 'thermal2':'B11' 200 | } 201 | break 202 | } 203 | 204 | // BANDS by PROCESS 205 | switch (this.process) { 206 | case 'SR': 207 | this.band.pixel_qa = 'pixel_qa' 208 | this.band.radsat_qa = 'radsat_qa' 209 | switch (this.number) { 210 | case 4: case 5: case 7: 211 | this.band.atmos_opacity = 'sr_atmos_opacity' 212 | this.band.cloud_qa = 'sr_cloud_qa' 213 | this.band.thermal = 'B6' 214 | break 215 | case 8: 216 | this.band.aerosol = 'sr_aerosol' 217 | break 218 | } 219 | break 220 | case 'TOA': case 'RAW': 221 | this.band.bqa = 'BQA' 222 | switch (this.number) { 223 | case 4: case 5: 224 | switch (this.sensor) { 225 | case 'TM': 226 | this.band.thermal = 'B6' 227 | break 228 | } 229 | break 230 | case 7: case 8: 231 | this.band.pan = 'B8' 232 | break 233 | } 234 | switch (this.number) { 235 | case 7: 236 | this.band.thermal1 = 'B6_VCID_1' 237 | this.band.thermal2 = 'B6_VCID_2' 238 | break 239 | case 8: 240 | this.band.cirrus = 'B9' 241 | this.band.thermal = 'B10' 242 | this.band.thermal2 = 'B11' 243 | break 244 | } 245 | break 246 | } 247 | 248 | // SCALE by NUMBER 249 | switch (this.number) { 250 | case 4: case 5: 251 | // by SENSOR 252 | switch (this.sensor) { 253 | case 'TM': 254 | this.scale = { 255 | 'blue':30, 'green':30, 256 | 'red':30, 'nir':30, 257 | 'swir':30, 'swir2':30, 258 | 'thermal':120 259 | } 260 | break 261 | case 'MSS': 262 | this.scale = { 263 | 'green': 60, 'red': 60, 264 | 'nir': 60, 'nir2': 60 265 | } 266 | break 267 | } 268 | break 269 | case 7: 270 | this.scale = { 271 | 'blue': 30, 'green': 30, 'red': 30, 272 | 'nir':30, 'swir':30, 'swir2':30, 273 | 'pan':15, 'thermal':100, 274 | } 275 | break 276 | case 8: 277 | this.scale = { 278 | 'ublue': 30, 'blue': 30, 279 | 'green': 30, 'red': 30, 280 | 'nir':30, 'swir':30, 'swir2':30, 281 | 'pan':15, 'thermal':100, 'thermal2':100 282 | } 283 | break 284 | } 285 | 286 | // CLOUD COVER 287 | this.cloud_cover = 'CLOUD_COVER' 288 | 289 | // BANDS RANGE 290 | this.min = 0 291 | switch (process) { 292 | case ('RAW'): 293 | this.max = 255 294 | break 295 | case ('TOA'): 296 | this.max = 1 297 | break 298 | case ('SR'): 299 | this.max = 10000 300 | break 301 | } 302 | 303 | // INDEXES 304 | switch (number) { 305 | case 4: case 5: case 7: 306 | this.ndvi = indexes.NDVI.landsat457 307 | this.evi = indexes.EVI.landsat457 308 | this.nbr = indexes.NBR.landsat457 309 | this.nbr2 = indexes.NBR2.landsat457 310 | break 311 | case 8: 312 | this.ndvi = indexes.NDVI.landsat8 313 | this.evi = indexes.EVI.landsat8 314 | this.nbr = indexes.NBR.landsat8 315 | this.nbr2 = indexes.NBR2.landsat8 316 | break 317 | } 318 | 319 | // CLOUD MASKS 320 | switch (this.process) { 321 | case 'SR': 322 | this.cloud_masks = {'qa': cld.landsatSR} 323 | this.cloud_mask = cld.landsatSR 324 | break 325 | case 'TOA': case 'RAW': 326 | //print(this.process) 327 | this.cloud_masks = {'qa': cld.landsatTOA} 328 | this.cloud_mask = cld.landsatTOA 329 | break 330 | } 331 | 332 | // VISAULIZATION 333 | this.vis = {}; 334 | this.vis.NSR = { 335 | bands:[this.band.nir, this.band.swir, this.band.red], 336 | min: 0, 337 | max: this.max/2 338 | } 339 | this.vis.RGB = { 340 | bands:[this.band.red, this.band.green, this.band.blue], 341 | min: 0, 342 | max: this.max/3 343 | } 344 | this.vis.falseColor = { 345 | bands:[this.band.nir, this.band.red, this.band.green], 346 | min: 0, 347 | max: this.max/2 348 | } 349 | } 350 | exports.Landsat = Landsat 351 | 352 | var Sentinel = function(number, process) { 353 | this.number = number 354 | this.process = process 355 | 356 | var numbers = [2] 357 | var processes = ['TOA', 'SR'] 358 | 359 | if (numbers.indexOf(number) === -1) { 360 | print('Sentinel '+number+' not available') 361 | return null; 362 | } 363 | 364 | if (processes.indexOf(process) === -1) { 365 | print('Sentinel '+number+' '+process+' not available') 366 | return null; 367 | } 368 | 369 | switch (number) { 370 | case 2: 371 | // BANDS 372 | this.band = { 373 | 'aerosol': 'B1', 'blue': 'B2', 'green': 'B3', 'red':'B4', 374 | 'red_edge_1': 'B5', 'red_edge_2': 'B6', 'red_edge_3': 'B7', 375 | 'nir': 'B8', 'red_edge_4': 'B8A', 'vapor': 'B9', 'cirrus': 'B10', 376 | 'swir': 'B11', 'swir2': 'B12', 'qa': 'QA60' 377 | } 378 | // SCALES 379 | this.scale = { 380 | 'aerosol': 60, 'blue': 10, 'green': 10, 'red':10, 381 | 'red_edge_1': 20, 'red_edge_2': 20, 'red_edge_3': 20, 382 | 'nir': 10, 'red_edge_4': 20, 'vapor': 60, 'cirrus': 60, 383 | 'swir': 20, 'swir2': 20, 'qa': 60 384 | } 385 | 386 | this.cloud_cover = 'CLOUD_COVERAGE_ASSESSMENT' 387 | this.ndvi = indexes.NDVI.sentinel2 388 | this.evi = indexes.EVI.sentinel2 389 | this.nbr = indexes.NBR.sentinel2 390 | this.nbr2 = indexes.NBR2.sentinel2 391 | this.savi = indexes.SAVI.sentinel2 392 | 393 | this.min = 0 394 | this.max = 10000 395 | this.end_date = null 396 | 397 | // VISAULIZATION 398 | this.vis = {}; 399 | this.vis.NSR = { 400 | bands:[this.band.nir, this.band.swir, this.band.red], 401 | min: 0, 402 | max: this.max/2 403 | } 404 | this.vis.RGB = { 405 | bands:[this.band.red, this.band.green, this.band.blue], 406 | min: 0, 407 | max: this.max/3 408 | } 409 | this.vis.falseColor = { 410 | bands:[this.band.nir, this.band.red, this.band.green], 411 | min: 0, 412 | max: this.max/2 413 | } 414 | 415 | switch (process) { 416 | case 'TOA': 417 | this.id = 'COPERNICUS/S2' 418 | this.cloud_masks = { 419 | 'qa': cld.sentinel2, 420 | 'hollstein': cld.hollstein_S2 421 | } 422 | this.cloud_mask = cld.sentinel2 423 | this.start_date = '2015-01-23' 424 | break 425 | case 'SR': 426 | this.id = 'COPERNICUS/S2_SR' 427 | this.cloud_masks = { 428 | 'scl': cld.sclMask 429 | } 430 | this.cloud_mask = cld.sclMask 431 | this.band['scl'] = 'SCL' 432 | this.band['aot'] = 'AOT' 433 | this.band['wvp'] = 'WVP' 434 | this.start_date = '2017-03-28' 435 | break 436 | } 437 | 438 | break 439 | } 440 | 441 | this.collection = ee.ImageCollection(this.id) 442 | this.shortName = SHORTS[this.id] 443 | } 444 | exports.Sentinel = Sentinel 445 | 446 | var Modis = function(product) { 447 | this.product = product 448 | this.base = 'MODIS/006/' 449 | switch (product) { 450 | case 'MOD09GQ': 451 | this.band = { 452 | 'num_observations': 'num_observations', 453 | 'red': 'sur_refl_b01', 454 | 'nir': 'sur_refl_b02', 455 | 'QC_250m': 'QC_250m', 456 | 'obscov': 'obscov', 457 | 'iobs_res': 'iobs_res', 458 | 'orbit_pnt': 'orbit_pnt', 459 | 'granule_pnt': 'granule_pnt' 460 | } 461 | this.scale = { 462 | 'num_observations': 250, 463 | 'red': 250, 464 | 'nir': 250, 465 | 'QC_250m': 250, 466 | 'obscov': 250, 467 | 'iobs_res': 250, 468 | 'orbit_pnt': 250, 469 | 'granule_pnt': 250 470 | } 471 | this.process = 'SR' 472 | this.min = 100 473 | this.max = 16000 474 | this.start_date = '2000-02-24' 475 | this.end_date = null 476 | break 477 | /* WIP 478 | case 'MOD09GA': 479 | this.band = { 480 | 'state_1km': 'state_1km', 481 | 'num_observations': 'num_observations', 482 | 'red': 'sur_refl_b01', 483 | 'nir': 'sur_refl_b02', 484 | 'QC_250m': 'QC_250m', 485 | 'obscov': 'obscov', 486 | 'iobs_res': 'iobs_res', 487 | 'orbit_pnt': 'orbit_pnt', 488 | 'granule_pnt': 'granule_pnt' 489 | } 490 | this.scale = { 491 | 'num_observations': 250, 492 | 'red': 250, 493 | 'nir': 250, 494 | 'QC_250m': 250, 495 | 'obscov': 250, 496 | 'iobs_res': 250, 497 | 'orbit_pnt': 250, 498 | 'granule_pnt': 250 499 | } 500 | this.process = 'SR' 501 | this.min = 100 502 | this.max = 16000 503 | this.start_date = '2000-02-24' 504 | this.end_date = null 505 | break 506 | */ 507 | } 508 | this.ndvi = indexes.NDVI[this.product] 509 | this.id = this.base+this.product 510 | this.collection = ee.ImageCollection(this.id) 511 | } 512 | exports.Modis = Modis 513 | 514 | var fromId = function(id) { 515 | var list = id.split('/') 516 | var sat = list[0] 517 | if (sat === 'LANDSAT') { 518 | var sensors = {'LT': 'TM', 'LM': 'MSS', 'LE': 'ETM', 'LC': 'OLI'} 519 | var number_sensor = list[1] 520 | var number = Number(number_sensor.substring(2,4)) 521 | var sensor = sensors[number_sensor.substring(0,2)] 522 | var tier_process = list[3] 523 | var tier = tier_process.split('_')[0].substring(1) 524 | var process = tier_process.split('_')[1] || 'RAW' 525 | return new Landsat(number, process, sensor, Number(tier)) 526 | } 527 | if (sat === 'COPERNICUS') { 528 | if (list[1] === 'S1_GRD') { 529 | return new Sentinel(1) 530 | } else if (list[1] === 'S2') { 531 | return new Sentinel(2, 'TOA') 532 | } else if (list[1] === 'S2_SR') { 533 | return new Sentinel(2, 'SR') 534 | } else if (list[1] === 'S3') { 535 | return new Sentinel(3) 536 | } 537 | } 538 | } 539 | exports.fromId = fromId 540 | 541 | var fromCollection = function(collection) { 542 | var id = collection.get('system:id').getInfo() 543 | return fromId(id) 544 | } 545 | exports.fromCollection = fromCollection 546 | 547 | // TEST 548 | var test = function() { 549 | var sats = [4,5,7,8] 550 | var process = ['RAW', 'TOA', 'SR'] 551 | var sensors = ['TM', 'MSS', 'ETM', 'OLI'] 552 | var tiers = [1, 2] 553 | 554 | for (var s in sats) { 555 | var sat = sats[s] 556 | if (sat !== 4) { continue } 557 | for (var p in process) { 558 | var proc = process[p] 559 | for (var se in sensors) { 560 | var sen = sensors[se] 561 | for (var t in tiers) { 562 | var tie = tiers[t] 563 | // 564 | var satellite = new Landsat(sat, proc, sen, tie) 565 | print(satellite.id) 566 | var col = satellite.collection.limit(1) 567 | var i = ee.Image(col.first()) 568 | var bands_dict = ee.Dictionary(satellite.bands) 569 | var inverse = ee.Dictionary.fromLists(bands_dict.values(), bands_dict.keys()) 570 | var bands = bands_dict.values() 571 | print(satellite.scale) 572 | /* 573 | var bands2 = i.bandNames() 574 | var cont = bands2.containsAll(bands) 575 | 576 | if (cont.getInfo() === false) { 577 | print('real', bands) 578 | print('module', bands2) 579 | } 580 | 581 | var ibands = bands.getInfo() 582 | 583 | for (var band in ibands) { 584 | var b = ibands[band] 585 | var name = inverse.get(b).getInfo() 586 | print(b, name) 587 | var ib = i.select(b) 588 | var scale_real = ib.projection().nominalScale().getInfo() 589 | var scale_mine = satellite.scale[name] 590 | print(scale_real === scale_mine) 591 | }*/ 592 | } 593 | } 594 | } 595 | //break 596 | } 597 | } --------------------------------------------------------------------------------