package app.akexorcist.gdaplibrary; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.model.CameraPosition; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; import com.google.android.gms.maps.model.Polyline; import com.google.android.gms.maps.model.PolylineOptions; import android.annotation.SuppressLint; import android.content.Context; import android.os.AsyncTask; import android.os.Handler; import android.util.DisplayMetrics; import android.util.Log; @SuppressLint("NewApi") public class GoogleDirection { public final static String MODE_DRIVING = "driving"; public final static String MODE_WALKING = "walking"; public final static String MODE_BICYCLING = "bicycling"; public final static String STATUS_OK = "OK"; public final static String STATUS_NOT_FOUND = "NOT_FOUND"; public final static String STATUS_ZERO_RESULTS = "ZERO_RESULTS"; public final static String STATUS_MAX_WAYPOINTS_EXCEEDED = "MAX_WAYPOINTS_EXCEEDED"; public final static String STATUS_INVALID_REQUEST = "INVALID_REQUEST"; public final static String STATUS_OVER_QUERY_LIMIT = "OVER_QUERY_LIMIT"; public final static String STATUS_REQUEST_DENIED = "REQUEST_DENIED"; public final static String STATUS_UNKNOWN_ERROR = "UNKNOWN_ERROR"; public final static int SPEED_VERY_FAST = 1; public final static int SPEED_FAST = 2; public final static int SPEED_NORMAL = 3; public final static int SPEED_SLOW = 4; public final static int SPEED_VERY_SLOW = 5; private OnDirectionResponseListener mDirectionListener = null; private OnAnimateListener mAnimateListener = null; private boolean isLogging = false; private LatLng animateMarkerPosition = null; private LatLng beginPosition = null; private LatLng endPosition = null; private ArrayList<LatLng> animatePositionList = null; private Marker animateMarker = null; private Polyline animateLine = null; private GoogleMap gm = null; private int step = -1; private int animateSpeed = -1; private int zoom = -1; private double animateDistance = -1; private double animateCamera = -1; private double totalAnimateDistance = 0; private boolean cameraLock = false; private boolean drawMarker = false; private boolean drawLine = false; private boolean flatMarker = false; private boolean isCameraTilt = false; private boolean isCameraZoom = false; private boolean isAnimated = false; private Context mContext = null; public GoogleDirection(Context context) { mContext = context; } public String request(LatLng start, LatLng end, String mode) { final String url = "http://maps.googleapis.com/maps/api/directions/xml?" + "origin=" + start.latitude + "," + start.longitude + "&destination=" + end.latitude + "," + end.longitude + "&sensor=false&units=metric&mode=" + mode; if(isLogging) Log.i("GoogleDirection", "URL : " + url); new RequestTask().execute(new String[]{ url }); return url; } private class RequestTask extends AsyncTask<String, Void, Document> { protected Document doInBackground(String... url) { try { HttpClient httpClient = new DefaultHttpClient(); HttpContext localContext = new BasicHttpContext(); HttpPost httpPost = new HttpPost(url[0]); HttpResponse response = httpClient.execute(httpPost, localContext); InputStream in = response.getEntity().getContent(); DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); return builder.parse(in); } catch (IOException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } return null; } protected void onPostExecute(Document doc) { super.onPostExecute(doc); if(mDirectionListener != null) mDirectionListener.onResponse(getStatus(doc), doc, GoogleDirection.this); } private String getStatus(Document doc) { NodeList nl1 = doc.getElementsByTagName("status"); Node node1 = nl1.item(0); if(isLogging) Log.i("GoogleDirection", "Status : " + node1.getTextContent()); return node1.getTextContent(); } } public void setLogging(boolean state) { isLogging = state; } public String getStatus(Document doc) { NodeList nl1 = doc.getElementsByTagName("status"); Node node1 = nl1.item(0); if(isLogging) Log.i("GoogleDirection", "Status : " + node1.getTextContent()); return node1.getTextContent(); } public String[] getDurationText(Document doc) { NodeList nl1 = doc.getElementsByTagName("duration"); String[] arr_str = new String[nl1.getLength() - 1]; for(int i = 0 ; i < nl1.getLength() - 1 ; i++) { Node node1 = nl1.item(i); NodeList nl2 = node1.getChildNodes(); Node node2 = nl2.item(getNodeIndex(nl2, "text")); arr_str[i] = node2.getTextContent(); if(isLogging) Log.i("GoogleDirection", "DurationText : " + node2.getTextContent()); } return arr_str; } public int[] getDurationValue(Document doc) { NodeList nl1 = doc.getElementsByTagName("duration"); int[] arr_int = new int[nl1.getLength() - 1]; for(int i = 0 ; i < nl1.getLength() - 1 ; i++) { Node node1 = nl1.item(i); NodeList nl2 = node1.getChildNodes(); Node node2 = nl2.item(getNodeIndex(nl2, "value")); arr_int[i] = Integer.parseInt(node2.getTextContent()); if(isLogging) Log.i("GoogleDirection", "Duration : " + node2.getTextContent()); } return arr_int; } public String getTotalDurationText(Document doc) { NodeList nl1 = doc.getElementsByTagName("duration"); Node node1 = nl1.item(nl1.getLength() - 1); NodeList nl2 = node1.getChildNodes(); Node node2 = nl2.item(getNodeIndex(nl2, "text")); if(isLogging) Log.i("GoogleDirection", "TotalDuration : " + node2.getTextContent()); return node2.getTextContent(); } public int getTotalDurationValue(Document doc) { NodeList nl1 = doc.getElementsByTagName("duration"); Node node1 = nl1.item(nl1.getLength() - 1); NodeList nl2 = node1.getChildNodes(); Node node2 = nl2.item(getNodeIndex(nl2, "value")); if(isLogging) Log.i("GoogleDirection", "TotalDuration : " + node2.getTextContent()); return Integer.parseInt(node2.getTextContent()); } public String[] getDistanceText(Document doc) { NodeList nl1 = doc.getElementsByTagName("distance"); String[] arr_str = new String[nl1.getLength() - 1]; for(int i = 0 ; i < nl1.getLength() - 1 ; i++) { Node node1 = nl1.item(i); NodeList nl2 = node1.getChildNodes(); Node node2 = nl2.item(getNodeIndex(nl2, "text")); arr_str[i] = node2.getTextContent(); if(isLogging) Log.i("GoogleDirection", "DurationText : " + node2.getTextContent()); } return arr_str; } public int[] getDistanceValue(Document doc) { NodeList nl1 = doc.getElementsByTagName("distance"); int[] arr_int = new int[nl1.getLength() - 1]; for(int i = 0 ; i < nl1.getLength() - 1 ; i++) { Node node1 = nl1.item(i); NodeList nl2 = node1.getChildNodes(); Node node2 = nl2.item(getNodeIndex(nl2, "value")); arr_int[i] = Integer.parseInt(node2.getTextContent()); if(isLogging) Log.i("GoogleDirection", "Duration : " + node2.getTextContent()); } return arr_int; } public String getTotalDistanceText(Document doc) { NodeList nl1 = doc.getElementsByTagName("distance"); Node node1 = nl1.item(nl1.getLength() - 1); NodeList nl2 = node1.getChildNodes(); Node node2 = nl2.item(getNodeIndex(nl2, "text")); if(isLogging) Log.i("GoogleDirection", "TotalDuration : " + node2.getTextContent()); return node2.getTextContent(); } public int getTotalDistanceValue(Document doc) { NodeList nl1 = doc.getElementsByTagName("distance"); Node node1 = nl1.item(nl1.getLength() - 1); NodeList nl2 = node1.getChildNodes(); Node node2 = nl2.item(getNodeIndex(nl2, "value")); if(isLogging) Log.i("GoogleDirection", "TotalDuration : " + node2.getTextContent()); return Integer.parseInt(node2.getTextContent()); } public String getStartAddress(Document doc) { NodeList nl1 = doc.getElementsByTagName("start_address"); Node node1 = nl1.item(0); if(isLogging) Log.i("GoogleDirection", "StartAddress : " + node1.getTextContent()); return node1.getTextContent(); } public String getEndAddress(Document doc) { NodeList nl1 = doc.getElementsByTagName("end_address"); Node node1 = nl1.item(0); if(isLogging) Log.i("GoogleDirection", "StartAddress : " + node1.getTextContent()); return node1.getTextContent(); } public String getCopyRights(Document doc) { NodeList nl1 = doc.getElementsByTagName("copyrights"); Node node1 = nl1.item(0); if(isLogging) Log.i("GoogleDirection", "CopyRights : " + node1.getTextContent()); return node1.getTextContent(); } public ArrayList<LatLng> getDirection(Document doc) { NodeList nl1, nl2, nl3; ArrayList<LatLng> listGeopoints = new ArrayList<LatLng>(); nl1 = doc.getElementsByTagName("step"); if (nl1.getLength() > 0) { for (int i = 0; i < nl1.getLength(); i++) { Node node1 = nl1.item(i); nl2 = node1.getChildNodes(); Node locationNode = nl2.item(getNodeIndex(nl2, "start_location")); nl3 = locationNode.getChildNodes(); Node latNode = nl3.item(getNodeIndex(nl3, "lat")); double lat = Double.parseDouble(latNode.getTextContent()); Node lngNode = nl3.item(getNodeIndex(nl3, "lng")); double lng = Double.parseDouble(lngNode.getTextContent()); listGeopoints.add(new LatLng(lat, lng)); locationNode = nl2.item(getNodeIndex(nl2, "polyline")); nl3 = locationNode.getChildNodes(); latNode = nl3.item(getNodeIndex(nl3, "points")); ArrayList<LatLng> arr = decodePoly(latNode.getTextContent()); for(int j = 0 ; j < arr.size() ; j++) { listGeopoints.add(new LatLng(arr.get(j).latitude , arr.get(j).longitude)); } locationNode = nl2.item(getNodeIndex(nl2, "end_location")); nl3 = locationNode.getChildNodes(); latNode = nl3.item(getNodeIndex(nl3, "lat")); lat = Double.parseDouble(latNode.getTextContent()); lngNode = nl3.item(getNodeIndex(nl3, "lng")); lng = Double.parseDouble(lngNode.getTextContent()); listGeopoints.add(new LatLng(lat, lng)); } } return listGeopoints; } public ArrayList<LatLng> getSection(Document doc) { NodeList nl1, nl2, nl3; ArrayList<LatLng> listGeopoints = new ArrayList<LatLng>(); nl1 = doc.getElementsByTagName("step"); if (nl1.getLength() > 0) { for (int i = 0; i < nl1.getLength(); i++) { Node node1 = nl1.item(i); nl2 = node1.getChildNodes(); Node locationNode = nl2.item(getNodeIndex(nl2, "end_location")); nl3 = locationNode.getChildNodes(); Node latNode = nl3.item(getNodeIndex(nl3, "lat")); double lat = Double.parseDouble(latNode.getTextContent()); Node lngNode = nl3.item(getNodeIndex(nl3, "lng")); double lng = Double.parseDouble(lngNode.getTextContent()); listGeopoints.add(new LatLng(lat, lng)); } } return listGeopoints; } public PolylineOptions getPolyline(Document doc, int width, int color) { ArrayList<LatLng> arr_pos = getDirection(doc); PolylineOptions rectLine = new PolylineOptions().width(dpToPx(width)).color(color); for(int i = 0 ; i < arr_pos.size() ; i++) rectLine.add(arr_pos.get(i)); return rectLine; } private int getNodeIndex(NodeList nl, String nodename) { for(int i = 0 ; i < nl.getLength() ; i++) { if(nl.item(i).getNodeName().equals(nodename)) return i; } return -1; } private ArrayList<LatLng> decodePoly(String encoded) { ArrayList<LatLng> poly = new ArrayList<LatLng>(); int index = 0, len = encoded.length(); int lat = 0, lng = 0; while (index < len) { int b, shift = 0, result = 0; do { b = encoded.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 = encoded.charAt(index++) - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); lng += dlng; LatLng position = new LatLng((double)lat / 1E5, (double)lng / 1E5); poly.add(position); } return poly; } private int dpToPx(int dp) { DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics(); int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)); return px; } public void setOnDirectionResponseListener(OnDirectionResponseListener listener) { mDirectionListener = listener; } public void setOnAnimateListener(OnAnimateListener listener) { mAnimateListener = listener; } public interface OnDirectionResponseListener { public void onResponse(String status, Document doc, GoogleDirection gd); } public interface OnAnimateListener { public void onFinish(); public void onStart(); public void onProgress(int progress, int total); } public void animateDirection(GoogleMap gm, ArrayList<LatLng> direction, int speed , boolean cameraLock, boolean isCameraTilt, boolean isCameraZoom , boolean drawMarker, MarkerOptions mo, boolean flatMarker , boolean drawLine, PolylineOptions po) { if(direction.size() > 1) { isAnimated = true; animatePositionList = direction; animateSpeed = speed; this.drawMarker = drawMarker; this.drawLine = drawLine; this.flatMarker = flatMarker; this.isCameraTilt = isCameraTilt; this.isCameraZoom = isCameraZoom; step = 0; this.cameraLock = cameraLock; this.gm = gm; setCameraUpdateSpeed(speed); beginPosition = animatePositionList.get(step); endPosition = animatePositionList.get(step + 1); animateMarkerPosition = beginPosition; if(mAnimateListener != null) mAnimateListener.onProgress(step, animatePositionList.size()); if(cameraLock) { float bearing = getBearing(beginPosition, endPosition); CameraPosition.Builder cameraBuilder = new CameraPosition.Builder() .target(animateMarkerPosition).bearing(bearing); if(isCameraTilt) cameraBuilder.tilt(90); else cameraBuilder.tilt(gm.getCameraPosition().tilt); if(isCameraZoom) cameraBuilder.zoom(zoom); else cameraBuilder.zoom(gm.getCameraPosition().zoom); CameraPosition cameraPosition = cameraBuilder.build(); gm.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); } if(drawMarker) { if(mo != null) animateMarker = gm.addMarker(mo.position(beginPosition)); else animateMarker = gm.addMarker(new MarkerOptions().position(beginPosition)); if(flatMarker) { animateMarker.setFlat(true); float rotation = getBearing(animateMarkerPosition, endPosition) + 180; animateMarker.setRotation(rotation); } } if(drawLine) { if(po != null) animateLine = gm.addPolyline(po.add(beginPosition) .add(beginPosition).add(endPosition) .width(dpToPx((int)po.getWidth()))); else animateLine = gm.addPolyline(new PolylineOptions() .width(dpToPx(5))); } new Handler().postDelayed(r, speed); if(mAnimateListener != null) mAnimateListener.onStart(); } } public void cancelAnimated() { isAnimated = false; } public boolean isAnimated() { return isAnimated; } private Runnable r = new Runnable() { public void run() { animateMarkerPosition = getNewPosition(animateMarkerPosition, endPosition); if(drawMarker) animateMarker.setPosition(animateMarkerPosition); if(drawLine) { List<LatLng> points = animateLine.getPoints(); points.add(animateMarkerPosition); animateLine.setPoints(points); } if((animateMarkerPosition.latitude == endPosition.latitude && animateMarkerPosition.longitude == endPosition.longitude)) { if(step == animatePositionList.size() - 2) { isAnimated = false; totalAnimateDistance = 0; if(mAnimateListener != null) mAnimateListener.onFinish(); } else { step++; beginPosition = animatePositionList.get(step); endPosition = animatePositionList.get(step + 1); animateMarkerPosition = beginPosition; if(flatMarker && step + 3 < animatePositionList.size() - 1) { float rotation = getBearing(animateMarkerPosition, animatePositionList.get(step + 3)) + 180; animateMarker.setRotation(rotation); } if(mAnimateListener != null) mAnimateListener.onProgress(step, animatePositionList.size()); } } if(cameraLock && (totalAnimateDistance > animateCamera || !isAnimated)) { totalAnimateDistance = 0; float bearing = getBearing(beginPosition, endPosition); CameraPosition.Builder cameraBuilder = new CameraPosition.Builder() .target(animateMarkerPosition).bearing(bearing); if(isCameraTilt) cameraBuilder.tilt(90); else cameraBuilder.tilt(gm.getCameraPosition().tilt); if(isCameraZoom) cameraBuilder.zoom(zoom); else cameraBuilder.zoom(gm.getCameraPosition().zoom); CameraPosition cameraPosition = cameraBuilder.build(); gm.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); } if(isAnimated) { new Handler().postDelayed(r, animateSpeed); } } }; public Marker getAnimateMarker() { return animateMarker; } public Polyline getAnimatePolyline() { return animateLine; } private LatLng getNewPosition(LatLng begin, LatLng end) { double lat = Math.abs(begin.latitude - end.latitude); double lng = Math.abs(begin.longitude - end.longitude); double dis = Math.sqrt(Math.pow(lat, 2) + Math.pow(lng, 2)); if(dis >= animateDistance) { double angle = -1; if(begin.latitude <= end.latitude && begin.longitude <= end.longitude) angle = Math.toDegrees(Math.atan(lng / lat)); else if(begin.latitude > end.latitude && begin.longitude <= end.longitude) angle = (90 - Math.toDegrees(Math.atan(lng / lat))) + 90; else if(begin.latitude > end.latitude && begin.longitude > end.longitude) angle = Math.toDegrees(Math.atan(lng / lat)) + 180; else if(begin.latitude <= end.latitude && begin.longitude > end.longitude) angle = (90 - Math.toDegrees(Math.atan(lng / lat))) + 270; double x = Math.cos(Math.toRadians(angle)) * animateDistance; double y = Math.sin(Math.toRadians(angle)) * animateDistance; totalAnimateDistance += animateDistance; double finalLat = begin.latitude + x; double finalLng = begin.longitude + y; return new LatLng(finalLat, finalLng); } else { return end; } } private float getBearing(LatLng begin, LatLng end) { double lat = Math.abs(begin.latitude - end.latitude); double lng = Math.abs(begin.longitude - end.longitude); if(begin.latitude < end.latitude && begin.longitude < end.longitude) return (float)(Math.toDegrees(Math.atan(lng / lat))); else if(begin.latitude >= end.latitude && begin.longitude < end.longitude) return (float)((90 - Math.toDegrees(Math.atan(lng / lat))) + 90); else if(begin.latitude >= end.latitude && begin.longitude >= end.longitude) return (float)(Math.toDegrees(Math.atan(lng / lat)) + 180); else if(begin.latitude < end.latitude && begin.longitude >= end.longitude) return (float)((90 - Math.toDegrees(Math.atan(lng / lat))) + 270); return -1; } public void setCameraUpdateSpeed(int speed) { if(speed == SPEED_VERY_SLOW) { animateDistance = 0.000005; animateSpeed = 20; animateCamera = 0.0004; zoom = 19; } else if(speed == SPEED_SLOW) { animateDistance = 0.00001; animateSpeed = 20; animateCamera = 0.0008; zoom = 18; } else if(speed == SPEED_NORMAL) { animateDistance = 0.00005; animateSpeed = 20; animateCamera = 0.002; zoom = 16; } else if(speed == SPEED_FAST) { animateDistance = 0.0001; animateSpeed = 20; animateCamera = 0.004; zoom = 15; } else if(speed == SPEED_VERY_FAST) { animateDistance = 0.0005; animateSpeed = 20; animateCamera = 0.004; zoom = 13; } else { animateDistance = 0.00005; animateSpeed = 20; animateCamera = 0.002; zoom = 16; } } }