I want to refresh the position of about 50-100 markers on a map every 5 seconds. It gets the position data from a web service, which all works fine. The problem is, I am getting huge lag on a Galaxy S5 when deleting all the markers and creating new ones.
Is there any other way to move markers? I don't think deleting them and recreating them is a good idea.
I saw other apps using the maps api and refreshing marker positions, but without any lag at all (for example FlightRadar24). How to solve that problem?
Update:
I am using the code from the answer below but it still recreates the marker every time. Does someone know why?
My ServiceData class:
public static class ServiceData {
String ID;
String title;
String snippet;
float hue;
LatLng latlon;
ServiceData(String id, String title, String snippet, float hue, LatLng latlon) {
this.ID = id;
this.title = title;
this.snippet = snippet;
this.hue = hue;
this.latlon = latlon;
}
}
Every time I update the markers I call itemList.clear(); (Containing the ServiceData class) to clear it.
then I'm using
itemList.add(new ServiceData(id, line, snippet, hue, new LatLng(lat, lon)); to add the items to it.
When you first add markers on the map, also add them to some List or Map (that will depend on data you are retrieving, and how you can identify markers), and then when you have to refresh marker location don't recreate markers, but set their positions to new values with marker.setPosition(latlng);
Code example for updating marker positions on the map:
public class ServiceData
{
String id;
LatLng latlng;
}
GoogleMap gmap;
public Map<String, Marker> markers = new HashMap<String, Marker>();
public List<ServiceData> list = new ArrayList<ServiceData>();
public void refreshMarkers()
{
Map<String, Marker> updatedMarkers = new HashMap<String, Marker>();
for (ServiceData data: list)
{
// if marker exists move its location, if not add new marker
Marker marker = markers.get(data.id);
if (marker == null)
{
marker = gmap.addMarker(new MarkerOptions().position(data.latlng));
}
else
{
marker.setPosition(data.latlng);
markers.remove(data.id);
}
updatedMarkers.put(data.id, marker);
}
// all markers that are left in markers list need to be deleted from the map
for (Marker marker : markers.values())
{
marker.remove();
}
markers = updatedMarkers;
}
Related
I have this application where I show markers on google maps for pharmacies nearby. When I click on marker I want the marker to change the colour. When I click on some other marker, it should change the previous marker's colour to default and will change new marker's colour.
This is working randomly, I mean sometimes marker colour is getting changed and sometimes it stays the default color.
Marker lastClicked;
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
// Add a marker in user's location
// User's location is taken from the postal code entered
mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
if(lastClicked != null){
lastClicked.setIcon(BitmapDescriptorFactory.defaultMarker());
}
marker.setIcon(getMarkerIcon(getResources().getColor(R.color.app_color)));
lastClicked=marker;
return false;
}
});
}
Code for getMarkerIcon() method is:
public BitmapDescriptor getMarkerIcon(int color){
float[] hsvFloat = new float[3];
Color.colorToHSV(color, hsvFloat);
return BitmapDescriptorFactory.defaultMarker(hsvFloat[0]);
}
NOTE: I added debugger in every line to see what part of code does not run, and it is strange that when debugger come to this line
marker.setIcon(getMarkerIcon(getResources().getColor(R.color.app_color)));
it is getting compiled and yet sometimes it does not change the color of the marker.
I solved this problem by checking my list of markers. From that I found that there were markers with exactly same lat and lng, that's why markers were overlapped, that means there were more than 1 markers on one
public boolean isDuplicate(Pharmacy pharmacy){
for(int i=0; i<pharmacyList.size(); i++){
if(pharmacyList.get(i).getLatitude() == pharmacy.getLatitude()
&&pharmacyList.get(i).getLongitude() == pharmacy.getLongitude())
return true;
}
return false;
}
NOTE: Pharmacy is class that lat and lng properties.
you should try this out
googlemap.addMarker(new MarkerOptions()
.position(new LatLng( 65.07213,-2.109375))
.title("This is my title")
.snippet("and snippet")
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE)));
Hope this will work for you!
Probably at marker creation step several markers with same coordinates added and overlap each other. To check marker existence by coordinates you can use O(1) complexity (instead of O(N) for iterating over all markers case) approach by using LatLng of Marker object as HashMap/HashSet key. For do that you need to implement approach described, for example, in this answer of Tomasz Nurkiewicz. For example, Key class can be like this:
public class LatLngKey {
private LatLng mLatLng;
public LatLngKey(LatLng latLng) {
mLatLng = latLng;
}
public double getLattitude() {
return mLatLng.latitude;
}
public double getLongitude() {
return mLatLng.longitude;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof LatLngKey)) return false;
LatLngKey key = (LatLngKey) o;
return mLatLng.latitude == key.getLattitude() && mLatLng.longitude == key.getLongitude();
}
#Override
public int hashCode() {
// for better accuracy it is needed to convert double values into integer
int result = (int) mLatLng.latitude * 10_000;
result = 31 * result + (int) mLatLng.longitude * 10_000;
return result;
}
}
and using of it like that:
...
mGoogleMap = googleMap;
Map<LatLngKey, Marker> markersMap = new HashMap<>();
LatLng positionNoida = new LatLng(28.57, 77.32);
Marker markerNoida = mGoogleMap.addMarker(new MarkerOptions().position(positionNoida)
.title("Marker in Noida"));
LatLngKey markerKey = new LatLngKey(markerNoida.getPosition());
markersMap.put(markerKey, markerNoida);
LatLng positionNoida2 = new LatLng(28.57, 77.32);
Marker markerNoida2 = mGoogleMap.addMarker(new MarkerOptions().position(positionNoida2)
.title("Marker in Noida 2"));
LatLngKey markerKey2 = new LatLngKey(markerNoida2.getPosition());
if (markersMap.containsKey(markerKey2)) {
// marker with same coordinates exists
...
} else {
// marker with same coordinates do not exists
...
}
...
You need to implement PharmacyKey class for your Pharmacy.
in my application i get 25 markers from the webservice, and i add them to the map, then when user move the map i detect the center position and i get new 25 markers to add them in the map. the process must be:
1-get 25 markers from webservice
2-add the 25 markers to the map
3-when map move, detect the center position
4-get new 25 markers
5-add the new 25 markers to the map
6-delete the old 25 markers from the map
my problem is in number 6, how can i delete the 25 old markers after adding the 25 new markers.
i hope that i can find any helpful ideas, and thank you
#Barns thank you for your help, that loop doesn't work and i get this error java.util.HashMap$HashIterator.nextEntry, so i change it with this loop and finnaly it works
for (Iterator<Map.Entry<String, Marker>> iterator = markerList.entrySet().iterator(); iterator.hasNext();){
Map.Entry<String, Marker> entry = iterator.next();
//Log.e("key", entry.getKey()+"");
String bikeId = entry.getKey();
int i = SearchBike(bikeId);
//Log.e("i",i+"");
if (i == 0) {
Marker marker = entry.getValue();
marker.remove();
iterator.remove();
markerList.remove(bikeId);
}
}
for SearchBike(bikeId) i create it because i can't use arrayList.contain() because the result is an object not a string
private int SearchBike(String id){
int i = 0;
for (Bikes bike : arrayList){
if (bike.getId().equals(id)) {
i++;
}
}
return i;
}
so if i==0 that's mean the marker doesn't exist in the new list and should be deleted from the map
Create a HashMap class variable to hold information about the markers:
HashMap<Long, Marker> markerList = new HashMap<>();
(I assume bike.getId() returns a long type, but if you have used a different type you must change the definition of the HashMap and the following code to reflect that type.)
After the for (Bikes bike : arrayList) loop is run go through all the values in the markerList and remove the Markers that aren't in the arrayList.
private void addMarkers(){
//set Markers of bikes list
//First loop!!
for (Bikes bike : arrayList){
BitmapDescriptor pinMarker = null;
LatLng latLng = new LatLng(Double.parseDouble(bike.getLatitude()),Double.parseDouble(bike.getLongitude()));
switch (bike.getBreakdown()){
case "false":
pinMarker = pinWork;
break;
case "true":
pinMarker = pinMaintenance;
break;
}
Marker marker = VelosMap.addMarker(new MarkerOptions()
.position(latLng)
.icon(pinMarker)
.zIndex(1));
if(!markerList.containsKey(bike.getId())){
Marker marker = map.addMarker(marker);
//Add the marker to the marker list
markerList.put(bike.getId(), marker);
HashMap<String,String>data=new HashMap<String,String>();
data.put("id",bike.getId());
data.put("imei",bike.getImei());
data.put("inUse",bike.getInUse());
data.put("breakdown",bike.getBreakdown());
marker.setTag(data);
}
}
// This will iterate through all the items in the new list ...
//...and remove the markers that are not found in the new list
// Second Loop!!
Iterator<Long> it = markerList.keySet().iterator();
while(it.hasNext();){
Long key = it.next();
if(!bikeContains(key)){
Marker marker = markerList.remove(key);
marker.remove();
}
}
}
private boolean bikeContains(Long id){
for (Bikes bike : arrayList){
Long bikeId = bike.getId();
if(bikeId == id){
return true;
}
}
return false;
}
EXPLANATION:
The first time through addMarkers():
The markerList is empty so in line if(!markerList.containsKey(bike.getId())){ a new entry will be added every pass through the first loop for (Bikes bike : arrayList){. And the Marker will be added to the map. In the second loop for (Long key : markerList.keySet()){ we iterate through the HashMap and if the arrayList does not contain the "key" then it is removed from the markerList and from the map. But, because this is the first time through, nothing will be removed.
The second time through the addMarkers() method different bike locations are present in the arrayList containing the Bike data which should have bikes with different "id" values than the first time around--at least for some bikes.
This time when if(!markerList.containsKey(bike.getId())){ is called some of the values for bike.getId() will still be in the markerList --> nothing is done! But, some bike.getId() values will not be in the list--> these will be add to the list and markers added to the map. This means you will have more than 25 markers and more than 25 elements in markerList.
In the second loop for (Long key : markerList.keySet()){ a check is performed to see if the arrayList contains the keys from the markerList. If not in arrayList then that marker is removed from the markerList and the from the map.
What you must do is, before adding the new markers, you must clear the map by calling mGoogleMap.clear() . And you have to do this every time before adding the new markers.
In my app, I am trying to have people mark their locations, and have it brought back through Firebase.
This is my code for grabbing the latLng:
final LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
I then send the string value of latLng to another activity so it gets sent to the databse:
Intent co_intent = new Intent(MapsActivity.this, ServerImage.class);
co_intent.putExtra("pelatlng",String.valueOf(latLng));
MapsActivity.this.startActivity(co_intent);
When I do this, the coordinates gets saved in the Database like this(fake coordinates for obvious reasons):
Markers:
coordinates: "lat/lng:(35.000000,119.0000000)"
I then got the Databse value so I could bring back the coordinates:
mDatabaseMarker=FirebaseDatabase.getInstance().getReference().child("Markers");
Now I am trying to put the saved markers through ChildEventListener like this:
refDatabase.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
);
mMap.addMarker(new MarkerOptions()
.position(?)
}
However, I don't know what to put for the marker position.
I could get the value back as a String just fine, but having trouble putting the marker on that location. Thanks for the help.
Initially Split values inside brackets as below:
String example = "lat/lng:(35.000000,119.0000000)";
Matcher m = Pattern.compile("\\(([^)]+)\\)").matcher(example);
while(m.find()) {
from_lat_lng = m.group(1) ;
}
Once you have String with lat,lon Split it and parse it to double.
String[] gpsVal = from_lat_lng.split(",");
double lat = Double.parseDouble(gpsVal[0]);
double lon = Double.parseDouble(gpsVal[1]);
After this you can use lat,lon in your marker as below:
Marker a = gMap.addMarker(new MarkerOptions().zIndex(100)
.position(new LatLng(lat,lon))
I am displaying the location of buses in google map where I am getting the location from the bus database table on the server. I am facing problem to delete or to update their locations on google map since a new marker is always being created when the longitude and latitude change in the bus table. How can I delete and update specific Marker in Google Map?
I appreciate any help.
Code:
private void gotoLocation(double lat, double lng, String route_direct) {
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
final float zoom = 11;
LatLng ll = new LatLng(lat, lng);
if (lat != 0 && lng != 0 && !route_direct.isEmpty()) {
MarkerOptions markerOpt = new MarkerOptions().title(route_direct)
.position(ll).visible(true);
Marker marker = map.addMarker(markerOpt);
marker.showInfoWindow();
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(ll, zoom);
map.moveCamera(update);
}
}
I solved in this way. I created HashMap for markers.
Sample,
HashMap<String, Marker> markerlist = new HashMap<>();
markerlist.put(route_direct, yourmarker);//add marker to list
markerlist.get(route_direct);//get marker from list
Then in your update process, try this code
if(markerlist.containsKey(route_direct)){
Marker marker = markerlist.get(route_direct);
//update marker
}else{
//add marker or do anything
}
but to use this flow, you need to have unique data for marker such as marker id.
Hope this will help you. Good luck.
Here we have used on our project in didTap marker delegates, We have created two functions one is Create marker and second one is delete marker code is given below in IOS swift
1.CreateMarker function call when you get Response from APIs
2.DeleteMarker call when you want to delete marker from map
//MARK: CreateMarker
func CreateMarker(TripLocation:[RECDATA])
{
//let path = GMSMutablePath()
for i in 0..<TripLocation.count
{
// Marker icon
var image = UIImage(named: "ic_greenMark")
// Get location coordinate
let locationTujuan = CLLocation(latitude: Double(TripLocation[i].facilityLatitude ?? 0.0) , longitude: Double(TripLocation[i].facilityLongitude ?? 0.0) )
image = UIImage(named: "ic_greenMark")
// create marker
let marker = GMSMarker()
marker.position = CLLocationCoordinate2DMake(locationTujuan.coordinate.latitude, locationTujuan.coordinate.longitude)
// marker.title = titleMarker
marker.icon = image
marker.map = MapView
let camera = GMSCameraPosition.camera(withLatitude:CLLocationDegrees(TripLocation[i].facilityLatitude!), longitude: CLLocationDegrees(TripLocation[i].facilityLongitude ?? 0.0), zoom: 8)
MapView.camera = camera
// append marker into markers array to show all marker in map
markers.append(marker)
}
}
//MARK: DeleteMarker
func DeleteMarker(marker:GMSMarker)
{
// Create Temp Object array
var Tempmarkers = [RECDATA]()
//check marker is exist or not
for obj in arrayRECDATA
{
let lat = Double(obj.facilityLatitude!)
let log = Double(obj.facilityLongitude!)
// remove marker from object array
if marker.position.latitude != lat && marker.position.longitude != log
{
Tempmarkers.append(obj)
}
}
// store temp array into original array
arrayRECDATA = Tempmarkers
// clean all marker and reload
MapView.clear()
CreateMarker(TripLocation: arrayRECDATA)
}
Thank you :)
Define variable for each marker like
Marker m1;
Marker m2;
etc.
And do whatever operation on specific marker using that variable. To delete the specific marker
m1.delete ();
m2.delete ();
Something like this, you can try and let me know if you face any issue.
so i already have a map set up and i have added all markers with icons and title using a function addMarkersToMap() but they are so sparse so i was thinking of clustering them. Is there a way for me to cluster the markers that i have already set up on my map? I found this code which is awesome for clustering from a .json file but my markers are already set up on the map and i don't know how to cluster markers with already set up icon and title.
protected void startDemo() {
map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(51.503186, -0.126446), 10));
mClusterManager = new ClusterManager<MyItem>(this,map);
map.setOnCameraChangeListener(mClusterManager);
try {
readItems();
} catch (JSONException e) {
Toast.makeText(this, "Problem reading list of markers.", Toast.LENGTH_LONG).show();
}
}
private void readItems() throws JSONException {
InputStream inputStream = getResources().openRawResource(R.raw.radar_search);
List<MyItem> items = new MyItemReader().read(inputStream);
for (int i = 0; i < 5; i++) {
double offset = i / 60d;
for (MyItem item : items) {
LatLng position = item.getPosition();
double lat = position.latitude + offset;
double lng = position.longitude + offset;
MyItem offsetItem = new MyItem(lat, lng);
mClusterManager.addItem(offsetItem);
}
}
You cannot use cluster as well as your already setup custom marker (with Title and Snippet) together.
Once you start using cluster, all marker addition and related tasks will be taken care of by the clustermanager.
But all hope is not lost !!
You can add the POJO variables, their getters and setters inside the MarkerItem object you created for the cluster manager.
You can then create a class like this inside your map activity itself,
class OwnIconRendered extends DefaultClusterRenderer<MyItem> {
public OwnIconRendered(Context context, GoogleMap map,
ClusterManager<MyItem> clusterManager) {
super(context, map, clusterManager);
}
#Override
protected void onBeforeClusterItemRendered(MyItem item, MarkerOptions markerOptions) {
//markerOptions.icon(item.getIcon());
markerOptions.snippet(item.getContact());
markerOptions.title(item.getName());
super.onBeforeClusterItemRendered(item, markerOptions);
}
}
and set this render for your clustermanager like this
mClusterManager.setRenderer(new OwnIconRendered(activity.getApplicationContext(), getMap(), mClusterManager));
For more detailed explanation you can refer this answer which I have referred. http://stackoverflow.com/questions/27745299/how-to-add-title-snippet-and-icon-to-clusteritem