基于 Mapbox 实现 PyQt5上的离线地图
本文最后更新于:July 27, 2024 8:58 PM
懒得看的话可以直接 clone 我的 repo:Offline-Mapbox-in-PyQt5 记得给个小星星🧐
到底是谁先流行的 Folium??一点也不好用!😭
我一共试了 Folium, Google Map 和 Mapbox,Folium 不能动态更新 marker,Google Map 不能离线,只有 Mapbox 还行。。还没试 Leaflet,谁有空谁试吧😇(记得告诉我可不可以)
获取 Maptile
我用的是 OpenStreetMap 和 Maperitive
- 用 OpenStreetMap 获取需要地图的
.osm
文件 - 用 Maperitive 把
.osm
转换成.png
Web 部分
创建一个 html,可以叫map.html
获取需要的文件
- 建议自己注册一个 mapbox 账户,输入 token 然后按照步骤嵌入地图到 html
- 用浏览器打开 html 后打开 Developer Tool,在 source 里找到需要的js, css文件和其他文件(如sprite等)下载下来
- html 里引入需要文件,如我写一个字符的 JavaScript 就想用 jQuery, 所以我这里也引入了 jQuery
1
2
3<script src='./api/assets/mapbox-gl.js'></script>
<link href='./api/assets/mapbox-gl.css' rel='stylesheet'/>
<script src="jquery-3.6.4.min.js"></script>
Host Files on Local Server
把文件放进对应路径后写一个server.py
用来 host 文件1
2
3
4
5
6
7
8
9import http.server
import socketserver
PORT = 8888
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
httpd.serve_forever()
Setup Config
在 html 里按自己要求设置一下 Mapbox 的参数,比如我是这样的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28<script>
var map = new mapboxgl.Map({
container: 'map',
style: {
"version": 8,
"name": "Mapbox Streets",
"sprite": "http://localhost:8888/mapbox_build/sprite/sprite",
// "glyphs": "http://localhost:8888/mapbox_build/fonts/{fontstack}/{range}.pbf",
"sources": {
"osm-tiles": {
"type": "raster",
'tiles': [
"http://localhost:8888/tiles/{z}/{x}/{y}.png"
],
}
},
"layers": [{
"id": "123",
"type": "raster",
"source": "osm-tiles",
"source-layer": "osmtiles"
}]
},
center: [-71.6516848, 48.5107057],
zoom: 15
});
</script>
现在这里所有东西都是本地文件,已经可以离线用了
嵌入 PyQt5
用 PyQt 的 webengin 嵌入 html,新建.py
文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout
from PyQt5.QtWebEngineWidgets import QWebEngineView # pip install PyQtWebEngine
from PyQt5 import QtCore
from PyQt5.QtCore import *
"""
Mapbox in PyQt5
"""
class MyApp(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Mapbox in PyQt Example')
self.window_width, self.window_height = 600, 400
self.setMinimumSize(self.window_width, self.window_height)
layout = QVBoxLayout()
self.setLayout(layout)
webView = QWebEngineView()
webView.load(QUrl('http://localhost:8888/map.html'))
layout.addWidget(webView)
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyleSheet('''
QWidget {
font-size: 35px;
}
''')
myApp.show()
try:
sys.exit(app.exec_())
except SystemExit:
print('Closing Window...')
Python 和 Html 的交互
因为我需要实时更改 mapbox marker 的位置,所以我用了一个 json 来存 coordinate 的参数
json:
1 |
|
js:
1 |
|
python:
写入 json 的 function1
2
3
4
5
6
7
8def reloadMap():
with open("map.json", "r") as jsonFile:
data = json.load(jsonFile)
data["coordinate"] = [data["coordinate"][0]+0.00001,data["coordinate"][1]+0.00001]
with open("map.json", "w") as jsonFile:
json.dump(data, jsonFile)
用 PyQt 自带的 QTimer 在新的线程上重复调用reloadMap()
1
2
3
4timer = QTimer()
timer.timeout.connect(reloadMap) # execute `reloadMap`
timer.setInterval(50) # 1000ms = 1s
timer.start()
撒花
有问题的话欢迎评论呀!