Source code for geepillow.image
"""image module."""
from io import BytesIO
from typing import Any
import ee
import requests
from PIL import Image
from geepillow import colors
[docs]
def from_eeimage(
image: ee.Image,
dimensions: tuple | int,
viz_params: dict | None = None,
scale: float | None = None,
region: ee.Geometry | ee.Feature | None = None,
overlay: ee.FeatureCollection | ee.Feature | ee.Geometry | None = None,
overlay_style: dict | None = None,
style_property: str | None = None,
) -> Image:
"""Create a Pillow Image from an ee.Image.
Args:
image: the ee.Image
dimensions: dimensions of the image, in pixels. If only one number is passed, it is used as the maximum, and
the other dimension is computed by proportional scaling.
viz_params: dict with visualization parameters. Admits the following keys:
- bands: a list of the 3 bands that will represent R G B, or one band that will work with palette.
- min: the minimum value.
- max: the maximum value.
- palette: a list of colors to use as a palette. Will only work with one single band.
scale: spatial resolution of the image. If None it'll use the image scale.
region: the region to extract the image from. If None it'll use the boundaries of the image.
overlay: a vector layer to overlay on top of the image.
overlay_style: style of the vector layer to overlay.
style_property: A per-feature property expected to contain a dictionary. Values in the dictionary override any
default values for that feature.
"""
viz_params = viz_params or dict(min=0, max=1)
overlay_style = overlay_style or dict(width=2, fillColor=colors.create("white").hex(0))
if style_property is not None:
overlay_style["styleProperty"] = style_property
if scale is not None:
proj = image.select([0]).projection().atScale(scale)
image = image.reproject(proj)
if isinstance(overlay, ee.Geometry):
overlay = ee.FeatureCollection([ee.Feature(overlay)])
elif isinstance(overlay, ee.Feature):
overlay = ee.FeatureCollection([overlay])
if overlay is not None:
overlay_image = ee.Image(overlay.style(**overlay_style))
source = image.visualize(**viz_params)
viz_image = source.blend(overlay_image)
else:
viz_image = image.visualize(**viz_params)
bands = "vis-gray" if len(viz_params.get("bands", [])) < 2 else "vis-red,vis-green,vis-blue"
_min = "0" if bands == "vis-gray" else "0,0,0"
_max = "255" if bands == "vis-gray" else "255,255,255"
viz: dict[str, Any] = {
"bands": bands,
"min": _min,
"max": _max,
}
viz.update({"format": "png", "region": region, "dimensions": dimensions})
url = viz_image.getThumbURL(viz)
raw = requests.get(url)
if raw.status_code != requests.codes.ok:
error_message = raw.text
try:
# Earth Engine errors are typically returned as JSON.
error_details = raw.json()
# The actual message is nested under 'error' -> 'message'.
error_message = error_details.get("error", {}).get("message", error_message)
except requests.exceptions.JSONDecodeError:
# Not a JSON response, so we'll use the raw text as the error.
pass
raise RuntimeError(f"Error fetching image from Earth Engine: {error_message}")
return Image.open(BytesIO(raw.content))