3

I'm programming an app for android, in which i have to represent certain block facades. For this purpose, i draw a polyline for each relevant block facade (very simple polylines, 2 point lines actually...).

Worst case scenario, there could be up to 100k polylines.

My problem is that i'm running tests with about 3000 lines and it's already really slow. I have no problem with getting the data to show, and no problem drawing the lines, but the map becomes laggy when more than 1k lines are drawn.

I create a PolylineOptions for each polyline, and then draw it and store it in a variable like this:

PolylineOptions options = new PolylineOptions();
options.add(latlon1, latlon2);
options.width(5);
options.color(Color.RED);
Polyline myPolyline = map.addPolyline(options);
myPolyline.setVisible(true);
myPolylines.add(myPolyline);

I tried not storing them (though this is necessary for later processing) in case it was a memory issue, but didn't make a difference... Also, when storing them, the rest of the app works fluently (some extra buttons/text fields), it's just the map that becomes slow (when moving/zooming).

Oh, and I'm using mapfragment class of google maps api v2.

So, the question being: Is there any way to improve the map performance?

Thanks in advance.

EDIT: The polylines are all visible at once. To avoid drawing unnecessary lines I added a "previous step" in which i draw circles that represent the amount of facades with the fill color (green to red) and only show the lines when a circle is selected and the user asks for the detailed view.

EDIT2: Here's what i'm trying to accomplish (it's from a mapguide map, but i'm trying to replicate on android) facades

Community
  • 1
  • 1
Chess
  • 33
  • 1
  • 5
  • Are all polylines visible at once? If not, just draw the lines that are visible in the map area. – Steve Benett May 29 '14 at 15:23
  • Yes, the polylines are all visible at once. I even added a "previous step" in which i draw circles that represent the amount of facades with the fill color (green to red) and only show the lines when a circle is selected and the user asks for the detailed view. – Chess May 29 '14 at 15:57
  • You need to do clustering – Muhammad Babar Nov 13 '14 at 08:12

3 Answers3

2

If the polylines are not changing, I would draw them on background tiles rather than using the maps polyline feature.

For an example how to draw shapes based on LatLng-Coordinates into tiles, see my answer to this SO question

Community
  • 1
  • 1
user2808624
  • 2,502
  • 14
  • 28
  • Thanks so much, it's a fair bit more efficient. I'm having some errors with memory filling up, and randomly, the tiles don't show up, but it might be my coding since i just now tried it, i'll test it up some more. Thanks again. – Chess Jun 02 '14 at 14:25
  • You have to be very careful about thread safety. The tiles are created in many threads simultaneously. That some tiles do not show up, may point into this direction. – user2808624 Jun 02 '14 at 15:19
  • I was creating too many providers at first (because of the data being divided by groups, i created a provider for each group) i now went back to drawing everything at once on the tiles and so far no problems and it's very smooth. I'm now gonna rewrite my ontap, since the overlay seems to be blocking it. Thanks again, your code was most helpful. – Chess Jun 02 '14 at 15:30
  • Nevermind the ontap, it was a version mix up on my side, it doesn't block the touch event. Thank you. – Chess Jun 02 '14 at 15:47
  • @Chess Can you please share the result code if possible ? – Hady Hallak Oct 07 '16 at 15:49
1

instead of plotting everything all at once only plot what you can see.

so you would get the bounds of the visable map

VisibleRegion vr = map.getProjection().getVisibleRegion();
bounds = vr.latLngBounds;

then check if you line is inside the bounds of the visible region is it is then plot the line

doing this will allow you to run your code in an asynctask where you can use a handler to call back to the main thread to plot a line when you need to.

you also have to override onCameraChange to know when the VisibleRegion changes ie. you mode the map. there are also other things you will have to do with this method like keeping track of if a line was plotted or not so you dont plot the same line many times

tyczj
  • 71,600
  • 54
  • 194
  • 296
  • Sadly the lines are all visible at once. On my test data set, the smallest relevant portion of lines are about 500, and this could be around 10,000 on the worst case. Also, there could be reasons to analyse bigger areas, thus needing around 3000 lines on my test set and around 50,000 on a crisis... – Chess May 29 '14 at 16:05
  • yeah so that is why you see the lag, only plot what is visible at that point in time and it will be faster – tyczj May 29 '14 at 16:13
  • But they are all visible, and need to be... Even drawing very (relatively) small portions of the data set (1000 out of 7000) it lags. Thanks for the replies! – Chess May 29 '14 at 16:16
  • no they dont need to be, when the map moves you check to see if there are any new lines that you need to plot. you 100% dont need to plot every single line – tyczj May 29 '14 at 16:17
  • I'm not drawing every line... As i edited on my post, i divided the data set into several groups that should be looked at one time. Problem is, this is in some cases more than 1000 lines, and it could be up to 10,000. With the 1000 lines it lags, and i'm not even close to filling the whole screen... – Chess May 29 '14 at 16:27
  • when I say everything I mean everything in that "group" you do not need to plot every single element in that group and that is the only want to make it faster. how do you thing google maps works? open the app and take a look at how they load stuff and you will see it is exactly how I am describing – tyczj May 29 '14 at 17:02
  • I get what you're saying, but i'm not drawing any lines outside the visible area. I did implement something similar before the group workaround, but for the practical use of the application, 1000+ is the usual number of lines. Let's say you're looking at 20 blocks by 20 blocks, that's 1600 facades. Let's say i have to draw lines on 1000 of those, it lags... and i have to see those 20x20 for it to mean anything... And 20x20 is a pretty close zoom... 40x40 would mean 6400 facades... – Chess May 29 '14 at 17:49
  • I guess I dont know what you mean by "block facades" or what you are trying to accomplish. maybe `Ground Overlay` or `Tile Overlay` might be of use to you but as it stands since plotting has to be done on the UI thread there is nothing you can do – tyczj May 29 '14 at 18:00
  • A block facade is one of the four sides of a block (maybe it's not the correct term, english is not my native tongue). The problem isn't really the ui thread collapsing, but the map itself, since the rest of the app works great, it's just the map that stutters. – Chess May 29 '14 at 18:16
  • show an image of that it looks like to maybe get a better idea – tyczj May 29 '14 at 18:18
  • 1
    ok so looking at the image I see what you mean and really your best bet is going to be turning that into an image and using a `Tile Overlay` because there will be no way of preventing the UI lag. https://developers.google.com/maps/documentation/android/tileoverlay maybe try starting out at a higher zoom level and not showing everything at once and show more when the user zooms in – tyczj Jun 02 '14 at 14:17
0

I'm using a asynctask to create polylineoption with 50 coordinates each and in publishprogress method I add the polyline to the map. If it does run too long and it doesn't I still have a progressdialog that the user can cancel the task with the back button. My largest test was for 3000 coordinates from sqllite database. However with two coordinates per polylineoption this code would get bogged down calling publishprogress so you'd have to stack up a couple dozen of the two coordinate polylines then add them to the map in the publishprogress method. PublishProgress takes an array so it wouldn't be difficult to pass in 50 polylineoptions. In debug with the 50 coordinates per polyline you can see the polylines being drawn on the map. In live mode the polylines just show up in about a second on my cheap $50 test phone.

Note: I do all my asynctasks as seperate .java files so I can pass in whatever I need in a constructor and so I have the same cancelMe() code. Most of the code I use is below I've omitted the database stuff because it will different for everyone.

public class TaskParseKmldb extends AsyncTask<KmlSummary, PolylineOptions, Boolean>
        implements OnCancelListener {

public interface TaskParseKmldbCallback {
    void onTaskParseKmldbComplete(boolean success);
}


public TaskParseKmldb(Activity activity, KmlSummary kmlSummary, GoogleMap mMap,
            ProgressDialog pd) {
    this.activity = activity;
    this.kmlSummary = kmlSummary;
    this.mMap = mMap;
    this.pd = pd;
    // determine if calling activity has implemented the callback interface
    callback = (TaskParseKmldbCallback.class.isAssignableFrom(activity
.getClass())) ? (TaskParseKmldbCallback) activity : null;   
    }

... setup a process dialog listener for the onCanceled routine.

@Override
    protected void onPreExecute() {
        pd.setOnCancelListener(this);
        pd.setMessage("Loading Recorded Route...");
        pd.show();
    }


@Override
protected void onCancelled(Boolean result) {
    if (pd != null) {
        pd.dismiss();
        pd = null;
    }
    if (callback != null) {
        callback.onTaskParseKmldbComplete(result);
    }
}

@Override
    public void onCancel(DialogInterface dialog) {
        cancelMe(); // this just gracefully cancels the task.
    }

public void cancelMe() {
    if (!getStatus().equals(Status.FINISHED)) {
        // not canceled
        if (!isCancelled()) {
            cancel(false);
            try {
                get();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (CancellationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ExecutionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

... somewhere in doInBackground

if (progress.getPoints().size() > 0) {
                publishProgress(progress);
                ret = true;
            }


protected void onProgressUpdate(PolylineOptions... progress) {
        if (isCancelled()) {
            return;
        }
        if (progress[0] != null) {
            // add the poly line to the map
            // save the polyline returned by the map for later use
            polylines.add(mMap.addPolyline(progress[0]));
        }
    }

...

@Override
protected void onPostExecute(Boolean result) {

    if (pd != null) {
        pd.dismiss();
        pd = null;
    }
danny117
  • 5,581
  • 1
  • 26
  • 35
  • Thanks, but my problem isn't loading the lines, it's the performance after the lines are on the map. The tile overlay seem to be a way to solve it. – Chess Jun 02 '14 at 14:28
  • I have no issues with performance of the map with 3000++ coordinates in polylines of 50 segments each. 600 polylines no issues in performance: draws in a second and map movement is still smooth and fluid. – danny117 Jun 05 '14 at 15:09
  • I think it's the amount of polylines instead of the amount of coords. In my app, i had 1000+ polylines at any given time. Anyways, the tile overlay worked like a charm. Thanks for the input. – Chess Jun 05 '14 at 15:47