如何在 Android 中的 Google 地图中生成两个位置之间的路线?
谷歌地图或任何其他此类应用程序具有生成两个位置之间路线的方法。通常,有很多参数,如最近距离、最快距离、替代路线等,足以满足需求。这些应用程序真的很吸引人,但开发人员知道开发如此漂亮的应用程序背后的痛苦。
通过本文,我们将向您展示如何在 Android 中的 Google 地图中生成两个位置之间的路线。请按照以下步骤开始。
分步实施
第 1 步:在 Android Studio 中创建一个新项目
要在 Android Studio 中创建新项目,请参阅如何在 Android Studio 中创建/启动新项目。我们在Kotlin 中演示了该应用程序,因此请确保在创建新项目时选择Kotlin作为主要语言。
第 2 步:添加这些依赖项并同步项目
// For Map fragment
implementation ‘com.google.android.libraries.places:places:2.4.0’
// To make a call to for getting Coordinates response from a Web URL
implementation ‘com.squareup.okhttp3:okhttp:4.9.0’
第 3 步:在 AndroidManifest.xml 文件中添加此权限
第 4 步:在 activity_main.xml 文件中添加此 Google Map 片段
XML
Kotlin
if (!Places.isInitialized()) {
Places.initialize(applicationContext, apiKey)
}
Kotlin
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
Kotlin
// GeeksforGeeks coordinates
private var originLatitude: Double = 28.5021359
private var originLongitude: Double = 77.4054901
// Coordinates of a park nearby
private var destinationLatitude: Double = 28.5151087
private var destinationLongitude: Double = 77.3932163
Kotlin
private lateinit var mMap: GoogleMap
Kotlin
override fun onMapReady(p0: GoogleMap?) {
mMap = p0!!
val originLocation = LatLng(originLatitude, originLongitude)
mMap.clear()
mMap.addMarker(MarkerOptions().position(originLocation))
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(originLocation, 18F))
}
Kotlin
private fun getDirectionURL(origin:LatLng, dest:LatLng, secret: String) : String{
return "https://maps.googleapis.com/maps/api/directions/json?origin=${origin.latitude},${origin.longitude}" +
"&destination=${dest.latitude},${dest.longitude}" +
"&sensor=false" +
"&mode=driving" +
"&key=$secret"
}
Kotlin
fun decodePolyline(encoded: String): List {
val poly = ArrayList()
var index = 0
val len = encoded.length
var lat = 0
var lng = 0
while (index < len) {
var b: Int
var shift = 0
var result = 0
do {
b = encoded[index++].code - 63
result = result or (b and 0x1f shl shift)
shift += 5
} while (b >= 0x20)
val dlat = if (result and 1 != 0) (result shr 1).inv() else result shr 1
lat += dlat
shift = 0
result = 0
do {
b = encoded[index++].code - 63
result = result or (b and 0x1f shl shift)
shift += 5
} while (b >= 0x20)
val dlng = if (result and 1 != 0) (result shr 1).inv() else result shr 1
lng += dlng
val latLng = LatLng((lat.toDouble() / 1E5),(lng.toDouble() / 1E5))
poly.add(latLng)
}
return poly
}
Kotlin
class MapData {
var routes = ArrayList()
}
class Routes {
var legs = ArrayList()
}
class Legs {
var distance = Distance()
var duration = Duration()
var end_address = ""
var start_address = ""
var end_location =Location()
var start_location = Location()
var steps = ArrayList()
}
class Steps {
var distance = Distance()
var duration = Duration()
var end_address = ""
var start_address = ""
var end_location =Location()
var start_location = Location()
var polyline = PolyLine()
var travel_mode = ""
var maneuver = ""
}
class Duration {
var text = ""
var value = 0
}
class Distance {
var text = ""
var value = 0
}
class PolyLine {
var points = ""
}
class Location{
var lat =""
var lng =""
}
Kotlin
@SuppressLint("StaticFieldLeak")
private inner class GetDirection(val url : String) : AsyncTask>>(){
override fun doInBackground(vararg params: Void?): List> {
val client = OkHttpClient()
val request = Request.Builder().url(url).build()
val response = client.newCall(request).execute()
val data = response.body!!.string()
val result = ArrayList>()
try{
val respObj = Gson().fromJson(data,MapData::class.java)
val path = ArrayList()
for (i in 0 until respObj.routes[0].legs[0].steps.size){
path.addAll(decodePolyline(respObj.routes[0].legs[0].steps[i].polyline.points))
}
result.add(path)
}catch (e:Exception){
e.printStackTrace()
}
return result
}
override fun onPostExecute(result: List>) {
val lineoption = PolylineOptions()
for (i in result.indices){
lineoption.addAll(result[i])
lineoption.width(10f)
lineoption.color(Color.GREEN)
lineoption.geodesic(true)
}
mMap.addPolyline(lineoption)
}
}
Kotlin
// MainActivity.kt
import android.annotation.SuppressLint
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.Color
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
import com.google.android.gms.maps.model.PolylineOptions
import com.google.android.libraries.places.api.Places
import com.google.gson.Gson
import okhttp3.OkHttpClient
import okhttp3.Request
class MainActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var mMap: GoogleMap
private var originLatitude: Double = 28.5021359
private var originLongitude: Double = 77.4054901
private var destinationLatitude: Double = 28.5151087
private var destinationLongitude: Double = 77.3932163
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Fetching API_KEY which we wrapped
val ai: ApplicationInfo = applicationContext.packageManager
.getApplicationInfo(applicationContext.packageName, PackageManager.GET_META_DATA)
val value = ai.metaData["com.google.android.geo.API_KEY"]
val apiKey = value.toString()
// Initializing the Places API with the help of our API_KEY
if (!Places.isInitialized()) {
Places.initialize(applicationContext, apiKey)
}
// Map Fragment
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
val gd = findViewById
Kotlin
// MapData.kt
class MapData {
var routes = ArrayList()
}
class Routes {
var legs = ArrayList()
}
class Legs {
var distance = Distance()
var duration = Duration()
var end_address = ""
var start_address = ""
var end_location =Location()
var start_location = Location()
var steps = ArrayList()
}
class Steps {
var distance = Distance()
var duration = Duration()
var end_address = ""
var start_address = ""
var end_location =Location()
var start_location = Location()
var polyline = PolyLine()
var travel_mode = ""
var maneuver = ""
}
class Duration {
var text = ""
var value = 0
}
class Distance {
var text = ""
var value = 0
}
class PolyLine {
var points = ""
}
class Location{
var lat =""
var lng =""
}
XML
第 5 步:获取并存储您的 Places API 密钥
- 我们的应用程序使用 Google 的 Places API,因此我们需要从 Google 获取 Places API 密钥。要获取 API 密钥,请参阅生成用于使用任何 Google API 的 API 密钥。
- 隐藏 API 密钥是必不可少的,为此,请参阅如何在 Android Studio 中隐藏 API 和秘密密钥?
第 6 步:在 MainActivity 中检索并验证您的密钥
参考以上两篇文章后,您将在 Main 中调用您的 API 密钥。要验证使用 Map Fragment 的密钥,请添加此代码。
科特林
if (!Places.isInitialized()) {
Places.initialize(applicationContext, apiKey)
}
第七步:初始化地图片段
添加以下代码后,IDE 将希望将MainActivity扩展到OnMapReadyCallback 。应用这些更改。您现在还需要实现成员函数。成员函数将是onMapReady 。完成后,代码将没有错误。
科特林
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
第八步:获取两个地方的坐标
我们已经手动声明了两个地方的纬度和经度值,我们希望在两个地方之间生成一条路线。我们在全球范围内宣布了它们。但是您可以使用自己的方法来获取这些坐标。
科特林
// GeeksforGeeks coordinates
private var originLatitude: Double = 28.5021359
private var originLongitude: Double = 77.4054901
// Coordinates of a park nearby
private var destinationLatitude: Double = 28.5151087
private var destinationLongitude: Double = 77.3932163
步骤 9:编辑 onMapReady函数
mMap 已在全局变量中声明。
科特林
private lateinit var mMap: GoogleMap
现在我们将更改 onMapReady 调用。
科特林
override fun onMapReady(p0: GoogleMap?) {
mMap = p0!!
val originLocation = LatLng(originLatitude, originLongitude)
mMap.clear()
mMap.addMarker(MarkerOptions().position(originLocation))
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(originLocation, 18F))
}
第 10 步:创建一个函数来生成方向 URL
科特林
private fun getDirectionURL(origin:LatLng, dest:LatLng, secret: String) : String{
return "https://maps.googleapis.com/maps/api/directions/json?origin=${origin.latitude},${origin.longitude}" +
"&destination=${dest.latitude},${dest.longitude}" +
"&sensor=false" +
"&mode=driving" +
"&key=$secret"
}
第 11 步:创建一个函数来解码折线
科特林
fun decodePolyline(encoded: String): List {
val poly = ArrayList()
var index = 0
val len = encoded.length
var lat = 0
var lng = 0
while (index < len) {
var b: Int
var shift = 0
var result = 0
do {
b = encoded[index++].code - 63
result = result or (b and 0x1f shl shift)
shift += 5
} while (b >= 0x20)
val dlat = if (result and 1 != 0) (result shr 1).inv() else result shr 1
lat += dlat
shift = 0
result = 0
do {
b = encoded[index++].code - 63
result = result or (b and 0x1f shl shift)
shift += 5
} while (b >= 0x20)
val dlng = if (result and 1 != 0) (result shr 1).inv() else result shr 1
lng += dlng
val latLng = LatLng((lat.toDouble() / 1E5),(lng.toDouble() / 1E5))
poly.add(latLng)
}
return poly
}
第 12 步:为地图参数创建一个类
传递第 10 步时的响应需要一个对象实例来捕获 JSON。这个类就是那个对象。
科特林
class MapData {
var routes = ArrayList()
}
class Routes {
var legs = ArrayList()
}
class Legs {
var distance = Distance()
var duration = Duration()
var end_address = ""
var start_address = ""
var end_location =Location()
var start_location = Location()
var steps = ArrayList()
}
class Steps {
var distance = Distance()
var duration = Duration()
var end_address = ""
var start_address = ""
var end_location =Location()
var start_location = Location()
var polyline = PolyLine()
var travel_mode = ""
var maneuver = ""
}
class Duration {
var text = ""
var value = 0
}
class Distance {
var text = ""
var value = 0
}
class PolyLine {
var points = ""
}
class Location{
var lat =""
var lng =""
}
Step 13:创建内部类,传递Step 8生成的URL字符串,调用decode polyline函数
科特林
@SuppressLint("StaticFieldLeak")
private inner class GetDirection(val url : String) : AsyncTask>>(){
override fun doInBackground(vararg params: Void?): List> {
val client = OkHttpClient()
val request = Request.Builder().url(url).build()
val response = client.newCall(request).execute()
val data = response.body!!.string()
val result = ArrayList>()
try{
val respObj = Gson().fromJson(data,MapData::class.java)
val path = ArrayList()
for (i in 0 until respObj.routes[0].legs[0].steps.size){
path.addAll(decodePolyline(respObj.routes[0].legs[0].steps[i].polyline.points))
}
result.add(path)
}catch (e:Exception){
e.printStackTrace()
}
return result
}
override fun onPostExecute(result: List>) {
val lineoption = PolylineOptions()
for (i in result.indices){
lineoption.addAll(result[i])
lineoption.width(10f)
lineoption.color(Color.GREEN)
lineoption.geodesic(true)
}
mMap.addPolyline(lineoption)
}
}
完整的源代码:
从这里下载源代码。
科特林
// MainActivity.kt
import android.annotation.SuppressLint
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.Color
import android.os.AsyncTask
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
import com.google.android.gms.maps.model.PolylineOptions
import com.google.android.libraries.places.api.Places
import com.google.gson.Gson
import okhttp3.OkHttpClient
import okhttp3.Request
class MainActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var mMap: GoogleMap
private var originLatitude: Double = 28.5021359
private var originLongitude: Double = 77.4054901
private var destinationLatitude: Double = 28.5151087
private var destinationLongitude: Double = 77.3932163
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Fetching API_KEY which we wrapped
val ai: ApplicationInfo = applicationContext.packageManager
.getApplicationInfo(applicationContext.packageName, PackageManager.GET_META_DATA)
val value = ai.metaData["com.google.android.geo.API_KEY"]
val apiKey = value.toString()
// Initializing the Places API with the help of our API_KEY
if (!Places.isInitialized()) {
Places.initialize(applicationContext, apiKey)
}
// Map Fragment
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
val gd = findViewById
科特林
// MapData.kt
class MapData {
var routes = ArrayList()
}
class Routes {
var legs = ArrayList()
}
class Legs {
var distance = Distance()
var duration = Duration()
var end_address = ""
var start_address = ""
var end_location =Location()
var start_location = Location()
var steps = ArrayList()
}
class Steps {
var distance = Distance()
var duration = Duration()
var end_address = ""
var start_address = ""
var end_location =Location()
var start_location = Location()
var polyline = PolyLine()
var travel_mode = ""
var maneuver = ""
}
class Duration {
var text = ""
var value = 0
}
class Distance {
var text = ""
var value = 0
}
class PolyLine {
var points = ""
}
class Location{
var lat =""
var lng =""
}
XML
输出: