D3.js svg does not draw Map - javascript
I am trying to draw maps using D3.js. The GeoJson file is converted from shapefile and stored in the project folder.
The GeoJson data format:
{"type":"FeatureCollection", "features": [{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[31.287890625000017,-22.40205078125001],[31.429492187500017,-22.298828125],[31.57148437500001,-22.15351562500001],[31.737695312500023,-21.9833984375],[31.88593750000001,-21.83154296875],[32.01630859375001,-21.698046875000003],[32.19472656250002,-21.515429687500003],[32.37109375,-21.33486328125001],[32.41240234375002,-21.311816406250003],[32.429785156250006,-21.29707031250001],[32.353613281250006,-21.136523437500003],[32.476171875000006,-20.95009765625001],[32.48281250000002,-20.828906250000003],[32.477636718750006,-20.712988281250006],[32.49238281250001,-20.659765625000006],[32.529296875,-20.613085937500003],[32.67255859375001,-20.51611328125],[32.780859375000006,-20.36152343750001],[32.86962890625,-20.21718750000001],[32.992773437500006,-19.98486328125],[33.0048828125,-19.93017578125],[33.00673828125002,-19.873828125000003],[32.97265625,-19.79541015625],[32.89042968750002,-19.668066406250006],[32.83076171875001,-19.558203125000006],[32.77763671875002,-19.388769531250006],[32.83095703125002,-19.24140625000001],[32.85000000000002,-19.152441406250006],[32.84980468750001,-19.10439453125001],[32.826171875,-19.05878906250001],[32.766210937500006,-19.02431640625001],[32.71650390625001,-19.00185546875001],[32.69970703125,-18.94091796875],[32.69921875,-18.868457031250003],[32.72197265625002,-18.828417968750003],[32.8544921875,-18.763671875],[32.88457031250002,-18.728515625],[32.90029296875002,-18.689062500000006],[32.90166015625002,-18.632910156250006],[32.942480468750006,-18.49267578125],[32.99306640625002,-18.35957031250001],[32.99638671875002,-18.312597656250006],[32.978515625,-18.271484375],[32.96464843750002,-18.1962890625],[32.95556640625,-18.08291015625001],[32.954687500000006,-17.765429687500003],[32.98076171875002,-17.4375],[32.969335937500006,-17.251562500000006],[32.884375000000006,-17.03779296875001],[32.87626953125002,-16.883593750000003],[32.93789062500002,-16.775976562500006],[32.94804687500002,-16.71230468750001],[32.902929687500006,-16.704199218750006],[32.81025390625001,-16.69765625000001],[32.741796875000006,-16.67763671875001],[32.635839843750006,-16.589453125000006],[32.45195312500002,-16.515722656250006],[32.243261718750006,-16.44873046875],[31.939843750000023,-16.428808593750006],[31.687597656250006,-16.214160156250003],[31.489843750000006,-16.1796875],[31.426171875000023,-16.15234375],[31.236230468750023,-16.02363281250001],[30.938769531250017,-16.01171875],[30.630175781250017,-15.999218750000011],[30.437792968750017,-15.995312500000011],[30.40937500000001,-15.978222656250011],[30.39814453125001,-15.80078125],[30.396093750000006,-15.64306640625],[30.25068359375001,-15.643457031250009],[29.994921875000017,-15.64404296875],[29.729589843750006,-15.644628906250006],[29.4873046875,-15.69677734375],[29.287890625000017,-15.776464843750006],[29.050585937500017,-15.901171875000003],[28.973046875000023,-15.950097656250009],[28.9130859375,-15.98779296875],[28.875585937500006,-16.0361328125],[28.856738281250017,-16.14228515625001],[28.856738281250017,-16.30615234375],[28.83271484375001,-16.424121093750003],[28.760546875000017,-16.53212890625001],[28.760644531250023,-16.53193359375001],[28.399804687500023,-16.66279296875001],[28.16376953125001,-16.76972656250001],[27.932226562500006,-16.89619140625001],[27.75654296875001,-17.060351562500003],[27.63671875,-17.26210937500001],[27.437890625000023,-17.51191406250001],[27.235742187500023,-17.728320312500003],[27.020800781250017,-17.95839843750001],[26.779882812500006,-18.04150390625],[26.577539062500023,-18.022558593750006],[26.333398437500023,-17.929296875000006],[26.139550781250023,-17.911718750000006],[25.995898437500017,-17.969824218750006],[25.86328125,-17.951953125000003],[25.741601562500023,-17.858203125000003],[25.6396484375,-17.82412109375001],[25.55712890625,-17.84951171875001],[25.451757812500006,-17.84511718750001],[25.2587890625,-17.793554687500006],[25.239062500000017,-17.843066406250003],[25.224023437500023,-17.91523437500001],[25.242285156250006,-17.969042968750003],[25.28242187500001,-18.04121093750001],[25.340234375000023,-18.1044921875],[25.384375000000006,-18.14199218750001],[25.43671875000001,-18.234960937500006],[25.4892578125,-18.35126953125001],[25.55830078125001,-18.44179687500001],[25.76123046875,-18.649218750000003],[25.78369140625,-18.72353515625001],[25.811914062500023,-18.79707031250001],[25.939355468750023,-18.93867187500001],[25.95917968750001,-18.985644531250003],[25.95068359375,-19.08173828125001],[26.081933593750023,-19.369921875000003],[26.168066406250006,-19.53828125000001],[26.241015625000017,-19.5693359375],[26.474609375,-19.748632812500006],[26.67822265625,-19.89277343750001],[26.91669921875001,-19.99013671875001],[27.091796875,-20.05419921875],[27.17822265625,-20.10097656250001],[27.221484375000017,-20.145800781250003],[27.256738281250023,-20.232031250000006],[27.27460937500001,-20.3818359375],[27.28076171875,-20.47871093750001],[27.46894531250001,-20.47480468750001],[27.624609375000006,-20.48359375000001],[27.679296875000006,-20.503027343750006],[27.699609375000023,-20.53066406250001],[27.69482421875,-20.594531250000003],[27.69697265625001,-20.689746093750003],[27.70429687500001,-20.766406250000003],[27.688085937500006,-20.84833984375001],[27.67695312500001,-20.94482421875],[27.66943359375,-21.064257812500003],[27.693457031250006,-21.11103515625001],[27.844140625000023,-21.261523437500003],[27.90742187500001,-21.35908203125001],[27.974609375,-21.50673828125001],[28.014062500000023,-21.55419921875],[28.04560546875001,-21.573046875000003],[28.181640625,-21.58935546875],[28.532031250000017,-21.65126953125001],[28.74775390625001,-21.707617187500006],[28.919335937500023,-21.76601562500001],[28.99072265625,-21.78144531250001],[29.02558593750001,-21.796875],[29.03730468750001,-21.811328125000003],[29.01582031250001,-21.93994140625],[29.023339843750023,-21.981250000000003],[29.042382812500023,-22.018359375000003],[29.07148437500001,-22.047460937500006],[29.106835937500023,-22.065722656250003],[29.237207031250023,-22.07949218750001],[29.315234375000017,-22.15771484375],[29.364843750000006,-22.193945312500006],[29.37744140625,-22.19277343750001],[29.6630859375,-22.146289062500003],[29.90234375,-22.184179687500006],[30.1904296875,-22.291113281250006],[30.46015625000001,-22.32900390625001],[30.71162109375001,-22.2978515625],[30.916113281250006,-22.29072265625001],[31.07343750000001,-22.30781250000001],[31.197265625,-22.34492187500001],[31.287890625000017,-22.40205078125001]]]},"properties":{"featurecla":"Admin-0 country","scalerank":1,"LABELRANK":3,"SOVEREIGNT":"Zimbabwe","SOV_A3":"ZWE","ADM0_DIF":0,"LEVEL":2,"TYPE":"Sovereign country","ADMIN":"Zimbabwe","ADM0_A3":"ZWE","GEOU_DIF":0,"GEOUNIT":"Zimbabwe","GU_A3":"ZWE","SU_DIF":0,"SUBUNIT":"Zimbabwe","SU_A3":"ZWE","BRK_DIFF":0,"NAME":"Zimbabwe","NAME_LONG":"Zimbabwe","BRK_A3":"ZWE","BRK_NAME":"Zimbabwe","BRK_GROUP":"","ABBREV":"Zimb.","POSTAL":"ZW","FORMAL_EN":"Republic of Zimbabwe","FORMAL_FR":"","NAME_CIAWF":"Zimbabwe","NOTE_ADM0":"","NOTE_BRK":"","NAME_SORT":"Zimbabwe","NAME_ALT":"","MAPCOLOR7":1,"MAPCOLOR8":5,"MAPCOLOR9":3,"MAPCOLOR13":9,"POP_EST":13805084,"POP_RANK":14,"GDP_MD_EST":28330,"POP_YEAR":2017,"LASTCENSUS":2002,"GDP_YEAR":2016,"ECONOMY":"5. Emerging region: G20","INCOME_GRP":"5. Low income","WIKIPEDIA":-99,"FIPS_10_":"ZI","ISO_A2":"ZW","ISO_A3":"ZWE","ISO_A3_EH":"ZWE","ISO_N3":"716","UN_A3":"716","WB_A2":"ZW","WB_A3":"ZWE","WOE_ID":23425004,"WOE_ID_EH":23425004,"WOE_NOTE":"Exact WOE match as country","ADM0_A3_IS":"ZWE","ADM0_A3_US":"ZWE","ADM0_A3_UN":-99,"ADM0_A3_WB":-99,"CONTINENT":"Africa","REGION_UN":"Africa","SUBREGION":"Eastern Africa","REGION_WB":"Sub-Saharan Africa","NAME_LEN":8,"LONG_LEN":8,"ABBREV_LEN":5,"TINY":-99,"HOMEPART":1,"MIN_ZOOM":0,"MIN_LABEL":3,"MAX_LABEL":8,"NE_ID":1159321441,"WIKIDATAID":"Q954","NAME_AR":"زيمبابوي","NAME_BN":"জিম্বাবুয়ে","NAME_DE":"Simbabwe","NAME_EN":"Zimbabwe","NAME_ES":"Zimbabue","NAME_FR":"Zimbabwe","NAME_EL":"Ζιμπάμπουε","NAME_HI":"ज़िम्बाब्वे","NAME_HU":"Zimbabwe","NAME_ID":"Zimbabwe","NAME_IT":"Zimbabwe","NAME_JA":"ジンバブエ","NAME_KO":"짐바브웨","NAME_NL":"Zimbabwe","NAME_PL":"Zimbabwe","NAME_PT":"Zimbábue","NAME_RU":"Зимбабве","NAME_SV":"Zimbabwe","NAME_TR":"Zimbabve","NAME_VI":"Zimbabwe","NAME_ZH":"辛巴威"}},
My code:
<svg id="mapSVG" width="560"; height="350" style="border: solid 1px black;"></svg>
<script src="js/d3.js"></script>
<script src="js/d3.min.js"></script>
<script type="text/javascript">
//Setting svg width and attributes
var svg = d3.select("svg");
var width = +svg.attr("width");
var height = +svg.attr("height");
d3.json("Resources/countries.json").then(function (json) {
var projection = d3.geoMercator().fitSize([width, height], json);
//Projections
var geoPath = d3.geoPath().projection(projection);
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", geoPath)
.style("fill", "steelblue")
})
Problems: The SVG is completely empty. There is no error log in the console and I tried to output the json and it is actually getting the data.
When I inspect the svg, the size of the whole svg is 560x350, so I tried to play with the translate and center, but had no luck with it.
#AndrewReid was correct. It was a winding problem and fortunately there is a simple way to fix it using turf.js
d3.json(path).then(function (json) {
var projection = d3.geoMercator();
var features = json.features;
var fixed = features.map(function (feature) {
return turf.rewind(feature, { reverse: true });
})
//Projections
var geoPath = d3.geoPath().projection(projection);
projection.fitSize([width, height], { "type": "FeatureCollection", "features": fixed })
svg.selectAll("path")
.data(fixed)
.enter()
.append("path")
.attr("d", geoPath)
Related
Choropleth map using D3.js and CSV file
I am using d3.js in a project for university where I have to visualize the world Choropleth world MAP passing the data to color the country form a file csv internally built this way: nationality,victims Austria,4500 France,1345 China,16000000 Italy,452345 Hungary,70000 and so on. Until I create only the world map it works and a map will show on the web. When I add the d3.csv part to read data it shows nothing. I attached the code below: function mapRender(){ var width = 900; var height = 550; //Select the div to show the maps var svg = d3.select("#map") .append("svg") .attr("width", width) .attr("height", height) .append('g') //Setting the color domains for the choropleth maps var color = d3.scaleThreshold() .domain([0, 50000, 100000, 150000, 200000, 500000, 1000000, 5000000, 10000000, 20000000]) .range(["#ffff00", "#FFF933", "#DCFF33", "#97FF33", "#40FF33", "#33FFA7", "#33FFFB", "#33AAFF", "#336EFF"]); var projection = d3.geoMercator() .scale(125) .translate([width / 2, height / 1.4]); var path = d3.geoPath().projection(projection); d3.json("https://unpkg.com/world-atlas#1/world/110m.json", function (error, world) { if (error) throw error; //Load data from CSV file d3.csv("../datasets/csv/choropleth.csv", function (data) { var dataByName = {}; data.forEach(function (d) { dataByName[d.nationality] = +d.victims; }); svg.append("path") .datum(topojson.feature(world, world.objects.land)) .append("path") .attr("d", path) .attr("fill", function(d) { return color(dataByName[d.nationality]); }); // .attr("fill", "#000000") // .attr("d", path); }) }) } Anyone that can help me to understand why it doesn't work?
Choropleth Not Displaying
I'm working on a project in d3.js, and I can't seem to make my map work. Every time I run it there are no syntax errors, but there's only a blank screen. Here's the code: <svg id="my_dataviz" width="1000" height="1000"></svg> <script> // The svg var svg = d3.select("svg"), width = +svg.attr("width"), height = +svg.attr("height"); // Map and projection var path = d3.geoPath(); var projection = d3.geoMercator() .scale(10000) .center([30, -85]) .translate([width / 2, height / 2]); // Data and color scale var data = d3.map(); var colorScale = d3.scaleThreshold() .domain([-5000, -3000, -1000, 1000, 3000, 5000]) .range(d3.schemeReds[7]); // Load external data and boot d3.queue() .defer(d3.json, "G2.geojson") .defer(d3.csv, "ps.csv", function(d) { data.set(d.GEOID20, +d.PRE_SC); }) .await(ready); function ready(error, topo) { // Draw the map svg.append("g") .selectAll("path") .data(topo.features) .enter() .append("path") // draw each country .attr("d", d3.geoPath() .projection(projection) ) // set the color of each country .attr("fill", function (d) { d.total = data.get(d.id) || 0; return colorScale(d.total); }); } </script> </body> </html> I made sure that my projection was WGS 84. If I have any errors in the code please let me know! Thank you all!
How to make to drawing a SVG with D3.js?
I'm trying to draw a map in an SVG with the GEOJSON data that I bring from this API, the SVG paths are filled with the data, however, the SVG is blank, as can be seen when executing the code below. Notice the document.write, the data is returned correctly. var svg = d3.select("svg") d3.json('https://api.mocki.io/v1/d214eb47') .then(data => { svg.append('g') .selectAll('path') .data(data.features) .enter() .append('path') .attr('d', d3.geoPath().projection(d3.geoMercator())) document.write(JSON.stringify(data)); }) <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.0/d3.min.js"></script> <svg width="600" height="600"></svg> I tested it with another GEOJSON file, and managed to draw in SVG as seen when executing the code below: const link = "https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson"; var svg = d3.select("svg") d3.json(link) .then(data => { svg.append("g") .selectAll("path") .data(data.features) .enter() .append("path") .attr("d", d3.geoPath() .projection(d3.geoMercator()) ) //document.write('data ', JSON.stringify(data)) }); <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.0/d3.min.js"></script> <svg width="600" height="600"></svg> Does anyone know what's wrong with the first code snippet?
There is no issue in the data, the problem here is just that you're trying to map a very small region (San Francisco), unlike the world map in your second example. That said, you should set the scale and the center of the projection. In your case: const projection = d3.geoMercator() .scale(100000) .center([-122.3, 37.75]) The resulting code: var svg = d3.select("svg"); const projection = d3.geoMercator() .scale(100000) .center([-122.3, 37.75]) d3.json('https://api.mocki.io/v1/d214eb47') .then(data => { svg.append('g') .selectAll('path') .data(data.features) .enter() .append('path') .attr('d', d3.geoPath().projection(projection)) }) path { fill: wheat; stroke: darkslateblue; } <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.0/d3.min.js"></script> <svg width="600" height="600"></svg> But it's way easier just using fitExtent with your SVG width and height: const projection = d3.geoMercator() .fitExtent([ [0, 0], [600, 600] ], data) And here is the resulting code: var svg = d3.select("svg"); d3.json('https://api.mocki.io/v1/d214eb47') .then(data => { const projection = d3.geoMercator() .fitExtent([ [0, 0], [600, 600] ], data) svg.append('g') .selectAll('path') .data(data.features) .enter() .append('path') .attr('d', d3.geoPath().projection(projection)) }) path { fill: wheat; stroke: darkslateblue; } <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.0/d3.min.js"></script> <svg width="600" height="600"></svg>
Your GeoJSON file has very small areas that are displayed but with practically zero size shapes in given projection and default SVG viewBox. Check how I've found the boundaries of the SVG G element with your geodata. I then used that to define SVG viewBox attribute. If SVG width or height are not set then SVG will expand to available "container" size. So you can add a surrounding DIV element and set its size if you wish (fitting SVG in your container and thus in your layout). UPDATE: you can even change the SVG viewBox attribute later with javascript by using returned values from the getBBox() function. The other possibility in another answer is of course great, too (fitting extent of your SVG). var svg = d3.select("svg"); const link = "https://api.mocki.io/v1/d214eb47"; //const link = "https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson"; d3.json(link) .then(data => { svg.append('g') .selectAll('path') .data(data.features) .enter() .append('path') .attr('d', d3.geoPath().projection(d3.geoMercator())); //console.log(data.features); console.log(document.getElementsByTagName("g")[0].getBBox()); }) <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.6.0/d3.min.js"></script> <svg viewBox="152.95315551757812 140.7493133544922 0.421722412109375 0.4228363037109375"></svg>
Why is my choropleth coming out all the same colour?
This is my first question on here so please bear with. I'm trying to make a choropleth (a map where different sections are coloured in based on some value assigned to them) using d3.js. I'm using the example given at https://www.d3-graph-gallery.com/graph/choropleth_basic.html, but changing the map to one of Scotland and changing the values to population density. When I run it, I get a map but it's all coloured in the same shade of blue. I've tried changing the domain of colorScale but to no avail. This is what I've got at the minute: // The svg var svg = d3.select("svg"), width = +svg.attr("width"), height = +svg.attr("height"); // Map and projection var path = d3.geoPath(); var projection = d3.geoNaturalEarth() .scale(20 * width / Math.PI) .translate([width / 2 + 150, height / 2 + 2500]); // Data and color scale var data = d3.map(); var colorScale = d3.scaleThreshold() .domain([0, 600]) .range(d3.schemeBlues[7]); // Load external data and boot d3.queue() .defer(d3.json, "https://raw.githubusercontent.com/squirrel-star/scotland/main/geojsonscotlandladjson.geojson") .defer(d3.csv, "https://raw.githubusercontent.com/squirrel-star/scotland/main/scotlanddensitywithid.csv", function(d) { data.set(d.code, +d.density); }) .await(ready); function ready(error, topo) { console.log(data); // Draw the map svg.append("g") .selectAll("path") .data(topo.features) .enter() .append("path") // draw each country .attr("d", d3.geoPath() .projection(projection) ) // set the color of each country .attr("fill", function(d) { d.total = data.get(d.id) || 0; return colorScale(d.total); }); } <script src="https://d3js.org/d3.v4.js"></script> <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> <script src="https://d3js.org/d3-geo-projection.v2.min.js"></script> <svg id="my_dataviz" width="400" height="400"></svg> Any suggestions for fixing it would be greatly appreciated. Thank you!
I think you may have misread the documentation for d3.scaleThreshold, because it says you need to have N values in your domain if you have N + 1 values in your range. In your case, that makes N = 6. Also, d.id didn't exist. I used d.properties.LAD13NM instead, because that field contained the name of the relevant county. Finally, there was no need to use a map, since you were only using it as an object of some sorts, so I just replaced it with a regular object. // The svg var svg = d3.select("svg"), width = +svg.attr("width"), height = +svg.attr("height"); // Map and projection var path = d3.geoPath(); var projection = d3.geoNaturalEarth() .scale(20 * width / Math.PI) .translate([width / 2 + 150, height / 2 + 2500]); // Data and color scale var data = {}; var colorScale = d3.scaleThreshold() .domain([100, 200, 300, 400, 500, 600]) .range(d3.schemeBlues[7]); // Load external data and boot d3.queue() .defer(d3.json, "https://raw.githubusercontent.com/squirrel-star/scotland/main/geojsonscotlandladjson.geojson") .defer(d3.csv, "https://raw.githubusercontent.com/squirrel-star/scotland/main/scotlanddensitywithid.csv", function(d) { data[d.code] = +d.density; }) .await(ready); function ready(error, topo) { // Draw the map svg.append("g") .selectAll("path") .data(topo.features) .enter() .append("path") // draw each country .attr("d", d3.geoPath() .projection(projection) ) // set the color of each country .attr("fill", function(d) { d.total = data[d.properties.LAD13NM] || 0; return colorScale(d.total); }); } <script src="https://d3js.org/d3.v4.js"></script> <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> <script src="https://d3js.org/d3-geo-projection.v2.min.js"></script> <svg id="my_dataviz" width="400" height="400"></svg>
How to control the order of the layers on a map in d3js
I have a simple question about making maps with d3js. I want to make a map with two layers. One layer is the contour of the map (geoJson) and the other layer contains the streets (topoJson). My problem is that streets layer always is loaded in front of the layer of contour, no matter which one is written before in the code. I would like the opposite situation: the contour layer in front of the streets. I think this problem happen because both request are asynchronous and the contour layer is loaded before because is the lightest. Here is the code... //Width and height var w = 900; var h = 500; //Define map projection var projection = d3.geo.transverseMercator() .center([2.5, -34.65]) .rotate([61.5, 0]) .scale((h * 750.5) / 3) .translate([(w/2), (h/2)]); //Define path generator var path = d3.geo.path() .projection(projection); //Create SVG element var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); //Load in TopoJSON data d3.json("streets.json", function(error, topology) { svg.selectAll("path") .data(topojson.feature(topology, topology.objects.layer1).features) .enter() .append("path") .attr("d", path) .style("fill", "white") .style("stroke", "#cccccc"); }); //Load in GeoJSON data d3.json("contour.geojson", function(json) { //Bind data and create one path per GeoJSON feature svg.selectAll("path") .data(json.features) .enter() .append("path") .attr("d", path) .style("fill","white") .style("stroke","black") .style("stroke-width",4); }); Is it possible load first the layer of streets, wait until the streets were drawn and then load the layer of contour? Thanks in advance.
I would create two separate g elements for the layers in the beginning. This way you can control their order: var countourG = svg.append("g"), streetG = svg.append("g"); d3.json("streets.json", function(error, topology) { streetG.selectAll("path")... }); d3.json("contour.json", function(error, topology) { contourG.selectAll("path")... });