Как использовать кластеризацию маркеров в Angular 10

#angular #google-maps

#angular #google-карты

Вопрос:

Следующий код взят из этой официальной документации и довольно хорошо работает в моем приложении Angular 10: https://developers.google.com/maps/documentation/javascript/marker-clustering?hl=en

 import { AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, ViewChild } from '@angular/core'
import { Coordinates } from '@shared/models/coordinates.model';
import MarkerClusterer from '@googlemaps/markerclustererplus'

@Component({
  selector: 'store-map',
  template: `<div id="map" style="height: 500px"></div>`,
  styleUrls: ['./store-map.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class StoreMapComponent implements AfterViewInit {

  @ViewChild('map', {static: false}) info: ElementRef;

  ngAfterViewInit() {
    const locations = [
      { lat: -31.56391, lng: 147.154312 },
      { lat: -33.718234, lng: 150.363181 },
      { lat: -33.727111, lng: 150.371124 },
      { lat: -33.848588, lng: 151.209834 },
      { lat: -33.851702, lng: 151.216968 },
      { lat: -34.671264, lng: 150.863657 },
      { lat: -35.304724, lng: 148.662905 },
      { lat: -36.817685, lng: 175.699196 },
      { lat: -36.828611, lng: 175.790222 },
      { lat: -37.75, lng: 145.116667 },
      { lat: -37.759859, lng: 145.128708 },
      { lat: -37.765015, lng: 145.133858 },
      { lat: -37.770104, lng: 145.143299 },
      { lat: -37.7737, lng: 145.145187 },
      { lat: -37.774785, lng: 145.137978 },
      { lat: -37.819616, lng: 144.968119 },
      { lat: -38.330766, lng: 144.695692 },
      { lat: -39.927193, lng: 175.053218 },
      { lat: -41.330162, lng: 174.865694 },
      { lat: -42.734358, lng: 147.439506 },
      { lat: -42.734358, lng: 147.501315 },
      { lat: -42.735258, lng: 147.438 },
      { lat: -43.999792, lng: 170.463352 },
    ];
    const map = new google.maps.Map(document.getElementById("map") as HTMLElement, {
        zoom: 3,
        center: { lat: -28.024, lng: 140.887 },
      }
    )

    const labels = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
    const markers = locations.map((location, i) => {
      return new google.maps.Marker({
        position: location,
        label: labels[i % labels.length],
      });
    });

    new MarkerClusterer(map, markers, {
      imagePath:
        "https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m",
    });

  }
}
 

Кластеризация маркеров здесь работает хорошо. К сожалению, на самом деле это не «угловой способ» для достижения того, чего я хочу. Что я хочу, так это использовать «новый» собственный компонент Google Maps из Angular. Итак, вот что я сделал (без кластеризации маркеров):

Компонент:

 @Component({
  selector: 'store-map',
  templateUrl: './store-map.component.html',
  styleUrls: ['./store-map.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class StoreMapComponent {

  @Input()
  coordinates: Coordinates

  @Input()
  zoom: number

  @Input()
  set stores(value: Store[]) {
    // Allow time for the map to load before showing markers
    if (this.isFirstLoad amp;amp; value.length > 0) {
      this.isFirstLoad = false
      setTimeout(() => {
        this._stores = value
        this.changeDetector.markForCheck() // Needed because of setTimeout()
      }, 1000)
    } else {
      this._stores = value
    }

  }

  private isFirstLoad = true

  _stores: Store[] = []
  markerOptions: google.maps.MarkerOptions = {
    animation: Animation.DROP,
    icon: {
      url: '/assets/img/yarn.png',
      size: new google.maps.Size(32, 32)
    }
  }

  constructor(private changeDetector: ChangeDetectorRef) { }

  getStoreLatLngLiteral(store: Store): google.maps.LatLngLiteral {
    return this.mapCoordinatesToLatLngLiteral({
      latitude: store.lat,
      longitude: store.lng
    })
  }

  mapCoordinatesToLatLngLiteral(coordinates: Coordinates): google.maps.LatLngLiteral {
    return {
      lat: coordinates.latitude,
      lng: coordinates.longitude
    }
  }
 

Шаблон:

 <google-map [center]="mapCoordinatesToLatLngLiteral(coordinates)" [zoom]="zoom" width="auto">
  <map-marker *ngFor="let store of _stores" [position]="getStoreLatLngLiteral(store)"
              [title]="store.name"
              [options]="markerOptions">
  </map-marker>
</google-map>
 

Проблема в том, что на данный момент я не знаю, как настроить кластеризацию маркеров «угловым способом».

Ответ №1:

Если вы хотите использовать официальную документацию, вы можете просто загрузить JS-скрипт непосредственно на index.html файл, вместо того, чтобы полагаться на сторонние модули / пакеты, которые имеют свою собственную документацию.

Вот пример для справки: https://stackblitz.com/edit/angular-marker-cluster-64960574

index.html

 <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
<script src="https://unpkg.com/@google/markerclustererplus@4.0.1/dist/markerclustererplus.min.js"></script>
<my-app>loading</my-app>
 

style.css

 html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}

.map {
  height:100%;
}
 

map-with-marker.component.html

 <div #map class="map"></div>
 

map-with-marker.component.ts

 import { Component, OnInit, ViewChild } from "@angular/core";
declare const google: any;

@Component({
  selector: "my-maps",
  templateUrl: "./map-with-marker.component.html",
  styleUrls: ["./map-with-marker.component.css"]
})
export class MapComponent implements OnInit {
  @ViewChild("map", { static: true }) mapElement: any;
  map: any;

  constructor() {}

  ngOnInit() {
    const locations = [
      { lat: -31.56391, lng: 147.154312 },
      { lat: -33.718234, lng: 150.363181 },
      { lat: -33.727111, lng: 150.371124 },
      { lat: -33.848588, lng: 151.209834 },
      { lat: -33.851702, lng: 151.216968 },
      { lat: -34.671264, lng: 150.863657 },
      { lat: -35.304724, lng: 148.662905 },
      { lat: -36.817685, lng: 175.699196 },
      { lat: -36.828611, lng: 175.790222 },
      { lat: -37.75, lng: 145.116667 },
      { lat: -37.759859, lng: 145.128708 },
      { lat: -37.765015, lng: 145.133858 },
      { lat: -37.770104, lng: 145.143299 },
      { lat: -37.7737, lng: 145.145187 },
      { lat: -37.774785, lng: 145.137978 },
      { lat: -37.819616, lng: 144.968119 },
      { lat: -38.330766, lng: 144.695692 },
      { lat: -39.927193, lng: 175.053218 },
      { lat: -41.330162, lng: 174.865694 },
      { lat: -42.734358, lng: 147.439506 },
      { lat: -42.734358, lng: 147.501315 },
      { lat: -42.735258, lng: 147.438 },
      { lat: -43.999792, lng: 170.463352 }
    ];

    const labels = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    var center = { lat: -33.8569, lng: 151.2152 };
    const mapProperties = {
      center: center,
      zoom: 11,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    this.map = new google.maps.Map(
      this.mapElement.nativeElement,
      mapProperties
    );

    this.addMarker(locations, labels);
  }
  addMarker(locations, labels) {
    const markers = locations.map((location, i) => {
      return new google.maps.Marker({
        position: location,
        label: labels[i % labels.length],
        map: this.map
      });
    });
    new MarkerClusterer(this.map, markers, {
      imagePath:
        "https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m"
    });
  }
}

 

Если вы столкнулись google is not defined , просто поставьте пробел в любой части кода (например, в файле .ts), поскольку у Stackblitz, похоже, есть проблема.

Комментарии:

1. Как я уже говорил в своем первоначальном посте, кластеризация маркеров хорошо работает, когда я использую JS-скрипты напрямую. Но я хотел бы сделать это «угловым способом», используя компонент и директивы Angular.

Ответ №2:

С помощью пакета Google Maps от Angular ( @angular/google-maps ) вы можете использовать кластеры маркеров аналогично приведенному выше примеру;

component.ts

 import {Component} from '@angular/core';

@Component({
  selector: 'google-map-demo',
  templateUrl: 'google-map-demo.html',
})
export class GoogleMapDemo {
  center: google.maps.LatLngLiteral = {lat: 24, lng: 12};
  zoom = 4;
  markerPositions: google.maps.LatLngLiteral[] = [];
  markerClustererImagePath =
      'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m';

  addMarker(event: google.maps.MouseEvent) {
    this.markerPositions.push(event.latLng.toJSON());
  }
}
 

component.html

 <google-map height="400px"
            width="750px"
            [center]="center"
            [zoom]="zoom"
            (mapClick)="addMarker($event)">
  <map-marker-clusterer [imagePath]="markerClustererImagePath">
    <map-marker *ngFor="let markerPosition of markerPositions"
                [position]="markerPosition"></map-marker>
  </map-marker-clusterer>
</google-map>
 

Полная ссылка на их документацию и как загрузить библиотеку здесь

Это будет доступно в версии 11.1.0 (как здесь) или вы можете загрузить сборку сейчас.

Комментарии:

1. Можно ли что-нибудь сделать, чтобы заставить это работать, например, импортировать модуль или что-то в этом роде? Потому что Angular говорит мне об этом 'map-marker-clusterer' is not a known element . Я использую @angular/google-maps@10.2.7

2. @Flobesst Вы перешли по ссылке, о которой я упоминал выше, о загрузке библиотеки через <script src="https://unpkg.com/@googlemaps/markerclustererplus/dist/index.min.js"></script> as, как она предлагает? Или через @googlemaps/markerclustererplus

3. Да, я добавил скрипт в свой index.html , и у меня все еще есть то же сообщение об ошибке.

4. Похоже, что его еще предстоит развернуть как часть пакета Angular в версии 11.1.0. Обновленный ответ с соответствующими ссылками и загрузкой.

5. О, хорошо, поэтому мне придется обновить Angular до последней версии. Я дам вам знать.