commit 62d0803379b9b31b32abaf4e2f7ce62c687bf339 Author: putro Date: Sun Apr 5 19:21:02 2020 +0200 initial repository creation diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..49d20bc --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +stazioni.csv diff --git a/README b/README new file mode 100644 index 0000000..3815085 --- /dev/null +++ b/README @@ -0,0 +1,34 @@ +Software per il recupero delle informazioni dei dati dei sensori ambientali pubblicati dall'arpa sul portale opendata della regione lombardia + +la lista delle stazioni e dai sensori si trova qui': +https://www.dati.lombardia.it/Ambiente/Stazioni-qualit-dell-aria/ib47-atvt +lo script stazioni.py stampa la lista delle stazioni e dei sensori recuperando i dati da link qui' sopra, +se si salva invece il contenuto di quel link come stazioni.csv la ricerca viene fatta offline in questo file. + +il portale contiene due tipi di documenti: i dataset e i files. +i dataset esistono solo dal 2017 in avanti, sono annuali, e non contengono i dati dei sensori che ad oggi risultano non piu' attivi, +(ad es. per un sensore che ha smesso di funzionare nel 2018 non sono piu' disponibili i dati del 2017). +Sono interrogabili da remoto richiedendo solo i dati di sensori specifici, senza necessita' di scaricare tutto il dataset. + +I files invece sono disponibilli in formato csv (non compressi o zippati), la lista attuale e' questa: +sensori_aria_1968-1995.zip - 47M +sensori_aria_1996-2000.zip - 67M +sensori_aria_2001-2004.zip - 70M +sensori_aria_2005-2007.zip - 62M +sensori_aria_2008-2010.zip - 69M +sensori_aria_2011.zip - 24M +sensori_aria_2012.zip - 23M +sensori_aria_2013.zip - 23M +sensori_aria_2014.zip - 21M +sensori_aria_2015.zip - 21M +sensori_aria_2016.zip - 21M +sensori_aria_2017.zip - 13M +sensori_aria_2018.zip - 13M +sensori_aria_2019.zip - 13M + +per visualizzarne i dati e' necessario scaricare l'intero file e processarlo. + +Quindi per l'analisi dei dati recenti e' piu' comodo lavorare con i dataset, per analisi storiche invece bisogna usare i csv + +openlamb usa i dataset, +in futuro ci sara' una versione che lavorera' con i csv diff --git a/openlamb.py b/openlamb.py new file mode 100755 index 0000000..14e6f26 --- /dev/null +++ b/openlamb.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# pylint: skip-file + +import argparse +import traceback +import sys +import subprocess +import pandas as pd +import numpy as np +from sodapy import Socrata +import matplotlib.pyplot as plt + + +datasets_ambiente = {"2020": "nicp-bhqi", + "2019": "kujm-kavy", + "2018": "bgqm-yq56", + "2017": "j8j8-qsb2"} + + +def _connect(): + client = Socrata("www.dati.lombardia.it", None) + return client + +def process_dataset(dataset, sensore): + client = _connect() + results = client.get(dataset, IdSensore=sensore) + results_df = pd.DataFrame.from_records(results) + try: + results_df = results_df.astype({'valore': 'float64'}) + except: + print('\nspiacente, dati non disponibili per il sensore %s') % sensore + sys.exit(-1) + results_df["data"] = pd.to_datetime(results_df["data"]) + results_df = results_df.replace(-9999, np.nan) + results_df.sort_values(by=['data'], inplace=True) + results_df.rename(columns = {'valore':sensore}, inplace = True) + results_df.drop(columns=['idoperatore', 'idsensore', 'stato'], inplace = True) + return results_df + + +def merge_df(dataframes, sensori): + df = dataframes[sensori[0]] + for sensore in sensori[1:]: + df = pd.merge(df, dataframes[sensore]) + return df + + +def get_dataframes(datasets, sensori): + dataframes = {} + for sensore in sensori: + df = process_dataset(datasets[0], sensore) + for dataset in datasets[1:]: + df = pd.concat([df, process_dataset(dataset, sensore)], axis=0) + dataframes[sensore] = df + return dataframes + + +def plot_dataframe(dataframe): + dataframe.plot(x='data') + plt.axhline(y=50, color='black', linestyle='-', label='24-hour average EU limit') + plt.show() + + +def main(): + + parser = argparse.ArgumentParser() + parser.add_argument("--dataset", nargs='+', required=True, help="ricerca dei datasets") + parser.add_argument('--sensori', nargs='+', required=True, help="specifica i sensori") + args = parser.parse_args() + + try: + datasets = [] + if "all" in args.dataset: + for k in datasets_ambiente.keys(): + datasets.append(datasets_ambiente[k]) + else: + for d in args.dataset: + datasets.append(datasets_ambiente[d]) + sensori = args.sensori + dataframes = get_dataframes(datasets, sensori) + datamerged = merge_df(dataframes, sensori) + import stazioni + s = stazioni.get_stazioni() + for sensore in sensori: + location = s.loc[s['idsensore'] == sensore, 'nomestazione'].iloc[0] + print('Valore medio per il sensore %s %s: %s' % (sensore, location, datamerged[sensore].mean().round(1))) + plot_dataframe(datamerged) + + + except KeyError: + print("\nErrore:") + #print("Datasets disponibili:") + #print('\n'.join([str(lst) for lst in sorted(datasets_ambiente.keys())])) + traceback.print_exc() + except KeyboardInterrupt: + print("program terminated by user") + except SystemExit: + print("program terminated, bye") + except: + print("\nAn unhandled exception occured, here's the traceback!\n") + traceback.print_exc() + print("\nReport this to putro@autistici.org") + sys.exit() + +if __name__ == '__main__': + main() diff --git a/stazioni.py b/stazioni.py new file mode 100755 index 0000000..7b4ce12 --- /dev/null +++ b/stazioni.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# pylint: skip-file + +import os.path +import pandas as pd +from sodapy import Socrata + + +def _connect(): + client = Socrata("www.dati.lombardia.it", None) + return client + + +def get_stazioni(offline=False): + if offline: + stazioni = pd.read_csv("stazioni.csv") + stazioni_df = pd.DataFrame.from_records(stazioni) + stazioni_df.drop(columns=["Storico", "Idstazione", "Utm_Nord", "UTM_Est", "lat", "lng", "location", "Limiti amministrativi 2014 delle province di Regione Lombardia", "Limiti amministrativi 2015 delle province di Regione Lombardia"], inplace=True) + stazioni_df.columns = [x.lower() for x in stazioni_df.columns] + else: + client = _connect() + stazioni = client.get("ib47-atvt") + stazioni_df = pd.DataFrame.from_records(stazioni) + stazioni_df.drop(columns=[":@computed_region_6hky_swhk", ":@computed_region_ttgh_9sm5", "utm_est", "utm_nord", "storico", "idstazione", "lat", "lng", "location"], inplace=True) + stazioni_df["datastart"] = pd.to_datetime(stazioni_df["datastart"]) + stazioni_df["datastop"] = pd.to_datetime(stazioni_df["datastop"]) + return stazioni_df + + +def print_stazioni(): + pd.set_option('display.max_rows', None) + if os.path.exists("stazioni.csv"): + stazioni = get_stazioni(offline=True) + else: + stazioni = get_stazioni() + print(stazioni) + + +if __name__ == '__main__': + print_stazioni()