Cómo instalar geemap y django en Ubuntu 20.04.1
Abstract
Django es un framework Python con todas las funciones necesarias para crear aplicaciones web complejas escrito en Python. El motor de Google Earth proporciona la API de Python.
Suponemos que tenemos una versión Ubuntu desktop 20.04.1 recién instalada, que puedes descargar desde aquí en una máquina virtual de Oracle VM Virtualbox sin actualizaciones.
Es muy importante que lo primero que hagas sea desactivar las opciones de descarga automática de paquetes para evitar tener conflictos con procesos ya abiertos en nuestras instalaciones.
2.1. Haz un enlace simbólico del binario de Python3 ‘/usr/bin/python3’ a ‘/usr/bin/python’ como comando de Python predeterminado usando el siguiente comando:
~$ sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 1
2.2. Instala pip para poder hacer nuestras descargas:
~$ sudo apt install python3-pip -y --fix-missing
2.3. Una vez completa la instalación, haz que el comando ‘pip3’ sea la versión predeterminada de ‘pip’.
~$ sudo update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1
2.5. Instala Visual Studio Code:
~$ sudo snap install --classic code
3.1. Descarga el .rar que nos servirá de ejemplo base desde aquí. Llevemos la carpeta descomprimida al escritorio.
3.2. Dirijámonos desde consola a la carpeta que hemos descargado y descomprimido
~/Escritorio/Django-GEE-master$
3.3. Instalemos la herramienta que nos permite crear entornos virtuales:
~/Escritorio/Django-GEE-master$ sudo apt-get install python3-venv
3.3. Construimos un entorno virtual llamado entornogee:
~/Escritorio/Django-GEE-master$ python3 -m venv entornogee
3.4. Lo activamos:
~/Escritorio/Django-GEE-master$ source entornogee/bin/activate
(entornogee) c@c:~/Escritorio/Django-GEE-master$
3.6. Instala Django en el entorno virtual:
(entornogee) c@c:~/Escritorio/Django-GEE-master$ pip install django
3.7. Instalemos dos librerias que vamos a necesitar:
(entornogee) c@c:~/Escritorio/Django-GEE-master$ pip install folium
(entornogee) c@c:~/Escritorio/Django-GEE-master$ pip install ee
(entornogee) c@c:~/Escritorio/Django-GEE-master$ pip install geemap
3.8. Corramos el servidor:
(entornogee) c@c:~/Escritorio/Django-GEE-master$ python manage.py runserver
Se te desplegará un error relacionado al tipo StringIO.
Éste error aparece porque no nos hemos autenticado en GEE. Para hacerlo, debemos seguir los siguientes pasos (la referencia la puedes encontrar aquí).
4.1 Ejecuta la siguiente línea para descargar/instalar el cliente de la API de Python:
(entornogee) c@c:~/Escritorio/Django-GEE-master$ pip install google-api-python-client
4.2 Asegurémosnos de tener instaladas las bibliotecas de cifrado adecuadas:
(entornogee) c@c:~/Escritorio/Django-GEE-master$ python -c "from oauth2client import crypt"
4.3 Si obtienes un mensaje de error, deberás instalar las bibliotecas de cifrado adecuadas con el siguiente comando:
(entornogee) c@c:~/Escritorio/Django-GEE-master$ pip install pyCrypto
4.4 Instala la biblioteca de Python de Earth Engine:
(entornogee) c@c:~/Escritorio/Django-GEE-master$ pip install earthengine-api
4.5 Ejecuta lo siguiente para inicializar la API y verificar tu cuenta:
(entornogee) c@c:~/Escritorio/Django-GEE-master$ python -c "import ee; ee.Initialize()"
4.6 Lo anterior dará como resultado un mensaje de error debido al hecho de que Google aún necesita verificar su cuenta con Earth Engine y actualmente no tiene las credenciales adecuadas. Por lo tanto, ejecuta:
(entornogee) c@c:~/Escritorio/Django-GEE-master$ earthengine authenticate
La última línea te abrirá un navegador donde te debes logear en gee para obtener un token validador.
Y ahora si se desplegará el mapa. Copia http://127.0.0.1:8000 en cualquier navegador y lo verás.
(entornogee) c@c:~/Escritorio/Django-GEE-master$ python manage.py runserver
5.1 El siguiente trozo de código lo adaptamos para que opere con geemap:
from django.shortcuts import render
from django.views.generic import TemplateView
import folium
from folium import plugins
import geemap.eefolium as geemap
import ee
ee.Initialize()
class home(TemplateView):
template_name = 'index.html'
def get_context_data(self, **kwargs):
figure = folium.Figure()
m = geemap.Map()
m.add_basemap('HYBRID')
landcover = ee.Image('USGS/NLCD/NLCD2016').select('landcover')
m.addLayer(landcover, {}, 'NLCD Land Cover')
m.add_legend(builtin_legend='NLCD')
m.add_to(figure)
m.add_child(folium.LayerControl())
figure.render()
print('test')
return {"map": figure}
5.1 Tenemos un código original JavaScript sobre el cual trabajar. Lo primero será traducirlo a Python con las herramientas de Geemap.
Código original JavaScript:
//-----------------------------------------------Mapa izquierdo--------------------------------------------//
//**********************************************---------------********************************************//
//Panel 1: Izquierda
var panel = ui.Panel();
panel.style({shown: false, backgroundColor: 'rgba(255, 255, 255, 0.8)', fontSize: '11px', fontWeight: '500'}).set({
width: '250px',
position: 'bottom-left',
//border : '1px solid #000000',
backgroundColor: 'rgba(255, 255, 255, 0.5)',
});
var map_left = ui.Map();var Cobertura_CA_2010_GIZ= ee.Image("users/efrainduarte/TiposBosqueLandsat1");
var data_GIZ = Cobertura_CA_2010_GIZ.clip(studyarea);
var palette= ['#006400', //Bosque Latifoliado Maduro 0
'#32CD32', //Bosque de Coníferas 1
'#556B2F', //Bosque Mixto 2
'#BA55D3', //Bosque de Mangle 3
'#FFA500', //Bosque y/o Matorral Seco 4
'#9ACD32', //Bosque Latifoliado Intervenido 5
'#FFFF00', //Pastos y/o cultivos 6
'#FFFFE0', //Sabanas de Pino 7
'#1E90FF', //Cuerpos de Agua 8
'#696969', //No Clasificado 9
'#A0522D', //Café con Sombra 10
];
var palette2= ['32CD32', //Bosque Coníferas 1
'9ACD32', //Bosque Latifoliado Intervenido 5
'006400', //Bosque Latifoliado Maduro 0
'BA55D3', //Bosque Mangle 3
'556B2F', //Bosque Mixto 2
'FFA500', //Bosque y/o Matorral Seco 4
'A0522D', //Café con Sombra 10
'1E90FF', //Cuerpos de Agua 8
'696969', //No Clasificado 9
'FFFF00', //Pastos y/o cultivos 6
'FFFFE0', //Sabanas de Pino 7
];
var igbpLandCoverVis = {
min: 0,
max: 10,
palette: palette}
//Map.setCenter(-88.6, 26.4, 1);
//Area Calculation
var names = ['0-Bosque Latifoliado Maduro',
'1-Bosque Coníferas',
'2-Bosque Mangle',
'3-Bosque Mixto',
'4-Bosque Matorral Seco',
'5-Bosque Latifoliado Intervenido',
'6-Pastos y/o cultivos',
'7-Sabanas de Pino',
'8-Cuerpos de Agua',
'9-No Clasificado',
'10-Café con Sombra',
]
var count = data_GIZ.eq([0,1,2,3,4,5,6,7,8,9,10]).rename(names);
var total = count.multiply(ee.Image.pixelArea()).divide(1000*1000);
var area = total.reduceRegion({
reducer:ee.Reducer.sum(),
geometry:studyarea,
scale:1000,
maxPixels: 1e12,
bestEffort:true,
tileScale: 16
});
var area_pxa = ee.Number(area);
var chart = ui.Chart.image.regions({
image: total,
regions: studyarea,
reducer: ee.Reducer.sum(),
scale: 10,
})
.setChartType('PieChart').setOptions({
width: 250,
height: 350,
title: 'Área por categoría (km²)',
is3D: true,
colors: palette
});
//print ('Área por categoría (km²):', area_pxa);
//print ('Área por categoría (km²):', chart);
//Add the point to the map
panel.add(chart)
map_left.addLayer(data_GIZ,igbpLandCoverVis, "Cobertura GIZ 2010");
map_left.addLayer(studyarea);
map_left.centerObject(studyarea)
//-----------------------------------------------Mapa Derecho----------------------------------------------//
//**********************************************---------------********************************************//
var map_right = ui.Map();
//Panel 2: Derecho
var panel2 = ui.Panel();
panel2.style({shown: false, backgroundColor: 'rgba(255, 255, 255, 0.8)', fontSize: '11px', fontWeight: '500'}).set({
width: '250px',
position: 'bottom-right',
//border : '1px solid #000000',
backgroundColor: 'rgba(255, 255, 255, 0.5)',
});
//To display as a map
var gfc2019 = ee.Image('UMD/hansen/global_forest_change_2019_v1_7');
//forest loss to show up as bright red and forest gain to show up as bright blue.
var mask8 = gfc2019.clip(studyarea);
map_right.addLayer(mask8, {
bands: ['loss', 'treecover2000', 'gain'],
max: [1, 255, 1]
}, 'Forest cover, loss, gain');
//Whole script
var lossImage = gfc2019.select(['loss']);
var mask2 = lossImage.clip(studyarea);
var gainImage = gfc2019.select(['gain']);
var mask3 = gainImage.clip(studyarea);
// Add the loss layer in red.
map_right.addLayer(mask2.updateMask(lossImage),
{palette: ['FF0000']}, 'Loss');
// Add the gain layer in blue.
map_right.addLayer(mask3.updateMask(gainImage),
{palette: ['0000FF']}, 'Gain');
// Get the loss image.
// This dataset is updated yearly, so we get the latest version.
var gfc2019 = ee.Image('UMD/hansen/global_forest_change_2019_v1_7');
var lossImage = gfc2019.select(['loss']);
var lossAreaImage = lossImage.multiply(ee.Image.pixelArea().divide(10000));
var lossYear = gfc2019.select(['lossyear']);
var lossByYear = lossAreaImage.addBands(lossYear).reduceRegion({
reducer: ee.Reducer.sum().group({
groupField: 1
}),
geometry: studyarea,
scale: 30,
maxPixels: 1e9
});
//Notice that we are using the format() method to convert the year values from 0-18 to 2000-2018.
var statsFormatted = ee.List(lossByYear.get('groups'))
.map(function(el) {
var d = ee.Dictionary(el);
return [ee.Number(d.get('group')).format("20%02d"), d.get('sum')];
});
var statsDictionary = ee.Dictionary(statsFormatted.flatten());
//prepare a chart
var chart = ui.Chart.array.values({
array: statsDictionary.values(),
axis: 0,
xLabels: statsDictionary.keys()
}).setChartType('ColumnChart')
.setOptions({
title: 'Pérdida anual de Bosque',
hAxis: {title: 'Años', format: '####'},
vAxis: {title: 'Area (Ha)'},
legend: { position: "none" },
lineWidth: 1,
pointSize: 3,
colors: ['Salmon']
});
panel2.add(chart);
// Get the Gain image.
// This dataset is updated yearly, so we get the latest version.
var gfc20199 = ee.Image('UMD/hansen/global_forest_change_2019_v1_7');
var gainImage = gfc20199.select(['gain']);
var gainAreaImage = gainImage.multiply(ee.Image.pixelArea().divide(10000));
var gainYear = gfc20199.select(['gain']);
var gainByYear = gainAreaImage.addBands(gainYear).reduceRegion({
reducer: ee.Reducer.sum().group({
groupField: 1
}),
geometry: studyarea,
scale: 30,
maxPixels: 1e9
});
print(gainByYear);
//Notice that we are using the format() method to convert the year values from 0-18 to 2000-2018.
var statsFormatted = ee.List(gainByYear.get('groups'))
.map(function(el) {
var d = ee.Dictionary(el);
return [ee.Number(d.get('group')).format("20%01d"+"2"), d.get('sum')];
})
var statsDictionary = ee.Dictionary(statsFormatted.flatten());
//prepare a chart
var chart2 = ui.Chart.array.values({
array: statsDictionary.values(),
axis: 0,
xLabels: statsDictionary.keys()
}).setChartType('ColumnChart')
.setOptions({
title: 'Ganancia de Bosque durante el 2000-2012',
hAxis: {title: 'Años', format: '####'},
vAxis: {title: 'Area (Ha)'},
legend: { position: "none" },
lineWidth: 1,
pointSize: 3,
colors: ['GreenYellow']
});
panel2.add(chart2)
map_right.addLayer(studyarea);
map_right.centerObject(studyarea);
ui.root.clear();
ui.root.add(map_left);
ui.root.insert(0, panel);
ui.root.add(map_right);
ui.root.insert(4, panel2);
ui.Map.Linker([map_left, map_right], 'change-bounds');
Código versión Python:
import ee
import geemap
Map = geemap.Map()
Cobertura_CA_2010_GIZ= ee.Image("users/efrainduarte/TiposBosqueLandsat1")
data_GIZ = Cobertura_CA_2010_GIZ.clip(studyarea)
palette= ['#006400', #Bosque Latifoliado Maduro 0
'#32CD32', #Bosque de Coníferas 1
'#556B2F', #Bosque Mixto 2
'#BA55D3', #Bosque de Mangle 3
'#FFA500', #Bosque y/o Matorral Seco 4
'#9ACD32', #Bosque Latifoliado Intervenido 5
'#FFFF00', #Pastos y/o cultivos 6
'#FFFFE0', #Sabanas de Pino 7
'#1E90FF', #Cuerpos de Agua 8
'#696969', #No Clasificado 9
'#A0522D', #Café con Sombra 10
]
palette2= ['32CD32', #Bosque Coníferas 1
'9ACD32', #Bosque Latifoliado Intervenido 5
'006400', #Bosque Latifoliado Maduro 0
'BA55D3', #Bosque Mangle 3
'556B2F', #Bosque Mixto 2
'FFA500', #Bosque y/o Matorral Seco 4
'A0522D', #Café con Sombra 10
'1E90FF', #Cuerpos de Agua 8
'696969', #No Clasificado 9
'FFFF00', #Pastos y/o cultivos 6
'FFFFE0', #Sabanas de Pino 7
]
igbpLandCoverVis = {
'min': 0,
'max': 10,
'palette': palette}
#Map.setCenter(-88.6, 26.4, 1)
#Area Calculation
names = ['0-Bosque Latifoliado Maduro',
'1-Bosque Coníferas',
'2-Bosque Mangle',
'3-Bosque Mixto',
'4-Bosque Matorral Seco',
'5-Bosque Latifoliado Intervenido',
'6-Pastos y/o cultivos',
'7-Sabanas de Pino',
'8-Cuerpos de Agua',
'9-No Clasificado',
'10-Café con Sombra',
]
count = data_GIZ.eq([0,1,2,3,4,5,6,7,8,9,10]).rename(names)
total = count.multiply(ee.Image.pixelArea()).divide(1000*1000)
area = total.reduceRegion({
'reducer':ee.Reducer.sum(),
'geometry':studyarea,
'scale':1000,
'maxPixels': 1e12,
'bestEffort':True,
'tileScale': 16
})
area_pxa = ee.Number(area)
chart = ui.Chart.image.regions({
'image': total,
'regions': studyarea,
'reducer': ee.Reducer.sum(),
'scale': 10,
}) \
.setChartType('PieChart').setOptions({
'width': 250,
'height': 350,
'title': 'Área por categoría (km²)',
'is3D': True,
'colors': palette
})
#print ('Área por categoría (km²):', area_pxa)
#print ('Área por categoría (km²):', chart)
#Add the point to the map
panel.add(chart)
map_left.addLayer(data_GIZ,igbpLandCoverVis, "Cobertura GIZ 2010")
map_left.addLayer(studyarea)
map_left.centerObject(studyarea)
#-----------------------------------------------Mapa Derecho----------------------------------------------#
#**********************************************---------------********************************************#
map_right = ui.Map()
#Panel 2: Derecho
panel2 = ui.Panel()
panel2.style({'shown': False, 'backgroundColor': 'rgba(255, 255, 255, 0.8)', 'fontSize': '11px', 'fontWeight': '500'}).set({
'width': '250px',
'position': 'bottom-right',
#border : '1px solid #000000',
'backgroundColor': 'rgba(255, 255, 255, 0.5)',
})
#To display as a map
gfc2019 = ee.Image('UMD/hansen/global_forest_change_2019_v1_7')
#forest loss to show up as bright red and forest gain to show up as bright blue.
mask8 = gfc2019.clip(studyarea)
map_right.addLayer(mask8, {
'bands': ['loss', 'treecover2000', 'gain'],
'max': [1, 255, 1]
}, 'Forest cover, loss, gain')
#Whole script
lossImage = gfc2019.select(['loss'])
mask2 = lossImage.clip(studyarea)
gainImage = gfc2019.select(['gain'])
mask3 = gainImage.clip(studyarea)
# Add the loss layer in red.
map_right.addLayer(mask2.updateMask(lossImage),
{'palette': ['FF0000']}, 'Loss')
# Add the gain layer in blue.
map_right.addLayer(mask3.updateMask(gainImage),
{'palette': ['0000FF']}, 'Gain')
# Get the loss image.
# This dataset is updated yearly, so we get the latest version.
gfc2019 = ee.Image('UMD/hansen/global_forest_change_2019_v1_7')
lossImage = gfc2019.select(['loss'])
lossAreaImage = lossImage.multiply(ee.Image.pixelArea().divide(10000))
lossYear = gfc2019.select(['lossyear'])
lossByYear = lossAreaImage.addBands(lossYear).reduceRegion({
'reducer': ee.Reducer.sum().group({
'groupField': 1
}),
'geometry': studyarea,
'scale': 30,
'maxPixels': 1e9
})
#Notice that we are using the format() method to convert the year values from 0-18 to 2000-2018.
statsFormatted = ee.List(lossByYear.get('groups'))
def func_dyo(el):
d = ee.Dictionary(el)
return [ee.Number(d.get('group')).format("20%02d"), d.get('sum')] \
.map(func_dyo)
statsDictionary = ee.Dictionary(statsFormatted.flatten())
#prepare a chart
chart = ui.Chart.array.values({
'array': statsDictionary.values(),
'axis': 0,
'xLabels': statsDictionary.keys()
}).setChartType('ColumnChart') \
.setOptions({
'title': 'Pérdida anual de Bosque',
'hAxis': '{title': 'Años', 'format': '####'},
'vAxis': '{title': 'Area (Ha)'},
'legend': '{ position': "none" },
'lineWidth': 1,
'pointSize': 3,
'colors': ['Salmon']
})
panel2.add(chart)
# Get the Gain image.
# This dataset is updated yearly, so we get the latest version.
gfc20199 = ee.Image('UMD/hansen/global_forest_change_2019_v1_7')
gainImage = gfc20199.select(['gain'])
gainAreaImage = gainImage.multiply(ee.Image.pixelArea().divide(10000))
gainYear = gfc20199.select(['gain'])
gainByYear = gainAreaImage.addBands(gainYear).reduceRegion({
'reducer': ee.Reducer.sum().group({
'groupField': 1
}),
'geometry': studyarea,
'scale': 30,
'maxPixels': 1e9
})
print(gainByYear)
#Notice that we are using the format() method to convert the year values from 0-18 to 2000-2018.
statsFormatted = ee.List(gainByYear.get('groups'))
def func_btk(el):
d = ee.Dictionary(el)
return [ee.Number(d.get('group')).format("20%01d"+"2"), d.get('sum')] \
.map(func_btk)
statsDictionary = ee.Dictionary(statsFormatted.flatten())
#prepare a chart
chart2 = ui.Chart.array.values({
'array': statsDictionary.values(),
'axis': 0,
'xLabels': statsDictionary.keys()
}).setChartType('ColumnChart') \
.setOptions({
'title': 'Ganancia de Bosque durante el 2000-2012',
'hAxis': '{title': 'Años', 'format': '####'},
'vAxis': '{title': 'Area (Ha)'},
'legend': '{ position': "none" },
'lineWidth': 1,
'pointSize': 3,
'colors': ['GreenYellow']
})
panel2.add(chart2)
map_right.addLayer(studyarea)
map_right.centerObject(studyarea)
ui.root.clear()
ui.root.add(map_left)
ui.root.insert(0, panel)
ui.root.add(map_right)
ui.root.insert(4, panel2)
ui.Map.Linker([map_left, map_right], 'change-bounds')
Map