Quellcode durchsuchen

[new]增加轨迹点显示

zk vor 6 Monaten
Ursprung
Commit
735576d811

+ 8 - 0
lib/data/bean/track_daily_bean.dart

@@ -22,6 +22,12 @@ class TrackDailyBean {
   @JsonKey(name: 'status')
   int status;
 
+  @JsonKey(name: 'lng')
+  double? lng;
+
+  @JsonKey(name: 'lat')
+  double? lat;
+
   @JsonKey(name: 'totalUnlock')
   int? totalUnlock;
 
@@ -34,6 +40,8 @@ class TrackDailyBean {
     required this.end,
     required this.duration,
     this.network,
+    this.lat,
+    this.lng,
     required this.status,
     this.totalUnlock,
     this.highUnlock,

+ 4 - 0
lib/data/bean/track_daily_bean.g.dart

@@ -13,6 +13,8 @@ TrackDailyBean _$TrackDailyBeanFromJson(Map<String, dynamic> json) =>
       end: (json['end'] as num).toInt(),
       duration: (json['duration'] as num).toInt(),
       network: json['network'] as String?,
+      lat: (json['lat'] as num?)?.toDouble(),
+      lng: (json['lng'] as num?)?.toDouble(),
       status: (json['status'] as num).toInt(),
       totalUnlock: (json['totalUnlock'] as num?)?.toInt(),
       highUnlock: (json['highUnlock'] as num?)?.toInt(),
@@ -26,6 +28,8 @@ Map<String, dynamic> _$TrackDailyBeanToJson(TrackDailyBean instance) =>
       'duration': instance.duration,
       'network': instance.network,
       'status': instance.status,
+      'lng': instance.lng,
+      'lat': instance.lat,
       'totalUnlock': instance.totalUnlock,
       'highUnlock': instance.highUnlock,
     };

+ 16 - 14
lib/module/track/track_controller.dart

@@ -1,4 +1,5 @@
 import 'package:flutter_map/flutter_map.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:get/get.dart';
 import 'package:get/get_core/src/get_main.dart';
 import 'package:injectable/injectable.dart';
@@ -15,7 +16,6 @@ import '../../data/bean/user_info.dart';
 
 @injectable
 class TrackController extends BaseController {
-
   final Rxn<UserInfo> _userInfo = Rxn<UserInfo>();
 
   UserInfo? get userInfo => _userInfo.value;
@@ -24,17 +24,6 @@ class TrackController extends BaseController {
 
   SheetController sheetController = SheetController();
 
-  final Rxn<LocationInfo> _currentLocation = Rxn<LocationInfo>();
-
-  LocationInfo? get currentLocation => _currentLocation.value;
-
-  List<LatLng>? points;
-  List<AtmobTrackPoint>? originPoints;
-
-  final RxBool _isShowTraceDetailBtn = false.obs;
-
-  bool get isShowTraceDetailBtn => _isShowTraceDetailBtn.value;
-
   final RxList<TrackDays> daysList = RxList<TrackDays>();
 
   final TrackRepository trackRepository;
@@ -76,9 +65,22 @@ class TrackController extends BaseController {
     }
   }
 
+  ///显示轨迹以及标记点
+  void showMapTrack(List<LatLng> points, List<Marker> markers) {
+    mapController.clear();
+    //画折线
+    if (points.length > 1) {
+      mapController.addPolyline(points,
+          mapPadding: MapPadding(
+              left: 50.w, top: 100.w, right: 50.w, bottom: Get.height / 2));
+    }
+    //画标记点
+    if (markers.isNotEmpty) {
+      mapController.updateOrAddMarkers(markers);
+    }
+  }
+
   void back() {
     Get.back();
   }
-
-
 }

+ 92 - 5
lib/module/track/track_day_detail/track_day_detail_controller.dart

@@ -2,6 +2,7 @@ import 'dart:async';
 import 'dart:convert';
 
 import 'package:flutter/cupertino.dart';
+import 'package:flutter_map/flutter_map.dart';
 import 'package:get/get.dart';
 import 'package:get/get_core/src/get_main.dart';
 import 'package:location/base/base_controller.dart';
@@ -11,6 +12,7 @@ import 'package:location/dialog/loading_dialog.dart';
 import 'package:location/handler/error_handler.dart';
 import 'package:location/module/track/track_controller.dart';
 import 'package:location/module/track/track_day_detail/time_proportion/pie_chat_data.dart';
+import 'package:location/module/track/track_util.dart';
 import 'package:location/resource/string.gen.dart';
 import 'package:location/sdk/wechat/wechat_share_util.dart';
 import 'package:location/utils/async_util.dart';
@@ -19,6 +21,7 @@ import 'package:location/utils/pair.dart';
 import 'package:location/utils/toast_util.dart';
 import 'package:url_launcher/url_launcher.dart';
 import '../../../data/api/response/track_daily_summary_response.dart';
+import '../../../data/bean/atmob_track_point.dart';
 import '../../../data/bean/stream_chat_origin_data.dart';
 import '../../../data/bean/track_days.dart';
 import '../../../utils/capture_util.dart';
@@ -68,6 +71,10 @@ class TrackDayDetailController extends BaseController {
 
   final RxnString _summaryError = RxnString();
 
+  CancelableFuture? requestTrackFuture;
+
+  List<AtmobTrackPoint>? trackOriginPoints;
+
   String? get summaryError => _summaryError.value;
 
   final GlobalKey shareGlobalKey = GlobalKey();
@@ -82,12 +89,91 @@ class TrackDayDetailController extends BaseController {
   @override
   void onInit() {
     super.onInit();
-    _requestTrackDaily();
+    _requestTrackData();
     _requestTrackDailySummary();
-    _requestTrackHistoryPoints();
   }
 
-  void _requestTrackHistoryPoints() {}
+  _requestTrackData() {
+    requestTrackFuture?.cancel();
+    requestTrackFuture = AsyncUtil.waitForAll(
+        [_requestTrackHistoryPoints(), _requestTrackDaily()]);
+    requestTrackFuture!.then((_) {
+      //组装地图新的数据
+      List<LatLng> points = [];
+      List<Marker> markers = [];
+
+      for (int i = 0; i < trackDailyList.length; i++) {
+        final bean = trackDailyList[i];
+        double? markerLatitude;
+        double? markerLongitude;
+        if (bean.status == TrackStatus.stay) {
+          //停留
+          markerLatitude = bean.lat;
+          markerLongitude = bean.lng;
+          points.add(
+              LatLng(latitude: markerLatitude, longitude: markerLongitude));
+        } else if (bean.status == TrackStatus.moving) {
+          //移动
+          final movePoints = TrackUtil.getTrackMovePoints(
+              trackOriginPoints, bean.start, bean.end);
+          points.addAll(movePoints);
+          continue;
+        } else if (bean.status == TrackStatus.error) {
+          continue;
+        }
+
+        MarkerType markerType;
+        String? markerName;
+        String? customAvatarUrl;
+        if (i == 0) {
+          //起点
+          markerType = MarkerType.traceStartPoint;
+        } else if (i == trackDailyList.length - 1) {
+          //终点
+          final userInfo = trackController.userInfo;
+          markerName = userInfo?.getUserNickName();
+          markerType = userInfo?.isMine == true
+              ? MarkerType.traceEndMinePoint
+              : MarkerType.traceEndMinePoint;
+          customAvatarUrl = userInfo?.avatar;
+        } else {
+          markerType = MarkerType.tracePassingPoint;
+        }
+        markers.add(Marker(
+            id: '${bean.start}',
+            markerName: markerName ?? '',
+            longitude: markerLongitude,
+            latitude: markerLatitude,
+            markerType: markerType,
+            customAvatarUrl: customAvatarUrl));
+      }
+      if (markers.last.markerType != MarkerType.traceEndMinePoint ||
+          markers.last.markerType != MarkerType.traceEndFriendPoint) {
+        markers.last.markerType = trackController.userInfo?.isMine == true
+            ? MarkerType.traceEndMinePoint
+            : MarkerType.traceEndFriendPoint;
+        markers.last.markerName =
+            trackController.userInfo?.getUserNickName() ?? '';
+        markers.last.customAvatarUrl = trackController.userInfo?.avatar;
+      }
+      trackController.showMapTrack(points, markers);
+    });
+  }
+
+  Future<void> _requestTrackHistoryPoints() {
+    final userInfo = trackController.userInfo;
+    trackOriginPoints = null;
+    return Future.value().then((_) {
+      if (userInfo?.virtual == true) {
+        return trackRepository.queryVirtualTrack();
+      } else {
+        return trackRepository.queryTrack(
+            startTime: days.start, endTime: days.end, userId: userInfo?.id);
+      }
+    }).then((data) {
+      trackOriginPoints = data.trackPoints;
+    });
+  }
 
   void _requestTrackDailySummary() {
     summaryFuture?.cancel();
@@ -112,10 +198,10 @@ class TrackDayDetailController extends BaseController {
     });
   }
 
-  void _requestTrackDaily() {
+  Future<void> _requestTrackDaily() {
     _isRequested.value = false;
     CustomLoadingDialog.show(loadingTxt: StringName.trackLoadingTxt);
-    trackRepository
+    return trackRepository
         .trackDailyQuery(
             startTime: days.start,
             endTime: days.end,
@@ -287,5 +373,6 @@ class TrackDayDetailController extends BaseController {
     super.onClose();
     _streamChatSubscription?.cancel();
     graduallyController.dispose();
+    requestTrackFuture?.cancel();
   }
 }

+ 15 - 0
lib/module/track/track_util.dart

@@ -44,4 +44,19 @@ class TrackUtil {
       return '${minutes}min';
     }
   }
+
+  static List<LatLng> getTrackMovePoints(
+      List<AtmobTrackPoint>? pointsList, int startTime, int endTime) {
+    if (pointsList == null || pointsList.isEmpty) {
+      return [];
+    }
+    List<LatLng> movePoints = [];
+    for (var point in pointsList) {
+      if (point.time >= startTime && point.time <= endTime) {
+        movePoints
+            .add(LatLng(latitude: point.latitude, longitude: point.longitude));
+      }
+    }
+    return movePoints;
+  }
 }

+ 3 - 3
lib/socket/location_message.dart

@@ -12,11 +12,11 @@ class LocationMessage extends BaseMessage {
     message ??= LocationMessage();
     message!.cmd = "u.location";
     message!.data = jsonEncode(MessageData(
-            longitude: location.longitude,
-            latitude: location.latitude,
+            longitude: 113.415532,
+            latitude: 23.131072,
             speed: location.speed,
             bearing: location.bearing,
-            address: location.address)
+            address: '广州市优托邦购物中心')
         .toJson());
     return jsonEncode(message!.toJson());
   }

+ 59 - 0
lib/utils/async_util.dart

@@ -138,6 +138,65 @@ class AsyncUtil {
     }
     return controller;
   }
+
+  static CancelableFuture<List<AsyncResult<T>>> waitForAll<T>(
+      Iterable<Future<T>> futures) {
+    final completers = futures.map((_) => Completer<void>()).toList();
+    final results = <AsyncResult<T>>[];
+    bool isCancelled = false;
+
+    void attempt(Completer<List<AsyncResult<T>>> completer) async {
+      for (int i = 0; i < futures.length; i++) {
+        if (isCancelled) {
+          completer.completeError(CancelledError());
+          return;
+        }
+        try {
+          final value = await futures.elementAt(i);
+          results.add(AsyncResult.success(value));
+        } catch (e, stackTrace) {
+          results.add(AsyncResult.failure(e, stackTrace));
+        } finally {
+          completers[i].complete();
+        }
+      }
+      await Future.wait(completers.map((c) => c.future));
+      if (!completer.isCompleted) {
+        completer.complete(results);
+      }
+    }
+
+    return CancelableFuture<List<AsyncResult<T>>>((completer) {
+      attempt(completer);
+    }, () {
+      isCancelled = true;
+    });
+  }
+}
+
+class AsyncResult<T> {
+  final T? value;
+  final Object? error;
+  final StackTrace? stackTrace;
+
+  bool get isSuccess => error == null;
+
+  AsyncResult.success(this.value)
+      : error = null,
+        stackTrace = null;
+
+  AsyncResult.failure(this.error, this.stackTrace) : value = null;
+
+  void handle({
+    required void Function(T) onSuccess,
+    required void Function(Object, StackTrace?) onError,
+  }) {
+    if (isSuccess) {
+      onSuccess(value as T);
+    } else {
+      onError(error!, stackTrace);
+    }
+  }
 }
 
 abstract interface class Cancelable {

+ 3 - 1
plugins/map/lib/src/consts/marker_type.dart

@@ -5,7 +5,9 @@ enum MarkerType {
   friend(2),
   traceStartPoint(3),
   traceEndFriendPoint(4),
-  traceEndMinePoint(5);
+  traceEndMinePoint(5),
+  traceSelectPoint(6),
+  tracePassingPoint(7); //轨迹途径点样式
 
   final int value;
 

+ 2 - 1
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/bean/MakerInfo.java

@@ -92,7 +92,7 @@ public class MakerInfo {
     }
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({MarkerType.MINE, MarkerType.FRIEND, MarkerType.TRACE_START_POINT, MarkerType.TRACE_END_FRIEND_POINT, MarkerType.TRACE_END_MINE_POINT,MarkerType.TRACE_SELECT_POINT})
+    @IntDef({MarkerType.MINE, MarkerType.FRIEND, MarkerType.TRACE_START_POINT, MarkerType.TRACE_END_FRIEND_POINT, MarkerType.TRACE_END_MINE_POINT, MarkerType.TRACE_SELECT_POINT, MarkerType.TRACE_PASSING_POINT})
     public @interface MarkerType {
         int MINE = 1;
         int FRIEND = 2;
@@ -100,6 +100,7 @@ public class MakerInfo {
         int TRACE_END_FRIEND_POINT = 4;
         int TRACE_END_MINE_POINT = 5;
         int TRACE_SELECT_POINT = 6;
+        int TRACE_PASSING_POINT = 7;
     }
 
 

+ 16 - 7
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/overlays/marker/MarkersController.java

@@ -6,6 +6,7 @@ import android.graphics.Canvas;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
 import androidx.constraintlayout.widget.ConstraintLayout;
@@ -20,6 +21,7 @@ import com.atmob.map_amap_android.R;
 import com.atmob.map_amap_android.bean.MakerInfo;
 import com.atmob.map_amap_android.contants.Constants;
 import com.atmob.map_amap_android.databinding.ItemLocationMarkerBinding;
+import com.atmob.map_amap_android.databinding.ItemTrackPassingMarkerBinding;
 import com.atmob.map_amap_android.databinding.ItemTrackStartMarkerBinding;
 import com.atmob.map_amap_android.overlays.MyMethodCallHandler;
 import com.atmob.map_amap_android.util.BitmapCallback;
@@ -162,7 +164,8 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
                     LogUtil.i(TAG, "updateMarkers==修改头像");
                     ImageCacheLoader.loadBitmapAsync(context, makerInfo.getCustomAvatarUrl(), new BitmapCallback() {
                         @Override
-                        public void onStartLoading() {}
+                        public void onStartLoading() {
+                        }
 
                         @Override
                         public void onBitmapLoaded(Bitmap bitmap) {
@@ -195,7 +198,8 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
             // 加载头像后再添加 marker
             ImageCacheLoader.loadBitmapAsync(context, makerInfo.getCustomAvatarUrl(), new BitmapCallback() {
                 @Override
-                public void onStartLoading() {}
+                public void onStartLoading() {
+                }
 
                 @Override
                 public void onBitmapLoaded(Bitmap bitmap) {
@@ -213,7 +217,6 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
     }
 
 
-
     private void addMarkerToMap(MakerInfo makerInfo, LatLng latLng, Bitmap avatarBitmap) {
         BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo, avatarBitmap);
         MarkerOptions markerOption = new MarkerOptions()
@@ -221,6 +224,10 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
                 .icon(markerBitmap)
                 .anchor(0.5f, 0.9f);
 
+        if (makerInfo.getMarkerType() == MakerInfo.MarkerType.TRACE_PASSING_POINT) {
+            markerOption.anchor(0.5f, 0.5f);
+        }
+
         Marker newMarker = map.addMarker(markerOption);
         newMarker.setObject(makerInfo);
         newMarker.setZIndex(makerInfo.isSelected() ? 999 : 0);
@@ -228,8 +235,6 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
     }
 
 
-
-
     @Override
     public String[] getRegisterMethodIdArray() {
         return Constants.METHOD_ID_LIST_FOR_MARKER;
@@ -248,9 +253,9 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
             } else {
                 markerBinding.ivMarkerAvatar.setImageBitmap(customAvatarBitmap);
             }
-            if(markerInfo.getMarkerType() == MakerInfo.MarkerType.MINE){
+            if (markerInfo.getMarkerType() == MakerInfo.MarkerType.MINE) {
                 markerBinding.vBottom.setBackgroundResource(markerInfo.isSelected() ? R.drawable.icon_mine_marker_selected_bottom_circle : R.drawable.icon_mine_marker_normal_bottom_circle);
-            }else{
+            } else {
                 markerBinding.vBottom.setBackgroundResource(markerInfo.isSelected() ? R.drawable.icon_marker_selected_bottom_circle : R.drawable.icon_marker_normal_bottom_circle);
             }
             markerBinding.locationMarkerIvBg.setImageResource(markerInfo.isSelected() ? (markerInfo.getMarkerType() == MakerInfo.MarkerType.MINE ? R.drawable.icon_location_mine_marker_selected : R.drawable.icon_location_marker_selected) : R.drawable.icon_location_marker_normal);
@@ -271,6 +276,10 @@ public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClic
             trackEndPointMarkerBinding.locationMarkerIvBg.setImageResource(R.drawable.icon_location_marker_normal);
             trackEndPointMarkerBinding.vBottom.setBackgroundResource(R.drawable.icon_marker_track_bottom_circle);
             return viewGetBitmapDescriptor(view);
+        } else if (markerInfo.getMarkerType() == MakerInfo.MarkerType.TRACE_PASSING_POINT) {
+            ItemTrackPassingMarkerBinding trackPassingMarkerBinding = ItemTrackPassingMarkerBinding.inflate(LayoutInflater.from(context));
+            ConstraintLayout view = trackPassingMarkerBinding.getRoot();
+            return viewGetBitmapDescriptor(view);
         }
         return null;
     }

+ 9 - 0
plugins/map_amap_android/android/src/main/res/drawable/icon_marker_passing.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <corners android:radius="100dp" />
+    <solid android:color="#15CBA1" />
+    <stroke
+        android:width="2dp"
+        android:color="#fff" />
+</shape>

+ 15 - 0
plugins/map_amap_android/android/src/main/res/layout/item_track_passing_marker.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <View
+        android:layout_width="14dp"
+        android:layout_height="14dp"
+        android:background="@drawable/icon_marker_passing"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+</androidx.constraintlayout.widget.ConstraintLayout>