קיבוץ סמנים

בחירת פלטפורמה: Android iOS JavaScript

בדף הזה מתואר כלי לאיגוד סמנים שזמין בספריית כלי העזר של SDK של מפות ל-iOS.

קיבוץ סמנים מאפשר להציב מספר גדול של סמנים במפה בלי שהמפה תהיה קשה לקריאה. הכלי לאשכול סמנים עוזר לכם לנהל כמה סמנים ברמות שונות של שינוי גודל התצוגה.

כשמשתמש צופה במפה ברמת זום גבוהה, הסמנים הבודדים מוצגים במפה. כשמשתמש מצמצם את התצוגה, הסמנים מתקבצים לאשכולות כדי להקל על הצפייה במפה.

בצילום המסך הבא מוצג הסגנון שמוגדר כברירת מחדל לסימון מקבצים:

מפה עם סמנים מקובצים בסגנון ברירת המחדל

דוגמה לאוספים של סמנים בהתאמה אישית:

מפה עם סמנים מקובצים בהתאמה אישית

דרישות מוקדמות והערות

‫Maps SDK for iOS Utility Library

כלי האשכולות של הסמנים הוא חלק מספריית כלי השירות של SDK של מפות ל-iOS. אם עדיין לא הגדרתם את הספרייה, מומלץ לעיין במדריך ההגדרה לפני שתמשיכו לקרוא את שאר הדף הזה.

כדי להשיג את הביצועים הטובים ביותר, מומלץ להשתמש במקסימום 10,000 סמנים.

הרשאת מיקום

בדוגמה הזו נעשה שימוש ב-GPS של המכשיר כדי לאתר את המשתמש ולמקם את המפה בקואורדינטות שלו. כדי להפעיל את האפשרות הזו, צריך להוסיף תיאור להרשאה NSLocationWhenInUseUsageDescription בקובץ Info.plist של הפרויקט.

כדי להוסיף את זה:

  1. לוחצים על קובץ Info.plist בחלונית הניווט בפרויקט ב-Xcode כדי לפתוח את הכלי לעריכת רשימת מאפיינים.
  2. לוחצים על סמל הפלוס (+) לצד 'רשימת מאפייני מידע' כדי להוסיף מאפיין חדש.
  3. בשדה 'מפתח', מקלידים 'NSLocationWhenInUseUsageDescription'. ‫Xcode יתרגם את זה אוטומטית לשם הארוך Privacy - Location When In Use Usage Description. רשימה מלאה של מאפייני הרשאת המיקום האפשריים מופיעה במאמר בקשת הרשאה לשירותי מיקום בתיעוד של אפל למפתחים.
  4. משאירים את השדה 'סוג' מוגדר כ 'מחרוזת'.
  5. בשדה 'ערך', מקלידים תיאור של הסיבה שבגללה האפליקציה דורשת את השימוש במיקום של המשתמש. לדוגמה: "איתור המשתמש כדי לספק רשימות של עסקים בקרבת מקום".

הטמעת קיבוץ סמנים

הטמעה של קיבוץ סמנים כוללת שלושה שלבים:

  1. יוצרים מכונה לניהול אשכולות.
  2. מעבירים את הסמנים שרוצים לקבץ למנהל האשכולות.
  3. מפעילים את מנהל האשכולות.
כדי לראות דוגמה מלאה להטמעה של אשכולי סמנים, אפשר לעיין באפליקציות לדוגמה ב-Objective-C וב-Swift ב-GitHub.

יצירת מנהל האשכול

כדי להשתמש במרכז לניהול אשכולות:

  1. מגדירים את ViewController שבו המפה מוצגת כך שתתאים לפרוטוקול GMSMapViewDelegate.
  2. יוצרים מופע של GMUClusterManager.
  3. מעבירים את המופע של GMSMapView שרוצים להטמיע בו את התכונה 'קיבוץ סמנים' ואת ההטמעות של הפרוטוקולים הבאים למופע GMUClusterManager:
    • GMUClusterIconGenerator: מספק לוגיקה של אפליקציה שמביאה את סמלי האשכולות לשימוש ברמות זום שונות.
    • GMUClusterAlgorithm: מציין אלגוריתם שקובע את ההתנהגות של אופן קיבוץ הסמנים, כמו המרחק בין הסמנים שייכללו באותו אשכול.
    • GMUClusterRenderer: מספק לוגיקה של אפליקציה שמטפלת בעיבוד בפועל של סמלי האשכול במפה.
  4. מגדירים את נציג המפה במופע GMUClusterManager.

ספריית כלי העזר כוללת הטמעות ברירת מחדל של מחולל הסמלים (GMUDefaultClusterIconGenerator), האלגוריתם (GMUNonHierarchicalDistanceBasedAlgorithm) והרכיב לעיבוד התצוגה (GMUDefaultClusterRenderer). אפשר גם ליצור מחולל סמלים, אלגוריתם ורכיב לעיבוד תצוגה בהתאמה אישית.

הקוד הבא יוצר מנהל אשכולות באמצעות ברירות המחדל האלה בקריאה החוזרת (callback) של ViewController:viewDidLoad

Swift

import GoogleMaps
import GoogleMapsUtils

class MarkerClustering: UIViewController, GMSMapViewDelegate {
  private var mapView: GMSMapView!
  private var clusterManager: GMUClusterManager!

  override func viewDidLoad() {
    super.viewDidLoad()

    // Set up the cluster manager with the supplied icon generator and
    // renderer.
    let iconGenerator = GMUDefaultClusterIconGenerator()
    let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
    let renderer = GMUDefaultClusterRenderer(mapView: mapView,
                                clusterIconGenerator: iconGenerator)
    clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm,
                                                      renderer: renderer)

    // Register self to listen to GMSMapViewDelegate events.
    clusterManager.setMapDelegate(self)
    // ...
  }
  // ...
}
      

Objective-C

@import GoogleMaps;
@import GoogleMapsUtils;

@interface MarkerClustering () <GMSMapViewDelegate>

@end

@implementation MarkerClustering {
  GMSMapView *_mapView;
  GMUClusterManager *_clusterManager;
}

- (void)viewDidLoad {
  [super viewDidLoad];

  // Set up the cluster manager with a supplied icon generator and renderer.
  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];

  // Register self to listen to GMSMapViewDelegate events.
  [_clusterManager setMapDelegate:self];
  // ...
}
// ...
@end
      

הוספת סמנים

יש שתי דרכים להוסיף סמנים לקבוצת סמנים: בנפרד או כמערך.

סמן בודד

Swift

let position = CLLocationCoordinate2D(latitude: 47.60, longitude: -122.33)
let marker = GMSMarker(position: position)
clusterManager.add(marker)
      

Objective-C

CLLocationCoordinate2D position = CLLocationCoordinate2DMake(47.60, -122.33);
GMSMarker *marker = [GMSMarker markerWithPosition:position];
[_clusterManager addItem:marker];
      

מערך של סמנים

Swift

let position1 = CLLocationCoordinate2D(latitude: 47.60, longitude: -122.33)
let marker1 = GMSMarker(position: position1)

let position2 = CLLocationCoordinate2D(latitude: 47.60, longitude: -122.46)
let marker2 = GMSMarker(position: position2)

let position3 = CLLocationCoordinate2D(latitude: 47.30, longitude: -122.46)
let marker3 = GMSMarker(position: position3)

let position4 = CLLocationCoordinate2D(latitude: 47.20, longitude: -122.23)
let marker4 = GMSMarker(position: position4)

let markerArray = [marker1, marker2, marker3, marker4]
clusterManager.add(markerArray)
      

Objective-C

CLLocationCoordinate2D position1 = CLLocationCoordinate2DMake(47.60, -122.33);
GMSMarker *marker1 = [GMSMarker markerWithPosition:position1];

CLLocationCoordinate2D position2 = CLLocationCoordinate2DMake(47.60, -122.46);
GMSMarker *marker2 = [GMSMarker markerWithPosition:position2];

CLLocationCoordinate2D position3 = CLLocationCoordinate2DMake(47.30, -122.46);
GMSMarker *marker3 = [GMSMarker markerWithPosition:position3];

CLLocationCoordinate2D position4 = CLLocationCoordinate2DMake(47.20, -122.23);
GMSMarker *marker4 = [GMSMarker markerWithPosition:position4];

NSArray<GMSMarker *> *markerArray = @[marker1, marker2, marker3, marker4];
[_clusterManager addItems:markerArray];
      

הפעלת מקבץ הסמנים

אחרי שיוצרים את מקבץ הסמנים ומעבירים לו את הסמנים שרוצים לקבץ, כל מה שצריך לעשות הוא לקרוא למתודה cluster במופע של מקבץ הסמנים.

Swift

clusterManager.cluster()
      

Objective-C

[_clusterManager cluster];
      

טיפול באירועים בסמנים ובאשכולות

באופן כללי, כשמשתמשים ב-SDK של מפות ל-iOS, כדי להאזין לאירועים במפה צריך להטמיע את פרוטוקול GMSMapViewDelegate. אפשר להאזין לאירועים במפה, אבל אי אפשר להאזין לאירועים של מנהל האשכולות מסוג type-safe. כשמשתמש מקיש על סמן, על פריט ספציפי באוסף או על אוסף, ה-API מפעיל את mapView:didTapMarker: ומצרף את נתוני האוסף הנוספים למאפיין marker.userData. אחר כך אפשר לבדוק אם userData תואם לפרוטוקול GMUCluster כדי לקבוע אם בוצעה הקשה על סמל של צביר או על סמן.

Swift

func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
  // center the map on tapped marker
  mapView.animate(toLocation: marker.position)
  // check if a cluster icon was tapped
  if marker.userData is GMUCluster {
    // zoom in on tapped cluster
    mapView.animate(toZoom: mapView.camera.zoom + 1)
    NSLog("Did tap cluster")
    return true
  }

  NSLog("Did tap a normal marker")
  return false
}
      

Objective-C

- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker {
  // center the map on tapped marker
    [_mapView animateToLocation:marker.position];
    // check if a cluster icon was tapped
    if ([marker.userData conformsToProtocol:@protocol(GMUCluster)]) {
      // zoom in on tapped cluster
      [_mapView animateToZoom:_mapView.camera.zoom + 1];
      NSLog(@"Did tap cluster");
      return YES;
    }

    NSLog(@"Did tap marker in cluster");
    return NO;
}
      

מנהל האשכולות מיירט עכשיו את כל האירועים שהטמעתם ב-clusterManager. אם צוין נציג למפה, כל האירועים שנותרו מועברים אליו. שימו לב שאירועים של סמנים רגילים (כלומר, סמנים שלא נוצרו על ידי רכיב העיבוד של האשכול) תמיד מועברים לנציג המפה.

התאמה אישית של קיבוץ סמנים

אפשר לספק הטמעה מותאמת אישית של GMUClusterRenderer, ‏GMUClusterIconGenerator או GMUClusterAlgorithm. אתם יכולים לבסס את ההטמעה המותאמת אישית שלכם על ההטמעה לדוגמה של הפרוטוקולים האלה שכלולה בספריית כלי השירות, או שאתם יכולים לכתוב קוד להטמעה מותאמת אישית לחלוטין על ידי מילוי הפרוטוקולים.