#javascript #d3.js #charts #maps #geojson
Вопрос:
Я пытаюсь визуализировать регионы России. Я получил данные отсюда, проверил здесь, и все было хорошо.
Но когда я пытаюсь его нарисовать, я получаю только один большой черный прямоугольник.
var width = 700, height = 400;
var svg = d3.select(".graph").append("svg")
.attr("viewBox", "0 0 " (width) " " (height))
.style("max-width", "700px")
.style("margin", "10px auto");
d3.json("83.json", function (error, mapData) {
var features = mapData.features;
var path = d3.geoPath().projection(d3.geoMercator());
svg.append("g")
.attr("class", "region")
.selectAll("path")
.data(features)
.enter()
.append("path")
.attr("d", path)
});
Пример — http://ustnv.ru/d3/index.html
Файл Geojson — http://ustnv.ru/d3/83.json
Ответ №1:
Проблема заключается в порядке намотки координат (см. Этот блок). Большинство инструментов/утилит/библиотек/валидаторов на самом деле не заботятся о порядке намотки, потому что они рассматривают GeoJSON как содержащий декартовы координаты. Не так с D3 — D3 использует эллипсоидальную математику — преимущества этого включают возможность легко пересекать антимеридиан и выбирать перевернутый многоугольник.
Следствием использования эллипсоидальных координат является неправильный порядок намотки, который создаст особенность всего на планете, которая не является вашей целью (перевернутый многоугольник). Ваши полигоны на самом деле содержат комбинацию обоих порядков намотки. Вы можете убедиться в этом, изучив пути svg:
Здесь один путь кажется точно нарисованным, в то время как другой путь поверх него охватывает всю планету — за исключением той части, которую он должен занимать (пространство, которое он должен занимать, покрыто другими путями, которые охватывают весь мир).
Это может быть легко исправить — вам просто нужно изменить порядок координат, — но поскольку у вас есть функции, которые содержат обе обмотки в одной коллекции, вам будет проще использовать библиотеку, такую как turf.js для создания нового набора правильно настроенных функций:
var fixed = features.map(function(feature) {
return turf.rewind(feature,{reverse:true});
})
Обратите внимание на порядок обратной намотки — из-за странной причуды D3, которая, вероятно, является наиболее распространенной платформой, где порядок намотки имеет значение, на самом деле не соответствует спецификации GeoJSON (RFC 7946) по порядку намотки, она использует противоположный порядок намотки, см. Этот комментарий Майка Бостока:
Я разочарован тем, что RFC 7946 стандартизирует порядок намотки, противоположный порядку намотки D3, шейп-файлов и PostGIS. И я не вижу простого способа для D3 изменить свое поведение, так как это нарушило бы все существующие (сферические) GeoJSON, используемый D3. (источник)
Перематывая каждый многоугольник, мы получаем немного более полезную карту:
Это улучшение, но функции немного малы при таких настройках проекции.
Добавив метод fitSize для масштабирования и перевода, мы получим гораздо более красивую карту (см. Блок здесь).:
Комментарии:
1. Результат операции без намотки, который почти как муравей на экране, напомнил мне карту на шаге 2 этого урока Бостока , где он говорит: «Мелкие картографы теперь объявили бы, что работа выполнена хорошо, и пошли бы домой за пивом» … о, я раскололся!
2. Я помню эту строчку, ценю его чувство юмора — также помню, как я проходил этот урок в качестве своего первого набега на d3. Но, как только у вас есть данные и вы можете отобразить их в той или иной форме, самое время выпить пива, часто это половина дела.
3. В моем случае, когда я это делаю
d3.select("body").append("svg")
, уже самое время сделать перерыв и выпить пива…4. После 10 часов выдергивания волос я нашел этот драгоценный камень, спасибо!
5. вы можете перемотать свой geojson отсюда: davidb.dev/инструменты/перемотка-geojson-онлайн
Ответ №2:
Вот быстрое решение вашей проблемы, проекция нуждается в небольшой настройке, также путь fill:#000
по умолчанию и stroke: #FFF
может сделать его более разборчивым.
var width = 700, height = 400;
var svg = d3.select(".graph").append("svg")
.attr("viewBox", "0 0 " (width) " " (height))
.style("max-width", "700px")
.style("margin", "10px auto");
d3.json("mercator_files/83.json", function (error, mapData) {
var features = mapData.features;
var center = d3.geoCentroid(mapData);
//arbitrary
var scale = 7000;
var offset = [width/2, height/2];
var projection = d3.geoMercator().scale(scale).center(center)
.translate(offset);
var path = d3.geoPath().projection(projection);
svg.append("g")
.attr("class", "region")
.selectAll("path")
.data(features)
.enter()
.append("path")
.attr("d", path)
});
Комментарии:
1. Хотя стиль пути и проекция требуют настройки, я не вижу, как это решает основную проблему: объекты инвертированы и, таким образом, охватывают всю плоскость карты. Используя ваш код , я нахожу , что не все функции видны (все нарисованы), и пути все еще выходят за пределы svg (вот почему
fitSize()
/fitExtent()
не будет работать с этими функциями, это также может привести к неправильному поведению сd3.geoCentroid()
)