Обзор нового интерфейса командной строки d3-geo.

[Это часть 4 учебника по созданию тематических карт из командной строки с использованием d3-geo, TopoJSON и ndjson-cli. Прочтите Часть 3 здесь.]

Слово хороплет происходит от греческого khôros, означающего область или регион, и plêthos, означающего большое количество. Чтобы построить его, мы применяем цветовую кодировку данных о населении к геометрии, которую мы подготовили ранее. (Будьте осторожны, вы случайно не вызовете апокрифический хлороплет, который представляет собой огромное количество зелени.)

Как и в Части 2, разумной отправной точкой является последовательная шкала с перцептуально-мотивированной цветовой схемой, такой как Viridis. Однако теперь мы применим ndjson-map к коллекции функций как единый объект, а не будем оценивать выражение независимо для каждой функции; это позволяет выполнять глобальные операции, такие как вычисление максимальной плотности любого участка.

Используйте topo2geo для извлечения упрощенных участков из топологии, конвейер для ndjson-map, чтобы назначить свойство fill для каждого тракта, конвейер для ndjson-split, чтобы разбить коллекцию на функции, и, наконец, конвейер для geo2svg :

topo2geo tracts=- \
  < ca-topo.json \
  | ndjson-map -r d3 'z = d3.scaleSequential(d3.interpolateViridis).domain([0, 4000]), d.features.forEach(f => f.properties.fill = z(f.properties.density)), d' \
  | ndjson-split 'd.features' \
  | geo2svg -n --stroke none -p 1 -w 960 -h 960 \
  > ca-tracts-color.svg

(При желании вы можете запускать их как отдельные команды и сохранять промежуточные результаты в отдельные файлы. Мне надоело придумывать уникальные имена файлов, поэтому я буду использовать каналы.)

Как и прежде, это неоптимальное визуальное кодирование из-за характера переписных участков. 😩 По данным Бюро переписи населения:

На переписных участках обычно проживает от 1 200 до 8 000 человек, при оптимальной численности 4 000 человек. Переписной участок обычно охватывает прилегающую территорию; однако пространственный размер переписных участков широко варьируется в зависимости от плотности заселения.

Сильная отрицательная корреляция между площадью земли и плотностью населения легко прослеживается на диаграмме рассеяния участков переписи населения Калифорнии:

В результате большая часть Калифорнии темно-фиолетовая, густонаселенные городские районы - ярко-желтого цвета, а промежуточных участков очень мало. Вы можете подумать о расширении области цветовой шкалы выше 4000 человек на квадратную милю, но это не поможет; проблема в том, что переход от низкой плотности к высокой происходит слишком быстро.

Нам нужно нелинейное преобразование, чтобы распределять цвета более равномерно, делая области с низкой плотностью светлее, а с высокой - темнее. Мы можем реализовать экспоненциальное преобразование, передав вместо этого квадратный корень из плотности населения в цветовую шкалу и соответствующим образом скорректировав область:

topo2geo tracts=- \
  < ca-topo.json \
  | ndjson-map -r d3 'z = d3.scaleSequential(d3.interpolateViridis).domain([0, 100]), d.features.forEach(f => f.properties.fill = z(Math.sqrt(f.properties.density))), d' \
  | ndjson-split 'd.features' \
  | geo2svg -n --stroke none -p 1 -w 960 -h 960 \
  > ca-tracts-sqrt.svg

Лучше! 👏 Для сравнения, вот преобразование журнала:

topo2geo tracts=- \
  < ca-topo.json \
  | ndjson-map -r d3 'z = d3.scaleLog().domain(d3.extent(d.features.filter(f => f.properties.density), f => f.properties.density)).interpolate(() => d3.interpolateViridis), d.features.forEach(f => f.properties.fill = z(f.properties.density)), d' \
  | ndjson-split 'd.features' \
  | geo2svg -n --stroke none -p 1 -w 960 -h 960 \
  > ca-tracts-log.svg

Что, если мы визуализируем p - квантиль плотности вместо ее абсолютного значения?

topo2geo tracts=- \
  < ca-topo.json \
  | ndjson-map -r d3 'z = d3.scaleQuantile().domain(d.features.map(f => f.properties.density)).range(d3.quantize(d3.interpolateViridis, 256)), d.features.forEach(f => f.properties.fill = z(f.properties.density)), d' \
  | ndjson-split 'd.features' \
  | geo2svg -n --stroke none -p 1 -w 960 -h 960 \
  > ca-tracts-quantile.svg

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

Еще один подход заключается в применении дискретной цветовой схемы вместо непрерывной, где смежные диапазоны значений имеют общие цвета. Это облегчает читателям сопоставление цвета области на карте с определенным значением с помощью ключа. Кроме того, это означает, что мы можем вручную (или алгоритмически) выбрать подходящие пороги (разрывы) в цветовой шкале.

ColorBrewer Синтии А. Брюер предоставляет фантастический набор хорошо продуманных дискретных цветовых схем. Пакет d3-scale-chromatic предоставляет удобный API для ColorBrewer на JavaScript. Установить:

npm install -g d3-scale-chromatic

Затем, чтобы реализовать шкалу пороговых значений с помощью цветовой схемы OrRd:

topo2geo tracts=- \
  < ca-topo.json \
  | ndjson-map -r d3 -r d3=d3-scale-chromatic 'z = d3.scaleThreshold().domain([1, 10, 50, 200, 500, 1000, 2000, 4000]).range(d3.schemeOrRd[9]), d.features.forEach(f => f.properties.fill = z(f.properties.density)), d' \
  | ndjson-split 'd.features' \
  | geo2svg -n --stroke none -p 1 -w 960 -h 960 \
  > ca-tracts-threshold.svg

Чтобы добавить наши границы округов к хороплету, просто перенаправьте их также на geo2svg. Вы можете запустить несколько команд и передать объединенный вывод по конвейеру, используя точки с запятой и круглые скобки: (foo; bar; baz). (Можно даже петлю протянуть!)

(topo2geo tracts=- \
    < ca-topo.json \
    | ndjson-map -r d3 -r d3=d3-scale-chromatic 'z = d3.scaleThreshold().domain([1, 10, 50, 200, 500, 1000, 2000, 4000]).range(d3.schemeOrRd[9]), d.features.forEach(f => f.properties.fill = z(f.properties.density)), d' \
    | ndjson-split 'd.features'; \
topo2geo counties=- \
    < ca-topo.json \
    | ndjson-map 'd.properties = {"stroke": "#000", "stroke-opacity": 0.3}, d')\
  | geo2svg -n --stroke none -p 1 -w 960 -h 960 \
  > ca.svg

Наконец, вы должны добавить ключ, который сообщает читателю, что означают цвета. Хоплет без ключа может быть красивым, но редко бывает информативным! 👀 Мой любимый стиль порогового ключа - Ford Fessenden’s, который вы можете создать в браузере с помощью D3, а затем вставить результат в свой SVG. (Если вы предпочитаете другой стиль, см. D3-legend Сьюзи Лу.)

Вопросы или комментарии? Ответьте ниже или в Твиттере. Спасибо за чтение!