Проблема выбора элемента по идентификатору с помощью d3 js - PullRequest
1 голос
/ 13 марта 2020

Я пытаюсь сделать карту хороплета в d3 js.

В своем коде я использую ge json для рисования французских департаментов (округов), а затем хочу раскрасить их, используя данные из файла CSV.

Сначала я заполняю все округа их официальным идентификатором, который состоит из 5 цифр (например, 75001).

Затем я хочу покрасить их с помощью colorScale. Для этого я делаю a для каждого l oop, где я выбираю округа, используя их ID (в этот раз в файле csv), и использую цветовую шкалу и данные из csv, чтобы получить цвет счета ie на карте.

Я думаю, что проблема в том, что d3.select ("# d" + e.insee) не работает.

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

Вот весь мой код. Данные загружаются с github, поэтому каждый может его выполнить. Я извинился за длинный код.

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
 <head>
 <meta charset="utf-8">

 <title>All in One</title>

 <script src="https://d3js.org/d3.v5.min.js"></script>

 <style type="text/css">
   #info {
     margin-top: 50px;
   }

   #deptinfo {
     margin-top: 30px;
   }

   .department {
     cursor: pointer;
     stroke: black;
     stroke-width: .5px;
   }

   .department:hover {
     stroke-width: 2px;
   }

  div.tooltip {
    position: absolute;
    opacity:0.8;
    z-index:1000;
    text-align:left;
    border-radius:4px;
    -moz-border-radius:4px;
    -webkit-border-radius:4px;
    padding:8px;
    color:#fff;
    background-color:#000;
    font: 12px sans-serif;
    max-width: 300px;
    height: 40px;
  }

  #svg {
    display: block;
    margin: auto;
  }
</style>
 </head>
   <body>
     <div id="map"></div>
   </body>
 </html>

 <script type="text/javascript"> 
   const width = 850, 
   const height = 800,
   colors = ['#d4eac7', '#c6e3b5', '#b7dda2', '#a9d68f', '#9bcf7d', '#8cc86a', '#7ec157', '#77be4e', '#70ba45', '#65a83e', '#599537', '#4e8230', '#437029', '#385d22', '#2d4a1c', '#223815'];

 const path = d3.geoPath();

 const projection = d3.geoMercator()
 .center([2.332978, 48.860117])
 .scale(40000)
 .translate([width / 2, height / 2]);

 path.projection(projection);

 const svg = d3.select('#map').append("svg")
 .attr("id", "svg")
 .attr("width", width)
 .attr("height", height)
 .attr("class", "Blues");

 // Append the group that will contain our paths
 const deps = svg.append("g");

 var promises = []; 


promises.push(d3.json('https://raw.githubusercontent.com/cerezamo/dataviz/master/Graphique_bokeh/pop_comgeo.geojson'))

promises.push(d3.csv("https://raw.githubusercontent.com/cerezamo/dataviz/master/variables.csv"))

Promise.all(promises).then(function(values){
 const geojson = values[0];
 const csv = values[1];


var features = deps
 .selectAll("path")
 .data(geojson.features)
 .enter()
 .append("path")
 .attr('id', function(d) {return "d" + d.properties.insee;})// Creation of the id as (ex :"d75005")
 // I add a d so the id is not a pure number as it could create error when selecting it
 .attr("d", path);

var quantile = d3.scaleQuantile()
 .domain([0, d3.max(csv, function(e) { return + e.densitehabkm2; })])
 .range(colors);

var legend = svg.append('g')
 .attr('transform', 'translate(725, 150)')
 .attr('id', 'legend');

  legend.selectAll()
   .data(d3.range(colors.length))
   .enter().append('svg:rect')
     .attr('height', '20px')
     .attr('width', '20px')
     .attr('x', 5)
     .attr('y', function(d) { return d * 20; })
     .style("fill", function(d) { return colors[d]; });

var legendScale = d3.scaleLinear()
 .domain([0, d3.max(csv, function(e) { return +e.densitehabkm2; })])
 .range([0, colors.length * 20]);

var legendAxis = svg.append("g")
 .attr('transform', 'translate(750, 150)')
 .call(d3.axisRight(legendScale).ticks(3));


csv.forEach(function(e,i) {
 d3.select(("#d" +  e.insee)) // Line where I think the problem is
                              // Here I'm trying to select Id's using the same code but reading it in the csv file. I have check and id's in geojson and csv do correspond
   .style("fill", function(d) { return quantile(+e.densitehabkm2); })
   .on("mouseover", function(d) {
    div.transition()        
      .duration(200)      
      .style("opacity", .9);
    div.html("IZI")
      .style("left", (d3.event.pageX + 30) + "px")     
      .style("top", (d3.event.pageY - 30) + "px");
  })
  .on("mouseout", function(d) {
    div.style("opacity", 0);
    div.html("")
      .style("left", "-500px")
      .style("top", "-500px");
    });
  });

 //console.log(csv.insee);

});



// Append a DIV for the tooltip
 var div = d3.select("body").append("div")   
  .attr("class", "tooltip")               
  .style("opacity", 0);

</script>

Большое спасибо за потраченное время.

Ответы [ 2 ]

2 голосов
/ 14 марта 2020
Элементы

SVG <path> по умолчанию имеют черную заливку. Однако это не проблема: ваш style("fill", ...) должен работать независимо от

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

Все, что говорится, Простейшим решением является фильтрация исходных данных, например:

geojson.features = geojson.features.filter(function(d) {
    return d.properties.year === 1962;
});

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

С этим изменением ваш style метод будет работать. Вот бегущая демонстрация:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">

<head>
  <meta charset="utf-8">

  <title>All in One</title>

  <script src="https://d3js.org/d3.v5.min.js"></script>

  <style type="text/css">
    #info {
      margin-top: 50px;
    }
    
    #deptinfo {
      margin-top: 30px;
    }
    
    .department {
      cursor: pointer;
      stroke: black;
      stroke-width: .5px;
    }
    
    .department:hover {
      stroke-width: 2px;
    }
    
    div.tooltip {
      position: absolute;
      opacity: 0.8;
      z-index: 1000;
      text-align: left;
      border-radius: 4px;
      -moz-border-radius: 4px;
      -webkit-border-radius: 4px;
      padding: 8px;
      color: #fff;
      background-color: #000;
      font: 12px sans-serif;
      max-width: 300px;
      height: 40px;
    }
    
    #svg {
      display: block;
      margin: auto;
    }
  </style>
</head>

<body>
  <div id="map"></div>
</body>

</html>

<script type="text/javascript">
  const width = 850,
    height = 800,
    colors = ['#d4eac7', '#c6e3b5', '#b7dda2', '#a9d68f', '#9bcf7d', '#8cc86a', '#7ec157', '#77be4e', '#70ba45', '#65a83e', '#599537', '#4e8230', '#437029', '#385d22', '#2d4a1c', '#223815'];

  const path = d3.geoPath();

  const projection = d3.geoMercator()
    .center([2.332978, 48.860117])
    .scale(40000)
    .translate([width / 2, height / 2]);

  path.projection(projection);

  const svg = d3.select('#map').append("svg")
    .attr("id", "svg")
    .attr("width", width)
    .attr("height", height)
    .attr("class", "Blues");

  // Append the group that will contain our paths
  const deps = svg.append("g");

  var promises = [];


  promises.push(d3.json('https://raw.githubusercontent.com/cerezamo/dataviz/master/Graphique_bokeh/pop_comgeo.geojson'))

  promises.push(d3.csv("https://raw.githubusercontent.com/cerezamo/dataviz/master/variables.csv"))

  Promise.all(promises).then(function(values) {
    const geojson = values[0];
    const csv = values[1];

    geojson.features = geojson.features.filter(function(d) {
      return d.properties.year === 1962;
    })

    var features = deps
      .selectAll("path")
      .data(geojson.features)
      .enter()
      .append("path")
      .attr('id', function(d) {
        return "d" + d.properties.insee;
      }) // Creation of the id as (ex :"d75005")
      // I add a d so the id is not a pure number as it could create error when selecting it
      .attr("d", path);

    var quantile = d3.scaleQuantile()
      .domain([0, d3.max(csv, function(e) {
        return +e.densitehabkm2;
      })])
      .range(colors);

    var legend = svg.append('g')
      .attr('transform', 'translate(725, 150)')
      .attr('id', 'legend');

    legend.selectAll()
      .data(d3.range(colors.length))
      .enter().append('svg:rect')
      .attr('height', '20px')
      .attr('width', '20px')
      .attr('x', 5)
      .attr('y', function(d) {
        return d * 20;
      })
      .style("fill", function(d) {
        return colors[d];
      });

    var legendScale = d3.scaleLinear()
      .domain([0, d3.max(csv, function(e) {
        return +e.densitehabkm2;
      })])
      .range([0, colors.length * 20]);

    var legendAxis = svg.append("g")
      .attr('transform', 'translate(750, 150)')
      .call(d3.axisRight(legendScale).ticks(3));


    csv.forEach(function(e, i) {
      d3.select(("path#d" + e.insee)) // Line where I think the problem is
        // Here I'm trying to select Id's using the same code but reading it in the csv file. I have check and id's in geojson and csv do correspond
        .style("fill", function() {
          return quantile(+e.densitehabkm2);
        })
        .on("mouseover", function(d) {
          console.log(d);
          div.transition()
            .duration(200)
            .style("opacity", .9);
          div.html("IZI")
            .style("left", (d3.event.pageX + 30) + "px")
            .style("top", (d3.event.pageY - 30) + "px");
        })
        .on("mouseout", function(d) {
          div.style("opacity", 0);
          div.html("")
            .style("left", "-500px")
            .style("top", "-500px");
        });
    });

    //console.log(csv.insee);

  });



  // Append a DIV for the tooltip
  var div = d3.select("body").append("div")
    .attr("class", "tooltip")
    .style("opacity", 0);
</script>
0 голосов
/ 14 марта 2020

Ну, проблема на самом деле была в css.

Спасибо Райану Мортону за ответ.

Добавление .attr('fill', 'none') к при первом создании объектов карты , Это автозаполнение черным и как-то предотвращает ваши цвета позже: https://jsfiddle.net/bz3o5yah/

...