예제 #1
0
def about(options: Munch):
    """
    Output possible arguments for command line options
    "--parameter", "--resolution" and "--period".

    :param options: Normalized docopt command line options.
    """
    def output(thing):
        for item in thing:
            if item:
                if hasattr(item, "value"):
                    value = item.value
                else:
                    value = item
                print("-", value)

    if options.parameters:
        output(DwdObservationDataset)

    elif options.resolutions:
        output(DwdObservationResolution)

    elif options.periods:
        output(DwdObservationPeriod)

    elif options.coverage:
        metadata = DwdObservationRequest.discover(
            filter_=options.resolution,
            dataset=read_list(options.parameter),
            flatten=False,
        )
        output = json.dumps(metadata, indent=4)
        print(output)

    elif options.fields:
        metadata = DwdObservationRequest.describe_fields(
            dataset=read_list(options.parameter),
            resolution=options.resolution,
            period=read_list(options.period),
            language=options.language,
        )
        output = pformat(dict(metadata))
        print(output)

    else:
        log.error(
            'Please invoke "wetterdienst dwd about" with one of these subcommands:'
        )
        output(["parameters", "resolutions", "periods", "coverage"])
        sys.exit(1)
예제 #2
0
def about(options: Munch):
    """
    Output possible arguments for command line options
    "--parameter", "--resolution" and "--period".

    :param options: Normalized docopt command line options.
    """
    def output(thing):
        for item in thing:
            if item:
                if hasattr(item, "value"):
                    value = item.value
                else:
                    value = item
                print("-", value)

    if options.parameters:
        output(DWDObservationParameterSet)

    elif options.resolutions:
        output(DWDObservationResolution)

    elif options.periods:
        output(DWDObservationPeriod)

    elif options.coverage:
        output = json.dumps(
            DWDObservationMetadata(
                resolution=options.resolution,
                parameter_set=read_list(options.parameter),
                period=read_list(options.period),
            ).discover_parameter_sets(),
            indent=4,
        )
        print(output)

    else:
        log.error(
            'Please invoke "wetterdienst dwd about" with one of these subcommands:'
        )
        output(["parameters", "resolutions", "periods", "coverage"])
        sys.exit(1)
예제 #3
0
def about(options: Munch):
    """
    Output possible arguments for command line options
    "--parameter", "--resolution" and "--period".

    :param options: Normalized docopt command line options.
    """

    def output(thing):
        for item in thing:
            if item:
                if hasattr(item, "value"):
                    value = item.value
                else:
                    value = item
                print("-", value)

    if options.parameters:
        output(Parameter)

    elif options.resolutions:
        output(TimeResolution)

    elif options.periods:
        output(PeriodType)

    elif options.coverage:
        print(
            discover_climate_observations(
                time_resolution=options.resolution,
                parameter=read_list(options.parameter),
                period_type=read_list(options.period),
            )
        )

    else:
        log.error(
            'Please invoke "wetterdienst dwd about" with one of these subcommands:'
        )
        output(["parameters", "resolutions", "periods", "coverage"])
        sys.exit(1)
예제 #4
0
def dwd_values(
    kind: str,
    stations: str = Query(default=None),
    parameter: str = Query(default=None),
    resolution: str = Query(default=None),
    period: str = Query(default=None),
    mosmix_type: str = Query(default=None),
    date: str = Query(default=None),
    sql: str = Query(default=None),
    tidy: bool = Query(default=True),
):
    """
    Acquire data from DWD.

    # TODO: Obtain lat/lon distance/number information.

    :param provider:
    :param kind:        string for product, either observation or forecast
    :param stations:     Comma-separated list of station identifiers.
    :param parameter:   Observation measure
    :param resolution:  Frequency/granularity of measurement interval
    :param period:      Recent or historical files
    :param mosmix_type: MOSMIX type. Either "small" or "large".
    :param date:        Date or date range
    :param sql:         SQL expression
    :param tidy:        Whether to return data in tidy format. Default: True.
    :return:
    """
    if kind not in ["observation", "mosmix"]:
        return HTTPException(
            status_code=404,
            detail=f"Unknown value for query argument 'kind={kind}' {kind}",
        )

    if stations is None:
        raise HTTPException(
            status_code=400, detail="Query argument 'stations' is required"
        )

    station_ids = map(str, read_list(stations))

    if kind == "observation":
        if parameter is None or resolution is None or period is None:
            raise HTTPException(
                status_code=400,
                detail="Query arguments 'parameter', 'resolution' "
                "and 'period' are required",
            )

        # Data acquisition.
        request = DwdObservationRequest(
            parameter=parameter,
            resolution=resolution,
            period=period,
            tidy=tidy,
            si_units=False,
        )
    else:
        if parameter is None or mosmix_type is None:
            raise HTTPException(
                status_code=400, detail="Query argument 'mosmix_type' is required"
            )

        request = DwdMosmixRequest(
            parameter=parameter, mosmix_type=mosmix_type, si_units=False
        )

    # Postprocessing.
    results = request.filter_by_station_id(station_id=station_ids).values.all()

    if date is not None:
        results.filter_by_date(date)

    if sql is not None:
        results.filter_by_sql(sql)

    data = json.loads(results.to_json())

    return make_json_response(data)
예제 #5
0
def dwd_readings(
    product: str,
    station: str = Query(default=None),
    parameter: str = Query(default=None),
    resolution: str = Query(default=None),
    period: str = Query(default=None),
    mosmix_type: str = Query(default=None, alias="mosmix-type"),
    date: str = Query(default=None),
    sql: str = Query(default=None),
):
    """
    Acquire data from DWD.

    # TODO: Obtain lat/lon distance/number information.

    :param product:     string for product, either observations or mosmix
    :param station:     Comma-separated list of station identifiers.
    :param parameter:   Observation measure
    :param resolution:  Frequency/granularity of measurement interval
    :param period:      Recent or historical files
    :param mosmix_type  type of mosmix, either small or large
    :param date:        Date or date range
    :param sql:         SQL expression
    :return:
    """
    if product not in ["observations", "mosmix"]:
        return HTTPException(status_code=404, detail=f"product {product} not found")

    if station is None:
        raise HTTPException(
            status_code=400, detail="Query argument 'station' is required"
        )

    station_ids = map(str, read_list(station))

    if product == "observations":
        if parameter is None or resolution is None or period is None:
            raise HTTPException(
                status_code=400,
                detail="Query arguments 'parameter', 'resolution' "
                "and 'period' are required",
            )

        parameter = parse_enumeration_from_template(
            parameter, DWDObservationParameterSet
        )
        resolution = parse_enumeration_from_template(
            resolution, DWDObservationResolution
        )
        period = parse_enumeration_from_template(period, DWDObservationPeriod)

        # Data acquisition.
        readings = DWDObservationData(
            station_ids=station_ids,
            parameters=parameter,
            resolution=resolution,
            periods=period,
            tidy_data=True,
            humanize_parameters=True,
        )
    else:
        if mosmix_type is None:
            raise HTTPException(
                status_code=400, detail="Query argument 'mosmix_type' is required"
            )

        mosmix_type = parse_enumeration_from_template(mosmix_type, DWDMosmixType)

        readings = DWDMosmixData(station_ids=station_ids, mosmix_type=mosmix_type)

    # Postprocessing.
    df = readings.all()

    if date is not None:
        df = df.dwd.filter_by_date(date, resolution)

    df = df.dwd.lower()

    if sql is not None:
        df = df.io.sql(sql)

    data = json.loads(df.to_json(orient="records", date_format="iso"))

    return make_json_response(data)
예제 #6
0
def dwd_readings(
    station: str = Query(default=None),
    parameter: str = Query(default=None),
    resolution: str = Query(default=None),
    period: str = Query(default=None),
    date: str = Query(default=None),
    sql: str = Query(default=None),
):
    """
    Acquire data from DWD.

    # TODO: Obtain lat/lon distance/number information.

    :param station:     Comma-separated list of station identifiers.
    :param parameter:   Observation measure
    :param resolution:  Frequency/granularity of measurement interval
    :param period:      Recent or historical files
    :param date:        Date or date range
    :param sql:         SQL expression
    :return:
    """

    if station is None:
        raise HTTPException(
            status_code=400, detail="Query argument 'station' is required"
        )

    if parameter is None or resolution is None or period is None:
        raise HTTPException(
            status_code=400,
            detail="Query arguments 'parameter', 'resolution' "
            "and 'period' are required",
        )

    station_ids = map(int, read_list(station))
    parameter = parse_enumeration_from_template(parameter, DWDObservationParameterSet)
    resolution = parse_enumeration_from_template(resolution, DWDObservationResolution)
    period = parse_enumeration_from_template(period, DWDObservationPeriod)

    # Data acquisition.
    observations = DWDObservationData(
        station_ids=station_ids,
        parameters=parameter,
        resolution=resolution,
        periods=period,
        tidy_data=True,
        humanize_column_names=True,
    )

    # Postprocessing.
    df = observations.collect_safe()

    if date is not None:
        df = df.dwd.filter_by_date(date, resolution)

    df = df.dwd.lower()

    if sql is not None:
        df = df.io.sql(sql)

    data = json.loads(df.to_json(orient="records", date_format="iso"))
    return make_json_response(data)
예제 #7
0
def values(
        provider: str = Query(default=None),
        network: str = Query(default=None),
        parameter: str = Query(default=None),
        resolution: str = Query(default=None),
        period: str = Query(default=None),
        date: str = Query(default=None),
        issue: str = Query(default="latest"),
        all_: str = Query(alias="all", default=False),
        station: str = Query(default=None),
        name: str = Query(default=None),
        coordinates: str = Query(default=None),
        rank: int = Query(default=None),
        distance: float = Query(default=None),
        bbox: str = Query(default=None),
        sql: str = Query(default=None),
        sql_values: str = Query(alias="sql-values", default=None),
        humanize: bool = Query(default=True),
        tidy: bool = Query(default=True),
        si_units: bool = Query(alias="si-units", default=True),
        skip_empty: bool = Query(alias="skip-empty", default=False),
        skip_threshold: float = Query(
            alias="skip-threshold", default=0.95, gt=0, le=1),
        dropna: bool = Query(alias="dropna", default=False),
        pretty: bool = Query(default=False),
        debug: bool = Query(default=False),
):
    """
    Acquire data from DWD.

    :param provider:
    :param network:        string for network of provider
    :param parameter:   Observation measure
    :param resolution:  Frequency/granularity of measurement interval
    :param period:      Recent or historical files
    :param date:        Date or date range
    :param issue:
    :param all_:
    :param station:
    :param name:
    :param coordinates:
    :param rank:
    :param distance:
    :param bbox:
    :param sql:         SQL expression
    :param sql_values:
    :param fmt:
    :param humanize:
    :param tidy:        Whether to return data in tidy format. Default: True.
    :param si_units:
    :param pretty:
    :param debug:
    :return:
    """
    # TODO: Add geojson support
    fmt = "json"

    if provider is None or network is None:
        raise HTTPException(
            status_code=400,
            detail="Query arguments 'provider' and 'network' are required",
        )

    if parameter is None or resolution is None:
        raise HTTPException(
            status_code=400,
            detail="Query arguments 'parameter', 'resolution' "
            "and 'date' are required",
        )

    if fmt not in ("json", "geojson"):
        raise HTTPException(
            status_code=400,
            detail="format argument must be one of json, geojson",
        )

    set_logging_level(debug)

    try:
        api: ScalarRequestCore = Wetterdienst(provider, network)
    except ProviderError:
        return HTTPException(
            status_code=404,
            detail=f"Given combination of provider and network not available. "
            f"Choose provider and network from {Wetterdienst.discover()}",
        )

    parameter = read_list(parameter)
    if period:
        period = read_list(period)
    if station:
        station = read_list(station)

    try:
        values_ = get_values(
            api=api,
            parameter=parameter,
            resolution=resolution,
            date=date,
            issue=issue,
            period=period,
            all_=all_,
            station_id=station,
            name=name,
            coordinates=coordinates,
            rank=rank,
            distance=distance,
            bbox=bbox,
            sql=sql,
            sql_values=sql_values,
            si_units=si_units,
            skip_empty=skip_empty,
            skip_threshold=skip_threshold,
            dropna=dropna,
            tidy=tidy,
            humanize=humanize,
        )
    except Exception as e:
        log.exception(e)

        return HTTPException(status_code=404, detail=str(e))

    indent = None
    if pretty:
        indent = 4

    output = values_.df

    output[Columns.DATE.value] = output[Columns.DATE.value].apply(
        lambda ts: ts.isoformat())

    output = output.replace({np.NaN: None, pd.NA: None})

    output = output.to_dict(orient="records")

    output = make_json_response(output, api.provider)

    output = json.dumps(output, indent=indent, ensure_ascii=False)

    return Response(content=output, media_type="application/json")
예제 #8
0
def stations(
        provider: str = Query(default=None),
        network: str = Query(default=None),
        parameter: str = Query(default=None),
        resolution: str = Query(default=None),
        period: str = Query(default=None),
        all_: str = Query(alias="all", default=False),
        station_id: str = Query(default=None),
        name: str = Query(default=None),
        coordinates: str = Query(default=None),
        rank: int = Query(default=None),
        distance: float = Query(default=None),
        bbox: str = Query(default=None),
        sql: str = Query(default=None),
        fmt: str = Query(alias="format", default="json"),
        debug: bool = Query(default=False),
        pretty: bool = Query(default=False),
):
    if provider is None or network is None:
        raise HTTPException(
            status_code=400,
            detail="Query arguments 'provider' and 'network' are required",
        )

    if parameter is None or resolution is None:
        raise HTTPException(
            status_code=400,
            detail="Query arguments 'parameter', 'resolution' "
            "and 'period' are required",
        )

    if fmt not in ("json", "geojson"):
        raise HTTPException(
            status_code=400,
            detail="format argument must be one of json, geojson",
        )

    set_logging_level(debug)

    try:
        api = Wetterdienst(provider, network)
    except ProviderError:
        return HTTPException(
            status_code=404,
            detail=
            f"Choose provider and network from {app.url_path_for('coverage')}",
        )

    parameter = read_list(parameter)
    if period:
        period = read_list(period)
    if station_id:
        station_id = read_list(station_id)

    try:
        stations_ = get_stations(
            api=api,
            parameter=parameter,
            resolution=resolution,
            period=period,
            date=None,
            issue=None,
            all_=all_,
            station_id=station_id,
            name=name,
            coordinates=coordinates,
            rank=rank,
            distance=distance,
            bbox=bbox,
            sql=sql,
            tidy=False,
            si_units=False,
            humanize=False,
            skip_empty=False,
            skip_threshold=0.95,
            dropna=False,
        )
    except (KeyError, ValueError) as e:
        return HTTPException(status_code=404, detail=str(e))

    if not stations_.parameter or not stations_.resolution:
        return HTTPException(
            status_code=404,
            detail=
            f"No parameter found for provider {provider}, network {network}, "
            f"parameter(s) {parameter} and resolution {resolution}.",
        )

    stations_.df = stations_.df.replace({np.nan: None, pd.NA: None})

    indent = None
    if pretty:
        indent = 4

    if fmt == "json":
        output = stations_.to_dict()
    elif fmt == "geojson":
        output = stations_.to_ogc_feature_collection()

    output = make_json_response(output, api.provider)

    output = json.dumps(output, indent=indent, ensure_ascii=False)

    return Response(content=output, media_type="application/json")
예제 #9
0
def run():
    """
    Usage:
      wetterdienst dwd observation stations --parameter=<parameter> --resolution=<resolution> --period=<period> [--station=<station>] [--latitude=<latitude>] [--longitude=<longitude>] [--rank=<rank>] [--distance=<distance>] [--sql=<sql>] [--format=<format>] [--target=<target>]
      wetterdienst dwd observation values --parameter=<parameter> --resolution=<resolution> [--station=<station>] [--period=<period>] [--date=<date>] [--tidy] [--sql=<sql>] [--format=<format>] [--target=<target>]
      wetterdienst dwd observation values --parameter=<parameter> --resolution=<resolution> --latitude=<latitude> --longitude=<longitude> [--period=<period>] [--rank=<rank>] [--distance=<distance>] [--tidy] [--date=<date>] [--sql=<sql>] [--format=<format>] [--target=<target>]
      wetterdienst dwd forecast stations [--parameter=<parameter>] [--mosmix-type=<mosmix-type>] [--date=<date>] [--station=<station>] [--latitude=<latitude>] [--longitude=<longitude>] [--rank=<rank>] [--distance=<distance>] [--sql=<sql>] [--format=<format>] [--target=<target>]
      wetterdienst dwd forecast values --parameter=<parameter> [--mosmix-type=<mosmix-type>] --station=<station> [--date=<date>] [--tidy] [--sql=<sql>] [--format=<format>] [--target=<target>]
      wetterdienst dwd about [parameters] [resolutions] [periods]
      wetterdienst dwd about coverage [--parameter=<parameter>] [--resolution=<resolution>] [--period=<period>]
      wetterdienst dwd about fields --parameter=<parameter> --resolution=<resolution> --period=<period> [--language=<language>]
      wetterdienst radar stations [--odim-code=<odim-code>] [--wmo-code=<wmo-code>] [--country-name=<country-name>]
      wetterdienst dwd radar stations
      wetterdienst restapi [--listen=<listen>] [--reload]
      wetterdienst explorer [--listen=<listen>] [--reload]
      wetterdienst --version
      wetterdienst (-h | --help)

    Options:
      --parameter=<parameter>       Parameter Set/Parameter, e.g. "kl" or "precipitation_height", etc.
      --resolution=<resolution>     Dataset resolution: "annual", "monthly", "daily", "hourly", "minute_10", "minute_1"
      --period=<period>             Dataset period: "historical", "recent", "now"
      --station=<station>           Comma-separated list of station identifiers
      --latitude=<latitude>         Latitude for filtering by geoposition.
      --longitude=<longitude>       Longitude for filtering by geoposition.
      --rank=<rank>                 Rank of nearby stations when filtering by geoposition.
      --distance=<distance>         Maximum distance in km when filtering by geoposition.
      --date=<date>                 Date for filtering data. Can be either a single date(time) or
                                    an ISO-8601 time interval, see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals.
      --mosmix-type=<mosmix-type>   type of mosmix, either 'small' or 'large'
      --sql=<sql>                   SQL query to apply to DataFrame.
      --format=<format>             Output format. [Default: json]
      --target=<target>             Output target for storing data into different data sinks.
      --language=<language>         Output language. [Default: en]
      --version                     Show version information
      --debug                       Enable debug messages
      --listen=<listen>             HTTP server listen address.
      --reload                      Run service and dynamically reload changed files
      -h --help                     Show this screen


    Examples requesting observation stations:

      # Get list of all stations for daily climate summary data in JSON format
      wetterdienst dwd observation stations --parameter=kl --resolution=daily --period=recent

      # Get list of all stations in CSV format
      wetterdienst dwd observation stations --parameter=kl --resolution=daily --period=recent --format=csv

      # Get list of specific stations
      wetterdienst dwd observation stations --resolution=daily --parameter=kl --period=recent --station=1,1048,4411

      # Get list of specific stations in GeoJSON format
      wetterdienst dwd observation stations --resolution=daily --parameter=kl --period=recent --station=1,1048,4411 --format=geojson

    Examples requesting observation values:

      # Get daily climate summary data for specific stations
      wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=daily --period=recent

      # Get daily climate summary data for specific stations in CSV format
      wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=daily --period=recent

      # Get daily climate summary data for specific stations in tidy format
      wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=daily --period=recent --tidy

      # Limit output to specific date
      wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=daily --period=recent --date=2020-05-01

      # Limit output to specified date range in ISO-8601 time interval format
      wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=daily --period=recent --date=2020-05-01/2020-05-05

      # The real power horse: Acquire data across historical+recent data sets
      wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=daily --date=1969-01-01/2020-06-11

      # Acquire monthly data for 2020-05
      wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=monthly --date=2020-05

      # Acquire monthly data from 2017-01 to 2019-12
      wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=monthly --date=2017-01/2019-12

      # Acquire annual data for 2019
      wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=annual --date=2019

      # Acquire annual data from 2010 to 2020
      wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=annual --date=2010/2020

      # Acquire hourly data
      wetterdienst dwd observation values --station=1048,4411 --parameter=air_temperature --resolution=hourly --period=recent --date=2020-06-15T12

    Examples requesting forecast stations:

      wetterdienst dwd forecast stations

    Examples requesting forecast values:

      wetterdienst dwd forecast values --parameter=ttt,ff --station=65510

    Examples using geospatial features:

      # Acquire stations and readings by geoposition, request specific number of nearby stations.
      wetterdienst dwd observation stations --resolution=daily --parameter=kl --period=recent --lat=49.9195 --lon=8.9671 --rank=5
      wetterdienst dwd observation values --resolution=daily --parameter=kl --period=recent --lat=49.9195 --lon=8.9671 --rank=5 --date=2020-06-30

      # Acquire stations and readings by geoposition, request stations within specific distance.
      wetterdienst dwd observation stations --resolution=daily --parameter=kl --period=recent --lat=49.9195 --lon=8.9671 --distance=25
      wetterdienst dwd observation values --resolution=daily --parameter=kl --period=recent --lat=49.9195 --lon=8.9671 --distance=25 --date=2020-06-30

    Examples using SQL filtering:

      # Find stations by state.
      wetterdienst dwd observation stations --parameter=kl --resolution=daily --period=recent --sql="SELECT * FROM data WHERE state='Sachsen'"

      # Find stations by name (LIKE query).
      wetterdienst dwd observation stations --parameter=kl --resolution=daily --period=recent --sql="SELECT * FROM data WHERE lower(station_name) LIKE lower('%dresden%')"

      # Find stations by name (regexp query).
      wetterdienst dwd observation stations --parameter=kl --resolution=daily --period=recent --sql="SELECT * FROM data WHERE regexp_matches(lower(station_name), lower('.*dresden.*'))"

      # Filter measurements: Display daily climate observation readings where the maximum temperature is below two degrees celsius.
      wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=daily --period=recent --sql="SELECT * FROM data WHERE temperature_air_max_200 < 2.0;"

      # Filter measurements: Same as above, but use tidy format.
      # FIXME: Currently, this does not work, see https://github.com/earthobservations/wetterdienst/issues/377.
      wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=daily --period=recent --sql="SELECT * FROM data WHERE parameter='temperature_air_max_200' AND value < 2.0;" --tidy

    Examples for inquiring metadata:

      # Display list of available parameters (air_temperature, precipitation, pressure, ...)
      wetterdienst dwd about parameters

      # Display list of available resolutions (10_minutes, hourly, daily, ...)
      wetterdienst dwd about resolutions

      # Display list of available periods (historical, recent, now)
      wetterdienst dwd about periods

      # Display coverage/correlation between parameters, resolutions and periods.
      # This can answer questions like ...
      wetterdienst dwd about coverage

      # Tell me all periods and resolutions available for 'air_temperature'.
      wetterdienst dwd about coverage --parameter=air_temperature

      # Tell me all parameters available for 'daily' resolution.
      wetterdienst dwd about coverage --resolution=daily

    Examples for exporting data to files:

      # Export list of stations into spreadsheet
      wetterdienst dwd observations stations --parameter=kl --resolution=daily --period=recent --target=file://stations.xlsx

      # Shortcut command for fetching readings
      alias fetch="wetterdienst dwd observations values --station=1048,4411 --parameter=kl --resolution=daily --period=recent"

      # Export readings into spreadsheet (Excel-compatible)
      fetch --target="file://observations.xlsx"

      # Export readings into Parquet format and display head of Parquet file
      fetch --target="file://observations.parquet"

      # Check Parquet file
      parquet-tools schema observations.parquet
      parquet-tools head observations.parquet

      # Export readings into Zarr format
      fetch --target="file://observations.zarr"

    Examples for exporting data to databases:

      # Shortcut command for fetching readings
      alias fetch="wetterdienst dwd observation values --station=1048,4411 --parameter=kl --resolution=daily --period=recent"

      # Store readings to DuckDB
      fetch --target="duckdb:///dwd.duckdb?table=weather"

      # Store readings to InfluxDB
      fetch --target="influxdb://localhost/?database=dwd&table=weather"

      # Store readings to CrateDB
      fetch --target="crate://localhost/?database=dwd&table=weather"

    Invoke the HTTP REST API service:

      # Start service on standard port, listening on http://localhost:7890.
      wetterdienst restapi

      # Start service on standard port and watch filesystem changes.
      # This is suitable for development.
      wetterdienst restapi --reload

      # Start service on public interface and specific port.
      wetterdienst restapi --listen=0.0.0.0:8890

    Invoke the Wetterdienst Explorer UI service:

      # Start service on standard port, listening on http://localhost:7891.
      wetterdienst explorer

      # Start service on standard port and watch filesystem changes.
      # This is suitable for development.
      wetterdienst explorer --reload

      # Start service on public interface and specific port.
      wetterdienst explorer --listen=0.0.0.0:8891

    """
    appname = f"{__appname__} {__version__}"

    # Read command line options.
    options = normalize_options(docopt(run.__doc__, version=appname))

    # Setup logging.
    debug = options.get("debug")

    log_level = logging.INFO

    if debug:  # pragma: no cover
        log_level = logging.DEBUG

    setup_logging(log_level)

    # Run HTTP service.
    if options.restapi:  # pragma: no cover
        listen_address = options.listen
        log.info(f"Starting {appname}")
        log.info(f"Starting HTTP web service on http://{listen_address}")
        from wetterdienst.ui.restapi import start_service

        start_service(listen_address, reload=options.reload)
        return

    # Run UI service.
    if options.explorer:  # pragma: no cover
        listen_address = options.listen
        log.info(f"Starting {appname}")
        log.info(f"Starting UI web service on http://{listen_address}")
        from wetterdienst.ui.explorer.app import start_service

        start_service(listen_address, reload=options.reload)
        return

    # Handle radar data inquiry. Currently, "stations only".
    if options.radar:
        if options.dwd:
            data = DwdRadarSites().all()
        else:
            if options.odim_code:
                data = OperaRadarSites().by_odimcode(options.odim_code)
            elif options.wmo_code:
                data = OperaRadarSites().by_wmocode(options.wmo_code)
            elif options.country_name:
                data = OperaRadarSites().by_countryname(options.country_name)
            else:
                data = OperaRadarSites().all()

        output = json.dumps(data, indent=4)
        print(output)
        return

    # Output domain information.
    if options.about:
        about(options)
        return

    # Sanity checks.
    if (options["values"] or options.forecast) and options.format == "geojson":
        raise KeyError("GeoJSON format only available for stations output")

    # Acquire station list, also used for readings if required.
    # Filtering applied for distance (a.k.a. nearby) and pre-selected stations
    stations = None
    if options.observation:
        stations = DwdObservationRequest(
            parameter=read_list(options.parameter),
            resolution=options.resolution,
            period=options.period,
            tidy=options.tidy,
            si_units=False,
        )
    elif options.forecast:
        stations = DwdMosmixRequest(
            parameter=read_list(options.parameter),
            mosmix_type=DwdMosmixType.LARGE,
            tidy=options.tidy,
            si_units=False,
        )

    if options.latitude and options.longitude:
        if options.rank:
            stations = stations.filter_by_rank(
                latitude=float(options.latitude),
                longitude=float(options.longitude),
                rank=int(options.rank),
            )
        elif options.distance:
            stations = stations.filter_by_distance(
                latitude=float(options.latitude),
                longitude=float(options.longitude),
                distance=int(options.distance),
            )
        else:
            raise DocoptExit(
                "Geospatial queries need either --rank or --distance")
        results = stations

    elif options.station:
        results = stations.filter_by_station_id(read_list(options.station))

    else:
        results = stations.all()

    df = pd.DataFrame()

    if options.stations:
        pass

    elif options["values"]:
        try:
            # TODO: Add stream-based processing here.
            results = results.values.all()
        except ValueError as ex:
            log.exception(ex)
            sys.exit(1)

    df = results.df

    if df.empty:
        log.error("No data available for given constraints")
        sys.exit(1)

    # Filter readings by datetime expression.
    if options["values"] and options.date:
        results.filter_by_date(options.date)

    # Apply filtering by SQL.
    if options.sql:
        if options.tidy:
            log.error("Combining SQL filtering with tidy format not possible")
            sys.exit(1)

        log.info(f"Filtering with SQL: {options.sql}")
        results.filter_by_sql(options.sql)

    # Emit to data sink, e.g. write to database.
    if options.target:
        results.to_target(options.target)
        return

    # Render to output format.
    try:
        if options.format == "json":
            output = results.to_json()
        elif options.format == "csv":
            output = results.to_csv()
        elif options.format == "geojson":
            output = results.to_geojson()
        else:
            raise KeyError("Unknown output format")

    except KeyError as ex:
        log.error(
            f'{ex}. Output format must be one of "json", "geojson", "csv".')
        sys.exit(1)

    print(output)
예제 #10
0
def get_stations(options: Munch) -> pd.DataFrame:
    """
    Convenience utility function to dispatch command
    line options related to geospatial requests.

    It will get used from both acquisition requests for station-
    and measurement data.

    It is able to handle both "number of stations nearby"
    and "maximum distance in kilometers" query flavors.

    :param options: Normalized docopt command line options.
    :return: nearby_stations
    """

    # Obtain DWIM date range for station liveness.
    # TODO: Obtain from user.
    #   However, some dates will not work and Pandas will croak with:
    #   KeyError: "None of [Int64Index([0, 0, 0, 0, 0], dtype='int64')] are in the [index]"
    # See also https://github.com/earthobservations/wetterdienst/pull/145#discussion_r487698588.

    days500 = datetime.utcnow() + timedelta(days=-500)
    now = datetime.utcnow() + timedelta(days=-2)

    start_date = datetime(days500.year, days500.month, days500.day)
    end_date = datetime(now.year, now.month, now.day)

    stations = None
    if options.stations or (options.latitude and options.longitude):
        if options.observations:
            stations = DWDObservationStations(
                parameter_set=options.parameter,
                resolution=options.resolution,
                period=options.period,
                start_date=start_date,
                end_date=end_date,
            )
        elif options.forecasts:
            stations = DWDMosmixStations()

        if options.latitude and options.longitude:
            if options.number:
                stations = stations.nearby_number(
                    latitude=float(options.latitude),
                    longitude=float(options.longitude),
                    num_stations_nearby=int(options.number),
                )
            elif options.distance:
                stations = stations.nearby_radius(
                    latitude=float(options.latitude),
                    longitude=float(options.longitude),
                    max_distance_in_km=int(options.distance),
                )
        else:
            stations = stations.all()

        if options.station:
            station_ids = read_list(options.station)

            if options.observations:
                stations = stations[stations.STATION_ID.isin(station_ids)]
            else:
                stations = stations[stations.WMO_ID.isin(station_ids)]

        return stations
    return pd.DataFrame()
예제 #11
0
def run():
    """
    Usage:
      wetterdienst dwd observations stations --parameter=<parameter> --resolution=<resolution> --period=<period> [--station=<station>] [--latitude=<latitude>] [--longitude=<longitude>] [--number=<number>] [--distance=<distance>] [--sql=<sql>] [--format=<format>]
      wetterdienst dwd observations readings --parameter=<parameter> --resolution=<resolution> --station=<station> [--period=<period>] [--date=<date>] [--tidy] [--sql=<sql>] [--format=<format>] [--target=<target>]
      wetterdienst dwd observations readings --parameter=<parameter> --resolution=<resolution> --latitude=<latitude> --longitude=<longitude> [--period=<period>] [--number=<number>] [--distance=<distance>] [--tidy] [--date=<date>] [--sql=<sql>] [--format=<format>] [--target=<target>]
      wetterdienst dwd forecasts stations [--date=<date>] [--station=<station>] [--latitude=<latitude>] [--longitude=<longitude>] [--number=<number>] [--distance=<distance>] [--sql=<sql>] [--format=<format>]
      wetterdienst dwd forecasts readings --mosmix-type=<mosmix-type> --station=<station> [--parameter=<parameter>] [--date=<date>] [--tidy] [--sql=<sql>] [--format=<format>] [--target=<target>]
      wetterdienst dwd about [parameters] [resolutions] [periods]
      wetterdienst dwd about coverage [--parameter=<parameter>] [--resolution=<resolution>] [--period=<period>]
      wetterdienst dwd about fields --parameter=<parameter> --resolution=<resolution> --period=<period> [--language=<language>]
      wetterdienst service [--listen=<listen>]
      wetterdienst --version
      wetterdienst (-h | --help)

    Options:
      --parameter=<parameter>       Parameter Set/Parameter, e.g. "kl" or "precipitation_height", etc.
      --resolution=<resolution>     Dataset resolution: "annual", "monthly", "daily", "hourly", "minute_10", "minute_1"
      --period=<period>             Dataset period: "historical", "recent", "now"
      --station=<station>           Comma-separated list of station identifiers
      --latitude=<latitude>         Latitude for filtering by geoposition.
      --longitude=<longitude>       Longitude for filtering by geoposition.
      --number=<number>             Number of nearby stations when filtering by geoposition.
      --distance=<distance>         Maximum distance in km when filtering by geoposition.
      --date=<date>                 Date for filtering data. Can be either a single date(time) or
                                    an ISO-8601 time interval, see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals.
      --mosmix-type=<mosmix-type>   type of mosmix, either 'small' or 'large'
      --sql=<sql>                   SQL query to apply to DataFrame.
      --format=<format>             Output format. [Default: json]
      --target=<target>             Output target for storing data into different data sinks.
      --language=<language>         Output language. [Default: en]
      --version                     Show version information
      --debug                       Enable debug messages
      --listen=<listen>             HTTP server listen address. [Default: localhost:7890]
      -h --help                     Show this screen


    Examples requesting stations:

      # Get list of all stations for daily climate summary data in JSON format
      wetterdienst dwd stations --parameter=kl --resolution=daily --period=recent

      # Get list of all stations in CSV format
      wetterdienst dwd stations --parameter=kl --resolution=daily --period=recent --format=csv

      # Get list of specific stations
      wetterdienst dwd stations --resolution=daily --parameter=kl --period=recent --station=1,1048,4411

      # Get list of specific stations in GeoJSON format
      wetterdienst dwd stations --resolution=daily --parameter=kl --period=recent --station=1,1048,4411 --format=geojson

    Examples requesting readings:

      # Get daily climate summary data for specific stations
      wetterdienst dwd readings --station=1048,4411 --parameter=kl --resolution=daily --period=recent

      # Optionally save/restore to/from disk in order to avoid asking upstream servers each time
      wetterdienst dwd readings --station=1048,4411 --parameter=kl --resolution=daily --period=recent

      # Limit output to specific date
      wetterdienst dwd readings --station=1048,4411 --parameter=kl --resolution=daily --period=recent --date=2020-05-01

      # Limit output to specified date range in ISO-8601 time interval format
      wetterdienst dwd readings --station=1048,4411 --parameter=kl --resolution=daily --period=recent --date=2020-05-01/2020-05-05

      # The real power horse: Acquire data across historical+recent data sets
      wetterdienst dwd readings --station=1048,4411 --parameter=kl --resolution=daily --period=historical,recent --date=1969-01-01/2020-06-11

      # Acquire monthly data for 2020-05
      wetterdienst dwd readings --station=1048,4411 --parameter=kl --resolution=monthly --period=recent,historical --date=2020-05

      # Acquire monthly data from 2017-01 to 2019-12
      wetterdienst dwd readings --station=1048,4411 --parameter=kl --resolution=monthly --period=recent,historical --date=2017-01/2019-12

      # Acquire annual data for 2019
      wetterdienst dwd readings --station=1048,4411 --parameter=kl --resolution=annual --period=recent,historical --date=2019

      # Acquire annual data from 2010 to 2020
      wetterdienst dwd readings --station=1048,4411 --parameter=kl --resolution=annual --period=recent,historical --date=2010/2020

      # Acquire hourly data
      wetterdienst dwd readings --station=1048,4411 --parameter=air_temperature --resolution=hourly --period=recent --date=2020-06-15T12

    Examples using geospatial features:

      # Acquire stations and readings by geoposition, request specific number of nearby stations.
      wetterdienst dwd stations --resolution=daily --parameter=kl --period=recent --lat=49.9195 --lon=8.9671 --num=5
      wetterdienst dwd readings --resolution=daily --parameter=kl --period=recent --lat=49.9195 --lon=8.9671 --num=5 --date=2020-06-30

      # Acquire stations and readings by geoposition, request stations within specific radius.
      wetterdienst dwd stations --resolution=daily --parameter=kl --period=recent --lat=49.9195 --lon=8.9671 --distance=25
      wetterdienst dwd readings --resolution=daily --parameter=kl --period=recent --lat=49.9195 --lon=8.9671 --distance=25 --date=2020-06-30

    Examples using SQL filtering:

      # Find stations by state.
      wetterdienst dwd stations --parameter=kl --resolution=daily --period=recent --sql="SELECT * FROM data WHERE state='Sachsen'"

      # Find stations by name (LIKE query).
      wetterdienst dwd stations --parameter=kl --resolution=daily --period=recent --sql="SELECT * FROM data WHERE lower(station_name) LIKE lower('%dresden%')"

      # Find stations by name (regexp query).
      wetterdienst dwd stations --parameter=kl --resolution=daily --period=recent --sql="SELECT * FROM data WHERE regexp_matches(lower(station_name), lower('.*dresden.*'))"

      # Filter measurements: Display daily climate observation readings where the maximum temperature is below two degrees.
      wetterdienst dwd readings --station=1048,4411 --parameter=kl --resolution=daily --period=recent --sql="SELECT * FROM data WHERE element='temperature_air_max_200' AND value < 2.0;"

    Examples for inquiring metadata:

      # Display list of available parameters (air_temperature, precipitation, pressure, ...)
      wetterdienst dwd about parameters

      # Display list of available resolutions (10_minutes, hourly, daily, ...)
      wetterdienst dwd about resolutions

      # Display list of available periods (historical, recent, now)
      wetterdienst dwd about periods

      # Display coverage/correlation between parameters, resolutions and periods.
      # This can answer questions like ...
      wetterdienst dwd about coverage

      # Tell me all periods and resolutions available for 'air_temperature'.
      wetterdienst dwd about coverage --parameter=air_temperature

      # Tell me all parameters available for 'daily' resolution.
      wetterdienst dwd about coverage --resolution=daily

    Examples for exporting data to databases:

      # Shortcut command for fetching readings from DWD
      alias fetch="wetterdienst dwd readings --station=1048,4411 --parameter=kl --resolution=daily --period=recent"

      # Store readings to DuckDB
      fetch --target="duckdb://database=dwd.duckdb&table=weather"

      # Store readings to InfluxDB
      fetch --target="influxdb://localhost/?database=dwd&table=weather"

      # Store readings to CrateDB
      fetch --target="crate://localhost/?database=dwd&table=weather"

    Run as HTTP service:

      wetterdienst dwd service
      wetterdienst dwd service --listen=0.0.0.0:9999

    """

    appname = f"{__appname__} {__version__}"

    # Read command line options.
    options = normalize_options(docopt(run.__doc__, version=appname))

    # Setup logging.
    debug = options.get("debug")

    log_level = logging.INFO

    if debug:  # pragma: no cover
        log_level = logging.DEBUG

    setup_logging(log_level)

    # Run service.
    if options.service:  # pragma: no cover
        listen_address = options.listen
        log.info(f"Starting {appname}")
        log.info(f"Starting web service on {listen_address}")
        from wetterdienst.service import start_service

        start_service(listen_address)
        return

    # Output domain information.
    if options.about:
        about(options)
        return

    # Sanity checks.
    if (options.readings or options.forecasts) and options.format == "geojson":
        raise KeyError("GeoJSON format only available for stations output")

    # Acquire station list, also used for readings if required.
    # Filtering applied for distance (a.k.a. nearby) and pre-selected stations
    df = get_stations(options)

    if options.stations and df.empty:
        log.error("No data available for given constraints")
        sys.exit(1)

    # Acquire observations.
    if options.readings:
        # Use list of station identifiers.
        if options.station:
            station_ids = read_list(options.station)
        elif options.latitude and options.longitude:
            try:
                station_ids = df.STATION_ID.unique()
            except AttributeError:
                station_ids = df.WMO_ID.unique()
        else:
            raise KeyError(
                "Either --station or --latitude, --longitude required")

        # Funnel all parameters to the workhorse.
        if options.observations:
            readings = DWDObservationData(
                station_ids=station_ids,
                parameters=read_list(options.parameter),
                resolution=options.resolution,
                periods=read_list(options.period),
                humanize_parameters=True,
                tidy_data=options.tidy,
            )
        elif options.forecasts:
            readings = DWDMosmixData(
                station_ids=station_ids,
                parameters=read_list(options.parameter),
                mosmix_type=options.mosmix_type,
                humanize_parameters=True,
                tidy_data=options.tidy,
            )

        # Collect data and merge together.
        try:
            df = readings.all()
        except ValueError as ex:
            log.exception(ex)
            sys.exit(1)

    # Sanity checks.
    if df.empty:
        log.error("No data available")
        sys.exit(1)

    # Filter readings by datetime expression.
    if options.readings and options.date:
        resolution = None
        if options.observations:
            resolution = readings.resolution

        df = df.dwd.filter_by_date(options.date, resolution)

    # Make column names lowercase.
    df = df.dwd.lower()

    # Apply filtering by SQL.
    if options.sql:
        log.info(f"Filtering with SQL: {options.sql}")
        df = df.io.sql(options.sql)

    # Emit to data sink, e.g. write to database.
    if options.target:
        log.info(f"Writing data to target {options.target}")
        df.io.export(options.target)
        return

    # Render to output format.
    try:
        output = df.dwd.format(options.format)
    except KeyError as ex:
        log.error(
            f'{ex}. Output format must be one of "json", "geojson", "csv", "excel".'
        )
        sys.exit(1)

    print(output)