1

I am trying to draw a map for UAE emirates using geojson.

The geojson is valid and validated on geojson.io, the result appeared instead of a map a rectangle , the written code is here:

var width = 20,
height = 20;
var scal = (1 << 10) / 2 / Math.PI; 

//Define map projection 
var projection = d3.geo.mercator();
      projection
        .scale(scal)///3.5
        .translate([width/2, height/2]);

//Define path generator
var path = d3.geo.path().projection(projection);

d3.json("http://localhost/data/uae-em.json", function(error,json) {

    if(error) alert("error fetching data");
    var svg = d3.select("article")
        .append("svg")
        .attr("width",width)
        .attr("height",height);
    //draw map
    var map = svg.selectAll("path")
        .data(json.features)
        .enter()
        .append("path")
        .attr("d", path)
        .style("fill", "#3498db"); 
    });

The JSON data is here: https://files.fm/f/ndsz3v85

And here is an example feature from the geojson:

{"type":"Feature","id":"4","properties":{"name":"UmmAlQwain"},"geometry":{"type":"Polygon","coordinates":[[[55.2350748,25.5700834],[55.4229709,25.3989174],[55.4236655,25.3981583],[55.423986,25.3978339],[55.4254059,25.3960785],[55.4272991,25.3935724],[55.4570596,25.3762406],[55.4577152,25.375909],[55.4915399,25.3593419],[55.4926741,25.3588472],[55.4954455,25.35793],[55.5013237,25.3571279],[55.5031779,25.3570031],[55.5390972,25.3630675],[55.5909015,25.3679027],[55.5918297,25.3680754],[55.5927443,25.3683591],[55.5935906,25.3688154],[55.5943278,25.3693581],[55.5952833,25.3702339],[55.6010034,25.3768825],[55.6044809,25.3808245],[55.6295136,25.4095735],[55.630044,25.4101151],[55.6303207,25.4106671],[55.6302516,25.411542],[55.6299137,25.4123589],[55.6293946,25.413368],[55.6287288,25.4144586],[55.6280518,25.4152536],[55.6211794,25.4201663],[55.6196221,25.4209307],[55.6177489,25.4214301],[55.605742,25.4232035],[55.6024463,25.4237462],[55.5975759,25.4244245],[55.5956543,25.4249531],[55.5919637,25.4262723],[55.554199,25.4399865],[55.5518752,25.4408552],[55.5505241,25.4414505],[55.5490758,25.4421533],[55.5473004,25.4425344],[55.5456819,25.4430884],[55.5370397,25.4462406],[55.5358338,25.4466226],[55.5349558,25.4467468],[55.5337817,25.4467659],[55.523193,25.4459922],[55.5198927,25.4460304],[55.5162327,25.4457248],[55.513895,25.4454286],[55.4882538,25.4420186],[55.4870268,25.4419613],[55.4857574,25.4421332],[55.4843188,25.4426681],[55.4771469,25.4472339],[55.4709611,25.4516781],[55.324428,25.6230138],[55.3205286,25.6184125],[55.3117729,25.6106044],[55.3015804,25.5994214],[55.2892555,25.5829995],[55.2795994,25.5665793],[55.2730684,25.5680481],[55.2385191,25.570346],[55.2350748,25.5700834]]]}}
Andrew Reid
  • 37,021
  • 7
  • 64
  • 83
Haneen
  • 13
  • 3
  • Welcome to Stack Overflow! Other users marked your question for low quality and need for improvement. I re-worded/formatted your input to make it easier to read/understand. Please review my changes to ensure they reflect your intentions. But I think your question is still not answerable. **You** should [edit] your question now, to add missing details (see [mcve])). Your question should be self-contained. You should not use links to external sites, and you shouldnt rely on screenshots. Have all relevant content here as nicely formatted **text**. – GhostCat Sep 06 '18 at 10:42
  • Feel free to drop me a comment in case you have further questions or feedback for me. – GhostCat Sep 06 '18 at 10:42
  • The geojson may be valid, but it makes no sense. For every feature the polygon coordinates repeat the same value over and over. Here's the [file for inspection](https://jsonblob.com/c07bb155-b1c7-11e8-b795-4b6084b7a499). – Mark Sep 06 '18 at 11:34

1 Answers1

0

There are a few issues here.

First, the reason why you get a rectangle is because of winding order. D3 doesn't incorporate the winding order of the geojson spec - it follows the winding order of shapefiles. It's quite odd, because D3 is the only framework that comes to mind where winding order matters in terms of drawing because it uses spherical geometry rather than planar. In your case you are drawing everything except the UAE:

enter image description here

I've added a stroke, we can see the edge of the world (+/-180 degrees east west, not the top because a Mercator stretches to infinity on the y axis, and even if rounded or cut off, the limits are beyond the edge of the SVG), we can also see the outline of the UAE (or part of it at least). So we need to reverse the winding order, this means reversing the arrays that hold the coordinates that define each ring in the polygon.

For this we can use turf.js, either to prep the data beforehand or to call it everytime we load the data:

json.features.forEach(function(feature) {
  feature.geometry = turf.rewind(feature.geometry, {reverse:true});
})

Now that we have data in the proper winding order, we get:

enter image description here

The feature appears larger because we can see all features, in the first image only the last drawn feature is really visible: it covers the rest of the features.

Now we have a different problem, I've displayed your map on a 960x500 pixel frame (using your code (fiddle)). If we were to use a 20x20 frame and your initial scale, we get nothing visible. The map is centered at [0,0], in the Atlantic off the coast of Africa:

enter image description here

The square outlines the rough area of your map shown in the 20x20 version using your scale and translate, I've added a world map and increased the size to 200x200 to give some visual context to what we're looking at in the 20pixel version


If the goal is to show the entire world, we need the proper scale. A cylindrical projection such as the Mercator (if the cylinder is oriented north/south) takes the 360 degrees around the earth (along the equator) and stretches it over a certain distance in map units. In d3 the scale is this conversion factor, the default assumes a 960 pixel across map showing the entire planet and the earth is 2π radians around, so the scale value is 960/2π (960 pixels per 2π radians). So, we can set your scale to width/Math.PI/2:

enter image description here

This will lead to a new problem, the UAE is about 5 degrees across, the world is 360 degrees around, which is about 1% of world "width". This is a bit problematic when displaying your map on a 20 pixel by 20 pixel SVG: the UAE is about .2 pixels across, something that won't render very satisfactorily. The only options would be to increase the size of the map, or center the map on the UAE and increase the scale.

Automagic map centering has been covered many times, and I wouldn't the previous excellent explanations any justice by covering it again (all I can say is v4/5 offer an excellent fitSize/fitExtent method, but v3 centering info can be found in this question). For the sake of demonstration, however, I'll manually center the UAE below.

The centroid of the UAE is about 24 N, 54E (from here).

If we assume the UAE is wider than it is tall, and assume a number such as about 5 degrees across, and we want to display those five degrees over 20 pixels, then we know the world is about 1440 pixels across (mapWidthPixels * earthWidthDegrees / mapWidthDegrees = 20 * 360 / 5). The scale is then 1440/2/π, rounding that down a bit for a bit of extra margin we get: 200.

So using:

var width = 20;
var height = 20;
var scale = 200;
var center = [54,24];

We can get this:

enter image description here

Still small, but with the above principles, easily expanded.

fiddle.

I've added a border to the 20px SVGs above to provide a boundary to the SVG given the small size and lack of distinction of where the SVG's edges are

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