Google Earth Engine (GEE) is a powerful cloud-based system for analysing massive amounts of remote sensing data. One area where Google Earth Engine shines is the ability to calculate time series of values extracted from a deep stack of imagery. While GEE is great at crunching numbers, it has limited cartographic capabilities. That’s where QGIS comes in. Using the Google Earth Engine Plugin for QGIS and Python, you can combine the computing power of GEE with the cartographic capabilities of QGIS. In this post, I will show how to write PyQGIS code to programmatically fetch time-series data, and render a map template to create an animated maps like below.

In QGIS, go to Plugins → Manage and Install Plugins and install the ‘Google Earth Engine’ plugin. After installation, you will be prompted to authenticate using your Google Earth Engine account.

I prepared a map layout using the QGIS Print Layout and saved it as a template file. Download the template ndvi_map.qpt and save it to your Desktop. Note that for each frame of the animation, we will have to extract the image data and render the template with values for date_label, ndvi_label, and season_label.

We will use the Sentinel-2 Surface Reflectance (SR) data to compute the NDVI values for the year 2019 over a farm in northern India. The concept behind extracting the time series from an image collection is nicely demonstrated in this tutorial by Nicholas Clinton. This example adapts this code for Python and adds a few enhancements to pick images where 100% of farm area is cloud-free.
Earth Engine code integrates seamlessly with PyQGIS code. You can use the Earth Engine Python API just the way you would use it elsewhere in the Python Console. Open Plugins → Python Console. Click ‘Show Editor’ button to open the built-in editor. Copy/Paste the code below and click ‘Run’ to execute the code.

import ee
from ee_plugin import Map
import os
from datetime import datetime
# Script assumes you put the ndvi_map.qpt file on your Desktop
home_dir = os.path.join(os.path.expanduser('~'))
template = os.path.join(home_dir, 'Desktop', 'ndvi_map.qpt')
geometry = ee.Geometry.Polygon([[
[79.38757620268325, 27.45829434648896],
[79.38834214903852, 27.459092793050313],
[79.38789690234205, 27.459397436737895],
[79.38718343474409, 27.458592985177017]
]])
farm = ee.Feature(geometry, {'name': 'farm'})
fc = ee.FeatureCollection([farm])
Map.centerObject(fc)
empty = ee.Image().byte();
outline = empty.paint(**{
'featureCollection': fc,
'color': 1,
'width': 1
});
viz_params = {'bands': ['B4', 'B3', 'B2'], 'min': 0, 'max': 2000}
def maskCloudAndShadows(image):
cloudProb = image.select('MSK_CLDPRB')
snowProb = image.select('MSK_SNWPRB')
cloud = cloudProb.lt(5)
snow = snowProb.lt(5)
scl = image.select('SCL');
shadow = scl.eq(3) #3 = cloud shadow
cirrus = scl.eq(10) # 10 = cirrus
# Cloud and Snow probability less than 5% or cloud shadow classification
mask = (cloud.And(snow)).And(cirrus.neq(1)).And(shadow.neq(1))
return image.updateMask(mask);
def addNDVI(image):
ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi')
return image.addBands([ndvi])
start_date = '2019-01-01'
end_date = '2019-12-31'
collection = ee.ImageCollection('COPERNICUS/S2_SR')\
.filterDate(start_date, end_date)\
.map(maskCloudAndShadows)\
.map(addNDVI)\
.filter(ee.Filter.intersects('.geo', farm.geometry()))
def get_ndvi(image):
stats = image.select('ndvi').reduceRegion(**{
'geometry': farm.geometry(),
'reducer': ee.Reducer.mean().combine(**{
'reducer2': ee.Reducer.count(),
'sharedInputs': True}
).setOutputs(['mean', 'pixelcount']),
'scale': 10
})
ndvi = stats.get('ndvi_mean')
pixelcount = stats.get('ndvi_pixelcount')
return ee.Feature(None, {
'ndvi': ndvi,
'validpixels': pixelcount,
'id': image.id(),
'date': ee.Date(image.get('system:time_start')).format('YYYY-MM-dd')
})
with_ndvi = collection.map(get_ndvi)
# Find how many pixels in the farm extent
max_validpixels = with_ndvi.aggregate_max('validpixels').getInfo()
def select_color(ndvi):
ndvi_colors = {
0.3: QColor('#dfc27d'),
0.5: QColor('#c2e699'),
1: QColor('#31a354')}
for max_value, color in ndvi_colors.items():
if ndvi < max_value:
return color
def select_season(date_str):
seasons = {
'Kharif': [7, 8, 9,10],
'Rabi': [11, 12, 1, 2, 3],
'Summer': [4, 5, 6]
}
date = datetime.strptime(date_str, '%Y-%m-%d')
month = date.month
for season, months in seasons.items():
if month in months:
return season
features = with_ndvi.getInfo()['features']
for feature in features:
ndvi = feature['properties']['ndvi']
validpixels = feature['properties']['validpixels']
date = feature['properties']['date']
id = feature['properties']['id']
# The following condition ensures we pick images where
# all pixels in the farm geometry are unmasked
if ndvi and (validpixels == max_validpixels):
image = ee.Image(collection.filter(
ee.Filter.eq('system:index', id)).first())
Map.addLayer(image, viz_params, 'Image')
Map.addLayer(outline, {'palette': '0000FF'}, 'farm')
project = QgsProject.instance()
layout = QgsLayout(project)
layout.initializeDefaults()
with open(template) as f:
template_content = f.read()
doc = QDomDocument()
doc.setContent(template_content)
# adding to existing items
items, ok = layout.loadFromTemplate(doc, QgsReadWriteContext(), False)
ndvi_label = layout.itemById('ndvi_label')
ndvi_label.setText('{:.2f}'.format(ndvi))
ndvi_label.setFontColor(select_color(ndvi))
date_label = layout.itemById('date_label')
date_label.setText(date)
season = select_season(date)
season_label = layout.itemById('season_label')
season_label.setText(season)
exporter = QgsLayoutExporter(layout)
output_image = os.path.join(home_dir, 'Desktop', '{}.png'.format(date))
exporter.exportToImage(output_image, QgsLayoutExporter.ImageExportSettings())
The script would iterate through each image and render the template with appropriate image and data. All of the processing is done in the cloud using Google Earth Engine and only the results are streamed to QGIS. If all went well, in a few minutes, the system would have crunched through Gigabytes of data and you will have a bunch of images on your desktop. You can animate them using a program like ezgif.com

I try this code it’s work with this coordinates when I try it in my study area it run but there’s a problem with export images in desktop
I sent mail and attached my code
can you help me to solve the problem
thanks in advance
Thank you so much for sharing this work! It’s really amazing.
I have a doubt about the code: in this example a temporary monitoring is done based on a single polygon defined in line 10.
Is it possible to iterate on the different polygons within a .shp (being this uploaded as an asset to EE?
I think the logic in pseudocode would be to iterate over the polygon or ROI, and then apply this code to get the images and load them into Qgis.
Then, to export each image, you would have to center the zoom and extension on it in Qgis.
Could you please help me with this? Thanks!
Jorge
You don’t even need to upload the shapefile to Earth Engine. You can open the shapefile in QGIS, select the layer and run the following code to iterate over the features.
View formatted code
Hi Ujaval. I tried to run your code in qgis 3.10LTR. It worked but nvdi outputs (PGN files) were blank in my desktop, only your template but no NDVI image :(. There is no error in running the code, is it possible?
Thanks Ujaval for sharing such application. As you described the Plugin would ask for authentication but the the version 0.0.2 not asking such authentication after installation rather showing a error message such as ‘Couldn’t load plugin ‘ee_plugin’ due to an error when calling its classFactory() method…’. Do you have any idea how to resolve it?
I had the same issue. It needs re-authentication. See the FAQ on how to do it https://github.com/gee-community/qgis-earthengine-plugin#faq
Same problem as Nestor and faten above, the program works and NDVI images are properly mappes in canvas but the output is blank. Any indication to output the images is highly welcome! Thank you in advance!
You probably changed the location and tried other places. When you do so, you should also change the extent of Layour map from Layout Manager, in the file you downloaded from this webpage it is set to the locations used in this map.
Hello sir, Excuse me! I am having a problem in ee_plugin of QGIS with the code of
samples = image.sampleRegions({
‘collection’: classes,
‘properties’: [‘landcover’],
‘scale’: 30
}).randomColumn(‘random’)
I will share my code to be available to figure out my problem and I hope you could help me and I am looking forward to hearing from you soon!
#################
import ee
from ee_plugin import Map
#Start importing Landsat 8 and STRM 30meters
L8 = ee.ImageCollection(‘LANDSAT/LC08/C01/T1_SR’) #we can obtain this code from google earth engine website
SRTM = ee.Image(‘USGS/SRTMGL1_003’)
#1/ Set up the map
#Center the map to the region of interest using the region shapefile
#1.1/Create ROI in polygon
geometry = ee.FeatureCollection(
[ee.Feature(
ee.Geometry.Polygon(
[[[-59.86659594299564, 8.183991509034232],
[-59.48207445862064, 7.988203807396915],
[-59.11952563049564, 7.879392130916319],
[-58.87782641174564, 7.60723863000331],
[-58.69105883362064, 7.389391151065465],
[-58.51527758362064, 7.324015809133201],
[-58.49330492737064, 7.0297093172255245],
[-58.54823656799564, 6.844309047976527],
[-58.32851000549564, 6.844309047976527],
[-58.16371508362064, 6.7788563614612265],
[-57.94398852112064, 6.516957567165574],
[-57.73524828674564, 6.28768369655626],
[-57.69130297424564, 6.080158189079884],
[-57.46059008362064, 6.1347780402601995],
[-57.35072680237064, 6.036458312021658],
[-57.31776781799564, 5.774185907777155],
[-57.17494555237064, 5.643003818748446],
[-56.92226000549564, 5.806976712141076],
[-56.44984789612064, 5.796046655541718],
[-56.42787523987064, 5.894409498190616],
[-56.50477953674564, 6.036458312021658],
[-57.04310961487064, 6.069233551646879],
[-57.04310961487064, 6.178469894471565],
[-57.27382250549564, 6.331362818143105],
[-57.51552172424564, 6.495126416878322],
[-57.96596117737064, 6.909752761188248],
[-58.30653734924564, 7.313118984292941],
[-58.55922289612064, 7.694346835655885],
[-59.19642992737064, 8.183991509034232],
[-59.92152758362064, 8.575276164536835],
[-60.08632250549564, 8.36881384889053]]]),
{
“system:index”: “0”
})])
#Set up polygon of ROI (region of interest)
Map.addLayer(geometry, {‘color’: ‘FF0000’}, ‘geometry’) #add map layer with red color shape and name” geometry”
#2) Set up Filtered Landsat Composite
#2.1) Cloud Masking
#Landsat data includes a ‘pixel_qa’ band which can be used to create a function to mask clouds
def maskClouds(image) :
#Bits 3 and 5 are cloud shadow and cloud, respectively. from https://www.usgs.gov/core-science-systems/nli/landsat/landsat-sr-derived-spectral-indices-pixel-quality-band
cloudShadowBitMask = ee.Number(2).pow(3).int()
cloudsBitMask = ee.Number(2).pow(5).int()
#Get the pixel QA band.(the pixel quality assurance)
qa = image.select(‘pixel_qa’)
#Both flags should be set to zero, indicating clear conditions.
mask = qa.bitwiseAnd(cloudShadowBitMask).eq(0) and(qa.bitwiseAnd(cloudsBitMask).eq(0))
#Return the masked image, scaled to [0, 1].
return image.updateMask(mask).divide(10000).copyProperties(image, [‘system:time_start’])
#2) Set up Filtered Landsat Composite
#2.1) Cloud Masking
#Landsat data includes a ‘pixel_qa’ band which can be used to create a function to mask clouds
def maskClouds(image) :
#Bits 3 and 5 are cloud shadow and cloud, respectively. from https://www.usgs.gov/core-science-systems/nli/landsat/landsat-sr-derived-spectral-indices-pixel-quality-band
cloudShadowBitMask = ee.Number(2).pow(3).int()
cloudsBitMask = ee.Number(2).pow(5).int()
#Get the pixel QA band.(the pixel quality assurance)
qa = image.select(‘pixel_qa’)
#Both flags should be set to zero, indicating clear conditions.
mask = qa.bitwiseAnd(cloudShadowBitMask).eq(0) and(qa.bitwiseAnd(cloudsBitMask).eq(0))
#Return the masked image, scaled to [0, 1].
return image.updateMask(mask).divide(10000).copyProperties(image, [‘system:time_start’])
#2.2) Adding Spectral Indices is for mathematic equations to apply on various spectral bands
#This function maps spectral indices for Mangrove Mapping using Landsat 8 Imagery
def addIndicesL8 (img):
#NDVI Normalized Difference Vegetation Index
ndvi = img.normalizedDifference([‘B5′,’B4’]).rename(‘NDVI’)
#NDMI Normalized Difference Mangrove Index (https://doi.org/10.1080/2150704X.2016.1195935) – Shi et al 2016 – New spectral metrics for mangrove forest identification
ndmi = img.normalizedDifference([‘B7′,’B3’]).rename(‘NDMI’)
#MNDWI (Modified Normalized Difference Water Index – Hanqiu Xu, 2006)
mndwi = img.normalizedDifference([‘B3′,’B6’]).rename(‘MNDWI’)
#SR (Simple Ratio) (B4 is red and B5 is Near Infrared NIR)
sr = img.select(‘B5’).divide(img.select(‘B4’)).rename(‘SR’)
#Band Ratio 54 (I check in indices https://www.indexdatabase.de/db/i-single.php?id=20 I see it is ferrous oxides)
ratio54 = img.select(‘B6’).divide(img.select(‘B5’)).rename(‘R54’) #band 6 is Short Wave Infrared #band 5 is Near Infrared
#Band Ratio 35
ratio35 = img.select(‘B4’).divide(img.select(‘B6’)).rename(‘R35’)
#GCVI green chlorphyll vegetation Index (NIR and Green)
gcvi = img.expression(‘(NIR/GREEN)-1’,{
‘NIR’:img.select(‘B5’),
‘GREEN’:img.select(‘B3′)
}).rename(“GCVI”)
return img\
.addBands(ndvi)\
.addBands(ndmi)\
.addBands(mndwi)\
.addBands(sr)\
.addBands(ratio54)\
.addBands(ratio35)\
.addBands(gcvi)
#Filter Landsat data by date and region
#Temporal parameters
#select desire temporal year
#year = 2019
#startDate = (year – 1) #+’ -01-01 ‘
#endDate= (year + 1) #+’ -12-31 ‘
#Apply Filters and Masks to Landsat 8 Imagery
l8 = L8.filterDate(‘2018-01-01′,’2020-12-31’)\
.map(maskClouds)\
.map(addIndicesL8)
#Composite the Landsat Image Collection
composite = l8\
.median()\
.clip(geometry)
#Mask to Areas of Low Elevation and High NDVI and MNDWI
srtmClip = SRTM.clip(geometry)
elevationMask = srtmClip.lt(65)
NDVIMask = composite.select(‘NDVI’).gt(0.25)
MNDWIMask = composite.select(‘MNDWI’).gt(-0.50)
compositeNew = composite .updateMask(NDVIMask).updateMask(MNDWIMask) .updateMask(elevationMask)
#Display Results
visPar = {‘bands’:[‘B5′,’B6′,’B4’], ‘min’: 0,’max’: 0.35}
Map.addLayer(compositeNew.clip(geometry), visPar, ‘Landsat Composite 2019’)
#Add Training Areas for Mangrove Forest
Mangrove = ee.FeatureCollection(
[ee.Feature(
ee.Geometry.Polygon(
[[[-56.818476792365914, 5.985798163972564],
[-56.81611644843281, 5.987420053248756],
[-56.8102799616164, 5.987420053248756],
[-56.80787670233906, 5.9892553432012665],
[-56.80860626319111, 5.991090626986097],
[-56.8122969827956, 5.991560117195954],
[-56.81276905158222, 5.990151645355214],
[-56.81637394049824, 5.98874316988139],
[-56.819935914070015, 5.986438383998846]]]),
{
“landcover”: 1,
“system:index”: “0”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-56.80015194037617, 5.9863103400535955],
[-56.796504136115914, 5.987035922013213],
[-56.797748681098824, 5.988444401889661],
[-56.7992936334914, 5.989127299916325],
[-56.800452347785836, 5.98635302137202]]]),
{
“landcover”: 1,
“system:index”: “1”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-56.80259811499775, 5.985712801245717],
[-56.80800544837177, 5.981914146403828],
[-56.798950310737496, 5.982298281239187]]]),
{
“landcover”: 1,
“system:index”: “2”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-57.00670566043602, 5.969390611000828],
[-57.00314368686424, 5.968536957537676],
[-57.00177039584862, 5.974939326086341],
[-57.00739230594383, 5.970884501361134]]]),
{
“landcover”: 1,
“system:index”: “3”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-57.01285471351216, 5.961982264200126],
[-57.009292739940385, 5.957927343638916],
[-57.00487245948384, 5.955793162891705],
[-57.00491537482808, 5.959976149351207],
[-57.009807724071244, 5.963177392963958],
[-57.00817694099019, 5.964756666264999],
[-57.01161016852925, 5.964372519123846]]]),
{
“landcover”: 1,
“system:index”: “4”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-57.488664100697946, 6.334763619561866],
[-57.48360009007783, 6.3341238194942955],
[-57.475231597951364, 6.333995859385617],
[-57.47488827519746, 6.337365464998087],
[-57.485187957814645, 6.3389009741806195],
[-57.48862118535371, 6.335531378596004]]]),
{
“landcover”: 1,
“system:index”: “5”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-57.79273811396108, 6.581631352056014],
[-57.7922016721581, 6.579265241928807],
[-57.79170814569936, 6.5801392118471425],
[-57.791858349404194, 6.581482138236861],
[-57.79248062189565, 6.582164258186938]]]),
{
“landcover”: 1,
“system:index”: “6”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-57.78853241022573, 6.58190846331547],
[-57.789347801766255, 6.57932919099936],
[-57.787373695931294, 6.578242055680411],
[-57.785699997506, 6.58205767700651]]]),
{
“landcover”: 1,
“system:index”: “7”
})])
Map.addLayer(Mangrove,{“color”: “98ff00″},”Mangrove_Forest”)
#Add Training Areas for Non-Mangrove Forest
NonMangrove = ee.FeatureCollection(
[ee.Feature(
ee.Geometry.Point([-57.79338184412465, 6.584253244704557]),
{
“landcover”: 0,
“system:index”: “0”
}),
ee.Feature(
ee.Geometry.Point([-57.79338184412465, 6.583656392310747]),
{
“landcover”: 0,
“system:index”: “1”
}),
ee.Feature(
ee.Geometry.Point([-57.81085872225377, 6.603437085745056]),
{
“landcover”: 0,
“system:index”: “2”
}),
ee.Feature(
ee.Geometry.Point([-57.81085872225377, 6.60254184153283]),
{
“landcover”: 0,
“system:index”: “3”
}),
ee.Feature(
ee.Geometry.Point([-57.81042956881139, 6.601561334109304]),
{
“landcover”: 0,
“system:index”: “4”
}),
ee.Feature(
ee.Geometry.Point([-57.81424903444859, 6.605057047346009]),
{
“landcover”: 0,
“system:index”: “5”
}),
ee.Feature(
ee.Geometry.Point([-57.810687060876816, 6.598875586440279]),
{
“landcover”: 0,
“system:index”: “6”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-57.826694484277695, 6.612900996902869],
[-57.827466960473984, 6.610172680734986],
[-57.82536410860631, 6.6073590889184475],
[-57.82450580172154, 6.609021667835791],
[-57.82583617739293, 6.610343200936609]]]),
{
“landcover”: 0,
“system:index”: “7”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-57.83555002683317, 6.6013392506755055],
[-57.837609963356606, 6.593750906782822],
[-57.83542128080045, 6.590937221651177],
[-57.831902222572914, 6.594774061052865],
[-57.8323313760153, 6.598312453283749]]]),
{
“landcover”: 0,
“system:index”: “8”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-58.02768695698738, 6.744142136969142],
[-58.034553412065506, 6.727435440231387],
[-58.015327337846756, 6.720616215116252],
[-58.01429736958504, 6.74959726019283]]]),
{
“landcover”: 0,
“system:index”: “9”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-58.006267008140306, 6.7770269104871295],
[-58.00641721184514, 6.7738946617575575],
[-58.004056867912034, 6.7734258880888465],
[-58.00264066155217, 6.77481089988553],
[-58.00386374886296, 6.775876290868889]]]),
{
“landcover”: 0,
“system:index”: “10”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-58.014270719840745, 6.774256895644079],
[-58.012296614005784, 6.7737668144385355],
[-58.00922816689275, 6.772722726731655],
[-58.00944274361394, 6.77713344920177],
[-58.01358407433293, 6.776387677704869]]]),
{
“landcover”: 0,
“system:index”: “11”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-57.22473172938481, 5.979085344036318],
[-57.224045083876995, 5.973963493971241],
[-57.217006967421916, 5.975500054023467],
[-57.21760778224125, 5.980280435486353]]]),
{
“landcover”: 0,
“system:index”: “12”
}),
ee.Feature(
ee.Geometry.Polygon(
[[[-57.230739877578166, 5.999486903784929],
[-57.229538247939495, 5.9970968023253635],
[-57.22722081935063, 5.997608967805745],
[-57.22764997279301, 5.999486903784929],
[-57.22945241725102, 6.00042586934887]]]),
{
“landcover”: 0,
“system:index”: “13”
})])
Map.addLayer(NonMangrove,{“color”: “0b4a8b”},”Non_mangrove_forest”)
#3) Construct Random Forest Model
#3.1) Prepare training data and predictors
#After drawing training polygons, merge them together
classes = Mangrove.merge(NonMangrove)
#Define the bands you want to include in the model
bands = [‘B5′,’B6′,’B4′,’NDVI’,’MNDWI’,’SR’,’GCVI’]
#Create a variable called image to select the bands of interest and clip to geometry
image = compositeNew.select(bands).clip(geometry)
#Assemble Samples for the model
samples = image.sampleRegions({
‘collection’: classes,
‘properties’: [‘landcover’],
‘scale’: 30
}).randomColumn(‘random’)
Hi Ujaval
Can we export the image or image collection to local computer? If yes, could you please provide a working example?
You can only export to Google Drive or Cloud Storage. To get the exported data to your local computer, you will have to download it from Google Drive. To make this seamless, I export all assets to a specific folder in drive and have the Google Drive client on my computer sync that folder. So as soon as the asset is exported, it gets downloaded.
I have doubt regarding End to End google earth engine in the section 01. Basic Supervised Classification
I try to run the code with my area of interest but it shows error
2020: Layer error: Collection.loadTable: Collection asset ‘training’ not found.Can you help me out
This error means you don’t have any asset named training in your script. If you uploaded your own shapefile, you need to import it to the script and use the same name of the asset in the script. For help with the content of that course, please email at ujaval@spatialthoughts.com. Make sure to include your script and share asset as per instructions at https://courses.spatialthoughts.com/end-to-end-gee.html#sharing-a-single-script (Read the whole section and do all the steps. If you share the wrong link or don’t share your asset, I won’t be able to help)
Hello Ujaval.
Thank you very much for sharing the script.
It works in my setup (QGIS 3.22.4, GEE plugin 0.0.6 & ubuntu 22.04) but I get the following error messages
“Mapflow
Error checking raster layers validity: QgsDataProvider.dataSourceUri(): argument 1 has unexpected type ‘NoneType”
In a emerging window.
“Traceback (most recent call last):
File “/home/user/.local/share/QGIS/QGIS3/profiles/default/python/plugins/mapflow/mapflow.py”, line 323, in filter_bad_rasters
self.dlg.rasterCombo.setExceptedLayerList(filtered_raster_layers)
UnboundLocalError: local variable ‘filtered_raster_layers’ referenced before assignment”
In python console.
Can you help me?
Regards,
Leandro, from Argentina.