Hourly solar analemmas (ignoring daylight savings time) as seen from San Francisco in 2014.
forked from mbostock's block: Solar Analemmas (local)
license: gpl-3.0 | |
height: 960 |
Hourly solar analemmas (ignoring daylight savings time) as seen from San Francisco in 2014.
forked from mbostock's block: Solar Analemmas (local)
<!DOCTYPE html> | |
<svg width="960" height="960" font-family="sans-serif" font-size="10" text-anchor="middle" fill="none" stroke="black"></svg> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://unpkg.com/[email protected]"></script> | |
<script> | |
var svg = d3.select("svg"), | |
width = +svg.attr("width"), | |
height = +svg.attr("height"), | |
scale = width * 0.45; | |
var start = new Date(Date.UTC(2014, 0, 1)), | |
end = new Date(Date.UTC(2015, 0, 1)); | |
var projection = d3.geoProjection(flippedStereographic) | |
.scale(scale) | |
.clipAngle(130) | |
.rotate([0, -90]) | |
.translate([width / 2 + 0.5, height / 2 + 0.5]) | |
.precision(0.1); | |
var path = d3.geoPath(projection); | |
navigator.geolocation.getCurrentPosition(located); | |
function located({coords}) { | |
var solar = solarCalculator([coords.longitude, coords.latitude]); | |
svg.append("path") | |
.datum(d3.geoCircle().center([0, 90]).radius(90)) | |
.attr("stroke-width", 1.5) | |
.attr("d", path); | |
svg.append("path") | |
.datum(d3.geoGraticule()) | |
.attr("stroke-width", 0.15) | |
.attr("d", path); | |
svg.append("g") | |
.selectAll("line") | |
.data(d3.range(360)) | |
.enter().append("line") | |
.each(function(d) { | |
var p0 = projection([d, 0]), | |
p1 = projection([d, d % 10 ? -1 : -2]); | |
d3.select(this) | |
.attr("x1", p0[0]) | |
.attr("y1", p0[1]) | |
.attr("x2", p1[0]) | |
.attr("y2", p1[1]); | |
}); | |
svg.append("g") | |
.attr("fill", "black") | |
.attr("stroke", "none") | |
.selectAll("text") | |
.data(d3.range(0, 360, 10)) | |
.enter().append("text") | |
.each(function(d) { | |
var p = projection([d, -4]); | |
d3.select(this) | |
.attr("x", p[0]) | |
.attr("y", p[1]); | |
}) | |
.attr("dy", "0.35em") | |
.text(function(d) { return d === 0 ? "N" : d === 90 ? "E" : d === 180 ? "S" : d === 270 ? "W" : d + "°"; }) | |
.data(d3.range(0, 360, 90), function(d) { return d; }) | |
.attr("font-weight", "bold") | |
.attr("font-size", 14); | |
svg.append("g") | |
.attr("fill", "black") | |
.attr("stroke", "none") | |
.selectAll("text") | |
.data(d3.range(10, 91, 10)) | |
.enter().append("text") | |
.each(function(d) { | |
var p = projection([0, d]); | |
d3.select(this) | |
.attr("x", p[0]) | |
.attr("y", p[1]); | |
}) | |
.attr("dy", "0.35em") | |
.text(function(d) { return d + "°"; }); | |
svg.append("g") | |
.attr("stroke", "red") | |
.attr("stroke-width", 2) | |
.selectAll("path") | |
.data(d3.range(24)) | |
.enter().append("path") | |
.datum(function(h) { | |
return { | |
type: "LineString", | |
coordinates: d3.utcDays(start, end).map(function(d) { | |
return solar.position(d3.utcHour.offset(d, h)); | |
}) | |
}; | |
}) | |
.attr("d", path); | |
} | |
function flippedStereographic(x, y) { | |
var cx = Math.cos(x), cy = Math.cos(y), k = 1 / (1 + cx * cy); | |
return [k * cy * Math.sin(x), -k * Math.sin(y)]; | |
} | |
</script> |