You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
5.2 KiB

import os
from flask import Flask, send_file, abort, Response, url_for
import folium
from folium import plugins
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import json
import time
import re
from threading import Thread, Lock
# Flask 服务
app = Flask(__name__, static_folder='static')
tile_dir = os.path.join(os.path.dirname(__file__), "tiles")
kml_dir = os.path.join(os.path.dirname(__file__), "kml") # KML文件目录
# 确保KML目录存在
os.makedirs(kml_dir, exist_ok=True)
# 用于存储文件变化事件的队列
file_changes = []
changes_lock = Lock()
def extract_tile_coords(filepath):
"""从文件路径中提取瓦片坐标信息"""
# 匹配形如 tiles/19/123_456.png 的路径
pattern = r'tiles/(\d+)/(\d+)_(\d+)\.png$'
match = re.search(pattern, filepath)
if match:
z = int(match.group(1))
x = int(match.group(2))
y = int(match.group(3))
return {'z': z, 'x': x, 'y': y}
return None
class TileHandler(FileSystemEventHandler):
def on_any_event(self, event):
if event.is_directory:
return
if event.src_path.endswith('.png'):
coords = extract_tile_coords(event.src_path)
if coords:
with changes_lock:
file_changes.append({
'coords': coords,
'time': time.time()
})
# 只保留最近100个变化
if len(file_changes) > 100:
file_changes.pop(0)
# 启动文件监控
observer = Observer()
observer.schedule(TileHandler(), tile_dir, recursive=True)
observer.start()
@app.route('/tiles/<int:z>/<x_y>.png')
def serve_tile(z, x_y):
"""提供瓦片图片服务"""
print(f"请求瓦片:层级={z}, 文件名={x_y}.png")
filename = f"{x_y}.png"
filepath = os.path.join(tile_dir, str(z), filename)
if os.path.exists(filepath):
return send_file(filepath)
else:
abort(404)
@app.route('/check_changes')
def check_changes():
"""检查文件变化"""
with changes_lock:
changes = file_changes.copy()
file_changes.clear()
return json.dumps(changes)
@app.route('/kml/<filename>')
def serve_kml(filename):
"""提供KML文件服务"""
filepath = os.path.join(kml_dir, filename)
if os.path.exists(filepath):
try:
with open(filepath, 'r', encoding='utf-8') as f:
kml_content = f.read()
return Response(kml_content, mimetype='application/vnd.google-earth.kml+xml')
except Exception as e:
print(f"处理KML文件 {filename} 时出错: {str(e)}")
abort(500)
else:
abort(404)
@app.route('/')
def index():
"""显示地图页面"""
# 创建地图
m = folium.Map(
location=[30.253729, 114.453506], # 武汉市中心坐标
zoom_start=19,
min_zoom=1,
max_zoom=23,
tiles=None, # 禁用默认图层
control_scale=True # 显示比例尺
)
# 添加在线瓦片图层
folium.TileLayer(
tiles='https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
attr='Google Maps',
name='谷歌地图',
min_zoom=1,
max_zoom=20,
).add_to(m)
# folium.TileLayer(
# tiles='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
# attr='OpenStreetMap',
# name='OpenStreetMap',
# min_zoom=1,
# max_zoom=19,
# ).add_to(m)
# folium.TileLayer(
# tiles='https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
# attr='OpenTopoMap',
# name='OpenTopoMap',
# min_zoom=1,
# max_zoom=17,
# ).add_to(m)
# 添加自定义瓦片层
folium.TileLayer(
tiles='http://localhost:5000/tiles/{z}/{x}_{y}.png',
attr='高德瓦片服务',
min_zoom=1,
max_zoom=23,
maxNativeZoom=19,
minNativeZoom=19,
name='本地瓦片',
).add_to(m)
# 添加KML文件
# kml_files = [f for f in os.listdir(kml_dir) if f.endswith('.kml')]
# for kml_file in kml_files:
# kml_url = url_for('serve_kml', filename=kml_file)
# try:
# plugins.Kml(
# kml_url,
# name=kml_file,
# show=True,
# overlay=True
# ).add_to(m)
# except Exception as e:
# print(f"加载KML文件 {kml_file} 时出错: {str(e)}")
# 添加控件
plugins.Fullscreen().add_to(m) # 全屏控件
plugins.MousePosition().add_to(m) # 鼠标位置显示
folium.LayerControl().add_to(m) # 添加图层控制
# 添加自定义JavaScript
m.get_root().html.add_child(folium.Element(f"""
<script src="{url_for('static', filename='js/tile-updater.js')}"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {{
// 初始化瓦片更新器
new TileUpdater(map);
}});
</script>
"""))
# 返回地图HTML
return m._repr_html_()
if __name__ == "__main__":
try:
app.run(port=5000, debug=True)
finally:
observer.stop()
observer.join()