You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Leaflet+Flask加载MBTiles地图仅显示控件,如何解决?

问题描述

尝试使用Leaflet、Flask和Sqlite3加载MBTiles格式的新西兰地图,在网页上展示指定经纬度的位置。已通过Flask路由实现瓦片服务,但浏览器加载页面时仅显示地图控件,地图本身不可见。浏览器控制台报错经纬度为NaN,无法初始化地图:

map.html:15 Latitude:  NaN
map.html:16 Longitude:  NaN
Uncaught Error: Invalid LatLng object: (NaN, NaN)
    at new D (LatLng.js:32:9)
    at j (LatLng.js:123:11)
    at i.setView (Map.js:181:30)
    at map.html:18:32. 

原代码

Flask代码(app.py)

import sqlite3
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def display_map():
    longitude = 175.6138189
    latitude = -40.34256356
    return render_template('map.html', lat=latitude, lon=longitude)

@app.route('/tiles/<int:z>/<int:x>/<int:y>.png')
def serve_tile(z, x, y):
    mbtiles_path = 'path/to/new-zealand.mbtiles'

    with sqlite3.connect(mbtiles_path) as conn:
        cursor = conn.cursor()
        cursor.execute(
            'SELECT tile_data FROM tiles WHERE zoom_level=? AND tile_column=? AND tile_row=?',
            (z, x, (2 ** z) - 1 - y)
        )
        tile = cursor.fetchone()
        if tile:
            return tile[0], 200, {
                'Content-Type': 'image/png',
                'Cache-Control': 'public, max-age=3600'
            }
    return 'Tile not found', 404

if __name__ == '__main__':
    app.run(debug=True)

前端代码(map.html)

<!DOCTYPE html>
<html>
<head>
    <title>Location on Map</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.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
</head>
<body>
    <div id="map" style="width: 100%; height: 100vh;"></div>
    <script>
        var lat = parseFloat("{{ lat|tojson|safe }}");
        var lon = parseFloat("{{ lon|tojson|safe }}");
        console.log("Latitude: ", lat);
        console.log("Longitude: ", lon);

        var map = L.map('map').setView([lat, lon], 13);
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: 'Map data &copy; OpenStreetMap contributors'
        }).addTo(map);

        L.marker([lat, lon]).addTo(map).bindPopup("<b>Location</b><br>Latitude: " + lat + "<br>Longitude: " + lon).openPopup();
    </script>
</body>
</html>
解决步骤

1. 修复经纬度传递问题

原模板中使用{{ lat|tojson|safe }}会将数值转为带引号的字符串,导致parseFloat解析异常。直接传递Python数值到JS即可,无需额外转义:
修改map.html中的JS变量定义:

var lat = {{ lat }};
var lon = {{ lon }};

Flask会自动将Python的数值类型渲染为JS原生数值,避免NaN问题。

2. 切换到自定义MBTiles瓦片服务

原代码使用OpenStreetMap的公共瓦片,需替换为自己Flask提供的瓦片路由:

L.tileLayer('/tiles/{z}/{x}/{y}.png', {
    attribution: 'New Zealand Map Data',
    maxZoom: 18, // 根据MBTiles实际支持的缩放级别调整
    minZoom: 0
}).addTo(map);

3. 确认MBTiles文件路径正确性

确保mbtiles_path指向正确的文件位置:

  • 若文件在Flask项目根目录,直接写文件名:mbtiles_path = 'new-zealand.mbtiles'
  • 若在子目录,使用相对路径或绝对路径,比如'static/mbtiles/new-zealand.mbtiles'

4. 验证瓦片坐标转换逻辑

MBTiles的tile_row从顶部计数,Leaflet从底部计数,代码中(2 ** z) - 1 - y的转换是正确的,但需确认MBTiles文件包含当前地图缩放级别对应的瓦片数据。

修改后的完整代码

app.py

import sqlite3
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def display_map():
    longitude = 175.6138189
    latitude = -40.34256356
    return render_template('map.html', lat=latitude, lon=longitude)

@app.route('/tiles/<int:z>/<int:x>/<int:y>.png')
def serve_tile(z, x, y):
    # 替换为实际的MBTiles文件路径
    mbtiles_path = 'new-zealand.mbtiles'

    with sqlite3.connect(mbtiles_path) as conn:
        cursor = conn.cursor()
        cursor.execute(
            'SELECT tile_data FROM tiles WHERE zoom_level=? AND tile_column=? AND tile_row=?',
            (z, x, (2 ** z) - 1 - y)
        )
        tile = cursor.fetchone()
        if tile:
            return tile[0], 200, {
                'Content-Type': 'image/png',
                'Cache-Control': 'public, max-age=3600'
            }
    return 'Tile not found', 404

if __name__ == '__main__':
    app.run(debug=True)

map.html

<!DOCTYPE html>
<html>
<head>
    <title>Location on Map</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.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
</head>
<body>
    <div id="map" style="width: 100%; height: 100vh;"></div>
    <script>
        var lat = {{ lat }};
        var lon = {{ lon }};
        console.log("Latitude: ", lat);
        console.log("Longitude: ", lon);

        var map = L.map('map').setView([lat, lon], 13);
        L.tileLayer('/tiles/{z}/{x}/{y}.png', {
            attribution: 'New Zealand Map Data',
            maxZoom: 18
        }).addTo(map);

        L.marker([lat, lon]).addTo(map).bindPopup("<b>Location</b><br>Latitude: " + lat + "<br>Longitude: " + lon).openPopup();
    </script>
</body>
</html>

内容的提问来源于stack exchange,提问作者Kishor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 06:05:00