Getting Choropleth map to show with DC.js

1,462 views
Skip to first unread message

J Khalaf

unread,
Apr 26, 2016, 6:50:16 AM4/26/16
to dc-js user group
Hello Friends

I am new to DC and Crossfilter and would greatly appreciate any help.

With D3, I am able to create a map, associate some CSV data to it and get it to show each area (e.g. county) in a different colour.

When I try and do the same with DC.js, I get an unhelpful error of Uncaught TypeError: Cannot read property 'length' of undefined.

My csv data is in the following format

ccgcode, ccgname, metric, male, female
06M, Great Yarmouth And Waveney, 3, 70, 30

In my topoJSON for the map, I also have a property called ccgcode and that is what I used to join the csv data to each path of map.

Here is my JS code with DC.js

'use strict'


var ccgMap       = dc.geoChoroplethChart('#map-wrap');


d3
.csv("../data/metricdata.csv", function (data) {
 
var data = crossfilter(data);


 d3
.json("../data/ccg.json", function (map) {
 ccgMap
.width(800)
 
.height(800)
 
.dimension(data)
 
.group(data)
 
.colors(d3.scale.quantize().range(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF", "#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"]))
 
.colorDomain([0, 200])
 
.colorCalculator(function (d) { return d ? ccgMap.colors()(d) : '#ccc'; })
 
.overlayGeoJson(map.features, "ccgcode", function (d) {
 
return d.properties.ccgname;
 
})
 
.title(function (d) {
 
return "CCG: " + d.key + "\nTotal Amount Raised: " + numberFormat(d.value ? d.value : 0) + "M";
 
});

 dc
.renderAll();
 
});
});

What am I doing wrong here? As far as I know, my data is already in good shape, and doesn't need any juggling with crossfilter.

Any help would be greatly appreciated.

Gordon Woodhull

unread,
Apr 26, 2016, 7:35:44 AM4/26/16
to dc-js-us...@googlegroups.com
Hi J,

You need to create crossfilter dimensions and groups and pass those in, instead of the crossfilter object as you are doing now. 

Otherwise dc.js doesn't know how you want to group and aggregate the data.

Take a look at the other examples (in particular the venture capital landscape) for ideas how you might do this.


Austin Lyons' tutorial is a good introduction to the concepts.


Cheers, 
Gordon 
--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dc-js-user-group/62edf5ca-d3b4-4fc1-86d1-ea1ba967932b%40googlegroups.com.

J Khalaf

unread,
Apr 26, 2016, 8:17:16 AM4/26/16
to dc-js user group
Thanks Gordon, that link is quite useful, it led me to this nice tutorial, though I am still finding it difficult to apply to my scenario (with my csv data).

I'm struggling to define my dimension! I've no idea what my dimension would be for what I'm trying to do.

The data that concerns me for the map is the ccgcode and the metric (see csv in my original post).

J Khalaf

unread,
Apr 26, 2016, 8:39:00 AM4/26/16
to dc-js user group
Based on the tutorial I linked to earlier, I had a bit of a play, and now my code looks like this:

'use strict'

var numberFormat = d3.format(".2f");
var ccgMap       = dc.geoChoroplethChart('#map-wrap');
var sexPieChart  = dc.pieChart('.pie-chart');

d3.csv("../data/metricdata.csv", function (data) {
var data = crossfilter(data);

var ccgs = data.dimension(function (d) {
return d["ccgcode"];
});

d3.json("../data/ccg.json", function (map) {
ccgMap.width(800)
.height(800)
.dimension(ccgs)
.group(ccgs.groupAll())
.colors(d3.scale.quantize().range(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF", "#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"]))
.colorDomain([0, 200])
.colorCalculator(function (d) { return d ? ccgMap.colors()(d) : '#ccc'; })
.overlayGeoJson(map.features, "ccgcode", function (d) {
return d.properties.ccgname;
})
.title(function (d) {
return "CCG: " + d.key + "\nTotal Amount Raised: " + numberFormat(d.value ? d.value : 0) + "M";
});

dc.renderAll();
});
});

But code still doesn't work, I still get the same error as before


J Khalaf

unread,
Apr 26, 2016, 9:08:06 AM4/26/16
to dc-js user group
Here is another thing I tried, again not working :(

'use strict'

var numberFormat = d3.format(".2f");
var ccgMap       = dc.geoChoroplethChart('#map-wrap');
var sexPieChart  = dc.pieChart('.pie-chart');

d3.csv('../data/metricdata.csv', function (data) {
var data = crossfilter(data);

var ccgs = data.dimension(function (d) {
return d['ccgcode'];
});

var stateCCGMetric = ccgs.group().reduceSum(function (d) {
console.log(d);
return d['metric'];
});

d3.json("../data/ccg.json", function (map) {
ccgMap.width(800)
.height(800)
.dimension(ccgs)
.group(stateCCGMetric)
.colors(d3.scale.quantize().range(["#E2F2FF", "#C4E4FF", "#9ED2FF"]))
.colorDomain([0, 200])
.colorCalculator(function (d) { return d ? ccgMap.colors()(d) : '#ccc'; })
.overlayGeoJson(map.features, "CCGCode", function (d) {

J Khalaf

unread,
Apr 26, 2016, 10:55:56 AM4/26/16
to dc-js user group
OK, I think I made some progress, but still no map is showing! Here is my code right now:

'use strict'

var numberFormat = d3.format(".2f");
var ccgMap       = dc.geoChoroplethChart('.map-wrap');
// var sexPieChart  = dc.pieChart('.pie-chart');

d3.csv('../data/metricdata.csv', function (data) {
var data = crossfilter(data);

var ccgs = data.dimension(function (d) {
return d['ccgcode'];
});

var ccgMetric = ccgs.group();

d3.json("../data/ccg.json", function (map) {
ccgMap.width(800)
.height(800)
.dimension(ccgs)
.group(ccgMetric)
.colors(d3.scale.quantize().range(["#7cbd30", "#0066cc", "#ee2e11"]))
.colorDomain([0, 200])
.colorCalculator(function (d) { return d ? ccgMap.colors()(d) : '#ccc'; })
.overlayGeoJson(topojson.feature(map, map.objects.ccg).features, "CCGcode", function (d) {
return d.properties.CCGcode;
});

dc.renderAll();
});
});

Although I get no errors and when I inspect the page in chrome, I can see that the svg has been drawn, the paths have no geometrical data!



J Khalaf

unread,
Apr 28, 2016, 6:50:10 AM4/28/16
to dc-js user group
Got it working! I was using TopoJSON, and apparently DC only works with GeoJSON, also I had to give my map a projection, as default projection only works for USA. Hope this helps someone in the future.

Toby Schaeffer

unread,
Jul 4, 2016, 10:57:58 AM7/4/16
to dc-js user group
I ran into the same issue, and tried to convert TopoJSON into GeoJSON via the method Bostock used in his Let's Make A Map tutorial.  Wasn't able to get it, so I ended up converting my shapefiles to GeoJSON via the great Mapshaper tool.

I'm running into some issues with projection and bounding for my map (also out of the US).  Can you share your code for how you did that?  Or anyone else who has suggestions for how to do that, getting the bounding box in as tight as you can for your map, using only dc.js or d3.js (i don't want to use Leaflet or another library to pull in map tiles).  Here's a good thread for how to do it in D3, but i can't quite get it to work in my dc.js map.  

Thanks!  

Toby Schaeffer

unread,
Jul 4, 2016, 6:16:29 PM7/4/16
to dc-js user group
i ended up getting it - I found this thread helpful:  http://stackoverflow.com/questions/34318121/dc-js-geochoropleth-map-scaling

Here's what I ended up using:  

d3.json("data/mdg_admin1.json", function(admin1JSON) {
 
var width = 300;
 
var height = 450
 
var projection = d3.geo.mercator();
 
var path = d3.geo.path().projection(projection);
 
 
//set up scale and translate
 
var bounds, scale, offset;
 projection
.scale(1).translate([0,0]);
 
var bounds = path.bounds(admin1JSON);
 
var scale = .90 / Math.max((bounds[1][0] - bounds[0][0]) / width, (bounds[1][1] - bounds[0][1]) / height);
 
var offset = [(width - scale * (bounds[1][0] + bounds[0][0])) /2, (height - scale * (bounds[1][1] + bounds[0][1])) /2 ];
 projection
.scale(scale).translate(offset);


 admin1Map
 
.width(width)
 
.height(height)
 
.dimension(admin1MapDim)
 
.group(admin1MapGroup)

 
.colors(d3.scale.quantize().range(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF", "#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"]))
       
.colorDomain([0, 200])

       
.colorCalculator(function (d) { return d ? admin1Map.colors()(d) : '#ccc'; })
 
.projection(projection)
 
.overlayGeoJson(admin1JSON.features, "mdg_adm1",
 
function(d) {
 
return d.properties.code;
 
})

Reply all
Reply to author
Forward
0 new messages