Chatbox

Các bạn vui lòng dùng từ ngữ lịch sự và có văn hóa,sử dụng Tiếng Việt có dấu chuẩn. Chúc các bạn vui vẻ!
19/02/2014 20:02 # 1
vnttqb
Cấp độ: 13 - Kỹ năng: 8

Kinh nghiệm: 5/130 (4%)
Kĩ năng: 39/80 (49%)
Ngày gia nhập: 21/03/2011
Bài gởi: 785
Được cảm ơn: 319
WORKING WITH GOOGLE MAPS AND DIRECTIONS API IN ANDROID


Source : Tutorial about Google Map Version 2 Android.
 

Google provides an external library called Google Maps that makes it relatively easy to add powerful mapping functions to your Android applications. It is a Java package calledcom.google.android.maps, and it contains classes that allow for a wide variety of functions relating to downloading, rendering, and caching map tiles, as well as a variety of user control systems and display options.
 
 
One of the most important classes in the maps package is MapView class, a subclass of ViewGroup, which displays a map using data supplied from the Google Maps service. Essentially this class is a wrapper providing access to the functions of the Google Maps API, allowing your applications to manipulate Google Maps through MapView methods that allow maps and their data to be accessed much as though you would access any other View object. In the following tutorial you’ll see how to use Overlays and the MapController to create dynamic mapbased Activities. You'll also use Directions API to draw routes between two locations.


Getting Your Maps API Key

In order to use a Map View in your application you must first obtain an API key from the Android developer web site.
 
Without an API key the Map View will not download the tiles used to display the map. To obtain a key you need to specify the MD5 fingerprint of the certificate used to sign your application. Generally, you will sign your application using two certificates — a default debug certificate and a production certificate.
 
If you are using Eclipse with the ADT plug-in to debug your applications, they will be signed with the default debug certificate. To view map tiles while debugging you will need to obtain a Maps API key registered via the MD5 fingerprint of the debug certificate. You can find the location of your keystore in the Default Debug Keystore textbox after selecting Windows ➪Preferences ➪ Android ➪ build. Typically the debug keystore is stored in the following platform-specific locations:
 
    ➪ Windows 7 or 8  - \users\<username>\.android\debug.keystore
    ➪ Linux or Mac ∼/.android/debug.keystore
 
Each computer you use for development will have a different debug certificate and MD5 value. To find the MD5 fingerprint of your debug certificate use the keytool command from your Java installation:
 
keytool -list -alias androiddebugkey -keystore <keystore_location>.keystore
 -storepass android -keypass android

Getting your Production MD5 Fingerprint
Before you compile and sign your application for release, you will need to obtain a map API key using the MD5 fingerprint for your release certificate.Find the MD5 fingerprint using the keytool command and specifying the -list parameter and the keystore and alias you will use to sign your release application.
 
 
 keytool -list -alias my-android-alias -keystore my-android-keystore

You will be prompted for your keystore and alias passwords before the MD5 fingerprint is returned.

Creating a Map-Based Activity
To use maps in your applications you need to extend MapActivity. The Android maps library is not a standard Android package; as an optional API, it must be explicitly included in the application manifest before it can be used. Add the library to your manifest using a uses-library tag within the application node, as shown here:
 
 <uses-library android:name="com.google.android.maps">

To see map tiles in your Map View you need to add a <uses-permission> tag to your application manifest for INTERNET: 
 
 <uses-permission android:name="android.permission.INTERNET"/>

Once you’ve added the library and configured your permission, you’re ready to create your new map-based Activity. MapView controls can be used only within an Activity that extends MapActivity.

 

public class MyGoogleMap extends MapActivity {...}


One of the primary methods of the MapActivity class is the isRouteDisplayed() method, which must be implemented, and once it is, you will be able to pan around a map: 
 

@Override
protected boolean isRouteDisplayed() {
   // This method must return true if your Activity
   // is displaying driving directions. Otherwise return false.
   // Because we will be using routes in this example the 
   //method will return true. 
   return true;
}


Next step is to add the MapView into your activity_main.xml layout:
 

<com.google.android.maps.MapView
 android:id="@+id/mapView"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:enabled="true"
 android:clickable="true"
 android:apiKey="Your Maps API Key"
/>


Now add the layout with the MapView to the previously created class MyGoogleMap, as shown:
 

public class MyGoogleMap extends MapActivity {
 private MapController mapControll;
 private MapView mapview;
 private GeoPoint locationOne;
 private GeoPoint locationTwo;
 private MyItemizedOverlay markerOneOverlay;
 private MyItemizedOverlay markerTwoOverlay;
 private Drawable markerOne, markerTwo;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  requestWindowFeature(Window.FEATURE_NO_TITLE);
  setContentView(R.layout.activity_main);
 }
}


By default the Map View will show the standard street map, as shown below:
 

 
Now that the map is displayed, it's time to add some functionality, such as zoom in/out controls and markers for the two GeoPoints. Later we'll display routes between this two locations. For this matter we'll create a method showMap(), which will be called from the onCreate() method.
 
private void showMap() {
 // for simplicity we'll use static locations
 locationOne = new GeoPoint((int) (41.029223 * 1E6), 
 (int) (21.34974 * 1E6));
 mapview = (MapView) findViewById(R.id.mapView);
 mapControll = mapview.getController();
 mapview.setBuiltInZoomControls(true);
 mapControll.setZoom(17);
 mapControll.animateTo(locationOne);

 // show the first marker on locationOne
 markerOne = this.getResources().getDrawable(R.drawable.pin);
 markerOneOverlay = new MyItemizedOverlay(markerOne);
 OverlayItem overlayItem = new OverlayItem(locationOne,
 "Location One", null);
 markerOneOverlay.addOverlay(overlayItem);
 mapview.getOverlays().add(markerOneOverlay);

 locationTwo = new GeoPoint((int) (lat * 1E6), 
 (int) (lon * 1E6));
 markerTwo = this.getResources().getDrawable(
 R.drawable.pin_two);
 markerTwoOverlay = new MyItemizedOverlay(markerTwo);
 OverlayItem overlayItemUserLocation = new 
 OverlayItem( locationTwo,    "Location Two",null);
 markerTwoOverlay.addOverlay(overlayItemUserLocation);
 mapview.getOverlays().add(markerTwoOverlay);

 // Added symbols will be displayed when map is redrawn so force
 // redraw now
 mapview.postInvalidate();
}

 

Next we need to add ItemizedOverlay class in MyGoogleMap. OverlayItems are used to supply simple maker functionality to your Map Views via the ItemizedOverlay class. ItemizedOverlays provide a convenient shortcut for adding markers to a map, letting you assign a marker image and associated text to a particular geographical position. The ItemizedOverlay instance handles the drawing, placement, click handling, focus control, and layout optimization of each OverlayItem marker for you.

 

public class MyItemizedOverlay extends 
  ItemizedOverlay<OverlayItem> {

 private ArrayList<OverlayItem> myOverlays;

 public MyItemizedOverlay(Drawable defaultMarker) {
  super(boundCenterBottom(defaultMarker));
  myOverlays = new ArrayList<OverlayItem>();
  populate();
 }

 public void addOverlay(OverlayItem overlay) {
  myOverlays.add(overlay);
  populate();
 }

 @Override
 protected OverlayItem createItem(int i) {
  return myOverlays.get(i);
 }

 // Removes overlay item i
 public void removeItem(int i) {
  myOverlays.remove(i);
  populate();
 }

 // Returns present number of items in list
 @Override
 public int size() {
  return myOverlays.size();
 }

 public void addOverlayItem(OverlayItem overlayItem) {
  myOverlays.add(overlayItem);
  populate();
 }

 public void addOverlayItem(int lat, int lon, String title) {
  try {
   GeoPoint point = new GeoPoint(lat, lon);
   OverlayItem overlayItem = new OverlayItem(point, title, null);
   addOverlayItem(overlayItem);
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 @Override
 protected boolean onTap(int index) {
  return super.onTap(index);
 }
}


After you've done this, you should call showMap(), from the onCreate() method: 
 

@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 requestWindowFeature(Window.FEATURE_NO_TITLE);
 setContentView(R.layout.activity_main);
 showMap();
}


Now Your map should look something like this:
 


That was easy right? Well, now it's the interesting part - creating and displaying routes between the two GeoPoints when a button is clicked. To do this we'll have to add the interface Parser and the classes XMLParser, Segment, Route, GoogleParser and RouteOverlay in our project.

 

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

 public void setName(final String name) {
  this.name = name;
 }

 public String getName() {
  return name;
 }

 public void setCopyright(String copyright) {
  this.copyright = copyright;
 }

 public String getCopyright() {
  return copyright;
 }

 public void setWarning(String warning) {
  this.warning = warning;
 }

 public String getWarning() {
  return warning;
 }

 public void setLength(int length) {
  this.length = length;
 }

 public int getLength() {
  return length;
 }

 public void setPolyline(String polyline) {
  this.polyline = 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 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);
 }

 public final void setColour(final int c) {
  colour = c;
 }

 public final void clear() {
  routePoints.clear();
 }

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

 

Finally we need to add the method directions() in the activity that extends MapActivity.This method have two parameters start GeoPoint and destination GeoPoint, and returns a Route type. Later we'll call this method from a private class that extends AsyncTask, on different thread because it's processing network operations, and it will block the UI thread if we call it directly.

 

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


Right after this method add startRequest(), which will start the GetRoutes class on a different thread: 
 

private void startRequest() {
 new GetRoutes().execute();
}


Now let's implement the private GetRoutes class in MyGoogleMap activity: 
 

private class GetRoutes extends AsyncTask<String, Void, Void> {

 @Override
 protected void onPreExecute() {}

 @Override
 protected Void doInBackground(String... params) {

  Route route = directions(locationOne, locationTwo);
  RouteOverlay routeOverlay = new RouteOverlay(route, 0xFF9966cc);
  mapview.getOverlays().add(routeOverlay);
  mapview.postInvalidate();

  return null;
 }

 @Override
 protected void onPostExecute(Void result) {}
}

 

what we need to do next is add a button to our activity_main.xml layout, so when clicked it will call startRequest(), and the routes will be displayed. Let's see the final code for our layout.


main_activity.xml 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:background="@drawable/main_background"
    android:orientation="vertical">

 <com.google.android.maps.MapView
          android:id="@+id/mapView"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:layout_marginBottom="85dp"
          android:apiKey="your api key here"
          android:clickable="true" />

 <Button 
                 android:id="@+id/showRoutesButton"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:background="@drawable/menu_hover"
                 android:text="@string/show_routes"
                 android:padding="10dp"
                 android:textSize="20sp"
                 android:textColor="#FFFFFFFF"/>

</LinearLayout>


Now let's find the button in onCreate(), and call startRequest(). 
 

@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 requestWindowFeature(Window.FEATURE_NO_TITLE);
 setContentView(R.layout.activity_main);
 showMap();

 Button showDirectionsButton = (Button) findViewById(R.id.showRoutesButton);
 showDirectionsButton.setOnClickListener(new OnClickListener() {

   @Override
   public void onClick(View v) {
    startRequest();
   }
 });
}


Finally on the screen you will find something similar like the image below.
 

 
 


======================================================================================================

Cuộc đời là một dòng sông. Ai không bơi thì chết. 
 

Name: Tien (Tory) TRAN
Email: TranTien29@gmail.com


 
Copyright© Đại học Duy Tân 2010 - 2024