Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto center based on marker's location #260

Closed
mmahalwy opened this issue May 20, 2016 · 19 comments
Closed

Auto center based on marker's location #260

mmahalwy opened this issue May 20, 2016 · 19 comments

Comments

@mmahalwy
Copy link

The best answer I have found is:

var bounds = new google.maps.LatLngBounds();
var infowindow = new google.maps.InfoWindow();    

for (i = 0; i < locations.length; i++) {  
  var marker = new google.maps.Marker({
    position: new google.maps.LatLng(locations[i][1], locations[i][2]),
    map: map
  });

  //extend the bounds to include each marker's position
  bounds.extend(marker.position);

  google.maps.event.addListener(marker, 'click', (function(marker, i) {
    return function() {
      infowindow.setContent(locations[i][0]);
      infowindow.open(map, marker);
    }
  })(marker, i));
}

//now fit the map to the newly inclusive bounds
map.fitBounds(bounds);

//(optional) restore the zoom level after the map is done scaling
var listener = google.maps.event.addListener(map, "idle", function () {
    map.setZoom(3);
    google.maps.event.removeListener(listener);
});

But how to do this with this library?

@dylantf
Copy link

dylantf commented May 20, 2016

I've done this just by having a property in state called center and passing that to the map. When you click on a marker, you can just update center with the coordinates of the marker.

@markogresak
Copy link

Helpful note here, which caused me to spend almost 1 hour in debugging: there are props defaultCenter and center on GoogleMap component. The defaultCenter doesn't seem to update even if it's changed later on, so changing this to center fixed it.

Hope this helps someone save some time in the future.

@mmahalwy
Copy link
Author

Thanks for the advice guys - I guess I was inquiring on if I have a number of markers, I'd like to set center or defaultCenter to make sure they are all on the map (say all the markers are in the UK, I don't want my center to be NYC).

@dylantf
Copy link

dylantf commented May 25, 2016

@mmahalwy Have you looked at the fitBounds method in the API? You give it a list of all your markers' coordinates and it automatically sets the bounds of your map to fit them all in.

@tomchentw
Copy link
Owner

We're working on this to make it clearer.

We're also looking for maintainers. Involve in #266 to help strengthen our community!

@ryan-hamblin
Copy link

This was closed, but was there ever an implementation into the library? It would save me a whole lot of time if someone had an example of how they dropped this functionality into the library.

@dylantf
Copy link

dylantf commented Jul 15, 2016

@ryanhca Are you just looking for the fitBounds implementation with react-google-maps? If so, it is an exposed method that you can access via refs on the map component.

@ryan-hamblin
Copy link

ryan-hamblin commented Jul 18, 2016

@Dyyylan thanks. I'll give it a whirl.

EDIT: After digging in further, I cannot seem to find the method. I've tried binding the component to a ref="map" and console.log(this.refs.map) shows no sign of the method. Did this get updated?

@zverbeta
Copy link

@Dyyylan I have same question

@dylantf
Copy link

dylantf commented Nov 12, 2016

It's important to note that since the actual map is mounted asynchronously from the react component, you may not have methods available as soon as it mounts.

So it depends on your use case for when you want to call the method, if you already have all your data when the map loads up, you can add a callback on the GoogleMap component with onTilesloaded and then use your map ref.

Here's an example that I just tested using the most recent version:
https://gist.github.com/dyyylan/a086ed46670efccace96404c8ab97ad8

@agilgur5
Copy link

Can confirm @Dyyylan's method works (thanks!), though keep in mind onTilesloaded is called during every resize, pan, zoom, etc. If you need to call it exactly once on initialization, you'll have to use some hacky method like below:

onTilesloaded = () => {
  this.fitBounds()
  // haxxors -- make sure to only fire a single time
  this.onTilesloaded = () => {}
}

@ryan-hamblin
Copy link

Wow, this worked really well for me! Thank you. I'm now just diggin in to see if I can force the map to fit bounds, but also zoom out a little. The fit bounds sort of force it to zoom in too far.

@ryan-hamblin
Copy link

ryan-hamblin commented Dec 2, 2016

following up. I used @agilgur5 's solution above to only call my set bounds handler once. Now my issue is that the bounds are too far zoomed in if the coords all exist in or near the same place. I'm looking to expose the "setZoom" method on the "map" ref. I can use "getZoom" but not setZoom for some reason. I'm digging in deeper

Edit/Solution

after my setBoundsHandler is done doing what it's doing, I set the state of a zoom property to whatever number I want. And on the

short snippet:
be sure to import LatLng and LatLngBounds accordingly

const { LatLng, LatLngBounds }  = google.maps;
    _fitTheBounds(){
        const bounds = new LatLngBounds();
        const {markers, ...props} = this.props;
        let newBounds = [];
        markers.map(m=>{
            newBounds.push(new LatLng(Number(m.lat), Number(m.lng)))
        })
        newBounds.forEach(bound => bounds.extend(bound));
        this.refs.map.fitBounds(bounds);
        this._fitTheBounds = () => {}
        let currentZoom = this.refs.map.getZoom();
        console.log(currentZoom)
        if(currentZoom > 15){
            setTimeout(()=>{
                this.setState({defaultZoom: 15})
            }, 100)
        }
    }
<GoogleMap
       {...this.props}
       zoom={this.state.defaultZoom}
       ref="map"
        onTilesloaded={()=>this._fitTheBounds()}

@dylantf
Copy link

dylantf commented Dec 2, 2016

There is a difference between defaultZoom and zoom, just like defaultValue and value on an <input /> component.

If you want to control the zoom dynamically, just use the zoom prop on the map, it will update whenever the value changes. But it does also allow the user to zoom/pan around themself without being locked into whatever value you set.

Most (in my experience anyway) of the setFoo methods are just implemented as foo as a prop on the component.

@Andriy-Kulak
Copy link

@ryanhca I am working on a similar problem for fitting map view in bounds, but I am getting inconsistent results. Do you have sample code of the component you can share by any chance? Thanks!

@ryan-hamblin
Copy link

ryan-hamblin commented Jan 9, 2017

@Andriy-Kulak So I had this working really well, then something broke my implementation ... IDK if it was the Google api being updated or some faulty logic on my end. But I've tabled a fix for this for a little bit until I have some time to go back to it. I'll let you know what comes of it when/if I get it re-implemented.

For now I updated my code snippet above to show you what I did for the zoom. I just set the zoom to a state property and then onTilesLoaded calls the method I use to fit the bounds. Within that method I check to see what the zoom is defaulting too with the new bounds and setState if its zoomed in too far .

@Andriy-Kulak
Copy link

@ryanhca I ended up doing to a workaround for fitbounds. the google maps geometry api was also giving me trouble so I decided to take a different approach. I have two points on a map so all I need to do is to make sure the map is centered in between the two points and the zoom is appropriate.

I calculated the middle point between two points and used that a defaultCenter using geoLib npm package (geoLib.getCenter(...) . This package can actually calculate mid-point between multiple coordinates so it works well for 2+ points as well.

Then, I calculated the distance from one point to another and used that to proportionally increase/decrease my defaultZoom... using geoLib.getDistance... or you can use Pythagorean theory most people learned in high school geometry class.

@dylantf
Copy link

dylantf commented Jan 10, 2017

@Andriy-Kulak my gist above is a complete component with fitBounds, if you needed an example of how to use it.

@agilgur5
Copy link

I'll note that on my solution, I actually added a line at the end, this.forceUpdate() due to a React nuance:

onTilesloaded = () => {
  this.fitBounds()
  // haxxors -- make sure to only fire a single time
  this.onTilesloaded = () => {}
  this.forceUpdate()
}

As I didn't change the state in my callback, it doesn't cause the React part of the view to rerender, and therefore the binding on the component onTilesloaded={this.onTilesloaded} doesn't get rebound. forceUpdate forces the stale reference to be updated.

There are likely a few alternatives to this. Also note that this is only necessary if you want it to fire exactly once. That new line isn't on production yet, but you can see this working/in-action here (click the "Map" button) on Yorango's Map View.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants