Gas Prices Data Visualization
by Admiral Potato
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.js"></script>
<script src="http://threejs.org/examples/fonts/helvetiker_regular.typeface.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
<div id="rootContainer" class="container-fluid">
<div id="rootRow" class="row">
<div class="col-xs-2">
<div class="panel">
<div class="btn-group-vertical" role="group">
<button type="button" class="btn btn-default">Bob</button>
<button type="button" class="btn btn-default">Joe</button>
<button type="button" class="btn btn-default">Sally</button>
<button type="button" class="btn btn-default">Lill</button>
</div>
</div>
</div>
<div id="rootCanvasHolder" class="col-xs-10">
<div id="canvasHolder"></div>
</div>
</div>
</div>
<div id="data">
<div id="data-receipts" data-source="Admiral's gas receipt collection">
date time businessName address city zipCode lat lng pricePerGal volume total mileage
8/1/2012 10:42 Smiths 133 845 E 4500 S SLC 84107 40.675392 -111.867491 3.399 12.798 43.5 91759
8/17/2012 16:05 Costco 113 1818 S 300 W SLC 84115 40.730608 -111.902929 3.359 16.968 57 92119
9/13/2012 20:32 Smiths 133 845 E 4500 S SLC 84107 40.675392 -111.867491 3.299 16.069 53.01 92786
9/18/2013 0:35 Nolan's Chevron 210 S 700 E SLC 84102 40.764568 -111.871518 3.689 14.028 51.75 103411
12/2/2012 18:43 Smiths 133 845 E 4500 S SLC 84107 40.675392 -111.867491 1.439 13.201 19 94608
3/17/2013 17:22 Holiday Oil #47 309 W Main St American Fork 84003 40.376708 -111.807044 3.279 9.76 32 97671
9/5/2013 23:06 Extramart #90 2110 S 1300...
CSS
body{
padding: 0;
margin: 0;
}
#data{
display: none;
}
#canvasHolder{
height: 100%;
background-color: #f00;
}
html,
body,
#rootContainer,
#rootRow,
#rootCanvasHolder,
#canvasHolder{
height: 100%;
}
#rootCanvasHolder{
padding: 0;
}
canvas{
display: block;
width: 100%;
height: 100%;
}
JavaScript
//data prep parts
var prepAsString = function (record, label, string) {
record[label] = string;
},
prepAsNumber = function (record, label, string) {
record[label] = parseFloat(string);
},
dataPrepMethodMap = {
"date": function (record, label, string) {
record[label] = new Date(string);
},
"time": function (record, label, string) {
var split = string.split(':');
record['date'].setHours(split[0]);
record['date'].setMinutes(split[1]);
},
"businessName": prepAsString,
"address": prepAsString,
"city": prepAsString,
"state": prepAsString,
"abbrev": prepAsString,
"label": prepAsString,
"zipCode": prepAsNumber,
"lat": prepAsNumber,
"lng": prepAsNumber,
"pricePerGal": prepAsNumber,
"volume": prepAsNumber,
"total": prepAsNumber,
"mileage": prepAsNumber,
"hue": prepAsNumber,
"bordindx": prepAsNumber,
"st1st2": prepAsString,
"milemark": prepAsString,
"st1": prepAsString,
"st2": prepAsString
},
prepDataByTypeName = function (dataTypeName) {
var result = [],
split = dataText = document.getElementById('data-' + dataTypeName).innerText.trim().split('\n'),
labels = split.shift().split('\t');
split.forEach(function (rowString) {
var row = rowString.split('\t'),
record = {
type: dataTypeName
};
row.forEach(function (value, index) {
var label = labels[index];
dataPrepMethodMap[label](record, label, value);
});
result.push(record);
});
return result;
},
dataTypes = 'receipts,orientation,borders,capitals'.split(','),
getDataByTypes = function(){
var result = {};
dataTypes.forEach(function(typeName){
result[typeName] = prepDataByTypeName(typeName);
});
return result;
},
data = getDataByTypes(),
lerp = function (a, b, t) {
return a + ((b - a) * t);
},
getDataLatLngBoundingBox = function (dataSet) {
var cloneSet = dataSet.slice(),//because we're operating on it
firstResult = cloneSet.shift(),
result = {
latMin: firstResult.lat,
latMax:...