为啥使用 KML 数据检索适用于 Android 的 Google 路线不再有效? [复制]
Posted
技术标签:
【中文标题】为啥使用 KML 数据检索适用于 Android 的 Google 路线不再有效? [复制]【英文标题】:Why retrieving Google Directions for Android using KML data is not working anymore? [duplicate]为什么使用 KML 数据检索适用于 Android 的 Google 路线不再有效? [复制] 【发布时间】:2012-07-29 12:38:29 【问题描述】:可能重复:Google Maps output=kml broken?
几天以来,我在尝试使用 KML 数据检索 Google 路线时开始出现错误。错误似乎我请求的 URL 不再检索 KML 数据,它返回整个页面。为了实现这一目标,我关注了this。
解决办法是什么?有没有替代品?
【问题讨论】:
【参考方案1】:更新:
由于现在使用的是 android Google Maps v2,因此需要调整代码以在 v2 地图上工作,可以找到 here。
原答案:
从 2012 年 7 月 27 日起,这种通过解析 KML 文件从 Google 中提取 Google 路线的方式不再可用(因为 Google 更改了检索 Google 路线的结构,现在您只能通过 JSON 或 XML 获取),它是时候将您的代码迁移到 JSON 而不是 KML。
我通过创建 6 个这样的类来做到这一点:
Parser.java:
public interface Parser
public Route parse();
XMLParser.java:
public class XMLParser
// names of the XML tags
protected static final String MARKERS = "markers";
protected static final String MARKER = "marker";
protected URL feedUrl;
protected XMLParser(final String feedUrl)
try
this.feedUrl = new URL(feedUrl);
catch (MalformedURLException e)
//Log.e(e.getMessage(), "XML parser - " + feedUrl);
protected InputStream getInputStream()
try
return feedUrl.openConnection().getInputStream();
catch (IOException e)
//Log.e(e.getMessage(), "XML parser - " + feedUrl);
return null;
Segment.java:
public class Segment
/** Points in this segment. **/
private GeoPoint start;
/** Turn instruction to reach next segment. **/
private String instruction;
/** Length of segment. **/
private int length;
/** Distance covered. **/
private double distance;
/**
* Create an empty segment.
*/
public Segment()
/**
* Set the turn instruction.
* @param turn Turn instruction string.
*/
public void setInstruction(final String turn)
this.instruction = turn;
/**
* Get the turn instruction to reach next segment.
* @return a String of the turn instruction.
*/
public String getInstruction()
return instruction;
/**
* Add a point to this segment.
* @param point GeoPoint to add.
*/
public void setPoint(final GeoPoint point)
start = point;
/** Get the starting point of this
* segment.
* @return a GeoPoint
*/
public GeoPoint startPoint()
return start;
/** Creates a segment which is a copy of this one.
* @return a Segment that is a copy of this one.
*/
public Segment copy()
final Segment copy = new Segment();
copy.start = start;
copy.instruction = instruction;
copy.length = length;
copy.distance = distance;
return copy;
/**
* @param length the length to set
*/
public void setLength(final int length)
this.length = length;
/**
* @return the length
*/
public int getLength()
return length;
/**
* @param distance the distance to set
*/
public void setDistance(double distance)
this.distance = distance;
/**
* @return the distance
*/
public double getDistance()
return distance;
Route.java:
public class Route
private String name;
private final List<GeoPoint> points;
private List<Segment> segments;
private String copyright;
private String warning;
private String country;
private int length;
private String polyline;
public Route()
points = new ArrayList<GeoPoint>();
segments = new ArrayList<Segment>();
public void addPoint(final GeoPoint p)
points.add(p);
public void addPoints(final List<GeoPoint> points)
this.points.addAll(points);
public List<GeoPoint> getPoints()
return points;
public void addSegment(final Segment s)
segments.add(s);
public List<Segment> getSegments()
return segments;
/**
* @param name the name to set
*/
public void setName(final String name)
this.name = name;
/**
* @return the name
*/
public String getName()
return name;
/**
* @param copyright the copyright to set
*/
public void setCopyright(String copyright)
this.copyright = copyright;
/**
* @return the copyright
*/
public String getCopyright()
return copyright;
/**
* @param warning the warning to set
*/
public void setWarning(String warning)
this.warning = warning;
/**
* @return the warning
*/
public String getWarning()
return warning;
/**
* @param country the country to set
*/
public void setCountry(String country)
this.country = country;
/**
* @return the country
*/
public String getCountry()
return country;
/**
* @param length the length to set
*/
public void setLength(int length)
this.length = length;
/**
* @return the length
*/
public int getLength()
return length;
/**
* @param polyline the polyline to set
*/
public void setPolyline(String polyline)
this.polyline = polyline;
/**
* @return the polyline
*/
public String getPolyline()
return polyline;
GoogleParser.java:
public class GoogleParser extends XMLParser implements Parser
/** Distance covered. **/
private int distance;
public GoogleParser(String feedUrl)
super(feedUrl);
/**
* Parses a url pointing to a Google JSON object to a Route object.
* @return a Route object based on the JSON object.
*/
public Route parse()
// turn the stream into a string
final String result = convertStreamToString(this.getInputStream());
//Create an empty route
final Route route = new Route();
//Create an empty segment
final Segment segment = new Segment();
try
//Tranform the string into a json object
final JSONObject json = new JSONObject(result);
//Get the route object
final JSONObject jsonRoute = json.getJSONArray("routes").getJSONObject(0);
//Get the leg, only one leg as we don't support waypoints
final JSONObject leg = jsonRoute.getJSONArray("legs").getJSONObject(0);
//Get the steps for this leg
final JSONArray steps = leg.getJSONArray("steps");
//Number of steps for use in for loop
final int numSteps = steps.length();
//Set the name of this route using the start & end addresses
route.setName(leg.getString("start_address") + " to " + leg.getString("end_address"));
//Get google's copyright notice (tos requirement)
route.setCopyright(jsonRoute.getString("copyrights"));
//Get the total length of the route.
route.setLength(leg.getJSONObject("distance").getInt("value"));
//Get any warnings provided (tos requirement)
if (!jsonRoute.getJSONArray("warnings").isNull(0))
route.setWarning(jsonRoute.getJSONArray("warnings").getString(0));
/* Loop through the steps, creating a segment for each one and
* decoding any polylines found as we go to add to the route object's
* map array. Using an explicit for loop because it is faster!
*/
for (int i = 0; i < numSteps; i++)
//Get the individual step
final JSONObject step = steps.getJSONObject(i);
//Get the start position for this step and set it on the segment
final JSONObject start = step.getJSONObject("start_location");
final GeoPoint position = new GeoPoint((int) (start.getDouble("lat")*1E6),
(int) (start.getDouble("lng")*1E6));
segment.setPoint(position);
//Set the length of this segment in metres
final int length = step.getJSONObject("distance").getInt("value");
distance += length;
segment.setLength(length);
segment.setDistance(distance/1000);
//Strip html from google directions and set as turn instruction
segment.setInstruction(step.getString("html_instructions").replaceAll("<(.*?)*>", ""));
//Retrieve & decode this segment's polyline and add it to the route.
route.addPoints(decodePolyLine(step.getJSONObject("polyline").getString("points")));
//Push a copy of the segment to the route
route.addSegment(segment.copy());
catch (JSONException e)
Log.e(e.getMessage(), "Google JSON Parser - " + feedUrl);
return route;
/**
* Convert an inputstream to a string.
* @param input inputstream to convert.
* @return a String of the inputstream.
*/
private static String convertStreamToString(final InputStream input)
final BufferedReader reader = new BufferedReader(new InputStreamReader(input));
final StringBuilder sBuf = new StringBuilder();
String line = null;
try
while ((line = reader.readLine()) != null)
sBuf.append(line);
catch (IOException e)
Log.e(e.getMessage(), "Google parser, stream2string");
finally
try
input.close();
catch (IOException e)
Log.e(e.getMessage(), "Google parser, stream2string");
return sBuf.toString();
/**
* Decode a polyline string into a list of GeoPoints.
* @param poly polyline encoded string to decode.
* @return the list of GeoPoints represented by this polystring.
*/
private List<GeoPoint> decodePolyLine(final String poly)
int len = poly.length();
int index = 0;
List<GeoPoint> decoded = new ArrayList<GeoPoint>();
int lat = 0;
int lng = 0;
while (index < len)
int b;
int shift = 0;
int result = 0;
do
b = poly.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 = poly.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
while (b >= 0x20);
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lng += dlng;
decoded.add(new GeoPoint(
(int) (lat*1E6 / 1E5), (int) (lng*1E6 / 1E5)));
return decoded;
RouteOverlay.java:
public class RouteOverlay extends Overlay
/** GeoPoints representing this routePoints. **/
private final List<GeoPoint> routePoints;
/** Colour to paint routePoints. **/
private int colour;
/** Alpha setting for route overlay. **/
private static final int ALPHA = 120;
/** Stroke width. **/
private static final float STROKE = 4.5f;
/** Route path. **/
private final Path path;
/** Point to draw with. **/
private final Point p;
/** Paint for path. **/
private final Paint paint;
/**
* Public constructor.
*
* @param route Route object representing the route.
* @param defaultColour default colour to draw route in.
*/
public RouteOverlay(final Route route, final int defaultColour)
super();
routePoints = route.getPoints();
colour = defaultColour;
path = new Path();
p = new Point();
paint = new Paint();
@Override
public final void draw(final Canvas c, final MapView mv,
final boolean shadow)
super.draw(c, mv, shadow);
paint.setColor(colour);
paint.setAlpha(ALPHA);
paint.setAntiAlias(true);
paint.setStrokeWidth(STROKE);
paint.setStyle(Paint.Style.STROKE);
redrawPath(mv);
c.drawPath(path, paint);
/**
* Set the colour to draw this route's overlay with.
*
* @param c Int representing colour.
*/
public final void setColour(final int c)
colour = c;
/**
* Clear the route overlay.
*/
public final void clear()
routePoints.clear();
/**
* Recalculate the path accounting for changes to
* the projection and routePoints.
* @param mv MapView the path is drawn to.
*/
private void redrawPath(final MapView mv)
final Projection prj = mv.getProjection();
path.rewind();
final Iterator<GeoPoint> it = routePoints.iterator();
prj.toPixels(it.next(), p);
path.moveTo(p.x, p.y);
while (it.hasNext())
prj.toPixels(it.next(), p);
path.lineTo(p.x, p.y);
path.setLastPoint(p.x, p.y);
然后您在包含地图的 Activity 中执行此操作:
1-添加此功能:
private Route directions(final GeoPoint start, final GeoPoint dest)
Parser parser;
//https://developers.google.com/maps/documentation/directions/#JSON <- get api
String jsonURL = "http://maps.googleapis.com/maps/api/directions/json?";
final StringBuffer sBuf = new StringBuffer(jsonURL);
sBuf.append("origin=");
sBuf.append(start.getLatitudeE6()/1E6);
sBuf.append(',');
sBuf.append(start.getLongitudeE6()/1E6);
sBuf.append("&destination=");
sBuf.append(dest.getLatitudeE6()/1E6);
sBuf.append(',');
sBuf.append(dest.getLongitudeE6()/1E6);
sBuf.append("&sensor=true&mode=driving");
parser = new GoogleParser(sBuf.toString());
Route r = parser.parse();
return r;
2- 在onCreate()
函数中添加这个:
MapView mapView = (MapView) findViewById(R.id.mapview); //or you can declare it directly with the API key
Route route = directions(new GeoPoint((int)(26.2*1E6),(int)(50.6*1E6)), new GeoPoint((int)(26.3*1E6),(int)(50.7*1E6)));
RouteOverlay routeOverlay = new RouteOverlay(route, Color.BLUE);
mapView.getOverlays().add(routeOverlay);
mapView.invalidate();
编辑:如果您遇到异常,请在 AsyncTask
中使用 directions()
函数以避免 UI 线程上的网络处理。
【讨论】:
+1 得到很好的答案,它对我有帮助。但是 StringBuffer 中的 US_API 是什么 sBuf = new StringBuffer(US_API);是 US_API = "maps.google.com/maps/api/directions/json?"; @DeepakSwami 嗨,我已经按照上面的方法进行操作,但是我正在强制关闭你能在这个问题上帮助我吗?我有点困惑主要活动代码出现错误怎么办?只有你也可以发布该代码? @DeepakSwami:是的,就是这样,我忘了添加它,因为我在我的 Activity 中将它声明为全局变量。 @KAREEMMAHAMMED:您的主 Activity 应该扩展 MapActivity 类,并在其中添加代码的最后两个 sn-ps(我编辑了其中一个)。如果您仍然遇到问题,请发布堆栈跟踪或发布有关它不工作的部分的另一个问题。 @HeshamSaeed:感谢您的回复,我还有一个问题要问您,我想在两个位置之间画线,最初效果很好,但是现在运行时在 doc = db.parse( urlConnection.getInputStream());这是链接***.com/questions/11932797/…,我在其中发布了我的源代码,并且我还提到了在 cmets 中获得空值的位置,所以请任何人帮助我解决这个错误,提前感谢以上是关于为啥使用 KML 数据检索适用于 Android 的 Google 路线不再有效? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
如何在自定义地图上进行离线地理定位,phonegap+kml
为啥我在 Eclipse 上找不到适用于 Eclipse 的 Android SDK?
为啥这个 CSS 不适用于 Android 上的 Chrome,但适用于其他任何地方?