Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I would like to use this java code inside Mathematica using J/Link. It converts Google Maps API Encoded Polylines to a list of points. I tried the Mathematica tutorial but got lost.

There is the Java code:

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;
}

As example:

wjiGtdpcNrAlBJZ

should return

43.64175, -79.38651
43.64133, -79.38706
43.64127, -79.3872

I have no experience in Java. The code came from here.

share|improve this question
4  
This answer by Leonid is exactly what you want. –  rm -rf Oct 3 '12 at 22:01
    
I sow the post, tks. But it do not worked (the original post test code worked). I just replaced the code above in the argument jlcsCode and tried to compile it. I should have done something more? –  Murta Oct 4 '12 at 2:39
    
Leonid's code tests for the presence of a class, and this is only a function. Also, if it were a class, we would still need access to the GeoPoint class, and I have no idea where to get it. Lastly, Mark McClure has Mathematica code to do perform the decoding, so you could use his –  rcollyer Oct 4 '12 at 4:15
1  
@rcollyer Actually, my Mathematica code only encodes polylines, rather than decoding them. The decoding scheme is bitwise operation based and would likely be a pain to translate. I responded with instructions on accessing a Java class, though. –  Mark McClure Oct 4 '12 at 8:06
    
Also, I believe the GeoPoint class is part of Google's Android map development kit, which seems a bit heavyweight if you just want to decode a polyline. –  Mark McClure Oct 4 '12 at 8:08

2 Answers 2

up vote 10 down vote accepted

Comment

I've placed a Notebook version of this post on my webspace here: http://facstaff.unca.edu/mcmcclur/polylineDecoder.zip

The Notebook is actually contained in a ZIP file that also contains all the Java class files necessary to get this code to work. The Notebook sets the Java class relative to it's own directory using AddToClassPath[NotebookDirectory[]], so everything should just run without difficulty.

Answer

Here's my preferred java polyline decoder: https://github.com/scoutant/polyline-decoder

The advantage of this version is that it is self-contained, while Jeffery's depends on a GeoPoint class defined in a rather large SDK.

After compiling the files in src/main/java/org/scoutant/polyline, you can use the decoder like so:

Needs["JLink`"];
InstallJava[];
AddToClassPath["scoutant-polyline-decoder-76406ba/src/main/java/"];
polylineDecoder = JavaNew["org.scoutant.polyline.PolylineDecoder"];
decoded = polylineDecoder@decode["wjiGtdpcNrAlBJZ"];
length = decoded@size[];
Table[
 point = decoded@get[k];
 {point@getLat[], point@getLng[]},
 {k, 0, length - 1}]

(* Out:
   {{1.3638, -79.3865}, {1.36338, -79.3871}, {1.36332, -79.3872}}
*)

While the latitudes all disagree with your expected result, it agrees with my javascript decoder: http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/decode.html

It also agrees with Google's own polyline decoder utility: https://developers.google.com/maps/documentation/utilities/polylineutility

An example using imported directions

As a fun example, we could download some driving directions from Google Maps, extract the encoded polyline string, and display it on a CountryData map.

Needs["JLink`"];
InstallJava[];
AddToClassPath[NotebookDirectory[]];
polylineDecoder = JavaNew[
 "org.scoutant.polyline.PolylineDecoder"
];
jsonString = StringDrop[Import[
  "https://maps.google.com/maps?saddr=NY,+NY&daddr=LA,+CA&output=json"
], 9];
json = ImportString[jsonString, "JSON"];
polylines = "polylines" /. 
  Cases[json, HoldPattern["polylines" -> _], Infinity];
pointStrings = Table["points" /. polyline, {polyline, polylines}];
pointString = First[pointStrings];
pointString = StringReplace[pointString, "\\\\" -> "\\"];
decoded = polylineDecoder@decode[pointString];
length = decoded@size[];
pts = {#@getLng[], #@getLat[]} & /@ decoded@toArray[];
Show[{
  CountryData["UnitedStates", {"Shape", "Equirectangular"}],
  Graphics[{{Blue, Line[pts]},
   {PointSize[Large], Green, Point[First[pts]], Red, 
    Point[Last[pts]]}}]
}]

enter image description here

share|improve this answer
    
Tks Mark!. Very good post, exact what I need. Just one more question: I download the file "scoutant-polyline-decoder-76406ba" from github as you pointed, but how I compile it to use in Mathematica? It's using "ReinstallJava[ClassPath -> "~/MyJavaDir/MyPackage.jar:~/MyJavaDir"]" ? Some clue? I use Mac OS. –  Murta Oct 4 '12 at 11:32
    
@Murta Awesome! If you use Mac OS, open the terminal application (Go menu -> Applications, then terminal is in Utilities). You should get a command line interface. Use the cd command to get to the polyline directory and then type "javac *". If you have a java compiler, which you might, then this should generate java class files. If not, you can get one by installing XCode. If that's all a bit too much, I'd be happy to compile and post the executables with a working notebook in a ZIP file. –  Mark McClure Oct 4 '12 at 11:40
    
HI @Mark. I compiled it, and 3 .class files where created together with pre existent .java. Sorry, but I don't know how to connect it with your first code. The notebook is welcome. Tks in advance! –  Murta Oct 4 '12 at 12:13
    
@Murta If you've got the .class files, then you should be ready to go! The only issue I can imagine is at the AddToClassPath step. The easiest thing to do would be to save your working notebook right into the scoutant-polyline-decoder-76406ba directory. (I wonder if the "76506ba" is randomly generated?). Then the AddToClassPath call should be AddToClassPath["src/main/java/"]. I'll probably create the notebook anyway, but not until later. –  Mark McClure Oct 4 '12 at 12:22
    
Also, note that I've made some edits to the code in the post to fix a couple of errors. –  Mark McClure Oct 4 '12 at 12:23

I recently implemented a native Mathematica decoder for the encoded Google polyline strings returned during a request to its API. This uses Compile and I found it faster than the above J/Link based solution. I guess it could have been even faster if we could compile the BitShiftLeft and BitShiftRight. But I guess these two functions are pretty optimized in Mathematica. My compiled version for these two functions were slower so decided to stick with the built-ins.

The decoder:

Clear[Com];
Com = Compile[ {{barray, _Integer, 1}},
   Module[ {index = 1, lat = 0, lang = 0, latlng = {{0., 0.}}, b, 
     shift, result, dlat, bRight},
    While[index <=  Length@barray,
     b = shift = result = 0;
     b = -63 + barray[[index++]];
     result = BitXor[result, BitShiftLeft[BitAnd[b, 31], shift]];
     shift += 5;
     While[b >= 32,
      b = -63 + barray[[index++]];
      result = BitXor[result, BitShiftLeft[BitAnd[b, 31], shift]];
      shift += 5;];
     bRight = BitShiftRight[result, 1];
     lat += If[BitAnd[result, 1] > 0, BitNot[bRight], bRight];
     shift = result = 0;
     b = -63 + barray[[index++]];
     result = BitXor[result, BitShiftLeft[BitAnd[b, 31], shift]];
     shift += 5;
     While[b >= 32,
      b = -63 + barray[[index++]];
      result = BitXor[result, BitShiftLeft[BitAnd[b, 31], shift]];
      shift += 5;];
     bRight = BitShiftRight[result, 1];
     lang += If[BitAnd[result, 1] > 0, BitNot[bRight], bRight];
     AppendTo[latlng, {lang*10.^-5, lat*10.^-5}];
     ]; latlng], RuntimeOptions -> "Speed", CompilationTarget -> "C", 
   RuntimeAttributes -> {Listable}
         , CompilationOptions -> {"ExpressionOptimization" -> True}
   ];
MathematicaDecode[encoded_?StringQ] := Block[{barray},
   barray = ToCharacterCode@encoded;
   (Rest@Com[barray])
   ];

Utility Function:

Lets take a route connecting several cities given as a list.

route2 = {"Vienna,Austria", "Erlangen,Germany","Nurnberg,Germany","Heilbronn,Germany",
"Heidelberg,Germany", "Mannheim,Germany","Ludwigshafen,Germany", "Berlin,Germany"};

Here is a function that takes routes like above as input and forms the API request URL and do a respective data fetch from Google. For free users the map API serves up to 11-13 way-points requests in one single call.

UrlData[route_?(VectorQ[#, StringQ] &)] /; (2 <=  Length@route < 11) :=
   Block[{WaypointSeperatorPosition, url, urlData, encoded},
   WaypointSeperatorPosition = 
    Transpose@{1 + Range[-2 + Length@route]};
   url = "http://maps.googleapis.com/maps/api/directions/json" <>
     "?origin=" <> First@route <>
     "&destination=" <> Last@route <>
     "&waypoints=" <> 
     StringJoin[Most@Insert[route[[2 ;; -2]], "|", WaypointSeperatorPosition]] <>
   "&sensor=false&mode=driving&units=metric";
   URLFetch[url]
   ];

Testing:

Now we can test our decoder to extract the longitude and latitude pairs of each points that constitute the Google polyline.

dat = UrlData[route2];
strings=(Flatten@(
ImportString[dat,{"JSON","Data","routes",1,"legs",All,"steps",All,"polyline","points"}]));
res = MathematicaDecode[#] & /@strings; // AbsoluteTiming

{2.685532, Null}

The J/Link version

res1 = With[{pointString = #}, 
      decoded = polylineDecoder@decode[pointString];
      pts = {#@getLng[], #@getLat[]} & /@ decoded@toArray[];
      pts] & /@ strings; // AbsoluteTiming

{9.156631, Null}

Both the results agree with each other.

Norm@(Chop@(Flatten[res1, 1] - Flatten[res, 1]))

0

Visualize:

Here goes the route from Vienna to Berlin. With all the points on the polygon one could compute the road curvature/bending at each subsequent stretches on the polyline. enter image description here

share|improve this answer
1  
What about the code for the beautiful figure in the end? :) –  Öskå Dec 13 '13 at 16:30

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.