3

Community, I would like to create a Choropleth Map using D3.js similar to the one found here.

My map differs from this example, in that, I would like to use a SVG I created and resides in my index.html,instead of using a TopoJSON or GeoJson file for shape data, as I do not have that shape data for the countries districts elsewhere. My SVG uses paths or polygons with an id value for each district in my map that correspond with the my .tsv file's ids.

I have my .tsv data set file with id, an range equivalent to the example the example.

I considered converting the SVG to GeoJSON then to TopoJson using this tool and that tool, however I have no Command Line experience. More over, After reading the third answer to this question on Stackoverflow. which states:

"If you're creating a choropleth and have no geolocation requirements then you can simply add the SVG directy to your page and use D3 to map data to your image! Once you have the SVG in your page then you can even manually edit all path data to include classes and ids that will make your D3 job easier. "-Roberto Sterling

I attempted to go for using the SVG, using the corresponding id values to my data for the path and polygon id attribute.

Below is my customized D3 code from the example:

<script>
var width = 960,
    height = 600;

var rateById = d3.map();

var quantize = d3.scale.quantize()
.domain([0, .15])
.range(d3.range(9).map(function(i) { return "q" + i + "-9"; }));

var projection = d3.geo.albersUsa()
.scale(1280)
.translate([width / 2, height / 2]);

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

var svg = d3.select("body svg");
var constituencies = svg.selectAll("path, polygon");

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

function ready(error, us) {
  if (error) throw error;

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

  svg.append("path")
  .datum(topojson.mesh(constituencies, constituencies.d.id, function(a, b) { return a !== b; }))
  .attr("class", "constituency")
  .attr("d", path);
}

d3.select(self.frameElement).style("height", height + "px");

</script>

The is line of code :

      .datum(topojson.mesh(constituencies, constituencies.d.id, function(a, b) { return a !== b; }))

is causing the following error:

Uncaught TypeError: Cannot read property 'id' of undefined

Where have I gone wrong? and or How do I Bind Data from a .tsv to an SVG's paths using d3.js for a Choropleth Map?

Community
  • 1
  • 1
user3741085
  • 99
  • 1
  • 2
  • 12
  • 1
    so you saying that you already have a map in your html...and you want to link the tsv to that map ? – Cyril Cherian Feb 15 '16 at 12:03
  • 1
    Check out [Bounding csv data to topojson path - cloropleth](http://stackoverflow.com/questions/18466584/) and [How to add properties to topojson file?](http://stackoverflow.com/questions/18444261/) – Hugolpz Feb 15 '16 at 14:08
  • @Cyril Yes I have my map as an svg , within the document, not generated by D3 from GeoJSON OR TopoJSON – user3741085 Feb 15 '16 at 18:43
  • You don't need any topoJson stuff here, loop your data, find the path in your svg with same id and then just set it's color. Can add a link to your SVG or include it in your question? – Mark Feb 15 '16 at 23:44
  • Hi @user3741085. If you already have an SVG file that represents the US map (I'm assuming it is US because you're using AlbersUSA on your example) then you don't need to call d3.map/projection etc. Instead you just bind data to your SVG (through a d3 selection) or fill the SVG path elements directly without a data bind. Take a look at [SVG EXAMPLE] (http://stelling.cc/cprj) It has an embedded SVG on the index.html that is used by coloring.js to produce multiple cloropleths. Also, do you have a complete example of your code ? It will be easier to help you out with complete code... – Roberto Stelling Feb 15 '16 at 23:54
  • @RobertoStelling I am not using a map of the US, I need to change AlbersUSA projection to the correct choice.I am using my own SVG of Jamaica I have created in Illustrator. – user3741085 Feb 16 '16 at 04:25
  • Got it, will take a deeper look at your code but maybe it will be better if I simplify my example to the "barebones" so that the concept of using an embedded SVG with D3 is easier to grock... – Roberto Stelling Feb 16 '16 at 10:06

1 Answers1

0

Here goes a brief description and an example of what you need to do to embed an SVG within an html page and make a d3 cloropleth out of it while reading the data from a csv file:

First add your SVG to the html page. Ideally it will be located within a div like the example below:

 <div class="map">
 <?xml version="1.0" enconding="UTF-8" standalone="no"?>
 <svg ...your svg data...>
 ...
 <path id="AngraDosReis"... class="fil3"...>
 <path id="Aperibe"... class="fil3"...>
 ...
 </svg>
 </div>

In my example the ids are not important but the "fil3" class and data order are important as my data is on the same order of the svg elements (In other words, the first line on the data.csv file corresponds to the data associated with the first path.fil3 element on my svg). Later on the code I'll be binding the data to the svg elements with d3.selectAll("svg.fil3"), so the class on each path element is really important: if a path do not represent a district then it does not belong to the fil3 class. With you SVG in place you just need to read your variables from the csv file:

var ready = false; // Tells if file has been read and variables are ready
var dsv = d3.dsv(";", "text/plain");
dsv("./data.csv",  function(dados) {
    return {
      indMed14: +dados.indMed14,
      densidades: +dados.densidades,
      gastos: +dados.gastos
    }; }, function(dados) {
    for ( i = 0; i < dados.length; i++) {
      indMed14.push(dados[i].indMed14);
      densidades.push(dados[i].densidades);
      gastos.push(dados[i].gastos);
    }
    ready = true;
  });

In this example I'm reading a csv and creating 3 arrays (indMed14, densidades and gastos) that will be passed to the painting routine later on the road. The ready variable is important because d3.dsv is asynchronous, so the cloropleth buttons will be active only when the data is ready.

Later on the html you have the calls to the painting routine:

<div class="col-md-4">
  <button onclick="pintaMapa('linear', apaga, [0,1], ['#ffffff', '#ffffff'], 'Rio de Janeiro', 'região', textoOriginal)">
    Limpa Mapa
  </button>
</div>
<div class="col-md-4">
  <button onclick="if (ready) pintaMapa( 'linear', indMed14, [d3.min(indMed14), d3.max(indMed14)], ['#a50f15', '#fee5d9'], 'Indicador de Saúde 2014', 'mapa', isaude)">
    Indicador Saude 2014
  </button>
  <button onclick="if (ready) pintaMapa( 'log', gastos, [d3.min(gastos),d3.max(gastos)], ['#eff3ff', '#08519c'], 'Gastos dos Públicos Totais', 'mapa', gpub)">
    Gastos Publicos
  </button>
  <button onclick="if (ready) pintaMapa( 'log', densidades, [d3.min(densidades), d3.max(densidades)], ['#feedde', '#a63603'], 'Densidade Demográfica', 'mapa', tdd)">
    Densidade Demografica
  </button>

At the end of the painting routine, after creatind the color function (cor(d)) you just bind the data you have [.data(variavel)] to the "path.fil3" elements that were created to paint your cloropleth.

// Binds data to the SVG map and paints the cloropleth
d3.selectAll("path.fil3").data(variavel)
  .transition()
  .duration(1000)
  .ease("cubic-in-out")
  .style("fill", function(d) {return cor(d)})
  .text(function(d,i) { return(nMunicipio(i) + ": " + d)});

The full code can be found at http://stelling.cc/svgExample The SVG embedded on this example was taken from Wikipedia and adapted to my needs (adding ids and classes to fit my requirements).

  • By the way @user3741085 , I've created another cloropleth example with the same logic but the svg coming from an external file but I'd rather wait for your feedback before adding more salt into this discussion. Cheers. – Roberto Stelling Feb 18 '16 at 11:34
  • I have been working on translating you code and updating it to match my id's and so on. I will share with you the results, thanks for your help so far, I really appreciate it. – user3741085 Feb 18 '16 at 14:38
  • Take your time, once you finish working on your code you can update the question accordingly with your results and we will see the best way to move forward, happy coding. ;) – Roberto Stelling Feb 18 '16 at 16:12