2

I'm new to d3.js. I'm trying to create this choropleth map from scratch:

enter image description here

My script is showing no errors and in elements tab I can see data but nothing is showing on screen. I have read multiple documentations but still don't know what I'm missing.

jsfiddle: https://jsfiddle.net/jx3hjfgw

    var width = 960,
    height = 1160;

// SVG element as a JavaScript object that we can manipulate later
var svg = d3.select("#map").append("svg")
      .attr("width", width)
      .attr("height", height);

var center = [24.679341, 46.680381];

// Instantiate the projection object
var projection = d3.geo.conicConformal()
      .center(center)
      .clipAngle(180)
          // Size of the map itself, you may want to play around with this in
          // relation to your canvas size
      .scale(10000)
          // Center the map in the middle of the canvas
      .translate([width / 2, height / 2])
      .precision(.1);

// Assign the projection to a path
var path = d3.geo.path().projection(projection);

d3.json("https://code.highcharts.com/mapdata/countries/sa/sa-all.geo.json", function(err, data) {
  $.each(data.features, function(i, feature) {
    svg.append("path")
      .datum(feature.geometry)
      .attr("class", "border")
      .attr("stroke", "black")
      .attr("fill", "blue");
  });
});
Shadin
  • 1,867
  • 5
  • 26
  • 37
  • Your paths have no `d` attribute, so they don't actually draw anything. You need to use d3-geo to convert coordinates from the json data to svg paths – Charlie Martin May 26 '17 at 17:20

3 Answers3

3

There are a couple issues with your map.

The primary issue is that your projection is not WGS84, that is to say it does not comprise of longitude/latitude pairs. The projection of the geojson is specified in your geojson itself:

"crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:EPSG:32638"}}

CRS stands for spatial reference system

This projection (EPSG:32638) is a UTM system. D3 uses unprojected data (or data that is 'projected' to WGS84), points on a three dimensional ellipsoid, not already projected points on a planar grid (like UTM). If the geojson did not indicate what projection it used, you can still tell that it is not WGS84, or longitude latitude pairs, because the coordinates are not valid longitude/latitudes:

...[1348,4717],[1501,4754],[1572,4753]...

You have two options, one is to use unprojected data (or data in WGS84) and build your map around a d3 projection. The other is to use a geo.transform to show your data.

Solution 1

As noted in the other answer, you'll need to use the path function to actually display the data, you also shouldn't need an each loop in d3 to append features

For this solution to work, and it is probably the most straight-forward, you'll need to either re-project/un-project the geojson you have (with some tool other than d3) or, alternatively, you'll have to find an new data source (which is probably not too difficult) with the spatial data represented as long/lat pairs.

Also note that coordinates in geojson and d3 are [long,lat] therefore your centering coordinate of [24.679341, 46.680381] refers to 46.7 degrees North, 24.7 degrees East - this point is not in Saudi Arabia but is in Romania.

Take a look at an example projection suitable for Saudi Arabia here (using a basic geojson - just a country outline) (in d3v4 - slight differences from v3).

Together, that would give you something like:

var projection = d3.geo.mercator()
      .center([46.680381,24.679341])
      .scale(1000)
      .translate([width / 2, height / 2])
      .precision(.1);

var path = d3.geo.path().projection(projection)

d3.json("source.json", function(err, data) {
    svg.selectAll("path")
      .data(data.features)
      .enter()
      .append('path')
      .attr("class", "border")
      .attr("stroke", "black")
      .attr("fill", "blue")
      .attr("d", path);
  });

Note that if you really want to keep the conical projection (d3.geo.conicConformal()), take a look on centering that type of projection here, it's for an albers projection, but the method is the same as they are both conical projections of the same sort.

Solution 2

The other option is to use a geo.transfrom which will translate and scale your data (since it is already planar in this case) to match your desired view. The downside of this approach is that you can't use a lat/long point to indicate anything - the map units will be the projection units and the map units are not degrees longitude or latitude.

The goal is to translate/scale/shift coordinates such as these:

...[1348,4717],[1501,4754],[1572,4753]...

to [x,y] pixel values that are within your SVG bounds.

This is more complex, you can try to figure out the translate manually (like I demonstrate with your data in this bl.ock, (uses d3v4, which has slightly different method names, but otherwise is the same for this type of operation), or use an automated function to determine the appropriate transform you need to use.

Andrew Reid
  • 37,021
  • 7
  • 64
  • 83
1

Change your last function to this

svg.append("path")
      .data(feature.geometry)
      .attr("d", path)
      .attr("class", "border")
      .attr("stroke", "black")
      .attr("fill", "blue");
Shaishav Jogani
  • 2,111
  • 3
  • 23
  • 33
1

I think there is something wrong with you geojson file

I have uploaded it to github, which is automatically detecting geojson content and displays it as a map. You can see that only one line is displayed

As already pointed out, you don't have d property defined for path

I have updated your fiddle to match Github result:

var width = 960,
    height = 1160;

// SVG element as a JavaScript object that we can manipulate later
var svg = d3.select("#map").append("svg")
      .attr("width", width)
      .attr("height", height);

var center = [24.679341, 46.680381];

// Instantiate the projection object
var projection = d3.geo.mercator()
      .center(center)
      .clipAngle(180)
          // Size of the map itself, you may want to play around with this in
          // relation to your canvas size
      .scale(1000)
          // Center the map in the middle of the canvas
      .translate([width / 2, height / 2])
      .precision(.1);

// Assign the projection to a path
var path = d3.geo.path().projection(projection);

d3.json("https://raw.githubusercontent.com/bumbeishvili/Assets/master/Other/junk/sa-all.geo.json", function(err, data) {
debugger;
  
  svg.selectAll('path')
      .data(data.features)
      .enter()
      .append("path")
      .attr("d", path)
      .attr("class", "border")
      .attr("stroke", "black")
      .attr("fill", "blue");
      
});
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>

<div id="map"></div> 
bumbeishvili
  • 1,342
  • 14
  • 27