7

I'm new to React and have been playing around with the react-google-maps package. I'm trying to curve a Polyline that joins two places. After going through the documentation, I'm trying to incorporate the curve polyline function under the 'editable' prop.

Here's the function to curve the polyline:

var map;
var curvature = 0.4; // Arc of the Polyline

function init() {
        var Map = google.maps.Map,
            LatLng = google.maps.LatLng,
            LatLngBounds = google.maps.LatLngBounds,
            Marker = google.maps.Marker,
            Point = google.maps.Point;

            // Initial location of the points
            var pos1 = new LatLng(this.state.srcMarker);
            var pos2 = new LatLng(this.state.desMarker);

            var bounds = new LatLngBounds();
            bounds.extend(pos1);
            bounds.extend(pos2);

            map = new Map(document.getElementById('map-canvas'), {
                    center: bounds.getCenter(),
                    zoom: 12
            });
            map.fitBounds(bounds);

            var markerP1 = new Marker({
                    position: pos1,
                    map: map
            });
            var markerP2 = new Marker({
                    position: pos2,
                    map: map
            });

            var curveMarker;

            function updateCurveMarker() {
                    var pos1 = markerP1.getPosition(), 
                    pos2 = markerP2.getPosition(),
                    projection = map.getProjection(),
                    p1 = projection.fromLatLngToPoint(pos1), 
                    p2 = projection.fromLatLngToPoint(pos2);

                    // Calculating the arc.
                    var e = new Point(p2.x - p1.x, p2.y - p1.y), // endpoint
                        m = new Point(e.x / 2, e.y / 2), // midpoint
                        o = new Point(e.y, -e.x), // orthogonal
                        c = new Point( m.x + curvature * o.x, m.y + curvature * o.y); //curve control point

                    var pathDef = 'M 0,0 ' + 'q ' + c.x + ',' + c.y + ' ' + e.x + ',' + e.y;

                    var zoom = map.getZoom(),
                        scale = 1 / (Math.pow(2, -zoom));

                    var symbol = {
                            path: pathDef,
                            scale: scale,
                            strokeWeight: 1,
                            fillColor: 'none'
                    };

                    if (!curveMarker) {
                            curveMarker = new Marker({
                                    position: pos1,
                                    clickable: false,
                                    icon: symbol,
                                    zIndex: 0, // behind the other markers
                                    map: map
                            });
                    } else {
                            curveMarker.setOptions({
                                    position: pos1,
                                    icon: symbol,
                            });
                    }
            }

            google.maps.event.addListener(map, 'projection_changed', updateCurveMarker);
            google.maps.event.addListener(map, 'zoom_changed', updateCurveMarker);

            google.maps.event.addListener(markerP1, 'position_changed', updateCurveMarker);
            google.maps.event.addListener(markerP2, 'position_changed', updateCurveMarker);
    }

    google.maps.event.addDomListener(window, 'load', init);

I'm not able to understand how to use this function in the Polyline component. I'm able to mark a line between any two places, but not able to use this function in order to curve the given polyline. This is the Polyline component that I'm using.

<Polyline
        path={pathCoordinates} 
        geodesic={true} 
        options={{ 
                strokeColor: '#ff2527',
                        strokeOpacity: 1.0,
                        strokeWeight: 5,
        }}
/>

I have two markers in my state (srcMarker, desMarker) that store the coordinates of the given cities once the user inputs the city name. Any help would be appreciated in incorporating this function with the Polyline component. I haven't come across any built in feature that allows curving of the polyline. Thanks in advance!

Ritwick
  • 73
  • 1
  • 6

1 Answers1

4

I took the code you provided and adapted it to work with React and react-google-maps. Check out this CodeSandbox to see a simple application that contains two markers and a curved line between them.

The curved line that connects the two markers is actually a marker as well. The only difference between it and the two red markers is that its icon prop is set to the curved line (which is computed beforehand).

Here is the code for the CurveMarker component:

const CurveMarker = ({ pos1, pos2, mapProjection, zoom }) => {
  if (!mapProjection) return <div/>;
  var curvature = 0.4

  const p1 = mapProjection.fromLatLngToPoint(pos1),
        p2 = mapProjection.fromLatLngToPoint(pos2);

  // Calculating the arc.
  const e = new google.maps.Point(p2.x - p1.x, p2.y - p1.y), // endpoint
    m = new google.maps.Point(e.x / 2, e.y / 2), // midpoint
    o = new google.maps.Point(e.y, -e.x), // orthogonal
    c = new google.maps.Point(m.x + curvature * o.x, m.y + curvature * o.y); //curve control point

  const pathDef = 'M 0,0 ' + 'q ' + c.x + ',' + c.y + ' ' + e.x + ',' + e.y;

  const scale = 1 / (Math.pow(2, -zoom));

  const symbol = {
    path: pathDef,
    scale: scale,
    strokeWeight: 2,
    fillColor: 'none'
  };

  return <Marker 
            position={pos1} 
            clickable={false} 
            icon={symbol}
            zIndex={0}
          />;
};

Let me know if you have any questions.

Iavor
  • 1,997
  • 16
  • 27
  • Thanks a lot, was able to get it working. If I were to calculate the mid point of the curved marker between the two places, how could I achieve that? I'm able to figure out how to find the midpoint when it is a straight line. – Ritwick Feb 13 '18 at 11:48
  • Trying to figure out how you could do that. I'll let you know if I come up with something. Do you want to put a marker at the mid point of the curved line? – Iavor Feb 13 '18 at 17:38
  • I think that the constant `c` stores the mid point of the curve. However when I try to convert it back to a LatLng coordinate (and place a marker at that position) using [the `point2LatLng` function here](https://stackoverflow.com/a/32784450/4481169), it doesn't work. – Iavor Feb 13 '18 at 19:52
  • I used the point2LatLng function that you had referenced and was able to get a LatLng value for the constant `c`, but it did not appear to be the midpoint of the curve between the pair of points, as I checked the address of the corresponding LatLng pair and was getting the marker to drop off at a very distant point. – Ritwick Feb 14 '18 at 12:09
  • Yeah, the same thing happened to me :/ I'll let you know if I figure something out. What do you need the mid point of the curved line for by the way? – Iavor Feb 14 '18 at 14:33
  • I'm trying to put up an Info Window at the mid point of the curve to display the distance and the time it would take to travel from one point to the other. I was able to place the Info Window at the mid point of the line segment joining the two points. – Ritwick Feb 15 '18 at 03:06
  • 1
    Was able to place an Info Window at the mid point of the curve. Had looked up [this](https://stackoverflow.com/questions/23596802/calculate-middle-point-of-bezier-curve) to get the equation to calculate different points along the Bezier Curve. – Ritwick Feb 15 '18 at 08:55
  • Nice! I'm glad you were able to figure it out. If you have an example of it up somewhere, I'd like to see how you did it. – Iavor Feb 15 '18 at 13:55
  • 1
    Using the above `CurveMarker` function, I used the equation that computes the curve control point (the constant `c`) and just replaced the mid-point `m` with the Bezier Curve equation. The x and y coordinates of the mid point of the curve would be `mpc_x = ((0.5 * p1.x) + (0.5 * p2.x)) + ((curvature / 2) * (p2.y - p1.y))` & `mpc_y = ((0.5 * p1.y) + (0.5 * p2.y)) + ((curvature / 2) * (p1.x - p2.x))`. Hope this is clear. – Ritwick Feb 16 '18 at 11:14
  • Thanks for explaining that and sharing those equations! – Iavor Feb 16 '18 at 13:57
  • I had the exact same question, and your fiddle helped me a lot. However, I don't understand how to setup the "projection" in order to convert the latlng to a point. can you help me out please? @lavor – Rafael Marques Apr 02 '18 at 17:38
  • Looking back at my code here https://codesandbox.io/s/q7jj92x5lw I see that I passed `onProjectionChanged={props.projectionChanged}` as a prop to my `GoogleMap` component. `projectionChanged` sets the `mapProjection` value in the state and this is passed down to the `Markers` component and then the `CurveMarker` component. The `CurveMarker` component then uses it to compute the point value from the lat/lng. It is tricky working with `react-google-maps` and I don't know much about using it. What exactly don't you understand? – Iavor Apr 02 '18 at 18:09
  • Possibly, I would create a new question. – Iavor Mar 12 '20 at 16:17
  • can we use geodesic: true, property on react-google-maps/api? – programmers_view Jul 05 '23 at 06:00