Circles in the map are displayed incorrectly Location in D3 V4

I am using a tutorial to learn how to create maps in D3.v3, but I am using D3.v4. I'm just trying to get some circles to appear on the map (see below). The code works, except that the circles are over Nevada and should be in the bay area. I am guessing this is a mismatch between the map projections and the projected coordinates. I'm not sure what projection the map is in, but I tried to get it to be albersUsa (see the comments of the commands in which I generate the path), but this causes the entire map to disappear. Any help would be appreciated!

<!DOCTYPE html>

<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script>

var w = 960,
    h = 600;
var projection = d3.geoAlbersUsa();
var path = d3.geoPath()
             //.projection(projection)

d3.json("https://d3js.org/us-10m.v1.json", function(error, us) {
  if (error) throw error;

  var svg = d3.select("body").append("svg")
    		.attr("width", w)
    		.attr("height", h);

  svg.selectAll("path")
     .data(topojson.feature(us, us.objects.states).features)
     .enter().append("path")
	 .attr("class", "states")
     .attr("d", path);

   svg.append("path")
      .attr("class", "state-borders")
      .attr("d", path(topojson.mesh(us, us.objects.states)))

   svg.append("path")
      .attr("class", "county-borders")
      .attr("d", path(topojson.mesh(us, us.objects.counties)));

      aa = [-122.490402, 37.786453];
  	  bb = [-122.389809, 37.72728];

   svg.selectAll("circle")
      .data([aa,bb]).enter()
      .append("circle")
      .attr("cx", function (d) { return projection(d)[0]; })
      .attr("cy", function (d) { return projection(d)[1]; })
      .attr("r", "8px")
      .attr("fill", "red")
    });

</script>
      

Run codeHide result


+3


source to share


1 answer


Your American json is already projected and you are using a zero projection to display it:

var path = d3.geoPath()
             //.projection(projection)

      

Without defining a projection, your topoison / geoison coordinates will be converted to straight pixel coordinates. It just so happens that this particular topoison file has pixel coordinates that are between [0,0] and [960600], almost the same size as the default bl.ock view. Without knowing the projection in use that generates this file, you cannot replicate this projection to align geographic features with your data. Unless you place your objects directly with pixel values ​​and skip the projection at all (useless for points that are not near recognizable landmarks or where accuracy is important).

Your US topographic features disappear when projecting with c geoUsaAlbers()

because you take pixel coordinates on a plane and convert them to SVG coordinates as if they were points on a 3D globe (d3 projections expect latitude and longitude pairs).

Instead, use a topoison or geoison that is not projected. That is, it contains latitude / longitude pairs and projects that data along with your points. Check out this bl.ock for an example with non-projected (latitude / long pair) json for the US using your code (but assigning the projection to path

).

To check if you have latitude / longitude pairs, you can easily view the geometry of these objects in the geojson file and see if the values ​​are valid long, lat values ​​of the points. For topoison, the topoison library converts features to geoison so you can view the geometry after this transformation.

Here is the unpredictable US topoison: https://bl.ocks.org/mbostock/raw/4090846/us.json




Let's say you really wanted to use the same topojson file, but we can probably output the projection it uses. First, I'll show the difference between your projected points (using a non-projected US outline) and an already projected topoison (a non-projected topoison is projected with d3.geoAlbersUsa()

, but projected with a zero projection):

enter image description here

Most likely the d3.geoAlbersUsa projection is optimized for the standard bl.ocks.org viewport, 960x500. The unprojected dataset has a bounding box of roughly 960x600, so perhaps if we zoom in 600/500 and adjust the translation, we can align our functions in SVG format equal to 960x600:

var projection = d3.geoAlbersUsa();
var scale = projection.scale() * 600 / 500;
projection.scale(scale).translate([960/2,600/2])
var projectedPath = d3.geoPath().projection(projection);

      

And it looks pretty good, I don't see any difference between the two:

enter image description here

Here's a block showing the aligned elements.

But, as I mention in the comments, even if you can tweak the features: any scaling or centering will be problematic since you need to use geotransformation for the data already projected, but geo-projection for the raw geographic data. Using all (evenly) projected data or all unpredictable data makes life easier.

+4


source







All Articles