Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Looking for a world map with a specific projection in d3js

I have played around with the d3js (v5) maps, i'm trying to generate this map (the screenshot was taken from a random website), For my particular case there is no need to present Antarctica.

enter image description here

I have read the documentation here: https://github.com/d3/d3-geo#projections, and followed the instructions and used geoMercator, got this flat map which gets cutoff in the top north for some reason.

enter image description here

What is the correct approach for getting the first map's layout? any suggestions?

like image 242
JammingThebBits Avatar asked Jan 02 '23 06:01

JammingThebBits


1 Answers

The projection you are looking at is a Mercator projection.

With d3.geoMercator(), the scale value is derived from the circumference of the cylinder that forms the projection surface. The scale value is the number of pixels per radian. The default value anticipates stretching the 360 degrees of the cylinder over 960 pixels: 960/Math.PI/2.

For vertical angular distances, there is no such scaling factor, as one moves to extreme longitudes, the angular distance between points is increasingly exaggerated, such that the poles will be at ± infinity on the y axis. Because of this Mercator's, especially web Mercator's are often truncated at ±~85 degrees. With an extent of [-180,85] and [180,-85], a Mercator is square.

This limit is incorporated into d3-geoMercator, which "Defines a default projection.clipExtent such that the world is projected to a square, clipped to approximately ±85° latitude. (docs)"

This means that if we want to show the full extent of a d3-geoMercator, across 960 x 960 pixels, we can use:

d3.geoMercator()
 .scale(960/Math.PI/2)  // 960 pixels over 2 π radians
 .translate([480,480])  // the center of the SVG/canvas

Which gives us:

enter image description here

The default center of d3-geoMercator is [0°,0°], so if we want [0°,0°] to be in the middle of the SVG/canvas, we translate the center so that it is in the middle, with a translate of [width/2,height/2]

Now that we are showing the whole world, we can refine to show only the portion we want. The simplest method might just be lopping off pixels from the bottom of the svg/canvas. Using the above code with a canvas/svg height of 700 pixels (and keeping 960 pixels across, using the same scale and translate) I get:

enter image description here

I did not remove Antarctica from this image - it just happens that it is cut off without having to filter it out (this is not necessarily ideal practice: it is still drawn).

So, an SVG/Canvas with width 960, height 700, with a projection scale of 960/Math.PI/2 and a translate of [480,480] appears to be ok. These values will scale together for different view port sizes.

With maps, there is often a lot of eyeballing to get the visual effect desired, tweaking projection.translate() or projection.center() can help shift the map to the desired location. But we can do this computationally. I'll speak to one method here, using projection.fitSize() (though this won't solve the required aspect ratio without extra steps).

Project.fitSize([width,height],geojson) takes an array specifying the dimensions of the SVG/canvas and a geojson object and tweaks the projection scale and translate values so that the geojson feature is contained in the SVG/canvas. The geojson feature could be a bounding box of the part of the world you want to show, so you could use:

projection.fitSize([width,height], {
    type: "Polygon",
    coordinates: [[ 
      [-179.999,84] , 
      [-179.999,-57] , 
      [179.999,-57] , 
      [179.999,84], 
      [-179.999,84] 
     ]]
   })

Where ~84 degrees north is the north end of Greenland and ~56 degrees south is roughly the tip of South America. This will ensure that the entire portion of the world you want to see is visible. However, as noted above, this doesn't consider aspect, so if you constrain the above extent to square dimensions, you'll still be showing the full extent of the Mercator.

like image 50
Andrew Reid Avatar answered Jan 03 '23 19:01

Andrew Reid