Built with blockbuilder.org
forked from woodyrew's block: Tides visualisation (d3 v4)
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://momentjs.com/downloads/moment.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0;
max-width: 600px;
font-family: sans-serif
}
input {
width: 100%
}
text {
font-family: sans-serif
}
.night-labels {
fill: #FFEFD5
}
.day-label {
fill: #191970;
text-anchor: middle
}
.domain {
display: none
}
svg {
height: auto;
margin-left: 10px;
width: calc(100% - 20px);
}
</style>
</head>
<body>
<h1>Tides Visualisation</h1>
<svg id="tides" viewBox="0 0 600 150"></svg>
<label for="day_select">Day</label>
<input type="range" id="day_select" name="day_select"
min="0" max="0" value="0" list="ticks"/>
<datalist id="ticks">
</datalist>
<p>Date: <strong id="human_date"></strong>
<script>
const dimension = {
width: 600,
height: 150
};
const margin = {
bottom: 20
};
const colour = {
night: '#191970',
dim: '#ccc',
day: 'AliceBlue',
seaIn: '#20783b',
seaOut: '#38ab5b'
};
d3.json('dateTides.json', (tidesErr, dateTides) => {
if (tidesErr) return console.error(tidesErr);
const maxTide =
Math.ceil(
dateTides.reduce((maxTide, date) => {
const dateMax = date.tides.reduce(
(max, tide) => (tide.height > max ? tide.height : max),
0
);
return dateMax > maxTide ? dateMax : maxTide;
}, 0)
) + 1;
const xScale = d3
.scaleLinear()
.domain([0, 24])
.range([0, dimension.width]);
const yScale = d3
.scaleLinear()
.domain([0, maxTide])
.range([dimension.height - margin.bottom, 0]);
const valueLine = d3.area()
.x(d => xScale(d.hours))
.y1(d => yScale(d.height))
.y0(yScale(0))
.curve(d3.curveCardinal);
const svg = d3.select('#tides');
const daySelect = d3.select('#day_select');
const humanDate = d3.select('#human_date');
let sky = svg.append('rect')
.attr("width", dimension.width )
.attr("height", dimension.height - margin.bottom)
.style("fill", "url(#sky-gradient)")
let gradient = svg.append("defs")
.append("linearGradient")
.attr("id", "sky-gradient")
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "100%")
.attr("y2", "0%");
let nightLabels = svg.selectAll(".night-labels")
.data(["Night", "Night"])
.enter()
.append("text")
.text(d => d)
.attr("class", "night-labels")
.attr("y", 20)
.attr("x", function (d, i) {
if (i == 0) { return 5 }
if (i == 1) { return dimension.width - 5 }
})
.style("text-anchor", function(d, i){
if (i == 0) { return "start" }
if (i == 1) { return "end" }
})
let dayLabel = svg.selectAll(".day-label")
.data(["Day"])
.enter()
.append("text")
.text(d => d)
.attr("class", "day-label")
.attr("y", 20)
d3.select('#ticks')
.selectAll('option')
.data(dateTides)
.enter()
.append('option')
.text((d, i) => i)
// Setup the Range slider
daySelect.attr('max', dateTides.length - 1)
.attr("step", 1)
const render = today => {
const {
isoDate,
dawnHours,
sunriseHours,
sunsetHours,
duskHours,
tides
} = today;
const humanDateText = moment(isoDate).format('dddd Do MMMM YYYY');
humanDate.text(humanDateText);
let stopsData = [
{ "offset": 0, "stopColour": colour.night },
{ "offset": (dawnHours/24), "stopColour": colour.night },
//{ "offset": (((sunriseHours + dawnHours)/2)/24), "stopColour": "Coral" },
{ "offset": (sunriseHours/24), "stopColour": colour.day },
{ "offset": (sunsetHours/24), "stopColour": colour.day },
//{ "offset": (((sunsetHours + duskHours)/2)/24), "stopColour": "Coral" },
{ "offset": (duskHours/24), "stopColour": colour.night },
{ "offset": 1, "stopColour": colour.night }
];
gradient.selectAll("stop").remove()
gradient.selectAll("stop").data(stopsData)
.enter()
.append("stop")
.attr("offset", function (d) { return d.offset })
.attr("stop-color", function (d) { return d.stopColour })
dayLabel.attr("x", function(d){
return xScale((sunriseHours + sunsetHours)/2)
})
// Add chart area
let chart = svg.selectAll('.line')
.data([tides], d => d.id);
const chartEnter = chart
.enter()
.append('path')
.attr('class', 'line')
.style('fill', '#00B6BE');
chart = chartEnter
.merge(chart)
.attr('d', valueLine);
};
// Add the x axis
svg
.append('g')
.attr('class', 'tick')
.attr('transform', `translate(0,${dimension.height - margin.bottom})`)
.call(d3.axisBottom(xScale).tickSizeOuter(0));
// Remove 0 and 24 numbers from scale
svg
.selectAll('.tick')
.filter(d => d === 0 || d === 24)
.remove();
// Handler for range input change
const rangeTrigger = () => {
const index = daySelect.property('value');
const date = dateTides[index];
render(date);
};
daySelect.on('input', rangeTrigger);
rangeTrigger();
});
</script>
</body>
https://d3js.org/d3.v4.min.js
https://momentjs.com/downloads/moment.min.js