import pandas as pd
import folium
from datetime import datetime, timedelta
from pyinaturalist import (
Observation,
get_observations,
#pprint,
)
import ipyplot
from itertools import compressDe Adelaida a Kati Thanda/Lago Eyre
Adelaide to Kati Thanda–Lake Eyre and back!
Esta es un resumen de las observaciones realizadas durante un viaje familiar en Julio de 2025. Fuimos desde Adelaida a William Creek por tierra y desde allí sobrevolamos el Kati Thanda/Lago Eyre. Aquí muestro las observaciones de plantas y animales que he cargado a iNaturalist después del viaje.
Cargar los módulos en Python
Estos son los módulos que usé para todos los pasos de este documento.
Descargar observaciones de iNaturalist
Uso la función get_observations del paquete pyinaturalist. Tengo que incluir mi ID de usuario en iNaturalist, y las fechas de inicio y fin (12 al 19 of Julio, pero añado un día para evitar excluir algunas observaciones. Filtro para incluir solamente las observaciones con fotos.
observations = get_observations(user_id='NeoMapas',
d1="2025-07-11",
d2="2025-07-20",
photos = True,
per_page=1000)len(observations['results'])142
Crear mapa
Para crear un mapa de las observaciones, tengo que extraer la información básica de cada observación usando un bucle:
records=list()
for obs in observations['results']:
record = {'uuid': obs['uuid'],
'quality': obs['quality_grade'],
'description': obs['description'],
'location': obs['place_guess'],
'longitude': obs['location'][1],
'latitude': obs['location'][0],
'species guess': obs['species_guess'],
'Fecha': obs['observed_on'],
}
if len(obs['observation_photos'])>0:
record['url'] = obs['observation_photos'][0]['photo']['url']
record['attribution'] = obs['observation_photos'][0]['photo']['attribution']
else:
record['url'] = 'https://upload.wikimedia.org/wikipedia/commons/d/d9/Icon-round-Question_mark.svg'
record['attribution'] = "no photo"
records.append(record)Selecciono un mapa de fondo para usar con folium:
map = folium.Map(tiles="Esri NatGeoWorldMap")Aquí agrupo todas las observaciones juntas, y las añado al mapa:
fg = folium.FeatureGroup(name="iNaturalist observations", control=True, attribution="observers @ iNaturalist").add_to(map)
popup_text = """<img src='{url}'>
<caption><i>{species}</i> observed on {observed_on} / {attribution}</caption> {desc}
"""
for obs in observations['results']:
if obs['quality_grade'] == 'research':
if obs['description'] is None:
desc = ""
else:
desc = obs['description']
pincolor = 'green'
else:
desc = "Observation is not research quality grade."
pincolor = 'gray'
fg.add_child(
folium.Marker(
location=obs['location'],
popup=popup_text.format(
species=obs['species_guess'],
observed_on=obs['observed_on'],
desc=desc,
url = obs['observation_photos'][0]['photo']['url'],
attribution = obs['observation_photos'][0]['photo']['attribution']),
icon=folium.Icon(color=pincolor),
)
)
folium.LayerControl().add_to(map)<folium.map.LayerControl object at 0x133802a80>
Ajusto el zoom y muestro el mapa:
map.fit_bounds(map.get_bounds())
mapItinerario del viaje
Para resumir los datos de las observaciones del viaje, puedo usar funciones del paquete pandas:
inat_obs=pd.DataFrame(records)inat_obs["dia"]=inat_obs.Fecha.apply(datetime.date)inat_obs.groupby('dia').agg({'uuid': [pd.Series.nunique],
'species guess': ['count',pd.Series.nunique]})| uuid | species guess | ||
|---|---|---|---|
| nunique | count | nunique | |
| dia | |||
| 2025-07-12 | 11 | 10 | 10 |
| 2025-07-13 | 34 | 25 | 16 |
| 2025-07-14 | 23 | 13 | 12 |
| 2025-07-15 | 9 | 6 | 6 |
| 2025-07-16 | 21 | 5 | 5 |
| 2025-07-17 | 18 | 15 | 14 |
| 2025-07-18 | 14 | 13 | 10 |
| 2025-07-19 | 12 | 12 | 9 |
Para hacer un itinerario del viaje, es más práctico transformar las observacioens a un formato diferente usando las pfunciones de pyinaturalist y extrayendo listas de las imágenes y sus etiquetas.
my_observations = Observation.from_json_list(observations)images = [obs.photos[0].small_url for obs in my_observations]
labels = [str(obs) for obs in my_observations]Para cada fecha filtro las observaciones del día y muestro una mini galería.
de Adelaide a Port Augusta / 12 de Julio
Primer día de viaje
ss = [datetime.date(obs.observed_on).strftime('%Y-%m-%d') in ['2025-07-12'] for obs in my_observations]
ipyplot.plot_images(list(compress(images, ss)), list(compress(labels, ss)), max_images=60,)Pt Augusta to Woomera / 13 de Julio
Segundo día de viaje
ss = [datetime.date(obs.observed_on).strftime('%Y-%m-%d') in ['2025-07-13']
for obs in my_observations]
ipyplot.plot_images(list(compress(images, ss)), list(compress(labels, ss)), max_images=60,)Woomera a Coober Pedy / 14 de Julio
Tercer día de viaje
ss = [datetime.date(obs.observed_on).strftime('%Y-%m-%d') in ['2025-07-14']
for obs in my_observations]
ipyplot.plot_images(list(compress(images, ss)), list(compress(labels, ss)), max_images=60,)Coober Pedy / 15 de Julio
Cuarto día de viaje
ss = [datetime.date(obs.observed_on).strftime('%d/%m/%Y') in ['15/07/2025',] for obs in my_observations]
ipyplot.plot_images(list(compress(images, ss)), list(compress(labels, ss)), max_images=60,)Coober Pedy a William Creek / 16 de Julio
Quinto día de viaje
ss = [datetime.date(obs.observed_on).strftime('%d/%m/%Y') in ['16/07/2025',] for obs in my_observations]
ipyplot.plot_images(list(compress(images, ss)), list(compress(labels, ss)), max_images=60,)Volando sobre el lago Eyre / 17 de Julio
Sexto día de viaje
ss = [datetime.date(obs.observed_on).strftime('%d/%m/%Y') in ['17/07/2025',] for obs in my_observations]
ipyplot.plot_images(list(compress(images, ss)), list(compress(labels, ss)), max_images=60,)De regreso a Adelaide / 18 de Julio
Estas son todas las observaciones del viaje de regreso a Adelaide con paradas en
ss = [datetime.date(obs.observed_on).strftime('%d/%m/%Y') in ['18/07/2025',] for obs in my_observations]
ipyplot.plot_images(list(compress(images, ss)), list(compress(labels, ss)), max_images=60,)Adelaide / 19 de Julio
Último día de viaje
ss = [datetime.date(obs.observed_on).strftime('%d/%m/%Y') in ['19/07/2025',] for obs in my_observations]
ipyplot.plot_images(list(compress(images, ss)), list(compress(labels, ss)), max_images=60,)