Skip to content

Instantly share code, notes, and snippets.

@por
Created May 20, 2011 08:58
Show Gist options
  • Save por/982591 to your computer and use it in GitHub Desktop.
Save por/982591 to your computer and use it in GitHub Desktop.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="refresh" content="3600" />
<title>Scrobble Map</title>
<link rel="stylesheet" href="scrobblemap.css" type="text/css" media="screen" title="no title" charset="utf-8">
</head>
<body>
<div id="map3d"></div>
<script src="http://cdn.last.fm/prototyping/scrobblemap/mootools-1.2.4-core.js"></script>
<script src="http://cdn.last.fm/prototyping/scrobblemap/mootools-1.2.4.4-more.js"></script>
<script src="http://www.google.com/jsapi?key=ABQIAAAAef4neLxKbGqqMWYSo_SV8BTm1w6-I7rkOK_FGHRvVLZcy0O7EhQeEyUSqJjgTzCZH93mE14rYLfiuQ"></script>
<script src="scrobblemap3D.js"></script>
<script>
// Simple transition
/*
var map = new ScrobbleMap3D("map3d");
map.addEvent('onViewChangeBegin', map.hideBalloon.bind(map));
map.addEvent('onViewChangeEnd', map.showBalloon.bind(map));
map.addEvent('onShowScrobble', function(scrobble) {
this.setPoint(scrobble);
}.bind(map));
map.addEvent('onSetPoint', function() {
this.getView(this.options.defaultView);
}.bind(map));
map.start();
*/
// Complex transition
var map = new ScrobbleMap3D("map3d", {
showStatusBar: false,
scrobbleInterval: 10000,
waitUntilLoaded: true
});
var views = [
{ altitude: 0, heading: -65, tilt: 65, range: 1500 },
{ altitude: 0, heading: 115, tilt: 70, range: 2800 },
{ altitude: 0, heading: 45, tilt: 60, range: 2000 },
{ altitude: 0, heading: -95, tilt: 65, range: 2500 }
];
map.addEvent('onShowScrobble', function(scrobble) {
this.setPoint(scrobble);
}.bind(map));
map.addEvent('onSetPoint', function() {
this.addEvent('onViewChangeBegin', function() {
this.removeEvents('onViewChangeBegin');
this.hideBalloon();
});
this.addEvent('onViewChangeEnd', function() {
this.removeEvents('onViewChangeEnd');
this.addEvent('onViewChangeEnd', function() {
this.removeEvents('onViewChangeEnd');
this.showBalloon();
});
var rand = $random(0, views.length-1);
var view = views[rand];
this.getView(view);
});
this.getView({
altitude: 0,
heading: 0,
tilt: 0,
range: 5000000
});
}.bind(map));
map.start();
</script>
</body>
[{"user":"mrspeppa","image":"http:\/\/userserve-ak.last.fm\/serve\/126s\/63086423.jpg","track":{"artist":"Michael Jackson","name":"They Don't Care About Us"},"location":{"city":"Police","countrycode":"PL","latitude":53.5499992371,"longitude":14.5666999817},"timestamp":"1305882819"},{"user":"kitsune_arisa","image":"http:\/\/userserve-ak.last.fm\/serve\/126s\/61490441.jpg","track":{"artist":"In Flames","name":"Leeches"},"location":{"city":"Tomsk","countrycode":"RU","latitude":56.5,"longitude":84.9666976929},"timestamp":"1305883132"},{"user":"xtran","image":"http:\/\/userserve-ak.last.fm\/serve\/126s\/4471011.jpg","track":{"artist":"Action Biker","name":"Dance To Keep From Crying"},"location":{"city":"Kista","countrycode":"SE","latitude":59.4500007629,"longitude":17.9167003632},"timestamp":"1305883058"},{"user":"henrynavarre","image":"http:\/\/userserve-ak.last.fm\/serve\/126s\/62700273.jpg","track":{"artist":"The Pillows","name":"Hybrid Rainbow"},"location":{"city":"Chaska","countrycode":"US","latitude":44.8054008484,"longitude":-93.6248016357},"timestamp":"1305883074"},{"user":"mykkyr","image":"http:\/\/userserve-ak.last.fm\/serve\/126s\/52491325.jpg","track":{"artist":"Grant Hart","name":"You're the Reflection of the Moon on the Water"},"location":{"city":"","countrycode":"FI","latitude":64,"longitude":26},"timestamp":"1305882869"},{"user":"Amazon1969","image":"http:\/\/userserve-ak.last.fm\/serve\/126s\/6654115.jpg","track":{"artist":"The Bar-Kays","name":"Humpin'"},"location":{"city":"Utrecht","countrycode":"NL","latitude":52.0833015442,"longitude":5.13329982758},"timestamp":"1305882898"},{"user":"Gatsby0202","image":"http:\/\/userserve-ak.last.fm\/serve\/126s\/44332461.jpg","track":{"artist":"Fink","name":"Move On Me"},"location":{"city":"Oxford","countrycode":"GB","latitude":51.75,"longitude":-1.25},"timestamp":"1305882989"},{"user":"xJonnyx","image":"http:\/\/userserve-ak.last.fm\/serve\/126s\/45523249.jpg","track":{"artist":"Bon Iver","name":"Hinnom, TX"},"location":{"city":"Manchester","countrycode":"GB","latitude":53.5,"longitude":-2.21670007706},"timestamp":"1305883125"},{"user":"Candlelight89","image":"http:\/\/userserve-ak.last.fm\/serve\/126s\/62865905.gif","track":{"artist":"Malcolm Lincoln","name":"Uu Monica"},"location":{"city":"Pobiedziska","countrycode":"PL","latitude":52.4667015076,"longitude":17.2999992371},"timestamp":"1305882869"},{"user":"bluekey","image":"http:\/\/userserve-ak.last.fm\/serve\/126s\/723289.jpg","track":{"artist":"Cold War Kids","name":"Out Of The Wilderness"},"location":{"city":"Vancouver","countrycode":"CA","latitude":49.25,"longitude":-123.133300781},"timestamp":"1305882837"}]
html, body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
overflow: hidden;
background-color: transparent;
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 18px;
line-height: 24px;
}
p, h2 {
margin: 0;
padding: 0;
}
#map3d {
background-color: transparent;
height: 100%;
width: 100%;
}
#mapOverlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.scrobbleballoon {
padding: 10px 10px 10px 166px;
color: #111;
text-shadow: 0 1px 0 #fff;
}
.scrobbleballoon .user-image {
margin: 0 0 10px -156px;
display: inline;
float: left;
border: 5px solid white;
-webkit-box-shadow: 0 0 10px rgba(0,0,0,0.2);
}
.scrobbleballoon .title {
font-size: 30px;
margin-bottom: 0.5em;
display: block;
}
.trackname,
.artistname {
font-weight: bold;
}
.location {
color: #666;
}
#scrobbleCounter {
color: #eee;
float: right;
position: absolute;
font-size: 40px;
color: #eee;
}
/**
* Scrobble Map 3D
* @author Tim ([email protected])
* @version 0.1
*
* Requires:
* <script type="text/javascript" src="http://cdn.last.fm/prototyping/scrobblemap/mootools-1.2.4-core.js"></script>
* <script type="text/javascript" src="http://cdn.last.fm/prototyping/scrobblemap/mootools-1.2.4.4-more.js"></script>
* <script src="http://www.google.com/jsapi?key=ABQIAAAAef4neLxKbGqqMWYSo_SV8BTm1w6-I7rkOK_FGHRvVLZcy0O7EhQeEyUSqJjgTzCZH93mE14rYLfiuQ"></script>
*/
var ScrobbleMap3D = new Class({
Implements: [Options, Events],
ge: null,
placemark: null,
la: null,
balloon: null,
scrobbleInterval: null,
scrobbleQueue: [],
settings: {
scrobbleStream: 'sample_data.json',
placemarkIcon: 'http://static.last.fm/prototyping/scrobblemap/scrobblepin.png',
logo: {
href: 'http://static.last.fm/prototyping/scrobblemap/lastfm-logo-white.png',
width: 100,
height: 30
},
balloon: {
maxWidth: 500,
minWidth: 500,
backgroundColor: '#eeeeee',
closeButtonEnabled: false
}
},
options: {
language: "en-GB",
layers: [
// "LAYER_BORDERS",
"LAYER_TERRAIN",
"LAYER_BUILDINGS"
],
showNavigation: false,
showStatusBar: false,
showOverviewMap: false,
showScaleLegend: false,
showAtmosphere: true,
showSun: false,
flyToSpeed: 1,
scrobbleInterval: 10000, // ms
defaultView: {
altitude: 0,
heading: 0,
tilt: 45,
range: 1000000
},
waitUntilLoaded: false
},
initialize: function(container, options) {
// Set container
this.container = container;
// Set options
this.setOptions(options);
// Initialize Google Earth API
google.load("earth", "1");
google.setOnLoadCallback(this.init.bind(this));
},
init: function() {
// Create Google Earth instance
google.earth.createInstance(this.container, this.initCB.bind(this), this.failCB.bind(this));
},
initCB: function(instance) {
this.ge = instance;
this.ge.getWindow().setVisibility(true);
// Initialize layers
this.options.layers.each(function(item, index){
var layer = eval("this.ge." + item);
this.ge.getLayerRoot().enableLayerById(layer, true);
}, this);
// Options
this.ge.getNavigationControl().setVisibility(this.options.showNavigation); // Navigation
this.ge.getOptions().setStatusBarVisibility(this.options.showStatusBar); // Status bar
this.ge.getOptions().setOverviewMapVisibility(this.options.showOverviewMap); // Overview map
this.ge.getOptions().setScaleLegendVisibility(this.options.showScaleLegend); // Scale legend
this.ge.getOptions().setAtmosphereVisibility(this.options.showAtmosphere); // Atmosphere
this.ge.getSun().setVisibility(this.options.showSun); // Sun
// Fly to speed
this.ge.getOptions().setFlyToSpeed(this.options.flyToSpeed);
// Create logo overlay
this.createLogoOverlay();
// Initialize placemark
this.createPlacemark();
// Initialize camera
this.la = this.ge.createLookAt('');
// Create balloon
this.createBalloon();
// Load scrobblestream
this.getMoreScrobbles();
// Add listeners
google.earth.addEventListener(this.ge.getView(), 'viewchangebegin', this.viewChangeBeginHandler.bind(this));
google.earth.addEventListener(this.ge.getView(), 'viewchangeend', this.viewChangeEndHandler.bind(this));
},
failCB: function() {
console.log('FAIL');
},
createLogoOverlay: function() {
// Create the ScreenOverlay
var screenOverlay = this.ge.createScreenOverlay('');
// Specify a path to the image and set as the icon
var icon = this.ge.createIcon('');
icon.setHref(this.settings.logo.href);
screenOverlay.setIcon(icon);
// Set the ScreenOverlay’s position in the window
screenOverlay.getOverlayXY().setXUnits(this.ge.UNITS_PIXELS);
screenOverlay.getOverlayXY().setYUnits(this.ge.UNITS_PIXELS);
screenOverlay.getOverlayXY().setX(this.settings.logo.width/2+10);
screenOverlay.getOverlayXY().setY(this.settings.logo.height/2+10);
// Set the overlay’s size in pixels
screenOverlay.getSize().setXUnits(this.ge.UNITS_PIXELS);
screenOverlay.getSize().setYUnits(this.ge.UNITS_PIXELS);
screenOverlay.getSize().setX(this.settings.logo.width);
screenOverlay.getSize().setY(this.settings.logo.height);
// Add the ScreenOverlay to Earth
this.ge.getFeatures().appendChild(screenOverlay);
},
createPlacemark: function() {
// Initialize placemark
this.placemark = this.ge.createPlacemark('');
// Create a style map.
var styleMap = this.ge.createStyleMap('');
// Create normal style for style map.
var normalStyle = this.ge.createStyle('');
var normalIcon = this.ge.createIcon('');
normalIcon.setHref(this.settings.placemarkIcon);
normalStyle.getIconStyle().setIcon(normalIcon);
normalStyle.getIconStyle().setScale(2.0);
styleMap.setNormalStyle(normalStyle);
// Apply stylemap to placemark.
this.placemark.setStyleSelector(styleMap);
// Add the placemark to Earth
this.ge.getFeatures().appendChild(this.placemark);
},
createBalloon: function() {
this.balloon = this.ge.createHtmlStringBalloon('');
this.balloon.setMinWidth(this.settings.balloon.minWidth);
this.balloon.setMaxWidth(this.settings.balloon.maxWidth);
this.balloon.setCloseButtonEnabled(this.settings.balloon.closeButtonEnabled);
this.balloon.setBackgroundColor(this.settings.balloon.backgroundColor);
},
showScrobble: function() {
// retrieve more scrobbles if the queue is empty or down to the last few
if (this.scrobbleQueue.length == 0 || this.scrobbleQueue.length == 2) {
this.getMoreScrobbles();
}
if (this.scrobbleQueue.length == 0) {
return;
}
// Be nice and only start when everything has finished loading
if (this.options.waitUntilLoaded) {
if(this.isLoading()) {
console.log('loading...');
this.stop();
this.start.delay(1000, this);
return;
}
}
scrobble = this.scrobbleQueue.shift();
this.fireEvent('onShowScrobble', [scrobble]);
},
getMoreScrobbles: function() {
new Request.JSON({
url: this.settings.scrobbleStream,
onComplete: this.getMoreScrobblesCallback.bind(this)
}).send();
},
getMoreScrobblesCallback: function(obj) {
// wipe out any cached scrobbles
this.scrobbleQueue = [];
// just take the first 10 or we’ll be too out of date
for (var i = 0, ilen = 10; i < ilen; i++) {
this.scrobbleQueue.push(obj[i]);
}
},
setPoint: function(scrobble) {
var user = scrobble.user;
var image = scrobble.image;
var trackname = scrobble.track.name;
var artistname = scrobble.track.artist;
var cityname = scrobble.location.city;
var countrycode = scrobble.location.countrycode;
var html = '<div class="scrobbleballoon"><img src="'+ image +'" height="126" width="126" alt="' + user + '" class="user-image" /><h2 class="title">'+ user +'</h2><p class="listeningto"><span class="artistname">' + artistname + '</span> &ndash; <span class="trackname">' + trackname + '</span></p><p class="location">Scrobbling now in ' + cityname + ' (' + countrycode + ')' + '</p></div>';
// this.placemark.setName(user);
// this.placemark.setDescription(html);
this.balloon.setContentString(html);
this.balloon.setFeature(this.placemark);
var latitude = scrobble.location.latitude;
var longitude = scrobble.location.longitude
// Set the placemark’s location
var point = this.ge.createPoint('');
point.setLatitude(latitude);
point.setLongitude(longitude);
this.placemark.setGeometry(point);
this.fireEvent('onSetPoint');
},
getView: function(view) {
var point = this.placemark.getGeometry();
var latitude = point.getLatitude();
var longitude = point.getLongitude();
this.la.set(
latitude,
longitude,
view.altitude,
this.ge.ALTITUDE_RELATIVE_TO_GROUND,
view.heading,
view.tilt,
view.range
);
this.ge.getView().setAbstractView(this.la);
},
start: function() {
this.showScrobble();
$clear(this.scrobbleInterval); // just in case
this.scrobbleInterval = this.showScrobble.periodical(this.options.scrobbleInterval, this);
},
stop: function() {
$clear(this.scrobbleInterval);
},
hideBalloon: function() {
thisge.setBalloon(null);
},
showBalloon: function() {
if(this.balloon.getContentString() != ''){
this.ge.setBalloon(this.balloon);
}
},
viewChangeBeginHandler: function() {
this.fireEvent('onViewChangeBegin');
},
viewChangeEndHandler: function() {
this.fireEvent('onViewChangeEnd');
},
getStreamingPercent: function() {
return this.ge.getStreamingPercent();
},
isLoading: function() {
var pct = map.getStreamingPercent();
return (pct < 100);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment