Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to center & scale map in d3js (use projection.center/translate/rotate?)

Given that I have topoJSON data of a given geographical feature and a specific projection.

How should I center and scale the map to fit its parent object?

It seems I can use either projection.rotate(), projection.translate() or projection.center() to center a map:

https://github.com/d3/d3-3.x-api-reference/blob/master/Geo-Projections.md

What are the differences and how does scale affect the different functions?

like image 546
pac w Avatar asked Mar 10 '23 19:03

pac w


2 Answers

Use projection.fitExtent() in v4. Documentation. Example.

fitExtent takes two parameters:

  1. extent is the top left and bottom right corner of the projection, represented by an array of two arrays – e.g. [[0, 0], [width, height]].
  2. object is a GeoJSON object.

If the top left corner of the projection is [0, 0], you can use the convenience method projection.fitSize(), where you only pass the bottom right corner of the extent, represented by a single array of two items – e.g. [width, height].

like image 189
Harry Stevens Avatar answered Apr 30 '23 05:04

Harry Stevens


Actually, it's a mix of both. According to the API, projection.center:

sets the projection’s center to the specified location, a two-element array of longitude and latitude in degrees and returns the projection.

So, it's used to set the center of the map. Regarding projection.translate:

If point is specified, sets the projection’s translation offset to the specified two-element array [x, y] and returns the projection. If point is not specified, returns the current translation offset which defaults to [480, 250]. The translation offset determines the pixel coordinates of the projection’s center. The default translation offset places ⟨0°,0°⟩ at the center of a 960×500 area.

As you can see, projection.translate depends on projection.center ("the translation offset determines the pixel coordinates of the projection’s center"). So, both values will determine how the map sits in its container

This is a demo showing the map of Japan (this code is not mine) in a smaller SVG, 500x500. In this one, we'll set the translate to the middle of the SVG:

.translate([width/2, height/2]);

Check the demo:

var topoJsonUrl = "https://dl.dropboxusercontent.com/u/1662536/topojson/japan.topo.json";

var width = 500,
    height = 500,
    scale = 1;

d3.select("body").append("svg")
	.attr("width", width)
	.attr("height", height)
    .append("g").attr("id", "all-g");

var projection = d3.geo.mercator()
		.center([138, 38])
		.scale(1000)
		.translate([width / 2, height / 2]);
    
d3.json(topoJsonUrl, onLoadMap);

function onLoadMap (error, jpn) {
    var path = d3.geo.path()
        		.projection(projection);
	var features = topojson.object(jpn, jpn.objects.japan);
  
  var mapJapan = features;
  

	d3.select("#all-g")
        .append("g").attr("id", "path-g").selectAll("path")
            .data(features.geometries)
            .enter()
            .append("path")
            .attr("fill", "#f0f0f0")
            .attr("id", function(d,i){ return "path" + i})
            .attr("stroke", "#999")
            .attr("stroke-width", 0.5/scale)
            .attr("d", path);

}
path {
  stroke: black;
  stroke-width: 1.5;
  }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://d3js.org/topojson.v0.min.js"></script>

And, in this one, to the left:

.translate([width/4, height/2]);

Check the demo:

var topoJsonUrl = "https://dl.dropboxusercontent.com/u/1662536/topojson/japan.topo.json";

var width = 500,
    height = 500,
    scale = 1;

d3.select("body").append("svg")
	.attr("width", width)
	.attr("height", height)
    .append("g").attr("id", "all-g");

var projection = d3.geo.mercator()
		.center([138, 38])
		.scale(1000)
		.translate([width / 4, height / 2]);
    
d3.json(topoJsonUrl, onLoadMap);

function onLoadMap (error, jpn) {
    var path = d3.geo.path()
        		.projection(projection);
	var features = topojson.object(jpn, jpn.objects.japan);
  
  var mapJapan = features;
  

	d3.select("#all-g")
        .append("g").attr("id", "path-g").selectAll("path")
            .data(features.geometries)
            .enter()
            .append("path")
            .attr("fill", "#f0f0f0")
            .attr("id", function(d,i){ return "path" + i})
            .attr("stroke", "#999")
            .attr("stroke-width", 0.5/scale)
            .attr("d", path);

}
path {
  stroke: black;
  stroke-width: 1.5;
  }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://d3js.org/topojson.v0.min.js"></script>

In both cases, however, changing projection.center will move the map in its container.

like image 40
Gerardo Furtado Avatar answered Apr 30 '23 03:04

Gerardo Furtado