cordova-plugin-googlemaps: Performance problems when adding many markers.

I’m proposing a patch to the PluginMarker.java file that will greatly improve the performance when adding hundreds of markers to the map.

I propose the creation of a createMarkers method (note the plural), which will take the same marker definition as does the currently available createMarker, except for the fact they’ll be in an array, therefore allowing the creation of multiple markers with a single over-the-bridge request. Naturally, the callback has to return an array of markers, instead of a single marker.

A private createMarker_ method is to be called by createMarkers. This does not break the current API, and the current createMarker method can be refactored to simply call the private version with its argument in an array.

I have already implemented marker caching and marker pre-loading which on top of that will drastically reduce memory consumption and prevent leaks.

This is where my understanding of this project ends. I don’t know what my constraints as a developer are:

  • Are APIs frozen?
  • Are they meant to mimic their respective native environments?
  • Or abstract them away? (This preferred, imho.)
  • Do iOS and Android APIs diverge today?

I’m seeing fantastic performance with these improvements, I’d really like to contribute them.

Thanks a lot for the help.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 165 (81 by maintainers)

Most upvoted comments

@cvaliere you should really leave this forum. I’m sick of your attitude. When this is a shitty plugin and has ton of problems for you (because it just don’t fit your needs), go ahead and develop your very own version. I don’t know who you think you are. When you want to build something commercial then go ahead, hire an iOS and Android developer, pay like 5000-8000 € per month and create your own, perfect, non-shitty plugin and be disrespectful with them. After that, create a Git, and share your perfect solution with everybody.

If not, shut the hell up. I really don’t know how you even dare making demands on an open source project.

Hi everyone. As I said before, today is the day. The next version (2.0-beta) is ready to go (however beta).

First of all, try the demo app. You can download from my dropbox. cordova-googlemaps-v2-demo_01.apk

Demo video of the above apk

https://www.youtube.com/watch?v=oZBkTIpjTqg

screen shot 2016-09-30 at 7 29 16 pm


There are several changes.

change 1 . multiple maps

As you may know, the next version supports multiple map in the same html. However one map takes large memory, so I recommend 2,3 maps at the one page. If you don’t use a map, remove it. That’s save the memory.

change 2. multiple pages

Version 1.x supports only single html file (typically index.html) The next version supports multiple HTML file (such as index.html, page1.html, page2.html …etc) You don’t need to cleanup the maps if you change html files. The plugin will do automatically.

However, changing tab page in the same html is a different. You need to still do map.setVisible(true/false) by yourself.

change 3. Recognize all html element

Version 1.x supports only under the map div. The next version recognize all HTML element of the page. It means you don’t need to execute map.setClickable(true/false) when HTML dialog is popped up on the map div.

Here is the tips If the plugin does not recognize your HTML elements on the map div, specify css

.dialog {
    position: fixed;
    z-index: 1000; //any value is fine
}

Since the html elements that have position:fixed takes a priority always in the maps plugin. (You don’t need to do this usually (I believe so))

change 4. save battery life

The problem of the version 1.x is the KeepWatching Timer. This timer watches HTML element positions under the map div periodically. However, if you don’t do anything on your app, still the timer is running. This is affect for battery life.

In the next version, the timer stops automatically if the user does not touch on the map anything for a while. If the user touch on the app again, the timer also starts. If no changes for a while, stop the timer again.

If you need to start the timer programmatically, you can do like this:

cordova.fireDocumentEvent('touch_start', {});

or

var event = document.createEvent('touch_start');
event.initEvent(eventName, false, false);
document.dispatchEvent(event);

change 5. performance improved

Another big problem of the version 1.x is that all (most) native code run on the UI thread (or run on the WebCore thread. The reason of this is the Google Maps native APIs require do it. However I tested so much time and rewrite all most whole code in both native and javascript, the most code run on the background thread.

And you are lucky. I work as Android developer usually. I work for one

If you don’t understand this talk , you can skip this. Just remember the performance is improved.

change 6. split the JS file

The version 1.x code of the Javascript is googlemaps-cdv-plugin.js The file includes 2873 lines. Wow, it’s too large. Not suite for maintenance…even for me.

I split the JS files for each classes (such as Marker.js, Circle.js, etc) https://github.com/mapsplugin/cordova-plugin-googlemaps/tree/multiple_maps/www

You can debug easily 😃

change 7. introduce BaseArrayClass

If you are familiar with the Google Maps Javascritp API v3, you probably know the MVCArray class The benefit of this class is you can monitor the events: insert_at, set_at, and remove_at. Using this class, your code would be simple.

change 8. property synchronizing

This is really useful. Most of getXXXX() methods return the values as normal javascript object.

For example, in version 1.x,

marker.getPosition(function(position) {
   // you have to wait the callback
});

in version 2.0-beta

var position = marker.getPosition();

You know what? Since the marker (and polyline,polygon…etc) extends the BaseClass (which is MVC class), you can monitor like this.

markers[0].on("position_changed", onPositionChanged);
markers[1].on("position_changed", onPositionChanged);
markers[2].on("position_changed", onPositionChanged);

function onPositionChanged() {
  var marker = this;
  var position = marker.getPosition();
}

However, map.getVisibleRegion() does not support this way. you still have to use callbak.

change 9. chain programming

In version 2.0, most of all setXXX() methods are able to chain.

marker.setPosition({"lat": ...., "lng": ....}).setTitle("Hello");

change 10. add more events, and renamed

Version 1.x events

event name Androide iOS
event name Android iOS
---|-
MAP_CLICK YES YES
MAP_LONG_CLICK YES YES
MY_LOCATION_CHANGE YES NO
MY_LOCATION_BUTTON_CLICK YES YES
INDOOR_BUILDING_FOCUSED YES YES
INDOOR_LEVEL_ACTIVATED YES YES
CAMERA_CHANGE YES YES
CAMERA_IDLE NO YES
MAP_READY YES YES
MAP_LOADED YES NO
MAP_WILL_MOVE NO YES
MAP_CLOSE YES YES
OVERLAY_CLICK YES YES
INFO_CLICK YES YES
MARKER_DRAG YES YES
MARKER_DRAG_START YES YES
MARKER_DRAG_END YES YES

Version 2.x events

event name Androide iOS arguments[0]
MAP_READY YES YES none
MAP_CLICK YES YES LatLng
MAP_LONG_CLICK YES YES LatLng
MY_LOCATION_BUTTON_CLICK YES YES none
INDOOR_BUILDING_FOCUSED YES YES none
INDOOR_LEVEL_ACTIVATED YES YES building information
CAMERA_MOVE_START YES YES true if the camera move start by gesture
CAMERA_MOVE YES YES CameraPosition
CAMERA_MOVE_END YES YES CameraPosition
POLYGON_CLICK YES YES LatLng(clicked position)
POLYLINE_CLICK YES YES LatLng(clicked position)
CIRCLE_CLICK YES YES LatLng(clicked position)
GROUND_OVERLAY_CLICK YES YES LatLng(clicked position)
INFO_CLICK YES YES LatLng(marker position)
INFO_LONG_CLICK YES YES LatLng(marker position)
INFO_CLOSE YES YES LatLng(marker position)
INFO_OPEN YES YES LatLng(marker position)
MARKER_CLICK YES YES LatLng(marker position)
MARKER_DRAG YES YES LatLng(marker position)
MARKER_DRAG_START YES YES LatLng(marker position)
MARKER_DRAG_END YES YES LatLng(marker position)

change 11. set background color

use plugin.google.maps.environment.setBackgroundColor()

change 12. geocoding/reverse geocoding

You can do like this

plugin.google.maps.Geocoder.geocode({
  "address" : [
     "address1", "address2" ... "addressN"
  ]
}, function( mvcArray ) {

  mvcArray.on('insert_at', function(index) {
     console.log( mvcArray.getAt(index) );
  });

});

change 13. add some features

I forgot to much. See the demo apk.

change 14. Not yet

map.showDialog(), map.closeDialog(), and map.addKmlOverlay() are not ready yet.

change 15. UIWebView & WKWebView

Both are Supported.

Crosswalk is not confirmed yet.

I think I miss something. I will post them if I remember.

@wf9a5m75 6 months same situation help still available if you decide to value teamwork

My sincere condolences. Take all the time you need

hi @wf9a5m75 my usual ping for you 😃 do you have an ETA now?

Sorry for late, but keep waiting please. I have been developing the feature of multiple maps currently.

multiple_maps

So this is enough, isn’t it?

@sam2x Before asking easily, please try to debug (such as insert bitmap.recycle()) at first.

I’m also experiencing poor performance on Android with many (~400) markers in v2. Switching from v2 to v1 cuts loading time from 19 seconds to under 2 seconds. Has there been any progress on resolving this? I’m happy to help test, but I’m pretty sure resolving it is beyond my skill. Thanks!

@blckshrk If you have find any problem, please fix it, and send it as a pull request with test code.

Yes, nice! (With murker clustering i think it would be perfect 😉

In the second test, all versions are faster since the Panoramio server is faster. However, v1.x and v2.x are different mechanism.

adding_marker

The npm v1.3.9 depends on the server response and the network speed.

The v1.3.9 master branch depends on the cache hit percentage.

The v2-beta access to the server in parallel and use cache data.

So pls let me make a conclusion:

  • In current stable version (1.3.9) it is not possible to get satisfying results with a bunch of markers (maybe 500 or 1000+)
  • marker clustering is needed to get a great result and UX (no lagging anymore, no overlaying markers etc.)
  • you (@wf9a5m75) are currently developing version 1.4 of this plugin which will resolve all this issues

is that right?

–> If yes: pls understand as mentioned before by lots of other people that time is a limiting factor for most of us. Thus I would prefer if you can make a statement if it would take 3 months, 6 months, 1 year, 2 years or 5 years for you. It is not important if this is exactly right but than everyone can decide how to proceed with his/her project and this maps plugin.

–> If no: Maybe the other people here should decide to make a fork to get some more features in it and than it would be okay as well because from my point of view the only thing I currently missing is marker clustering, with this feature it would resolve all my open problems with the map I think.

P.S.: I really can not understand why everyone else has stopped working and lots of people (myself as well) have offered their support without feedback, which is killing the open source mind in my opinion. If sb. can link me to a previous discussion about the future of this plugin that I can comprehend this would be great, thx.

Hey @cvaliere, just trowing my 2 cents out there… Instead of having to use the optimized version which is not supported, did you try to handle the marker creation in a different way on javascript? I also have lots of markers in my app and things work just fine by throttling the creation and giving some time to the UI to refresh every few miliseconds…

OK, so we have to change our JS code a bit, because until there, when we called getMap(), it was always the same instance, so there was no need to redefine events listeners for instance

The problem is that we need to have a common API for Android & iOS, so I will wait until multiple maps is ready for both platforms

Any ETA?

It’s turn on the iOS. I use the same test code above.

While the current iOS code is not so slow, but the optimized version is more faster.

Before (current master branch)

before

After

after