I've downloaded a shapefile of the Spanish provinces from the website of the Spanish Ministry of Agriculture:
http://servicios2.marm.es/sia/visualizacion/descargas/mapas.jsp
Then I've used mapshaper, with "repairing points" and exporting it as shapefile.
Next, I've put the .shp and the .shx files in a folder, with the original .dbf and I've converted it to json with ogr2ogr, exactly as Scott Murray explains in his book. http://chimera.labs.oreilly.com/books/1230000000345/ch12.html
The result is not a map, but a black box:
http://bl.ocks.org/murtra/raw/6150452a9ecca742e95c/
I can't understand why, because the GeoJSON has the same structure as other shapefiles I've used before, and if I inspect the svg, the paths are there. I've tried with another projections/scales, but nothing changes... any idea?
NOTE: changing the provincias.json by ESP_adm2.json (downloaded from http://www.gadm.org/country), the map works. All the files can be found here: https://gist.github.com/murtra/6150452a9ecca742e95c
Chances are you're seeing black boxes because of the specific way your shapes are encoded.
Try changing the styles of drawn paths for "fill" and "stroke" to none and black.
Hope this helps,
ioniATX
My issue was due to having an id attr mentioned instead of a class. Could be a similar issue:
async ngOnInit() {
this.topo = await this._geoService.getUSTopoJson();
const projection = d3.geoAlbersUsa().scale(1070).translate([ this._width / 2, this._height / 2 ]);
const path = d3.geoPath().projection(projection);
this._svg = d3.select('svg')
.attr('width', this._width)
.attr('height', this._height);
this._svg.append('rect')
.attr('class', 'background')
.attr('width', this._width)
.attr('height', this._height);
const g = this._svg.append('g');
g.append('g')
.attr('class', 'states') // <-- Needed to be 'class' instead of 'id'
.selectAll('path')
.data(topojson.feature(this.topo, this.topo.objects.states).features)
.enter().append('path')
.attr('d', path);
g.append('path')
.datum(topojson.mesh(this.topo, this.topo.objects.states, (a, b) => a !== b ))
.attr('id', 'state-borders')
.attr('d', path);
}
Related
I'am working on a bubble-chart using d3. Now there should be an arrow as a graphical asset below the text elements. What I wan to achieve is a dynamic positioning of the arrow-group having a defined gap between itself and the last text:
I've already tried to position it with a percentage value:
arr.append("g").attr("class", "arrow").style('transform', 'translate(-1%, 5%)');
Which does not give the effect I want to.
I also tried to insert a dynamic value based on the radius of the circles which is simply not working and I don't know why:
arr.append("g")
.attr("class", "arrow")
.attr('transform', function(d, i) { return "translate(20," + d.r / 2 + ")");});
OR
arr.append("g")
.attr("class", "arrow")
.style('transform', (d, i) => 'translate(0, ${d.r/2})');
Please find my pen here.
Thank you very much in advance!
Ok.. solved it! For everyone who is interested or having the same trouble:
My last attempt was nearly correct but I was not able to transform via .style(...). I had to use .attr(...) like this:
arr.append("g")
.attr("class", "arrow")
.attr('transform', (d, i) => translate(0, ${d.r/2})');
Update
I have made some progress and updated my question in light of my new understanding of topojson's functions.
To achieve my previous goal I used a method similar to this example. Namely, I hard-coded the FIPS id's of the counties I wished to merge into a set then created a FeatureCollection to use for creating my paths. Here is one example for northern Texas:
var set1 = d3.set([
48111, 48421, 48195, 48357, 48295, 48205, 48341, 48233, 48393, 48211,
48359, 48375, 48065, 48179, 48483, 48485, 48077, 48337, 48237, 48009,
48503, 48023, 48269, 48125, 48107, 48303, 48219, 48079, 48501, 48445,
48305, 48169,
48117, 48381, 48011, 48129, 48087, 48369, 48069, 48437, 48045, 48191,
48075, 48017, 48279, 48189, 48153, 48345, 48101, 48155, 48197, 48487
]);
var region1 = {type: "FeatureCollection", features: counties.filter(function(d) {return set1.has(d.id); })};
countiesG.append("path")
.datum(region1)
.attr("class", "region")
.attr("d", path);
countiesG.append("path")
.datum(topojson.merge(us, us.objects.counties.geometries.filter(function(d) {return set1.has(d.id); })))
.attr("class", "region-boundary")
.attr("d", path);
var set2 = d3.set([
17035, 17032, 17038, 17083, 17022, 17062
]);
var region2 = {type: "FeatureCollection", features: counties.filter(function(d) {return set2.has(d.id); })};
countiesG.append("path")
.datum(region2)
.attr("class", "region")
.attr("d", path);
countiesG.append("path")
.datum(topojson.merge(us, us.objects.counties.geometries.filter(function(d) {return set2.has(d.id); })))
.attr("class", "region-boundary")
.attr("d", path);
I will just provide two region's code above for reference. In reality, I repeated this process many times to create all of my desired merged conglomerate regions. Hard-coding all of this data was arduous and makes my code less readable. My hope is that the hard work is behind me, and I can somehow use these new merged multi-polygon regions in the future.
My question is: Now that I have hard-coded every region, can topojson somehow save the coordinates of these new merged multipolygons into the original us.json file or even a new json file? That way when I parse the json's coordinates it will have all of my custom merged regions -- which would allow me to delete all my hard-coded regions. I'm open to other approaches as well, as long as I can achieve my goal of exporting the merged multipolygon boundary coordinates.
I want to create a new world map and using D3.js with topojson and if possible QGIS. So at the end I need one shapefile that contains countrys, rivers, and so on. After creating all these layers I want that file converted by mapshaper so that the outcome is a topojson. So far so good.
I tried it with a simple polygon, so it was just one layer. I created the shapefile and than converted it to topojson. Now I wanted to use that file with d3:
{"type":"Topology","transform":{"scale":[0.8423423423423423,0.808252427184466],"translate":[1491.6423611111095,-3184.7916666666715]},"objects":{"country":{"type":"GeometryCollection","geometries":[{"type":"LineString","arcs":[0]}]}},"arcs":[[[437,17],[-7,87],[69,64],[23,74],[-31,80],[-14,54],[19,69],[-9,18],[-45,-35],[-53,-32],[-33,-12],[-26,-38],[-26,-27],[-29,-15],[-54,-12],[-29,22],[-40,27],[-31,10],[-36,35],[-2,30],[-17,57],[-14,19],[-19,28],[-31,62],[-2,47],[7,39],[17,40],[28,57],[119,37],[180,22],[247,-2],[105,-109],[78,-122],[52,-96],[55,-176],[-12,-109],[-47,-69],[-60,-77],[-104,-57],[-181,-7],[-49,30]]]}
This is just one polygon, that's right. This is my d3 code:
var width = 960,
height = 500;
var path = d3.geo.path();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("file.json", function(error, topology) {
console.clear();
var featureCollection = topojson.feature(topology, topology.objects.country);
var bounds = d3.geo.bounds(featureCollection);
var centerX = d3.sum(bounds, function(d) {return d[0];}) / 2,
centerY = d3.sum(bounds, function(d) {return d[1];}) / 2;
var projection = d3.geo.mercator()
.scale(3000)
.center([centerX, centerY]);
path.projection(projection);
svg.selectAll("path")
.data(featureCollection.features)
.enter().append("path")
.attr("d", path);
});
But I always get "TypeError: t is undefined". But if I use a json file from someone else it's working. So how can I get this small example running? Is my QGIS bugging or maybe am I using it wrong? Thanks.
Or even do you know a better workaround for this?
Simply remove
.scale(3000)
.center([centerX, centerY])
and it should work. Or edit it to correct values.
I'm basically using a modified version of : http://dimplejs.org/advanced_examples_viewer.html?id=advanced_bar_labels .
I'd like to be able to add for each value a border on the left as high as the value (with a specific color for that border).
I'm not really sure where to start for adding that.
Any ideas?
Thanks.
More details : This is what I'd like to obtain : https://dl.dropboxusercontent.com/u/2227188/Image%202.png - the border on the left is the issue. (jsfiddle.net/mkzTk/5/ this what I currently have which is pretty much what's in the example - I don't know where to start really for adding a border)
You could append a rectangle after drawing for each element of the series as follows:
mySeries.afterDraw = function (s, d) {
var shape = d3.select(s);
svg.append("rect")
.attr("x", shape.attr("x"))
.attr("y", shape.attr("y"))
.attr("height", shape.attr("height"))
.attr("width", "10px")
.style("fill", shape.style("stroke"))
.style("pointer-events", "none");
};
The example you mention already uses the afterDraw function so just add the contents above to the existing method for labelling.
It looks nice, here's an example:
http://jsbin.com/lorin/9/edit?js,output#J:L20
I would set up each bar + edge pair as its own group based on a certain data point, and then append two rect elements to that group. Differences in color can be used to give them their distinctive colors.
Your code would look something like this:
var monthBars = d3.selectAll('.monthBar') //These will be for each chart
.data(allMyData, idFunction) //Assign and key your data
.enter()
.append('g')
.classed('monthBar', true);
.each(function(d){
var taskGroups = d3.select(this).selectAll('.taskGroup')
.data(d.dataForThisMonth, taskIdFn)
.enter()
.append('g')
.classed('.taskGroup', true);
.attr('transform', ...) //Define the x and y positioning for the group
taskGroups.append('rect')
//Make this the 'body' rect with the text in it
taskGroups.append('rect')
//Make this the edge rect
})
I try to use d3.scale.category.20b() to generate a color scale, problem is whatever number of the list I ask for, it always returns first element of the list.
var color = d3.scale.category20b();
console.log(color(X));
OR
console.log(d3.scale.category20b()(X);
No matter what X is, it always logs #393b79 which is the first elements, according to the d3 API
This can happen because categorical scales in d3 append to the domain as new data comes in. If every enter() creates a new categorical scale, the domain of the categorical scale remains the same.
As an example, please consider this jFiddle: http://jsfiddle.net/seldomawake/MV55j/1/
Here, we see that as data enters, we append to a categorical scale in the global namespace, $colorScale (specific code below).
function redraw(theData) {
var localColorScale = d3.scale.category20c(); //< NOT USED HERE
var svg = d3.select("svg");
var circles = svg.selectAll("circle")
.data(theData).enter().append("circle")
var circleAttributes = circles.attr("cx", getRandomInt(50, 450))
.attr("cy", getRandomInt(50, 450))
.attr("r", function (d) { return d.value; })
.style("fill", function () { return $colorScale(getRandomInt(0, 19)); });
}
However, if we were to replace return $colorScale(getRandomInt(0, 19)) with return localColorScale(getRandomInt(0, 19)), we would no longer have the data append to the range of the categorical scale, and which would result in a single-color output.
Edit: fixing URL to jsfiddle.
At first I thought this would have been a bug with D3.js so created this jsfiddle which works fine.
var data = d3.range(0,20);
var color = d3.scale.category20b();
d3.select('.target').selectAll('div')
.data(data)
.enter()
.append('div')
.text(function(d){return color(d);})
.attr('style', function(d){return "background-color:"+ color(d) + ";" ;})
It had been raised by others about version of D3 you are using. This looks unlikely to be the cause of your issue as the code in question has hardly been touched. If the code has not been touched much and others have no issue it raises the question of browser compatibly. I sent my jsfiddle to browsershots and did not see any browser output a single block of color instead of the expected pretty color stripes.
After all this it seams there is not enough information to properly answer your problem. I suggest you have a look to see if X is really changing by making a small change to the code console.log({'color':color(X), 'x':X}).
Which version of D3 are you using? I wrote a jsFiddle (D3 3.0.4), the colors are shown normally:
var color = d3.scale.category20b();
var svg = d3.select('#chart').append('svg')
.attr('width', 200)
.attr('height', 100);
svg.append('rect')
.attr('width', 100)
.attr('height', 100)
.attr('fill', color(0));
svg.append('rect')
.attr('x', 100)
.attr('width', 100)
.attr('height', 100)
.attr('fill', color(1));
The result is: