demo
titiler.PgSTAC Demo¶
This Notebook aims to show the different features provided by titiler.pgstac application
In order to run this demo you'll need to have a PgSTAC database and the titiler.pgstac application running. The easiest way to launch them is to use the repo's docker-compose.yml
docker compose up tiler
Python requirements¶
pip install httpx folium pypgstac psycopg psycopg-pool geojson-pydantic
Populate the PgSTAC db with data¶
$ pypgstac load collections tests/fixtures/noaa-emergency-response.json --dsn postgresql://username:password@localhost:5439/postgis --method insert
$ pypgstac load items tests/fixtures/noaa-eri-nashville2020.json --dsn postgresql://username:password@localhost:5439/postgis --method insert
In [1]:
Copied!
import json
import httpx
from folium import Map, TileLayer, GeoJson
from geojson_pydantic import Feature, Polygon
endpoint = "http://127.0.0.1:8081"
print(httpx.get(f"{endpoint}/healthz").json())
import json
import httpx
from folium import Map, TileLayer, GeoJson
from geojson_pydantic import Feature, Polygon
endpoint = "http://127.0.0.1:8081"
print(httpx.get(f"{endpoint}/healthz").json())
{'database_online': True}
In [2]:
Copied!
# bounds of the noaa-eri-nashville2020.json items
bounds = (-87.0251, 36.0999, -85.4249, 36.2251)
poly = Polygon.from_bounds(*bounds)
geojson = Feature(type="Feature", geometry=poly, properties=None).model_dump(exclude_none=True)
m = Map(
tiles="OpenStreetMap",
location=((bounds[1] + bounds[3]) / 2,(bounds[0] + bounds[2]) / 2),
zoom_start=8
)
geo_json = GeoJson(
data=geojson,
style_function=lambda x: {
'opacity': 1, 'dashArray': '1', 'fillOpacity': 0, 'weight': 1
},
)
geo_json.add_to(m)
m
# bounds of the noaa-eri-nashville2020.json items
bounds = (-87.0251, 36.0999, -85.4249, 36.2251)
poly = Polygon.from_bounds(*bounds)
geojson = Feature(type="Feature", geometry=poly, properties=None).model_dump(exclude_none=True)
m = Map(
tiles="OpenStreetMap",
location=((bounds[1] + bounds[3]) / 2,(bounds[0] + bounds[2]) / 2),
zoom_start=8
)
geo_json = GeoJson(
data=geojson,
style_function=lambda x: {
'opacity': 1, 'dashArray': '1', 'fillOpacity': 0, 'weight': 1
},
)
geo_json.add_to(m)
m
Out[2]:
Make this Notebook Trusted to load map: File -> Trust Notebook
Register Search query¶
In [6]:
Copied!
search_request = {
# Filter collection
"collections": ["noaa-emergency-response"],
# limit bounds of the known items (note: the bbox will also be used in the tilejson response)
"bbox": bounds,
"filter-lang": "cql-json",
}
response = httpx.post(
f"{endpoint}/searches/register", json=search_request,
).json()
print(response)
searchid = response["id"]
search_request = {
# Filter collection
"collections": ["noaa-emergency-response"],
# limit bounds of the known items (note: the bbox will also be used in the tilejson response)
"bbox": bounds,
"filter-lang": "cql-json",
}
response = httpx.post(
f"{endpoint}/searches/register", json=search_request,
).json()
print(response)
searchid = response["id"]
{'id': '6d436413d0eed760acc2f6bd16ca77a5', 'links': [{'href': 'http://127.0.0.1:8081/searches/6d436413d0eed760acc2f6bd16ca77a5/info', 'rel': 'metadata', 'title': 'Mosaic metadata'}, {'href': 'http://127.0.0.1:8081/searches/6d436413d0eed760acc2f6bd16ca77a5/{tileMatrixSetId}/tilejson.json', 'rel': 'tilejson', 'templated': True, 'title': 'Link for TileJSON (Template URL)'}, {'href': 'http://127.0.0.1:8081/searches/6d436413d0eed760acc2f6bd16ca77a5/{tileMatrixSetId}/map', 'rel': 'map', 'templated': True, 'title': 'Link for Map viewer (Template URL)'}, {'href': 'http://127.0.0.1:8081/searches/6d436413d0eed760acc2f6bd16ca77a5/{tileMatrixSetId}/WMTSCapabilities.xml', 'rel': 'wmts', 'templated': True, 'title': 'Link for WMTS (Template URL)'}]}
Show list of Mosaics¶
In [12]:
Copied!
response = httpx.get(f"{endpoint}/searches/list").json()
print(
json.dumps(
[
(search["search"]["hash"], search["search"]["metadata"].get("name"))
for search in response["searches"]
],
indent=4,
)
)
response = httpx.get(f"{endpoint}/searches/list").json()
print(
json.dumps(
[
(search["search"]["hash"], search["search"]["metadata"].get("name"))
for search in response["searches"]
],
indent=4,
)
)
[
[
"37c6ebb942cc5393a9eb408ad8431f62",
"Mosaic for 'MAXAR_BayofBengal_Cyclone_Mocha_May_23' Collection"
],
[
"0152ee7c4a9d7e672d15e318c92046df",
"Mosaic for 'MAXAR_BayofBengal_Cyclone_Mocha_May_23' Collection"
],
[
"9c8888d0bbc70ceaff403255442634a3",
"Mosaic for 'MAXAR_BayofBengal_Cyclone_Mocha_May_23' Collection"
],
[
"492e7cd904d75979afa38d8a5b6e2b83",
"Mosaic for 'MAXAR_BayofBengal_Cyclone_Mocha_May_23' Collection"
],
[
"8410ce526b9644e177db97f275db172c",
"Mosaic for 'noaa-emergency-response' Collection"
],
[
"ca495d2ef671c17cc743966be61fa346",
"Mosaic for 'noaa-emergency-response' Collection"
],
[
"f2ed44110ac820d456ac53e44f42daeb",
"Mosaic for 'noaa-emergency-response' Collection"
],
[
"3f7bd36cbeddbfc49921b9b127283478",
"Mosaic for 'noaa-emergency-response' Collection"
],
[
"f3049b538d2c4189a057f809a90a6a13",
"Mosaic for 'noaa-emergency-response' Collection"
],
[
"4530a5f9f38e8721c6a1b1dfb974fac2",
"Mosaic for 'noaa-emergency-response' Collection"
]
]
Get Search Metadata¶
In [13]:
Copied!
info_response = httpx.get(f"{endpoint}/searches/{searchid}/info").json()
print(json.dumps(info_response, indent=4))
info_response = httpx.get(f"{endpoint}/searches/{searchid}/info").json()
print(json.dumps(info_response, indent=4))
{
"search": {
"hash": "6d436413d0eed760acc2f6bd16ca77a5",
"search": {
"bbox": [
-87.0251,
36.0999,
-85.4249,
36.2251
],
"collections": [
"noaa-emergency-response"
],
"filter-lang": "cql-json"
},
"_where": "collection = ANY ('{noaa-emergency-response}') AND st_intersects(geometry, '0103000020E610000001000000050000004BC8073D9BC155C0696FF085C90C42404BC8073D9BC155C0302AA913D01C42408104C58F315B55C0302AA913D01C42408104C58F315B55C0696FF085C90C42404BC8073D9BC155C0696FF085C90C4240')",
"orderby": "datetime DESC, id DESC",
"lastused": "2024-11-28T11:21:15.887027Z",
"usecount": 2,
"metadata": {
"type": "mosaic"
}
},
"links": [
{
"href": "http://127.0.0.1:8081/searches/6d436413d0eed760acc2f6bd16ca77a5/info",
"rel": "self",
"title": "Mosaic metadata"
},
{
"href": "http://127.0.0.1:8081/searches/6d436413d0eed760acc2f6bd16ca77a5/{tileMatrixSetId}/tilejson.json",
"rel": "tilejson",
"templated": true,
"title": "TileJSON link (Template URL)."
},
{
"href": "http://127.0.0.1:8081/searches/6d436413d0eed760acc2f6bd16ca77a5/{tileMatrixSetId}/map",
"rel": "map",
"templated": true,
"title": "Map viewer link (Template URL)."
},
{
"href": "http://127.0.0.1:8081/searches/6d436413d0eed760acc2f6bd16ca77a5/{tileMatrixSetId}/WMTSCapabilities.xml",
"rel": "wmts",
"templated": true,
"title": "WMTS link (Template URL)"
}
]
}
Get TileJSON¶
Note: to return a valid tilejson document you'll need to pass either the assets or expression option.
In [16]:
Copied!
tj_response = httpx.get(f"{endpoint}/searches/{searchid}/WebMercatorQuad/tilejson.json?assets=cog").json()
print(json.dumps(tj_response, indent=4))
tj_response = httpx.get(f"{endpoint}/searches/{searchid}/WebMercatorQuad/tilejson.json?assets=cog").json()
print(json.dumps(tj_response, indent=4))
{
"tilejson": "2.2.0",
"name": "6d436413d0eed760acc2f6bd16ca77a5",
"version": "1.0.0",
"scheme": "xyz",
"tiles": [
"http://127.0.0.1:8081/searches/6d436413d0eed760acc2f6bd16ca77a5/tiles/WebMercatorQuad/{z}/{x}/{y}?assets=cog"
],
"minzoom": 0,
"maxzoom": 24,
"bounds": [
-87.0251,
36.0999,
-85.4249,
36.2251
],
"center": [
-86.225,
36.162499999999994,
0
]
}
Load tiles¶
In [17]:
Copied!
m = Map(
location=((bounds[1] + bounds[3]) / 2,(bounds[0] + bounds[2]) / 2),
zoom_start=14
)
geo_json = GeoJson(
data=geojson,
style_function=lambda x: {
'opacity': 1, 'dashArray': '1', 'fillOpacity': 0, 'weight': 1
},
)
geo_json.add_to(m)
aod_layer = TileLayer(
tiles=tj_response["tiles"][0],
attr="Mosaic",
min_zoom=14,
max_zoom=18,
max_native_zoom=18,
)
aod_layer.add_to(m)
m
m = Map(
location=((bounds[1] + bounds[3]) / 2,(bounds[0] + bounds[2]) / 2),
zoom_start=14
)
geo_json = GeoJson(
data=geojson,
style_function=lambda x: {
'opacity': 1, 'dashArray': '1', 'fillOpacity': 0, 'weight': 1
},
)
geo_json.add_to(m)
aod_layer = TileLayer(
tiles=tj_response["tiles"][0],
attr="Mosaic",
min_zoom=14,
max_zoom=18,
max_native_zoom=18,
)
aod_layer.add_to(m)
m
Out[17]:
Make this Notebook Trusted to load map: File -> Trust Notebook
Register a Mosaic with Metadata¶
In [19]:
Copied!
search_request = {
# Filter collection
"collections": ["noaa-emergency-response"],
# limit bounds of the known items (note: the bbox will also be used in the tilejson response)
"bbox": bounds,
"filter-lang": "cql-json",
"metadata": {
"bounds": [-87.0251, 36.0999, -85.4249, 36.2251], # This is redondant because it's in the bbox filter
"minzoom": 14,
"maxzoom": 18,
"assets": ["cog"],
"defaults": {
"true_color": {
"bidx": [1, 2, 3],
},
},
},
}
response = httpx.post(
f"{endpoint}/searches/register", json=search_request,
).json()
print(json.dumps(response, indent=4))
searchid = response["id"]
search_request = {
# Filter collection
"collections": ["noaa-emergency-response"],
# limit bounds of the known items (note: the bbox will also be used in the tilejson response)
"bbox": bounds,
"filter-lang": "cql-json",
"metadata": {
"bounds": [-87.0251, 36.0999, -85.4249, 36.2251], # This is redondant because it's in the bbox filter
"minzoom": 14,
"maxzoom": 18,
"assets": ["cog"],
"defaults": {
"true_color": {
"bidx": [1, 2, 3],
},
},
},
}
response = httpx.post(
f"{endpoint}/searches/register", json=search_request,
).json()
print(json.dumps(response, indent=4))
searchid = response["id"]
{
"id": "4b0db3dbd1858d54a3a55f84de97d1ca",
"links": [
{
"href": "http://127.0.0.1:8081/searches/4b0db3dbd1858d54a3a55f84de97d1ca/info",
"rel": "metadata",
"title": "Mosaic metadata"
},
{
"href": "http://127.0.0.1:8081/searches/4b0db3dbd1858d54a3a55f84de97d1ca/{tileMatrixSetId}/tilejson.json",
"rel": "tilejson",
"templated": true,
"title": "Link for TileJSON (Template URL)"
},
{
"href": "http://127.0.0.1:8081/searches/4b0db3dbd1858d54a3a55f84de97d1ca/{tileMatrixSetId}/map",
"rel": "map",
"templated": true,
"title": "Link for Map viewer (Template URL)"
},
{
"href": "http://127.0.0.1:8081/searches/4b0db3dbd1858d54a3a55f84de97d1ca/{tileMatrixSetId}/WMTSCapabilities.xml",
"rel": "wmts",
"templated": true,
"title": "Link for WMTS (Template URL)"
}
]
}
In [21]:
Copied!
tj_response = httpx.get(f"{endpoint}/searches/{searchid}/WebMercatorQuad/tilejson.json?assets=cog").json()
print(json.dumps(tj_response, indent=4))
tj_response = httpx.get(f"{endpoint}/searches/{searchid}/WebMercatorQuad/tilejson.json?assets=cog").json()
print(json.dumps(tj_response, indent=4))
{
"tilejson": "2.2.0",
"name": "4b0db3dbd1858d54a3a55f84de97d1ca",
"version": "1.0.0",
"scheme": "xyz",
"tiles": [
"http://127.0.0.1:8081/searches/4b0db3dbd1858d54a3a55f84de97d1ca/tiles/WebMercatorQuad/{z}/{x}/{y}?assets=cog"
],
"minzoom": 14,
"maxzoom": 18,
"bounds": [
-87.0251,
36.0999,
-85.4249,
36.2251
],
"center": [
-86.225,
36.162499999999994,
14
]
}
In [22]:
Copied!
m = Map(
location=((bounds[1] + bounds[3]) / 2,(bounds[0] + bounds[2]) / 2),
zoom_start=14
)
geo_json = GeoJson(
data=geojson,
style_function=lambda x: {
'opacity': 1, 'dashArray': '1', 'fillOpacity': 0, 'weight': 1
},
)
geo_json.add_to(m)
aod_layer = TileLayer(
tiles=tj_response["tiles"][0],
attr="Mosaic",
min_zoom=tj_response["minzoom"],
max_zoom=tj_response["maxzoom"],
max_native_zoom=tj_response["maxzoom"],
)
aod_layer.add_to(m)
m
m = Map(
location=((bounds[1] + bounds[3]) / 2,(bounds[0] + bounds[2]) / 2),
zoom_start=14
)
geo_json = GeoJson(
data=geojson,
style_function=lambda x: {
'opacity': 1, 'dashArray': '1', 'fillOpacity': 0, 'weight': 1
},
)
geo_json.add_to(m)
aod_layer = TileLayer(
tiles=tj_response["tiles"][0],
attr="Mosaic",
min_zoom=tj_response["minzoom"],
max_zoom=tj_response["maxzoom"],
max_native_zoom=tj_response["maxzoom"],
)
aod_layer.add_to(m)
m
Out[22]:
Make this Notebook Trusted to load map: File -> Trust Notebook
In [ ]:
Copied!