{"id":1861,"date":"2024-11-19T14:05:33","date_gmt":"2024-11-19T14:05:33","guid":{"rendered":"https:\/\/www.pyxll.com\/blog\/?p=1861"},"modified":"2024-11-19T16:08:28","modified_gmt":"2024-11-19T16:08:28","slug":"geospatial-plots-in-excel-with-folium-python-and-pyxll","status":"publish","type":"post","link":"https:\/\/www.pyxll.com\/blog\/geospatial-plots-in-excel-with-folium-python-and-pyxll\/","title":{"rendered":"Geospatial plots in Excel with Folium, Python, and PyXLL"},"content":{"rendered":"\n<p>Excel is an incredibly versatile tool, but its built-in charting options are limited when it comes to geospatial data visualization. Thankfully, with Python, you can tap into powerful libraries like Folium to create interactive geospatial plots in Excel. By integrating Python into Excel with PyXLL, you can seamlessly bring these visualizations into your spreadsheets.<\/p>\n\n\n\n<p>In this post, we\u2019ll explore how to use Folium, Python, and PyXLL to create and display geospatial maps directly in Excel.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-why-folium\">Why Folium?<\/h2>\n\n\n\n<p><a href=\"https:\/\/python-visualization.github.io\/folium\/latest\/\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>Folium<\/strong><\/a> is a Python library built on the popular <a href=\"https:\/\/leafletjs.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Leaflet.js<\/a> framework. It allows you to create beautiful and interactive maps with minimal code. From simple point plotting to complex choropleth maps, Folium makes geospatial data accessible and visually stunning.<\/p>\n\n\n\n<p>By embedding these maps into Excel using <a href=\"https:\/\/www.pyxll.com\" target=\"_blank\" rel=\"noreferrer noopener\">PyXLL<\/a>, you can deliver geospatial insights in a tool familiar to almost everyone\u2014no need for additional software installations.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting Up Your Environment<\/h2>\n\n\n\n<p>To get started, you\u2019ll need:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Python installed with the following libraries:<\/strong>\n<ul class=\"wp-block-list\">\n<li><code>folium<\/code><\/li>\n\n\n\n<li><code>pandas<\/code> (for handling geospatial data)<\/li>\n\n\n\n<li><code>pyxll<\/code><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>PyXLL installed and configured in Excel.<\/strong><br>PyXLL lets you call Python functions directly from Excel and return results, including images, charts, or even interactive HTML content.<br>See <a href=\"https:\/\/www.pyxll.com\/docs\/userguide\/installation\/firsttime.html\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/www.pyxll.com\/docs\/userguide\/installation\/firsttime.html<\/a> for details of how to install PyXLL.<\/li>\n<\/ol>\n\n\n\n<p>I also recommend using a Python code editor such as VS Code to help write Python code, but any text editor will do.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Create a Basic Folium Map<\/h2>\n\n\n\n<p>Here\u2019s a simple Python script to generate a map using Folium. The code first loads a CSV file containing a value for the ecological footprint for each country, creates a map using data from geojson.xyz, adds a choropleth layer to the map, and finally saves the map as an html file.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nimport folium\nimport pandas as pd\n\neco_footprints = pd.read_csv(&quot;footprint.csv&quot;)\n\npolitical_countries_url = (\n    &quot;http:\/\/geojson.xyz\/naturalearth-3.3.0\/ne_50m_admin_0_countries.geojson&quot;\n)\n\nm = folium.Map(location=(30, 10), zoom_start=3, tiles=&quot;cartodb positron&quot;)\nfolium.GeoJson(political_countries_url).add_to(m)\n\nfolium.Choropleth(\n    geo_data=political_countries_url,\n    data=eco_footprints,\n    columns=&#x5B;&quot;Country\/region&quot;, &quot;Ecological footprint&quot;],\n    key_on=&quot;feature.properties.name&quot;,\n).add_to(m)\n\nm.save(&quot;footprint.html&quot;)\n<\/pre><\/div>\n\n\n<p>The CSV file contains the information as it\u2019s\u00a0<a href=\"https:\/\/en.wikipedia.org\/w\/index.php?title=List_of_countries_by_ecological_footprint&amp;diff=cur&amp;oldid=1124910714\">currently shown<\/a>\u00a0on Wikipedia, which is based on data gathered by\u00a0<a href=\"https:\/\/footprint.info.yorku.ca\/\">York University<\/a>, the\u00a0<a href=\"https:\/\/www.fodafo.org\/\">Footprint Data Foundation<\/a>, and the\u00a0<a href=\"https:\/\/www.footprintnetwork.org\/\">Global Footprint Network<\/a>.<\/p>\n\n\n\n<div class=\"wp-block-file\"><a id=\"wp-block-file--media-f831220c-eac5-49eb-ae25-6bf1c90e15e3\" href=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/footprint.csv\">footprint.csv<\/a><a href=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/footprint.csv\" class=\"wp-block-file__button wp-element-button\" download aria-describedby=\"wp-block-file--media-f831220c-eac5-49eb-ae25-6bf1c90e15e3\">Download<\/a><\/div>\n\n\n\n<p>The output file shows the data from the CSV file as a map.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image.png\"><img decoding=\"async\" width=\"1024\" height=\"632\" data-attachment-id=\"1864\" data-permalink=\"https:\/\/www.pyxll.com\/blog\/geospatial-plots-in-excel-with-folium-python-and-pyxll\/image-22\/\" data-orig-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image.png\" data-orig-size=\"1402,865\" data-comments-opened=\"0\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-1024x632.png\" src=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-1024x632.png\" alt=\"\" class=\"wp-image-1864\" srcset=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-1024x632.png 1024w, https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-300x185.png 300w, https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-768x474.png 768w, https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image.png 1402w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Embed the Map in Excel with PyXLL<\/h2>\n\n\n\n<p>PyXLL makes it easy to embed HTML maps directly into Excel as web views or static images. To display your Folium map in Excel, follow these steps:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Write a Python function that generates the map.<\/strong><\/li>\n\n\n\n<li><strong>Create a &#8220;bridge&#8221; class so PyXLL can understand folium map objects.<\/strong><\/li>\n\n\n\n<li><strong>Call the function from Excel using PyXLL.<\/strong><\/li>\n<\/ol>\n\n\n\n<p>For the function that generates the map, instead of reading a CSV file we will pass in the data to the function. Since we&#8217;ll be calling this function from Excel that makes it much more flexible as the user of the function can pass in data directly from the Excel worksheet.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; highlight: [1,6,25]; title: ; notranslate\" title=\"\">\nfrom pyxll import xl_func, plot\nimport folium\nimport pandas as pd\n\n\n@xl_func\ndef plot_choropleth(data: pd.DataFrame,\n                                 countries_column: str,\n                                 values_column: str):\n\n    political_countries_url = (\n        &quot;http:\/\/geojson.xyz\/naturalearth-3.3.0\/ne_50m_admin_0_countries.geojson&quot;\n    )\n\n    m = folium.Map(location=(30, 10), zoom_start=3, tiles=&quot;cartodb positron&quot;)\n    folium.GeoJson(political_countries_url).add_to(m)\n\n    folium.Choropleth(\n        geo_data=political_countries_url,\n        data=data,\n        columns=&#x5B;countries_column, values_column],\n        key_on=&quot;feature.properties.name&quot;,\n    ).add_to(m)\n\n    plot(m)\n\n    return m\n<\/pre><\/div>\n\n\n<p>The code is the same as in the previous section, but instead of loading the data from a CSV file it now takes it as a pandas DataFrame argument. <\/p>\n\n\n\n<p>The columns in the DataFrame containing the country names and the value we want to plot are also passed as arguments, allowing us to use the same function for different data sets more easily.<\/p>\n\n\n\n<p>The function is decorated with the &#8220;@xl_func&#8221; decorator from the pyxll package. This tells the PyXLL add-in this function should be exposed to Excel and be callable as a worksheet function.<\/p>\n\n\n\n<p>Rather than saving the map as an HTML file we use the &#8220;pyxll.plot&#8221; function to display the plot in Excel. However, as our code is written now this will not work as PyXLL won&#8217;t recognize the &#8220;folium.Map&#8221; object that we are passing to pyxll.plot.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-bridging-pyxll-and-folium\">Bridging PyXLL and Folium<\/h4>\n\n\n\n<p>In order for PyXLL to understand how to display a folium map we need to write what PyXLL calls a &#8220;plotting bridge&#8221;. This might sound complicated, but really it&#8217;s quite simple. The bridge class simply takes the object being plotted and provides a way of converting that to another format. In our case we can convert it to HTML using the &#8220;save&#8221; method we saw earlier.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; title: ; notranslate\" title=\"\">\nfrom pyxll import xl_plot_bridge, PlotBridgeBase\n\n# To display a plot of type &#039;folium.folium.Map&#039; we need a custom bridge\n# that handles exporting the map to an html file\n@xl_plot_bridge(&quot;folium.folium.Map&quot;)\nclass FoliumBridge(PlotBridgeBase):\n\n    def __init__(self, map):\n        self.__map = map\n\n    # PyXLL can use various different formats, but we are only interested in html\n    def can_export(self, format):\n        return format == &quot;html&quot;\n\n    # PyXLL will call this to export the map object as an html file\n    def export(self, width, height, dpi, format, filename, **kwargs):\n        self.__map.save(filename)\n<\/pre><\/div>\n\n\n<p>The bridge class is registered using the &#8220;@xl_plot_bridge&#8221; decorator, and now any time we call &#8220;pyxll.plot&#8221; with a folium Map object this bridge class will be used to handle exporting it as html.<\/p>\n\n\n\n<p>This code needs to be added to the list of modules imported by PyXLL by adding it to the &#8220;modules&#8221; setting in the pyxll.cfg file. It can in the same module as the &#8220;plot_choropleth&#8221; function written above, but I prefer to keep it in a separate module since it can be used by any functions using folium.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"h-calling-the-function-and-plotting-our-map-in-excel\">Calling the function and plotting our map in Excel<\/h4>\n\n\n\n<p>Now we have everything we need to test out our function in Excel!<\/p>\n\n\n\n<p>If you&#8217;ve been following along, don&#8217;t forget to save your Python module (or modules) and add them to the &#8220;modules&#8221; setting in your pyxl.cfg file. You might also need to add the folder where you&#8217;ve saved your modules to the &#8220;pythonpath&#8221; setting.<\/p>\n\n\n\n<p>With PyXLL configured to load our code, either start Excel or if you already have Excel running, select &#8220;Reload&#8221; from the PyXLL menu. Now you should have a new function &#8220;plot_choropleth&#8221; available to you as a worksheet function!<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-1.png\"><img decoding=\"async\" width=\"1024\" height=\"638\" data-attachment-id=\"1869\" data-permalink=\"https:\/\/www.pyxll.com\/blog\/geospatial-plots-in-excel-with-folium-python-and-pyxll\/image-23\/\" data-orig-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-1.png\" data-orig-size=\"1830,1141\" data-comments-opened=\"0\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-1-1024x638.png\" src=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-1-1024x638.png\" alt=\"\" class=\"wp-image-1869\" srcset=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-1-1024x638.png 1024w, https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-1-300x187.png 300w, https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-1-768x479.png 768w, https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-1-1536x958.png 1536w, https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/image-1.png 1830w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>To call the function enter it into a cell in the worksheet, passing in the range of data as the first argument, the column header for the countries in the second, and the column header for the ecological footprint in the third.<\/p>\n\n\n\n<p>The map is added to the Excel worksheet just below the function. It&#8217;s a fully interactive html map, not just a static image! I&#8217;ve added a little more formatting to the function, but this is still a fairly simple example of what you can do using folium in Excel.<\/p>\n\n\n\n<p>All of the code used in this blog post, along with the spreadsheet, is available for you to download.<\/p>\n\n\n\n<div class=\"wp-block-file\"><a id=\"wp-block-file--media-2b529e0e-92bd-4d2e-9285-185885609832\" href=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/folium_example.zip\">folium_example.zip<\/a><a href=\"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/folium_example.zip\" class=\"wp-block-file__button wp-element-button\" download aria-describedby=\"wp-block-file--media-2b529e0e-92bd-4d2e-9285-185885609832\">Download<\/a><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Enhancing the Workflow<\/h2>\n\n\n\n<p>Once you\u2019ve mastered the basics, you can:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Add dynamic layers<\/strong> such as heatmaps or GeoJSON overlays.<\/li>\n\n\n\n<li><strong>Link maps to Excel data<\/strong> so they update dynamically based on cell values.<\/li>\n\n\n\n<li><strong>Create interactive dashboards<\/strong> combining Excel&#8217;s strengths with Python&#8217;s geospatial libraries.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Integrating Folium maps into Excel with PyXLL transforms how you visualize and interact with geospatial data. Whether you\u2019re mapping customer locations, analyzing environmental data, or presenting demographic trends, this powerful combination unlocks new possibilities.<\/p>\n\n\n\n<p>With just a few lines of Python and the power of PyXLL, your Excel spreadsheets can become dynamic geospatial visualization tools.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Excel is an incredibly versatile tool, but its built-in charting options are limited when it comes to geospatial data visualization. Thankfully, with Python, you can tap into powerful libraries like Folium to create interactive geospatial plots in Excel. By integrating Python into Excel with PyXLL, you can seamlessly bring these visualizations into your spreadsheets. In<\/p>\n","protected":false},"author":1,"featured_media":1873,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":true,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[12,2,504,272],"tags":[6,663,664,666,665,3,8],"class_list":["post-1861","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-python","category-pyxll","category-user-interface","category-visualization","tag-excel","tag-folium","tag-geospatial","tag-maps","tag-plotting","tag-python","tag-pyxll"],"acf":[],"jetpack_featured_media_url":"https:\/\/www.pyxll.com\/blog\/wp-content\/uploads\/2024\/11\/folium.gif","jetpack_shortlink":"https:\/\/wp.me\/p7l3LP-u1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/posts\/1861","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/comments?post=1861"}],"version-history":[{"count":1,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/posts\/1861\/revisions"}],"predecessor-version":[{"id":1880,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/posts\/1861\/revisions\/1880"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/media\/1873"}],"wp:attachment":[{"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/media?parent=1861"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/categories?post=1861"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pyxll.com\/blog\/wp-json\/wp\/v2\/tags?post=1861"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}