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.
Related
Edit: Show what the mapManager.filter() method does.
I have an Android app with ~20k markers being draw on the map and with filters option so the markers are being drawn and remove a lot.
I'm currently using this clustering library because it's way more efficient for displaying a large amount of markers than the classic google map library. But it's not supported and hasn't been updated since 3 years and i haven't found any alternatives.
Here is a video of the bug.
Here is my code that is call when i click on an filtering option in my app:
private void filter(){
//currentMapManager contain all the filters option and also the array list
//of the unfiltered markers (~20,000 markers)
//it's filter method retrieve the current filtering option enable (marker type, and other specs)
//And return markers from the full arraylist that match those filters options.
ArrayList<MarkerModel> filteredList = currentMapManager.filter(currentMapManager.getAllMarkers());
if (clusterManager != null) {
clusterManager.setItems(new ArrayList<>());
clusterManager.onCameraIdle();
clusterManager.setItems(filteredList);
clusterManager.onCameraIdle();
}
Here the mapManager.filter() method.
public ArrayList<MarkerModel> filter(List<MarkerModel> currentMarkers) {
ArrayList<Object> filtersArray = filters.getFilters();
/* filtersArray is an array like this :
* ['type', ['typeFilter1', 'typeFilter2'],
* 'date', dateTimestamp,
* 'withPictures', true,
* ....]
*/
return filter(currentMarkers, filtersArray);
}
private ArrayList<MarkerModel> filter(List<MarkerModel> currentMarkers,
ArrayList<Object> fieldsAndValues) {
if (fieldsAndValues.size() % 2 == 1) {
throw new IllegalArgumentException("Missing value in call to filterList().
There must be an even number of arguments that alternate between
field names and values");
} else {
//Here the arraylist that we are returning
ArrayList<MarkerModel> filteredMarkers = new ArrayList<>();
List<Object> argumentList = new ArrayList();
Collections.addAll(argumentList, fieldsAndValues);
if (!currentMarkers.isEmpty()) {
for (int j = 0; j < currentMarkers.size(); j++) {
MarkerModel marker = currentMarkers.get(j);
boolean isInFilter = true;
//Check fieldsAndValue filters...
//If isInFilter stay to true we
filteredMarkers.add(marker);
}
}
return filteredMarkers;
}
}
I think the problem is that the cluster library is not supposed to have so much redrawn and it broke at some point from reading and working on 20k items arraylist.
I have look through all the forks branch of this library and look/try to understand the library source code but hasn't find any solution at my problem...
If you need other part of my source code to understand more tell me i will add it.
Thanks a lot if you can help me i have been struggling a lot with this issue...
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
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.
Until now, I always draw "points" in the MapActivities with OverlayItems and with a class Point made by me. With this method I can draw some "points" in the MapActivity. But how I can draw "clickable" points ??
I improvise this class Point, and follow a tutorial about the OverlayItems method, but I can implement any method that yourselves explain me for this "clickable points".
Thanks.
Have a look at this project as example how to implement clickable OverlayItems https://github.com/jgilfelt/android-mapviewballoons
I couldn't find a way to do this so I wrote my own click logic. This is from a project I've put on ice for now and is work in progress (hard coded values for example) but the logic works. Hope it helps:
#Override
public boolean onTap(GeoPoint geoPoint, MapView mapView){
if (!isRoute){ // nothing to do if it's a route
Cursor cursor = (Cursor) mapView.getTag();
Projection projection = mapView.getProjection();
// get pixels for the point clicked
Point clickedPoint = new Point();
projection.toPixels(geoPoint, clickedPoint);
if (cursor.moveToFirst()){
do {
try {
Double lat = cursor.getFloat(Database.LAT_COLUMN) * 1E6;
Double lng = cursor.getFloat(Database.LONG_COLUMN) * 1E6;
GeoPoint thisGeoPoint = new GeoPoint(lat.intValue(),
lng.intValue());
// get pixels for this point
Point overlayPoint = new Point();
projection.toPixels(thisGeoPoint,overlayPoint);
// did the user click within 30 pixels?
if ((Math.abs(clickedPoint.x - overlayPoint.x) < 30) && (Math.abs(clickedPoint.y - overlayPoint.y) < 30)){
// get a cursor to this record
Cursor thisCursor = TestApp.db.rawQuery("SELECT * FROM " + Database.DATABASE_TABLE_PINS + " WHERE CID='" + cursor.getString(Database.ID_COLUMN) + "'", null);
thisCursor.moveToFirst();
// create and show an instance of the PinDetailsDialog
PinDetailsDialog customiseDialog ;
// TODO this is a kludge, why does this throw an exception sometimes?
try{
customiseDialog = new PinDetailsDialog(mapView, context,thisCursor,context.getResources().getConfiguration().orientation);
customiseDialog.show();
} catch (Exception e){
customiseDialog = new PinDetailsDialog(mapView, mapView.getContext(),thisCursor,context.getResources().getConfiguration().orientation);
customiseDialog.show();
}
return true;
}
} catch (Exception e){
e.printStackTrace();
}
} while(cursor.moveToNext());
}
}
return true;
}
The basic idea is to get the point where the user clicked on the map, convert the point into lat long then iterate my data points looking for a match. Note that I am using a hit test of +/- 30 pixels (which I will not hard code when I pick this up again.
I left the TODO in there just in case you stumble into something similar but I do suspect it's entirely down to a problem somewhere in my PinDetailsDialog class implementation.
I use multiple map views, each of which uses data stored in a SQLite table. I store a reference to a cursor to read the data in the tag property of the Mapview, hence the .getTag() call.
I'm fetching all the 's stores in "Göteborg" and adding a marker on my mapView.
The problem is that with this code my markers get all bunched up in Africa.
I've checked that coordinates is correct so it isnt that.
Anyone know what's the problem is?
df = ((ClassHandler)getApplication()).getStoreDatabadeFacade();
Cursor plotstore = df.getAllStorePos("Göteborg");
startManagingCursor(plotstore);
plotstore.moveToFirst();
while(plotstore.isAfterLast() == false){
GeoPoint addStore = new GeoPoint(plotstore.getColumnIndex("lat"), plotstore.getColumnIndex("long"));
//OverlayItem overlayitem = new OverlayItem(addStore, plotstore.getString(plotstore.getColumnIndex("_ID")), plotstore.getString(plotstore.getColumnIndex("ADDRESS")));
OverlayItem overlayitem = new OverlayItem(addStore, plotstore.getString(plotstore.getColumnIndex("_id")), plotstore.getString(plotstore.getColumnIndex("address")));
itemizedStoreOverlay.addOverlay(overlayitem);
storeOverlays.add(itemizedStoreOverlay);
plotstore.moveToNext();
}
Doesn't getColumnIndex just return the index of the column within the cursor, rather than the value at that index? You seem to be using it correctly for the id:
plotstore.getString(plotstore.getColumnIndex("_id"))
but not for the lat and long:
plotstore.getColumnIndex("lat")
Try changing this (and the "long") to:
plotstore.getInt(plotstore.getColumnIndex("lat"))