Skip to content Skip to sidebar Skip to footer

Add A Large Shapefile To Map In Python Using Folium

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 cu

Solution 1:

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

The html is loaded immediately; external objects are loaded asynchronously.

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.

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.

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.

Warning: This function works only for HTML, for other mime types (such as XHTML and SVG) setContent() should be used instead.

Warning: The content will be percent encoded before being sent to the renderer via IPC. This may increase its size. The maximum size of the percent encoded content is 2 megabytes minus 30 bytes.

(emphasis mine)

setHtml() does not support content greater than 2MB, so in your particular case there are 2 solutions:

  • 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__))
    
    
    classLeafWidget(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)
    
    
    defmain():
        app = QtWidgets.QApplication([])
        w = LeafWidget()
        w.show()
        app.exec_()
    
    
    if __name__ == "__main__":
        main()
    
  • Use a QWebEngineUrlSchemeHandler to return the html:

    qfolium.py

    import json
    import io
    
    from PyQt5 import QtCore, QtWebEngineCore, QtWebEngineWidgets
    
    
    classFoliumSchemeHandler(QtWebEngineCore.QWebEngineUrlSchemeHandler):
        def__init__(self, app):
            super().__init__(app)
            self.m_app = app
    
        defrequestStarted(self, request):
            url = request.requestUrl()
            name = url.host()
            m = self.m_app.process(name, url.query())
            if m isNone:
                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)
    
    
    classFoliumApplication(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()
    
        definit_handler(self, profile=None):
            if profile isNone:
                profile = QtWebEngineWidgets.QWebEngineProfile.defaultProfile()
            handler = profile.urlSchemeHandler(self.scheme)
            if handler isnotNone:
                profile.removeUrlSchemeHandler(handler)
    
            self.m_handler = FoliumSchemeHandler(self)
            profile.installUrlSchemeHandler(self.scheme, self.m_handler)
    
        defregister(self, name):
            defdecorator(f):
                self.m_functions[name] = f
                return f
    
            return decorator
    
        defprocess(self, name, query):
            f = self.m_functions.get(name)
            if f isNone:
                print("not found")
                return
    
            items = QtCore.QUrlQuery(query).queryItems()
            params_json = dict(items).get("json", None)
            if params_json isnotNone:
                return f(**json.loads(params_json))
            return f()
    
        defcreate_url(self, name, params=None):
            url = QtCore.QUrl()
            url.setScheme(self.scheme.decode())
            url.setHost(name)
            if params isnotNone:
                params_json = json.dumps(params)
                query = QtCore.QUrlQuery()
                query.addQueryItem("json", params_json)
                url.setQuery(query)
            return url
    

    main.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")defload_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
    
    
    classLeafWidget(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)
    
    
    defmain():
        app = QtWidgets.QApplication([])
        folium_app.init_handler()
        w = LeafWidget()
        w.show()
        app.exec_()
    
    
    if __name__ == "__main__":
        main()
    

Post a Comment for "Add A Large Shapefile To Map In Python Using Folium"