{ "cells": [ { "cell_type": "markdown", "id": "0", "metadata": {}, "source": [ "[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/gee-community/geemap/blob/master/docs/workshops/Alaska_2024_Part3.ipynb)\n", "\n", "**Geospatial Cloud Computing with the GEE Python API - Part 3**\n", "\n", "- Notebook: \n", "- Earth Engine: \n", "- Geemap: \n", "\n", "## Introduction\n", "\n", "This notebook contains the materials for the third part of the workshop **Geospatial Cloud Computing with the GEE Python API** at the University of Alaska Fairbanks.\n", "\n", "This workshop provides an introduction to cloud-based geospatial analysis using the Earth Engine Python API. Attendees will learn the basics of Earth Engine data types and how to visualize, analyze, and export Earth Engine data in a Jupyter environment with geemap. In addition, attendees will learn how to develop and deploy interactive Earth Engine web apps with Python. Through practical examples and hands-on exercises, attendees will enhance their learning experience. During each hands-on session, attendees will walk through Jupyter Notebook examples on Google Colab with the instructors. At the end of each session, they will complete a hands-on exercise to apply the knowledge they have learned.\n", "\n", "### Agenda\n", "\n", "The workshop is divided into three parts. The third part will cover the following topics:\n", "\n", "- Image Classification (focused on land cover in Alaska)\n", "- Accuracy assessment\n", "- Create and export maps\n", "- Building interactive web apps\n", "\n", "### Prerequisites\n", "\n", "- To use geemap and the Earth Engine Python API, you must [register](https://code.earthengine.google.com/register) for an Earth Engine account and follow the instructions [here](https://docs.google.com/document/d/1ZGSmrNm6_baqd8CHt33kIBWOlvkh-HLr46bODgJN1h0/edit?usp=sharing) to create a Cloud Project. Earth Engine is free for [noncommercial and research use](https://earthengine.google.com/noncommercial). To test whether you can use authenticate the Earth Engine Python API, please run [this notebook](https://colab.research.google.com/github/gee-community/geemap/blob/master/docs/notebooks/geemap_colab.ipynb) on Google Colab.\n", "\n", "## Technical requirements\n", "\n", "### Install packages\n", "\n", "```bash\n", "conda create -n gee python=3.11\n", "conda activate gee\n", "conda install -c conda-forge mamba\n", "mamba install -c conda-forge pygis cartopy solara\n", "```" ] }, { "cell_type": "code", "execution_count": null, "id": "1", "metadata": {}, "outputs": [], "source": [ "# %pip install geemap pygis cartopy solara" ] }, { "cell_type": "markdown", "id": "2", "metadata": {}, "source": [ "### Import libraries" ] }, { "cell_type": "code", "execution_count": null, "id": "3", "metadata": {}, "outputs": [], "source": [ "import ee\n", "import geemap" ] }, { "cell_type": "code", "execution_count": null, "id": "4", "metadata": {}, "outputs": [], "source": [ "geemap.ee_initialize()" ] }, { "cell_type": "markdown", "id": "5", "metadata": {}, "source": [ "## Image Classification\n", "\n", "### North American Land Change Monitoring System (NALCMS)\n", "\n", "The [2020 North American Land Cover 30-meter dataset](https://developers.google.com/earth-engine/datasets/catalog/USGS_NLCD_RELEASES_2020_REL_NALCMS) was produced as part of the North American Land Change Monitoring System (NALCMS), a trilateral effort between Natural Resources Canada, the United States Geological Survey, and three Mexican organizations." ] }, { "cell_type": "code", "execution_count": null, "id": "6", "metadata": {}, "outputs": [], "source": [ "m = geemap.Map(center=[40, -100], zoom=4, height=700)\n", "m.add_basemap(\"Esri.WorldImagery\", False)\n", "landcover = ee.Image(\"USGS/NLCD_RELEASES/2020_REL/NALCMS\")\n", "\n", "states = ee.FeatureCollection(\"TIGER/2018/States\")\n", "fc = states.filter(ee.Filter.eq(\"NAME\", \"Alaska\"))\n", "\n", "legend_dict = {\n", " \"1 Temperate forest\": \"033e00\",\n", " \"2 Sub-polar forest\": \"939b71\",\n", " \"3 Tropical evergreen forest\": \"196d12\",\n", " \"4 Tropical deciduous forest\": \"1fab01\",\n", " \"5 Temperate deciduous forest\": \"5b725c\",\n", " \"6 Mixed forest\": \"6b7d2c\",\n", " \"7 Tropical shrubland\": \"b29d29\",\n", " \"8 Temperate shrubland\": \"b48833\",\n", " \"9 Tropical grassland\": \"e9da5d\",\n", " \"10 Temperate grassland\": \"e0cd88\",\n", " \"11 Sub-polar shrubland\": \"a07451\",\n", " \"12 Sub-polar grassland\": \"bad292\",\n", " \"13 Sub-polar barren\": \"3f8970\",\n", " \"14 Wetland\": \"6ca289\",\n", " \"15 Cropland\": \"e6ad6a\",\n", " \"16 Barren land\": \"a9abae\",\n", " \"17 Urban and built-up\": \"db2126\",\n", " \"18 Water\": \"4c73a1\",\n", " \"19 Snow and ice \": \"fff7fe\",\n", "}\n", "\n", "palette = list(legend_dict.values())\n", "vis_params = {\"palette\": palette, \"min\": 1, \"max\": 19}\n", "\n", "m.add_layer(landcover, vis_params, \"NALCMS Land Cover\")\n", "m.add_layer(fc, {}, \"Alaska\", False)\n", "m.center_object(fc, 4)\n", "m.add_legend(title=\"Land Cover Type\", legend_dict=legend_dict)\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "7", "metadata": {}, "outputs": [], "source": [ "fig = geemap.image_histogram(\n", " landcover, region=fc, x_label=\"Land cover type\", y_label=\"Pixels\"\n", ")\n", "fig" ] }, { "cell_type": "code", "execution_count": null, "id": "8", "metadata": {}, "outputs": [], "source": [ "values = list(fig.data[0][\"x\"])\n", "values" ] }, { "cell_type": "code", "execution_count": null, "id": "9", "metadata": {}, "outputs": [], "source": [ "classes = [list(legend_dict.keys())[int(value) - 1] for value in values]\n", "classes" ] }, { "cell_type": "code", "execution_count": null, "id": "10", "metadata": {}, "outputs": [], "source": [ "fig.update_xaxes(tickvals=values, ticktext=classes)\n", "fig" ] }, { "cell_type": "markdown", "id": "11", "metadata": {}, "source": [ "### Unsupervised classification" ] }, { "cell_type": "code", "execution_count": null, "id": "12", "metadata": {}, "outputs": [], "source": [ "region = ee.Geometry.BBox(-149.352, 64.5532, -147.0976, 65.1277)\n", "collection = geemap.landsat_timeseries(\n", " region, start_year=2021, end_year=2021, start_date=\"06-01\", end_date=\"09-01\"\n", ")\n", "image = collection.first()" ] }, { "cell_type": "code", "execution_count": null, "id": "13", "metadata": {}, "outputs": [], "source": [ "m = geemap.Map()\n", "m.add_basemap(\"Esri.WorldImagery\")\n", "vis_params = {\"min\": 0, \"max\": 0.3, \"bands\": [\"NIR\", \"Red\", \"Green\"]}\n", "m.add_layer(image, vis_params, \"Image\")\n", "m.add_layer(landcover.clip(region), {}, \"NALCMS land cover\", False)\n", "# m.add_legend(title='Land Cover Type', legend_dict=legend_dict, layer_name='NALCMS land cover')\n", "m.center_object(region, 9)\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "14", "metadata": {}, "outputs": [], "source": [ "training = image.sample(\n", " **{\n", " \"region\": region,\n", " \"scale\": 150,\n", " \"numPixels\": 5000,\n", " \"seed\": 1,\n", " \"geometries\": True,\n", " }\n", ")\n", "m.add_layer(training, {}, \"Training samples\")" ] }, { "cell_type": "code", "execution_count": null, "id": "15", "metadata": {}, "outputs": [], "source": [ "geemap.ee_to_df(training.limit(5))" ] }, { "cell_type": "code", "execution_count": null, "id": "16", "metadata": {}, "outputs": [], "source": [ "clusterer = ee.Clusterer.wekaXMeans(minClusters=3, maxClusters=6).train(training)" ] }, { "cell_type": "code", "execution_count": null, "id": "17", "metadata": {}, "outputs": [], "source": [ "result = image.cluster(clusterer)\n", "m.add_layer(result.randomVisualizer(), {}, \"Clusters\")\n", "m.add(\"layer_manager\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "18", "metadata": {}, "outputs": [], "source": [ "geemap.image_histogram(landcover, region=region, scale=30)" ] }, { "cell_type": "code", "execution_count": null, "id": "19", "metadata": {}, "outputs": [], "source": [ "geemap.image_histogram(result, region=region, scale=300)" ] }, { "cell_type": "markdown", "id": "20", "metadata": {}, "source": [ "### Supervised classification" ] }, { "cell_type": "code", "execution_count": null, "id": "21", "metadata": {}, "outputs": [], "source": [ "m = geemap.Map()\n", "m.add_basemap(\"Esri.WorldImagery\")\n", "vis_params = {\"min\": 0, \"max\": 0.3, \"bands\": [\"NIR\", \"Red\", \"Green\"]}\n", "m.add_layer(image, vis_params, \"Image\")\n", "m.add_layer(landcover.clip(region), {}, \"NALCMS land cover\", False)\n", "# m.add_legend(title='Land Cover Type', legend_dict=legend_dict, layer_name='NALCMS land cover')\n", "m.center_object(region, 9)\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "22", "metadata": {}, "outputs": [], "source": [ "points = landcover.sample(\n", " **{\n", " \"region\": region,\n", " \"scale\": 150,\n", " \"numPixels\": 5000,\n", " \"seed\": 1,\n", " \"geometries\": True,\n", " }\n", ")\n", "\n", "m.add_layer(points, {}, \"Training\", False)" ] }, { "cell_type": "code", "execution_count": null, "id": "23", "metadata": {}, "outputs": [], "source": [ "label = \"landcover\"\n", "features = image.sampleRegions(\n", " **{\"collection\": points, \"properties\": [label], \"scale\": 150}\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "24", "metadata": {}, "outputs": [], "source": [ "geemap.ee_to_df(features.limit(5))" ] }, { "cell_type": "code", "execution_count": null, "id": "25", "metadata": {}, "outputs": [], "source": [ "bands = image.bandNames().getInfo()\n", "params = {\n", " \"features\": features,\n", " \"classProperty\": label,\n", " \"inputProperties\": bands,\n", "}\n", "classifier = ee.Classifier.smileCart(maxNodes=None).train(**params)" ] }, { "cell_type": "code", "execution_count": null, "id": "26", "metadata": {}, "outputs": [], "source": [ "classified = image.classify(classifier).rename(\"landcover\")\n", "m.add_layer(classified.randomVisualizer(), {}, \"Classified\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "27", "metadata": {}, "outputs": [], "source": [ "class_values = list(range(1, 20))\n", "class_palette = list(legend_dict.values())\n", "classified = classified.set(\n", " {\"landcover_class_values\": class_values, \"landcover_class_palette\": class_palette}\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "28", "metadata": {}, "outputs": [], "source": [ "m.add_layer(classified, {}, \"Land cover\")\n", "m.add_legend(title=\"Land cover type\", builtin_legend=\"NLCD\")\n", "m" ] }, { "cell_type": "markdown", "id": "29", "metadata": {}, "source": [ "## Accuracy assessment" ] }, { "cell_type": "code", "execution_count": null, "id": "30", "metadata": {}, "outputs": [], "source": [ "m = geemap.Map()\n", "point = ee.Geometry.Point([-122.4439, 37.7538])\n", "\n", "img = (\n", " ee.ImageCollection(\"COPERNICUS/S2_SR\")\n", " .filterBounds(point)\n", " .filterDate(\"2020-01-01\", \"2021-01-01\")\n", " .sort(\"CLOUDY_PIXEL_PERCENTAGE\")\n", " .first()\n", " .select(\"B.*\")\n", ")\n", "\n", "vis_params = {\"min\": 100, \"max\": 3500, \"bands\": [\"B11\", \"B8\", \"B3\"]}\n", "\n", "m.centerObject(point, 9)\n", "m.add_layer(img, vis_params, \"Sentinel-2\")\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "31", "metadata": {}, "outputs": [], "source": [ "lc = ee.Image(\"ESA/WorldCover/v100/2020\")\n", "classValues = [10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 100]\n", "remapValues = ee.List.sequence(0, 10)\n", "label = \"lc\"\n", "lc = lc.remap(classValues, remapValues).rename(label).toByte()" ] }, { "cell_type": "code", "execution_count": null, "id": "32", "metadata": {}, "outputs": [], "source": [ "sample = img.addBands(lc).stratifiedSample(\n", " **{\n", " \"numPoints\": 100,\n", " \"classBand\": label,\n", " \"region\": img.geometry(),\n", " \"scale\": 10,\n", " \"geometries\": True,\n", " }\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "33", "metadata": {}, "outputs": [], "source": [ "sample = sample.randomColumn()\n", "trainingSample = sample.filter(\"random <= 0.8\")\n", "validationSample = sample.filter(\"random > 0.8\")" ] }, { "cell_type": "code", "execution_count": null, "id": "34", "metadata": {}, "outputs": [], "source": [ "trainedClassifier = ee.Classifier.smileRandomForest(numberOfTrees=10).train(\n", " **{\n", " \"features\": trainingSample,\n", " \"classProperty\": label,\n", " \"inputProperties\": img.bandNames(),\n", " }\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "35", "metadata": {}, "outputs": [], "source": [ "# print('Results of trained classifier', trainedClassifier.explain().getInfo())" ] }, { "cell_type": "code", "execution_count": null, "id": "36", "metadata": {}, "outputs": [], "source": [ "trainAccuracy = trainedClassifier.confusionMatrix()\n", "trainAccuracy.getInfo()" ] }, { "cell_type": "code", "execution_count": null, "id": "37", "metadata": {}, "outputs": [], "source": [ "trainAccuracy.accuracy().getInfo()" ] }, { "cell_type": "code", "execution_count": null, "id": "38", "metadata": {}, "outputs": [], "source": [ "trainAccuracy.kappa().getInfo()" ] }, { "cell_type": "code", "execution_count": null, "id": "39", "metadata": {}, "outputs": [], "source": [ "validationSample = validationSample.classify(trainedClassifier)\n", "validationAccuracy = validationSample.errorMatrix(label, \"classification\")\n", "validationAccuracy.getInfo()" ] }, { "cell_type": "code", "execution_count": null, "id": "40", "metadata": {}, "outputs": [], "source": [ "validationAccuracy.accuracy()" ] }, { "cell_type": "code", "execution_count": null, "id": "41", "metadata": {}, "outputs": [], "source": [ "validationAccuracy.producersAccuracy().getInfo()" ] }, { "cell_type": "code", "execution_count": null, "id": "42", "metadata": {}, "outputs": [], "source": [ "validationAccuracy.consumersAccuracy().getInfo()" ] }, { "cell_type": "code", "execution_count": null, "id": "43", "metadata": {}, "outputs": [], "source": [ "import csv\n", "\n", "with open(\"training.csv\", \"w\", newline=\"\") as f:\n", " writer = csv.writer(f)\n", " writer.writerows(trainAccuracy.getInfo())\n", "\n", "with open(\"validation.csv\", \"w\", newline=\"\") as f:\n", " writer = csv.writer(f)\n", " writer.writerows(validationAccuracy.getInfo())" ] }, { "cell_type": "code", "execution_count": null, "id": "44", "metadata": {}, "outputs": [], "source": [ "imgClassified = img.classify(trainedClassifier)" ] }, { "cell_type": "code", "execution_count": null, "id": "45", "metadata": {}, "outputs": [], "source": [ "classVis = {\n", " \"min\": 0,\n", " \"max\": 10,\n", " \"palette\": [\n", " \"006400\",\n", " \"ffbb22\",\n", " \"ffff4c\",\n", " \"f096ff\",\n", " \"fa0000\",\n", " \"b4b4b4\",\n", " \"f0f0f0\",\n", " \"0064c8\",\n", " \"0096a0\",\n", " \"00cf75\",\n", " \"fae6a0\",\n", " ],\n", "}\n", "m.add_layer(lc, classVis, \"ESA Land Cover\", False)\n", "m.add_layer(imgClassified, classVis, \"Classified\")\n", "m.add_layer(trainingSample, {\"color\": \"black\"}, \"Training sample\")\n", "m.add_layer(validationSample, {\"color\": \"white\"}, \"Validation sample\")\n", "m.add_legend(title=\"Land Cover Type\", builtin_legend=\"ESA_WorldCover\")\n", "m.centerObject(img)\n", "m" ] }, { "cell_type": "markdown", "id": "46", "metadata": {}, "source": [ "## Exercise 1 - Unsupervised classification\n", "\n", "Perform an unsupervised classification of a Sentinel-2 imagery for your preferred area. Relevant Earth Engine assets:\n", "\n", "- [ee.ImageCollection(\"COPERNICUS/S2_HARMONIZED\")](https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_HARMONIZED)" ] }, { "cell_type": "code", "execution_count": null, "id": "47", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "48", "metadata": {}, "source": [ "## Create and export maps" ] }, { "cell_type": "code", "execution_count": null, "id": "49", "metadata": {}, "outputs": [], "source": [ "m = geemap.Map(center=(41.0462, -109.7424), zoom=6)\n", "\n", "dem = ee.Image(\"USGS/SRTMGL1_003\")\n", "landsat7 = ee.Image(\"LANDSAT/LE7_TOA_5YEAR/1999_2003\").select(\n", " [\"B1\", \"B2\", \"B3\", \"B4\", \"B5\", \"B7\"]\n", ")\n", "\n", "vis_params = {\n", " \"min\": 0,\n", " \"max\": 4000,\n", " \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n", "}\n", "\n", "m.add_layer(\n", " landsat7,\n", " {\"bands\": [\"B4\", \"B3\", \"B2\"], \"min\": 20, \"max\": 200, \"gamma\": 2},\n", " \"landsat\",\n", ")\n", "m.add_layer(dem, vis_params, \"dem\", True, 1)\n", "m" ] }, { "cell_type": "code", "execution_count": null, "id": "50", "metadata": {}, "outputs": [], "source": [ "try:\n", " m.layer_to_image(\"dem\", output=\"dem.tif\", crs=\"EPSG:3857\", region=None, scale=None)\n", " m.layer_to_image(\"dem\", output=\"dem.jpg\", scale=500)\n", " geemap.show_image(\"dem.jpg\")\n", "except:\n", " pass" ] }, { "cell_type": "code", "execution_count": null, "id": "51", "metadata": {}, "outputs": [], "source": [ "try:\n", " m.layer_to_image(\"landsat\", output=\"landsat.tif\")\n", " geemap.geotiff_to_image(\"landsat.tif\", output=\"landsat.jpg\")\n", " geemap.show_image(\"landsat.jpg\")\n", "except:\n", " pass" ] }, { "cell_type": "code", "execution_count": null, "id": "52", "metadata": {}, "outputs": [], "source": [ "from geemap import cartoee\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "id": "53", "metadata": {}, "source": [ "### Plotting single-band images" ] }, { "cell_type": "code", "execution_count": null, "id": "54", "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "from geemap import cartoee" ] }, { "cell_type": "code", "execution_count": null, "id": "55", "metadata": {}, "outputs": [], "source": [ "srtm = ee.Image(\"CGIAR/SRTM90_V4\")\n", "\n", "# define bounding box [east, south, west, north] to request data\n", "region = [180, -60, -180, 85]\n", "vis = {\"min\": 0, \"max\": 3000}" ] }, { "cell_type": "code", "execution_count": null, "id": "56", "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(15, 9))\n", "\n", "# use cartoee to get a map\n", "ax = cartoee.get_map(srtm, region=region, vis_params=vis)\n", "\n", "# add a color bar to the map using the visualization params we passed to the map\n", "cartoee.add_colorbar(\n", " ax, vis, loc=\"bottom\", label=\"Elevation (m)\", orientation=\"horizontal\"\n", ")\n", "\n", "# add grid lines to the map at a specified interval\n", "cartoee.add_gridlines(ax, interval=[60, 30], linestyle=\":\")\n", "\n", "# add coastlines using the cartopy api\n", "ax.coastlines(color=\"red\")\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "57", "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(15, 7))\n", "\n", "cmap = \"terrain\"\n", "\n", "ax = cartoee.get_map(srtm, region=region, vis_params=vis, cmap=cmap)\n", "cartoee.add_colorbar(\n", " ax, vis, cmap=cmap, loc=\"right\", label=\"Elevation (m)\", orientation=\"vertical\"\n", ")\n", "\n", "cartoee.add_gridlines(ax, interval=[60, 30], linestyle=\"--\")\n", "ax.coastlines(color=\"red\")\n", "ax.set_title(label=\"Global Elevation Map\", fontsize=15)\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "58", "metadata": {}, "outputs": [], "source": [ "cartoee.savefig(fig, fname=\"srtm.jpg\", dpi=300, bbox_inches=\"tight\")" ] }, { "cell_type": "markdown", "id": "59", "metadata": {}, "source": [ "### Plotting multi-band images" ] }, { "cell_type": "code", "execution_count": null, "id": "60", "metadata": {}, "outputs": [], "source": [ "image = ee.Image(\"LANDSAT/LC08/C01/T1_SR/LC08_044034_20140318\")\n", "vis = {\"bands\": [\"B5\", \"B4\", \"B3\"], \"min\": 0, \"max\": 5000, \"gamma\": 1.3}" ] }, { "cell_type": "code", "execution_count": null, "id": "61", "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(15, 10))\n", "\n", "ax = cartoee.get_map(image, vis_params=vis)\n", "cartoee.pad_view(ax)\n", "cartoee.add_gridlines(ax, interval=0.5, xtick_rotation=0, linestyle=\":\")\n", "ax.coastlines(color=\"yellow\")\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "62", "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(15, 10))\n", "\n", "region = [-121.8025, 37.3458, -122.6265, 37.9178]\n", "ax = cartoee.get_map(image, vis_params=vis, region=region)\n", "cartoee.add_gridlines(ax, interval=0.15, xtick_rotation=0, linestyle=\":\")\n", "ax.coastlines(color=\"yellow\")\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "63", "metadata": {}, "source": [ "### Using custom projections\n", "\n", "#### The PlateCarree projection" ] }, { "cell_type": "code", "execution_count": null, "id": "64", "metadata": {}, "outputs": [], "source": [ "ocean = (\n", " ee.ImageCollection(\"NASA/OCEANDATA/MODIS-Terra/L3SMI\")\n", " .filter(ee.Filter.date(\"2018-01-01\", \"2018-03-01\"))\n", " .median()\n", " .select([\"sst\"], [\"SST\"])\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "65", "metadata": {}, "outputs": [], "source": [ "visualization = {\"bands\": \"SST\", \"min\": -2, \"max\": 30}\n", "bbox = [180, -88, -180, 88]" ] }, { "cell_type": "code", "execution_count": null, "id": "66", "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(15, 10))\n", "\n", "ax = cartoee.get_map(ocean, cmap=\"plasma\", vis_params=visualization, region=bbox)\n", "cb = cartoee.add_colorbar(ax, vis_params=visualization, loc=\"right\", cmap=\"plasma\")\n", "\n", "ax.set_title(label=\"Sea Surface Temperature\", fontsize=15)\n", "\n", "ax.coastlines()\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "67", "metadata": {}, "outputs": [], "source": [ "cartoee.savefig(fig, \"SST.jpg\", dpi=300)" ] }, { "cell_type": "markdown", "id": "68", "metadata": {}, "source": [ "#### Custom projections" ] }, { "cell_type": "code", "execution_count": null, "id": "69", "metadata": {}, "outputs": [], "source": [ "import cartopy.crs as ccrs" ] }, { "cell_type": "code", "execution_count": null, "id": "70", "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(15, 10))\n", "\n", "projection = ccrs.Mollweide(central_longitude=-180)\n", "ax = cartoee.get_map(\n", " ocean, vis_params=visualization, region=bbox, cmap=\"plasma\", proj=projection\n", ")\n", "cb = cartoee.add_colorbar(\n", " ax, vis_params=visualization, loc=\"bottom\", cmap=\"plasma\", orientation=\"horizontal\"\n", ")\n", "ax.set_title(\"Mollweide projection\")\n", "ax.coastlines()\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "71", "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(15, 10))\n", "\n", "projection = ccrs.Robinson(central_longitude=-180)\n", "ax = cartoee.get_map(\n", " ocean, vis_params=visualization, region=bbox, cmap=\"plasma\", proj=projection\n", ")\n", "cb = cartoee.add_colorbar(\n", " ax, vis_params=visualization, loc=\"bottom\", cmap=\"plasma\", orientation=\"horizontal\"\n", ")\n", "ax.set_title(\"Robinson projection\")\n", "ax.coastlines()\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "72", "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(15, 10))\n", "\n", "projection = ccrs.InterruptedGoodeHomolosine(central_longitude=-180)\n", "ax = cartoee.get_map(\n", " ocean, vis_params=visualization, region=bbox, cmap=\"plasma\", proj=projection\n", ")\n", "cb = cartoee.add_colorbar(\n", " ax, vis_params=visualization, loc=\"bottom\", cmap=\"plasma\", orientation=\"horizontal\"\n", ")\n", "ax.set_title(\"Goode homolosine projection\")\n", "ax.coastlines()\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "73", "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(15, 10))\n", "\n", "projection = ccrs.EqualEarth(central_longitude=-180)\n", "ax = cartoee.get_map(\n", " ocean, vis_params=visualization, region=bbox, cmap=\"plasma\", proj=projection\n", ")\n", "cb = cartoee.add_colorbar(\n", " ax, vis_params=visualization, loc=\"right\", cmap=\"plasma\", orientation=\"vertical\"\n", ")\n", "ax.set_title(\"Equal Earth projection\")\n", "ax.coastlines()\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "74", "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(11, 10))\n", "\n", "projection = ccrs.Orthographic(-130, -10)\n", "ax = cartoee.get_map(\n", " ocean, vis_params=visualization, region=bbox, cmap=\"plasma\", proj=projection\n", ")\n", "cb = cartoee.add_colorbar(\n", " ax, vis_params=visualization, loc=\"right\", cmap=\"plasma\", orientation=\"vertical\"\n", ")\n", "ax.set_title(\"Orographic projection\")\n", "ax.coastlines()\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "75", "metadata": {}, "source": [ "## Exercise 2 - Creating NDVI maps\n", "\n", "Create and export a global NDVI map using MODIS data. Relevant Earth Engine assets:\n", "\n", "- [ee.ImageCollection(\"MODIS/MCD43A4_006_NDVI\")](https://developers.google.com/earth-engine/datasets/catalog/MODIS_MCD43A4_006_NDVI)" ] }, { "cell_type": "code", "execution_count": null, "id": "76", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "markdown", "id": "77", "metadata": {}, "source": [ "## Building interactive web apps" ] }, { "cell_type": "code", "execution_count": null, "id": "78", "metadata": {}, "outputs": [], "source": [ "import ee\n", "import geemap\n", "import solara\n", "\n", "\n", "class Map(geemap.Map):\n", " def __init__(self, **kwargs):\n", " super().__init__(**kwargs)\n", " self.add_ee_data()\n", "\n", " def add_ee_data(self):\n", " years = [\"2001\", \"2004\", \"2006\", \"2008\", \"2011\", \"2013\", \"2016\", \"2019\"]\n", "\n", " def getNLCD(year):\n", " dataset = ee.ImageCollection(\"USGS/NLCD_RELEASES/2019_REL/NLCD\")\n", " nlcd = dataset.filter(ee.Filter.eq(\"system:index\", year)).first()\n", " landcover = nlcd.select(\"landcover\")\n", " return landcover\n", "\n", " collection = ee.ImageCollection(ee.List(years).map(lambda year: getNLCD(year)))\n", " labels = [f\"NLCD {year}\" for year in years]\n", " self.ts_inspector(\n", " left_ts=collection,\n", " right_ts=collection,\n", " left_names=labels,\n", " right_names=labels,\n", " )\n", " self.add_legend(\n", " title=\"NLCD Land Cover Type\",\n", " builtin_legend=\"NLCD\",\n", " height=\"460px\",\n", " add_header=False,\n", " )\n", "\n", "\n", "@solara.component\n", "def Page():\n", " with solara.Column(style={\"min-width\": \"500px\"}):\n", " Map.element(\n", " center=[40, -100],\n", " zoom=4,\n", " height=\"800px\",\n", " )\n", "\n", "\n", "Page()" ] }, { "cell_type": "markdown", "id": "79", "metadata": {}, "source": [ "```bash\n", "solara run ./pages\n", "```" ] }, { "cell_type": "code", "execution_count": null, "id": "80", "metadata": {}, "outputs": [], "source": [ "# geemap.get_ee_token()" ] }, { "cell_type": "markdown", "id": "81", "metadata": {}, "source": [ "## Exercise 3 - Deploying an Earth Engine app on Hugging Face.\n", "\n", "Follow the instructions [here](https://huggingface.co/spaces/giswqs/solara-geemap) to deploy an Earth Engine web app on Hugging Face." ] }, { "cell_type": "code", "execution_count": null, "id": "82", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }