25
votes

I am using AGM maps for my angular 4 application, there I am facing issues, I will be having the multiple markers which are fetched from api as an array of Latitude and Longitude. I want to set the zoom level exactly covering all the markers on the map. Even if one marker in one country and other in some other country also, It should set the zoom level on load to show all the markers on the map. Is there a way to do that in AGM angular maps? Could anyone please help me

5

5 Answers

46
votes

Objectives

We would like to set a zoom level of AGM map to show all the markers on the map. Typically in Google Maps JavaScript API you use the fitBounds() method of the google.maps.Map class to achieve this.

https://developers.google.com/maps/documentation/javascript/reference/3/map

So, we have to get instance of the map object (the JavaScript API instance) and apply fitBounds() on it.

Solution

I have created a simple example that has a mock service that provides JSON data for 5 markers and draws map and markers using AGM map. In this example I used @ViewChild decorator to get the instance of AGM map and listen the mapReady event to get instance of map object (from JavaScript API). Once I get map instance I can easily create LatLngBounds object and call fitBounds() on map object. Have a look at the ngAfterViewInit() method in the app.component.ts to get an idea.

Code snippet

app.component.ts

import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
import { MyMarker } from './marker';
import { MarkersService } from './markers.service';
import { GoogleMapsAPIWrapper, AgmMap, LatLngBounds, LatLngBoundsLiteral} from '@agm/core';

declare var google: any;

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, AfterViewInit {

  title = 'AGM project (so48865595)';
  lat = 41.399115;
  lng = 2.160962;
  markers: MyMarker[];

  @ViewChild('AgmMap') agmMap: AgmMap;

  constructor(private markersService: MarkersService) { }

  ngOnInit() {
    this.getMarkers();
  }

  ngAfterViewInit() {
    console.log(this.agmMap);
    this.agmMap.mapReady.subscribe(map => {
      const bounds: LatLngBounds = new google.maps.LatLngBounds();
      for (const mm of this.markers) {
        bounds.extend(new google.maps.LatLng(mm.lat, mm.lng));
      }
      map.fitBounds(bounds);
    });
  }

  getMarkers(): void {
    this.markers = this.markersService.getMarkers();
  }

  mapIdle() {
    console.log('idle');
  }
}

app.component.html

<h1>{{ title }}</h1>

<!-- this creates a google map on the page with the given lat/lng from -->
<!-- the component as the initial center of the map: -->
<agm-map #AgmMap [latitude]="lat" [longitude]="lng" (idle)="mapIdle()">
  <agm-marker *ngFor="let p of markers" [latitude]="p.lat" [longitude]="p.lng"></agm-marker>
</agm-map>

Conclusion

If you would like to check complete example please download the sample project:

https://github.com/xomena-so/so48865595

I hope this helps!

43
votes

Since September 2018 there is the AgmFitBounds directive. Super easy.

<agm-map
  style="width:100vw; height:100vh;"
  [fitBounds]="true">

  <agm-marker
    *ngFor="let location of locations"
    [latitude]="location.latitude"
    [longitude]="location.longitude"
    [agmFitBounds]="true"></agm-marker>
</agm-map>
1
votes

@renil-babu

Instead of doing the fitBounds on mapReady, you have to do it in your adding markers subscription block

      const bounds: LatLngBounds = new google.maps.LatLngBounds();
      for (const mm of this.markers) {
        bounds.extend(new google.maps.LatLng(mm.lat, mm.lng));
      }
      // @ts-ignore
      this.agmMap._mapsWrapper.fitBounds(bounds);
0
votes

I got it to work with the LatLngBounds() function. Here is the snippet.

import {AgmInfoWindow, MapsAPILoader} from '@agm/core';

latlngBounds: any;

// fits the map to the bounds
    if (this.points.length.) {
      this.mapsAPILoader.load().then(() => {
          this.latlngBounds = new window['google'].maps.LatLngBounds();
          _.each(this.points, location => {
            this.latlngBounds.extend(new window['google'].maps.LatLng(location.lat, location.lng));
          });
        }
      );
    }
0
votes

If you don't use agm-marker for some reason (you use html markers, as me) Then it is possible that your component is not supporting agmFitBounds accessor. In order to make it work write your custom component which implements FitBoundsAccessor:

import { ChangeDetectionStrategy, Component, forwardRef, Input, OnChanges, SimpleChanges } from '@angular/core';
import { FitBoundsAccessor, FitBoundsDetails } from '@agm/core';
import { BehaviorSubject, Observable } from 'rxjs';

@Component({
    selector: 'guide-map-bounds',
    template: `<ng-content></ng-content>`,
    providers: [{ provide: FitBoundsAccessor, useExisting: forwardRef(() => MapBoundsComponent) }],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class MapBoundsComponent implements FitBoundsAccessor, OnChanges {
    @Input() latitude: number;
    @Input() longitude: number;

    private fitBoundsDetails$ = new BehaviorSubject<FitBoundsDetails>(null);

    constructor() { }

    ngOnChanges(changes: SimpleChanges): void {
        const latLng: google.maps.LatLngLiteral = { lat: this.latitude, lng: this.longitude };

        this.fitBoundsDetails$.next({ latLng });
    }

    getFitBoundsDetails$(): Observable<FitBoundsDetails> {
        return this.fitBoundsDetails$.asObservable();
    }
}

Then in your html, inside agm-map

<guide-map-bounds [agmFitBounds]="true" *ngFor="let marker of markers" [latitude]="marker.lat" [longitude]="marker.lon">
  <agm-html-overlay [latitude]="marker.lat" [longitude]="marker.lon">
    <div class="marker" >
      
    </div>
  </agm-html-overlay>
</guide-map-bounds>

Please notice agm-html-overlay is custom component that doesn't support agmFitBounds