Skip to content

Instantly share code, notes, and snippets.

@jdbcode
Last active February 3, 2024 18:43
Show Gist options
  • Save jdbcode/ad396df3bbc4dac1dad3f1f3a057b462 to your computer and use it in GitHub Desktop.
Save jdbcode/ad396df3bbc4dac1dad3f1f3a057b462 to your computer and use it in GitHub Desktop.
ee_landtrendr_example.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "ee_landtrendr_example.ipynb",
"provenance": [],
"collapsed_sections": [],
"authorship_tag": "ABX9TyMgofxswSm5JB/4/uGddUos",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/jdbcode/ad396df3bbc4dac1dad3f1f3a057b462/ee_landtrendr_example.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "fV7jrOOkEawj"
},
"outputs": [],
"source": [
"# Copyright 2022 The Earth Engine Community Authors\n",
"#\n",
"# Licensed under the Apache License, Version 2.0 (the \"License\")\n",
"# you may not use this file except in compliance with the License.\n",
"# You may obtain a copy of the License at\n",
"#\n",
"# https://www.apache.org/licenses/LICENSE-2.0\n",
"#\n",
"# Unless required by applicable law or agreed to in writing, software\n",
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
"# See the License for the specific language governing permissions and\n",
"# limitations under the License.\n",
"\n",
"\n",
"from google.colab import auth\n",
"auth.authenticate_user()\n",
"\n",
"import google\n",
"SCOPES = ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/earthengine']\n",
"CREDENTIALS, project_id = google.auth.default(default_scopes=SCOPES)\n",
"\n",
"import ee\n",
"ee.Initialize(CREDENTIALS, project='my-project (edit this string)')\n",
"\n",
"import folium\n",
"import pprint\n",
"\n",
"\n",
"# A MODIS collection filtered by year and month, non-QA bands selected.\n",
"col = (ee.ImageCollection('MODIS/061/MOD13Q1')\n",
" .filterDate('2000', '2021')\n",
" .filter(ee.Filter.calendarRange(7, 8, 'month'))\n",
" .select('NDVI|EVI|sur_refl.*'))\n",
"\n",
"# Add year property to all images in the collection.\n",
"col = col.map(lambda img: img.set('year', img.date().get('year')))\n",
"\n",
"# Join all images from the same year into lists.\n",
"distinctCol = col.distinct(['year'])\n",
"joinFilter = ee.Filter.equals(**{'leftField': 'year', 'rightField': 'year'})\n",
"joinCol = ee.Join.saveAll('same_year').apply(distinctCol, col, joinFilter)\n",
"\n",
"# Composite the images from each unique year by median.\n",
"def annualComposite(img):\n",
" img = ee.Image(img)\n",
" # Calculate median per-pixel annual July-August composite.\n",
" median = ee.ImageCollection.fromImages(img.get('same_year')).median()\n",
" # Construct an image for LandTrendr to base segmentation on. The algorithm\n",
" # expects that vegetation loss for a given spectral index is a positive\n",
" # delta, so here we invert the EVI band.\n",
" segBand = median.select(['EVI']).multiply(-1).rename('EVI_lt_seg')\n",
" # LandTrendr uses the first band of an image for segmentation and then\n",
" # applies vertex-anchored interpolation to subsequent bands' time series, so\n",
" # here we prepend the constructed segmentation band to the original median\n",
" # image composite. \n",
" ltImage = segBand.addBands(median)\n",
" # Transfer date metadata to the new image.\n",
" year = img.getNumber('year')\n",
" millis = ee.Date.fromYMD(year, 8, 1).millis() # Unix timestamp required\n",
" return ltImage.set({'year': year, 'system:time_start': millis})\n",
"\n",
"annualCol = joinCol.map(annualComposite)\n",
"\n",
"# Set LandTrendr segmentation parameters. Note that the timeSeries argument\n",
"# is the annual composite from above.\n",
"ltParams = {\n",
" 'timeSeries': ee.ImageCollection(annualCol),\n",
" 'maxSegments': 7,\n",
" 'spikeThreshold': 0.9,\n",
" 'vertexCountOvershoot': 3,\n",
" 'preventOneYearRecovery': False,\n",
" 'recoveryThreshold': 0.25,\n",
" 'pvalThreshold': 0.1,\n",
" 'bestModelProportion': 0.75,\n",
" 'minObservationsNeeded': 6\n",
"}\n",
"\n",
"# Run the LandTrendr algorithm.\n",
"lt = ee.Algorithms.TemporalSegmentation.LandTrendr(**ltParams)\n",
"print('LandTrendr results structure')\n",
"pprint.pprint(lt.getInfo)\n",
"\n",
"# Subset an example fit-to-vertices (FTV) index array, EVI here.\n",
"eviFtvArray = lt.select('EVI_fit')\n",
"\n",
"# Convert the image array to an image with bands labeled by year.\n",
"years = (distinctCol.aggregate_array('year')\n",
" .sort().map(lambda i: ee.Number(i).format()))\n",
"eviFtvImg = eviFtvArray.arrayFlatten([years])\n",
"\n",
"# Display the FTV image to the map using write function memory insertion\n",
"# to highlight change between three time slices. Colored pixels indicate\n",
"# change, grey scale indicates no change.\n",
"visParams = {'bands': ['2000', '2010', '2020'], 'min': 1400, 'max': 5800}\n",
"aoi = ee.Geometry.BBox(-124.67, 41.89, -121.10, 46.29)\n",
"map = folium.Map(\n",
" location=[44.12, -122.67],\n",
" zoom_start=7,\n",
" tiles=eviFtvImg.clip(aoi).getMapId(visParams)['tile_fetcher'].url_format,\n",
" attr='Map Data &copy; <a href=\"https://earthengine.google.com/\">Google Earth Engine</a>',\n",
")\n",
"display(map)\n",
"\n",
"# For more information on working with LandTrendr outputs, please see:\n",
"# https:#emapr.github.io/LT-GEE/working-with-outputs.html"
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment