在 D3.js 中绘制一个固定长度的圆
Drawing a fixed-length circle in D3.js
这看起来应该很简单,但我正在努力弄明白。
在D3.js的地图投影上画圆很容易,例如
p = projection([-73.94,40.7]);
svg
.append("svg:circle")
.attr("cx", function(d, i) { return p[0]; }) //x position translated through projection function
.attr("cy", function(d, i) { return p[1]; }) //y position
.attr("r", function(d, i) { return 20; })
;
好的,但是如果我不想将半径设置为像素值(如上所示,设置为 20 像素),而是想将其设置为可适当缩放到投影的距离值,该怎么办?
简而言之,这归结为我的问题,"what's the pixel value of 20 km in this projection?"现在我知道这会因投影而有所不同——例如墨卡托 20 公里的圆在赤道处(接近球形)与在两极附近(高度拉长)看起来非常不同。
D3.js 可以解决许多其他巧妙的投影问题,我很惊讶地发现我无法在这里轻易地想出某种可接受的答案。有什么建议么?我在想,好吧,你可以计算一个圆的 lat/lon 个点,然后将其绘制为某种路径……这对 D3.js 来说感觉异常笨重。有没有更简单的方法?
好的,所以我想出了一个办法。它有点笨拙——感觉应该更自动化——但它确实有效。它只涉及使用圆点手动构建路径元素。下面是两个函数和使用示例:
//example — draws a 15 km circle centered on New York City using my existing projection
var circle = svg
.append("path")
.attr("d", "M"+circlePath( 40.7, -73.94, 15, projection).join("L")+"Z")
.attr("fill","none")
.attr("stroke","red")
.attr("stroke-width",2);
//this function generates the points for the path. If a projection is specified, it will
//automatically convert them to it. If not, it returns lat/lon positions.
//from with modifications
function circlePath(lat, lon, radius, projection) {
var intervals = 72;
var intervalAngle = (360 / intervals);
var pointsData = [];
for(var i = 0; i < intervals; i++){
pointsData.push(getDestinationPoint(lat, lon, i * intervalAngle, radius));
}
if(projection) {
pointsData2 = [];
for(i in pointsData) {
pointsData2.push([projection([pointsData[i][1],pointsData[i][0]])[0],projection([pointsData[i][1],pointsData[i][0]])[1]]);
}
return pointsData2;
} else {
return pointsData;
}
}
//function to get destination points given an initial lat/lon, bearing, distance
//from http://www.movable-type.co.uk/scripts/latlong.html
function getDestinationPoint(lat,lon, brng, d) {
var R = 6371; //earth's radius in km — change to whatever unit you plan on using (e.g. miles = 3959)
var deg2rad = Math.PI/180; var rad2deg = 180/Math.PI;
brng*=deg2rad; lat*=deg2rad; lon*=deg2rad;
var lat2 = Math.asin( Math.sin(lat)*Math.cos(d/R) +
Math.cos(lat)*Math.sin(d/R)*Math.cos(brng) );
var lon2 = lon + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat),
Math.cos(d/R)-Math.sin(lat)*Math.sin(lat2));
return [lat2*rad2deg, lon2*rad2deg];
}
这看起来应该很简单,但我正在努力弄明白。
在D3.js的地图投影上画圆很容易,例如
p = projection([-73.94,40.7]);
svg
.append("svg:circle")
.attr("cx", function(d, i) { return p[0]; }) //x position translated through projection function
.attr("cy", function(d, i) { return p[1]; }) //y position
.attr("r", function(d, i) { return 20; })
;
好的,但是如果我不想将半径设置为像素值(如上所示,设置为 20 像素),而是想将其设置为可适当缩放到投影的距离值,该怎么办?
简而言之,这归结为我的问题,"what's the pixel value of 20 km in this projection?"现在我知道这会因投影而有所不同——例如墨卡托 20 公里的圆在赤道处(接近球形)与在两极附近(高度拉长)看起来非常不同。
D3.js 可以解决许多其他巧妙的投影问题,我很惊讶地发现我无法在这里轻易地想出某种可接受的答案。有什么建议么?我在想,好吧,你可以计算一个圆的 lat/lon 个点,然后将其绘制为某种路径……这对 D3.js 来说感觉异常笨重。有没有更简单的方法?
好的,所以我想出了一个办法。它有点笨拙——感觉应该更自动化——但它确实有效。它只涉及使用圆点手动构建路径元素。下面是两个函数和使用示例:
//example — draws a 15 km circle centered on New York City using my existing projection
var circle = svg
.append("path")
.attr("d", "M"+circlePath( 40.7, -73.94, 15, projection).join("L")+"Z")
.attr("fill","none")
.attr("stroke","red")
.attr("stroke-width",2);
//this function generates the points for the path. If a projection is specified, it will
//automatically convert them to it. If not, it returns lat/lon positions.
//from with modifications
function circlePath(lat, lon, radius, projection) {
var intervals = 72;
var intervalAngle = (360 / intervals);
var pointsData = [];
for(var i = 0; i < intervals; i++){
pointsData.push(getDestinationPoint(lat, lon, i * intervalAngle, radius));
}
if(projection) {
pointsData2 = [];
for(i in pointsData) {
pointsData2.push([projection([pointsData[i][1],pointsData[i][0]])[0],projection([pointsData[i][1],pointsData[i][0]])[1]]);
}
return pointsData2;
} else {
return pointsData;
}
}
//function to get destination points given an initial lat/lon, bearing, distance
//from http://www.movable-type.co.uk/scripts/latlong.html
function getDestinationPoint(lat,lon, brng, d) {
var R = 6371; //earth's radius in km — change to whatever unit you plan on using (e.g. miles = 3959)
var deg2rad = Math.PI/180; var rad2deg = 180/Math.PI;
brng*=deg2rad; lat*=deg2rad; lon*=deg2rad;
var lat2 = Math.asin( Math.sin(lat)*Math.cos(d/R) +
Math.cos(lat)*Math.sin(d/R)*Math.cos(brng) );
var lon2 = lon + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat),
Math.cos(d/R)-Math.sin(lat)*Math.sin(lat2));
return [lat2*rad2deg, lon2*rad2deg];
}