Tuesday, August 12, 2014

Blogger Map widget, for showing geotagged blog posts on a map


This post will show you how to add a side-bar widget to your Blogger/Blogspot blog. The map will display pushpins at each of your location-tagged posts' locations. Click the pins, and a small card appears with the title, date, and a link to the post.
If you'd like the cards/pins to show the full post entries, click here for my other post.

UPDATED June 2015
You can embed the map in a page too, not just as a widget. 

Location-tagged Blogger posts


I like using Blogger, and I always geotag the posts on my travel blog using the "Location" setting in the right-hand side of the Blogger compose page.

Blogger's "Location" tag feature



This adds a nice "Location" line under my posts that displays the address of my blog post's geotag, and clicking it opens a Google Map of that location.

Until today, I thought that was all you could do with this feature, which seemed a bit disappointing. Ideally, I was hoping there was a way to display, on one single map, the locations of all my blog posts. Sort of like how you can visualize all your Foursquare checkins with its KML feed, I wanted to treat my Blogger blog posts as "Check-ins" and see where all I've been.

It turns out that this is not only possible, but also very easy to do. The technology behind it seems ancient (2008) but I just tested it out (2014) and it works beautifully!

Adding the widget from the original source (easy)


The most simple way is just to follow the directions from this old "Blogger in Draft" post:

  1. Go to Blogger's "Layout" editing section
  2. Add a new Gadget
  3. Add by URL
  4. Paste this URL in the box:
     http://blogmap-gadget.googlecode.com/svn/trunk/blogmap.xml(See below for alternate URL)
  5. Save it and edit the gadget, using your blog's feed URL as the source
    (http://yourblog.blogger.com/feeds/posts/default)
Alternate URL: Google Code has depreciated and the code is now gone from their host. However, CameronWills has preserved it on GitHub, so you can use his URL:

https://raw.githubusercontent.com/CameronWills/blogmap-gadget/master/blogmap.xml

Adding the widget from your own source (optional)


The widget source-code is coming from a very old "Blogger in Draft" post, and you might worry that it could be deleted or lost, especially since this seems to be the only location on the net for it. I'll repost it here, so that you could potentially self-host the file if need-be.

In that case:
  1. Copy the code from the box below and save it in a plain text document, giving it the extension .xml
  2. Upload the document to a place that allows public full access to the document. Don't paste it into a blog post. It has to be accessible as text. Google Sites sometimes works, as can your Dropbox Public folder, or any other file hosting site that lets you link directly to the file. Google Drive can also do this.  
  3. Use this new URL, the location of the file you just uploaded, as the URL when you add the widget via the "Adding by URL" option.
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Blog Map" description="Display blog posts on a map." height="250" author="Brian Ngo" author_email="briancse+blogmap@gmail.com"> <Require feature="dynamic-height" /> <Require feature="setprefs" /> </ModulePrefs> <UserPref name="feedUrl" display_name="Blog's GeoRSS feed URL. Save as empty to reset." /> <UserPref name="height" display_name="Height in pixels" required="true" default_value="250" /> <Content type="html"> <![CDATA[ <style type="text/css"> .inputBox { border-color: #777777 #AAAAAA #AAAAAA #777777; border-style: solid; border-width: 1px; padding: 2px; width: 100%; } .inputBox-hint { border-color: #777777 #AAAAAA #AAAAAA #777777; border-style: solid; border-width: 1px; padding: 2px; width: 96%; color: #999; font-style: italic; } .statusMsg { font-size: small; color: #333; font-style: italic; } .statusMsg-error { font-size: small; color: red; font-weight: bold; } </style> <script src="http://maps.google.com/maps?file=api&amp;v=2.x&amp;key=ABQIAAAAn138JcpDQexRxBMx-GYAehTZqGWfQErE9pT-IucjscazSdFnjBS4hjjTEOYU89NegWNS5Bv9cZZO8g" type="text/javascript"></script> <div id="instructions" style="display: none; font-size: 14px;"> <p> To display your blog posts on a map, enter your <b>blog's URL</b> below. Alternatively, you can also enter your blog's <b>RSS or Atom feed URL</b>. </p> <input type="text" id="feedUrl" name="feedUrl" class="inputBox-hint" value="Example: http://your-blog-url.blogspot.com" /> <div style="padding: 5px 10px 0 0;"> <input type="button" id="okButton" name="okButton" value="Fetch my blog!" /> </div> <div id="statusMsg" class="statusMsg"></div> </div> <div id="map"></div> <script type="text/javascript"> var PREFS = new gadgets.Prefs(); var MAP = null; var WIDTH = gadgets.window.getViewportDimensions().width; var HEIGHT = PREFS.getInt('height'); var FEEDURL = PREFS.getString('feedUrl'); var XML = null; var BLOGSPOT_REGEX = new RegExp('blogspot\.com/?$'); var inputBoxActivated = false; function init() { gadgets.window.adjustHeight(HEIGHT); var container = document.getElementById('map'); container.style.height = HEIGHT + 'px'; container.style.width = WIDTH + 'px'; if (!FEEDURL) { document.getElementById('instructions').style.display = ''; document.getElementById('feedUrl').onclick = activateInput; document.getElementById('okButton').onclick = validateFeedUrl; } else { fetchFeed(); } } function activateInput() { if (!inputBoxActivated) { document.getElementById('feedUrl').className = 'inputBox'; document.getElementById('feedUrl').value = ''; inputBoxActivated = true; } } function validateFeedUrl() { document.getElementById('statusMsg').className = 'statusMsg'; document.getElementById('statusMsg').innerHTML = 'Validating feed...'; var feedUrl = maybeFixUrl(document.getElementById('feedUrl').value); var params = {}; params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.FEED; params[gadgets.io.RequestParameters.NUM_ENTRIES] = 1; params[gadgets.io.RequestParameters.GET_SUMMARIES] = false; gadgets.io.makeRequest(feedUrl, handleValidateFeed, params); } /** * Will attempt to detect a blogspot.com url without the feed suffix * (e.g., http://ginternfoodblog.blogspot.com/). If detected, adds * the feed suffix ("/feeds/posts/default"). Also, adds an http:// * protocol if one isn't present. */ function maybeFixUrl(url) { // trim whitepsace. url = url.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1"); // test blogspot url fix if (BLOGSPOT_REGEX.test(url)) { if (url[url.length - 1] != '/') { url += '/'; } url += 'feeds/posts/default'; } // test http protocol fix if (url.indexOf('http://') != 0) { url = 'http://' + url; } return url; } function handleValidateFeed(response) { if (response && response.data && response.data.Entry) { var feedUrl = response.data.URL; PREFS.set('feedUrl', feedUrl); FEEDURL = feedUrl; fetchFeed(); } else { document.getElementById('statusMsg').className = 'statusMsg-error'; document.getElementById('statusMsg').innerHTML = 'Couldn\'t find feed. Is the URL correct?'; } } function fetchFeed() { var params = {}; params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.DOM; params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET; gadgets.io.makeRequest(FEEDURL, handleFetchFeed, params); } function handleFetchFeed(response) { document.getElementById('instructions').style.display = 'none'; var data = response.data; var points = data.getElementsByTagName('georss:point'); // We weren't able to get any georss:point nodes, this might be a // Webkit browser and just wants "point" as the tag name. if (points.length == 0) { points = data.getElementsByTagName('point'); } var geoposts = []; for (var i = 0; i < points.length; i++) { var latlngPair = points[i].firstChild.nodeValue.split(' '); var title = points[i].parentNode.getElementsByTagName('title')[0] .firstChild.nodeValue; var date; var link; var pubDate = points[i].parentNode.getElementsByTagName('pubDate'); if (pubDate.length > 0) { // This is an RSS feed. var tempDate = new Date(); tempDate.setTime(Date.parse(pubDate[0].firstChild.nodeValue)); date = tempDate.toLocaleDateString(); link = points[i].parentNode.getElementsByTagName('link')[0] .firstChild.nodeValue; } else { // This is an Atom feed. date = points[i].parentNode.getElementsByTagName('updated')[0] .firstChild.nodeValue.substring(0, 10); var links = points[i].parentNode.getElementsByTagName('link'); for (var n = 0; n < links.length; n++) { if (links[n].getAttribute('rel') == 'alternate') { link = links[n].getAttribute('href'); break; } } } geoposts.push({ 'title': title, 'point': new google.maps.LatLng(latlngPair[0], latlngPair[1]), 'date': date, 'link': link }); } createMap(geoposts); } // Doing two loops here... non-ideal.. too tired to optimize. function createMap(geodata) { var bounds = new google.maps.LatLngBounds(); for (var i = 0; i < geodata.length; i++) { bounds.extend(geodata[i].point); } MAP = new google.maps.Map2(document.getElementById('map')); MAP.setCenter(bounds.getCenter(), MAP.getBoundsZoomLevel(bounds)); MAP.addControl(new google.maps.SmallZoomControl()); for (var i = 0; i < geodata.length; i++) { var marker = new google.maps.Marker(geodata[i].point, { 'title': geodata[i].title }); MAP.addOverlay(marker); var infoHtml = '<div style="font-size: small">' + '<b>' + geodata[i].title + '</b>' + '<div style="color: #666;">' + 'Posted on ' + geodata[i].date + '</div>' + '<div style="font-size: small">' + '<a href="' + geodata[i].link + '" target="_blank">' + 'View post</a></div>' + '</div>'; marker.bindInfoWindowHtml(infoHtml); } } gadgets.util.registerOnLoadHandler(init); </script> ]]> </Content> </Module>


You may also be able to insert this code into the widget itself, though you'll need to extract just the [CDATA[ portion. To see what I mean, see here and here

Blog Map widget in action


Here is a screenshot of this beautiful, underutilized, and probably forgotten feature. The map that is now displayed in your blog's sidebar shows pins at your posts' geotagged locations. You can click the pins for a small pop-up dialog that contains a link to the blog post that was tagged at that location.

Blog Map sidebar widget
This relies on your blog's RSS feed, so it might not display your older location-tagged entries. In this case, just add the parameter ?max-results=200 to the end of your blog feed URL. (Note: see my other post for a potentially better blog URL format to ensure all posts are pulled in.)

Embedding the map in a Blogger Page


You don't have to just use this as a side-bar widget. The map can also be embedded on one of your Blogger pages (it works better on a "page" not a "post").

This is very easy. Just create a new Blogger page, switch to HTML editing, and paste this code, changing the URL for your blog:

<iframe frameborder="0" height="250" id="map" name="map" src="//www-blogger-opensocial.googleusercontent.com/gadgets/ifr?url=http://blogmap-gadget.googlecode.com/svn/trunk/blogmap.xml&amp;container=blogger&amp;view=default&amp;lang=en&amp;country=ALL&amp;sanitize=0&amp;v=38841e006da8e2dd&amp;libs=core:dynamic-height:setprefs&amp;parent=http://YOURBLOG.blogspot.com/&amp;up_feedUrl=http://YOURBLOG.blogspot.com/feeds/posts/default&amp;up_height=250&amp;mid=1#up_height=250&amp;up_feedUrl=http://YOURBLOG.blogspot.com/feeds/posts/default&amp;st=e%3DAFlCd0Vr5Ehr8RmECOHLq0S%252F20Y33QUXiflbjKfRMJKtj9KwkD9YS3Uq3Xezr3ffRqOsHwYznsMMLJL1lzdH2mGi20gGUDm6kZZjf5a%252F1QQDC%252B%252B6NPHmoRqokx%252BjvN6pekTyBhptzGC9%26c%3Dblogger&amp;rpctoken=2160341050058157284" style="display: block; width: 100%;"></iframe>

It does not seem to work if your blog's feed is redirected through Feedburner, and the script can sometimes hang if you have too many entries. You may also find that you need to replace your blog's username-based URL with its numerical ID equivalent (http://www.blogger.com/feeds/BLOGIDNUMBER/posts/default?max-results=500)


If you use Blogger, please add the widget to your Blogger blog, and demonstrate its usefulness to the masses! I'd love for this wonderful bit of code to be revived, and for Google to be more active in supporting Blogger. In the crazy world of Google+ checkins and Swarm checkins and Facebook checkins and all the other checkin services, it's nice to see location-tags on Blogger still being supported. It's a great way to revisit old memories and see your blog journey in a new way.

No comments :

Post a Comment