first commit

This commit is contained in:
along
2026-02-22 10:51:28 +08:00
commit 7218809f1d
7 changed files with 1061 additions and 0 deletions

147
app.py Normal file
View File

@@ -0,0 +1,147 @@
"""rio-tiler dynamic tile server for I7D16.tif"""
import os
from pathlib import Path
from fastapi import FastAPI, HTTPException
from fastapi.responses import HTMLResponse
from starlette.requests import Request
from starlette.responses import Response
from rio_tiler.errors import TileOutsideBounds
from rio_tiler.io import Reader
from rio_tiler.profiles import img_profiles
DATA_PATH = str(Path(__file__).parent / "data" / "I7D16.tif")
# Per-band rescale range derived from p2/p98 statistics of the source image
# Band order: Red, Green, Blue (band 4 NIR is ignored)
RESCALE = [
(6130, 40453), # Red
(10641, 41211), # Green
(12482, 36323), # Blue
]
app = FastAPI(
title="rio-tiler",
description="Dynamic tile server for I7D16.tif (NJ 2020 aerial imagery)",
)
@app.get(
"/tiles/{z}/{x}/{y}.png",
responses={
200: {"content": {"image/png": {}}, "description": "Return a PNG tile."},
404: {"description": "Tile outside image bounds."},
},
response_class=Response,
description="Read COG tile and return a PNG image (RGB bands, UInt16 rescaled)",
)
def tile(z: int, x: int, y: int):
"""Return a map tile for the given z/x/y coordinates."""
try:
with Reader(DATA_PATH) as cog:
img = cog.tile(x, y, z, indexes=(1, 2, 3))
except TileOutsideBounds:
raise HTTPException(status_code=404, detail="Tile outside image bounds")
content = img.render(
img_format="PNG",
rescale=RESCALE,
**img_profiles.get("png", {}),
)
return Response(content, media_type="image/png")
@app.get("/tilejson.json", responses={200: {"description": "Return a TileJSON document"}})
def tilejson(request: Request):
"""Return a TileJSON 2.2.0 document describing the available tiles."""
with Reader(DATA_PATH) as cog:
bounds = cog.get_geographic_bounds(cog.tms.rasterio_geographic_crs)
minzoom = cog.minzoom
maxzoom = cog.maxzoom
base_url = str(request.base_url).rstrip("/")
tile_url = f"{base_url}/tiles/{{z}}/{{x}}/{{y}}.png"
return {
"tilejson": "2.2.0",
"name": os.path.basename(DATA_PATH),
"description": "NJ Open Imagery 2020 - NAD83(2011) / New Jersey (ftUS)",
"tiles": [tile_url],
"bounds": list(bounds),
"minzoom": minzoom,
"maxzoom": maxzoom,
"center": [
(bounds[0] + bounds[2]) / 2,
(bounds[1] + bounds[3]) / 2,
minzoom + 2,
],
}
@app.get("/", response_class=HTMLResponse)
def index(request: Request):
"""Simple Leaflet.js viewer for the imagery."""
with Reader(DATA_PATH) as cog:
bounds = cog.get_geographic_bounds(cog.tms.rasterio_geographic_crs)
minzoom = cog.minzoom
maxzoom = cog.maxzoom
base_url = str(request.base_url).rstrip("/")
tile_url = f"{base_url}/tiles/{{z}}/{{x}}/{{y}}.png"
center_lat = (bounds[1] + bounds[3]) / 2
center_lng = (bounds[0] + bounds[2]) / 2
init_zoom = minzoom + 2
return f"""<!DOCTYPE html>
<html lang="en">
<head>
<title>I7D16 Tile Viewer</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
html, body, #map {{ height: 100%; margin: 0; padding: 0; }}
#info {{
position: absolute; top: 10px; right: 10px; z-index: 1000;
background: white; padding: 10px 14px; border-radius: 4px;
font-family: sans-serif; font-size: 13px;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
line-height: 1.6;
}}
</style>
</head>
<body>
<div id="map"></div>
<div id="info">
<b>I7D16.tif</b><br>
NJ Open Imagery 2020<br>
Zoom: {minzoom}{maxzoom}<br>
CRS: NAD83(2011) / NJ ftUS
</div>
<script>
var map = L.map('map').setView([{center_lat}, {center_lng}], {init_zoom});
L.tileLayer('https://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png', {{
attribution: '© <a href="https://openstreetmap.org">OpenStreetMap</a>',
opacity: 0.4,
maxZoom: 19
}}).addTo(map);
L.tileLayer('{tile_url}', {{
minZoom: {minzoom},
maxZoom: {maxzoom},
opacity: 1.0,
attribution: 'NJ Open Imagery 2020'
}}).addTo(map);
map.fitBounds([
[{bounds[1]}, {bounds[0]}],
[{bounds[3]}, {bounds[2]}]
]);
</script>
</body>
</html>"""