Drawing geo paths to HTML5 canvas is really straightforward--d3.geo.path has a context() function that creates the necessary drawing code. But doing it in conjunction with transform zoom seems to require drawing by hand and accounting for current zoom.scale and zoom.translate individually for each point, which slows down canvas drawing.
The method of transferring the canvas drawing on-the-fly to an svg:image shows significant problems in Safari and is noticeably slower in Chrome than just using the canvas element.
xxxxxxxxxx
<html xmlns="https://www.w3.org/1999/xhtml">
<head>
<title>Canvas and Transform Zoom</title>
<meta charset="utf-8" />
</head>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="topojson.js" type="text/javascript">
</script>
<script src="tile.js" type="text/javascript">
</script>
<body>
<footer>
<script>
topoData =[];
d3.json("new_routes.topojson", function(error, data) {
console.log(topoData)
topoData = topojson.feature(data, data.objects.base_routes).features;
drawMap();
})
function drawMap() {
var tile = d3.geo.tile().size([500,500]);
projection = d3.geo.mercator()
.scale((1 << 13) / 2 / Math.PI)
.scale(4096);
var path = d3.geo.path()
.projection(projection);
var c = projection([12, 42]);
zoom = d3.behavior.zoom()
.scale(projection.scale() * 2 * Math.PI)
.translate([500 - c[0], 500 - c[1]])
.on("zoom", zoomed)
;
projection
.scale(1 / 2 / Math.PI)
.translate([0, 0]);
selectedDiv = d3.select("body").append("div").style("height", "500px").style("width", "500px");
canvasCanvas = selectedDiv.append("canvas").style("height", "500px").style("width", "500px").style("pointer-events", "none")
.attr("height", 500).attr("width", 500).style("position", "fixed").style("z-index", 99);
mapSVG = selectedDiv.append("svg").style("height", "500px").style("width", "500px")
.call(zoom);
canvasImage = mapSVG.append("g").append("image").attr("height", 500).attr("width", 500).style("height", "500px").style("width", "500px");
tileG = mapSVG.insert("g", "g");
// MAP ZOOMING
function zoomed() {
renderTiles();
renderCanvasFeatures();
}
function renderTiles() {
//Tile drawing needs to only draw the topmost baselayer, or designate base layers through the layer control dialogue
var tiles = tile
.scale(zoom.scale())
.translate(zoom.translate())
();
var image = tileG
.attr("transform", "scale(" + tiles.scale + ")translate(" + tiles.translate + ")")
.selectAll("image")
.data(tiles, function(d) { return d; });
image.exit()
.remove();
image.enter().append("image")
.attr("xlink:href", function(d) { return "https://" + ["a", "b", "c", "d"][Math.random() * 4 | 0] + ".tiles.mapbox.com/v3/examples.map-h67hf2ic/" + d[2] + "/" + d[0] + "/" + d[1] + ".png"; })
.attr("width", 1)
.attr("height", 1)
.attr("x", function(d) { return d[0]; })
.attr("y", function(d) { return d[1]; });
}
zoomed();
}
function renderCanvasFeatures() {
var context = canvasCanvas.node().getContext("2d");
context.clearRect(0,0,500,500);
context.strokeStyle = "rgba(0, 0, 0, 0.5)";
context.lineWidth = 2;
for (x in topoData) {
var g = topoData[x].geometry.coordinates;
for (y in g) {
var projectedPoint = projection([g[y][0],g[y][1]])
var projX = projectedPoint[0] * zoom.scale() + zoom.translate()[0];
var projY = projectedPoint[1] * zoom.scale() + zoom.translate()[1];
if (y == 0) {
context.beginPath();
context.moveTo(projX,projY)
}
else {
context.lineTo(projX,projY)
}
}
context.stroke();
}
var imgUrl = canvasCanvas.node().toDataURL("image/png");
canvasImage.attr("xlink:href", imgUrl);
context.clearRect(0,0,500,500);
}
</script>
</footer>
</body>
</html>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js