مسیریابی بر روی نقشه گوگل از داخل اپلیکیشن + سورس (آموزش)
خُب من خیلی دنبال این قضیه بودم و برام خیلی مهم بود ، بعد از چند وقت روش این کار رو پیدا کردم .
شما با استفاده از این روش میتونید داخل اپلیکیشن بر روی نقشه خودتون مسیر یابی رو انجام بدید و همینطور میتونید یک نرم افزار مسیریاب کامل بنویسید.
از اونجا که این روش یکم پیچیده هست من فقط بعضی از قسمت ها رو توضیح میدم بقیش رو خودتون در حین کار یاد میگیرید.
اول از همه یه اکتیویتی میسازید به اسم NavigationActivity و از FragmentActivity اکستندز میکنید .
داخلش یک نقشه گوگل ایجاد میکنید که قبلا آموزش استفاده از نقشه گوگل توسط استاد آقاجانی ارائه شده .لینک
یه دکمه برای مسیر یابی میسازید و برای متد onClick رو Override میکنید.
داخل متد کلیک از این کد استفاده کنید :
findDirections( TEHRAN.latitude, TEHRAN.longitude,MASHAD.latitude, MASHAD.longitude, GMapV2Direction.MODE_DRIVING );
فقط یه توضیح میدم ما مختصات تهران و مشهد رو به صورت فیلدی از جنس LatLng (موقعیت جغرافی) در میاریم به شکل زیر :
private static final LatLng TEHRAN = new LatLng(35.7014396, 51.3498186); private static final LatLng MASHAD = new LatLng(36.3164008, 59.5493205);
خب متد findDirections :
public void findDirections(double fromPositionDoubleLat, double fromPositionDoubleLong,
double toPositionDoubleLat, double toPositionDoubleLong, String mode) { Map<String, String> map = new HashMap<String, String>(); map.put(GetDirectionsAsyncTask.USER_CURRENT_LAT, String.valueOf(fromPositionDoubleLat)); map.put(GetDirectionsAsyncTask.USER_CURRENT_LONG, String.valueOf(fromPositionDoubleLong)); map.put(GetDirectionsAsyncTask.DESTINATION_LAT, String.valueOf(toPositionDoubleLat)); map.put(GetDirectionsAsyncTask.DESTINATION_LONG, String.valueOf(toPositionDoubleLong)); map.put(GetDirectionsAsyncTask.DIRECTIONS_MODE, mode); GetDirectionsAsyncTask asyncTask = new GetDirectionsAsyncTask(this); asyncTask.execute(map); }
از HashMap استفاده میکنیم که قبلا در آموزش های استاد توضیحاتش داده شده و مقادیری که در کلاس AsyncTask که بعدا میسازیم وجود داره رو داخل این HashMap ذخیره میکنیم.
و در آخر هم کلاس GetDirectionsAsyncTask رو اجرا میکنیم .
یک کلاس جدید میسازیم به نام GetDirectionsAsyncTask و از AsyncTask اکستندز میکنیم .
داخلش فیلد های زیر رو میسازیم :
public static final String USER_CURRENT_LAT = "user_current_lat"; public static final String USER_CURRENT_LONG = "user_current_long"; public static final String DESTINATION_LAT = "destination_lat"; public static final String DESTINATION_LONG = "destination_long"; public static final String DIRECTIONS_MODE = "directions_mode";
این فیلد ها همونطور که از اسمشون مشخصه مربوط به موقعیت کاربر و تنظیمات مسیریابی هستند که باهاشون بعدا آشنا میشید .
داخل این کلاس ما چند فیلد دیگه هم معرفی میکنیم :
private NavigationActivity activity; private Exception exception; private ProgressDialog progressDialog;
این فیلد ها مربوط به کانتکست اکتیویتی ، دیالاگ و اکسپشن هستن .
یک کانستراکتور برای کلاس ایجاد میکنیم و داخلش کانتکستی از اکتیویتی NavigationActivity موقع اجرا میگیریم که به صورت زیر کدش رو مینویسیم.
public GetDirectionsAsyncTask(NavigationActivity activity)
{
super();
this.activity = activity;
}
در متد onPreExecute دیالاگ رو به کاربر نشون میدیم توجه داشته باشید این متد داخل کلاس مین ترد اجرا میشه .
public void onPreExecute()
{
progressDialog = new ProgressDialog(activity);
progressDialog.setMessage("Calculating directions");
progressDialog.show();
}
داخل متد onPostExecute دیالاگ رو در صورتی که خطایی وجود نداشته باشه میبندیم و متدی رو که در اصلی یا همون
NavigationActivity هست رو صدا میزنیم .
توضیحات متد handleGetDirectionsResult رو بعدا میدم، الان فقط در همین حد بدونید که یک متدی با این نام داخل اکتیویتیNavigationActivity وجود داره.
نکته ای که اینجا هست متد onPostExecute هم داخل ترد مین اجرا میشه .
public void onPostExecute(ArrayList result)
{
progressDialog.dismiss();
if (exception == null)
{
activity.handleGetDirectionsResult(result);
}
else
{
processException();
}
}
میریم سراغ متد doInBackground ، توجه داشته باشید که این متد داخل ترد مین صدا زده نمیشه و ما نمیتونیم در UI اعمال تغییرات کنیم .
protected ArrayList<LatLng> doInBackground(Map<String, String>... params)
{
Map<String, String> paramMap = params[0];
try
{
LatLng fromPosition = new LatLng(Double.valueOf(paramMap.get(USER_CURRENT_LAT))
, Double.valueOf(paramMap.get(USER_CURRENT_LONG)));
LatLng toPosition = new LatLng(Double.valueOf(paramMap.get(DESTINATION_LAT)) ,
Double.valueOf(paramMap.get(DESTINATION_LONG)));
GMapV2Direction md = new GMapV2Direction();
Document doc = md.getDocument(fromPosition, toPosition, paramMap.get(DIRECTIONS_MODE));
ArrayList<LatLng> directionPoints = md.getDirection(doc);
return directionPoints;
}
catch (Exception e)
{
exception = e;
return null;
}
}
داخل این متد ما مختصاتی که قبلا براش در یک HashMap ذخیره کردیم رو فراخونی میکنیم و داخل یک متغیر از جنس LatLng (موقعیت جغرافیایی) میریزیم .
حالا اینجا یک کلاس جدید داریم به اسم GMapV2Direction که بهتون میگم این کلاس چیه در واقع.
غیر از این کلاس ما بایک کلاس جدید از کتابخانه اندروید آشنا میشیم به اسم Document که در واقع این کلاس برای نمایش xml , html به کار میره . توضیحات بیشتر رو میتونید در مستندات گوگل بخونید : لینک
کلاس GMapV2Direction همه این داسان زیر سر این کلاس هست و نکات خیلی ریزی هم داره که نمیشه همش رو توضیح بدم یکم روش کار کنید متوجه میشید .
داخل این کلاس ما 2 فیلد تعریف میکنیم برای مشخص کردن نوع حرکت که پیاده هستید یا با ماشین :
public final static String MODE_DRIVING = "driving"; public final static String MODE_WALKING = "walking";
متدی داریم با نام getDocument که کارش گرفتن اطلاعات مسیریابی از سرور گوگل هست .
کد زیر کاملا واضح هست و توضیحی نداره ، اطلاعات بیشتر در مورد کلاس های لایبری اندروید و متد هاشون میتونید در اسناد گوگل پیدا کنید .
public Document getDocument(LatLng start, LatLng end, String mode) {
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=driving";
try {
HttpClient httpClient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpPost httpPost = new HttpPost(url);
HttpResponse response = httpClient.execute(httpPost, localContext);
InputStream in = response.getEntity().getContent();
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(in);
return doc;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
خوب حالا متدی داریم شامل این دستورات :
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;
}
توضیحات این متد یکم زیاده خودتون مطالعه کنید فقط در این حد بهتون بگم که این متد مجموعه ای از موقعیت های جغرافیایی یا Geopoint رو به ما میده .
من روی این قسمت زیاد توضیح نمیدوم داخل کد خودتون بررسی کنید قسمت های که نوشته شده مربوط به الگوریتم های مسیریابی هست .
برمیگردیم سراغ متد handleGetDirectionsResult که داخل اکتیویتی اصلی ما قرار داره (NavigationActivity) که کدش به صورت زیر هست :
public void handleGetDirectionsResult(ArrayList<LatLng> directionPoints) {
PolylineOptions rectLine = new PolylineOptions().width(5).color(Color.RED);
for(int i = 0 ; i < directionPoints.size() ; i++)
{
rectLine.add(directionPoints.get(i));
}
if (newPolyline != null)
{
newPolyline.remove();
}
newPolyline = map.addPolyline(rectLine);
latlngBounds = createLatLngBoundsObject(TEHRAN, MASHAD);
map.animateCamera(CameraUpdateFactory.newLatLngBounds(latlngBounds,
width, height, 150));
}
این متد هم مسیر های که بدست آورده رو روی نقشه رسم میکنه .
یکم سخت و پیچیده هست و شما با استفاده از این پروژه میتونید برای خودتون یک اپلیکیشن حرفه ای برای مسیریابی بسازید.
انشاا... اگر فرصتی بود براتون یه سری متد های جدید رو هم آماده میکنم تا فاصله مسیر و یه سری چیزهای دیگه رو هم بتونید بدست بیارید.
متاسفانه سورس اصلاح شده توسط در دسترس نیست
از این لینک میتونید سورس رو دریافت کنید
موفق و سربلند باشید :)
پاسخگویی و مشاهده پاسخ های این سوال تنها برای اعضای ویژه سایت امکان پذیر است .
چنانچه تمایل دارید به همه بخش ها دسترسی داشته باشید میتوانید از این بخش لایسنس این آموزش را خریداری نمایید .