Building the Flask Application

This is where the magic happens. We are going to create a script that initializes Flask, connects to PostgreSQL, and defines a route that accepts {z}/{x}/{y} parameters to fetch the corresponding tile.

Create a new file named app.py and paste the following code:

import os
import psycopg2
from flask import Flask, Response, abort

app = Flask(__name__)

# Database connection parameters (Ideally, load these from a .env file)
DB_HOST = os.getenv("DB_HOST", "localhost")
DB_NAME = os.getenv("DB_NAME", "your_database")
DB_USER = os.getenv("DB_USER", "postgres")
DB_PASS = os.getenv("DB_PASS", "password")

def get_db_connection():
    return psycopg2.connect(
        host=DB_HOST,
        database=DB_NAME,
        user=DB_USER,
        password=DB_PASS
    )

@app.route('/tiles/<int:z>/<int:x>/<int:y>.pbf')
def get_tile(z, x, y):
    # Here is our PostGIS magic from Lesson 1!
    query = """
        WITH bounds AS (
            SELECT ST_TileEnvelope(%s, %s, %s) AS geom
        ),
        mvtgeom AS (
            SELECT 
                id,
                name,
                ST_AsMVTGeom(p.geom, bounds.geom) AS geom
            FROM places p, bounds
            WHERE ST_Intersects(p.geom, bounds.geom)
        )
        SELECT ST_AsMVT(mvtgeom, 'places') FROM mvtgeom;
    """
    
    conn = None
    try:
        conn = get_db_connection()
        with conn.cursor() as cur:
            cur.execute(query, (z, x, y))
            tile = cur.fetchone()[0]
            
            # If no data is found for this tile, return a 404 Not Found
            if not tile:
                abort(404)
                
            # Vector tiles MUST be returned as binary protocol buffers
            return Response(bytes(tile), mimetype='application/vnd.mapbox-vector-tile')
            
    except Exception as e:
        print(f"Error generating tile: {e}")
        abort(500)
    finally:
        if conn:
            conn.close()

if __name__ == '__main__':
    # Start the server on port 5000
    app.run(debug=True, port=5000)

Read through the SQL query in the code carefully. Notice how it seamlessly strings together ST_TileEnvelope, ST_AsMVTGeom, and ST_AsMVT!

← Previous