問題描述
我正在使用 python、PyQt5 和 Qt 設計器在我的應用程序中顯示一個葉圖.由于 Qt 設計器中沒有地圖小部件,我添加了一個通用小部件,然后將其提升到我的自定義地圖小部件.一切正常.這是我推廣的小部件的 python 代碼:
I am displaying a folium map in my application using python, PyQt5 and Qt designer. Since there is no map widget in Qt designer, I add a general widget and then promote it to my custom map widget. It all works fine. Here is the python code for my promoted widget:
import io
import folium
from PyQt5 import QtWebEngineWidgets
from PyQt5.QtWidgets import *
class LeafWidget (QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
m = folium.Map(
location=[40, -120] , zoom_start=10
)
self.view = QtWebEngineWidgets.QWebEngineView()
data = io.BytesIO()
m.save(data, close_file=False)
self.view.setHtml(data.getvalue().decode())
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.view)
self.show()
這很好,我可以在我的應用程序中看到地圖.
This works fine and I can see the map in my application.
我還試圖在這張地圖上顯示一個 GIS shapefile.我做了一些研究,似乎我無法將 GIS shapefile (.shp) 直接添加到葉地圖中.因此,我嘗試先將其轉換為 json,然后將 json 添加到地圖頂部.我修改了我的代碼,將 .shp 文件添加到地圖:
I am also trying to display a GIS shapefile on top of this map. I have done some research and it seems like I cannot add GIS shapefile (.shp) directly to a folium map. So, I try to convert it to json first and then add the json on top of the map. I modified my code as below to add the .shp file to map:
import io
import folium
import os.path
from PyQt5 import QtWebEngineWidgets
from PyQt5.QtWidgets import *
import geopandas as gpd
class LeafWidget (QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
m = folium.Map(
location=[40, -120] , zoom_start=10
)
self.view = QtWebEngineWidgets.QWebEngineView()
# converting shp to geojson
shp_file = gpd.read_file('input/2015_loaded_NoCC.shp')
shp_file.to_file('myshpfile.json', driver='GeoJSON')
shp = os.path.join('', 'myshpfile.json')
data = io.BytesIO()
folium.GeoJson(shp).add_to(m)
m.save(data, close_file=False)
self.view.setHtml(data.getvalue().decode())
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.view)
self.show()
但現在我的地圖根本不顯示.它只是一個空白空間,控制臺或錯誤日志中沒有錯誤.如果我使用m.save('map.html')"將地圖保存為 HTML 文件雖然,它確實保存了文件,當我打開它時,它會在地圖上顯示 json 文件,但由于某種原因,我在應用程序中顯示地圖的方式在添加 shp 后不起作用-->json 文件.我做錯了什么?
but now my map doesn't show up at all. It's just an empty space with no errors in the console or error log. If I save the map as an HTML file using "m.save('map.html')" though, it does save the file and when I open it, it displays the json file on the map, but for some reason, the way I am doing it to show the map in my application is not working after adding the shp-->json file. What am I doing wrong?
推薦答案
正如這些問題中已經指出的(1和 2) 和 官方文檔:
As already pointed out in these questions(1 and 2) and in the official docs:
void QWebEnginePage::setHtml(const QString &html, const QUrl &baseUrl= QUrl())
將此頁面的內容設置為 html.baseUrl 是可選的,用于解析文檔中的相對 URL,例如引用的圖片或樣式表.
void QWebEnginePage::setHtml(const QString &html, const QUrl &baseUrl = QUrl())
Sets the content of this page to html. baseUrl is optional and used to resolve relative URLs in the document, such as referenced images or stylesheets.
html 會立即加載;加載外部對象異步.
The html is loaded immediately; external objects are loaded asynchronously.
如果 html 中的腳本運行時間超過默認腳本超時時間(當前為 10 秒),例如由于被模態阻塞JavaScript 警告對話框,此方法將盡快返回超時后,將加載任何后續 html異步.
If a script in the html runs longer than the default script timeout (currently 10 seconds), for example due to being blocked by a modal JavaScript alert dialog, this method will return as soon as possible after the timeout and any subsequent html will be loaded asynchronously.
使用此方法時,網絡引擎假定外部資源,例如 JavaScript 程序或樣式表,被編碼在除非另有說明,否則為 UTF-8.例如,一個編碼外部腳本可以通過 charset 屬性指定HTML 腳本標記.也可以指定編碼通過網絡服務器.
When using this method, the web engine assumes that external resources, such as JavaScript programs or style sheets, are encoded in UTF-8 unless otherwise specified. For example, the encoding of an external script can be specified through the charset attribute of the HTML script tag. It is also possible for the encoding to be specified by the web server.
這是一個等價于 setContent(html,"text/html", baseUrl).
This is a convenience function equivalent to setContent(html, "text/html", baseUrl).
注意:此方法不會影響會話或全局歷史記錄頁面.
Note: This method will not affect session or global history for the page.
警告:此功能僅適用于 HTML,適用于其他 mime 類型(例如作為 XHTML 和 SVG)應該使用 setContent().
Warning: This function works only for HTML, for other mime types (such as XHTML and SVG) setContent() should be used instead.
警告:內容在發送到通過 IPC 渲染器.這可能會增加其大小.的最大尺寸編碼內容百分比為 2 兆字節減去 30 字節.
(強調我的)
setHtml()
不支持大于 2MB 的內容,因此在您的特定情況下有 2 個解決方案:
setHtml()
does not support content greater than 2MB, so in your particular case there are 2 solutions:
將葉圖保存在 html 文件中:
Save the folium map in an html file:
import io
import os
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class LeafWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self.view = QtWebEngineWidgets.QWebEngineView()
shp_filename = os.path.join(CURRENT_DIR, "input", "2015_loaded_NoCC.shp")
shp_file = gpd.read_file(shp_filename)
shp_file_json_str = shp_file.to_json()
m = folium.Map(location=[40, -120], zoom_start=10)
folium.GeoJson(shp_file_json_str).add_to(m)
tmp_file = QtCore.QTemporaryFile("XXXXXX.html", self)
if tmp_file.open():
m.save(tmp_file.fileName())
url = QtCore.QUrl.fromLocalFile(tmp_file.fileName())
self.view.load(url)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.view)
def main():
app = QtWidgets.QApplication([])
w = LeafWidget()
w.show()
app.exec_()
if __name__ == "__main__":
main()
使用 QWebEngineUrlSchemeHandler 返回 html:
Use a QWebEngineUrlSchemeHandler to return the html:
qfolium.py
import json
import io
from PyQt5 import QtCore, QtWebEngineCore, QtWebEngineWidgets
class FoliumSchemeHandler(QtWebEngineCore.QWebEngineUrlSchemeHandler):
def __init__(self, app):
super().__init__(app)
self.m_app = app
def requestStarted(self, request):
url = request.requestUrl()
name = url.host()
m = self.m_app.process(name, url.query())
if m is None:
request.fail(QtWebEngineCore.QWebEngineUrlRequestJob.UrlNotFound)
return
data = io.BytesIO()
m.save(data, close_file=False)
raw_html = data.getvalue()
buf = QtCore.QBuffer(parent=self)
request.destroyed.connect(buf.deleteLater)
buf.open(QtCore.QIODevice.WriteOnly)
buf.write(raw_html)
buf.seek(0)
buf.close()
request.reply(b"text/html", buf)
class FoliumApplication(QtCore.QObject):
scheme = b"folium"
def __init__(self, parent=None):
super().__init__(parent)
scheme = QtWebEngineCore.QWebEngineUrlScheme(self.scheme)
QtWebEngineCore.QWebEngineUrlScheme.registerScheme(scheme)
self.m_functions = dict()
def init_handler(self, profile=None):
if profile is None:
profile = QtWebEngineWidgets.QWebEngineProfile.defaultProfile()
handler = profile.urlSchemeHandler(self.scheme)
if handler is not None:
profile.removeUrlSchemeHandler(handler)
self.m_handler = FoliumSchemeHandler(self)
profile.installUrlSchemeHandler(self.scheme, self.m_handler)
def register(self, name):
def decorator(f):
self.m_functions[name] = f
return f
return decorator
def process(self, name, query):
f = self.m_functions.get(name)
if f is None:
print("not found")
return
items = QtCore.QUrlQuery(query).queryItems()
params_json = dict(items).get("json", None)
if params_json is not None:
return f(**json.loads(params_json))
return f()
def create_url(self, name, params=None):
url = QtCore.QUrl()
url.setScheme(self.scheme.decode())
url.setHost(name)
if params is not None:
params_json = json.dumps(params)
query = QtCore.QUrlQuery()
query.addQueryItem("json", params_json)
url.setQuery(query)
return url
ma??in.py
import io
import os
import folium
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
import geopandas as gpd
from qfolium import FoliumApplication
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
folium_app = FoliumApplication()
@folium_app.register("load_shapefile")
def load_shapefile(latitude, longitude, zoom_start, shp_filename):
shp_file = gpd.read_file(shp_filename)
shp_file_json_str = shp_file.to_json()
m = folium.Map(
location=[latitude, longitude], zoom_start=zoom_start
)
folium.GeoJson(shp_file_json_str).add_to(m)
print(m)
return m
class LeafWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
QtWidgets.QWidget.__init__(self, parent)
self.view = QtWebEngineWidgets.QWebEngineView()
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.view)
self.resize(640, 480)
shp_filename = os.path.join(CURRENT_DIR, "input", "2015_loaded_NoCC.shp")
params = {
"shp_filename": shp_filename,
"latitude": 40,
"longitude": -120,
"zoom_start": 5,
}
url = folium_app.create_url("load_shapefile", params=params)
self.view.load(url)
def main():
app = QtWidgets.QApplication([])
folium_app.init_handler()
w = LeafWidget()
w.show()
app.exec_()
if __name__ == "__main__":
main()
這篇關于使用folium在python中添加一個大的shapefile來映射的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!