Home > OS >  D3 polygon projection is wrong
D3 polygon projection is wrong

Time:04-01

I have a working world projection using D3 geoOrthographic and topoJSON. It rotates and everything.

I wanted to place a hexagon shape at a coordinate and projected according to its place on the globe, which works... except my shape is really weird. I get a 5-sided shape, like one of the points is just missing, that rotates properly with the globe.

And then also a circle around the edge of the globe that does not rotate.

I have a function that throws out hexagon coordinates, I've tried with several scales and offsets, always the exact same behavior.

let hex = svgOrbit.append("path")
    .datum({"type":"GeometryCollection","geometries":[{"type":"Polygon","coordinates":[[[6.732,6],[5,7],[3.268,6],[3.268,4],[5,3],[6.732,4]]]}]})
    .attr("d", myGeoOrthographicProjection);

That's no hexagon.

The circle looks like that no matter how I rotate, the trying-to-hexagon orients as desired sans that missing point.

The path does show a d attr with these two separate polygons.

I just plain don't understand what's happening here. There aren't even any weird numbers, like a zero or NaN or anything in the coordinates. The entire planet projects correctly, but a hexagon throws it for a loop?

CodePudding user response:

The outer circle indicates that you have an inverted polygon: you are drawing a feature of the world minus the intended feature. As d3 uses spherical math in calculating projections, winding order matters, as opposed to most geographic tools which treat spherical coordinates as Cartesian (even when projecting). The first map below in red shows this by applying a fill.

The missing point is a bit odd, normally D3 won't render invalid geojson syntax and it won't throw an error or warning in not rendering anything. The issue here is that the last point in your coordinate array should be the first coordinate. I've forgotten where in the spec this is, and haven't looked as to why D3 renders it like this at all. When attempting to take a look at your geojson at geojson.io I noticed it didn't render at all with the missing end point.

I've rewound the coordinates (lazily with .reverse()) and added the extra point in the map on the right.

let hex = {"type":"GeometryCollection","geometries":[{"type":"Polygon","coordinates":[[[6.732,6],[5,7],[3.268,6],[3.268,4],[5,3],[6.732,4]]]}]};

let hex2 = {"type":"GeometryCollection","geometries":[{"type":"Polygon","coordinates":[[[6.732,6],[5,7],[3.268,6],[3.268,4],[5,3],[6.732,4],[6.732,6]].reverse()]}]};



    
    
let projection = d3.geoOrthographic().scale(125).translate([125,125]);
let path = d3.geoPath(projection);

let svg = d3.select("body")
  .append("svg")
  .attr("width", 500)
  .attr("height", 250);
  
svg
  .append("path")
  .datum(hex)
  .attr("d", path)
  .attr("fill", "crimson");
  
svg.append("g")
  .attr("transform","translate(250,0)")
  .append("path")
  .datum(hex2)
  .attr("d", path)
  .attr("fill","steelblue");
  

  

  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

  • Related