Existe-t-il un code pour le clustering de points dans Android? Comment puis-je charger des milliers de points sans avoir de problèmes de performances?
Réponses
Trop de publicités?Hier soir je suis en PointClustering sur Android MapView. Vu qu'il n'y avait rien pour la communauté, alors que je voudrais partager.
Les groupes de la geopoints si la projection de dans la mapView est trop proche. Rend également visibles seulement poins.
Mise à JOUR
Code retravaillé à partir de scrach.
Maintenant disponible sur GitHub
- Code Retravaillé à partir de zéro
- Utilisé GVM algorithme de clustering (assez rapide mais n'a pas de position cluster point aussi bonne que la mienne)
- Bientôt ajouter précédent algorithme de clustering trop
J'ai retravaillé le code ci-dessus et contrôlé l'absence de l'icône de recouvrement de la vue de la carte et séparés du groupe et un seul point.
Mon Code:
MMapView.java
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.impiger.maphighlight.R;
//Reference - http://stackoverflow.com/questions/7447350/android-maps-point-clustering
public class MMapView extends MapView {
private static final String TAG = MMapView.class.getSimpleName();
private static final int MAX_VISIBLE_POINTS = 1;
private PMapViewOverlay itemizedOverlay;
private List<Overlay> mapOverlays;
private List<GeoPoint> geoPoints = new ArrayList<GeoPoint>();
private BitmapDrawable drawable;
private Context context;
private Drawable emptyDrawable;
private int count;
private int oldZoomLevel = -1;
ArrayList<OverlayItemExtended> mOverlays;
public MMapView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
mapOverlays = getOverlays();
drawable = new BitmapDrawable(
BitmapFactory.decodeResource(getResources(),
R.drawable.blue_65));
itemizedOverlay = new PMapViewOverlay(drawable, context);
emptyDrawable = context.getResources().getDrawable(
R.drawable.marker);
mOverlays = new ArrayList<OverlayItemExtended>();
init();
}
private GeoPoint getPoint(double lat, double lon) {
return (new GeoPoint((int) (lat * 1000000.0), (int) (lon * 1000000.0)));
}
private void init(){
putPoint(11, 77, true);
putPoint(11.5, 76.6, false);
putPoint(10.98383, 77.32112, false);
putPoint(10, 77, false);
putPoint(11, 78, false);
putPoint(11, 77.5, false);
putPoint(10.5, 77, false);
putPoint(12, 77, false);
putPoint(11.77, 77.11, false);
putPoint(12.1, 78.33, false);
putPoint(11.83, 77.293, false);
putPoint(11.12, 77, false);
putPoint(11.13, 77, false);
putPoint(11.14, 77, false);
putPoint(11.15, 77, false);
putPoint(11.12, 77.2, false);
putPoint(11.13, 77.34, false);
putPoint(11.14, 77.4, false);
putPoint(11.15, 77.1977, false);
putPoint(11.347373, 77.5627783, true);
putPoint(11.53454, 76.696645, false);
putPoint(10.19282, 77.847373, false);
putPoint(10.4728, 76.39388, false);
putPoint(11.4563, 78, false);
putPoint(11.73663, 77.5927, false);
putPoint(10.5674, 77.6762, false);
putPoint(12.02882, 77.672782, false);
putPoint(11.7767876, 77.1123423, false);
putPoint(12.18332, 78.33, false);
putPoint(11.8393883, 77.293938783, false);
putPoint(11.388323, 77.9478723, false);
putPoint(11.1345645, 77.97723, false);
putPoint(11.1423423, 77.73774, false);
putPoint(11.1552, 77.793783, false);
putPoint(11.127895434, 77.2944554, false);
putPoint(11.13232345, 77.342234, false);
putPoint(11.14456573, 77.4, false);
putPoint(11.159765, 77.1977, false);
}
public void putPoint(double lat, double lon, boolean isMyPosition) {
int latitude = (int) (lat * 1E6);
int longitude = (int) (lon * 1E6);
GeoPoint geo = new GeoPoint(latitude, longitude);
geo = getPoint(lat, lon);
/*
* Remove doubles
*/
Boolean alreadyExists = false;
for (GeoPoint item : geoPoints) {
if (item.getLatitudeE6() == geo.getLatitudeE6()
&& item.getLongitudeE6() == geo.getLongitudeE6()) {
alreadyExists = true;
}
}
if (!alreadyExists) {
geoPoints.add(geo);
}
}
/*
* Place the overlays
*/
public void placeOverlays() {
itemizedOverlay.removeAllOverlays();
getOverlays().clear();
mapOverlays.clear();
mOverlays.clear();
int i = 1;
for (GeoPoint item : geoPoints) {
OverlayItemExtended overlayitem = new OverlayItemExtended(item,
"title "+i, "snippet");
// Here is where the magic happens
addOverlayItemClustered(overlayitem, this,
geoPoints.size());
i++;
}
for(int j=0;j<mOverlays.size();j++){
OverlayItemExtended overlayItem = mOverlays.get(j);
if(overlayItem.isMaster){
if(overlayItem.slaves.size() > 0){
itemizedOverlay = new PMapViewOverlay(drawable, context);
itemizedOverlay.addOverlayItem(overlayItem);
}else{
itemizedOverlay = new PMapViewOverlay(emptyDrawable, context);
itemizedOverlay.addOverlayItem(overlayItem);
}
mapOverlays.add(itemizedOverlay);
}
}
}
/*
* Update the points at panned / zoom etc
*/
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (getZoomLevel() != oldZoomLevel) {
placeOverlays();
}
oldZoomLevel = getZoomLevel();
}
public void addOverlayItemClustered(OverlayItemExtended thisOverlay,
MapView mapView, int totalPoints) {
for (OverlayItemExtended otherOverlay : mOverlays) {
/*
* Thresshold for the clustering
*/
/*
* Zoom level >15 don't cluster If less than Max_Visible_points
* don't cluster
*/
if (mapView.getZoomLevel() >= 14
|| (MAX_VISIBLE_POINTS > totalPoints)
&& PointCluster.getOverLayItemDistance(thisOverlay,
otherOverlay, mapView) > 60) {
mOverlays.add(thisOverlay);
return;
}
if (PointCluster.getOverLayItemDistance(thisOverlay, otherOverlay,
mapView) < 90 && !thisOverlay.isClustered) {
// Here is where the clustering actually happens
if (otherOverlay.isMaster) {
thisOverlay.isMaster = false;
// otherOverlay.isMaster = false;
thisOverlay.isClustered = true;
otherOverlay.isClustered = true;
otherOverlay.slaves.push(thisOverlay);
thisOverlay.parent = otherOverlay;
} else if (PointCluster.getOverLayItemDistance(thisOverlay,
otherOverlay.parent, mapView) < 90
&& otherOverlay.isClustered) {
thisOverlay.isMaster = false;
thisOverlay.isClustered = true;
thisOverlay.parent = otherOverlay.parent;
otherOverlay.parent.slaves.push(thisOverlay);
}
}
}
mOverlays.add(thisOverlay);
}
}
OverlayItemExtended.java
import java.util.Stack;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.OverlayItem;
public class OverlayItemExtended extends OverlayItem {
public boolean isClustered = false;
public boolean isMaster = true;
public boolean isMe = false;
public OverlayItemExtended parent;
public Stack<OverlayItemExtended> slaves = new Stack<OverlayItemExtended>();
public OverlayItemExtended(GeoPoint point, String title, String snippet) {
super(point, title, snippet);
}
}
PMapViewOverlay.java
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.widget.Toast;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapView;
@SuppressWarnings("rawtypes")
public class PMapViewOverlay extends ItemizedOverlay {
private static final String TAG = PMapViewOverlay.class.getSimpleName();
private Context context;
private ArrayList<OverlayItemExtended> mOverlays;
public PMapViewOverlay(Drawable defaultMarker, Context context) {
super(boundCenterBottom(defaultMarker));
this.context = context;
mOverlays = new ArrayList<OverlayItemExtended>();
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(25);
paint.setAntiAlias(true);
paint.setStrokeWidth(5);
paint.setColor(Color.WHITE);
}
@Override
protected OverlayItemExtended createItem(int i) {
return mOverlays.get(i);
}
@Override
public int size() {
return mOverlays.size();
}
public void addOverlayItem(OverlayItemExtended overlay) {
mOverlays.add(overlay);
populate();
}
public void removeAllOverlays() {
mOverlays.clear();
populate();
}
public void removePointsButMe() {
for (int i = 0; i < mOverlays.size(); i++) {
OverlayItemExtended overlay = mOverlays.get(i);
if (overlay.isMe) {
mOverlays.clear();
addOverlayItem(overlay);
break;
}
}
populate();
}
Paint paint = new Paint();
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
super.draw(canvas, mapView, shadow);
// cycle through all overlays
for (int index = 0; index < mOverlays.size(); index++) {
OverlayItemExtended item = mOverlays.get(index);
// Converts lat/lng-Point to coordinates on the screen
GeoPoint point = item.getPoint();
Point ptScreenCoord = new Point();
mapView.getProjection().toPixels(point, ptScreenCoord);
if (item.isMaster) {
if (item.slaves.size() > 0) {
canvas.drawText(item.slaves.size() + 1 + "",
ptScreenCoord.x, ptScreenCoord.y - 13, paint);
}
}
}
}
@Override
protected boolean onTap(int index) {
OverlayItemExtended item = mOverlays.get(index);
if (item.isMaster) {
if (item.slaves.size() == 0) {
Toast.makeText(context, "You tapped item " + item.getTitle(),
Toast.LENGTH_LONG).show();
}
}
return super.onTap(index);
}
}
Je n'ai pas changé le code PointCluster.java.
J'espère que cela aidera quelqu'un.
Il y a un bon échantillon pour cela. Vérifiez-le ici: http://code.google.com/p/android-playground-erdao/source/browse/trunk/SampleClusterMap/?r=226
Il existe une demande d'extraction sur github pour la bibliothèque Polaris (https://github.com/cyrilmottier/Polaris) qui ajoute la mise en cluster:
Là aussi, cette réponse qui ne vous demande que de remplacer la méthode de dessin de votre superposition. Il divise votre mapView en sections, ce qui en fait un peu moins sophistiqué. Mais au moins cela a fonctionné.