2

In this code sample, I want to show the distance attribute on top of the PolyLine with an offset of 45% (almost next to the arrow). But I was not successful in achieving that.

  <GoogleMap
      id="react-google-maps"
      mapContainerClassName={"google-map-container-style"}
      onLoad={onLoad}
      onUnmount={onUnmount}
      onZoomChanged={() => {}}
    >
      {data.map(({ source, destination, distance }, index) => (
        <Polyline
          key={index}
          options={{
            strokeColor: "#4468E1",
            icons: [
              {
                icon: { path: google.maps.SymbolPath.BACKWARD_CLOSED_ARROW },
                offset: "50%"
              }
            ]
          }}
          path={[source, destination]}
        />
        /* If I wrap this section in a Fragment or div and render another overlay component here as a sibling, will receive the below error*/
      ))}
    </GoogleMap>

creating a sibling component as an overlay throws DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.

Edit: I have implemented a solution with

            <OverlayView
              position={getMiddlePosition(source, destination)}
              mapPaneName={OverlayView.OVERLAY_MOUSE_TARGET}
              getPixelPositionOffset={(width, height) => ({ x: -10, y: -10 })}
            >
              <div> the text here </div>
            </OverlayView>

But it is not always in the desired position like the arrow, especially in zoom in/zoom out it does not look nice. Is there any way to create a custom icon with modifiable text that can be added to the list of Polyline icons?

Yrll
  • 1,619
  • 2
  • 5
  • 19
Amir-Mousavi
  • 4,273
  • 12
  • 70
  • 123
  • Have you tried using advanced marker? – Yrll May 30 '23 at 01:39
  • @Yrll What is the advanced marker? And how is it related to the Polyline? – Amir-Mousavi May 30 '23 at 11:06
  • Nevermind my comment above, but would infoWindow work for your use case too? I was able to find a workaround for this and I'm on my way to post it as an answer. Though I'm not sure if it is what you wanted/needed. – Yrll May 30 '23 at 11:16
  • Here's the link to advanced markers btw if you want to know more about it: https://developers.google.com/maps/documentation/javascript/advanced-markers/overview. I was trying to use this in answering your question but unfortunately it still was not included in the `react-google-maps/api` library I guess. – Yrll May 30 '23 at 11:44

1 Answers1

3

[EDIT] You can use the Geometry Library to get the % offset between two points

Inside your onload function from the <Map /> component, you can use the Geometry Library of Maps Javascript API to calculate the following with its respective methods:

  1. computeDistanceBetween() - to get the distance between points
  2. computeHeading() - to compute the heading to be used in computeOffsetMethod.
  3. computeOffset() - to get the latLng of the offset distance from the origin.

Combining the three and a little math, will give you the desired results.

First is to include the "geometry" library:

const libraries: Libraries = ["drawing", "geometry"];

Then create a state for the calculated results to be used as a position for your <InfoWindow /> component.

const [polylineText, setPolylineText] = useState<
  google.maps.LatLng | google.maps.LatLngLiteral | undefined
>(undefined);

Then inside your onload function from the component, you will use the Geometry Library to do the following:

// store the spherical property in a variable for later use
const compute = google.maps.geometry.spherical;

// use computeDistanceBetween() method
// to get the distance between the two locations for calculating the offset Length
const distanceBetween = compute.computeDistanceBetween(
  location1,
  location2
);
console.log("Distance Between: " + distanceBetween);

// multiply the distance to 0.45 to get the 45% of its length
// You can change the value here if you want to modify it
// ex. multiplying it with 0.5 will provide you the center.
const offsetDistance = distanceBetween * 0.45;
console.log("Offset length: " + offsetDistance);

// use computeHeading() method to get the heading for later use
const heading = compute.computeHeading(location1, location2);
console.log("Heading: " + heading);

// use the computeOffset() method to get the latLng of the offsetDistance
const offsetLocation = compute.computeOffset(
  location1,
  offsetDistance,
  heading
);
console.log("Offset location: " + offsetLocation);

// update the state for the position of your infoWindow
setPolylineText(offsetLocation);

Lastly, your <InfoWindow /> component should look like this and should be a direct child of the <GoogleMaps /> component:

<InfoWindow position={polylineText}>
  <div>
    <p>Distance Between Points: {data[1].distance}</p>
  </div>
</InfoWindow>

NOTE: As this is just a demonstration on how to do the calculations, its up to you to do the map() method if you're dealing with lots of points/polylines/data

Your whole return should look like this:

return !isLoaded ? null : (
  <GoogleMap
    id="react-google-maps"
    mapContainerClassName={"google-map-container-style"}
    onLoad={onLoad}
    onUnmount={onUnmount}
    onZoomChanged={() => {}}
  >
<InfoWindow position={polylineText}>
<div>
  <p>Distance Between Points: {data[1].distance}</p>
</div>
</InfoWindow>
    {data.map(({ source, destination, distance }, index) => (
      <Marker key={index} position={source}></Marker>
    ))}
    {data.map(({ source, destination, distance }, index) => (
      <Polyline
        key={index}
        options={{
          strokeColor: "#4468E1",
          icons: [
            {
              icon: { path: google.maps.SymbolPath.BACKWARD_CLOSED_ARROW },
              offset: "50%"
            }
          ]
        }}
        path={[source, destination]}
      />
    ))}
  </GoogleMap>
);

Here's what your map should look like with 45% offset:

Final result

I hope this helps! Here's a proof of concept snippet for you to check and play around with: https://codesandbox.io/s/how-to-attach-a-text-to-react-google-maps-api-polyline-using-geometry-library-r8ij7h?file=/src/App.tsx:3108-3949

Yrll
  • 1,619
  • 2
  • 5
  • 19
  • Thanks for the answer, but it is the same solution with `OverlayView ` and has the same problem I have stated about the box's position on the line. e.g. zooming in cause the `InfoWindow` to be misplaced. – Amir-Mousavi May 30 '23 at 14:29
  • Also, the libraries `InfoBox` is implemented on `InfoWindow`, so it does not bring any specific benefit over InfoBox – Amir-Mousavi May 30 '23 at 14:31
  • Unfortunately, as mentioned in my answer, the offset parameters of the `InfoWindow/InfoBox` is not the same with the `icon` offset parameter which is just a % value. You can try filing a [feature request](https://developers.google.com/maps/support#issue_tracker) for this in the Maps Javascript API. – Yrll Jun 01 '23 at 00:41
  • Hi @Amir-Mousavi, I found a solution. I'll update my answer now and see if this would work for you. – Yrll Jun 01 '23 at 10:26
  • Answer just got updated! – Yrll Jun 01 '23 at 11:12
  • Thanks, but I still believe that my solution with `OverlayView ` and repositioning x,y by 10 pixels gives the same result and conceptually is the same. My main concern was to receive a solution so that the badge could be attached to the Plyline as the arrow icons are attached. – Amir-Mousavi Jun 02 '23 at 14:32
  • Ohhh. Well, I guess your best bet would be to really file a feature request as mentioned in my comment above. Please do note that the react library is still using the PolylineOptions interface in the standalone [Maps Javascript API](https://developers.google.com/maps/documentation/javascript/reference/polygon#PolylineOptions). The one that you wanted is currently not a feature of the API itself, and the icon was already a built in props for it. So yeap, a feature request would be nice. :') – Yrll Jun 02 '23 at 15:10