From 1ffcee57f2afd0805bb78e8206e0f0be8e8863db Mon Sep 17 00:00:00 2001 From: aibingbing Date: Fri, 21 Jun 2024 18:59:28 +0800 Subject: [PATCH] =?UTF-8?q?[646]feat:=E5=A2=9E=E5=8A=A0=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E8=BD=A8=E8=BF=B9=E7=BB=98=E5=88=B6=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eagle/core/function/MapBizProvider.kt | 2 + .../MogoTrajectoryOverlayManager.java | 85 +++++ .../TrajectoryOverlayDrawer.java | 301 ++++++++++++++++++ .../trajectoryoverlay/TrajectoryStrategy.kt | 169 ++++++++++ .../java/com/mogo/map/overlay/core/Level.kt | 5 + 5 files changed, 562 insertions(+) create mode 100644 core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/business/trajectoryoverlay/MogoTrajectoryOverlayManager.java create mode 100644 core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/business/trajectoryoverlay/TrajectoryOverlayDrawer.java create mode 100644 core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/business/trajectoryoverlay/TrajectoryStrategy.kt diff --git a/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/MapBizProvider.kt b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/MapBizProvider.kt index aa9311d344..4c03580f0c 100644 --- a/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/MapBizProvider.kt +++ b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/MapBizProvider.kt @@ -11,6 +11,7 @@ import com.mogo.eagle.core.function.business.SpeedLimitDataManager import com.mogo.eagle.core.function.business.ai.AiCloudIdentifyDataManager.Companion.aiCloudIdentifyDataManager import com.mogo.eagle.core.function.business.identify.MapIdentifySubscriber import com.mogo.eagle.core.function.business.routeoverlay.MogoRouteOverlayManager +import com.mogo.eagle.core.function.business.trajectoryoverlay.MogoTrajectoryOverlayManager import com.mogo.eagle.core.function.call.map.CallerVisualAngleManager import com.mogo.eagle.core.utilcode.util.DeviceUtils import com.mogo.map.MapDataWrapper @@ -25,6 +26,7 @@ class MapBizProvider :IMoGoFunctionServerProvider, IMogoRoma { MapDataWrapper.init() MapIdentifySubscriber.instance MogoRouteOverlayManager.getInstance().init() + MogoTrajectoryOverlayManager.getInstance().init() MapPointCloudSubscriber.instance SpeedLimitDataManager.getInstance().start() if(DeviceUtils.isLenovoModel() || DeviceUtils.isEB5Model()){ //todo 新增稳定设备类型需要添加,目的避免在nuc设备上使用此类功能 diff --git a/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/business/trajectoryoverlay/MogoTrajectoryOverlayManager.java b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/business/trajectoryoverlay/MogoTrajectoryOverlayManager.java new file mode 100644 index 0000000000..be09ee92a8 --- /dev/null +++ b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/business/trajectoryoverlay/MogoTrajectoryOverlayManager.java @@ -0,0 +1,85 @@ +package com.mogo.eagle.core.function.business.trajectoryoverlay; + +import androidx.annotation.Nullable; + +import com.mogo.eagle.core.data.map.MogoLocation; +import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationGCJ02Listener; +import com.mogo.eagle.core.function.api.autopilot.IMoGoPlanningRottingListener; +import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager; +import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationGCJ02ListenerManager; +import com.mogo.eagle.core.function.call.autopilot.CallerPlanningRottingListenerManager; +import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger; + +import java.util.LinkedList; +import java.util.List; + +import mogo.telematics.pad.MessagePad; + +public class MogoTrajectoryOverlayManager implements + IMoGoPlanningRottingListener, + IMoGoChassisLocationGCJ02Listener { + private static volatile MogoTrajectoryOverlayManager sInstance; + private static final String TAG = "MogoTrajectoryOverlayManager"; + + private final LinkedList> queue = new LinkedList<>(); + + private MogoTrajectoryOverlayManager() { + } + + + public void init() { + CallerPlanningRottingListenerManager.INSTANCE.addListener(TAG, this); + CallerChassisLocationGCJ02ListenerManager.INSTANCE.addListener(TAG, 1,this); + } + + public static MogoTrajectoryOverlayManager getInstance() { + if (sInstance == null) { + synchronized (MogoTrajectoryOverlayManager.class) { + if (sInstance == null) { + sInstance = new MogoTrajectoryOverlayManager(); + } + } + } + return sInstance; + } + + @Override + public void onAutopilotRotting(@Nullable MessagePad.GlobalPathResp globalPathResp) { + CallerLogger.i(TAG, "onAutopilotRotting size="+globalPathResp.getWayPointsList().size()); + if (globalPathResp != null) { + synchronized (queue) { + queue.clear(); + queue.offer(globalPathResp.getWayPointsList()); + } + } + } + + @Override + public void onChassisLocationGCJ02(@Nullable MogoLocation gnssInfo) { + if (gnssInfo == null) { + return; + } + int autoPilotState = CallerAutoPilotStatusListenerManager.INSTANCE.getState(); + boolean isArriveAtStation = CallerAutoPilotStatusListenerManager.INSTANCE.isArriveAtStation(); +// Log.d(TAG, "-- onChassisLocationGCJ02 -- 1 ---" + ":auto-mode:" + autoPilotState + ", isArriveAtStation: " + isArriveAtStation); +// if (isArriveAtStation && autoPilotState != 2) { +// RouteOverlayDrawer.getInstance().clearMogoRouteOverlay(); +// return; +// } +// Log.d(TAG, "-- onChassisLocationGCJ02 -- 2 ---" + "auto-mode:" + autoPilotState + ", isDemoMode:" + FunctionBuildConfig.isDemoMode + ", force:" + FunctionBuildConfig.isForceDrawAutopilotTrajectoryByDebugSettingView); +// boolean force = FunctionBuildConfig.isForceDrawAutopilotTrajectoryByDebugSettingView || FunctionBuildConfig.isDemoMode && FunctionBuildConfig.isIgnoreConditionsDrawAutopilotTrajectoryData; +// if (!force && autoPilotState != 2) { +// RouteOverlayDrawer.getInstance().clearMogoRouteOverlay(); +// return; +// } +// Log.d(TAG, "-- onChassisLocationGCJ02 -- 3 ---"); + synchronized (queue) { + if (!queue.isEmpty()) { + List items = queue.pollLast(); + if (items != null && !items.isEmpty()) { + TrajectoryOverlayDrawer.getInstance().drawTrajectoryList(items, gnssInfo.getHeading()); + } + } + } + } +} diff --git a/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/business/trajectoryoverlay/TrajectoryOverlayDrawer.java b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/business/trajectoryoverlay/TrajectoryOverlayDrawer.java new file mode 100644 index 0000000000..7ba5a7beb7 --- /dev/null +++ b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/business/trajectoryoverlay/TrajectoryOverlayDrawer.java @@ -0,0 +1,301 @@ +package com.mogo.eagle.core.function.business.trajectoryoverlay; + +import static com.mogo.map.MogoMap.DEFAULT; + +import android.annotation.SuppressLint; +import android.graphics.Color; +import android.os.Handler; +import android.os.HandlerThread; +import android.util.Log; + +import androidx.core.util.Pools; + +import com.mogo.eagle.core.data.map.MogoLatLng; +import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationWGS84ListenerManager; +import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager; +import com.mogo.eagle.core.utilcode.util.DrivingDirectionUtils; +import com.mogo.map.overlay.IMoGoOverlayManager; +import com.mogo.map.overlay.core.Level; +import com.mogo.map.overlay.line.Polyline; +import com.zhidaoauto.map.sdk.open.common.tools.MapTools; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import kotlin.Pair; +import mogo.telematics.pad.MessagePad; + +public class TrajectoryOverlayDrawer { + + private static final String TAG = "TrajectoryOverlayDrawer"; + + // 连接线参数 + private Handler mRenderHandler; + private final IMoGoOverlayManager mogoOverlayManager; + private static volatile TrajectoryOverlayDrawer sInstance; + private static final byte[] obj = new byte[0]; + private Polyline.Options mPolylineOptions; + private static final int COLOR_LIGHT = Color.parseColor("#BAEBF5"); + + + //用于taxi乘客屏渐变颜色集合 + private static List colors = null; + + private TrajectoryOverlayDrawer() { + // 渐变色 + mogoOverlayManager = CallerMapUIServiceManager.INSTANCE.getOverlayManager(); + if (mogoOverlayManager != null) { + mPolylineOptions = new Polyline.Options.Builder("trajectory_overlay", Level.TRAJECTORY_LINE) + .setUseGps(true) + .setWidth(30) + .setIsGradient(true) + .build(); + + // 线条粗细,渐变,渐变色值 + HandlerThread renderTask = new HandlerThread("trajectory_render") { + @Override + protected void onLooperPrepared() { + super.onLooperPrepared(); + mRenderHandler = new Handler(getLooper()); + } + }; + renderTask.start(); + } + } + + public static TrajectoryOverlayDrawer getInstance() { + if (sInstance == null) { + synchronized (obj) { + if (sInstance == null) { + sInstance = new TrajectoryOverlayDrawer(); + } + } + } + return sInstance; + } + + public void clearMogoTrajectoryOverlay() { + if (mogoOverlayManager != null) { + if (mRenderTask != null) { + mRenderHandler.removeCallbacks(mRenderTask); + } + mogoOverlayManager.removeAllLinesInLevel(Level.TRAJECTORY_LINE); + } + } + + private class RenderTask implements Runnable { + private volatile List routeList; + + private final Pools.Pool pools; + private final LinkedList points; + + private double bearing; + + public RenderTask() { + this.pools = new Pools.SimplePool<>(500); + this.points = new LinkedList<>(); + } + + public void setData(List routeList, double bearing) { + this.routeList = routeList; + this.bearing = bearing; + } + + @SuppressLint("LongLogTag") + @Override + public void run() { + IMoGoOverlayManager overlayManager = mogoOverlayManager; + if (overlayManager == null) { + return; + } + LinkedList pps = this.points; + boolean isExcept = false; + int total; + + try { + pps.clear(); + List routes = this.routeList; + if (routes == null || (total = routes.size()) < 2) { + isExcept = true; + return; + } +// boolean isColorfulStrategy = !AppIdentityModeUtils.isTaxi(FunctionBuildConfig.appIdentityMode) || !AppIdentityModeUtils.isPassenger(FunctionBuildConfig.appIdentityMode); + //TODO + boolean isColorfulStrategy = false; + if (isColorfulStrategy) { + TrajectoryStrategy.INSTANCE.start(); + } else { + if (colors == null) { + ArrayList> temps = new ArrayList<>(); +// temps.add(new Pair<>(0, 51)); +// temps.add(new Pair<>(10, 102)); +// temps.add(new Pair<>(30, 51)); +// temps.add(new Pair<>(100, 0)); + temps.add(new Pair<>(0, 51)); + temps.add(new Pair<>(10, 51)); + temps.add(new Pair<>(30, 51)); + temps.add(new Pair<>(100, 51)); + List alphas = MapTools.INSTANCE.getColorAlpha(temps); + if (alphas != null && !alphas.isEmpty()) { + colors = new ArrayList<>(); + for (int i : alphas) { + colors.add(Color.argb(i, 48,203,251)); + } + } + } + } + for (int i = 0; i < total; i++) { + MessagePad.Location route = null; + try { + route = routes.get(i); + if (route == null) { + continue; + } + } catch (Throwable t) { + Log.d("Trajectory", "render-error:" + t.getMessage()); + } + if (route == null) { + //数组越界了,结束循环 + break; + } + MogoLatLng acquire = pools.acquire(); + double latitude = route.getLatitude(); + double longitude = route.getLongitude(); + if (acquire == null) { + acquire = new MogoLatLng(latitude, longitude); + } else { + acquire.lon = longitude; + acquire.lat = latitude; + } +// acquire.acc = route.getAcceleration(); +// acquire.speed = route.getVelocity(); + pps.add(acquire); +// if (isColorfulStrategy) { +//// RouteStrategy.INSTANCE.check(route.getVelocity(), route.getAcceleration(), routeList.size()); +// } + } + double lon = CallerChassisLocationWGS84ListenerManager.INSTANCE.getChassisLocationWGS84().getLongitude(); + double lat = CallerChassisLocationWGS84ListenerManager.INSTANCE.getChassisLocationWGS84().getLatitude(); + if (points.size() > 0) { + MogoLatLng top = null; + while (points.size() != 0) { + MogoLatLng first = points.peek(); + if (first == null) { + continue; + } + if (first == top) { + break; + } + lon = CallerChassisLocationWGS84ListenerManager.INSTANCE.getChassisLocationWGS84().getLongitude(); + lat = CallerChassisLocationWGS84ListenerManager.INSTANCE.getChassisLocationWGS84().getLatitude(); + long angle = isPointOnCarFront(lon, lat, bearing, first.lon, first.lat); + if (angle >= 90) { + if (isColorfulStrategy) { + TrajectoryStrategy.INSTANCE.remove(first.acc); + } + pools.release(first); + points.poll(); + } + top = first; + } + if (points.size() == 0) { + isExcept = true; + return; + } +// MogoLatLng self = pools.acquire(); +// if (self == null) { +// self = new MogoLatLng(lat, lon); +// } else { +// self.lat = lat; +// self.lon = lon; +// } +// points.addFirst(self); + + Polyline.Options.Builder builder; + if (mPolylineOptions == null) { + builder = new Polyline.Options.Builder("trajectory_overlay", Level.TRAJECTORY_LINE) + .setUseGps(true) + .setWidth(20) + .setIsGradient(true); + } else { + builder = mPolylineOptions.builder(); + } + + if (isColorfulStrategy) { + TrajectoryStrategy.INSTANCE.end(); + Strategy strategy = TrajectoryStrategy.INSTANCE.getStrategy(); + List colors = strategy.getColors(); + boolean isLightOn = strategy instanceof ColorfulStrategy && ((ColorfulStrategy) strategy).isLightOn(); + builder.colors(colors); + builder.setLightOn(isLightOn); + builder.setLightColor(COLOR_LIGHT); + builder.setLightSpeed(0.3f); + } else { + if (colors != null && !colors.isEmpty()) { + builder.colors(colors); + builder.setIsGradient(true); +// builder.setLightOn(true); + builder.setLightOn(false); + builder.setLightColor(COLOR_LIGHT); + builder.setLightSpeed(0.3f); + } + } + builder.points(points); + builder.setVisible(true); + Polyline.Options options = builder.build(); + if (mPolylineOptions == null) { + mPolylineOptions = options; + } + overlayManager.showOrUpdateLine(options,DEFAULT); + } else { + isExcept = true; + } + } catch (Throwable t) { + t.printStackTrace(); + } finally { + if (isExcept) { + setVisible(false); + } + if (points.size() > 0) { + for (int i = 0; i < points.size(); i++) { + MogoLatLng latLng = points.get(i); + if (latLng == null) { + continue; + } + pools.release(latLng); + } + } + } + } + + private long isPointOnCarFront(double car_lon, double car_lat, double car_head, double lon, double lat) { + return DrivingDirectionUtils.getDegreeOfCar2Poi2(car_lon, car_lat, lon, lat, car_head); + } + } + + private volatile RenderTask mRenderTask; + + public void drawTrajectoryList(List routeList, double bearing) { + if (mogoOverlayManager != null) { + if (mRenderTask == null) { + mRenderTask = new RenderTask(); + } + mRenderTask.setData(routeList, bearing); + if (mRenderHandler != null) { + mRenderHandler.removeCallbacks(mRenderTask); + mRenderHandler.post(mRenderTask); + } + } + } + + public void setVisible(boolean isVisible) { + if (mogoOverlayManager != null) { + if (isVisible) { + mogoOverlayManager.showAllLinesInLevel(Level.TRAJECTORY_LINE); + } else { + mogoOverlayManager.hideAllLinesInLevel(Level.TRAJECTORY_LINE); + } + } + } +} diff --git a/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/business/trajectoryoverlay/TrajectoryStrategy.kt b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/business/trajectoryoverlay/TrajectoryStrategy.kt new file mode 100644 index 0000000000..5891babd2e --- /dev/null +++ b/core/function-impl/mogo-core-function-map/src/main/java/com/mogo/eagle/core/function/business/trajectoryoverlay/TrajectoryStrategy.kt @@ -0,0 +1,169 @@ +package com.mogo.eagle.core.function.business.trajectoryoverlay + +import android.animation.ArgbEvaluator +import android.graphics.Color +import android.view.animation.AccelerateInterpolator +import com.mogo.eagle.core.data.config.HmiBuildConfig +import com.mogo.eagle.core.function.business.routeoverlay.Colors.Companion.COLOR_BLUE +import com.mogo.eagle.core.function.business.routeoverlay.Colors.Companion.COLOR_BLUE_DARK +import com.mogo.eagle.core.function.business.routeoverlay.Colors.Companion.COLOR_RED_DARK +import com.mogo.eagle.core.function.business.routeoverlay.Colors.Companion.COLOR_TRANSPARENT +import java.util.* +import kotlin.properties.Delegates + + +interface IStrategy { + fun getColors(): List +} + +class Colors { + + companion object { + val COLOR_BLUE = Color.parseColor("#FF2ABAD9") + val COLOR_BLUE_DARK = Color.parseColor("#FF074EFF") + val COLOR_RED_DARK = Color.parseColor("#FFFF5F00") + val COLOR_TRANSPARENT = Color.parseColor("#002ABAD9") + } +} + +sealed class Strategy : IStrategy + +class DefaultStrategy(private val colors: List? = null) : Strategy() { + + override fun getColors(): List = colors ?: listOf(COLOR_BLUE, COLOR_TRANSPARENT) +} + +class ColorfulStrategy(private val colors: List = emptyList(), var isLightOn: Boolean) : + Strategy() { + override fun getColors(): List = colors +} + +object TrajectoryStrategy { + + private var isEnable by Delegates.observable(HmiBuildConfig.isShowRouteStrategy) { _, _, newValue -> + if (!newValue) { + strategy = null + colors.clear() + } + } + + private var strategy: Strategy? = null + + private val colors: ArrayList = ArrayList() + + private var index = 0 + + private val sorted: NavigableMap by lazy { TreeMap() } + + private var endEvaluator: ArgbEvaluator? = null + + private var startColor = Int.MAX_VALUE + + private var hasLessThan0 = false + + fun start() { + if (sorted.isEmpty()) { + fill() + } + strategy = null + index = 0 + startColor = Int.MAX_VALUE + colors.clear() + endEvaluator = null + hasLessThan0 = false + } + + fun end() { + if (isEnable) { + if (colors.isEmpty()) { + return + } + val first = colors[0] + colors.add(0, first) + strategy = ColorfulStrategy(colors, true) + } + } + + fun check(speed: Double, acc: Double, total: Int) { + if (!isEnable) { + return + } + if (sorted.isEmpty()) { + return + } + if (acc < 0) { + hasLessThan0 = true + } + val delta = (total * 0.35).toInt() + val last = total - delta + val entry = sorted.floorEntry(acc) + if (entry != null) { + if (index >= last - 1) { + if (startColor == Int.MAX_VALUE) { + startColor = entry.value + if (endEvaluator == null) { + endEvaluator = ArgbEvaluator() + } + colors += entry.value + } else { + if (endEvaluator != null) { + val fraction = (index - last) * 1.0f / delta + colors += endEvaluator!!.evaluate( + fraction, + startColor, + COLOR_TRANSPARENT + ) as Int + } + } + } else { + colors += entry.value + } + } + index++ + } + + fun remove(acc: Double): List { + if (!isEnable) { + return emptyList() + } + if (sorted.isEmpty()) { + throw AssertionError("sorted map must not be null.") + } + val entry = sorted.floorEntry(acc) + if (entry != null) { + colors.remove(entry.value) + } + return ArrayList(colors) + } + + private fun fill() { + var startValue = -4.0 + var endValue = 0.0 + val step = 0.01 + var current = startValue + val evaluator = ArgbEvaluator() + val interceptor = AccelerateInterpolator() + var total = endValue - startValue + while (current <= endValue) { + val fraction = interceptor.getInterpolation(((current - startValue) / total).toFloat()) + val colorValue = evaluator.evaluate(fraction, COLOR_RED_DARK, COLOR_BLUE) as Int + sorted[current] = colorValue + current += step + } + startValue = 0.01 + endValue = 3.0 + current = startValue + total = endValue - startValue + while (current <= endValue) { + val fraction = (current - startValue) / total + val colorValue = + evaluator.evaluate(fraction.toFloat(), COLOR_BLUE, COLOR_BLUE_DARK) as Int + sorted[current] = colorValue + current += step + } + } + + fun getStrategy(): Strategy = if (isEnable) { + (strategy ?: DefaultStrategy()) + } else DefaultStrategy() +} \ No newline at end of file diff --git a/libraries/mogo-map-api/src/main/java/com/mogo/map/overlay/core/Level.kt b/libraries/mogo-map-api/src/main/java/com/mogo/map/overlay/core/Level.kt index 620cd25678..a1e56e7edd 100644 --- a/libraries/mogo-map-api/src/main/java/com/mogo/map/overlay/core/Level.kt +++ b/libraries/mogo-map-api/src/main/java/com/mogo/map/overlay/core/Level.kt @@ -15,6 +15,11 @@ enum class Level(val zIndex: Int) { */ ROAD_CENTER_LINE(40000), + /** + * 全局轨迹线 + */ + TRAJECTORY_LINE(74000), + /** * 前车引导线 */