import requests
import pandas as pd
pd.options.mode.chained_assignment = None # default='warn'
import geopandas as gpd
from tqdm import tqdm
import shapely.geometry
class telechargement_geo_spatial():
"""Méthode de télécharment des observations d'une observedProperties par intersection spatiale et filtre temporel
Attributes:
url_service (string): url du service sensorthing
geom (geometry): geometry pour réaliser l'intersection
obsP (string): observedProperty ciblée
date_start (string): date de début d'observation
date_end (string): date de fin d'observation
"""
def __init__(self, url_service, geom, obsP, date_start, date_end):
self.url_service = url_service
self.geom = geom
self.obsP = obsP
self.date_start = date_start
self.date_end = date_end
if self.geom.geom_type == 'MultiPolygon':
print("MultipolygonE, conversion en polygone")
self.geom =self.geom.geoms[0]
def get_data(self,url_requete):
"""Télécharge les données d'une requête sensorthing avec itération dans les pages
Args:
url_requete (str): url à télécharger
Returns:
dataframe: dataframe (pandas) des données téléchargées
"""
r=requests.get(url=url_requete)
if r.status_code not in [200, 201, 202]:
print(r.status_code)
if r.status_code == 414:
print("url trop grande = la géometrie en entrée est trop complexe \n \
tentative d'intersection par la bounding box")
self.geom=shapely.geometry.box(*self.geom.bounds, ccw=True)
url_requete=self.inter_geo_temporelle()
print(url_requete)
r=requests.get(url=url_requete)
data=r.json()
df_all=pd.DataFrame(data['value'])
while '@iot.nextLink' in data:
r=requests.get(url=data['@iot.nextLink'])
data=r.json()
df_all=pd.concat([df_all,pd.DataFrame(data['value'])])
return df_all
def inter_geo_temporelle(self):
"""Façonne l'url de requête selon les différents arguments de la classe
Returns:
string: url avec les filtres
"""
objet='Datastreams'
rqt=f"$filter=geo.intersects(Thing/Locations/location, geography'{self.geom}') \
and ObservedProperty/name eq'{self.obsP}'\
&$select=name,description,unitOfMeasurement,Observations,phenomenonTime\
&$expand=Thing/Locations($select=name,@iot.id,location)"
url_rqt=f"{self.url_service}{objet}?{rqt}"
print("Requête générée:\n")
#print(url_rqt)
return url_rqt
def get_all(self,df):
"""Télécharge les observations des datastreams
Args:
df (dataframe): dataframe des datastreams avec une colonne contenant les lien vers les observations
Returns:
dataframe: dataframe des données téléchargées pour tous les datastreams avec les coordonnées géographiques de chaque observations
"""
df_data=pd.DataFrame()
print("\nTéléchargement en cours : \n")
for idx,row in tqdm(df.iterrows(), total=df.shape[0]):
#print(row['Observations@iot.navigationLink'])
rqt=row['Observations@iot.navigationLink']
rqt+=f"?$select=result,phenomenonTime&$filter=phenomenonTime ge {self.date_start} and phenomenonTime le {self.date_end}"
df_inter=pd.DataFrame(self.get_data(rqt))
df_inter['name']=row['name']
df_inter['x']=row['Thing']['Locations'][0]['location']['geometry']['coordinates'][0]
df_inter['y']=row['Thing']['Locations'][0]['location']['geometry']['coordinates'][1]
df_data=pd.concat([df_data,df_inter])
df_data=df_data.reset_index(drop=True)
self.unite = row['unitOfMeasurement']['name']
self.symbol = row['unitOfMeasurement']['symbol']
return df_data
def telechargement(self):
"""Fonction qui automatise la classe
Returns:
dataframe: dataframe des données téléchargées pour tous les datastreams avec les coordonnées géographiques de chaque observations
"""
print(f"Recherche des Datastreams par l'ObservedProperty : {self.obsP} par intersection spatial sur la période : {self.date_start} {self.date_end}")
url_rqt = self.inter_geo_temporelle()
df_rqt = self.get_data(url_rqt)
if len(df_rqt) == 0:
print("\nIl n'y a pas d'objet dans la zone d'intersection, veuillez changer de zone")
return 0
else:
print(f"\nNombre d'objet à téléchargerger :{len(df_rqt)}")
#faire un retour si pas d'objet
#refaire la requete si erreur 414 par bounding box
df = self.get_all(df_rqt)
df=df.rename(columns={'result':self.obsP})
return df
zone=gpd.read_file('/home/tloree/Téléchargements/bv2.json')
print(zone.crs)
zone=zone.to_crs('EPSG:4326')
print(zone.crs)
zone.plot()
epsg:3857 EPSG:4326
<Axes: >
url_service='https://frost.geosas.fr/agri4cast/v1.0/'
geom=zone.geometry[0]
obsP="temperature"
date_start, date_end = '2016-06-01T00:00:00Z','2016-10-01T00:00:00Z'
instance = telechargement_geo_spatial(url_service, geom, obsP, date_start, date_end)
df = instance.telechargement()
unite = instance.unite
symbol = instance.symbol
display(df)
MultipolygonE, conversion en polygone Recherche des Datastreams par l'ObservedProperty : temperature par intersection spatial sur la période : 2016-06-01T00:00:00Z 2016-10-01T00:00:00Z Requête générée: 414 url trop grande = la géometrie en entrée est trop complexe tentative d'intersection par la bounding box Requête générée: https://frost.geosas.fr/agri4cast/v1.0/Datastreams?$filter=geo.intersects(Thing/Locations/location, geography'POLYGON ((-3.306542346883707 48.12010680427483, -3.306542346883707 48.48722797229124, -4.137462844378464 48.48722797229124, -4.137462844378464 48.12010680427483, -3.306542346883707 48.12010680427483))') and ObservedProperty/name eq'temperature'&$select=name,description,unitOfMeasurement,Observations,phenomenonTime&$expand=Thing/Locations($select=name,@iot.id,location) Nombre d'objet à téléchargerger :3 Téléchargement en cours :
100%|██████████| 3/3 [00:01<00:00, 2.96it/s]
temperature | phenomenonTime | name | x | y | |
---|---|---|---|---|---|
0 | 290.991000 | 2016-08-29T02:00:00Z | t_29263002 | -4.098 | 48.228 |
1 | 289.429167 | 2016-08-30T02:00:00Z | t_29263002 | -4.098 | 48.228 |
2 | 289.786667 | 2016-08-31T02:00:00Z | t_29263002 | -4.098 | 48.228 |
3 | 289.357083 | 2016-09-01T02:00:00Z | t_29263002 | -4.098 | 48.228 |
4 | 287.677917 | 2016-09-02T02:00:00Z | t_29263002 | -4.098 | 48.228 |
... | ... | ... | ... | ... | ... |
729 | 287.465000 | 2016-09-26T02:00:00Z | t_29278001 | -3.730 | 48.170 |
730 | 289.824167 | 2016-09-27T02:00:00Z | t_29278001 | -3.730 | 48.170 |
731 | 287.527917 | 2016-09-28T02:00:00Z | t_29278001 | -3.730 | 48.170 |
732 | 287.055417 | 2016-09-29T02:00:00Z | t_29278001 | -3.730 | 48.170 |
733 | 285.899583 | 2016-09-30T02:00:00Z | t_29278001 | -3.730 | 48.170 |
734 rows × 5 columns
for i in df['name'].unique():
df_inter=df[df['name']==i]
print(obsP,i)
print(df_inter[obsP].describe())
temperature t_29263002 count 246.000000 mean 289.866627 std 2.256061 min 282.689706 25% 288.083229 50% 289.785417 75% 291.023379 max 297.372083 Name: temperature, dtype: float64 temperature t_29277001 count 244.000000 mean 289.529495 std 2.492328 min 285.966667 25% 287.517500 50% 289.348125 75% 290.884167 max 299.327083 Name: temperature, dtype: float64 temperature t_29278001 count 244.000000 mean 289.823108 std 2.351803 min 285.899583 25% 287.860417 50% 289.727083 75% 291.210417 max 298.786250 Name: temperature, dtype: float64
#exemple de visulisation
import seaborn as sns
import matplotlib.pyplot as plt
df['phenomenonTime']=pd.to_datetime(df['phenomenonTime'])
sns.lineplot(data=df, x='phenomenonTime',y=obsP,hue='name').set(title=f"ObservedProperties : {obsP}")
plt.xticks(rotation = 25)
plt.ylabel(f"{unite} ({symbol})")
Text(0, 0.5, 'Kelvin (K)')