Abrir en Colab

Análisis a nivel de condado#

Analiza los datos de GLOBE y las condiciones ambientales de tu condado en Florida.

Escribe entre comillas el nombre de tu condado. Por ejemplo, county_name = "Broward County" o county_name = "Miami-Dade County".

Los nombres deben escribirse en inglés para que coincidan con el archivo de límites de la Oficina del Censo. En los ejemplos de este cuaderno utilizaremos Broward County.

county_name = "Nombre de tu condado"

# Por ejemplo:
# county_name = "Broward County"
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.colors import to_rgb
import seaborn as sns

pd.set_option("display.max_columns", None)

Primero, carga:

  1. Los datos de GLOBE Mosquito Habitat Mapper.

  2. Un archivo con los límites de los condados de Florida, basado en datos de la Oficina del Censo de Estados Unidos.

Utilizaremos los límites para filtrar las observaciones correspondientes a cada condado.

data = gpd.read_file(
    "https://github.com/geo-di-lab/emerge-lessons/raw/"
    "refs/heads/main/docs/data/globe_mosquito.zip"
)

fl = gpd.read_file(
    "https://github.com/geo-di-lab/emerge-lessons/raw/"
    "refs/heads/main/docs/data/florida_counties.geojson"
)

Después, selecciona los límites geográficos de tu condado.

county = fl.loc[
    fl["NAMELSAD"] == county_name
].copy()

if county.empty:
    available_examples = ", ".join(
        fl["NAMELSAD"].dropna().head(5)
    )
    raise ValueError(
        f"No se encontró '{county_name}'. "
        "Escribe el nombre oficial del condado en inglés, "
        f"por ejemplo: {available_examples}."
    )

# Asegurar que ambas capas utilicen el mismo sistema de coordenadas
county = county.to_crs(data.crs)

Ahora utilizaremos esos límites para obtener todas las observaciones de GLOBE Mosquito Habitat Mapper realizadas dentro del condado entre 2018 y 2024.

data_county = data.sjoin(
    county,
    how="inner",
    predicate="within"
)

num_total = len(data_county)

print(
    f"Entre 2018 y 2024, participantes en ciencia participativa "
    f"registraron {num_total} observaciones de GLOBE Mosquito "
    f"Habitat Mapper dentro de {county_name}."
)

if num_total > 0:
    eliminated_mask = (
        data_county["BreedingGroundEliminated"]
        .astype(str)
        .str.lower()
        .eq("true")
    )
    num_eliminated = eliminated_mask.sum()
    percent_eliminated = round(
        num_eliminated * 100 / num_total
    )

    print(
        f"En {num_eliminated} de esas observaciones "
        f"({percent_eliminated} %), los participantes eliminaron "
        "el criadero de mosquitos. Esta acción puede reducir el "
        "riesgo de que el lugar continúe produciendo mosquitos."
    )

Nota: Si no se encontraron observaciones dentro de tu condado, vuelve a la primera celda de código y escribe el nombre de otro condado.

data_county.head()

¿Cuántas observaciones de mosquitos se enviaron a GLOBE Observer cada año? Crearemos un gráfico de barras para averiguarlo.

# Asegurar que la fecha tenga el formato correcto
data_county["MeasuredAt"] = pd.to_datetime(
    data_county["MeasuredAt"],
    errors="coerce"
)

# Agregar una columna para el año
data_county["MeasuredYear"] = (
    data_county["MeasuredAt"].dt.year
)

# Contar las observaciones de mosquitos de cada año
years = (
    data_county[
        ["SiteId", "MeasuredYear"]
    ]
    .dropna(subset=["MeasuredYear"])
    .groupby(
        "MeasuredYear",
        as_index=False
    )
    .count()
)

plt.bar(
    years["MeasuredYear"],
    years["SiteId"]
)
plt.title(
    "Observaciones de mosquitos por año",
    loc="left"
)
plt.title(
    county_name,
    loc="right"
)
plt.xlabel("Año")
plt.ylabel("Cantidad de observaciones")
plt.tight_layout()
plt.show()

Crearemos gráficos circulares de los tipos de fuentes de agua, tanto generales como específicas, donde se registraron mosquitos en el condado.

types = (
    data_county[
        ["SiteId", "WaterSourceType"]
    ]
    .dropna(subset=["WaterSourceType"])
    .groupby(
        "WaterSourceType",
        as_index=False
    )
    .count()
)

plt.figure(figsize=(5, 5))

patches, texts = plt.pie(
    x=types["SiteId"],
    colors=sns.color_palette(
        "Set2",
        n_colors=len(types)
    )
)

plt.title(
    f"Observaciones de mosquitos de GLOBE en "
    f"{county_name}: tipos generales de fuentes de agua"
)

plt.legend(
    patches,
    types["WaterSourceType"],
    loc="center left",
    bbox_to_anchor=(1, 0.5),
    frameon=False
)

plt.tight_layout()
plt.show()
types = (
    data_county[
        ["SiteId", "WaterSource"]
    ]
    .dropna(subset=["WaterSource"])
    .groupby(
        "WaterSource",
        as_index=False
    )
    .count()
)

plt.figure(figsize=(5, 5))

patches, texts = plt.pie(
    x=types["SiteId"],
    colors=sns.color_palette(
        "Set2",
        n_colors=len(types)
    )
)

plt.title(
    f"Observaciones de mosquitos de GLOBE en "
    f"{county_name}: fuentes de agua específicas"
)

plt.legend(
    patches,
    types["WaterSource"],
    loc="center left",
    bbox_to_anchor=(1, 0.5),
    frameon=False
)

plt.tight_layout()
plt.show()

Temperatura y uso del suelo#

El siguiente código prepara las herramientas necesarias para realizar el análisis mediante Google Earth Engine.

from datetime import datetime
import numpy as np
import folium
import ee
import geemap

ee.Authenticate()

# Escribe aquí el ID de tu proyecto, entre comillas
ee.Initialize(project="emerge-lessons")


def add_ee_layer(self, ee_image_object, vis_params, name):
    """
    Agrega a un mapa de Folium una capa de teselas
    procedente de una imagen de Earth Engine.
    """
    map_id_dict = ee.Image(
        ee_image_object
    ).getMapId(vis_params)

    folium.raster_layers.TileLayer(
        tiles=map_id_dict[
            "tile_fetcher"
        ].url_format,
        attr=(
            'Map Data © '
            '<a href="https://earthengine.google.com/">'
            "Google Earth Engine</a>"
        ),
        name=name,
        overlay=True,
        control=True
    ).add_to(self)


folium.Map.add_ee_layer = add_ee_layer
# Convertir los límites del condado al formato de Earth Engine
region = geemap.geopandas_to_ee(county)

# Calcular el centro del condado en una proyección adecuada
county_projected = county.to_crs(epsg=3857)
county_center = (
    county_projected.geometry
    .centroid
    .to_crs(epsg=4326)
    .iloc[0]
)

# Centrar el mapa en el condado
map = folium.Map(
    [county_center.y, county_center.x],
    tiles="Cartodb dark_matter",
    zoom_start=10
)

Representa la temperatura de la superficie terrestre en todo el condado.

Primero, define un intervalo de fechas. Calcularemos la temperatura promedio dentro de ese período. En este ejemplo utilizaremos junio de 2025. La fecha inicial está incluida, mientras que la fecha final está excluida. Por lo tanto, se obtendrán los datos desde el 1 de junio hasta el 30 de junio.

start_date = "2025-06-01"
end_date = "2025-07-01"

Los datos proceden de Google Earth Engine: MOD11A1.061 Terra Land Surface Temperature and Emissivity Daily Global 1km.

Los valores de temperatura de la superficie terrestre incluyen un factor de escala. Para obtener la temperatura original en kelvin, debemos multiplicar cada valor por 0.02. Después, podemos convertir el resultado a grados Celsius o Fahrenheit.

def to_fahrenheit(lst):
    """Convierte un valor LST escalado a grados Fahrenheit."""
    celsius = lst * 0.02 - 273.15
    fahrenheit = celsius * 1.8 + 32
    return fahrenheit


def to_lst(fahrenheit):
    """Convierte grados Fahrenheit al valor LST escalado."""
    celsius = (
        fahrenheit - 32
    ) / 1.8
    lst = (
        celsius + 273.15
    ) / 0.02
    return lst
lst = (
    ee.ImageCollection(
        "MODIS/061/MOD11A1"
    )
    .filterDate(
        start_date,
        end_date
    )
    .select("LST_Day_1km")
    # También puedes utilizar median() para obtener la mediana
    .mean()
    .clip(region)
)

colors = [
    "040274", "040281", "0502a3", "0502b8", "0502ce", "0502e6",
    "0602ff", "235cb1", "307ef3", "269db1", "30c8e2", "32d3ef",
    "3be285", "3ff38f", "86e26f", "3ae237", "b5e22e", "d6e21f",
    "fff705", "ffd611", "ffb613", "ff8b13", "ff6e08", "ff500d",
    "ff0000", "de0101", "c21301", "a71001", "911003"
]

lst_vis = {
    "min": to_lst(50),
    "max": to_lst(100),
    "palette": colors
}

map.add_ee_layer(
    lst,
    lst_vis,
    "Temperatura de la superficie terrestre"
)

display(map)

La siguiente barra de colores muestra lo que representan los colores del mapa: la temperatura de la superficie terrestre en grados Fahrenheit.

plt.figure(
    figsize=(len(colors), 1)
)

plt.imshow(
    [
        [
            to_rgb(f"#{color}")
            for color in colors
        ]
    ]
)

plt.text(
    -0.6,
    0.1,
    "50 °F",
    va="center",
    ha="right",
    fontsize=24
)

plt.text(
    len(colors) - 0.4,
    0.1,
    "100 °F",
    va="center",
    ha="left",
    fontsize=24
)

plt.axis("off")
plt.show()

A continuación, calcula la temperatura promedio del condado.

mean_lst = (
    lst.reduceRegion(
        reducer=ee.Reducer.mean(),
        geometry=region,
        scale=1000,
        maxPixels=1e12
    )
    .get("LST_Day_1km")
    .getInfo()
)

if mean_lst is None:
    raise ValueError(
        "No se encontraron datos de temperatura "
        "para el condado y las fechas seleccionadas."
    )

print(
    f"La temperatura promedio de {county_name} "
    f"entre {start_date} y {end_date} fue de "
    f"{round(to_fahrenheit(mean_lst), 2)} °F."
)

Examina el uso del suelo y la cobertura terrestre del condado.

# Uso del suelo

# Crear un mapa nuevo
map = folium.Map(
    [county_center.y, county_center.x],
    tiles="Cartodb dark_matter",
    zoom_start=10
)

# Definir la paleta de colores del uso del suelo
palette = [
    "fbff97",
    "e6558b",
    "004e2b",
    "9dbac5",
    "a6976a",
    "1b1716"
]

visual = {
    "min": 1,
    "max": 6,
    "palette": palette
}

landcover_1985 = (
    ee.ImageCollection(
        "USFS/GTAC/LCMS/v2024-10"
    )
    .filterDate("1985", "1986")
    .filter('study_area == "CONUS"')
    .first()
    .clip(region)
)

map.add_ee_layer(
    landcover_1985.select("Land_Use"),
    visual,
    "Uso del suelo en 1985"
)

landcover_2024 = (
    ee.ImageCollection(
        "USFS/GTAC/LCMS/v2024-10"
    )
    .filterDate("2024", "2025")
    .filter('study_area == "CONUS"')
    .first()
    .clip(region)
)

map.add_ee_layer(
    landcover_2024.select("Land_Use"),
    visual,
    "Uso del suelo en 2024"
)

folium.LayerControl(
    collapsed=False
).add_to(map)

display(map)
\[\color{#fbff97}Agricultura\]
\[\color{#e6558b}Zona\space desarrollada\]
\[\color{#004e2b}Bosque\]
\[\color{#9dbac5}Otra\space categoría\]
\[\color{#a6976a}Pastizal\space o\space zona\space de\space pastoreo\]

Crearemos una función para calcular el porcentaje del condado correspondiente a cada categoría de uso del suelo o cobertura terrestre.

def land_stats(image, name, labels):
    """
    Calcula e imprime el porcentaje de cada clase
    de uso del suelo o cobertura terrestre.
    """
    result = (
        image.select(name)
        .reduceRegion(
            reducer=ee.Reducer.frequencyHistogram(),
            geometry=region,
            scale=30,
            maxPixels=1e12
        )
        .getInfo()
    )

    histogram = result.get(
        name,
        {}
    )

    land_counts = {}

    for key, label in labels.items():
        if key in histogram:
            land_counts[label] = histogram[key]

    total_pixels = sum(
        land_counts.values()
    )

    if total_pixels == 0:
        print(
            "No se encontraron píxeles válidos "
            f"para {name}."
        )
        return

    for label, count in land_counts.items():
        percent = round(
            100 * count / total_pixels,
            1
        )
        if percent > 0:
            print(
                f"{label}: {percent} %"
            )
land_use_labels = {
    "1": "Agricultura",
    "2": "Zona desarrollada",
    "3": "Bosque",
    "4": "Otra categoría",
    "5": "Pastizal o zona de pastoreo",
    "6": "Máscara de zona no procesada"
}

land_stats(
    landcover_1985,
    "Land_Use",
    land_use_labels
)

Ahora repetiremos el cálculo para 2024.

land_stats(
    landcover_2024,
    "Land_Use",
    land_use_labels
)
# Cobertura terrestre

# Crear un mapa nuevo
map = folium.Map(
    [county_center.y, county_center.x],
    tiles="Cartodb dark_matter",
    zoom_start=10
)

palette = [
    "004e2b",
    "009344",
    "61bb46",
    "acbb67",
    "8b8560",
    "cafd4b",
    "f89a1c",
    "8fa55f",
    "bebb8e",
    "e5e98a",
    "ddb925",
    "893f54",
    "e4f5fd",
    "00b6f0",
    "1b1716"
]

visual = {
    "min": 1,
    "max": 15,
    "palette": palette
}

# Agregar la cobertura terrestre de 1985 y 2024
map.add_ee_layer(
    landcover_1985.select("Land_Cover"),
    visual,
    "Cobertura terrestre en 1985"
)

map.add_ee_layer(
    landcover_2024.select("Land_Cover"),
    visual,
    "Cobertura terrestre en 2024"
)

folium.LayerControl(
    collapsed=False
).add_to(map)

display(map)
\[\color{#004e2b}Árboles\]
\[\color{#61bb46}Mezcla\space de\space arbustos\space y\space árboles\]
\[\color{#acbb67}Mezcla\space de\space pasto,\space herbáceas\space y\space árboles\]
\[\color{#8b8560}Mezcla\space de\space terreno\space descubierto\space y\space árboles\]
\[\color{#f89a1c}Arbustos\]
\[\color{#8fa55f}Mezcla\space de\space pasto,\space herbáceas\space y\space arbustos\]
\[\color{#bebb8e}Mezcla\space de\space terreno\space descubierto\space y\space arbustos\]
\[\color{#e5e98a}Pasto,\space plantas\space herbáceas\space o\space hierbas\]
\[\color{#ddb925}Mezcla\space de\space terreno\space descubierto,\space pasto\space y\space herbáceas\]
\[\color{#893f54}Terreno\space descubierto\space o\space superficie\space impermeable\]
\[\color{#e4f5fd}Nieve\space o\space hielo\]

Calcula el porcentaje de cada categoría de cobertura terrestre.

land_cover_labels = {
    "1": "Árboles",
    "2": "Mezcla de arbustos altos y árboles (solo Alaska)",
    "3": "Mezcla de arbustos y árboles",
    "4": "Mezcla de pasto, herbáceas y árboles",
    "5": "Mezcla de terreno descubierto y árboles",
    "6": "Arbustos altos (solo Alaska)",
    "7": "Arbustos",
    "8": "Mezcla de pasto, herbáceas y arbustos",
    "9": "Mezcla de terreno descubierto y arbustos",
    "10": "Pasto, plantas herbáceas o hierbas",
    "11": "Mezcla de terreno descubierto, pasto y herbáceas",
    "12": "Terreno descubierto o superficie impermeable",
    "13": "Nieve o hielo",
    "14": "Agua",
    "15": "Máscara de zona no procesada"
}
land_stats(
    landcover_1985,
    "Land_Cover",
    land_cover_labels
)
land_stats(
    landcover_2024,
    "Land_Cover",
    land_cover_labels
)

Datos#