Skip to content

Instantly share code, notes, and snippets.

Last active January 9, 2017 13:07
Show Gist options
  • Save jsanz/1ebcb326e6cd2ff2eac0 to your computer and use it in GitHub Desktop.
Save jsanz/1ebcb326e6cd2ff2eac0 to your computer and use it in GitHub Desktop.
OL3 map from Google Doc forms

This is an example of extending the OL3 TextFeature format class to parse the data coming from a Google Form backed by a public Google Spreadsheet.

Add an event (name, web and coordinates) and check the demo.

The magic comes from calling the JSON version of the spreadsheet. The weirdest part is the owz14uv string that I had to guess thanks to this blog post.

The code is really horrible and dirty, but as this is a proof of concept, I'm satisfied as it is. I've used bootstrap Superhero theme just to have a nice presentation, modernizr just in case and moustache to render the popup.

<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>OL3 from GDoc</title>
<meta name="description" content="Eventos desde un gdoc">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="//" />
<link rel="stylesheet" href="" />
.popover-content a{color: white;}
<!--[if lt IE 7]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="">upgrade your browser</a> to improve your experience.</p>
<div class="container">
<div class="page-header">
<p class="lead">Map of events loaded from a Google Docs <a href="">form</a>.</p>
<div class="row">
<div class="md-12">
<div id="map">
<div id="popup"></div>
<script src="//"></script>
<script src="//"></script>
<script src="//"></script>
<script src=""></script>
<script src=""></script>
'use strict';
$( document ).ready(function() {
// Get a list of features from the JSON object
var getFeaturesFromData = function(data) {
var features = [];
// Common style
var iconStyle = new{
image: new** @type {} */ ({
anchor: [0.5, 24],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
opacity: 0.75,
src: ''
// Convert the text coordinates into a valid OL Coordinates array
var getPoint = function(entry){
var lonlat = entry.gsx$longitudlatitud.$t.split(',');
return [lonlat[0]*1.0,lonlat[1]*1.0];
// Process the data and create a feature for every entry
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform(getPoint(entry),'EPSG:4326','EPSG:3857')),
entry: entry
} catch(e){
return features;
// Function to process when JSON data is received
var loadFeatures = function(data){
// Parse data and get the features
var features = getFeaturesFromData(data);
// Instance a vector layer
var vector = new ol.layer.Vector({
source: new ol.source.Vector({
features: features
// Instance a map
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
renderer: ['canvas','svg'],
target: 'map',
view: new ol.View({
center: ol.proj.transform([-5.383301,34.397845],'EPSG:4326','EPSG:3857'),
zoom: 4
// Get the popup div
var element = document.getElementById('popup');
// Instance the OL popup overlay
var popup = new ol.Overlay({
element: element,
positioning: 'bottom-center',
stopEvent: false
var renderContent = function(feature){
var entry = feature.get('entry');
var view = {
web : entry.gsx$web.$t,
evento: entry.gsx$nombredelevento.$t
var template = "<a href={{web}}>{{evento}}</a>"
return Mustache.render(template, view);
// display popup on click
map.on('click', function(evt) {
// get the closest feature to the event point
var feature = map.forEachFeatureAtPixel(evt.pixel,
function(feature, layer) {
return feature;
// show the popup
if (feature) {
var geometry = feature.getGeometry();
var coord = geometry.getCoordinates();
'placement': 'top',
'html': true,
'content': renderContent(feature)
} else {
// change mouse cursor when over marker
$(map.getViewport()).on('mousemove', function(e) {
var pixel = map.getEventPixel(e.originalEvent);
var hit = map.forEachFeatureAtPixel(pixel, function(feature, layer) {
return true;
if (hit) {
document.getElementById('map').style.cursor = 'pointer';
} else {
document.getElementById('map').style.cursor = '';
Action begins here, loading the JSON data
from the Google Doc spreadsheet
url: "",
dataType: 'jsonp',
success: loadFeatures,
error: function(obj){
Copy link

aborruso commented Nov 2, 2014

Great work @jsanz!

A question: if someone submits new data via your form, is the data source and the map automatically updated?

Thank you

Copy link

jsanz commented Apr 24, 2015

Hey @aborruso, sorry for not answering 😦 . Yes, the JSON always retreives the data from the spreadsheet, next reload of the page and it's done.

Copy link

Looks great! However the example doesn't seem to include any events now. Maybe Googledocs or OpenStreetMap have changed their interface somehow? Does it still work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment