Как я могу вложить геометрию GeoJSON / TopoJSON ИЛИ вложить сгенерированные пути с помощью D3?

Проблема:

Я пытаюсь создать интерактивную карту США, на которой отображаются границы штата, округа и страны. Округа заштрихованы на основе данных, и при наведении указателя мыши на штат должны быть выделены все округа в штате, а штат должен быть кликабельным. Я хочу добиться этого, имея SVG с фигурами округов внутри фигур штатов, внутри фигур США.

Я могу сгенерировать карту округа на основе файла формы округа CENSUS и заштриховать штаты на основе данных во внешнем CSV-файле, подготовив файл с помощью командной строки TopoJSON и используя следующий код в D3:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

path {
  fill: none;
  stroke-linejoin: round;
  stroke-linecap: round;
}

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>

var width = 960,
    height = 600;

var path = d3.geo.path()
    .projection(d3.geo.albersUsa());

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

d3.json("counties_pa.json", function(error, us) {
  if (error) return console.error(error);

var color = d3.scale.threshold()
    .domain([1, 10, 50, 100, 500, 1000, 2000, 5000])
    .range(["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"]);



svg.append('g').attr('class','counties').selectAll("path").data(topojson.feature(us, us.objects.cb_2014_us_county_20m).features).enter().append('path').attr('d',path).attr('style',function(d){return 'fill:'+color(d.properties.population / d.properties.area * 2.58999e6);});

});



</script>

Это в основном визуально приемлемо (за исключением того, что у него нет отдельных государственных/национальных границ), но функционально неадекватно. Чтобы применить CSS к округам при наведении курсора на штат, округа должны быть внутри формы штата или каким-то образом сгруппированы.

Что я пробовал:

  • Использование topojson-merge в командной строке для объединения округов в фигуры штатов, а затем рендеринга фигур штатов по отдельности — это помогает иметь дискретные границы штатов — но я не нашел способа вложить округа в соответствующие фигуры штатов.

Чем я занимаюсь сейчас:

  • Каким-то образом объединив файл TopoJSON штата и файл TopoJSON округа и вложив округа в штаты, а затем отобразив их с помощью D3.

  • Каким-то образом используя d3, чтобы взять не вложенные данные о состоянии и округе и просто вложить их в клиент на уровне клиента.

В конце я хотел бы узнать о наиболее эффективном и быстром процессе рендеринга для достижения желаемой функциональности.

Заранее благодарны за Вашу помощь.


person David Capalbo    schedule 14.06.2015    source источник
comment
Возможно, я что-то упустил, но не могли бы вы просто взять данные округов и для начального просмотра раскрасить все округа в штате (и их границы) одним цветом?   -  person Lars Kotthoff    schedule 14.06.2015
comment
Возможно, вы могли бы использовать свойства в вашем файле topojson, чтобы сопоставить округа со штатами, которым они принадлежат. т.е. иметь свойство state для каждого округа, которое идентифицирует штат, к которому они принадлежат, это позволит вам выбрать все подходящие округа при наведении курсора на штат. Я не очень понимаю, чего вы пытаетесь достичь, кроме как выделить округа при наведении курсора. Если это так, то то, что я предложил, должно быть выполнимо. Откуда вы берете шейп-файлы округов, и есть ли в них состояние, которому принадлежит каждый округ, в существующем свойстве, которое вы можете использовать повторно?   -  person Ben Lyall    schedule 15.06.2015


Ответы (1)


Я проверил ваши источники данных, и вот чего вы пытаетесь достичь: http://bl.ocks.org/benlyall/55bc9474e6d531a1c1fe

По сути, я создал файл TopoJSON, используя следующую командную строку:

topojson -o counties_pa.json --id-property=+GEOID -p -e POP01.txt --id-property=+STCOU -p population=+POP010210D,area=ALAND,state=+STATEFP,county=+COUNTYFP cb_2014_us_county_20m.shp cb_2014_us_state_20m.shp

Некоторые пояснения по этому поводу:

  • -o counties_pa.json устанавливает имя выходного файла
  • --id-property=+GEOID будет использовать это свойство во входном файле как id каждой выходной геометрии.
  • -p означает включение всех свойств из входного файла
  • -e POP01.txt будет извлекать внешние данные из файла POP01.txt. Этот файл представляет собой CSV-файл, сгенерированный из электронной таблицы POP01.xls, доступной по адресу http://www.census.gov/support/USACdataDownloads.html#POP
  • --id-property=+STCOU означает, что свойство id из внешнего файла (POP01.txt) находится в столбце STCOU. Это используется для сопоставления с соответствующими ids во входном файле (которые находятся в свойстве GEOID, как описано выше).
  • -p population=+POP010210D,area=ALAND,state=+STATEFP,county=+COUNTYFP явно перечисляет свойства, которые мне нужны в выходном файле, поэтому ничего лишнего не будет включено. POP010210D — это название столбца для населения по данным переписи 2010 года, поэтому я просто использовал его для демонстрационных целей.
  • cb_2014_us_county_20m.shp cb_2014_us_state_20m.shp — это два входных файла. Один для форм округа и один для форм штата. Каждый из них будет добавлен в выходной файл в отдельных свойствах, названных в честь их имен файлов.

Я сделал это таким образом, поскольку вы, казалось, раскрашивали районы своих округов в зависимости от плотности населения, поэтому в выходном файле должны были быть и население, и площадь. Население было взято из электронной таблицы POP01 и связано с каждым округом на основе GEOID (это просто номер штата, объединенный с номером округа).

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

Из этого я взял ваш код выше и обновил его до:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

path {
  fill: none;
  stroke-linejoin: round;
  stroke-linecap: round;
}

path.state {
    fill: none;
    stroke: black;
    stroke-width: .5px;
}

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>

var width = 960,
    height = 600;

var path = d3.geo.path()
    .projection(d3.geo.albersUsa());

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

d3.json("counties_pa.json", function(error, us) {
  if (error) return console.error(error);

var color = d3.scale.threshold()
    .domain([1, 10, 50, 100, 500, 1000, 2000, 5000])
    .range(["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"]);

    svg.append('g')
        .attr('class','counties')
        .selectAll("path")
      .data(topojson.feature(us, us.objects.cb_2014_us_county_20m).features).enter()
        .append('path')
        .attr('d', path)
        .attr("id", function(d) { return "county-" + d.id; })
        .attr("data-state", function(d) { return d.properties.state; })
        .attr('style',function(d) { 
            return 'fill:'+color(d.properties.population / d.properties.area * 2.58999e6);
        })
        .on("mouseover", hoverCounty)
        .on("mouseout", outCounty);

    svg.append('g')
        .attr('class', 'states')
        .selectAll("path")
      .data(topojson.feature(us, us.objects.cb_2014_us_state_20m).features).enter()
        .append("path")
        .attr("class", "state")
        .attr("id", function(d) { return "state-" + d.id; })
        .attr("d", path);
    });

function hoverCounty(county) {
    d3.selectAll("path[data-state='" + county.properties.state + "']").style("opacity", .5);
}

function outCounty(county) {
    d3.select(".counties").selectAll("path").style("opacity", null);
}

</script>

Новые и интересные фрагменты кода:

  1. Добавьте атрибут data-state к каждому округу, чтобы определить, к какому штату он принадлежит:

    .attr("data-state", function(d) { return d.properties.state; })
    
  2. Добавьте границы штатов (я объединил состояния в файл TopoJSON в командной строке topojson)

    svg.append('g')
        .attr('class', 'states')
        .selectAll("path")
      .data(topojson.feature(us, us.objects.cb_2014_us_state_20m).features).enter()
        .append("path")
        .attr("class", "state")
        .attr("id", function(d) { return "state-" + d.id; })
        .attr("d", path);
    });
    
  3. Добавлены обработчики наведения, чтобы вы могли видеть, как я определяю группировку округов в штаты:

    function hoverCounty(county) {
        d3.selectAll("path[data-state='" + county.properties.state + "']").style("opacity", .5);
    }
    
    function outCounty(county) {
        d3.select(".counties").selectAll("path").style("opacity", null);
    }
    
  4. Привязал эти обработчики наведения к каждому округу, чтобы они выполнялись в соответствующее время:

    .on("mouseover", hoverCounty)
    .on("mouseout", outCounty);
    
person Ben Lyall    schedule 15.06.2015
comment
Бен, это то, что я искал, большое спасибо. Я не знал, что вы можете использовать два шейп-файла в командной строке topojson. Также этот подход работает, но мне интересно, может ли он быть менее подробным, буквально каким-то образом вложив округа в штаты, и не нужно устанавливать идентификатор каждого округа (с состоянием) для использования в событии наведения. У меня есть хорошая точка для перехода, и я очень ценю вашу помощь. - person David Capalbo; 16.06.2015
comment
Не могли бы вы просмотреть предоставленную вами командную строку topojson? Я немного сбит с толку, потому что вижу значение -id-property (+STCOU), которого нет ни в одном файле, и мне интересно, является ли это типом или нет. Кроме того, я никогда не работал с двумя файлами, и мне не помешал бы более описательный анализ. - person David Capalbo; 16.06.2015
comment
Я добавил объяснение к своему ответу, не стесняйтесь задавать любые дополнительные вопросы, если это необходимо. - person Ben Lyall; 17.06.2015
comment
С точки зрения того, чтобы делать что-то по-другому с вложением или иным образом. Я подозреваю, что вы обнаружите, что компромисс, вероятно, не стоит того. Обработка данных для создания новой структуры данных, имитирующей физическую иерархию, вероятно, не сильно прояснит ситуацию, и вам потребуется создать структуру javascript, которая по-прежнему совместима с передачей данных в topojson для создания границ. - person Ben Lyall; 17.06.2015
comment
Еще раз спасибо за вашу помощь. Меня больше всего увлек параметр double --id-property — я научился использовать там значения, разделенные запятыми, только при связывании внешних файлов. Последнее, что я не совсем понимаю, это две опции -p - вы указываете, что хотите сохранить все, а затем явно хотите сохранить только некоторые столбцы - это необходимо или моя интерпретация неверна? Кроме того, я просто предполагаю, что на самом деле нет порядка включения параметров и что все параметры в основном применяются к обоим шейп-файлам. - person David Capalbo; 17.06.2015