D3 для карт --- на каком этапе вводить данные в гео?


12

Я хотел бы отобразить мир хороплет для отображения с D3, а-ля:

У меня есть набор данных, который я хотел бы отобразить, который связан с ключами ISO-alpha-3. Так...

danger.csv
iso,level
AFG,100
ALB,0
DZA,12

и т.п.

Следуя инструкциям на топойсон, я знаю, что могу сделать ...

wget "http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/50m/cultural/ne_50m_admin_0_countries.zip"
unzip ne_50m_admin_0_countries.zip
ogr2ogr -f "GeoJSON" output_features.json ne_50m_admin_0_countries.shp -select iso_a3
topojson -o topo.json output_features.json --id-property iso_a3

создать карту мира JSON, которая идентифицируется по ISO3.

Мой вопрос: в какой момент в рабочем процессе я должен объединить данные из danger.csv в геоданные? Ранее я работал с qGIS в качестве графического интерфейса, но где / должно произойти слияние? В .шп? После огр2огр? Динамически в браузере после сжатия topojson (как здесь http://bl.ocks.org/mbostock/4060606 http://bl.ocks.org/mbostock/3306362 )?

Я довольно хорошо разбираюсь в python, но довольно плохо знаком с javascript и обнаруживаю, что копирую и вставляю примеры Bostock больше, чем просто являюсь генеративным программистом.

(У меня также есть связанное, но более сложное продолжение Stackoverflow, которое, возможно, мне следует перенести сюда: /programming/18604877/how-to-do-time-data-in-d3-maps )


Я просто смотрел на примеры @ mbostock и увидел, что есть один, который специально обращается к GeoJoins , или «Простой скрипт для соединения файла GeoJSON с внешними свойствами в файле CSV или TSV; извлеченный из TopoJSON» .
RyanKDalton

Ответы:


11

Задайте себе два вопроса:

  1. Собираетесь ли вы повторно использовать географию для нескольких наборов данных?

    Если вы будете использовать одну и ту же географию с несколькими наборами данных, то имеет смысл разделить географию и данные и объединить их в клиенте. По этой причине во многих моих примерах есть отдельные файлы CSV (или TSV). Таким образом, TopoJSON для штатов и округов США или аналогичных стран мира можно использовать повторно вместо создания отдельного TopoJSON для каждого примера.

    С другой стороны, если вы будете использовать эту географию только один раз , то вам, вероятно, следует «запекать» данные в географию как свойства, хотя бы для упрощения кода. Этот подход проще, потому что вам нужно загрузить только один файл ( т.е. нет queue.js ), и, поскольку данные хранятся как свойства каждой функции, вам не нужно объединять данные в клиенте (поэтому нет d3. карта ).

    Примечание: TSV и CSV часто гораздо эффективнее хранят свойства, чем GeoJSON и TopoJSON, просто потому, что последние должны повторять имена свойств для каждого объекта. Размер файла может быть еще одной причиной, чтобы хранить ваши данные в отдельном файле и присоединить его к клиенту.

  2. Ваши данные уже привязаны к географии (например, свойство вашего шейп-файла)?

    Если вы ответили «нет» на первый вопрос и хотите внести данные в географию (а не делать это в клиенте), то, как вы это сделаете, зависит от формата данных.

    Если ваши данные уже являются свойством шейп- topojson -pфайла , используйте для управления тем, какие свойства сохраняются в сгенерированном файле TopoJSON. Вы также можете использовать это, чтобы переименовать свойства и привести их к числам, а также. См. Давайте создадим карту для примеров.

    Если ваши данные находятся в отдельном файле CSV или TSV, используйте topojson -e (в дополнение к -p), чтобы указать файл внешних свойств, который можно присоединить к вашим географическим объектам. Копирование примера из вики, если у вас был такой файл TSV:

    FIPS    rate
    1001    .097
    1003    .091
    1005    .134
    1007    .121
    1009    .099
    1011    .164
    1013    .167
    1015    .108
    1017    .186
    1019    .118
    1021    .099

    Используя -e, вы можете сопоставить их с числовым выходным свойством под названием «безработица»:

    topojson \
      -o output.json \
      -e unemployment.tsv \
      --id-property=+FIPS \
      -p unemployment=+rate \
      -- input.shp

    Примером такого подхода является хороплет населения Кентукки, bl.ocks.org/5144735 .


2
И здесь я задавал свои сложные вопросы отображения D3 по стеку вместо потока gis.stackexchange, потому что я думал, что там было больше опыта - и тогда сам мастер отвечает на мой вопрос здесь. =) Ну, это делает 2 вещи, которые я узнал сегодня. Благодарность!
Mittenchops

3

Хороший вопрос. Один из приведенных вами примеров, кажется, делает свое дело, хотя за ним трудно следовать.

Вы заметите, что в примере есть два внешних файла данных, us.json и безработица.tsv . Вы можете думать о безработице как о своей опасности.csv; us.json - это географические объекты, с которыми вы хотите связать параметры из danger.csv. Последний, безработица.tsv, имеет idи rateполя, где idто же самое, что idв us.json.

Именно в клиенте с D3 вы должны объединить свои данные и функции , по крайней мере, в этом примере. Именно в клиенте уровень безработицы в этом примере соединяется с функциями округа с помощью функции d3.map () . Вот где это инициализируется:

var rateById = d3.map();

И вот где rateотображается на id:

queue()
    .defer(d3.json, "/mbostock/raw/4090846/us.json")
    .defer(d3.tsv, "unemployment.tsv", function(d) { rateById.set(d.id, +d.rate); })
    .await(ready);

Должен признаться, я не знаю, для чего queue(), но это не важно для этой дискуссии. Что важно отметить , что поле в каждой функции уездного заменяется безработицы . теперь доступен по общим идентификатором ( EDIT: Как @ blord-Castillo указывает, что это на самом деле поколение нового ассоциативного массива или хэш - ключ, где сопоставляется с ). Вот где вызывается для целей символики (здесь предопределенные классы CSS доступны для каждого квантиля):idraterateidrateidrate

...
.enter().append("path")
  .attr("class", function(d) { return quantize(rateById.get(d.id)); })
  .attr("d", path);

Где quantize()функция возвращает имя класса CSS, который должен использоваться для стилизации этой функции (графства) на основе уровня безработицы, который теперь определяется в поле функции id.


1
К сведению: в очереди: bsumm.net/2013/03/31/analyzing-mbostocks-queue-js.html
johanvdw

очередь позволяет асинхронную параллельную загрузку источников данных вместо последовательной загрузки.
blord-castillo

1
В этом примере происходит то, что rateById является ключевым хешем. Изменения в особенностях страны не производятся, и данные us.json не затрагиваются. Вместо этого безработица.tsv преобразуется в ключевой хеш, называемый rateById. Параметр rateById.set () зацикливается на сайте Employment.tsv, так что ключ вставляется для каждого идентификатора в безработице.tsv (не в us.json), а значение этого ключа устанавливается равным полю ставки для этого идентификатора в безработице. , Позже, rateById.get () вызывается, чтобы использовать хеш для поиска уровня безработицы по id; это значение используется для установки стиля для функций us.json, а затем отбрасывается.
blord-castillo

Почему это / заменить / идентификатор со скоростью вместо того, чтобы прикрепить его в качестве атрибута где-то еще? Казалось бы, потом будет сложнее сделать выбор.
Mittenchops

1
Это не заменяет идентификатор с тарифом. Это создает хеш поиска от идентификатора до оценки.
blord-castillo

2

Прежде всего, первая строка вашего csv должна быть списком имен столбцов через запятую, чтобы использовать этот метод. Если это невозможно, добавьте комментарий об этом, и я посмотрю, смогу ли я решить, как использовать d3.csv.parseRowsвместо d3.csv.parse. d3.csv.parseвызывается функцией оценщика на .defer(function, url, assessor).

Я предполагаю, что ваш файл теперь выглядит так:

danger.csv
iso,level
AFG,100
ALB,0
DZA,12
...

Используя это, вы можете создать хеш поиска от ISO3 до уровня опасности.

var dangerByISO3 = d3.map();
queue()
    .defer(d3.json, "url to topo.json")
    .defer(d3.csv, "url to danger.csv", function(d) {dangerByISO3.set(d.iso, +d.level);})
    .await(ready);
function ready(error, world) {
    //You now have world as your available topojson
    //And you have dangerByISO3 as your danger level hash
    //You can lookup a danger level by dangerByISO3.get(ISO3 code)
}

Код прохождение

var dangerByISO3 = d3.map();

Сначала вы создаете объект d3.map (), который будет функционировать в качестве ключевого хэша, и сохраняете его в переменной dangerByISO3.

queue()

Используйте очередь для параллельной загрузки.

.defer(d3.json, "url to topo.json")

Загрузите ваш топойсон в качестве первого аргумента, который будет передан функции await (после ошибки). Обратите внимание на стиль, в котором эта функция queue()включена, но указана в отдельной строке (нет конечной точки с запятой queue()).

.defer(d3.csv, "url to danger.csv", function(d) {dangerByISO3.set(d.iso, +d.level);})

Здесь происходят две вещи. Во-первых, вы загружаете danger.csv в качестве второго аргумента, который передается в функцию await. Как вы увидите ниже, этот аргумент фактически не используется. Вместо этого в функцию загрузки передается аргумент оценки d3.csv. Этот оценщик будет обрабатывать каждый ряд CSV. В этом случае мы вызываем функцию set для dangerByISO3, чтобы для каждой комбинации isoклавиш мы указывали levelзначение, соответствующее этой клавише. В +d.levelнотации используется унарный, +чтобы привести значение d.level к числу.

.await(ready);

После загрузки обоих источников данных они передаются в качестве двух отдельных аргументов функции ready(). Первым аргументом обратного вызова всегда является первая произошедшая ошибка. Если ошибки не произошло, то в качестве первого аргумента будет передано значение NULL. Второй аргумент - это первый источник данных (результат первой задачи), а третий аргумент - второй источник данных (результат второй задачи).

function ready(error, world) {...}

Это функция обратного вызова ready(). Сначала мы возьмем errorаргумент, который должен быть нулевым, если две задачи загрузки завершены успешно (вы должны добавить язык для отлова и обработки ошибок). Далее мы берем данные топойсона как объект countries. Эти данные должны быть обработаны в теле функции с чем-то вроде .data(topojson.feature(world,world.objects.countries).features). Поскольку ready()третий аргумент не принимает, результат второй задачи, нашего csv, просто отбрасывается. Мы использовали его только для создания ключевого хэша, и после этого он нам не понадобился.


Да, вы правы, мой CSV на самом деле выглядит как правильно сформированный CSV вместо небрежного демо, которое я опубликовал. =) Извините, я обновлю это.
Mittenchops
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.