Skip to content

Instantly share code, notes, and snippets.

@darrenwiens
Created May 14, 2025 15:43
Show Gist options
  • Save darrenwiens/685465ec3467f3b635bb43ed0d7d23e9 to your computer and use it in GitHub Desktop.
Save darrenwiens/685465ec3467f3b635bb43ed0d7d23e9 to your computer and use it in GitHub Desktop.
A FastAPI that fetches relevant map tiles for given tile coordinates, and returns an image warped to a cube face
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
import cv2
import numpy as np
import io
import os
import random
import requests
MAPTILER_KEY = os.getenv("MAPTILER_KEY")
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"], # Adjust this to your frontend URL
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
def combine(imgs, orientation):
if orientation == "h":
combined = np.concatenate((imgs[0], imgs[1], imgs[2]), axis=1)
else:
combined = np.concatenate((imgs[0], imgs[1], imgs[2]), axis=0)
return combined
def warp(image, face):
h, w = image.shape[:2]
h -= 1
w -= 1
if face == "f":
src_points = np.float32(
[
[0, 0], # top-left
[w, 0], # top-right
[int(w / 3 * 2), h], # bottom-right
[int(w / 3), h], # bottom-left
]
)
elif face == "l":
src_points = np.float32(
[
[0, 0],
[w, int(h / 3)],
[w, int((h / 3) * 2)],
[0, h],
]
)
elif face == "b":
src_points = np.float32(
[
[int(w / 3), 0],
[int((w / 3) * 2), 0],
[w, h],
[0, h],
]
)
elif face == "r":
src_points = np.float32(
[
[0, int(h / 3)],
[w, 0],
[w, h],
[0, int((h / 3) * 2)],
]
)
dst_size = 1024
dst_points = np.float32(
[[0, 0], [dst_size, 0], [dst_size, dst_size], [0, dst_size]]
)
matrix = cv2.getPerspectiveTransform(src_points, dst_points)
return cv2.warpPerspective(image, matrix, (dst_size, dst_size))
def generate_sky():
img = np.zeros((256, 256), dtype=np.uint8)
num_stars = 100
for _ in range(num_stars):
x = random.randint(0, 255)
y = random.randint(0, 255)
img[y, x] = random.randint(0, 255)
return img
def get_map_tile(x, y, z, f):
if f == "u":
image = generate_sky()
else:
url = f"https://api.maptiler.com/maps/streets-v2/{z}/{x}/{y}.png?key={MAPTILER_KEY}"
response = requests.get(url)
image_array = np.frombuffer(response.content, dtype=np.uint8)
image = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
return image
@app.get("/tile/{z}/{x}/{y}/{f}")
def read_item(x: int, y: int, z: int, f: str):
center_tile_coord = [x, y, z]
tile_coords = {
"f": [
[center_tile_coord[0] - 1, center_tile_coord[1] - 1, center_tile_coord[2]],
[center_tile_coord[0], center_tile_coord[1] - 1, center_tile_coord[2]],
[center_tile_coord[0] + 1, center_tile_coord[1] - 1, center_tile_coord[2]],
],
"l": [
[center_tile_coord[0] - 1, center_tile_coord[1] - 1, center_tile_coord[2]],
[center_tile_coord[0] - 1, center_tile_coord[1], center_tile_coord[2]],
[center_tile_coord[0] - 1, center_tile_coord[1] + 1, center_tile_coord[2]],
],
"b": [
[center_tile_coord[0] - 1, center_tile_coord[1] + 1, center_tile_coord[2]],
[center_tile_coord[0], center_tile_coord[1] + 1, center_tile_coord[2]],
[center_tile_coord[0] + 1, center_tile_coord[1] + 1, center_tile_coord[2]],
],
"r": [
[center_tile_coord[0] + 1, center_tile_coord[1] - 1, center_tile_coord[2]],
[center_tile_coord[0] + 1, center_tile_coord[1], center_tile_coord[2]],
[center_tile_coord[0] + 1, center_tile_coord[1] + 1, center_tile_coord[2]],
],
"d": [center_tile_coord],
"u": [center_tile_coord],
}
imgs = []
for i in tile_coords[f]:
imgs.append(get_map_tile(i[0], i[1], i[2], f))
actions = {
"f": lambda: warp(combine(imgs, "h"), f),
"l": lambda: cv2.rotate(warp(combine(imgs, "v"), f), cv2.ROTATE_90_CLOCKWISE),
"b": lambda: cv2.rotate(warp(combine(imgs, "h"), f), cv2.ROTATE_180),
"r": lambda: cv2.rotate(
warp(combine(imgs, "v"), f), cv2.ROTATE_90_COUNTERCLOCKWISE
),
"u": lambda: imgs[0],
"d": lambda: imgs[0],
}
result_image = actions[f]()
success, encoded_image = cv2.imencode(".png", result_image)
if not success:
return {"error": "Image encoding failed"}
image_bytes = encoded_image.tobytes()
return StreamingResponse(io.BytesIO(image_bytes), media_type="image/png")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment