I have developed an application to read KML files of point type and then update their elevation using Google Elevation API. As you can see it receives latitude and longitude of the point and appends it with an API key to retrieve the elevation. Because my KML files have multiple points, I've used ThreadPool to read lat and long of points, append it with the key, and send the URL to Google Elevation API.Something like this:
ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(CORE_NUMBERS + 1);
String providerURL = provider.getServiceURL();
String providerKey = provider.getServiceAPIkey();
for (PointFeature p: points) {
String coordinate = p.getLatitude() + "," + p.getLongitude(); // get latitude and longitude of the feature
String url = providerURL + "locations=" + coordinate + providerKey; // creating the url of web service which contains coordinate
HeightTask task = new HeightTask(url, p); // task of each points
executor.execute(task);
}
The heightTask class is where I parse the JSON result from API and get the elevation and set the heithUpdate flag. Here is the snippet:
public class HeightTask implements Runnable {
private String url;
private Feature feature;
public HeightTask(String url, Feature f) {
this.feature = f;
this.url = url;
}
#Override
public void run() {
if (feature instanceof PointFeature) {
float height = GoogleAPIJsonParser.parsePoint(HttpManager.getData(url));
if (height != Float.NaN){
feature.updateHeight(height);
feature.setHeightUpdated(true);
Log.d("elevationPoint",height+"");
}
}
}
}
What I need is a callback to know if the elevation of all points in a layer has been updated. Is there any pattern in threadPool or just loop through all points and check the hieghtUpdate flags?
Modify code as below.
Change your HeightTask to implement Callable interface.
Prepare a collection of Callable tasks and submit using invokeAll()
List<HeightTask > futureList = new ArrayList<HeightTask >();
for (PointFeature p: points) {
String coordinate = p.getLatitude() + "," + p.getLongitude(); // get latitude and longitude of the feature
String url = providerURL + "locations=" + coordinate + providerKey; // creating the url of web service which contains coordinate
HeightTask task = new HeightTask(url, p);
futureList.add(taks);
}
executor.invokeAll(futureList);
invokeAll:
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException
Executes the given tasks, returning a list of Futures holding their status and results when all complete. Future.isDone() is true for each element of the returned list. Note that a completed task could have terminated either normally or by throwing an exception.
Related
I have this json
https://maps.googleapis.com/maps/api/directions/json?origin=-22.8895625,-47.0714089&destination=-22.892376,-47.027553&key=
And I need deserialize it
But I get this error
Additional text encountered after finished reading JSON content: ,. Path '', line 8, position 4.
What I am doing:
public static async Task<List<Model.Localizacao>> GetDirectionsAsync(Localizacao locUser, Localizacao locLoja)
{
using (var client = new HttpClient())
{
try
{
List<Model.Localizacao> lstLoc = new List<Model.Localizacao>();
var json = await client.GetStringAsync("https://maps.googleapis.com/maps/api/directions/json?origin=" + locUser.latitude + "," + locUser.longitude + "&destination="+ locLoja.latitude+","+locLoja.longitude+"&key=" + GOOGLEMAPSKEY);
json = json.Substring(json.IndexOf('['));
json = json.Substring(0, json.LastIndexOf(']') + 1);
lstLoc = JsonConvert.DeserializeObject<List<Model.Localizacao>>(json);
return lstLoc;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return null;
}
}
}
this is my class:
namespace neoFly_Montana.Model
{
class Localizacao
{
public double latitude { get; set; }
public double longitude { get; set; }
}
}
How can I solve that?
My key is the same for the maps of google
I believe the problem is in these lines:
json = json.Substring(json.IndexOf('['));
json = json.Substring(0, json.LastIndexOf(']') + 1);
This appears to set json to be all the text from the first [ to the last ]. That means that you're going to wind up with some malformed json.
geocoded_waypoints is an array, but so is routes, which means you're going to wind up with a String that looks like this:
[
{ "geocoder_status" : "OK" ... }
{ "geocoder_status" : "OK" ... }
], "routes": [
{ "bounds": { ... } ... }
]
That , "routes": [ will fail to parse.
Update
After some discussion in the comments, I think I'm at the end of the help I'm able to provide. I'm not familiar with C# or the particular JSON parsing library you're using.
However, I can offer some ideas as a starting point.
The JSON coming back from that Google call has a particular structure. I suspect you will have to create new model classes that match this structure. For example, the top-level object would have three fields, and might look like this in Java:
public class ApiResponse {
private List<Waypoint> geocoded_waypoints;
private List<Route> routes;
private String status;
}
Then you'd have to implement Waypoint and Route, again matching the structure of the Google response:
public class Waypoint {
private String geocoder_status;
private String place_id;
private List<String> types;
}
public class Route {
private Bounds bounds;
private String copyrights;
private List<Leg> legs;
private Polyline overview_polyline;
private String summary;
private List<String> warnings;
private List<String> waypoint_order;
}
And so on. Once you have a class to represent the top-level response as well as all the various sub-objects inside that response, you would probably be able to change this code:
List<Model.Localizacao> lstLoc = new List<Model.Localizacao>();
var json = await client.GetStringAsync("https://maps.googleapis.com/maps/api/directions/json?origin=" + locUser.latitude + "," + locUser.longitude + "&destination="+ locLoja.latitude+","+locLoja.longitude+"&key=" + GOOGLEMAPSKEY);
json = json.Substring(json.IndexOf('['));
json = json.Substring(0, json.LastIndexOf(']') + 1);
lstLoc = JsonConvert.DeserializeObject<List<Model.Localizacao>>(json);
return lstLoc;
to this:
var json = await client.GetStringAsync("https://maps.googleapis.com/maps/api/directions/json?origin=" + locUser.latitude + "," + locUser.longitude + "&destination="+ locLoja.latitude+","+locLoja.longitude+"&key=" + GOOGLEMAPSKEY);
return JsonConvert.DeserializeObject<ApiResponse>(json);
The first time the Asynctask executes takes 30 or more seconds. It happens when I enter the activity. After that, subsequents call to the Asyntask(when I enter the activity again from the previous activity) takes only 4 or 5 seconds, which I consider to be "acceptable".
Here is the code where I execute the AsyncTask
#Override
public void onLocationChanged(Location location) {
if (location!=null) {
mlastLocation=location;
double latitud=Double.parseDouble(getIntent().getStringExtra("latitud").replace("Latitud:", ""));
double longitud=Double.parseDouble(getIntent().getStringExtra("longitud").replace("Longitud:", ""));
LatLng origen= new LatLng(latitud,longitud);
LatLng destino=new LatLng(mlastLocation.getLatitude(),mlastLocation.getLongitude());
if (mCount==0) {
FillVariablesAsyncTask tareaAsincrona = new FillVariablesAsyncTask();
tareaAsincrona.execute(origen, destino);
mCount++;
}
}
}
And here the code of the AsyncTask, where onPostExecute updates members variables and update the UI.
private class FillVariablesAsyncTask extends AsyncTask<LatLng,Void,Document>{
#Override
protected Document doInBackground(LatLng... params) {
md=new GMapV2Direction();
LatLng origen=new LatLng(params[0].latitude,params[0].longitude);
LatLng destino=new LatLng(params[1].latitude,params[1].longitude);
Document doc = md.getDocument(origen, destino, GMapV2Direction.MODE_WALKING);
/*mUbicacionActual = md.getStartAddress(doc);
mDuration=md.getDurationText(doc);
mDistancia=md.getDistanceText(doc);*/
return doc;
}
#Override
protected void onPostExecute(Document doc) {
super.onPostExecute(doc);
mUbicacionActual = md.getStartAddress(doc);
mDuration=md.getDurationText(doc);
mDistancia=md.getDistanceText(doc);
if (mUbicacionActual!=null && mDistancia!=null && mDuration!=null) {
progressBar.setVisibility(View.GONE);
btnIr.setEnabled(true);
tvOrigenLatitud.setText("Latitud:"+String.valueOf(mlastLocation.getLatitude()));
tvOrigenLongitud.setText("Longitud"+String.valueOf(mlastLocation.getLongitude()));
tvDestino.setText("Destino:" + getIntent().getStringExtra("info").replace("Info:", ""));
tvDestinoLatitud.setText("Latitud:" + getIntent().getStringExtra("latitud").replace("Latitud:", ""));
tvDestinoLongitud.setText("Longitud:" + getIntent().getStringExtra("longitud").replace("Longitud:", ""));
tvOrigen.setText("Origen:" + mUbicacionActual);
tvDistancia.setText("Distancia:"+mDistancia);
tvTiempo.setText("Tiempo:" + mDuration);
}
}
}
I've tried out .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) but the app crashes. I've tried out also with a handler but it shows the same Behaviour, the first time takes 30 seconds or so, while the subsequents takes only a few.
Edit to show to points where i added log:
if (mCount==0) {
FillVariablesAsyncTask tareaAsincrona = new FillVariablesAsyncTask();
mStart=System.currentTimeMillis();
Log.i("START_BEFORE_EXECUTE", mStart + "");
tareaAsincrona.execute(origen, destino);
long end=System.currentTimeMillis();
Log.i("ELAPSED_EXECUTE", ((end-mStart)/1000) + "");
mCount++;
}
Here :
protected Document doInBackground(LatLng... params) {
long end;
end =System.currentTimeMillis();
Log.i("ELAPSE_PRE_BACKGROUND",((end- mStart)/1000)+"");
md=new GMapV2Direction();
end=System.currentTimeMillis();
Log.i("ELAPSED_POS_GMAPV2DIR",((end- mStart)/1000)+"");
LatLng origen=new LatLng(params[0].latitude,params[0].longitude);
LatLng destino=new LatLng(params[1].latitude,params[1].longitude);
end=System.currentTimeMillis();
Log.i("ELAPSED_PRE_GETDOCUMENT",((end- mStart)/1000)+"");
Document doc = md.getDocument(origen, destino, GMapV2Direction.MODE_WALKING);
end=System.currentTimeMillis();
Log.i("ELAPSED_POS_BACKGROUND",((end- mStart)/1000)+"");
/*mUbicacionActual = md.getStartAddress(doc);
mDuration=md.getDurationText(doc);
mDistancia=md.getDistanceText(doc);*/
return doc;
}
and here:
long end=System.currentTimeMillis();
Log.i("ELAPSED_onPostExecute",((end-mStart)/1000)+"");
mUbicacionActual = md.getStartAddress(doc);
mDuration=md.getDurationText(doc);
mDistancia=md.getDistanceText(doc);
end=System.currentTimeMillis();
Log.i("ELAPSED_POST_FILLVARS",((end-mStart)/1000)+"");
if (mUbicacionActual!=null && mDistancia!=null && mDuration!=null) {
progressBar.setVisibility(View.GONE);
btnIr.setEnabled(true);
tvOrigenLatitud.setText("Latitud:" + String.valueOf(mlastLocation.getLatitude()));
tvOrigenLongitud.setText("Longitud" + String.valueOf(mlastLocation.getLongitude()));
tvDestino.setText("Destino:" + getIntent().getStringExtra("info").replace("Info:", ""));
tvDestinoLatitud.setText("Latitud:" + getIntent().getStringExtra("latitud").replace("Latitud:", ""));
tvDestinoLongitud.setText("Longitud:" + getIntent().getStringExtra("longitud").replace("Longitud:", ""));
tvOrigen.setText("Origen:" + mUbicacionActual);
tvDistancia.setText("Distancia:"+mDistancia);
tvTiempo.setText("Tiempo:" + mDuration);
end=System.currentTimeMillis();
Log.i("ELAPSED_POS_onPostExecute", ((end - mStart) / 1000) + "");
}
this image shows the log output, it shows 3 but in reality is 30 or more.
[IMG]http://i61.tinypic.com/95o1ef.png[/IMG]
The time is taking have absolutely nothing to do with AsyncTask or Thread or Handler or Executor
The issue is that library you're using really takes that long to make calls to a server, or to process the information and I don't believe there's much you can do about it. It's only the 1st time because I'm guessing the library caches the result, but it still have nothing to do with the threading model you use.
The main point of my answer is that you're asking the wrong question. The correct question is:
Why this library is taking that long to process and what can you do to shorten it?
Unfortunately the only possible way to answer it is by analysing the source code of it or talking directly with the library developer.
edit:
to help you measure the executions:
public static class TimeMeasure {
private final DecimalFormat format;
private final double start;
private final String tag;
public TimeMeasure(String tag) {
this.format = new DecimalFormat("0.0");
this.start = System.currentTimeMillis();
this.tag = tag;
log("start);
}
public void log(String message) {
double elapsed = ((double) (System.currentTimeMillis() - start)) / 1000.0;
Log.d(tag, format.format(elapsed) + ": " + message);
}
}
then during doInBackground
protected Document doInBackground(LatLng... params) {
TimeMeasure tm = new TimeMeasure("Carlos");
// execute something:
tm.log("action 1");
// execute next
tm.log("action 2);
// etc...
}
I can't comment yet so here you go: you can use your IDE's debugger after you place breakpoints on every line in the two given blocks of code to see which line takes the most to execute. Then post your findings.
As the above answer mentioned it is actually the library that takes too much time.
I had a similar problem a while ago, I remember that after debugging what causes the problem was distance and duration and also if you were getting some kind of an address, it will contribute to the delay
(For Example) getting street name, city name and country etc..
I didn't really find any better API or library to deal with this but what i did was something to improve the user experience:
1-start another AsyncTask from onPostExecute() that will get duration and distance.
2- while the app is getting duration and distance don't block the user interface with a progress dialog or progress bar, just put loading... as an default value of your TextView then when the app receives the data setText to the actual value.
3-Do all of you data manipulation later after setting important data.
Hope this will help.
What I want to do is this:
I receive a list of directions/paths (that the user will have to follow using my app).
I am having trouble drawing the path on the map. The directions/paths contains the name of the streets, the coordinates of the streets and the segments of the streets.
I cant figure out how to draw the path/route on the map and make the route update - for example when the user moves (on the way) an icon to move indicating the progress of the user or the line drawn for the route gets shorter this really doesn't matter that much. So can you point me to tutorials which I can refer to?
I've seen a lot so far, but most of them get the directions from Google maps or the lines drawn are just straight lines from Start point to end point and doesn't fit the streets at all.
To achieve this, follow the below steps
Get list of ArrayList markerPoints;
Create your markers for it
single path,
LatLng origin = markerPoints.get(0);
LatLng dest = markerPoints.get(1);
// Getting URL to the Google Directions API
String url = getDirectionsUrl(origin, dest);
DownloadTask downloadTask = new DownloadTask();
// Start downloading json data from Google Directions API
downloadTask.execute(url);
for multiple destination path, for example A-B-D-C etc
private List<String> getDirectionsUrl(ArrayList<LatLng> markerPoints) {
List<String> mUrls = new ArrayList<>();
if (markerPoints.size() > 1) {
String str_origin = markerPoints.get(0).latitude + "," + markerPoints.get(0).longitude;
String str_dest = markerPoints.get(1).latitude + "," + markerPoints.get(1).longitude;
String sensor = "sensor=false";
String parameters = "origin=" + str_origin + "&destination=" + str_dest + "&" + sensor;
String output = "json";
String url = "https://maps.googleapis.com/maps/api/directions/" + output + "?" + parameters;
mUrls.add(url);
for (int i = 2; i < markerPoints.size(); i++)//loop starts from 2 because 0 and 1 are already printed
{
str_origin = str_dest;
str_dest = markerPoints.get(i).latitude + "," + markerPoints.get(i).longitude;
parameters = "origin=" + str_origin + "&destination=" + str_dest + "&" + sensor;
url = "https://maps.googleapis.com/maps/api/directions/" + output + "?" + parameters;
mUrls.add(url);
}
}
return mUrls;
}
Call the above method from
List<String> urls = getDirectionsUrl(markerPoints);
if (urls.size() > 1) {
for (int i = 0; i < urls.size(); i++) {
String url = urls.get(i);
DownloadTask downloadTask = new DownloadTask();
// Start downloading json data from Google Directions API
downloadTask.execute(url);
}
}
}
the above code will call for to create multiple paths, like A-B, B-D, D-C etc
try following this tutorial. You should draw between user location and marker. On user side call function onLocationChange to get the actual position and redraw the line. http://wptrafficanalyzer.in/blog/driving-route-from-my-location-to-destination-in-google-maps-android-api-v2/
Follow this:Android Google Map V3 PolyLine cannot be drawn
It'll help.
You just need to parse the data received after hitting Google Directions API
Hi im trying to create an array of the keys/values in a Object from a map. i have a function in a global actions class that formulates a url by passing in Objects with values in breaking them up to form a string to add to my url. does anyone know how to map an object and extract all the keys/values to a string?
heres what i have so far
public final static void startAPICallRequest(Context activityContext, String request, String apiLocation, Object postVarsObj, JSONObject getVarsObj){
long unixTimeStamp = System.currentTimeMillis() / 1000L;
Log.v("globals", "getVarsObj: " + getVarsObj);
if(getVarsObj != null){
Map map = (Map) getVarsObj;
for (Object key : map.keySet()) {
((Object) getVarsObj).toString();
}
Log.v("globals","map=" + map);
}
}
Try using parameters when you declare your Map
Map<K,V> map = new Map<K,V>();
Where K is the data type you want to use for your key and V is the data type you will use for your value in the Map. Looks like your value will be an Object and your key maybe a String. Then add your objects to the Map with map.put(k,v)
I am trying to run AsyncTask every 1 minute, by using handler,but it doesn't work:(
In AsyncTask I am reading data from Sqlite DB and drawing result in mapview, aim of doing like that is another service updating my Sqlite every 1 min by taking real data from server and i want keep mapview updated too.
Is there any possible way of calling AsyncTask every minute rather than using Handler?
public Runnable getData;
private final Handler _handler = new Handler();
private static int DATA_INTERVAL = 60 * 1000;
getData = new Runnable()
{
public void run()
{
getDataFrame();
}
private void getDataFrame() {
// TODO Auto-generated method stub
_handler.postDelayed(MapViewActivity.this.getData, DATA_INTERVAL);
new DrawFromDataTask();
}
};
DrawFromDataTask is described below:
private class DrawFromDataTask extends AsyncTask<Void, Integer, FriendItemizedOverlay> {
#Override
protected FriendItemizedOverlay doInBackground(Void... params) {
// TODO Auto-generated method stub
mDbAdapter.open();
List<Refresher> nearFriends = mDbAdapter.getAllRecords();
for(Refresher friend : nearFriends)
{
double lat = Double.parseDouble(friend.Latitude);
double lon = Double.parseDouble(friend.Longitude);
OverlayItem item = new OverlayItem(new GeoPoint((int)(lat * 1000000),
(int)(lon* 1000000)),
"" + friend.name,
"" + friend.type);
mFriendOverlay.addOverlay(item);
}
return mFriendOverlay;
}
protected void onProgressUpdate(Integer... progress) {
setProgress(progress[0]);
}
protected void onPostExecute(FriendItemizedOverlay result) {
System.out.println("in AsyncTask execution!");
Location loc = get_location();
final double mLatitude = loc.getLatitude();
final double mLongitude = loc.getLongitude();
// get the last location from the database
GeoPoint lastLocation = new GeoPoint(
(int) (mLatitude * 1E6),
(int) (mLongitude * 1E6));
Drawable marker = getResources().getDrawable(R.drawable.arrow);
int amountOFplayers = result.size()-1;
for (int j=0; j<amountOFplayers; j++) {
result.getItem(amountOFplayers).setMarker(marker);
}
//System.out.println("Number of overlays -- "+amountOFplayers);
mMapView.postInvalidate();
//mMapView.invalidate();
// animate to last location
mMapController.animateTo(lastLocation);
// zoom to the required level
mMapController.setZoom(ZOOM_LEVEL);
}
}
Use the broadcast intent that runs on minute tick and then just execute the Async task. It is more accurate.
http://developer.android.com/reference/android/content/Intent.html#ACTION_TIME_TICK
Make sure you create new instance every time, since the thread object cannot be reused/relaunched unless you create new instance.
http://developer.android.com/guide/components/processes-and-threads.html
Just use a Timer, TimerTask and run in a fixed rate scheduleAtFixedRate()
Instead of using a service to poll the server every one minute, why dont you use Google Cloud Messaging for android(previously C2DM).. In that way you can send some notification to your app that yes data is changed on the server, which then leads you to change some data in your db if needed ,and then start the thread.
But yes I sincerely dont have any idea about your actual requirements