This chart shows the path of the Sun in the sky today based on your current location. The red dot represents the current apparent position of the Sun in terms of azimuth and elevation.
The outer black circle represents the horizon, where the elevation of the Sun is 0°. If the Sun is outside this circle, it is below the horizon. (This means it’s dark outside, though note that some definitions of twilight extend as far as 18° below the horizon.) Where the path intersects the horizon determines sunrise and sunset. The inner concentric circles represent higher solar elevations.
The radiating lines of the graticule represent azimuth. For example, 0° azimuth means that the Sun is due North of your current location. The azimuth tells you the direction of shadows cast by the Sun, while the elevation determines their length.
forked from mbostock's block: Solar Path
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<style>
path {
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
text {
font: 10px sans-serif;
}
.horizon {
stroke: #000;
stroke-width: 1.5px;
}
.graticule {
stroke: #000;
stroke-opacity: .15;
}
.solar-path {
stroke: #f00;
stroke-width: 2px;
}
.sun circle {
fill: red;
stroke: #000;
}
.sun text {
text-anchor: middle;
}
.ticks--sun circle {
fill: red;
stroke: #fff;
stroke-width: 2px;
}
.ticks--sun text {
text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
}
.ticks line {
stroke: #000;
}
.ticks text {
text-anchor: middle;
}
.ticks--azimuth text:nth-of-type(9n + 1) {
font-weight: bold;
font-size: 14px;
}
#waiting {
font: 14px sans-serif;
position: absolute;
top: 540px;
left: 240px;
width: 480px;
margin: auto;
text-align: center;
}
#waiting b {
font-size: 24px;
line-height: 1.5em;
}
</style>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
#inputSliders { font-family:sans-serif;outline:none;margin-top:10px}
.inputgroup {border:none;}
.slider { width:420px;float:left;padding:10px;}
.slider2 { width:290px;float:left;padding:10px;}
label { float:left;font-weight:bold;padding-bottom:10px;}
input[type=range] { float:left;clear:left;margin-right:10px;width:380px;}
.slider2 input[type=range] { float:left;clear:left;margin-right:10px;width:180px;}
input[type=range]::-ms-track { background: transparent;border-color: transparent;color: transparent;-webkit-appearance: none}
input[type=range]::-webkit-slider-runnable-track { height: 5px;background:#7c7c7c; margin-top: -4px;}
input[type=range]::-webkit-slider-thumb { margin-top:-6px;}
#inputSliders p {padding-top:10px;}
</style>
</head>
<body>
<div id="inputSliders">
<form id="sliders" autocomplete="off">
<fieldset class="inputgroup">
<div class="slider2">
<label>Latitude</label>
<input type="range" name="lat" id="lat" value="45" min="-90" max="90" step = "1"><p id="latoutput">45</p></div>
<div class="slider2" >
<label>Longitude</label>
<input type="range" name="lon" id="lon" value="-70" min="-180" max="180" step = "1"><p id="lonoutput">-70</p></div>
<div class="slider2" >
<label>Time Zone</label>
<input type="range" name="tz" id="tz" value="-4" min="-12" max="12" step = "1"><p id="zoneoutput">GMT-4</p></div>
<div class="slider2">
<label>Month</label>
<input type="range" name="month" id="month" value="3" min="1" max="12" step = "1"><p id="monthoutput">3</p></div>
<div class="slider2" >
<label>Day</label>
<input type="range" name="day" id="day" value="21" min="1" max="31" step = "1"><p id="dayoutput">21</p></div>
<div class="slider2">
<label>Hour</label>
<input type="range" name="hour" id="hour" value="10.5" min="1" max="24" step = "0.25"><p id="houroutput">10.5</p></div>
</fieldset>
</form>
</div>
<svg width="960" height="960"></svg>
<script src="solar-calculator.js"></script>
<script>
// Inputs
var Lat = parseFloat($("#lat").val());
var Lon = parseFloat($("#lon").val());
var TimeZone = parseFloat($("#tz").val());
var Month = parseFloat($("#month").val());
var Day = parseFloat($("#day").val());
var Hour = parseFloat($("#hour").val());
// Update the displayed values for the sliders
$("#lat").on("input", function(event) {
Lat = parseFloat($(this).val());
$("#latoutput").text(Lat.toString());
refresh();
});
$("#lon").on("input", function(event) {
Lon = parseFloat($(this).val());
$("#lonoutput").text(Lon.toString());
refresh();
});
$("#tz").on("input", function(event) {
TimeZone = parseFloat($(this).val());
$("#zoneoutput").text('GMT' + TimeZone.toString());
refresh();
});
$("#month").on("input", function(event) {
Month = parseFloat($(this).val());
$("#monthoutput").text(Month.toString());
refresh();
});
$("#day").on("input", function(event) {
Day = parseFloat($(this).val());
$("#dayoutput").text(Day.toString());
refresh();
});
$("#hour").on("input", function(event) {
Hour = parseFloat($(this).val());
$("#houroutput").text(Hour.toString());
refresh();
});
// Sunpath
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
scale = width * .45;
var formatTime = d3.time.format("%I"),
formatNumber = d3.format(".1f"),
formatAngle = function(d) { return formatNumber(d) + "°"; };
var projection = d3.geo.projection(flippedStereographic)
.scale(scale)
.clipAngle(130)
.rotate([0, -90])
.translate([width / 2 + .5, height / 2 + .5])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
svg.append("path")
.datum(d3.geo.circle().origin([0, 90]).angle(90))
.attr("class", "horizon")
.attr("d", path);
svg.append("path")
.datum(d3.geo.graticule())
.attr("class", "graticule")
.attr("d", path);
var ticksAzimuth = svg.append("g")
.attr("class", "ticks ticks--azimuth");
ticksAzimuth.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]);
});
ticksAzimuth.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", ".35em")
.text(function(d) { return d === 0 ? "N" : d === 90 ? "E" : d === 180 ? "S" : d === 270 ? "W" : d + "°"; });
svg.append("g")
.attr("class", "ticks ticks--elevation")
.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", ".35em")
.text(function(d) { return d + "°"; });
refresh();
function refresh() {
var solar = solarCalculator([Lon, Lat]);
svg.selectAll('.solar-path').remove();
svg.selectAll('.sun').remove();
svg.selectAll('.ticks--sun').remove();
svg.insert("path", ".sphere")
.attr("class", "solar-path");
var sun = svg.insert("g", ".sphere")
.attr("class", "sun");
sun.append("circle")
.attr("r", 5);
sun.append("text")
.attr("class", "sun-label sun-label--azimuth")
.attr("dy", ".71em")
.attr("y", 10);
sun.append("text")
.attr("class", "sun-label sun-label--elevation")
.attr("dy", "1.81em")
.attr("y", 10);
var tickSun = svg.insert("g", ".sphere")
.attr("class", "ticks ticks--sun")
.selectAll("g");
offset = (new Date().getTimezoneOffset())/60
now = new Date(2000, Month-1, Day, Hour - TimeZone - offset, (Hour%parseInt(Hour))*60)
start = d3.time.day.floor(now)
end = d3.time.day.offset(start, 1)
svg.select(".solar-path")
.datum({type: "LineString", coordinates: d3.time.minutes(start, end).map(solar.position)})
.attr("d", path);
sun
.datum(solar.position(now))
.attr("transform", function(d) { return "translate(" + projection(d) + ")"; });
sun.select(".sun-label--azimuth")
.text(function(d) { return formatAngle(d[0]) + " φ"; });
sun.select(".sun-label--elevation")
.text(function(d) { return formatAngle(d[1]) + " θ"; });
tickSun = tickSun
.data(d3.time.hours(start, end), function(d) { return +d; });
tickSun.exit().remove();
var tickSunEnter = tickSun.enter().append("g")
.attr("transform", function(d) { return "translate(" + projection(solar.position(d)) + ")"; });
tickSunEnter.append("circle")
.attr("r", 2.5);
tickSunEnter.append("text")
.attr("dy", "-.31em")
.attr("y", -6)
.text(function(d) { return d.getHours() + TimeZone + offset});
}
d3.select(self.frameElement).style("height", height + 200 + "px");
function flippedStereographic(λ, φ) {
var cosλ = Math.cos(λ),
cosφ = Math.cos(φ),
k = 1 / (1 + cosλ * cosφ);
return [
k * cosφ * Math.sin(λ),
-k * Math.sin(φ)
];
}
</script>
</body>
https://d3js.org/d3.v3.min.js
https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js
https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js