|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
form { |
|
position: absolute; |
|
right: 10px; |
|
top: 10px; |
|
} |
|
|
|
|
|
svg { |
|
font: 10px sans-serif; |
|
shape-rendering: crispEdges; |
|
} |
|
|
|
.axis path { |
|
display: none; |
|
} |
|
|
|
.axis line { |
|
stroke: #000; |
|
shape-rendering: crispEdges; |
|
} |
|
|
|
.station line { |
|
stroke: #ddd; |
|
stroke-dasharray: 1,1; |
|
shape-rendering: crispEdges; |
|
} |
|
|
|
.station text { |
|
text-anchor: end; |
|
} |
|
|
|
.train path { |
|
fill: none; |
|
stroke-width: 1.5px; |
|
shape-rendering: crispEdges; |
|
} |
|
|
|
.train circle { |
|
stroke: #fff; |
|
} |
|
|
|
.hidden { |
|
display: none; |
|
} |
|
|
|
.train .daily path { stroke: rgb(34,34,34); } |
|
.train .daily circle { fill: rgb(34,34,34); } |
|
|
|
.train .working path { stroke: rgb(183,116,9); } |
|
.train .working circle { fill: rgb(183,116,9); } |
|
|
|
.train .weekend path { stroke: rgb(158, 137, 193); } |
|
.train .weekend circle { fill: rgb(158, 137, 193); } |
|
|
|
.train .ex_saturday path { stroke: rgb(158, 137, 193); } |
|
.train .ex_saturday circle { fill: rgb(158, 137, 193); } |
|
|
|
|
|
.train .ex_sunday path { stroke: rgb(158, 137, 193); } |
|
.train .ex_sunday circle { fill: rgb(158, 137, 193); } |
|
|
|
.train .cancelled path { stroke: rgb(255,64,64); } |
|
.train .cancelled circle { fill: rgb(255,64,64); } |
|
|
|
#tooltip { |
|
position: absolute; |
|
width: auto; |
|
height: auto; |
|
padding: 10px; |
|
background-color: white; |
|
opacity: .9; |
|
-webkit-border-radius: 10px; |
|
-moz-border-radius: 10px; |
|
border-radius: 10px; |
|
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); |
|
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); |
|
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); |
|
pointer-events: none; |
|
} |
|
|
|
#tooltip.hidden { |
|
display: none; |
|
} |
|
|
|
#tooltip p { |
|
margin: 0; |
|
font-family: sans-serif; |
|
font-size: 10px; |
|
line-height: 12px; |
|
} |
|
|
|
</style> |
|
<body> |
|
|
|
<div id="tooltip" class="hidden"> |
|
<p><span id="stations"> </span></p> |
|
<p><span id="time">100</span></p> |
|
</div> |
|
|
|
|
|
<div id='form2'> |
|
<input type='radio' name='f_direction' id='moscow' /><label for='moscow'>в Москву</label> |
|
<input type='radio' name='f_direction' id='oblast' /><label for='oblast'>в область</label> |
|
|
|
</div> |
|
|
|
<div id='form'> |
|
<input type='radio' name='type_train' id='working' /><label for='working'>рабочие</label> |
|
<input type='radio' name='type_train' id='saturday' /><label for='saturday'>суббота</label> |
|
<input type='radio' name='type_train' id='sunday' /><label for='sunday'>воскресенье</label> |
|
|
|
|
|
</div> |
|
|
|
|
|
<script src="http://d3js.org/d3.v3.min.js"></script> |
|
<script> |
|
|
|
var stations = []; // lazily loaded |
|
|
|
var formatTime = d3.time.format("%H:%M"); |
|
|
|
var margin = {top: 20, right: 20, bottom: 20, left: 150}, |
|
width = 960 - margin.left - margin.right, |
|
height = 600 - margin.top - margin.bottom; |
|
|
|
|
|
var x = d3.time.scale() |
|
.domain([parseTime("4:20"), parseTime("12:00")]) |
|
.range([0, width]); |
|
|
|
var y = d3.scale.linear() |
|
.range([0, height]); |
|
|
|
var xAxis = d3.svg.axis() |
|
.scale(x) |
|
.ticks(10) |
|
.tickFormat(formatTime); |
|
|
|
var zoom = d3.behavior.zoom() |
|
.x(x) |
|
.scaleExtent([1, 1]) |
|
.on("zoom", zoom); |
|
|
|
var line = d3.svg.line() |
|
.x(function(d) { return x(d.time); }) |
|
.y(function(d) { return y(d.station.distance); }); |
|
|
|
var svg = d3.select("body").append("svg") |
|
.attr("width", width + margin.left + margin.right) |
|
.attr("height", height + margin.top + margin.bottom) |
|
.append("g") |
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
|
|
|
svg.append("defs").append("clipPath") |
|
.attr("id", "clip") |
|
.append("rect") |
|
.attr("y", -margin.top) |
|
.attr("width", width) |
|
.attr("height", height + margin.top + margin.bottom); |
|
|
|
d3.csv("electro.csv", type, function(error, trains) { |
|
|
|
console.log(trains) |
|
y.domain(d3.extent(stations, function(d) { return d.distance; })); |
|
|
|
var station = svg.append("g") |
|
.attr("class", "station") |
|
.selectAll("g") |
|
.data(stations) |
|
.enter().append("g") |
|
.attr("transform", function(d) { return "translate(0," + y(d.distance) + ")"; }); |
|
|
|
station.append("text") |
|
.attr("x", -6) |
|
.attr("dy", ".35em") |
|
.text(function(d) { return d.name; }); |
|
|
|
station.append("line") |
|
.attr("x2", width); |
|
|
|
svg.append("g") |
|
.attr("class", "x top axis") |
|
.call(xAxis.orient("top")); |
|
|
|
svg.append("g") |
|
.attr("class", "x bottom axis") |
|
.attr("transform", "translate(0," + height + ")") |
|
.call(xAxis.orient("bottom")); |
|
|
|
var train = svg.append("g") |
|
.attr("class", "train") |
|
.attr("clip-path", "url(#clip)") |
|
.selectAll("g") |
|
.data(trains) |
|
.enter().append("g") |
|
. attr("class", function(d) { |
|
var tt = d.type + " " + d.direction; |
|
//console.log(tt); |
|
return tt; }); |
|
|
|
|
|
train.append("path") |
|
.attr("d", function(d) { return line(d.stops); }); |
|
|
|
// Выбор отрисовывающихся поездов в зависимости от выбора меню |
|
d3.selectAll("input[name=type_train]") |
|
.on("change", function(){ |
|
|
|
if (this.id == 'working'){ |
|
d3.selectAll(".daily"+"."+cur_direction).classed("hidden", false); |
|
d3.selectAll(".working"+"."+cur_direction).classed("hidden", false); |
|
d3.selectAll(".weekend"+"."+cur_direction).classed("hidden", true); |
|
d3.selectAll(".ex_saturday"+"."+cur_direction).classed("hidden", true); |
|
d3.selectAll(".ex_sunday"+"."+cur_direction).classed("hidden", true); |
|
} |
|
|
|
if (this.id == 'saturday'){ |
|
d3.selectAll(".daily"+"."+cur_direction).classed("hidden", false); |
|
d3.selectAll(".working"+"."+cur_direction).classed("hidden", true); |
|
d3.selectAll(".weekend"+"."+cur_direction).classed("hidden", false); |
|
d3.selectAll(".ex_saturday"+"."+cur_direction).classed("hidden", true); |
|
d3.selectAll(".ex_sunday"+"."+cur_direction).classed("hidden", false); |
|
} |
|
|
|
|
|
|
|
if (this.id == 'sunday'){ |
|
d3.selectAll(".daily"+"."+cur_direction).classed("hidden", false); |
|
d3.selectAll(".working"+"."+cur_direction).classed("hidden", true); |
|
d3.selectAll(".weekend"+"."+cur_direction).classed("hidden", false); |
|
d3.selectAll(".ex_saturday"+"."+cur_direction).classed("hidden", false); |
|
d3.selectAll(".ex_sunday"+"."+cur_direction).classed("hidden", true); |
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
// реагировать на выбор направления |
|
var cur_direction = "" |
|
|
|
d3.selectAll("input[name=f_direction]") |
|
.on("change", function(){ |
|
cur_direction = this.id; |
|
if (this.id == 'moscow'){ |
|
d3.selectAll(".moscow").classed("hidden", false); |
|
d3.selectAll(".oblast").classed("hidden", true); |
|
|
|
} |
|
|
|
if (this.id == 'oblast'){ |
|
d3.selectAll(".moscow").classed("hidden", true); |
|
d3.selectAll(".oblast").classed("hidden", false); |
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
train.selectAll("circle") |
|
.data(function(d) { return d.stops; }) |
|
.enter().append("circle") |
|
.attr("transform", function(d) { return "translate(" + x(d.time) + "," + y(d.station.distance) + ")"; }) |
|
.attr("r", 3) |
|
.on("mouseover", function(d) { |
|
//console.log(d3.select(this).attr("transform")); |
|
|
|
|
|
|
|
var xPosition = x(d.time)+margin.left + margin.right; |
|
var yPosition = y(d.station.distance); |
|
|
|
|
|
d3.select("#tooltip") |
|
.style("left", xPosition + "px") |
|
.style("top", yPosition + "px") |
|
.select("#stations") |
|
.text(d.station.name); |
|
d3.select("#tooltip") |
|
.select("#time") |
|
.text(formatTime(d.time)); |
|
d3.select("#tooltip").classed("hidden", false); |
|
|
|
}) |
|
|
|
.on("mouseout", function() { |
|
d3.select("#tooltip").classed("hidden", true); |
|
}); |
|
// d3.selectAll("") |
|
}); |
|
|
|
function type(d, i) { |
|
|
|
// Extract the stations from the "stop|*" columns. |
|
if (!i) for (var k in d) { |
|
if (/^stop\|/.test(k)) { |
|
var p = k.split("|"); |
|
stations.push({ |
|
key: k, |
|
name: p[1], |
|
distance: +p[2], |
|
zone: +p[3] |
|
}); |
|
} |
|
} |
|
|
|
return { |
|
number: d.number, |
|
type: d.type, |
|
direction: d.direction, |
|
stops: stations |
|
.map(function(s) { return {station: s, time: parseTime(d[s.key])}; }) |
|
.filter(function(s) { return s.time != null; }) |
|
}; |
|
} |
|
|
|
function parseTime(s) { |
|
var t = formatTime.parse(s); |
|
if (t != null && t.getHours() < 3) t.setDate(t.getDate() + 1); |
|
return t; |
|
} |
|
|
|
|
|
</script> |