Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Marker using GMUClusterManager

I want to display Custom marker using GMUClusterManager. I followed all steps for marker clustering here.

but there is blue and red color Icon like this. enter image description here

But when I Zoom in that map it display only red color Marker but I don't want that.

there is instance method where I have implemented my logic but no use.

    - (instancetype)initWithMapView:(GMSMapView *)mapView clusterIconGenerator:(id<GMUClusterIconGenerator>)iconGenerator
{
    if ((self = [super init])) {

        GMSMarker *marker= [GMSMarker markerWithPosition:CLLocationCoordinate2DMake(24.0, 75.30)];

        UIView *customMarker =[[UIView alloc] initWithFrame:CGRectMake(0, 0, 63, 40)];
        customMarker.backgroundColor = [UIColor blueColor];

        marker.iconView = [self EmployeeMarker:0] ;
        marker.appearAnimation = kGMSMarkerAnimationPop;
        marker.map = mapView;
    }
    return self;
}

-(UIView *)EmployeeMarker:(int)labelTextInt{
    UIView *customMarker =[[UIView alloc] initWithFrame:CGRectMake(0, 0, 63, 40)];
    UIImageView *imgViewCustomMarker = [[UIImageView alloc]initWithFrame:CGRectMake(0, 15, 24, 25)];
    imgViewCustomMarker.image = [UIImage imageNamed:@"iconMapUser.png"];
    [customMarker addSubview:imgViewCustomMarker];
    UIView *viewRatingCustom = [[UIView alloc] initWithFrame:CGRectMake(15, 0, 40, 15)];
    viewRatingCustom.backgroundColor = [UIColor colorWithRed:192.0/255.0 green:192.0/255.0 blue:192.0/255.0 alpha:1.0];
    UILabel *lblRatingEmployees = [[UILabel alloc] initWithFrame:CGRectMake(8, 1, 17,8)];
    lblRatingEmployees.textColor = [UIColor colorWithRed:0.00/255.0 green:100.0/255.0 blue:150.0/255.0 alpha:1.0];
    lblRatingEmployees.text = @"1";
    lblRatingEmployees.font = [UIFont fontWithName:@"Helvetica-Bold" size:10];
    [lblRatingEmployees sizeToFit];
    [viewRatingCustom addSubview:lblRatingEmployees];
    UIImageView *imageViewStar = [[UIImageView alloc] initWithFrame:CGRectMake(25, 3, 10, 8)];
    imageViewStar.image = [UIImage imageNamed:@"iconBlueStar.png"];
    [viewRatingCustom addSubview:imageViewStar];
    [customMarker addSubview:viewRatingCustom];
    return customMarker;
}

I have used this method for display possible number of Marker that is by default red.

id<GMUClusterAlgorithm> algorithm = [[GMUNonHierarchicalDistanceBasedAlgorithm alloc] init];

id<GMUClusterIconGenerator> iconGenerator = [[GMUDefaultClusterIconGenerator alloc] init];


id<GMUClusterRenderer> renderer =
  [[GMUDefaultClusterRenderer alloc] initWithMapView:_mapView
                                clusterIconGenerator:iconGenerator];

_clusterManager =
  [[GMUClusterManager alloc] initWithMap:_mapView algorithm:algorithm renderer:renderer];

 // Generate and add random items to the cluster manager.

// [self generateClusterItems];


for (int i = 0; i<latitudeArray.count; i++) {

    id<GMUClusterItem> item =

    [[POIItem alloc]initWithPosition:CLLocationCoordinate2DMake([[latitudeArray objectAtIndex:i]doubleValue], [[longitudeArray objectAtIndex:i]doubleValue]) name:@"Name"];

    [_clusterManager addItem:item];
}

Adde delegates and also cluster method.

 [_clusterManager cluster];
 [_clusterManager setDelegate:self mapDelegate:self];

So please help me for adding custom marker in place of red that is in default.

like image 702
Mad Burea Avatar asked Nov 28 '16 05:11

Mad Burea


4 Answers

In Swift 4.2 :

add this exstention to your controller and be sure that your controller is the delegate of GMUClusterRendererDelegate :

willRenderMarker will call each time a marker going to be render (both cluster marker and clusterItemMarker so you can check it by simple if).so you can modify it's icon and etc before showing it to user

extension YourController: GMUClusterRendererDelegate {
    func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {
        // if your marker is pointy you can change groundAnchor
        marker.groundAnchor = CGPoint(x: 0.5, y: 1)
        if  let markerData = (marker.userData as? PersonMarker) {
           let icon = markerData.imageURL
           marker.iconView = CustomMarkerView(forUrl: url)
        }
    }
}

And PersonMarker is your marker class that subclass NSObject and GMUClusterItem : (you can use default class of GMUClusterItem but if you need some other properties you can subclass it)

class PersonMarker: NSObject, GMUClusterItem {

  var position: CLLocationCoordinate2D
  var imageURL : String?
  var name: String?
  var userdId: String?
  var lastSeen: String?

  init(position: CLLocationCoordinate2D, url: String?, name: String?, userId: String?, lastSeen: String?) {
      self.position = position
      self.imageURL = url
      self.name = name
      self.userdId = userId
      self.lastSeen = lastSeen
  }

}

You can add PersonMarker to your GMUClusterManager like this :

let position = CLLocationCoordinate2D(latitude: item.latitude!, longitude: item.longitute!)
let person = PersonMarker(position: position, url: item.user?.avaterUrl, name: item.user?.name, userId: item.user?.userId, lastSeen: item.lastUpdate)
clusterManager.add(person)
like image 162
andesta.erfan Avatar answered Nov 14 '22 09:11

andesta.erfan


You need custom class implementing GMUClusterIconGenerator with designed func icon(forSize size: UInt) -> UIImage! method which returns UIImage for your cluster.

I suggest creating view and functionality to add designed label with cluster's title to it and then creating UIImage from your UIView.

Then you'll be able to create your custom cluster generator like this:

let iconGenerator = ClusterIconGenerator()

Example result:

enter image description here


Working generator:

class ClusterIconGenerator: NSObject, GMUClusterIconGenerator {

    private struct IconSize {

        private let initialFontSize: CGFloat = 12
        private let fontMultiplier: CGFloat = 0.1

        private let initialSize: CGFloat = 25
        private let sizeMultiplier: CGFloat = 0.18

        /**
         Rounded cluster sizes  (like 10+, 20+, etc.)
         */
        private let sizes = [10,20,50,100,200,500,1000]

        let size: UInt

        /**
         Returns scale level based on size index in `sizes`. Returns `1` if size doesn't have rounded representation
         */
        private var scaleLevel: UInt {
            if let index = sizes.lastIndex(where: { $0 <= size }) {
                return UInt(index) + 2
            } else {
                return 1
            }
        }

        /**
         Returns designed title from cluster's size
         */
        var designedTitle: String {
            if let size = sizes.last(where: { $0 <= size }) {
                return "\(size)+"
            } else {
                return "\(size)"
            }
        }

        /**
         Returns initial font size multiplied by recursively created multiplier
         */
        var designedFontSize: CGFloat {
            let multiplier: CGFloat = (1...scaleLevel).reduce(1) { n,_ in n + n * fontMultiplier }
            return initialFontSize * multiplier
        }

        /**
         Returns initial `CGSize` multiplied by recursively created multiplier
         */
        var designedSize: CGSize {
            let multiplier: CGFloat = (1...scaleLevel).reduce(1) { n,_ in n + n * sizeMultiplier }
            return CGSize(width: initialSize * multiplier, height: initialSize * multiplier)
        }

    }

    /**
     Returns image based on current cluster's size
     */
    func icon(forSize size: UInt) -> UIImage! {

        let iconSize = IconSize(size: size)

        let frame = CGRect(origin: .zero, size: iconSize.designedSize)

        let view = UIView(frame: frame)
        view.layer.cornerRadius = iconSize.designedSize.height / 2
        view.backgroundColor = .green

        let label = UILabel(frame: frame)
        label.textAlignment = .center
        label.textColor = .white
        label.text = iconSize.designedTitle
        view.addSubview(label)

        return view.asImage
    }

}

extension UIView {

    var asImage: UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }

}
like image 27
Robert Dresler Avatar answered Nov 14 '22 10:11

Robert Dresler


You need to create custom class, which conforms to GMUClusterIconGenerator protocol:

CustomClusterIconGenerator.h file

@interface CustomClusterIconGenerator : NSObject
<GMUClusterIconGenerator>

@end

CustomClusterIconGenerator.m file

@implementation CustomClusterIconGenerator

- (UIImage *)iconForSize:(NSUInteger)size {
    // Return custom icon for cluster
    return [UIImage imageNamed:@"Your Custom Cluster Image"];
}

- (UIImage *)iconForMarker {
    // Return custom icon for pin
    return [UIImage imageNamed:@"Your Custom Marker Image"];
}

- (CGPoint)markerIconGroundAnchor {
    // If your marker icon center shifted, return custom value for anchor
    return CGPointMake(0, 0);
}

- (CGPoint)clusterIconGroundAnchor {
    // If your cluster icon center shifted, return custom value for anchor
    return CGPointMake(0, 0);
}

@end

and then then, instead of

id<GMUClusterIconGenerator> iconGenerator = [[GMUDefaultClusterIconGenerator alloc] init];

use

CustomClusterIconGenerator *iconGenerator = [[GMUDefaultClusterIconGenerator alloc] init];

Here is example from my project: enter image description here

like image 42
Vitalii Gozhenko Avatar answered Nov 14 '22 10:11

Vitalii Gozhenko


As of version 1.1.0, new features were added for easy customization of markers (read more).

You can add the GMUClusterRendererDelegate and GMUDefaultClusterRenderer.h and add the method - (void)renderer:(id<GMUClusterRenderer>)renderer willRenderMarker:(GMSMarker *)marker;

There, you can customize your markers and clusters. For example:

- (void)renderer:(id<GMUClusterRenderer>)renderer willRenderMarker:(GMSMarker *)marker{
    if ([marker.userData conformsToProtocol:@protocol(GMUCluster)]) {
        marker.icon=[UIImage imageNamed:@"custom_cluster_image.png"];
    }else if ([marker.userData conformsToProtocol:@protocol(GMUClusterItem)]) {
        marker.icon=[UIImage imageNamed:@"custom_marker_image.png"];
    }
}

Remember to set the delegate properly:

id<GMUClusterRenderer> renderer = [[GMUDefaultClusterRenderer alloc] initWithMapView:_mapView clusterIconGenerator:iconGenerator];
((GMUDefaultClusterRenderer *)renderer).delegate=self;
like image 27
Gerardo Avatar answered Nov 14 '22 10:11

Gerardo