为啥使用 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,但适用于其他任何地方?

是否已经有适用于 android 的 StopWatch 类,为啥我的实现不起作用?

ArcGIS使用模型构造器批量转换KML格式

用 Javascript 读出 KML 文件