Search code examples
javascriptreactjsgoogle-mapsreact-propspolyline

Passing Data Using Props to Display Polylines with Google Maps


Here is the reactjs code for displaying the movement of a vehicle on google map. In the code, for the path array, latitude and longitude coordinates are assigned as hard-code values. What I need is, how should pass latitude and longitude coordinates to "path" array from another component using props.

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

class Map extends React.Component {
  state = {
    progress: [],
  };

  path = [
    { lat: 18.558908, lng: -68.389916 },
    { lat: 18.558853, lng: -68.389922 },
    { lat: 18.558375, lng: -68.389729 },
    { lat: 18.558032, lng: -68.389182 },
    { lat: 18.55805, lng: -68.388613 },
    { lat: 18.558256, lng: -68.388213 },
    { lat: 18.558744, lng: -68.387929 },
  ];

  velocity = 5;
  initialDate = new Date();

  getDistance = () => {
    // seconds between when the component loaded and now
    const differentInTime = (new Date() - this.initialDate) / 1000; // pass to seconds
    return differentInTime * this.velocity; // d = v*t -- thanks Newton!
  };

  componentDidMount = () => {
    this.interval = window.setInterval(this.moveObject, 1000);
  };

  componentWillUnmount = () => {
    window.clearInterval(this.interval);
  };

  moveObject = () => {
    const distance = this.getDistance();
    if (!distance) {
      return;
    }

    let progress = this.path.filter(
      (coordinates) => coordinates.distance < distance
    );

    const nextLine = this.path.find(
      (coordinates) => coordinates.distance > distance
    );
    if (!nextLine) {
      this.setState({ progress });
      return; // it's the end!
    }
    const lastLine = progress[progress.length - 1];

    const lastLineLatLng = new window.google.maps.LatLng(
      lastLine.lat,
      lastLine.lng
    );

    const nextLineLatLng = new window.google.maps.LatLng(
      nextLine.lat,
      nextLine.lng
    );

    // distance of this line
    const totalDistance = nextLine.distance - lastLine.distance;
    const percentage = (distance - lastLine.distance) / totalDistance;

    const position = window.google.maps.geometry.spherical.interpolate(
      lastLineLatLng,
      nextLineLatLng,
      percentage
    );

    progress = progress.concat(position);
    this.setState({ progress });
  };

  componentWillMount = () => {
    this.path = this.path.map((coordinates, i, array) => {
      if (i === 0) {
        return { ...coordinates, distance: 0 }; // it begins here!
      }
      const { lat: lat1, lng: lng1 } = coordinates;
      const latLong1 = new window.google.maps.LatLng(lat1, lng1);

      const { lat: lat2, lng: lng2 } = array[0];
      const latLong2 = new window.google.maps.LatLng(lat2, lng2);

      // in meters:
      const distance = window.google.maps.geometry.spherical.computeDistanceBetween(
        latLong1,
        latLong2
      );

      return { ...coordinates, distance };
    });

    console.log(this.path);
  };

  render = () => {
    return (
      <GoogleMap
        defaultZoom={16}
        defaultCenter={{ lat: 18.559008, lng: -68.388881 }}
      >
        {this.state.progress && (
          <>
            <Polyline
              path={this.state.progress}
              options={{ strokeColor: "#FF0000 " }}
            />
            <Marker
              position={this.state.progress[this.state.progress.length - 1]}
            />
          </>
        )}
      </GoogleMap>
    );
  };
}

const MapComponent = withScriptjs(withGoogleMap(Map));

export default () => (
  <MapComponent
    googleMapURL="https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places"
    loadingElement={<div style={{ height: `100%` }} />}
    containerElement={<div style={{ height: `400px`, width: "940px" }} />}
    mapElement={<div style={{ height: `100%` }} />}
  />
);

Here is sample data from json object, which I get from other component.I need to pass this data using props to the above path array.

[]
0: {lat: 6.8667528, lng: 79.8769134}
1: {lat: 6.8667112, lng: 79.8769667}
2: {lat: 6.8666556, lng: 79.8769856}
3: {lat: 6.8666023, lng: 79.8769823}
4: {lat: 6.8665584, lng: 79.8770412}
5: {lat: 6.8665478, lng: 79.8771573}
6: {lat: 6.8665295, lng: 79.8772695}
7: {lat: 6.8664823, lng: 79.8774434}
8: {lat: 6.8664434, lng: 79.8777684}
9: {lat: 6.8664023, lng: 79.87823}
10: {lat: 6.8663373, lng: 79.8786712}
11: {lat: 6.86628, lng: 79.87902}
12: {lat: 6.8662312, lng: 79.879335}
13: {lat: 6.8662145, lng: 79.8795562}
14: {lat: 6.8662095, lng: 79.879695}
15: {lat: 6.8661978, lng: 79.8797523}
16: {lat: 6.8659873, lng: 79.8798639}

Can anyone help me to build this? Thanks for your help!


Solution

  • This is a sample code Note: use your own API key for the code to work) and a code snippet below on how I implement it. In the index.js, I put the path array in json file then imported the json file to be used as an element in my map. Then in my Map.js, I set the constuctor(props) and super(props). I put the react-google-maps <GoogleMap> in the render inside the GoogleMapExample variable. Then I use this variable in the return. In the componentWillMount function of your code, you need to use this.props.path.map to get the value of your path from the props.

    Index.js

    import React, { Component } from "react";
    import { render } from "react-dom";
    import { withScriptjs } from "react-google-maps";
    import Map from "./Map";
    import "./style.css";
    import jsonPath from "./data.json";
    
    const App = () => {
      const MapLoader = withScriptjs(Map);
    
      return (
        <MapLoader
          path={jsonPath}
          googleMapURL="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=geometry,places"
          loadingElement={<div style={{ height: `100%` }} />}
        />
      );
    };
    
    render(<App />, document.getElementById("root"));
    

    Map.js

    import React, { Component } from "react";
    import {
      withGoogleMap,
      GoogleMap,
      Polyline,
      Marker
    } from "react-google-maps";
    
    class Map extends Component {
      constructor(props) {
        super(props);
        this.state = {
          progress: []
        };
      }
    
      velocity = 5;
      initialDate = new Date();
    
      getDistance = () => {
        // seconds between when the component loaded and now
        const differentInTime = (new Date() - this.initialDate) / 1000; // pass to seconds
        return differentInTime * this.velocity; // d = v*t -- thanks Newton!
      };
    
      componentDidMount = () => {
        this.interval = window.setInterval(this.moveObject, 1000);
        console.log(this.props.path);
      };
    
      componentWillUnmount = () => {
        window.clearInterval(this.interval);
      };
    
      moveObject = () => {
        const distance = this.getDistance();
        if (!distance) {
          return;
        }
    
        let progress = this.path.filter(
          coordinates => coordinates.distance < distance
        );
    
        const nextLine = this.path.find(
          coordinates => coordinates.distance > distance
        );
        if (!nextLine) {
          this.setState({ progress });
          return; // it's the end!
        }
        const lastLine = progress[progress.length - 1];
    
        const lastLineLatLng = new window.google.maps.LatLng(
          lastLine.lat,
          lastLine.lng
        );
    
        const nextLineLatLng = new window.google.maps.LatLng(
          nextLine.lat,
          nextLine.lng
        );
    
        // distance of this line
        const totalDistance = nextLine.distance - lastLine.distance;
        const percentage = (distance - lastLine.distance) / totalDistance;
    
        const position = window.google.maps.geometry.spherical.interpolate(
          lastLineLatLng,
          nextLineLatLng,
          percentage
        );
    
        progress = progress.concat(position);
        this.setState({ progress });
      };
    
      componentWillMount = () => {
        this.path = this.props.path.map((coordinates, i, array) => {
          if (i === 0) {
            return { ...coordinates, distance: 0 }; // it begins here!
          }
          const { lat: lat1, lng: lng1 } = coordinates;
          const latLong1 = new window.google.maps.LatLng(lat1, lng1);
    
          const { lat: lat2, lng: lng2 } = array[0];
          const latLong2 = new window.google.maps.LatLng(lat2, lng2);
    
          // in meters:
          const distance = window.google.maps.geometry.spherical.computeDistanceBetween(
            latLong1,
            latLong2
          );
    
          return { ...coordinates, distance };
        });
    
        console.log(this.path);
      };
    
      render() {
        const GoogleMapExample = withGoogleMap(props => (
          <GoogleMap
            defaultZoom={16}
            defaultCenter={{ lat: 6.8667528, lng: 79.8769134 }}
          >
            {this.state.progress && (
              <>
                <Polyline
                  path={this.state.progress}
                  options={{ strokeColor: "#FF0000 " }}
                />
                <Marker
                  position={this.state.progress[this.state.progress.length - 1]}
                />
              </>
            )}
          </GoogleMap>
        ));
    
        return (
          <div>
            <GoogleMapExample
              containerElement={<div style={{ height: `500px`, width: "500px" }} />}
              mapElement={<div style={{ height: `100%` }} />}
            />
          </div>
        );
      }
    }
    
    export default Map;