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

How to access the map.flyTo method and use it dynamically ? #484

Closed
aziz-boudi4 opened this Issue Dec 24, 2017 · 14 comments

Comments

Projects
None yet
7 participants
@aziz-boudi4
Copy link

commented Dec 24, 2017

@alex3165
I'm currently using the map.flyTo inside the
onStyleLoad={map => { }}

It's working fine but, the flyTo happens at the initial render

my goal is to update the map.flyTo options dynamically on button Click?

how can I achieve that?
is the map.flyTo provided as a prop?

here's my current code:

constructor(props) {
  super(props);
    this.state = {
      flyTo: { center: [-118.4107187, 33.9415889], zoom: 11, speed: 0.4 }
    }
      ...
      ...
    
    handleClickFlyDestinationAirport = () => {
      this.setState({ flyTo: { center: [-122.404357, 37.791246], zoom: 11, speed: 0.4 } })
    }

    <Map
       style="mapbox://styles/myAccount/vfvjiej45de7k"
       center={this.state.start}
       zoom={[9.8]}
       movingMethod="flyTo"
       containerStyle={{
         height: '100vh',
         width: '100vw'
       }}
       onStyleLoad={map => {
          map.addLayer({
             id: 'routes',
             type: 'line',
             source: {
               type: 'geojson',
                  data: {
                    type: 'FeatureCollection',
                    'features': [{
                        type: 'Feature',
                        properties: {
                          'color': 'blue',
                        },
                        geometry: this.state.geoJsonData1
                    }, {
                        type: 'Feature',
                        properties: {
                          'color': '#FF793F',
                        },
                        geometry: {
                            "type": "LineString",
                            "coordinates": [
                              this.state.start2,
                              this.state.destination2
                            ]
                        }
                    }, {
                        type: 'Feature',
                        properties: {
                          'color': 'green',
                        },
                        geometry: this.state.geoJsonData2
                    }]
                  }
                },
                paint: {
                  'line-width': 3,
                  'line-color': {
                    'type': 'identity',
                    'property': 'color'
                  }
                }
              }),
              map.flyTo(this.state.flyTo);
          }}
        />

<button className={css(styles.flyToButtonMap)} onClick={this.handleClickFlyDestinationAirport}>Fly to the dest Airport</button>

screen shot 2017-12-23 at 5 43 54 pm

@aderaaij

This comment has been minimized.

Copy link

commented Dec 24, 2017

You could get a reference to your map by adding <Map ref={(e) => { this.map = e; }} />. After that you've got this.map available which you could set as a prop or state. You can call flyTo and any other method on it!

@aziz-boudi4

This comment has been minimized.

Copy link
Author

commented Dec 26, 2017

@aderaaij I got a reference to my map by following your advice, then I added it to the state in componentDidMount by doing this.setState({ mapRef: this.map }).
but how can I call the flyTomethod from within my clickHandler function ?

@gaearon

This comment has been minimized.

Copy link

commented Dec 27, 2017

You don’t need to put refs in the state. It’s fine to read them from the instance where you already store it. In the click handler, you can call this.map.flyTo(), can you not?

@aziz-boudi4

This comment has been minimized.

Copy link
Author

commented Dec 27, 2017

@gaearon

Well when I use

handleClickFlyDestinationAirport = () => {
    this.map.flyTo({ center: [-118.4107187, 33.9415889] })
}

I get an Uncaught TypeError: _this.map.flyTo is not a function

HelpPage.js:81 Uncaught TypeError: _this.map.flyTo is not a function
    at HelpPage._this.handleClickFlyDestinationAirport (HelpPage.js:81)
    at HTMLUnknownElement.callCallback (modules.js?hash=04ceea1cc27f31ca61574df316b23853ebac8f98:17388)
    at Object.invokeGuardedCallbackDev (modules.js?hash=04ceea1cc27f31ca61574df316b23853ebac8f98:17427)
    at Object.invokeGuardedCallback (modules.js?hash=04ceea1cc27f31ca61574df316b23853ebac8f98:17284)
    at Object.invokeGuardedCallbackAndCatchFirstError (modules.js?hash=04ceea1cc27f31ca61574df316b23853ebac8f98:17298)
    at executeDispatch (modules.js?hash=04ceea1cc27f31ca61574df316b23853ebac8f98:17682)
    at executeDispatchesInOrder (modules.js?hash=04ceea1cc27f31ca61574df316b23853ebac8f98:17704)
    at executeDispatchesAndRelease (modules.js?hash=04ceea1cc27f31ca61574df316b23853ebac8f98:17802)
    at executeDispatchesAndReleaseTopLevel (modules.js?hash=04ceea1cc27f31ca61574df316b23853ebac8f98:17813)
    at Array.forEach (<anonymous>)

@aderaaij

This comment has been minimized.

Copy link

commented Dec 27, 2017

@aziz-boudi4 The actual map object is not this.map, the ref you get is a ref to the React component, not to the map itself. You can find the map in this.map.state.map. Just console.log this.map and you'll see what's available!

@aziz-boudi4

This comment has been minimized.

Copy link
Author

commented Dec 27, 2017

@aderaaij yes Arden, It's indeed working when I use this.map.state.map. I didn't try that because when I consoled logged earlier the method flyTo was not there.

quick question though, just to fully understand:
1- how come the this.map has a state.map ? can you please elaborate on that?
and thank you again for your help and guidance.

@aderaaij

This comment has been minimized.

Copy link

commented Dec 27, 2017

@aziz-boudi4 glad to hear it's working. When you get your ref to the map, it actually refers to the ReactMapboxGL component, which is a React component and not the map itself. The ReactMapboxGL component has the actual mapboxgl map in its state on which we can apply the normal mapbox API. To be honest, I'm not sure if this is the best way to get a reference to the map but I had the same problem as you and this seems to work fine for me!

@gaearon wow you're everywhere 🙌 (was really enjoying your React thread on dev.to). Anyhow, I also pass the reference from the map to the state because I pass it as prop to a child component. As the ref is undefined until the component is mounted, I add it to state in componentDidMount. Would there be a better way to do this?

@aziz-boudi4

This comment has been minimized.

Copy link
Author

commented Dec 27, 2017

@aderaaij great, well it's working for me as well.
maybe @alex3165 or @gaearon can tell us if there's a better way to do that ?

@markerikson

This comment has been minimized.

Copy link

commented Dec 27, 2017

@aziz-boudi4 : I actually show an example of this exact idea in my post Declaratively Rendering Earth in 3D, Part 2: Controlling Cesium with React. I'd encourage you to read through that post. Your specific question is covered in the section where I talk about updating the camera position based on state changes.

@alex3165

This comment has been minimized.

Copy link
Owner

commented Dec 28, 2017

Hey @aziz-boudi4, sorry for the late reply, thanks everyone for helping out on this issue. I would rather avoid calling map.flyTo manually, you can play with center and zoom props of the map component to achieve what you want to do (and keep center and zoom in your state). The purpose of the library is to abstract those manual calls to mapbox API so that it is more declarative.

Also instead of calling map.addLayer you can use the GeoJsonLayer component https://github.com/alex3165/react-mapbox-gl/blob/master/example/src/demos/geojsonLayer.tsx or you could even use Layer and Feature to render a line, see the allShapes example: https://github.com/alex3165/react-mapbox-gl/blob/master/example/src/demos/allShapes.tsx

@aziz-boudi4

This comment has been minimized.

Copy link
Author

commented Dec 29, 2017

@alex3165 thank you for the info Alex, oh you mean putting Center and Zoom on the state and updating them when the user clicks on a specific button? can I do the same thing for Bearing and Pitch which are options of the flyTo method?

Thank you, I will have a look at the examples you gave me, and try to use GeoJsonLayer

@gaearon

This comment has been minimized.

Copy link

commented Dec 30, 2017

Just to make sure we're clear, it's a bad idea to read .state. of another component. State is typically considered private.

I'm finding it hard to understand what you're asking. If you created a tiny mockup with a fake map component just to illustrate the issue that would help.

@khollund

This comment has been minimized.

Copy link

commented Jan 4, 2018

I think the "mistake" here is the way you look at flyto. You should think of flyTo not as a method of changing the center point or other properties of the map, but an animation method to move the camera. As soon as you need to use interactions to move the map it's important to grasp the concept of the camera being your view into the map.

So if you want to fly somewhere, in reality what you essentially want is to update position, bearing, zoom etc and move from current state to next state with a flyto animation type. And there's many ways to do that. Fit bounds is one, depends a bit what you are trying to do. Looks like you can get away with just updating position though.

@alex3165 alex3165 added the Question label Jan 9, 2018

@alex3165 alex3165 closed this Jan 9, 2018

@lpfunding

This comment has been minimized.

Copy link

commented Mar 18, 2019

Does anyone here know how you would center the map on a custom ShapeSource icon on click? The icon gets its coordinate from GeoJson. I've tried the following inside Shapesource, but it just takes me to the first location (not the icon's coordinates).

onPress={() => {this.map.flyTo(featureCollection.features[0].geometry.coordinates, 1000);}}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.