d3.js zoom transition with map tiles - d3.js
I'm trying to implement a zoom transition on a map that uses tiles. I want to zoom in on one location, then go back and forth between two other locations.
This is the example I'm working from: http://bl.ocks.org/mbostock/6242308
That does what I want, only I wish to use map tiles instead of a topojson or geojson file.
Right now the map is calling the final location in the jump function, but the transitions between locations aren't working. Any idea on what's happening would be greatly welcomed-- nothing showing in console and I'm a bit stuck. Many thanks.
My code is below, and it's also in this plunker.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
margin: 0;
}
#container {
position: relative;
overflow: hidden;
}
#map{
width:100%;
height:100%;
}
.layer {
position: absolute;
}
.tile {
pointer-events: none;
position: absolute;
width: 256px;
height: 256px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/d3.geo.tile.v0.min.js"></script>
<div id="canvas">
<div id="container">
<g id="map">
<div class="layer"></div>
</div>
</div>
</div>
<script>
var width = Math.max(960, window.innerWidth),
height = Math.max(500, window.innerHeight),
prefix = prefixMatch(["webkit", "ms", "Moz", "O"]);
var tile = d3.geo.tile()
.size([width, height]);
var sf = [-122.417, 37.775],
belowsf = [-122.510962, 37.580284],
ny = [-74.0064, 40.7142],
brooklyn = [-73.975536, 40.691674];
var projection = d3.geo.mercator()
.scale(200000)
.translate([-width / 2, -height / 2]); // just temporary
var zoom = d3.behavior.zoom();
var canvas = d3.select("#canvas")
.style("width", width + "px")
.style("height", height + "px");
var container = d3.select("#container")
.style("width", width + "px")
.style("height", height + "px");
canvas
.call(zoomTo(ny,200300).event)
.transition()
.duration(20000)
.each(jump);
var map = d3.select("#map");
var layer = d3.select(".layer");
function zoomTo(location, newscale) {
return zoom
.scale((newscale) * 2 * Math.PI)
.translate(projection(location).map(function(x) { return -x; }));
}
function jump() {
var t = d3.select(this);
(function repeat() {
t = t.transition()
.call(zoomTo(sf, 200000).event)
.transition()
.call(zoomTo(belowsf, 200000).event)
.each("end", repeat);
})();
}
zoomed();
function zoomed() {
var tiles = tile
.scale(zoom.scale())
.translate(zoom.translate())
();
projection
.scale(zoom.scale() / 2 / Math.PI)
.translate(zoom.translate());
var image = layer
.style(prefix + "transform", matrix3d(tiles.scale, tiles.translate))
.selectAll(".tile")
.data(tiles, function(d) { return d; });
image.exit()
.remove();
image.enter().append("img")
.attr("class", "tile")
.attr("src", function(d) { return "http://" + ["a", "b", "c"][Math.random() * 3 | 0] + ".basemaps.cartocdn.com/light_all/" + d[2] + "/" + d[0] + "/" + d[1] + ".png"; })
.style("left", function(d) { return (d[0] << 8) + "px"; })
.style("top", function(d) { return (d[1] << 8) + "px"; });
}
function matrix3d(scale, translate) {
var k = scale / 256, r = scale % 1 ? Number : Math.round;
return "matrix3d(" + [k, 0, 0, 0, 0, k, 0, 0, 0, 0, k, 0, r(translate[0] * scale), r(translate[1] * scale), 0, 1 ] + ")";
}
function prefixMatch(p) {
var i = -1, n = p.length, s = document.body.style;
while (++i < n) if (p[i] + "Transform" in s) return "-" + p[i].toLowerCase() + "-";
return "";
}
function formatLocation(p, k) {
var format = d3.format("." + Math.floor(Math.log(k) / 2 - 2) + "f");
return (p[1] < 0 ? format(-p[1]) + "°S" : format(p[1]) + "°N") + " "
+ (p[0] < 0 ? format(-p[0]) + "°W" : format(p[0]) + "°E");
}
</script>
</body>
</html>
After a lot of headscratching it turned out that the main thing was that you didn't actually tell the zoom behaviour to call the zoomed function:
var zoom = d3.behavior.zoom().on("zoom", zoomed);
This required moving some variable definitions such that everything is defined when this is called. Apart from that, you don't need to adjust the projection in the zoom handler, as the zoom of the tiles is achieved by setting a transform on the container.
Complete demo here.
Related
Fill not working with areaRadial() generated shape
I'm currently working on a thing which is supposed to look like a lava lamp later. Unfortunately, I'm already failing at the beginning: I managed to create randomly generated blobs, but I just can't apply a fill. The stroke works just fine. here is the relevant code which creates the blobs: var ctx = this; // context this.path = d3.areaRadial() .angle(function(d) {return d.theta}) .radius(function(d) {return d.r}) .curve(d3.curveCatmullRomClosed.alpha(1)); // create the blob this.create = function() { var anchors = []; for (var i = 0; i < ctx.options.anchors; i++) { var currTheta = i * (360 / ctx.options.anchors) + rand(ctx.options.spread.theta); var currRadians = Math.radians(currTheta); var currRadius = ctx.options.radius + rand(ctx.options.spread.r); anchors.push({theta: currRadians, r: currRadius}); } var pathData = ctx.path(anchors); d3.select(ctx.options.target).append('path') .attr('d', pathData) .attr('class', 'blob') .style('opacity', ctx.options.opacity) .style('transform', 'translate(' + ctx.x + 'px, ' + ctx.y + 'px)') .attr('transform', 'translate(' + ctx.x + 'px, ' + ctx.y + 'px)'); console.log(pathData); } function rand(x) // creates a random number between -0.5 * x and 0.5 * x Full code: https://codepen.io/normanwink/pen/BrMVrE Thanks!
That's the expected behaviour, since you're using radius, which is: Equivalent to area.y, except the accessor returns the radius: the distance from the origin ⟨0,0⟩. And for area.y: If y is specified, sets y0 to y and y1 to null and returns this area generator. If y is not specified, returns the current y0 accessor. (emphasis mine) Therefore, you probably want outerRadius here. this.path = d3.areaRadial() .angle(function(d) { return d.theta }) .outerRadius(function(d) { return d.r }) .curve(d3.curveCatmullRomClosed.alpha(1)); This is your code with that change only: (function() { "use strict"; // window size var windowX = window.innerWidth; var windowY = window.innerHeight; var svgX = windowX; var svgY = windowY; var blobCount = 1; function init() { // console.log(new Blob()); var svg = d3.select('#svg') .attr('viewBox', '0 0 ' + svgX + ' ' + svgY) .attr('preserveAspectRatio', 'xMinYMin meet'); for (var i = 0; i < blobCount; i++) { var newBlob = new Blob(svgX * 0.5, svgY * 0.5); } } function Blob(x, y) { var ctx = this; // context this.options = { anchors: 8, breathe: 30, fill: '#ffffff', opacity: 0.5, radius: 150, spread: { theta: 10, // angle r: 300 // radius }, target: '#svg', }; this.x = x || 0; this.y = y || 0; this.path = d3.areaRadial() .angle(function(d) {return d.theta}) .outerRadius(function(d) {return d.r}) .curve(d3.curveCatmullRomClosed.alpha(1)); // create the blob this.create = function() { var anchors = []; for (var i = 0; i < ctx.options.anchors; i++) { var currTheta = i * (360 / ctx.options.anchors) + rand(ctx.options.spread.theta); var currRadians = Math.radians(currTheta); var currRadius = ctx.options.radius + rand(ctx.options.spread.r); anchors.push({theta: currRadians, r: currRadius}); } var pathData = ctx.path(anchors); d3.select(ctx.options.target).append('path') .attr('d', pathData) .attr('class', 'blob') .style('opacity', ctx.options.opacity) .style('transform', 'translate(' + ctx.x + 'px, ' + ctx.y + 'px)') .attr('transform', 'translate(' + ctx.x + 'px, ' + ctx.y + 'px)'); } // update position and anchor movement this.update = function() { } // apply changes this.render = function() { } this.create(); } function rand(i) { return (Math.random()-0.5) * (i || 1); } Math.radians = function(degrees) { return degrees * Math.PI / 180; }; // init when ready init(); })(); #svg { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; } .blob { fill: blue; /* why is this not working? */ stroke: red; } <script src="https://d3js.org/d3.v4.min.js"></script> <svg id="svg"></svg>
Your problem is your pathData , because if you replace your .attr('d', pathData) with .attr('d', 'M37,17v15H14V17z M50,0H0v50h50z') which is a valid path, your fill is working properly. I will continue searching the issue, just want to give you a hint, maybe you will find the issue faster then me. :)
Continent zoom topojson
I'm trying to make the map able to zoom in on continenents on mouseclick. The code is based on Mike Bostock's tutorial on making maps and uses a CSV dataset to bind some values to the countries. https://bost.ocks.org/mike/map/. I've tried to use various examples like this one: https://bl.ocks.org/mbostock/2206590 but nothing seems to work. The map just dissapears when I try to add a .onclick attribute. Does anyone have an idea how I can make the zoom work? HTML <!DOCTYPE html> <meta charset="utf-8"> <style> body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; width: 960px; height: 500px; position: relative; } #canvas { } #canvas-svg { } .land { fill: #222; } .boundary { fill: none; stroke: #fff; stroke-width: 1px; } #tooltip-container { position: absolute; background-color: #fff; color: #000; padding: 10px; border: 1px solid; display: none; } .tooltip_key { font-weight: bold; } .tooltip_value { margin-left: 20px; float: right; } </style> <div id="tooltip-container"></div> <div id="canvas-svg"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.20/topojson.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> <script> d3.csv("import.csv", function(err, data) { var config = {"data0":"Country","data1":"Total","label0":"label 0","label1":"label 1","color0":"#99ccff","color1":"#0050A1","width":800,"height":400} var width = 960, height = 960; var COLOR_COUNTS = 9; function Interpolate(start, end, steps, count) { var s = start, e = end, final = s + (((e - s) / steps) * count); return Math.floor(final); } function Color(_r, _g, _b) { var r, g, b; var setColors = function(_r, _g, _b) { r = _r; g = _g; b = _b; }; setColors(_r, _g, _b); this.getColors = function() { var colors = { r: r, g: g, b: b }; return colors; }; } function clicked(d) { var x, y, k; if (d && centered !== d) { var centroid = path.centroid(d); x = centroid[0]; y = centroid[1]; k = 4; centered = d; } else { x = width / 2; y = height / 2; k = 1; centered = null; } g.selectAll("path") .classed("active", centered && function(d) { return d === centered; }); g.transition() .duration(750) .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")") .style("stroke-width", 1.5 / k + "px"); } function hexToRgb(hex) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; } function valueFormat(d) { if (d > 1000000000) { return "$" + Math.round(d / 1000000000 * 10) / 10 + "M"; } else if (d > 1000000) { return "$" + Math.round(d / 1000000 * 10) / 10 + "M"; } else if (d > 1000) { return "$" + Math.round(d / 1000 * 10) / 10 + "B"; } else { return "$" + d + "M"; } } var COLOR_FIRST = config.color0, COLOR_LAST = config.color1; var rgb = hexToRgb(COLOR_FIRST); var COLOR_START = new Color(rgb.r, rgb.g, rgb.b); rgb = hexToRgb(COLOR_LAST); var COLOR_END = new Color(rgb.r, rgb.g, rgb.b); var startColors = COLOR_START.getColors(), endColors = COLOR_END.getColors(); var colors = []; for (var i = 0; i < COLOR_COUNTS; i++) { var r = Interpolate(startColors.r, endColors.r, COLOR_COUNTS, i); var g = Interpolate(startColors.g, endColors.g, COLOR_COUNTS, i); var b = Interpolate(startColors.b, endColors.b, COLOR_COUNTS, i); colors.push(new Color(r, g, b)); } var MAP_KEY = config.data0; var MAP_VALUE = config.data1; var projection = d3.geo.mercator() .scale((width + 1) / 2 / Math.PI) .translate([width / 2, height / 2]) .precision(.1); var path = d3.geo.path() .projection(projection); var graticule = d3.geo.graticule(); var svg = d3.select("#canvas-svg").append("svg") .attr("width", width) .attr("height", height); svg.append("path") .datum(graticule) .attr("class", "graticule") .attr("d", path); var valueHash = {}; function log10(val) { return Math.log(val); } data.forEach(function(d) { valueHash[d[MAP_KEY]] = +d[MAP_VALUE]; }); var quantize = d3.scale.quantize() .domain([0, 1.0]) .range(d3.range(COLOR_COUNTS).map(function(i) { return i })); quantize.domain([d3.min(data, function(d){ return (+d[MAP_VALUE]) }), d3.max(data, function(d){ return (+d[MAP_VALUE]) })]); d3.json("./world-topo-min.json", function(error, world) { var countries = topojson.feature(world, world.objects.countries).features; svg.append("path") .datum(graticule) .attr("class", "choropleth") .attr("d", path); var g = svg.append("g"); g.append("path") .datum({type: "LineString", coordinates: [[-180, 0], [-90, 0], [0, 0], [90, 0], [180, 0]]}) .attr("class", "equator") .attr("d", path); var country = g.selectAll(".country").data(countries); country.enter().insert("path") .attr("class", "country") .attr("d", path) .attr("id", function(d,i) { return d.id; }) .attr("title", function(d) { return d.properties.name; }) .style("fill", function(d) { if (valueHash[d.properties.name]) { var c = quantize((valueHash[d.properties.name])); var color = colors[c].getColors(); return "rgb(" + color.r + "," + color.g + "," + color.b + ")"; } else { return "#ccc"; } }) .on("mousemove", function(d) { var html = ""; html += "<div class=\"tooltip_kv\">"; html += "<span class=\"tooltip_key\">"; html += d.properties.name; html += "</span>"; html += "<span class=\"tooltip_value\">"; html += (valueHash[d.properties.name] ? valueFormat(valueHash[d.properties.name]) : ""); html += ""; html += "</span>"; html += "</div>"; $("#tooltip-container").html(html); $(this).attr("fill-opacity", "0.8"); $("#tooltip-container").show(); var coordinates = d3.mouse(this); var map_width = $('.choropleth')[0].getBoundingClientRect().width; if (d3.event.pageX < map_width / 2) { d3.select("#tooltip-container") .style("top", (d3.event.layerY + 15) + "px") .style("left", (d3.event.layerX + 15) + "px"); } else { var tooltip_width = $("#tooltip-container").width(); d3.select("#tooltip-container") .style("top", (d3.event.layerY + 15) + "px") .style("left", (d3.event.layerX - tooltip_width - 30) + "px"); } }) .on("mouseout", function() { $(this).attr("fill-opacity", "1.0"); $("#tooltip-container").hide(); }); g.append("path") .datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; })) .attr("class", "boundary") .attr("d", path); }); 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 = .9 / Math.max(dx / width, dy / height), translate = [width / 2 - scale * x, height / 2 - scale * y]; g.transition() .duration(750) .style("stroke-width", 1.5 / scale + "px") .attr("transform", "translate(" + translate + ")scale(" + scale + ")"); } function reset() { active.classed("active", false); active = d3.select(null); g.transition() .duration(750) .style("stroke-width", "1.5px") .attr("transform", ""); } d3.select(self.frameElement).style("height", height + "px"); }); </script> CSV ,Country,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,Total ,Afghanistan,,,34,,,35,3,41,150,344,377,660,521,235,288,74,2763 ,African Union**,,,,,,53,9,,10,,,,,2,,2,76 ,Albania,,,,2,,7,,5,,,,,15,7,7,7,50 ,Algeria,412,546,249,195,242,159,340,509,1529,1075,829,1115,889,373,487,636,9585 ,Angola,147,156,148,48,8,39,7,31,29,20,,,,31,2,61,726 ,Argentina,213,6,16,12,168,3,9,24,23,16,14,46,36,44,14,23,668 ,Armenia,,,,,118,,,1,,46,52,5,,16,,,238 ,Australia,338,1236,663,801,472,459,727,675,445,775,1507,1567,876,255,932,1574,13301 ,Austria,25,15,69,42,55,23,4,269,190,286,7,8,8,6,2,,1008 ,Azerbaijan,3,,89,,4,53,159,211,29,142,146,557,333,398,602,285,3012 ,Bahamas,33,,,,,3,,,,1,,,,,29,22,88 ,Bahrain,299,30,54,1,10,66,63,26,20,,103,1,26,78,10,,786 ,Bangladesh,203,266,41,8,32,10,221,88,13,,35,193,252,727,257,653,2997 ,Barbados,,,,,,,,10,10,10,,,,,,,29 ,Belarus,,,,,,6,140,,,3,,75,75,75,60,164,598 ,Belgium,35,29,56,21,15,3,27,174,203,90,32,21,32,46,103,49,934 ,Belize,1,,,,,,,,,,,,,,,,1 ,Benin,,,7,,,,,3,,1,0,0,20,2,,,33 ,Bhutan,,,,,0,,,,,,,,,,,1,1 ,Bolivia,19,,,5,1,1,9,2,3,5,1,25,12,1,46,7,136 ,Botswana,53,15,1,9,8,,,,,13,10,,,8,,,119 ,Brazil,122,626,213,100,81,224,179,189,179,189,318,312,311,223,284,289,3840 ,Brunei,,4,1,0,0,1,2,,,33,33,229,22,15,122,36,498 ,Bulgaria,,,,2,12,132,22,48,116,119,8,29,5,,,3,495 ,Burkina Faso,,,,,,12,1,4,,2,0,15,4,,5,6,48 ,Burundi,,1,3,,,,,,,1,2,,5,,,1,14 ,Cambodia,,,,,,,14,56,,4,34,,77,67,,,252 ,Cameroon,,7,6,,,5,2,,1,,10,,,39,107,2,177 ,Canada,570,538,428,152,352,124,104,456,408,106,236,351,211,194,306,395,4931 ,Cape Verde,1,,,,,,,,,2,,,10,,,,12 ,Central African Republic,,,,,,,9,,0,,,7,,,,,16 ,Chad,,16,,,,,17,20,75,35,38,1,4,59,57,,322 ,Chile,202,60,74,187,70,449,1095,662,397,335,475,316,62,51,125,114,4673 ,China,2552,2833,2891,2373,3316,3558,2900,1692,1892,1407,1045,1128,1703,1452,1184,1214,33140 ,Colombia,60,278,171,147,18,16,56,234,110,248,255,145,207,162,190,215,2510 ,Comoros,,,,,,,,,6,,,,1,,,,7 ,Democratic Republic of Congo,0,,,,,4,1,0,,1,27,,23,12,13,,81 ,Costa Rica,,,,,,,,,,,,,1,,1,,2 ,Cote d'Ivoire,0,,32,61,9,,,,,,,,,,6,6,113 ,Croatia,,57,2,24,8,,,15,99,4,10,17,17,,63,11,326 ,Cyprus,4,115,1,6,16,20,26,17,,,44,41,6,,,,295 ,Czech Republic,14,65,47,97,7,593,45,10,23,6,13,71,22,15,,0,1029 ,Denmark,50,153,51,54,228,120,83,146,95,129,17,,109,42,177,0,1455 ,Djibouti,1,,3,,4,7,,,,,,,6,5,12,7,44 ,Dominican Republic,13,5,0,8,35,2,,,,10,51,12,,,,1,137 ,DR Congo,88,15,12,,,19,17,,18,41,151,,10,1,,,374 ,Ecuador,,10,1,,15,48,17,2,102,77,90,68,88,11,53,1,583 ,Egypt,837,847,719,630,654,778,678,651,333,159,686,630,281,675,368,1475,10401 ,El Salvador,,,19,10,,,,,4,,,,2,,20,,55 ,Equatorial Guinea,,7,,,7,12,,28,30,68,3,82,65,2,92,,394 ,Eritrea,20,57,18,17,80,98,,4,,,,,,,,,293 ,Estonia,27,,1,13,8,23,7,19,29,43,13,1,2,18,2,22,228 ,Ethiopia,140,,20,174,239,,,,,,54,76,193,153,30,,1079 ,Finland,474,9,12,227,77,107,136,108,135,42,50,58,76,280,156,228,2172 ,France,107,66,43,57,93,2,67,74,5,76,103,33,91,107,17,34,976 ,Gabon,,,,,6,,20,18,,,22,5,1,2,,3,77 ,Gambia,,,,,5,,,,,16,,,,,,,21 ,Georgia,6,85,,1,37,92,97,155,91,36,,2,,5,1,,608 ,Germany,113,133,71,64,239,204,416,82,292,339,282,84,157,113,114,102,2804 ,Ghana,1,12,1,6,35,0,0,16,,11,2,76,39,63,14,13,290 ,Greece,708,787,400,2274,1368,405,731,1712,516,1229,652,80,37,52,199,762,11910 ,Guatemala,,,,,,,,,0,,,,2,,,33,35 ,Guinea,18,6,,2,,1,,0,,,4,1,4,,5,,40 ,Guyana,,7,3,,,,,,0,,,,,,,,10 ,Palestine,,,,,,,,,,,1,2,,,0,,3 ,Lebanon,0,0,3,0,1,0,9,,,20,20,,,,,,54 ,Honduras,,,,,,,,,0,,,0,0,20,13,6,39 ,Hungary,14,14,,,72,13,253,196,5,4,18,9,,2,7,,604 ,Iceland,,,,,,,,,,,,50,,,,,50 ,India,995,1321,1911,2878,2180,1161,1480,2299,1867,1945,3017,3706,4545,5291,3487,3078,41160 ,Indonesia,151,27,68,356,79,36,61,577,239,436,225,250,218,802,1136,683,5341 ,Iran,418,524,435,282,121,57,423,331,62,62,103,103,47,31,13,13,3025 ,Iraq,,,,,71,186,305,263,377,398,453,603,474,353,650,1215,5348 ,Ireland,0,45,22,,19,5,13,19,22,0,5,2,1,,40,41,232 ,Israel,368,131,349,189,852,1133,1122,862,676,153,67,85,120,153,510,617,7387 ,Italy,241,221,243,559,446,162,433,525,220,109,115,298,219,91,145,596,4623 ,Jamaica,,,,,,10,10,13,2,,1,,,1,,2,38 ,Japan,491,441,477,480,400,464,511,498,680,525,426,292,270,306,480,310,7050 ,Jordan,133,160,121,281,157,49,48,176,161,234,87,214,175,81,252,198,2526 ,Kazakhstan,121,139,20,,47,42,44,82,25,39,58,52,84,141,93,419,1406 ,Kenya,15,,,,,,,10,,,112,14,60,1,17,30,258 ,Kosovo,,,,,,,,,,,,,,,1,,1 ,Kuwait,245,67,23,45,2,12,5,279,5,9,85,113,31,73,692,366,2052 ,Kyrgyzstan,,,,9,5,3,2,,,,,,,,,,19 ,Laos,7,36,,0,,4,,,7,26,,14,19,19,,7,138 ,Latvia,3,13,3,25,12,4,9,49,44,11,14,27,9,5,5,1,234 ,Lebanon,4,,,,,1,,5,1,52,64,2,31,24,5,50,239 ,Lesotho,,6,,,1,,1,,,,,,,,,,8 ,Liberia,8,,,,,,,,,,,,,,,,8 ,Libya,1,14,15,16,26,24,8,73,120,10,44,349 ,Lithuania,6,16,12,1,47,9,52,4,27,27,8,1,1,73,3,21,308 ,Uganda,,,0,,,,,,,,,,,,,,0 ,Sri Lanka,1,,1,,,0,,,,,,,,,,,2 ,Luxembourg,,,,1,,,,,,7,7,,1,,,,16 ,Macedonia,14,112,,,,,0,,,,,,,1,,,128 ,Madagascar,,,,,,,,,,0,,,,0,,,0 ,Malawi,,,,,,,,,3,,,,1,2,2,,7 ,Malaysia,26,26,154,147,65,57,398,526,508,1512,421,5,47,71,73,190,4227 ,Maldives,,,,,,,15,,,,5,,,5,,,24 ,Mali,7,,1,,,13,,8,3,10,,,9,6,,,57 ,Malta,0,0,,,,18,,,,,,8,8,,12,,45 ,Mauritania,31,,26,7,,,,,,,9,5,11,10,5,,103 ,Mauritius,,,,,6,,,,,,,,,,45,,51 ,Mexico,227,152,97,31,320,33,75,,22,59,112,229,261,69,97,500,2284 ,Mongolia,,,,,,,,,14,,27,3,51,,,13,107 ,Morocco,125,11,156,12,14,101,47,27,46,39,306,1398,826,82,572,42,3803 ,Mozambique,0,,,1,,,,,,,,0,0,9,12,,22 ,Myanmar,16,149,157,83,194,160,175,138,93,52,63,664,478,237,300,320,3277 ,Namibia,,21,11,,16,,72,6,66,15,,1,57,9,2,,276 ,NATO**,,18,,,,,116,,,420,,,22,,,,576 ,Nepal,,11,9,9,34,6,,,1,,,3,,,5,14,91 ,Netherlands,125,178,244,125,127,102,320,240,157,288,193,149,376,332,10,86,3052 ,New Zealand,,45,17,107,49,10,5,81,2,48,58,23,25,26,79,56,631 ,Nicaragua,,,,,,,,14,,14,,,,,,,28 ,Niger,,,,14,,,,,8,1,0,,,13,3,9,48 ,Nigeria,36,7,6,62,6,,15,52,19,65,186,65,4,35,224,188,971 ,Macedonia,,0,,,,,,,,,,,,,,,0 ,Afghanistan,19,207,,,,,,,,,,,,,,,226 ,North Korea,18,28,9,8,8,5,15,5,5,4,1,,,,,,103 ,Norway,229,99,90,14,6,14,542,567,624,588,159,614,149,62,23,143,3921 ,Libya,,,,,,,,,,,,0,,,,,0 ,Oman,118,33,40,36,41,173,301,16,75,93,30,21,120,490,736,148,2469 ,Pakistan,175,408,541,628,455,421,338,670,1047,1214,2176,1063,1028,1144,752,735,12797 ,Palestine,,,,,,,,2,,14,6,,,,,,21 ,Panama,,,1,,,,,,,7,,,15,30,,,52 ,Papua New Guinea,,,,,,,,,,,,,,,3,,3 ,Paraguay,,6,,,4,1,,,,,3,8,7,0,,,29 ,Peru,24,5,16,22,47,368,193,172,,43,46,63,10,49,153,169,1380 ,Philippines,,10,5,10,34,12,23,16,10,1,3,63,16,75,12,158,449 ,PIJ (Israel/Palestine)*,,,,,,,,,,,,,0,,,,0 ,Poland,148,36,255,349,225,112,463,990,594,169,158,92,207,155,252,131,4336 ,Portugal,2,19,,29,70,157,220,60,144,369,892,168,40,13,4,11,2196 ,PRC (Israel/Palestine)*,,,,,,,,,,,,,,,0,,0 ,Qatar,14,11,11,11,,,,27,,286,30,198,319,73,55,655,1690 ,Romania,21,19,16,17,285,437,61,98,78,56,105,80,21,20,0,22,1335 ,Russian Federation,,,,,,,4,100,,8,22,11,98,153,206,88,690 ,Rwanda,14,,,,,,2,15,8,11,,,5,11,14,7,86 ,Saudi Arabia,85,61,567,167,1170,167,205,214,363,796,1070,1237,1080,1672,2782,3161,14796 ,Senegal,,,,,,15,9,18,6,4,4,20,,6,7,11,99 ,Serbia,1,27,,,,,,,,,16,,0,,,,44 ,Seychelles,,,,,,15,,,,,,8,,7,16,,45 ,Sierra Leone,,,,,,,9,,,,,2,0,1,,,12 ,Singapore,798,254,234,83,376,538,69,355,1113,1481,1020,935,828,780,683,98,9645 ,Slovakia,0,,27,,,4,,0,,9,9,,2,6,,,57 ,Slovenia,2,41,2,17,17,3,3,2,,6,40,28,,,,,162 ,Somalia,,,,,,,,,,,,,,3,,0,3 ,South Africa,6,18,,,,262,708,881,486,128,180,212,132,2,50,,3065 ,South Korea,1396,773,528,752,1059,804,1650,1755,1683,796,1250,1553,1066,182,715,245,16207 ,South Sudan,,,,,,,,37,44,1,,61,3,5,18,22,190 ,Spain,334,168,289,253,315,363,315,337,346,235,290,181,238,176,127,153,4119 ,Sri Lanka,297,161,45,35,49,58,97,89,71,,5,21,,,5,,934 ,Sudan,,106,49,204,293,132,65,33,128,89,182,173,119,196,61,27,1858 ,Suriname,2,,,,,,,,,,,,,7,,3,11 ,Swaziland,1,1,,,,,,,,,,,,,,,2 ,Sweden,204,165,75,64,47,78,122,41,44,54,51,191,206,52,43,43,1481 ,Switzerland,23,68,63,117,203,168,83,108,18,39,47,31,9,4,0,2,983 ,Syrian Arab Republic,64,28,45,69,25,35,100,20,276,193,298,368,371,361,15,,2267 ,Syria rebels*,,,,,,,,,,,,,1,1,0,,2 ,Taiwan (ROC),585,345,298,117,319,691,503,12,11,60,97,198,425,553,1084,681,5978 ,Tajikistan,,,,,,,13,7,,32,,,,3,,,55 ,Tanzania,3,,,51,,10,12,1,,25,,78,115,92,26,20,433 ,Thailand,85,117,152,131,114,70,47,8,13,64,49,270,289,392,93,185,2078 ,Timor-Leste,,,,,,,,,,,18,,,,,,18 ,Togo,,,,,,,,,,,1,,4,,16,,21 ,Tonga,,,,,,,,,,,,,,,,5,5 ,Trinidad and Tobago,11,0,2,,,,,6,,,24,12,12,,,46,113 ,Tunisia,,,83,1,,168,2,,3,,7,7,,38,43,6,357 ,Turkey,1190,510,905,330,257,1113,531,692,710,780,484,770,1503,650,1556,448,12427 ,Turkmenistan,,,,18,21,,,,7,36,14,236,115,69,102,122,739 ,UAE,247,186,222,685,1210,2170,2021,919,752,561,605,1210,1088,2235,731,1289,16128 ,Uganda,6,,34,,36,10,5,,5,23,37,471,219,20,,3,868 ,UIC (Somalia)*,,,,,,,0,,,,,,,,,,0 ,Ukraine,,,,,,,,,,,,,,,1,10,11 ,Ukraine Rebels*,,,,,,,,,,,,,,,24,,24 ,United Kingdom,871,1277,719,761,212,27,308,764,508,383,511,368,586,492,214,382,8381 ,United Nations**,31,,,,2,1,2,1,0,,,,,5,23,4,69 ,United States,330,487,499,592,560,520,641,819,951,968,1111,995,1180,802,566,565,11587 ,United Wa State (Myanmar)*,,1,,,,,,,,,,,,,,,1 ,Unknown country,,2,,0,,,,,12,,8,6,,30,31,51,139 ,Unknown rebel group*,,0,0,,,,,,,,,,,,,,0 ,Uruguay,4,,11,,,20,8,3,78,29,20,,,6,9,8,196 ,Uzbekistan,,8,9,,,,,,,,,,,,,62,79 ,Venezuela,108,103,50,15,9,21,380,774,737,358,208,594,680,1165,173,162,5535 ,Viet Nam,7,85,66,28,304,297,41,8,204,78,184,1039,766,362,1078,870,5414 ,Yemen,158,85,593,62,254,317,38,66,40,5,258,45,38,23,6,12,2000 ,Zambia,33,,2,,,0,30,5,2,,,1,66,,,24,161 ,Zimbabwe,3,10,,16,,25,25,,,,,,,,,,78
Can't get d3 map centered correctly
I have a map that is zooming back and forth between a couple of locations. However, my map coordinates are off. It appears the map is centered at [0,0] by default. I changed projection.center to a new point, but this caused some chaos-- (the map started zooming to other continents...) Here is a plunker with the map, with the 0,0 center (mouse over and you'll see the coordinates at the bottom). I also tried setting projection.center within the zoomTo function, but that caused another problem. I feel like this should be a quick edit, but I've spent a lot of time pulling my hair out on this. Any ideas on what needs to change? Many thanks. Code also below: <!DOCTYPE html> <meta charset="utf-8"> <style> body { margin: 0; } #container { position: relative; overflow: hidden; } #map{ width:100%; height:100%; } .layer { position: absolute; } .tile { pointer-events: none; position: absolute; width: 256px; height: 256px; } .info { position: absolute; bottom: 0px; left: 0px; padding: 20px; background: #000; color: #fff; width: 100%; z-index: 1000; } </style> <body> <script src="http://d3js.org/d3.v3.min.js"></script> <script src="http://d3js.org/d3.geo.tile.v0.min.js"></script> <div id="canvas"> <div id="container"> <div id="map"> <div class="layer"></div> </div> </div> </div> <script> var width = Math.max(960, window.innerWidth), height = Math.max(500, window.innerHeight), prefix = prefixMatch(["webkit", "ms", "Moz", "O"]); var tile = d3.geo.tile() .size([width, height]); var sf = [-122.417, 37.775], belowsf = [-122.510962, 37.580284]; var projection = d3.geo.mercator() .scale((1 << 18) / 2 / Math.PI) .translate([-width / 2, -height / 2]); // just temporary var map = d3.select("#map"); var layer = d3.select(".layer"); var zoom = d3.behavior.zoom().on("zoom", zoomed); var canvas = d3.select("#canvas") .style("width", width + "px") .style("height", height + "px"); var container = d3.select("#container") .style("width", width + "px") .style("height", height + "px") .on("mousemove", mousemoved); canvas .call(zoomTo(sf).event) .transition() .duration(10000) .each(jump); var info = map.append("div") .attr("class", "info"); function zoomTo(place) { return zoom .scale(projection.scale() * 2 * Math.PI) .translate(projection(place).map(function(x) { return -x; })); } function jump() { var t = d3.select(this); (function repeat() { t = t.transition() .call(zoomTo(belowsf).event) .transition() .call(zoomTo(sf).event) .each("end", repeat); })(); } function mousemoved() { info.text(formatLocation(projection.invert(d3.mouse(this)), zoom.scale())); } function zoomed() { var tiles = tile .scale(zoom.scale()) .translate(zoom.translate()) (); var image = layer .style(prefix + "transform", matrix3d(tiles.scale, tiles.translate)) .selectAll(".tile") .data(tiles, function(d) { return d; }); image.exit() .remove(); image.enter().append("img") .attr("class", "tile") .attr("src", function(d) { return "http://" + ["a", "b", "c"][Math.random() * 3 | 0] + ".basemaps.cartocdn.com/light_all/" + d[2] + "/" + d[0] + "/" + d[1] + ".png"; }) .style("left", function(d) { return (d[0] << 8) + "px"; }) .style("top", function(d) { return (d[1] << 8) + "px"; }); } function matrix3d(scale, translate) { var k = scale / 256, r = scale % 1 ? Number : Math.round; return "matrix3d(" + [k, 0, 0, 0, 0, k, 0, 0, 0, 0, k, 0, r(translate[0] * scale), r(translate[1] * scale), 0, 1 ] + ")"; } function prefixMatch(p) { var i = -1, n = p.length, s = document.body.style; while (++i < n) if (p[i] + "Transform" in s) return "-" + p[i].toLowerCase() + "-"; return ""; } function formatLocation(p, k) { var format = d3.format("." + Math.floor(Math.log(k) / 2 - 2) + "f"); return (p[1] < 0 ? format(-p[1]) + "°S" : format(p[1]) + "°N") + " " + (p[0] < 0 ? format(-p[0]) + "°W" : format(p[0]) + "°E"); } </script> </body> </html> Screenshot, showing incorrect coordinates:
Ok I think I understand. For your first question about centering the map, you can translate the zoom using the projection of the San Francisco coordinates: var projection = d3.geo.mercator() .scale((1 << 18) / 2 / Math.PI) .translate([width / 2, height / 2]); var center = projection(sf); var zoom = d3.behavior.zoom() .scale(projection.scale() * 2 * Math.PI) .translate([width - center[0], height - center[1]]); Once you have set the center, you then need to update your projection to align with the zoom in order to get the correct coordinates showing on the screen: projection .scale(zoom.scale() / 2 / Math.PI) .translate(zoom.translate()); This is what I get when the map loads which seems to me the coordinates you are looking for: I also modified the ZoomTo function to account for the above changes when transitioning: function zoomTo(place) { // project back to main projection projection = d3.geo.mercator() .scale((1 << 18) / 2 / Math.PI) .translate([width / 2, height / 2]) // set new center center = projection(place); // set zoom with new center return zoom .scale(projection.scale() * 2 * Math.PI) .translate([width - center[0], height - center[1]]); } Full code: http://plnkr.co/edit/eE0XZQPHB6rAc9cg4LMH?p=preview
d3.js Pie Chart With label
I started working with this d3.js Donut Chart: JSFiddleI am trying to change it into a Pie Chart without the circle in the middle. I am new to d3.js. I have tried several different ideas but have been unable to get this to remove the circle in the middle of the chart. Any and all help is appreciated Here is my code: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <style> .label-text { alignment-baseline : middle; font-size: 12px; font-family: arial,helvetica,"sans-serif"; fill: #393939; } .label-line { stroke-width: 1; stroke: #393939; } .label-circle { fill: #393939; } </style> <script src="http://d3js.org/d3.v3.min.js"></script> </head> <body> <svg> <g id="canvas"> <g id="art" /> <g id="labels" /></g> </svg> <script> var data = [{ label: 'Star Wars', instances: 207 }, { label: 'Lost In Space', instances: 3 }, { label: 'the Boston Pops', instances: 20 }, { label: 'Indiana Jones', instances: 150 }, { label: 'Harry Potter', instances: 75 }, { label: 'Jaws', instances: 5 }, { label: 'Lincoln', instances: 1 }]; svg = d3.select("svg"); canvas = d3.select("#canvas"); art = d3.select("#art"); labels = d3.select("#labels"); // Create the pie layout function. // This function will add convenience // data to our existing data, like // the start angle and end angle // for each data element. jhw_pie = d3.layout.pie(); jhw_pie.sort(null); jhw_pie.value(function (d) { // Tells the layout function what // property of our data object to // use as the value. return d.instances; }); // Store our chart dimensions cDim = { height: 500, width: 500, innerRadius: 50, outerRadius: 150, labelRadius: 175 } // Set the size of our SVG element svg.attr({ height: cDim.height, width: cDim.width }); // This translate property moves the origin of the group's coordinate // space to the center of the SVG element, saving us translating every // coordinate individually. canvas.attr("transform", "translate(" + (cDim.width / 2) + "," + (cDim.height / 2) + ")"); pied_data = jhw_pie(data); // The pied_arc function we make here will calculate the path // information for each wedge based on the data set. This is // used in the "d" attribute. pied_arc = d3.svg.arc() .innerRadius(50) .outerRadius(150); // This is an ordinal scale that returns 10 predefined colors. // It is part of d3 core. pied_colors = d3.scale.ordinal() .range(["#04B486", "#F2F2F2", "#F5F6CE", "#00BFFF","orange","purple","pink"]); // Let's start drawing the arcs. enteringArcs = art.selectAll(".wedge").data(pied_data) .enter(); enteringArcs .append("g") .attr("class", "wedge") .append("path") .attr("d", pied_arc) .style("fill", function (d, i) { return pied_colors(i); }); // Now we'll draw our label lines, etc. enteringLabels = labels.selectAll(".label").data(pied_data).enter(); labelGroups = enteringLabels.append("g").attr("class", "label"); labelGroups.append("circle").attr({ x: 0, y: 0, r: 2, fill: "#000", transform: function (d, i) { centroid = pied_arc.centroid(d); return "translate(" + pied_arc.centroid(d) + ")"; }, 'class': "label-circle" }); // "When am I ever going to use this?" I said in // 10th grade trig. textLines = labelGroups.append("line").attr({ x1: function (d, i) { return pied_arc.centroid(d)[0]; }, y1: function (d, i) { return pied_arc.centroid(d)[1]; }, x2: function (d, i) { centroid = pied_arc.centroid(d); midAngle = Math.atan2(centroid[1], centroid[0]); x = Math.cos(midAngle) * cDim.labelRadius; return x; }, y2: function (d, i) { centroid = pied_arc.centroid(d); midAngle = Math.atan2(centroid[1], centroid[0]); y = Math.sin(midAngle) * cDim.labelRadius; return y; }, 'class': "label-line" }); textLabels = labelGroups.append("text").attr({ x: function (d, i) { centroid = pied_arc.centroid(d); midAngle = Math.atan2(centroid[1], centroid[0]); x = Math.cos(midAngle) * cDim.labelRadius; sign = (x > 0) ? 1 : -1 labelX = x + (5 * sign) return labelX; }, y: function (d, i) { centroid = pied_arc.centroid(d); midAngle = Math.atan2(centroid[1], centroid[0]); y = Math.sin(midAngle) * cDim.labelRadius; return y; }, 'text-anchor': function (d, i) { centroid = pied_arc.centroid(d); midAngle = Math.atan2(centroid[1], centroid[0]); x = Math.cos(midAngle) * cDim.labelRadius; return (x > 0) ? "start" : "end"; }, 'class': 'label-text' }).text(function (d) { return d.data.label }); alpha = 0.5; spacing = 12; function relax() { again = false; textLabels.each(function (d, i) { a = this; da = d3.select(a); y1 = da.attr("y"); textLabels.each(function (d, j) { b = this; // a & b are the same element and don't collide. if (a == b) return; db = d3.select(b); // a & b are on opposite sides of the chart and // don't collide if (da.attr("text-anchor") != db.attr("text-anchor")) return; // Now let's calculate the distance between // these elements. y2 = db.attr("y"); deltaY = y1 - y2; // Our spacing is greater than our specified spacing, // so they don't collide. if (Math.abs(deltaY) > spacing) return; // If the labels collide, we'll push each // of the two labels up and down a little bit. again = true; sign = deltaY > 0 ? 1 : -1; adjust = sign * alpha; da.attr("y", +y1 + adjust); db.attr("y", +y2 - adjust); }); }); // Adjust our line leaders here // so that they follow the labels. if (again) { labelElements = textLabels[0]; textLines.attr("y2", function (d, i) { labelForLine = d3.select(labelElements[i]); return labelForLine.attr("y"); }); setTimeout(relax, 20) } } relax(); </script> </body> </html> Thanks
See this updated fiddle. The code contained the following lines, of which the innerRadious was changed to 0. pied_arc = d3.svg.arc() .innerRadius(00) // <- this .outerRadius(150); It's a bit misleading, as there's an innerRadius variable somewhere before that, but it's not used at this point. While you're at it, you might want to align all of that stuff.
D3 Rolling map updates d3.geo.circle, but not arcs
I built a visualization on top of http://bl.ocks.org/patricksurry/6621971. Basically I added d3.geo.circle and d3.svg.arc on the map. What I observed is when I pan/zoom the map, the circle remains intact, but the arc disappears. When I inspected the elements in chrome, I saw that the attribute 'd' of arc path vanished, but for circle path, it got updated appropriately. Can anyone help me understand why the updated projection got applied to circle path element but not in arc. Is there a way to force re-projection of arcs without have to remove and re-create them? UPDATE 1: Since this question seemed difficult to recreate, and jsfiddle won't not allow uploading a geo-data file , I am posting the source here: <!DOCTYPE html> <meta charset="utf-8"> <style> svg { background-color: lavender; border: 1px solid black; } path { fill: oldlace; stroke: #666; stroke-width: .5px; } path.circle { fill: red; stroke: #666; stroke-width: .5px; } path.arc1 { fill: green; } </style> <body> <script src="http://d3js.org/d3.v3.min.js"></script> <script src="http://d3js.org/topojson.v1.min.js"></script> <script> var width = 1600, height = 400, rotate = 60, // so that [-60, 0] becomes initial center of projection maxlat = 83; // clip northern and southern poles (infinite in mercator) var projection = d3.geo.mercator() .rotate([rotate,0]) .scale(1) // we'll scale up to match viewport shortly. .translate([width/2, height/2]); // find the top left and bottom right of current projection function mercatorBounds(projection, maxlat) { var yaw = projection.rotate()[0], xymax = projection([-yaw+180-1e-6,-maxlat]), xymin = projection([-yaw-180+1e-6, maxlat]); return [xymin,xymax]; } // set up the scale extent and initial scale for the projection var b = mercatorBounds(projection, maxlat), s = width/(b[1][0]-b[0][0]), scaleExtent = [s, 10*s]; projection.scale(scaleExtent[0]); var zoom = d3.behavior.zoom() .scaleExtent(scaleExtent) .scale(projection.scale()) .translate([0,0]) // not linked directly to projection .on("zoom", redraw); var path = d3.geo.path() .projection(projection); var svg = d3.selectAll('body') .append('svg') .attr('width',width) .attr('height',height) .attr('id', 'svg') .call(zoom); d3.json("js/data/world-110m2.json", function ready(error, world) { // adding geo paths svg.selectAll('path') .data(topojson.feature(world, world.objects.countries).features) .enter().append('path') // adding a circle svg.append("path") .datum(d3.geo.circle().angle(2).origin([-10, 0])) .attr("d", path) .attr("class", "circle"); redraw(); // adding a pie arc var r = 10; var p = Math.PI * 2; var arc1 = d3.svg.arc() .innerRadius(r - 5) .outerRadius(r) .startAngle(0); var arcData = JSON.parse('[{ "lon" : "0", "lat":"0", "endAngle":"6.4" }]'); var arcs1 = svg.selectAll("path.arc1"); arcs1 = arcs1.data(arcData) .enter() .append("path") .attr("class", "arc1") .attr("fill", "green") .attr("transform", function(d, i) { return "translate(" + projection([d.lon, d.lat])[0] + ", " + projection([d.lon, d.lat])[1] + ")"; }) .attr("d", arc1); }); // track last translation and scale event we processed var tlast = [0,0], slast = null; function redraw() { if (d3.event) { var scale = d3.event.scale, t = d3.event.translate; console.log(d3.event.scale + " [" +d3.event.translate + "]"); // if scaling changes, ignore translation (otherwise touch zooms are weird) if (scale != slast) { projection.scale(scale); } else { var dx = t[0]-tlast[0], dy = t[1]-tlast[1], yaw = projection.rotate()[0], tp = projection.translate(); // use x translation to rotate based on current scale projection.rotate([yaw+360.*dx/width*scaleExtent[0]/scale, 0, 0]); // use y translation to translate projection, clamped by min/max var b = mercatorBounds(projection, maxlat); if (b[0][1] + dy > 0) dy = -b[0][1]; else if (b[1][1] + dy < height) dy = height-b[1][1]; projection.translate([tp[0],tp[1]+dy]); } // save last values. resetting zoom.translate() and scale() would // seem equivalent but doesn't seem to work reliably? slast = scale; tlast = t; } svg.selectAll('path').attr('d', path); } </script>
I finally figured out what was going wrong. I was supposed to apply transformation on arc elements. So basically, in the redraw() method I did: var scaleRatio = 1; function redraw() { if (d3.event) { var scale = d3.event.scale, t = d3.event.translate; //console.log(d3.event.scale + " [" +d3.event.translate + "]"); // if scaling changes, ignore translation (otherwise touch zooms are weird) if (scale != slast) { projection.scale(scale); } else { var dx = t[0]-tlast[0], dy = t[1]-tlast[1], yaw = projection.rotate()[0], tp = projection.translate(); // use x translation to rotate based on current scale projection.rotate([yaw+360.*dx/width*scaleExtent[0]/scale, 0, 0]); // use y translation to translate projection, clamped by min/max var b = mercatorBounds(projection, maxlat); if (b[0][1] + dy > 0) dy = -b[0][1]; else if (b[1][1] + dy < height) dy = height-b[1][1]; projection.translate([tp[0],tp[1]+dy]); } // save last values. resetting zoom.translate() and scale() would // seem equivalent but doesn't seem to work reliably? if(slast==null) scaleRatio=1; else scaleRatio = scaleRatio * (scale/slast); console.log(slast+'-' + scaleRatio); slast = scale; tlast = t; } svg.selectAll('path').attr('d', path); svg.selectAll("path.arc1") .attr("transform", function(d, i) { return "translate(" + projection([d.lon, d.lat])[0] + ", " + projection([d.lon, d.lat])[1] + ")scale(" + scaleRatio + ")" }) .attr("d", arc1); } But, I still have a knowledge gap, as to why svg.path elements require explicit re-projection, unlike d3.geo.path elements . Hope someone helps me on this.