Riku
2025-10-15 53857f42f777e2b9753b8f00cce1a60ce3dcb8fd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package com.flightfeather.uav.common.net
 
import com.flightfeather.uav.common.exception.BizException
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import org.apache.http.util.EntityUtils
import java.net.URLEncoder
 
/**
 * 高德地图Web服务API
 * Date: 2024/07/14
 */
object AMapService {
 
    private const val TAG = "AMapService"
    private const val KEY = "b36a93bac8950d3d7c6c06f21133de51"
 
    private val httpMethod = HttpMethod("restapi.amap.com", 443, true)
 
    data class AMapAddress(
        val country: String,
        val province: String,
        val city: String,
        val citycode: String,
        val district: String,
        val adcode: String,
        val township: String,
        val towncode: String,
        val address: String,
        val streetNumber: String,
        val roadinter: String,
    )
 
    data class AMapDirection(
        // 路线类型,driving: 驾车;
        val type: String,
        // 起点经纬度
        val origin: Pair<Double, Double>,
        // 终点经纬度
        val destination: Pair<Double, Double>,
        // 途径路线经纬度(不包括起点终点)
        val paths: List<Pair<Double, Double>>,
        // 方案距离,单位:米
        val distance: String
    )
 
    /**
     * 驾车路线规划
     */
    fun directionDriving(origin: Pair<Double, Double>, destination: Pair<Double, Double>): AMapDirection {
        val res = httpMethod.get(
            "/v5/direction/driving", listOf(
                "key" to KEY,
                "origin" to "${origin.first},${origin.second}",
                "destination" to "${destination.first},${destination.second}",
                "show_fields" to "polyline"
            )
        )
        val obj = handleRes(res)
        try {
            val count = obj["count"].asString.toIntOrNull()
            if (count != null && count > 0) {
                val path = obj["route"].asJsonObject["paths"].asJsonArray.get(0).asJsonObject
                val finalPaths = mutableListOf<Pair<Double, Double>>()
                path["steps"].asJsonArray.forEach {
                    finalPaths.addAll(
                        it.asJsonObject["polyline"].asString.split(";").map { str ->
                            val strArr = str.split(",")
                            strArr[0].toDouble() to strArr[1].toDouble()
                        }
                    )
                }
                return AMapDirection("driving", origin, destination, finalPaths, path["distance"].asString)
            } else {
                throw BizException("高德API驾车路线规划失败,没有找到可行的路线")
            }
        } catch (e: Exception) {
            throw BizException("高德API驾车路线规划错误,${e.message}")
        }
    }
 
    /**
     * 地理逆编码
     * @param location 坐标点
     * @return 所在街道
     */
    fun reGeo(location: Pair<Double, Double>): AMapAddress {
        val res = httpMethod.get(
            "/v3/geocode/regeo", listOf(
                "key" to KEY,
                "location" to "${location.first},${location.second}",
                "extensions" to "all"
            )
        )
        val obj = handleRes(res)
        try {
            val regeocode = obj["regeocode"].asJsonObject
            val a = regeocode["addressComponent"].asJsonObject
            val streetNumber = a["streetNumber"].asJsonObject
            val roads = regeocode["roads"].asJsonArray
            val roadinters = regeocode["roadinters"].asJsonArray
            val roadinter = if (roadinters.size() > 0) roadinters.get(0).asJsonObject else null
            return AMapAddress(
                a["country"].asString,
                a["province"].asString,
                "",
                a["citycode"].asString,
                a["district"].asString,
                a["adcode"].asString,
                a["township"].asString,
                a["towncode"].asString,
                regeocode["formatted_address"].asString,
                streetNumber["street"].asString + streetNumber["number"].asString
                        + streetNumber["direction"].asString + streetNumber["distance"].asDouble.toInt() + "米",
                if(roadinter == null) "" else roadinter.get("first_name")?.asString +"和" + roadinter.get("second_name")?.asString + "交叉口"
                        + roadinter.get("direction")?.asString + roadinter.get("distance")?.asDouble?.toInt() + "米",
            )
        } catch (e: Exception) {
            throw BizException("高德API坐标转换错误,${e.message}", e.cause)
        }
    }
 
    /**
     * 坐标转换
     * @param locations 原始坐标
     * @param coordsys 原坐标系,可选值:gps;mapbar;baidu;autonavi(不进行转换)
     */
    fun coordinateConvert(locations: List<Pair<Double, Double>>, coordsys: String = "gps"): List<Pair<Double, Double>> {
        val locationsStr = URLEncoder.encode(locations.joinToString("|") { "${it.first},${it.second}" }, "UTF-8")
        val res = httpMethod.get(
            "/v3/assistant/coordinate/convert", listOf(
                "key" to KEY,
                "locations" to locationsStr,
                "coordsys" to coordsys
            )
        )
        val obj = handleRes(res)
        try {
            return obj["locations"].asString.split(";").map {
                val l = it.split(",")
                l[0].toDouble() to l[1].toDouble()
            }
        } catch (e: Exception) {
            throw BizException("高德API坐标转换错误,${e.message}")
        }
    }
 
    private fun handleRes(res: HttpMethod.MyResponse): JsonObject {
        if (res.success) {
            val str = EntityUtils.toString(res.m.entity)
            val json = JsonParser.parseString(str)
            return resCheck(json)
        } else {
            throw BizException("高德API网路链接错误,状态码:${res.m.statusLine.statusCode}")
        }
    }
 
    private fun resCheck(json: JsonElement): JsonObject {
        if (!json.isJsonObject) throw BizException("高德API失败,返回值不是一个object")
 
        val jo = json.asJsonObject
        if (jo["status"].asInt != 1) throw BizException("高德API失败,错误${jo["info"]}")
 
        return jo
    }
}