"use strict"; var width = 960, height = 500, active = d3.select(null); var zoom = d3.behavior.zoom() .translate([0, 0]) .scale(1) .scaleExtent([.5, 8]) .on("zoom", zoomed); var path = d3.geo.path() .pointRadius(3) .projection(null); 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", clicked); 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(0.5).event); }); function clicked(d) { if (active.node() === this) return reset(); active.classed("active", false); active = d3.select(this).classed("active", 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() { active.classed("active", false); active = d3.select(null); svg.transition() .duration(750) .call(zoom.translate([0, 0]).scale(.5).event); } function zoomed() { g.style("stroke-width", 1.0 / d3.event.scale + "px"); g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); path.pointRadius(5 / (d3.event.scale + .45)); g.selectAll("g.feature--station path").attr("d", path); g.selectAll("g.boundary--states").style("stroke-width", .8 / d3.event.scale + "px"); } function stopped() { if (d3.event.defaultPrevented) d3.event.stopPropagation(); }