We welcome you to the 3ie tutorial on applications of remote sensing for impact evaluation. Our hope is that equipped with the theoretical considerations and the practical hands-on code, you will be in a better position to decide which application of remote sensing is suitable for your work and what can be done to calculate the same. Moreover, we believe that laden with this geospatial literacy of earth observation, you can generate evidence faster in a cost-effective manner and over a large area. With extensive on-ground surveys limited by the COVID-19 pandemic and socio-political scenarios, remote sensing can help you to scale your work effectively.
This tutorial uses Python and Jupyter Notebooks. Python is a general purpose programming language that is used extensively in the field of machine learning and data analytics. Jupyter Notebooks are executable notebooks which allow us to document and run our code simultaneoulsy using Markdown. To reduce the access barrier, we are sharing this tutorial as a Google Colab Notebook, so that users can run the code and access the tutorial without downloading any software if they desire to do so.
We provide a general guidance on usage of these software. However, we believe that prior experience of basic programming will be helpful.
The tutorial is divided into four modules. We recommend Module 1 for the audience who are not familiar with the concepts of earth observation using remote sensing. It may include commissioners of evaluation, as well as leadership staff who want to be informed consumers of earth observation services. The module introduces the core concepts, opportunities, and challenges associate with remote sensing. The Modules 2, 3, and 4 are geared more towards an audience who have fundamental familiarity with Python and want to use it for doing earth observation. The only prerequisite for Module 2, 3, and 4 is familiarity with basics of coding, in particular Python, although we believe that those who have worked on languages such as R or Javascript may also find it easy to follow the tutorial.
Hands-on exercises are provided throughout the tutorial to provide readers with ample opportunities for practicing what they are learning. Additional Resources are provided at the end of the tutorial for those who would like to delve deeper into the field of remote sensing and its use for causal inference.
Please note that this tutorial is geared towards an audience which is primarily interested in measuring agriculture and water outcomes using Remote Sensing. Further applications of remote sensing such as population density estimation, urban sprawl measurement, object detection, land cover estimation, district/block level economic activity estimation, measurement of pollution indicators, species and biodiversity classification, city-wide solar power generation estimation, waterlogging estimation post flooding etc are some of the current applications of remote sensing which are not part of the scope of current tutorial, but could be developed if useful.
After completing this tutorial successfully, a learner will be able to do the following:
For instance, one may be interested in measuring the impact of an intervention on developing sustainable agriculture or checking deforestation. In this case counting the number of trees may not always yield into measurable and accurate outcomes, however measuring the greenery as shown by a vegetation around the intervention unit may give us valuable information. Such an information can be easily quanitified alongside other covariates of interest into an econometric model to investigate the causal effects of that intervention.
The term remote sensing is made up of two words: remote and sensing. Remote means being far away from the unit of observation (field, building, person) and sensing means using digital sensors to measure the data across time and space. Thus, remote sensing is the science of collecting measurements on units without coming in touch with them. Satellite Imagery is one form of remotely sensed data. For this tutorial, we will focus on satellite imagery only.
Source: NASA’s Goddard Space Flight Center. Remotely Sensing Our Planet. 2017. URL: https://svs.gsfc.nasa.gov/30892
The sensors on satellites can be classified into two main categories: Passive and Active
Passive Sensors: Passive Sensors record the energy that is naturally reflected from the surface of earth. For example, reflection of sunlight in a desert region will be different from the reflection of sunlight from a water body. Passive Sensors capture this differentiated energy reflection.
Active Sensors: Active Sensors provide their own energy source for differentiated energy reflection. For example, RADARs send out electromagnetic waves via the instrument on board a system and then captures the reflection.
The Electromagnetic (EM) Spectrum is the range of all types of EM radiations that exist around us. As humans, our eyes are only sensitive to the visible portion of EM spectrum, which we call as Visible Spectrum. However, there are other waves in the EM Spectrum that are of special use to remote sensing especially Infrared Waves (IR Waves) and Visible Light.
Although most of the satellites use EM for their operations, some satellites have started using RADAR for earth observation. The major advantages of RADAR based systems are their ability to generate an image when there are clouds, so this is particularly helpful for regions with distinct wet and dry season or areas which receive rainfall for whole year, like tropical rainforests. For this tutorial, we will focus on EM-based imagery only.
SOURCE: Imperial Encyclopedia Traveller, Marc Miller, July 2019
The fundamental technology that is behind the remote sensing is differentiated reflection of EM waves by different objects. The sun's energy which is composed of full EM Spectrum is reflected differently by different objects on the planet. For example, healthy plants that are lush green will reflect the sun's energy in a different way as compared to the plants that are dull and are under stress. The sensors which are on board these satellites capture these differentiated amounts of reflections and use it to construct the satellite imagery as we see. These data help us to identify the changes happening on the planet remotely.
SOURCE: Linking Student Performance in Massachusetts Elementary Schools with the "Greenness'' of School Surroundings Using Remote Sensing, PubMed, DOI
Evidence-based decision-making is becoming more and more common in policy, but the cost of generating evidence continues to increase. Add to this fact that some of the regions of the world, where development interventions are most needed may not always be accessible due to natural and social causes. Keeping these issues in mind, remote sensing promises to be an alternative that can be readily used in a cost-effective manner to assist in measuring the impact of development interventions in LMICs. Doing remote sensing is not a replacement for traditional surveys. It is actually a complement to make traditional data collection better and especially useful when any other means of surveying is not possible.
There are multiple benefits and applications of using remote sensing data for impact evaluation purposes.
In contrast to ground-based data, remote sensing data allows us to capture larger areas at a relatively lower cost. For example, remote sensing was used to do earthquake damage assessment in Italy as highlighted here.
Remote Sensing allows us to ask more relevant questions where we see a geographic element plays an important role. For example, it may allow us to measure spillover effect, as shown in this study which measured spatial spillover effects of a reclaimed mining subsided lake in China.
The sampling bias is lower as data can be collected for harder to reach areas impacted by geographic or political constraints. Thus, we can have a more credible identification strategy. For example, this study does drought evaluation in the North East Region of Thailand.
Remote sensing data can be used to calculate various indices such as vegetation indices or water indices, or pollutants indices to capture the effects of a treatment in an area remotely. For example, it has been used for detection and monitoring of marine pollution.
Deep Learning (making computers learn on the basis of huge amounts of data) based Computer Vision (making computers see and interpret images like humans do) methods allow us to do efficient program targeting to know where to put in our resources and who are the people who would benefit most from the interventions. For example, deep learning and remote sensing were used to locate the poor in Nigeria.
Note that Deep Learning may not always be need for doing an analysis that requires satellite imagery. For example making a land-usage map using satellite imagery may not always require one to perform deep learning computations. For motivated learners who are interested in knowing what is possible, we recommend going to this resource
It is also worth mentioning that the advanced technologies such as getting high resolution commerical imagery and performing deep learning based computer vision algorithms may have cost and human resources implications which may not always be readily available with organizations based in LMICs.
#### Ground Truthing We may always be not sure if what remote sensing shows us is actually true, because it is "remotely sensed". For example, if benefits from a development programme are directed to the households with a straw roof, then we need to ensure that houses below the straw roof are actually mud houses, and have not been masked by straw roofing for getting the benefits. However, if we need to measure impact remotely and there is no chance we can go ground truthing, it will be better to apply remote sensing to measure at least what's possible.
### Cost Implications for High Resolution Data Because many of the freely available satellite data do not have the granularity that can be used to make the inferences on, let's say smaller plots in LMICs or object detection, remote sensing can have cost and administrative implications. It is worth noting that geospatial analysis is a broad endeavour encompasses many different things. The mapping part and conversion of imagery to numbers within particular user-defined boundaries may be feasible within a few weeks, but extra time may be required for other, more involved work - e.g., spatial statistics.
#### Crop Type and Plot Boundaries (Lack of Vector Datasets) For doing a plot/small area level analysis, one may need to have a clear idea of what the plot boundaries are then turn them into vector formats. Deep Learning methods allow us to do so with reasonable accuracy, and it is expected that it will be better in the long run.
Note that at this point, it would be important to differentiate between raster and vector datasets. The satellite images you get from Google Earth Engine (GEE) are multi-spectral raster datasets. The more you zoom in, the more pixelated an image becomes. You may wish to restrict your retrieval of those images only for your area of interest. This is where vector dataset comes into picture. Geographical vector data can come in various file formats, but the most commonly used is Shapefiles format developed by ESRI. It is easy to find administrative shapefiles from respective government body or private places, but sometimes the administrative shapefile may not match intervention area. For example, if your area of interest is a village, but the shapefiles you have are only at the district level, you may need to create your own shapefile. This may result into additional costs (person time and software). This will help you to aggregate your data. For example, what is the average Normalized Difference Vegetation Index (NDVI) for the year 2020 in the village X of Benin? To answer those questions, you require shapefiles for your intervention areas.
#### Cloud Cover It may so happen that you may not get a satellite image at all because of dense cloud cover in particular months. For example, it is almost impossible to retrieve satellite images during the months of July, August, and September during the Monsoon Season in India. The phenomenon is true for other parts of the world too, where there is a distinct wet season (Kenya, for example). However, this disadvantage is solved by RADAR-based satellites which use RADAR waves for imagery that penetrate clouds.
#### Temporal Compatibility Satellite sensors age over time and are replaced periodically. Thus, the same digital number does not necessarily mean the same level of light intensity across years and satellites. This is an issue especially with the nighttime light data.
#### Lack of geographic dependence When our research question is not expected to be affected by a geographical variation, remote sensing is not a wise thing to do. For example, measuring the effect of training on staff's performance in different offices located in a country may not be affected by where the offices are located.
#### Doing Remote Sensing ONLY We are proponents of using ground truthing always before taking the decisions on the basis of purely remote analysis. Doing so can be a socially, politically, and economically costly affair that can have far and wide repercussions.
#### Measuring variables that cannot be measured from above With all the merits of Remote Sensing, it may not help us with measuring let's say height of a building or how many houses are located in an apartment building very accurately (although, LiDAR remote sensing may help with that). Moreover, variables such as gender, social capital, political participation etc cannot be measured by remote sensing.
Remote Sensing offers a lot of possibility for measurement of developmental outcomes at a relatively lower cost and at a faster speed. However, ethical considerations are needed to be kept in mind so that no damage is done to the privacy and security of the people while doing remote sensing. This section has been adapted from Ethical Considerations When Using Geospatial Technologies For Evidence Generation and we recommend they should be considered before the application of remote sensing for geospatial analytics.
Remotely sensed data can be used to augment the traditional methods of impact evaluation to generate evidence faster, at a relatively lower cost, and for a larger area. The publicly available data along with tools such as Google Earth Engine have lowered the barriers to access of using remote sensing data for impact evaluation. The subsequent chapters will allow you to download and use spatial data for your work using Google Earth Engine and Python.
Satellite images can have different resolutions depending upon which satellite the images are coming from. Keeping in mind the uses of satellite imagery for a particular use-cases, we may need to have some or other image depending upon the resolution and purpose of the work. Written below are the three types of resolutions we can classify the satellite images into.
Spatial Resolution refers to the size of one pixel on ground. In simple words, objects placed at a distance higher than the spatial resolution of a satellite can be discerned by the satellite. The spatial resolution for SENTINEL is 10 meters. This means that the satellite's sensor would not be able to differentiate between objects less than 10 m apart. For some of the commercial remote sensing products from MAXAR, the spatial resolution is 15 cm.
SOURCE: Maxar Blog
It is important to keep in mind that the higher resolution images may be required to perform some of the analyses that require granularity, for example cattle monitoring via remote sensing requires a very high resolution image. In those cases, it is worthwhile investing in buying higher resolution satellite imagery from commerical providers.
You can also check out this blog that shows how different resolution images look.
SOURCE: Medium Blog
Temporal Resolution or revisit time is the time it takes for a satellite to image the same area again. The temporal resolution for SENTINEL 2 is 5 days on equator and for LANDSAT 8, it's 16 days on equator. The revisit time is lower on higher latitudes, which means that an area is frequented more often by a satellite for areas closer to poles.
Spectral resolution refers to the number of bands (range of frequencies in an electromagnetic spectrum used for satellite imagery) that there are in a satellite imagery. Because satellite images contain multiple wavelengths images, we are able to compute relevant indices for specific purposes. We will cover more about multispectral images when we learn computing indices using Google Earth Engine. The spectral resolution of LANDSAT 8 is 9 bands, while that of SENTINEL 2 is 13 bands.
Source: Timothy A Warner, M Duane Nellis, and Giles M Foody. Remote sensing scale and data selection issues. The SAGE handbook of remote sensing, 2:568, 2009
There is an inherent tradeoff between spatial, spectral and temporal resolutions. Typically, the higher the spatial resolution, the lower the spectral and the temporal resolution and the higher the temporal resolution, the lower the spatial and spectral resolutions.
There are two fundamental technologies we will be using for extracting, processing, analyzing, and exporting satellite spatial data (aka satellite images).
The reader is assumed to have a basic familiarity with Python. In case the reader wants to have a refresher training before starting, one can refer to this link for a quick reference.
We will be using Jupyter Notebooks for working with satellite data and analysis. To install Jupyter Notebook, one needs to install Anaconda Distribution. For more information please refer to this link. Alternatively, one may use Google Colab to run the same on cloud and share it with others on the team
Jupyter Notebooks run by connecting to virtual machines (VM) that have maximum lifetimes that can be as much as 12 hours. Notebooks will also disconnect from VMs when left idle for too long. Maximum VM lifetime and idle timeout behavior may vary over time, or based on your usage. This is necessary for Colab to be able to offer computational resources for free.
Google Earth Engine is a cloud based platform for planetary-scale geospatial analysis which allows to process a variety of geographical data at scale and handle large geographical datasets.
The main benefit of using Google Earth Engine is the savings in computational resources needed to do large scale analysis. In addition to this, it is free to use for non-commerical use, i.e. free for educational and research purposes.
GEE contains petabytes of ready-to-use satellite data. The platform can be accessed via two methods.
We will use the second method for our work.
It is important to register on Google Earth Engine before using the platform. To register at Google Earth Engine, users are requested to refer to this link.
Users can also explore features of Google Earth Engine, including browsing case studies and data catalog available.
Before proceeding, users are requested to install the following packages
pip install earthengine-api
pip install geemap
As a first step, we will teach how to download and visualize map from Google Earth Engine. Before using GEE, you need to authenticate your account. In order to do so, follow these steps.
In this part of the module, we will learn how to import the necessary packages, authenticate, initialize, and render your first map using Google Earth Engine. We will visualize Addis Ababa, Ethiopia. Before you begin this step, please ensure you have registered for Google Earth Engine. That will allow you to authenticate your session and use GEE. Run the code below when you are running this notebook for each session.
#@title
#press SHIFT + ENTER to execute the command
# you need to run it only once per session
#this installs earth-engine api and geemap
!pip install earthengine-api
!pip install geemap
Requirement already satisfied: earthengine-api in /usr/local/lib/python3.7/dist-packages (0.1.300)
Requirement already satisfied: httplib2shim in /usr/local/lib/python3.7/dist-packages (from earthengine-api) (0.0.3)
Requirement already satisfied: httplib2<1dev,>=0.9.2 in /usr/local/lib/python3.7/dist-packages (from earthengine-api) (0.17.4)
Requirement already satisfied: google-api-python-client<2,>=1.12.1 in /usr/local/lib/python3.7/dist-packages (from earthengine-api) (1.12.10)
Requirement already satisfied: google-cloud-storage in /usr/local/lib/python3.7/dist-packages (from earthengine-api) (1.18.1)
Requirement already satisfied: future in /usr/local/lib/python3.7/dist-packages (from earthengine-api) (0.16.0)
Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from earthengine-api) (1.15.0)
Requirement already satisfied: google-auth-httplib2>=0.0.3 in /usr/local/lib/python3.7/dist-packages (from earthengine-api) (0.0.4)
Requirement already satisfied: google-auth>=1.4.1 in /usr/local/lib/python3.7/dist-packages (from earthengine-api) (1.35.0)
Requirement already satisfied: google-api-core<3dev,>=1.21.0 in /usr/local/lib/python3.7/dist-packages (from google-api-python-client<2,>=1.12.1->earthengine-api) (1.26.3)
Requirement already satisfied: uritemplate<4dev,>=3.0.0 in /usr/local/lib/python3.7/dist-packages (from google-api-python-client<2,>=1.12.1->earthengine-api) (3.0.1)
Requirement already satisfied: requests<3.0.0dev,>=2.18.0 in /usr/local/lib/python3.7/dist-packages (from google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api) (2.23.0)
Requirement already satisfied: googleapis-common-protos<2.0dev,>=1.6.0 in /usr/local/lib/python3.7/dist-packages (from google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api) (1.55.0)
Requirement already satisfied: packaging>=14.3 in /usr/local/lib/python3.7/dist-packages (from google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api) (21.3)
Requirement already satisfied: setuptools>=40.3.0 in /usr/local/lib/python3.7/dist-packages (from google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api) (57.4.0)
Requirement already satisfied: pytz in /usr/local/lib/python3.7/dist-packages (from google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api) (2018.9)
Requirement already satisfied: protobuf>=3.12.0 in /usr/local/lib/python3.7/dist-packages (from google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api) (3.17.3)
Requirement already satisfied: cachetools<5.0,>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from google-auth>=1.4.1->earthengine-api) (4.2.4)
Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.7/dist-packages (from google-auth>=1.4.1->earthengine-api) (0.2.8)
Requirement already satisfied: rsa<5,>=3.1.4 in /usr/local/lib/python3.7/dist-packages (from google-auth>=1.4.1->earthengine-api) (4.8)
Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /usr/local/lib/python3.7/dist-packages (from packaging>=14.3->google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api) (3.0.7)
Requirement already satisfied: pyasn1<0.5.0,>=0.4.6 in /usr/local/lib/python3.7/dist-packages (from pyasn1-modules>=0.2.1->google-auth>=1.4.1->earthengine-api) (0.4.8)
Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests<3.0.0dev,>=2.18.0->google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api) (2.10)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests<3.0.0dev,>=2.18.0->google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api) (1.24.3)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests<3.0.0dev,>=2.18.0->google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api) (2021.10.8)
Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests<3.0.0dev,>=2.18.0->google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api) (3.0.4)
Requirement already satisfied: google-cloud-core<2.0dev,>=1.0.0 in /usr/local/lib/python3.7/dist-packages (from google-cloud-storage->earthengine-api) (1.0.3)
Requirement already satisfied: google-resumable-media<0.5.0dev,>=0.3.1 in /usr/local/lib/python3.7/dist-packages (from google-cloud-storage->earthengine-api) (0.4.1)
Collecting geemap
Downloading geemap-0.11.5-py2.py3-none-any.whl (1.9 MB)
|████████████████████████████████| 1.9 MB 5.2 MB/s
Requirement already satisfied: plotly in /usr/local/lib/python3.7/dist-packages (from geemap) (5.5.0)
Collecting pycrs
Downloading PyCRS-1.0.2.tar.gz (36 kB)
Requirement already satisfied: palettable in /usr/local/lib/python3.7/dist-packages (from geemap) (3.3.0)
Requirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from geemap) (1.21.5)
Collecting mss
Downloading mss-6.1.0-py3-none-any.whl (76 kB)
|████████████████████████████████| 76 kB 5.7 MB/s
Collecting owslib
Downloading OWSLib-0.25.0-py2.py3-none-any.whl (216 kB)
|████████████████████████████████| 216 kB 48.4 MB/s
Collecting sankee
Downloading sankee-0.0.7.tar.gz (29 kB)
Collecting ipyevents
Downloading ipyevents-2.0.1-py2.py3-none-any.whl (130 kB)
|████████████████████████████████| 130 kB 46.6 MB/s
Collecting bqplot
Downloading bqplot-0.12.33-py2.py3-none-any.whl (1.2 MB)
|████████████████████████████████| 1.2 MB 39.9 MB/s
Collecting colour
Downloading colour-0.1.5-py2.py3-none-any.whl (23 kB)
Requirement already satisfied: matplotlib in /usr/local/lib/python3.7/dist-packages (from geemap) (3.2.2)
Requirement already satisfied: click in /usr/local/lib/python3.7/dist-packages (from geemap) (7.1.2)
Collecting ipyleaflet>=0.14.0
Downloading ipyleaflet-0.15.0-py2.py3-none-any.whl (3.3 MB)
|████████████████████████████████| 3.3 MB 41.0 MB/s
Collecting ipytree
Downloading ipytree-0.2.1-py2.py3-none-any.whl (1.3 MB)
|████████████████████████████████| 1.3 MB 26.8 MB/s
Requirement already satisfied: earthengine-api>=0.1.230 in /usr/local/lib/python3.7/dist-packages (from geemap) (0.1.300)
Requirement already satisfied: googledrivedownloader in /usr/local/lib/python3.7/dist-packages (from geemap) (0.4)
Collecting here-map-widget-for-jupyter>=1.1.1
Downloading here_map_widget_for_jupyter-1.1.3-py2.py3-none-any.whl (5.4 MB)
|████████████████████████████████| 5.4 MB 50.4 MB/s
Collecting folium>=0.11.0
Downloading folium-0.12.1.post1-py2.py3-none-any.whl (95 kB)
|████████████████████████████████| 95 kB 3.8 MB/s
Collecting geojson
Downloading geojson-2.5.0-py2.py3-none-any.whl (14 kB)
Collecting ee-extra>=0.0.10
Downloading ee_extra-0.0.10.tar.gz (176 kB)
|████████████████████████████████| 176 kB 69.9 MB/s
Collecting pyshp>=2.1.3
Downloading pyshp-2.2.0-py3-none-any.whl (44 kB)
|████████████████████████████████| 44 kB 2.6 MB/s
Collecting geeadd>=0.5.1
Downloading geeadd-0.5.5-py3-none-any.whl (30 kB)
Collecting whiteboxgui>=0.6.0
Downloading whiteboxgui-0.7.0-py2.py3-none-any.whl (99 kB)
|████████████████████████████████| 99 kB 9.4 MB/s
Requirement already satisfied: pillow in /usr/local/lib/python3.7/dist-packages (from geemap) (7.1.2)
Collecting voila
Downloading voila-0.3.2-py3-none-any.whl (1.7 MB)
|████████████████████████████████| 1.7 MB 58.2 MB/s
Collecting jupyterlab>=3
Downloading jupyterlab-3.2.9-py3-none-any.whl (8.5 MB)
|████████████████████████████████| 8.5 MB 45.1 MB/s
Collecting ipynb-py-convert
Downloading ipynb-py-convert-0.4.6.tar.gz (3.9 kB)
Collecting xyzservices
Downloading xyzservices-2022.2.0-py3-none-any.whl (35 kB)
Collecting ipyfilechooser>=0.6.0
Downloading ipyfilechooser-0.6.0-py3-none-any.whl (11 kB)
Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (from geemap) (1.3.5)
Collecting ffmpeg-python
Downloading ffmpeg_python-0.2.0-py3-none-any.whl (25 kB)
Collecting geocoder
Downloading geocoder-1.38.1-py2.py3-none-any.whl (98 kB)
|████████████████████████████████| 98 kB 8.2 MB/s
Collecting python-box
Downloading python_box-5.4.1-py3-none-any.whl (21 kB)
Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from earthengine-api>=0.1.230->geemap) (1.15.0)
Requirement already satisfied: google-api-python-client<2,>=1.12.1 in /usr/local/lib/python3.7/dist-packages (from earthengine-api>=0.1.230->geemap) (1.12.10)
Requirement already satisfied: google-auth-httplib2>=0.0.3 in /usr/local/lib/python3.7/dist-packages (from earthengine-api>=0.1.230->geemap) (0.0.4)
Requirement already satisfied: google-auth>=1.4.1 in /usr/local/lib/python3.7/dist-packages (from earthengine-api>=0.1.230->geemap) (1.35.0)
Requirement already satisfied: google-cloud-storage in /usr/local/lib/python3.7/dist-packages (from earthengine-api>=0.1.230->geemap) (1.18.1)
Requirement already satisfied: future in /usr/local/lib/python3.7/dist-packages (from earthengine-api>=0.1.230->geemap) (0.16.0)
Requirement already satisfied: httplib2shim in /usr/local/lib/python3.7/dist-packages (from earthengine-api>=0.1.230->geemap) (0.0.3)
Requirement already satisfied: httplib2<1dev,>=0.9.2 in /usr/local/lib/python3.7/dist-packages (from earthengine-api>=0.1.230->geemap) (0.17.4)
Requirement already satisfied: branca>=0.3.0 in /usr/local/lib/python3.7/dist-packages (from folium>=0.11.0->geemap) (0.4.2)
Requirement already satisfied: jinja2>=2.9 in /usr/local/lib/python3.7/dist-packages (from folium>=0.11.0->geemap) (2.11.3)
Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from folium>=0.11.0->geemap) (2.23.0)
Collecting logzero>=1.5.0
Downloading logzero-1.7.0-py2.py3-none-any.whl (16 kB)
Collecting beautifulsoup4>=4.9.0
Downloading beautifulsoup4-4.10.0-py3-none-any.whl (97 kB)
|████████████████████████████████| 97 kB 5.7 MB/s
Collecting soupsieve>1.2
Downloading soupsieve-2.3.1-py3-none-any.whl (37 kB)
Requirement already satisfied: google-api-core<3dev,>=1.21.0 in /usr/local/lib/python3.7/dist-packages (from google-api-python-client<2,>=1.12.1->earthengine-api>=0.1.230->geemap) (1.26.3)
Requirement already satisfied: uritemplate<4dev,>=3.0.0 in /usr/local/lib/python3.7/dist-packages (from google-api-python-client<2,>=1.12.1->earthengine-api>=0.1.230->geemap) (3.0.1)
Requirement already satisfied: googleapis-common-protos<2.0dev,>=1.6.0 in /usr/local/lib/python3.7/dist-packages (from google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api>=0.1.230->geemap) (1.55.0)
Requirement already satisfied: pytz in /usr/local/lib/python3.7/dist-packages (from google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api>=0.1.230->geemap) (2018.9)
Requirement already satisfied: setuptools>=40.3.0 in /usr/local/lib/python3.7/dist-packages (from google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api>=0.1.230->geemap) (57.4.0)
Requirement already satisfied: packaging>=14.3 in /usr/local/lib/python3.7/dist-packages (from google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api>=0.1.230->geemap) (21.3)
Requirement already satisfied: protobuf>=3.12.0 in /usr/local/lib/python3.7/dist-packages (from google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api>=0.1.230->geemap) (3.17.3)
Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.7/dist-packages (from google-auth>=1.4.1->earthengine-api>=0.1.230->geemap) (0.2.8)
Requirement already satisfied: rsa<5,>=3.1.4 in /usr/local/lib/python3.7/dist-packages (from google-auth>=1.4.1->earthengine-api>=0.1.230->geemap) (4.8)
Requirement already satisfied: cachetools<5.0,>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from google-auth>=1.4.1->earthengine-api>=0.1.230->geemap) (4.2.4)
Requirement already satisfied: ipywidgets<8,>=7.6.0 in /usr/local/lib/python3.7/dist-packages (from here-map-widget-for-jupyter>=1.1.1->geemap) (7.6.5)
Collecting traittypes<3,>=0.2.1
Downloading traittypes-0.2.1-py2.py3-none-any.whl (8.6 kB)
Requirement already satisfied: jupyterlab-widgets>=1.0.0 in /usr/local/lib/python3.7/dist-packages (from ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (1.0.2)
Requirement already satisfied: ipykernel>=4.5.1 in /usr/local/lib/python3.7/dist-packages (from ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (4.10.1)
Requirement already satisfied: ipython>=4.0.0 in /usr/local/lib/python3.7/dist-packages (from ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (5.5.0)
Requirement already satisfied: traitlets>=4.3.1 in /usr/local/lib/python3.7/dist-packages (from ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (5.1.1)
Requirement already satisfied: ipython-genutils~=0.2.0 in /usr/local/lib/python3.7/dist-packages (from ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (0.2.0)
Requirement already satisfied: widgetsnbextension~=3.5.0 in /usr/local/lib/python3.7/dist-packages (from ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (3.5.2)
Requirement already satisfied: nbformat>=4.2.0 in /usr/local/lib/python3.7/dist-packages (from ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (5.1.3)
Requirement already satisfied: jupyter-client in /usr/local/lib/python3.7/dist-packages (from ipykernel>=4.5.1->ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (5.3.5)
Requirement already satisfied: tornado>=4.0 in /usr/local/lib/python3.7/dist-packages (from ipykernel>=4.5.1->ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (5.1.1)
Requirement already satisfied: pickleshare in /usr/local/lib/python3.7/dist-packages (from ipython>=4.0.0->ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (0.7.5)
Requirement already satisfied: simplegeneric>0.8 in /usr/local/lib/python3.7/dist-packages (from ipython>=4.0.0->ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (0.8.1)
Requirement already satisfied: decorator in /usr/local/lib/python3.7/dist-packages (from ipython>=4.0.0->ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (4.4.2)
Requirement already satisfied: pygments in /usr/local/lib/python3.7/dist-packages (from ipython>=4.0.0->ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (2.6.1)
Requirement already satisfied: prompt-toolkit<2.0.0,>=1.0.4 in /usr/local/lib/python3.7/dist-packages (from ipython>=4.0.0->ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (1.0.18)
Requirement already satisfied: pexpect in /usr/local/lib/python3.7/dist-packages (from ipython>=4.0.0->ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (4.8.0)
Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.7/dist-packages (from jinja2>=2.9->folium>=0.11.0->geemap) (2.0.1)
Collecting tornado>=4.0
Downloading tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl (428 kB)
|████████████████████████████████| 428 kB 68.1 MB/s
Collecting jupyterlab-server~=2.3
Downloading jupyterlab_server-2.10.3-py3-none-any.whl (61 kB)
|████████████████████████████████| 61 kB 7.4 MB/s
Requirement already satisfied: jupyter-core in /usr/local/lib/python3.7/dist-packages (from jupyterlab>=3->geemap) (4.9.2)
Collecting jupyter-server~=1.4
Downloading jupyter_server-1.13.5-py3-none-any.whl (397 kB)
|████████████████████████████████| 397 kB 56.2 MB/s
Collecting nbclassic~=0.2
Downloading nbclassic-0.3.5-py3-none-any.whl (25 kB)
Requirement already satisfied: prometheus-client in /usr/local/lib/python3.7/dist-packages (from jupyter-server~=1.4->jupyterlab>=3->geemap) (0.13.1)
Requirement already satisfied: terminado>=0.8.3 in /usr/local/lib/python3.7/dist-packages (from jupyter-server~=1.4->jupyterlab>=3->geemap) (0.13.1)
Requirement already satisfied: nbconvert in /usr/local/lib/python3.7/dist-packages (from jupyter-server~=1.4->jupyterlab>=3->geemap) (5.6.1)
Collecting websocket-client
Downloading websocket_client-1.3.1-py3-none-any.whl (54 kB)
|████████████████████████████████| 54 kB 2.7 MB/s
Requirement already satisfied: pyzmq>=17 in /usr/local/lib/python3.7/dist-packages (from jupyter-server~=1.4->jupyterlab>=3->geemap) (22.3.0)
Collecting anyio<4,>=3.1.0
Downloading anyio-3.5.0-py3-none-any.whl (79 kB)
|████████████████████████████████| 79 kB 7.8 MB/s
Requirement already satisfied: argon2-cffi in /usr/local/lib/python3.7/dist-packages (from jupyter-server~=1.4->jupyterlab>=3->geemap) (21.3.0)
Collecting jupyter-client
Downloading jupyter_client-7.1.2-py3-none-any.whl (130 kB)
|████████████████████████████████| 130 kB 56.2 MB/s
Requirement already satisfied: Send2Trash in /usr/local/lib/python3.7/dist-packages (from jupyter-server~=1.4->jupyterlab>=3->geemap) (1.8.0)
Requirement already satisfied: typing-extensions in /usr/local/lib/python3.7/dist-packages (from anyio<4,>=3.1.0->jupyter-server~=1.4->jupyterlab>=3->geemap) (3.10.0.2)
Collecting sniffio>=1.1
Downloading sniffio-1.2.0-py3-none-any.whl (10 kB)
Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.7/dist-packages (from anyio<4,>=3.1.0->jupyter-server~=1.4->jupyterlab>=3->geemap) (2.10)
Requirement already satisfied: nest-asyncio>=1.5 in /usr/local/lib/python3.7/dist-packages (from jupyter-client->ipykernel>=4.5.1->ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (1.5.4)
Requirement already satisfied: entrypoints in /usr/local/lib/python3.7/dist-packages (from jupyter-client->ipykernel>=4.5.1->ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (0.4)
Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from jupyter-client->ipykernel>=4.5.1->ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (2.8.2)
Requirement already satisfied: jsonschema>=3.0.1 in /usr/local/lib/python3.7/dist-packages (from jupyterlab-server~=2.3->jupyterlab>=3->geemap) (4.3.3)
Collecting json5
Downloading json5-0.9.6-py2.py3-none-any.whl (18 kB)
Requirement already satisfied: babel in /usr/local/lib/python3.7/dist-packages (from jupyterlab-server~=2.3->jupyterlab>=3->geemap) (2.9.1)
Requirement already satisfied: attrs>=17.4.0 in /usr/local/lib/python3.7/dist-packages (from jsonschema>=3.0.1->jupyterlab-server~=2.3->jupyterlab>=3->geemap) (21.4.0)
Requirement already satisfied: importlib-metadata in /usr/local/lib/python3.7/dist-packages (from jsonschema>=3.0.1->jupyterlab-server~=2.3->jupyterlab>=3->geemap) (4.11.1)
Requirement already satisfied: importlib-resources>=1.4.0 in /usr/local/lib/python3.7/dist-packages (from jsonschema>=3.0.1->jupyterlab-server~=2.3->jupyterlab>=3->geemap) (5.4.0)
Requirement already satisfied: pyrsistent!=0.17.0,!=0.17.1,!=0.17.2,>=0.14.0 in /usr/local/lib/python3.7/dist-packages (from jsonschema>=3.0.1->jupyterlab-server~=2.3->jupyterlab>=3->geemap) (0.18.1)
Requirement already satisfied: zipp>=3.1.0 in /usr/local/lib/python3.7/dist-packages (from importlib-resources>=1.4.0->jsonschema>=3.0.1->jupyterlab-server~=2.3->jupyterlab>=3->geemap) (3.7.0)
Requirement already satisfied: notebook<7 in /usr/local/lib/python3.7/dist-packages (from nbclassic~=0.2->jupyterlab>=3->geemap) (5.3.1)
Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /usr/local/lib/python3.7/dist-packages (from packaging>=14.3->google-api-core<3dev,>=1.21.0->google-api-python-client<2,>=1.12.1->earthengine-api>=0.1.230->geemap) (3.0.7)
Requirement already satisfied: wcwidth in /usr/local/lib/python3.7/dist-packages (from prompt-toolkit<2.0.0,>=1.0.4->ipython>=4.0.0->ipywidgets<8,>=7.6.0->here-map-widget-for-jupyter>=1.1.1->geemap) (0.2.5)
Requirement already satisfied: pyasn1<0.5.0,>=0.4.6 in /usr/local/lib/python3.7/dist-packages (from pyasn1-modules>=0.2.1->google-auth>=1.4.1->earthengine-api>=0.1.230->geemap) (0.4.8)
Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->folium>=0.11.0->geemap) (3.0.4)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->folium>=0.11.0->geemap) (2021.10.8)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests->folium>=0.11.0->geemap) (1.24.3)
Requirement already satisfied: ptyprocess in /usr/local/lib/python3.7/dist-packages (from terminado>=0.8.3->jupyter-server~=1.4->jupyterlab>=3->geemap) (0.7.0)
Collecting whitebox
Downloading whitebox-2.1.2-py2.py3-none-any.whl (75 kB)
|████████████████████████████████| 75 kB 4.4 MB/s
Requirement already satisfied: argon2-cffi-bindings in /usr/local/lib/python3.7/dist-packages (from argon2-cffi->jupyter-server~=1.4->jupyterlab>=3->geemap) (21.2.0)
Requirement already satisfied: cffi>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from argon2-cffi-bindings->argon2-cffi->jupyter-server~=1.4->jupyterlab>=3->geemap) (1.15.0)
Requirement already satisfied: pycparser in /usr/local/lib/python3.7/dist-packages (from cffi>=1.0.1->argon2-cffi-bindings->argon2-cffi->jupyter-server~=1.4->jupyterlab>=3->geemap) (2.21)
Collecting ratelim
Downloading ratelim-0.1.6-py2.py3-none-any.whl (4.0 kB)
Requirement already satisfied: google-cloud-core<2.0dev,>=1.0.0 in /usr/local/lib/python3.7/dist-packages (from google-cloud-storage->earthengine-api>=0.1.230->geemap) (1.0.3)
Requirement already satisfied: google-resumable-media<0.5.0dev,>=0.3.1 in /usr/local/lib/python3.7/dist-packages (from google-cloud-storage->earthengine-api>=0.1.230->geemap) (0.4.1)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib->geemap) (0.11.0)
Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->geemap) (1.3.2)
Requirement already satisfied: testpath in /usr/local/lib/python3.7/dist-packages (from nbconvert->jupyter-server~=1.4->jupyterlab>=3->geemap) (0.6.0)
Requirement already satisfied: mistune<2,>=0.8.1 in /usr/local/lib/python3.7/dist-packages (from nbconvert->jupyter-server~=1.4->jupyterlab>=3->geemap) (0.8.4)
Requirement already satisfied: bleach in /usr/local/lib/python3.7/dist-packages (from nbconvert->jupyter-server~=1.4->jupyterlab>=3->geemap) (4.1.0)
Requirement already satisfied: pandocfilters>=1.4.1 in /usr/local/lib/python3.7/dist-packages (from nbconvert->jupyter-server~=1.4->jupyterlab>=3->geemap) (1.5.0)
Requirement already satisfied: defusedxml in /usr/local/lib/python3.7/dist-packages (from nbconvert->jupyter-server~=1.4->jupyterlab>=3->geemap) (0.7.1)
Requirement already satisfied: webencodings in /usr/local/lib/python3.7/dist-packages (from bleach->nbconvert->jupyter-server~=1.4->jupyterlab>=3->geemap) (0.5.1)
Requirement already satisfied: pyyaml in /usr/local/lib/python3.7/dist-packages (from owslib->geemap) (3.13)
Collecting pyproj>=2
Downloading pyproj-3.2.1-cp37-cp37m-manylinux2010_x86_64.whl (6.3 MB)
|████████████████████████████████| 6.3 MB 40.9 MB/s
Requirement already satisfied: tenacity>=6.2.0 in /usr/local/lib/python3.7/dist-packages (from plotly->geemap) (8.0.1)
Collecting nbconvert
Downloading nbconvert-6.4.2-py3-none-any.whl (558 kB)
|████████████████████████████████| 558 kB 71.3 MB/s
Collecting websockets>=9.0
Downloading websockets-10.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (111 kB)
|████████████████████████████████| 111 kB 70.7 MB/s
Requirement already satisfied: nbclient<0.6,>=0.4.0 in /usr/local/lib/python3.7/dist-packages (from voila->geemap) (0.5.11)
Requirement already satisfied: jupyterlab-pygments in /usr/local/lib/python3.7/dist-packages (from nbconvert->jupyter-server~=1.4->jupyterlab>=3->geemap) (0.1.2)
Building wheels for collected packages: ee-extra, ipynb-py-convert, pycrs, sankee
Building wheel for ee-extra (setup.py) ... done
Created wheel for ee-extra: filename=ee_extra-0.0.10-py3-none-any.whl size=186748 sha256=60d932e06c0705e187381517f9cb630d92c505954caa7511a1c7e646b01ee205
Stored in directory: /root/.cache/pip/wheels/17/e0/8f/c3553eed58285eb5fec73002519406ef543a949fee1c96c3c8
Building wheel for ipynb-py-convert (setup.py) ... done
Created wheel for ipynb-py-convert: filename=ipynb_py_convert-0.4.6-py3-none-any.whl size=4640 sha256=3d76b7e0935c7607b0f64be2ff76539344d1fb73b8029830c98a13be6d5084de
Stored in directory: /root/.cache/pip/wheels/af/31/a9/761b134adbbca3c92d491eff3bad785c0a0c0079695d6f0504
Building wheel for pycrs (setup.py) ... done
Created wheel for pycrs: filename=PyCRS-1.0.2-py3-none-any.whl size=32704 sha256=6b8a33a30b79e2517816f0bf201d0f7096b6d16993558c9942c1275ca35be6b1
Stored in directory: /root/.cache/pip/wheels/3e/ce/32/1ec0aba6b9770681a423e82f0274c57d09ad2c20c2864901f9
Building wheel for sankee (setup.py) ... done
Created wheel for sankee: filename=sankee-0.0.7-py3-none-any.whl size=27639 sha256=c688d4c4c33eefd31d2787b4ab08a8f264ae47c630087333ce9cbf913ac66821
Stored in directory: /root/.cache/pip/wheels/13/28/83/5726f82a8cb0baf76169d99ee0595c420afedb9020e22bbd4b
Successfully built ee-extra ipynb-py-convert pycrs sankee
Installing collected packages: tornado, jupyter-client, nbconvert, sniffio, websocket-client, anyio, soupsieve, jupyter-server, json5, xyzservices, whitebox, websockets, traittypes, ratelim, pyproj, nbclassic, logzero, jupyterlab-server, ipytree, ipyfilechooser, beautifulsoup4, whiteboxgui, voila, sankee, python-box, pyshp, pycrs, owslib, mss, jupyterlab, ipynb-py-convert, ipyleaflet, ipyevents, here-map-widget-for-jupyter, geojson, geocoder, geeadd, folium, ffmpeg-python, ee-extra, colour, bqplot, geemap
Attempting uninstall: tornado
Found existing installation: tornado 5.1.1
Uninstalling tornado-5.1.1:
Successfully uninstalled tornado-5.1.1
Attempting uninstall: jupyter-client
Found existing installation: jupyter-client 5.3.5
Uninstalling jupyter-client-5.3.5:
Successfully uninstalled jupyter-client-5.3.5
Attempting uninstall: nbconvert
Found existing installation: nbconvert 5.6.1
Uninstalling nbconvert-5.6.1:
Successfully uninstalled nbconvert-5.6.1
Attempting uninstall: beautifulsoup4
Found existing installation: beautifulsoup4 4.6.3
Uninstalling beautifulsoup4-4.6.3:
Successfully uninstalled beautifulsoup4-4.6.3
Attempting uninstall: folium
Found existing installation: folium 0.8.3
Uninstalling folium-0.8.3:
Successfully uninstalled folium-0.8.3
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
google-colab 1.0.0 requires tornado~=5.1.0; python_version >= "3.0", but you have tornado 6.1 which is incompatible.
datascience 0.10.6 requires folium==0.2.1, but you have folium 0.12.1.post1 which is incompatible.
Successfully installed anyio-3.5.0 beautifulsoup4-4.10.0 bqplot-0.12.33 colour-0.1.5 ee-extra-0.0.10 ffmpeg-python-0.2.0 folium-0.12.1.post1 geeadd-0.5.5 geemap-0.11.5 geocoder-1.38.1 geojson-2.5.0 here-map-widget-for-jupyter-1.1.3 ipyevents-2.0.1 ipyfilechooser-0.6.0 ipyleaflet-0.15.0 ipynb-py-convert-0.4.6 ipytree-0.2.1 json5-0.9.6 jupyter-client-7.1.2 jupyter-server-1.13.5 jupyterlab-3.2.9 jupyterlab-server-2.10.3 logzero-1.7.0 mss-6.1.0 nbclassic-0.3.5 nbconvert-6.4.2 owslib-0.25.0 pycrs-1.0.2 pyproj-3.2.1 pyshp-2.2.0 python-box-5.4.1 ratelim-0.1.6 sankee-0.0.7 sniffio-1.2.0 soupsieve-2.3.1 tornado-6.1 traittypes-0.2.1 voila-0.3.2 websocket-client-1.3.1 websockets-10.2 whitebox-2.1.2 whiteboxgui-0.7.0 xyzservices-2022.2.0
#importing the libraries ee (earth-engine) and gm (geemap)
import ee
# we use gm so that we don't have to type geemap every time we use a function in geemap
import geemap as gm
#Press ENTER once you have entered your authentication code
# the code to authenticate the session
ee.Authenticate()
To authorize access needed by Earth Engine, open the following URL in a web browser and follow the instructions. If the web browser does not start automatically, please manually browse the URL below. https://accounts.google.com/o/oauth2/auth?client_id=517222506229-vsmmajv00ul0bs7p89v5m89qs8eb9359.apps.googleusercontent.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fearthengine+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&response_type=code&code_challenge=636Ioco4RVecmNZ31z0sqiZRWsZCydzZKywgwsRj16Y&code_challenge_method=S256 The authorization workflow will generate a code, which you should paste in the box below. Enter verification code: 4/1AX4XfWh1Cj1xKgJpO8-0FZ60ZW4e2n127GY3zsaFmIrFDykK7fpW7Le99ZY Successfully saved authorization token.
# initialize the sesion
ee.Initialize()
Tip: To find the zoom level and coordinates, it is recommended to look at the address bar and copy the link to see them. For example, https://www.google.com/maps/place/Nagpur,+Maharashtra/@21.1612315,79.0024702,12z/ in this link we see coordinates after the '@' sign and zoom before 'z'. Zoom level is not absolute and depending upon the screen size, different users may need to have different zoom level for their need.
# set our initial coordinates to Addis Ababa in Ethiopia
# latitude and longitude
center_lat = 9.0061525
center_long = 38.765783
zoomlevel = 12
# this makes the map
my_first_map = gm.Map(center = [center_lat, center_long], zoom = zoomlevel) #gm denotes geemap
# to display the map we run the following commands
# addLayerControl adds UI control on the map output so that user can interactr with the layers and other information
my_first_map.addLayerControl()
# shows the output
my_first_map
Congratulations on your first map generation using Google Earth Engine and Python. You can explore the panel on the left hand side. In the coming tutorials, we will learn how to select a particular area of interest using custom shape files.
There are a couple of reasons why you may not get the desired output. This will help you troubleshoot the probable problems.
# Exercise 1
# Create a map with its center in Khartoum
# Hint: You may need to use Google Maps for finding the lat-long and appropriate zoom level.
# Begin writing your code HERE
In this part of the tutorial, you will learn how to find and import the satellite images, draw or select a particular area of interest, import custom shape files, select the time period for which you want the image, and selecting the bands from the satellite images. This will be a long tutorial, so take a small break, grab a cup of coffee, and let's go!
The best way to find a satellite image of interest is to go to Google Earth Engine Catalog and search for the required dataset. A user can go this link to search for the satellite imagery. One needs to enter the name of the satellite in the search bar and they will be shown the relevant results. What we need for our purpose is the unique ID associated with each satellite imagery that one can find in the description after clicking the relevant areas. The visuals below explain the whole process step by step.
Sentinel-2 MSI: MultiSpectral Instrument, Level-1C
panel and you will come across the following screen. We need the highlighted ID (in red) for our information.Thus for Sentinel-2 MSI, the unique ID for the satellite images is COPERNICUS/S2
and the code to access the image collection will be ee.ImageCollection("COPERNICUS/S2")
The final code for importing the image collection would be
# example code not to be run
image_col = ee.ImageCollection('COPERNICUS/S2') #image collection
In most of the cases, however, we are interested in getting data for a particular region and for specified dates. We will learn how to do this in the next sections.
To reduce computational intensity and make the processes faster, it is recommened to select an area of interest. Selecting that area requires the knowledge of latitude and longitude of a place. For example, if we need to select the boundaries of India, we may look at Northern and Southern Latitude Value and Eastern and Western Longitude Value. In India's case, it would be approximately 68.191522 E to 97.395130 E and 8.077273 N to 37.083957 N. It can be helpful to use Google Maps to know the most approximate absolute value of latitude and longitude. Thus, our area of interest will be [68.191522, 97.395130, 8.077273, 37.083957]
. This step may take couple of minutes depending upon the size of area of interest.
# example code not to be run
#(North lat, South lat, East long, West long)
aoi = ee.Geometry.Rectangle([68.191522, 97.395130, 8.077273, 37.083957])
At this point it is worth mentioning that selecting a rectangular area of interest is not the only possibility. We can select an area of interest as per the administrative boundaries as well, as discussed below. However, rectangular regions are preferred if we want to see the change not just in the administrative boundaries of a place, but also in the border areas. For example, we may be interested in seeing the outcome of an intervention at the political or geographical border of a place, such as border between sea and land or a border between two states of a country.
We can also use Global Administrative Unit Layers 2015 provided by the Food and Agriculture Organization as built-in region selector in Google Earth Engine. For more information, refer to this page. For the list of names and spellings used in FAO GAUL dataset, please go to this link
# example code not to be run
# Get a feature collection of administrative boundaries.
states = ee.FeatureCollection('FAO/GAUL/2015/level2').select('ADM1_NAME')
# Filter the feature collection to subset Bihar
bihar = states.filter(ee.Filter.eq('ADM1_NAME', 'Bihar'))
image = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-01-01', '2020-12-31').median().clip(bihar)
One can also import custom shape files using Google Earth Engine Assets. It may happen that the shapefile one gets from FAO GAUL may not represent the correct political shape of a country. In that case, one may do the following steps to use the shape file.
# example code not to be run
india = ee.FeatureCollection("users/amalik/indian_states")
ai_image = ee.ImageCollection("COPERNICUS/S5P/OFFL/L3_AER_AI").filterDate('2020-01-01', '2020-12-31').select('absorbing_aerosol_index').median().clip(india)
To select an image for a specific time period, we need to specify the start date and the end date for the image collection.
For selecting an image collection, we specify satellite image source and the dates as shown below.
# example code not to be run
#SENTINEL 2 LEVEL 2A https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR
image_collection = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-01-01', '2020-12-31')
However, if we are looking for the most realistic single image for this time period, we take the median value of the pixel. To accomplish this, we add median()
at the end of previous line and resulting code looks like this:
# example code not to be run
image = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-01-01', '2020-12-31').median()
At this point, it is worth mentioning that median value is not the only value a researcher may be interested in. Depending upon the research question, they may need to have a look at the maximum, or minimum or the variation between maximum and minimum. This will necessitate the relevant change in the code.
Please note that the code does not correct the errors we may get because of a cloudy image. To increase our probability of getting only images with low cloud coverage, we can add a filter, which will allow us to retrieve only those images that have clouds percentage below a certain threshold. The code for that would be:
# example code not to be run
image = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-01-01', '2020-12-31').filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 15)).median()
A satellite image is composed of many bands that we call as Multispectral Satellite Bands. For example, SENTINEL 2A has 13 bands. The visualization below will help to understand what a satellite multispectral image looks like.
Source: Research Gate
Thus each pixel contains 13 values and if we have to get values for a particular band, we may need to select the name of that band. One can look at the Google Earth Engine Image Description for selecting the band. For example, the code below selects the avg_rad
from the VIIRS Nighttime Lights Data.
#this code selects the imagery for the year 2020
viirs2019_12 = ee.ImageCollection("NOAA/VIIRS/DNB/MONTHLY_V1/VCMSLCFG").filterDate("2020-01-01","2020-12-31").select('avg_rad').median()
# initialize our map
map1 = gm.Map() #map is a reserved keyword in Python, so we cannot use it for a variable name, thus map1
map1.add_basemap('SATELLITE')
map1.addLayer(viirs2019_12, {}, "VIIRS-DNB Dec 2019")
map1.addLayerControl()
#this plots the average nighttime light radiance which is highly correlated with the economic development of an area
map1
# Exercise 2
# Go to Sentinel 5P Catalog and select the Sentinel-5P OFFL NO2: Offline Nitrogen Dioxide
# Define your area of interest as Chile
# Create a map for the median NO2 level in Chile for the year 2019
# Hint: The band you shall be selecting is NO2_column_number_density
# Begin writing your code HERE
A Vegetation Index (VI) is a spectral transformation of two or more bands designed to enhance the contribution of vegetation properties and allow reliable spatial and temporal inter-comparisons of terrestrial photosynthetic activity and canopy structural variations.
Vegetation indices are remote sensing approaches used to quantify vegetation cover, vigor or biomass for each pixel in an image (Ouyang et al. 2010).
There are many Vegetation Indices (VIs), with many being functionally equivalent. Many of the indices make use of the inverse relationship between red and near-infrared reflectance associated with healthy green vegetation.
Vegetation indices have been used to:
It is worth noting that every vegetation index has atmospheric and sensor effects, and thus it also has high variability and low repeatability or comparability (Huang et al, 2021.
A short article discussing various vegetation indices, their applications, and formulae is available here.
Tip: Index Database is a helpful resource for finding indices and the relevant formula for multiple satellite images.
The Normalized Difference Vegetation Index (NDVI) is a simple indicator that is frequently used to monitor large scale events like drought, agricultural production, identifying fire zones and finding areas most suspectible to desertification.
This index defines values from -1.0 to 1.0, basically representing green vegetation, where negative values are mainly formed from clouds, water and snow, and values close to zero are primarily formed from rocks and bare soil. Very small values (0.1 or less) of the NDVI function correspond to empty areas of rocks, sand or snow. Moderate values (from 0.2 to 0.3) represent shrubs and meadows, while large values (from 0.6 to 0.8) indicate temperate and tropical forests. However, when using NDVI for crop monitoring purposes, it is helpful to focus on the type of crop, as different crops have different reflectance.
In simple words one can say that NDVI is a measure of the state of plant health based on how the plant reflects light at certain frequencies, because the chlorophyll absorbs and reflects certain wavelengths.
According to Earth Observing System, "Chlorophyll (a health indicator) strongly absorbs visible light, and the cellular structure of the leaves strongly reflect near-infrared light. When the plant becomes dehydrated, sick, afflicted with disease, etc., the spongy layer deteriorates, and the plant absorbs more of the near-infrared light, rather than reflecting it. Thus, observing how NIR changes compared to red light provides an accurate indication of the presence of chlorophyll, which correlates with plant health."
$$NDVI = {(NIR - RED)\over (NIR + RED)}$$Some of the limitations of NDVI include its sensitivity to the effects of soil and atmosphere, and dependency on the time an image was taken. That’s why it’s recommended to ascertain if your research question can be answered by NDVI or any other relevant index.
This link shows an example of land degradation assessment done using NDVI.
Some applications of NDVI include precision farming and to measure biomass. Whereas, in forestry, foresters use NDVI to quantify forest supply and leaf area index. Furthermore, NASA states that NDVI is a good indicator of drought.
# this function calculates the NDVI for a given image
# the normalized difference in general is computed as (first − second) / (first + second)
# formula source https://www.indexdatabase.de/db/i-single.php?id=58
def getNDVI(image):
return image.normalizedDifference(['B8', 'B4'])
# creating map for visualization of NDVI
# center_lat and center_long have been defined earlier for Addis Ababa
# one can change the coordinates for any location required
# this example doesn't use area of interest, for specific area of interest, please refer to the subsequent sections (for eg GNDVI)
ndvi_map = gm.Map(center = [center_lat, center_long], zoom = zoomlevel)
# image that will be used for calculation of NDVI
# COPERNICUS/S2_SR is the Sentinel 2 Image
# we use filterDate for filtering the 2020 values
# CLOUDY_PIXEL_PERCENTAGE filtering only shows the image wher cloud cover is 15% or less
# median() computes the median value of each pixel for the given dates
ndvi_image = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-01-01', '2020-12-31').filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 15)).median()
# calculation of NDVI for the image above using getNDVI function
ndvi = getNDVI(ndvi_image)
# visualization parameters that will be needed
# the color scheme chosen for the visualization uses range of values between 0 and 0.5
# this may change depending upon the use-case
ndviParams = {'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850'],
'min': 0,
'max': 0.5}
# adding calculated ndvi layer to the map
ndvi_map.addLayer(ndvi, ndviParams, 'NDVI')
ndvi_map.addLayerControl()
ndvi_map
The dark red areas are water bodies, whereas the reddish areas are urban areas with low vegetation.
Bonus question: Where do you see an airport in the image above?
# Exercise 3
# Create a NDVI map for Mombasa Kenya
# Hint: The code will be same as before with only lat-long changed
# Begin writing your code HERE
The Green Normalized Difference Vegetation Index (GNDVI) method is a vegetation index for estimating photo synthetic activity and is a commonly used vegetation index to determine water and nitrogen uptake into the plant canopy. Green NDVI is a modification of NDVI which uses NIR and GREEN bands for calculating the vegetative index. The benefit of GNDVI over NDVI is that the green band can cover a broader range of chlorophyll in plants than the red band. This applies to mature plants and can be useful for monitoring yield crop in the late growing season.
GNDVI has been used as an indicator to estimate the amount of Nitrogen available in plants, which impacts the growth and health of the plant.
$$GNDVI = {(NIR - GREEN)\over (NIR + GREEN)}$$def getGNDVI(image):
# B8 NIR and B3 GREEN
# Reference https://www.mdpi.com/2072-4292/12/13/2104/pdf
return image.normalizedDifference(['B8', 'B3']) #NIR and GREEN
gndvi_map = gm.Map(center = [13.50162, 7.11147], zoom = 8)
# Get a feature collection of administrative boundaries.
states = ee.FeatureCollection('FAO/GAUL/2015/level2').select('ADM1_NAME')
# Filter the feature collection to subset Maradi
maradi = states.filter(ee.Filter.eq('ADM1_NAME', 'Maradi'))
gndvi_image = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-01-01', '2020-12-31').filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 15)).median().clip(maradi)
gndvi = getGNDVI(gndvi_image)
gndviParams = {'min': 0,
'max': 0.5,
'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850']}
gndvi_map.addLayer(gndvi, gndviParams, 'GNDVI')
gndvi_map.addLayerControl()
gndvi_map
# Exercise 4
# Create a GNDVI map for Arusha Region, Tanzania
# For the list of names and spellings used in FAO GAUL dataset, please go to this link https://data.apps.fao.org/catalog/dataset/gaul-codes/resource/cfdaf156-26b9-46c2-aab2-eb437fc16622
# Hint: The code will be same as before with only lat-long changed
# Begin writing your code HERE
EVI is similar to Normalized Difference Vegetation Index (NDVI) and can be used to quantify vegetation greenness. The Enhanced Vegetation Index was invented by Liu and Huete to simultaneously correct NDVI results for atmospheric influences and soil background signals, especially in areas of dense canopy. The value range for EVI is -1 to 1, and for healthy vegetation it varies between 0.2 and 0.8. EVI has been found to perform well under high aerosol loads , biomass burning conditions Huete et al., 2002. According to Huete et al 1997, the original EVI is
$$2.5*((NIR - RED)\over (NIR + 6*RED - 7.5*BLUE + 1))$$The B(lue) band correspond to band 2 of Sentinel-2 MSI, NIR to Band 8 and R(ed) to band 4.
It is worth mentioning that EVI must be used to analyze areas with large amounts of chlorophyll (such as rainforests), and preferably with minimum topographic effects (not mountainous regions). One of the advantages of EVI is its high resolution and good spatial coverage over all terrains. It has also been used to identify drought related stress over different landscapes.
evi_map = gm.Map(center = [9.321014, 1.772556], zoom = 8)
# Get a feature collection of administrative boundaries.
states = ee.FeatureCollection('FAO/GAUL/2015/level2').select('ADM1_NAME')
donga = states.filter(ee.Filter.eq('ADM1_NAME', 'Donga'))
evi_image = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-01-01', '2020-12-31').filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 15)).median().clip(donga)
evi = evi_image.expression(
'(2.5 * (NIR - RED)) / ((NIR + 6 * RED - 7.5 * BLUE) + 1)', {
'NIR': evi_image.select('B8'),
'RED': evi_image.select('B4'),
'BLUE': evi_image.select('B2')
})
eviParams = {'min': -1,
'max': 1,
'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850'],}
evi_map.addLayer(evi, eviParams, 'EVI')
evi_map.addLayerControl()
evi_map
# Exercise 5
# Create an EVI map for Kano, Nigeria
# Hint: The code will be same as before with only lat-long and area of interest changed
# Begin writing your code HERE
Similar to vegetation indices (VIs) that have been used to measure vigor of vegetation, researchers have developed water indices too. They have been mainly used to identify the urban water bodies, change in water depth, and for measuring changes in basin area after rare events. A few of them are discussed below.
The NDWI makes use of reflected near-infrared radiation and visible green light to enhance the presence of such features while eliminating the presence of soil and terrestrial vegetation features. It is suggested that the NDWI may also provide researchers with turbidity estimations of water bodies using remotely-sensed digital data.
Normalized Difference Water Index (NDWI), is used to differentiate water from the dry land or rather most suitable for water body mapping. Water bodies have a low radiation and strong absorbability in the visible infrared wavelengths range. NDWI uses near Infra-red and green bands of remote sensing images based on the occurrence. It can boost the water information efficiently in most of the cases. It’s subtle in land built-up and often ends up in overestimated water bodies.
It has been found that water features may not be accurately extracted using NDWI due to spectral confusion of built-up land with water bodies because built-up land may also have positive values in the NDWIderived image (Xu 2007). Therefore, a new method was proposed in 2006 by Xu called Modified NDWI which is discussed in the next section.
$$NDWI = {(B3 - B8)\over (B3 + B8)}$$$$NDWI = {(GREEN - NIR)\over (GREEN + NIR)}$$Please note that it may be confused with GNDVI as both use GREEN and NIR Bands. In NDWI, the normalized difference is between GREEN and NIR bands, whereas for GNDVI, the normalized difference is between NIR and GREEN.
def getNDWI(image):
return image.normalizedDifference(['B3', 'B8'])
ndwi_map = gm.Map(center = [20.546, -89.587], zoom = 9)
ndwi_image = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-01-01', '2020-12-31').filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 15)).median()
ndwi = getNDWI(ndwi_image)
ndwiParams = {'palette': ['#f1eef6', '#bdc9e1', '#74a9cf', '#2b8cbe', '#045a8d']}
ndwi_map.addLayer(ndwi, ndwiParams, 'NDWI')
ndwi_map.addLayerControl()
ndwi_map
# Exercise 6
# Create a NDWI map for Kumasi to Accra region in Ghana
# Hint: The code will be same as before with only lat-long changed
# Begin writing your code HERE
To overcome the shortcomings of NDWI, Xu proposed the modified NDWI (MNDWI) in 2006. According to Xu (2006), the MNDWI can enhance open water features while efficiently suppressing and even removing built‐up land noise as well as vegetation and soil noise. The enhanced water information using the NDWI is often mixed with built‐up land noise and the area of extracted water is thus overestimated. Accordingly, the MNDWI is more suitable for enhancing and extracting water information for a water region with a background dominated by built‐up land areas because of its advantage in reducing and even removing built‐up land noise over the NDWI.
Please note that calculation of MNDWI using SENTINEL 2 imagery will require Pansharpening. For more information, users are requested to visit the following notebook.
#using landsat for calculating mndwi
#green - swir / green + swir
def getMNDWI(image):
return image.normalizedDifference(['SR_B3', 'SR_B6']) #https://gis.stackexchange.com/questions/327075/computing-mndwi-for-sentinel-2-images-in-google-earth-engine
#Kassala, Sudan
mndwi_map = gm.Map(center = [15.4007,73.9160], zoom = 11)
# Get a feature collection of administrative boundaries.
states = ee.FeatureCollection('FAO/GAUL/2015/level2').select('ADM1_NAME')
# Filter the feature collection to subset Goa, India
# because many times shape files tend to exclude large water bodies, one may need to select a rectangular area of interest depending upon the research question
ga = states.filter(ee.Filter.eq('ADM1_NAME', 'Goa'))
# note that for filtering less cloudy images from landsat we use .filter(ee.Filter.lte('CLOUD_COVER', 15))
mndwi_image = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2").filterDate('2020-01-01', '2020-12-31').filter(ee.Filter.lte('CLOUD_COVER', 15)).median().clip(ga)
mndwi = getMNDWI(mndwi_image)
mndwi_masked = mndwi.updateMask(mndwi.gte(0))
mndwiParams = {'min': 0,
'max': 0.2,
'palette': ['#f1eef6', '#ff0000']}
mndwi_map.addLayer(mndwi_masked, mndwiParams, 'MNDWI')
mndwi_map.addLayerControl()
mndwi_map
# Exercise 7
# Create a MNDWI map for Lake Chad, Chad
# Hint: The code will be same as before with only lat-long and area of interest changed
# Begin writing your code HERE
In this section, we will learn how to export the calculated values as a CSV file that can be incorporated as covariates in an econometric model. The file that gets generated in this process contains three things:
This piece of code returns a CSV file which is saved in the Google Drive of the account which was used to register for Google Earth Engine. For more information, pleas refer to the official guide from Google Earth Engine here.
You can export a FeatureCollection as CSV, SHP (shapefile), GeoJSON, KML, KMZ or TFRecord using Export.table
def getNDVI(image): #this function takes a google earth engine image
return image.normalizedDifference(['B8', 'B4'])
# Get a feature collection of administrative boundaries.
#states = ee.FeatureCollection('FAO/GAUL/2015/level2').select('ADM1_NAME')
states = ee.FeatureCollection('FAO/GAUL/2015/level2')
# Filter the feature collection to subset Bihar
bihar = states.filter(ee.Filter.eq('ADM1_NAME', 'Bihar')) #feature collection, multiple regions stored in a feature collection
image = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-01-01', '2020-12-31').median().clip(bihar)
ndvi = getNDVI(image) #the input to this function is an image and the output is an image too
biharMeansFeatures = ndvi.reduceRegions( #input to this function are an image and a feature collection, i.e. image and bihar in this case
collection = bihar,
reducer = ee.Reducer.median(),
scale = 5000, #for lower scale, you may get an error because of limitations on free features of GEE
)
# this saves to drive, you might save it in the assets and cloud storage
ee.batch.Export.table.toDrive(collection = biharMeansFeatures, description = 'exportTableExample', fileFormat = 'CSV').start()
# Exercise 8
# Create an EVI map for the regions of Namibia and export it as CSV file
This is an example of how vegetation indices can be used to see the change in the vegetation after an event. We try to explore how the state of Maharashtra is transformed after the monsoon rains which last approximately from June to October and is instrumental in the agriculture of the state.
#Maharashtra March
def getNDVI(image):
return image.normalizedDifference(['B8', 'B4'])
# Get a feature collection of administrative boundaries.
states = ee.FeatureCollection('FAO/GAUL/2015/level2').select('ADM1_NAME')
# Filter the feature collection to subset Maharashtra
mh = states.filter(ee.Filter.eq('ADM1_NAME', 'Maharashtra'))
image_left = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-05-01', '2020-05-31').median().clip(mh) #May
ndvi_left = getNDVI(image_left) #input and output are images
ndviParams = {'min': 0,
'max': 0.5,
'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850']}
map_left = gm.Map(center = [19.079046, 77.468456], zoom = 7)
map_left.addLayer(ndvi_left, ndviParams, 'NDVI')
map_left.addLayerControl()
map_left
#Maharashtra March
def getNDVI(image):
return image.normalizedDifference(['B8', 'B4'])
# Get a feature collection of administrative boundaries.
states = ee.FeatureCollection('FAO/GAUL/2015/level2').select('ADM1_NAME')
# Filter the feature collection to subset Maharashtra
mh = states.filter(ee.Filter.eq('ADM1_NAME', 'Maharashtra'))
image_right = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-11-01', '2020-11-30').median().clip(mh) #November
ndvi_right = getNDVI(image_right) #input and output are images
ndviParams = {'min': 0,
'max': 0.5,
'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850']}
map_right = gm.Map(center = [19.079046, 77.468456], zoom = 7)
map_right.addLayer(ndvi_right, ndviParams, 'NDVI')
map_right.addLayerControl()
map_right
map_final = gm.Map(center = [19.079046, 77.468456], zoom = 7)
map_final.split_map(left_layer = gm.ee_tile_layer(ndvi_left, ndviParams, 'May 2020'), right_layer = gm.ee_tile_layer(ndvi_right, ndviParams, 'November 2020'))
map_final.addLayerControl()
map_final
We shall be taking the example of this study that was funded by 3ie and undertaken by Rohini Pande and Anant Sudarshan. This study evaluated the impact of a set of reforms to the environmental clearance process in India, which included subjecting larger projects to scrutiny from regulators, independent experts and the public. Mines that applied for clearance after the reforms experienced substantially shorter clearance times, but were more likely to deforest illegally before receiving clearance. Public hearings did not significantly alter the costs or benefits of the clearance process. We got the data from Harvard Dataverse repository maintained by 3ie.
The author uses remote sensing to capture the average, median, minimum and maximum EVI in an area one km around the location of the mine. To accomplish the task, we perform the following operations.
The illustrative example below shows the code needed for exporting median EVI values. The same can be done for exporting minimum, average, and maximum value as well by changing the word median
to the relevant function in the given code.
evi_image = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-01-01', '2020-12-31').median().clip(mine_area)
# we need to make a list of coordinates where each coordinate represents a mine
# we are showing a sample of locations only as free version of GEE has limited functionalities
co_ords = [[13.37063,76.69898],
[15.16263,76.43827],
[11.32831,79.24147],
[13.41373,76.67617],
[15.14278,76.45530],
[15.53973,74.04421],
[9.551391,78.10116]]
locations = ee.List(co_ords).map(lambda coords : ee.Feature(ee.Geometry.Point(coords).buffer(1000)))
mine_area = ee.FeatureCollection(locations)
evi_image = ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2020-01-01', '2020-12-31').median().clip(mine_area) #annual EVI for 2020
evi = evi_image.expression(
'2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))', {
'NIR': evi_image.select('B8'),
'RED': evi_image.select('B4'),
'BLUE': evi_image.select('B2')
})
eviParams = {'min': -1,
'max': 1,
'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850']}
mine_map = gm.Map(center = [24.922695, 77.531718], zoom = 6, lite_mode = True)
mine_map.addLayer(mine_area, {'color': 'red'})
mine_map.addLayer(evi, eviParams, 'EVI')
mine_map.addLayerControl()
mine_map
# the calculation may result into user memory exceeded if you use free version of Google Earth Engine
# or if the scale is low
# or if the number of observations is too large
mineMeansFeatures = evi.reduceRegions(
collection = mine_area,
reducer = ee.Reducer.mean(),
scale = 30,
)
ee.batch.Export.table.toDrive(collection = mineMeansFeatures, description = 'minesEVI', fileFormat = 'CSV').start()
This tutorial was developed by Aayush Malik on behalf of International Initiative for Impact Evaluation (3ie), with funding from Foreign, Commonwealth and Development Office (FCDO), Government of the United Kingdom through A2012 CEDIL agreement with CEDIL.
Developing a large piece of technical information requires support from a variety of people working in a team. This tutorial has been accomplished by the collective effort of multiple people working at the Data Innovations Group at the International Initiative for Impact Evaluation.
Douglas Glandon, Lead Evaluation Specialist, helped steer the project from the beginning by providing background information and work that 3ie had done so far on using remote sensing for impact evaluation. Moreover, he conceptualized the tutorial and provided substantial feedback on the content and user-friendliness of the code. Furthermore, he co-developed the section on benefits, applications, and challenges of remote sensing.
Jane Hammaker, Research Associate, acted as the Project Manager for this technical piece and was instrumental in taking care of the operational side by constantly staying in touch with FCDO, marketing the event to the right audience, communicating with the Communications Office at 3ie, and sharing feedback on the flow and structure of the tutorial.
Sayak Khatua, Evaluation Specialist, provided feedback on the tutorial, contributed to past work done by 3ie on the applications of big data for impact evaluation, and helped with the case-study development by sharing the data and checking the outputs of the case-study.
All the above members tested the code themselves and shared feedback on the output, explanation of output, graphical representation, and sharing the instances of where the code was not working. I am grateful to all these people for their contributions.
It is worth mentioning that this tutorial follows the general structure used by the Open Nighttime Lights tutorial. We suggest you to follow that tutorial too for a better understanding of how nighttime satellite imagery can be used to perform various forms of evaluations.