0

I am attempting to simplify a d3 map on zoom, and I am using this example as a starting point. However, when I replace the json file in the example with my own (http://weather-bell.com/res/nws_regions.topojson), I get a tiny upside-down little map.

Here is my jsfiddle: http://jsfiddle.net/8ejmH code:

var width = 900,
    height = 500;

var chesapeake = [-75.959, 38.250];

var scale,
translate,
visibleArea, // minimum area threshold for points inside viewport
invisibleArea; // minimum area threshold for points outside viewport

var simplify = d3.geo.transform({
    point: function (x, y, z) {
        if (z < visibleArea) return;
        x = x * scale + translate[0];
        y = y * scale + translate[1];
        if (x >= 0 && x <= width && y >= 0 && y <= height || z >= invisibleArea) this.stream.point(x, y);
    }
});

var zoom = d3.behavior.zoom()
    .size([width, height])
    .on("zoom", zoomed);

// This projection is baked into the TopoJSON file,
// but is used here to compute the desired zoom translate.
var projection = d3.geo.mercator().translate([0, 0])



var canvas = d3.select("#map").append("canvas")
    .attr("width", width)
    .attr("height", height);

var context = canvas.node().getContext("2d");

var path = d3.geo.path()
    .projection(simplify)
    .context(context);

d3.json("http://weather-bell.com/res/nws_regions.topojson", function (error, json) {
    canvas.datum(topojson.mesh(topojson.presimplify(json)))
        .call(zoomTo(chesapeake, 0.05).event)
        .transition()
        .duration(5000)
        .each(jump);
});

function zoomTo(location, scale) {
    var point = projection(location);
    return zoom.translate([width / 2 - point[0] * scale, height / 2 - point[1] * scale])
        .scale(scale);
}

function zoomed(d) {
    translate = zoom.translate();
    scale = zoom.scale();
    visibleArea = 1 / scale / scale;
    invisibleArea = 200 * visibleArea;
    context.clearRect(0, 0, width, height);
    context.beginPath();
    path(d);
    context.stroke();
}

function jump() {
    var t = d3.select(this);
    (function repeat() {
        t = t.transition()
            .call(zoomTo(chesapeake, 100).event)
            .transition()
            .call(zoomTo(chesapeake, 0.05).event)
            .each("end", repeat);
    })();
}

My guess is that the topojson file I am using already has the projection built in, so I should be using a null projection in d3. The map renders properly if I do not use a projection at all: (http://jsfiddle.net/KQfrK/1/) - but then I cannot simplify on zoom.

I feel like I am missing something basic... perhaps I just need to somehow rotate and zoom into the map in my first fiddle.

Either way, I'd appreciate some help. Been struggling with this one.

Edit: I used QGIS to save the geojson file with a "EPSG:3857 - WGS 84 / Pseudo Mercator" projection. However, when I convert this to topojson with the topojson command-line utility and then display it with D3 using the same code as above I get a blank screen.

Should I specify the projection within the topojson command-line utility? I tried to do that but I got an error message:

topojson --projection EPSG:3857 E:\gitstore\public\res\nws.geojson -o E:\gitstore\public\res\nws.topojson --id-property NAME


[SyntaxError: Unexpected token :]
user1614080
  • 2,854
  • 1
  • 19
  • 22
Mike Furlender
  • 3,869
  • 5
  • 47
  • 75

2 Answers2

0

The TopoJSON file doesn't have a projection built-in, you're simply using the default projection when you don't specify one (which is albersUsa, see the documentation). You can retrieve this projection by calling d3.geo.projection() without an argument. Then you can modify this projection in the usual way for zoom etc.

Lars Kotthoff
  • 107,425
  • 16
  • 204
  • 204
  • If I replace d3.geo.mercator() with d3.geo.projection() I get an error message: "Uncaught TypeError: Cannot read property 'invert' of undefined." If I replace it with d3.geo.albersUsa() then I get another upside down little map that flies in a different direction. – Mike Furlender Sep 27 '13 at 14:43
  • You need to adjust your hardcoded coordinates etc to be valid for that projection as well. – Lars Kotthoff Sep 27 '13 at 14:53
  • please see my upadate - I am trying to translate the map to a mercator projection and hardcode it into the topojson file. – Mike Furlender Sep 27 '13 at 16:19
  • Ok, that's a different thing. You need to give one of the [D3 projections](https://github.com/d3/d3-geo-projection) to topojson though. – Lars Kotthoff Sep 27 '13 at 17:04
  • 1
    @ Mike, I've bumped your second fiddle [to here](http://jsfiddle.net/j27BT/) including altered projection and path lines which might clarify few things. To me it looks as though your json is in Mercator. – user1614080 Sep 28 '13 at 00:16
0

I set up this fiddle using the Mercator projection and I took a different approach to zooming in and out based on this block, which to me was a simpler approach. I have a feeling that there was an issue in the zoomTo function in the translate bit, but I could exactly what it was. So I replaced with the code below and included a recursive call:

function clicked(k) {

    if (typeof k === 'undefined') k = 8;

    g.transition()
        .duration(5000)
        .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -projection(chesapeake)[0] + "," + -projection(chesapeake)[1] + ")")
        .each("end", function () {
        (k === 8) ? k = 1 : k = 8;
        clicked(k);
    });
user1614080
  • 2,854
  • 1
  • 19
  • 22