Source code for disdrodb.metadata.geolocation

#!/usr/bin/env python3

# -----------------------------------------------------------------------------.
# Copyright (c) 2021-2023 DISDRODB developers
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# -----------------------------------------------------------------------------.
"""Metadata tools to verify/complete geolocation information."""
import time

import numpy as np
import requests


[docs] def infer_altitude(latitude, longitude, dem="aster30m"): """Infer station altitude using a Digital Elevation Model (DEM). This function uses the OpenTopoData API to infer the altitude of a given location specified by latitude and longitude. By default, it uses the ASTER DEM at 30m resolution. Parameters ---------- latitude : float The latitude of the location for which to infer the altitude. longitude : float The longitude of the location for which to infer the altitude. dem : str, optional The DEM to use for altitude inference. Options are "aster30m" (default), "srtm30", and "mapzen". Returns ------- elevation : float The inferred altitude of the specified location. Raises ------ ValueError If the altitude retrieval fails. Notes ----- - The OpenTopoData API has a limit of 1000 calls per day. - Each request can include up to 100 locations. - The API allows a maximum of 1 call per second. References ---------- https://www.opentopodata.org/api/ """ import requests url = f"https://api.opentopodata.org/v1/{dem}?locations={latitude},{longitude}" r = requests.get(url) data = r.json() if data["status"] == "OK": elevation = data["results"][0]["elevation"] else: raise ValueError("Altitude retrieval failed.") return elevation
[docs] def infer_altitudes(lats, lons, dem="aster30m"): """ Infer altitude of a given location using OpenTopoData API. Parameters ---------- lats : list or array-like List or array of latitude coordinates. lons : list or array-like List or array of longitude coordinates. dem : str, optional Digital Elevation Model (DEM) to use for altitude inference. The default DEM is "aster30m". Returns ------- elevations : numpy.ndarray Array of inferred altitudes corresponding to the input coordinates. Raises ------ ValueError If the latitude and longitude arrays do not have the same length. If altitude retrieval fails for any block of coordinates. Notes ----- - The OpenTopoData API has a limit of 1000 calls per day. - Each request can include up to 100 locations. - The API allows a maximum of 1 call per second. - The API requests are made in blocks of up to 100 coordinates, with a 2-second delay between requests. """ # Check that lats and lons have the same length if len(lats) != len(lons): raise ValueError("Latitude and longitude arrays must have the same length.") # Maximum number of locations per API request max_locations = 100 elevations = [] # Total number of coordinates total_coords = len(lats) # Loop over the coordinates in blocks of max_locations for i in range(0, total_coords, max_locations): # Wait 2 seconds before another API request time.sleep(2) # Get the block of coordinates block_lats = lats[i : i + max_locations] block_lons = lons[i : i + max_locations] # Create the list_coords string in the format "lat1,lon1|lat2,lon2|..." list_coords = "|".join([f"{lat},{lon}" for lat, lon in zip(block_lats, block_lons)]) # Define API URL url = f"https://api.opentopodata.org/v1/{dem}?locations={list_coords}&interpolation=nearest" # Retrieve info r = requests.get(url) data = r.json() # Parse info if data.get("status") == "OK": elevations.extend([result["elevation"] for result in data["results"]]) else: raise ValueError(f"Altitude retrieval failed for block starting at index {i}.") elevations = np.array(elevations).astype(float) return elevations