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
183 lines
5.2 KiB
1 month ago
|
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()
|