parent
796fe7746c
commit
080a4251a9
15 changed files with 533 additions and 17846 deletions
@ -0,0 +1,19 @@ |
||||
//=============================
|
||||
//
|
||||
// IIFE ENTRY POINT
|
||||
//
|
||||
//=============================
|
||||
(function() { |
||||
Promise.resolve() |
||||
.then(BuoyAnalysisData.populateMapData) |
||||
.then(BuoyAnalysisData.populateSrcFile) |
||||
|
||||
.then(BuoyAnalysisSvgMap.drawMap) |
||||
.then(BuoyAnalysisSvgMap.drawStations) |
||||
.then(BuoyAnalysisSvgMap.drawReticle) |
||||
|
||||
.then(BuoyAnalysisUiDom.populateMonths) |
||||
.then(BuoyAnalysisUiDom.populateStationDetail) |
||||
|
||||
.then(BuoyAnalysisUiBehaviors.attachBehaviors) |
||||
})(); |
File diff suppressed because one or more lines are too long
@ -0,0 +1,90 @@ |
||||
var BuoyAnalysisData = { |
||||
mapW: 450, |
||||
|
||||
mapH: 450, |
||||
|
||||
mapJson: {}, |
||||
|
||||
stationJson: {}, |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
populateMapData: function() { |
||||
return new Promise(function(resolve) { |
||||
d3.json('client/_map.json', function(error, json) { |
||||
BuoyAnalysisData.mapJson = json; |
||||
resolve(); |
||||
}); |
||||
}); |
||||
}, |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
populateSrcFile: function() { |
||||
return new Promise(function(resolve) { |
||||
d3.json('client/_stations.json', function(error, json) { |
||||
BuoyAnalysisData.stationJson = json; |
||||
resolve(); |
||||
}); |
||||
}); |
||||
}, |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
calculateYearlyAverages: function(stations) { |
||||
var sum, count, avg; |
||||
var years = {}; |
||||
|
||||
for (var i = 1982; i < 2016; i++) { |
||||
sum = 0; |
||||
count = 0; |
||||
|
||||
stations.forEach(function(id) { |
||||
avg = BuoyAnalysisData.stationJson[id]['avgs' + i]; |
||||
|
||||
if (avg === undefined || avg.y === 0) { |
||||
return; |
||||
} |
||||
|
||||
sum += avg.y; |
||||
count++; |
||||
}); |
||||
|
||||
years['y' + i] = (sum / count) || 0; |
||||
} |
||||
|
||||
return years; |
||||
}, |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
calculateMonthlyAverages: function(stations, year) { |
||||
var sum, count, avg; |
||||
var months = {}; |
||||
|
||||
for (var i = 0; i < 12; i++) { |
||||
sum = 0; |
||||
count = 0; |
||||
|
||||
stations.forEach(function(id) { |
||||
avg = BuoyAnalysisData.stationJson[id]['avgs' + year]; |
||||
|
||||
if (avg === undefined || avg.m[i] === 0) { |
||||
return; |
||||
} |
||||
|
||||
|
||||
sum += avg.m[i]; |
||||
count++; |
||||
}); |
||||
|
||||
months['m' + i] = (sum / count) || 0; |
||||
} |
||||
|
||||
return months; |
||||
} |
||||
}; |
@ -0,0 +1,70 @@ |
||||
'use strict'; |
||||
|
||||
var BuoyAnalysisSvgMap = { |
||||
/** |
||||
* |
||||
*/ |
||||
projection: d3.geo.mercator() |
||||
.center([-122, 38]) |
||||
.scale(1960) |
||||
.translate([BuoyAnalysisData.mapW / 2, BuoyAnalysisData.mapH / 2]), |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
drawMap: function() { |
||||
var path = d3.geo.path().projection(BuoyAnalysisSvgMap.projection); |
||||
|
||||
d3.select("svg#map").selectAll('.subunit') |
||||
.data(BuoyAnalysisData.mapJson.features) |
||||
.enter().append('path') |
||||
.attr('d', path) |
||||
.attr('fill', 'khaki') |
||||
.attr('stroke', 'lime'); |
||||
}, |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
drawStations: function() { |
||||
var tmp; |
||||
var station; |
||||
var stations = []; |
||||
|
||||
for (var prop in BuoyAnalysisData.stationJson) { |
||||
station = BuoyAnalysisData.stationJson[prop]; |
||||
|
||||
tmp = BuoyAnalysisSvgMap.projection([station.lon, station.lat]); |
||||
station.x = tmp[0]; |
||||
station.y = tmp[1]; |
||||
|
||||
stations.push(station); |
||||
} |
||||
|
||||
d3.select("svg#map").selectAll('.station') |
||||
.data(stations) |
||||
.enter().append('circle') |
||||
.attr('cx', function(d) { return d.x; }) |
||||
.attr('cy', function(d) { return d.y; }) |
||||
.attr('r', 3) |
||||
.attr('id', function(d) { return 's' + d.id; }) |
||||
.attr('fill', '#f00') |
||||
.attr('stroke', '#f00') |
||||
.attr('stroke-width', 5) |
||||
// .on("mouseover", startAnimateDetail)
|
||||
// .on("mouseout", stopAnimateDetail)
|
||||
}, |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
drawReticle: function() { |
||||
d3.select("svg#map").append('circle') |
||||
.attr('r', 50) |
||||
.attr('fill', 'rgba(200, 200, 200, 0.5)') |
||||
.attr('stroke', '#f00') |
||||
.attr("transform", "translate(" + 100 + "," + 100 + ")") |
||||
.data([ {"x": 100, "y": 100} ]) |
||||
.classed('reticle', true); |
||||
} |
||||
}; |
@ -0,0 +1,98 @@ |
||||
'use strict'; |
||||
|
||||
var BuoyAnalysisUiBehaviors = { |
||||
stationClick: function() { |
||||
|
||||
}, |
||||
|
||||
stationMouseover: function() { |
||||
|
||||
}, |
||||
|
||||
reticleDrag: function(d) { |
||||
d.x += d3.event.dx |
||||
d.y += d3.event.dy |
||||
|
||||
d3.select(this).attr('transform', 'translate(' + [ d.x, d.y ] + ')'); |
||||
}, |
||||
|
||||
reticleDragEnd: function(d) { |
||||
var dX, dY, squares, distance, station; |
||||
var stations = []; |
||||
|
||||
for (var s in BuoyAnalysisData.stationJson) { |
||||
station = BuoyAnalysisData.stationJson[s]; |
||||
|
||||
dX = d.x - station.x; |
||||
dY = d.y - station.y; |
||||
|
||||
squares = Math.pow(dX, 2) + Math.pow(dY, 2) |
||||
distance = Math.pow(squares, 0.5); |
||||
|
||||
if (distance < 50) { |
||||
stations.push(s); |
||||
} |
||||
}; |
||||
|
||||
BuoyAnalysisData.calculateYearlyAverages(stations); |
||||
BuoyAnalysisData.calculateMonthlyAverages(stations, 1982); |
||||
}, |
||||
|
||||
reticleResize: function() { |
||||
|
||||
}, |
||||
|
||||
attachBehaviors: function() { |
||||
// d3.selectAll('.detail')
|
||||
// .on('mouseover', startAnimateStation)
|
||||
// .on('mouseout', stopAnimateStation);
|
||||
|
||||
d3.select('.reticle') |
||||
.call(d3.behavior.drag() |
||||
.on('drag', BuoyAnalysisUiBehaviors.reticleDrag) |
||||
.on('dragend', BuoyAnalysisUiBehaviors.reticleDragEnd) |
||||
); |
||||
|
||||
|
||||
}, |
||||
}; |
||||
|
||||
|
||||
|
||||
// function startAnimateStation() {
|
||||
// var id = d3.event.target.id || d3.event.target.parentNode.id;
|
||||
// var selector = '#' + id.split('-')[1];
|
||||
|
||||
// d3.select(selector)
|
||||
// .transition()
|
||||
// .attr('r', '10')
|
||||
// .duration(200)
|
||||
// };
|
||||
|
||||
// function stopAnimateStation() {
|
||||
// var id = d3.event.target.id || d3.event.target.parentNode.id;
|
||||
// var selector = '#' + id.split('-')[1];
|
||||
|
||||
// d3.select(selector)
|
||||
// .transition()
|
||||
// .attr('r', '3')
|
||||
// .duration(200)
|
||||
// };
|
||||
|
||||
// /**
|
||||
// *
|
||||
// */
|
||||
// function startAnimateDetail() {
|
||||
// var id = d3.event.target.id;
|
||||
// d3.select('#detail-' + id)
|
||||
// .classed('active', true)
|
||||
// }
|
||||
|
||||
// /**
|
||||
// *
|
||||
// */
|
||||
// function stopAnimateDetail() {
|
||||
// var id = d3.event.target.id;
|
||||
// d3.select('#detail-' + id)
|
||||
// .classed('active', false)
|
||||
// }
|
@ -0,0 +1,67 @@ |
||||
'use strict'; |
||||
|
||||
var BuoyAnalysisUiDom = { |
||||
/** |
||||
* |
||||
*/ |
||||
populateMonths: function() { |
||||
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; |
||||
|
||||
var div; |
||||
var bottom = document.querySelector('.container-bottom'); |
||||
|
||||
for (var i = 0; i < 12; i++) { |
||||
div = document.createElement('div'); |
||||
div.className = 'month'; |
||||
div.innerHTML = months[i]; |
||||
bottom.appendChild(div); |
||||
} |
||||
}, |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
populateStationDetail: function() { |
||||
var div, name, lat, lon; |
||||
var center = document.querySelector('.container-center'); |
||||
var arr = []; |
||||
|
||||
for (var prop in BuoyAnalysisData.stationJson) { |
||||
arr.push({ |
||||
id: prop, |
||||
lat: BuoyAnalysisData.stationJson[prop].lat, |
||||
lon: BuoyAnalysisData.stationJson[prop].lon, |
||||
name: BuoyAnalysisData.stationJson[prop].name |
||||
}); |
||||
} |
||||
|
||||
arr.sort(function(a, b) { |
||||
return a.lon < b.lon; |
||||
}); |
||||
|
||||
arr.forEach(function(obj) { |
||||
name = document.createElement('span'); |
||||
name.className = 'station-name'; |
||||
name.innerHTML = obj.name; |
||||
|
||||
lat = document.createElement('span'); |
||||
lat.className = 'station-lat'; |
||||
lat.innerHTML = obj.lat; |
||||
|
||||
lon = document.createElement('span'); |
||||
lon.className = 'station-lon'; |
||||
lon.innerHTML = obj.lon; |
||||
|
||||
div = document.createElement('div'); |
||||
div.className = 'detail'; |
||||
div.id = 'detail-' + obj.id; |
||||
div.style.border = '1px solid red' |
||||
|
||||
div.appendChild(name); |
||||
div.appendChild(lat); |
||||
div.appendChild(lon); |
||||
|
||||
center.appendChild(div); |
||||
}); |
||||
} |
||||
}; |
@ -1,174 +0,0 @@ |
||||
'use strict'; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
function Mesh(width, height, side) { |
||||
// Length of a side
|
||||
this.side = side; |
||||
this.width = width; |
||||
this.height = height; |
||||
|
||||
// How far a seed will extend its reach
|
||||
this.seedWeight = 5; |
||||
|
||||
// Color seed coordinants
|
||||
// this.addSeed(new MeshSeed(0, 0, '#ff0000', this.seedWeight), faces);
|
||||
// this.addSeed(new MeshSeed(0, this.height - 1, '#0000ff', this.seedWeight), faces);
|
||||
// this.addSeed(new MeshSeed(this.width - 1, 0, '#00ff00', this.seedWeight), faces);
|
||||
// this.addSeed(new MeshSeed(this.width - 1, this.height - 1, '#ffff00', this.seedWeight), faces);
|
||||
|
||||
// this.calculateFills(faces);
|
||||
|
||||
// var svg = d3.select("body").append("svg").attr('width', this.width).attr('height', this.height);
|
||||
|
||||
// d3.select(id).attr('fill', seed.fill);
|
||||
// for (var i = 0, len = seeds.length; i < len; i++) {
|
||||
// d3.select(seeds[i].faceId).attr('fill', seeds[i].fill);
|
||||
// }
|
||||
}; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
Mesh.prototype = { |
||||
/** |
||||
* Faces must be a one dimensional array to pass to D3. |
||||
*/ |
||||
initFaces: function(width, height, side) { |
||||
var countW = Math.ceil(width / side); |
||||
var countH = Math.ceil(height / side); |
||||
var all = []; |
||||
|
||||
for (var w = 0; w < countW; w++) { |
||||
for (var h = 0; h < countH; h++) { |
||||
all.push(new MeshFace( |
||||
`f-${w}-${h}-${all.length}`, |
||||
w * side, |
||||
h * side, |
||||
side |
||||
)); |
||||
} |
||||
} |
||||
|
||||
return all; |
||||
}, |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
calculateFills: function(faces) { |
||||
var len = faces.length; |
||||
var hexes, colors; |
||||
|
||||
var populate = function(color) { |
||||
var arr = []; |
||||
for (var k = 0; k < color.weight; k++) { |
||||
arr.push(color.hex); |
||||
} |
||||
return arr; |
||||
}; |
||||
|
||||
for (var i = 0; i < len; i++) { |
||||
hexes = []; |
||||
|
||||
for (var j = 0; j < faces[i].colors.length; j++) { |
||||
colors = faces[i].colors[j]; |
||||
hexes = hexes.concat(populate(colors)); |
||||
} |
||||
|
||||
while (hexes.length < this.seedWeight) { |
||||
hexes.push('#ffffff'); |
||||
} |
||||
|
||||
faces[i].fill = this.averageHexCollection(hexes); |
||||
} |
||||
}, |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
addSeed: function(seed, faces) { |
||||
var faceX = Math.floor(seed.x / this.side); |
||||
var faceY = Math.floor(seed.y / this.side); |
||||
|
||||
var countX = this.width / this.side; |
||||
var countY = this.height / this.side; |
||||
|
||||
var n = this.seedWeight; |
||||
var len = faces.length; |
||||
var delta, index, weight; |
||||
|
||||
for (var x = (faceX - n + 1); x < faceX + n; x++) { |
||||
if (x < 0 || x >= countX) { |
||||
continue; |
||||
} |
||||
|
||||
for (var y = (faceY - n + 1); y < faceY + n; y++) { |
||||
if (y < 0 || y >= countY) { |
||||
continue; |
||||
} |
||||
|
||||
// Weights decrement outwards from source in a given shape:
|
||||
|
||||
// Diamonds:
|
||||
// delta = Math.abs(faceX - x) + Math.abs(faceY - y);
|
||||
// weight = Math.max(0, n - delta);
|
||||
|
||||
// Squares:
|
||||
var weight = n - Math.max(Math.abs(faceX - x), Math.abs(faceY - y)); |
||||
|
||||
index = x * countY + y; |
||||
|
||||
if (weight > 0 && index >= 0 && index < len) { |
||||
faces[index].colors.push({ hex: seed.fill, weight: weight }); |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
averageComponentCollection: function(collection) { |
||||
var len = collection.length; |
||||
var total = 0; |
||||
|
||||
for (var i = 0; i < len; i++) { |
||||
total += parseInt(collection[i], 16); |
||||
} |
||||
|
||||
var avg = Math.round(total / len); |
||||
avg = avg.toString(16); |
||||
avg = ('00' + avg).slice(-2); |
||||
|
||||
return avg; |
||||
}, |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
averageHexCollection: function(collection) { |
||||
// check hex against regex
|
||||
|
||||
var allR = []; |
||||
var allG = []; |
||||
var allB = []; |
||||
var len = collection.length; |
||||
var tmp, hex; |
||||
|
||||
for (var i = 0; i < len; i++) { |
||||
tmp = collection[i].substr(1); |
||||
|
||||
allR.push(tmp.substr(0, 2)); |
||||
allG.push(tmp.substr(2, 2)); |
||||
allB.push(tmp.substr(4, 2)); |
||||
} |
||||
|
||||
var r = this.averageComponentCollection(allR); |
||||
var g = this.averageComponentCollection(allG); |
||||
var b = this.averageComponentCollection(allB); |
||||
|
||||
return '#' + r + g + b; |
||||
} |
||||
}; |
@ -1,13 +0,0 @@ |
||||
'use strict'; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
function MeshFace(id, x, y, side) { |
||||
this.id = id; |
||||
this.x = x; |
||||
this.y = y; |
||||
this.side = side; |
||||
this.fill = '#fff'; |
||||
this.colors = []; |
||||
}; |
@ -1,11 +0,0 @@ |
||||
'use strict'; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
function MeshSeed(x, y, fill, weight) { |
||||
this.x = x; |
||||
this.y = y; |
||||
this.fill = fill; |
||||
this.weight = weight; |
||||
}; |
@ -1,102 +0,0 @@ |
||||
'use strict'; |
||||
|
||||
(function() { |
||||
var mapFile = 'data/map.json'; |
||||
var stationsFile = 'data/stations.json'; |
||||
|
||||
var w = 450; |
||||
var h = 450; |
||||
var s = 25; |
||||
|
||||
var svg = d3.select("body").append("svg") |
||||
.attr("style", "border:1px solid lime") |
||||
.attr("width", w) |
||||
.attr("height", h); |
||||
|
||||
var projection = d3.geo.mercator() |
||||
.center([-122, 38]) |
||||
.scale(1960) |
||||
.translate([w / 2, h / 2]); |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
function drawMap() { |
||||
d3.json(mapFile, function(error, json) { |
||||
var path = d3.geo.path().projection(projection); |
||||
|
||||
svg.selectAll('.subunit') |
||||
.data(json.features) |
||||
.enter().append('path') |
||||
.attr('d', path) |
||||
.attr('stroke', 'lime'); |
||||
}); |
||||
}; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
function drawStations() { |
||||
function randomColor() { |
||||
var hexes = ['#ff0000', '#ffff00', '#ff00ff', '#00ffff', '#0000ff', '#00ff00']; |
||||
var random = Math.floor(Math.random() * hexes.length); |
||||
return hexes[random]; |
||||
}; |
||||
|
||||
d3.json(stationsFile, function(error, json) { |
||||
var tmp; |
||||
var zzz = -1; |
||||
|
||||
json.stations.forEach(function(station) { |
||||
// console.log(station);
|
||||
|
||||
tmp = projection([station.lon, station.lat]); |
||||
station.x = tmp[0]; |
||||
station.y = tmp[1]; |
||||
|
||||
if (zzz === -1) { |
||||
zzz = 0 |
||||
mesh.addSeed(new MeshSeed(tmp[0], tmp[1], randomColor(), mesh.seedWeight), faces); |
||||
} |
||||
}); |
||||
|
||||
drawMesh(); |
||||
|
||||
svg.selectAll('.station') |
||||
.data(json.stations) |
||||
.enter().append('circle') |
||||
.attr('cx', function(d) { return d.x; }) |
||||
.attr('cy', function(d) { return d.y; }) |
||||
.attr('r', 5) |
||||
.attr('fill', 'crimson'); |
||||
}); |
||||
}; |
||||
|
||||
/** |
||||
* |
||||
*/ |
||||
function drawMesh() { |
||||
mesh.calculateFills(faces); |
||||
|
||||
svg.selectAll('rect') |
||||
.data(faces) |
||||
.enter() |
||||
.append('rect') |
||||
.attr('id', function(d) { return d.id; }) |
||||
.attr('x', function(d) { return d.x; }) |
||||
.attr('y', function(d) { return d.y; }) |
||||
.attr('width', function(d) { return d.side; }) |
||||
.attr('height', function(d) { return d.side; }) |
||||
.attr('fill', function(d) { return d.fill; }) |
||||
// .attr('stroke', '#aaa')
|
||||
|
||||
drawMap(); |
||||
}; |
||||
|
||||
var mesh = new Mesh(w, h, s); |
||||
var faces = mesh.initFaces(w, h, s); |
||||
|
||||
// TODO update to promise chain
|
||||
// TODO add render type to mesh
|
||||
drawStations(); |
||||
})(); |
Loading…
Reference in new issue