(Android / iOS) Version `cluster-20170728-1500`
MODIFY
plugin.xml
plugin.xml
<?xml version='1.0' encoding='utf-8'?>
-<plugin id="cordova-plugin-googlemaps" version="cluster-20170728-0012" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
+<plugin id="cordova-plugin-googlemaps" version="cluster-20170728-1500" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
<name>cordova-plugin-googlemaps</name>
<js-module name="BaseClass" src="www/BaseClass.js">
<runs />

MODIFY
src/android/plugin/google/maps/PluginMarkerCluster.java
src/android/plugin/google/maps/PluginMarkerCluster.java
@@ -17,6 +17,7 @@ import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
@@ -27,7 +28,7 @@ public class PluginMarkerCluster extends PluginMarker {
private final static ConcurrentHashMap<String, Boolean> debugFlags = new ConcurrentHashMap<String, Boolean>();
private final static ArrayList<String> deleteMarkers = new ArrayList<String>();
- private final Object dummyObj = new Object();
+ private final Object semaphore = new Object();
private boolean stopFlag = false;
enum STATUS {
@@ -150,14 +151,14 @@ public class PluginMarkerCluster extends PluginMarker {
public void redrawClusters(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
- final HashMap<String, String> updateClusterIDs = new HashMap<String, String>();
+ final HashSet<String> updateClusterIDs = new HashSet<String>();
final HashMap<String, Bundle> changeProperties = new HashMap<String, Bundle>();
final String clusterId = args.getString(0);
final boolean isDebug = debugFlags.get(clusterId);
- synchronized (dummyObj) {
+ synchronized (semaphore) {
JSONObject params = args.getJSONObject(1);
- String clusterId_markerId, deleteMarkerId;
+ String clusterId_markerId;
String markerId;
JSONArray deleteClusters = null;
if (params.has("delete")) {
@@ -168,22 +169,24 @@ public class PluginMarkerCluster extends PluginMarker {
new_or_update = params.getJSONArray("new_or_update");
}
- int deleteCnt = 0;
- int new_or_updateCnt = 0;
if (deleteClusters != null) {
- deleteCnt = deleteClusters.length();
+ //-------------------------------------
+ // delete markers on the delete thread
+ //-------------------------------------
+ int deleteCnt = deleteClusters.length();
for (int i = 0; i < deleteCnt; i++) {
markerId = deleteClusters.getString(i);
deleteMarkers.add(clusterId + "-" + markerId);
}
}
- if (new_or_update != null) {
- new_or_updateCnt = new_or_update.length();
- }
//---------------------------
// Determine new or update
//---------------------------
+ int new_or_updateCnt = 0;
+ if (new_or_update != null) {
+ new_or_updateCnt = new_or_update.length();
+ }
JSONObject clusterData, positionJSON;
Bundle properites;
for (int i = 0; i < new_or_updateCnt; i++) {
@@ -192,15 +195,14 @@ public class PluginMarkerCluster extends PluginMarker {
markerId = clusterData.getString("id");
clusterId_markerId = clusterId + "-" + markerId;
+ // Save the marker properties
self.objects.put("marker_property_" + clusterId_markerId, clusterData);
- if (self.objects.containsKey(clusterId_markerId) || pluginMarkers.containsKey(clusterId_markerId)) {
- updateClusterIDs.put(clusterId_markerId, clusterId_markerId);
- } else {
+ // Set the WORKING status flag
pluginMarkers.put(clusterId_markerId, STATUS.WORKING);
- updateClusterIDs.put(clusterId_markerId, null);
- }
+ updateClusterIDs.add(clusterId_markerId);
+ // Prepare the marker properties for addMarker()
properites = new Bundle();
properites.putDouble("lat", positionJSON.getDouble("lat"));
properites.putDouble("lng", positionJSON.getDouble("lng"));
@@ -301,35 +303,43 @@ public class PluginMarkerCluster extends PluginMarker {
Boolean isNew;
//---------
- // reuse or update
+ // new or update
//---------
waitCntManager.put(clusterId, updateClusterIDs.size());
- iterator = updateClusterIDs.keySet().iterator();
+ iterator = updateClusterIDs.iterator();
while (iterator.hasNext()) {
markerId = iterator.next();
synchronized (pluginMarkers) {
pluginMarkers.put(markerId, STATUS.WORKING);
}
isNew = !objects.containsKey(markerId);
+
+ // Get the marker properties
markerProperties = changeProperties.get(markerId);
if (isNew) {
+ // If the requested id is new location, create a marker
marker = map.addMarker(new MarkerOptions()
.position(new LatLng(markerProperties.getDouble("lat"), markerProperties.getDouble("lng")))
.visible(false));
+ marker.setTag(markerProperties.getString("id"));
+
+ // Store the marker instance with markerId
+ synchronized (self.objects) {
+ self.objects.put(markerId, marker);
+ }
+ } else {
+ marker = getMarker(markerId);
+ }
+ //----------------------------------------
+ // Set the title and snippet properties
+ //----------------------------------------
if (markerProperties.containsKey("title")) {
marker.setTitle(markerProperties.getString("title"));
}
if (markerProperties.containsKey("snippet")) {
marker.setSnippet(markerProperties.getString("snippet"));
}
- marker.setTag(markerProperties.getString("id"));
- } else {
- marker = getMarker(markerId);
- }
- synchronized (self.objects) {
- self.objects.put(markerId, marker);
- }
-
+/*
if (!isNew) {
synchronized (pluginMarkers) {
pluginMarkers.put(markerId, STATUS.CREATED);
@@ -337,12 +347,16 @@ public class PluginMarkerCluster extends PluginMarker {
decreaseWaitCnt(clusterId);
continue;
}
+*/
if (markerProperties.containsKey("icon")) {
Bundle icon = markerProperties.getBundle("icon");
//Log.d(TAG, "---> targetMarkerId = " + targetMarkerId + ", marker = " + marker);
setIconToClusterMarker(markerId, marker, icon, new PluginAsyncInterface() {
@Override
public void onPostExecute(Object object) {
+ //--------------------------------------
+ // Marker was updated
+ //--------------------------------------
decreaseWaitCnt(clusterId);
}
@@ -371,9 +385,9 @@ public class PluginMarkerCluster extends PluginMarker {
}
});
- synchronized (dummyObj) {
+ synchronized (semaphore) {
try {
- dummyObj.wait();
+ semaphore.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
@@ -391,8 +405,8 @@ public class PluginMarkerCluster extends PluginMarker {
int waitCnt = waitCntManager.get(clusterId);
waitCnt = waitCnt - 1;
if (waitCnt == 0) {
- synchronized (dummyObj) {
- dummyObj.notify();
+ synchronized (semaphore) {
+ semaphore.notify();
}
}
@@ -446,6 +460,7 @@ public class PluginMarkerCluster extends PluginMarker {
self.objects.remove("marker_property_" + markerId);
pluginMarkers.remove(markerId);
objects.remove(markerId);
+ pluginMarkers.put(markerId, STATUS.DELETED);
}
callback.onPostExecute(errorMsg);
}

MODIFY
src/ios/GoogleMaps/MyPlgunProtocol.h
src/ios/GoogleMaps/MyPlgunProtocol.h
#import <Cordova/CDV.h>
@protocol MyPlgunProtocol <NSObject>
-@property (nonatomic, strong) NSMutableDictionary* objects;
-- (void)onHookedPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId;
+@property (nonatomic) NSMutableDictionary* objects;
+//- (void)onHookedPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId;
- (void)pluginUnload;
@end

MODIFY
src/ios/GoogleMaps/MyPluginLayer.h
src/ios/GoogleMaps/MyPluginLayer.h
@property (nonatomic) UIView *webView;
@property (nonatomic) MyPluginScrollView *pluginScrollView;
@property (nonatomic) NSTimer *redrawTimer;
-@property (nonatomic) BOOL *isSuspended;
+@property (nonatomic) BOOL isSuspended;
+@property (nonatomic) BOOL stopFlag;
+@property (nonatomic) NSOperationQueue *executeQueue;
- (id)initWithWebView:(UIView *)webView;
- (void)resizeTask:(NSTimer *)timer;

MODIFY
src/ios/GoogleMaps/MyPluginLayer.m
src/ios/GoogleMaps/MyPluginLayer.m
#import "MyPluginLayer.h"
-NSOperationQueue *executeQueue;
-BOOL stopFlag = NO;
BOOL hasCordovaStatusBar = NO; // YES if the app has cordova-plugin-statusbar
@implementation MyPluginLayer
- (id)initWithWebView:(UIView *)webView {
- executeQueue = [NSOperationQueue new];
+ self.executeQueue = [NSOperationQueue new];
self = [super initWithFrame:[webView frame]];
self.webView = webView;
- self.isSuspended = NO;
+ self.isSuspended = false;
self.opaque = NO;
[self.webView removeFromSuperview];
// prevent webView from bouncing
@@ -101,7 +99,7 @@ BOOL hasCordovaStatusBar = NO; // YES if the app has cordova-plugin-statusbar
- (void)putHTMLElements:(NSDictionary *)elementsDic {
- [executeQueue addOperationWithBlock:^{
+ [self.executeQueue addOperationWithBlock:^{
CGRect rect = CGRectMake(0, 0, 0, 0);
NSMutableDictionary *domInfo, *size;
NSString *domId;
@@ -183,10 +181,10 @@ BOOL hasCordovaStatusBar = NO; // YES if the app has cordova-plugin-statusbar
// Assumes all touches for the browser
return;
}
- if (stopFlag) {
+ if (self.stopFlag) {
return;
}
- stopFlag = YES;
+ self.stopFlag = YES;
NSArray *keys=[self.pluginScrollView.debugView.mapCtrls allKeys];
NSString *mapId;
GoogleMapsViewController *mapCtrl;
@@ -203,7 +201,7 @@ BOOL hasCordovaStatusBar = NO; // YES if the app has cordova-plugin-statusbar
[self.pluginScrollView.debugView setNeedsDisplay];
});
}
- stopFlag = NO;
+ self.stopFlag = NO;
}
- (void)updateViewPosition:(GoogleMapsViewController *)mapCtrl {

MODIFY
src/ios/GoogleMaps/PluginGeocoder.m
src/ios/GoogleMaps/PluginGeocoder.m
int idx = 0;
if ([json objectForKey:@"idx"]) {
- idx = [[json objectForKey:@"idx"] integerValue];
+ idx = (int)[[json objectForKey:@"idx"] integerValue];
}
if (address && position == nil) {

MODIFY
src/ios/GoogleMaps/PluginMap.h
src/ios/GoogleMaps/PluginMap.h
@property (nonatomic, strong) GoogleMapsViewController* mapCtrl;
@property (nonatomic) BOOL isRemoved;
@property (nonatomic) NSOperationQueue *executeQueue;
+@property (nonatomic) NSMutableDictionary* objects;
- (void)clear:(CDVInvokedUrlCommand*)command;
- (void)setClickable:(CDVInvokedUrlCommand*)command;

MODIFY
src/ios/GoogleMaps/PluginMap.m
src/ios/GoogleMaps/PluginMap.m
self.mapCtrl = viewCtrl;
}
+- (void)pluginInitialize
+{
+ if (self.objects) {
+ return;
+ }
+ // Initialize this plugin
+ self.objects = [NSMutableDictionary dictionary];
+}
+
- (void)pluginUnload
{
if (self.executeQueue != nil){
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
- // Trigger the map_loaded event if request method is `getMap()`
+ // Trigger the map_loaded event if current mapType == kMapTypeNone && request method is `getMap()`
+ if (self.mapCtrl.map.mapType == kGMSTypeNone) {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self.mapCtrl mapViewDidFinishTileRendering:self.mapCtrl.map];
});
+ }
return;
}
NSDictionary *cameraOpts = [initOptions objectForKey:@"camera"];
[self _changeCameraPosition:@"moveCamera" requestMethod:requestMethod params:cameraOpts command:command];
} else {
- [self.mapCtrl.view setHidden:NO];
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
- // Trigger the map_loaded event if request method is `getMap()`
- if ([requestMethod isEqualToString:@"getMap"]) {
+ // Trigger the map_loaded event if current mapType == kMapTypeNone && request method is `getMap()`
+ if (self.mapCtrl.map.mapType == kGMSTypeNone &&
+ [[initOptions objectForKey:@"method"] isEqualToString:@"getMap"]) {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self.mapCtrl mapViewDidFinishTileRendering:self.mapCtrl.map];

MODIFY
src/ios/GoogleMaps/PluginMarker.m
src/ios/GoogleMaps/PluginMarker.m
NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
-
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSMutableDictionary *iconProperty = nil;
NSString *animation = nil;

MODIFY
src/ios/GoogleMaps/PluginMarkerCluster.h
src/ios/GoogleMaps/PluginMarkerCluster.h
#import "NSData+Base64.h"
@interface PluginMarkerCluster : PluginMarker<MyPlgunProtocol>
+@property (nonatomic) NSMutableDictionary* objects;
+@property (atomic, strong) NSMutableDictionary *debugFlags;
@property (atomic, strong) NSMutableDictionary *pluginMarkers;
@property (atomic, strong) NSMutableDictionary *waitCntManager;
-@property (atomic, strong) NSMutableDictionary *_pluginResults;
+@property (atomic, strong) NSMutableArray *deleteMarkers;
+@property (atomic, strong) dispatch_semaphore_t semaphore;
+@property (nonatomic) BOOL stopFlag;
- (void)create:(CDVInvokedUrlCommand*)command;
- (void)redrawClusters:(CDVInvokedUrlCommand*)command;

MODIFY
src/ios/GoogleMaps/PluginMarkerCluster.m
src/ios/GoogleMaps/PluginMarkerCluster.m
const NSString *GEOCELL_ALPHABET = @"0123456789abcdef";
const int GEOCELL_GRID_SIZE = 4;
-NSObject *dummyObject;
-BOOL recyleOption = NO;
-(void)setGoogleMapsViewController:(GoogleMapsViewController *)viewCtrl
@@ -26,17 +24,61 @@ BOOL recyleOption = NO;
return;
}
// Initialize this plugin
- dummyObject = [[NSObject alloc] init];
self.objects = [[NSMutableDictionary alloc] init];
- self._pluginResults = [[NSMutableDictionary alloc] init];
self.waitCntManager = [NSMutableDictionary dictionary];
self.pluginMarkers = [NSMutableDictionary dictionary];
- self.executeQueue = [NSOperationQueue new];
+ self.debugFlags = [NSMutableDictionary dictionary];
+ self.deleteMarkers = [NSMutableArray array];
+ self.semaphore = dispatch_semaphore_create(0);
+ self.stopFlag = NO;
+
+ //---------------------
+ // Delete thread
+ //---------------------
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ while(!self.stopFlag) {
+
+ dispatch_async(dispatch_get_main_queue(), ^(void) {
+ @synchronized (self.deleteMarkers) {
+ NSString *markerId;
+ GMSMarker *marker = nil;
+ //---------
+ // delete
+ //---------
+ for (int i = (int)([self.deleteMarkers count] - 1); i > -1; i--) {
+ markerId = [self.deleteMarkers objectAtIndex:i];
+ @synchronized (self.objects) {
+ marker = [self.objects objectForKey: markerId];
+ }
+
+ @synchronized (self.pluginMarkers) {
+ if (![[self.pluginMarkers objectForKey:markerId] isEqualToString:@"WORKING"]) {
+ @synchronized (self.objects) {
+ [self _removeMarker:marker];
+ marker = nil;
+ if ([self.objects objectForKey:[NSString stringWithFormat:@"marker_property_%@", markerId]]) {
+ [self.objects removeObjectForKey:[NSString stringWithFormat:@"marker_property_%@", markerId]];
+ }
+ }
+ [self.pluginMarkers removeObjectForKey:markerId];
+ [self.deleteMarkers removeObjectAtIndex:i];
+ } else {
+ [self.pluginMarkers setObject:@"DELETED" forKey:markerId];
+ }
+ }
+ }
+ }
+
+ });
+
+ }
+ });
}
- (void)pluginUnload
{
-
+ self.stopFlag = true;
// Plugin destroy
NSArray *keys = [self.objects allKeys];
@@ -46,13 +88,6 @@ BOOL recyleOption = NO;
[self.objects removeObjectForKey:key];
}
self.objects = nil;
-
- keys = [self._pluginResults allKeys];
- for (int i = 0; i < [keys count]; i++) {
- key = [keys objectAtIndex:i];
- [self._pluginResults removeObjectForKey:key];
- }
- self._pluginResults = nil;
}
- (void)create:(CDVInvokedUrlCommand*)command {
@@ -75,21 +110,24 @@ BOOL recyleOption = NO;
[result setObject:clusterId forKey:@"id"];
[result setObject:[NSString stringWithFormat:@"%lu", (unsigned long)command.hash] forKey:@"hashCode"];
+ [self.debugFlags setObject:[NSNumber numberWithBool:[[params objectForKey:@"debug"] boolValue]] forKey:clusterId];
+
CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (void)redrawClusters:(CDVInvokedUrlCommand*)command {
-
- __block NSMutableDictionary *updateClusterIDs = [NSMutableDictionary dictionary];
- __block NSMutableArray *deleteClusterIDs = [NSMutableArray array];
+ __block NSMutableArray *updateClusterIDs = [NSMutableArray array];
__block NSMutableDictionary *changeProperties = [NSMutableDictionary dictionary];
__block NSString *clusterId = [command.arguments objectAtIndex: 0];
- @synchronized (dummyObject) {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ @synchronized (self.semaphore) {
+ BOOL isDebug = [[self.objects objectForKey:clusterId] boolValue];
+
NSDictionary *params = [command.arguments objectAtIndex:1];
- NSString *clusterId_markerId, *deleteMarkerId, *markerId;
+ NSString *clusterId_markerId, *markerId;
NSMutableArray *deleteClusters = nil;
if ([params objectForKey:@"delete"]) {
@@ -100,23 +138,28 @@ BOOL recyleOption = NO;
new_or_update = [params objectForKey:@"new_or_update"];
}
- int deleteCnt = 0;
- int new_or_updateCnt = 0;
if (deleteClusters != nil) {
+ //-------------------------------------
+ // delete markers on the delete thread
+ //-------------------------------------
+ int deleteCnt = 0;
deleteCnt = (int)[deleteClusters count];
- }
- if (new_or_update != nil) {
- new_or_updateCnt = (int)[new_or_update count];
- }
+ @synchronized (self.deleteMarkers) {
for (int i = 0; i < deleteCnt; i++) {
markerId = [deleteClusters objectAtIndex:i];
- [deleteClusterIDs addObject:[NSString stringWithFormat:@"%@-%@", clusterId, markerId]];
+ [self.deleteMarkers addObject:[NSString stringWithFormat:@"%@-%@", clusterId, markerId]];
+ }
+ }
}
-
//---------------------------
// Determine new or update
//---------------------------
+ int new_or_updateCnt = 0;
+ if (new_or_update != nil) {
+ new_or_updateCnt = (int)[new_or_update count];
+ }
+
NSDictionary *clusterData, *positionJSON;
NSMutableDictionary *properties;
for (int i = 0; i < new_or_updateCnt; i++) {
@@ -125,31 +168,17 @@ BOOL recyleOption = NO;
markerId = [clusterData objectForKey:@"id"];
clusterId_markerId = [NSString stringWithFormat:@"%@-%@", clusterId, markerId];
- @synchronized (self.objects) {
+ // Save the marker properties
[self.objects setObject:clusterData forKey:[NSString stringWithFormat:@"marker_property_%@", clusterId_markerId]];
- }
- if ([self.objects objectForKey:clusterId_markerId] != nil || [_pluginMarkers objectForKey:clusterId_markerId] != nil) {
- [updateClusterIDs setObject:clusterId_markerId forKey:clusterId_markerId];
- } else {
- if (recyleOption && deleteCnt > 0) {
- //---------------
- // Reuse a marker
- //---------------
- deleteMarkerId = [deleteClusterIDs objectAtIndex:0];
- [deleteClusterIDs removeObjectAtIndex:0];
- deleteCnt--;
- [updateClusterIDs setObject:clusterId_markerId forKey:deleteMarkerId];
- } else {
- [_pluginMarkers setObject:@"WORKING" forKey:clusterId_markerId];
- [updateClusterIDs setObject:@"nil" forKey:clusterId_markerId];
- }
+ // Set the WORKING status flag
+ [updateClusterIDs addObject:clusterId_markerId];
+ @synchronized (self.pluginMarkers) {
+ [self.pluginMarkers setObject:@"WORKING" forKey:clusterId_markerId];
}
+ // Prepare the marker properties for addMarker()
properties = [NSMutableDictionary dictionary];
- if ([clusterData objectForKey:@"geocell"]) {
- [properties setObject:[clusterData objectForKey:@"geocell"] forKey:@"geocell"];
- }
[properties setObject:[positionJSON objectForKey:@"lat"] forKey:@"lat"];
[properties setObject:[positionJSON objectForKey:@"lng"] forKey:@"lng"];
if ([clusterData objectForKey:@"title"]) {
@@ -168,13 +197,21 @@ BOOL recyleOption = NO;
NSMutableDictionary *iconProperties = [NSMutableDictionary dictionaryWithDictionary:iconObj];
if ([iconProperties objectForKey:@"label"]) {
NSMutableDictionary *label = [NSMutableDictionary dictionaryWithDictionary:[iconProperties objectForKey:@"label"]];
+ if (isDebug) {
+ [label setObject:markerId forKey:@"text"];
+ } else {
[label setObject:[clusterData objectForKey:@"count"] forKey:@"text"];
+ }
[iconProperties setObject:label forKey:@"label"];
} else {
NSMutableDictionary *label = [NSMutableDictionary dictionary];
- [label setObject:[NSNumber numberWithInt:20] forKey:@"fontSize"];
+ if (isDebug) {
+ [label setObject:markerId forKey:@"text"];
+ } else {
+ [label setObject:[NSNumber numberWithInt:15] forKey:@"fontSize"];
[label setObject:[NSNumber numberWithBool:TRUE] forKey:@"bold"];
[label setObject:[clusterData objectForKey:@"count"] forKey:@"text"];
+ }
[iconProperties setObject:label forKey:@"label"];
}
@@ -187,244 +224,114 @@ BOOL recyleOption = NO;
[properties setObject:iconProperties forKey:@"icon"];
}
- }
+ } // if ([clusterData objectForKey:@"icon"]) {..}
+
[changeProperties setObject:properties forKey:clusterId_markerId];
- }
- }
+
+ } // for (int i = 0; i < new_or_updateCnt; i++) { .. }
+
+ } // @synchronized (self.semaphore)
//---------------------------
// mapping markers on the map
//---------------------------
[[NSOperationQueue mainQueue] addOperationWithBlock: ^{
self.mapCtrl.map.selectedMarker = nil;
- NSString *oldMakrerId, *newMarkerId, *targetMarkerId, *iconCacheKey;
+ NSString *markerId;
NSMutableDictionary *markerProperties;
GMSMarker *marker;
- GMSPolygon *polygon;
- GMSCoordinateBounds *bounds;
- GMSMutablePath *boundsPath;
CLLocationCoordinate2D position;
double latitude, longitude;
+ BOOL isNew;
//---------------------
- // reuse or update
+ // new or update
//---------------------
- [_waitCntManager setObject:[NSNumber numberWithInteger:[updateClusterIDs count]] forKey:clusterId];
- NSArray<NSString *> *markerIDList = [updateClusterIDs allKeys];
- for (int i = 0; i < [markerIDList count]; i++) {
- oldMakrerId = [markerIDList objectAtIndex:i];
- newMarkerId = [updateClusterIDs objectForKey:oldMakrerId];
- if ([newMarkerId isEqualToString:@"nil"]) {
- targetMarkerId = oldMakrerId;
- markerProperties = [changeProperties objectForKey:targetMarkerId];
+ [self.waitCntManager setObject:[NSNumber numberWithInteger:[updateClusterIDs count]] forKey:clusterId];
+ for (int i = 0; i < [updateClusterIDs count]; i++) {
+ markerId = [updateClusterIDs objectAtIndex:i];
+ @synchronized(self.pluginMarkers) {
+ [self.pluginMarkers setObject:@"WORKING" forKey:markerId];
+ }
+ isNew = [self.objects objectForKey:markerId] == nil;
+ // Get the marker properties
+ markerProperties = [changeProperties objectForKey:markerId];
+
+ if (isNew) {
+ // If the requested id is new location, create a marker
latitude = [[markerProperties objectForKey:@"lat"] doubleValue];
longitude = [[markerProperties objectForKey:@"lng"] doubleValue];
position = CLLocationCoordinate2DMake(latitude, longitude);
marker = [GMSMarker markerWithPosition:position];
marker.tracksViewChanges = NO;
marker.tracksInfoWindowChanges = NO;
- marker.appearAnimation = NO;
- if ([markerProperties objectForKey:@"title"]) {
- marker.title = [markerProperties objectForKey:@"title"];
- }
- if ([markerProperties objectForKey:@"snippet"]) {
- marker.snippet = [markerProperties objectForKey:@"snippet"];
- }
- marker.userData = targetMarkerId;
- @synchronized (self.objects) {
- [self.objects setObject:marker forKey:targetMarkerId];
- [_pluginMarkers setObject:@"WORKING" forKey:targetMarkerId];
- }
+ marker.userData = markerId;
- if ([markerProperties objectForKey:@"geocell"]) {
- bounds = [self computeBox:[markerProperties objectForKey:@"geocell"]];
-
- boundsPath = [GMSMutablePath path];
- [boundsPath addCoordinate:bounds.northEast];
- [boundsPath addLatitude:bounds.northEast.latitude longitude:bounds.southWest.longitude];
- [boundsPath addCoordinate:bounds.southWest];
- [boundsPath addLatitude:bounds.southWest.latitude longitude:bounds.northEast.longitude];
- polygon = [GMSPolygon polygonWithPath: boundsPath];
- polygon.map = self.mapCtrl.map;
- polygon.strokeColor = UIColor.blackColor;
- polygon.strokeWidth = 2;
- polygon.userData = @"polygon";
- [self.objects setObject:polygon forKey:[NSString stringWithFormat:@"polygon%@", targetMarkerId]];
- }
- } else {
- marker = nil;
- polygon = nil;
- while (marker == nil) {
- marker = [self.objects objectForKey:oldMakrerId];
- polygon = [self.objects objectForKey:[NSString stringWithFormat:@"polygon%@", oldMakrerId]];
-
- if (marker == nil || marker.userData == nil) {
+ // Store the marker instance with markerId
@synchronized (self.objects) {
- if ([self.objects objectForKey:oldMakrerId]) {
- [self.objects removeObjectForKey:oldMakrerId];
- }
- if ([self.objects objectForKey:[NSString stringWithFormat:@"marker_property_%@", oldMakrerId]]) {
- [self.objects removeObjectForKey:[NSString stringWithFormat:@"marker_property_%@", oldMakrerId]];
- }
- if ([[self.pluginMarkers objectForKey:oldMakrerId] isEqualToString:@"DELETED"]) {
- [self.pluginMarkers removeObjectForKey:oldMakrerId];
- } else {
- [self.pluginMarkers setObject:@"DELETED" forKey:oldMakrerId];
- }
- polygon = [self.objects objectForKey:[NSString stringWithFormat:@"polygon%@", oldMakrerId]];
- if (polygon != nil && polygon.userData != nil) {
- polygon.userData = nil;
- polygon.map = nil;
- polygon = nil;
- [self.objects removeObjectForKey:[NSString stringWithFormat:@"polygon%@", oldMakrerId]];
- }
- }
- if ([deleteClusterIDs count] > 0) {
- oldMakrerId = [deleteClusterIDs objectAtIndex:0];
- [deleteClusterIDs removeObjectAtIndex:0];
- } else {
- marker = [GMSMarker markerWithPosition:CLLocationCoordinate2DMake(0, 0)];
- marker.tracksViewChanges = NO;
- marker.tracksInfoWindowChanges = NO;
- marker.map = nil;
+ [self.objects setObject:marker forKey:markerId];
}
} else {
- marker.map = nil;
- }
- }
-
- markerProperties = [changeProperties objectForKey:newMarkerId];
- if ([[_pluginMarkers objectForKey:newMarkerId] isEqualToString:@"DELETED"]) {
-
- if ([self.objects objectForKey:[NSString stringWithFormat:@"polygon%@", newMarkerId]]) {
- [self.objects removeObjectForKey:[NSString stringWithFormat:@"polygon%@", newMarkerId]];
+ @synchronized (self.objects) {
+ marker = [self.objects objectForKey:markerId];
}
-
- [self decreaseWaitCntOrExit:clusterId command:command];
- continue;
}
- marker.position = CLLocationCoordinate2DMake(
- [[markerProperties objectForKey:@"lat"] doubleValue], [[markerProperties objectForKey:@"lng"] doubleValue]);
+ //----------------------------------------
+ // Set the title and snippet properties
+ //----------------------------------------
if ([markerProperties objectForKey:@"title"]) {
marker.title = [markerProperties objectForKey:@"title"];
- } else {
- marker.title = nil;
}
if ([markerProperties objectForKey:@"snippet"]) {
marker.snippet = [markerProperties objectForKey:@"snippet"];
- } else {
- marker.snippet = nil;
- }
- marker.userData = newMarkerId;
-
- targetMarkerId = newMarkerId;
- if ([markerProperties objectForKey:@"geocell"]) {
- bounds = [self computeBox:[markerProperties objectForKey:@"geocell"]];
- boundsPath = [GMSMutablePath path];
- [boundsPath addCoordinate:bounds.northEast];
- [boundsPath addLatitude:bounds.northEast.latitude longitude:bounds.southWest.longitude];
- [boundsPath addCoordinate:bounds.southWest];
- [boundsPath addLatitude:bounds.southWest.latitude longitude:bounds.northEast.longitude];
-
- if (polygon == nil || polygon.userData == nil) {
- polygon = [GMSPolygon polygonWithPath: boundsPath];
- polygon.map = self.mapCtrl.map;
- polygon.strokeColor = UIColor.blackColor;
- polygon.strokeWidth = 2;
- polygon.userData = @"polygon";
- } else {
- polygon.path = boundsPath;
}
- } else if (polygon != nil && polygon.userData != nil) {
- polygon.userData = nil;
- polygon.map = nil;
- polygon = nil;
- if ([self.objects objectForKey:[NSString stringWithFormat:@"polygon%@", oldMakrerId]]) {
- [self.objects removeObjectForKey:[NSString stringWithFormat:@"polygon%@", oldMakrerId]];
- }
- }
-
- if (![oldMakrerId isEqualToString:newMarkerId]) {
- @synchronized (self.objects) {
- [self.objects setObject:marker forKey:newMarkerId];
- if (polygon != nil && polygon.userData != nil) {
- [self.objects setObject:polygon forKey:[NSString stringWithFormat:@"polygon%@", newMarkerId]];
- }
- [_pluginMarkers setObject:@"CREATD" forKey:newMarkerId];
- [self.objects removeObjectForKey:oldMakrerId];
- iconCacheKey = [self.objects objectForKey:[NSString stringWithFormat:@"marker_icon_%@", oldMakrerId]];
- if (iconCacheKey != nil) {
- [self.objects removeObjectForKey:[NSString stringWithFormat:@"marker_icon_%@", oldMakrerId]];
- [self.objects setObject:iconCacheKey forKey:[NSString stringWithFormat:@"marker_icon_%@", newMarkerId]];
- }
- [_pluginMarkers removeObjectForKey:oldMakrerId];
- }
- } else {
- @synchronized (self.objects) {
- [self.objects setObject:marker forKey:newMarkerId];
- if (polygon != nil && polygon.userData != nil) {
- [self.objects setObject:polygon forKey:[NSString stringWithFormat:@"polygon%@", newMarkerId]];
- }
- }
+/*
+ if (isNew == NO) {
+ @synchronized(self.pluginMarkers) {
+ [self.pluginMarkers setObject:@"CREATED" forKey:markerId];
}
+ [self decreaseWaitWithClusterId:clusterId];
+ continue;
}
-
+*/
if ([markerProperties objectForKey:@"icon"]) {
PluginMarkerCluster *self_ = self;
NSDictionary *icon = [markerProperties objectForKey:@"icon"];
- [self setIconToClusterMarker:targetMarkerId marker:marker iconProperty:icon callbackBlock:^(BOOL successed, id resultObj) {
+ [self setIconToClusterMarker:markerId marker:marker iconProperty:icon callbackBlock:^(BOOL successed, id resultObj) {
if (successed == NO) {
+ //--------------------------------------
+ // Could not read icon for some reason
+ //--------------------------------------
NSLog(@"(error) %@", resultObj);
} else {
+ //--------------------------------------
+ // Marker was updated
+ //--------------------------------------
marker.map = self_.mapCtrl.map;
}
- [self_ decreaseWaitCntOrExit:clusterId command:command];
+ [self_ decreaseWaitWithClusterId:clusterId];
}];
} else {
marker.map = self.mapCtrl.map;
@synchronized (self.pluginMarkers) {
- [self.pluginMarkers setObject:@"CREATED" forKey:targetMarkerId];
- }
- [self decreaseWaitCntOrExit:clusterId command:command];
- marker.icon = nil;
+ [self.pluginMarkers setObject:@"CREATED" forKey:markerId];
}
+ [self decreaseWaitWithClusterId:clusterId];
}
+ } // for (int i = 0; i < [updateClusterIDs count]; i++) {..}
- //---------
- // delete
- //---------
- for (int i = 0; i < [deleteClusterIDs count]; i++) {
- oldMakrerId = [deleteClusterIDs objectAtIndex:i];
- @synchronized (self.objects) {
- marker = [self.objects objectForKey:oldMakrerId];
- polygon = [self.objects objectForKey:[NSString stringWithFormat:@"polygon%@", oldMakrerId]];
- }
- @synchronized (self.pluginMarkers) {
- if (![[self.pluginMarkers objectForKey:oldMakrerId] isEqualToString:@"WORKING"]) {
- if (polygon != nil && polygon.userData != nil) {
- polygon.userData = nil;
- polygon.map = nil;
- polygon = nil;
- }
- @synchronized (self.objects) {
- [self _removeMarker:marker];
- marker = nil;
- if ([self.objects objectForKey:[NSString stringWithFormat:@"polygon%@", oldMakrerId]]) {
- [self.objects removeObjectForKey:[NSString stringWithFormat:@"polygon%@", oldMakrerId]];
- }
- }
- [self.pluginMarkers removeObjectForKey:oldMakrerId];
- } else {
- [self.pluginMarkers setObject:@"DELETED" forKey:oldMakrerId];
- }
- }
- }
- }];
+ }]; // [[NSOperationQueue mainQueue] addOperationWithBlock: ^{..}
+ dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+
+
+ }); // dispatch_async
}
- (void) setIconToClusterMarker:(NSString *) markerId marker:(GMSMarker *)marker iconProperty:(NSDictionary *)iconProperty callbackBlock:(void (^)(BOOL successed, id resultObj)) callbackBlock {
@@ -494,16 +401,15 @@ BOOL recyleOption = NO;
}];
}
-- (void) decreaseWaitCntOrExit:(NSString *) key command: (CDVInvokedUrlCommand*)command{
+- (void) decreaseWaitWithClusterId:(NSString *) clusterId{
@synchronized (_waitCntManager) {
- int waitCnt = [[_waitCntManager objectForKey:key] intValue];
+ int waitCnt = [[_waitCntManager objectForKey:clusterId] intValue];
waitCnt = waitCnt - 1;
- [_waitCntManager setObject:[NSNumber numberWithInt:waitCnt] forKey:key];
+ [_waitCntManager setObject:[NSNumber numberWithInt:waitCnt] forKey:clusterId];
if (waitCnt == 0) {
- CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
- [(CDVCommandDelegateImpl *)self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ dispatch_semaphore_signal(self.semaphore);
}
}
}

MODIFY
src/ios/GoogleMaps/PluginTileOverlay.h
src/ios/GoogleMaps/PluginTileOverlay.h
-(void)setZIndex:(CDVInvokedUrlCommand *)command;
-(void)setFadeIn:(CDVInvokedUrlCommand *)command;
-(void)setOpacity:(CDVInvokedUrlCommand *)command;
--(void)setVisible:(CDVInvokedUrlCommand *)command;
-(void)onGetTileUrlFromJS:(CDVInvokedUrlCommand *)command;
@end

MODIFY
www/MarkerCluster.js
www/MarkerCluster.js
@@ -127,7 +127,6 @@ var MarkerCluster = function(map, id, markerClusterOptions) {
console.log("self.taskQueue.push = " + self.taskQueue.length);
}, 5000);
}
-
return self;
};
@@ -225,8 +224,12 @@ MarkerCluster.prototype._redraw = function(clusterDistance, force) {
// Calculates geocells of the current viewport
//----------------------------------------------------------------
var visibleRegion = map.getVisibleRegion();
- var swCell = geomodel.getGeocell(visibleRegion.southwest.lat, visibleRegion.southwest.lng, resolution);
- var neCell = geomodel.getGeocell(visibleRegion.northeast.lat, visibleRegion.northeast.lng, resolution);
+ var expandedRegion = new LatLngBounds();
+ expandedRegion.extend(spherical.computeOffset(visibleRegion.farLeft, clusterDistance * 2, 315));
+ expandedRegion.extend(spherical.computeOffsetOrigin(visibleRegion.nearRight, clusterDistance * 2, 315));
+
+ var swCell = geomodel.getGeocell(expandedRegion.southwest.lat, expandedRegion.southwest.lng, resolution);
+ var neCell = geomodel.getGeocell(expandedRegion.northeast.lat, expandedRegion.northeast.lng, resolution);
if (currentZoomLevel > self.maxZoomLevel) {
resolution = -1;
@@ -257,7 +260,7 @@ MarkerCluster.prototype._redraw = function(clusterDistance, force) {
// return;
//}
if (ignoreGeocells.indexOf(geocell) === -1) {
- if (!visibleRegion.contains(marker.getPosition())) {
+ if (!expandedRegion.contains(marker.getPosition())) {
marker.set("isAdded", false, true);
ignoreGeocells.push(geocell);
} else {
@@ -282,8 +285,8 @@ MarkerCluster.prototype._redraw = function(clusterDistance, force) {
var cluster = self._clusters[prevResolution][geocell];
var bounds = cluster.getBounds();
- if (!visibleRegion.contains(bounds.northeast) &&
- !visibleRegion.contains(bounds.southwest)) {
+ if (!expandedRegion.contains(bounds.northeast) &&
+ !expandedRegion.contains(bounds.southwest)) {
ignoreGeocells.push(geocell);
if (cluster.getMode() === cluster.NO_CLUSTER_MODE) {
@@ -313,6 +316,7 @@ MarkerCluster.prototype._redraw = function(clusterDistance, force) {
});
} else if (prevResolution in self._clusters) {
+
keys = Object.keys(self._clusters[prevResolution]);
if (prevResolution < resolution) {
//--------------
@@ -373,8 +377,8 @@ MarkerCluster.prototype._redraw = function(clusterDistance, force) {
}
if (ignoreGeocells.indexOf(geocell) === -1) {
var bounds = geomodel.computeBox(geocell);
- if (visibleRegion.contains(bounds.northeast) ||
- visibleRegion.contains(bounds.southwest)) {
+ if (expandedRegion.contains(bounds.northeast) ||
+ expandedRegion.contains(bounds.southwest)) {
targetMarkers.push(marker);
allGeocells.push(geocell);
} else {
@@ -395,8 +399,8 @@ MarkerCluster.prototype._redraw = function(clusterDistance, force) {
}
if (ignoreGeocells.indexOf(geocell) === -1) {
var bounds = geomodel.computeBox(geocell);
- if (visibleRegion.contains(bounds.northeast) ||
- visibleRegion.contains(bounds.southwest)) {
+ if (expandedRegion.contains(bounds.northeast) ||
+ expandedRegion.contains(bounds.southwest)) {
targetMarkers.push(marker);
allGeocells.push(geocell);
} else {
@@ -434,7 +438,7 @@ MarkerCluster.prototype._redraw = function(clusterDistance, force) {
if (marker.get("isAdded")) {
return;
}
- if (!visibleRegion.contains(marker.get("position"))) {
+ if (!expandedRegion.contains(marker.get("position"))) {
return;
}
var geocell = marker.get("geocell").substr(0, resolution + 1);
@@ -539,7 +543,7 @@ MarkerCluster.prototype._redraw = function(clusterDistance, force) {
if (marker.get("isAdded")) {
return;
}
- if (visibleRegion.contains(marker.getPosition())) {
+ if (expandedRegion.contains(marker.getPosition())) {
var markerOptions = marker.getOptions();
markerOptions.count = 1;
if (self.debug) {

(Android / iOS) Version `cluster-20170728-1500`