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!