2018年6月13日 星期三

在 Delphi 中使用 TMapView 顯示路徑、使用Google Map API進行兩點或多點之間的導航.

在App當中使用地圖元件,常常用來提供導航、路線規劃等功能,這些功能的展現,端看畫面上在地圖元件中,能夠盡量詳盡的為使用者繪製出適當的路線。

在前一篇文章裡面,我們介紹了在 TMapView 上面標示地點的作法,在本篇文章中,我們要介紹更進一步的功能:在地圖上面畫線

常使用Google Map網頁版開發功能的朋友們大都已經使用過 Google Map API的強大功能之一:direction API, 也就是我們常使用的『導航功能』。通常我們比較常用的是在兩個地點之間試圖繪製出最佳的交通路線,這是 Google Map Direction API 的基本功能之一,且讓我們娓娓道來。

導航功能的思路,一般會包含幾個步驟:
  • 地點的選定(可能只有起點與終點,也可能加入中途點)
  • 交通工具的選定
  • 路線的繪製
而這些也恰好都是 Google Map Direction API的基本功能。要使用Google Map Direction API,跟前一篇文章的介紹中會有些重疊之處,例如需要申請Google API Key,需要在App裡面作些基本設定,這些基本設定請參考前一篇文章:在 Delphi 當中使用 TMapView 顯示地圖之一:程式設定與加上地點標示。

在使用 Google Map Direction API之前,則有一組金鑰得先申請,您可以到 Google Map Direction API 的頁面申請。申請完畢以後,就可以一起來看這組 API 的強大功能了。

Google Map Direction API 的使用
如果是直接透過瀏覽器來使用,我們可以在網址列輸入以下這個網址:
https://maps.googleapis.com/maps/api/directions/json?origin=25.0416460,121.5362860&destination=23.5531950,120.3472670&key=您的GoogleAPIKey

在上面的網址中,我們可以看到主要網址後是以json開始帶入參數,依照Google Map Direction API 的說明,這代表我們是要索取以JSON作為輸出格式的資料。

之後的參數有兩組, origin跟destination,分別是兩個座標代表起點跟終點,如果您在網址列上面輸入了上面的文字,就按下了Enter,則畫面應該會顯示如下圖的資訊:





這些資訊就包含了導航的相關資訊、每一段路之間的座標、一共有多少段路、起始點與目的地的座標等資訊。
 
我們在Delphi當中,取得了這些資訊之後,就可以來找出其中有用的資訊了,先來看看完整的訊息有哪些:
{
"geocoded_waypoints" : [
{
"geocoder_status" : "OK",
"place_id" : "EicxMDblj7DngaPlj7DljJfluILlpKflronljYDlj7A157eaOTTomZ8iGhIYChQKEgkpEFFufalCNBHcc2G4xJNGAxBe",
"types" : [ "street_address" ]
},
{
"geocoder_status" : "OK",
"place_id" : "ChIJKbHLslW9bjQRGOc7frCCI6Q",
"types" : [ "street_address" ]
}
],
"routes" : [
{
"bounds" : {
"northeast" : {
"lat" : 25.0782095,
"lng" : 121.5376204
},
"southwest" : {
"lat" : 23.5496167,
"lng" : 120.3473252
}
},
"copyrights" : "地圖資料©2018 Google",
"legs" : [
{
"distance" : {
"text" : "246 公里",
"value" : 246382
},
"duration" : {
"text" : "2 小時 41 分",
"value" : 9643
},
"end_address" : "616台灣嘉義縣新港鄉中山路116號",
"end_location" : {
"lat" : 23.5531974,
"lng" : 120.3473666
},
"start_address" : "106台灣台北市大安區台5線94號",
"start_location" : {
"lat" : 25.0417488,
"lng" : 121.5363047
},
"steps" : [
{
"distance" : {
"text" : "59 公尺",
"value" : 59
},
"duration" : {
"text" : "1 分",
"value" : 17
},
"end_location" : {
"lat" : 25.0416756,
"lng" : 121.5368846
},
"html_instructions" : "往\u003cb\u003e東\u003c/b\u003e走\u003cb\u003e忠孝東路\u003c/b\u003e/\u003cb\u003e忠孝東路三段\u003c/b\u003e/\u003cb\u003e台5線\u003c/b\u003e朝\u003cb\u003e建國南路一段\u003c/b\u003e前進",
"polyline" : {
"points" : "}}ywC{pxdV@QDi@Dw@"
},
"start_location" : {
"lat" : 25.0417488,
"lng" : 121.5363047
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "0.1 公里",
"value" : 111
},
"duration" : {
"text" : "1 分",
"value" : 27
},
"end_location" : {
"lat" : 25.0407313,
"lng" : 121.5372438
},
"html_instructions" : "於\u003cb\u003e建國南路一段\u003c/b\u003e向\u003cb\u003e右\u003c/b\u003e轉",
"maneuver" : "turn-right",
"polyline" : {
"points" : "o}ywCotxdVj@Mb@KlBm@"
},
"start_location" : {
"lat" : 25.0416756,
"lng" : 121.5368846
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "0.2 公里",
"value" : 165
},
"duration" : {
"text" : "1 分",
"value" : 77
},
"end_location" : {
"lat" : 25.0417789,
"lng" : 121.5372723
},
"html_instructions" : "於\u003cb\u003e迴轉道\u003c/b\u003e處\u003cb\u003e迴轉\u003c/b\u003e",
"maneuver" : "uturn-left",
"polyline" : {
"points" : "qwywCwvxdVI{A_@N}@Za@Li@P]H"
},
"start_location" : {
"lat" : 25.0407313,
"lng" : 121.5372438
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "0.3 公里",
"value" : 297
},
"duration" : {
"text" : "1 分",
"value" : 32
},
"end_location" : {
"lat" : 25.0443566,
"lng" : 121.5366898
},
"html_instructions" : "上匝道,往\u003cb\u003e福爾摩沙高速公路\u003c/b\u003e方向走",
"polyline" : {
"points" : "c~ywC}vxdVSNg@Lm@Pw@Pq@JUBQBo@Da@BE?]@W?U?w@AE@G@G@IBKH"
},
"start_location" : {
"lat" : 25.0417789,
"lng" : 121.5372723
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "1.6 公里",
"value" : 1615
},
"duration" : {
"text" : "1 分",
"value" : 88
},
"end_location" : {
"lat" : 25.058881,
"lng" : 121.5369893
},
"html_instructions" : "走\u003cb\u003e建國高架道路\u003c/b\u003e",
"maneuver" : "merge",
"polyline" : {
"points" : "gnzwCisxdVs@A]?]?w@?uCC_A?S?SAK?W?WAaHE[?w@AcBAy@?UAW?qGEaDA]?_@AgDCcBAcBA{BAI?UAY?eBAO?K?M?gBAO?M?yBCM?K?cCCa@?a@AoA?a@A_@?W?"
},
"start_location" : {
"lat" : 25.0443566,
"lng" : 121.5366898
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "1.0 公里",
"value" : 982
},
"duration" : {
"text" : "1 分",
"value" : 57
},
"end_location" : {
"lat" : 25.0669266,
"lng" : 121.5348117
},
"html_instructions" : "靠\u003cb\u003e左\u003c/b\u003e繼續走\u003cb\u003e建國高架道路\u003c/b\u003e/\u003cb\u003e建國高架道路(國1桃園/濱江街基隆)\u003c/b\u003e\u003cdiv style=\"font-size:0.9em\"\u003e收費路段\u003c/div\u003e",
"maneuver" : "keep-left",
"polyline" : {
"points" : "_i}wCeuxdVsB?K?M?}BCO?M?w@A{HG_@AA?M?O?sECC?M?K?c@?[@Q@O@e@Fc@FMBYJA?WHQFQHOF[Tc@V_@ZGHQNWZU\\W\\q@bAeAtA_@f@"
},
"start_location" : {
"lat" : 25.058881,
"lng" : 121.5369893
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "1.2 公里",
"value" : 1234
},
"duration" : {
"text" : "1 分",
"value" : 81
},
"end_location" : {
"lat" : 25.0739412,
"lng" : 121.52885
},
"html_instructions" : "靠\u003cb\u003e左\u003c/b\u003e繼續走\u003cb\u003e建國高架道路\u003c/b\u003e/\u003cb\u003e圓山交流道 號出口\u003c/b\u003e\u003cdiv style=\"font-size:0.9em\"\u003e收費路段\u003c/div\u003e",
"maneuver" : "keep-left",
"polyline" : {
"points" : "i{~wCqgxdVc@v@OTQTs@bAa@j@IJONMHSNIDOFUHQFSBMBQ@W@O?MAYAi@Es@Ec@CgACqACI?QAKAeA@kCAaA?g@@Y@OBaAN[F[Ja@POHk@f@YRGFEFGJGJCJEJELEPGVAJCRA^?V@V@d@BZDZBND`@JbABn@?P?T?J@HALAVARATIl@ALEVGZAHAN?H"
},
"start_location" : {
"lat" : 25.0669266,
"lng" : 121.5348117
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "11.4 公里",
"value" : 11423
},
"duration" : {
"text" : "8 分",
"value" : 467
},
"end_location" : {
"lat" : 25.0641299,
"lng" : 121.4194995
},
"html_instructions" : "走\u003cb\u003e國道一號\u003c/b\u003e\u003cdiv style=\"font-size:0.9em\"\u003e收費路段\u003c/div\u003e",
"maneuver" : "merge",
"polyline" : {
"points" : "cg`xCibwdVGTITW~@o@dCGVITOj@Oj@K\\_@zA_@nA{@hDGVIVs@lCSt@ER_@~A_A~Dg@dCg@lCSdAIf@_@vBIj@UxA_@zCq@`GKdAI~@Kt@Gz@Gl@Ej@CXAZMdBCd@E~@GbA?BIhBGtBA^A^Cl@A`@Af@?d@Ab@ChAA~AArEEf@@dA?b@?d@@b@?P@p@@n@@RF|ADtBHrBL|CRtDPpCB^D^VjDBRBR@B?D@F@D@L@JDZB\\NrAFd@p@pFNfADRBP`D`Ub@`CVxAFb@Hb@j@|DnA~IBVDTx@rFJt@Lr@dBlLNbAP`A^fCp@~GBRDt@d@vI@~A@n@?l@BlBSzGIfBY~CUvBg@~Da@~BO`AEZ]rBO|@Kl@mApHs@pEg@~C]lCWvBOdBKvBCj@?J@LCVCp@?@KdDAbACjD?t@@jB@`AJ~DFpAJ`BLpBDZDZLxA\\lDt@zEDTBVFX^hBFXFZDRDRdLfk@zIva@zAlHtCfNH`@J`@DTlA|IBTf@bGTxCRzC\\dF@d@Bd@D`ANvGHzCBjBL~IJbFFzBDnAJzATrBFd@XdBNn@R|@XbA\\fA\\bAVp@Nb@BDj@tAj@hAp@nAPZz@xAb@x@j@fAxC|GFLHRHRp@dAh@~@"
},
"start_location" : {
"lat" : 25.0739412,
"lng" : 121.52885
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "0.3 公里",
"value" : 290
},
"duration" : {
"text" : "1 分",
"value" : 12
},
"end_location" : {
"lat" : 25.0625881,
"lng" : 121.417177
},
"html_instructions" : "靠\u003cb\u003e左\u003c/b\u003e以繼續行駛\u003cb\u003e國道一號\u003c/b\u003e\u003cdiv style=\"font-size:0.9em\"\u003e收費路段\u003c/div\u003e",
"maneuver" : "keep-left",
"polyline" : {
"points" : "yi~wC{vadV`@p@p@fAR^|BbElApB"
},
"start_location" : {
"lat" : 25.0641299,
"lng" : 121.4194995
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "17 公尺",
"value" : 17
},
"duration" : {
"text" : "1 分",
"value" : 1
},
"end_location" : {
"lat" : 25.0626106,
"lng" : 121.4170086
},
"html_instructions" : "在\u003cb\u003e泰山收费站\u003c/b\u003e出口下交流道\u003cdiv style=\"font-size:0.9em\"\u003e收費路段\u003c/div\u003e",
"maneuver" : "ramp-right",
"polyline" : {
"points" : "e`~wCkhadVCT?J"
},
"start_location" : {
"lat" : 25.0625881,
"lng" : 121.417177
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "23.5 公里",
"value" : 23496
},
"duration" : {
"text" : "15 分",
"value" : 879
},
"end_location" : {
"lat" : 24.9788469,
"lng" : 121.2266636
},
"html_instructions" : "靠\u003cb\u003e右\u003c/b\u003e,然後併入\u003cb\u003e國道一號\u003c/b\u003e\u003cdiv style=\"font-size:0.9em\"\u003e收費路段\u003c/div\u003e",
"maneuver" : "keep-right",
"polyline" : {
"points" : "i`~wCigadV`HbOhKtRXp@Zn@tAbCpCfFxAfCXf@Vh@d@fA\\z@Z|@r@`CVhARhAN~@F`@J~@?P@TBr@?V?^AJ?L?lBA~@KxC_@hCSjAYjA_@fAc@fAk@lAqAtBqAxAsBlB{BdBiFpDyB|AwIrHgBlB_BdBmA~AuAlBq@pAgAbDa@`By@`Fe@bIEzA@hBBhAJhBPdBPlAx@xGBn@@~@ChAGpAEbAOnAUvAYvAQp@i@vB_A~CY|AOdAKx@Kz@S~CCLGtBA~ABvBHpCVfF^hILnCNjD@FBb@Bb@NdBPtBFr@Dp@LzAFpAFhADhAB~@B`C@T?d@DpADlA?B@\\Bj@?Z?\\@vAAzGB|BAn@?j@?B?DCxD@bB?\\CtC?XChDC|DMfDCb@At@Cv@KfDMpFChAEz@Ex@Ad@[tJCjBCvE?fDBvBD~CBfADtAFbALrBRdCR|BXlCHj@L|@f@nC\\dBTnA`@rB`@|Ap@bCvAfErAlDRf@FNn@pANZ|AxCtBjDpApB|BfDnAdBfCbDp@v@xEhFtApArExDpFzD~B|AxA`ApBpAvCnBbAt@|@r@n@h@`A|@hB`BbBfBtDvE|@lAnAjB`B|CbAvBtArCrAjD^fAh@dB^tAdB`HbBrGxAvFn@xBPl@Pl@FRrApE~AfFrBlGHRFTdAdDxAtEzC`JJ\\JZxBvF~CrH^~@P`@JVd@dAd@bALZNZj@rAXl@^x@`@v@h@dAf@dAP^R^Vn@Xl@Tb@Xf@fBpDR\\Xh@Zf@bErH|@xAP^NXpG|JpGzJb@r@`@n@~AbCPTNTRV~FbIfCbDrCxDnEfFnCbDjSzUX\\X\\p@v@h@v@z@dAbFxGxAhBX`@Z`@`EzFb@h@|@hAV`@V`@h@l@h@l@~AxBNT|@hAFHt@x@t@x@`BvBhD`E`@b@^b@dBzBpAfBf@n@LPJPjCrDfNrRTZTZzBbDz@rAhC~C`DrE|F~Hv@hAv@fArF~HtDnF\\`@Zb@pBrClCnDjCpDl@z@j@z@lCtDf@n@~BdD^f@zAnBjBvBbBnB`DpDtExEdD|CHJ|A|AbKhK`@^b@^tDtDbBfBPRPPTTnEdEz@|@VVVXxAxAlFfFnApARRPRzB|BJLLL|@~@vH~HRVJJHLdAlAhAvAnEhFfNlPrDlEpBbCJJx@z@NNn@j@"
},
"start_location" : {
"lat" : 25.0626106,
"lng" : 121.4170086
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "1.5 公里",
"value" : 1466
},
"duration" : {
"text" : "1 分",
"value" : 58
},
"end_location" : {
"lat" : 24.9698095,
"lng" : 121.2160748
},
"html_instructions" : "走\u003cb\u003e國道一號\u003c/b\u003e\u003cdiv style=\"font-size:0.9em\"\u003e收費路段\u003c/div\u003e",
"maneuver" : "merge",
"polyline" : {
"points" : "ytmwCsa|bV`OrQx@bAV\\V\\dGnHNRNPfIzJd@h@b@j@fBxBpClDdBxB`CxCHL"
},
"start_location" : {
"lat" : 24.9788469,
"lng" : 121.2266636
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "105 公里",
"value" : 104969
},
"duration" : {
"text" : "1 小時 4 分",
"value" : 3865
},
"end_location" : {
"lat" : 24.2754319,
"lng" : 120.6933458
},
"html_instructions" : "分岔路口靠\u003cb\u003e左\u003c/b\u003e以繼續行駛\u003cb\u003e國道一號\u003c/b\u003e\u003cdiv style=\"font-size:0.9em\"\u003e收費路段\u003c/div\u003e",
"maneuver" : "fork-left",
"polyline" : {
"points" : "i|kwCm_zbVzA|A@BbAjAjExFtDdFtXle@JRJPvBxDR`@T^t@pApFdIh@t@`CxCbBjBxGfGz@n@z@p@^`@PPTNRNpD~BvCdB\\PrCzAf@TbBv@bBt@`DjApA`@vDnAtRvDtFdAnAVpSxDhIzBJDdDjA~An@rCnAtC`BhGpEvArA`@\\@@tBvBlArA~BvCbChDf@`A|B~DrA`DdC~GbBpGHXtA|F|G`WdDdHrC`FtCfEr@dAbAnAv@`AbB`BnD`DjFtDlC`BtAn@xB`AlBr@`@NjCz@zPpEjAZf@Jf@LnMfDfDz@rA\\TFVFXHVHrBh@jBX`OxDf@Lf@LlEhAhBn@^Lh@T`Ah@r@d@FBh@\\h@\\LHlA~@\\\\zCpD~@lA`AvAbAbB|ApCvBdFdAnDnAtFdApFLr@Hd@Hd@h@|CbD`RbCpMbAbGpCvORhAH^F`@hCzNDRDRhBjKJf@Hf@~Mhv@@HBFh@~CN|@DXTrATdBXvBR|ARdBLjA@@JfAFz@H`APrBNxBNnDHtDDpDBxA?DAR?R@r@AtBCnDGfDAR?NGhCIhBK~BKbDIlBGxBKlL?X?V?xBVtJL~BXxBBP@RdArIXbBh@bDXzA~@jEvApFtBjGhBvExC~FtNfXrB`FnAdDDLFN^hAjAdFXxAN|@d@vCB\\DZTnCPzBLdERvLLvFLnDThDN|ATtBLhABJJz@RlA\\fB^jB\\zAd@pBZfADRFR@Bp@|Bb@lA@@HTJTPb@p@bBl@nAd@z@r@rAj@dAn@bA~AjCpB`DzB~Dv@zAn@lA~@tB`@dALZJZ@?lAjD`AzCn@|Bl@dCXpA\\hBXbBNdANnANnA?@Fn@Dj@PtCNdDJxEBhA@b@?`@DxB@bC?d@?v@CvEM`HMlEC~@Ah@]xKUnGIpBKdCMbDEx@M`ECr@C|@C|@APItDKnFE|CCdG?bEBvDDfEJ|CLlDLrB`@zHH~@@H@F@Td@~EhAtJjAvGDTNt@`@vBp@zCVnAFZXlA\\rA`@|A\\tAp@lC|@rC|A|DPd@dB~Dt@~A|ArCjB`D`A~AfAvA^h@^f@l@v@d@h@fBpBzAbBl@l@r@p@\\Z^\\|AlAvBbB`BpA`Ar@`Ar@tDvBvHnDlAb@j@Tj@PdBr@dB`@fA\\pB\\lBTd@D~AHd@BdB?vAEzBI`@ENAdBYv@Of@K`@Mr@Wz@]nAm@fAm@tA{@vAkA`BwAn@q@TW`@e@p@w@tBiC|DaF|EmGZ_@hAsAnA}AxAmBz@kAdAcAx@w@rAgA|AkAfAq@RM\\QZQp@Y~@_@lBo@vCu@xB_@zJiAnDSRCTAt@C~AIlBCnCEpED|HVzF\\V@PB`@DN@hEl@dALzHtAhIxB~DpAfE~Al@Xl@XZL`@Lt@\\hDfBzBnAVLVN`HvDZPZPdE|BJFh@Z~ElC|@b@TJTL`HdD~E~BhFnBXJXJpFpBRFRFpEtA`Cr@hDv@lDx@xAXbDj@LBLBv@NzEh@zATdCXtCZ`@F`@BdBNnFh@~@HzALD?h@DjBNfAHbBN|APt@FL@xAP`Fh@zARdALjBVfBZhDl@hCh@xBf@tDz@h@NfBd@fIfC~Br@fJtCPFRFjFbBbD`AxCz@v@TxDv@tEr@`AJjGb@tBJfDH~EAfQQtDChDEh@?bA?tD?`DDn@?zFVjC^FBrBZnDh@|FrAbF~A^JtBz@tB`AvAt@dBbA@@t@b@DBvA|@nB`B~A~Aj@r@l@t@|@pA|@|AfAvBf@pAL\\^jAt@lCr@xCZhBn@rCv@bEj@lCn@tB`@jAZx@b@|@HNJPf@~@x@pAlAvAdBdB`Ax@x@j@nBzAvAz@j@b@DBxBzA|@f@~B|ArBtAvA`Ab@Zl@`@^ZZT^XZTb@\\ZXZXvAzAtAdBRTfAfBhArBjB|D`BrDtAvCJNpArBvAnBfAlA^\\?@l@h@VV^X^XZTTPTPZPZRXPXPXN^PZN\\N\\Lf@Pf@R^L`@L`A\\p@RbAXp@R`@LD@b@LRDNFx@Tj@P`@LXJbA^d@Pf@Td@R\\Np@Zb@Td@V`@TZRf@Zd@\\b@Zb@Zf@`@VP`@\\XVZX^ZZX|B|BtCtD`CxDBDTb@hGfL`CvDtBpCd@d@VZNLLNn@p@jGvFxCvCd@j@JLJNp@t@vAvBfAjB~A|C|@zBHNFNLXr@rBf@fBh@xB`@dCN~@BZDXDZr@dHFlAFlAF`BDv@BfCJfM?T@RRjV@R?RBzBZdH^fFNzAB\\BZ@RLdA^tBr@dDp@hCx@fCjAdDXn@HTHR@Bz@hBfBfDxA|BdAvAzDvEPPPPbA~@dCzBdAp@\\Vh@b@v@b@v@f@dAj@lBz@vB|@fA^VJVHz@TtD`AnB`@|Cf@nDb@dCLtIb@~PNrEPT?TB|BRnBXrDv@rEvAxAn@fCjAdElC`D`CZVXXjIpIr@t@vBvBbC|BzFvE~E~CzA|@d@Rb@T|Av@xE|BtDdBxCpAbCjA`CjAt@^pAt@n@\\l@^vDbC`@\\b@\\pC|Br@n@dBfBjCrCd@n@vAfBlBpCf@|@JRHJFJ|C~FlGvMtBvDPZPZlBbDbAvArA`B~BlCx@v@pNlNpBtBbCzCtAhB~ArCh@bAPX~@pB|AfEbAvDv@nD\\tBjAzKDVBVTvBh@nDt@tDFRJ^Vz@`@rAd@nAZn@`@x@NZNXJRXf@z@tAv@|@^d@l@p@TTJLRP@@nAfA`BpA~AjALHd@XTNZLfAf@~Al@LDjB`@lB\\B?rBb@fItAj@Fz@L|KvA`ALX@X@bNvAl@Jl@JhDf@xFlAjAX|DdAr@RB@vCdAt@V~@Zr@ZdAf@TLjAl@zBtAlAx@nAfA`Ax@r@r@t@r@pA|AnB~BZ^`BjBx@`A\\b@VZ\\`@^f@`@f@p@`A~@lAl@t@j@p@hApA|@`A|@bATTTTvEhFd@f@lIfJZZLNh@j@nBlBt@j@JHJH\\XvA|@hBbAxB`AnA\\vBh@pC`@zAPdCH\\@N@P?P@L?t@C`AA\\Cd@El@Gj@Gb@GbC_@hDs@JCJC|Dy@zAWfASzAQ`AKzAIn@EP?TATAB?jACn@?dD?hCLbBJjCZ~AX~AZtAZb@Ll@Nn@TlCdAfBv@^RfFlCfAl@n@Zt@^VJ\\Nb@N|@Zj@PVFd@LXD|@P^Fp@HZBf@DTBH?B?H@F?n@@nA@jAAv@Eh@Cr@GZClAQt@MlCi@xA]bFqAx@S~A]rBa@pBYn@GfCQjAEdA?lBBfCJhCVF@F@bF|@tA`@nGbCr@^pMtGfAh@XLdC~@HBhBn@xFbBdCd@lC^fF`@bDNbCAH?fBCV?PAP?F?tAI`CKbBMzBS~AUrDYJ?LAN?`@ChBC~A@jADnAJlANl@L`AN|A\\|Af@nAd@TJRHPFt@\\lBz@vBlA~A|@pA`AfAx@~BdBJFPLdH~ExDxBdCnAf@Rt@\\F@pBp@TFRFJ@tBn@fBh@`KrCDBF@bAXlA^dDdA~BdA`Ah@pBrAPNDFtBjB~@hAt@bAbBnCbB~ClArB|@vAbA|AbBtBlArAPNPNJN`A|@rElDPLPLNLNJbBnA`H`ElHnDjGfC~FpBXH^H\\HpLzCpH`Bj@LD@|Bj@rBj@tCz@tA`@THTHfDlAfAd@v@Zx@`@lCtAv@b@PJPJNJn@^fBjAjAz@pB`BfCvBjCzBRPRPr@l@`@\\|CjCr@n@PNPN|@x@tAhAdCrBbDhC|AdAnCzAjAj@d@Tv@ZvAj@B@\\LZLLBdBh@jBh@dATz@R\\JF@lCf@nBb@~AZnB`@xBf@jBd@JDj@N\\JzAd@l@Tj@Rl@Vv@\\fB|@JFJFzCpBzC`CfBzAl@l@nD~DRVPV~@lAd@v@zApCnA~B|A`DXj@Xj@d@dAnAfC|AzCnAtBP\\Rd@Vf@^n@d@r@z@hA`AjAPRfDxDjDnDzE|DbG~D|@d@FB~At@NHZN\\N`Bt@pMxFn@XdEbCpBrAHDdBnAfCxBz@|@bEtE~@rAFHPXRX~BtDjA~BP`@l@lAdBlEdAdDpBxH@D`CzIFXHXhB|GfB|G~@tDLd@Ld@DNTfAp@`Dt@tC@@d@~An@fBHR^fATj@Rd@Th@vAvC`AbBRZb@t@b@n@h@p@h@t@v@|@NLLNNNj@j@~AhBXVXXNLNJJH~ApAVRl@`@`@Zj@X~@j@|@f@f@T|@b@^PTJnAh@rAd@`Bh@p@Tj@NLDNDjCn@vB^`APrATz@Hn@Jt@Fj@Ft@FN@H@H?fAHt@D|AHj@@lBHf@?rA?xBA`AAj@CfACrAGH?PAPCnGq@fDc@bCc@pAYjBa@pMiDNELGzNyFZKXM`@On@Up@UlAa@xOqFpEmApEaA^KXIXGnAYrCa@z@OlEa@xDObEG~BB\\B\\@^B|CNfDZhCd@nAXLBNDtBd@zE`B`CfAjC|AFBFDdCvA~E`DnGvDdFzBFBXFVFhFdA~Er@tFf@D?tEZN?N@jKn@vEn@zFlAjEhA|FtBp@VnJvDdA^~DnApDx@tGz@`DPvCHtNBpBFfBLz@F|APjBVrCj@bI|ATDRDHB|Cf@tBNfEP~EHhCLlDZbDh@rFjAb@J`@JfCf@xB^`CX|Eh@H@J?ZBf@FP@PBP@n@JtEl@zCj@jHpBB@JBHDjHzC~Ax@`DlBh@ZpCrBdCtBdCbCjLrM\\^Z\\jApAhBpBfBbBxAlA|BlBnAz@`C`B`BdARJlYjOxDzB`UjNzJbGhDjBnDfBtHfDVJlQdHfOpFr^zMtClAVJ~@`@|GlCpEvB|E`CzHlEtHnEbHvEfLtIfF`Eh^rZx@n@x@n@x@v@z@v@JH~DvDTRdHzF^XfBxAp@h@xb@h^VTXTbLnJxEvD|CxBfFlDVPTNb@XdBfAhE`CvCxAfDdBzMjGvOnHtBfA`@T`@Rn@\\|BnAzGjExFdEdEhD^X|DdDvL`LfSxQpFrEdLzH`KfGtGdD`@R^RPHhQtH`RvG`JfCjUfGPDPBbGrAlUfEfDf@pJhAbD\\hP~ApYpBlN~@~Hd@rRhAlFZdBLP@R@t@Dv@FxO|@`CNP@R@"
},
"start_location" : {
"lat" : 24.9698095,
"lng" : 121.2160748
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "24.1 公里",
"value" : 24090
},
"duration" : {
"text" : "15 分",
"value" : 881
},
"end_location" : {
"lat" : 24.1189755,
"lng" : 120.5824256
},
"html_instructions" : "靠\u003cb\u003e左\u003c/b\u003e以繼續行駛\u003cb\u003e國道一號\u003c/b\u003e\u003cdiv style=\"font-size:0.9em\"\u003e收費路段\u003c/div\u003e",
"maneuver" : "keep-left",
"polyline" : {
"points" : "mhdsCm|s_VV?z@DlCNdQbAvId@zHb@fF^rJj@vO|@l@Dl@D~Hb@N@P@lV|AlKt@P@P@nMz@R@T@bCNpQfAz@Dz@FZBtFZfCNzALjHl@fJz@H@d@FzBRhFj@RBR@l@Hl@HZD\\DzAP~B\\lANdAPNBNB|IzAnB`@jIhBdDz@XHXHbCl@zDnA~G~BzFxBnChA|Ar@VJvFtCXPTLTN`@VXNTLVJz@j@z@d@dGfEfBrAVPTPvAfA@?zBjBhErDdBdBd@b@Z\\ZZVX|A~ABB|AfBrChD|CxDfCnDpDpF|ClFXf@~BrEvAxCpAtClBpErAzCbBhETh@`AjCj@bBrGxQTj@tCnIbFfNx@vBRd@Pd@dHhQBHn@rA\\t@vBrEnBnDpEjI`CxDxBdDrC`ELPLP~EdGnJtJb@d@JHxIlHhBrAZT\\VtBzArErCtKzFtDbBRJlAf@hCjArCbAvBr@\\J\\JzHbCnA^jJjC\\J\\HdAXlJzCt@TLDdBn@`@NfBr@NFVLVLdAf@p@\\fCpAzCfBVPf@\\v@h@^X^Xf@`@h@b@v@n@j@f@`GfFFFBFHRJHHHHHtAlAtBlBh@f@j@f@j@h@p@n@VTfAbAvBpB\\ZrAfALJLJXTtA~@vBvAxEvCdB`ATJrCnAjChAvBt@b@N`@Nb@PnDdAtBj@dGpA^HdANf@HjALvCXTBVDbALz@JN@P?l@HdJ^dBFzBHP@T?R@fA?`AD~@BfADfADhADvERfG\\bIj@lBNVBXBbBR`Ed@VBbFd@@?~@D|CX`CV~AHtBTzCPzD\\p@DnAJlAJr@F~I`@`@DjBFf@@X?p@D^Bf@@v@@`@@f@B|@FpABD?bGNzLVnDFlEJxBHjERnAHH?J@XBlBNhANd@DlCNbEj@rGdAv@HXFx@Px@VrC|@n@RRHRFp@ThAd@bDtAlAn@dBdANJJHLFj@d@~BlBlBxA~B`CvAdBdAxAz@nAHLHNd@v@\\n@f@dAR^\\r@b@bA|@`CBDBLJZHXBHPh@n@lCn@`CVvANx@Hn@Hf@NbBDjAB`BHtA@t@?lAC\\CfBQvBARKx@Ij@Gh@OdA_@jBo@fCa@lAo@bBiAhC_ArBeBhDq@pAGLGLq@lAaAhBmBxDy@dByBfFk@tAoAnDqAdEg@hBe@pBGTERCHu@pDUzAg@bD]lDKbAWtCK|BAXCVIhC"
},
"start_location" : {
"lat" : 24.2754319,
"lng" : 120.6933458
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "67.8 公里",
"value" : 67754
},
"duration" : {
"text" : "40 分",
"value" : 2383
},
"end_location" : {
"lat" : 23.5588388,
"lng" : 120.4177033
},
"html_instructions" : "靠\u003cb\u003e左\u003c/b\u003e以繼續行駛\u003cb\u003e國道一號\u003c/b\u003e\u003cdiv style=\"font-size:0.9em\"\u003e收費路段\u003c/div\u003e",
"maneuver" : "keep-left",
"polyline" : {
"points" : "sverCeg~~UCpB?N?N?j@AlB?`ADhEHdCJdCJ~AFj@@LXnCHj@PbALp@DVDTRdA\\`Bf@lBp@tBTn@L\\JZBH|@tB^z@b@|@bAfBx@rAhA`B^l@vAnBdAhAFFFHpBtBNNzDjDRPRRHHvPfN`VrRj@d@HFb@^xAjA~MjLnBtB~@x@z@~@fC`D^h@Zh@Xb@|@|AdBpDv@nBfBhFf@pB^`Bx@zEVzBT|BNtD@V@TDn@RtEZlI@X@VRvE`@`FVbBBPDRh@xCf@nBRz@DNL^J`@L\\~@nCdB`ExC`Gv@|A@DTd@LP~AjCl@~@jAbBzAtBd@l@xAlB`@d@PRRRd@l@~AdBtCrCLNNLdAdAjBbBxGnFjCjBvCtBdHnEhDjB~Ax@lCpAvGxCbBj@lE|AbCv@pA\\hAZjEdAZH^HTDx@N|FdA~B^dEj@jBRbBLL@ZBXBnDTpCNP@RBl@?lEDP?p@?n@?vGB`GI@?pBE^A^AfAEN?fDQb@Ad@C~Z{AjCS`I_@PANA|@EPAP?xBIxAITA~FOvHG`@AJ?xJElLLjVhA`ADPBnK|@XBNBbBLxHdANBPB`KtApKnB|N`DzBl@RFRDtMlDTFTFjEhAn@Pl@PbAVPDRF`KlCVFXHzXnHn\\nGdRnC^D^FdVjDXDXF~O|BPBPBxB\\fOvBNBLBjFt@nG~@F@H@vYfEJBL@|O|BrVtDz@H`AN~HdAnAPTDnAPzAVx@TdC\\nBXNBb@Dd@Fh@Fh@HN@LBxHt@N@tBRtD`@zAP^BL@N@zFZhEVxM^d@@f@@vJLd@@d@?n@@nA?vD@hDApOStBAxACn@AhCEhAATAR?v@C|BExGMdFGT?RApAAvCGfHCfGBhDBf@?R?T@l@?bDFdDHf@B\\@^BrFRtDPz@FT@TB@?nDXt@D`CRpCTb@DvCZbGp@hANfBVh@FzDj@fDj@|Cj@zCh@~FlAlGvA~A`@pA\\bAVz@TlA\\bBb@bBf@`AXvA`@p[pJPFPFtBj@|Cz@hBf@jCp@`HxArB^bCb@vNfB`BLdF^`NVt@@dMMzM]dHYzL_@tEG|FCjEDrEJJ@J?H?xDR|Hl@lHz@F?D@\\DtAPp@L`LtBdCl@JBHDd@JVHLBLBxI~BPDPDhD|@p@RhOfE`AZnEfAtHbBj@LnMdCbARjBZZD@?nFp@bM|ApJx@p@BjBLP@N@\\@bb@zB`Jl@L?L@tG^zD`@~C`@rC\\NBLBzARdAN|Er@bKdBfH`BdARbARrI`Bz@Pz@PnKpBx@NNDbOfC`CZj@FtBVxBZjFf@|@HrDZpF`@`CPP?pEPv@Bv@DtDNbL?X?zADn@?d@@rFGvGAXAL?LAlDIxKUlAEP?RAnCIvV}@pGQZ?LAN?rDIhHSbEKzHM|@?PARAdCAxB?r@?r@AD?bE@`PPhFLR@R?rELpVhAjQtAjNjA`CTtVdDxDn@LBN@fKfBF@F@lB\\VDH@LDhEbAXFXFxAZx@Rz@PrD~@rCr@`KdCVHXHdFvA|Br@pDfAtAb@XJZHfBj@tFhBnFfBB@PFNFHDLBxTzHv@Vv@X`ErAjDlA~KxDtC|@JBTH~Aj@tAd@nErANDLDtBn@nHvBdOxDNDfEdAPDPBND`LzBH@NDNBlMvBp@FJBJ@tN`BlBNdIl@~Kf@nABt@BV@T@|CH|IFb@?L?L?pGAfJMFAN?LAlFOtLu@`BKLALAJAjAIPCPAjGi@xCa@~HcAfAONCHALATEpDo@vDu@LCNCdB_@lEaAtAYlBg@\\I^KlBc@hCq@rCy@JCJCZK~C{@vFaBRGhEmA~C}@d@MBArD_AtA_@hCs@vA]lCo@XGXINEzBg@bCg@|Ey@pAWvB]`Eg@dD_@~BWLANC\\EnFe@fEWdCOhCIP?PAPAd@Ar@CrPMxNNzAHP@R@fNr@jBPpDZTBTBd@DpGz@pGz@NBND`F`ANDNBjCh@TDRDpCj@nAZ~HnBxAd@vA^LDJDzC|@vKnDZJTHTJjJjDdC`AFDHB^NbCdA`Bx@@@@?HDHBBB~G~CdE|BNFLHbCpANHLHrH`EPHNHlTlLJDHFlM~GNHNFzDtB`ExBPJLFpJlFh@Xd@XzBlAlAp@tGjD\\R\\PrMbHnAp@nAp@|EfCrDjB^P`@R`@RdMdGvGzCh@T`@Rb@PxGvCfChA|EjBf@Rf@RpHtCdFjBZJZLLFrHjCxE~ANFNDhA^hA`@bFbBrE|AhH|BjD~@jOlEt@Rr@R|@TpCl@rB^vCh@nAVbBZ|Cr@bDr@`Cf@lDr@~Cl@nDr@rCf@vAXRBRDF@vCd@bEp@~Dp@`El@hDh@vEn@|En@fCZn@JvEh@dEd@jEd@pKdARBTBdAL|BRVBdAJdAJtCXhGj@t@HZDF@H@H?RBn@JrUxB`@Db@DfAJjDXtCV~BT~Eh@pBPF@H@R@RBt@HnBRnCVlCVrMlATBVDbPdBZDXDfFn@`Df@VDVDtEn@~Cd@VFVDXFjQfDTDRDjB^fAVNFPDjTzFvBp@THVHvBn@zNfFpChARJRHhV`KbM~FfH`DNFNH~PxHdCbAVJVJdLtElE`BlItCZLZJhBl@fFxApD~@xQbFRDRF~DnAnA`@"
},
"start_location" : {
"lat" : 24.1189755,
"lng" : 120.5824256
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "0.7 公里",
"value" : 667
},
"duration" : {
"text" : "1 分",
"value" : 41
},
"end_location" : {
"lat" : 23.5546293,
"lng" : 120.4140711
},
"html_instructions" : "在\u003cb\u003e257-民雄\u003c/b\u003e出口下交流道,朝\u003cb\u003e新港\u003c/b\u003e前進\u003cdiv style=\"font-size:0.9em\"\u003e收費路段\u003c/div\u003e",
"maneuver" : "ramp-right",
"polyline" : {
"points" : "wixnCsa~}Ul@\\LFTHp@NXJZJFB~CdAPF|Aj@|CtANFLF|@`@r@ZBHFFLJFHDDFJT^Rl@Fd@D`@Ab@Gj@Sz@"
},
"start_location" : {
"lat" : 23.5588388,
"lng" : 120.4177033
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "0.2 公里",
"value" : 171
},
"duration" : {
"text" : "1 分",
"value" : 15
},
"end_location" : {
"lat" : 23.5555297,
"lng" : 120.4127124
},
"html_instructions" : "走\u003cb\u003e縣道164號\u003c/b\u003e",
"maneuver" : "merge",
"polyline" : {
"points" : "mownC}j}}UEHeBtCc@x@c@t@"
},
"start_location" : {
"lat" : 23.5546293,
"lng" : 120.4140711
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "6.1 公里",
"value" : 6139
},
"duration" : {
"text" : "7 分",
"value" : 443
},
"end_location" : {
"lat" : 23.5559742,
"lng" : 120.3543138
},
"html_instructions" : "繼續直行,並繼續走\u003cb\u003e縣道164號\u003c/b\u003e",
"maneuver" : "straight",
"polyline" : {
"points" : "auwnCmb}}Uq@dAuErIa@t@U\\UZo@r@_EvCA?g@`@[TKFEDSJOD@RBjA?DBvA?B?hD@j@@TDbD@VHvG@XNdKD`D@h@?hDLnD?^D`BJvFH|C@LHvC?RVlFR~EPdDNvCXdFJlBHjA@NDdAXvHNfD?PHhBB\\?Fh@lLB`@VlGBb@XpHBb@VvFN|CHzA@\\dA~T@\\P~DJnBBd@^nIB\\j@|L@\\`@jIPdF@TFrA?D@RN~CFpB?|@An@Ar@CpACh@EvAIrCANCz@Aj@Cp@EbBE`AC`ACn@?JAVWfJEhAEfBAJAXG~BAf@ClA"
},
"start_location" : {
"lat" : 23.5555297,
"lng" : 120.4127124
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "1.0 公里",
"value" : 1034
},
"duration" : {
"text" : "2 分",
"value" : 111
},
"end_location" : {
"lat" : 23.5496167,
"lng" : 120.3476798
},
"html_instructions" : "於\u003cb\u003e嘉民路\u003c/b\u003e向\u003cb\u003e左\u003c/b\u003e轉",
"maneuver" : "turn-left",
"polyline" : {
"points" : "ywwnCmuq}Uf@ZfAl@~AbAzCjBxA|@h@ZfC~AxD`CdAj@z@j@xBxAVTNPX\\JPR\\LXN^Ph@FXHd@`@dE@dB"
},
"start_location" : {
"lat" : 23.5559742,
"lng" : 120.3543138
},
"travel_mode" : "DRIVING"
},
{
"distance" : {
"text" : "0.4 公里",
"value" : 403
},
"duration" : {
"text" : "2 分",
"value" : 108
},
"end_location" : {
"lat" : 23.5531974,
"lng" : 120.3473666
},
"html_instructions" : "於\u003cb\u003e中山路\u003c/b\u003e向\u003cb\u003e右\u003c/b\u003e轉\u003cdiv style=\"font-size:0.9em\"\u003e目的地在左邊\u003c/div\u003e",
"maneuver" : "turn-right",
"polyline" : {
"points" : "cpvnC_lp}U{@XoB^oAJE?q@@yAEc@?eACiACiCB"
},
"start_location" : {
"lat" : 23.5496167,
"lng" : 120.3476798
},
"travel_mode" : "DRIVING"
}
],
"traffic_speed_entry" : [],
"via_waypoint" : []
}
],
"overview_polyline" : {
"points" : "}}ywC{pxdV`DgGiIfCuKn@wcAk@mt@]aJdEwLtOcN?qRpCo@bVkVpmAkBxu@xQjcBjJf}@kLxdA_A`h@|Epa@|a@ltB~Bb]bGh}@j~@phBfFfX_DxV{b@t`@aQz]bAjc@}Gri@pEltAyAhrB`Oxt@zTv]rb@z]nWbVfP|\\t_@|pAbp@rrAbgEnwFt|AnaB`oBl~B~k@z~@b\\j`@~N|Jdl@tQfu@dR~YjWfUvk@lVpm@rGxHt_@jTxl@pOdl@dQdO`QrMnc@di@lzC`Gvf@Ixp@Qzi@pEv]je@piAlEzSvG~bAhLb\\|Z`r@hEbk@sChkAt@`jArNzx@b`@|p@lPvMf\\fNdUv@hKaCbj@}k@fNmLx`@oHpz@|Dvw@h]lw@h\\vu@tK`l@xGrkAh[xrAhBhb@xLvN`LnUls@|^v\\dY|XnWz_@rc@pPnR`Ovk@fv@|Qne@~DnsAfOrh@hQlShP~IfXzFho@dCf_@lQxe@fa@zq@|_@zg@ry@jd@dh@~Lpg@vJ|^xTrRb}@|Mhn@|TzYd]zb@td@nWxI|IHrc@qGtb@rDd_@pOhQL|YmGzXQfd@fQh_@lIbe@aBr]`Fpm@v]fk@dUh`@hf@xf@~Uda@xJva@hRtg@hb@|~@`ZbVzTxMpWlJjPl]pZr]lPpTtS|MlYxO`m@zNxb@`O`QpZhO|YdE`j@}BnkAs^~[wCzVxBlw@h]th@xE~o@fSxl@jCj[hFxz@lJfW`Fd[rQb^j_@ncApm@lbB~r@lp@`\\b}BlkBbb@|[|v@f`@l^lVnn@tj@~a@|VnhAd_@xnB|Td{AvIhyB|MvkA|Iro@hLbp@rWnc@v]h]~d@~Rlc@bt@~gB~_@ff@hf@d\\f_Ad[p[jPzX|Vtf@d\\lh@zKphB`LbbBdH~\\zFjPlHpXh[~It]eDpd@m^nz@mGzk@~Bzd@`Sjd@rjA~aAfOfRtLtd@jGrv@fXrh@zs@xl@va@`Pz[`Gdo@vA||@cE`Zo@``A`D`tAjXhgAdWp~@xM`mBvXfk@tHv|@zDh{B[pr@xGtrAt\\t`@fJ`h@lD|f@cApi@Wx]dDl_AtUnf@~IvbAjHviA`OxsAnRhz@ZnwAsDxnBlHt{@vNzeCbw@`dApUbjAhFp_AiGpd@iJ|r@sRls@yKfR{@t|@pBnr@hMpq@hVdu@h`@xeClpAd_At\\liAxXh~BrZv|B|U~~@fTbzB`~@rgAx_@gB`LmMlT{HnFLlL`BjbAfFphAxJ|gCwAvf@OnGjJxFxUbO`C|El@pIaGdAiMG"
},
"summary" : "國道一號",
"warnings" : [],
"waypoint_order" : []
}
],
"status" : "OK"
}



裡面的 legs, steps, 就是每個轉折點的路徑, 但是用這個路徑資訊來畫圖, 地圖只會變成很醜的線段相連, 不能呈現沿著路畫出漂亮曲線的結果。

但裡面也包含有完美曲線的資訊, 就隱藏在 overview_polyline 這個屬性當中,只是overview_polyline 的資訊已經經過編碼,所以無法直接拿來套用在地圖上。

為了這個遺憾,我把以前在 iOS SDK裡面看過的程式碼改寫成了TMapPolylineDescriptorHelper 這個 class Helper (請參閱 Object Pascal Handbook 的 record helper 與 Class Helper, 中文版Object Pascal程式語言手冊第358頁):

type
   TMapPolylineDescriptorHelper = record helper for TMapPolylineDescriptor
      class function CreateByGME(GoogleMapEncodingString: String):
               TMapPolylineDescriptor; static;
   end;

實作程式碼:
class function TMapPolylineDescriptorHelper.CreateByGME(GoogleMapEncodingString: String)
    : TMapPolylineDescriptor;
var
   dataStr: RawByteString;
   coordCount, rawLength: integer;
   idx, currentCoordIdx: integer;
   APoint: TMapCoordinate;
   latitude, longitude, finalLat, finalLon, deltaLat, detlaLon: Double;
   Abyte, shift: Byte;
   res: Int32;
   offset: integer;
begin
   dataStr := GoogleMapEncodingString;
   rawLength := Length(dataStr);
   latitude := 0;
   longitude := 0;

   {$IFDEF WINDOWS}
      offset := 1;
   {$ELSE}
      offset := 0;
   {$ENDIF}

   currentCoordIdx := 0;
   idx := 0;
   while idx < rawLength do begin
      AByte := 0;
      res := 0;
      shift := 0;

      repeat
         AByte := Byte(dataStr[idx+offset]) - 63;
         Inc(idx);

         res := res or ((AByte and $1F) shl shift);
         Inc(shift, 5);
      until (Abyte < $20);

      if (res and 1) = 1 then begin
         deltaLat := not(res shr 1);
      end
      else begin
         deltaLat := res shr 1;
      end;
      latitude := latitude + deltaLat;


      shift := 0;
      res := 0;
      repeat
         AByte := Byte(dataStr[idx+offset]) - 63;
         Inc(idx);

         res := res or ((AByte and $1F) shl shift);
         Inc(shift, 5);
      until (Abyte < $20);

      if (res and 1) = 1 then begin
         detlaLon := not(res shr 1);
      end
      else begin
         detlaLon := res shr 1;
      end;
      longitude := longitude + detlaLon;

      finalLat := latitude * (0.00001);
      finalLon := longitude * (0.00001);

      Inc(currentCoordIdx);
      setLength(Result.Points.Points, currentCoordIdx);

      APoint := TMapCoordinate.Create(finalLat, finalLon);
      Result.Points.Points[currentCoordIdx - 1] := APoint;
   end;
end;

然後在製作地圖的程式碼裡面,把 overview_polyline 的所有點放進去:

overview_polyline := lv0.GetValue('overview_polyline')
                as TJSONObject;
MapPolyline := TMapPolylineDescriptor.CreateByGME
                (overview_polyline.GetValue<String>('points'));
MapPolyline.StrokeWidth := 12.0;
MapPolyline.StrokeColor := TAlphaColor($FF5CC2BE);


AMapPolyline := self.MapView2.AddPolyline(MapPolyline);
AMapPolyline.SetVisible(TRUE);

這樣就可以畫出完整、完美的曲線了。

規劃路線的完整 function 如下:
procedure TFormNovMain.btnTrafficRouteClick(Sender: TObject);
var
   targetURL, mode: string;
   directionJsonStr: string;
   routeObj, lv0, leg0, step, startP, endP, overview_polyline: TJSONObject;
   route, legs, steps: TJSONArray;
   idx: integer;
   APoint: TMapCoordinate;
   Points: TArray<TMapCoordinate>;
   MapPolyline: TMapPolylineDescriptor;
   mapCenter: TMapCoordinate;
   MyMarker: TMapMarkerDescriptor;
begin
   if (self.ComboEditStart.Text <> '') and (self.ComboEditTarget.Text <> '')
   then begin
      if (txtTrafficTitle.Text = self.TxtPublic.Text) then begin
         mode := 'transit';
      end
      else if (txtTrafficTitle.Text = self.TxtDriveTo.Text) then begin
         mode := 'transit';
      end
      else begin
         mode := 'bicycling';
      end;

      if self.ComboEditStart.ItemIndex = 3 then begin
         targetURL := Format('https://maps.googleapis.com/maps/api/directions/'
             + 'json?mode=%s&origin=%g,%g&destination=%s&key=AIzaSyCsQG8bTrKou0FRH0IDOvNdk3iWHDcq1iA',
             [mode, self.Here.Latitude, self.Here.Longitude,
             self.ComboEditTarget.Text]);
      end
      else begin
         targetURL := Format('https://maps.googleapis.com/maps/api/directions/'
             + 'json?mode=%s&origin=%s&destination=%s&key=AIzaSyCsQG8bTrKou0FRH0IDOvNdk3iWHDcq1iA',
             [mode, self.ComboEditStart.Text, self.ComboEditTarget.Text]);
      end;

      self.RESTClient1.baseURL := targetURL;
      self.RESTRequest1.Execute;

      routeObj := self.RESTResponse1.JSONValue as TJSONObject;
      if Assigned(routeObj) then begin
         route := routeObj.GetValue<TJSONArray>('routes');

         lv0 := route.Items[0] as TJSONObject;

         legs := lv0.GetValue<TJSONArray>('legs');

         leg0 := legs.Items[0] as TJSONObject;
         startP := leg0.GetValue<TJSONObject>('start_location');
         mapCenter := TMapCoordinate.Create
             (StrToFloat(startP.GetValue<String>('lat')),
             StrToFloat(startP.GetValue<String>('lng')));
         startCood := mapCenter;
         self.MapView2.Location := mapCenter;

         if bUsePolyline then begin
            legs := lv0.GetValue<TJSONArray>('legs');

            leg0 := legs.Items[0] as TJSONObject;
            steps := leg0.GetValue<TJSONArray>('steps');

            SetLength(Points, steps.Count + 1);
            for idx := 0 to steps.Count - 1 do begin
               step := steps.Items[idx] as TJSONObject;

               startP := step.GetValue<TJSONObject>('start_location');
               APoint := TMapCoordinate.Create
                   (StrToFloat(startP.GetValue<String>('lat')),
                   StrToFloat(startP.GetValue<String>('lng')));
               Points[idx] := APoint;
            end;

            step := steps.Items[steps.Count - 1] as TJSONObject;
            startP := step.GetValue<TJSONObject>('end_location');
            APoint := TMapCoordinate.Create
                (StrToFloat(startP.GetValue<String>('lat')),
                StrToFloat(startP.GetValue<String>('lng')));
            Points[steps.Count] := APoint;

            MapPolyline := TMapPolylineDescriptor.Create(Points);
            MapPolyline.StrokeWidth := 12.0;
            MapPolyline.StrokeColor := TAlphaColor($FF5CC2BE);
         end
         else begin
            overview_polyline := lv0.GetValue('overview_polyline')
                as TJSONObject;
            MapPolyline := TMapPolylineDescriptor.CreateByGME
                (overview_polyline.GetValue<String>('points'));
            MapPolyline.StrokeWidth := 12.0;
            MapPolyline.StrokeColor := TAlphaColor($FF5CC2BE);
         end;

         AMapPolyline := self.MapView2.AddPolyline(MapPolyline);
         AMapPolyline.SetVisible(TRUE);
      end;
   end
   else if self.ComboEditTarget.Text = '' then begin
      ShowMessage('請輸入目的地');
   end
   else begin
      ShowMessage('請輸入出發地點');
   end;
end;

大家可以試試看用 Android 手機拉一個簡單的範例試試看, 立刻就可以看到完美的地圖出現喔.


沒有留言:

張貼留言