parent
4575df5e59
commit
86f4cafca5
14 changed files with 498 additions and 458 deletions
@ -1 +1,3 @@ |
|||||||
natural-earth |
experiments |
||||||
|
d3.min.js |
||||||
|
node_modules |
||||||
|
@ -1,39 +0,0 @@ |
|||||||
var file = 'natural-earth/california.geojson'; |
|
||||||
|
|
||||||
d3.json(file, function(error, mapJson) { |
|
||||||
|
|
||||||
var projection = |
|
||||||
d3.geo.mercator() |
|
||||||
.center([-122, 38]) |
|
||||||
.scale(1960) |
|
||||||
.translate([w / 2, h / 2]); |
|
||||||
|
|
||||||
var path = d3.geo.path().projection(projection); |
|
||||||
|
|
||||||
svg.selectAll('.subunit') |
|
||||||
.data(mapJson.features) |
|
||||||
.enter().append('path') |
|
||||||
.attr('d', path) |
|
||||||
.attr('stroke', 'lime'); |
|
||||||
|
|
||||||
var buoys = []; |
|
||||||
buoys.push(projection([-122.634, 37.786])); |
|
||||||
buoys.push(projection([-122.839, 37.755])); |
|
||||||
buoys.push(projection([-122.977, 37.996])); |
|
||||||
|
|
||||||
svg.selectAll('.buoy') |
|
||||||
.data(buoys) |
|
||||||
.enter().append('circle') |
|
||||||
.attr('cx', function(d) { return d[0]; }) |
|
||||||
.attr('cy', function(d) { return d[1]; }) |
|
||||||
.attr('r', 5) |
|
||||||
.attr('fill', 'crimson'); |
|
||||||
}); |
|
||||||
|
|
||||||
var w = 450; |
|
||||||
var h = 450; |
|
||||||
|
|
||||||
var svg = d3.select("body").append("svg") |
|
||||||
.attr("style", "border:1px solid lime") |
|
||||||
.attr("width", w) |
|
||||||
.attr("height", h); |
|
File diff suppressed because one or more lines are too long
@ -1,155 +0,0 @@ |
|||||||
'use strict'; |
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
function Point(x, y, name, hex) { |
|
||||||
if (typeof x !== typeof 1) { |
|
||||||
throw new Error("x = " + x + " must be of type 'number'; it is currently of type '" + (typeof x) + "'."); |
|
||||||
} |
|
||||||
|
|
||||||
if (typeof y !== typeof 1) { |
|
||||||
throw new Error("y = " + y + " must be of type 'number'; it is currently of type '" + (typeof y) + "'."); |
|
||||||
} |
|
||||||
|
|
||||||
if (x < 0 || y < 0) { |
|
||||||
throw new Error("X and Y must be greater than 0."); |
|
||||||
} |
|
||||||
|
|
||||||
this.x = x; |
|
||||||
this.y = y; |
|
||||||
this.name = name; |
|
||||||
this.hex = hex; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
function Triangle(a, b, c) { |
|
||||||
if (!(a instanceof Point) || !(b instanceof Point) || !(c instanceof Point)) { |
|
||||||
throw new Error("All three arguments must be of type Point."); |
|
||||||
} |
|
||||||
|
|
||||||
this.a = a; |
|
||||||
this.b = b; |
|
||||||
this.c = c; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
Triangle.prototype = { |
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
findMidpoint: function(p1, p2) { |
|
||||||
var minX = Math.min(p1.x, p2.x); |
|
||||||
var maxX = Math.max(p1.x, p2.x); |
|
||||||
var minY = Math.min(p1.y, p2.y); |
|
||||||
var maxY = Math.max(p1.y, p2.y); |
|
||||||
|
|
||||||
var midX = minX + ((maxX - minX) / 2); |
|
||||||
var midY = minY + ((maxY - minY) / 2); |
|
||||||
|
|
||||||
return new Point(midX, midY); |
|
||||||
}, |
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
buildGradient: function(p) { |
|
||||||
var opp1, opp2; |
|
||||||
|
|
||||||
if (p === this.a) { |
|
||||||
opp1 = this.b; |
|
||||||
opp2 = this.c; |
|
||||||
} |
|
||||||
|
|
||||||
if (p === this.b) { |
|
||||||
opp1 = this.a; |
|
||||||
opp2 = this.c; |
|
||||||
} |
|
||||||
|
|
||||||
if (p === this.c) { |
|
||||||
opp1 = this.a; |
|
||||||
opp2 = this.b; |
|
||||||
} |
|
||||||
|
|
||||||
var fill = d3.select(document.createElementNS(d3.ns.prefix.svg, "linearGradient")) |
|
||||||
.attr("gradientUnits", "userSpaceOnUse") |
|
||||||
.attr("id", `fill${p.name}`) |
|
||||||
.attr("x1", p.x) |
|
||||||
.attr("y1", p.y) |
|
||||||
.attr("x2", this.findMidpoint(opp1, opp2).x) |
|
||||||
.attr("y2", this.findMidpoint(opp1, opp2).y) |
|
||||||
|
|
||||||
fill.append("stop") |
|
||||||
.attr("offset", "0%") |
|
||||||
.attr("stop-color", p.hex) |
|
||||||
|
|
||||||
fill.append("stop") |
|
||||||
.attr("offset", "100%") |
|
||||||
.attr("stop-color", "#fff") |
|
||||||
.attr("stop-opacity", "0") |
|
||||||
|
|
||||||
return function() { |
|
||||||
return fill.node(); |
|
||||||
}; |
|
||||||
}, |
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
buildPath: function(p) { |
|
||||||
var path = d3.select(document.createElementNS(d3.ns.prefix.svg, "path")) |
|
||||||
.attr("d", `M${this.a.x},${this.a.y} L${this.b.x},${this.b.y} L${this.c.x},${this.c.y} Z`) |
|
||||||
.attr("fill", `url(#fill${p.name})`) |
|
||||||
|
|
||||||
return function() { |
|
||||||
return path.node(); |
|
||||||
}; |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
(function() { |
|
||||||
var svg = d3.select("body").append("svg") |
|
||||||
.attr("width", 500) |
|
||||||
.attr("height", 500); |
|
||||||
|
|
||||||
var defs = svg.append("defs"); |
|
||||||
|
|
||||||
var points = [ |
|
||||||
[ |
|
||||||
new Point(100, 0, 'A1', '#f00'), |
|
||||||
new Point(200, 200, 'B1', '#0f0'), |
|
||||||
new Point(0, 200, 'C1', '#00f') |
|
||||||
], |
|
||||||
|
|
||||||
[ |
|
||||||
new Point(100, 0, 'A2', '#f00'), |
|
||||||
new Point(200, 200, 'B2', '#0f0'), |
|
||||||
new Point(300, 0, 'C2', '#00f') |
|
||||||
], |
|
||||||
|
|
||||||
[ |
|
||||||
new Point(300, 300, 'A3', '#f00'), |
|
||||||
new Point(300, 0, 'C3', '#00f'), |
|
||||||
new Point(200, 200, 'B3', '#0f0'), |
|
||||||
], |
|
||||||
]; |
|
||||||
|
|
||||||
for (var i = 0, len = points.length; i < len; i++) { |
|
||||||
var T = new Triangle(points[i][0], points[i][1], points[i][2]); |
|
||||||
|
|
||||||
defs.append(T.buildGradient(points[i][0])); |
|
||||||
defs.append(T.buildGradient(points[i][1])); |
|
||||||
defs.append(T.buildGradient(points[i][2])); |
|
||||||
|
|
||||||
svg.append(T.buildPath(points[i][0])); |
|
||||||
svg.append(T.buildPath(points[i][1])); |
|
||||||
svg.append(T.buildPath(points[i][2])); |
|
||||||
} |
|
||||||
}()); |
|
@ -1,184 +0,0 @@ |
|||||||
'use strict'; |
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
function Point(x, y, hex) { |
|
||||||
if (typeof x !== typeof 1) { |
|
||||||
throw new Error("x = " + x + " must be of type 'number'; it is currently of type '" + (typeof x) + "'."); |
|
||||||
} |
|
||||||
|
|
||||||
if (typeof y !== typeof 1) { |
|
||||||
throw new Error("y = " + y + " must be of type 'number'; it is currently of type '" + (typeof y) + "'."); |
|
||||||
} |
|
||||||
|
|
||||||
if (x < 0 || y < 0) { |
|
||||||
throw new Error("X and Y must be greater than 0."); |
|
||||||
} |
|
||||||
|
|
||||||
this.x = x; |
|
||||||
this.y = y; |
|
||||||
this.hex = hex; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
function Triangle(p1, p2, p3) { |
|
||||||
if (!(p1 instanceof Point) || !(p2 instanceof Point) || !(p3 instanceof Point)) { |
|
||||||
throw new Error("All three arguments must be of type Point."); |
|
||||||
} |
|
||||||
|
|
||||||
this.vertexA = p1; |
|
||||||
this.vertexB = p2; |
|
||||||
this.vertexC = p3; |
|
||||||
|
|
||||||
// Midpoints on each edge.
|
|
||||||
this.midpointAB = this.findMidpoint(this.vertexA, this.vertexB); |
|
||||||
this.midpointAC = this.findMidpoint(this.vertexA, this.vertexC); |
|
||||||
this.midpointBC = this.findMidpoint(this.vertexB, this.vertexC); |
|
||||||
|
|
||||||
// Midpoints of segments between midpoints.
|
|
||||||
this.midpointABAC = this.findMidpoint(this.midpointAB, this.midpointAC); |
|
||||||
this.midpointABBC = this.findMidpoint(this.midpointAB, this.midpointBC); |
|
||||||
this.midpointACBC = this.findMidpoint(this.midpointAC, this.midpointBC); |
|
||||||
|
|
||||||
// Paths created from a vertex and the midpoints on its two adjacent edges.
|
|
||||||
this.subpathA = [this.vertexA, this.midpointAB, this.midpointAC]; |
|
||||||
this.subpathB = [this.vertexB, this.midpointAB, this.midpointBC]; |
|
||||||
this.subpathC = [this.vertexC, this.midpointAC, this.midpointBC]; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
Triangle.prototype = { |
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
findMidpoint: function(p1, p2) { |
|
||||||
var minX = Math.min(p1.x, p2.x); |
|
||||||
var maxX = Math.max(p1.x, p2.x); |
|
||||||
var minY = Math.min(p1.y, p2.y); |
|
||||||
var maxY = Math.max(p1.y, p2.y); |
|
||||||
|
|
||||||
var midX = minX + ((maxX - minX) / 2); |
|
||||||
var midY = minY + ((maxY - minY) / 2); |
|
||||||
|
|
||||||
return new Point(midX, midY); |
|
||||||
}, |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
buildPath: function(p1, p2, p3, fill) { |
|
||||||
var path = d3.select(document.createElementNS(d3.ns.prefix.svg, "path")) |
|
||||||
.attr("d", `M${p1.x},${p1.y} L${p2.x},${p2.y} L${p3.x},${p3.y} Z`) |
|
||||||
// .attr("stroke", "#000")
|
|
||||||
|
|
||||||
var hex = /^#[\da-f]{3}$|^#[\da-f]{6}$/i; |
|
||||||
|
|
||||||
if (fill !== undefined) { |
|
||||||
if (fill.match(hex)) { |
|
||||||
path.attr("fill", fill).attr("stroke", fill); |
|
||||||
} |
|
||||||
else { |
|
||||||
path.attr("fill", `url(#${fill})`) |
|
||||||
// .attr("stroke", `url(#${fill})`);
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return function() { |
|
||||||
return path.node(); |
|
||||||
}; |
|
||||||
}, |
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
buildGradient: function(p1, p2, id, hex) { |
|
||||||
var fill = d3.select(document.createElementNS(d3.ns.prefix.svg, "linearGradient")) |
|
||||||
.attr("gradientUnits", "userSpaceOnUse") |
|
||||||
.attr("id", id) |
|
||||||
.attr("x1", p1.x) |
|
||||||
.attr("y1", p1.y) |
|
||||||
.attr("x2", p2.x) |
|
||||||
.attr("y2", p2.y) |
|
||||||
|
|
||||||
fill.append("stop") |
|
||||||
.attr("offset", "0%") |
|
||||||
.attr("stop-color", hex) |
|
||||||
|
|
||||||
fill.append("stop") |
|
||||||
.attr("offset", "100%") |
|
||||||
.attr("stop-color", "#fff") |
|
||||||
.attr("stop-opacity", "0") |
|
||||||
|
|
||||||
return function() { |
|
||||||
return fill.node(); |
|
||||||
}; |
|
||||||
}, |
|
||||||
}; |
|
||||||
|
|
||||||
/** |
|
||||||
* |
|
||||||
*/ |
|
||||||
(function() { |
|
||||||
var svg = d3.select("body").append("svg") |
|
||||||
.attr("width", 500) |
|
||||||
.attr("height", 500); |
|
||||||
|
|
||||||
var defs = svg.append("defs"); |
|
||||||
|
|
||||||
var points = [ |
|
||||||
[ |
|
||||||
new Point(100, 0, '#f00'), |
|
||||||
new Point(200, 200, '#0f0'), |
|
||||||
new Point(0, 300, '#00f') |
|
||||||
] |
|
||||||
|
|
||||||
// [
|
|
||||||
// new Point(100, 0, '#f00'),
|
|
||||||
// new Point(200, 200, '#0f0'),
|
|
||||||
// new Point(300, 0, '#00f')
|
|
||||||
// ],
|
|
||||||
|
|
||||||
// [
|
|
||||||
// new Point(300, 300, '#f00'),
|
|
||||||
// new Point(200, 200, '#0f0'),
|
|
||||||
// new Point(300, 0, '#00f')
|
|
||||||
// ],
|
|
||||||
]; |
|
||||||
|
|
||||||
var subpathA; |
|
||||||
var subpathB; |
|
||||||
var subpathC; |
|
||||||
var subpathM1; |
|
||||||
var subpathM2; |
|
||||||
var subpathM3; |
|
||||||
|
|
||||||
for (var i = 0, len = points.length; i < len; i++) { |
|
||||||
var T = new Triangle(points[i][0], points[i][1], points[i][2]); |
|
||||||
|
|
||||||
subpathA = T.buildPath(T.subpathA[0], T.subpathA[1], T.subpathA[2], points[i][0].hex); |
|
||||||
subpathB = T.buildPath(T.subpathB[0], T.subpathB[1], T.subpathB[2], points[i][1].hex); |
|
||||||
subpathC = T.buildPath(T.subpathC[0], T.subpathC[1], T.subpathC[2], points[i][2].hex); |
|
||||||
|
|
||||||
defs.append(T.buildGradient(T.midpointACBC, T.midpointBC, `fill-${i}-0`, points[i][0].hex)); |
|
||||||
defs.append(T.buildGradient(T.midpointABBC, T.midpointAC, `fill-${i}-1`, points[i][1].hex)); |
|
||||||
defs.append(T.buildGradient(T.midpointABAC, T.midpointAB, `fill-${i}-2`, points[i][2].hex)); |
|
||||||
|
|
||||||
subpathM1 = T.buildPath(T.midpointAB, T.midpointAC, T.midpointBC, `fill-${i}-0`); |
|
||||||
subpathM2 = T.buildPath(T.midpointAB, T.midpointAC, T.midpointBC, `fill-${i}-1`); |
|
||||||
subpathM3 = T.buildPath(T.midpointAB, T.midpointAC, T.midpointBC, `fill-${i}-2`); |
|
||||||
|
|
||||||
svg.append(subpathA); |
|
||||||
svg.append(subpathB); |
|
||||||
svg.append(subpathC); |
|
||||||
|
|
||||||
svg.append(subpathM1); |
|
||||||
svg.append(subpathM2); |
|
||||||
svg.append(subpathM3); |
|
||||||
} |
|
||||||
}()); |
|
@ -0,0 +1,47 @@ |
|||||||
|
var stations = require('./server/stations.js'); |
||||||
|
var meteo = require('./server/meteorological.js'); |
||||||
|
var Promise = require('es6-promise').Promise; |
||||||
|
var chalk = require('chalk'); |
||||||
|
|
||||||
|
//===== Weather data scraper
|
||||||
|
// var date = new Date();
|
||||||
|
|
||||||
|
var len = stations.stations.length; |
||||||
|
|
||||||
|
//===== Meteorological data scraping - careful!
|
||||||
|
(function scrapeMeteo(currentStation) { |
||||||
|
if (currentStation !== 0) { |
||||||
|
console.log(chalk.cyan('=== Finished.\n')); |
||||||
|
} |
||||||
|
|
||||||
|
if (currentStation >= len) { |
||||||
|
// if (currentStation > 1) {
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
var arr = []; |
||||||
|
|
||||||
|
console.log(chalk.cyan('\n=== (' + currentStation + ') Starting ' + stations.stations[currentStation])); |
||||||
|
|
||||||
|
// Monthly files
|
||||||
|
// for (var m = 0; m < 12; m++) {
|
||||||
|
// arr.push(meteo.getInconsistent(stations.stations[currentStation], m, 2015));
|
||||||
|
// arr.push(meteo.getMonth(stations.stations[currentStation], m));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO refactor to above format
|
||||||
|
// TODO refactor station requests into promises format
|
||||||
|
// for (var year = 1982; year < date.getFullYear() - 1; year++) {
|
||||||
|
// getYear(stationIDs[i], year);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// getCurrent(stationIDs[i]);
|
||||||
|
|
||||||
|
// Wait for all to resolve, then recurse.
|
||||||
|
Promise.all(arr).then(scrapeMeteo.bind(null, currentStation + 1)); |
||||||
|
|
||||||
|
})(0); |
||||||
|
|
||||||
|
//===== Station data scraping - shouldn't need to change often.
|
||||||
|
// stations.downloadAllMetadata();
|
||||||
|
// stations.parseAllMetadata();
|
@ -0,0 +1,47 @@ |
|||||||
|
'use strict' |
||||||
|
|
||||||
|
var http = require('http'); |
||||||
|
var fs = require('fs'); |
||||||
|
var Promise = require('es6-promise').Promise; |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
|
||||||
|
downloadedCount: 0, |
||||||
|
|
||||||
|
downloadedSize: 0, |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
*/ |
||||||
|
mkdir: function(path) { |
||||||
|
try { |
||||||
|
fs.mkdirSync(path); |
||||||
|
} catch(e) { |
||||||
|
if (e.code !== 'EEXIST'){ |
||||||
|
console.log(e); |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
*/ |
||||||
|
download: function(url, filename) { |
||||||
|
return new Promise(function(resolve, reject) { |
||||||
|
var file = fs.createWriteStream(filename); |
||||||
|
|
||||||
|
http.get(url, function(response) { |
||||||
|
var pipe = response.pipe(file); |
||||||
|
|
||||||
|
pipe.on('finish', function() { |
||||||
|
module.exports.downloadedSize += pipe.bytesWritten; |
||||||
|
module.exports.downloadedCount++; |
||||||
|
|
||||||
|
console.log(url + ' --> ' + filename + ' --> ' + pipe.bytesWritten + ' bytes | ' + module.exports.downloadedSize + ' bytes total | ' + module.exports.downloadedCount + ' file(s)'); |
||||||
|
|
||||||
|
resolve(pipe.bytesWritten); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,68 @@ |
|||||||
|
'use strict' |
||||||
|
|
||||||
|
var downloader = require('./downloader.js'); |
||||||
|
var dir = 'data/meteorological/' |
||||||
|
var Promise = require('es6-promise').Promise; |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
*/ |
||||||
|
getYear: function(buoy, yyyy) { |
||||||
|
var filename = buoy + 'h' + yyyy + '.txt'; |
||||||
|
|
||||||
|
var url = 'http://www.ndbc.noaa.gov/view_text_file.php?'+ |
||||||
|
'filename=' + filename + '.gz&dir=data/historical/stdmet/'; |
||||||
|
|
||||||
|
return downloader.download(url, dir + filename); |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
*/ |
||||||
|
getMonth: function(buoy, m, yyyy) { |
||||||
|
var month = m + 1; |
||||||
|
month = (month == 10 ? 'a' : month); |
||||||
|
month = (month == 11 ? 'b' : month); |
||||||
|
month = (month == 12 ? 'c' : month); |
||||||
|
|
||||||
|
var filename = buoy + month.toString() + yyyy + '.txt'; |
||||||
|
|
||||||
|
var url = 'http://www.ndbc.noaa.gov/view_text_file.php?' + |
||||||
|
'filename=' + filename + '.gz&dir=data/stdmet/' + this.months[m] + '/'; |
||||||
|
|
||||||
|
var path = dir + buoy + '/'; |
||||||
|
downloader.mkdir(path); |
||||||
|
return downloader.download(url, path + filename); |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
*/ |
||||||
|
getCurrent: function(buoy) { |
||||||
|
var filename = buoy + '.txt'; |
||||||
|
|
||||||
|
var url = 'http://www.ndbc.noaa.gov/data/realtime2/' + filename; |
||||||
|
|
||||||
|
return downloader.download(url, dir + filename); |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
*/ |
||||||
|
getInconsistent: function(buoy, m) { |
||||||
|
var url = 'http://www.ndbc.noaa.gov/data/stdmet/' + this.months[m] + '/' + buoy + '.txt'; |
||||||
|
|
||||||
|
var month = m + 1; |
||||||
|
month = (month == 10 ? 'a' : month); |
||||||
|
month = (month == 11 ? 'b' : month); |
||||||
|
month = (month == 12 ? 'c' : month); |
||||||
|
|
||||||
|
var path = dir + buoy + '/'; |
||||||
|
downloader.mkdir(path); |
||||||
|
|
||||||
|
return downloader.download(url, path + buoy.toString() + month + '2015-newest.txt'); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,132 @@ |
|||||||
|
'use strict' |
||||||
|
|
||||||
|
var fs = require('fs'); |
||||||
|
var downloader = require('./downloader'); |
||||||
|
var xml2js = require ('xml2js'); |
||||||
|
var dir = 'data/stations/'; |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
/** |
||||||
|
* Add station IDs here, A-Z 0-9 |
||||||
|
*/ |
||||||
|
stations: [ |
||||||
|
'ANVC1', |
||||||
|
'BDXC1', |
||||||
|
'CECC1', |
||||||
|
'CPXC1', |
||||||
|
'HBYC1', |
||||||
|
'ICAC1', |
||||||
|
'NTBC1', |
||||||
|
'PRYC1', |
||||||
|
'PTGC1', |
||||||
|
'46011', |
||||||
|
'46012', |
||||||
|
'46013', |
||||||
|
'46014', |
||||||
|
'46022', |
||||||
|
'46025', |
||||||
|
'46027', |
||||||
|
'46028', |
||||||
|
'46042', |
||||||
|
'46053', |
||||||
|
'46054', |
||||||
|
'46092', |
||||||
|
'46213', |
||||||
|
'46214', |
||||||
|
'46215', |
||||||
|
'46216', |
||||||
|
'46217', |
||||||
|
'46221', |
||||||
|
'46223', |
||||||
|
'46225', |
||||||
|
'46232', |
||||||
|
'46234', |
||||||
|
'46237', |
||||||
|
'46239', |
||||||
|
'46240', |
||||||
|
'46242', |
||||||
|
'46244', |
||||||
|
'46253', |
||||||
|
'46254', |
||||||
|
'46256', |
||||||
|
'46257' |
||||||
|
], |
||||||
|
|
||||||
|
/** |
||||||
|
* Downloads each station's data XML. |
||||||
|
*/ |
||||||
|
downloadAllMetadata: function() { |
||||||
|
var len = this.stations.length; |
||||||
|
var url; |
||||||
|
|
||||||
|
for (var i = 0; i < len; i++) { |
||||||
|
url = 'http://www.ndbc.noaa.gov/get_observation_as_xml.php?station=' + this.stations[i]; |
||||||
|
downloader.download(url, dir + this.stations[i] + '.txt'); |
||||||
|
} |
||||||
|
}, |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
*/ |
||||||
|
parseAllMetadata: function() { |
||||||
|
function done() { |
||||||
|
if (data.length === len) { |
||||||
|
fs.writeFile(outfile, data.join('\n'), function(err) { |
||||||
|
if (err) { |
||||||
|
throw new Error(err) |
||||||
|
} |
||||||
|
|
||||||
|
console.log('Station data written to ' + outfile); |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
function next() { |
||||||
|
// Wait for other concurrent files to finish.
|
||||||
|
if (count !== data.length) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
var concurrent = 3; |
||||||
|
var tmp; |
||||||
|
|
||||||
|
for (var i = 0; i < concurrent; i++) { |
||||||
|
tmp = count + i; |
||||||
|
|
||||||
|
if (tmp === len) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// console.log(tmp + "(" + count + ")" + " reading " + dir + module.exports.stations[tmp] + '.txt');
|
||||||
|
fs.readFile(dir + module.exports.stations[tmp] + '.txt', 'utf8', thenParse); |
||||||
|
} |
||||||
|
|
||||||
|
count += concurrent; |
||||||
|
}; |
||||||
|
|
||||||
|
function thenParse(err, xml) { |
||||||
|
if (err) { |
||||||
|
throw new Error(err) |
||||||
|
} |
||||||
|
|
||||||
|
xml2js.parseString(xml, thenReport); |
||||||
|
}; |
||||||
|
|
||||||
|
function thenReport(err, json) { |
||||||
|
if (err) { |
||||||
|
throw new Error(err) |
||||||
|
} |
||||||
|
|
||||||
|
data.push(JSON.stringify(json.observation.$)); |
||||||
|
next(); |
||||||
|
done(); |
||||||
|
}; |
||||||
|
|
||||||
|
var outfile = 'data-stations.json'; |
||||||
|
var len = module.exports.stations.length; |
||||||
|
var count = 0; |
||||||
|
var data = []; |
||||||
|
|
||||||
|
next(); |
||||||
|
}, |
||||||
|
}; |
@ -0,0 +1,13 @@ |
|||||||
|
'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 = []; |
||||||
|
}; |
@ -0,0 +1,11 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
/** |
||||||
|
* |
||||||
|
*/ |
||||||
|
function MeshSeed(x, y, fill, weight) { |
||||||
|
this.x = x; |
||||||
|
this.y = y; |
||||||
|
this.fill = fill; |
||||||
|
this.weight = weight; |
||||||
|
}; |
@ -0,0 +1,102 @@ |
|||||||
|
'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