4

So I've spent a good amount of time attempting to figure out how to speed this up, but I'm out of ideas now. I have a class, mapPopup in which a MapView is displayed over the entire screen. There is an array of an array of GeoPoint in mapPopup, and I want to draw lines between each GeoPoint in the 2nd dimensions of the array. I've accomplished this task already using a custom class, mapOverlay, that extends Overlay, but the problem I am having is that once all the map overlays are drawn, the map is extremely slow to zoom or pan. Once all the overlays are added to the map there are often over 2000, but they are all very small.

Thinking that the map would work more quickly if there were less overlays, I drew all the lines to three seperate overlays instead of a seperate overlay for each line. This actually resulted in SLOWER panning and zooming of the map, so I reverted back to the many small overlays.

I would appreciate some informative and easy to understand descriptions of a method that I could employ to make the map faster. Pseudocode or real code for the potential method would also help me to understand it better. My code is posted below. Once more, please note that my overlays and map display properly; I would just like a method that will allow faster panning and zooming.

mapOverlay class

public class mapOverlay extends Overlay {

private Projection projection;
private GeoPoint gp1;
private GeoPoint gp2;
private int color;

public mapOverlay(int color, MapView map, GeoPoint geo1, GeoPoint geo2) {
    // super();
    this.projection = map.getProjection();
    this.gp1 = geo1;
    this.gp2 = geo2;
    this.color = color;
}

public void draw(Canvas canvas, MapView mapv, boolean shadow) {
    super.draw(canvas, mapv, false);

    Paint mPaint = new Paint();
    mPaint.setDither(true);
    mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setStrokeWidth(4);
    mPaint.setColor(this.color);

    Point p1 = new Point();
    Point p2 = new Point();
    Path path1 = new Path();

    projection.toPixels(gp1, p1);
    projection.toPixels(gp2, p2);
    path1.moveTo(p1.x, p1.y);
    path1.lineTo(p2.x, p2.y);
    canvas.drawPath(path1, mPaint);

}

}

mapPopup class

public class mapPopup extends MapActivity {

public String[] trailNames;
public String tableName = "";
public int numTrails = 0;
public static GeoPoint[][] geoPoints;
public int[] colors = new int[] { Color.WHITE, Color.BLUE, Color.CYAN,
        Color.RED, Color.YELLOW, Color.MAGENTA, Color.GRAY, Color.LTGRAY };
public int[] newColors;
// public Bitmap b;
public GeoPoint firstP;

public void loadMapData(Bitmap b, MapView map, int[] colors,
        GeoPoint[][] GPAA, int ZoomLevel) {
    // GPAA holds an array of an array of GeoPoint
    Log.i("DEBUG", "starting loadMapDataTask");

    map.setSatellite(true);
    MapController mc = map.getController();

    mapOverlay[][] mapOverlay = new mapOverlay[GPAA.length][];
    Log.i("DEBUG", "length of GPAA is: " + GPAA.length);

    // i cycles through the first dimension of GPAA
    for (int i = 0; i < GPAA.length; i++) {
        GeoPoint[] geoPoints = GPAA[i];
        int length = geoPoints.length - 1;
        mapOverlay[i] = new mapOverlay[length]; //
        int pointCount = 0;
        // z cycles through the second dimension of GPAA
        for (int z = 0; z < length; z++) {
            mapOverlay[i][z] = new mapOverlay(colors[i], map,
                    geoPoints[pointCount], geoPoints[pointCount + 1]);
            pointCount++;
        }
    }

    // Actually adds overlays to map
    List<Overlay> mapOverlays = map.getOverlays();
    for (int i = 0; i < mapOverlay.length; i++) {
        int length = mapOverlay[i].length;
        Log.i("DEBUG", "Adding map overlays for trail: " + i);
        Log.i("DEBUG", "Length of mapOverlay[i] is: " + length);
        for (int z = 0; z < length; z++) {
            mapOverlays.add(mapOverlay[i][z]);
        }
    }

    mc.animateTo(GPAA[0][0]);
    mc.setZoom(ZoomLevel);
    Rect r = new Rect();
    map.getDrawingRect(r);
    map.invalidate(r);
}

public static class runBGLoad extends
        AsyncTask<bgLoadParam, Integer, GeoPoint[][]> {

    public GeoPoint[][] geoPoints;

    protected GeoPoint[] getGPa(Context context, String name, int ID) {
        File file = context.getFileStreamPath(name);
        if (file.exists() == false) {
            Log.i("DEBUG", "Creating file");
            InputStream is;
            FileOutputStream fos;

            try {
                Log.i("DEBUG", "id is " + ID);
                is = context.getResources().openRawResource(ID);
                byte[] buffer = new byte[is.available()];
                is.read(buffer);
                fos = context.openFileOutput(name, Context.MODE_PRIVATE);
                fos.write(buffer);
                fos.close();
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        } else {
            Log.i("DEBUG", "File already exists");
        }

        // Log.i("DEBUG", "starting to get geopoints");

        List<Location> gpsPoints = XMLParser.getPoints(file);

        int i = 0;
        int index = 0;

        GeoPoint[] geoPoints = new GeoPoint[gpsPoints.size()];

        // makes list of gpsPoints into GeoPoint[]
        ListIterator<Location> it = gpsPoints.listIterator();

        while (it.hasNext()) {
            index = it.nextIndex();
            Location loc = gpsPoints.get(index);
            geoPoints[i] = new GeoPoint((int) (loc.getLatitude() * 1E6),
                    (int) (loc.getLongitude() * 1E6));
            it.next();
            i++;
        }

        return geoPoints;
    }

    @Override
    protected GeoPoint[][] doInBackground(bgLoadParam... params) {

        Context context = params[0].getContext();
        int tNLength = params[0].getTNames().length;
        geoPoints = new GeoPoint[tNLength][];

        for (int i = 0; i < params[0].getTNames().length; i++) {
            String modName = params[0].getTNames()[i].toLowerCase()
                    .replace(' ', '_');
            int identifier = context.getResources().getIdentifier(modName,
                    "raw", context.getPackageName());
            geoPoints[i] = getGPa(params[0].getContext(), modName
                    + "_mapfile", identifier);
        }
        Log.i("DEBUG", "TEST");
        mapPopup.geoPoints = geoPoints;
        Log.i("DEBUG", "TEST2");
        return geoPoints;
    }

    @Override
    protected void onPostExecute(GeoPoint[][] result) {
        Log.i("DEBUG", "The points are loaded.");
        mapPopup.geoPoints = result;
    }
}

@Override
public void onCreate(Bundle savedInstanceState) {
    Intent intent = getIntent();
    String[] extras = intent.getStringArrayExtra("strings");

    tableName = extras[1];
    numTrails = Integer.parseInt(extras[2]);
    trailNames = intent.getStringArrayExtra("trailNamesA");

    super.onCreate(savedInstanceState);
    setContentView(R.layout.map_popup_layout);

    newColors = new int[numTrails];
    for (int i = 0; i < numTrails; i++) {
        newColors[i] = colors[i];
    }
    ViewGroup layout = (ViewGroup) findViewById(R.id.map_popup);
    TextView[] tVs = new TextView[numTrails];

    for (int i = 0; i < numTrails; i++) {
        LayoutParams params = new RelativeLayout.LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        tVs[i] = new TextView(getApplicationContext());
        tVs[i].setText(trailNames[i]);
        tVs[i].setId(i + 700);
        tVs[i].setTextColor(colors[i]);
        tVs[i].setBackgroundColor(Color.BLACK);

        if (i > 0) {
            params.addRule(RelativeLayout.BELOW, (699 + i));
        }

        layout.addView(tVs[i], params);
    }

}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);

    MapView map = (MapView) findViewById(R.id.popupMV);
    Bitmap b = Bitmap.createBitmap(map.getWidth(), map.getHeight(),
            Bitmap.Config.RGB_565);

    try {
        trailsActivity.mapPreLoad.get();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

    loadMapData(b, map, newColors, geoPoints, 17);
}

@Override
protected boolean isRouteDisplayed() {
    // TODO Auto-generated method stub
    return false;
}

}    
Brian Ecker
  • 2,017
  • 1
  • 21
  • 30

1 Answers1

2

I'm currently facing the same problematic, and I just found a workaround : prevent the overlays from drawing when zooming or panning. That's not perfect and I'm still looking for a better solution, but at least the map is usable without waiting 5 seconds for each pan or zoom.

Here is the code I used in my Overlay extension. It's not Java but C# (using Monodroid) - but it should be easily understandable.

public override bool OnTouchEvent (MotionEvent e, Android.GoogleMaps.MapView mapView)
{
    if (e.Action == MotionEventActions.Down)
        _mustDraw = false;
    else if (e.Action == MotionEventActions.Up)
        _mustDraw = true;
    return base.OnTouchEvent (e, mapView);
}

public override void Draw (Android.Graphics.Canvas canvas, Android.GoogleMaps.MapView mapView, bool shadow)
{
    if (shadow || !_mustDraw)
        return;
    // ...
}

That solution works for every map-touch based action, and now performs with a good speed, I just lack implementation of the same behaviour when zooming in or out using built-in zoom controls, but I first need to fight some of my bugs before doing this part, I'll come back on this part later.

psycho
  • 631
  • 10
  • 27
  • 3
    This helped me quite a bit. I replaced MotionEvent.ACTION_DOWN with just MotionEvent.ACTION_MOVE though and used an else block with mustDraw = true. Works like a charm and makes panning more comfortable in my app. – gelupa Oct 01 '12 at 20:27
  • Can this same line of thinking be applied somehow to ItemizedOverlay as well? – topwik Oct 02 '12 at 20:34
  • @towpse : never used this one, but since it extends `overlay` I can't see why it wouldn't be right. – psycho Oct 02 '12 at 20:59