如何在地图上画曲线轨迹(贝塞尔曲线)

做过地图开发的朋友都知道,对于高德或者百度地图来说,通过polylineOptions.add(point);aMap.addPolyline(polylineOptions);方法得到的地图路径是折线,而且颜色无法渐变,很难达到美观的项目需求。在我的上一个项目中,需求是行程结束后得到平滑的路径曲线,而且要有渐变色。本人开始从高德的API中找了很久也无法实现这样的需求,甚至想过自己写一个贝塞尔曲线的算法,但是水平有限只好放弃。最后采用的方法是在地图上面加一个画板,自定义一个View,通过path.quadTo(x1,
y1, x2, y2)方法实现曲线需求,通过LinearGradient实现渐变色需求。以高德地图为例,详情如下:

1.在行程结束的时候记录下形成过程中所有的定位点的坐标值,保存在实体类的集合中。代码如下:

/**
 *
定位点的数据结构实体类
*/
public class FileRecordData {
    public double lat;
    public double lon;
}

在主Activity中,创建FileRecordData的List集合:

ArrayList<FileRecordData> fileDatas;

在每次定位重写的onLocationChanged方法里调用代码:

FileRecordData .lat = aLocation.getLatitude();

FileRecordData .lon = aLocation.getLongitude();

fileDatas.add(FileRecordData );

2.得到了所有的定位点,需要将所有的点都显示在可视化地图空间内:

Projection projection = aMap.getProjection();

public Point[] pointzu;

pointzu = new Point[fileDatas.size()];

if (fileDatas != null && 0 != fileDatas.size()) {

//获得起点和终点坐标
pointstart = new LatLng(fileDatas.get(0).lat, fileDatas.get(0).lon);
pointend = new LatLng(fileDatas.get(fileDatas.size() – 1).lat,
fileDatas.get(fileDatas.size() – 1).lon);
}
// 设置所有maker显示在View中
if (null != fileDatas && 0 != fileDatas.size()) {
LatLngBounds bounds;
double xMax = fileDatas.get(0).lat, xMin = fileDatas.get(0).lat, yMax = fileDatas
.get(0).lon, yMin = fileDatas.get(0).lon;
// 挑选出最大和最小的经纬度
for (int i = 0; i < fileDatas.size(); i++) {
if (fileDatas.get(i).lat >= xMax) {
xMax = fileDatas.get(i).lat;
}
if (fileDatas.get(i).lat <= xMin) {
xMin = fileDatas.get(i).lat;
}
if (fileDatas.get(i).lon >= yMax) {
yMax = fileDatas.get(i).lon;
}
if (fileDatas.get(i).lon <= yMin) {
yMin = fileDatas.get(i).lon;
}
}

//圈出一个能显示所有定位点的最左上、左下、右上、右下的区域的坐标
double xx = (xMax – xMin) / 5, yy = (yMax – yMin) / 5;
LatLng leftup = new LatLng(xMax + xx, yMin – yy);
LatLng leftdown = new LatLng(xMin – xx, yMin – yy);
LatLng rightup = new LatLng(xMax + xx, yMax + yy);
LatLng rightdown = new LatLng(xMin – xx, yMax + yy);
//将四个点放入可视化地图内
bounds = new LatLngBounds.Builder().include(pointstart)
.include(pointend).include(leftup).include(leftdown)
.include(rightdown).include(rightup).build();

aMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 20));
aMap.getUiSettings().setMyLocationButtonEnabled(false);
aMap.getUiSettings().setZoomControlsEnabled(false);

addOverlayToMap();
}

3.所有定位点的坐标已经有了,将定位坐标转化为屏幕坐标:

for (int i = 0; i < fileDatas.size(); i++) {
LatLng point = new LatLng(fileDatas.get(i).lat,
fileDatas.get(i).lon);
pointzu[i] = projection.toScreenLocation(point);
}

4.将pointzu传到自定义的View类中。重写onMeasure和onDraw方法。

public Point[] pointzu;

private Paint barPaint;

private float x0,y0,x1,y1;//起始点和终点坐标

/**
* 初始化画笔等
*/
private void init() {
barPaint = new Paint();
barPaint.setColor(Color.parseColor(“#ed6731”));
barPaint.setColor(0xff15B503);
barPaint.setStrokeWidth(10);
barPaint.setAntiAlias(true);// 设置画笔为无锯齿
barPaint.setStyle(Style.STROKE);// 设置为空心
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);

setMeasuredDimension(widthSize, heightSize);
}

@Override
protected void onDraw(Canvas canvas) {
Path pointPath = new Path();
if (pointzu != null) {
float firstX = (float) pointzu[0].x;
float firstY = (float) pointzu[0].y;

/**
  *
设置曲线渐变色

*/

LinearGradient lgGradient = new LinearGradient(x0, y0, x1, y1,
new int[] { 0xff00ff00, 0xffff0000, 0xff0000ff }, null,
TileMode.MIRROR);
barPaint.setShader(lgGradient);

/**
  *
设置贝塞尔曲线

*/

pointPath.reset();
pointPath.moveTo(firstX, firstY);
x0 = (float) pointzu[0].x;
y0 = (float) pointzu[0].y;
x1 = (float)pointzu[pointzu.length-1].x;
y1 = (float)pointzu[pointzu.length-1].y;

for (int n = 1; n < pointzu.length; n++) {
float x = (float) pointzu[n].x;
float y = (float) pointzu[n].y;
pointPath.quadTo(firstX, firstY, (firstX + x) / 2, (firstY + y) / 2);
firstX = x;
firstY = y;
}
pointPath.quadTo((firstX+(float)pointzu[pointzu.length-2].x)/2, (firstY+(float)pointzu[pointzu.length-2].y)/2, firstX, firstY);//三次贝塞尔曲线
canvas.drawPath(pointPath, barPaint);

/**
  *
给曲线添加起点和终点图片
*/

Bitmap bitmapStart = BitmapFactory.decodeResource(getResources(), R.drawable.riding_start_point);
Bitmap bitmapEnd = BitmapFactory.decodeResource(getResources(), R.drawable.riding_end_point);

/**
  *
因为图片的坐标不是图片中心作为参照点的,而是从左上角作为参照点,所有需要偏移量将参照点移到图片中心。
*/

canvas.drawBitmap(bitmapStart, x0-25, y0-34, barPaint);
canvas.drawBitmap(bitmapEnd, x1-25, y1-34, barPaint);

}else {
System.out.println(“pointzu = null”);
}
super.onDraw(canvas);
}

5.进行到上一步就可以看到地图上的平滑渐变曲线啦,不过这个例子是行程结束得到的曲线,并且地图是不能放大的,因为已经被画板盖住了;如果需求是在行驶过程中的路线变为曲线,那么需要每次定位后都将重新刷新一次自定义View,不过因为被画板盖住,所以地图依然无法放大,这一点目前还没有太好的解决办法,如果有大神知道,请留言指教,谢谢!

效果图如下:

转载自:https://blog.csdn.net/jiack50/article/details/49796005

You may also like...