Working with Statistics¶
Intro¶
Titiler allows you to get statistics and summaries of your data without having to load the entire dataset yourself. These statistics can be summaries of entire COG files, STAC items, or individual parts of the file, specified using GeoJSON.
Below, we will go over some of the statistical endpoints in Titiler - /bounds
, /info
, and /statistics
.
(Note: these examples will be using the /cog
endpoint, but everything is also available for /stac
and /mosaicjson
unless otherwise noted)
# setup
import httpx
import json
titiler_endpoint = "https://titiler.xyz" # Developmentseed Demo endpoint. Please be kind.
cog_url = "https://opendata.digitalglobe.com/events/mauritius-oil-spill/post-event/2020-08-12/105001001F1B5B00/105001001F1B5B00.tif"
Bounds¶
The /bounds
endpoint returns the bounding box of the image/asset. These bounds are returned in the projection EPSG:4326 (WGS84), in the format (minx, miny, maxx, maxy)
.
r = httpx.get(
f"{titiler_endpoint}/cog/bounds",
params = {
"url": cog_url,
}
).json()
bounds = r["bounds"]
print(r)
{'bounds': [57.664053823239804, -20.55473177712791, 57.84021477996238, -20.25261582755764]}
For a bit more information, you can get summary statistics from the /info
endpoint. This includes info such as:
- Bounds (identical to the
/bounds
endpoint) - Min and max zoom
- Band metadata, such as names of the bands and their descriptions
- Number of bands in the image
- Overview levels
- Image width and height
These are statistics available in the metadata of the image, so should be fast to read.
r = httpx.get(
f"{titiler_endpoint}/cog/info",
params = {
"url": cog_url,
}
).json()
print(json.dumps(r))
{"bounds": [57.664053823239804, -20.55473177712791, 57.84021477996238, -20.25261582755764], "minzoom": 10, "maxzoom": 18, "band_metadata": [["b1", {}], ["b2", {}], ["b3", {}]], "band_descriptions": [["b1", ""], ["b2", ""], ["b3", ""]], "dtype": "uint8", "nodata_type": "Mask", "colorinterp": ["red", "green", "blue"], "count": 3, "width": 38628, "driver": "GTiff", "overviews": [2, 4, 8, 16, 32, 64, 128], "height": 66247}
Statistics¶
For even more statistics of the image, you can use the /statistics
endpoint. This includes even more info, including:
- Summary statistics about overall pixel values, such min, max, mean, and count
- Histogram of the pixel values
- Percentiles
Statistics are generated both for the image as a whole and for each band individually.
r = httpx.get(
f"{titiler_endpoint}/cog/statistics",
params = {
"url": cog_url,
}
).json()
print(json.dumps(r))
{"b1": {"min": 0.0, "max": 255.0, "mean": 36.94901407469342, "count": 574080.0, "sum": 21211690.0, "std": 48.282133573955264, "median": 3.0, "majority": 1.0, "minority": 246.0, "unique": 256.0, "histogram": [[330584.0, 54820.0, 67683.0, 57434.0, 30305.0, 14648.0, 9606.0, 5653.0, 2296.0, 1051.0], [0.0, 25.5, 51.0, 76.5, 102.0, 127.5, 153.0, 178.5, 204.0, 229.5, 255.0]], "valid_percent": 93.75, "masked_pixels": 38272.0, "valid_pixels": 574080.0, "percentile_2": 0.0, "percentile_98": 171.0}, "b2": {"min": 0.0, "max": 255.0, "mean": 57.1494356187291, "count": 574080.0, "sum": 32808348.0, "std": 56.300819175100656, "median": 37.0, "majority": 5.0, "minority": 0.0, "unique": 256.0, "histogram": [[271018.0, 34938.0, 54030.0, 69429.0, 70260.0, 32107.0, 29375.0, 9697.0, 2001.0, 1225.0], [0.0, 25.5, 51.0, 76.5, 102.0, 127.5, 153.0, 178.5, 204.0, 229.5, 255.0]], "valid_percent": 93.75, "masked_pixels": 38272.0, "valid_pixels": 574080.0, "percentile_2": 5.0, "percentile_98": 180.0}, "b3": {"min": 0.0, "max": 255.0, "mean": 51.251764562430324, "count": 574080.0, "sum": 29422613.0, "std": 39.65505035854822, "median": 36.0, "majority": 16.0, "minority": 252.0, "unique": 254.0, "histogram": [[203263.0, 150865.0, 104882.0, 42645.0, 30652.0, 25382.0, 12434.0, 2397.0, 1097.0, 463.0], [0.0, 25.5, 51.0, 76.5, 102.0, 127.5, 153.0, 178.5, 204.0, 229.5, 255.0]], "valid_percent": 93.75, "masked_pixels": 38272.0, "valid_pixels": 574080.0, "percentile_2": 14.0, "percentile_98": 158.0}}
This endpoint is far more configurable than /bounds
and info
. You can specify which bands to analyse, how to generate the histogram, and pre-process the image.
For example, if you wanted to get the statistics of the VARI of the image you can use the expression
parameter to conduct simple band math:
r = httpx.get(
f"{titiler_endpoint}/cog/statistics",
params = {
"url": cog_url,
"expression": "(b2-b1)/(b1+b2-b3)", # expression for the VARI
"histogram_range": "-1,1"
}
).json()
print(json.dumps(r))
{"(b2-b1)/(b1+b2-b3)": {"min": -1.7976931348623157e+308, "max": 1.7976931348623157e+308, "mean": null, "count": 574080.0, "sum": null, "std": null, "median": -0.15384615384615385, "majority": -0.4, "minority": -149.0, "unique": 18718.0, "histogram": [[5646.0, 10176.0, 130905.0, 97746.0, 50184.0, 95842.0, 60322.0, 21478.0, 13552.0, 12204.0], [-1.0, -0.8, -0.6, -0.3999999999999999, -0.19999999999999996, 0.0, 0.20000000000000018, 0.40000000000000013, 0.6000000000000001, 0.8, 1.0]], "valid_percent": 93.75, "masked_pixels": 38272.0, "valid_pixels": 574080.0, "percentile_2": -3.5, "percentile_98": 3.3870967741935485}}
Alternatively, if you would like to get statistics for only a certain area, you can specify an area via a feature or a feature collection.
(Note: this endpoint is not available in the mosaicjson endpoint, only /cog
and /stac
)
mahebourg = """
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
[
[
57.70358910197049,
-20.384114558699935
],
[
57.68564920588395,
-20.384114558699935
],
[
57.68209507552771,
-20.39855066753664
],
[
57.68666467170024,
-20.421074640746554
],
[
57.70341985766697,
-20.434397129770545
],
[
57.72999121319131,
-20.42392955694521
],
[
57.70358910197049,
-20.384114558699935
]
]
],
"type": "Polygon"
}
}
]
}
"""
# NOTE: This is a POST request, unlike all other requests in this example
r = httpx.post(
f"{titiler_endpoint}/cog/statistics",
data=mahebourg,
params = {
"url": cog_url,
}
).json()
print(json.dumps(r))
{"type": "FeatureCollection", "features": [{"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[[57.70358910197049, -20.384114558699935], [57.68564920588395, -20.384114558699935], [57.68209507552771, -20.39855066753664], [57.68666467170024, -20.421074640746554], [57.70341985766697, -20.434397129770545], [57.72999121319131, -20.42392955694521], [57.70358910197049, -20.384114558699935]]]}, "properties": {"statistics": {"b1": {"min": 0.0, "max": 255.0, "mean": 88.5634794986129, "count": 619641.0, "sum": 54877563.0, "std": 55.18714964714274, "median": 77.0, "majority": 52.0, "minority": 253.0, "unique": 256.0, "histogram": [[67233.0, 110049.0, 129122.0, 90849.0, 77108.0, 44091.0, 44606.0, 37790.0, 18033.0, 760.0], [0.0, 25.5, 51.0, 76.5, 102.0, 127.5, 153.0, 178.5, 204.0, 229.5, 255.0]], "valid_percent": 62.0, "masked_pixels": 379783.0, "valid_pixels": 619641.0, "percentile_2": 4.0, "percentile_98": 208.0}, "b2": {"min": 0.0, "max": 255.0, "mean": 112.07155594933195, "count": 619641.0, "sum": 69444131.0, "std": 42.64508357271268, "median": 107.0, "majority": 103.0, "minority": 1.0, "unique": 256.0, "histogram": [[6004.0, 31108.0, 107187.0, 126848.0, 130731.0, 73650.0, 107827.0, 33264.0, 2403.0, 619.0], [0.0, 25.5, 51.0, 76.5, 102.0, 127.5, 153.0, 178.5, 204.0, 229.5, 255.0]], "valid_percent": 62.0, "masked_pixels": 379783.0, "valid_pixels": 619641.0, "percentile_2": 34.0, "percentile_98": 189.0}, "b3": {"min": 0.0, "max": 255.0, "mean": 84.54690377170006, "count": 619641.0, "sum": 52388728.0, "std": 44.64862735915829, "median": 77.0, "majority": 53.0, "minority": 254.0, "unique": 256.0, "histogram": [[40704.0, 130299.0, 138014.0, 85866.0, 86381.0, 91182.0, 41872.0, 4116.0, 993.0, 214.0], [0.0, 25.5, 51.0, 76.5, 102.0, 127.5, 153.0, 178.5, 204.0, 229.5, 255.0]], "valid_percent": 62.0, "masked_pixels": 379783.0, "valid_pixels": 619641.0, "percentile_2": 11.0, "percentile_98": 170.0}}}}]}