def search_proximity(cls, lat=50.848, lon=4.351, radius=8): """List stations within given radius from a location. Args: lat: latitude of the center of search, in decimal degrees lon: longitude of the center of search, in decimal degrees radius: maximum distance from center, in kilometers Default values are the approximate center and radius of Brussels. Returns: Dataframe of matching stations, listing sensor types, locations and distances in kilometers from the search center, indexed by station ID The search is based on the station list retrieved as part of the metadata. The irceline.be API offers an alternative way to get an (unordered) list of stations near a location: `https://geo.irceline.be/sos/api/v1/stations? near={{"center":{{"type":"Point","coordinates":[{lon}, {lat}]}},"radius":{radius}}}` """ near_stations = cls.stations.copy() near_stations["distance"] = (near_stations.apply( lambda x: haversine(lat, lon, x["lat"], x["lon"]), axis=1)) near_stations = (near_stations[ near_stations["distance"] <= radius].sort_values("distance")) return near_stations
def search_proximity(lat=50.848, lon=4.351, radius=8): """Find sensors within given radius from a location. Args: lat: latitude of the center of search, in decimal degrees lon: longitude of the center of search, in decimal degrees radius: maximum distance from center, in kilometers Default values are the approximate center and radius of Brussels. Returns: Dataframe of matching sensors, listing sensor types, locations and distances in kilometers from the search center, indexed by sensor ID Raises: requests.HTTPError if request failed """ url = (API_ENDPOINTS["proximity search pattern"].format(lat=lat, lon=lon, radius=radius)) call_rate_limiter() response = requests.get(url) response.raise_for_status() sensors = json_normalize(response.json()) if len(sensors) == 0: sensors = pd.DataFrame( columns=["sensor_type", "latitude", "longitude", "distance"]) sensors.index.name = "sensor_id" return sensors sensors = (sensors[[ "sensor.id", "sensor.sensor_type.name", "location.latitude", "location.longitude" ]].rename( columns={ "sensor.id": "sensor_id", "sensor.sensor_type.name": "sensor_type", "location.latitude": "latitude", "location.longitude": "longitude" })) for col in "latitude", "longitude": sensors[col] = pd.to_numeric(sensors[col], downcast="float") sensors.set_index("sensor_id", inplace=True) # Drop duplicates - sensors appear once for each measurement in past 5 mins sensors = sensors[~sensors.index.duplicated()] # Calculate distances from search center and sort by those distances sensors["distance"] = sensors.apply(lambda x: utils.haversine( lat, lon, float(x["latitude"]), float(x["longitude"])), axis=1) sensors.sort_values("distance", inplace=True) return sensors
def query_time_series(cls, phenomenon, lat_nearest=None, lon_nearest=None): """Convenience method to filter time series for those that measure a given phenomenon, and sort by distance to a point if given. Args: phenomenon: character sequence or regular expression to filter phenomena by; operates on the "phenomenon" column of the time_series dataframe lat_nearest: latitude of the reference point lon_nearest: longitude of the reference point Returns: Subset of time_series property. If lat_nearest and lon_nearest are given, the result has an additional column indicating distance in km from that point, and is sorted by that distance. Raises: ValueError if only one of lat_nearest, lon_nearest is given """ if bool(lat_nearest is None) != bool(lon_nearest is None): raise ValueError("Provide both or none of lat_nearest, " "lon_nearest") phenomena_lower = cls.time_series["phenomenon"].str.lower() matches = phenomena_lower.str.contains(phenomenon.lower()) results = cls.time_series[matches].copy() if lat_nearest is None: return results if len(results) == 0: results["distance"] = None return results results["distance"] = results.apply(lambda row: haversine( lat_nearest, lon_nearest, row["station_lat"], row["station_lon"]), axis=1) results = results.sort_values("distance") return results