百度地图-地图SDK经常使用方法总结(一)

最近在作关于地图的项目,这里将用到的关于地图SDK的相关方法作一个总结概括。html

初始化

SDKInitializer.initialize(Context) 
复制代码

MapView 与 BaiduMap

简单配置

mapView.showScaleControl(showScaleControl);//是否显示比例尺
        mapView.showZoomControls(showZoomControl);//是否显示缩放按钮
        baiduMap.setCompassEnable(compassEnable);//是否显示指南针
        baiduMap.setTrafficEnabled(boolean enabled)//设置是否打开交通图层
        baiduMap.showMapPoi(false);//是否显示地图标注(各类道路地点等)
        
        //设置中心坐标与缩放比例
        //地点坐标能够在如下网址得到http://api.map.baidu.com/lbsapi/getpoint/index.html
        //默认北京天安门的坐标,缩放等级最大为4
        LatLng center = new LatLng(latitude, longitude);
        MapStatus.Builder builder = new MapStatus.Builder();
        builder.target(center);
        if (4 <= zoom && zoom <= 20) {
            builder.zoom(zoom);
        }
        MapStatus mapStatus = builder.build();
        baiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(mapStatus));
复制代码
/** * 地图显示区域 * * @param latLng 判断改点是否在屏幕中,若不在则地图缩放至能包含该点 */
    public void setMapCenterBounds(LatLng latLng) {
        //当前地图区域不包含这个点,则须要缩放
        boolean needZoom = !baiduMap.getMapStatus().bound.contains(latLng);
        if (needZoom) {
            LatLngBounds.Builder latLngBuilder = new LatLngBounds.Builder();
            latLngBuilder.include(latLng);
            //更新显示区域
            baiduMap.setMapStatus(MapStatusUpdateFactory.newLatLngBounds(latLngBuilder.build()));
            //缩放一倍,留出边界
            MapStatusUpdate mapStatusUpdate = MapStatusUpdateFactory.zoomBy(-1f);
            baiduMap.setMapStatus(mapStatusUpdate);
        }
    }
    
    //若是是须要显示一个集合中的全部坐标点,则只需遍历该集合而且连续调用include(Latlng latlng)便可
复制代码

MapView:

用来显示地图的控件,手势识别由该View处理,同时该View的生命周期应该与Activity或Fragment同步java

<com.baidu.mapapi.map.MapView
            android:id="@+id/mapView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
复制代码

addView():

基于屏幕坐标或者经纬度添加一个View,这里的坐标指定的是View的左下角坐标android

Point point = new Point(50,240);
        ImageView imageView = new ImageView(this);
        imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        imageView.setImageDrawable(getResources().getDrawable(R.drawable.huaji));
        imageView.setBackgroundColor(Color.BLACK);
        MapViewLayoutParams params = new MapViewLayoutParams.Builder()
                .layoutMode(MapViewLayoutParams.ELayoutMode.absoluteMode)//添加View以屏幕坐标为准
                .point(point)//屏幕坐标
// .layoutMode(MapViewLayoutParams.ELayoutMode.mapMode) //添加View以经纬度坐标为准
// .position(new LatLng(31.1519, 121.555972))//经纬度坐标
                .align(MapViewLayoutParams.ALIGN_LEFT,MapViewLayoutParams.ALIGN_BOTTOM)//对其方式,(x方向,y方向)
                .height(300)//高
                .width(200)//宽
                .yOffset(0)//y方向偏移量
                .build();
        mMapView.addView(imageView,params);
复制代码

inRangeOfView(float x, float y)

判断当前触摸点是否在地图上,返回booleangit

BaiduMap:

地图控制器-手势监听、添加覆盖物、控制地图各类相关参数算法

setMapType(int type)

设置地图模式
BaiduMap.MAP_TYPE_NORMAL 普通图
BaiduMap.MAP_TYPE_SATELLITE 卫星图
BaiduMap.MAP_TYPE_NONE 卫星图api

addOverlay(OverlayOptions options)、addOverlays(java.util.List options)

添加覆盖物ide

//绘制多个点
        List<LatLng> points = new ArrayList<>();
        points.add(new LatLng(31.153517, 121.553807));
        points.add(new LatLng(31.154073, 121.556924));
        points.add(new LatLng(31.15123, 121.557688));
        points.add(new LatLng(31.150727, 121.554571));
        //点的纹理图片
        BitmapDescriptor icon = BitmapDescriptorFactory.fromResource(R.drawable.icon_mark1);
        List<OverlayOptions> markerOptions = new ArrayList<>();
        //循环设置各个点的位置及纹理
        for (int i = 0; i < points.size(); i++) {
            markerOptions.add(new MarkerOptions()
            .extraInfo(new Bundle())//传递相关数据,在点击事件OnMarkerClickListener回调中能够获取该信息
            .position(points.get(i))
            .icon(icon));
        }
        //添加全部的点
        List<Overlay> overLays=mBaiduMap.addOverlays(markerOptions);
        
        //若要移除这些点(不要和集合的remove()搞混了)
        //for (Overlay overlay : overlays) {
        // overlay.remove();
        //}
复制代码

//绘制选段(轨迹)
        List<LatLng> trackPoints = new ArrayList<>();
        trackPoints.add(new LatLng(31.154073, 121.556924));
        trackPoints.add(new LatLng(31.154081, 121.556969));
        trackPoints.add(new LatLng(31.150727, 121.554571));
        trackPoints.add(new LatLng(31.153517, 121.553807));
        trackPoints.add(new LatLng(31.15123, 121.557688));
        //线段(轨迹)自定义纹理
        List<BitmapDescriptor> customTextureList = new ArrayList<>();//自定义纹理集合
        BitmapDescriptor lineBitmap = BitmapDescriptorFactory.fromResource(R.drawable.line_arrow);//转换图片
        customTextureList.add(lineBitmap);//将图片添加到纹理集合中
        List<Integer> textureIndex = new ArrayList<>();//自定义线段纹理集合的下标
        textureIndex.add(0);
        //线段(轨迹)Options
        OverlayOptions polylineOptions = new PolylineOptions()
                .width(15)//线段宽度
                .extraInfo(new Bundle())//传递相关数据,在点击事件OnMarkerClickListener回调中能够获取该信息
                .dottedLine(true)//虚线(自定义纹理是利用虚线来实现的)
                .textureIndex(textureIndex)//虚线每段取用的纹理下标集合(根据textureIndex集合中的数值去取customTextureList中的纹理图片)
                .customTextureList(customTextureList)//自定义纹理集合
                .points(trackPoints);//线段拐点
        //添加线段覆盖物
        Overlay overlay=mBaiduMap.addOverlay(polylineOptions);
        
        //若要移除该线段
        //overlay.remove();
复制代码

//绘制多边形(围栏,其余还有)
        List<LatLng> points = new ArrayList<>();
        points.add(new LatLng(31.153517, 121.553807));
        points.add(new LatLng(31.154073, 121.556924));
        points.add(new LatLng(31.15123, 121.557688));
        points.add(new LatLng(31.150727, 121.554571));
        //围栏配置
        OverlayOptions polygonOptions = new PolygonOptions()
                .extraInfo(new Bundle())//传递相关数据,在点击事件OnMarkerClickListener回调中能够获取该信息
                .points(points)//顶点坐标集合
                .fillColor(Color.parseColor("#4DF44336"))//填充色
                .stroke(new Stroke(5, Color.parseColor("#4DF44336")));//边的宽度、颜色
        //添加多边形
        Overlay overlay=mBaiduMap.addOverlay(polygonOptions);
        //若要移除该多边形
        //overlay.remove();
复制代码

OnMarkerClickListener()

覆盖物点击事件函数

//Marker点击事件(Marker-Overlay的子类,便是本身绘制的点、线、icon等对象)
        mBaiduMap.setOnMarkerClickListener(new BaiduMap.OnMarkerClickListener() {
            @Override
            public boolean onMarkerClick(Marker marker) {
                Toast.makeText(MainActivity.this, marker.getPosition().toString(), Toast.LENGTH_SHORT).show();
                Bundle bundle=marker.getExtraInfo();//获取该对象的信息
                return false;
            }
        });
复制代码

InfoWindow

地图弹框工具

//自定义一个InfoWindow布局
            View infoWindowView = LayoutInflater.from(Utils.getContext()).inflate(R.layout.map_marker_item, mapView, false);
            infoWindowBtnClose = infoWindowView.findViewById(R.id.ivClose);//关闭按钮

            infoWindowBtnClose.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    hideInfoWindow();//隐藏InfoWindow
                }
            });
            //实例化InfoWindow,infoWindowYOffsetPosition:y方向的偏移量
            InfoWindow infoWindow=new InfoWindow(infoWindowView,latLng,infoWindowYOffsetPosition);
            //显示InfoWindow
            baiduMap.showInfoWindow(infoWindow);
            
            //更新InfoWindow显示的位置
            //infoWindow.setPosition(LatLng mPosition)
复制代码

获取全部显示的InfoWindow布局

baiduMap.getAllInfoWindows()//地图sdk5.4.0版本及以上 获取已添加的全部InfoWindow对象
复制代码

clear()

清空地图全部的 Overlay 覆盖物以及 InfoWindow

baiduMap.clear();
复制代码

监听

setOnBaseIndoorMapListener(BaiduMap.OnBaseIndoorMapListener listener)//设置室内图模式监听者
	setOnMapClickListener(BaiduMap.OnMapClickListener listener)//设置地图单击事件监听者
	setOnMapLongClickListener(BaiduMap.OnMapLongClickListener listener)//设置地图长按事件监听者
	setOnMapTouchListener(BaiduMap.OnMapTouchListener listener)//设置触摸地图事件监听者
	setOnMapDoubleClickListener(BaiduMap.OnMapDoubleClickListener listener)//设置地图双击事件监听者
	setOnMapDrawFrameCallback(BaiduMap.OnMapDrawFrameCallback callback)//设置百度地图在每一帧绘制时的回调接口,该接口在绘制线程中调用
	setOnMapLoadedCallback(BaiduMap.OnMapLoadedCallback callback)//设置地图加载完成回调
	setOnMapRenderCallbadk(BaiduMap.OnMapRenderCallback callback)//设置地图渲染完成回调
	setOnMapStatusChangeListener(BaiduMap.OnMapStatusChangeListener listener)//设置地图状态监听者
	setOnMarkerClickListener(BaiduMap.OnMarkerClickListener listener)//设置地图 Marker 覆盖物点击事件监听者,自3.4.0版本起可设置多个监听对象,中止监听时调用removeMarkerClickListener移除监听对象
	setOnMarkerDragListener(BaiduMap.OnMarkerDragListener listener)//设置 Marker 拖拽事件监听者
	setOnMyLocationClickListener(BaiduMap.OnMyLocationClickListener listener)//设置定位图标点击事件监听者
	setOnPolylineClickListener(BaiduMap.OnPolylineClickListener listener)//设置地图 Polyline 覆盖物点击事件监听者
复制代码

三种特殊坐标

//得到地图显示的区域范围
 	LatLngBounds bounds = baiduMap.getMapStatus().bound;
复制代码

屏幕右上角(东北)

bounds.northeast
复制代码

屏幕左下角(西南)

bounds.southwest
复制代码

屏幕中心

bounds.getCenter()
复制代码

其余工具

//计算p一、p2两点之间的直线距离,单位:米
DistanceUtil. getDistance(p1, p2);

//计算northeast, southwest两点构成矩形的地理面积,即东北、西南坐标。单位:平方米
AreaUtil.calculateArea(northeast, southwest);

//判断点pt是否在位置点列表mPoints构成的多边形内。
SpatialRelationUtil.isPolygonContainsPoint(mPoints,pt);

//判断点pt是否在位置点列表mPoints构成的多边形内。
SpatialRelationUtil.isPolygonContainsPoint(mPoints,pt);

//获取线段上距离目标点最近的点坐标
//pt1是点pt在折线(由points构成)上最近的点。
//points为构成polyline的点列表
LatLng pt1 = SpatialRelationUtil.getNearestPointFromLine(points,pt);
复制代码

轨迹回放

去噪

根据速度去出移动距离过大及近似未移动点

public class NoiseFilter {
    private static LatLng lastLatLng;//比较基准点

    private static int minDistance=14*60;//步行速度 1.39m/s x 上传间隔 10s
    private static int maxDistance=330*60;//开车速度(120km/h) 33m/s x 上传间隔 10s

    public static LatLng filter(LatLng nextLatlng) {
        if (lastLatLng == null) {
            lastLatLng = nextLatlng;
            return nextLatlng;
        } else {
            double distance = DistanceUtil.getDistance(lastLatLng, nextLatlng);
            if (distance <= minDistance||distance>maxDistance) {//小于 minDistance 认为未移动,大于 maxDistance 认为是信号偏移,忽略该点
                return null;
            } else {
                lastLatLng=nextLatlng;
                return nextLatlng;
            }
        }
    }
}
复制代码

抽稀

道格拉斯-普克算法,选取一个起始点,一个结束点进行连线,而后分别计算这两点之间全部的连续点对于该条直线的垂直距离。若是距离太小,则近似认为该点在这条直线上,抛弃该点。
以下图,抛弃1,3,4,5点,保留2,6点

/** * 轨迹点抽稀算法 */
public class DouglasPeuckerUtil {
    public static List<LatLng> DouglasPeucker(List<LatLng> points, double epsilon) {
        // 找到最大阈值点,即操做(1)
        double maxH = 0;
        int index = 0;
        int end = points.size();
        for (int i = 1; i < end - 1; i++) {
            double h = H(points.get(i), points.get(0), points.get(end - 1));
            if (h > maxH) {
                maxH = h;
                index = i;
            }
        }

        // 若是存在最大阈值点,就进行递归遍历出全部最大阈值点
        List<LatLng> result = new ArrayList<>();
        if (maxH > epsilon) {
            List<LatLng> leftPoints = new ArrayList<>();// 左曲线
            List<LatLng> rightPoints = new ArrayList<>();// 右曲线
            // 分别提取出左曲线和右曲线的坐标点
            for (int i = 0; i < end; i++) {
                if (i <= index) {
                    leftPoints.add(points.get(i));
                    if (i == index)
                        rightPoints.add(points.get(i));
                } else {
                    rightPoints.add(points.get(i));
                }
            }

            // 分别保存两边遍历的结果
            List<LatLng> leftResult;
            List<LatLng> rightResult;
            leftResult = DouglasPeucker(leftPoints, epsilon);
            rightResult = DouglasPeucker(rightPoints, epsilon);

            // 将两边的结果整合
            rightResult.remove(0);
            leftResult.addAll(rightResult);
            result = leftResult;
        } else {// 若是不存在最大阈值点则返回当前遍历的子曲线的起始点
            result.add(points.get(0));
            result.add(points.get(end - 1));
        }
        return result;
    }

    /** * 计算点到直线的距离 * * @param p * @param s * @param e * @return */
    private static double H(LatLng p, LatLng s, LatLng e) {
        double AB = DistanceUtil.getDistance(s, e);
        double CB = DistanceUtil.getDistance(p, s);
        double CA = DistanceUtil.getDistance(p, e);

        double S = helen(CB, CA, AB);
        double H = 2 * S / AB;

        return H;
    }

    /** * 海伦公式,已知三边求三角形面积 * * @param CB * @param CA * @param AB * @return 面积 */
    private static double helen(double CB, double CA, double AB) {
        double p = (CB + CA + AB) / 2;
        double S = Math.sqrt(p * (p - CB) * (p - CA) * (p - AB));
        return S;
    }
}
复制代码

实现轨迹回放

部分截图

思路:

  1. 不停绘制连续的点,实现轨迹的移动
    问题:当移动的距离过远时,须要分割绘制的点过多,致使卡顿
  2. 先绘制一条,而后移除,再绘制更长的一条线段
    问题:因为不停的移除重绘,轨迹会出现闪烁的现象
  3. 对于思路2进行优化,先绘制下一条更长的线段,再移除前一段短一些的线段
    问题:当数据点过多,轨迹过长,则先后两条线段同时存在且愈来愈长时,内存占用也愈来愈高,有OOM的风险
  4. 对于思路一、思路2进行优化,分段绘制,每当绘制点达到100个时则再也不移除当前绘制的线段,而是清空待绘制的轨迹点的集合,并将绘制起始点移动到这个第100个点

对思路4举例:
说明:

  1. 绘制下一段100个点表明的线段用的是思路2中的方法,也能够用思路1中的方法。切换方法也很简单,准备绘制下一条线段以前,不移除当前线段,而是将下一条线段的起始点移动到当前线段的最后一个点上
  2. 轨迹绘制时,List的处理请不要用subList()
  3. 不要不停的new 对象,在方法体外部新建对象,而后修改其持有的引用
/** * 轨迹回放 */
    private void drawTrackLineWithAnim(List<LatLng> trackPoints) throws Exception {
        //全部轨迹点的集合
        if (trackPoints.size() < 2) {
            throw new Exception("Points.size must greater than 2");
        }
        
        //轨迹计时器
        trackTimer = new Timer();
        
        trackReset();//轨迹绘制初始化
        
        //待绘制轨迹的点集合
        drawLatList = new ArrayList<>();
        drawLatList.add(cacheList.get(0));//从第一个点开始绘制轨迹路径
        
        trackPeriodCount = 1;//初始绘制第0点到第1个点
        
        enableTrackPlay = true;//控制绘制暂停与否,true为容许绘制,false为不容许绘制
        //轨迹绘制任务
        trackTimerTask = new CustomIntervalTimerTask() {//自定义能改变计时间隔的TimerTask
            @Override
            public void run() {
                if (!enableTrackPlay) {//暂停或继续轨迹播放
                    return;
                }
                if (trackPeriodCount >= cacheList.size()) {//绘制结束,从新开始
                    trackPeriodCount = 1;
                    handler.post(() -> {
                        stopTranslatingMarkerAnim(); //中止动画效果
                        trackReset();//重置绘制工做,从新开始绘制
                        drawLatList.clear();//清空待绘制点的集合
                        drawLatList.add(cacheList.get(0));//从0开始
                    });
                } else {
                    //添加一个点进行绘制
                    drawLatList.add(cacheList.get(trackPeriodCount));
                    if (drawLatList.size() < 2) {
                        trackPeriodCount++;
                        return;
                    }
                    
                    //移动位置小图标
                    markerTransformation(
                            marker
                            , drawLatList.get(drawLatList.size() - 2)
                            , drawLatList.get(drawLatList.size() - 1));
                    //移除上一段(移除得是本段100个点内的线段)
                    removeLastTrackLine();
                    //绘制线段
                    oldOverlay = MapHelper.getInstance().drawTrackLine(drawLatList);
                    //判断当前最新绘制点是否在屏幕内,不在则自动缩放
                    MapHelper.getInstance().setMapCenterBounds(drawLatList.get(drawLatList.size() - 1));
                    trackPeriodCount++;
                    //每100个点换一条线段
                    if (trackPeriodCount > 0 && trackPeriodCount % 100 == 0) {
                        drawLatList.clear();//清空待绘制点的集合
                        drawLatList.add(cacheList.get(trackPeriodCount - 1));//将绘制点的起始点设置为本地的第100个点
                        oldOverlay = null;
                    }
                }
            }
        };
        //开始轨迹回听任务
        trackTimer.schedule(trackTimerTask, 200, trackPlayPeriod);
    }
    
     /** * 轨迹绘制初始化 */
    private void trackReset() {
        MapHelper.getInstance().mapGC(); //回收一下Map资源
        MapHelper.getInstance().clearAllMarkers();//绘制前清空全部其余无关标记
        MapHelper.getInstance().setMapCenter(trackPoints.get(0).latitude, trackPoints.get(0).longitude, 18f); //将地图中心移至起始点
        //绘制一个起点
        MapHelper.getInstance().drawSingleMember(cacheList.get(0), MapHelper.ICON_START).setZIndex(1);
        //绘制移动点
        marker = MapHelper.getInstance().drawAnimSingleMember(cacheList.get(0));
        //启动移动点的呼吸动画
        startTranslatingMarkerAnim();
    }
    
        /** * 开启轨迹播放中移动Marker的呼吸动画 */
    private void startTranslatingMarkerAnim() {
        if (marker != null) {
            Marker markerBreathArrow = (Marker) marker[0];
            markerBreathArrow.setAnimation(getScaleSmallerAnimation());
            markerBreathArrow.startAnimation();

            Marker markerBreathBg = (Marker) marker[1];
            markerBreathBg.setAnimation(getScaleBiggerAnimation());
            markerBreathBg.startAnimation();
        }
    }

    /** * 终止轨迹播放中移动Marker的呼吸动画 */
    private void stopTranslatingMarkerAnim() {
        if (marker != null) {
            Marker markerBreathArrow = (Marker) marker[0];
            markerBreathArrow.cancelAnimation();
            Marker markerBreathBg = (Marker) marker[1];
            markerBreathBg.cancelAnimation();
        }
    }

    /** * 移除上一段绘制的线段 */
    private void removeLastTrackLine() {
        if (oldOverlay != null) {
            oldOverlay.remove();
            oldOverlay = null;
        }
    }

    /** * 建立无线循环缩放动画(一倍-2倍-1倍)-背景气泡 */
    private Animation getScaleBiggerAnimation() {
        ScaleAnimation mScale = new ScaleAnimation(1f, 1.5f, 1f);
        mScale.setDuration(1500);
        mScale.setRepeatMode(Animation.RepeatMode.RESTART);//动画重复模式
        mScale.setRepeatCount(-1);//动画重复次数
        return mScale;
    }

    /** * 建立无线循环缩放动画(一倍-0.5倍-1倍)-箭头 */
    private Animation getScaleSmallerAnimation() {
        ScaleAnimation mScale = new ScaleAnimation(1f, 0.7f, 1f);
        mScale.setDuration(1500);
        mScale.setRepeatMode(Animation.RepeatMode.RESTART);//动画重复模式
        mScale.setRepeatCount(-1);//动画重复次数
        return mScale;
    }


    /** * 移动 轨迹中表明物体的Marker 位置 * 这里是呼吸泡和箭头 * @param marker 须要移动的Marker * @param latLngs 0当前位置点 1目标移动位置点 */
    private void markerTransformation(Overlay[] marker, LatLng... latLngs) {
        float rotate = (float) MapHelper.getInstance().getAngle(latLngs[0], latLngs[1]);
        ((Marker) (marker[0])).setPosition(latLngs[latLngs.length - 1]);
        ((Marker) (marker[1])).setPosition(latLngs[latLngs.length - 1]);
        ((Marker) (marker[0])).setRotate(rotate);
    }

    /** * 轨迹回放分割相邻两点为更多的连续的点 * 利用三角函数及距离算中间点坐标 * @param latLngLast 前一个点的经纬度 * @param latNext 后一个点的经纬度 */
    private List<LatLng> splitLatLng(LatLng latLngLast, LatLng latNext) {
        final double a_x = latLngLast.latitude;
        final double a_y = latLngLast.longitude;
        final double b_x = latNext.latitude;
        final double b_y = latNext.longitude;
        final double distance = DistanceUtil.getDistance(new LatLng(a_x, a_y), new LatLng(b_x, b_y));
        final double partX = Math.abs(a_x - b_x) / distance;
        final double partY = Math.abs(a_y - b_y) / distance;
        final List<LatLng> list = new ArrayList<>();
        LatLng latLng;
        for (int i = 0; i < distance; i++) {
            //每隔10米切割一个点
            if (i % 10 == 0) {
                double x;
                if (a_x < b_x) {
                    x = a_x + partX * i;
                } else if (a_x > b_x) {
                    x = a_x - partX * i;
                } else {
                    x = a_x;
                }
                double y;
                if (a_y < b_y) {
                    y = a_y + partY * i;
                } else if (a_y > b_y) {
                    y = a_y - partY * i;
                } else {
                    y = a_y;
                }
                latLng = new LatLng(x, y);
                list.add(latLng);
            }
        }
        list.add(latNext);
        return list;
    }

复制代码

后续

将会继续总结概括定位及室内图的实现方法

相关文章
相关标签/搜索