diff --git a/docker-compose.yml b/docker-compose.yml
index 4884cc4..0cb9980 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,6 +1,6 @@
services:
database:
- image: ghcr.io/stac-utils/pgstac:v0.9.8
+ image: ghcr.io/stac-utils/pgstac:v0.9.10
environment:
- POSTGRES_USER=username
- POSTGRES_PASSWORD=password
@@ -56,11 +56,11 @@ services:
interval: 10s
timeout: 5s
retries: 3
- start_period: 10s
+ start_period: 120s
stac-fastapi:
- image: ghcr.io/stac-utils/stac-fastapi-pgstac:6.0.2
+ image: ghcr.io/stac-utils/stac-fastapi-pgstac:6.2.2
ports:
- 8081:8081
environment:
@@ -80,21 +80,19 @@ services:
condition: service_healthy
command:
bash -c "uvicorn stac_fastapi.pgstac.app:app --host 0.0.0.0 --port 8081"
- volumes:
- - ./dockerfiles/scripts:/tmp/scripts
titiler-pgstac:
platform: linux/amd64
- image: ghcr.io/stac-utils/titiler-pgstac:1.9.0
+ image: ghcr.io/stac-utils/titiler-pgstac:3.0.0
ports:
- 8082:8082
environment:
# Postgres connection
- - POSTGRES_USER=username
- - POSTGRES_PASS=password
- - POSTGRES_DBNAME=postgis
- - POSTGRES_HOST=database
- - POSTGRES_PORT=5432
+ - PGUSER=username
+ - PGPASSWORD=password
+ - PGDATABASE=postgis
+ - PGHOST=database
+ - PGPORT=5432
- DB_MIN_CONN_SIZE=1
- DB_MAX_CONN_SIZE=10
# - DB_MAX_QUERIES=10
@@ -112,9 +110,11 @@ services:
# TiTiler Config
- MOSAIC_CONCURRENCY=1
- TITILER_PGSTAC_API_ENABLE_EXTERNAL_DATASET_ENDPOINTS=True
- # AWS S3 endpoint config
+ # AWS S3 endpoint config — the workshop's s3:// assets (glad collection)
+ # are in public buckets; read unsigned unless real credentials are provided
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
+ - AWS_NO_SIGN_REQUEST=${AWS_NO_SIGN_REQUEST:-YES}
depends_on:
database:
condition: service_started
@@ -124,7 +124,7 @@ services:
bash -c "uvicorn titiler.pgstac.main:app --host 0.0.0.0 --port 8082"
tipg:
- image: ghcr.io/developmentseed/tipg:1.1.2
+ image: ghcr.io/developmentseed/tipg:1.4.0
ports:
- 8083:8083
environment:
diff --git a/docs/02-database.ipynb b/docs/02-database.ipynb
index 6f22ec1..0b82a2f 100644
--- a/docs/02-database.ipynb
+++ b/docs/02-database.ipynb
@@ -325,10 +325,20 @@
"source": [
"from IPython.display import IFrame\n",
"\n",
- "stac_api_endpoint = os.getenv(\"STAC_API_ENDPOINT\").replace(\"stac-fastapi\", \"localhost\")\n",
+ "# Use the stack's own STAC Browser when deployed (its catalog is already this\n",
+ "# STAC API); otherwise fall back to the public STAC Browser in external mode.\n",
+ "stac_browser_endpoint = os.getenv(\"STAC_BROWSER_ENDPOINT\")\n",
+ "\n",
+ "if stac_browser_endpoint:\n",
+ " browser_url = f\"{stac_browser_endpoint}/#/collections/{my_collection.id}\"\n",
+ "else:\n",
+ " browser_stac_url = os.getenv(\"STAC_API_ENDPOINT\").replace(\n",
+ " \"stac-auth-proxy:8000\", \"localhost:8084\"\n",
+ " )\n",
+ " browser_url = f\"https://radiantearth.github.io/stac-browser/#/external/{browser_stac_url}/collections/{my_collection.id}\"\n",
"\n",
"IFrame(\n",
- " f\"https://radiantearth.github.io/stac-browser/#/external/{stac_api_endpoint}/collections/{my_collection.id}\",\n",
+ " browser_url,\n",
" 1200,\n",
" 800,\n",
")"
@@ -467,7 +477,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.12.11"
+ "version": "3.12.13"
}
},
"nbformat": 4,
diff --git a/docs/03-stac_fastapi_pgstac.ipynb b/docs/03-stac_fastapi_pgstac.ipynb
index 7880724..53eb2b7 100644
--- a/docs/03-stac_fastapi_pgstac.ipynb
+++ b/docs/03-stac_fastapi_pgstac.ipynb
@@ -14,7 +14,6 @@
]
},
{
- "attachments": {},
"cell_type": "markdown",
"id": "c8ec4c5b-1156-4608-8ab9-b2cdcbcc47e1",
"metadata": {},
@@ -49,7 +48,7 @@
"### 3.1.2 Authentication\n",
"stac-fastapi-pgstac does not contain any authentication mechanism out-of-the-box, meaning your STAC API will be accessible to anyone if it is deployed to a public web address. If you want to make your STAC API accessible only with a username/password or token, check out the [FastAPI docs](https://fastapi.tiangolo.com/tutorial/security) for examples of how to add them to the application in a custom runtime.\n",
"\n",
- "There is a new project called [stac-auth-proxy](https://github.com/developmentseed/stac-auth-proxy) that can provide fine-grained access controls to a STAC API by adding a proxy layer between users and the actual STAC API.\n",
+ "There is a new project called [stac-auth-proxy](https://github.com/developmentseed/stac-auth-proxy) that can provide fine-grained access controls to a STAC API by adding a proxy layer between users and the actual STAC API. In this workshop stack, the STAC API is exposed through stac-auth-proxy at `http://localhost:8084`.\n",
"\n",
"### 3.1.3 STAC API interface\n",
"Once your STAC API is up and running, its capabilities will be described in the `/conformance` endpoint response:"
@@ -196,7 +195,9 @@
"source": [
"from IPython.display import IFrame\n",
"\n",
- "local_stac_api_endpoint = stac_api_endpoint.replace(\"stac-fastapi\", \"localhost\")\n",
+ "local_stac_api_endpoint = os.getenv(\n",
+ " \"STAC_API_BROWSER_URL\"\n",
+ ") or stac_api_endpoint.replace(\"stac-auth-proxy:8000\", \"localhost:8084\")\n",
"api_docs = (\n",
" f\"{local_stac_api_endpoint}/api.html#/default/Get_Collections_collections_get\"\n",
")\n",
@@ -544,7 +545,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.12.11"
+ "version": "3.12.13"
}
},
"nbformat": 4,
diff --git a/docs/04-titiler_pgstac.ipynb b/docs/04-titiler_pgstac.ipynb
index 95a9bb9..3cd9921 100644
--- a/docs/04-titiler_pgstac.ipynb
+++ b/docs/04-titiler_pgstac.ipynb
@@ -70,10 +70,13 @@
"\n",
"from IPython.display import IFrame, Image\n",
"\n",
- "titiler_pgstac_endpoint = os.getenv(\"TITILER_PGSTAC_API_ENDPOINT\").replace(\n",
- " \"titiler-pgstac\", \"localhost\"\n",
- ")\n",
- "api_docs = f\"{titiler_pgstac_endpoint}/api.html\"\n",
+ "titiler_pgstac_endpoint = os.getenv(\"TITILER_PGSTAC_API_ENDPOINT\")\n",
+ "# browser-facing URL for the IFrame/map cells (the user's browser can't reach\n",
+ "# the server-side endpoint above when running on Kubernetes or docker-compose)\n",
+ "titiler_browser_endpoint = os.getenv(\n",
+ " \"TITILER_BROWSER_URL\"\n",
+ ") or titiler_pgstac_endpoint.replace(\"titiler-pgstac\", \"localhost\")\n",
+ "api_docs = f\"{titiler_browser_endpoint}/api.html\"\n",
"print(api_docs)\n",
"\n",
"IFrame(\n",
@@ -152,7 +155,7 @@
" - Leaflet\n",
" - Mapbox\n",
"\n",
- "For now you can take a shortcut to view the map directly in this notebook using the `/map` endpoint which will generate an HTML file with a Leaflet map that can be rendered directly in the notebook.\n",
+ "For now you can take a shortcut to view the map directly in this notebook using the `/map.html` endpoint which will generate an HTML file with a Leaflet map that can be rendered directly in the notebook.\n",
"\n",
"
\n",
"It may take a while to render tiles for the full view because the titiler-pgstac container is downloading data from S3 in order to render the images - zoom in to have a better browsing experience. This performance can be improved when deploying your own eoAPI through careful preparation of data and sourcing of hardware resources, the demo runs on a very small server.\n",
@@ -167,7 +170,7 @@
"outputs": [],
"source": [
"IFrame(\n",
- " f\"{titiler_pgstac_endpoint}/collections/{collection_id}/WebMercatorQuad/map?{urlencode(params, doseq=True)}\",\n",
+ " f\"{titiler_browser_endpoint}/collections/{collection_id}/WebMercatorQuad/map.html?{urlencode(params, doseq=True)}\",\n",
" 1200,\n",
" 800,\n",
")"
@@ -180,8 +183,8 @@
"source": [
"### How does it work?\n",
"\n",
- "- titiler-pgstac is running as a Lambda (serverless) function in AWS that started up when you made the request for the `/map` endpoint.\n",
- "- The `/map` endpoint returns an HTML file that is pre-populated with some map code that includes the layer that you specified with the request parameters\n",
+ "- titiler-pgstac is running as a Lambda (serverless) function in AWS that started up when you made the request for the `/map.html` endpoint.\n",
+ "- The `/map.html` endpoint returns an HTML file that is pre-populated with some map code that includes the layer that you specified with the request parameters\n",
"- As you browse the map, the map is sending XYZ tile requests to titiler-pgstac function in AWS\n",
"- Each request contains the information titiler-pgstac needs to search for items in the pgstac database and how to construct an image from the items' assets\n",
" - `collection_id`: by specifying the collection ID in the request path you are instructing titiler-pgstac to search for items from a specific STAC collection. Unless otherwise specified, pgstac will retrieve the STAC items in descending order by datetime and it will stop returning results when a tile's geometry is completely covered.\n",
@@ -247,7 +250,7 @@
"source": [
"The response comes back with an `id` which uniquely identifies this search and a handful of useful links associated with our newly registered search.\n",
"\n",
- "Now you can browse the results of this search with the `/map` endpoint like you did earlier."
+ "Now you can browse the results of this search with the `/map.html` endpoint like you did earlier."
]
},
{
@@ -267,7 +270,7 @@
")\n",
"\n",
"IFrame(\n",
- " f\"{titiler_pgstac_endpoint}/searches/{search_id}/WebMercatorQuad/map?{urlencode(params, doseq=True)}\",\n",
+ " f\"{titiler_browser_endpoint}/searches/{search_id}/WebMercatorQuad/map.html?{urlencode(params, doseq=True)}\",\n",
" 1200,\n",
" 800,\n",
")"
@@ -303,6 +306,8 @@
"outputs": [],
"source": [
"params = (\n",
+ " (\"assets\", \"nir\"),\n",
+ " (\"assets\", \"red\"),\n",
" (\"asset_as_band\", \"True\"),\n",
" (\"expression\", \"(nir - red) / (nir + red)\"),\n",
" (\"colormap_name\", \"viridis\"),\n",
@@ -310,7 +315,7 @@
")\n",
"\n",
"IFrame(\n",
- " f\"{titiler_pgstac_endpoint}/searches/{search_id}/WebMercatorQuad/map?{urlencode(params, doseq=True)}\",\n",
+ " f\"{titiler_browser_endpoint}/searches/{search_id}/WebMercatorQuad/map.html?{urlencode(params, doseq=True)}\",\n",
" 1200,\n",
" 800,\n",
")"
@@ -366,6 +371,7 @@
" params={\n",
" \"url\": cog_href,\n",
" },\n",
+ " timeout=None,\n",
")\n",
"\n",
"print(json.dumps(cog_info_request.json(), indent=2))"
@@ -394,6 +400,7 @@
" \"url\": cog_href,\n",
" \"maxsize\": 2048,\n",
" },\n",
+ " timeout=None,\n",
")\n",
"\n",
"Image(preview_request.content)"
@@ -506,7 +513,7 @@
"outputs": [],
"source": [
"map_request = httpx.get(\n",
- " f\"{titiler_pgstac_endpoint}/external/WebMercatorQuad/map\",\n",
+ " f\"{titiler_pgstac_endpoint}/external/WebMercatorQuad/map.html\",\n",
" params={\n",
" \"url\": cog_href,\n",
" \"maxsize\": 2048,\n",
@@ -517,7 +524,7 @@
"\n",
"\n",
"IFrame(\n",
- " map_request.url,\n",
+ " str(map_request.url).replace(titiler_pgstac_endpoint, titiler_browser_endpoint),\n",
" 1200,\n",
" 800,\n",
")"
@@ -541,7 +548,7 @@
"outputs": [],
"source": [
"map_request = httpx.get(\n",
- " f\"{titiler_pgstac_endpoint}/collections/glad-global-forest-change-1.11/WebMercatorQuad/map\",\n",
+ " f\"{titiler_pgstac_endpoint}/collections/glad-global-forest-change-1.11/WebMercatorQuad/map.html\",\n",
" params={\n",
" \"assets\": \"lossyear\",\n",
" \"colormap\": json.dumps({i: rgb for i, rgb in colormap.items()}),\n",
@@ -551,7 +558,7 @@
"\n",
"\n",
"IFrame(\n",
- " map_request.url,\n",
+ " str(map_request.url).replace(titiler_pgstac_endpoint, titiler_browser_endpoint),\n",
" 1200,\n",
" 800,\n",
")"
diff --git a/docs/05-tipg.ipynb b/docs/05-tipg.ipynb
index 800f0e8..b3b9b90 100644
--- a/docs/05-tipg.ipynb
+++ b/docs/05-tipg.ipynb
@@ -60,6 +60,11 @@
"import httpx\n",
"\n",
"tipg_endpoint = os.getenv(\"TIPG_API_ENDPOINT\")\n",
+ "# browser-facing URL for the IFrame/viewer cells (the user's browser can't\n",
+ "# reach the server-side endpoint above when running on Kubernetes)\n",
+ "tipg_browser_endpoint = os.getenv(\"TIPG_BROWSER_URL\") or tipg_endpoint.replace(\n",
+ " \"tipg\", \"localhost\"\n",
+ ")\n",
"\n",
"collections_request = httpx.get(f\"{tipg_endpoint}/collections\")\n",
"\n",
@@ -76,9 +81,9 @@
"- `/collections/{collection_id}/items`: where features can be accessed\n",
"- `/collections/{collection_id}/tiles`: list of tile matrix set IDs that are available for tile requests\n",
"- `/collections/{collection_id}/tiles/{tileMatrixSetId}`: returns a tilejson for a vector tile layer\n",
- "- `/collections/{collection_id}/tiles/{tileMatrixSetId}/viewer`: interactive map of the collection\n",
+ "- `/collections/{collection_id}/tiles/{tileMatrixSetId}/map.html`: interactive map of the collection\n",
"\n",
- "The `/items`, `/tiles/{tileMatrixSetId}`, and `/tiles/{tileMatrixSetId}/viewer` endpoints will all accept field filters in the form of `{queryable}={value}` where `queryable` is one of the fields listed in the `/queryables` response for that collection."
+ "The `/items`, `/tiles/{tileMatrixSetId}`, and `/tiles/{tileMatrixSetId}/map.html` endpoints will all accept field filters in the form of `{queryable}={value}` where `queryable` is one of the fields listed in the `/queryables` response for that collection."
]
},
{
@@ -261,7 +266,7 @@
" },\n",
")\n",
"\n",
- "local_url = str(bbox_filtered_request.url).replace(\"tipg\", \"localhost\")\n",
+ "local_url = str(bbox_filtered_request.url).replace(tipg_endpoint, tipg_browser_endpoint)\n",
"\n",
"IFrame(\n",
" local_url,\n",
@@ -285,7 +290,7 @@
"metadata": {},
"outputs": [],
"source": [
- "local_tipg_endpoint = tipg_endpoint.replace(\"tipg\", \"localhost\")\n",
+ "local_tipg_endpoint = tipg_browser_endpoint\n",
"IFrame(\n",
" f\"{local_tipg_endpoint}/api.html#OGC Features API/items_collections__collectionId__items_get\",\n",
" width=1200,\n",
@@ -374,11 +379,11 @@
"outputs": [],
"source": [
"viewer_request = httpx.get(\n",
- " f\"{tipg_endpoint}/collections/{collection_id}/tiles/WebMercatorQuad/viewer\",\n",
+ " f\"{tipg_endpoint}/collections/{collection_id}/tiles/WebMercatorQuad/map.html\",\n",
")\n",
"\n",
"IFrame(\n",
- " str(viewer_request.url).replace(\"tipg\", \"localhost\"),\n",
+ " str(viewer_request.url).replace(tipg_endpoint, tipg_browser_endpoint),\n",
" width=1200,\n",
" height=800,\n",
")"
@@ -400,14 +405,14 @@
"outputs": [],
"source": [
"filtered_viewer_request = httpx.get(\n",
- " f\"{tipg_endpoint}/collections/{collection_id}/tiles/WebMercatorQuad/viewer\",\n",
+ " f\"{tipg_endpoint}/collections/{collection_id}/tiles/WebMercatorQuad/map.html\",\n",
" params={\n",
" \"na_l2name\": \"MEDITERRANEAN CALIFORNIA\",\n",
" },\n",
")\n",
"\n",
"IFrame(\n",
- " str(filtered_viewer_request.url).replace(\"tipg\", \"localhost\"),\n",
+ " str(filtered_viewer_request.url).replace(tipg_endpoint, tipg_browser_endpoint),\n",
" width=1200,\n",
" height=800,\n",
")"