百度地圖-地圖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;
    }

複製代碼

後續

將會繼續總結概括定位及室內圖的實現方法

相關文章
相關標籤/搜索