D3js scaling, converting and translating

I created a nycMap project that uses angularJS (MVC), yeoman (build), d3 (mapping) and geoJSON (geodata).

Everything works very well, but I had to spend quite a lot of time on the correct scale and translation. I was wondering how I can automatically figure out at what scale the map will show its best and which values ​​of x and y will be included in the translation?

'use strict'; japanAndCo2App.controller('MainCtrl', function($scope) { function makeJapanAll(){ var path, vis, xy; xy = d3.geo.mercator().scale(16000).translate([-5600,2200]); path = d3.geo.path().projection(xy); vis = d3.select("#japanAll").append("svg:svg").attr("width", 1024).attr("height", 700); d3.json("data/JPN_geo4.json", function(json) { return vis.append("svg:g") .attr("class", "tracts") .selectAll("path") .data(json.features).enter() .append("svg:path") .attr("d", path) .attr("fill",function(d,i){ return d.properties.color || "transparent"}); }); } makeJapanAll(); }); 

(If you are interested in the code, everything is on github . The code for the card is in scripts / controllers / main.js , which is the same as shown above.)

+8
source share
3 answers

I had the same problems. But this is very easy to do when you have a bounding box that can be defined using GeoJSON (for example, in a meeting) or when creating GeoJson. And the width of the desired SVG.

I'll start with the variables lattop, lonleft, lonright, width and height for the geojson bounding box and image sizes. I have not yet started calculating a good height from the difference in latutude. Thus, the height is estimated to be large enough to fit the image. The rest should be clear from the code:

 var xym = d3.geo.mercator(); // Coordinates of Flanders var lattop = 51.6; var lonleft = 2.4; var lonright = 7.7; var width = 1500; var height =1000; // make the scale so that the difference of longitude is // exactly the width of the image var scale = 360*width/(lonright-lonleft); xym.scale(scale); // translate the origin of the map to [0,0] as a start, // not to the now meaningless default of [480,250] xym.translate([0,0]); // check where your top left coordinate is projected var trans = xym([lonleft,lattop]); // translate your map in the negative direction of that result xym.translate([-1*trans[0],-1*trans[1]]); var path = d3.geo.path().projection(xym); var svg = d3.select("body").append("svg").attr("width",width).attr("height",height); 

Note that if you go to the date bar (180 degrees), you will have to consider overflow.

+14
source share

Considering this:

 xy = d3.geo.mercator().scale(someScale).translate([0, 0]); 

someScale pixel width of the whole world when projected using the projection of the mercator. So, if your json data was outlined for the whole world - from lat / lng -180.90 to latLng 180, -90 - and if someScale is 1024, then the world will be drawn in such a way that it exactly matches 1024x1024 pixels. This is what you see on this Google Maps (well ... sort of ... not really ... read on ...).

But this is not enough. When the world is drawn at 1024px without translation, lat / lng 0,0 (ie the “Medium” of the world) will sit on the 0,0 pixels of the projected map (ie, Top left). Under these conditions, the entire northern hemisphere and the western hemisphere have negative x or y values ​​and, therefore, go beyond the elongated region. In addition, under these conditions, the lower right edge of the world (i.e. lat / lng -90, 180) will be located in the exact middle of the square 1024x1024 (that is, at 512 512 pixels).

So, to center the world on the square described here, you need to translate display half its width in the X and Y directions. That is, you need

 xy = d3.geo.mercator().scale(1024).translate([512, 512]); 

This will give you exactly the kind of Google Map I am associated with.

If your json data has only part of the world (e.g. nyc or New York State), drawing it with this xy projection will display the outlines in the correct geographic position with respect to the entire 1024x1024 circumferential range. So it looks pretty small with a lot of spaces.

The challenge is how to scale and translate the projection so that the area in question fills the 1024x1024 square. And ... until I answered this question, but I hope that this explanation points you in the right direction to understand this math. I will also try to continue the answer later when I have more time.: /

+7
source share

Here is an example here that gets country borders from geojson, and then scales and translates the map into that country. The code is a little ugly; however, there are attempts to make this easier in the future (see this and this question ).

0
source share

All Articles