Skip to content

Commit

Permalink
Merge branch 'main' into docs/getting-started-nb
Browse files Browse the repository at this point in the history
  • Loading branch information
zacharyDez authored Dec 21, 2023
2 parents 821d17b + 80a7255 commit 9019ca0
Show file tree
Hide file tree
Showing 22 changed files with 714 additions and 383 deletions.
84 changes: 65 additions & 19 deletions .github/workflows/tests/test_raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,61 @@ def test_raster_api():
def test_mosaic_api():
"""test mosaic."""
query = {"collections": ["noaa-emergency-response"], "filter-lang": "cql-json"}
resp = httpx.post(f"{raster_endpoint}/mosaic/register", json=query)
resp = httpx.post(f"{raster_endpoint}/searches/register", json=query)
assert resp.headers["content-type"] == "application/json"
assert resp.status_code == 200
assert resp.json()["searchid"]
assert resp.json()["id"]
assert resp.json()["links"]

searchid = resp.json()["searchid"]
searchid = resp.json()["id"]

resp = httpx.get(f"{raster_endpoint}/mosaic/{searchid}/-85.6358,36.1624/assets")
resp = httpx.get(f"{raster_endpoint}/searches/{searchid}/-85.6358,36.1624/assets")
assert resp.status_code == 200
assert len(resp.json()) == 1
assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"]
assert resp.json()[0]["id"] == "20200307aC0853900w361030"

resp = httpx.get(f"{raster_endpoint}/mosaic/{searchid}/tiles/15/8589/12849/assets")
resp = httpx.get(
f"{raster_endpoint}/searches/{searchid}/tiles/15/8589/12849/assets"
)
assert resp.status_code == 200
assert len(resp.json()) == 1
assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"]
assert resp.json()[0]["id"] == "20200307aC0853900w361030"

z, x, y = 15, 8589, 12849
resp = httpx.get(
f"{raster_endpoint}/mosaic/{searchid}/tiles/{z}/{x}/{y}",
f"{raster_endpoint}/searches/{searchid}/tiles/{z}/{x}/{y}",
params={"assets": "cog"},
headers={"Accept-Encoding": "br, gzip"},
timeout=10.0,
)
assert resp.status_code == 200
assert resp.headers["content-type"] == "image/jpeg"
assert "content-encoding" not in resp.headers


def test_mosaic_collection_api():
"""test mosaic collection."""
resp = httpx.get(
f"{raster_endpoint}/collections/noaa-emergency-response/-85.6358,36.1624/assets"
)
assert resp.status_code == 200
assert len(resp.json()) == 1
assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"]
assert resp.json()[0]["id"] == "20200307aC0853900w361030"

resp = httpx.get(
f"{raster_endpoint}/collections/noaa-emergency-response/tiles/15/8589/12849/assets"
)
assert resp.status_code == 200
assert len(resp.json()) == 1
assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"]
assert resp.json()[0]["id"] == "20200307aC0853900w361030"

z, x, y = 15, 8589, 12849
resp = httpx.get(
f"{raster_endpoint}/collections/noaa-emergency-response/tiles/{z}/{x}/{y}",
params={"assets": "cog"},
headers={"Accept-Encoding": "br, gzip"},
timeout=10.0,
Expand Down Expand Up @@ -102,11 +134,11 @@ def test_mosaic_search():
},
]
for search in searches:
resp = httpx.post(f"{raster_endpoint}/mosaic/register", json=search)
resp = httpx.post(f"{raster_endpoint}/searches/register", json=search)
assert resp.status_code == 200
assert resp.json()["searchid"]
assert resp.json()["id"]

resp = httpx.get(f"{raster_endpoint}/mosaic/list")
resp = httpx.get(f"{raster_endpoint}/searches/list")
assert resp.headers["content-type"] == "application/json"
assert resp.status_code == 200
assert (
Expand All @@ -122,9 +154,11 @@ def test_mosaic_search():
assert len(links) == 2
assert links[0]["rel"] == "self"
assert links[1]["rel"] == "next"
assert links[1]["href"] == f"{raster_endpoint}/mosaic/list?limit=10&offset=10"
assert links[1]["href"] == f"{raster_endpoint}/searches/list?limit=10&offset=10"

resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"limit": 1, "offset": 1})
resp = httpx.get(
f"{raster_endpoint}/searches/list", params={"limit": 1, "offset": 1}
)
assert resp.status_code == 200
assert resp.json()["context"]["matched"] > 10
assert resp.json()["context"]["limit"] == 1
Expand All @@ -133,33 +167,33 @@ def test_mosaic_search():
links = resp.json()["links"]
assert len(links) == 3
assert links[0]["rel"] == "self"
assert links[0]["href"] == f"{raster_endpoint}/mosaic/list?limit=1&offset=1"
assert links[0]["href"] == f"{raster_endpoint}/searches/list?limit=1&offset=1"
assert links[1]["rel"] == "next"
assert links[1]["href"] == f"{raster_endpoint}/mosaic/list?limit=1&offset=2"
assert links[1]["href"] == f"{raster_endpoint}/searches/list?limit=1&offset=2"
assert links[2]["rel"] == "prev"
assert links[2]["href"] == f"{raster_endpoint}/mosaic/list?limit=1&offset=0"
assert links[2]["href"] == f"{raster_endpoint}/searches/list?limit=1&offset=0"

# Filter on mosaic metadata
resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"owner": "vincent"})
resp = httpx.get(f"{raster_endpoint}/searches/list", params={"owner": "vincent"})
assert resp.status_code == 200
assert resp.json()["context"]["matched"] == 7
assert resp.json()["context"]["limit"] == 10
assert resp.json()["context"]["returned"] == 7

# sortBy
resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"sortby": "lastused"})
resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "lastused"})
assert resp.status_code == 200

resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"sortby": "usecount"})
resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "usecount"})
assert resp.status_code == 200

resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"sortby": "-owner"})
resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "-owner"})
assert resp.status_code == 200
assert (
"owner" not in resp.json()["searches"][0]["search"]["metadata"]
) # some mosaic don't have owners

resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"sortby": "owner"})
resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "owner"})
assert resp.status_code == 200
assert "owner" in resp.json()["searches"][0]["search"]["metadata"]

Expand Down Expand Up @@ -201,3 +235,15 @@ def test_collections():
collections = resp.json()
assert len(collections) == 1
assert collections[0]["id"] == "noaa-emergency-response"


def test_cog_endpoints():
"""test /cog endpoints"""
resp = httpx.get(
f"{raster_endpoint}/cog/info",
params={
"url": "https://noaa-eri-pds.s3.us-east-1.amazonaws.com/2020_Nashville_Tornado/20200307a_RGB/20200307aC0854500w361030n.tif",
},
)
assert resp.status_code == 200
assert resp.headers["content-type"] == "application/json"
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,8 @@ node_modules/

# Demo files to ignore
demo/cmip6/CMIP6_daily_*stac_items.ndjson


# browser compiled code and default config
infrastructure/aws/stac-browser
infrastructure/aws/browser_config.js
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@

- **OGC Features and Vector Tiles** API built on top of [https://github.com/developmentseed/tipg](https://github.com/developmentseed/tipg)

- **A STAC Catalog browsing UI** based on the radiant earth browser : [https://github.com/radiantearth/stac-browser](https://github.com/radiantearth/stac-browser)

---

## 🌍 eoAPI: An Open-Source Community Project
Expand All @@ -57,22 +59,19 @@ Then you can start exploring your dataset with:

- the STAC Metadata service [http://localhost:8081](http://localhost:8081)
- the Raster service [http://localhost:8082](http://localhost:8082)
- the browser UI [http://localhost:8085](http://localhost:8085)

If you've added a vector dataset to the `public` schema in the Postgres database, they will be available through the **Vector** service at [http://localhost:8083](http://localhost:8083).

## Deployment
## Deployment with standard runtimes

This repository has current runtimes that are consistently updated with new functionality.

The services can be deployed locally via docker with `docker compose up`.

Two Infrastructure as Code (IaC) repositories are available:
- [eoapi-cdk](https://github.com/developmentseed/eoapi-cdk): A set of AWS CDK constructs to deploy eoAPI services
- [eoapi-k8s](https://github.com/developmentseed/eoapi-k8s): IaC and Helm charts for deploying eoAPI services on AWS and GCP
### Local deployment

Finally, [eoapi-template](https://github.com/developmentseed/eoapi-template) is an AWS CDK app that shows how to configure the eoapi-cdk constructs.
The services can be deployed altogether locally with `docker compose up`.

Alternatively, you may install the libraries locally:
Alternatively, you may install the libraries and launch the applications manually :

<details>

Expand Down Expand Up @@ -109,7 +108,19 @@ Note: python libraries might have incompatible dependencies, which you can resol

</details>

## Custom runtimes
### Deployment on the cloud

#### Kubernetes

[eoapi-k8s](https://github.com/developmentseed/eoapi-k8s) contains IaC and Helm charts for deploying eoAPI services on AWS and GCP.

#### AWS CDK

[eoapi-cdk](https://github.com/developmentseed/eoapi-cdk) defines a set of AWS CDK constructs that can be used to deploy eoAPI services on AWS. This repository itself makes use of these in `infrastructure/aws`. An official example usage of these constructs can be found at [eoapi-template](https://github.com/developmentseed/eoapi-template).



## Deployment with custom runtimes

The eoAPI repository hosts customized versions of each base service which can work in parallel or in combination with each other.

Expand Down
18 changes: 18 additions & 0 deletions docker-compose.custom.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
version: '3'

services:
stac-browser:
profiles:
- gunicorn
build:
context: dockerfiles
dockerfile: Dockerfile.browser
ports:
- "${MY_DOCKER_IP:-127.0.0.1}:8085:8085"
depends_on:
- stac
- database
- raster
stac:
container_name: eoapi.stac
profiles:
Expand Down Expand Up @@ -34,6 +46,9 @@ services:
# https://github.com/developmentseed/eoAPI/issues/16
# - TITILER_ENDPOINT=raster
- TITILER_ENDPOINT=http://127.0.0.1:8082
# PgSTAC extensions
# - EOAPI_STAC_EXTENSIONS=["filter", "query", "sort", "fields", "pagination", "context", "transaction"]
# - EOAPI_STAC_CORS_METHODS='GET,POST,PUT,OPTIONS'
depends_on:
- database
- raster
Expand Down Expand Up @@ -159,6 +174,9 @@ services:
# https://github.com/developmentseed/eoAPI/issues/16
# - TITILER_ENDPOINT=raster
- TITILER_ENDPOINT=http://127.0.0.1:8082
# PgSTAC extensions
# - EOAPI_STAC_EXTENSIONS=["filter", "query", "sort", "fields", "pagination", "context", "transaction"]
# - EOAPI_STAC_CORS_METHODS='GET,POST,PUT,OPTIONS'
depends_on:
- database
- raster-uvicorn
Expand Down
17 changes: 15 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
version: '3'

services:

# change to official image when available https://github.com/radiantearth/stac-browser/pull/386
stac-browser:
build:
context: dockerfiles
dockerfile: Dockerfile.browser
ports:
- "${MY_DOCKER_IP:-127.0.0.1}:8085:8085"
depends_on:
- stac-fastapi
- titiler-pgstac
- database

stac-fastapi:
# Note:
# the official ghcr.io/stac-utils/stac-fastapi-pgstac image uses python 3.8 and uvicorn
Expand Down Expand Up @@ -43,7 +56,7 @@ services:
# At the time of writing, rasterio and psycopg wheels are not available for arm64 arch
# so we force the image to be built with linux/amd64
platform: linux/amd64
image: ghcr.io/stac-utils/titiler-pgstac:0.8.0
image: ghcr.io/stac-utils/titiler-pgstac:1.0.0a3
ports:
- "${MY_DOCKER_IP:-127.0.0.1}:8082:8082"
environment:
Expand Down Expand Up @@ -89,7 +102,7 @@ services:
- ./dockerfiles/scripts:/tmp/scripts

tipg:
image: ghcr.io/developmentseed/tipg:0.4.4
image: ghcr.io/developmentseed/tipg:0.5.0
ports:
- "${MY_DOCKER_IP:-127.0.0.1}:8083:8083"
environment:
Expand Down
34 changes: 34 additions & 0 deletions dockerfiles/Dockerfile.browser
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright Radiant Earth Foundation

FROM node:lts-alpine3.18 AS build-step
ARG DYNAMIC_CONFIG=true

WORKDIR /app

RUN apk add --no-cache git
RUN git clone https://github.com/radiantearth/stac-browser.git .
# remove the default config.js
RUN rm config.js
RUN npm install
# replace the default config.js with our config file
COPY ./browser_config.js ./config.js
RUN \[ "${DYNAMIC_CONFIG}" == "true" \] && sed -i 's/<!-- <script defer="defer" src=".\/config.js"><\/script> -->/<script defer="defer" src=".\/config.js"><\/script>/g' public/index.html
RUN npm run build


FROM nginx:1-alpine-slim

COPY --from=build-step /app/dist /usr/share/nginx/html
COPY --from=build-step /app/config.schema.json /etc/nginx/conf.d/config.schema.json

# change default port to 8085
RUN apk add jq pcre-tools && \
sed -i 's/\s*listen\s*80;/ listen 8085;/' /etc/nginx/conf.d/default.conf && \
sed -i 's/\s*location \/ {/ location \/ {\n try_files $uri $uri\/ \/index.html;/' /etc/nginx/conf.d/default.conf

EXPOSE 8085

STOPSIGNAL SIGTERM

# override entrypoint, which calls nginx-entrypoint underneath
COPY --from=build-step /app/docker/docker-entrypoint.sh ./docker-entrypoint.d/40-stac-browser-entrypoint.sh
39 changes: 39 additions & 0 deletions dockerfiles/browser_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module.exports = {
catalogUrl: "http://0.0.0.0:8081",
catalogTitle: "eoAPI STAC Browser",
allowExternalAccess: true, // Must be true if catalogUrl is not given
allowedDomains: [],
detectLocaleFromBrowser: true,
storeLocale: true,
locale: "en",
fallbackLocale: "en",
supportedLocales: [
"de",
"es",
"en",
"fr",
"it",
"ro"
],
apiCatalogPriority: null,
useTileLayerAsFallback: true,
displayGeoTiffByDefault: false,
buildTileUrlTemplate: ({href, asset}) => "http://0.0.0.0:8082/cog/tiles/{z}/{x}/{y}@2x?url=" + encodeURIComponent(asset.href.startsWith("/vsi") ? asset.href : href),
stacProxyUrl: null,
pathPrefix: "/",
historyMode: "history",
cardViewMode: "cards",
cardViewSort: "asc",
showThumbnailsAsAssets: false,
stacLint: true,
geoTiffResolution: 128,
redirectLegacyUrls: false,
itemsPerPage: 12,
defaultThumbnailSize: null,
maxPreviewsOnMap: 50,
crossOriginMedia: null,
requestHeaders: {},
requestQueryParameters: {},
preprocessSTAC: null,
authConfig: null
};
7 changes: 6 additions & 1 deletion docs/src/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,9 @@ By default, the API will look for tables in the `public` schema of the database.
<img alt="eoapi.vector landing page" src="https://github.com/developmentseed/eoAPI/assets/10407788/b2a8a8d4-d3a1-464a-8b1a-166499ee4abd">
</p>

Code: [/runtime/eoapi/vector](https://github.com/developmentseed/eoAPI/tree/main/runtime/eoapi/vector)
Code: [/runtime/eoapi/vector](https://github.com/developmentseed/eoAPI/tree/main/runtime/eoapi/vector)

--
# STAC browser

The custom browser configuration can be modified using the config located in [/dockerfiles/browser_config.js](https://github.com/developmentseed/eoAPI/tree/main/dockerfiles/browser_config.js). For more information about available configurations, see the [Radiant Earth repository](https://github.com/radiantearth/stac-browser).
2 changes: 1 addition & 1 deletion docs/src/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ hide:
## Via [eoapi-cdk](https://github.com/developmentseed/eoapi-cdk)


[eoapi-cdk](https://github.com/developmentseed/eoapi-cdk) is a set of AWS CDK constructs to deploy eoAPI services.
[eoapi-cdk](https://github.com/developmentseed/eoapi-cdk) is a set of AWS CDK constructs that can be used to easily deploy eoAPI services on AWS with the CDK.

[eoapi-template](https://github.com/developmentseed/eoapi-template) is an AWS CDK app that shows how to configure the [eoapi-cdk](https://github.com/developmentseed/eoapi-cdk) constructs.

Expand Down
Loading

0 comments on commit 9019ca0

Please sign in to comment.