I'm a bit of a newb with D3 and I'm trying to work on a project for the organization I work for. I need to draw a choropleth map of Kenya with some data we collected. I'm working off Scott Murray's book Interactive Data Visualization for the Web. In his book he uses the following to generate paths from a json file of US States:
//Width and height
var w = 500;
var h = 300;
//Define default path generator
var path = d3.geo.path();
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Load in GeoJSON data
d3.json("us-states.json", function(json) {
//Bind data and create one path per GeoJSON feature
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path);
});
I tried adapting this code to draw Kenyan counties from a json file I created from the Kenya shapefile I downloaded. The structure of the json file looks just like that of the US states file but when I look at the HTML in a browser I don't see any lines. I check the console and the path placeholders are there there is no data. If I swap in the US-states.json file I see the paths with data and the map in the browser.
Can someone help me please.
Thanks
I am doing something similar for Nairobi. As Lars has said in the comments it looks like you haven't set a projection for the map. The code below uses a mercator projection and the map is centered on Nairobi.
(Note that the scale is very zoomed and you would have to decrease this to get the whole of Kenya in).
var margin = {top: 50, right: 20, bottom: 20, left: 60},
dynwidth = $("#nairobistock").width(),
rowheight = 460;
width = dynwidth - margin.left - margin.right,
height = rowheight - margin.top - margin.bottom;
var projection = d3.geo.mercator()
.center([36.8, -1.3])
.scale([90000])
.translate([width/2, height/2]);
var nairobipathing = d3.geo.path().projection(projection);
var svg = d3.select("#nairobistock").append("svg")
.attr("width", (width + margin.left + margin.right) )
.attr("height", (height + margin.top + margin.bottom) );
d3.json("topojson/KEN-3topo.json", function(error, nairobi){
if (error) return console.error(error);
console.log(nairobi);
console.log("topojson added");
svg.selectAll("path")
.data(topojson.feature(nairobi, nairobi.objects.suburbs).features)
.enter()
.append("path")
.attr("class", function(d) {return d.ID;})
.attr("d", nairobipathing );
});
Hope this helps.
Related
I've created a map of Washington D.C. roads by converting a shapefile to JSON, and then mapping it. I have set up a zoom property to allow users to zoom in and out. It's here:
https://bl.ocks.org/KingOfCramers/raw/c8d575fb1322590012323a7953908d5f/56bd68ec204046076114413ef777706726bdb50a/
However, I'm having problems because when the user "zooms" in on the projected map, it jumps back to the middle of the screen. This must have something to do with the coordinates the zoom event initially receives. How do I change those settings so that, upon interaction, the map begins to zoom from its current position?
Here's the full code:
var transLat = -100;
var transLon = -350;
var transScale = 1.6;
var width = 960;
var height = 600;
var margin = {top: 0, bottom: 0, left: 0, right: 0}
d3.queue()
.defer(d3.json, "simpleroads.json")
.defer(d3.csv, "locations.csv")
.await(ready)
function ready(error, world, locations) {
var projection = d3.geoIdentity().reflectY(true).fitSize([width,height],world)
var path = d3.geoPath().projection(projection) // Geopath generator
var zoomExtent = d3.zoom().scaleExtent([1.6, 3]);
function zoom() {
g.attr("transform", d3.event.transform)
}
console.log(locations)
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.style("background-color","lightgrey")
.call(zoomExtent
.on("zoom", zoom))
var g = svg.append("g")
.attr("class", "mapInformation")
.attr("transform", `translate(${transLat},${transLon}) scale(${transScale})`)
var paths = g.selectAll("path")
.data(world.features)
.enter()
.append("path")
.attr("d", path)
}
You have to pass the initial transform to the zoom function. In your case:
.call(zoomExtent.transform,
d3.zoomIdentity.translate(transLat, transLon).scale(transScale));
Here is the updated blockbuilder: http://blockbuilder.org/anonymous/b1d7dcc08c3fbee934fd415d7127dd14
I am trying to make a static, interactive map of Wisconsin counties.
I am using Albers Equal Area Conic projection and I have tried .rotate, .center, .fitExtent, but whenever I add these into the code, the map completely disappears.
Anyone know what could be going on?
Here's the code:
var margin = {top: 20, left: 20, bottom: 20, right: 20}
height = 600- margin.top - margin.bottom,
width = 960 - margin.left - margin.right;
var svg2 = d3.select("#map2").append("svg")
.attr("height", height)
.attr("width", width)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.right + ")");
d3.queue()
.defer(d3.json, "WiscCountiesNoProjection.json")
.await(ready);
var projection2 = d3.geoAlbers()
.translate([width/3, height/1])
.scale(4000)
var path2 = d3.geoPath()
.projection(projection2)
function ready (error, data) {
var counties = topojson.feature(data, data.objects.WiscCounties).features
svg2.selectAll(".counties")
.data(counties)
.enter().append("path")
.attr("class", "counties")
.attr("d", path2)
}
And here is what it looks like:
You don't go into the details of how you're calling the different methods, but here's a few general tips on how to work with them:
fitExtent or the short hand version fitSize should definitely make your object appear on the SVG if you're not applying any other transformations. A minimum working example would be:
const proj = d3.geoAlbers()
.fitSize([width, height], wisconsin)
That should result in a nicely fitted, albeit not correctly rotated Wisconsin. If it doesn't, wisconsin is not a valid GeoJSON object, ie not a FeatureCollection, single Feature or geometric object.
The next question is how to fix the rotation. For conic projections, as I understand, you generally want to find the center of your object of interest and rotate by the inverse of the longitude. A very friendly StackOverflow user explained the details here: https://stackoverflow.com/a/41133970/4745643
In the case of Wisconsin the center of the state has a longitude of almost exactly -90°, so we do this:
const proj = d3.geoAlbers()
.rotate([90, 0, 0])
.fitSize([width, height], wisconsin)
Note that we're rotating the map before we fit the object into our SVG. As a general rule of thumb, you should apply spherical transforms before zooming and fitting your map (I shall follow up with a more detailed explanation in a moment).
That should leave you with a nicely rotated and fitted state:
I've been following the tutorial here: http://www.delimited.io/blog/2015/5/16/interactive-webgl-globes-with-threejs-and-d3
This example uses a Canvas element and then wraps this as a texture to the THREE SphereGeometry. I wasn't happy with how blurry the lines were rendering so opted to render as an SVG. The SVG path data is being constructed by D3 and topojson.
My question is this: can I translate this SVG data to a SphereGeometry texture? I'd like to be able to zoom and retain detail on the final object - if there's a way to do this with canvas I'd also be interested.
Here's my code that draws the SVG;
var width = 1024,
height = 512;
var projection = d3.geo.equirectangular()
.scale(163)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("data/world.json", function(err, data) {
svg.append("path")
.datum(topojson.feature(data,data.objects.countries))
.attr("d", path)
.attr("class", "countries");
I am trying to draw the states of India map (with disputed territories ) by D3 Map. I successfully generate the topojson file which looks good in http://www.mapshaper.org/
And the json link is https://dl.dropboxusercontent.com/s/wk9qd47wn1nhsjm/dddtopo.json
But I failed to draw the map. The link http://jsfiddle.net/sEFjd/47/ is how I did under jsfiddle.
var topoJsonUrl = "https://dl.dropboxusercontent.com/s/wk9qd47wn1nhsjm/dddtopo.json";
var width = 360,
height = 360;
d3.json(topoJsonUrl, function(error, mx) {
var projection = d3.geo.mercator();
// create the path
var path = d3.geo.path().projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
console.log(mx);
var geoPaths = svg.append("g")
.attr("class", "municipalities")
.selectAll("path")
.data(topojson.feature(mx, mx.objects.india).features);
geoPaths.enter().append("path")
.attr("d", path);
var p= svg.append("path")
.datum(topojson.mesh(mx, mx.objects.india))
.attr("d", path)
.attr("class", "state-boundary");
geoPaths.style("fill", function(d) {
return Math.random() > 0.5 ?'gray' : 'blue';
});
});
The code works well with other countries(Germany, Mexico) Not sure why it does not work this time. Any help will be very appreciated.
I'm trying to make a map using d3.js and three data files:
a simple .topojson file with my base map
a .geojson file with polygonal areas
a .csv file with data (which has a field in common with the .geojson)
First, I created my base map with this code:
var width = 360,
height = 600,
width2 = 479;
var proj = d3.geo.mercator()
.center([7.76, 48.57])
.scale(130000)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(proj);
var svg = d3.select("#carte").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("strasbourg.json", function(error, base) {
svg.append("path")
.data(topojson.feature(base, base.objects.contour).features)
.attr("class", "fond-stras")
.attr("d", path);
The result was fine (http://raphi.m0le.net/data/BAS/App_d3_strasrk/etape%201.html), so I made my code more complex:
var width = 360,
height = 600,
width2 = 479;
var proj = d3.geo.mercator()
.center([7.76, 48.57])
.scale(130000)
.translate([width / 2, height / 2]);
// This array will be used to the next choropleth
var couleurs = ['rgb(165,15,21)','rgb(222,45,38)','rgb(251,106,74)',
'rgb(252,146,114)','rgb(252,187,161)','rgb(254,229,217)'];
var path = d3.geo.path()
.projection(proj);
var svg = d3.select("#carte").append("svg")
.attr("width", width)
.attr("height", height);
// I use queue.v1.min.js to load my .topojson, .geojson and .csv
queue()
.defer(d3.json,"strasbourg.json")
.defer(d3.json, "chute_ries.json")
.defer(d3.csv, "progression_riesk.csv")
.await(ready);
function ready(error,base,areas,results) {
// I declare my base map like before, it works always fine
svg.append("path")
.data(topojson.feature(base, base.objects.contour).features)
.attr("class", "fond-stras")
.attr("d", path);
// the problematic part begins here :
svg.append("g").attr("class","areas")
.selectAll("path")
.data(areas.features)
.enter()
.append("path")
.attr("class", "area")
.attr("d", path)
// I write this to test an automatic colouring
.attr("fill", function(d,i){
if (results[i].diff_ries<-23){
return couleurs[0]
}
else {return couleurs[4]
}
})
Unfortunately, it doesn't work as you will see here: http://raphi.m0le.net/data/BAS/App_d3_strasrk/etape%202.html
Despite my efforts, I have not been able to get this to work. My .geojson was created with QGis, so I guess that it respects the Right-Hand-Rule.
My console does not display any errors so I haven't been able to determine what is wrong. I suspect that it could be the data() declaration, but I've seen several examples where it was written like this with .geojson data and worked perfectly.
It was a little tricky, but the problem comes from the projection of my original file : Lambert-93 (a french reference) instead of the Mercator precised in the script !
I resaved the file with a Mercator projection, and all worked perkectly fine !