From f33024a4cd6ca3fe50e32220f54cd6b9fa3f4619 Mon Sep 17 00:00:00 2001 From: Kurisu Date: Tue, 25 Oct 2022 09:55:30 +0800 Subject: [PATCH] 封装地图路线规划 --- lib/android/src/main/java/cn/feewee/amap3d/AMap3DPackage.kt | 4 +++- lib/android/src/main/java/cn/feewee/amap3d/Utils.kt | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------------------- lib/android/src/main/java/cn/feewee/amap3d/map_view/route/DrivingRoute.kt | 258 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/android/src/main/java/cn/feewee/amap3d/map_view/route/DrivingRouteManager.kt | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/android/src/main/java/cn/feewee/amap3d/map_view/route/RouteOverlay.kt | 355 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/android/src/main/res/drawable-hdpi/cqfw_amap_bus.png | Bin 0 -> 1071 bytes lib/android/src/main/res/drawable-hdpi/cqfw_amap_car.png | Bin 0 -> 1170 bytes lib/android/src/main/res/drawable-hdpi/cqfw_amap_end.png | Bin 0 -> 11147 bytes lib/android/src/main/res/drawable-hdpi/cqfw_amap_man.png | Bin 0 -> 1290 bytes lib/android/src/main/res/drawable-hdpi/cqfw_amap_ride.png | Bin 0 -> 1332 bytes lib/android/src/main/res/drawable-hdpi/cqfw_amap_start.png | Bin 0 -> 10448 bytes lib/android/src/main/res/drawable-hdpi/cqfw_amap_through.png | Bin 0 -> 4509 bytes lib/android/src/main/res/drawable-hdpi/cqfw_amap_train.png | Bin 0 -> 1111 bytes lib/android/src/main/res/drawable-hdpi/cqfw_amap_truck.png | Bin 0 -> 1171 bytes lib/android/src/main/res/drawable-hdpi/cqfw_custtexture.png | Bin 0 -> 1496 bytes lib/android/src/main/res/layout/dialog_progressbar_circle.xml | 28 ++++++++++++++++++++++++++++ lib/android/src/main/res/values/strings.xml | 4 ++++ lib/src/index.ts | 3 ++- lib/src/route/driving-route.tsx | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 19 files changed, 1027 insertions(+), 58 deletions(-) create mode 100644 lib/android/src/main/java/cn/feewee/amap3d/map_view/route/DrivingRoute.kt create mode 100644 lib/android/src/main/java/cn/feewee/amap3d/map_view/route/DrivingRouteManager.kt create mode 100644 lib/android/src/main/java/cn/feewee/amap3d/map_view/route/RouteOverlay.kt create mode 100644 lib/android/src/main/res/drawable-hdpi/cqfw_amap_bus.png create mode 100644 lib/android/src/main/res/drawable-hdpi/cqfw_amap_car.png create mode 100644 lib/android/src/main/res/drawable-hdpi/cqfw_amap_end.png create mode 100644 lib/android/src/main/res/drawable-hdpi/cqfw_amap_man.png create mode 100644 lib/android/src/main/res/drawable-hdpi/cqfw_amap_ride.png create mode 100644 lib/android/src/main/res/drawable-hdpi/cqfw_amap_start.png create mode 100644 lib/android/src/main/res/drawable-hdpi/cqfw_amap_through.png create mode 100644 lib/android/src/main/res/drawable-hdpi/cqfw_amap_train.png create mode 100644 lib/android/src/main/res/drawable-hdpi/cqfw_amap_truck.png create mode 100644 lib/android/src/main/res/drawable-hdpi/cqfw_custtexture.png create mode 100644 lib/android/src/main/res/layout/dialog_progressbar_circle.xml create mode 100644 lib/android/src/main/res/values/strings.xml create mode 100644 lib/src/route/driving-route.tsx diff --git a/lib/android/src/main/java/cn/feewee/amap3d/AMap3DPackage.kt b/lib/android/src/main/java/cn/feewee/amap3d/AMap3DPackage.kt index b5be078..0c98ff3 100644 --- a/lib/android/src/main/java/cn/feewee/amap3d/AMap3DPackage.kt +++ b/lib/android/src/main/java/cn/feewee/amap3d/AMap3DPackage.kt @@ -5,6 +5,7 @@ import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.uimanager.ViewManager import cn.feewee.amap3d.map_view.* +import cn.feewee.amap3d.map_view.route.DrivingRouteManager import cn.feewee.amap3d.modules.SdkModule import cn.feewee.amap3d.modules.AMapGeolocationModule @@ -24,7 +25,8 @@ class AMap3DPackage : ReactPackage { PolygonManager(), CircleManager(), HeatMapManager(), - MultiPointManager() + MultiPointManager(), + DrivingRouteManager() ) } } diff --git a/lib/android/src/main/java/cn/feewee/amap3d/Utils.kt b/lib/android/src/main/java/cn/feewee/amap3d/Utils.kt index a5292da..1327d30 100644 --- a/lib/android/src/main/java/cn/feewee/amap3d/Utils.kt +++ b/lib/android/src/main/java/cn/feewee/amap3d/Utils.kt @@ -1,11 +1,15 @@ package cn.feewee.amap3d +import android.content.Context import android.content.res.Resources import android.graphics.Bitmap import android.graphics.Point import android.location.Location import android.view.View +import android.widget.Toast import com.amap.api.maps.model.* +import com.amap.api.services.core.AMapException +import com.amap.api.services.core.LatLonPoint import com.facebook.drawee.backends.pipeline.Fresco import com.facebook.imagepipeline.common.ResizeOptions import com.facebook.imagepipeline.request.BasePostprocessor @@ -15,100 +19,236 @@ import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.WritableMap import com.facebook.react.views.imagehelper.ImageSource +import kotlin.math.asin +import kotlin.math.cos +import kotlin.math.sin +import kotlin.math.sqrt fun Float.toPx(): Int { - return (this * Resources.getSystem().displayMetrics.density).toInt() + return (this * Resources.getSystem().displayMetrics.density).toInt() } fun Int.toPx(): Int { - return (this * Resources.getSystem().displayMetrics.density).toInt() + return (this * Resources.getSystem().displayMetrics.density).toInt() } fun ReadableMap.toPoint(): Point { - return Point(getDouble("x").toFloat().toPx(), getDouble("y").toFloat().toPx()) + return Point(getDouble("x").toFloat().toPx(), getDouble("y").toFloat().toPx()) } fun ReadableMap.toLatLng(): LatLng { - return LatLng(getDouble("latitude"), getDouble("longitude")) + return LatLng(getDouble("latitude"), getDouble("longitude")) +} + +fun ReadableMap.toLatLonPoint(): LatLonPoint { + return LatLonPoint(getDouble("latitude"), getDouble("longitude")) } fun ReadableArray.toLatLngList(): List { - return (0 until size()).map { - // @todo 暂时兼容 0.63 - @Suppress("UNNECESSARY_NOT_NULL_ASSERTION") - getMap(it)!!.toLatLng() - } + return (0 until size()).map { + getMap(it)!!.toLatLng() + } } fun LatLng.toJson(): WritableMap { - return Arguments.createMap().apply { - putDouble("latitude", latitude) - putDouble("longitude", longitude) - } + return Arguments.createMap().apply { + putDouble("latitude", latitude) + putDouble("longitude", longitude) + } } fun Poi.toJson(): WritableMap { - return Arguments.createMap().apply { - putMap("position", coordinate.toJson()) - putString("id", poiId) - putString("name", name) - } + return Arguments.createMap().apply { + putMap("position", coordinate.toJson()) + putString("id", poiId) + putString("name", name) + } } fun CameraPosition.toJson(): WritableMap { - return Arguments.createMap().apply { - putMap("target", target.toJson()) - putDouble("zoom", zoom.toDouble()) - putDouble("tilt", tilt.toDouble()) - putDouble("bearing", bearing.toDouble()) - } + return Arguments.createMap().apply { + putMap("target", target.toJson()) + putDouble("zoom", zoom.toDouble()) + putDouble("tilt", tilt.toDouble()) + putDouble("bearing", bearing.toDouble()) + } } fun Location.toJson(): WritableMap { - return Arguments.createMap().apply { - putDouble("timestamp", time.toDouble()) - putMap("coords", Arguments.createMap().apply { - putDouble("latitude", latitude) - putDouble("longitude", longitude) - putDouble("latitude", latitude) - putDouble("accuracy", accuracy.toDouble()) - putDouble("heading", bearing.toDouble()) - putDouble("speed", speed.toDouble()) - }) - } + return Arguments.createMap().apply { + putDouble("timestamp", time.toDouble()) + putMap("coords", Arguments.createMap().apply { + putDouble("latitude", latitude) + putDouble("longitude", longitude) + putDouble("latitude", latitude) + putDouble("accuracy", accuracy.toDouble()) + putDouble("heading", bearing.toDouble()) + putDouble("speed", speed.toDouble()) + }) + } } fun LatLngBounds.toJson(): WritableMap { - return Arguments.createMap().apply { - putMap("southwest", southwest.toJson()) - putMap("northeast", northeast.toJson()) - } + return Arguments.createMap().apply { + putMap("southwest", southwest.toJson()) + putMap("northeast", northeast.toJson()) + } } fun ReadableMap.getFloat(key: String): Float? { - if (hasKey(key)) return getDouble(key).toFloat() - return null + if (hasKey(key)) return getDouble(key).toFloat() + return null } fun getEventTypeConstants(vararg list: String): Map { - return list.associateWith { mapOf("phasedRegistrationNames" to mapOf("bubbled" to it)) } + return list.associateWith { mapOf("phasedRegistrationNames" to mapOf("bubbled" to it)) } } fun View.fetchImage(source: ReadableMap, callback: (BitmapDescriptor) -> Unit) { - val uri = ImageSource(context, source.getString("uri")).uri - val request = ImageRequestBuilder.newBuilderWithSource(uri).let { - it.postprocessor = object : BasePostprocessor() { - override fun process(bitmap: Bitmap) { - callback(BitmapDescriptorFactory.fromBitmap(bitmap)) - } + val uri = ImageSource(context, source.getString("uri")).uri + val request = ImageRequestBuilder.newBuilderWithSource(uri).let { + it.postprocessor = object : BasePostprocessor() { + override fun process(bitmap: Bitmap) { + callback(BitmapDescriptorFactory.fromBitmap(bitmap)) + } + } + if (source.hasKey("width") && source.hasKey("height")) { + it.resizeOptions = ResizeOptions.forDimensions( + source.getInt("width").toPx(), + source.getInt("height").toPx() + ) + } + it.build() + } + Fresco.getImagePipeline().fetchDecodedImage(request, this) +} + +/** + * 把LatLonPoint对象转化为LatLon对象 + */ +fun LatLonPoint.convertToLatLng(): LatLng { + return LatLng(latitude, longitude) +} + +/** + * 把LatLng对象转化为LatLonPoint对象 + */ +fun LatLng.convertToLatLonPoint(): LatLonPoint { + return LatLonPoint(latitude, longitude) +} + +/** + * 计算两点之间的距离 + */ +fun calculateDistance(start: LatLng, end: LatLng): Int { + var x1 = start.longitude + var y1 = start.latitude + var x2 = end.longitude + var y2 = end.latitude + val NF_pi = 0.01745329251994329 // 弧度 PI/180 + x1 *= NF_pi + y1 *= NF_pi + x2 *= NF_pi + y2 *= NF_pi + val sinx1 = sin(x1) + val siny1 = sin(y1) + val cosx1 = cos(x1) + val cosy1 = cos(y1) + val sinx2 = sin(x2) + val siny2 = sin(y2) + val cosx2 = cos(x2) + val cosy2 = cos(y2) + val v1 = DoubleArray(3) + v1[0] = cosy1 * cosx1 - cosy2 * cosx2 + v1[1] = cosy1 * sinx1 - cosy2 * sinx2 + v1[2] = siny1 - siny2 + val dist = sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]) + return (asin(dist / 2) * 12742001.5798544).toInt() +} + +fun show(context: Context, info: String) { + Toast.makeText(context.applicationContext, info, Toast.LENGTH_LONG).show() +} + +fun show(context: Context, info: Int) { + Toast.makeText(context.applicationContext, info, Toast.LENGTH_LONG).show() +} + +fun showerror(context: Context, rCode: Int) { + fun logError(info: String, errorCode: Int) { + val sb = StringBuilder() + for (i in 0 until 80) { + sb.append("=") + } + print(sb.toString()) + print(" 错误信息 ") + print(sb.toString()) + print(info) + print("错误码: $errorCode") + print(" ") + print("如果需要更多信息,请根据错误码到以下地址进行查询") + print(" http://lbs.amap.com/api/android-sdk/guide/map-tools/error-code/") + print("如若仍无法解决问题,请将全部log信息提交到工单系统,多谢合作") + print(sb.toString()) } - if (source.hasKey("width") && source.hasKey("height")) { - it.resizeOptions = ResizeOptions.forDimensions( - source.getInt("width").toPx(), - source.getInt("height").toPx() - ) + + try { + when (rCode) { + 1001 -> throw AMapException(AMapException.AMAP_SIGNATURE_ERROR) + 1002 -> throw AMapException(AMapException.AMAP_INVALID_USER_KEY) + 1003 -> throw AMapException(AMapException.AMAP_SERVICE_NOT_AVAILBALE) + 1004 -> throw AMapException(AMapException.AMAP_DAILY_QUERY_OVER_LIMIT) + 1005 -> throw AMapException(AMapException.AMAP_ACCESS_TOO_FREQUENT) + 1006 -> throw AMapException(AMapException.AMAP_INVALID_USER_IP) + 1007 -> throw AMapException(AMapException.AMAP_INVALID_USER_DOMAIN) + 1008 -> throw AMapException(AMapException.AMAP_INVALID_USER_SCODE) + 1009 -> throw AMapException(AMapException.AMAP_USERKEY_PLAT_NOMATCH) + 1010 -> throw AMapException(AMapException.AMAP_IP_QUERY_OVER_LIMIT) + 1011 -> throw AMapException(AMapException.AMAP_NOT_SUPPORT_HTTPS) + 1012 -> throw AMapException(AMapException.AMAP_INSUFFICIENT_PRIVILEGES) + 1013 -> throw AMapException(AMapException.AMAP_USER_KEY_RECYCLED) + 1100 -> throw AMapException(AMapException.AMAP_ENGINE_RESPONSE_ERROR) + 1101 -> throw AMapException(AMapException.AMAP_ENGINE_RESPONSE_DATA_ERROR) + 1102 -> throw AMapException(AMapException.AMAP_ENGINE_CONNECT_TIMEOUT) + 1103 -> throw AMapException(AMapException.AMAP_ENGINE_RETURN_TIMEOUT) + 1200 -> throw AMapException(AMapException.AMAP_SERVICE_INVALID_PARAMS) + 1201 -> throw AMapException(AMapException.AMAP_SERVICE_MISSING_REQUIRED_PARAMS) + 1202 -> throw AMapException(AMapException.AMAP_SERVICE_ILLEGAL_REQUEST) + 1203 -> throw AMapException(AMapException.AMAP_SERVICE_UNKNOWN_ERROR) + 1800 -> throw AMapException(AMapException.AMAP_CLIENT_ERRORCODE_MISSSING) + 1801 -> throw AMapException(AMapException.AMAP_CLIENT_ERROR_PROTOCOL) + 1802 -> throw AMapException(AMapException.AMAP_CLIENT_SOCKET_TIMEOUT_EXCEPTION) + 1803 -> throw AMapException(AMapException.AMAP_CLIENT_URL_EXCEPTION) + 1804 -> throw AMapException(AMapException.AMAP_CLIENT_UNKNOWHOST_EXCEPTION) + 1806 -> throw AMapException(AMapException.AMAP_CLIENT_NETWORK_EXCEPTION) + 1900 -> throw AMapException(AMapException.AMAP_CLIENT_UNKNOWN_ERROR) + 1901 -> throw AMapException(AMapException.AMAP_CLIENT_INVALID_PARAMETER) + 1902 -> throw AMapException(AMapException.AMAP_CLIENT_IO_EXCEPTION) + 1903 -> throw AMapException(AMapException.AMAP_CLIENT_NULLPOINT_EXCEPTION) + 2000 -> throw AMapException(AMapException.AMAP_SERVICE_TABLEID_NOT_EXIST) + 2001 -> throw AMapException(AMapException.AMAP_ID_NOT_EXIST) + 2002 -> throw AMapException(AMapException.AMAP_SERVICE_MAINTENANCE) + 2003 -> throw AMapException(AMapException.AMAP_ENGINE_TABLEID_NOT_EXIST) + 2100 -> throw AMapException(AMapException.AMAP_NEARBY_INVALID_USERID) + 2101 -> throw AMapException(AMapException.AMAP_NEARBY_KEY_NOT_BIND) + 2200 -> throw AMapException(AMapException.AMAP_CLIENT_UPLOADAUTO_STARTED_ERROR) + 2201 -> throw AMapException(AMapException.AMAP_CLIENT_USERID_ILLEGAL) + 2202 -> throw AMapException(AMapException.AMAP_CLIENT_NEARBY_NULL_RESULT) + 2203 -> throw AMapException(AMapException.AMAP_CLIENT_UPLOAD_TOO_FREQUENT) + 2204 -> throw AMapException(AMapException.AMAP_CLIENT_UPLOAD_LOCATION_ERROR) + 3000 -> throw AMapException(AMapException.AMAP_ROUTE_OUT_OF_SERVICE) + 3001 -> throw AMapException(AMapException.AMAP_ROUTE_NO_ROADS_NEARBY) + 3002 -> throw AMapException(AMapException.AMAP_ROUTE_FAIL) + 3003 -> throw AMapException(AMapException.AMAP_OVER_DIRECTION_RANGE) + 4000 -> throw AMapException(AMapException.AMAP_SHARE_LICENSE_IS_EXPIRED) + 4001 -> throw AMapException(AMapException.AMAP_SHARE_FAILURE) + else -> { + Toast.makeText(context, "查询失败:$rCode", Toast.LENGTH_LONG).show() + logError("查询失败", rCode) + } + } + } catch (e: Exception) { + Toast.makeText(context.applicationContext, e.message, Toast.LENGTH_LONG).show() + e.message?.let { logError(it, rCode) } } - it.build() - } - Fresco.getImagePipeline().fetchDecodedImage(request, this) } \ No newline at end of file diff --git a/lib/android/src/main/java/cn/feewee/amap3d/map_view/route/DrivingRoute.kt b/lib/android/src/main/java/cn/feewee/amap3d/map_view/route/DrivingRoute.kt new file mode 100644 index 0000000..e58a222 --- /dev/null +++ b/lib/android/src/main/java/cn/feewee/amap3d/map_view/route/DrivingRoute.kt @@ -0,0 +1,258 @@ +package cn.feewee.amap3d.map_view.route + +import android.content.Context +import android.graphics.Color +import android.widget.Toast +import cn.feewee.amap3d.* +import cn.feewee.amap3d.R +import com.amap.api.maps.AMap +import com.amap.api.maps.model.* +import com.amap.api.services.core.AMapException +import com.amap.api.services.route.* +import com.facebook.react.bridge.ReadableArray + +class DrivingRoute(context: Context) : RouteOverlay(context) { + private var drivePath: DrivePathV2? = null + private var throughPointList: List = emptyList() + private var isColorfulline = true + private var throughPointMarkerList: ArrayList = ArrayList() + private var throughPointMarkerVisible = true + private var mPolylineOptions: PolylineOptions? = null + private var mPolylineOptionsColor: PolylineOptions? = null + + init { + try { + mRouteSearch = RouteSearchV2(context) + } catch (e: AMapException) { + e.printStackTrace() + } + } + + fun setThroughPointList(throughPoints: List) { + if (throughPoints.size > 6) { + throughPointList = throughPoints.take(6) + return + } + throughPointList = throughPoints + } + + fun setIsColorfulline(iscolorfulline: Boolean) { + isColorfulline = iscolorfulline + } + + fun setThroughPointMarkerVisible(visible: Boolean) { + try { + throughPointMarkerVisible = visible + if (this.throughPointMarkerList.isNotEmpty()) { + for (i in this.throughPointMarkerList.indices) { + this.throughPointMarkerList[i].isVisible = visible + } + } + } catch (e: Throwable) { + e.printStackTrace() + } + } + + override fun add(map: AMap) { + this.map = map + searchRouteResult() + } + + override fun remove() { + removeMarkerFromMap() + removeLineFromMap() + try { + if (throughPointMarkerList.size > 0) { + for (i in throughPointMarkerList.indices) { + throughPointMarkerList[i].remove() + } + throughPointMarkerList.clear() + } + } catch (e: Throwable) { + e.printStackTrace() + } + } + + private fun addToMap() { + initPolylineOptions() + if (getRouteWidth() == 0f || drivePath == null) { + return + } + + val mLatLngsOfPath = ArrayList() + val tmcs = ArrayList() + val drivePaths = drivePath?.steps + + for (step in drivePaths!!) { + val latlonPoints = step.polyline + val tmclist = step.tmCs + tmcs.addAll(tmclist) + addDrivingStationMarkers(step, latlonPoints[0].convertToLatLng()) + for (latlonpoint in latlonPoints) { + mPolylineOptions?.add(latlonpoint.convertToLatLng()) + mLatLngsOfPath.add(latlonpoint.convertToLatLng()) + } + } + addStartAndEndMarker() + addThroughPointMarker() + if (isColorfulline && tmcs.size > 0) { + colorWayUpdate(tmcs) + addPolyLine(mPolylineOptionsColor) + } else { + addPolyLine(mPolylineOptions) + } + } + + /** + * 初始化线段属性 + */ + private fun initPolylineOptions() { + mPolylineOptions = PolylineOptions() + .color(getRoadColor()) + .setCustomTexture(getRoadLine()) + .width(getRouteWidth()) + } + + /** + * 根据不同的路段拥堵情况展示不同的颜色 + * + * @param tmcSection + */ + private fun colorWayUpdate(tmcSection: List) { + var segmentTrafficStatus: TMC + mPolylineOptionsColor = PolylineOptions().width(getRouteWidth()) + mPolylineOptionsColor?.add(tmcSection[0].polyline[0].convertToLatLng()) + + val colorList: MutableList = ArrayList() + colorList.add(getRoadColor()) + for (i in tmcSection.indices) { + segmentTrafficStatus = tmcSection[i] + val color: Int = getColor(segmentTrafficStatus.status) + val mployline = segmentTrafficStatus.polyline + for (j in 1 until mployline.size) { + mPolylineOptionsColor?.add(mployline[j].convertToLatLng()) + colorList.add(color) + } + } + colorList.add(getRoadColor()) + mPolylineOptionsColor?.colorValues(colorList) + } + + private fun getColor(status: String): Int { + return when (status) { + "畅通" -> { + Color.GREEN + } + "缓行" -> { + Color.YELLOW + } + "拥堵" -> { + Color.RED + } + "严重拥堵" -> { + Color.parseColor("#990033") + } + else -> { + Color.parseColor("#537edc") + } + } + } + + /** + * @param driveStep + * @param latLng + */ + private fun addDrivingStationMarkers(driveStep: DriveStepV2, latLng: LatLng) { + addStationMarker( + MarkerOptions() + .position(latLng) + .title("\u65B9\u5411:" + driveStep.instruction + "\n\u9053\u8DEF:" + driveStep.road) + .snippet(driveStep.instruction).visible(nodeIconVisible) + .anchor(0.5f, 0.5f).icon(getDriveBit()) + ) + } + + /** + * 添加途经点marker + */ + private fun addThroughPointMarker() { + if (throughPointList.isNotEmpty()) { + for (i in throughPointList.indices) { + val latLonPoint = throughPointList[i] + throughPointMarkerList.add( + map.addMarker( + MarkerOptions() + .position(latLonPoint) + .visible(throughPointMarkerVisible) + .icon(getThroughBit()) + .title("\u9014\u7ECF\u70B9") + ) + ) + } + } + } + + override fun searchRouteResult() { + if (mRouteSearch == null) { + return + } + val fromAndTo = createFromAndTo() ?: return + try { + showProgressDialog() + val driveRouteResult = mRouteSearch!!.calculateDriveRoute( + RouteSearchV2.DriveRouteQuery( + fromAndTo, + RouteSearchV2.DrivingStrategy.DEFAULT, + throughPointList.map { r -> r.convertToLatLonPoint() }, + null, + null + ) + ) + onDriveRouteSearched(driveRouteResult) + } catch (e: AMapException) { + dissmissProgressDialog() + showerror(context, e.errorCode) + } + + } + + private fun onDriveRouteSearched(result: DriveRouteResultV2) { + dissmissProgressDialog() + map.clear() + if (result.paths != null) { + if (result.paths.size > 0) { + drivePath = result.paths[0] ?: return + remove() + addToMap() + zoomToSpan() + + } else if (result.paths == null) { + show(context, R.string.no_result) + } + } else { + show(context, R.string.no_result) + } + } + + + fun searchRoute(args: ReadableArray?) { + val start = args?.getMap(0)?.toLatLonPoint() + val end = args?.getMap(1)?.toLatLonPoint() + val through = args?.getArray(2)?.toLatLngList() + if (start == null) { + Toast.makeText(context, "起点不能为空", Toast.LENGTH_LONG).show() + return + } + if (end == null) { + Toast.makeText(context, "终点不能为空", Toast.LENGTH_LONG).show() + return + } + setStartPoint(start) + setEndPoint(end) + if (through?.isNotEmpty() == true) { + setThroughPointList(through) + } + searchRouteResult() + } + +} \ No newline at end of file diff --git a/lib/android/src/main/java/cn/feewee/amap3d/map_view/route/DrivingRouteManager.kt b/lib/android/src/main/java/cn/feewee/amap3d/map_view/route/DrivingRouteManager.kt new file mode 100644 index 0000000..111e910 --- /dev/null +++ b/lib/android/src/main/java/cn/feewee/amap3d/map_view/route/DrivingRouteManager.kt @@ -0,0 +1,95 @@ +package cn.feewee.amap3d.map_view.route + +import cn.feewee.amap3d.toLatLngList +import cn.feewee.amap3d.toLatLonPoint +import cn.feewee.amap3d.toPx +import com.facebook.react.bridge.ReadableArray +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.uimanager.SimpleViewManager +import com.facebook.react.uimanager.ThemedReactContext +import com.facebook.react.uimanager.annotations.ReactProp + +@Suppress("unused") +class DrivingRouteManager : SimpleViewManager() { + override fun getName(): String { + return "DrivingRoute" + } + + override fun createViewInstance(reactContext: ThemedReactContext): DrivingRoute { + return DrivingRoute(reactContext) + } + + companion object { + const val searchRoute = 1 + } + + override fun getCommandsMap(): Map { + return mapOf("searchRoute" to searchRoute) + } + + override fun receiveCommand(route: DrivingRoute, commandId: Int, args: ReadableArray?) { + when (commandId) { + searchRoute -> route.searchRoute(args) + } + } + + @ReactProp(name = "startPoint") + fun setStart(route: DrivingRoute, startPoint: ReadableMap) { + route.setStartPoint(startPoint.toLatLonPoint()) + } + + @ReactProp(name = "endPoint") + fun setEnd(route: DrivingRoute, endPoint: ReadableMap) { + route.setEndPoint(endPoint.toLatLonPoint()) + } + + @ReactProp(name = "width") + fun setWidth(route: DrivingRoute, width: Float) { + route.setRouteWidth(width.toPx().toFloat()) + } + + @ReactProp(name = "lineColor", customType = "Color") + fun setColor(route: DrivingRoute, lineColor: Int) { + route.setRoadColor(lineColor) + } + + @ReactProp(name = "throughPointVisible") + fun setThroughPointVisible(route: DrivingRoute, throughPointVisible: Boolean) { + route.setThroughPointMarkerVisible(throughPointVisible) + } + + @ReactProp(name = "colorFulLine") + fun setColorFulLine(route: DrivingRoute, colorFulLine: Boolean) { + route.setIsColorfulline(colorFulLine) + } + + @ReactProp(name = "throughPointList") + fun setThroughPointList(route: DrivingRoute, position: ReadableArray) { + route.setThroughPointList(position.toLatLngList()) + } + + @ReactProp(name = "driveIcon") + fun setDriveIcon(route: DrivingRoute, driveIcon: ReadableMap?) { + driveIcon?.let { route.setDriveBit(it) } + } + + @ReactProp(name = "startIcon") + fun setStartIcon(route: DrivingRoute, startIcon: ReadableMap?) { + startIcon?.let { route.setStartBit(it) } + } + + @ReactProp(name = "endIcon") + fun setEndIcon(route: DrivingRoute, endIcon: ReadableMap?) { + endIcon?.let { route.setEndBit(it) } + } + + @ReactProp(name = "throughPointIcon") + fun setThroughPointIcon(route: DrivingRoute, throughPoint: ReadableMap?) { + throughPoint?.let { route.setThroughPointBitDes(it) } + } + + @ReactProp(name = "roadLine") + fun setRoadLine(route: DrivingRoute, roadLine: ReadableMap?) { + roadLine?.let { route.setRoadLine(it) } + } +} \ No newline at end of file diff --git a/lib/android/src/main/java/cn/feewee/amap3d/map_view/route/RouteOverlay.kt b/lib/android/src/main/java/cn/feewee/amap3d/map_view/route/RouteOverlay.kt new file mode 100644 index 0000000..e2e0668 --- /dev/null +++ b/lib/android/src/main/java/cn/feewee/amap3d/map_view/route/RouteOverlay.kt @@ -0,0 +1,355 @@ +package cn.feewee.amap3d.map_view.route + +import android.annotation.SuppressLint +import android.app.AlertDialog +import android.content.Context +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import cn.feewee.amap3d.* +import cn.feewee.amap3d.map_view.Overlay +import com.amap.api.maps.AMap +import com.amap.api.maps.CameraUpdateFactory +import com.amap.api.maps.model.* +import com.amap.api.services.core.LatLonPoint +import com.amap.api.services.route.* +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.views.view.ReactViewGroup + +abstract class RouteOverlay(context: Context) : ReactViewGroup(context), Overlay { + lateinit var map: AMap + private var stationMarkers: MutableList = ArrayList() + private var allPolyLines: MutableList = ArrayList() + private var startPoint: LatLng? = null + private var endPoint: LatLng? = null + private var startMarker: Marker? = null + private var endMarker: Marker? = null + private var roadColor: Int = Color.parseColor("#537edc") + private var progDialog: AlertDialog? = null + + protected var mRouteSearch: RouteSearchV2? = null + + abstract fun searchRouteResult() + + /** + * 路段节点图标控制显示接口。 + * @param visible true为显示节点图标,false为不显示。 + * @since V2.3.1 + */ + protected var nodeIconVisible = true + fun setNodeIconVisibility(visible: Boolean) { + try { + nodeIconVisible = visible + if (stationMarkers.size > 0) { + for (i in stationMarkers.indices) { + stationMarkers[i].isVisible = visible + } + } + } catch (e: Throwable) { + e.printStackTrace() + } + } + + + fun setStartPoint(start: LatLonPoint?) { + start?.let { + startPoint = start.convertToLatLng() + } + } + + fun getStartPoint(): LatLng? { + return startPoint + } + + fun setEndPoint(end: LatLonPoint?) { + end?.let { + endPoint = end.convertToLatLng() + } + } + + fun getEndPoint(): LatLng? { + return endPoint + } + + /** + * 起点Marker图标。 + */ + private var startBit: BitmapDescriptor? = null + + fun setStartBit(source: ReadableMap) { + fetchImage(source) { + startBit = it + } + } + + fun getStartBit(): BitmapDescriptor { + return startBit ?: BitmapDescriptorFactory.fromResource(R.drawable.cqfw_amap_start) + } + + /** + * 终点Marker图标。 + */ + private var endBit: BitmapDescriptor? = null + + fun setEndBit(source: ReadableMap) { + fetchImage(source) { + endBit = it + } + } + + fun getEndBit(): BitmapDescriptor { + return endBit ?: BitmapDescriptorFactory.fromResource(R.drawable.cqfw_amap_end) + } + + /** + * 途经点图标 + */ + private var throughBit: BitmapDescriptor? = null + + fun setThroughBit(source: ReadableMap) { + fetchImage(source) { + throughBit = it + } + } + + fun getThroughBit(): BitmapDescriptor { + return throughBit ?: BitmapDescriptorFactory.fromResource(R.drawable.cqfw_amap_through) + } + + /** + * 驾车Marker图标 + */ + private var driveBit: BitmapDescriptor? = null + + fun setDriveBit(source: ReadableMap) { + fetchImage(source) { + driveBit = it + } + } + + fun getDriveBit(): BitmapDescriptor { + return driveBit ?: BitmapDescriptorFactory.fromResource(R.drawable.cqfw_amap_car) + } + + /** + * 骑行Marker图标 + */ + private var rideBit: BitmapDescriptor? = null + + fun setRideBit(source: ReadableMap) { + fetchImage(source) { + rideBit = it + } + } + + fun getRideBit(): BitmapDescriptor { + return rideBit ?: BitmapDescriptorFactory.fromResource(R.drawable.cqfw_amap_ride) + } + + /** + * 公交Marker图标 + */ + private var busBit: BitmapDescriptor? = null + + fun setBusBit(source: ReadableMap) { + fetchImage(source) { + busBit = it + } + } + + fun getBusBit(): BitmapDescriptor { + return busBit ?: BitmapDescriptorFactory.fromResource(R.drawable.cqfw_amap_bus) + } + + /** + * 步行Marker图标 + */ + private var walkBit: BitmapDescriptor? = null + + fun setWalkBit(source: ReadableMap) { + fetchImage(source) { + walkBit = it + } + } + + fun getWalkBit(): BitmapDescriptor { + return walkBit ?: BitmapDescriptorFactory.fromResource(R.drawable.cqfw_amap_man) + } + + private var roadLine: BitmapDescriptor? = null + + fun setRoadLine(source: ReadableMap) { + fetchImage(source) { + roadLine = it + } + } + + fun getRoadLine(): BitmapDescriptor { + return roadLine ?: BitmapDescriptorFactory.fromResource(R.drawable.cqfw_custtexture) + } + + private var routeWidth: Float = 18F + + /** + * 自定义路线宽度 + * @param width + */ + + fun setRouteWidth(width: Float?) { + width?.let { + routeWidth = width + } + } + + fun getRouteWidth(): Float { + return routeWidth + } + + /** + * 自定义路线颜色 + * + * @param color + */ + fun setRoadColor(color: Int) { + roadColor = color + } + + fun getRoadColor(): Int { + return roadColor + } + + protected fun addStartAndEndMarker() { + removeMarkerFromMap() + startMarker = map.addMarker( + MarkerOptions() + .position(startPoint) + .icon(getStartBit()) + .title("\u8D77\u70B9") + ) + endMarker = map.addMarker( + MarkerOptions() + .position(endPoint) + .icon(getEndBit()) + .title("\u7EC8\u70B9") + ) + } + + protected fun addStationMarker(options: MarkerOptions?) { + if (options == null) { + return + } + val marker = map.addMarker(options) + if (marker != null) { + stationMarkers.add(marker) + } + } + + protected fun addPolyLine(options: PolylineOptions?) { + if (options == null) { + return + } + val polyline = map.addPolyline(options) + if (polyline != null) { + allPolyLines.add(polyline) + } + } + + /** + * 移动镜头到当前的视角。 + * @since V2.1.0 + */ + protected fun zoomToSpan() { + if (startPoint != null) { + try { + val bounds = getLatLngBounds() + map.animateCamera( + CameraUpdateFactory + .newLatLngBounds(bounds, 100) + ) + } catch (e: Throwable) { + e.printStackTrace() + } + } + } + + protected open fun getLatLngBounds(): LatLngBounds { + return LatLngBounds.builder() + .include(LatLng(startPoint!!.latitude, startPoint!!.longitude)) + .include(LatLng(endPoint!!.latitude, endPoint!!.longitude)) + .build() + } + + /** + * 移除所有的Marker。 + */ + protected fun removeMarkerFromMap() { + startMarker?.destroy() + endMarker?.destroy() + + } + + /** + * 移除所有的路线。 + */ + protected fun removeLineFromMap() { + for (marker in stationMarkers) { + marker.destroy() + } + for (line in allPolyLines) { + line.remove() + } + } + + /** + * 开始搜索路径规划方案 + */ + protected fun createFromAndTo(): RouteSearchV2.FromAndTo? { + if (startPoint == null) { + show(context, "起点未设置") + return null + } + if (endPoint == null) { + show(context, "终点未设置") + return null + } + return RouteSearchV2.FromAndTo( + startPoint!!.convertToLatLonPoint(), + endPoint!!.convertToLatLonPoint() + ) + } + + /** + * 隐藏进度框 + */ + protected fun dissmissProgressDialog() { + progDialog?.dismiss() + } + + /** + * 显示进度框 + */ + protected fun showProgressDialog() { + if (progDialog == null) { + progressBarCircleDialog() + } + progDialog?.setCanceledOnTouchOutside(false); + progDialog?.setCancelable(false); + progDialog?.show(); + } + + + private fun progressBarCircleDialog() { + val builder = AlertDialog.Builder( + context, + AlertDialog.THEME_HOLO_LIGHT + ) + val inflater = LayoutInflater.from(context); + @SuppressLint("InflateParams") + val view: View = inflater.inflate(R.layout.dialog_progressbar_circle, null); + val tv: TextView = view.findViewById(R.id.tv); + tv.text = "正在搜索..."; + builder.setView(view); + progDialog = builder.create(); + } +} \ No newline at end of file diff --git a/lib/android/src/main/res/drawable-hdpi/cqfw_amap_bus.png b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_bus.png new file mode 100644 index 0000000..519d81e Binary files /dev/null and b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_bus.png differ diff --git a/lib/android/src/main/res/drawable-hdpi/cqfw_amap_car.png b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_car.png new file mode 100644 index 0000000..0d81d7d Binary files /dev/null and b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_car.png differ diff --git a/lib/android/src/main/res/drawable-hdpi/cqfw_amap_end.png b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_end.png new file mode 100644 index 0000000..5503dc5 Binary files /dev/null and b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_end.png differ diff --git a/lib/android/src/main/res/drawable-hdpi/cqfw_amap_man.png b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_man.png new file mode 100644 index 0000000..62a598b Binary files /dev/null and b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_man.png differ diff --git a/lib/android/src/main/res/drawable-hdpi/cqfw_amap_ride.png b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_ride.png new file mode 100644 index 0000000..98d6ee1 Binary files /dev/null and b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_ride.png differ diff --git a/lib/android/src/main/res/drawable-hdpi/cqfw_amap_start.png b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_start.png new file mode 100644 index 0000000..cd716c8 Binary files /dev/null and b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_start.png differ diff --git a/lib/android/src/main/res/drawable-hdpi/cqfw_amap_through.png b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_through.png new file mode 100644 index 0000000..e636b44 Binary files /dev/null and b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_through.png differ diff --git a/lib/android/src/main/res/drawable-hdpi/cqfw_amap_train.png b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_train.png new file mode 100644 index 0000000..0466c78 Binary files /dev/null and b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_train.png differ diff --git a/lib/android/src/main/res/drawable-hdpi/cqfw_amap_truck.png b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_truck.png new file mode 100644 index 0000000..9f48191 Binary files /dev/null and b/lib/android/src/main/res/drawable-hdpi/cqfw_amap_truck.png differ diff --git a/lib/android/src/main/res/drawable-hdpi/cqfw_custtexture.png b/lib/android/src/main/res/drawable-hdpi/cqfw_custtexture.png new file mode 100644 index 0000000..dd8f7e6 Binary files /dev/null and b/lib/android/src/main/res/drawable-hdpi/cqfw_custtexture.png differ diff --git a/lib/android/src/main/res/layout/dialog_progressbar_circle.xml b/lib/android/src/main/res/layout/dialog_progressbar_circle.xml new file mode 100644 index 0000000..6accd5f --- /dev/null +++ b/lib/android/src/main/res/layout/dialog_progressbar_circle.xml @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/lib/android/src/main/res/values/strings.xml b/lib/android/src/main/res/values/strings.xml new file mode 100644 index 0000000..fd96d18 --- /dev/null +++ b/lib/android/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + 对不起,没有搜索到相关数据! + \ No newline at end of file diff --git a/lib/src/index.ts b/lib/src/index.ts index 0b57598..a379c6d 100644 --- a/lib/src/index.ts +++ b/lib/src/index.ts @@ -1,8 +1,9 @@ export { default as Circle } from "./circle"; -export { default as Cluster } from "./cluster"; +export { default as Cluster } from "./cluster/index"; export { default as HeatMap } from "./heat-map"; export { default as MapView } from "./map-view"; export { default as Marker } from "./marker"; +export { default as DrivingRoute } from "./route/driving-route"; export { default as MultiPoint } from "./multi-point"; export { default as Polygon } from "./polygon"; export { default as Polyline } from "./polyline"; diff --git a/lib/src/route/driving-route.tsx b/lib/src/route/driving-route.tsx new file mode 100644 index 0000000..2b16b11 --- /dev/null +++ b/lib/src/route/driving-route.tsx @@ -0,0 +1,86 @@ +import * as React from "react"; +import { ColorValue, ImageSourcePropType, Platform, processColor, requireNativeComponent } from "react-native"; +import Component from "../component"; +// @ts-ignore +import resolveAssetSource from "react-native/Libraries/Image/resolveAssetSource"; +import { LatLng } from "../types"; + +export interface DrivingRouteProps { + /** + * 起点 + */ + startPoint: LatLng; + /** + * 终点 + */ + endPoint: LatLng; + /** + * 途经点 + */ + throughPointList?: LatLng[]; + /** + * 显示途经点 + */ + throughPointVisible?: boolean; + /** + * 根据不同的路段拥堵情况展示不同的颜色 + */ + colorFulLine?: boolean; + /** + * 线段宽度 + */ + width?: number; + /** + * 路线的纹理 + */ + roadLine?: ImageSourcePropType; + /** + * 驾车图标 + */ + driveIcon?: ImageSourcePropType; + /** + * 起点图标 + */ + startIcon?: ImageSourcePropType; + /** + * 终点图标 + */ + endIcon?: ImageSourcePropType; + /** + * 途经点图标 + */ + throughPointIcon?: ImageSourcePropType; + /** + * 线段颜色 + */ + lineColor?: ColorValue; +} + +export default class extends Component { + static defaultProps = { with: 14, throughPointVisible: true }; + + /** + * 路线规划 + */ + routePlan(start: LatLng, end: LatLng, throughPointList: LatLng[] = []) { + this.invoke("searchRoute", [start, end, throughPointList]); + } + + render() { + const props = { + ...this.props, + }; + return ( + + ); + } +} + +const NativeDrivingRoute = requireNativeComponent("DrivingRoute"); -- libgit2 0.22.2