Leaflet maps with data from Google Sheets - Chris Arderne

<div readability="5"> <head> <meta charset="utf-8"/><title>Leaflet maps with data from Google Sheets - Chris Arderne</title> <meta name="description" content="I love working with Python, but as soon as you want to put something online, JavaScript’s ability to process in the browser is a clear winner. In this post I’m going to go through making a web map in JavaScript that pulls data from Google Sheets – where non-coders can easily make updates."/> <meta property="og:type" content="article"/> <meta property="og:locale" content="enUS"/> <meta property="og:sitename" content="Chris Arderne"/> <meta property="og:title" content="Leaflet maps with data from Google Sheets"/> <meta property="og:url" content="https://rdrn.me/leaflet-maps-google-sheets/"/><meta property="og:description" content="I love working with Python, but as soon as you want to put something online, JavaScript’s ability to process in the browser is a clear winner. In this post I’m going to go through making a web map in JavaScript that pulls data from Google Sheets – where non-coders can easily make updates."/><meta property="article:publishedtime" content="2018-10-19T17:51:19+00:00"/><link rel="canonical" href="https://rdrn.me/leaflet-maps-google-sheets/"/> <link rel="alternate" href="https://rdrn.me/leaflet-maps-google-sheets/feed.xml" type="application/rss+xml" title="Chris Arderne"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> </head> <body id="readabilityBody" readability="141.03595397891"> <nav> </nav> <header> <span class="page-title vert-center">Leaflet maps with data from Google Sheets </span> <span class="page-date vert-center"><time datetime="2018-10-19T17:51:19+00:00">19 Oct 2018</time></span> </header> <p><em>I wrote another <a href="https://rdrn.me/python-mapping-automatic-updating/" style="color: inherit; text-decoration: none;" name="readabilityLink-1">post here</a><a href="#readabilityFootnoteLink-1" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[1]</sup></small></a>, outlining how this can be achieved without stepping out of a Python environment.</em></p> <p>I love working with Python, but as soon as you want to put something online, JavaScript’s ability to process in the browser is a clear winner. For one, you can have statically served HTML files performing complex tasks, whereas with Python you’d need a cloud instance running and a system for communicating between front- and back-end.</p> <p>In this post I’m going to go through making a web map in JavaScript that pulls data from Google Sheets – where non-coders can easily make updates. This seems to be a common request and something that is not well covered by the various GUI solutions available. There is a <a href="https://www.datavizforall.org/leaflet/with-google-sheets/" style="color: inherit; text-decoration: none;" name="readabilityLink-2">Data Visualization for All</a><a href="#readabilityFootnoteLink-2" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[2]</sup></small></a> guide, but it didn’t seem to work well (potentially due to changes in Google’s API) and is overly complex.</p> <p><em>Skip the first two sections if you just want the juicy stuff, or go straight to <a href="https://github.com/carderne/leaflet-gsheets" style="color: inherit; text-decoration: none;" name="readabilityLink-3">the repo</a><a href="#readabilityFootnoteLink-3" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[3]</sup></small></a>.</em></p> <h2 id="client-side-programming">Client-side programming</h2> <p>JavaScript is unique in being used on both the client- and server-sides of a web application. This means it can run directly in a user’s browser without anything happening on the server, but can also be used on the back-end (via <a href="https://nodejs.org/" style="color: inherit; text-decoration: none;" name="readabilityLink-4">Node.js</a><a href="#readabilityFootnoteLink-4" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[4]</sup></small></a> for moving data around and doing the heavy lifting. For this example, I’m basically taking advantage of the user’s computer to do the processing, so that I don’t have to pay <a href="https://aws.amazon.com/" style="color: inherit; text-decoration: none;" name="readabilityLink-5">Amazon</a><a href="#readabilityFootnoteLink-5" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[5]</sup></small></a> and friends to do it for me on a cloud instance. This dual nature of JavaScript is one of the reasons for its booming popularity in web development.</p> <h2 id="mapping-libraries-available-in-javascript">Mapping libraries available in JavaScript</h2> <p>Another reason for JavaScript’s ubiquity is the endless variety of libraries available, and the ease with which these can be used, thanks, in part, to <a href="https://blog.codinghorror.com/the-principle-of-least-power/" style="color: inherit; text-decoration: none;" name="readabilityLink-6">Atwood’s Law</a><a href="#readabilityFootnoteLink-6" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[6]</sup></small></a>:</p> <blockquote readability="6"> <p>any application that can be written in JavaScript, will eventually be written in JavaScript</p> </blockquote> <p>As for web mapping, there are a few obvious choices:</p> <ul> <li><a href="https://openlayers.org/" style="color: inherit; text-decoration: none;" name="readabilityLink-7">OpenLayers</a><a href="#readabilityFootnoteLink-7" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[7]</sup></small></a> – the most mature and powerful option, but also heavy and quite complex to use</li> <li><a href="https://developers.google.com/maps/documentation/javascript/tutorial" style="color: inherit; text-decoration: none;" name="readabilityLink-8">Google Maps JavaScript API</a><a href="#readabilityFootnoteLink-8" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[8]</sup></small></a> – limited basemap options and it’s closed source (noting the irony that this post is all about Google Sheets)</li> <li><a href="https://www.mapbox.com/mapbox-gl-js/api/" style="color: inherit; text-decoration: none;" name="readabilityLink-9">Mapbox GL JS</a><a href="#readabilityFootnoteLink-9" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[9]</sup></small></a> (or the older <a href="https://www.mapbox.com/help/define-mapbox-js/" style="color: inherit; text-decoration: none;" name="readabilityLink-10">Mapbox.js</a><a href="#readabilityFootnoteLink-10" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[10]</sup></small></a>) – great for integrating with <a href="https://www.mapbox.com/mapbox-studio/" style="color: inherit; text-decoration: none;" name="readabilityLink-11">Mapbox Studio</a><a href="#readabilityFootnoteLink-11" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[11]</sup></small></a> (designing basemaps) and their <a href="https://www.mapbox.com/geocoding/" style="color: inherit; text-decoration: none;" name="readabilityLink-12">geocoding</a><a href="#readabilityFootnoteLink-12" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[12]</sup></small></a> service</li> <li><a href="https://leafletjs.com/" style="color: inherit; text-decoration: none;" name="readabilityLink-13">Leaflet</a><a href="#readabilityFootnoteLink-13" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[13]</sup></small></a> – light-weight, well-documented and easy to use for simple projects – goldilocks!</li> </ul> <p>You can read a more complete <a href="http://ledeprogram.com/2015/absolutely-everything-you-need-to-know-about-mapping-tools/" style="color: inherit; text-decoration: none;" name="readabilityLink-14">overview here</a><a href="#readabilityFootnoteLink-14" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[14]</sup></small></a>; it’s a bit out of date, but includes comparisons with non JavaScript solutions such as <a href="https://carto.com/" style="color: inherit; text-decoration: none;" name="readabilityLink-15">Carto</a><a href="#readabilityFootnoteLink-15" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[15]</sup></small></a> (not cheap, unfortunately).</p> <h2 id="pulling-data-from-google-sheets">Pulling data from Google Sheets</h2> <p><em>In all the code excerpts, I’m stripping it to the bare essentials for readability. The full code is available at the <a href="https://github.com/carderne/leaflet-gsheets" style="color: inherit; text-decoration: none;" name="readabilityLink-16">repo</a><a href="#readabilityFootnoteLink-16" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[16]</sup></small></a>.</em></p> <p>To make maps from Google Sheets, we first need to get data from Google Sheets. This is made exceedingly easy by <a href="https://github.com/jsoma/tabletop" style="color: inherit; text-decoration: none;" name="readabilityLink-17">Tabletop.js</a><a href="#readabilityFootnoteLink-17" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[17]</sup></small></a>, a simple library for pulling in entire sheets as JSON objects. This is done in a few lines of code:</p> <div class="language-plaintext highlighter-rouge" readability="34"><div class="highlight" readability="13"><pre class="highlight"><code>function init() { Tabletop.init({ key: sheetsUrl, callback: myFunction, simpleSheet: true }) } window.addEventListener('DOMContentLoaded', init) function myFunction(data, tabletop) { console.log(data); } </code></pre></div></div> <p>You just need to get the public sharing link from your Sheet (follow the instructions at the Tabletop.js repo) and assign it to <code class="language-plaintext highlighter-rouge">sheetsUrl</code> and you’re done!</p> <p>The data I pulled in for this web map was two separate tables, which you can preview <a href="https://docs.google.com/spreadsheets/d/1kjJVPF0LyaiaDYF8zx23UulGciGtBALQ1a1pK0coRM/edit?usp=sharing" style="color: inherit; text-decoration: none;" name="readabilityLink-18">here</a><a href="#readabilityFootnoteLink-18" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[18]</sup></small></a> and <a href="https://docs.google.com/spreadsheets/d/1EUFSaqi30b6oefK0YWWNDDOzwmCTTXlXkFHAc2QrUxM/edit?usp=sharing" style="color: inherit; text-decoration: none;" name="readabilityLink-19">here</a><a href="#readabilityFootnoteLink-19" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[19]</sup></small></a>. The first has simple <code class="language-plaintext highlighter-rouge">lat</code> and <code class="language-plaintext highlighter-rouge">long</code> coordinates for a few points, while the second has a more complicated <code class="language-plaintext highlighter-rouge">geometry</code> column with polygon representations of each US state. In addition they each have extra columns with more information.</p> <h2 id="putting-it-together-for-an-easy-web-map">Putting it together for an easy web map</h2> <p>My objective for this web map was to show these point and polygon items on a map of the US, with pop-ups for each element showing additional information. With a few more steps, it’s easy to style each element based on other columns in the data.</p> <p>Firstly, let’s create a basic HTML file to hold our map. The example below provides the bare minimum of importing Tabletop.js, Leaflet.js and the Leaflet CSS styling. It then creates a <code class="language-plaintext highlighter-rouge">div</code> with <code class="language-plaintext highlighter-rouge">id="map"</code>, which is where our map will go, and then imports <code class="language-plaintext highlighter-rouge">leaflet-example.js</code>, which is where our new JavaScript code goes.</p> <div class="language-plaintext highlighter-rouge" readability="32.5"><div class="highlight" readability="10"><pre class="highlight"><code>&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;script src='https://cdnjs.cloudflare.com/ajax/libs/tabletop.js/1.5.1/tabletop.min.js'&gt;&lt;/script&gt; &lt;link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css"/&gt; &lt;script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet.js"&gt;&lt;/script&gt; &lt;/head&gt; &lt;body&gt; &lt;div id="map"&gt;&lt;/div&gt; &lt;script type="text/javascript" src="leaflet-example.js"&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </code></pre></div></div> <p>With that set up, let’s create a Leaflet map and insert it into our <code class="language-plaintext highlighter-rouge">div</code>, and add a beautiful basemap (basically a background map). A number of basemap options are demoed here [http://leaflet-extras.github.io/leaflet-providers/preview/] – just copy the provided URL into <code class="language-plaintext highlighter-rouge">baseMapURL</code> in the code below (and add the suggested attributions).</p> <div class="language-plaintext highlighter-rouge" readability="33"><div class="highlight" readability="11"><pre class="highlight"><code>var map = L.map('map-div').setView([startLat, startLong], startZoom); var basemap = L.tileLayer(baseMapURL, { attribution: attributionText }); basemap.addTo(map); </code></pre></div></div> <p>Next we need to add things to the map! The points are easier, and we can add them by simply looping through each item in the JSON and adding a Leaflet <code class="language-plaintext highlighter-rouge">marker</code>. This function is called by the init() function from further up, once the data has been retrieved from Google Sheets.</p> <div class="language-plaintext highlighter-rouge" readability="32.5"><div class="highlight" readability="10"><pre class="highlight"><code>function addPoints(data, tabletop) { for (var row in data) { var marker = L.marker([ data[row].lat, data[row].long ]).addTo(map); marker.bindPopup(data[row].category); } } </code></pre></div></div> <p>The polygons are slightly more complicated, as Leaflet needs a GeoJSON object to represent them. So in a new function like the previous one (also called by the <code class="language-plaintext highlighter-rouge">init()</code> function), we have the following code to create a single GeoJSON containing all of our polygons.</p> <div class="language-plaintext highlighter-rouge" readability="35.5"><div class="highlight" readability="16"><pre class="highlight"><code>function addPolygons(data, tabletop) { // the empty GeoJSON waiting to be populated with features var polygons = { "type": "FeatureCollection", "features": [] } for (var row in data) { // JSON.parse converts the geometry strings into JSON objects var coords = JSON.parse(data[row].geometry); polygons.features.push({ "type": "Feature", "geometry": { "type": "MultiPolygon", "coordinates": coords }, "properties": { "name": data[row].name, } }); } } </code></pre></div></div> <p>This sets up an empty GeoJSON and then loops through each element in <code class="language-plaintext highlighter-rouge">data</code> and inserts the coordinates and names as <code class="language-plaintext highlighter-rouge">Feature</code> elements within the GeoJSON. With this set up, it’s just a few lines to add this new object to our Leaflet map. The lines below can be added at the bottom of the <code class="language-plaintext highlighter-rouge">addPolygon</code> function for simplicity.</p> <div class="language-plaintext highlighter-rouge" readability="33"><div class="highlight" readability="11"><pre class="highlight"><code>polygonMarkers = L.geoJSON(polygons, { onEachFeature: function (feature, layer) { layer.bindPopup(feature.properties.name); }, }).addTo(map); </code></pre></div></div> <p>As for the points further up, this includes the code to add a popup, but nothing on styling. That’s probably a post for another day, but you can have a look at the <a href="https://github.com/carderne/leaflet-gsheets" style="color: inherit; text-decoration: none;" name="readabilityLink-20">repo</a><a href="#readabilityFootnoteLink-20" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[20]</sup></small></a> if you want to see what I used for this example.</p> <p>And we’re done! The result (with styling) is shown below, or click <a href="https://rdrn.me/leaflet-gsheets" style="color: inherit; text-decoration: none;" name="readabilityLink-21">here</a><a href="#readabilityFootnoteLink-21" class="readability-DoNotFootnote" style="color: inherit;"><small><sup>[21]</sup></small></a> to see it full screen. Every time a user loads this map in their browser, it will automatically hop over to the specified Google Sheets and pull the latest data to display it.</p> </body> </div><div id="readability-footnotes"><h3>References</h3><ol id="readability-footnotes-list"><li><small><sup><a href="#readabilityLink-1" title="Jump to Link in Article">^</a></sup></small> <a href="https://rdrn.me/python-mapping-automatic-updating/" name="readabilityFootnoteLink-1">post here</a><small> (rdrn.me)</small></li><li><small><sup><a href="#readabilityLink-2" title="Jump to Link in Article">^</a></sup></small> <a href="https://www.datavizforall.org/leaflet/with-google-sheets/" name="readabilityFootnoteLink-2">Data Visualization for All</a><small> (www.datavizforall.org)</small></li><li><small><sup><a href="#readabilityLink-3" title="Jump to Link in Article">^</a></sup></small> <a href="https://github.com/carderne/leaflet-gsheets" name="readabilityFootnoteLink-3">the repo</a><small> (github.com)</small></li><li><small><sup><a href="#readabilityLink-4" title="Jump to Link in Article">^</a></sup></small> <a href="https://nodejs.org/" name="readabilityFootnoteLink-4">Node.js</a><small> (nodejs.org)</small></li><li><small><sup><a href="#readabilityLink-5" title="Jump to Link in Article">^</a></sup></small> <a href="https://aws.amazon.com/" name="readabilityFootnoteLink-5">Amazon</a><small> (aws.amazon.com)</small></li><li><small><sup><a href="#readabilityLink-6" title="Jump to Link in Article">^</a></sup></small> <a href="https://blog.codinghorror.com/the-principle-of-least-power/" name="readabilityFootnoteLink-6">Atwood’s Law</a><small> (blog.codinghorror.com)</small></li><li><small><sup><a href="#readabilityLink-7" title="Jump to Link in Article">^</a></sup></small> <a href="https://openlayers.org/" name="readabilityFootnoteLink-7">OpenLayers</a><small> (openlayers.org)</small></li><li><small><sup><a href="#readabilityLink-8" title="Jump to Link in Article">^</a></sup></small> <a href="https://developers.google.com/maps/documentation/javascript/tutorial" name="readabilityFootnoteLink-8">Google Maps JavaScript API</a><small> (developers.google.com)</small></li><li><small><sup><a href="#readabilityLink-9" title="Jump to Link in Article">^</a></sup></small> <a href="https://www.mapbox.com/mapbox-gl-js/api/" name="readabilityFootnoteLink-9">Mapbox GL JS</a><small> (www.mapbox.com)</small></li><li><small><sup><a href="#readabilityLink-10" title="Jump to Link in Article">^</a></sup></small> <a href="https://www.mapbox.com/help/define-mapbox-js/" name="readabilityFootnoteLink-10">Mapbox.js</a><small> (www.mapbox.com)</small></li><li><small><sup><a href="#readabilityLink-11" title="Jump to Link in Article">^</a></sup></small> <a href="https://www.mapbox.com/mapbox-studio/" name="readabilityFootnoteLink-11">Mapbox Studio</a><small> (www.mapbox.com)</small></li><li><small><sup><a href="#readabilityLink-12" title="Jump to Link in Article">^</a></sup></small> <a href="https://www.mapbox.com/geocoding/" name="readabilityFootnoteLink-12">geocoding</a><small> (www.mapbox.com)</small></li><li><small><sup><a href="#readabilityLink-13" title="Jump to Link in Article">^</a></sup></small> <a href="https://leafletjs.com/" name="readabilityFootnoteLink-13">Leaflet</a><small> (leafletjs.com)</small></li><li><small><sup><a href="#readabilityLink-14" title="Jump to Link in Article">^</a></sup></small> <a href="http://ledeprogram.com/2015/absolutely-everything-you-need-to-know-about-mapping-tools/" name="readabilityFootnoteLink-14">overview here</a><small> (ledeprogram.com)</small></li><li><small><sup><a href="#readabilityLink-15" title="Jump to Link in Article">^</a></sup></small> <a href="https://carto.com/" name="readabilityFootnoteLink-15">Carto</a><small> (carto.com)</small></li><li><small><sup><a href="#readabilityLink-16" title="Jump to Link in Article">^</a></sup></small> <a href="https://github.com/carderne/leaflet-gsheets" name="readabilityFootnoteLink-16">repo</a><small> (github.com)</small></li><li><small><sup><a href="#readabilityLink-17" title="Jump to Link in Article">^</a></sup></small> <a href="https://github.com/jsoma/tabletop" name="readabilityFootnoteLink-17">Tabletop.js</a><small> (github.com)</small></li><li><small><sup><a href="#readabilityLink-18" title="Jump to Link in Article">^</a></sup></small> <a href="https://docs.google.com/spreadsheets/d/1kjJVPF0LyaiaDYF8z_x23UulGciGtBALQ1a1pK0coRM/edit?usp=sharing" name="readabilityFootnoteLink-18">here</a><small> (docs.google.com)</small></li><li><small><sup><a href="#readabilityLink-19" title="Jump to Link in Article">^</a></sup></small> <a href="https://docs.google.com/spreadsheets/d/1EUFSaqi30b6oefK0YWWNDDOzwmCTTXlXkFHAc2QrUxM/edit?usp=sharing" name="readabilityFootnoteLink-19">here</a><small> (docs.google.com)</small></li><li><small><sup><a href="#readabilityLink-20" title="Jump to Link in Article">^</a></sup></small> <a href="https://github.com/carderne/leaflet-gsheets" name="readabilityFootnoteLink-20">repo</a><small> (github.com)</small></li><li><small><sup><a href="#readabilityLink-21" title="Jump to Link in Article">^</a></sup></small> <a href="https://rdrn.me/leaflet-gsheets" name="readabilityFootnoteLink-21">here</a><small> (rdrn.me)</small></li></ol></div>

keywords

No Items Found.

Add Comment
Type in a Nick Name here
 
Search Linx
Search Linx by entering your search text above.
Welcome

This is my test area for webdev. I keep a collection of code here, mostly for my reference. Also if i find a good link, i usually add it here and then forget about it. more...

You could also follow me on twitter. I have a couple of youtube channels if you want to see some video related content. RuneScape 3, Minecraft and also a coding channel here Web Dev.

If you found something useful or like my work, you can buy me a coffee here. Mmm Coffee. ☕

❤️👩‍💻🎮

🪦 2000 - 16 Oct 2022 - Boots
Random Quote
Most people can do absolutely awe-inspiring things,” he said. “Sometimes they just need a little nudge.
Unknown
Latest News
## 🚀 AI Giants Hit Bullseye: Anthropic & OpenAI Achieve Product-Market Fit Anthropic and OpenAI have reached a significant milestone, finding product-market fit with their AI technologies, which means their products effectively meet the needs of their customers, driving growth and adoption. This achievement showcases the practical value of their innovations, enabling businesses and individuals to leverage AI for enhanced productivity and efficiency. With this alignment of product and market needs, these companies are poised to transform industries and shape the future of technology.