Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3.geo : Spherical arcs rather than straight lines for parallels?

I just made a D3js globe localisator, which looks like this :

enter image description here

If you look carefully, the red square looks ugly since it doesn't follow the Earth's curve. I have the area bounding box in decimal degrees :

var bb = {W:-5.0, N:50.0, E:10.0, S:40.0 }

And I draw the lines as follow:

svg.append("path")
.datum({type: "LineString", coordinates: 
        [[-5, 40], [-5, 50], [10, 50], [10, 40], [-5, 40]]
       })
.attr("d", path);

For larger areas, it's even the opposite curve from expectations (for a bounding box):

enter image description here

How to add rather elegant spherical arcs ?

like image 907
Hugolpz Avatar asked Aug 20 '14 23:08

Hugolpz


People also ask

What type of function best describes d3 geoMercator?

js geoMercator() Function. D3. js is a JavaScript library for producing dynamic, interactive data visualizations in web browsers.

What is the use of d3 geoPath () method?

The geographic path generator, d3. geoPath, is similar to the shape generators in d3-shape: given a GeoJSON geometry or feature object, it generates an SVG path data string or renders the path to a Canvas. Canvas is recommended for dynamic or interactive projections to improve performance.

What is d3 Geo projection?

# d3.geo.projection(raw) Constructs a new projection from the specified raw point projection function. For example, a Mercator projection can be implemented as: var mercator = d3. geo.


2 Answers

enter image description here

Given a known decimal degrees bounding box (dig in start here for bb) such as :

  bounds = [[-50.8,20.0][30,51.5]];
  WNES0 = bounds[0][0], // West    "W":-50.8  
  WNES1 = bounds[1][2], // North   "N": 51.5
  WNES2 = bounds[1][0], // East    "E": 30
  WNES3 = bounds[0][3], // South   "S": 20.0

Some maths are needed.

// *********** MATH TOOLKIT ********** //
function parallel(φ, λ0, λ1) {
  if (λ0 > λ1) λ1 += 360;
  var dλ = λ1 - λ0,
      step = dλ / Math.ceil(dλ);
  return d3.range(λ0, λ1 + .5 * step, step).map(function(λ) { return [normalise(λ), φ]; });
}
function normalise(x) {
  return (x + 180) % 360 - 180;
}   

Then, let's both calculate the polygon's coordinates and project it:

// *********** APPEND SHAPES ********** //
svg.append("path")
.datum({type: "Polygon", coordinates: [
    [[WNES0,WNES3]]
      .concat(parallel(WNES1, WNES0, WNES2))
      .concat(parallel(WNES3, WNES0, WNES2).reverse())
  ]})
.attr("d", path)
.style({'fill': '#B10000', 'fill-opacity': 0.3, 'stroke': '#B10000', 'stroke-linejoin': 'round'})
.style({'stroke-width': 1 });

180th meridian crossing: Boxes upon the 180th meridian need special management. By example, localising a set of pacific island between 155⁰ East and -155 West initially gives.... enter image description here ...with correct rotation (+180⁰) : enter image description here ... and with correct boxing: enter image description here

Localisator now perfect ! Live demo on blocks

var bb = { "item":"India", "W": 67.0, "N":37.5, "E": 99.0, "S": 5.0 }, 
localisator("body", 200, bb.item, bb.W, bb.N, bb.E, bb.S);

enter image description here

+1 welcome.

like image 166
Hugolpz Avatar answered Sep 28 '22 07:09

Hugolpz


You can use d3's built in graticule generator for this:

var bb = {W: -5.0, N: 50.0, E: 10.0, S: 40.0 };
var arc = d3.geo.graticule()
    .majorExtent([[bb.W, bb.S], [bb.E, bb.N]]);

Then use the outline function of the graticule generator to draw the path:

svg.append("path")
    .attr("class", "arc")
    .attr("d", path(arc.outline()));

geographic 'arc'

Full working example can be found here.

like image 35
Josh Avatar answered Sep 28 '22 06:09

Josh