"use strict"; var width = 960, height = 500, translate, scale, json_scale = 0.5, activeState = d3.select(null), activeTrain; var zoom = d3.behavior.zoom() .translate([0, 0]) .scale(1) .scaleExtent([json_scale, json_scale * 16]) .on("zoom", zoomed); var projection = d3.geo.albersUsa() .scale(2000) .translate([width, height]); var path = d3.geo.path() .projection(null); var trainPath = d3.geo.path() .projection(projection); var voronoi = d3.geom.voronoi() .clipExtent([[0, 0], [width / json_scale, height / json_scale]]) .x(function(d) {return d.properties.proj[0];}) .y(function(d) {return d.properties.proj[1];}); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height) .on("click", stopped, true); svg.append("rect") .attr("class", "background") .attr("width", width) .attr("height", height) .on("click", reset); var g = svg.append("g"); svg.call(zoom) .call(zoom.event); d3.json("railroads.json", function(error, rr) { window.rr = rr; g.append("g") .attr("class", "feature feature--state") .selectAll("path") .data(topojson.feature(rr, rr.objects.states).features) .enter().append("path") .attr("d", path) .on("click", function(d) {if (scale < 1) {clicked(d);}}); g.append("path") .datum(topojson.mesh(rr, rr.objects.states, function(a, b) { return a !== b; })) .attr("class", "boundary boundary--states") .attr("d", path); g.append("g") .attr("class", "feature feature--railroad") .selectAll("path") .data(topojson.feature(rr, rr.objects.railroads).features) .enter().append("path") .attr("d", path); g.append("g") .attr("class", "feature feature--station") .selectAll("path") .data(topojson.feature(rr, rr.objects.stations).features) .enter().append("path") .attr("d", path) .on("click", clicked); svg.call(zoom.scale(json_scale).event); var train_json_url = ("https://www.googleapis.com/mapsengine/v1/tables/" + "01382379791355219452-08584582962951999356" + "/features?&version=published&maxResults=1000&key=" + "AIzaSyB8k_VTTFxP75_a-na8jrY1Fk8oQClQMt8" + "&maxResults=250"); if (document.location.hostname == "localhost") { //train_json_url = "./amtrak-sample.json"; } d3.json(train_json_url, function(error, train_json) { train_json.features.forEach(function(t) { t.properties.proj = projection(t.geometry.coordinates); }); console.log(train_json); g.append("g") .attr("class", "feature feature--train") .selectAll("path") .data(train_json.features) .enter().append("path") .attr("d", trainPath); voronoi(train_json.features) .forEach(function(d) {d.point.cell = d;}); g.append("g") .attr("class", "voronoi") .selectAll("path") .data(train_json.features) .enter().append("path") .attr("class", "train-cell") .attr("d", function(d) {return d.cell.length ? "M" + d.cell.join("L") + "Z" : null;}) .on("mouseover", function(d) { trainLabel(d); d3.event.preventDefault(); }); }); }); function clicked(d) { if (activeState.node() === this) return reset(); activeState.classed("active-state", false); activeState = d3.select(this).classed("active-state", true); var bounds = path.bounds(d), dx = bounds[1][0] - bounds[0][0], dy = bounds[1][1] - bounds[0][1], x = (bounds[0][0] + bounds[1][0]) / 2, y = (bounds[0][1] + bounds[1][1]) / 2, scale = Math.min(.9 / Math.max(dx / width, dy / height), zoom.scaleExtent()[1]), translate = [width / 2 - scale * x, height / 2 - scale * y]; svg.transition() .duration(750) .call(zoom.translate(translate).scale(scale).event); } function reset() { activeState.classed("active-state", false); activeState = d3.select(null); activeTrain = null; d3.select('.label').remove(); svg.transition() .duration(750) .call(zoom.translate([0, 0]).scale(.5).event); } function zoomed() { translate = d3.event.translate; scale = d3.event.scale; g.style("stroke-width", 1.0 / d3.event.scale + "px"); g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); path.pointRadius(4 / (d3.event.scale + .4)); g.selectAll("g.feature--station path").attr("d", path); g.selectAll("g.boundary--states").style("stroke-width", .8 / d3.event.scale + "px"); trainPath.pointRadius(6 / (d3.event.scale + .4)); g.selectAll("g.feature--train path").attr("d", trainPath); if (activeTrain) {trainLabel(activeTrain);} } function stopped() { if (d3.event.defaultPrevented) d3.event.stopPropagation(); } var format = d3.time.format("%Y-%m-%d %I:%M%p"); function trainLabel(d) { activeTrain = d; var x = (d.properties.proj[0] * scale + translate[0]), y = (d.properties.proj[1] * scale + translate[1]); var label = d3.select('body').selectAll('div.label') .data([d]) var new_label = label.enter().append('div') .attr('class', 'label') .style('position', 'absolute'); new_label.append('span').attr('class', 'trainnum'); new_label.append('span').attr('class', 'routename'); new_label.append('br'); new_label.append('div').attr('class', 'origcode'); new_label.append('div').attr('class', 'destcode'); new_label.append('div').attr('class', 'velocity'); new_label.append('div').attr('class', 'lastvalts'); label .transition().duration(100) .style('left', (x + 10) + 'px') .style('top', y + 'px') .style('background-color', 'rgba(250, 250, 250, 0.5)') .style('border-radius', '5px') .style('padding', '3px') .style('margin', '8px'); label.selectAll('span.trainnum') .text(d.properties.TrainNum + ' - '); label.selectAll('span.routename') .text(d.properties.RouteName); label.selectAll('div.origcode') .text("Origin: " + d.properties.OrigCode); label.selectAll('div.destcode') .text("Destination: " + d.properties.DestCode); label.selectAll('div.velocity') .text("Velocity: " + (+d.properties.Velocity).toFixed(1) + "mph"); label.selectAll('div.lastvalts') .text("Last Update: " + format(new Date(d.properties.LastValTS))); }