масштаб d3js, преобразование и перевод
Я создал nycMap, проект, который использует angularJS (MVC), yeoman (build), d3 (mapping) и geoJSON (geo data).
все работает очень хорошо, но мне пришлось потратить довольно много времени, чтобы получить правильный масштаб и перевод. Мне было интересно, как я могу автоматически выяснить, в каком масштабе карта покажет свое лучшее и какие значения x и y входят в перевод?
'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();
});
(Если вас интересует код, это все on на GitHub. Код для карты находится в скрипты / контроллеры / main.js, который так же, как показано выше.)
3 ответов
у меня были те же проблемы. Но это очень легко сделать, когда у вас есть ограничивающая коробка, которая может быть определена из GeoJSON (как сказал meetamit) или при создании GeoJson. И ширина разыскиваемого SVG.
Я начну с переменных lattop, lonleft, lonright, width
и height
для ограничивающей рамки geojson и размеров изображения. Я еще не занимали себя с расчетом хорошей высоте из-за разницы в latutude. Поэтому высота всего оценивается большой достаточно, чтобы соответствовать образу. Остальное должно быть ясно из кода:
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);
обратите внимание, если вы перейдете через строку даты (180 градусов), вам придется учитывать переполнение.
при этом:
xy = d3.geo.mercator().scale(someScale).translate([0, 0]);
someScale
и пиксель шириной на весь мир, когда прогнозируемая использование проекции Меркатора. Итак, если ваши данные json имели контуры для всего мира-от lat / lng -180,90 до latLng 180,-90 – и если someScale
было 1024, тогда мир был бы нарисован таким образом, что он точно вписывается в 1024x1024-пиксельный квадрат. Вот что вы видите в это Google Maps view (ну... что-то вроде того... не совсем... читайте дальше...).
этого недостаточно. Когда мир нарисован на 1024px, без какого-либо перевода, lat/lng 0,0 (т. е. "середина" мира) будет сидеть на 0,0 пикселе проецируемой карты (т. е. вверху слева). В этих условиях все северное полушарие и Западное полушарие имеют отрицательные значения x или y и поэтому выходят за пределы нарисованной области. Кроме того, в этих условиях нижняя правая часть мира (т. е. lat/lng -90, 180) будет сидеть точно посередине квадрат 1024x1024 (т. е. в пикселе 512,512).
Итак, чтобы центрировать мир на описанной здесь площади, вам нужно translate
карта на половину ее ширины в направлениях X и Y. Т. е. вам нужно
xy = d3.geo.mercator().scale(1024).translate([512, 512]);
Это даст вам точно вид карты Google, с которым я связан.
если ваши данные json имеют только часть мира (например, Нью-Йорк или штат Нью-Йорк), рисование его с помощью этой проекции xy отобразит контуры в правильном географическом положении относительно весь 1024x1024 охватывающий весь мир регион. Таким образом, он будет казаться довольно маленьким, с большим количеством пробелов.
проблема заключается в том, как масштабировать и переводить проекцию так, чтобы рассматриваемая область заполняла квадрат 1024x1024. И... до сих пор я не ответил на этот вопрос, но я надеюсь, что это объяснение указывает вам в правильном направлении к выяснению этой математики. Я также попытаюсь продолжить ответ позже, когда у меня будет больше времени. :/
вот пример здесь это получает границы стран от geojson, а затем масштабирует и переводит карту в эту страну. Код немного уродлив; однако есть усилия, чтобы сделать это проще в будущем (см. этой и этот вопрос).