296 lines
11 KiB
HTML
296 lines
11 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
<head>
|
|
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
|
|
<title>Google Spreadsheet, Arbitrary Columns Example</title>
|
|
<script src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAASI0kCI-azC8RgbOZzWc3VRRarOQe_TKf_51Omf6UUSOFm7EABRRhO0PO4nBAO9FCmVDuowVwROLo3w"
|
|
type="text/javascript"></script>
|
|
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
|
|
<script type="text/javascript" src="../lib/jquery-1.6.2.min.js"></script>
|
|
<script type="text/javascript" src="../lib/mxn/mxn.js?(googlev3)"></script>
|
|
<script src="../lib/timeline-2.3.0.js" type="text/javascript"></script>
|
|
<script src="../src/timemap.js" type="text/javascript"></script>
|
|
<script src="../src/param.js" type="text/javascript"></script>
|
|
<script src="../src/loaders/json.js" type="text/javascript"></script>
|
|
<script src="../src/loaders/google_spreadsheet.js" type="text/javascript"></script>
|
|
<script src="../src/ext/circle_icons.js" type="text/javascript"></script>
|
|
<script type="text/javascript">
|
|
|
|
// We're going to define a new loader class extending the spreadsheet loader, which
|
|
// will do both the transformation and the consolidation by title in the preload
|
|
TimeMap.loaders.custom_gss = function(options) {
|
|
var loader = new TimeMap.loaders.gss(options),
|
|
extraColumns = options.extraColumns || [];
|
|
|
|
// We want the transform function, but not as such;
|
|
// save it under a different name and clear the function
|
|
loader.oldTransform = loader.transform;
|
|
loader.transform = function(data) { return data; };
|
|
|
|
// Do both transform and consolidation in the preload function
|
|
loader.preload = function(data) {
|
|
// basic preload: get rows
|
|
var rows = data.feed.entry,
|
|
counties = {}, items = [],
|
|
parser = TimeMap.dateParsers.hybrid,
|
|
item, i;
|
|
for (i=0; i < rows.length; i++) {
|
|
// call the original loader transform to get the formatted object
|
|
item = loader.oldTransform(rows[i]);
|
|
// consolidate by title (county)
|
|
if (!(item.title in counties)) {
|
|
// make an EventIndex for the data
|
|
item.options.dates = new SimileAjax.EventIndex(null);
|
|
// add to county map
|
|
counties[item.title] = item;
|
|
}
|
|
// save the data as a pseudo-event to the EventIndex
|
|
// (have to wrap in an anonymous function to fix scope)
|
|
(function(start) {
|
|
var d = parser(start),
|
|
datapoint = {
|
|
// these functions are to make it behave like an event
|
|
getStart: function() {
|
|
return d;
|
|
},
|
|
getEnd: function() {
|
|
return d;
|
|
},
|
|
getID: function() {
|
|
return start;
|
|
}
|
|
};
|
|
// add the data we care about; assuming it's extraColumns
|
|
for (x=0; x < extraColumns.length; x++) {
|
|
paramName = extraColumns[x];
|
|
datapoint[paramName] = item.options[paramName];
|
|
}
|
|
counties[item.title].options.dates.add(datapoint);
|
|
})(item.start);
|
|
}
|
|
// turn the consolidated items into an array again
|
|
for (i in counties) {
|
|
if (counties.hasOwnProperty(i)) {
|
|
item = counties[i];
|
|
// while we're at it, let's turn this into a duration event
|
|
item.start = item.options.dates.getEarliestDate();
|
|
item.end = item.options.dates.getLatestDate();
|
|
items.push(item);
|
|
}
|
|
}
|
|
return items;
|
|
}
|
|
|
|
// return the customized loader
|
|
return loader;
|
|
};
|
|
|
|
/**
|
|
* Get the current data, by date, for a particular item.
|
|
*
|
|
* @param {Date} d Current date
|
|
* @return {Object} Most recent data object for the current date
|
|
*/
|
|
TimeMapItem.prototype.getData = function(d) {
|
|
// get center date if none is supplied
|
|
d = d || this.dataset.timemap.timeline.getBand(0).getMaxVisibleDate(),
|
|
dates = this.opts.dates;
|
|
// look for the most recent data point in the past
|
|
var iterator = dates.getReverseIterator(this.getStart(), d);
|
|
if (!iterator.hasNext()) {
|
|
// event only exists in the future
|
|
return null;
|
|
}
|
|
return iterator.next();
|
|
};
|
|
|
|
/**
|
|
* Make an array of scaled circle icons and add them to a theme
|
|
*
|
|
* @param {TimeMapTheme} theme Theme to modify
|
|
*/
|
|
TimeMapTheme.addScaledIcons = function(theme) {
|
|
var imgPath = theme.eventIconPath,
|
|
icons = [], i,
|
|
sizes = [4,5,7,10,13,16,20,23,26,28];
|
|
// make icons
|
|
for (i=0; i<sizes.length; i++) {
|
|
icons.push(
|
|
TimeMapTheme.getCircleUrl(sizes[i], theme.color, 'bb')
|
|
);
|
|
}
|
|
// add to theme
|
|
theme.scaledIcons = icons;
|
|
};
|
|
|
|
/**
|
|
* Initialize the filter chain for the heatmap.
|
|
*/
|
|
TimeMap.prototype.initHeatmap = function() {
|
|
// this function needs to be set in the timemap options;
|
|
// it should take a data object and return a value 0-9
|
|
var getScaleFromData = tm.opts.getScaleFromData;
|
|
if (!getScaleFromData) return;
|
|
|
|
// add heatmap filter chain
|
|
tm.addFilterChain("heatmap",
|
|
// true condition: change marker icon
|
|
function(item) {
|
|
var data = item.getData(),
|
|
theme = item.opts.theme;
|
|
if (data) {
|
|
item.showPlacemark();
|
|
var scale = getScaleFromData(data),
|
|
newIcon = theme.scaledIcons[scale];
|
|
// note that this is specific to Google v3
|
|
item.getNativePlacemark().setIcon(newIcon);
|
|
} else {
|
|
item.hidePlacemark();
|
|
}
|
|
},
|
|
// false condition: do nothing
|
|
function(item) { }
|
|
);
|
|
// filter: change icon if visible
|
|
tm.addFilter("heatmap", function(item) {
|
|
return item.placemarkVisible;
|
|
});
|
|
// add listener for filter chain
|
|
tm.timeline.getBand(0).addOnScrollListener(function() {
|
|
tm.filter("heatmap");
|
|
});
|
|
};
|
|
|
|
var tm;
|
|
$(function() {
|
|
// make the theme
|
|
var theme = TimeMapTheme.createCircleTheme();
|
|
TimeMapTheme.addScaledIcons(theme);
|
|
theme.icon = theme.scaledIcons[0];
|
|
|
|
tm = TimeMap.init({
|
|
mapId: "map", // Id of map div element (required)
|
|
timelineId: "timeline", // Id of timeline div element (required)
|
|
options: {
|
|
theme: theme,
|
|
getScaleFromData: function(data) {
|
|
return (data["log1.6"] > 9) ? 9 : parseInt(data["log1.6"]);
|
|
},
|
|
mapFilter: "hideFuture",
|
|
// there isn't going to be a good way to show 139 duration events on the timeline
|
|
noEventLoad: true
|
|
},
|
|
datasets: [
|
|
{
|
|
title: "Events",
|
|
id: "events",
|
|
type: "custom_gss",
|
|
options: {
|
|
// note that your spreadsheet must be published for this to work
|
|
key: "tnobdBZ6zvWAWVZEW-uBW1Q",
|
|
// map spreadsheet column names to expected ids
|
|
paramMap: {
|
|
start: "year"
|
|
},
|
|
// load extra data from non-standard spreadsheet columns
|
|
extraColumns: [
|
|
"count",
|
|
"cumulative",
|
|
"log1.6"
|
|
],
|
|
openInfoWindow: function() {
|
|
var item = this,
|
|
data = item.getData();
|
|
item.opts.infoHtml = '<div class="infotitle">' + item.getTitle() + '</div>';
|
|
if (data) {
|
|
item.opts.infoHtml += '<div class="itemdata">'
|
|
+ '<p><em>Year: </em>' + data.getID() + '</p>'
|
|
+ '<p><em>Deaths: </em>' + data.count + '</p>'
|
|
+ '<p><em>Cumulative: </em>' + data.cumulative + '</p>'
|
|
+ '<div>';
|
|
}
|
|
// use basic window opener
|
|
TimeMapItem.openInfoWindowBasic.call(item);
|
|
}
|
|
}
|
|
}
|
|
],
|
|
// lay out timeline for a thin navigational display
|
|
bandInfo: [
|
|
{
|
|
width: "60%",
|
|
intervalUnit: Timeline.DateTime.YEAR,
|
|
intervalPixels: 70
|
|
},
|
|
{
|
|
width: "40%",
|
|
intervalUnit: Timeline.DateTime.DECADE,
|
|
intervalPixels: 160
|
|
}
|
|
],
|
|
// set up filter once the timeline is loaded
|
|
dataDisplayedFunction: function(tm) {
|
|
tm.initHeatmap();
|
|
tm.filter("heatmap");
|
|
},
|
|
// we aren't loading the items, so we need to set this manually
|
|
scrollTo: '1969'
|
|
});
|
|
});
|
|
</script>
|
|
<link href="examples.css" type="text/css" rel="stylesheet"/>
|
|
<style>
|
|
div#timelinecontainer{
|
|
width: 100%;
|
|
height: 20%;
|
|
}
|
|
|
|
div#timeline{
|
|
width: 100%;
|
|
height: 100%;
|
|
font-size: 12px;
|
|
background: #CCCCCC;
|
|
}
|
|
|
|
div#mapcontainer {
|
|
width: 100%;
|
|
height: 80%;
|
|
}
|
|
|
|
#timemap {
|
|
height: 650px;
|
|
}
|
|
div#map {
|
|
width: 100%;
|
|
height: 100%;
|
|
background: #EEEEEE;
|
|
}
|
|
|
|
div.itemdata {
|
|
font-size: 14px;
|
|
}
|
|
div.itemdata p {
|
|
padding: 0;
|
|
margin: .5em 0 0 0;
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="help">
|
|
<h1>Temporal Heatmap from a Google Spreadsheet</h1>
|
|
In this example, we're loading items from a Google Spreadsheet (<a href="http://spreadsheets.google.com/pub?key=tnobdBZ6zvWAWVZEW-uBW1Q" target=_blank>published here</a>) and sizing the markers on the map according to a data point in the spreadsheet (in this case, cumulative deaths from asbestosis and silicosis in Texas). This is still a bit of a work in progress; unfortunately, getting the performance to be reasonable means that the marker click area is a little wonky.
|
|
</div>
|
|
<div id="timemap">
|
|
<div id="timelinecontainer">
|
|
<div id="timeline"></div>
|
|
</div>
|
|
<div id="mapcontainer">
|
|
<div id="map"></div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|