This tutorial is all about getting everything setup so you can do the other tutorials on this site. In the process we will be reviewing fundamental python skills required. If at any point you're not sure what is happening please have a look at our background pages on Python, Geospatial Python, and Machine Learning.

Accounts

Google

For the lessons on this site you will need a Google Account (this can be @gmail or an institution/custom google account). You will use this account to access Google Drive, Google Colab, and activate Google Earth Engine.

Google Earth Engine

You will also need an account for Google Earth Engine for some lessons. You can sign up here, using your Google Account from the previous step to link everything together nicely.

Zindi

For some lessons we use data from Zindi.africa a Machine Learning competition site. Sign up here.

Connections

Let's go ahead an test some of our accounts

Google Drive

You will run the next code block. The output will show a link you have to open, copy the code from the page that loads and paste back into a cell provided.

from google.colab import drive
drive.mount('/content/drive/')
Mounted at /content/drive/

Explore your drive

Now that you've connected (mounted) google drive to your session, you can access it as a local disk.

Look at the panel on the left, for the folder icon (3rd from the top). You should see your google drive under drive where you should see My Drive and Shared Drives. At any point if you hover over a folder three vertical dots appear on the right, and if you click you will see the option to copy path.

Let's now explore using python to list files.

import os

# list the folders and files in your drive
mydrive = '/content/drive/My Drive'
colab = '/content/drive/My Drive/Colab Notebooks'
print(os.listdir(mydrive))

# list the current working directory
print(os.getcwd())

# make a data folder in your Colab Notebook directory if it doesn't exist
data_folder = os.path.join(colab,'data')
os.path.isdir(data_folder)
if (not os.path.isdir(data_folder)):
  os.mkdir(data_folder)
# list the contents
print(os.listdir(colab))
['Getting started.pdf', 'projects', 'personal', 'labs', 'publications', 'proposals', 'team', 'PROJECT End-of-Sprint Demos   Sprint Planning Agenda.gdoc', 'QGIS_US_donation_slide.pptx', 'community', 'Colab Notebooks']
/content
['EE_TF_PointData_Conv.ipynb', 'Copy of ee_ImageCollection.ipynb', 'data', '.ipynb_checkpoints', 'randomforest.ipynb', 'data1', '2020-02-19-GettingStarted.ipynb']

Google Earth Engine

Initialize

Next you will need to install the Google Earth Engine python API. If running on Colab it's already installed. It will autodetect and skip if already installed.

Then you need to initialize the Earth Engine session. The output will show a link you have to open, copy the code from the page that loads and paste back into a cell provided.

# If not on Colab you'll need install the earth-engine Python API
#!pip install earthengine-api #earth-engine Python API

# Athenticate to your GEE account. 
!earthengine authenticate
# Earth Engine Python API
import ee 
ee.Initialize()

Let's query Earth Engine [data collections] by looking for Imagery of Nepal.

We are going to:

  1. Define a bounding box or center point
  2. Identify the collection
  3. Query and create an ImageCollection
  4. Display on a map
  5. Export
  6. Reload Raster and Explore

Define a Bounding Box

You can define a Rectangle, Point, or Polygon for a spatial search by typing numbers, but that's tedious. It's much easier to get it from an existing data set or by selecting on a map. Let's get a Bounding Box in geojson format from geojson.io

  1. Search for a place (eyeglass in upper right), e.g Kathmandu
  2. Copy and Paste the geojson from the right panel, or Save GeoJSON file and then upload it to your Google Drive.
    Note: Due to some limitations we can’t use ipyleaflet in Colab so we needed to use an external tool.
# Make sure we have the libraries we need
%pip install folium
%pip install geopandas
# Let  see it on a map
import folium
import geopandas as gpd

# Paste the Geojson from geojsonio
aoi_geojson = '''{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              85.26712417602539,
              27.66558933380944
            ],
            [
              85.38213729858398,
              27.66558933380944
            ],
            [
              85.38213729858398,
              27.747202035778272
            ],
            [
              85.26712417602539,
              27.747202035778272
            ],
            [
              85.26712417602539,
              27.66558933380944
            ]
          ]
        ]
      }
    }
  ]
}'''

# Now let's read the geojson into a GeoPandasDataframe
aoi = gpd.read_file(aoi_geojson)
print(aoi) # so we can see what it looks like

#Get the bounding box
bbox = aoi.total_bounds
print(bbox) # see the coordinates

#Make it a GEE rectangle
gee_aoi = ee.Geometry.Rectangle(bbox.tolist())
center = aoi.centroid[0]
                                            geometry
0  POLYGON ((85.26712 27.66559, 85.38214 27.66559...
[85.26712418 27.66558933 85.3821373  27.74720204]
/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:53: UserWarning: Geometry is in a geographic CRS. Results from 'centroid' are likely incorrect. Use 'GeoSeries.to_crs()' to re-project geometries to a projected CRS before this operation.

Make this Notebook Trusted to load map: File -> Trust Notebook<iframe src="about:blank" style="position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;" data-html=PCFET0NUWVBFIGh0bWw+CjxoZWFkPiAgICAKICAgIDxtZXRhIGh0dHAtZXF1aXY9ImNvbnRlbnQtdHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PVVURi04IiAvPgogICAgPHNjcmlwdD5MX1BSRUZFUl9DQU5WQVM9ZmFsc2U7IExfTk9fVE9VQ0g9ZmFsc2U7IExfRElTQUJMRV8zRD1mYWxzZTs8L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2xlYWZsZXRAMS40LjAvZGlzdC9sZWFmbGV0LmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2NvZGUuanF1ZXJ5LmNvbS9qcXVlcnktMS4xMi40Lm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9ib290c3RyYXAvMy4yLjAvanMvYm9vdHN0cmFwLm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvTGVhZmxldC5hd2Vzb21lLW1hcmtlcnMvMi4wLjIvbGVhZmxldC5hd2Vzb21lLW1hcmtlcnMuanMiPjwvc2NyaXB0PgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2xlYWZsZXRAMS40LjAvZGlzdC9sZWFmbGV0LmNzcyIvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL21heGNkbi5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC8zLjIuMC9jc3MvYm9vdHN0cmFwLm1pbi5jc3MiLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9ib290c3RyYXAvMy4yLjAvY3NzL2Jvb3RzdHJhcC10aGVtZS5taW4uY3NzIi8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vbWF4Y2RuLmJvb3RzdHJhcGNkbi5jb20vZm9udC1hd2Vzb21lLzQuNi4zL2Nzcy9mb250LWF3ZXNvbWUubWluLmNzcyIvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9MZWFmbGV0LmF3ZXNvbWUtbWFya2Vycy8yLjAuMi9sZWFmbGV0LmF3ZXNvbWUtbWFya2Vycy5jc3MiLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9yYXdjZG4uZ2l0aGFjay5jb20vcHl0aG9uLXZpc3VhbGl6YXRpb24vZm9saXVtL21hc3Rlci9mb2xpdW0vdGVtcGxhdGVzL2xlYWZsZXQuYXdlc29tZS5yb3RhdGUuY3NzIi8+CiAgICA8c3R5bGU+aHRtbCwgYm9keSB7d2lkdGg6IDEwMCU7aGVpZ2h0OiAxMDAlO21hcmdpbjogMDtwYWRkaW5nOiAwO308L3N0eWxlPgogICAgPHN0eWxlPiNtYXAge3Bvc2l0aW9uOmFic29sdXRlO3RvcDowO2JvdHRvbTowO3JpZ2h0OjA7bGVmdDowO308L3N0eWxlPgogICAgCiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLAogICAgICAgIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCwgdXNlci1zY2FsYWJsZT1ubyIgLz4KICAgIDxzdHlsZT4jbWFwXzRlMzRhZmI5Yjc3ZDQ1YjBhOTkyZDc5ZjMxY2MzZmZkIHsKICAgICAgICBwb3NpdGlvbjogcmVsYXRpdmU7CiAgICAgICAgd2lkdGg6IDEwMC4wJTsKICAgICAgICBoZWlnaHQ6IDEwMC4wJTsKICAgICAgICBsZWZ0OiAwLjAlOwogICAgICAgIHRvcDogMC4wJTsKICAgICAgICB9CiAgICA8L3N0eWxlPgo8L2hlYWQ+Cjxib2R5PiAgICAKICAgIAogICAgPGRpdiBjbGFzcz0iZm9saXVtLW1hcCIgaWQ9Im1hcF80ZTM0YWZiOWI3N2Q0NWIwYTk5MmQ3OWYzMWNjM2ZmZCIgPjwvZGl2Pgo8L2JvZHk+CjxzY3JpcHQ+ICAgIAogICAgCiAgICAKICAgICAgICB2YXIgYm91bmRzID0gbnVsbDsKICAgIAoKICAgIHZhciBtYXBfNGUzNGFmYjliNzdkNDViMGE5OTJkNzlmMzFjYzNmZmQgPSBMLm1hcCgKICAgICAgICAnbWFwXzRlMzRhZmI5Yjc3ZDQ1YjBhOTkyZDc5ZjMxY2MzZmZkJywgewogICAgICAgIGNlbnRlcjogWzI3LjcwNjM5NTY4NDc5Mzg1OCwgODUuMzI0NjMwNzM3MzA0NjldLAogICAgICAgIHpvb206IDEyLAogICAgICAgIG1heEJvdW5kczogYm91bmRzLAogICAgICAgIGxheWVyczogW10sCiAgICAgICAgd29ybGRDb3B5SnVtcDogZmFsc2UsCiAgICAgICAgY3JzOiBMLkNSUy5FUFNHMzg1NywKICAgICAgICB6b29tQ29udHJvbDogdHJ1ZSwKICAgICAgICB9KTsKCgogICAgCiAgICB2YXIgdGlsZV9sYXllcl82OTRmNDVjOTAwN2E0NzEyOTc0NzhiNjBiY2JlYmM2MSA9IEwudGlsZUxheWVyKAogICAgICAgICdodHRwczovL3tzfS50aWxlLm9wZW5zdHJlZXRtYXAub3JnL3t6fS97eH0ve3l9LnBuZycsCiAgICAgICAgewogICAgICAgICJhdHRyaWJ1dGlvbiI6IG51bGwsCiAgICAgICAgImRldGVjdFJldGluYSI6IGZhbHNlLAogICAgICAgICJtYXhOYXRpdmVab29tIjogMTgsCiAgICAgICAgIm1heFpvb20iOiAxOCwKICAgICAgICAibWluWm9vbSI6IDAsCiAgICAgICAgIm5vV3JhcCI6IGZhbHNlLAogICAgICAgICJvcGFjaXR5IjogMSwKICAgICAgICAic3ViZG9tYWlucyI6ICJhYmMiLAogICAgICAgICJ0bXMiOiBmYWxzZQp9KS5hZGRUbyhtYXBfNGUzNGFmYjliNzdkNDViMGE5OTJkNzlmMzFjYzNmZmQpOwogICAgCiAgICAgICAgdmFyIG1hcmtlcl80MGY4YjE3NDRjN2M0OGIxOWYxM2YyYzY4NjQxNTlmZiA9IEwubWFya2VyKAogICAgICAgICAgICBbMjcuNzA2Mzk1Njg0NzkzODU4LCA4NS4zMjQ2MzA3MzczMDQ2OV0sCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGljb246IG5ldyBMLkljb24uRGVmYXVsdCgpLAogICAgICAgICAgICAgICAgfQogICAgICAgICAgICApLmFkZFRvKG1hcF80ZTM0YWZiOWI3N2Q0NWIwYTk5MmQ3OWYzMWNjM2ZmZCk7CiAgICAgICAgCiAgICAKCiAgICAgICAgICAgICAgICB2YXIgaWNvbl9hNjI2ZDVlMzY1YzM0ZWUyODk4OGRhNjkwODVmYmYzNiA9IEwuQXdlc29tZU1hcmtlcnMuaWNvbih7CiAgICAgICAgICAgICAgICAgICAgaWNvbjogJ29rLXNpZ24nLAogICAgICAgICAgICAgICAgICAgIGljb25Db2xvcjogJ3doaXRlJywKICAgICAgICAgICAgICAgICAgICBtYXJrZXJDb2xvcjogJ2dyZWVuJywKICAgICAgICAgICAgICAgICAgICBwcmVmaXg6ICdnbHlwaGljb24nLAogICAgICAgICAgICAgICAgICAgIGV4dHJhQ2xhc3NlczogJ2ZhLXJvdGF0ZS0wJwogICAgICAgICAgICAgICAgICAgIH0pOwogICAgICAgICAgICAgICAgbWFya2VyXzQwZjhiMTc0NGM3YzQ4YjE5ZjEzZjJjNjg2NDE1OWZmLnNldEljb24oaWNvbl9hNjI2ZDVlMzY1YzM0ZWUyODk4OGRhNjkwODVmYmYzNik7CiAgICAgICAgICAgIAogICAgCiAgICAgICAgICAgIHZhciBwb3B1cF84MGY4NWUyNjhjYTc0YWY3YmU3MGM2MzlkYmU0MmUyMSA9IEwucG9wdXAoe21heFdpZHRoOiAnMTAwJScKICAgICAgICAgICAgCiAgICAgICAgICAgIH0pOwoKICAgICAgICAgICAgCiAgICAgICAgICAgICAgICB2YXIgaHRtbF9mMDcyNWYwZjhkOTk0MmQwYjEyZGFmNjQ4NmVjZDQzNyA9ICQoYDxkaXYgaWQ9Imh0bWxfZjA3MjVmMGY4ZDk5NDJkMGIxMmRhZjY0ODZlY2Q0MzciIHN0eWxlPSJ3aWR0aDogMTAwLjAlOyBoZWlnaHQ6IDEwMC4wJTsiPkNlbnRlcjwvZGl2PmApWzBdOwogICAgICAgICAgICAgICAgcG9wdXBfODBmODVlMjY4Y2E3NGFmN2JlNzBjNjM5ZGJlNDJlMjEuc2V0Q29udGVudChodG1sX2YwNzI1ZjBmOGQ5OTQyZDBiMTJkYWY2NDg2ZWNkNDM3KTsKICAgICAgICAgICAgCgogICAgICAgICAgICBtYXJrZXJfNDBmOGIxNzQ0YzdjNDhiMTlmMTNmMmM2ODY0MTU5ZmYuYmluZFBvcHVwKHBvcHVwXzgwZjg1ZTI2OGNhNzRhZjdiZTcwYzYzOWRiZTQyZTIxKQogICAgICAgICAgICA7CgogICAgICAgICAgICAKICAgICAgICAKICAgIAogICAgICAgIHZhciBnZW9fanNvbl9iYTk3MjUyOWNlYjA0MDI3OGU2OWE1ODcxN2FiZmUxOCA9IEwuZ2VvSnNvbigKICAgICAgICAgICAgeyJmZWF0dXJlcyI6IFt7Imdlb21ldHJ5IjogeyJjb29yZGluYXRlcyI6IFtbWzg1LjI2NzEyNDE3NjAyNTM5LCAyNy42NjU1ODkzMzM4MDk0NF0sIFs4NS4zODIxMzcyOTg1ODM5OCwgMjcuNjY1NTg5MzMzODA5NDRdLCBbODUuMzgyMTM3Mjk4NTgzOTgsIDI3Ljc0NzIwMjAzNTc3ODI3Ml0sIFs4NS4yNjcxMjQxNzYwMjUzOSwgMjcuNzQ3MjAyMDM1Nzc4MjcyXSwgWzg1LjI2NzEyNDE3NjAyNTM5LCAyNy42NjU1ODkzMzM4MDk0NF1dXSwgInR5cGUiOiAiUG9seWdvbiJ9LCAicHJvcGVydGllcyI6IHsiaGlnaGxpZ2h0Ijoge30sICJzdHlsZSI6IHsiY29sb3IiOiAiYmx1ZSIsICJmaWxsY29sb3IiOiAidHJhbnNwYXJlbnQifX0sICJ0eXBlIjogIkZlYXR1cmUifV0sICJ0eXBlIjogIkZlYXR1cmVDb2xsZWN0aW9uIn0sCiAgICAgICAgICAgIHsKICAgICAgICAgICAgfQogICAgICAgICkuYWRkVG8obWFwXzRlMzRhZmI5Yjc3ZDQ1YjBhOTkyZDc5ZjMxY2MzZmZkICk7CiAgICAgICAgZ2VvX2pzb25fYmE5NzI1MjljZWIwNDAyNzhlNjlhNTg3MTdhYmZlMTguc2V0U3R5bGUoZnVuY3Rpb24oZmVhdHVyZSkge3JldHVybiBmZWF0dXJlLnByb3BlcnRpZXMuc3R5bGU7fSk7CiAgICAgICAgCjwvc2NyaXB0Pg== onload="this.contentDocument.open();this.contentDocument.write(atob(this.getAttribute('data-html')));this.contentDocument.close();" allowfullscreen webkitallowfullscreen mozallowfullscreen></iframe>
m = folium.Map(location=[center.y, center.x], tiles="OpenStreetMap", zoom_start=12)

folium.Marker(
    location=[center.y, center.x],
    popup='Center',
    icon=folium.Icon(color='green', icon='ok-sign'),
).add_to(m)

folium.features.GeoJson(aoi_geojson,
                        style_function = lambda x: {'color':'blue', 'fillcolor':'transparent'}
                        ).add_to(m)
    
m

Query Google Earth Engine

Now lets pick a collection of imagery to query for the region. We can choose from anything in the Google Earth Engine data catalog

Let's start with the Landsat 8 Surface Reflectance this year, using a cloud mask and selecting the bands to make an R,G,B mosiac

# To make a map we first need some helper functions

# Define the URL format used for Earth Engine generated map tiles.
EE_TILES = 'https://earthengine.googleapis.com/map/{mapid}/{{z}}/{{x}}/{{y}}?token={token}'

#@title Mapdisplay: Display GEE objects using folium.
def Mapdisplay(center, dicc, Tiles="OpensTreetMap",zoom_start=10):
    '''
    :param center: Center of the map (Latitude and Longitude).
    :param dicc: Earth Engine Geometries or Tiles dictionary
    :param Tiles: Mapbox Bright,Mapbox Control Room,Stamen Terrain,Stamen Toner,stamenwatercolor,cartodbpositron.
    :zoom_start: Initial zoom level for the map.
    :return: A folium.Map object.
    '''
    mapViz = folium.Map(location=center,tiles=Tiles, zoom_start=zoom_start)
    for k,v in dicc.items():
      if ee.image.Image in [type(x) for x in v.values()]:
        folium.TileLayer(
            tiles = v["tile_fetcher"].url_format,
            attr  = 'Google Earth Engine',
            overlay =True,
            name  = k
          ).add_to(mapViz)
      else:
        folium.GeoJson(
        data = v,
        name = k
          ).add_to(mapViz)
    mapViz.add_child(folium.LayerControl())
    return mapViz
# Query GEE for Landsat

l8_image = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')\
    .filterDate('2020-01-01', '2020-08-31')\
    .median()

# Time to make a map
l8_vis_params = {
  'bands': ['B4', 'B3', 'B2'],
  'min': 0,
  'max': 3000,
}

Mapdisplay(center=[center.y, center.x],
           dicc={'L8':l8_image.getMapId(l8_vis_params)}, 
           zoom_start=12)
Make this Notebook Trusted to load map: File -> Trust Notebook<iframe src="about:blank" style="position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;" data-html=PCFET0NUWVBFIGh0bWw+CjxoZWFkPiAgICAKICAgIDxtZXRhIGh0dHAtZXF1aXY9ImNvbnRlbnQtdHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PVVURi04IiAvPgogICAgPHNjcmlwdD5MX1BSRUZFUl9DQU5WQVM9ZmFsc2U7IExfTk9fVE9VQ0g9ZmFsc2U7IExfRElTQUJMRV8zRD1mYWxzZTs8L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2xlYWZsZXRAMS40LjAvZGlzdC9sZWFmbGV0LmpzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJodHRwczovL2NvZGUuanF1ZXJ5LmNvbS9qcXVlcnktMS4xMi40Lm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9ib290c3RyYXAvMy4yLjAvanMvYm9vdHN0cmFwLm1pbi5qcyI+PC9zY3JpcHQ+CiAgICA8c2NyaXB0IHNyYz0iaHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS9hamF4L2xpYnMvTGVhZmxldC5hd2Vzb21lLW1hcmtlcnMvMi4wLjIvbGVhZmxldC5hd2Vzb21lLW1hcmtlcnMuanMiPjwvc2NyaXB0PgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL2xlYWZsZXRAMS40LjAvZGlzdC9sZWFmbGV0LmNzcyIvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL21heGNkbi5ib290c3RyYXBjZG4uY29tL2Jvb3RzdHJhcC8zLjIuMC9jc3MvYm9vdHN0cmFwLm1pbi5jc3MiLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS9ib290c3RyYXAvMy4yLjAvY3NzL2Jvb3RzdHJhcC10aGVtZS5taW4uY3NzIi8+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vbWF4Y2RuLmJvb3RzdHJhcGNkbi5jb20vZm9udC1hd2Vzb21lLzQuNi4zL2Nzcy9mb250LWF3ZXNvbWUubWluLmNzcyIvPgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBocmVmPSJodHRwczovL2NkbmpzLmNsb3VkZmxhcmUuY29tL2FqYXgvbGlicy9MZWFmbGV0LmF3ZXNvbWUtbWFya2Vycy8yLjAuMi9sZWFmbGV0LmF3ZXNvbWUtbWFya2Vycy5jc3MiLz4KICAgIDxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0iaHR0cHM6Ly9yYXdjZG4uZ2l0aGFjay5jb20vcHl0aG9uLXZpc3VhbGl6YXRpb24vZm9saXVtL21hc3Rlci9mb2xpdW0vdGVtcGxhdGVzL2xlYWZsZXQuYXdlc29tZS5yb3RhdGUuY3NzIi8+CiAgICA8c3R5bGU+aHRtbCwgYm9keSB7d2lkdGg6IDEwMCU7aGVpZ2h0OiAxMDAlO21hcmdpbjogMDtwYWRkaW5nOiAwO308L3N0eWxlPgogICAgPHN0eWxlPiNtYXAge3Bvc2l0aW9uOmFic29sdXRlO3RvcDowO2JvdHRvbTowO3JpZ2h0OjA7bGVmdDowO308L3N0eWxlPgogICAgCiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLAogICAgICAgIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCwgdXNlci1zY2FsYWJsZT1ubyIgLz4KICAgIDxzdHlsZT4jbWFwXzU0Mjg5MWNiYjc0NDQzOTFiMGYwNWI2NDhjOWI1MTQzIHsKICAgICAgICBwb3NpdGlvbjogcmVsYXRpdmU7CiAgICAgICAgd2lkdGg6IDEwMC4wJTsKICAgICAgICBoZWlnaHQ6IDEwMC4wJTsKICAgICAgICBsZWZ0OiAwLjAlOwogICAgICAgIHRvcDogMC4wJTsKICAgICAgICB9CiAgICA8L3N0eWxlPgo8L2hlYWQ+Cjxib2R5PiAgICAKICAgIAogICAgPGRpdiBjbGFzcz0iZm9saXVtLW1hcCIgaWQ9Im1hcF81NDI4OTFjYmI3NDQ0MzkxYjBmMDViNjQ4YzliNTE0MyIgPjwvZGl2Pgo8L2JvZHk+CjxzY3JpcHQ+ICAgIAogICAgCiAgICAKICAgICAgICB2YXIgYm91bmRzID0gbnVsbDsKICAgIAoKICAgIHZhciBtYXBfNTQyODkxY2JiNzQ0NDM5MWIwZjA1YjY0OGM5YjUxNDMgPSBMLm1hcCgKICAgICAgICAnbWFwXzU0Mjg5MWNiYjc0NDQzOTFiMGYwNWI2NDhjOWI1MTQzJywgewogICAgICAgIGNlbnRlcjogWzI3LjcwNjM5NTY4NDc5Mzg1OCwgODUuMzI0NjMwNzM3MzA0NjldLAogICAgICAgIHpvb206IDEyLAogICAgICAgIG1heEJvdW5kczogYm91bmRzLAogICAgICAgIGxheWVyczogW10sCiAgICAgICAgd29ybGRDb3B5SnVtcDogZmFsc2UsCiAgICAgICAgY3JzOiBMLkNSUy5FUFNHMzg1NywKICAgICAgICB6b29tQ29udHJvbDogdHJ1ZSwKICAgICAgICB9KTsKCgogICAgCiAgICB2YXIgdGlsZV9sYXllcl9kMGI2NDRmMTkzZGE0MTE2OGViNTkzMmMxMWI3Y2FiMCA9IEwudGlsZUxheWVyKAogICAgICAgICdodHRwczovL3tzfS50aWxlLm9wZW5zdHJlZXRtYXAub3JnL3t6fS97eH0ve3l9LnBuZycsCiAgICAgICAgewogICAgICAgICJhdHRyaWJ1dGlvbiI6IG51bGwsCiAgICAgICAgImRldGVjdFJldGluYSI6IGZhbHNlLAogICAgICAgICJtYXhOYXRpdmVab29tIjogMTgsCiAgICAgICAgIm1heFpvb20iOiAxOCwKICAgICAgICAibWluWm9vbSI6IDAsCiAgICAgICAgIm5vV3JhcCI6IGZhbHNlLAogICAgICAgICJvcGFjaXR5IjogMSwKICAgICAgICAic3ViZG9tYWlucyI6ICJhYmMiLAogICAgICAgICJ0bXMiOiBmYWxzZQp9KS5hZGRUbyhtYXBfNTQyODkxY2JiNzQ0NDM5MWIwZjA1YjY0OGM5YjUxNDMpOwogICAgdmFyIHRpbGVfbGF5ZXJfYmMyYzg0ZjZhYWI4NDg0ZjgzOWNkMDk5YzAzMDgyOWIgPSBMLnRpbGVMYXllcigKICAgICAgICAnaHR0cHM6Ly9lYXJ0aGVuZ2luZS5nb29nbGVhcGlzLmNvbS92MWFscGhhL3Byb2plY3RzL2VhcnRoZW5naW5lLWxlZ2FjeS9tYXBzLzNlYzAzN2M5MjY3ZWFiOWIxODFmNTU3YzkyOGUwNzU2LWY3NGMzYzM3ZTVlYWZhMjA2Yzc2MDkzN2Y3OTQ2NzJjL3RpbGVzL3t6fS97eH0ve3l9JywKICAgICAgICB7CiAgICAgICAgImF0dHJpYnV0aW9uIjogIkdvb2dsZSBFYXJ0aCBFbmdpbmUiLAogICAgICAgICJkZXRlY3RSZXRpbmEiOiBmYWxzZSwKICAgICAgICAibWF4TmF0aXZlWm9vbSI6IDE4LAogICAgICAgICJtYXhab29tIjogMTgsCiAgICAgICAgIm1pblpvb20iOiAwLAogICAgICAgICJub1dyYXAiOiBmYWxzZSwKICAgICAgICAib3BhY2l0eSI6IDEsCiAgICAgICAgInN1YmRvbWFpbnMiOiAiYWJjIiwKICAgICAgICAidG1zIjogZmFsc2UKfSkuYWRkVG8obWFwXzU0Mjg5MWNiYjc0NDQzOTFiMGYwNWI2NDhjOWI1MTQzKTsKICAgIAogICAgICAgICAgICB2YXIgbGF5ZXJfY29udHJvbF82MDc5YjMxNWVkZjM0MTYxOGRmMTg4OTRhMGUzOWZlMSA9IHsKICAgICAgICAgICAgICAgIGJhc2VfbGF5ZXJzIDogeyAib3BlbnN0cmVldG1hcCIgOiB0aWxlX2xheWVyX2QwYjY0NGYxOTNkYTQxMTY4ZWI1OTMyYzExYjdjYWIwLCB9LAogICAgICAgICAgICAgICAgb3ZlcmxheXMgOiB7ICJMOCIgOiB0aWxlX2xheWVyX2JjMmM4NGY2YWFiODQ4NGY4MzljZDA5OWMwMzA4MjliLCB9CiAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICBMLmNvbnRyb2wubGF5ZXJzKAogICAgICAgICAgICAgICAgbGF5ZXJfY29udHJvbF82MDc5YjMxNWVkZjM0MTYxOGRmMTg4OTRhMGUzOWZlMS5iYXNlX2xheWVycywKICAgICAgICAgICAgICAgIGxheWVyX2NvbnRyb2xfNjA3OWIzMTVlZGYzNDE2MThkZjE4ODk0YTBlMzlmZTEub3ZlcmxheXMsCiAgICAgICAgICAgICAgICB7cG9zaXRpb246ICd0b3ByaWdodCcsCiAgICAgICAgICAgICAgICAgY29sbGFwc2VkOiB0cnVlLAogICAgICAgICAgICAgICAgIGF1dG9aSW5kZXg6IHRydWUKICAgICAgICAgICAgICAgIH0pLmFkZFRvKG1hcF81NDI4OTFjYmI3NDQ0MzkxYjBmMDViNjQ4YzliNTE0Myk7CiAgICAgICAgICAgIAogICAgICAgIAo8L3NjcmlwdD4= onload="this.contentDocument.open();this.contentDocument.write(atob(this.getAttribute('data-html')));this.contentDocument.close();" allowfullscreen webkitallowfullscreen mozallowfullscreen></iframe>

Exporting

At some point we often want to export data from Earth Engine for other uses. One might ask, can't we just do the whole analysis in Earth Engine? In some cases yes that's possible, but in other cases you need access to algorithms, data, or map making tools that just aren't possible in Earth Engine.

# But exporting the whole world to work with would be a pain, lets subset to our aoi
band_sel = ('B2', 'B3', 'B4', 'B5')

l8_image = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')\
    .filterDate('2020-01-01', '2020-08-31')\
    .select(band_sel)\
    .filterBounds(gee_aoi)\
    .median()

l8_image.getInfo()
{'bands': [{'crs': 'EPSG:4326',
   'crs_transform': [1, 0, 0, 0, 1, 0],
   'data_type': {'max': 32767,
    'min': -32768,
    'precision': 'double',
    'type': 'PixelType'},
   'id': 'B2'},
  {'crs': 'EPSG:4326',
   'crs_transform': [1, 0, 0, 0, 1, 0],
   'data_type': {'max': 32767,
    'min': -32768,
    'precision': 'double',
    'type': 'PixelType'},
   'id': 'B3'},
  {'crs': 'EPSG:4326',
   'crs_transform': [1, 0, 0, 0, 1, 0],
   'data_type': {'max': 32767,
    'min': -32768,
    'precision': 'double',
    'type': 'PixelType'},
   'id': 'B4'},
  {'crs': 'EPSG:4326',
   'crs_transform': [1, 0, 0, 0, 1, 0],
   'data_type': {'max': 32767,
    'min': -32768,
    'precision': 'double',
    'type': 'PixelType'},
   'id': 'B5'}],
 'type': 'Image'}
# Only run this when necessary, once exported you can use the file from google drive.
# Landsat 8 in this example takes about 10 minutes or less.
task.start()

Python & Map Data

Now that we've exported data from Google Earth Engine to Google Drive, we can practice loading data and working with spatial data in python.

%pip install rasterio
#%pip install geopandas # remember we already did this earlier
Collecting rasterio
  Downloading https://files.pythonhosted.org/packages/34/2b/c8de31dc2767ef1cdcec980b3fe041776262bcdc859417babaeaad42cf3f/rasterio-1.1.6-cp36-cp36m-manylinux1_x86_64.whl (18.3MB)
     |████████████████████████████████| 18.3MB 228kB/s 
Requirement already satisfied: attrs in /usr/local/lib/python3.6/dist-packages (from rasterio) (20.2.0)
Collecting affine
  Downloading https://files.pythonhosted.org/packages/ac/a6/1a39a1ede71210e3ddaf623982b06ecfc5c5c03741ae659073159184cd3e/affine-2.3.0-py2.py3-none-any.whl
Requirement already satisfied: cligj>=0.5 in /usr/local/lib/python3.6/dist-packages (from rasterio) (0.5.0)
Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from rasterio) (1.18.5)
Requirement already satisfied: click<8,>=4.0 in /usr/local/lib/python3.6/dist-packages (from rasterio) (7.1.2)
Requirement already satisfied: click-plugins in /usr/local/lib/python3.6/dist-packages (from rasterio) (1.1.1)
Collecting snuggs>=1.4.1
  Downloading https://files.pythonhosted.org/packages/cc/0e/d27d6e806d6c0d1a2cfdc5d1f088e42339a0a54a09c3343f7f81ec8947ea/snuggs-1.4.7-py3-none-any.whl
Requirement already satisfied: pyparsing>=2.1.6 in /usr/local/lib/python3.6/dist-packages (from snuggs>=1.4.1->rasterio) (2.4.7)
Installing collected packages: affine, snuggs, rasterio
Successfully installed affine-2.3.0 rasterio-1.1.6 snuggs-1.4.7
import rasterio as rio
import geopandas
%matplotlib inline
# Loading a raster and making a map
raster_path = os.path.join(output_mount_folder, output_drive_folder, raster_name)
l8_raster_path = ".".join([raster_path, "tif"])

# Check some attributes
with rio.open(l8_raster_path, 'r') as l8_raster:
  print(l8_raster.bounds)
  print(l8_raster.meta)
BoundingBox(left=85.26700879028404, bottom=27.665505636557317, right=85.38235247276498, top=27.747431990469018)
{'driver': 'GTiff', 'dtype': 'float64', 'nodata': None, 'width': 428, 'height': 304, 'count': 4, 'crs': CRS.from_epsg(4326), 'transform': Affine(0.00026949458523585647, 0.0, 85.26700879028404,
       0.0, -0.00026949458523585647, 27.747431990469018)}
import matplotlib.pyplot as plt
from rasterio.plot import show
%matplotlib inline

with rio.open(l8_raster_path, 'r') as l8_raster:
  fig, (ax1, ax2, ax3, ax4) = plt.subplots(ncols=4, nrows=1, figsize=(10, 4), sharey=True)

  # Plot Red, Green and Blue (rgb)
  show((l8_raster, 1), cmap='Blues', ax=ax1)
  show((l8_raster, 3), cmap='Reds', ax=ax3)
  show((l8_raster, 2), cmap='Greens', ax=ax2)
  show((l8_raster, 4), cmap='magma', ax=ax4)

  # Add titles
  ax1.set_title("Blue")
  ax2.set_title("Green")
  ax3.set_title("Red")
  ax4.set_title("NIR")
from rasterio.plot import reshape_as_image
import numpy

# Function to normalize the grid values
def normalize(array):
    """Normalizes numpy arrays into scale 0.0 - 1.0"""
    array_min, array_max = array.min(), array.max()
    return ((array - array_min)/(array_max - array_min))

# Open and read the raster data
with rio.open(l8_raster_path, 'r') as l8_raster:
  l8_data = l8_raster.read()
  
# To plot as RGB we have to normalize the data
l8_image = numpy.empty(l8_data.shape, dtype=numpy.float)
for band in range(l8_data.shape[0]):
  l8_image[band] = normalize(l8_data[band])


fig, (ax1, ax2) = plt.subplots(ncols=2, nrows=1, figsize=(10, 4), sharey=True)
show(l8_image[[2,1,0],:,:], transform=l8_raster.transform, adjust='linear', ax=ax1) # RGB
show(l8_image[[3,2,1],:,:], transform=l8_raster.transform, adjust='linear', ax=ax2) # False Color IR
<matplotlib.axes._subplots.AxesSubplot at 0x7f79b0c9c630>