-
Notifications
You must be signed in to change notification settings - Fork 917
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Performance problems when adding many markers. #835
Comments
The APIs aren't frozen, but I would like to have a heterogeneous environment. iOS and android should be the same level if possible. While the clustering branch resolves most of the memory problems, I would like to incorporate your suggestions, cause not everybody need clustering and the cluster branch has a lot less functionality currently. Once the funding is done, it will be released as own setup, but I would incorporate your edits there, too (ass the clustering can be deactivated by config). So I'm open for everything. I'm not the lead developer of this plugin. Masashi was, but he dropped it and I took over maintenance and fixed bugs and merged good PRs and incorporated some fixes and functions by myself. So I'm very thankful if someone would help me here. |
Can I see the clustering branch? Perhaps I should focus on these changes on that. |
Sure. Are you on Skype? We should chat |
I've though about this. A cluster is just one marker where many would be, and it should be the end-user's job to cluster, not the plugin's. The user has the added benefit of being able to pick the clustering method which by the ways begs the question: which are yo using? Are you going to provide multiple? Or perhaps the injection of a clustering interface? The questions on their own indicate that clustering should be handled outside the plugin. However you answer them, notice the plugin's clustering code will have to know about all the markers the user is creating, plus the clustering method the user wants to use and if the user wants a different clustering method they'll have to bake it in. Clustering is a black box that takes a bunch of markers and returns one single marker. I would provide clustering as a factory method, to be used as: var positions = [p1, p2, p3];
var clusterer = new plugin.google.maps.MarkerClusterer(opt_methodName);
var clusterPosition = clusterer.cluster(positions); Where The This can take advantage of the changes I've made since the user can use the We can also have it so that the user injects the These are just quick thoughts. Perhaps I miss the point entirely. Which way are you going? |
Test it: cordova plugin add https://github.com/mapsplugin/cordova-plugin-googlemaps#clusterer --variable API_KEY_FOR_ANDROID="YOURKEY" --variable API_KEY_FOR_IOS="YOURKEY" Add following config object to .getMap() 'controller': {
'clustering': true,
'rendering': 'animated',
'algorithm': 'nonHierarchicalDistanceBasedAlgorithm'
} eg $rootScope.map = plugin.google.maps.Map.getMap(mapDiv, {
'controls': {
'compass': false,
'myLocationButton': false,
'indoorPicker': false,
'toolbar': false,
'zoom': false
},
'gestures': {
'scroll': true,
'tilt': true,
'rotate': true
},
'controller': {
'clustering': true,
'rendering': 'animated',
'algorithm': 'nonHierarchicalDistanceBasedAlgorithm'
}
}); Edit: |
As soon as the user has zoomed enough, the real markers will be created. A real app to see it in action Not my app, but its the same codebase which is incorporated. The Cluster branch works, but it is a bit hacky - and generally this plugin needs a lot refactoring (but I don't have time for that). If you have time and the mood to provide a better clustering solution, I'm open minded to follow your path and spending all donations to you, if you provide a better way for both systems. |
Without clustering, I'm using a mix of KML overlay for the bulk of the 'noise' and plain css markers placed over top of the mapdiv together. |
the clustering layers (orange markers) are showing wrong numbers. Always double of the real marker-value is shown. Any ideas? |
dbesiryan - I'm not supporting something that isn't officially released. This branch is not finished |
I'd love to see this feature added. In an app I'm current working on, we have thousands of markers and the client doesn't want clustering. |
Could I maybe see your app that loads thousands of markers? |
I'll set-up a test app with how I'm doing it over the weekend. Sorry it took a while to respond. |
@atannus Do you have a fork containing your changes? Have you implemented the bulk marker adding, caching and pre-loading for Android & iOS or only für Android? |
No, I made changes directly on the version installed inside my project.
Is this project still being held hostage for funds? This throws me off.
|
It appears so. There is a clusterer branch but I'm unsure about how complete that is. It would be great if you could commit your changes to your fork (and maybe even open a pull request) if you want to share your work. |
I'll see what I can do. On Thu, Mar 3, 2016 at 10:41 AM, Christian notifications@github.com wrote:
André Tannús | Epungo | +55 11 2389-4360 |
I'm on this now. On Thu, Mar 3, 2016 at 10:44 AM, Andre Tannus andre.tannus@gmail.com
André Tannús | Epungo | +55 11 2389-4360 |
Well calling this a hostage is not very fine. And it's just for the cluster-branch. The cluster branch which is avaiable here "inoffically" was a paid job. It was just improved and cleaned by me. I just was about to give some money back to the code donator. If you guys help to finish this cluster branch, I'm open for it. The branch is not hidden, try it, fix it, improve it. |
Is there a suggested development flow? I have the plugin project checked out, so I have to make changes to that, uninstall/reinstall (so that the Cordova project picks up the changes), test, repeat. Also, (re)installing is a pain because the plugin name doesn't match the manifest name (or I'm doing something wrong). Is there a better workflow than this? It's a pain... Thanks. |
Hi @atannus The development flow I would suggest is to edit the plugin while installed: use a very simple Ionic app that uses this map, run "ionic run android", then open the Android project into the IDE you like (eg. Android Studio) and edit directly in here with live reload allowed by Android Studio. For clusters, I think you missed the point: the goal is to let the map decide, depending on the zoom level, to show individual markers or to cluster them. If you "create" the clusters outside the plugin, how is it supposed to work? Would you, for every zoom event, recalculate the clusters yourself? Please tell me if I can help you |
Thanks @cvaliere. On Tue, Mar 15, 2016 at 11:19 AM, cvaliere notifications@github.com wrote:
André Tannús | Epungo | +55 11 2389-4360 |
Can you share the createMarkers method with us? I think it's would be a great evolution. |
Hi @atannus +1 for sharing createMarkers, I would definitely use it thanks ! |
Hi guys. Thanks for the support. Aside from createMarkers, changes to the bridge call are also required. In Last week I put some time into making these changes decent enough to I'll post something soon, please be patient. On Fri, Mar 18, 2016 at 1:27 PM, cvaliere notifications@github.com wrote:
André Tannús | Epungo | +55 11 2389-4360 |
Hi guys. I've pushed the proposed changes to my fork, under branch Support is still very limited, and only tested with URL icons. You must preload icons in order to achieve the performance enhancement. If you don't, icons won't be cached and will be loaded individually, just as if you were adding markers one by one. Here is some code that demonstrate the plugin in action. Remeber to change the icon paths to something real. var map;
var icons = [
"http://some.thing/path/to/image1.png"
];
document.addEventListener("deviceready", function () {
// Initialize the map.
var div = document.getElementById("map_canvas");
map = plugin.google.maps.Map.getMap(div);
var initPosition = new plugin.google.maps.LatLng(-23.548, -46.5745);
map.addEventListener(plugin.google.maps.event.MAP_READY, function (map) {
// Post-preload callback.
var donePreloading = function () {
// Animate camera.
map.animateCamera({
'target': initPosition,
'zoom': 16,
'duration': 1000
},
function () {
placeMarkers();
})
};
// Preload images.
map.preloadImages(icons, function (results) {
donePreloading();
});
});
}, false);
placeMarkers = function () {
var markerDefinitions = createMarkers();
map.addMarkers(markerDefinitions, function (createdMarkers) {
console.log(createdMarkers);
});
}
createMarkers = function () {
var markers = [];
for (var i = 1; i < mockData.length; i++) {
var mock = mockData[i];
var position = new plugin.google.maps.LatLng(mock.lat, mock.lng);
var markerDefinition = {
'position': position
, 'icon': {
'url': icons[0],
'size': {
'width': 32,
'height': 32
}
}
};
markers.push(markerDefinition);
}
return markers;
}
var mockData = [
{
lat: "-23.548922600000000",
lng: "-46.574610800000000",
},
{
lat: "-23.549337900000000",
lng: "-46.574795600000000",
},
{
lat: "-23.550828933700000",
lng: "-46.576023101800000",
},
{
lat: "-23.550889500000000",
lng: "-46.576046300000000",
},
{
lat: "-23.551199100000000",
lng: "-46.575804400000000",
},
{
lat: "-23.550761500000000",
lng: "-46.575932700000000",
},
{
lat: "-23.550820100000000",
lng: "-46.575986100000000",
},
{
lat: "-23.550958200000000",
lng: "-46.576498800000000",
},
{
lat: "-23.550552368200000",
lng: "-46.574832916300000",
},
{
lat: "-23.550529480000000",
lng: "-46.575012207000000",
},
{
lat: "-23.549200058000000",
lng: "-46.573200225800000",
},
{
lat: "-23.549949646000000",
lng: "-46.572673797600000",
},
{
lat: "-23.550093900000000",
lng: "-46.572460600000000",
},
{
lat: "-23.550067000000000",
lng: "-46.572427400000000",
},
{
lat: "-23.549314300000000",
lng: "-46.571722400000000",
},
{
lat: "-23.548916800000000",
lng: "-46.572370800000000",
},
{
lat: "-23.550040200000000",
lng: "-46.571646100000000",
},
{
lat: "-23.551219500000000",
lng: "-46.571387300000000",
},
{
lat: "-23.550374984700000",
lng: "-46.571079254200000",
},
{
lat: "-23.551379900000000",
lng: "-46.572379600000000",
},
{
lat: "-23.551279068000000",
lng: "-46.571372985800000",
},
{
lat: "-23.550819700000000",
lng: "-46.571475300000000",
},
{
lat: "-23.550776800000000",
lng: "-46.571910200000000",
},
{
lat: "-23.551599502600000",
lng: "-46.575599670400000",
},
{
lat: "-23.551599502600000",
lng: "-46.575599670400000",
},
{
lat: "-23.552324295000000",
lng: "-46.575397491500000",
},
{
lat: "-23.551766600000000",
lng: "-46.575586100000000",
},
{
lat: "-23.551599502600000",
lng: "-46.575599670400000",
},
{
lat: "-23.552499771100000",
lng: "-46.574798584000000",
},
{
lat: "-23.552565800000000",
lng: "-46.574789700000000",
},
{
lat: "-23.552565800000000",
lng: "-46.574789700000000",
},
{
lat: "-23.551799774200000",
lng: "-46.573898315400000",
},
{
lat: "-23.551788330100000",
lng: "-46.574462890600000",
},
{
lat: "-23.551799774200000",
lng: "-46.573898315400000",
},
{
lat: "-23.553100585900000",
lng: "-46.575500488300000",
},
{
lat: "-23.553400039700000",
lng: "-46.575199127200000",
},
{
lat: "-23.553100500000000",
lng: "-46.574628900000000",
},
{
lat: "-23.552700042700000",
lng: "-46.573799133300000",
},
{
lat: "-23.553236007700000",
lng: "-46.574249267600000",
},
{
lat: "-23.553236007700000",
lng: "-46.574249267600000",
},
{
lat: "-23.552700042700000",
lng: "-46.573799133300000",
},
{
lat: "-23.553236007700000",
lng: "-46.574249267600000",
},
{
lat: "-23.552421900000000",
lng: "-46.573419800000000",
},
{
lat: "-23.552553176900000",
lng: "-46.573181152300000",
},
{
lat: "-23.552079300000000",
lng: "-46.572764700000000",
},
{
lat: "-23.552452087400000",
lng: "-46.573001861600000",
},
{
lat: "-23.552553176900000",
lng: "-46.573181152300000",
},
{
lat: "-23.552080154400000",
lng: "-46.572765350300000",
},
{
lat: "-23.552339553800000",
lng: "-46.571506500200000",
},
{
lat: "-23.552373800000000",
lng: "-46.571509900000000",
},
{
lat: "-23.551967620800000",
lng: "-46.571239471400000",
},
{
lat: "-23.553024292000000",
lng: "-46.572566986100000",
},
{
lat: "-23.553100585900000",
lng: "-46.572799682600000",
},
{
lat: "-23.553188324000000",
lng: "-46.573322296100000",
},
{
lat: "-23.553508758500000",
lng: "-46.573505401600000",
},
{
lat: "-23.552674900000000",
lng: "-46.573789000000000",
},
{
lat: "-23.552713600000000",
lng: "-46.573731100000000",
},
{
lat: "-23.553152200000000",
lng: "-46.571818300000000",
},
{
lat: "-23.553367614700000",
lng: "-46.571437835700000",
},
{
lat: "-23.553100585900000",
lng: "-46.571701049800000",
},
{
lat: "-23.553808212300000",
lng: "-46.571708679200000",
},
{
lat: "-23.553110600000000",
lng: "-46.571771400000000",
},
{
lat: "-23.553100585900000",
lng: "-46.571701049800000",
}
]; |
Hi everyone, this is Masashi who is the original author of this plugin. |
Sorry I have no idea when next is released at this point. |
I'm happy to help with anything I can. |
Thank you @hirbod |
Great work @wf9a5m75 huge +1 for clustering support I also just sent you USD100 - so you can have 50 more beer :) |
|
thank you @wf9a5m75, seems like a great upgrade |
No xwalk tested. And some method and event names are changed. I recommened you use the plugin in a new project. Don't use it in your project immediately |
ok, so, are you working on it, or you want someone else to work on it, and then can you give me a tip of what should be done? (I suppose you know what should be done, as the current master branch supports Xwalk) |
Basically I'm not interested in XWalk, but I tried a little. I don't know why but In that case, XWalk is not available.
|
Just giving my 2 cents on it: you might not be interested, but Cordova without Xwalk is a big piece of unusable shit. Xwalk=the same web view for every android device. The only real way to provide a good working app. So please don't ignore Xwalk |
Move your hands, please. So please move your hands to solve the issue. |
i have implement this code to render markers with the beta 2 map , according the result of this is on 1.0 secunds with the map beta 2 but on my project are 4.2 secunds ago, i dont idea, i have clone the beta 2 project and set cordova prepare but the time is the same
|
I have no idea. |
As response to @dsemerida : Also noticed this, but I didn't have so much time to deeply investigate it, I'd like to create a sample project with +300 markers and measure the time between 1.4 and 2.0 |
Hey! First, thanks for your job, this plugin is awesome :-) I used the v1 (with Ionic 1) on my app and I'm currently writing a new version using Ionic 2 and the v2 of the plugin. Everything is fine except the markers creation. I add 222 markers on my map. ~1.5s on this project (v1) : https://github.com/alexetmanon/vliller I make a test repo here : https://github.com/blckshrk/addmarker-debug I have no idea from where it can come from... Should it be the Android SDK ? Is 200 markers a lot (it seems not on the v1) ? I've tried every thing, but really, I don't get it. Could you take a look ? :-) |
@BlckShrk Specify the same icon as much as possible. |
I also asked you about this when you released v2 @wf9a5m75 , there's something causing slow drawing of markers on the map in v2. Point 5: https://github.com/mapsplugin/v2.0-demo/issues/6#issuecomment-253334483 But your answer about that point didn't help so much... https://github.com/mapsplugin/v2.0-demo/issues/6#issuecomment-253339084 I'd love to use v2 since I believe that it will work much better than v1, but adding lots of markers it's a common task, that HUGE time impact will be bad for UX... |
Hey guys! @wf9a5m75 I used the default icon for my tests (so no icons properties) and on the initial app each marker have the same icon object (with same url/size properties) @ZiFFeL1992 I agree, adding more than 100 marker have a really huge impact on performances, but it seems limited to the Android version (to be confirmed) |
Hi, I've taken a day to dig the problem and I found some issues. Observations
Interpretation I suspect a memory leak in the Conclusion I think we need to reduce the memory usage and fix the memory leaks to solve this problem. My advice is to avoid What do you think about ? |
@BlckShrk If you have find any problem, please fix it, and send it as a pull request with test code. |
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! |
@bmcbride As described above, I observed the same problem, but can't found a solution... |
Hey i bring my own experience in this thread. I also have some issue with v2 when i use custom marker. Environment:
Application Context Angular 1.6.4 using cordova, with 2views. The home view, and the Map view. Apk size is around 10Mo. Test performed I just switch between Home view and Map view. At each switch i check memory. Test1: not custom marker Test1 has 27 visibles and 39 invisibles markers during the whole test.
-> Looping between these two view seems to give almost the same results, which seems "legits" to me. Test2: all markers are custom, size option is set to 32x32 (~10 differents png images, dimensions 36x36, between 1.5Ko and 2Ko each, and stored locally) _Test2 has the same marker env as test1 => 27 visibles and 39 invisibles markers. Differents markers may set the same png. _
-> Looping between these two view increase memory, leading sooner or later the app to be killed by the system. So memory leaks likely occurs somewhere. How i leave the Map view ? I use destroy event in the Map view, in this function i do the following actions :
Statements/Questions Imho there is two distincts "statements":
So :
Anyway, any pointers/ideas from your point of view ? Regards |
@sam2x Imho, this is because the memory leaks are in the native code. So, destroy JS listeners won't clear native memory (I may said something wrong, I haven't looked at the remove() method) Additionally, it seems that during the markers creation step some leaks appears (may some references aren't freed somewhere en lost after, waiting for a garbage collection for example). Best, |
@BlckShrk thanks you for this notice. Yes, i'm aware this could be the native code guilty. However I'm trying first to take a deep look at the plugin (even if i'm not Java expert). Also It looks odd to me that so much memory is leaked from native code, in such simple "test-case", someone would have noticed if it was from native code (also i'm using last google-map sdk) ? I'm investigating the way this plugin is dealing with BitMap (since it looks like a good culprit to me): According the Android guideline Bitmap can "very easily exhaust an app's memory budget". They recommend to use the Glide library to "fetch, decode, and display bitmaps". Looking at the code, i dont understand something. When creating a marker, with icon option specified, the setIcon method is called. Since we specified a size, this method call an util method resizeBitmap. @wf9a5m75 Isnt "bitmap.recycle()" missing here ? According my understanding from http://stackoverflow.com/questions/3823799/android-bitmap-recycle-how-does-it-work when you assign a new image, if you dont explicitely call "recycle()" the old one is still in memory. I tried to rebuild my app without specifying the size (and let the plugin computes it via scaleBitmapForDevice which call recycle()) but result is still same as test2, so it seems to not solve the prob :/ So:
|
@sam2x Before asking easily, please try to debug (such as insert bitmap.recycle()) at first. |
@sam2x ok, could you make another issue at once? This thread is too long. |
Is there a way to dicuss in PM about this ? i'm willing to financially contribute for your free time with this issue and make the effort/result available for the community. I know we all have priorities, and can't afford time easily. |
Please send email to me (see my github profile page). |
Since this thread is too long, I lock this thread. |
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 availablecreateMarker
, 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, thecallback
has to return an array of markers, instead of a single marker.A
private createMarker_
method is to be called bycreateMarkers
. This does not break the current API, and the currentcreateMarker
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:
I'm seeing fantastic performance with these improvements, I'd really like to contribute them.
Thanks a lot for the help.
The text was updated successfully, but these errors were encountered: