I'm trying to plot a circle on a map using d3 and topojson, but I can't figure it out. Here is my code
var width = 960,
height = 500;
var projection = d3.geo.mercator()
.center([0, 5 ])
.scale(900)
.rotate([-180,0]);
var svg = d3.select("#map").append("svg")
.attr("width", width)
.attr("height", height)
.attr("class", "svg-map");
var path = d3.geo.path()
.projection(projection);
var g = svg.append("g");
d3.json("/static/lib/topojson/world-110.json", function(error, topology) {
svg.append("path")
.datum(topojson.feature(topology, topology.objects.countries))
.attr("d", d3.geo.path().projection(d3.geo.mercator()));
var coordinates = projection([10,20])
svg.append('svg:circle')
.attr('cx', coordinates[0])
.attr('cy', coordinates[1])
.attr('r', 5);
});
The issue you're seeing (or not seeing) is because you have defined a projection in this line:
var projection = d3.geo.mercator()
.center([0, 5 ])
.scale(900)
.rotate([-180,0]);
Which you use to transform your coordinates. Then, the projections for paths of the topojson data are set with this:
.attr("d", d3.geo.path().projection(d3.geo.mercator()));
So, the topojson renders using a mercator projection with no transforms where as your circle renders using a mercator projection that has been transformed.
I'm not entirely sure how you want you're final map to look, but assuming that it's a world map with a dot in Africa just comment out the centre, scale and rotate lines in the projection definition as in:
var projection = d3.geo.mercator();
//.center([0, 5 ])
//.scale(900)
//.rotate([-180,0]);
And replace the d3.geo.path in the svg.append block with path as in:
.attr("d", d3.geo.path().projection(d3.geo.mercator()));
to
.attr("d", path);
Oh, and if you haven't you'll need to give the circle some fill, so something like
.style("fill", "red")
Related
I have a projection of the world-50m.json file working, however when I fill it with a color there are several countries that are cut off on the edges which create horizontal fill sections/lines across the entire map.
This is actually visible on the d3-geo example projection here: https://github.com/d3/d3-geo/blob/master/test/data/world-50m.json
Is there another JSON file without these cutoff countries? Or perhaps I could omit specific polygons from my fill? Not quite sure how I'd locate every country/shape with the issue. While most are tiny and wouldn't be missed if omitted, a major one appears to be Russia.
Here is my code for reference:
var w = 960,
h = 660,
active = d3.select(null);
var projection = d3.geoMercator()
.scale(150)
.translate([w/2, h/2])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var countries = svg.append("svg:g").attr("id", "countries");
d3.json("world-50m.json", function(error, us) {
mapFeatures = topojson.feature(us, us.objects.countries).features;
mapFeatures.type = "countries";
drawMap();
});
function drawMap() {
countries.selectAll("path")
.data(mapFeatures)
.enter().append("svg:path")
.attr("d", path)
.attr("class", "feature")
.attr("data-id", function(d) { return d.id; })
.style("fill", "blue")
.style("stroke", "white")
.style("stroke-width", ".2px");
};
Any help would be greatly appreciated!
This solution is detailed in the comments above and was achieved with the help of #Andrew Reid and #altocumulus.
The observed issue was an instance of Antimeridian Cutting that was not being properly handled by d3, due to a mismatch of path and projection calls between d3 v3 and d3 v4 syntax.
The issue was solved by changing geo.path() to geoPath() which corrected the mismatch and enabled d3 to render the map correctly with its antimeridian cutting support.
Below is the correct code:
var w = 960,
h = 660,
active = d3.select(null);
var projection = d3.geoMercator()
.scale(150)
.translate([w/2, h/2])
.precision(.1);
var path = d3.geoPath()
.projection(projection);
var countries = svg.append("svg:g").attr("id", "countries");
d3.json("world-50m.json", function(error, us) {
mapFeatures = topojson.feature(us, us.objects.countries).features;
mapFeatures.type = "countries";
drawMap();
});
function drawMap() {
countries.selectAll("path")
.data(mapFeatures)
.enter().append("svg:path")
.attr("d", path)
.attr("class", "feature")
.attr("data-id", function(d) { return d.id; })
.style("fill", "blue")
.style("stroke", "white")
.style("stroke-width", ".2px");
};
I'm wondering how to calculate and determine a right value of projection.scale([value]) for a world map in full screen with D3.js.
The documentation says the default value is 150. But what does this "150" stand for?
I'm trying to draw a world map exact same width of the screen.
I set svg area to window.innerWidth but not sure what value I should give to the scale for the world map.
In some case people get the bounds of the feature they want to fit but I cannot get bounds of whole bounds of the world map.
What the right way to calculate the scale value in this case?
width = window.innerWidth;
height = window.innerHeight;
svg = d3.select("#svgArea").append("svg")
.attr("id", "svg")
.attr("width", width)
.attr("height", height);
projection = d3.geo.equirectangular()
.scale(????)
.translate([width / 2, height / 2])
.precision(.1);
path = d3.geo.path().projection(projection);
d3.json("world-50m.json", function(error, world) {
var land = topojson.feature(world, world.objects.land);
svg.insert("path", ".graticule")
.datum(land)
.attr("class", "land")
.attr("fill", "#aaaaaa")
.attr("stroke", "#000000")
.attr("stroke-width", "0.5px")
.attr("d", path);
})
Thanks in advance.
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 !
I'm trying to replicate http://bl.ocks.org/mbostock/4060606 using a UK Counties map.
I followed the following steps - pretty much what is suggested on http://bost.ocks.org/mike/map:
1) I downloaded the shapefile from Ordnance Survey and extracted some data using qGIS
2) when ready, I translated the shapefile into GeoJSON using ogr2ogr
3) I converted the GeoJSON into topoJSON making sure the IDs were preserved
I pretty much copied the original example for the choropleth from mbostock. However, instead of a nice map, I get a... circle. I wonder if I'm doing some errors with the projection?
For completeness, this is the javascript part of the page:
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.albers()
.center([0, 55.4])
.rotate([4.4, 0])
.parallels([50, 60])
.scale(50)
.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);
queue()
.defer(d3.json, "uk.topo.json")
.defer(d3.tsv, "unemployment.tsv", function(d) { rateById.set(d.id, +d.rate); })
.await(ready);
function ready(error, uk) {
svg.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(uk, uk.objects.counties).features)
.enter().append("path")
// .attr("class", function(d) { return quantize(rateById.get(d.id)); })
.attr("class", "q5-9" )
.attr("d", path);
// svg.append("path")
// .datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
// .attr("class", "states")
// .attr("d", path);
}
d3.select(self.frameElement).style("height", height + "px");
The counties topoJSON is too big to be pasted here, but roughly it's:
{"type":"Topology","objects":{"counties":{"type":"GeometryCollection","bbox":[220956.7,35190.3,655967.1,586683],"geometries":[{"type":"Polygon","properties":{"name":"Buckinghamshire"},"id":11901,"arcs":[[-1,-2,-3,-4,-5,-6]]},{"type":"Polygon","properties":{"name":"Cambridgeshire"},"id":1386,"arcs":[[-7,-8,-9,-10,-11,-12,-13,-14]]},{"type":"Polygon","properties":{"name":"Cumbria"},"id":13244,"arcs":[[-15,-16,-17]]},{"type":"Polygon","properties":{"name":"Derbs"},"id":13688,"arcs":[[-18,-19,-20,-21],[-22]]},...},"arcs":[[[5876,2688],[-67,53],[-21,101],[7,65],[96,66],[-7,66],[-78,69],[-234,12],[-5,42],...},"transform":{"scale":[43.5053905390539,55.15478547854785],"translate":[220956.7,35190.3]}}
I'm not a great expert here so I might be doing something fundamentally wrong. However, I have one certainty:
the UK counties map is correct, as it displays correctly on http://www.mapshaper.org/
Any idea? I'm happy to paste the complete files if needed.
Thanks!
The coordinates seem to be already projected (i.e. cartesian coordinates).
In this case you should use
d3.geo.path().projection(null);
But make sure you scale your topojson first to the desired size
topojson --width=960 --height=500 --margin=10 --cartesian -o out.json -- in.shp
Or reproject the shapefile first using ogr2ogr
ogr2ogr -t_srs EPSG:4326 out.shp in.shp