I’m trying to build a map that can be zoomed using the mouse wheel and panned with drag.
For that I’m using d3.behavior.zoom()
and it works fine for the map.
However, I also need to mark some points in the map (which are included in the same topojson used to draw the map). They get rendered just fine, but I’m having trouble handling the zoom behaviour with these points. If you try to zoom or pan, the points get translated abruptly and even seem to change their path!
Any ideas?
<!DOCTYPE html>
<meta charset="utf-8">
<script src="//d3js.org/d3.v3.js"></script>
<script src="topojson.v0.min.js"></script>
<html>
<style>
.background {
fill: none;
pointer-events: all;
}
.department {
fill: #aaa;
stroke: #fff;
stroke-width: 1.5px;
}
</style>
<body>
<script>
d3.json("santafe.json", function(error, theProvince) {
var width= 960, height= 500;
var svg = d3.select("body").append("svg");
var departments = topojson.object(theProvince, theProvince.objects.departments);
// The projection
var projection = d3.geo.mercator()
.scale(14000)
.center([-60.951,-31.2])
.translate([width / 2, height / 2]);
// The path
var path = d3.geo.path()
.projection(projection);
// Zoom behavior
var zoom = d3.behavior.zoom()
.translate(projection.translate())
.scaleExtent([height, Infinity])
.scale(projection.scale())
.on("zoom", function() {
projection.translate(d3.event.translate).scale(d3.event.scale)
map.selectAll("path.zoomable").attr("d", path);
});
// The map
var map = svg.append("g")
.classed("provinceMap", true)
.call(zoom);
map.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height);
// Departments
map.selectAll(".department")
.data(departments.geometries)
.enter().append("path")
.classed("department", true)
.classed("zoomable", true)
.attr("d", path);
// Places
map.selectAll(".place-label")
.data(topojson.object(theProvince, theProvince.objects.maternidades).geometries)
.enter().append("path")
.classed("place", true)
.classed("zoomable", true)
.attr("d", d3.svg.symbol().type("cross"))
.attr("transform", function(d) { return "translate(" + projection(d.coordinates.reverse()) + ")"; });
});
</script>
</body>
</html>
topojson=function(){function t(t,e){function n(e){var n=t.arcs[e],r=n[0],a=[0,0];return n.forEach(function(t){a[0]+=t[0],a[1]+=t[1]}),[r,a]}var r={},a={},o={};e.forEach(function(t){var e=n(t);(r[e[0]]||(r[e[0]]=[])).push(t),(r[e[1]]||(r[e[1]]=[])).push(~t)}),e.forEach(function(t){var e,r,c=n(t),i=c[0],s=c[1];if(e=o[i])if(delete o[e.end],e.push(t),e.end=s,r=a[s]){delete a[r.start];var u=r===e?e:e.concat(r);a[u.start=e.start]=o[u.end=r.end]=u}else if(r=o[s]){delete a[r.start],delete o[r.end];var u=e.concat(r.map(function(t){return~t}).reverse());a[u.start=e.start]=o[u.end=r.start]=u}else a[e.start]=o[e.end]=e;else if(e=a[s])if(delete a[e.start],e.unshift(t),e.start=i,r=o[i]){delete o[r.end];var f=r===e?e:r.concat(e);a[f.start=r.start]=o[f.end=e.end]=f}else if(r=a[i]){delete a[r.start],delete o[r.end];var f=r.map(function(t){return~t}).reverse().concat(e);a[f.start=r.end]=o[f.end=e.end]=f}else a[e.start]=o[e.end]=e;else if(e=a[i])if(delete a[e.start],e.unshift(~t),e.start=s,r=o[s]){delete o[r.end];var f=r===e?e:r.concat(e);a[f.start=r.start]=o[f.end=e.end]=f}else if(r=a[s]){delete a[r.start],delete o[r.end];var f=r.map(function(t){return~t}).reverse().concat(e);a[f.start=r.end]=o[f.end=e.end]=f}else a[e.start]=o[e.end]=e;else if(e=o[s])if(delete o[e.end],e.push(~t),e.end=i,r=o[i]){delete a[r.start];var u=r===e?e:e.concat(r);a[u.start=e.start]=o[u.end=r.end]=u}else if(r=a[i]){delete a[r.start],delete o[r.end];var u=e.concat(r.map(function(t){return~t}).reverse());a[u.start=e.start]=o[u.end=r.start]=u}else a[e.start]=o[e.end]=e;else e=[t],a[e.start=i]=o[e.end=s]=e});var c=[];for(var i in o)c.push(o[i]);return c}function e(e,r,a){function o(t){0>t&&(t=~t),(l[t]||(l[t]=[])).push(f)}function c(t){t.forEach(o)}function i(t){t.forEach(c)}function s(t){f=t,d[t.type](t.arcs)}var u=[];if(arguments.length>1){var f,l=[],d={LineString:c,MultiLineString:i,Polygon:i,MultiPolygon:function(t){t.forEach(i)}};"GeometryCollection"===r.type?r.geometries.forEach(s):s(r),l.forEach(3>arguments.length?function(t,e){u.push([e])}:function(t,e){a(t[0],t[t.length-1])&&u.push([e])})}else for(var h=0,v=e.arcs.length;v>h;++h)u.push([h]);return n(e,{type:"MultiLineString",arcs:t(e,u)})}function n(t,e){function n(t,e){e.length&&e.pop();for(var n,a=h[0>t?~t:t],o=0,c=a.length,i=0,s=0;c>o;++o)e.push([(i+=(n=a[o])[0])*u+l,(s+=n[1])*f+d]);0>t&&r(e,c)}function a(t){return[t[0]*u+l,t[1]*f+d]}function o(t){for(var e=[],r=0,a=t.length;a>r;++r)n(t[r],e);return e}function c(t){return t.map(o)}function i(t){return t=Object.create(t),t.coordinates=v[t.type](t),t}var s=t.transform,u=s.scale[0],f=s.scale[1],l=s.translate[0],d=s.translate[1],h=t.arcs,v={Point:function(t){return a(t.coordinates)},MultiPoint:function(t){return t.coordinates.map(a)},LineString:function(t){return o(t.arcs)},MultiLineString:function(t){return c(t.arcs)},Polygon:function(t){return c(t.arcs)},MultiPolygon:function(t){return t.arcs.map(c)}};return"GeometryCollection"===e.type?(e=Object.create(e),e.geometries=e.geometries.map(i),e):i(e)}function r(t,e){for(var n,r=t.length,a=r-e;--r>a;)n=t[a],t[a++]=t[r],t[r]=n}function a(t,e){for(var n=0,r=t.length;r>n;){var a=n+r>>>1;e>t[a]?n=a+1:r=a}return n}function o(t,e){function n(t,e){t.forEach(function(t){0>t&&(t=~t);var n=c[t]||(c[t]=[]);n[e]||(n.forEach(function(t){var n,r;r=a(n=i[e],t),n[r]!==t&&n.splice(r,0,t),r=a(n=i[t],e),n[r]!==e&&n.splice(r,0,e)}),n[e]=e)})}function r(t,e){t.forEach(function(t){n(t,e)})}function o(t,e){s[t.type](t.arcs,e)}var c=[],i=e.map(function(){return[]}),s={LineString:n,MultiLineString:r,Polygon:r,MultiPolygon:function(t,e){t.forEach(function(t){r(t,e)})}};return e.forEach(o),i}return{version:"0.0.8",mesh:e,object:n,neighbors:o}}();