Scaling a d3 v4 card to match SVG (or in general)

I try to make this map smaller. Either in SVG, or even manually.

This is my code in its simplest form:

function initializeMapDifferent(){ var svg = d3.select("#map").append("svg") .attr("width", 1000) .attr("height", 500); d3.json("https://d3js.org/us-10m.v1.json", function (error, us){ svg.append("g") .attr("class", "states") .selectAll("path") .data(topojson.feature(us, us.objects.states).features) .enter().append("path") .attr("fill", "gray") .attr("d", d3.geoPath()); }); } 

I tried something like:

  var path = d3.geoPath() .projection(d3.geoConicConformal() .parallels([33, 45]) .rotate([96, -39]) .fitSize([width, height], conus)); 

but every time I add something to the path variable, I get NAN errors from the internal parts of D3. Thanks for any help!

+5
source share
1 answer

Why data is not designed properly

The key problem is that your data is already projected. D3 geoProjections use undesigned data or long pairs of lengths. Data in the WGS84 database. In fact, geoProjection d3 takes spherical coordinates and translates them into flat Cartesian coordinates x, y.

Your data does not match this - it is already flat. You can see, apparently, because Alaska is not where it should be (unless someone changed the long pairs of Alaska, which is unlikely). Other signs and symptoms of already projected data may be a feature that spans the entire planet and NaN errors.

The fact that this is a composite projection makes the unproject process difficult, but you can display already projected data in d3.js.

Projecting Already Projected Data

Simply put, you can define your projection as null:

 var path = d3.geoPath(null); 

This will get the x, y data of the geo-geon geometry and display it as x, y data. However, if your x, y coordinates exceed the width and height of your svg, the map will not be contained in your svg (as you found in your example with .attr("d", d3.geoPath()); ).

If you want a little control over how this data is displayed, you can use geoTransform .

From Mike Bostock :

But what if your geometry is already flat? That is, if you just want to take the projected geometry, but still translate or scale it to fit the viewport?

You can implement custom geometric transformations to get full control over the design process.

Using geoTransform relatively simple, assuming you don't want to change the type of projection. For example, if you want to scale data, you can implement a short function for scaling using geoTransform :

 function scale (scaleFactor) { return d3.geoTransform({ point: function(x, y) { this.stream.point(x * scaleFactor, y * scaleFactor); } }); } var path = d3.geoPath().projection(scale(0.2)); 

Although this will zoom out in the upper left corner when you zoom out. To focus on the details, you can add code to center the projection:

 function scale (scaleFactor,width,height) { return d3.geoTransform({ point: function(x, y) { this.stream.point( (x - width/2) * scaleFactor + width/2 , (y - height/2) * scaleFactor + height/2); } }); } var path = d3.geoPath().projection(scale(0.2,width,height)) 

Demo:

In the event that the fragment does not load json, I placed bl.ock here here .

Here is an example of using your file:

 var width = 600; var height = 300; var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); function scale (scaleFactor,width,height) { return d3.geoTransform({ point: function(x, y) { this.stream.point( (x - width/2) * scaleFactor + width/2 , (y - height/2) * scaleFactor + height/2); } }); } d3.json("https://d3js.org/us-10m.v1.json", function (error, us){ var path = d3.geoPath().projection(scale(0.2,width,height)) svg.append("g") .attr("class", "states") .selectAll("path") .data(topojson.feature(us, us.objects.states).features) .enter().append("path") .attr("fill", "gray") .attr("d", path); }); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.6.0/d3.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/2.2.0/topojson.js"></script> 

How can you determine if your data is projected?

You can verify that the geometry of your objects meets latitude and longitude constraints. For example, if you must log in:

 d3.json("https://d3js.org/us-10m.v1.json", function (error, us){ console.log(topojson.feature(us, us.objects.states).features); }); 

You will quickly see that the values ​​exceed +/- 90 degrees N / S and +/- 180 degrees E / W. It is unlikely to be long long pairs.

Alternatively, you can import your data into an online service, such as mapshaper.org, and compare with another topoizon / geojson, which, as you know, is not designed (or "projected" using WGS84).

If you are dealing with geojson, you may be lucky enough to see a property that defines the projection, for example: "name": "urn:ogc:def:crs:OGC:1.3:CRS84" (CRS stands for coordinate system) or EPSG number: EPSG:4326 (EPSG stands for European Petroleum Survey Group).

In addition, if your data projects have a zero projection, but not a standard projection (scaling / scaling to ensure that you are not looking in the wrong area), you can deal with predicted data. Similarly, if your viewport is fully covered by one function (and you are not zoomed in). NaN coordinates are also a potential indicator. However, these latest predicted data indicators may also indicate other problems.

Finally, the data source may also indicate that the data is already projected into either the metadata or how it is used: look at block , we see that there was no projection when defining geoPath .

+6
source

Source: https://habr.com/ru/post/1264661/


All Articles