在 topojson 上查找比例距离
Finding Scaled Distance on topojson
我一直在评估将像素 space 与给定投影的距离(以实际单位表示)相关联的不同方法。我发现以下内容非常有用:
var actual_map_bounds = d3.geo.bounds(this_topojson);
var radians = d3.geo.distance(actual_map_bounds[0], actual_map_bounds[1]);
var earth_radius = 3959; // miles
var arc_length = earth_radius * radians; // s = r * theta
var projected_map_bounds = [
this_projection(actual_map_bounds[0]),
this_projection(actual_map_bounds[1])
];
var projected_map_width = projected_map_bounds[1][0] - projected_map_bounds[0][0];
var projected_map_height = projected_map_bounds[0][1] - projected_map_bounds[1][1];
var projected_map_hypotenuse = Math.sqrt(
(Math.pow(projected_map_width, 2)) + (Math.pow(projected_map_height, 2))
);
var pixels_per_mile = projected_map_hypotenuse / arc_length;
var pixel_distance = pixels_per_mile * miles;
但我当前的应用程序将通过减少所需的计算步骤而受益匪浅。是否有任何更简单或更 'elegant' 的解决方案可供 topojson 开发人员使用?
我想我明白你在问什么,它与 d3
没有任何关系,而只是 JavaScript。真正的问题是:
How can I make this a reusable function that doesn't re-run the bulk of the calculations without resorting to global variables?
答案是闭包:
function pixelLength(this_topojson, this_projection) {
var actual_map_bounds = d3.geo.bounds(this_topojson);
var radians = d3.geo.distance(actual_map_bounds[0], actual_map_bounds[1]);
var earth_radius = 3959; // miles
var arc_length = earth_radius * radians; // s = r * theta
var projected_map_bounds = [
this_projection(actual_map_bounds[0]),
this_projection(actual_map_bounds[1])
];
var projected_map_width = projected_map_bounds[1][0] - projected_map_bounds[0][0];
var projected_map_height = projected_map_bounds[0][1] - projected_map_bounds[1][1];
var projected_map_hypotenuse = Math.sqrt(
(Math.pow(projected_map_width, 2)) + (Math.pow(projected_map_height, 2))
);
var pixels_per_mile = projected_map_hypotenuse / arc_length;
return function(miles){
var pixel_distance = pixels_per_mile * miles;
return pixel_distance;
}
}
现在 pixelLength
将 return 一个函数,可用于计算相同的 topojson 和投影的像素距离一遍又一遍:
var pixelCalc = pixelLength(topojson.feature(data, data.objects['parishes']), projection4);
pixelCalc(1); // pixels for 1 mile
pixelCalc(100); // pixels for 100 miles
这是实际操作:
<!DOCTYPE html>
<head>
<title>Map distance scales</title>
<meta charset="utf-8">
<style>
body {
padding: 0;
margin: 0;
font-family: helvetica, arial, sans-serif;
}
.parishes {
fill: white;
stroke: #777;
stroke-opacity: 0.5;
stroke-width: 0.5px;
opacity: 0.8;
}
.parish-border {
fill: none;
stroke: #353535;
stroke-opacity: 0.4;
stroke-width: 0.5px;
opacity: 0.8;
}
.state-border {
fill: none;
stroke: #585858;
}
.distance-scale {
font-size: 11px;
line-height: 11px;
position: absolute;
font-weight: 500;
text-transform: uppercase;
color: #000;
}
.distance-scale-line {
stroke: #000;
stroke-width: 1;
stroke-opacity: 1;
opacity: 1;
fill: #000;
}
</style>
</head>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/queue.v1.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var projection4 = d3.geo.albers()
.center([0, 31.2])
.rotate([91.6, 0]) // Rotate CCW (looking down onto North Pole)
.parallels([29, 33])
.translate([width / 2, height / 2])
.scale(6000);
var map_path4 = d3.geo.path().pointRadius(2).projection(projection4);
queue()
.defer(d3.json, "https://jsonblob.com/api/e98bb5d0-df6f-11e6-90ab-c5f0718ee343")
.await(ready);
function pixelLength(this_topojson, this_projection) {
var actual_map_bounds = d3.geo.bounds(this_topojson);
var radians = d3.geo.distance(actual_map_bounds[0], actual_map_bounds[1]);
var earth_radius = 3959; // miles
var arc_length = earth_radius * radians; // s = r * theta
var projected_map_bounds = [
this_projection(actual_map_bounds[0]),
this_projection(actual_map_bounds[1])
];
var projected_map_width = projected_map_bounds[1][0] - projected_map_bounds[0][0];
var projected_map_height = projected_map_bounds[0][1] - projected_map_bounds[1][1];
var projected_map_hypotenuse = Math.sqrt(
(Math.pow(projected_map_width, 2)) + (Math.pow(projected_map_height, 2))
);
var pixels_per_mile = projected_map_hypotenuse / arc_length;
return function(miles){
var pixel_distance = pixels_per_mile * miles;
return pixel_distance;
}
}
function ready(error, data) {
if (error) throw error;
var map4 = svg.append("g")
.attr("class", "parishes")
.attr("id", "map4");
map4.selectAll("path")
.data(topojson.feature(data, data.objects.parishes).features)
.enter().append("path")
.attr("d", map_path4);
map4.append("path")
.datum(topojson.mesh(data, data.objects.parishes, function(a, b) { return a !== b; }))
.attr("class", "parish-border")
.attr("d", map_path4);
map4.append("path")
.datum(topojson.mesh(data, data.objects.parishes, function(a, b) { return a === b; }))
.attr("class", "state-border")
.attr("d", map_path4);
// Distance scale
// Line path generator
var line = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("basis");
// Scale4
var pixelCalc = pixelLength(topojson.feature(data, data.objects['parishes']), projection4);
var distance_scale4 = svg.selectAll("#distance-scale4")
.data(d3.range(1, 100, 5))
.enter().append("g")
.attr("class", "distance-scale")
.attr("id", "distance-scale4")
.attr("transform", function(d,i){
return "translate(20, " + (i * 20 + 40) + ")"
})
.attr("width", function(d) { return d; });
distance_scale4.append('text')
.attr("text-anchor", "start")
.text(function(d){
return d + " miles";
});
distance_scale4.append('path')
.attr("class", "distance-scale-line")
.attr("d", function(d, i) {
var p = pixelCalc(d);
var lineData = [
{"x": 0, "y": 0},
{"x": p, "y": 0}
];
return line(lineData);
});
}
</script>
</body>
</html>
编辑
等等你要的只是点与点之间的距离?啊,伙计,我误会了。它应该只是:
// since earth is sphere
var radians = d3.geo.distance([p1.Longitude, p1.Latitude], [p2.Longitude, p2.Latitude]);
var numberMiles = radians * 3959; // radius of earth
我一直在评估将像素 space 与给定投影的距离(以实际单位表示)相关联的不同方法。我发现以下内容非常有用:
var actual_map_bounds = d3.geo.bounds(this_topojson);
var radians = d3.geo.distance(actual_map_bounds[0], actual_map_bounds[1]);
var earth_radius = 3959; // miles
var arc_length = earth_radius * radians; // s = r * theta
var projected_map_bounds = [
this_projection(actual_map_bounds[0]),
this_projection(actual_map_bounds[1])
];
var projected_map_width = projected_map_bounds[1][0] - projected_map_bounds[0][0];
var projected_map_height = projected_map_bounds[0][1] - projected_map_bounds[1][1];
var projected_map_hypotenuse = Math.sqrt(
(Math.pow(projected_map_width, 2)) + (Math.pow(projected_map_height, 2))
);
var pixels_per_mile = projected_map_hypotenuse / arc_length;
var pixel_distance = pixels_per_mile * miles;
但我当前的应用程序将通过减少所需的计算步骤而受益匪浅。是否有任何更简单或更 'elegant' 的解决方案可供 topojson 开发人员使用?
我想我明白你在问什么,它与 d3
没有任何关系,而只是 JavaScript。真正的问题是:
How can I make this a reusable function that doesn't re-run the bulk of the calculations without resorting to global variables?
答案是闭包:
function pixelLength(this_topojson, this_projection) {
var actual_map_bounds = d3.geo.bounds(this_topojson);
var radians = d3.geo.distance(actual_map_bounds[0], actual_map_bounds[1]);
var earth_radius = 3959; // miles
var arc_length = earth_radius * radians; // s = r * theta
var projected_map_bounds = [
this_projection(actual_map_bounds[0]),
this_projection(actual_map_bounds[1])
];
var projected_map_width = projected_map_bounds[1][0] - projected_map_bounds[0][0];
var projected_map_height = projected_map_bounds[0][1] - projected_map_bounds[1][1];
var projected_map_hypotenuse = Math.sqrt(
(Math.pow(projected_map_width, 2)) + (Math.pow(projected_map_height, 2))
);
var pixels_per_mile = projected_map_hypotenuse / arc_length;
return function(miles){
var pixel_distance = pixels_per_mile * miles;
return pixel_distance;
}
}
现在 pixelLength
将 return 一个函数,可用于计算相同的 topojson 和投影的像素距离一遍又一遍:
var pixelCalc = pixelLength(topojson.feature(data, data.objects['parishes']), projection4);
pixelCalc(1); // pixels for 1 mile
pixelCalc(100); // pixels for 100 miles
这是实际操作:
<!DOCTYPE html>
<head>
<title>Map distance scales</title>
<meta charset="utf-8">
<style>
body {
padding: 0;
margin: 0;
font-family: helvetica, arial, sans-serif;
}
.parishes {
fill: white;
stroke: #777;
stroke-opacity: 0.5;
stroke-width: 0.5px;
opacity: 0.8;
}
.parish-border {
fill: none;
stroke: #353535;
stroke-opacity: 0.4;
stroke-width: 0.5px;
opacity: 0.8;
}
.state-border {
fill: none;
stroke: #585858;
}
.distance-scale {
font-size: 11px;
line-height: 11px;
position: absolute;
font-weight: 500;
text-transform: uppercase;
color: #000;
}
.distance-scale-line {
stroke: #000;
stroke-width: 1;
stroke-opacity: 1;
opacity: 1;
fill: #000;
}
</style>
</head>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/queue.v1.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var projection4 = d3.geo.albers()
.center([0, 31.2])
.rotate([91.6, 0]) // Rotate CCW (looking down onto North Pole)
.parallels([29, 33])
.translate([width / 2, height / 2])
.scale(6000);
var map_path4 = d3.geo.path().pointRadius(2).projection(projection4);
queue()
.defer(d3.json, "https://jsonblob.com/api/e98bb5d0-df6f-11e6-90ab-c5f0718ee343")
.await(ready);
function pixelLength(this_topojson, this_projection) {
var actual_map_bounds = d3.geo.bounds(this_topojson);
var radians = d3.geo.distance(actual_map_bounds[0], actual_map_bounds[1]);
var earth_radius = 3959; // miles
var arc_length = earth_radius * radians; // s = r * theta
var projected_map_bounds = [
this_projection(actual_map_bounds[0]),
this_projection(actual_map_bounds[1])
];
var projected_map_width = projected_map_bounds[1][0] - projected_map_bounds[0][0];
var projected_map_height = projected_map_bounds[0][1] - projected_map_bounds[1][1];
var projected_map_hypotenuse = Math.sqrt(
(Math.pow(projected_map_width, 2)) + (Math.pow(projected_map_height, 2))
);
var pixels_per_mile = projected_map_hypotenuse / arc_length;
return function(miles){
var pixel_distance = pixels_per_mile * miles;
return pixel_distance;
}
}
function ready(error, data) {
if (error) throw error;
var map4 = svg.append("g")
.attr("class", "parishes")
.attr("id", "map4");
map4.selectAll("path")
.data(topojson.feature(data, data.objects.parishes).features)
.enter().append("path")
.attr("d", map_path4);
map4.append("path")
.datum(topojson.mesh(data, data.objects.parishes, function(a, b) { return a !== b; }))
.attr("class", "parish-border")
.attr("d", map_path4);
map4.append("path")
.datum(topojson.mesh(data, data.objects.parishes, function(a, b) { return a === b; }))
.attr("class", "state-border")
.attr("d", map_path4);
// Distance scale
// Line path generator
var line = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("basis");
// Scale4
var pixelCalc = pixelLength(topojson.feature(data, data.objects['parishes']), projection4);
var distance_scale4 = svg.selectAll("#distance-scale4")
.data(d3.range(1, 100, 5))
.enter().append("g")
.attr("class", "distance-scale")
.attr("id", "distance-scale4")
.attr("transform", function(d,i){
return "translate(20, " + (i * 20 + 40) + ")"
})
.attr("width", function(d) { return d; });
distance_scale4.append('text')
.attr("text-anchor", "start")
.text(function(d){
return d + " miles";
});
distance_scale4.append('path')
.attr("class", "distance-scale-line")
.attr("d", function(d, i) {
var p = pixelCalc(d);
var lineData = [
{"x": 0, "y": 0},
{"x": p, "y": 0}
];
return line(lineData);
});
}
</script>
</body>
</html>
编辑
等等你要的只是点与点之间的距离?啊,伙计,我误会了。它应该只是:
// since earth is sphere
var radians = d3.geo.distance([p1.Longitude, p1.Latitude], [p2.Longitude, p2.Latitude]);
var numberMiles = radians * 3959; // radius of earth