【问题标题】:Add a large shapefile to map in python using folium使用folium在python中添加一个大的shapefile来映射
【发布时间】:2021-10-10 11:27:00
【问题描述】:

我正在使用 python、PyQt5 和 Qt 设计器在我的应用程序中显示一个叶图。由于 Qt 设计器中没有地图小部件,我添加了一个通用小部件,然后将其提升到我的自定义地图小部件。一切正常。这是我推广的小部件的 python 代码:

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()

这很好,我可以在我的应用程序中看到地图。

我还试图在这张地图上显示一个 GIS shapefile。我做了一些研究,似乎我无法将 GIS shapefile (.shp) 直接添加到叶地图中。因此,我尝试先将其转换为 json,然后将 json 添加到地图顶部。我修改了我的代码,将 .shp 文件添加到地图:

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 文件后,我在应用程序中显示地图的方式不起作用。我做错了什么?

【问题讨论】:

    标签: python pyqt pyqt5 shapefile folium


    【解决方案1】:

    正如在这些问题(12)和the official docs 中已经指出的那样:

    void QWebEnginePage::setHtml(const QString &html, const QUrl &baseUrl = QUrl())

    将此页面的内容设置为 html。 baseUrl 是可选的,用于解析文档中的相对 URL,例如引用的 图片或样式表。

    html 会立即加载;加载外部对象 异步。

    如果 html 中的脚本运行时间超过默认脚本超时时间 (当前为 10 秒),例如由于被模态阻塞 JavaScript 警告对话框,此方法将尽快返回 超时后,将加载任何后续 html 异步。

    使用此方法时,网络引擎假定外部 资源,例如 JavaScript 程序或样式表,被编码在 除非另有说明,否则为 UTF-8。例如,一个编码 外部脚本可以通过 charset 属性指定 HTML 脚本标记。也可以指定编码 通过网络服务器。

    这是一个等价于 setContent(html, "text/html", baseUrl)。

    注意:此方法不会影响会话或全局历史记录 页面。

    警告:此功能仅适用于 HTML,适用于其他 mime 类型(例如 作为 XHTML 和 SVG)应该使用 setContent()。

    警告:内容在发送到 通过 IPC 渲染器。这可能会增加其大小。的最大尺寸 编码内容百分比为 2 兆字节减去 30 字节。

    (强调我的)

    setHtml() 不支持大于 2MB 的内容,因此在您的特定情况下有 2 个解决方案:

    • 将叶图保存在 html 文件中:

      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:

      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()
      

    【讨论】:

    • 哇。感谢您的回答。我使用了你的第一个解决方案,因为它更容易让我理解。你太棒了!
    猜你喜欢
    • 1970-01-01
    • 2022-08-17
    • 2021-11-11
    • 2015-09-15
    • 2016-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多