d3.js карта диаграммы нарисовать прямоугольник

Я пытаюсь нарисовать карту для эмиратов ОАЭ, используя geojson.

Геойсон действителен и проверен на geojson.io, результат появился вместо карты прямоугольником, написанный код здесь:

var width = 20,
height = 20;
var scal = (1 << 10) / 2 / Math.PI; 

//Define map projection 
var projection = d3.geo.mercator();
      projection
        .scale(scal)///3.5
        .translate([width/2, height/2]);

//Define path generator
var path = d3.geo.path().projection(projection);

d3.json("http://localhost/data/uae-em.json", function(error,json) {

    if(error) alert("error fetching data");
    var svg = d3.select("article")
        .append("svg")
        .attr("width",width)
        .attr("height",height);
    //draw map
    var map = svg.selectAll("path")
        .data(json.features)
        .enter()
        .append("path")
        .attr("d", path)
        .style("fill", "#3498db"); 
    });

Данные JSON находятся здесь: https://files.fm/f/ndsz3v85

А вот пример функции из геойсона:

{"type":"Feature","id":"4","properties":{"name":"UmmAlQwain"},"geometry":{"type":"Polygon","coordinates":[[[55.2350748,25.5700834],[55.4229709,25.3989174],[55.4236655,25.3981583],[55.423986,25.3978339],[55.4254059,25.3960785],[55.4272991,25.3935724],[55.4570596,25.3762406],[55.4577152,25.375909],[55.4915399,25.3593419],[55.4926741,25.3588472],[55.4954455,25.35793],[55.5013237,25.3571279],[55.5031779,25.3570031],[55.5390972,25.3630675],[55.5909015,25.3679027],[55.5918297,25.3680754],[55.5927443,25.3683591],[55.5935906,25.3688154],[55.5943278,25.3693581],[55.5952833,25.3702339],[55.6010034,25.3768825],[55.6044809,25.3808245],[55.6295136,25.4095735],[55.630044,25.4101151],[55.6303207,25.4106671],[55.6302516,25.411542],[55.6299137,25.4123589],[55.6293946,25.413368],[55.6287288,25.4144586],[55.6280518,25.4152536],[55.6211794,25.4201663],[55.6196221,25.4209307],[55.6177489,25.4214301],[55.605742,25.4232035],[55.6024463,25.4237462],[55.5975759,25.4244245],[55.5956543,25.4249531],[55.5919637,25.4262723],[55.554199,25.4399865],[55.5518752,25.4408552],[55.5505241,25.4414505],[55.5490758,25.4421533],[55.5473004,25.4425344],[55.5456819,25.4430884],[55.5370397,25.4462406],[55.5358338,25.4466226],[55.5349558,25.4467468],[55.5337817,25.4467659],[55.523193,25.4459922],[55.5198927,25.4460304],[55.5162327,25.4457248],[55.513895,25.4454286],[55.4882538,25.4420186],[55.4870268,25.4419613],[55.4857574,25.4421332],[55.4843188,25.4426681],[55.4771469,25.4472339],[55.4709611,25.4516781],[55.324428,25.6230138],[55.3205286,25.6184125],[55.3117729,25.6106044],[55.3015804,25.5994214],[55.2892555,25.5829995],[55.2795994,25.5665793],[55.2730684,25.5680481],[55.2385191,25.570346],[55.2350748,25.5700834]]]}}

1 ответ

Решение

Здесь есть несколько вопросов.

Во-первых, причина, по которой вы получаете прямоугольник, заключается в порядке намотки. D3 не включает в себя порядок намотки спецификации Geojson - он следует порядку намотки шейп-файлов. Это довольно странно, потому что D3 - единственная структура, которая приходит на ум, где порядок намотки имеет значение с точки зрения рисования, потому что он использует сферическую геометрию, а не плоскую. В вашем случае вы рисуете все, кроме ОАЭ:

введите описание изображения здесь

Я добавил обводку, мы можем видеть край света (+/-180 градусов восток-запад, а не вершину, потому что меркатор растягивается до бесконечности по оси Y, и даже если он закруглен или обрезан, пределы превышают край SVG), мы также можем увидеть контур ОАЭ (или его часть, по крайней мере). Поэтому нам нужно изменить порядок намотки, это означает, что нужно поменять местами массивы, содержащие координаты, которые определяют каждое кольцо в многоугольнике.

Для этого мы можем использовать turf.js, чтобы подготовить данные заранее или вызывать их каждый раз, когда мы загружаем данные:

json.features.forEach(function(feature) {
  feature.geometry = turf.rewind(feature.geometry, {reverse:true});
})

Теперь, когда у нас есть данные в правильном порядке, мы получаем:

введите описание изображения здесь

Элемент выглядит больше, потому что мы видим все объекты, на первом изображении виден только последний нарисованный объект: он охватывает остальные элементы.

Теперь у нас другая проблема, я отобразил вашу карту в рамке 960x500 пикселей (используя ваш код ( скрипка)). Если бы мы использовали кадр 20x20 и ваш начальный масштаб, мы ничего не видим. Карта находится в центре [0,0]в Атлантике у побережья Африки:

введите описание изображения здесь

Квадрат очерчивает грубую область вашей карты, показанной в версии 20x20 с использованием вашего масштаба и перевода. Я добавил карту мира и увеличил ее размер до 200x200, чтобы придать некоторый визуальный контекст тому, что мы смотрим в 20-пиксельной версии.


Если цель - показать весь мир, нам нужен правильный масштаб. Цилиндрическая проекция, такая как Меркатор (если цилиндр ориентирован на север / юг), охватывает 360 градусов вокруг Земли (вдоль экватора) и растягивает ее на определенное расстояние в единицах карты. В d3 масштаб является этим коэффициентом преобразования, по умолчанию предполагается, что на карте отображается 960 пикселей по всей планете, а вокруг Земли - 2π радиана, поэтому значение масштаба составляет 960/2π (960 пикселей на 2π радиана). Итак, мы можем установить вашу шкалу на width/Math.PI/2:

введите описание изображения здесь

Это приведет к новой проблеме: ОАЭ имеет ширину около 5 градусов, мир вокруг - 360 градусов, что составляет около 1% мировой "ширины". Это немного проблематично при отображении вашей карты в формате SVG размером 20 на 20 пикселей: ширина ОАЭ составляет около 0,2 пикселя, что не очень хорошо рендерится. Единственным вариантом будет увеличение размера карты или центрирование карты в ОАЭ и увеличение масштаба.

Автоматическое центрирование карты было рассмотрено много раз, и я бы не стал отдавать должное предыдущим превосходным объяснениям, если расскажу об этом снова (все, что я могу сказать, это то, что в v4/5 предлагается отличный метод fitSize/fitExtent, но информацию по центрированию v3 можно найти в этом вопрос). Однако для демонстрации я вручную центрирую ОАЭ ниже.

Центр тяжести в ОАЭ составляет около 24 N, 54E ( отсюда).

Если мы предположим, что ОАЭ шире, чем он высокий, и предположим, что его число составляет около 5 градусов в поперечнике, и мы хотим отобразить эти пять градусов над 20 пикселями, то мы знаем, что мир имеет ширину около 1440 пикселей (mapWidthPixels * earthWidthDegrees / mapWidthDegrees = 20 * 360 / 5). Масштаб 1440/2/π, округляя его немного, чтобы получить дополнительный запас: 200.

Итак, используя:

var width = 20;
var height = 20;
var scale = 200;
var center = [54,24];

Мы можем получить это:

введите описание изображения здесь

Все еще небольшой, но с вышеуказанными принципами, легко расширяется.

скрипка

Я добавил границу к SVG с 20px выше, чтобы обеспечить границу для SVG, учитывая небольшой размер и отсутствие различия в том, где находятся края SVG.

Другие вопросы по тегам