My question is about making a choropleth of US counties using d3 js. I've merged a dataset (providing a value for each US county) with topojson data for the US. When I console.log the topojson data, the required value appears and I can pick out a value for any given index.
I've also provided a range of colors in a scaleQuantile scale and set the domain to be the extent of values. Again, I can console.log colorScale(value) and get back a color, and invertExtent the color to see the range of colors, so that appears to be working.
My problem is with setting the fill attribute. The data value I want is stored in data.objects.counties.geometries, but when I set that in the topojson.feature function, the map doesn't show at all. So I tried instead adding the geometries to the fill function itself, but get a map of all-black US counties, even when I set the default color to blue. Here is the relevant code, this is my first choropleth attempt (and first stackoverflow question) and I'd be grateful for any help. Thank you
svg.selectAll("path")
.data(topojson.feature(data, data.objects.counties).features)
.enter()
.append("path")
.attr("d", path)
.style("fill", function (d){
var value = geometries.d.value;
if (value) {
return colorScale(value);
} else {
return "blue";
}
});
Related
I am trying to create a "proper" version of the logo at IFMMS Logo
using the graticule feature of d3.js.
At https://gist.run/?id=22361573b05b541ac9799037116aea8d you'll find the current state of the code - specifically version https://gist.run/?id=22361573b05b541ac9799037116aea8d&sha=dd6aef5c64e3ac4c1c917fd5e3c7bbf4b91c75a8
In a loop i am trying to set the graticule steps for different scales of the logo draft.
var lonsteps=6+col*2;
var latsteps=10;
var title='IFMMS Logo';
createLogo(title,id,cx,cy,scale,15,lonsteps,latsteps,debug);
in the "createLogo" function i am using these steps as outlined in the answer:
https://stackoverflow.com/a/19033063/1497139
I get
instead of
so instead of having 6/8/10 steps for the longitudinal grid lines I get three times 6 steps. It seems as if the first setting "overrides" all others although i assign different values.
What is causing this and how can it be fixed?
If you pass a parameter (svgid) to a function use it and don't use the global variable (id) with the same value.
function createLogo(title,svgid,cx,cy,scale,strokeScaleFactor,lonsteps,latsteps,debug) {
// ...
ggraticule.selectAll("path.feature"+svgid)
.data(graticule.lines)
.enter().append("path")
.attr("class", "feature"+svgid)
.attr("d", path);
// ...
}
One indication of the problem can be that your scale is not working.
The reason a HTML ID should be unique.
fix it by making the graticule defs id unique and use this new id.
var ggraticule=defs.append("g")
.attr("id","graticule-"+svgid)
.attr("class", "graticule")
.attr("stroke-width",scale/strokeScaleFactor);
// ...
use(svg,"graticule-"+svgid);
You still have a problem with the background, choose a color and see the effect.
In D3 v5+ you can use the step method on your geoGraticules like so:
var graticules = d3.geoGraticule().step([15, 15])
The default step is 10° and 10° for longitude and latitude.
I’m building a (d3 v4) cartographic visualization which allows the user to switch between many datasets (json files) and two different regions (administrative units of a country and smaller administrative units into its capital city). Actually the switch from one to another dataset on the initial country level works well, through buttons and jquery.
Problem: it’s a bit less convincing when switching to a map/dataset about the capital city, as the projection is initially set for the whole country and consequently the user has to zoom many times to visualize properly the map of the capital city. I would like to change the values of .scale and .center when calling the projection but after several trials I haven’t found how to do it.
As I only have two different regions to show, my intuition was to set first values of scale and center and to change them to other values (I know the values of .scale and .center I would like to use in both cases) when the user switches to a map of the capital city through a function. Is there any possibility to switch easily these values? Do you have any suggestion to solve this problem?
As I load the json file path into a function when the user clicks on the button to switch to another dataset, I was trying to load the value of scale the same way but I’m probably doing wrong. It seems that the part of the code about the projection can't be put in a function?
Thanks for your help!
Small part of my code:
var width = 1100, height = 770;
var projection = d3.geoConicConformal()
.scale(19000) // value I would like to which when the region changes
.center([4.45, 50.53]) // value I would like to which when the region changes
.translate([width/2,height/2]);
var svg = d3.select( "#mapcontainer" )
.append( "svg" )
.attr("width", width)
.attr("height", height)
.style("border", "solid 1px black");
var path = d3.geoPath()
.projection(projection);
var color, jsonfile, legendtext;
function load (jsonfile, legendtext, color) {
d3.selectAll(".currentmap").remove() ;
d3.json(jsonfile, function(error, belgique) {
g.selectAll("path")
.data(belgique.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke", "#fff")
.attr( "class", "currentmap")
.style("fill", function(d) {
var value = d.properties.DATA;
if (value) {return color(value);}
else {return "rgb(250,110,110)"}
});
})
};
//one of the following function for each map
function BGQprovinces() {
jsonfile = "ATLAS/NewGeoJson/bgq-data1-provinces.json";
legendText [= …];
color = d3.scaleOrdinal()
.domain( […])
.range([…]);
load(jsonfile, legendtext, color) ;
};
;
There area few approaches to accomplish this.
fitSize and fitExtent
One is to modify the projection scale and translate as opposed to scale and center. This is nearly the same operation, but translate pans the projected plane and center will pan the unprojected plane. To do so you need to use projection.fitSize([width,height],geojsonObject), or projection.fitExtent([[x0,y0],[x1,y1]],geojsonObject). The latter will allow margins of say, the first coordinate provided is the top left and the second coordinate provided is the bottom right of a bounding box in which the feature will be constrained.
d3.json(jsonfile, function(error, belgique) {
projection.fitSize([width,height], belgique);
// now draw as you would:
d3.selectAll(".currentmap").remove() ;
g.selectAll("path")
.data(belgique.features)
.enter()
.append("path")
.attr("d", path)
...
Note that for showing all of a country you need to have a feature that shows the whole country or a feature collection that shows all the parts of a country. You cannot use an array with fitSize or fitExtent, if you have an array of features, you can create a feature collection by using:
var featureCollection = {"type":"featureCollection","features":featureArray}
For your case, I'd suggest using fitSize or fitExtent.
centroid
If you really wanted to modify the center attribute as opposed to translate, or perhaps you want to change the rotation (a more likely outcome for conic conformals in many parts of the world, Belgium should be fine), then you need the geographic coordinates of the center. One way of a handful to do this is to get the centroid of a feature from path.geoCentroid:
var centroid = path.geoCentroid(geojsonObject);
Then use that to set the projection parameters to rotate:
projection.rotate([ -centroid[0],-centroid[1] ])
projection.center([0,0])
or to center:
projection.rotate([0,0])
projection.center(centroid)
Or a combination of both (depending on map projection type). Now you can apply fitSize or fitExtent, the feature is in the middle already, but now we can set the scale. The reason I suggest this as a potential answer is because not all projections, concic projections in particular, will give desired results by modifying only scale along with translate and/or center.
Of course for conic projections, you may need to find a way to set the parallels as well, but I'll leave that for another answer if it ever comes up.
I'm having trouble drawing belgium with a dc.js choropleth map.
I've read it can be a projection problem in another stackoverflow response (map json works on Geojson.io but it doesnt work on dc.js choropleth map), but I can't find the right parameters to write.
I would really appreciate any help with this issue.
Big thanks in advance !
Here is the geojson data I used:
https://github.com/Datafable/rolling-blackout-belgium/blob/master/data/geospatial/municipalities-belgium.geojson
And here is my code:
d3.json("data/municipalities-belgium.geojson", function (geojson) {
mapChart
.dimension(provinceDimension)
.group(provinceGroup)
.width(400)
.height(400)
.transitionDuration(1000)
.projection(d3.geo.mercator()
// .parallels([49, 52])
// .origin([0,40])
// .translate([200,900])
// .scale(150000000)
)
.overlayGeoJson(geojson.features, 'somename', function(d) {
return d.properties.shn;
})
;
dc.renderAll();
});
(The result is a blue square 400x400 that seems to select shn=BE391141)
Even if you put a scale, you don't get anything drawn?
As for the overlay, the name of the dimension has to be the name of the province you want to set the color of. How is provinceDimension defined? does it use shn as the key?
As an aside, your geojson contains lots of stuff you don't need (eg uk), having it only for the belgium provinces would make it much smaller.
There is something wrong in the dataset.
I am trying to modify this D3 example to use with my dataset
http://mbostock.github.io/d3/talk/20111116/airports.html
I think my problem is creating the array of coordinates for calculating the Voronoi polygons, but at this point I'm not sure.
I'm getting a Uncaught TypeError: Cannot read property '0' of undefined error that points to the line where I am calling the array. The code is live, please see here
http://cds.library.brown.edu/projects/mapping-genres/symbol-maps/brown-voronoi-map.html
The map displays just fine, but the data points, the radio button, and the voronoi lines do not appear (I'm not trying to show lines between data points, so that code has been removed).
Any thoughts or suggestions would be most appreciated. Many thanks!
There are a number of small things going on here which can be easily sorted out but some will require a bit of work. The first is that you're using a new version of d3 (v3) and the example you're trying to replicate is an older version. There have been significant changes between the versions on the mapping side of things. So you can either use an old version of d3 (v2 will work I think) or investigate the changes.
The Uncaught TypeError: Cannot read property '0' of undefined error is being generated because d3.geom.voronoi(positions) line is producing NaN. I'm not 100% sure why but you could just filter these out to get a temporary fix. A simple filter would be something like:
g.append("svg:path")
.attr("class", "cell")
.attr("d", function(d, i) {
if (polygons[i][0][0]) {
return "M" + polygons[i].join("L") + "Z";
}
})
with the if true when there is not a false value (NULL, NaN, etc) in the first element of the polygon. Please note that this is temporary fix while you investigate why you're getting NaN and this will not produce the correct voronoi polygons.
The fix for the data points is very simple, you just have to set the radius to something greater than 0 such as 10 (although I imagine that you might want to scale the dots to a property of your data like TotalPublished). For instance see the last line:
circles.selectAll("circle")
.data(locations.rows
.sort(function(a, b) { return b.TotalPublished - a.TotalPublished; }))
.enter().append("svg:circle")
.attr("transform", function(d) { return "translate(" + projection(d.coordinates) + ")"; })
.attr("r", 10);
The radio button (or checkbox) does not show up as it's not generated by the javascript (although it can be). In the example you refer to its generated in the html in this snippet:
<div style="position:absolute;bottom:0;font-size:18px;">
<input type="checkbox" id="voronoi"> <label for="voronoi">show Voronoi</label>
</div>
Once you've worked through this you'll need to get the voronoi lines working. I think with your code as is the voronoi lines won't show up as the checkbox is not checked. So you could comment these lines out and see what happens. A quick tip with paths is that if you want to just show the lines you need to set the fill style to none and the stroke to whatever colour you want. If you don't set the fill to none you'll just get a black screen.
I'm attempting to update a d3 multi-line graph by pulling data at 5 second intervals from a mySQL database using PHP. At the moment the graph is displaying but not updating - I know that there is something not right with my updateData function but have tried everything can think of and nothing seems to work. Can anyone please help?
https://gist.github.com/Majella/ab32fe0151fd487da3f6
UPDATE:
As you can see the x-axis line is only showing sporadically and some of the lines aren't lined up with the y-axis.
Updated gist:
https://gist.github.com/Majella/ab32fe0151fd487da3f6
UPDATE 2: For some bizarre reason the lines are changing colour - or moving completely not exactly sure. So while on graph above the lines are from top - blue, orange then white - when graph updating the blue might move to bottom with orange on top and white in middle etc - but happening randomly?
In your original drawing of the graph, you correctly use:
var parameter = svg.selectAll(".parameter")
.data(data, function(d) { return d.key; })
.enter().append("g")
.attr("class", "parameter");
which joins the data (data) to the elements (g.parameter)
During your update function, you will need to join the data again in order to perform updates, deletes, and adds of elements. The 3 little circles tutorial is an excellent place to learn more about this.
Anyway, in your update function, you may want something like this (untested):
// re-acquire joined data
var containers = svg.selectAll("g.parameter")
.data( data );
// update existing elements for this data
containers
.select( "path.line" )
.attr( "d", function(d) { return line(d.values); })