I have more then 300 markers on top of Google Map in my Android project. They are vehicles in San Francisco as you can see from the screenshot.
I have direction and speed of the vehicles and I want to animate them for 10 seconds before the next call to the API.
To animate one marker I use this code:
static void animateMarkerToICS(Marker marker, LatLng finalPosition, final LatLngInterpolator latLngInterpolator) {
TypeEvaluator<LatLng> typeEvaluator = new TypeEvaluator<LatLng>() {
#Override
public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
return latLngInterpolator.interpolate(fraction, startValue, endValue);
}
};
Property<Marker, LatLng> property = Property.of(Marker.class, LatLng.class, "position");
ObjectAnimator animator = ObjectAnimator.ofObject(marker, property, typeEvaluator, finalPosition);
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(ANIMATION_MILLIS);
animator.start();
}
When I call this for all 300 marker the map is, as expected, slow and not very responsive.
The question is, what is the best way to animate 300 markers at the same time?
Is there an efficient way to achieve good performance for this task?
(I'm using Google Maps v2 on Android > 4.0)
p.s. I'm not planning to run the animation when the map if fully zoomed out so probably I will never have all 300 markers moving at the same time but still I would like to have feedback about the best way to work on this problem. Thanks!
Have you considered using a asynchronous task?
This will take the emphasis off your main UI thread and throw the "computation" to the background. Thus making your map more responsive and not lag.
This is something I would possibly consider....
Create a Asynch Class.
In the Do background method put your marker calculation method etc.
protected String doInBackground(Location... params) {
// Show user a Progress bar/Dialog
// your markers plotting function here...
}
In the post post execute function revert back to the map
protected void onPostExecute(String result) {
// Revert back to map
}
Note I am assuming you already have the Lat/Lng coordinates for the 300 markers and only plotting them takes forever. If however you don't have the coordinates you can create a Asych task for that aswell and free up your main thread.
Related
I would like to translate a view from pointA to pointB. imagine a line is drawn from pointA to pointB. the view should be translated to pointB along that invisible line. how a can i acheieve this. here is what i have tried so far:
getView().animate().translationX(deltaX)
getView().animate().translationY(deltaY)
but im not getting the results i want. to be specific i am using a google map and i want to move one marker to another location using translation.
I believe this repo will help you achieve your desired goal.
i am using a google map and i want to move one marker to another
location
I think the animateMarker() method in the MapUtils.java will help you achieve this,
public static void animateMarker(final Location destination, final Marker marker) {
if (marker != null) {
final LatLng startPosition = marker.getPosition();
final LatLng endPosition = new LatLng(destination.getLatitude(), destination.getLongitude());
final float startRotation = marker.getRotation();
final LatLngInterpolator latLngInterpolator = new LatLngInterpolator.LinearFixed();
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.setDuration(1000); // duration 1 second
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override public void onAnimationUpdate(ValueAnimator animation) {
try {
float v = animation.getAnimatedFraction();
LatLng newPosition = latLngInterpolator.interpolate(v, startPosition, endPosition);
marker.setPosition(newPosition);
marker.setRotation(computeRotation(v, startRotation, destination.getBearing()));
} catch (Exception ex) {
// I don't care atm..
}
}
});
valueAnimator.start();
}
}
I think your way must be more complex to achieve this goal.
Need to draw a transparent cover that must be clickable and focusable to receive touch events and prevent map changes from user input (for example, FrameLayout or custom View) above MapView. And then draw a "fake marker" on this Surface, animate, and finally destroy them.
The trick is next:
You have a marker at point A
Show Surface above all map to prevent touch
Draw new view "fake marker" on this Surface (above original marker)
Remove the original marker from the map
Animate/Translate your new marker to point B
Draw a new marker on the map at point B
Hide Surface with "fake marker"
I'm writing an application on android that will show a map from google maps. When I start the app, the map is centered on the current location. When I use animateCamera, I can see the zoom-in animation from the whole world until it focuses on current location.
The problem is that I need to touch the map to get the map to display at the zoom level I expected.
Here is what I get before I touch the screen :
Before touch
Here is what I get after having touch the screen :
After touch
If I touch the screen, the image will remain fine, until I drive a few hundred meters and then it's again unuseable. Sometimes the image appears, but it's only 1 or 2 times per 10km.
Here is how I move the camera inside LocationListener::onLocationChanged :
float zoom = 19.0f;
LatLng target = new LatLng(location.getLatitude(), location.getLongitude());
// moving car marker
m_locationMarkerG.setPosition(target);
m_locationMarkerG.setRotation(location.getBearing());
// tilting camera depending on speed
float tilt = Math.min(90, location.getSpeed()*10);
m_mapViewG.animateCamera(CameraUpdateFactory.newCameraPosition(CameraPosition.builder().zoom(zoom).bearing(location.getBearing()).
target(target).tilt(tilt).build()));
What could I try to solve this ?
Thanks
Found solution :
animateCamera MUST be called from the main looper. The LocationListener is called from another thread (sensor's thread).
So the code become :
final float zoom = 19.0f;
final LatLng target = new LatLng(location.getLatitude(), location.getLongitude());
// tilting camera depending on speed
final float tilt = Math.min(90, location.getSpeed()*10);
m_handler.post(new Runnable() {
public void run() {
// moving car marker
m_locationMarkerG.setPosition(target);
m_locationMarkerG.setRotation(location.getBearing());
// moving camera
m_mapViewG.animateCamera(CameraUpdateFactory.newCameraPosition(CameraPosition.builder().zoom(zoom).bearing(location.getBearing()).target(target).tilt(tilt).build()));
}
});
I am trying to zoom on a map
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(mBounds, this.getResources().getDisplayMetrics().widthPixels,
height, padding);
mMap.animateCamera(cameraUpdate, duration, null);
And after that I want to scroll the map vertically
CameraUpdate cameraUpdate =
CameraUpdateFactory.scrollBy(0, amountToScroll);
mMap.animateCamera(cameraUpdate, duration, null);
The thing is ... it is not working. If I call the scroll right after the zoom, only the scroll is taken into account. If I scroll the map once the zoom animation is finished I will have 2 animations.
I would like to do both operations with the same animation, is it possible?
If you call animateCamera multiple times, only the last one will finish its action.
The easy fix would be to use moveCamera instead of the first call to animateCamera, but that's not a nice solution from UX perspective.
The other way would be to do the math yourself and fill mBounds with the bounds you really want to show.
The easiest way to do it is to use CancelableCallback. You should check the first action is complete and then call the second:
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, size.x, height, 0), new CancelableCallback() {
#Override
public void onFinish() {
CameraUpdate cu_scroll = CameraUpdateFactory.scrollBy(0, 500);
mMap.animateCamera(cu_scroll);
}
#Override
public void onCancel() {
}
});
I have a google map (com.google.android.gms.maps.GoogleMap) where I have some markers set.
I am able to, separately,
1) adjust zoom level and center the map on a boundary:
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(getZoomBounds(), 10));
and
2) center the map above one of the markers:
LatLng poiSelectedLatLng = new LatLng(markerSelected.getPosition().latitude
+ offset, markerSelected.getPosition().longitude);
mMap.animateCamera(CameraUpdateFactory.newLatLng(poiSelectedLatLng));
but, for the life of me, I can't just do both, adjust the zoom level using newLatLngBounds and then center the map somewhere else. Whatever I do last is what I see happening in the map.
How do I do this?
For future visitors this is how you can chain camera animations:
map.animateCamera(CameraUpdateFactory.newLatLngBounds(getZoomBounds(), 10), 2000, new CancelableCallback() {
#Override
public void onFinish() {
LatLng poiSelectedLatLng = new LatLng(markerSelected.getPosition().latitude + offset, markerSelected.getPosition().longitude);
map.animateCamera(CameraUpdateFactory.newLatLng(poiSelectedLatLng));
}
#Override
public void onCancel() {
}
});
Also see AnimateCameraChainingExampleActivity.java for an example how to chain infinitely.
Try using both moveCamera and animateCamera...
mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(getZoomBounds(), 10));
LatLng poiSelectedLatLng = new LatLng(markerSelected.getPosition().latitude
+ offset, markerSelected.getPosition().longitude);
mMap.animateCamera(CameraUpdateFactory.newLatLng(poiSelectedLatLng));
moveCamera will move directly to that spot while animateCamera will provide the moving effect. They are linear in nature so one will happen after the other however layering them as I have done above will provide the potential effect you are looking for.
If you are trying to see the actual movement of both calls on the UI you will need to register for the callback post the completion of the animation as needed.
I'm trying to find a way to manage the markers I have on a android map (v2) implementation in my app. I'd like to be able to draw markers that are inside the viewable boundary and at the same time only show markers above a certain zoom level. It seems like a common problem. So, I'm asking if anybody here uses something like a marker manager library or something. Just to make things easier and that I don't have to make one from scratch. Thanks.
Not yet a manager, but you may want to check Android Maps Extensions, which has clustering functionality.
Edit:
In AME, drawing markers inside visible region can be achieved by using:
map.setClustering(new ClusteringSettings().addMarkersDynamically(true));
or
map.setClustering(new ClusteringSettings().enabled(false).addMarkersDynamically(true));
if you don't want clustering but only optimize for case when adding many markers.
Showing markers only when you hit a certain zoom level is not yet fully implemented, but already requested here.
I realise that this question is pretty old but if anybody has still the same problem, one can use Google Maps Android Marker Clustering Utility.
Steps which should be taken are listed below:
Implement ClusterItem to represent a marker on the map. The cluster item returns the position of the marker as a LatLng object.
Add a new ClusterManager to group the cluster items (markers) based on zoom level.
Set the map's OnCameraChangeListener() to the ClusterManager, since ClusterManager implements the listener.
If you want to add specific functionality in response to a marker click event, set the map's OnMarkerClickListener() to the ClusterManager, since ClusterManager implements the listener.
Feed the markers into the ClusterManager.
Example implementation:
public class MyItem implements ClusterItem {
private final LatLng mPosition;
public MyItem(double lat, double lng) {
mPosition = new LatLng(lat, lng);
}
#Override
public LatLng getPosition() {
return mPosition;
}
}
private void setUpClusterer() {
// Declare a variable for the cluster manager.
private ClusterManager<MyItem> mClusterManager;
// Position the map.
getMap().moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(51.503186, -0.126446), 10));
// Initialize the manager with the context and the map.
// (Activity extends context, so we can pass 'this' in the constructor.)
mClusterManager = new ClusterManager<MyItem>(this, getMap());
// Point the map's listeners at the listeners implemented by the cluster
// manager.
getMap().setOnCameraChangeListener(mClusterManager);
getMap().setOnMarkerClickListener(mClusterManager);
// Add cluster items (markers) to the cluster manager.
addItems();
}
private void addItems() {
// Set some lat/lng coordinates to start with.
double lat = 51.5145160;
double lng = -0.1270060;
// Add ten cluster items in close proximity, for purposes of this example.
for (int i = 0; i < 10; i++) {
double offset = i / 60d;
lat = lat + offset;
lng = lng + offset;
MyItem offsetItem = new MyItem(lat, lng);
mClusterManager.addItem(offsetItem);
}
}
For more information you can check here and the library's Github page