【问题标题】: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

解决办法是什么?有没有替代品?

【问题讨论】:

    标签: java android google-maps android-mapview kml


    【解决方案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());这是链接stackoverflow.com/questions/11932797/…,我在其中发布了我的源代码,并且我还提到了在 cmets 中获得空值的位置,所以请任何人帮助我解决这个错误,提前感谢
    猜你喜欢
    • 2023-04-08
    • 2019-08-18
    • 2020-05-02
    • 1970-01-01
    • 2020-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-16
    相关资源
    最近更新 更多