안 드 로 이 드 차량 운동 궤적 부 드 러 운 이동 (고 모방 점적 택시) 최 적 실천
                                            
 23216 단어  안 드 로 이 드 실전
                    
github 원본 다운로드 주소:https://github.com/geduo83/android-amap-movecar
차량 운동 에 관 한 글 은 모두 두 편 을 썼 습 니 다. 하 나 는 안 드 로 이 드 차량 운동 궤적 빅 데이터 수집 을 위 한 가장 좋 은 실천 입 니 다. 다른 하 나 는 안 드 로 이 드 차량 운동 궤적 데이터 수집 서비스 보존 에 대한 탐색 과 발견 입 니 다. 차량 운동 궤적 이 부 드 럽 게 이동 하 는 글 을 쓰 려 고 했 습 니 다. 몇 년 후에 업무 항목 이 너무 바 빠 서 쓸 시간 이 없어 서 일 을 끝 냈 습 니 다.이 어 자신의 또 다른 두 개의 오픈 소스 프로젝트 에 바 빴 다. 하 나 는 안 드 로 이 드 클 라 이언 트 프레임 워 크 인 FlyTour 와 다른 SpringCloud 마이크로 서비스 프레임 워 크 인 FlyCloud 로 지난주 에 드디어 일 단락 되 었 다.
데이터 도 수집 할 수 있 고 채집 서비스 도 할 수 있 습 니 다. 데이터 수집 서비스 가 백 스테이지 에서 시스템 에 의 해 죽 을 가능성 을 효과적으로 피 할 수 있 습 니 다. 그 다음 에 수집 한 데 이 터 를 모 바 일 기기 에 실시 간 으로 보 여 줘 야 합 니 다. 요 며칠 동안 저도 인터넷 에서 검색 해 봤 습 니 다. 안 드 로 이 드 차량 의 운동 궤적 이 부 드 럽 게 이동 하 는 글 은 정말 없습니다.대부분 질문 이 많 았 는데 어떻게 이 뤄 졌 냐 고요?모두 단편 적 인 대답 으로 실전 프로젝트 에 서 는 큰 실 용적 가치 가 없다.
차량 운동 에 대해 우리 가 일상생활 에서 가장 많이 본 것 은 바로 물방울 택시 입 니 다. 이 앱 은 모두 가 사용 한 적 이 있 을 것 입 니 다. 앱 의 호출 페이지 에 목적 지 를 입력 하고 호출 을 클릭 하면 기사 가 주문 을 받 으 면 차량 이 당신 이 있 는 위치 로 미끄러져 이동 하 는 것 을 똑똑히 볼 수 있 습 니 다.오늘 은 차량 이 부 드 럽 게 움 직 이 는 기능 을 한 걸음 한 걸음 실현 하도록 이 끌 겠 습 니 다.
이 기능 은 고 덕 지 도 를 바탕 으로 개 발 된 것 이기 때문에 저 는 고 덕 홈 페이지 에 가서 찾 아 봤 습 니 다. 고 덕 은 공식 적 인 궤적 이동 api 를 제 공 했 습 니 다. 우 리 는 공식 API 를 사용 하지 않 고 자신의 방법 으로 가장 간단 한 기능 을 실현 하 는 것 이 빠 릅 니 다.
단일 트랙 재생
궤적 데 이 터 를 알 고 있 습 니 다. 재생 단 추 를 누 르 면 소형 차 는 노선 을 따라 자동 으로 앞으로 운동 하고 재생 이 끝나 면 끝 납 니 다.
 public class MoveSingleThread extends Thread{
    private List mLatLngList;
    private Marker mCarMarker;
    public MoveSingleThread(List latLngs, Marker marker) {
        super();
        mLatLngList = latLngs;
        mCarMarker = marker;
    }
    @Override
    public void run() {
        super.run();
    }
    public void moveTrack(){
        //    for           
        int step = 0;
        for (int i = 0; i < mLatLngList.size() - 1; i++) {
            LatLng startPoint = mLatLngList.get(i);
            LatLng endPoint = mLatLngList.get(i + 1);
            double slope = getSlope(startPoint, endPoint);
            //         (      )
            boolean isReverse = (startPoint.latitude > endPoint.latitude);
            double xMoveDistance = isReverse ? getXMoveDistance(slope) : -1 * getXMoveDistance(slope);
            //           
            for (double j = startPoint.latitude; !((j >= endPoint.latitude) ^ isReverse); j =
                    j - xMoveDistance) {
                step++;
            }
        }
        //     ,          
        double mTimeInterval = 0;//        
        if (!TextUtils.isEmpty(mDistance)) {
            float totalDistance = Float.parseFloat(mDistance) * 1000;
            if (totalDistance <= 500) {
                mTimeInterval = 1000.0 / step;
            } else if (totalDistance > 500 && totalDistance <= 7500) {
                mTimeInterval = 2.0 * totalDistance / step;
            } else {
                mTimeInterval = 15000.0 / step;
            }
        }
        // while (true) {
        for (int i = 0; i < mLatLngList.size() - 1; i++) {
            if (stopFlag) {
                stopFlag = false;
                break;
            }
            mIsCarMoveing = true;
            LatLng startPoint = mLatLngList.get(i);
            LatLng endPoint = mLatLngList.get(i + 1);
            mCarMarker.setPosition(startPoint);
            mCarMarker.setRotateAngle((float) getAngle(startPoint, endPoint));
            double slope = getSlope(startPoint, endPoint);
            //         (      )
            boolean isReverse = (startPoint.latitude > endPoint.latitude);
            double intercept = getInterception(slope, startPoint);
            double xMoveDistance = isReverse ? getXMoveDistance(slope) : -1 * getXMoveDistance(slope);
            //           
            double mSleep = 0;
            for (double j = startPoint.latitude; !((j >= endPoint.latitude) ^ isReverse); j =
                    j - xMoveDistance) {
                LatLng latLng = null;
                if (slope != Double.MAX_VALUE) {
                    latLng = new LatLng(j, (j - intercept) / slope);
                    // latLng = new LatLng(j, k);
                } else {
                    latLng = new LatLng(j, startPoint.longitude);
                }
                mCarMarker.setPosition(latLng);
                //         1  ,       ,         1  :     
                if (mTimeInterval < 1) {
                    mSleep += mTimeInterval;
                    if (mSleep >= 1) {
                        SystemClock.sleep((long) mSleep);
                        mSleep = 0;
                    }
                } else
                    SystemClock.sleep((long) mTimeInterval);
            }
        }
    }
}
  실시 간 궤적 데이터 정렬 문제
실시 간 궤적 을 표시 하려 면 어떻게 해 야 합 니까? 위의 코드 에 문제 가 있 습 니 다. Thread. start () 방법 이 호출 되면 바로 그의 run 방법 을 실행 합 니 다. run 방법 이 실행 되 고 스 레 드 도 끝 납 니 다. 또한 위의 코드 는 궤적 데 이 터 를 한 번 만 달 릴 수 있 습 니 다. 만약 에 5 초 간격 으로 배경 에서 궤적 데 이 터 를 가 져 오 면 데이터 큐 가 이 데 이 터 를 저장 해 야 합 니 다.데 이 터 를 한 번 달 릴 때마다 데이터 큐 에서 가 져 오고 있 으 면 가 져 와 서 달 리 며 없 으 면 대기 상태 에 있 습 니 다.우 리 는 비동기 메시지 처리 스 레 드 를 만 들 었 습 니 다. 이 문 제 는 쉽게 해 결 될 수 있 습 니 다. 데 이 터 를 얻 으 면 handler 를 통 해 데이터 post 를 우리 에 게 준 하위 스 레 드, Handler 자체 데이터 대기 열 에 있 습 니 다. 이것 은 줄 을 서 있 는 상태 입 니 다. 데이터 가 있 으 면 궤적 을 뛰 기 시작 합 니 다. 데이터 가 없 으 면 대기 상태 에 있 습 니 다. 데이터 가 올 때 까지.비동기 메시지 처리 스 레 드 에 익숙 하지 않 으 면 제 다른 글 을 보 세 요. Android 실전 개발 Handler 메커니즘 에 대한 심도 있 는 분석https://menxindiaolong.blog.csdn.net/article/details/86560330
표준 비동기 메시지 처리 스 레 드 는 어떻게 써 야 합 니까?방법 1:
class LooperThread extends Thread {
      public Handler mHandler;
 
      public void run() {
          Looper.prepare();
          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };
          Looper.loop();
      }
}
방법 2:
  // Step 1:      HandlerThread  ,    Looper
    HandlerThread handlerThread = new HandlerThread("gityuan.com");
    handlerThread.start();
 
    // Step 2:   Handler
    Handler handler = new Handler(handlerThread.getLooper()) {
       public void handleMessage(Message msg) {
         // process incoming messages here
       }
     };
    // Step 3:     
     handler.post(new Runnable() {
        @Override
        public void run() {
            System.out.println("thread id="+Thread.currentThread().getId());
        }
    });
안 드 로 이 드 시스템 에서 비동기 메시지 처리 스 레 드 의 일반적인 쓰기 입 니 다.
운동 궤적 의 일시 정지, 계속 문제
운동 궤적 은 하위 스 레 드 에서 이 루어 지기 때문에 우 리 는 자 연 스 럽 게 스 레 드 의 기다 림, 깨 움, 즉 wait, notify 의 문제 가 생각 날 것 이다. 따라서 우 리 는 운동 과정 에 다음 과 같은 코드 를 추가 하면 된다.
if (pause) {
        try {
            lock.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
어떻게 그 를 운동 을 회복 시 킵 니까?notify 해 주시 면 됩 니 다.
public void reStartMove() {
        synchronized (lock) {
            pause = false;
            lock.notify();
        }
}
전체 코드 는 다음 과 같 습 니 다:
/**
 * Description: 
 * Author:      mxdl
 * Date:        2019/7/10
 * Version:     V1.0.0
 * Update:     
 */
public class MoveCarCustomThread extends Thread {
    public static final String TAG = MoveCarCustomThread.class.getSimpleName();
    private Handler moveCarHandler;//            
    private Object lock = new Object();//   
    private boolean moveing = false;//        
    private boolean pause = false;//    , true   
    private boolean stop = false;//    , true     
    private WeakReference mActivityWeakReference;//    Activity       
    private MOVE_STATE currMoveState = MOVE_STATE.START_STATUS;
    public void setCurrMoveState(MOVE_STATE currMoveState) {
        this.currMoveState = currMoveState;
    }
    public MOVE_STATE getCurrMoveState() {
        return currMoveState;
    }
    public MoveCarCustomThread(MainActivity activity) {
        mActivityWeakReference = new WeakReference<>(activity);
    }
    //    
    public void pauseMove() {
        pause = true;
    }
    //      ,       
    public void reStartMove() {
        synchronized (lock) {
            pause = false;
            lock.notify();
        }
    }
    public void stopMove() {
        stop = true;
        if(moveCarHandler != null){
            moveCarHandler.removeCallbacksAndMessages(null);
        }
        if(mActivityWeakReference.get() != null){
            mActivityWeakReference.get().mLatLngList.clear();
            mActivityWeakReference.get().mMainHandler.removeCallbacksAndMessages(null);
        }
    }
    public Handler getMoveCarHandler() {
        return moveCarHandler;
    }
    public boolean isMoveing() {
        return moveing;
    }
    @Override
    public void run() {
        super.run();
        //      loop  
        Looper.prepare();
        moveCarHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //               
                synchronized (lock) {
                    if (msg.obj != null && msg.obj instanceof List) {
                        List latLngList = (List) msg.obj;
                        moveCoarseTrack(latLngList);
                    }
                }
            }
        };
        //  loop  
        Looper.loop();
    }
    private void moveCoarseTrack(List latLngList) {
        if (latLngList == null || latLngList.size() == 0 || latLngList.size() == 1) {
            return;
        }
        Log.v(TAG, "moveCoarseTrack start.........................................................");
        long startTime = System.currentTimeMillis();
        Log.v(TAG, "startTime:" + startTime);
        int step = TrackMoveUtil.getStep(latLngList);//     ,          
        Log.v(TAG, "move step:" + step);
        float distance = TrackMoveUtil.getDistance(latLngList);
        Log.v(TAG, "move distance:" + distance);
        double mTimeInterval = TrackMoveUtil.getMoveTime(distance, step);//     ,          
        mTimeInterval = 10;//       10  
        Log.v(TAG, "move mTimeInterval:" + mTimeInterval);
        moveing = true;
        for (int i = 0; i < latLngList.size() - 1; i++) {
            //     ,     
            if (pause) {
                movePause();
            }
            if (stop) {
                break;
            }
            moveing = true;
            LatLng startLatLng = latLngList.get(i);
            LatLng endLatLng = latLngList.get(i + 1);
            MainActivity mainActivity = mActivityWeakReference.get();
            moveCar(startLatLng, endLatLng, mainActivity);
            moveLine(startLatLng, mainActivity);
            moveCamera(startLatLng, mainActivity);
            double slope = TrackMoveUtil.getSlope(startLatLng, endLatLng);//         
            double intercept = TrackMoveUtil.getInterception(slope, startLatLng);//           
            boolean isReverse = (startLatLng.latitude > endLatLng.latitude);//         (      )
            double xMoveDistance = isReverse ? TrackMoveUtil.getXMoveDistance(slope) : -1 * TrackMoveUtil.getXMoveDistance(slope);
            //           
            double sleep = 0;
            int flag = 0;
            for (double j = startLatLng.latitude; !((j >= endLatLng.latitude) ^ isReverse); j = j - xMoveDistance) {
                //               
                if (pause) {
                    movePause();
                }
                if (stop) {
                    break;
                }
                moveing = true;
                flag++;
                if (slope != Double.MAX_VALUE) {
                    startLatLng = new LatLng(j, (j - intercept) / slope);
                } else {
                    startLatLng = new LatLng(j, startLatLng.longitude);
                }
                moveCar(startLatLng, mainActivity);
                moveLine(startLatLng, mainActivity);
                if (flag % 100 == 0) {
                    moveCamera(startLatLng, mainActivity);
                }
                //         1  ,       ,         1  :     
                if (mTimeInterval < 1) {
                    sleep += mTimeInterval;
                    if (sleep >= 1) {
                        Log.v(TAG, "sleep:" + sleep);
                        SystemClock.sleep((long) sleep);
                        sleep = 0;
                    }
                } else {
                    SystemClock.sleep((long) mTimeInterval);
                }
            }
        }
        long endTime = System.currentTimeMillis();
        moveing = false;
        Log.v(TAG, "endTime:" + endTime);
        Log.v(TAG, "run mTimeInterval:" + (endTime - startTime));
        Log.v(TAG, "moveCoarseTrack end.........................................................");
    }
    private void moveLine(LatLng startLatLng, MainActivity mainActivity) {
        mainActivity.mLatLngList.add(startLatLng);//           
        mainActivity.mMovePolyline.setPoints(mainActivity.mLatLngList);//       
    }
    private void moveCar(LatLng startLatLng, LatLng endLatLng, MainActivity mainActivity) {
        moveCar(startLatLng,mainActivity);
        if (mainActivity.mCarMarker != null) {
            mainActivity.mCarMarker.setRotateAngle((float) TrackMoveUtil.getAngle(startLatLng, endLatLng));//          
        }
    }
    private void moveCar(LatLng startLatLng,MainActivity mainActivity) {
        if (mainActivity.mCarMarker != null) {
            mainActivity.mCarMarker.setPosition(startLatLng);//     
        }
    }
    private void movePause() {
        try {
            lock.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private void moveCamera(LatLng startLatLng, MainActivity mainActivity) {
        Message message = Message.obtain();
        message.what = MainActivity.EventType.MapMove;
        message.obj = startLatLng;
        mainActivity.mMainHandler.sendMessage(message);
    }
}
    핵심 알고리즘 도구 클래스
 *              Date: 2016-10-27 Created by mxdl
 */
public class TrackMoveUtil {
  private static double DISTANCE = 0.0001;
  /**
   *        
   */
  public static double getSlope(LatLng fromPoint, LatLng toPoint) {
    if (fromPoint == null || toPoint == null) {
      return 0;
    }
    if (toPoint.longitude == fromPoint.longitude) {
      return Double.MAX_VALUE;
    }
    double slope =
        ((toPoint.latitude - fromPoint.latitude) / (toPoint.longitude - fromPoint.longitude));
    return slope;
  }
  /**
   *             
   */
  public static double getAngle(LatLng fromPoint, LatLng toPoint) {
    if (fromPoint == null || toPoint == null) {
      return 0;
    }
    double slope = getSlope(fromPoint, toPoint);
    if (slope == Double.MAX_VALUE) {
      if (toPoint.latitude > fromPoint.latitude) {
        return 0;
      } else {
        return 180;
      }
    }
    float deltAngle = 0;
    if ((toPoint.latitude - fromPoint.latitude) * slope < 0) {
      deltAngle = 180;
    }
    double radio = Math.atan(slope);
    double angle = 180 * (radio / Math.PI) + deltAngle - 90;
    return angle;
  }
  /**
   *           
   */
  public static double getInterception(double slope, LatLng point) {
    if (point == null) {
      return 0;
    }
    return point.latitude - slope * point.longitude;
  }
  /**
   *   x         
   */
  public static double getXMoveDistance(double slope) {
    if (slope == Double.MAX_VALUE) {
      return DISTANCE;
    }
    return Math.abs((DISTANCE * slope) / Math.sqrt(1 + slope * slope));
  }
  /**
   *                
   * 
   * @param latLngList
   * @return
   */
  public static int getStep(List latLngList) {
    int step = 0;
    if (latLngList != null && latLngList.size() > 1) {
      for (int i = 0; i < latLngList.size() - 1; i++) {
        try {
          LatLng startPoint = latLngList.get(i);
          LatLng endPoint = latLngList.get(i + 1);
          double slope = getSlope(startPoint, endPoint);
          //         (      )
          boolean isReverse = (startPoint.latitude > endPoint.latitude);
          double xMoveDistance = isReverse ? getXMoveDistance(slope) : -1 * getXMoveDistance(slope);
          //           
          for (double j = startPoint.latitude; !((j >= endPoint.latitude) ^ isReverse); j =
              j - xMoveDistance) {
            step++;
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
    return step;
  }
  /**
   *               
   * 
   * @param distance
   * @param step
   * @return
   */
  public static double getMoveTime(float distance, int step) {
    double timeInterval = 0;
    if (distance > 0) {
      float totalDistance = distance * 1000;
      if (totalDistance <= 500) {
        timeInterval = 1000.0 / step;
      } else if (totalDistance > 500 && totalDistance <= 7500) {
        timeInterval = 2.0 * totalDistance / step;
      } else {
        timeInterval = 15000.0 / step;
      }
    }
    return timeInterval;
  }
  /**
   *             
   * 
   * @param latLngList
   * @return
   */
  public static float getDistance(List latLngList) {
    float distance = 0;
    if (latLngList != null && latLngList.size() > 1) {
      for (int i = 0; i < latLngList.size() - 1; i++) {
        try {
          distance += AMapUtils.calculateLineDistance(latLngList.get(i), latLngList.get(i + 1));
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
    return distance;
  }
  // latitude -      , -90  90    double    。
  // longitude -      , -180  180    double    。
  /**
   *                   a|b|c|d;
   * 
   * @param latlonStr
   * @return
   */
  public static List getListLatLng(String latlonStr) {
    if (!TextUtils.isEmpty(latlonStr)) {
      String[] trackArr = latlonStr.split("\\|");
      if (trackArr != null && trackArr.length > 0) {
        List latLngList = new ArrayList();
        for (int i = 0; i < trackArr.length - 1; i = i + 2) {
          try {
            String lat = trackArr[i + 1];
            String lng = trackArr[i];
            // Logger.v(TAG,"trackArr index:" + i);
            // Logger.v(TAG,"trackArr lat:" + lat);
            // Logger.v(TAG,"trackArr lng:" + lng);
            if (!TextUtils.isEmpty(lat) && !TextUtils.isEmpty(lng)) {
              Double dLat = Double.valueOf(lat);
              Double dLng = Double.valueOf(lng);
              if (dLat >= -90 && dLat <= 90 && dLng >= -180 && dLng <= 180
                  && !(dLat == 0 && dLng == 0)) {
                LatLng latLng = new LatLng(dLat, dLng);
                latLngList.add(latLng);
              }
            }
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
        return latLngList;
      }
    }
    return null;
  }
}
     
고 덕 API 가 실현 하 는 실시 간 운동 궤적
/**
 * Description: 
 * Author:      mxdl
 * Date:        2019/7/10
 * Version:     V1.0.0
 * Update:     
 */
public class MoveCarSmoothThread implements IMoveCar {
    public static final String TAG = MoveCarSmoothThread.class.getSimpleName();
    private MovingPointOverlay mMovingPointOverlay;
    private WeakReference mActivityWeakReference;
    private boolean isfirst = true;
    private MOVE_STATE currMoveState = MOVE_STATE.START_STATUS;
    public void setCurrMoveState(MOVE_STATE currMoveState) {
        this.currMoveState = currMoveState;
    }
    public MOVE_STATE getCurrMoveState() {
        return currMoveState;
    }
    public MoveCarSmoothThread(MainActivity activity) {
        mActivityWeakReference = new WeakReference<>(activity);
    }
    @Override
    public void startMove(List latLngs) {
        if (latLngs == null || latLngs.size() == 0) {
            return;
        }
        Log.v("MYTAG","startMove start:"+Thread.currentThread().getName());
        Log.v(TAG, "moveCoarseTrack start.........................................................");
        long startTime = System.currentTimeMillis();
        Log.v(TAG, "startTime:" + startTime);
        final MainActivity mainActivity = mActivityWeakReference.get();
        if (mMovingPointOverlay == null) {
            mMovingPointOverlay = new MovingPointOverlay(mainActivity.mAMap, mainActivity.mCarMarker);
            mMovingPointOverlay.setTotalDuration(5);
            mMovingPointOverlay.setMoveListener(new MovingPointOverlay.MoveListener() {
                @Override
                public void move(double v) {
                    if(isfirst){
                        isfirst = false;
                        Log.v("MYTAG","MoveCarSmoolthThread move start:"+Thread.currentThread().getName());
                    }
                    LatLng position = mMovingPointOverlay.getPosition();
                    mainActivity.mLatLngList.add(position);//           
                    mainActivity.mMovePolyline.setPoints(mainActivity.mLatLngList);//       
                    Message message = Message.obtain();
                    message.what = MainActivity.EventType.MapMove;
                    message.obj = position;
                    message.arg1 = (int)v;
                    mainActivity.mMainHandler.sendMessage(message);
                }
            });
        }
        mMovingPointOverlay.setPoints(latLngs);
        mMovingPointOverlay.startSmoothMove();
        long endTime = System.currentTimeMillis();
        Log.v(TAG, "endTime:" + endTime);
        Log.v(TAG, "moveCoarseTrack end.........................................................");
    }
    @Override
    public void reStartMove() {
        if(mMovingPointOverlay != null){
            mMovingPointOverlay.startSmoothMove();
        }
    }
    @Override
    public void pauseMove(){
        if(mMovingPointOverlay != null){
            mMovingPointOverlay.stopMove();
        }
    }
    @Override
    public void stopMove(){
        if(mMovingPointOverlay != null){
            mMovingPointOverlay.destroy();
            mMovingPointOverlay = null;
        }
        if(mActivityWeakReference.get() != null){
            mActivityWeakReference.get().mLatLngList.clear();
        }
    }
}
  마지막 으로 나 는 모든 항목 의 전체 코드 를 GitHub 에 전달 했다.https://github.com/geduo83/android-amap-movecar