等时圈是指从某点出发,以某种交通方式在特定时间内能到达的距离覆盖的范围。前段时间看到一个站点,该站点能够计算自驾、骑行、步行三种方式的等时圈。效果如下图
mapbox出品的图还是相当漂亮的,但毕竟是别人的东西,能自己做出来当然是好的(这个功能还是学生的时候就想做)。那就试试呗。
当然先看看mapbox是怎么做的,查看站点使用什么接口,如下图发生数据请求,接口连接:
对所有接口坐标进行分析,发现所有坐标的分布如下图:
从这些点就能猜出mapbox实现思路
参照这个思路,借助mapbox的接口完全能够自己实现等时圈功能。
- 生成网格,使用turf实现,上代码:
var centerPoint = turf.point([116.46, 39.92]);
//生成缓冲区
var buffered = turf.buffer(centerPoint, 12, {units: 'kilometers'});
//根据缓冲区生成bbox
var bbox = turf.bbox(buffered);
var cellSide = 1;
var options = {units: 'kilometers'};
//生成网格
var grid = turf.pointGrid(bbox, cellSide, options);
- 利用mapbox接口计算网格各点时间属性。
/**
*
* @param startPoint
* @param endPoints
* @param travelType walking driving,cycling
*/
function makeRequest(startPoint,endPoints,travelType){
var url= "https://api.mapbox.com/directions-matrix/v1/mapbox/"+travelType+"/"+startPoint.geometry.coordinates[0].toFixed(4)+","+startPoint.geometry.coordinates[1].toFixed(4)+";"
if(endPoints.length>0){
for(var i=0;i<endPoints.length;i++){
url=url+endPoints[i].geometry.coordinates[0].toFixed(4)+","+endPoints[i].geometry.coordinates[1].toFixed(4)+";";
}
url=url+"?sources=0&destinations=all&access_token=pk.eyJ1IjoicGV0ZXJxbGl1IiwiYSI6ImpvZmV0UEEifQ._D4bRmVcGfJvo1wjuOpA1g";
url=url.replace(";?","?")
$.ajax({
type : "get",
async:false,
url : url,
dataType : "json",
success : function(json){
var destinations=json.destinations;
var durations=json.durations;
for(var i=1;i<destinations.length;i++){
var point = turf.point(destinations[i].location,{duration:durations[0][i]});
points.push(point)
}
},
error:function(){
console.log('fail');
}
});
}
}
- 生成等值线
turf有生成等值线的功能
var collections = turf.featureCollection(features);
var breaks = [0, 600,700,800,900, 1200,1500, 1800,2100, 2400,2700, 3000, 3600, 4200, 4800,5400, 6000];
var lines = turf.isolines(collections, breaks, {zProperty: 'duration'});
看效果,虽然能实现,但是和mapbox效果感觉差了点什么,查看mapbox例子的代码,发现它使用该组件link进行等时线生成。
//它是这么使用的,但是实在看不懂这些参数是个啥意思
var data = [[0, 1, 0], [1, 2, 1], [0, 1, 0]];
var c = new Conrec;
c.contour(data, 0, 2, 0, 2, [1, 2, 3], [1, 2, 3], 3, [0, 1, 2]);
但是貌似这个组件效果是蛮好的。
function getNearestPoint(x,y,pts){
var ptsjson = turf.featureCollection(pts);
var targetPoint = turf.point([x, y]);
var nearest = turf.nearestPoint(targetPoint, ptsjson);
return nearest;
}
//这里为啥要这么写,琢磨了好久,就不解释了
xs=[];
ys=[];
for(var i=0;i<grid.features.length;i++){
if(xs.indexOf(parseFloat(grid.features[i].geometry.coordinates[0].toFixed(4)))==-1){
xs.push(parseFloat(grid.features[i].geometry.coordinates[0].toFixed(4)))
}
if(ys.indexOf(parseFloat(grid.features[i].geometry.coordinates[1].toFixed(4)))==-1){
ys.push(parseFloat(grid.features[i].geometry.coordinates[1].toFixed(4)))
}
}
var zs = [0,300, 600,900, 1200,1500, 1800,2100, 2400,2700, 3000,3300, 3600,3900, 4200,4500, 4800, 5100,5400, 5700,6000];
var data=[];
for (var y=0;y<ys.length;y++){
var rowData=[];
for (var x=0;x<xs.length;x++){
var nearest=getNearestPoint(xs[x],ys[y],points)
temperature=nearest.properties.temperature;
rowData.push(temperature);
}
data.push(rowData)
}
var c = new Conrec();
c.contour(data, 0, xs.length - 1, 0, ys.length - 1, xs, ys, zs.length, zs);
- 将等时线连成等时面
function polygonize(contourList){
var shapes = []
var results=[];
polygons = [];
for(var cIndex=0;cIndex<contourList.length;cIndex++){
var c=contourList[cIndex];
var level=c.level;
var shape=[];
for(var k=0;k<c.length;k++){
shape.push([c[k].x, c[k].y])
}
shape.push(shape[0]);
for (var p = shape.length; p<4; p++){
shape.push(shape[0]);
}
var polygon = turf.polygon([shape], { level: level });
shapes.push(polygon);
}
return shapes;
}
- 等时圈生成,并可视化
使用turf进行处理,将大圈内的小圈叠加掉。
function polygonize(contourList){
var shapes = []
var results=[];
polygons = [];
for(var cIndex=0;cIndex<contourList.length;cIndex++){
var c=contourList[cIndex];
var level=c.level;
var shape=[];
for(var k=0;k<c.length;k++){
shape.push([c[k].x, c[k].y])
}
shape.push(shape[0]);
for (var p = shape.length; p<4; p++){
shape.push(shape[0]);
}
var polygon = turf.polygon([shape], { level: level });
shapes.push(polygon);
}
for(var i=1;i<shapes.length;i++){
var level=shapes[i].properties.level;
var difference = turf.difference(shapes[i], shapes[i-1]);
if(difference!=null){
difference.properties.level=level;
results.push(difference);
}
}
return results;
}
没有正儿八经的进行可视化,但也能看到和mapbox的效果相差不多。但是这个方案存在一个问题,就是mapbox的接口由于网络问题太不稳定。为了保证稳定性需要自己开发该接口,当然可以使用高德的接口 link