问题
I'm trying to figure out how to display the driving distance from the current location to four known locations. I'm not showing a map on the screen, I just need to display how many miles away the user currently is from those locations. I took an example from Here and modified it a little. It is bringing back the correct distance but I can't figure out how to pass in each of the 4 end locations, or differentiate between them.
What do I need to do in order to get the distance to each of the 4 locations and display each of those distances in separate TextViews?
Update question: How do I calculate the distance for each of the 4 TextViews separately?
With the code below I'm able to do what I want for the first TextView
Java
public class Locations extends Fragment {
private Location currentLocation = null;
private LocationManager locationManager;
private GeoPoint currentPoint;
TextView location1;
ArrayList<LatLng> markerPoints;
GoogleMap map;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getLastLocation();
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_locations, container, false);
...some other stuff being done here...
// Return view
return view;
}
public void getLastLocation(){
String provider = getBestProvider();
currentLocation = locationManager.getLastKnownLocation(provider);
this.markerPoints = new ArrayList<LatLng>();
LatLng fromPosition = new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude());
LatLng toPosition = new LatLng(29.633289, -82.305838);
// These are the other 3 end locations
LatLng toPosition1 = new LatLng(35.205374, -82.614587);
LatLng toPosition2 = new LatLng(35.405342, -82.316587);
LatLng toPosition3 = new LatLng(35.702354, -82.515837);
Locations.this.markerPoints.add(fromPosition);
Locations.this.markerPoints.add(toPosition);
// Getting URL to the Google Directions API
String url = Locations.this.getDirectionsUrl(fromPosition, toPosition);
DownloadTask downloadTask = new DownloadTask();
// Start downloading json data from Google Directions API
downloadTask.execute(url);
if(currentLocation != null) {
setCurrentLocation(currentLocation);
} else {
// do something
}
}
public String getBestProvider() {
locationManager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
Criteria criteria = new Criteria();
criteria.setPowerRequirement(Criteria.NO_REQUIREMENT);
criteria.setAccuracy(Criteria.NO_REQUIREMENT);
String bestProvider = locationManager.getBestProvider(criteria, true);
return bestProvider;
}
public void setCurrentLocation(Location location){
// Get current location
int currLatitude = (int) (location.getLatitude()*1E6);
int currLongitude = (int) (location.getLongitude()*1E6);
currentPoint = new GeoPoint(currLatitude,currLongitude);
// Set current location
currentLocation = new Location("");
currentLocation.setLatitude(currentPoint.getLatitudeE6() / 1e6);
currentLocation.setLongitude(currentPoint.getLongitudeE6() / 1e6);
}
private String getDirectionsUrl(LatLng origin, LatLng dest) {
// Origin of route
String str_origin = "origin=" + origin.latitude + "," + origin.longitude;
// Destination of route
String str_dest = "destination=" + dest.latitude + "," + dest.longitude;
// Sensor enabled
String sensor = "sensor=false";
// Building the parameters to the web service
String parameters = str_origin + "&" + str_dest + "&" + sensor;
// Output format
String output = "json";
// Building the url to the web service
String url = "https://maps.googleapis.com/maps/api/directions/" + output + "?" + parameters;
return url;
}
/** A method to download json data from url */
private String downloadUrl(String strUrl) throws IOException {
String data = "";
InputStream iStream = null;
HttpURLConnection urlConnection = null;
try
{
URL url = new URL(strUrl);
// Creating an http connection to communicate with url
urlConnection = (HttpURLConnection) url.openConnection();
// Connecting to url
urlConnection.connect();
// Reading data from url
iStream = urlConnection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(iStream));
StringBuffer sb = new StringBuffer();
String line = "";
while ((line = br.readLine()) != null) {
sb.append(line);
}
data = sb.toString();
br.close();
} catch (Exception e) {
Log.d("Exception while downloading url", e.toString());
} finally {
iStream.close();
urlConnection.disconnect();
}
return data;
}
// Fetches data from url passed
private class DownloadTask extends AsyncTask<String, Void, ArrayList<String>> {
@Override
protected ArrayList<String> doInBackground(String... urlList) {
try {
ArrayList<String> returnList = new ArrayList<String>();
for (String url : urlList) {
// Fetching the data from web service
String data = Locations.this.downloadUrl(url);
returnList.add(data);
}
return returnList;
} catch (Exception e) {
Log.d("Background Task", e.toString());
return null; // Failed, return null
}
}
// Executes in UI thread, after the execution of
// doInBackground()
@Override
protected void onPostExecute(ArrayList<String> results) {
super.onPostExecute(results);
ParserTask parserTask = new ParserTask();
for (String url : results) {
parserTask.execute(url);
}
// Invokes the thread for parsing the JSON data
// parserTask.execute(results);
}
}
/** A class to parse the Google Places in JSON format */
private class ParserTask extends AsyncTask<String, Integer, ArrayList<List<HashMap<String, String>>>> {
// Parsing the data in non-ui thread
@Override
protected ArrayList<List<HashMap<String, String>>> doInBackground(String... jsonData) {
try {
ArrayList<List<HashMap<String, String>>> routes = new ArrayList<List<HashMap<String, String>>>();
// for (String url : jsonData) {
for (int i = 0; i < jsonData.length; i++) {
JSONObject jObject = new JSONObject(jsonData[i]);
DirectionsJSONParser parser = new DirectionsJSONParser();
// Starts parsing data
routes = (ArrayList<List<HashMap<String, String>>>) parser.parse(jObject);
}
return routes;
} catch (Exception e) {
Log.d("Background task", e.toString());
return null; // Failed, return null
}
}
// Executes in UI thread, after the parsing process
@Override
protected void onPostExecute(ArrayList<List<HashMap<String, String>>> result) {
if (result.size() < 1) {
Toast.makeText(getActivity(), "No Points", Toast.LENGTH_LONG).show();
return;
}
TextView tv1 = (TextView) getActivity().findViewById(R.id.location1);
TextView tv2 = (TextView) getActivity().findViewById(R.id.location2);
TextView tv3 = (TextView) getActivity().findViewById(R.id.location3);
TextView tv4 = (TextView) getActivity().findViewById(R.id.location4);
TextView[] views = { tv1, tv2, tv3, tv4 };
// Traversing through all the routes
for (int i = 0; i < result.size(); i++) {
// Fetching i-th route
List<HashMap<String, String>> path = result.get(i);
String distance = "No distance";
// Fetching all the points in i-th route
for (int j = 0; j < path.size(); j++) {
HashMap<String, String> point = path.get(j);
if (j == 0) {
distance = point.get("distance");
continue;
}
}
Log.d("Distance: ", distance);
// Set text
views[i].setText(distance);
}
}
}
XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/location1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/location2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="TextView" />
<TextView
android:id="@+id/location3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="TextView" />
<TextView
android:id="@+id/location4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="TextView" />
</LinearLayout>
</RelativeLayout>
Any help figuring this out and/or suggestions to better this code will be greatly appreciated.
回答1:
Here's an update of your existing code:
// Executes in UI thread, after the parsing process
@Override
protected void onPostExecute(List<List<HashMap<String, String>>> result)
{
if (result.size() < 1)
{
Toast.makeText(getActivity(), "No Points", Toast.LENGTH_SHORT).show();
return;
}
TextView tv1 = (TextView) getActivity().findViewById(R.id.location1);
TextView tv2 = (TextView) getActivity().findViewById(R.id.location2);
TextView tv3 = (TextView) getActivity().findViewById(R.id.location3);
TextView tv4 = (TextView) getActivity().findViewById(R.id.location4);
TextView[] views = {tv1, tv2, tv3, tv4};
// Traversing through all the routes
for (int i = 0; i < result.size(); i++)
{
// Fetching i-th route
List<HashMap<String, String>> path = result.get(i);
String distance = "No distance";
// Fetching all the points in i-th route
for (int j = 0; j < path.size(); j++)
{
HashMap<String, String> point = path.get(j);
if (j == 0)
{
distance = point.get("distance");
continue;
}
}
// Set text
views[i].setText(distance);
}
}
This code makes a not-so-good assumption: It assumes that the size of result
is the same size as views
, which in your case should be 4. When you run this code, you may get an IndexOutOfBounds
error if you have more than 4 results (which shouldn't happen). Eventually you will want to verify that the size of result
is 4, or the number of TextView
's you have. If you have any questions or this doesn't work right, just let me know :)
EDIT: To get all distances at once, you can modify your DownloadTask
to take in multiple URL's.
Change class definition:
private class DownloadTask extends AsyncTask<String, Void, ArrayList<String>>
This says that your background operation will return a list of String
's.
Modified doInBackground()
, which now can process multiple URL's:
// Downloading data in non-ui thread
@Override
protected ArrayList<String> doInBackground(String... urlList)
{
try
{
ArrayList<String> returnList = new ArrayList<String>();
for(String url : urlList)
{
// Fetching the data from web service
String data = Locations.this.downloadUrl(url);
returnList.add(data);
}
return returnList;
}
catch (Exception e)
{
Log.d("Background Task", e.toString());
return null; // Failed, return null
}
}
Then you onPostExecute()
becomes
// Executes in UI thread, after the execution of
// doInBackground()
@Override
protected void onPostExecute(ArrayList<String> results)
{
super.onPostExecute(results);
ParserTask parserTask = new ParserTask();
// Invokes the thread for parsing the JSON data
parserTask.execute(results);
}
Now, you will have to modify your ParserTask
code to take in a list of JSON Strings, and not just one JSON String. Just change your ParserTask
input parameters and put everything inside a for
loop to loop through each JSON String. You will also have to modify the parameter of onPostExecute()
to take in a List
of whatever is there already, so that way it doesn't process one result, but a list of results. I can't show you those modifications here because it would be way too long, and then there would be no challenge for you :)
EDIT TWO: In getLastLocation()
you're only calling DownloadTask
with one URL, but you should put four URL's like this downloadTask.execute(url1, url2, url3, url4)
. Also, since ParserTask
still only processes one JSON String, you should take out the four TextView
's and the array looping out of the onPostExecute()
. To tell the ParserTask
which TextView
to populate, add a constructor to ParserTask
which takes in a TextView
as a parameter. Then make an instance variable within ParserTask
that is assigned in the constructor and used in onPostExecute()
to display the distance.
Then, take that TextView
array stuff I gave you before and put it in the onPostExecute()
of DownloadTask
. When you loop through the String results, also loop through the TextView
array and pass in the TextView
in the ParserTask
constructor.
Basically, you're adding a constructor in the ParserTask
to tell it which TextView
to draw on. When your DownloadTask
is finished, you pass it the right TextView
for the URL. For example, R.id.location3
for the third URL.
回答2:
public static double distFrom(double lat1, double lng1, double lat2, double lng2) {
double earthRadius = 3958.75;
double dLat = Math.toRadians(lat2-lat1);
double dLng = Math.toRadians(lng2-lng1);
double sindLat = Math.sin(dLat / 2);
double sindLng = Math.sin(dLng / 2);
double a = Math.pow(sindLat, 2) + Math.pow(sindLng, 2)
* Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2));
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
double dist = earthRadius * c;
return dist;
}
this method will calculate distance from one location to another, the accuracy is depend upon the frequency of lat,lng. More lat,lat more accurate. Remember there is difference in distance and displacement.
来源:https://stackoverflow.com/questions/17880778/google-map-api-v2-get-driving-distance-from-current-location-to-known-location