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

Geocoder #324

Closed
thiagoterleski opened this issue Aug 11, 2016 · 13 comments
Closed

Geocoder #324

thiagoterleski opened this issue Aug 11, 2016 · 13 comments

Comments

@thiagoterleski
Copy link

How can i use the new google.maps.Geocoder() library?, i'm using the ScriptjsLoader to load map lib, and i need to search the address from my point.

@brandonmp
Copy link

This project uses geocode. I adapted some of his code to my project (using a material-ui autocomplete box instead of his custom component), so the geocode etc. happens outside of the react-google-maps component, but so far both components are working harmoniously.

His code handles the UI and the API stuff all together, so it's a bit hard to grok.here's my simplified adaptation below (it combines autoComplete with geocode).

the general flow is:

  1. grab the stuff we need from window in componentWillMount
  2. On user-input into our TextField, fire searchSuggests(), which invokes autocompleteService api
  3. when user selects item, capture the placeID and pass it to geocode to get the coordinates & return the place
componentWillMount() {
    if (typeof window === 'undefined') {
      return
    }
    // grab our googleMaps obj from whever she may lay
    var googleMaps = this.props.googleMaps ||
      (window.google && // eslint-disable-line no-extra-parens
        window.google.maps) ||
      this.googleMaps

    if (!googleMaps) {
      console.error(// eslint-disable-line no-console
        'Google map api was not found in the page.')
      return
    }
    // now grab the services we need
    this.googleMaps = googleMaps
    this.autocompleteService = new googleMaps.places.AutocompleteService()
    this.geocoder = new googleMaps.Geocoder()
  }

  // fire maps api call w/ current user input
  searchSuggests() {
    if (!this.state.userInput) {
      this.updateSuggests()
      return
    }

    // bias search for faster results
    // TODO bias further
    const options = {
      input: this.state.userInput,
      componentRestrictions: {
        country: 'us'
      }
    }
    // make it so
    this.autocompleteService.getPlacePredictions(
      options,
      suggestsGoogle => {
        // send our results to our update function
        this.updateSuggests(suggestsGoogle || [])
      }
    )
  }
  // turn API call results to user state to populate autocomplete box
  updateSuggests(suggestsGoogle = []) {
    let suggests = []
    let style = this.getStyles()
    suggestsGoogle.forEach(suggest => {
      let addressArr = suggest.description.split(',')

      suggests.push({
        placeID: suggest.place_id,
        value: <MenuItem
          primaryText={<div style={style.menuItem}>
            {addressArr[0]}
            <br />
            <span style={style.menuItemSubtitle}>{addressArr.slice(1, -1).join(',') }</span>
            <Divider style={style.menuItemDivider}/>
          </div>
          }
          />,
        address: suggest.description
      })
    })

    this.setState({ suggests })
  }

  onInputChange = userInput => {
    this.setState({ userInput }, this.searchSuggests)
  }

  // when user chooses result
  handleSuggestSelect = (suggestPlaceID) => {
    // user picks one, but so far we only have address & place IDS,
    // so we need to get coords to plot on map
    this.geocoder.geocode(
      { placeId: suggestPlaceID.placeID },
      (results, status) => {
        if (status !== this.googleMaps.GeocoderStatus.OK) {
          return
        }

        const location = results[0].geometry.location

        const suggestLocation = {
          address: suggestPlaceID.address,
          coordinates: {
            lat: location.lat(),
            lng: location.lng()
          },
          googlePlaceID: suggestPlaceID.placeID
        }

        this.props.handlePlaceSelect(suggestLocation)
      }
    )
  }

@tomchentw
Copy link
Owner

@thiagoterleski what @brandonmp did seems okay. react-google-maps doesn't wrap the Geocoder object for you. You'll need to connect yourself.

Also, 6.0.0 is released on npm beta tag now. We also have a new demo page. Feel free to try it:
https://tomchentw.github.io/react-google-maps/

@SyedSaifAli
Copy link

Is there any chance we can add this geocoder in react-google-maps? @tomchentw

@tomchentw
Copy link
Owner

You're free to submit a pull request anytime!

@tomchentw
Copy link
Owner

As I can tell, to use Geocode you don't need it to be a React Compnenent. You can simply use it in the react lifecycle callbacks

@AntoniusGolly
Copy link

Would you mind giving a short hint how to use it in the react lifecycle callbacks? I'm struggling with the basics here, I think, and would highly appreciate help. Thanks for the good work!

@slivniy
Copy link

slivniy commented Jan 24, 2018

@AntoniusGolly You can find Geocode object in window.google.maps
so for getting results from Geocode you need something like this:

let geocoder = new window.google.maps.Geocoder();
geocoder.geocode( { 'address': 'Bakerstreet, 2'}, function(results, status) {
            if (status == 'OK') {
                console.log(results);
            } else {
                console.log('Geocode was not successful for the following reason: ' + status);
            }
 });

@AntoniusGolly
Copy link

@slivniy Thanks for the reply.

This works (supposingly) only if I had the google maps library sourced manually with <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY></script>. But I would like to avoid putting a script tag in my document.

Alternatively, if use a stateless functional component (SFC) I can actually use new google.maps.Geocoder() within withScriptJs(withGoogleMap( XX )) as in

import React from "react"
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps"

const Map = withScriptjs(withGoogleMap((props) => {

  let geocoder = new google.maps.Geocoder();

  if (geocoder) {
    geocoder.geocode( { 'address': props.address}, function(results, status) {

      if (status == google.maps.GeocoderStatus.OK) {
        if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {

          var coords = results[0].geometry.location.toJSON();

          // this works fine
          console.log(coords)

        } else {
          alert("No results found");
        }
      } else {
        alert("Geocode was not successful for the following reason: " + status);
      }
    });
  }

}))

export default Map

and instantiate it with

import Map from "../components/Map"
<Map
        isMarkerShown
        address = 'Bakerstreet, 2'
        googleMapURL="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY&v=3.exp&libraries=geometry,drawing,places"
        loadingElement={<div style={{ height: `100%` }} />}
        containerElement={<div style={{ height: `400px` }} />}
        mapElement={<div style={{ height: `100%` }} />}
/>

However, <Map /> needs to return something to render it. In order to use the lifecycle callbacks as suggested by @tomchentw (I understand those as componentDidMount, componentWillUnmount, etc) I would need to write it as a stateful react component with Map extends React.component.

In order to use the SFC I would need to do the geocoder stuff before the mounting of <Map /> and put <Map /> in the callback as you suggested. But there, google is not defined. Any love?

@AntoniusGolly
Copy link

Hmm, still doesn't work without sourcing google maps api manually. The error remains "Cannot read property 'maps' of undefined. Do you suggest to put the <script> tag in the document?

@slivniy
Copy link

slivniy commented Jan 24, 2018

@AntoniusGolly
Sorry, didn't checked before comment... Really, in the moment of ComponentDidMount Google library isn't loaded.

I'm using Geocoder not right after component mounted, but on user click on some input - and in that time usually window.google already exist.

If you need it really in componentDidMount code can be next:

import React, { Component } from 'react';

import { withScriptjs, withGoogleMap, GoogleMap } from 'react-google-maps';
import { compose, withProps, lifecycle } from 'recompose';

export default class MapRow extends Component {
    render() {

        const MyMapComponent = compose(
            withProps({
                googleMapURL: ('https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=' + API_KEY),
                loadingElement: (<div style={{ height: '100%' }} />),
                containerElement: (<div style={{ height: '400px' }} />),
                mapElement: (<div style={{ height: '100%' }} />)
            }),
            withScriptjs,
            withGoogleMap,
            lifecycle({
                componentDidMount() {
                    let geocoder = new window.google.maps.Geocoder();
                    geocoder.geocode( { 'address': 'Bakerstreet, 2'}, function(results, status) {
                        if (status == 'OK') {
                            console.log('here result of geocoder', results);
                        } else {
                            console.log('Geocode was not successful for the following reason: ' + status);
                        }
                    });
                }
            })
        )(props =>
            <GoogleMap
                defaultZoom={7}
                defaultCenter={new window.google.maps.LatLng(41.8507300, -87.6512600)}
            >
            </GoogleMap>
        );

        return(
            <MyMapComponent/>
        );
    }
}

That's checked :)
Don't forget to change API_KEY and if need googleMapURL
Taken with changes from: https://tomchentw.github.io/react-google-maps/#directionsrenderer

That's simplest way. Though I prefer to declare MyMapComponent variable in constructor method - so when you will change state of that component it will not rerender map.

@AntoniusGolly
Copy link

Thank you, @slivniy !

@pranabunni
Copy link

I am trying to change the state value inside the componentDidMount().But it only accepts props values

@JosephScript
Copy link

@pranabunni If you change it to a Component or PureComponent you'll then have access to state. For example:

import React, { Component } from 'react'
import { withScriptjs, withGoogleMap, GoogleMap } from 'react-google-maps'
import { compose, withProps } from 'recompose'

class MapComponent extends Component {
  componentDidMount() {
      let geocoder = new window.google.maps.Geocoder();
      geocoder.geocode( { 'address': 'Bakerstreet, 2'}, function(results, status) {
          if (status == 'OK') {
            this.setState({ 
              lat: results[0].geometry.location.lat(), 
              lng: results[0].geometry.location.lng()
            })
          } else {
              console.log('Geocode was not successful for the following reason:', status);
          }
      })
    }
    render() {
      const { lat, lng } = this.state
      return lat && lng ? <GoogleMap
          defaultZoom={7}
          defaultCenter={{ lat, lng }}
      >
      </GoogleMap> : <div>Loading...</div>
    }
}

export default compose(
  withProps({
      googleMapURL: ('https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=' + API_KEY),
      loadingElement: (<div style={{ height: '100%' }} />),
      containerElement: (<div style={{ height: '400px' }} />),
      mapElement: (<div style={{ height: '100%' }} />)
  }),
  withScriptjs,
  withGoogleMap)(MapComponent)

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