HTTP API for Statistics from Zarr stores?

:waving_hand: My team on the NASA Office of Data Science and Informatics (ODSI) Data Systems Evolution (DSE) program is considering building an HTTP API for statistics generation from Zarr stores and we could use the communities input.

Such an API could be more broadly envisioned as a generalized framework for dimension reduction summaries of multidimensional datasets (thanks to @hrodmn for this description).

Such an API would enable clients and users to do things like zonal statistics and time series generation without having to manage any compute resources.

Would you use a Zarr HTTP API for statistics and how? Why or Why not?

=============================================================

You may be wondering:

  • Are there any existing standards in place we would implement? The OGC EDR API is a data retrieval standard we are considering. But it is not an analytics/aggregation engine. The EDR API is an option, but would but a lot of burden on clients to load and compute on that data in-memory. If another standard exists, even if it’s a new standard, we would like to know about it!
  • Would this just be an API for xarray and isn’t that what xpublish is? xpublish is currently configured per-dataset, not an API which accepts a parameterized an entrypoint URL.
2 Likes

I’ve been working with NOAA SST data and Copernicus Marine Service datasets for personal projects involving coral reef monitoring and oxygen minimum zone visualization. In both cases, fetching and computing statistics required managing the full pipeline locally- ingestion, processing, and aggregation. A parameterized HTTP API for zonal statistics and time series generation would significantly reduce that overhead, especially for users who need quick exploratory analysis without spinning up compute infrastructure. I’d definitely use it, particularly for region-based aggregations over time dimensions

Speak of the devil Matt released datatree support in xpublish this week, and if you squint at the internet the right way, it’s just a really big datatree right?

This will likely need snipe me that I’ll knock up a proof of concept in the next few days, but you would create a dataset provider where each dataset is instead the protocol (http, s3, gcs…) that it supports. Then the rest of the url is treated as the datatree group part of the path, and then whatever plugins you want or create work on top of that.

The APIs are also flexible enough to reshape the path structure if need be, which I’m working on for more conformant OGC support.

1 Like

Did you consider the openEO API? It should be closer to what you are looking for than the OGC EDR as it has processing/statistics built in and is also based on a concept of data cubes. In CDSE it is already used for that purpose. FWIW, It’s also an OGC community standard since some weeks.

2 Likes

The OGC Web Coverage Processing Standard (WCPS) seems to be the closest existing OGC standard to what you are asking for.

This standard defines a protocol-independent language for the extraction, processing, and analysis of multi-dimensional coverages representing sensor, image, or statistics data.

Unfortunately it basically only has one implementation, and it’s not open source: Rasdaman. The standard is tightly coupled to this implementation, and no real ecosystem exists. This is probably why it never caught on, despite theoretically solving a common problem.

(As a meta point, I think this points to the limitations of the “open standard + proprietary implementation” approach.)

OpenEO is probably a much better choice, although it could be a bit overkill for your use case.


The crux question for this type of application is scale. Is an HTTP REST API the right interface? It’s fine for a single timeseries or point extraction. But what if I want to generate zonal statistics for millions of polygons over a petabyte-scale data cube? For that, the OpenEO concept of a batch job is probably a better fit: the user creates a batch job and then comes back to check on the results later.

1 Like

@vythwahh thank you for your use case!

@abkfenris we did (are still?) consider(ing) xpublish an option but at this time I think the datasets having to be pre-configured with a deployed instance of the API is not what we are looking for at the moment.

@m-mohr I have to revisit the openEO API, thanks for the suggestion.

@rabernat I will take a look at WCPS as well. But I agree with your meta point that scale is an important consideration for this design. We will likely choose an implementation which for synchronous HTTP response comes with scale restrictions.

Xpublish doesn’t require datasets to be pre-configured. We really don’t consider that best practice, we just have that as the easy way for folks to get started and explore. We probably haven’t been as explicit as we should at recommending folks don’t use that method in production.

I spent nearly as many hours last weekend in a dry suit as I did at home so I didn’t get to hack up an example, and I’m at an IOOS event this week so maybe I can make it happen sometime later next week.

I’m certainly not an xpublish expert yet but it makes sense that xpublish could be adapted to accept dataset entrypoint URLs as a parameter. Don’t build anything on our account! We are still in an exploratory / design phase so having this information alone is helpful.

I consider XPublish to be a framework for service developers, not a standard itself. In that sense, it’s an implementation detail.

I’d break Aimee’s question into two parts:

  • What is the standard which best maps to this use case? (EDR, WCPS, and OpenEO have all been proposed; there may be others. Or you can always create something new, obligatory XKCD reference, etc.)
  • What existing implementations or frameworks exist to the chosen standard?

The eager vs. job-submission distinction is quite important because job submission introduces state into the backend. AFAIK Xpublish is stateless (or at least all the state lives in the Xarray datasets themselves), so it would be hard to extend it to support OpenEO’s concept of batch jobs.

2 Likes

Hi Aimee. Glad to hear of this thought process underway.

When it comes to statistics from Zarr stores, are you referring to multidimensional stats generation from the selected data itself or are you looking for statistics about the store itself, such as data quality metrics? For EarthScope, we have developed a REST-based system for the latter when it comes to seismic data stores. I don’t know if the ad-hoc parameter standard offers some ideas for what you would like to accomplish.

One thing I am curious about: since Zarr stores sidecar metadata along with the data, does this set the stage for maintaining some level of computed values as well? Something like a .zstats file. Perhaps such that it can provide a first-dimension precomputed digest that means you don’t have to compute on the raw data in all cases.

Replying to myself. Our QA REST service is called MUSTANG, and it is now running in AWS, but it is not ARCO as of yet.

Thank you Rob! Our use case is more the former, stats generated from the data itself.

As I understand it, some pre-computed metrics in sidecar metadata files has come up as a useful feature. But I think our primary use case is user-defined zonal statistics, so a client interface could submit arbitrary coordinate values and get a response back. So we cannot pre-compute the stats unless we always know ahead of time what “views” people will want to have into the data. I do imagine having pre-defined views (states, counties) is a possible scenario, but not one we are currently designing for.

1 Like

A quick and dirty Xpublish plugin for accessing Zarr stores over HTTPS or S3:

class WebDatasetPlugin(xpublish.Plugin):
    name: str = "web-dataset-provider"

    @xpublish.hookimpl
    def get_datasets(self):
        return ["zarr+https", "zarr+s3"]
    
    @xpublish.hookimpl
    def get_datatree(self, dataset_id: str, group: str):
        if dataset_id == "zarr+https":
            ds = xr.open_zarr(f"https://{group}")
            return xr.DataTree(dataset=ds)
        
        if dataset_id == "zarr+s3":
            mapper = fsspec.get_mapper(f"s3://{group}")
            ds = xr.open_zarr(mapper, consolidated=True)
            return xr.DataTree(dataset=ds)

In Xpublish parlance, it treats the format and protocol as the dataset_id and the group is then the rest of the URL.

In use for HTTPS:

Or S3:

I whipped up a quick stats plugin to give a mean and started a request (http://localhost:9005/datasets/zarr+s3/groups/mur-sst/zarr-v1/mean/analysed_sst) without thinking too much about it…

class StatPlugin(xpublish.Plugin):
    name: str = "stat-plugin"

    @xpublish.hookimpl
    def dataset_router(self, deps: xpublish.Dependencies):
        from fastapi import Depends
        from fastapi.responses import JSONResponse
        from fastapi.routing import APIRouter

        router = APIRouter(prefix="", tags=["stat"])

        @router.get("/groups/{group_path:path}/mean/{var}")
        def mean(
            dataset=Depends(deps.dataset),
            var: str,
        ) -> JSONResponse:
            """Returns the mean of a variable in the dataset."""
            means = dataset[var].mean()
            return JSONResponse(means.to_dict())

        return router

Xpublish has happily streamed over 200 GB while calculating the mean before I decided it was time for bed and killed it.

Dove into async jobs before realising you had mentioned sync HTTP responses

So some sort of async job submission type API is probably gonna be the way. For existing standards like OpenEO or OGC Processes (more REST-ful replacement for WCPS), the question becomes 'how complex of a query are you looking to support?`

Both of them have a form of job submission then digital thumb twiddling while waiting for a result.

OGC Processes is on the simpler side with one-off processes, and is more of a standard for the URL patterns than how you tell it what data to work on. So in some ways it would be easier to adapt to querying any Zarr store by including an input like zarr_url for all processes. pygeoapi has some support for the standard.

OpenEO allows chaining of processes together, running them synchronously when small enough, as batch jobs when they are bigger, or connecting them to a service like WMS for visualization (a server doesn’t have to support all of them, and there is a built in way to tell users to ask for less). It has from what I can tell a better set of existing API clients since it’s a more fully defined standard. From what I’ve seen it’s generally more focused on producing raster results, but it can get to JSON timeseries as well. I think that means it will take a little more creativity to figure out how to fit any HTTP accessible Zarr URL into how you specify a data cube, though I think collection_property could be molded into the right shape by doing a similar virtual collections/dataset trick like I did in Xpublish.

I’d lean towards the openEO API. It nicely is already structured to scale from sync responses > async jobs > web services, auth, and has an ‘you’ve asked for too much’ error with a spread of clients that understand that. WCPS and it’s successor OGC Processing are both for async jobs.

openEO API profiles

openEO API profiles

It is structured around named STAC items, but I think there are two ways to solve that.

  • Virtual collections using/abusing the collection_property could be molded into the right shape like I did in Xpublish.
  • For more flexibility, add some state and POST/PUT datasets as STAC collections on demand. You could probably accept some form of xr.open_dataset() kwargs. This also could give the API a easier ID to cache responses against, and potentially an easier entry point than ‘learn how to get creative with collection_property’. Though getting creative with collection_property could also be having collection_property consume open_dataset kwargs as well.

Maybe the move would be to support both, but encourage regular uses to register (give them higher limits), and to create their commonly used collections.


I now really want to build an openEO pluggable ecosystem on top of Xpublish. Anyone want to contract my team to do so since I really can’t justify that with what we currently have in our pipeline?

1 Like

For that, the OpenEO concept of a batch job is probably a better fit: the user creates a batch job and then comes back to check on the results later.

We will likely choose an implementation which for synchronous HTTP response comes with scale restrictions.

openEO has synchronous requests and batch jobs which can be run with the same workflow definition. In general openEO can be lightweight (see client-side processing) or heavyweight (see the CDSE instance for example), in the end it’s just API and process specifcations and you can choose how to implement them. The thing that might be more critical is potentially the custom “language” that you have to adapt to. It’s somewhat hidden through the programming libraries in Python, R and JS, but it still comes with a learning curve. Focus was raster and that still shines through, vector operations are still on the weak side and currently adding DGGS-native processing. All, feel free to get in touch (m.mohr@moregeo.it) if you have more questions, I don’t regularly monitor this forum.

It is structured around named STAC items

It’s a key component, but not required. The interface is extensible, in principle you can just add a custom load_zarr function or so and bypass the pre-defined load_collection/load_stac processes. The results can then be anything, don’t be fooled by the current implementations and its limits.