Amap【高德】/Google-开发,无人机航迹规划演示

   
ps:又到了周五,才有时间来写点博客记录下,这篇文章的所描述的内容是我2017年后 做的一个功能,也是我们的产品经理结合 航迹规划 以及我们所提供的 能做到的一些地图开发结合,做的一个自创性的 开发吧。其实开始做的时候挺不自信的,因为第一,地图sdk 并不是非常了解,再说,里面有好多效果和功能  ,主要是一个动态修改航点和航线这个效果,在评估的时候,着实让我觉得头疼。而且领导还说项目很急,让我一周把航迹规划和电子围栏的开发 做完,我和ios 当初可是评估后给了 3个星期的工作日的。他要我们一周搞完,心里挺虚的。 本身博主就是个菜鸡,毕业才半年,虽说在公司实习到现在有一年了,但是毕竟改变不了菜的现实。没办法,顶头干吧。在开发中,越搞越happay  ,常常加班到11点,觉得蛮有意思的,显然不是纯粹的使用地图的简单功能来完成开发的,而是有点难度的。听意外的,4个工作日,就把航迹规划和电子围栏 搞完了。满有成就感的。

 博主以前是学文科的,废话有点多,不要见怪。整个效果展示的比较长,但是呢,gif 又只能上传2M 的图,无奈只能一个一个截取了。fps 设置的也有点低,没办法,gif 2M 实在太小。将就看吧。


1.

2.

3.


一:这次只展示 航迹规划这一个项目,电子围栏,我已经在之前文章中写过了。

二:本次功能要点我做个简单的阐述。

①.要分清楚 你需要建立几个集合 分别是:lList<Latlng>,List<Marker>,List<PolyLine>,代表的分别是,坐标点,标记点,航线。

②.在UI 给的图片上 绘制 标号,技术好的应该都会,我下面会把代码贴出来

③.就是吧Marker的.draggable(true),前提 先吧拖拽功能打开。才可以操作

④重点是这个 线拖拽的问题,分别位2种情况

1)例如图一  在没有2 出现的情况下只有一种情况,拖拽 marker 1 将 线一拖动,

2)例如2 在拖拽2的时候会牵扯到 2根线,2根线要同步刷新。

3)额外说的一个,我的电子围栏中 更复杂一点,比如第一个点生成。 逻辑都和这个一样,但是在最后一根线的时候,他要围城一个围栏,在没围成之前,拖拽marker 1的时候 只有一根线,拖拽2 会有2根,拖拽最后一个Marker 也只有一根,但是一旦确认 围栏 形成,最后一个Marker会和第一个Marker 产生一条线,这时候在拖拽 1和最后一个marker 就需要联动,所有的点都是 2根线 在动。这个 就从代码上没什么技术含量,但是逻辑上 需要思考好多。这次就不说这个,我下面会把代码贴出来。



开始贴代码了。

重点的几个说完 就直接把所有的贴出来。

 private ArrayList<LatLng> mPfLatLngList = new ArrayList<>(); // latlng的数量比marker的数量多一,因为当前位置坐标也记录下来了
    private ArrayList<PathPlanBean> uploadList = new ArrayList<>(); //上传航迹规划列表的集合
    private ArrayList<Marker> mPfMarkerList = new ArrayList<>();
    private ArrayList<Polyline> mPfPolylineList = new ArrayList<>();
    
 /**
     * Marker地图点击监听事件
     *
     * @param latLng
     */
    @Override
    public void onMapClick(LatLng latLng) {
        LatLng curLatlng = latLng;
        // 航迹规划
        if (isOpenFlightRoutePlan && mPfMarkerList.size() < 50 && isWayPointMode_state == GduConfig.FpClose) {
            LatLng lastLatlng;
            if (!mPfLatLngList.isEmpty()) { // 这里判断不为空,是保证已经 有飞机的坐标点 已经添加
                if (AMapUtils.calculateLineDistance(currentphoneLatlng, curLatlng) > fp_validityRange) {
                    txhToast.show(getString(R.string.Label_Map_FlightRoute_flightpoit_invalidation));
                } else {
                    int tagNum = mPfLatLngList.size();//Marker 数_标
                    mPfMarkerList.add(mapMarkerTool.addPfMarker(curLatlng, tagNum));
                    mPfLatLngList.add(curLatlng);
                    mIv_Fp_state.setImageResource(R.drawable.bg_map_fp_go);
                    if (tagNum == 1) {
                        LatLng droneLat = mPfLatLngList.get(0);
                        mPfPolylineList.add(mapMarkerTool.addPolyDottedLine(droneLat, curLatlng));
                        mPfFlightDisList.add(calculateFlightDist(droneLat, curLatlng));
                    } else if (tagNum > 1) {
                        lastLatlng = mPfLatLngList.get(mPfLatLngList.size() - 2);
                        mPfPolylineList.add(mapMarkerTool.addPolyDottedLine(lastLatlng, curLatlng));
                        mPfFlightDisList.add(calculateFlightDist(lastLatlng, curLatlng));
                    }
                    setEraserState(true);//有Marker增加时候
                }
            }
        } else if (isOpenFlightRoutePlan && mPfMarkerList.size() == 50 && isWayPointMode_state == GduConfig.FpClose) {
            txhToast.show(getString(R.string.Label_Map_FlightRoute_flightpoit_overflow));
        } else if (isOpenFlightRoutePlan && mPfMarkerList.size() < 50 && isWayPointMode_state == GduConfig.FpGo) {
            // 即当开始航迹规划,在点击地图,无响应,不增加点
        }

在地图上增加点 必须在onMapClick 中操作,在onMapClick 获取最基本的Latlng 数据,并绘制 航线。

add  Marker 

 /**
     * <p>根据经纬度往地图上添加marker</p>
     * <p>[航迹规划]</p>
     *
     * @param lagLng    当前点击的 下的坐标
     * @param markerNum 计数
     * @return
     */
    public Marker addPfMarker(LatLng lagLng, int markerNum) {
     MarkerOptions options= options = new MarkerOptions().draggable(true);
        options.anchor(0.5f, 1f)
                .position(lagLng)
                .icon(BitmapDescriptorFactory.fromBitmap(getBitMap(markerNum)));

        return aMap.addMarker(options);
    }

add  PolyLine

 /**
     * <p>绘制两点之间线段</p>
     * <p>虚线</p>
     *
     * @param beginlatLng 起点
     * @param endLatLng   终点
     * @return
     */
    public Polyline addPolyDottedLine(LatLng beginlatLng, LatLng endLatLng) {

        PolylineOptions options = new PolylineOptions();
        options.add(beginlatLng, endLatLng)
                .width(10)
                .color(Color.rgb(255, 121, 24))
                .setDottedLine(true);// 虚线

        return aMap.addPolyline(options);
    }

计算距离:

    /**
     * <p>计算总的飞行距离</p>
     *
     * @param beginlatLng
     * @param endLatLng
     */
    private float calculateFlightDist(LatLng beginlatLng, LatLng endLatLng) {
        float onceDist = AMapUtils.calculateLineDistance(beginlatLng, endLatLng);
        AllFlightDist += onceDist;
        DecimalFormat df = new DecimalFormat("##.#");
        mTv_flightdis.setText(df.format(AllFlightDist) + StringUtils.getUnit());
        return Float.parseFloat(df.format(AllFlightDist));
    }

这是基本的 添加数据 在地图上显示,下面就让他来动起来吧。

设置监听。

    private void initGuideMap(AMap aMap) {
        // 设置marker可拖拽事件监听器
        aMap.setOnMarkerDragListener(this);
        // 设置显示marker信息监听,要想显示信息必须注册
//        aMap.setInfoWindowAdapter(this);
        aMap.setOnMarkerClickListener(this);
//        aMap.setOnInfoWindowClickListener(this);
        // 初始化Marker配置
        options = new MarkerOptions().draggable(true);
    }
 
监听回调:

/**
     * <p>Marker 拖拽开始监听</p>
     * <li>增加振荡器</li>
     * <li>获取当前Marker 的起始坐标并保存</li>
     * <li>作用:当不满足条件的时候,就可以使用起点坐标</li>
     *
     * @param marker
     */
    private LatLng curMarkerPosition;

    @Override
    public void onMarkerDragStart(Marker marker) {
        Vibrator vibrator = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE);
        vibrator.vibrate(150);
        if (isPfMode == GduConfig.FpMode) {
            for (int i = 0; i < mPfMarkerList.size(); i++) {
                if (marker.equals(mPfMarkerList.get(i))) {
                    curMarkerPosition = mPfLatLngList.get(i + 1);
                }
            }
        } else if (isPfMode == GduConfig.ErMode) {
            for (int i = 0; i < mErMarkerList.size(); i++) {
                if (marker.equals(mErMarkerList.get(i))) {
                    curMarkerPosition = mErLatLngList.get(i);
                }
            }
        }
    }

    /**
     * <p>拖拽过程中的监听</p>
     *
     * @param marker
     */
    @Override
    public void onMarkerDrag(Marker marker) {
        drawTrackLine(marker);// 正常的Marker监听中
    }

    /**
     * <p>拖拽完成后的监听</p>
     * <li>当状态为:航迹规划进行中/电子围栏已启动 ,拖动点回归原点</li>
     * <li>拖拽完成后检查是否超过2000的判定</li>
     *
     * @param marker
     */
    @Override
    public void onMarkerDragEnd(Marker marker) {
        // 模式开启,飞机在执行 指令操作,所有的点都不能生效

        if (isWayPointMode_state == GduConfig.FpGo || isWayPointMode_state == GduConfig.FpGoOn || isElectronicRail_state == GduConfig.ErStart) {
            BackDraggablePoint(marker);// 当前是:航迹规划状态/电子围栏状态
            return;
        }
        if (isPfMode == GduConfig.FpMode && AMapUtils.calculateLineDistance(mPfLatLngList.get(0), marker.getPosition()) > fp_validityRange) {
            txhToast.show(context.getString(R.string.Label_Map_FlightRoute_flightpoit_invalidation));
            BackDraggablePoint(marker);// 就是超过2000米 的判断
        }
    }

继续 上一个 代码中的方法

 /**
     * <p>画航线</p>
     * <li>第一:分航迹规划模式和围栏模式</li>
     * <li>第二:围栏模式分:围栏模式已经启动 -实线情况,围栏模式未启动 -虚线情况</li>
     * <li>第三:围栏模式拖拽:分几个特殊点 ① 起点 ②中间点 ③ 终点 </li>
     * <li>第四:围栏模式拖拽:没启动围栏模式前,是没有最后一根线的,也就是说 5个点四根线 可拖拽的效果也是要分情况的 </li>
     * <li>不仅在拖拽的时候要实况改变预设航线,</li>
     * <li>还要在拖拽结束的时候判断是否满足航线规划的要求</li>
     * <li>满足就按照用户的定义的来,不满足就要回到起始点</li>
     */
    private void drawTrackLine(Marker marker) {
        if (isPfMode == GduConfig.FpMode) { // 航迹规划
            boolean isExceed = AMapUtils.calculateLineDistance(mPfLatLngList.get(0), marker.getPosition()) > fp_validityRange;
            outofrangeCircle(isExceed);
            for (int i = 0; i < mPfMarkerList.size(); i++) {
                if (marker.equals(mPfMarkerList.get(i))) {
                    mPfLatLngList.set(i + 1, marker.getPosition()); // 第一个点是无人机的点,所以要  +1 ;
                    mPfPolylineList.get(i).remove();
                    mPfPolylineList.set(i,
                            addPolyDottedLine(mPfLatLngList.get(i),        //虚线
                                    mPfLatLngList.get(i + 1)));

                    if (i < mPfMarkerList.size() - 1) {
                        mPfPolylineList.get(i + 1).remove();
                        mPfPolylineList.set(i + 1,
                                addPolyDottedLine(mPfLatLngList.get(i + 1),//虚线
                                        mPfLatLngList.get(i + 2)));
                    }
                    updateFlightDisEvent(i, marker);//实况更新飞行距离的事件
                    return;
                }
            }
        } else if (isPfMode == GduConfig.ErMode) { // 电子围栏
            for (int i = 0; i < mErMarkerList.size(); i++) {
                if (marker.equals(mErMarkerList.get(i))) {
                    if (mErMarkerList.size() > 1) {
                        mErLatLngList.set(i, marker.getPosition());// 没有无人机的点,所以就按照第0点来
                        // 一根线  (点的后面那根)
                        if (isElectronicRail_state == GduConfig.ErStart) {   // 已经开启电子围栏模式
                            if (i == mErMarkerList.size() - 1) {  // 【特殊点】移动的是最后一个点就是【起点】,手动添加的 Polyline
                                mErPolylineList.get(i).remove();
                                mErPolylineList.set(i,
                                        addPolySolidLine(mErLatLngList.get(i),//实线
                                                mErLatLngList.get(0)));
                            } else {          // 移动的不是最后一个点
                                mErPolylineList.get(i).remove();
                                mErPolylineList.set(i,
                                        addPolySolidLine(mErLatLngList.get(i),//实线
                                                mErLatLngList.get(i + 1)));
                            }
                        } else {                       // 没有开启电子围栏
                            if (i < mErPolylineList.size()) {
                                mErPolylineList.get(i).remove();
                                mErPolylineList.set(i,
                                        addPolyDottedLine(mErLatLngList.get(i),//虚线
                                                mErLatLngList.get(i + 1)));
                            }
                        }
                        // 二根线  (点的后前面那根)
                        if (isElectronicRail_state == GduConfig.ErStart) {  // 已经开启电子围栏模式
                            if (i == 0) {     //【特殊点】 移动终点
                                mErPolylineList.get(mErPolylineList.size() - 1).remove();
                                mErPolylineList.set(mErPolylineList.size() - 1,
                                        addPolySolidLine(mErLatLngList.get(i), //实线
                                                mErLatLngList.get(mErLatLngList.size() - 1)));
                            } else {
                                mErPolylineList.get(i - 1).remove();
                                mErPolylineList.set(i - 1,
                                        addPolySolidLine(mErLatLngList.get(i),//实线
                                                mErLatLngList.get(i - 1)));
                            }
                        } else {
                            if (i > 0) {
                                mErPolylineList.get(i - 1).remove();
                                mErPolylineList.set(i - 1,
                                        addPolyDottedLine(mErLatLngList.get(i),//虚线
                                                mErLatLngList.get(i - 1)));
                            }
                        }
                        return;
                    }
                }
            }
        }
    }

继续上一个代码的方法

 /**
     * <p>回到拖拽的起点</p>
     * <li>当不满足要求的时候,就会回到起始点</li>
     *
     * @param marker
     */
    private void BackDraggablePoint(Marker marker) {
        marker.setPosition(curMarkerPosition);
        drawTrackLine(marker);// 不满足要求
    }
然后就是 有效域的判定
 我其实是事先做了2个 有效域  的圆形,根据 地图的 距离计算,当大于2000 就隐藏第一个圆形 显示下一个圆形,如果第二个没有
就新建,如果存在就 显示,
 boolean isExceed = AMapUtils.calculateLineDistance(mPfLatLngList.get(0), marker.getPosition()) > 2000;
有效域
 circle = aMap.addCircle(new CircleOptions()
                        .center(latLng)
                        .radius(fp_validityRange)
                        .fillColor(Color.argb(26, 0, 120, 255))  // 填充域颜色
                        .strokeColor(Color.argb(102, 0, 160, 233))// 边框颜色
                        .strokeWidth(2));
警告域
 /**
     * <p>是否超出有效域,超出2000 有效域变色</p>
     * <p>是超出范围的圈/p>
     */
    public void outofrangeCircle(boolean isExceed) {
        if (warning_circle == null) {
            warning_circle = aMap.addCircle(new CircleOptions()
                    .center(currentphoneLatlng)
                    .radius(fp_validityRange)
                    .fillColor(Color.argb(26, 255, 199, 65))  // 填充域颜色
                    .strokeColor(Color.argb(102, 255, 199, 65))// 边框颜色
                    .strokeWidth(2));
        }
        if (isExceed) {
            warning_circle.setVisible(true);
            normal_circle.setVisible(false);
        } else {
            normal_circle.setVisible(true);
            warning_circle.setVisible(false);
        }
    }

基本 的重点都讲完了,下面是我完整的 java 文件

GuideMapFragment.java
package com.gdu.mvp_view.flightRouteplan;

import android.app.Fragment;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.maps.AMap;
import com.amap.api.maps.AMapOptions;
import com.amap.api.maps.AMapUtils;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.LocationSource;
import com.amap.api.maps.TextureMapView;
import com.amap.api.maps.UiSettings;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.Circle;
import com.amap.api.maps.model.CircleOptions;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MyLocationStyle;
import com.amap.api.maps.model.Polyline;
import com.gdu._enum.ConnStateEnum;
import com.gdu.beans.PathPlanBean;
import com.gdu.config.GduConfig;
import com.gdu.config.GlobalVariable;
import com.gdu.config.UavStaticVar;
import com.gdu.event.PfMostDisEvent;
import com.gdu.mvp_view.application.GduApplication;
import com.gdu.mvp_view.flightRouteplan.helper.GPSTranslateGuide;
import com.gdu.mvp_view.flightRouteplan.helper.UpDroneCurLocation;
import com.gdu.phonedrone.R;
import com.gdu.socket.GduFrame;
import com.gdu.socket.GduMapComm;
import com.gdu.socket.GduSocketConfig;
import com.gdu.socket.SocketCallBack;
import com.gdu.util.AnimationUtils;
import com.gdu.util.BBLog;
import com.gdu.util.DialogUtils;
import com.gdu.util.SPUtils;
import com.gdu.util.StringUtils;
import com.gdu.util.ToastFactory;
import com.gdu.util.YhLog;
import com.gdu.util.dialog.BlackGeneralDialog;
import com.gdu.util.dialog.GeneralDialog;
import com.gdu.views.AngelaTXHToast;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

import de.greenrobot.event.EventBus;

/**
 * Created by shangbeibei on 2017/4/20.
 * <p>航迹规划--高德地图</p>
 */
public class GuideMapFragment extends Fragment implements
        AMap.OnMapLoadedListener
        , View.OnClickListener
        , LocationSource     // 回调监听
        , AMapLocationListener //定外监听
        , AMap.OnMapClickListener {

    // init GuideMapFragment  mapview
    private AMap aMap;
    // init MapUtils
    private GuideMapMarkerTool mapMarkerTool;
    // init 定位样式,定位蓝点
    private MyLocationStyle myLocationStyle;
    //声明AMapLocationClient类对象
    public AMapLocationClient mLocationClient = null;
    //声明mLocationOption对象
    public AMapLocationClientOption mLocationOption = null;
    //重要的地图工具,有 比例尺,有logo位置  有 缩放按钮, 指南针。滑动手势,缩放手势
    private UiSettings mUiSettings;
    private OnLocationChangedListener mListener;//定位成功回调
    // 定位当前手机的位置
    private LatLng currentphoneLatlng;
    // 有效域 圆
    private Circle circle;
    // 无效域 圆
    private Circle warning_circle;
    // 航迹规划 【有效域范围】半径
    public static int fp_validityRange = 2000;

    //  init GuideMapFragment view
    private static GuideMapFragment guideMapFragment;
    private View view;
    // 高德地图
    private TextureMapView mMapView;
    // 地图控制台
    private RelativeLayout mRl_MapControlPanel;
    // 清除航线规划
    private ImageView mIv_fp_cleanroutePlan;
    // 电子围栏
    private ImageView mIv_electronicRail_on2off;
    // 航迹规划的开关
    private ImageView mIv_flightPlan_on2off;
    // 隐藏控制台 iocn
    private ImageView mIv_hide_controlpanel;
    // 显示控制台 icon
    private ImageView mIv_show_controlpanel;
    // 地图定位回调
    private ImageView mIv_fp_maplocation;
    // 航迹规划的状态
    private ImageView mIv_Fp_state;
    // 电子围栏的状态
    private ImageView mIv_Er_state;
    // 地图样式切换 button
    private ImageView mIv_switch_mapstyle;
    // 地图切换 样式 的layout 选项框
    private LinearLayout mll_switch_maplayout;
    // (热区域)点击地图样式,普通地图
    private LinearLayout mll_click_mapstyle_normal;
    //  (热区域)点击地图样式,卫星地图
    private LinearLayout mll_click_mapstyle_satellite;
    // 点击后,普通地图样式被选择的background 变化
    private LinearLayout mll_bg_maptype_norma
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值