Trang chủ > Android > Map và Direction trong Android

Map và Direction trong Android


Ngày bắt đầu làm quen với lập trình Android, mình có viết một ứng liên quan đến Map, hôm nay lôi ra nghịch thử thấy nó không hoạt động nữa chẳng hiểu tại sao. Tìm hiểu nguyên nhân mới biết rằng google không còn support KML direction data nữa. Đang rảnh rỗi nên ngồi thử viết lại và tìm hiểu thêm về cái Map trong Android.
Trong bài đọc này mình sẽ giới thiệu với các bạn cách hiển thị map trên thiết bị Android và tìm đường từ vị trí hiện tại tới một vị trí khác cần đến. Dưới đâylà screenshot của màn hình hiển thị map trong ứng dụng (marker màu xanh giả lập vị trí hiện tại, các markers màu đỏ giả lập các vị trí cần tới)
maptest1
Bạn chọn một vị trí trên bản đồ (marker màu đỏ) bằng cách chạm vào vị trí đó, ứng dụng sẽ tìm và vẽ đường từ vị trí hiện tại tới vị trí mà bạn vừa chọn (đường đi hiển thị bằng đường màu xanh lá cây) như hình sau:
maptest2
Sau đây là phần code cho ứng dụng vừa mô tả. Trước hết để ứng dụng hoạt động được, các bạn cần khai báo những permissions sau trong file AndroidManifest.xml:
android.permission.INTERNET”
android.permission.ACCESS_COARSE_LOCATION
android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_NETWORK_STATE

1. Layout: my_map.xlm
<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android"
android:id=”@+id/map_screen” android:orientation=”vertical”
android:layout_width=”fill_parent” android:layout_height=”fill_parent”>
<com.google.android.maps.MapView
android:id=”@+id/mapview”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:gravity=”center”
android:clickable=”true”
android:apiKey=”your_google_apimap_key” />
</LinearLayout>

Trong file layout trên, các bạn chú ý cần đăng ký với google để lấy mapapi key. Để có google mapapi key, ta cần phải tạo MD5 fingerprint, mình sẽ hướng dẫn các bạn tạo MD5 fingerprint với debugkeystore vì nó đã có sẵn nên ta bỏ qua bước tạo keystore cho đỡ loằng ngoằng mất thời gian. Debug keystore trên máy mình có đường dẫn như dưới đây, các bạn tự tìm trên máy của các bạn.
C:\Documents and Settings\Administrator\.android\debug.keystore
Bây giờ các bạn bật command line console lên và change directory tới thư mục chứa keystore và dùng jdk keytool để tạo MD5 fingerprint như hình dưới đây:

fingerprint
Sau khi đã có MD5 fingerprint các bạn vào đây để Generate API Key.

2. Các Activity và Classes trong ứng dụng:
MyMapActivity.java
public class MyMapActivity extends MapActivity {
MapView mapView;
MapController mapController;
LocationListener locListener;
Button btn_back, btn_search, btn_config;
LinearLayout mainScreen;
ProgressDialog pd;

@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.my_map);
mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
mapController = mapView.getController();
mapController.setZoom(14);
doLoadMap();
}

@Override
protected boolean isRouteDisplayed() {
return false;
}

private void doLoadMap() {
Context context = getApplicationContext();
int latDegree = 0;
int longDegree = 0;
int numberOfPlaces = 10;
try {
Drawable redMarker = getResources().getDrawable(
R.drawable.redmarker);
MyItemOverlay mainMapOverlay = new MyItemOverlay(
redMarker, context);
//hardcode the curent latitude and longitude, in real world should get it from where we saved them
double currentLatitude = 21.0325;
double currentLongitude = 105.8492;
try {
latDegree = (int) (currentLatitude * 1E6);
longDegree = (int) (currentLongitude * 1E6);
} catch (NumberFormatException ex) {
Log.v(“EXCEPTION”, ex.toString());
}
// Provide some default locations and these to the map for testing purpose
for (int i = 1; i <= numberOfPlaces; i++) {
double itemLat = 0.0;
double itemLong = 0.0;
if(i%2 == 0){
itemLat = currentLatitude + (i * 0.001);
itemLong = currentLongitude – (i * 0.002);
}else{
itemLat = currentLatitude;
itemLong = currentLongitude – (i * 0.003);
}
GeoPoint geoPoint = new GeoPoint((int) (itemLat * 1E6),
(int) (itemLong * 1E6));
OverlayItem overlayItem = new OverlayItem(geoPoint, “”, “”);
mainMapOverlay.addOverlay(overlayItem);

}
Drawable greenMarker = getResources().getDrawable(
R.drawable.greenandroidmarker);
MyItemOverlay mainMapOverlayCurrent = new MyItemOverlay(
greenMarker, context);
GeoPoint myGeoPoint = new GeoPoint(latDegree, longDegree);
OverlayItem overlayItem = new OverlayItem(myGeoPoint, “Hello”,
“You are here”);
mainMapOverlayCurrent.addOverlay(overlayItem);
List mapOverlayList = mapView.getOverlays();
mapOverlayList.clear();
mapOverlayList.add(mainMapOverlay);
mapOverlayList.add(mainMapOverlayCurrent);
mapController.animateTo(myGeoPoint);
} catch (Exception e) {
e.printStackTrace();
return;
}
}
}
Class 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;

public Segment() {
}
public void setInstruction(final String turn) {
this.instruction = turn;
}
public String getInstruction() {
return instruction;
}
public void setPoint(final GeoPoint point) {
start = point;
}
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;
}
public void setLength(final int length) {
this.length = length;
}
public int getLength() {
return length;
}
public void setDistance(double distance) {
this.distance = distance;
}
public double getDistance() {
return distance;
}
}
Class Route.java
public class Route {
private String name;
private final List points;
private List segments;
private String copyright;
private String warning;
private String country;
private int length;
private String polyline;

public Route() {
points = new ArrayList();
segments = new ArrayList();
}
public void addPoint(final GeoPoint p) {
points.add(p);
}
public void addPoints(final List points) {
this.points.addAll(points);
}
public List getPoints() {
return points;
}
public void addSegment(final Segment s) {
segments.add(s);
}
public List 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 setCountry(String country) {
this.country = country;
}
public String getCountry() {
return country;
}
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;
}
}
Class XMLParser.java
public class XMLParser {
protected static final String MARKERS = "markers";
protected static final String MARKER = "marker";
protected URL url;
protected XMLParser(final String url) {
try {
this.url = new URL(url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
protected InputStream getInputStream() {
try {
return url.openConnection().getInputStream();
} catch (IOException e) {
return null;
}
}
}

Class GoogleParser.java
Google không support KML data nữa vì thế class dùng để parse direction JSON data và decode polyline mà google trả về
public class GoogleParser extends XMLParser{
/** Distance covered. **/
private int distance;

public GoogleParser(String url) {
super(url);
}

/**
* 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() {
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 – ” + url);
}
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 decodePolyLine(final String poly) {
int len = poly.length();
int index = 0;
List decoded = new ArrayList();
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;
}
}
Class MapRouteOverlay.java
public class MapRouteOverlay extends Overlay {
Route mRoute;
ArrayList mPoints;

public MapRouteOverlay(Route route, MapView mv) {
mRoute = route;
if (route.getPoints().size() > 0) {
mPoints = new ArrayList();
mPoints = (ArrayList)route.getPoints();
int moveToLat = (mPoints.get(0).getLatitudeE6() + (mPoints.get(
mPoints.size() – 1).getLatitudeE6() – mPoints.get(0)
.getLatitudeE6()) / 2);
int moveToLong = (mPoints.get(0).getLongitudeE6() + (mPoints.get(
mPoints.size() – 1).getLongitudeE6() – mPoints.get(0)
.getLongitudeE6()) / 2);
GeoPoint moveTo = new GeoPoint(moveToLat, moveToLong);
MapController mapController = mv.getController();
mapController.animateTo(moveTo);
//mapController.setZoom(14);
}
}

@Override
public boolean draw(Canvas canvas, MapView mv, boolean shadow, long when) {
super.draw(canvas, mv, shadow);
drawPath(mv, canvas);
return true;
}

public void drawPath(MapView mv, Canvas canvas) {
int x1 = -1, y1 = -1, x2 = -1, y2 = -1;
Paint paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
for (int i = 0; i < mPoints.size(); i++) { Point point = new Point(); mv.getProjection().toPixels(mPoints.get(i), point); x2 = point.x; y2 = point.y; if (i > 0) {
canvas.drawLine(x1, y1, x2, y2, paint);
}
x1 = x2;
y1 = y2;
}
}
}
Class MyItemOverlay.java
public class MyItemOverlay extends ItemizedOverlay {
private ArrayList mOverlay = new ArrayList();
Context context;
private Drawable marker = null;
MapView mapView;
private Route mRoute;
double fromLat, fromLong;

public MyItemOverlay(Drawable defaultMarker) {
super(boundCenterBottom(defaultMarker));
this.marker = defaultMarker;
}

public MyItemOverlay(Drawable defaultMarker, Context context) {
super(boundCenterBottom(defaultMarker));
this.marker = defaultMarker;
this.context = context;
}

public void addOverlay(OverlayItem item) {
mOverlay.add(item);
populate();
}

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

@Override
public int size() {
return mOverlay.size();
}

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

@Override
public boolean onTouchEvent(MotionEvent event, MapView mapView) {
final int action = event.getAction();
final int x = (int) event.getX();
final int y = (int) event.getY();
this.mapView = mapView;

if (action == MotionEvent.ACTION_DOWN) {
for (OverlayItem item : mOverlay) {
Point p = new Point(0, 0);
GeoPoint toPoint = mapView.getProjection().fromPixels(x, y);
mapView.getProjection().toPixels(item.getPoint(), p);
if (hitTest(item, marker, x – p.x, y – p.y)) {
//hardcode the curent latitude and longitude, in real world should get it from where we saved them
fromLat = 21.0325;
fromLong = 105.8492;
mRoute = getRoute(new GeoPoint((int)(fromLat*1E6),(int)(fromLong*1E6)), toPoint);
MapRouteOverlay mapOverlay = new MapRouteOverlay(mRoute,
mapView);
List listOfOverlays = mapView.getOverlays();
int size = listOfOverlays.size();
for (int i = 0; i < size; i++) {
if (listOfOverlays.get(i) instanceof MapRouteOverlay) {
listOfOverlays.remove(i);
}
}
listOfOverlays.add(mapOverlay);
mapView.invalidate();
}
}
}
return super.onTouchEvent(event, mapView);
}

private Route getRoute(final GeoPoint start, final GeoPoint dest) {
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”);
GoogleParser parser = new GoogleParser(sBuf.toString());
Route r = parser.parse();
return r;
}
}
Mình sẽ không up source code lên vì hay nghịch bậy nên tài khoản và các file up lên thường bị xóa dẫn đến tình trạng link không down được🙂, do đó các bạn tự copy các đoạn code trên vào project và chạy thử nhé. Chúc các bạn vui vẻ!

Chuyên mục:Android
  1. 13/01/2015 lúc 9:03 chiều

    Cám ơn bạn vì bài viết! Mình cũng đang làm về vấn đề này, mình muốn hỏi bạn cách tính chiều dài đường đi giữa 2 điểm được không? Thanks ban!

    mail mình là tuanson.bi0405@gmail.com

    • 14/01/2015 lúc 6:58 chiều

      Đối tượng Location có một phương thức là distanceTo bạn có thể sử dụng luôn phương này để tính:
      public float getDistance(Location loc1, Location loc2) {
      float distance = 0f;
      distance = loc1.distanceTo(loc2);
      return distance;
      }

      Còn dưới đây là phương thức tính khoảng cách giữa hai điểm sử dụng đối tượng GeoPoint:

      public double getDistance(GeoPoint StartP, GeoPoint EndP) {
      int Radius=6371;//radius of earth in Km
      double lat1 = StartP.getLatitudeE6()/1E6;
      double lat2 = EndP.getLatitudeE6()/1E6;
      double lon1 = StartP.getLongitudeE6()/1E6;
      double lon2 = EndP.getLongitudeE6()/1E6;
      double dLat = Math.toRadians(lat2-lat1);
      double dLon = Math.toRadians(lon2-lon1);
      double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
      Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
      Math.sin(dLon/2) * Math.sin(dLon/2);
      double c = 2 * Math.asin(Math.sqrt(a));
      double valueResult= Radius*c;
      double km=valueResult/1;
      DecimalFormat newFormat = new DecimalFormat(“####”);
      kmInDec = Integer.valueOf(newFormat.format(km));
      meter=valueResult%1000;
      meterInDec= Integer.valueOf(newFormat.format(meter));
      Log.i(“Radius Value”,””+valueResult+” KM “+kmInDec+” Meter “+meterInDec);

      return Radius * c;
      }

      • 17/01/2015 lúc 3:06 chiều

        Cám ơn bạn vì đã trả lời. Nhưng ở đây mình muốn hỏi là khoảng cách thật sự trên truyến đường ấy. Bạn có thể giúp mình vẽ đường đi giữa 2 điểm và tính chiều dài đường đi được không? Thanks bạn nhiều!

  2. Nguyễn Hữu Hoạt
    15/04/2013 lúc 1:26 chiều

    bạn có thể send cho mình source code không?
    Thanks nhiều!!

    • 15/04/2013 lúc 5:22 chiều

      Mình không còn source code. Bạn bỏ chút thời gian khoảng 30 phút tạo một project copy và paste code vào là chạy thôi mà. Còn map key thì bạn phải tự làm theo hướng dẫn để lấy vì map key trên mỗi máy là khác nhau.

  1. No trackbacks yet.

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s

%d bloggers like this: