Skip to content

Instantly share code, notes, and snippets.

@jdbcode
Last active September 24, 2024 12:08
Show Gist options
  • Save jdbcode/22e0193743fbcd95768cf42a141d5943 to your computer and use it in GitHub Desktop.
Save jdbcode/22e0193743fbcd95768cf42a141d5943 to your computer and use it in GitHub Desktop.
G4G24_Python_Plot_Report.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"authorship_tag": "ABX9TyPVCaS2HJrn/SCMYEGph3Xv",
"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/22e0193743fbcd95768cf42a141d5943/copy-of-g4g24_python_plot_report.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"source": [
"# Introduction"
],
"metadata": {
"id": "ngQxa_Z3V5Hy"
}
},
{
"cell_type": "markdown",
"source": [
"This notebook walks through the process of creating a PDF-format report for a given land plot. It shows the location of the plot and various spectral history figures that illustrate what is in the plot and how the plot changes seasonally and over 40 years. It uses Google Earth Engine along with many 3rd party Python libraries.\n",
"\n",
"If you need to get set up to use Earth Engine or are unfamiliar with running Earth Engine in a Colab notebook, use the [**Earth Engine Python Quickstart**](https://developers.google.com/earth-engine/guides/quickstart_python) to get going.\n",
"\n",
"_Presented at the 2024 Google Geo for Good Sao Paulo and Dublin meetings._"
],
"metadata": {
"id": "y828fUmgTBLk"
}
},
{
"cell_type": "markdown",
"source": [
"# Setup"
],
"metadata": {
"id": "X1kFvvXzVNil"
}
},
{
"cell_type": "markdown",
"source": [
"Intall packages we need that Colab doesn't have installed by defualt."
],
"metadata": {
"id": "Da-R2KND0rtW"
}
},
{
"cell_type": "code",
"source": [
"!pip install -q reportlab\n",
"!pip install -q -U pillow\n",
"!pip install -q cartopy\n",
"!pip install -q PyPDF2"
],
"metadata": {
"id": "IBFWlsQXVE87"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Import the libraries we'll need."
],
"metadata": {
"id": "WLSZOhgg0x0S"
}
},
{
"cell_type": "code",
"source": [
"import os\n",
"import io\n",
"import ee\n",
"import geemap\n",
"import glob\n",
"import requests\n",
"import json\n",
"import geopandas as gpd\n",
"import cartopy.crs as ccrs\n",
"import cartopy.feature as cfeature\n",
"import datetime\n",
"import numpy as np\n",
"import matplotlib\n",
"import PyPDF2\n",
"\n",
"from PIL import Image, ImageDraw, ImageFont\n",
"from matplotlib import pyplot as plt\n",
"from matplotlib import cm\n",
"from reportlab.pdfgen import canvas\n",
"from reportlab.lib.pagesizes import letter\n",
"from reportlab.lib.units import inch\n",
"from reportlab.lib.utils import ImageReader"
],
"metadata": {
"id": "hJ-MObQWLQ7E"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Authenticate and initialize to the Earth Engine service.\n",
"\n",
"CHANGE THE PROJECT TO YOURS."
],
"metadata": {
"id": "IrRZ_3XL023q"
}
},
{
"cell_type": "code",
"source": [
"ee.Authenticate()\n",
"ee.Initialize(project='ee-braaten') # CHANGE TO YOURS"
],
"metadata": {
"id": "Tzh8E3xCt15F"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Find a point of interest"
],
"metadata": {
"id": "c8EWxDEhVQfC"
}
},
{
"cell_type": "markdown",
"source": [
"Create a `geemap.Map` object and display it. Use it to identify a point to create a report for."
],
"metadata": {
"id": "5hnztizw1PgM"
}
},
{
"cell_type": "code",
"source": [
"m = geemap.Map()\n",
"m.add_basemap('SATELLITE')\n",
"m"
],
"metadata": {
"id": "UJ2Z6EzcxTSq"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Create a region of interest. Define the longitude and latitude of the selected location and define a buffer to add to the point."
],
"metadata": {
"id": "oP8WOga_hwfB"
}
},
{
"cell_type": "code",
"source": [
"lon = -123.6\n",
"lat = 48\n",
"buffer = 250 # meters\n",
"ee_region = ee.Geometry.Point(lon, lat).buffer(buffer)\n",
"m.add_layer(ee_region, {'color': 'yellow'}, 'ROI')"
],
"metadata": {
"id": "Uol0XAV0gHlB"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Set global and final output variables"
],
"metadata": {
"id": "z9E538eTVZKX"
}
},
{
"cell_type": "markdown",
"source": [
"Create some folders on the Colab VM and set some file names."
],
"metadata": {
"id": "fHX9NQiKhZ_H"
}
},
{
"cell_type": "code",
"source": [
"folder_figures = '/content/figures'\n",
"folder_monthly_images = '/content/monthly_images'\n",
"folder_annual_images = '/content/annual_images'\n",
"folder_pdfs = '/content/pdfs'\n",
"folders = [folder_figures, folder_monthly_images, folder_annual_images, folder_pdfs]\n",
"\n",
"small_scale_map = f'{folder_figures}/small_scale_map.png'\n",
"large_scale_map = f'{folder_figures}/large_scale_map.png'\n",
"single_year = f'{folder_figures}/landsat_large.png'\n",
"\n",
"monthly_nbr = f'{folder_figures}/monthly_nbr.png'\n",
"annual_nbr = f'{folder_figures}/annual_nbr.png'\n",
"\n",
"pdf_1 = f'{folder_pdfs}/page_1.pdf'\n",
"pdf_2 = f'{folder_pdfs}/page_2.pdf'\n",
"pdf_3 = f'{folder_pdfs}/page_3.pdf'\n",
"pdf_full = f'{folder_pdfs}/plot_report.pdf'\n",
"\n",
"annual_col = ee.ImageCollection('LANDSAT/COMPOSITES/C02/T1_L2_ANNUAL')\n",
"monthly_col = ee.ImageCollection('LANDSAT/COMPOSITES/C02/T1_L2_32DAY')\n",
"\n",
"for folder in folders:\n",
" if not os.path.exists(folder):\n",
" os.makedirs(folder)"
],
"metadata": {
"id": "45vWhWCVd7sV"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Make location figures"
],
"metadata": {
"id": "2GUjh99xVhtR"
}
},
{
"cell_type": "markdown",
"source": [
"Create a small-scale map of the location."
],
"metadata": {
"id": "kX7r7awHkRlX"
}
},
{
"cell_type": "code",
"source": [
"plt.figure(figsize=(5, 2.7))\n",
"ax = plt.axes(projection=ccrs.PlateCarree())\n",
"ax.add_feature(cfeature.LAND, color='lightgrey', zorder=0)\n",
"ax.gridlines(draw_labels=True, zorder=1)\n",
"ax.scatter(lon, lat, color='blue', zorder=2)\n",
"ax.set_global()\n",
"\n",
"plt.savefig(small_scale_map, bbox_inches='tight')\n",
"plt.show()"
],
"metadata": {
"id": "sgF8XtN306vR"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Create a medium-scale map of the location."
],
"metadata": {
"id": "uzSj3r18UQM0"
}
},
{
"cell_type": "code",
"source": [
"gpd_region = ee.data.computeFeatures({\n",
" 'expression': ee.FeatureCollection([ee.Feature(ee_region.bounds())]),\n",
" 'fileFormat': 'GEOPANDAS_GEODATAFRAME'\n",
"})\n",
"buffered_geom = gpd_region.buffer(10)\n",
"bounds = buffered_geom.bounds\n",
"\n",
"states_provinces = cfeature.NaturalEarthFeature(\n",
" category='cultural',\n",
" name='admin_1_states_provinces_lines',\n",
" scale='50m',\n",
" facecolor='none')\n",
"\n",
"plt.figure(figsize=(2.7, 2.7))\n",
"ax = plt.axes(projection=ccrs.PlateCarree())\n",
"ax.add_feature(cfeature.LAND, color='lightgrey', zorder=0)\n",
"ax.gridlines(draw_labels=True, zorder=1)\n",
"ax.add_feature(cfeature.BORDERS, edgecolor='gray', zorder=2)\n",
"ax.add_feature(cfeature.COASTLINE, edgecolor='gray', zorder=3)\n",
"ax.add_feature(states_provinces, edgecolor='gray', zorder=4)\n",
"ax.scatter(lon, lat, color='blue', zorder=5)\n",
"ax.set_extent(\n",
" [bounds['minx'], bounds['maxx'], bounds['miny'], bounds['maxy']],\n",
" crs=ccrs.PlateCarree())\n",
"plt.savefig(large_scale_map, bbox_inches='tight')\n",
"plt.show()"
],
"metadata": {
"id": "z38egOUGZ_Rb"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Define a function to fetch Landsat image chips."
],
"metadata": {
"id": "oRWUW-ggUcaV"
}
},
{
"cell_type": "code",
"source": [
"def prepare_image(image, region_geom, buffer, dim, width, filename):\n",
" '''Makes a visualization image clipped to a region with a plot overlay.'''\n",
" region_geom = ee.Geometry(region_geom)\n",
" region_extract = region_geom.buffer(buffer).bounds()\n",
" region_fc = ee.FeatureCollection(ee.Feature(region_geom))\n",
" region_image = ee.Image().paint(region_fc, width=width).visualize(palette=['FFFFFF'])\n",
"\n",
" vis = {\n",
" 'bands': ['swir1', 'nir', 'green'],\n",
" 'min': 0,\n",
" 'max': 0.4\n",
" }\n",
" vis_image = (image.visualize(**vis)\n",
" .reproject('EPSG:4326', None, 30)\n",
" .resample('bicubic')\n",
" .blend(region_image)\n",
" .clipToBoundsAndScale(geometry=region_extract, width=dim, height=dim))\n",
" return vis_image\n",
"\n",
"def get_png(image):\n",
" '''Makes a computePixels request from a given image.'''\n",
" return ee.data.computePixels({\n",
" 'expression': image,\n",
" 'fileFormat': 'PNG'\n",
" })\n",
"\n",
"def save_png(request, filename):\n",
" '''Saves the returned computePixels request to disk.'''\n",
" image = Image.open(io.BytesIO(request))\n",
" image.save(filename)\n",
" return None\n",
"\n",
"def process_image(image, region_geom, buffer, dim, width, filename):\n",
" '''Wraps functions to get an save an image chip for a region.'''\n",
" vis_image = prepare_image(image, region_geom, buffer, dim, width, filename)\n",
" png = get_png(vis_image)\n",
" save_png(png, filename)\n",
" return None"
],
"metadata": {
"id": "GSPr-1HFRiep"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Get an image for the region of interest – last year's annual median."
],
"metadata": {
"id": "3_XJ2t40Uh_x"
}
},
{
"cell_type": "code",
"source": [
"current_year = datetime.datetime.now().year\n",
"\n",
"last_year_landsat_mean = (annual_col.filterBounds(ee_region)\n",
" .filterDate(str(current_year - 1), str(current_year))).first()\n",
"\n",
"last_year_landsat_mean"
],
"metadata": {
"id": "VcP2NO2QS1Ew"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Request the image as a PNG image chip to include in the report."
],
"metadata": {
"id": "iTYsKkFNUvkg"
}
},
{
"cell_type": "code",
"source": [
"process_image(last_year_landsat_mean, ee_region, 5000, 540, 2, single_year)\n",
"\n",
"with Image.open(single_year) as image:\n",
" width, height = image.size\n",
" top = height * 0.15\n",
" bottom = height * 0.85\n",
" cropped_image = image.crop((0, top, width, bottom))\n",
" display(cropped_image)\n",
" cropped_image.save(single_year)"
],
"metadata": {
"id": "sdqBj2rXRtRw"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Create a PDF canvas and add text and the location figures to it."
],
"metadata": {
"id": "Yv5XtzmaVCgB"
}
},
{
"cell_type": "code",
"source": [
"width, height = letter\n",
"page_margin = 0.5 * inch\n",
"\n",
"c = canvas.Canvas(pdf_1, pagesize=letter)\n",
"\n",
"c.setFont('Helvetica-Bold', 16)\n",
"c.drawString(page_margin, 740, 'Important Ecological Project')\n",
"\n",
"c.setFont('Helvetica-Bold', 14)\n",
"c.drawString(page_margin, 720, 'Region 1')\n",
"\n",
"c.setFont('Helvetica', 12)\n",
"c.drawString(page_margin, 690, 'A short description about this marvelous plot.')\n",
"\n",
"c.setFont('Helvetica-Bold', 14)\n",
"c.drawString(page_margin, 640, 'Location')\n",
"\n",
"c.drawImage(small_scale_map, page_margin, 450, 320, 173)\n",
"c.drawImage(large_scale_map, page_margin + 340, 450, 200, 173)\n",
"c.drawImage(single_year, page_margin, page_margin)\n",
"\n",
"c.save()"
],
"metadata": {
"id": "PE2oI5UsVaaq"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Get intra-annual time series"
],
"metadata": {
"id": "Y-tdozSVHgfi"
}
},
{
"cell_type": "markdown",
"source": [
"Get a collections of 32-day Landsat composites for the previous year and save image chips for the region of interest."
],
"metadata": {
"id": "CxlwicMmXVXc"
}
},
{
"cell_type": "code",
"source": [
"#!rm {folder_monthly_images}/*.png\n",
"\n",
"img_col = (monthly_col.filterBounds(ee_region)\n",
" .filterDate(str(current_year-1), str(current_year)))\n",
"\n",
"img_list = img_col.toList(img_col.size())\n",
"\n",
"for i in range(img_col.size().getInfo()):\n",
" img = ee.Image(img_list.get(i))\n",
" date = img.date().format('YYYY-MM-dd').getInfo()\n",
" print(f'Processing: {date}')\n",
" process_image(img, ee_region, 1500, 256, 2, f'{folder_monthly_images}/img_{date}.png')"
],
"metadata": {
"id": "Okn-dSxSS_CY"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Display a representative image chip."
],
"metadata": {
"id": "G463khRnXiu6"
}
},
{
"cell_type": "code",
"source": [
"monthly_image_files = sorted(glob.glob(f'{folder_monthly_images}/*.png'))\n",
"\n",
"with Image.open(monthly_image_files[6]) as image:\n",
" display(image)"
],
"metadata": {
"id": "IEqWS_AwQznD"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Start the second page of the report. It starts with the 32-day image chips."
],
"metadata": {
"id": "fYU2uN2EXrKd"
}
},
{
"cell_type": "code",
"source": [
"width, height = letter\n",
"page_margin = 0.5 * inch\n",
"\n",
"image_margin_x = 0.11 * inch\n",
"image_margin_y = 0.25 * inch\n",
"\n",
"max_image_width = 1.77 * inch\n",
"max_image_height = 1.77 * inch\n",
"x_offset = page_margin\n",
"y_offset = height - page_margin - 0.75 * inch # Reduced initial spacing\n",
"\n",
"# Create a new PDF canvas\n",
"c = canvas.Canvas(pdf_2, pagesize=letter)\n",
"\n",
"c.setFont('Helvetica-Bold', 14)\n",
"c.drawString(page_margin, 740, 'Monthly spectral history for previous year and 10-year comparison')\n",
"c.setFont('Helvetica', 10)\n",
"\n",
"# Function to add an image to the canvas and wrap if necessary\n",
"def add_image(image_path):\n",
" global x_offset, y_offset\n",
"\n",
" # Open the image using Pillow\n",
" img = Image.open(image_path)\n",
"\n",
" # Resize image while preserving aspect ratio\n",
" img.thumbnail((max_image_width, max_image_height))\n",
" image_width, image_height = img.size\n",
"\n",
" # Check if the image and label fit on the current page\n",
" if x_offset + image_width + image_margin_x + page_margin > width:\n",
" x_offset = page_margin\n",
" y_offset -= max_image_height + image_margin_y + 0.15 * inch # Reduced spacing\n",
"\n",
" if y_offset - image_height - 0.15 * inch - page_margin < 0:\n",
" c.showPage()\n",
" x_offset = page_margin\n",
" y_offset = height - page_margin - 0.15 * inch\n",
"\n",
" # Add label above the image\n",
" c.drawString(\n",
" x_offset, y_offset, os.path.basename(image_path).replace('img_', '').replace('.png', ''))\n",
"\n",
" # Draw the image on the canvas\n",
" c.drawImage(image_path, x_offset, y_offset - image_height - 0.15*inch, image_width, image_height)\n",
"\n",
" # Update x_offset for the next image\n",
" x_offset += image_width + image_margin_x\n",
"\n",
"\n",
"for image_path in monthly_image_files:\n",
" add_image(image_path)\n"
],
"metadata": {
"id": "divYDkdmACLK"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Get a DataFrame that includes information to generate an inter-annual time series chart for the previous 11 years."
],
"metadata": {
"id": "XobgKwA1X1D5"
}
},
{
"cell_type": "code",
"source": [
"def get_region_mean(region):\n",
" '''Returns a function to compute the mean of a region.'''\n",
" def region_mean(img):\n",
" '''Computes the mean of a region.'''\n",
" region_stats = img.reduceRegion(\n",
" geometry=region, reducer=ee.Reducer.mean(), scale=30)\n",
" return ee.Feature(None, region_stats).set(\n",
" {'month': img.date().get('month'), 'year': img.date().get('year')})\n",
" return region_mean\n",
"\n",
"img_col = (monthly_col.filterBounds(ee_region)\n",
" .filterDate(str(current_year-11), str(current_year)))\n",
"\n",
"monthly_images = img_col.map(get_region_mean(ee_region))\n",
"\n",
"df = ee.data.computeFeatures(\n",
" {'expression': monthly_images, 'fileFormat': 'PANDAS_DATAFRAME'})\n",
"\n",
"df['nbr'] = (df['nir'] - df['swir1']) / (df['nir'] + df['swir1'])\n",
"df"
],
"metadata": {
"id": "19Mep2VaGcia"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Make a chart from the 11-year inter-annual time series DataFrame."
],
"metadata": {
"id": "1bxXRGB6YV4C"
}
},
{
"cell_type": "code",
"source": [
"plot_width = width - 2 * page_margin\n",
"plot_height = height - 2 * page_margin\n",
"\n",
"plot_height = 2.8\n",
"fig, ax = plt.subplots(figsize=(plot_width / inch, plot_height))\n",
"\n",
"viridis = cm.get_cmap('viridis', 10)\n",
"colors = viridis(np.linspace(0, 1, 10))\n",
"hex_colors = [matplotlib.colors.rgb2hex(color) for color in colors]\n",
"\n",
"for i, year in enumerate(df['year'].unique()):\n",
" df_subset = df[df['year'] == year]\n",
" if i == len(df['year'].unique()) -1:\n",
" ax.plot(df_subset['month'], df_subset['nbr'], label=year, color='orange', linewidth=3)\n",
" else:\n",
" ax.plot(df_subset['month'], df_subset['nbr'], label=year, color=hex_colors[i])\n",
"\n",
"ax.set_ylim(-0.3, 0.8)\n",
"ax.set_xlabel('Month')\n",
"ax.set_ylabel('NBR')\n",
"ax.set_title('')\n",
"ax.set_xticks(df['month'].unique())\n",
"ax.legend()\n",
"plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))\n",
"\n",
"plt.savefig(monthly_nbr, bbox_inches='tight')\n",
"plt.show()"
],
"metadata": {
"id": "RyS4rpXDL_EN"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Add the chart to the 2nd page of the PDF report."
],
"metadata": {
"id": "NPPiioe_YgYH"
}
},
{
"cell_type": "code",
"source": [
"c.drawImage(monthly_nbr, page_margin, page_margin, plot_width, 200)\n",
"c.save()"
],
"metadata": {
"id": "WYtFChOugfrV"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Get annual time series"
],
"metadata": {
"id": "VkuTSB7HnZRR"
}
},
{
"cell_type": "markdown",
"source": [
"Get a collection of annual Landsat composites for the previous year and get the DataFrame of the regional mean for each image."
],
"metadata": {
"id": "Sq_Gjo9NYlN7"
}
},
{
"cell_type": "code",
"source": [
"img_col = (annual_col.filterBounds(ee_region)\n",
" .filterDate('1984-01-01', f'{str(current_year)}-01-01'))\n",
"\n",
"annual_images = img_col.map(get_region_mean(ee_region))\n",
"\n",
"df = ee.data.computeFeatures(\n",
" {'expression': annual_images, 'fileFormat': 'PANDAS_DATAFRAME'})\n",
"\n",
"df['nbr'] = (df['nir'] - df['swir1']) / (df['nir'] + df['swir1'])\n",
"df"
],
"metadata": {
"id": "quafSeONb_xf"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Create a time series chart from the annual time series DataFrame."
],
"metadata": {
"id": "e_G0Mb1Vx4WO"
}
},
{
"cell_type": "code",
"source": [
"df['r'] = df['swir1'] * (255 / 0.4)\n",
"df['g'] = df['nir'] * (255 / 0.4)\n",
"df['b'] = df['green'] * (255 / 0.4)\n",
"rgb_values = df[['r', 'g', 'b']].values.astype(int) / 255\n",
"\n",
"plot_height = 3\n",
"fig, ax = plt.subplots(figsize=(plot_width / inch, plot_height))\n",
"\n",
"ax.plot(df['year'], df['nbr'], color='black', zorder=1)\n",
"ax.scatter(df['year'], df['nbr'], c=rgb_values, s=100, zorder=2)\n",
"ax.set_ylim(-0.3, 0.8)\n",
"ax.set_xticks(df['year'])\n",
"ax.set_xticklabels(df['year'], rotation=90)\n",
"\n",
"ax.set_xlabel('Year')\n",
"ax.set_ylabel('NBR')\n",
"\n",
"plt.savefig(annual_nbr, bbox_inches='tight')\n",
"plt.show()"
],
"metadata": {
"id": "YsNRJnjsH6ML"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Get a time series of image chips from the annual composite collection."
],
"metadata": {
"id": "vmng5jRvx_EY"
}
},
{
"cell_type": "code",
"source": [
"img_list = img_col.toList(img_col.size())\n",
"\n",
"for i in range(img_col.size().getInfo()):\n",
" img = ee.Image(img_list.get(i))\n",
" date = img.date().format('YYYY').getInfo()\n",
" print(f'Processing: {date}')\n",
" process_image(img, ee_region, 1500, 256, 2, f'{folder_annual_images}/img_{date}.png')"
],
"metadata": {
"id": "Fe7xMTNWuc-Z"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Add the annual time series chart and the annual image chips to the 3rd PDF file."
],
"metadata": {
"id": "aDtjqpGRZ2-A"
}
},
{
"cell_type": "code",
"source": [
"c = canvas.Canvas(pdf_3, pagesize=letter)\n",
"\n",
"c.setFont('Helvetica-Bold', 14)\n",
"c.drawString(page_margin, 740, 'Annual spectral history')\n",
"c.setFont('Helvetica', 10)\n",
"\n",
"c.drawImage(annual_nbr, page_margin, 475, plot_width, 245)\n",
"\n",
"y_offset = height - page_margin - 2.5 * inch\n",
"\n",
"annual_image_files = sorted(glob.glob(f'{folder_annual_images}/*.png'))\n",
"\n",
"for image_path in annual_image_files:\n",
" add_image(image_path)\n",
"\n",
"c.save()"
],
"metadata": {
"id": "a7jv90IDOa0W"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Put it all together"
],
"metadata": {
"id": "Qjmp71vNVywS"
}
},
{
"cell_type": "markdown",
"source": [
"Combine the 3 individual PDF files into a single-file report."
],
"metadata": {
"id": "jk8q8ap2ZoRO"
}
},
{
"cell_type": "code",
"source": [
"def merge_pdfs(paths, output):\n",
" merger = PyPDF2.PdfMerger()\n",
" for path in paths:\n",
" with open(path, 'rb') as f:\n",
" merger.append(f)\n",
" with open(output, 'wb') as f:\n",
" merger.write(f)\n",
"\n",
"pdfs = sorted(glob.glob(f'{folder_pdfs}/*.pdf'))\n",
"merge_pdfs(pdfs, pdf_full)"
],
"metadata": {
"id": "oDj-CRslcMUG"
},
"execution_count": null,
"outputs": []
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment