需求:项目需要做一个救援动画效果
技术栈:react、openlayer、高德导航接口、天地图导航接口
零、选择导航接口
老大最开始建议使用天地图导航接口,测试后接口响应时间大约为30秒以上,达不到商用条件。因此选择高德的接口。
一、调用高德接口
1、1申请key
在高德官网注册用户,创建应用,申请key
地址:https://lbs.amap.com/api/webservice/summary
点击【获取key】,按提示进入控制台,创建应用,创建key
创建应用,在应用下边添加key。我需要调用webapi中的【路径规划API】所以选择的是Web服务
1.2测试接口
https://restapi.amap.com/v3/direction/driving?origin=[出发经度],[出发纬度]&destination=[目标经度],[目标纬度]&extensions=all&output=json&key=[key]
二、实现导航
2.1、拾取爆点坐标
爆点的样式:对点要素的样式设置为pipeBroken图片
const pipeBrokenStyle = new Style({
image: new Icon({
anchor: [0.5, 20],
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
src: pipeBroken,
}),
});
处理点击地图事件。获取鼠标点击的坐标,构建点要素,绘制到地图上。同时写入state。构建查询资源的参数
beginGetFeatureInfo = (event) => {
const {map} = this.props;
const {featuresVectorSource, clickFeature} = this.state;
if (clickFeature) {
if (featuresVectorSource.getFeatures().length > 0) {
try {
clearMap(map);
} catch (e) {
console.log(e);
}
}
}
const pipeBrokenFeature = new Feature({
geometry: new Point(event.coordinate),
});
pipeBrokenFeature.setStyle(pipeBrokenStyle);
const pointInfo = `point(${event.coordinate[0]} ${event.coordinate[1]})`;
this.setState({clickFeature: pipeBrokenFeature, pointInfo,}, () => {
featuresVectorSource.addFeature(pipeBrokenFeature);
this.getResources();
});
};
2.2、获取资源坐标
调用业务系统接口查询资源的坐标,此部分按自己的业务系统处理即可,不做展示。
2.3、坐标转换
此时具备了资源坐标和爆点的坐标,构造高德导航参数即可。但是项目中使用的是3857坐标系,需要进行转换。
3857转4326使用的是proj4组件
4326转gcj02使用的是coordtransform组件(
https://www.npmjs.com/package/coordtransform
https://github.com/wandergis/coordtransform)
从3857转到4326再转换到gcj02(高德使用的是gcj02)
trans3857To4326 = (coordinate) => {
return proj4('EPSG:3857', 'EPSG:4326', coordinate);
}
// 进行坐标转换 3857转wgs84
const coordClick = this.trans3857To4326(clickFeature.getGeometry().getCoordinates());
// 84再转gcj02
const clickCoord = coordtransform.wgs84togcj02(coordClick[0], coordClick[1]);
2.4、调用接口
调用接口。建议使用axios而不是Fetch。最开始使用fetch总是调用失败,卡了一天。同事说试试axios,一下就通了
/**
* 导航
*/
navigationRequest = (origin, destination) => {
// 高德
const url = `https://restapi.amap.com/v3/direction/driving?origin=${origin[0]},${origin[1]}&destination=${destination[0]},${destination[1]}&extensions=all&output=json&key=[自己的key]`
return axios.get(url)
.then(function (response) {
return response;
})
.catch(function (error) {
console.log(error)
return error;
});
}
2.5、解析数据
返回数据的结构参见接口说明。我只需要【行驶距离】、【预计行驶时间】和路径坐标。
返回的数据需要坐标转换。从gcj02转4326,再转3857.转换函数如下
trans4326To3857 = (coordinate) => {
return proj4('EPSG:4326', 'EPSG:3857', coordinate);
}
// 生成轨迹要素
drawLineFeature = (steps) => {
// 取出所有polyline拼接为键值对
let polylines = "";
for (let i = 0; i < steps.length; i++) {
polylines += steps[i].polyline;
polylines += ";";
}
const points = polylines.split(";");
const pointList = [];
const pointList3857 = [];
for (let i = 0; i < points.length - 1; i++) {
pointList.push(points[i].split(","));
pointList3857.push(this.trans4326To3857(coordtransform.gcj02towgs84(points[i].split(",")[0], points[i].split(",")[1])));
}
return new Feature({geometry: new LineString(pointList3857)});
}
2.6、生成动效
动效需要路径要素:reliefSuppliesDriveLine、移动的点:reliefSuppliesCarFeatureTemp。在图层reliefSuppliesCarVL中对要素reliefSuppliesCarFeatureTemp添加动画路径anim,获得控制器reliefSuppliesPlayController。
控制器可以用于终止动画
/**
* 开始物资动画
*/
startReliefSuppliesAnimation = () => {
const {
selectedReliefSupplies, reliefSuppliesDriveLine, reliefSuppliesCarVS, reliefSuppliesCarVL, reliefSuppliesCarFeature,
} = this.state;
const anim = new Path({
path: reliefSuppliesDriveLine,
rotate: true,
easing: linear,
duration: 0.25 * 60 * 1000,
revers: false,
repeat: 99999999,
});
reliefSuppliesCarVS.addFeature(reliefSuppliesDriveLine);
if (!reliefSuppliesCarFeature) {
const reliefSuppliesCarFeatureTemp = new Feature({
zIndex: 999999999999,
geometry: new Point(selectedReliefSupplies.getGeometry().getCoordinates()),
});
reliefSuppliesCarVS.addFeature(reliefSuppliesCarFeatureTemp);
// 动画
const reliefSuppliesPlayController = reliefSuppliesCarVL.animateFeature(reliefSuppliesCarFeatureTemp, anim);
this.setState({reliefSuppliesCarFeature: reliefSuppliesCarFeatureTemp, reliefSuppliesPlayController});
}
}
有效代码:reliefSuppliesPlayController.stop();
/**
* 清除救援物资动画
*/
clearReliefSuppliesAnimation = () => {
const {
reliefSuppliesCarVS, reliefSuppliesDriveLine, reliefSuppliesCarFeature, reliefSuppliesPlayController,
} = this.state;
if (reliefSuppliesPlayController) {
reliefSuppliesPlayController.stop();
}
if (reliefSuppliesCarFeature) {
reliefSuppliesCarVS.removeFeature(reliefSuppliesCarFeature);
this.setState({reliefSuppliesCarFeature: null});
}
if (reliefSuppliesDriveLine) {
reliefSuppliesCarVS.removeFeature(reliefSuppliesDriveLine);
this.setState({reliefSuppliesDriveLine: null});
}
reliefSuppliesCarVS.clear();
}