Angular-google-maps: Fit Map Bounds to Makers/Elements Automatically?

Created on 7 Dec 2016  ·  38Comments  ·  Source: SebastianM/angular-google-maps

Can we add a Boolean input binding to the SebmGoogleMap component that automatically fits the map to the aggregate bounds of the markers/shapes/other map elements it contains?

If I'm not mistaken, it's pretty easy to do in the native Google Maps JS API..you just create a new bounds, then loop through all the elements on the map and extend the bounds, the finally tell the map object for fit the bounds.

Currently, its a pain to manually update the map bounds every time a marker changes. Thanks.

urgent AgmMap AgmMarker discussion / question feature-request

Most helpful comment

+1 :)

I need to bound the map around my marker on load. Here is how I solved that for now :

<agm-map (mapReady)="storeMapReady($event)">
    <agm-marker *ngFor="let store of storesList" [latitude]="store.latitude" [longitude]="store.longitude"></agm-marker>
</agm-map>
public storeMapReady(map){
    this.storeMap = map;
    this.storeMap.fitBounds(this.findStoresBounds());
}

public findStoresBounds(){
    let bounds:LatLngBounds = new google.maps.LatLngBounds();

    for(let store of this.storesList){
      bounds.extend(new google.maps.LatLng(store.latitude, store.longitude));
    }

    return bounds;
}

All 38 comments

Yeah I think also that this is a "must have" feature. But I'm struggling with a good API design.
Do you have a proposal?

I'm no expert in Angular2 API design, but what if it looked like this? Rather than specifying a zoom level and center lat/lng, you just give a fit-contents boolean?

<sebm-google-map [fitContents]="true">
...
</sebm-google-map>

...or maybe a an attribute directive?

<sebm-google-map sebmGoogleMapFitContents>
...
</sebm-google-map>

Support of bound to markers would be nice. Would prefer [fitContents]="true" as attribute

I would definitely favor [fitContents]="true" (or a similar name) over an attribute directive. It's impossible (as far as I know) to toggle presence of a directive easily, and with a boolean input it's simple to switch between the two.

It's also potentially better autocomplete in IDEs to have an input than a directive (eg. right now in WebStorm you type [ and get suggestions of all inputs -- directives aren't handled that well at the moment).

I'm doing research on the below requirement,

I need to make the map auto zoom / center based on the map points to be shown.
based on the google api and this library it looks like i need to make a "bounds" object and call .extend for each marker i need to show and then set or bind the fitBound property.
where i am stuck right now is how to populate the bounds object with the data.
i am having issues with how to create the LatLng object .
in the code i see an interface that has methods but no class that implements the interface
and the latlng interface says the lat and lng are methods not properties.
what am i missing in this picture ??

Could you please provide some sample solution or code snippet to zoom the markers area by using fitbounds?

Been thinking about this. "Fit content" sounds more like something of an action to me, rather than a property which can be turned on or off.

Some brainstorming:

  • A service with a method .fitContent() which would trigger latitude/longitude/zoom change (which can be regularly caught as an event from their respective outputs centerChange/zoomChange). But how do we specify which map to apply that on? My DI got rusty. We might need to get access to component instance with @ViewChild.
  • If we need @ViewChild anyway, we could make a public .fitContent() inside the component itself, without a service.
  • We could pass in an observable which the component would subscribe to and internally call .fitContent() whenever fired.

The most common use-case would be to always have all markers fit on the map. For that we could supply a directive/input which would execute .fitContent() whenever a change is detected in markers.

Feel free to discuss the API/behaviour here. I'll do some prototyping this week for this to see where it takes me.

A colleague and I have solved this myself with a directive.
The directive is added to the sebm-google-map element like this (where [fitContents] is our custom directive):

<sebm-google-map
                [fitContents]="fitContents"
                #bounds=fitContentsDirective
                [fitBounds]="bounds.mapBounds">
</sebm-google-map>

The fitContents directive has an input that is a Subject<Coordinates[]>. Every time there's an update to the map markers, the subject is updated with fitContents.next(coordinates) where "coordinates" is the full list of coordinates that should fit in the map.
The directive has a public property called mapBounds. This property contains a google LatLngBounds object that is passed into the [fitBounds] input of sebm-google-map.

The html template might not look pretty, but this works fine until some kind of official support should arrive.

The directive is available here: https://gist.github.com/glenngr/8b64194b86e4101386db22999813af10
Input, comments and improvements are welcome.

If a similar concept had been introduced into the angular2-google-maps component, some of the less pretty html syntax could perhaps be eliminated.

@jongunter @VaseemMalik @lazarljubenovic thx guys for the great discussion here!

I actually started to work on it a few days ago. It's not working yet, but you should get the idea:
https://github.com/SebastianM/angular2-google-maps/pull/868

Comments/ideas are very welcome!

any update on this ?

Any update ?

Any update?

Will be the next thing I'll finish once #1044 landed

Great!

Here for single polygon

```
public findCenterBounds(): LatLngBoundsLiteral {
let mostEast = this.paths.reduce( (acc, v) => (acc.lng < v.lng) ? v : acc );
let mostWest = this.paths.reduce( (acc, v) => (acc.lng > v.lng) ? v : acc );
let mostNorth = this.paths.reduce( (acc, v) => (acc.lat < v.lat) ? v : acc );
let mostSouth = this.paths.reduce( (acc, v) => (acc.lat > v.lat) ? v : acc );
return {east: mostEast.lng, west: mostWest.lng, north: mostNorth.lat, south: mostSouth.lat };
}

this._mapsApiWrapper.fitBounds(this.findCenterBounds());

@piotrmach Top! great!

If you only need two points, simplification based on @piotrmach solution:

public findCenterBounds(): LatLngBoundsLiteral {

    let a = this.origin;
    let b = this.destination;

    let mostEast  = (a.longitude > b.longitude) ? a.longitude : b.longitude;
    let mostWest  = (a.longitude < b.longitude) ? a.longitude : b.longitude;
    let mostNorth = (a.latitude > b.latitude) ? a.latitude : b.latitude;
    let mostSouth = (a.latitude < b.latitude) ? a.latitude : b.latitude;
    return <LatLngBoundsLiteral>{east: mostEast, west: mostWest, north: mostNorth, south: mostSouth };
  }

And don't forget to properly inject the native API, see https://stackoverflow.com/questions/44315771/setcenter-not-working-in-angular2-google-maps

It took me some time to figure out why fitBounds is not doing anything. AGM docs are nonexistent. (sadface)

Any update on this?

Any update ?

Any update ?

Any update?

I'm deeply interested in an update too.

Any update?

Any update?

+1 :)

I need to bound the map around my marker on load. Here is how I solved that for now :

<agm-map (mapReady)="storeMapReady($event)">
    <agm-marker *ngFor="let store of storesList" [latitude]="store.latitude" [longitude]="store.longitude"></agm-marker>
</agm-map>
public storeMapReady(map){
    this.storeMap = map;
    this.storeMap.fitBounds(this.findStoresBounds());
}

public findStoresBounds(){
    let bounds:LatLngBounds = new google.maps.LatLngBounds();

    for(let store of this.storesList){
      bounds.extend(new google.maps.LatLng(store.latitude, store.longitude));
    }

    return bounds;
}

@adrienlamotte can you please share the complete example of the above mentioned example it would really solve my problem ..Thanks in advance .

@MadhuAloor well i posted all the important code... The rest depend on your app! Which part don't you understand?

@adrienlamotte can you share the ... this piece .. I am trying to use this property of agm map [fitBounds]="bounds.mapBounds" but don't know how to assign an array of latitude ,longitude to mapBounds.. Can you please help me with this

@adrienlamotte @SebastianM @lazarljubenovic @glenngr I'm new to using agm maps and Angular 4 in general, i populated my map with markers from data in an array ,how can i get the data of only the markers that can be seen on the map, not all, just the ones visible on the map after i zoom, i dont know if its possible using the mapBounds . If it is ,i wouldnt mind some help and advice in doing it

@dwale Please do not use the GitHub issue tracker for support requests. Use Stack Overflow where you have a much larger chance of getting the answer and help you need. The issue tracker is or reporting bug, requesting features and tracking progress in th library. Thanks!

@adrienlamotte Thank you very much!! Your solution totally solved my problem!!

Thank you @adrienlamotte

Any update?
@adrienlamotte I am doing the same thing you are, but the map is way zoomed out.

public storeMapReady(map): void {
        this.map = map;
        this.findBounds(this.map);
    }

    public findBounds(map):void  {
        let bounds = new google.maps.LatLngBounds(null);
        for (let i = 0; i < this.visibleFilteredActivities.length; i++) {
            bounds.extend(new google.maps.LatLng(this.visibleFilteredActivities[i].geoPoint.latitude, this.visibleFilteredActivities[i].geoPoint.longitude));
        }
        map.fitBounds(bounds);
    }

@Defmetalhead dont know whether it is some reference issue with the 'map' object. Can you try this

public storeMapReady(map): void {
this.map = map;
this.map.fitBounds(this.findBounds());
}

public findBounds():void {
let bounds = new google.maps.LatLngBounds(null);
for (let i = 0; i < this.visibleFilteredActivities.length; i++) {
bounds.extend(new google.maps.LatLng(this.visibleFilteredActivities[i].geoPoint.latitude,
this.visibleFilteredActivities[i].geoPoint.longitude));
}
return bounds;
}

:up: vote for Fit Map Bounds to Makers/Elements Automatically

so any news for update?

Beta 4 Supports it now

@SebastianM How do I download the new Beta? Does fitbounds emits the current coords?

@syamlalz Codeblock / Markdown please! 🤢 Here

@SebastianM Of course a important feature. A workaround could be: Compute the middle point of points (e.g. of a Polyline) and the zoom value to set latLng and the zoom manually.

Fit to bounds could be an attribute of a drawing element (e.g. Polyline). If the element is ready (rendered) once / after change, the map can fit bounds.
Or the map itself gets an attribute with a reference (e.g. #myPolyline) to compute and fit the bounds. (On every input set.)

<agm-map [fitBounds]="myPolyline">
  <agm-polyline #myPolyline>...</agm-polyline>
</agm-map>

The Component input fitBounds should check the if a re-computing of the bounds is necessary.
Otherwise Angulars change detection will reduce the performance.
Just a spontaneous idea. Not sure if this works.

Was this page helpful?
0 / 5 - 0 ratings