使用新的Google Maps API解码折线路径

28

我正在地图上绘制两点之间的路径。我是这样接收这些点的:

StringBuilder urlString = new StringBuilder();
    urlString.append("http://maps.googleapis.com/maps/api/directions/json");
    urlString.append("?origin=");// from
    urlString.append(Double.toString(src.latitude));
    urlString.append(",");
    urlString.append(Double.toString(src.longitude));
    urlString.append("&destination=");// to
    urlString.append(Double.toString(dest.latitude));
    urlString.append(",");
    urlString.append(Double.toString(dest.longitude));
    urlString.append("&sensor=false&mode=");
    if (tipo != null) {
        urlString.append(tipo);
    }
return urlString.toString;

我从谷歌收到响应,并获得JSON:

resp = new JSONObject(builder.toString());
                Log.i("Location", "Contenido del kml: "+resp);
                JSONArray routeObject = resp.getJSONArray("routes");
                JSONObject routes = routeObject.getJSONObject(0);
                JSONObject overviewPolylines = routes
                        .getJSONObject("overview_polyline");
                String encodedString = overviewPolylines.getString("points");
                ArrayList<LatLng> puntos=decodePoly(encodedString);

我从谷歌获得的响应(JSON格式):

Response: {"status":"OK","routes":[{"waypoint_order":[],"summary":"R-3 and A-3","bounds":{"southwest":{"lng":-3.676540000000001,"lat":40.00040000000001},"northeast":{"lng":-2.99933,"lat":40.43357}},"legs":[{"duration":{"value":3267,"text":"54 mins"},"distance":{"value":85039,"text":"85.0 km"},"end_location":{"lng":-2.99933,"lat":40.00040000000001},"start_address":"Calle del General Díaz Porlier, 91, 28006 Madrid, Spain","end_address":"Camino Veinticuatro, 2, 16400 Tarancón, Cuenca, Spain","start_location":{"lng":-3.676540000000001,"lat":40.43331000000001},"via_waypoint":[],"steps":[{"html_instructions":"Head <b>north<\/b> on <b>Calle del Gral Díaz Porlier<\/b> toward <b>Calle de Maldonado<\/b>","duration":{"value":6,"text":"1 min"},"distance":{"value":29,"text":"29 m"},"end_location":{"lng":-3.676520000000001,"lat":40.43357},"polyline":{"points":"ecxuFjamUs@C"},"travel_mode":"DRIVING","start_location":{"lng":-3.676540000000001,"lat":40.43331000000001}},{"html_instructions":"Take the 1st <b>right<\/b> onto <b>Calle de Maldonado<\/b>","duration":{"value":62,"text":"1 min"},"distance":{"value":266,"text":"0.3 km"},"end_location":{"lng":-3.6734,"lat":40.43345},"polyline":{"points":"ydxuFfamUPkG@a@JuGGk@"},"travel_mode":"DRIVING","start_location":{"lng":-3.676520000000001,"lat":40.43357}},{"html_instructions":"Take the 2nd <b>right<\/b> onto <b>Calle de Fco. Silvela<\/b>","duration":{"value":57,"text":"1 min"},"distance":{"value":245,"text":"0.2 km"},"end_location":{"lng":-3.671830000000001,"lat":40.4316},"polyline":{"points":"adxuFvmlURQl@e@zAoATSTMf@]pBeBLK"},"travel_mode":"DRIVING","start_location":{"lng":-3.6734,"lat":40.43345}},{"html_instructions":"Slight <b>right<\/b> onto <b>Calle de Francisco Silvela<\/b>","duration":{"value":51,"text":"1 min"},"distance":{"value":437,"text":"0.4 km"},"end_location":{"lng":-3.66913,"lat":40.42827},"polyline":{"points":"oxwuF|clUJADAFCbBkAjAeA`BoA^[XUPMLMFEnAeALIdBuAZUPMXS"},"travel_mode":"DRIVING","start_location":{"lng":-3.671830000000001,"lat":40.4316}},{"html_instructions":"Turn <b>right<\/b> onto <b>Pl. de Manuel Becerra<\/b>","duration":{"value":32,"text":"1 min"},"distance":{"value":154,"text":"0.2 km"},"end_location":{"lng":-3.668380000000001,"lat":40.42827},"polyline":{"points":"ucwuF`skU@?@@@@@?@@@?FBD?F@FAFAFADEDCFGDGDIBI@I@K@KAI?IAGAGAGCGCC?ACEEEIGGEICI?I?G@IBGD"},"travel_mode":"DRIVING","start_location":{"lng":-3.66913,"lat":40.42827}},{"html_instructions":"Turn <b>right<\/b> onto <b>Calle Alcala<\/b>","duration":{"value":61,"text":"1 min"},"distance":{"value":709,"text":"0.7 km"},"end_location":{"lng":-3.66107,"lat":40.43126000000001},"polyline":{"points":"ucwuFjnkUo@}A_AcCiEeLKYq@mBYq@[o@u@kB?KAQ]aAI[EQQu@Gc@QwAM_BEa@AU"},"travel_mode":"DRIVING","start_location":{"lng":-3.668380000000001,"lat":40.42827}},{"html_instructions":"Turn <b>right<\/b> onto the ramp to <b>M-30\/A-3\/A-4<\/b>","duration":{"value":44,"text":"1 min"},"distance":{"value":453,"text":"0.5 km"},"end_location":{"lng":-3.660580000000001,"lat":40.42720000000001},"polyline":{"points":"kvwuFt`jU\\CjIg@nBKD?rCOvDY"},"travel_mode":"DRIVING","start_location":{"lng":-3.66107,"lat":40.43126000000001}},{"html_instructions":"Continue straight","duration":{"value":45,"text":"1 min"},"distance":{"value":1009,"text":"1.0 km"},"end_location":{"lng":-3.659120000000001,"lat":40.41822000000001},"polyline":{"points":"_}vuFr}iUzKgBvCe@t@KvGm@hDUbAMPCZElAM|D_@^En@EjBEd@Df@DnAD"},"travel_mode":"DRIVING","start_location":{"lng":-3.660580000000001,"lat":40.42720000000001}},{"html_instructions":"Take exit <b>7B<\/b> to merge onto <b>M-23<\/b> toward <b>Vicálvaro\/R-3\/Valencia<\/b>","duration":{"value":117,"text":"2 mins"},"distance":{"value":2552,"text":"2.6 km"},"end_location":{"lng":-3.63392,"lat":40.41499},"polyline":{"points":"{duuFntiUFHB@^PDBFFFDDFDFBDDFBH@BBJ@F@H?F@H?FAH?FAF?FADAFAFCDADADEFCFEDEDEDEBEBEBGBE@G@E@G?G?EAG?GCGAGCEEGEEEEEEGEICIEIAKCKAK?K?MDgA?G?E?KJq@BODY@CBODW?C@I@AFe@F_@V{An@gED[@C?EHe@Ho@@G?A^{BjA}HF[tAiGzBwIdAcEVgApAuFr@sEF]Ju@DW?EDUHa@f@qEFo@f@iGXmHDgFCgGG{DM

最后,我按照在Stack Overflow上找到的许多答案的方式解码接收到的字符串:

private ArrayList<LatLng> decodePoly(String encoded) {

    Log.i("Location", "String received: "+encoded);
    ArrayList<LatLng> poly = new ArrayList<LatLng>();
    int index = 0, len = encoded.length();
    int lat = 0, lng = 0;

    while (index < len) {
        int b, shift = 0, result = 0;
        do {
            b = encoded.charAt(index++) - 63;
            result |= (b & 0x1f) << shift;
            shift += 5;
        } while (b >= 0x20);
        int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
        lat += dlat;

        shift = 0;
        result = 0;
        do {
            b = encoded.charAt(index++) - 63;
            result |= (b & 0x1f) << shift;
            shift += 5;
        } while (b >= 0x20);
        int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
        lng += dlng;

        LatLng p = new LatLng((int) (((double) lat /1E5)* 1E6), (int) (((double) lng/1E5   * 1E6)));
        poly.add(p);
    }
   
   for(int i=0;i<poly.size();i++){
       Log.i("Location", "Point sent: Latitude: "+poly.get(i).latitude+" Longitude: "+poly.get(i).longitude);
   }
    return poly;
}

我认为错误出在这里,因为我设置了一个for循环来查看被解码的点,而答案是类似于:

04-10 13:33:54.578: I/Location(25065): Point sent: Latitude: 90.0 Longitude: -110.0
04-10 13:33:54.578: I/Location(25065): Point sent: Latitude: 90.0 Longitude: -120.0
04-10 13:33:54.578: I/Location(25065): Point sent: Latitude: 90.0 Longitude: 100.0
04-10 13:33:54.578: I/Location(25065): Point sent: Latitude: 90.0 Longitude: 120.0
04-10 13:33:54.578: I/Location(25065): Point sent: Latitude: 90.0 Longitude: 40.0
显然,这些点被错误地解析了。结果就是没有任何线条画出来。 我的问题是... 有人知道如何解析来自Google的答案,并将其转换为LatLng对象(而不是GeoPoints对象)吗? 编辑: 即将解码的字符串如下:
04-10 13:50:51.608: I/Location(25065): String to decode: gcxuFjamUq@CPkGLwHJ}@hCuBdEeDf@SrJuHnG{Ed@Fr@q@AmAm@k@e@DaIaSiEuLqAwJ~Mw@~ZwDtZuC`FKbCVjA`ANdAs@nBw@Py@]c@oAT}DhC{PlBcM~GaY|DyS`BmPRy]yBu\UsNvAqObAuEnC}KpAqIh@gK[}NcByWXoRt@}GlDeWvFcq@nFi`@~G}\|@aDdDoLn`@}gAfe@upA`JyV|CiN~@uId@{ZyBwVkBmOwB{^AsNt@iWb@qYcCyS_DkPmAaK{@gRN{RbCkWrFgUbHmPxLsP|JwIpPwN|G}JfE}I|C_KlCsNvAwSFaQj@i]jCmXbHg\pIgSpGaLnN}P`JsHfVaNbQkHtZyIrOsCnTaCx\aA`i@b@nSRz]K`J{AxJiDfNkJjU{WbKkJpGeEpVkPpEiEzMcQjQw]nJyM~IoJ`IoKnH{NrOaWtIeJdNgNrLcPjKqOhIuI~MeJxKgErR{Cn]EjQkBjHqBfJeE|\yTlI_JvEqJxHsS|Xuc@~IeR|EiRr@kEz@sNOoL_B}Lk@mCsFgKkCoGwAsG]}N`CuO`D}GjHoHtFwC|HgBhFg@fJ}BtHaF~D_FjLqSfGqE|FcBnGEnEv@xK`BzNeAxQfAlJ{@jIwEjE}FvBuF|B{NtCsNrBgEdHiIdGuIxDoFpGkFhDaBrHyBbF[pHPlDg@jEuBnCkCtFgKjEqEfIcGpC{D|GoNdAcExAkSjBgDrCsBpDa@vHLfSAfBk@nE_F|Im`@tO_ZxCyEzJmKtHyElG_ClZeF|j@sIlQaD|EuChGkHnK{MdIsMfPwa@bDeJpD_RpFsz@nA}IrCqIrYoa@|BeGlEiYpD}HbKoRfCyKdBwUjEmo@zBkKzDyJvCeQdB}LJqGmAqQJ_Gx@uFtHkO`ImMfMmXtFySpCkXRuN]aYVoT~@}LxB{NvD{OjJaYbFsLnCcEtLoMfJyXzIiXbIiL~D_EzQcMjDeF|AcFhA_M`BkXpC_KjCsDnDeClQcHvEgElFqJfF_Jp\mWzd@k]`WkWlVy`@nOcTz`@_s@rP_ZbNkQ~Tu^jJaUhCuIpHgUrPi\zd@uq@~McQh\e]vTaU~KcF~KoGhG{KzF{HzGmDbDwBfDiEzB}GbEmTrBsHpCuFxe@oq@xb@el@tDgC`ZeJnXkHrKmB~EeB~C{A`Ce@bCUrBoAnAuGSqIn@yEhDeOrCuQ|A}EvAwFdDgAfGSrHUJ\p@Bj@_A|Ao@tCsD|C}Rj@wAdCuW~Ds]VCJw@OUpEc_@jDKbDwB

如果您说的是LatLng格式,指的是创建LatLng对象的方式,可以在decodePoly方法中看到。这些latLng对象将添加到PolylineOptions对象中,然后再将其添加到地图上,如下所示:

PolylineOption ruta=new PolylineOptions();
for(int i=0;i<puntos.size();i++){
ruta.add(new LatLng(puntos.get(i).latitude, puntos.get(i).longitude));                      
}//puntos is an array where the array returned by the decodePoly method are stored                  
ruta.color(Color.RED).width(7);                     
Polyline polygon=mapa.addPolyline(ruta);

你可以展示一下你的回复和你的经纬度格式吗? - GrIsHu
很可能是1E6格式导致了问题。 例如回复:很可能是数据过大,应使用科学计数法表示。 - Lukas Knuth
我尝试在decodePoly方法中只使用new LatLng(lat,lng),但没有结果。也尝试删除1E6部分,但仍然没有结果... - Fustigador
这不起作用,因为该方法始终在进行转换。只需从您的JSON中获取值并将它们(原始)放入“LatLng”构造函数中即可。 - Lukas Knuth
我在另一篇Android/Java的帖子中发布了类似的答案。解码多边形并返回Latlng而不是Geopoints。 - Marlon
decode函数对字符串"ipswCw{cjN@@@f@Fd@Ff@Hb…@zECxEpKpJDDFD@H@FAHAN"抛出了StringIndexOutOfBoundException异常。你有什么想法为什么会发生这种情况? - MONU KUMAR
8个回答

31

对于那些现在需要的人,有一个开源库,其中包括与Google Maps Android API相关的许多有用的内容,包括对折线进行解码和编码。

请访问Android Maps UtilsAndroid Maps Util Github 进行查看。

要进行解码和编码,请使用:

PolyUtil.decode(String encodedPath);
PolyUtil.encode(List<LatLng> path);

3
现在应该使用这种方法,而不是自己编写解码器。 - Ramashish Baranwal
1
这个依赖看起来有点老,它写的是compile,它已经被弃用了吗? - Pemba Tamang
@PembaTamang,库最近已经更新,包括Gradle更新。 - CounterFlame
太好了,嘿,你能帮我解决这个问题吗?https://stackoverflow.com/q/58636511/10800406 - Pemba Tamang

28

4
android-maps-utils 包含实用方法,可帮助您在Google Maps上执行许多其他有用的操作。 - Zharf
我也不得不做@Fustigador提到的那个更改,但我没有看到他关于这个更改的注释,直到我自己花了20分钟来解决它,然后一旦我意识到他已经发布了修复程序,我就摇了摇头。哈哈,我真是个笨蛋。 - BigSauce

10

在JavaScript中相同

    function decodePolyline(encoded) {
        if (!encoded) {
            return [];
        }
        var poly = [];
        var index = 0, len = encoded.length;
        var lat = 0, lng = 0;

        while (index < len) {
            var b, shift = 0, result = 0;

            do {
                b = encoded.charCodeAt(index++) - 63;
                result = result | ((b & 0x1f) << shift);
                shift += 5;
            } while (b >= 0x20);

            var dlat = (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
            lat += dlat;

            shift = 0;
            result = 0;

            do {
                b = encoded.charCodeAt(index++) - 63;
                result = result | ((b & 0x1f) << shift);
                shift += 5;
            } while (b >= 0x20);

            var dlng = (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
            lng += dlng;

            var p = {
                latitude: lat / 1e5,
                longitude: lng / 1e5,
            };
            poly.push(p);
        }
        return poly;
    }

3

这种方法对于解码折线很有用

 private List<LatLng> decodePoly(String encoded) {
    List<LatLng> poly = new ArrayList<>();
    int index = 0, len = encoded.length();
    int lat = 0, lng = 0;

    while (index < len) {
        int b, shift = 0, result = 0;
        do {
            b = encoded.charAt(index++) - 63;
            result |= (b & 0x1f) << shift;
            shift += 5;
        } while (b >= 0x20);
        int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
        lat += dlat;

        shift = 0;
        result = 0;
        do {
            b = encoded.charAt(index++) - 63;
            result |= (b & 0x1f) << shift;
            shift += 5;
        } while (b >= 0x20);
        int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
        lng += dlng;

        LatLng p = new LatLng((((double) lat / 1E5)),
                (((double) lng / 1E5)));
        poly.add(p);
    }

    return poly;
}

这是我获取折线的方法。
JSONObject poly = route.getJSONObject("overview_polyline");
String polyline = poly.getString("points");
polyLineList = decodePoly(polyline);

新的 LatLng((((double) lat / 1E5)), (((double) lng / 1E5))) -- 这恰好是一个私有构造函数。也许它在最近的更新中被设为了私有。 - njari

2

对于使用Maps JavaScript SDK的用户,请使用@googlemaps/polyline-codec

npm install @googlemaps/polyline-codec

import { decode, encode } from "@googlemaps/polyline-codec";

const encoded = "_p~iF~ps|U_ulLnnqC_mqNvxq`@";
console.log(decode(encoded, 5));
// [
//   [38.5, -120.2],
//   [40.7, -120.95],
//   [43.252, -126.453],
// ]

const path = [
  [38.5, -120.2],
  [40.7, -120.95],
  [43.252, -126.453],
];
console.log(encode(path, 5));
// "_p~iF~ps|U_ulLnnqC_mqNvxq`@"

1

同样适用于Pascal(Delphi):

function DecodeLine(inputstring: String): String;
var
  lat, lon, lat_f, lon_f: Double;
  index: Integer;
  len: Integer;
  b: Integer;
  shift: Integer;
  decodeResult: Integer;
  encoded: String;
  dlat: Integer;
  dlng: Integer;
begin
  Result := '';

  encoded := inputstring;
  len := Length(encoded);
  index := 1;
  lat := 0;
  lon := 0;

  while (index <= len) do begin
    b := $20;
    shift := 0;
    decodeResult := 0;

    while (b >= $20) do begin
      b := ord(encoded[index]);
      b := b - 63;
      inc(index);
      decodeResult := decodeResult or (b and $1f) Shl shift;
      shift := shift + 5;
    end;

    if (decodeResult and 1) <> 0 then
      dlat := not (decodeResult shr 1)
    else
      dlat := decodeResult shr 1;
    lat := lat+dlat;

    shift := 0;
    decodeResult := 0;
    b := $20;
    while (b >= $20) do begin
      b := ord(encoded[index]);
      b := b-63;
      inc(index);
      decodeResult := decodeResult or (b and $1f) Shl shift;
      shift := shift + 5;
    end;

    if (decodeResult and 1) <> 0 then
      dlng := not (decodeResult shr 1)
    else
      dlng := decodeResult shr 1;
    lon := lon + dlng;

    { The coordinates of the point are used for our purposes }
    lon_f := lon/100000.0;
    lat_f := lat/100000.0;

    if Result <> '' then
      Result := Result + ', ';
    Result := Result + '(' + FloatToStr(lon_f) + ' - ' + FloatToStr(lat_f) + ')';
  end; //while
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  str: String;
begin
  // Do stuff
  str := DecodeLine('ibgqGq}ycIMvM');
  ShowMessage(str);
end;

1
这里是iOS的实现,供任何好奇的人参考,完全基于@Fustigador的答案,但已转换为iOS。请注意,我没有相应地格式化代码。我只添加了一些变量,以便返回承诺的对象。
- (GMSPath *)decodedPolylinePathFromEncodedPolylineString:(NSString *)encodedPolylineString {


    NSString *decodedPolylineString = @"";
    GMSMutablePath *decodedPolylinePath = [GMSMutablePath new];
    CLLocationCoordinate2D decodedCoordinate;
    CLLocationDegrees latitude, longitude;

    int index = 0;
    NSUInteger len = encodedPolylineString.length;

    int lat = 0, lng = 0;

    while (index < len) {
        int b, shift = 0, result = 0;
        do {

            b = [encodedPolylineString characterAtIndex:index++] - 63;
            result |= (b & 0x1f) << shift;
            shift += 5;
        } while (b >= 0x20);
        int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
        lat += dlat;

        shift = 0;
        result = 0;
        do {
            b = [encodedPolylineString characterAtIndex:index++] - 63;
            result |= (b & 0x1f) << shift;
            shift += 5;
        } while (b >= 0x20);
        int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
        lng += dlng;

        latitude =  (((double) lat / 1E5));
        longitude = (((double) lng / 1E5));

        decodedCoordinate = CLLocationCoordinate2DMake(latitude, longitude);

        decodedPolylineString = [NSString stringWithFormat:@"%f%f", latitude, longitude];
        ;
        NSLog(@"%@",decodedPolylineString);

        [decodedPolylinePath addCoordinate:decodedCoordinate];
    }


    return decodedPolylinePath;
}

0
private List<GeoPoint> decodePoly(String encoded){
     
     List<GeoPoint> poly = new ArrayList<GeoPoint>();
     int index = 0, len = encoded.length();
     int lat = 0, lng = 0;

     while (index < len){
            int b, shift = 0, result = 0;
            do{
            
               b = encoded.charAt(index++) - 63;
               result |= (b & 0x1f) << shift;
               shift += 5;
            }while(b >= 0x20);
            int dlat = ((result & 1) != 0 ?~(result >> 1) : (result >> 1));
            lat += dlat;

            shift = 0;
            result = 0;
            do{
               b = encoded.charAt(index++) - 63;
               result |= (b & 0x1f) << shift;
               shift += 5;
            }while(b >= 0x20);
            int dlng = ((result & 1) != 0 ?~(result >> 1) : (result >> 1));
            lng += dlng;

            GeoPoint p = new GeoPoint((int) (((double) lat / 1E5) * 1E6), (int) (((double) lng / 1E5) * 1E6));
            poly.add(p);
     } 
     return poly;
}

虽然这段代码片段可能解决了问题,但它并没有解释为什么或者如何回答这个问题。请在您的代码中包含解释,因为这真的有助于提高您的帖子质量。记住,您正在为未来的读者回答问题,而这些人可能不知道您提出代码建议的原因。您可以使用[编辑]按钮改进此答案以获得更多的投票和声望! - Brian Tompsett - 汤莱恩

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接