Displaying collection of ("moving") markers on map, performance - android

Im getting list of planes from web api with their locations and infos. Im calling this web api every few seconds and drawing the map markers (planes) to map.
When I call my method addPlanesToMap(), to add/update markers on map, map freezes for short period of time. If im movin the map at the same time the markers get added/updated there is some lag. This happens in devices like Nexus 5. There is around 200 - 300 markers in map.
Is there something how I can improve my code, do something completely differently?
HashMap<String, MarkerContainer> mMarkerHashMap = new HashMap <String, MarkerContainer>();
public void addPlanesToMap(List <? extends RealtimePlane > planes) {
View planeMarkerView = getActivity().getLayoutInflater().inflate(R.layout.map_marker_plane, null);
TextView markerShortCodeText = (TextView) planeMarkerView.findViewById(R.id.MapMarkerText);
ImageView markerArrow = (ImageView) planeMarkerView.findViewById(R.id.MapMarkerArrow);
for (RealtimePlane plane: planes) {
MarkerContainer markerContainer = mMarkerHashMap.get(plane.getPlaneId());
// Do not process, if not updated
if (markerContainer != null && markerContainer.getPlane().getRecordedAtTime().equals(plane.getRecordedAtTime())) {
continue;
}
markerShortCodeText.setText(plane.getPlaneCode());
markerShortCodeText.setBackgroundResource(getMapMarkerDrawableBg(plane.getType()));
markerArrow.setRotation((float) plane.getBearing());
markerArrow.setVisibility(View.VISIBLE);
// Add new plane marker to map
if (markerContainer == null) {
Marker m = mMap.addMarker(new MarkerOptions()
.position(plane.getLocation())
.flat(true)
.anchor(0.5f, 0.5f)
.title(plane.getLineCode())
.icon(BitmapDescriptorFactory.fromBitmap(loadBitmapFromView(getActivity(), planeMarkerView))));
mMarkerHashMap.put(plane.getPlaneId(), new MarkerContainer(m, plane));
}
// Marker already set, update
else {
markerContainer.getMarker().setIcon(BitmapDescriptorFactory.fromBitmap(loadBitmapFromView(getActivity(), planeMarkerView)));
markerContainer.getMarker().setPosition(plane.getLocation());
markerContainer.setPlane(plane);
}
}
}

Is there something how I can improve my code, do something completely differently
yes, do not plot every single marker and only plot what is in the visible area. you can also do marker clustering.
Plotting 2-300 markers every few seconds is not really a good idea to begin with

Related

Drawing a direction on Google maps is not continuous

I'm using these settings to create a direction from a location to another:
PolylineOptions polylineOptions = new PolylineOptions();
polylineOptions.width(15);
polylineOptions.color(colorPrimary);
mMap.addPolyline(polylineOptions.addAll(points));
And on the map, it looks like this:
https://ibb.co/D13xc0T
How to draw a continuous line from one point to another without those interruptions?
This is the code that gets the points:
if (direction != null) {
List<Route> routes = direction.routes;
Route route = routes.get(0);
List<Leg> legs = route.legs;
Leg leg = legs.get(0);
List<Step> steps = leg.steps;
for (Step step : steps) {
String point = step.polyline.points;
List<LatLng> points = PolyUtil.decode(point);
}
}
You add many PolylineOptions to map. They are not connected to each over and so you get this interruptions. Try to add all decoded paths to one common array.
Your edited code:
ArrayList<LatLng> points = new ArrayList<LatLng>();
for (Step step : steps) {
String point = step.polyline.points;
points.addAll(PolyUtil.decode(point));
}
PolylineOptions polylineOptions = new PolylineOptions();
polylineOptions.width(15);
polylineOptions.color(colorPrimary);
mMap.addPolyline(polylineOptions.addAll(points));
Try with this, it works fine for me. You can add multiple point also.
mMap.addPolyline(new PolylineOptions().add(PointA).add(PointB).width(8f).color(Color.RED));
I ran into the same problem with broken polylines. After not finding a solution here, I stumbled across a closed issue here. If you just add all points of all steps as given by the result of an call to Googles API you won't get a continuous line, because the endpoints will be added twice. Once as end of a step and a second time as beginning of the next step. That is why there is no single continuous line and why you have to remove duplicates. In Kotlin this is pretty easy by calling allPoints = allPoints.distinct() as ArrayList<LatLng>. Calling this will keep the order of the points, but remove any duplicates in the list.

Get precise position of user device with GPS

In my Android application i need to get the traveled road with car and i check it with google android directions API and GPS.
But the problem is that some times the GPS location is not precise and i can get an error of 10Km for some lecture and that means that in a total travel of 150km my "distance traveled" can be 250km and it's wrong!
Yesterday i test it in highway and the problem is that when the marker is located out from highway, the calculation of distance traveled from my current position and the last marker located by road is very wrong (in this case out of the highway).
There is some best way for getting traveled distance with car using the phone?
Maybe some better code for getting more precise GPS position?
Here is my code:
private void getUserLocation() {
Log.d(TAG, "getUserLocation()");
checkLocationPermission();
mFusedLocationClient.getLastLocation()
.addOnSuccessListener(getActivity(), new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
// Got last known location. In some rare situations this can be null.
if (location != null) {
// Logic to handle location object
String stringStartLat, stringStartLng;
double lat = location.getLatitude();
double lng = location.getLongitude();
//Start position of device
if (travelInfo.getStartLat() == 0 && travelInfo.getStartLng() == 0) {
travelInfo.setStartLat(lat);
travelInfo.setStartLng(lng);
//Set lastcoordinates equals to start
travelInfo.setLastLat(lat);
travelInfo.setLastLng(lng);
}
//Current position of device
travelInfo.setCurrentLat(lat);
travelInfo.setCurrentLng(lng);
stringStartLat = Double.toString(travelInfo.getStartLat());
stringStartLng = Double.toString(travelInfo.getStartLng());
//Set the TextView in the fragment with start coordinates
Log.d(TAG,"LatitudeStart: " + stringStartLat);
Log.d(TAG,"LongitudeStart: " + stringStartLng);
if (startMarker == null) {
//Add the user location to the map with a marker
LatLng userLoction = new LatLng(travelInfo.getStartLat(), travelInfo.getStartLng());
startMarker = googleMap.addMarker(new MarkerOptions()
.position(userLoction)
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE))
.title("Start Position")
.snippet("Show the start position of user"));
// For zooming automatically to the location of the marker
CameraPosition cameraPosition = new CameraPosition.Builder().target(userLoction).zoom(9).build();
googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
} else {
LatLng currentLocation = new LatLng(travelInfo.getCurrentLat(), travelInfo.getCurrentLng());
currentPositionMarker = googleMap.addMarker(new MarkerOptions()
.position(currentLocation)
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_YELLOW))
.title("Current Position")
.snippet("Show the current position of user during travel"));
// For zooming automatically to the location of the marker
CameraPosition cameraPosition = new CameraPosition.Builder().target(currentLocation).zoom(11).build();
googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
if (travelInfo.getEndLat() != 0 && travelInfo.getEndLng() != 0) {
Log.i(TAG, "Dentro if userlocation");
//Get total distance of travel
getTotalDistanceTime(travelInfo);
//Get the percurred distance from last known coordinates
getPercurredDistance(travelInfo);
}
}
}
});
}
And getpercurredDistance use google direction API with this url
String url = "https://maps.googleapis.com/maps/api/directions/json?origin=" + obj.getLastLat() + "," + obj.getLastLng() + "&destination=" + obj.getCurrentLat() + "," + obj.getCurrentLng() + "&mode=driving&key=AI*******************I";
For getting distance traveled from last marker and current position marker.
But some times it put the localized position too far from real position... how avoid this?
EDIT
The method that i use is good for my work? mFusedLocationClient.getLastLocation()
There is no possibilities to get more precise GPS position from GPS sensor of android device, but you can filter "wrong" points this ways:
1) for each travel point get several values of Lat and Lng, discard the minimum and maximum and average the remaining ones;
2) log timestamps of each travel point and compare it for two neighboring points: speed (distance between that two points divided by time interval of that points) grater than max. car speed (e.g. 250 km/h) that means the second point is wrong (if first point is good of course).
3) use Google Maps Roads API to find best-fit road geometry for a given set of GPS coordinates.
Also take a look at this question.
For real-time location purposes, location updates allow you to request location updates at different intervals. See the android developer location documentation. The function you want is FusedLocationProviderClient.requestLocationUpdates.
There is no reason GPS (or any other widely used location technology) should ever give you 10km error for a single location estimate. If you are getting this, there is a large mismatch between when you the location estimate was made and when you are requesting it.

CameraUpdateFactory zoom not working

private void setCurrentLocationMarker(LatLng currLatLng) {
if (currLatLng == null) {
Toast.makeText(getActivity(), "Check Internet Connection", Toast.LENGTH_SHORT).show();
return;
}
mMap.moveCamera(CameraUpdateFactory.newLatLng(currLatLng));
mMap.animateCamera(CameraUpdateFactory.zoomBy(R.integer.camera_zoom_value));
setMarkerAtCentre(mMap.getCameraPosition().target);
}
private void setMarkerAtCentre(LatLng cameraCentre){
if(cameraCentre == null) return;
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(cameraCentre);
markerOptions.icon(BitmapDescriptorFactory.defaultMarker());
markerOptions.title("Your Position");
sourceMarker = mMap.addMarker(markerOptions);
}
Above two are the functions for setting the marker to desired latitude and longitude and placing the camera focusing the location.
Everything is working fine.
But the problem which I face here is with the zoom
mMap.animateCamera(CameraUpdateFactory.zoomBy(R.integer.camera_zoom_value));
I may place any value for the zoom.
<integer name="camera_zoom_value">2</integer>
But the zoom which I get always is the maximum one i.e. the largest possible zoom, which is kinda issue here.
Can anyone help me out? Thanks in advance.
your are passing the resource integer directly without using getResources().getInteger() so the value passed is some long integer > 20 leading to maximum zoom. Fix that and it will work.

Route Polyline in osmbonus pack is invisible

Trying to routing via osmbonus pack here, everything works almost fine, but there is a problem which is I can not see the polyline drawn as the route on my map.
I'm sure it has een generated since (mRoadOverlay.getNumberOfPoints()) returns 41
And I'm following the OSMNavigator Sample https://code.google.com/p/osmbonuspack/source/browse/trunk/OSMNavigator/src/com/osmnavigator/MapActivity.java
void updateUIWithRoad(Road road){
mRoadNodeMarkers.getItems().clear();
TextView textView = (TextView)findViewById(R.id.tv_main_routeInfo);
textView.setText("");
List<Overlay> mapOverlays = map.getOverlays();
if (mRoadOverlay != null){
Log.d("Polylinenopoin",String.valueOf(mRoadOverlay.getNumberOfPoints()));
mapOverlays.remove(mRoadOverlay);
mRoadOverlay = null;
Log.d("POLYLINE","1");
}
if (road == null){
return;}
if (road.mStatus == Road.STATUS_TECHNICAL_ISSUE)
Toast.makeText(map.getContext(), "Technical issue when getting the route", Toast.LENGTH_SHORT).show();
else if (road.mStatus > Road.STATUS_TECHNICAL_ISSUE) //functional issues
Toast.makeText(map.getContext(), "No possible route here", Toast.LENGTH_SHORT).show();
mRoadOverlay = RoadManager.buildRoadOverlay(road, Main.this);
String routeDesc = road.getLengthDurationText(-1);
mRoadOverlay.setTitle("route" + " - " + routeDesc);
String det = String.valueOf(mRoadOverlay.getColor()) + "- " + String.valueOf(mRoadOverlay.getWidth());
Log.d("Polydet",det);
map.getOverlays().add(mRoadOverlay);
//we insert the road overlay at the "bottom", just above the MapEventsOverlay,
//to avoid covering the other overlays.
putRoadNodes(road);
map.invalidate();
//Set route info in the text view:
textView.setText(routeDesc);
}
Also I need to say that I can see road Nodes on the map, so it's safe to say that road is correct
For future reference:
I found the reason,
the reason was that to enlarge the font size of the maps I used this custom XYTileSource:
map.setTileSource(new XYTileSource("Google Terrian",
ResourceProxy.string.bing, 10, 17, 512, ".jpg", new String[] {
"http://otile1.mqcdn.com/tiles/1.0.0/map/",
"http://otile2.mqcdn.com/tiles/1.0.0/map/",
"http://otile3.mqcdn.com/tiles/1.0.0/map/",
"http://otile4.mqcdn.com/tiles/1.0.0/map/"}));
in which I had changed the aTileSizePixel to 512, while my tiles were in fact 256x256 ... this helped me to show tiles bigger and hence bigger font size, the latitude and longitude were alright as I could set points by myself on the map in exact location, also the route Nodes were alright, the route Polyline itself however was not, I guess it had been loaded in a different location which was not in the view I had checked.

Adding 500+ markers gets the app hanging

I am developing an app of places/shops and have 500+ markers from DB on a Google Map.
I add 2 kinds of places and one kind of shop like this:
Cursor places = db.getAllPlaces();
this.placeMarker0 = new ItemizedMarker(this.getResources().getDrawable(
R.drawable.place0), this);
this.placeMarker1 = new ItemizedMarker(this.getResources().getDrawable(
R.drawable.place1), this);
this.shopMarker = new ItemizedMarker(this.getResources().getDrawable(
R.drawable.shop), this);
if (places.moveToFirst()) {
do {
String[] coordinates = places.getString(11).split(",");
if (coordinates.length > 0) {
GeoPoint point = new GeoPoint((int) (Double
.parseDouble(coordinates[0]) * 1E6), (int) (Double
.parseDouble(coordinates[1]) * 1E6));
OverlayItem overlayitem = new OverlayItem(point, places
.getString(1), Integer.toString(places.getInt(0)));
if (places.getInt(12) == 0) {
placeMarker0.addMarker(overlayitem);
} else if (places.getInt(12) == 1) {
placeMarker1.addMarker(overlayitem);
} else {
shopMarker.addMarker(overlayitem);
}
} else {
Log.w("GMaps", "Place not located - " + places.getString(1));
}
} while (places.moveToNext());
this.placeMarker0.populateNow();
this.mapOverlays.add(placeMarker0);
this.placeMarker1.populateNow();
this.mapOverlays.add(placeMarker1);
this.shopMarker.populateNow();
}
This works just fine, and all markers are shown (not shops by default though:-) )
The problem is, that after adding all the markers, the app often freezes and you can't click any of the markers or pinch-zoom / move the map for a while. I have ZoomControls added, and they work, but not interacting with the map directly.
Is Android Google Map API just not good at handling many markers, or should I use another approach?
If you need more details, please let me now!
Have you considered using Marker Clusters instead of creating 500+ individual markers? In addition to the performance overhead, your current setup could overwhelm the user.

Categories

Resources