This is the simplest I could reduce it to:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>Google Maps Multiple Markers</title>
<script src="http://maps.google.com/maps/api/js?key=YOUR_API_KEY"
type="text/javascript"></script>
</head>
<body>
<div id="map" style="width: 500px; height: 400px;"></div>
<script type="text/javascript">
var locations = [
['Bondi Beach', -33.890542, 151.274856, 4],
['Coogee Beach', -33.923036, 151.259052, 5],
['Cronulla Beach', -34.028249, 151.157507, 3],
['Manly Beach', -33.80010128657071, 151.28747820854187, 2],
['Maroubra Beach', -33.950198, 151.259302, 1]
];
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 10,
center: new google.maps.LatLng(-33.92, 151.25),
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var infowindow = new google.maps.InfoWindow();
var marker, i;
for (i = 0; i < locations.length; i++) {
marker = new google.maps.Marker({
position: new google.maps.LatLng(locations[i][1], locations[i][2]),
map: map
});
google.maps.event.addListener(marker, 'click', (function(marker, i) {
return function() {
infowindow.setContent(locations[i][0]);
infowindow.open(map, marker);
}
})(marker, i));
}
</script>
</body>
</html>
👨💻 Edit/fork on a Codepen →
SCREENSHOT
There is some closure magic happening when passing the callback argument to the addListener
method. This can be quite a tricky topic if you are not familiar with how closures work. I would suggest checking out the following Mozilla article for a brief introduction if it is the case:
❯ Mozilla Dev Center: Working with Closures
As of today, You don't need to use external classes, just make your markers "draggable"
GMSMarker *myMarker = [[GMSMarker alloc] ...];
...
[myMarker setDraggable: YES];
// Use some kind of data to identify each marker, marker does not have 'tag' but 'userData' that is an 'id' type
[myMarker setUserData: markerId];
And implement the respective delegate
@interface YourController: UIViewController<GMSMapViewDelegate> ...
Set your delegate
GMSMapView *myMapView = [[GMSMapView alloc] ...];
...
myMapView.delegate = self;
Then you can handle each marker event, ie:
-(void)mapView:(GMSMapView *)mapView didEndDraggingMarker:(GMSMarker *)marker{
if ([marker.userData isEqualtoString:@"xMark"])
NSLog(@"marker dragged to location: %f,%f", marker.position.latitude, marker.position.longitude);
}
Best Answer
To understand the underlying concept of this double map solution, please have a look at this WWDC 2011 video (from 22'30). The Map kit code is directly extracted from this video except a few things that I described in a few notes. The Google Map SDK solution is just an adaptation.
Main idea: a map is hidden and holds every single annotation, including the merged ones (
allAnnotationMapView
in my code). Another is visible and shows only the cluster's annotations or the annotation if it's single (mapView in my code).Second main idea: I divide the visible map (plus a margin) into squares, and every annotation in a specific square are merged into one annotation.
The code I use for Google Maps SDK (Please note that I wrote this when
markers
property were available on GMSMapView class. It's not anymore but you can keep track of all the marker you add in your own array, and use this array instead of calling mapView.markers):A few notes:
PointMapItem
is my annotation data class (id<MKAnnotation>
if we were working with Map kit).shouldBeMerged
property onPointMapItem
because there are some annotations I don't want to merge. If you do not need this, remove the part that is using it or setshouldBeMerged
to YES for all your annotations. Though, you should probably keep the class testing if you don't want to merge user location!allAnnotationMapView
and callupdateVisibleAnnotation
.updateVisibleAnnotation
method is responsible for choosing which annotations to merge and which to show. It will then add the annotation tomapView
which is visible.For Map Kit I use the following code:
Both should work fine, but if you have any question, feel free to ask!