I am using @react-google-maps/api
to use google-maps in my react.js application, and I am using its different components. Now, I want to draw a polygon in Google DrawingManager using predefined polygons. How do I go about it?
import React, { useCallback, useRef, useState } from "react";
import { GoogleMap, useJsApiLoader } from "@react-google-maps/api";
import { DrawingManager } from "@react-google-maps/api";
export const libraries = ["geometry", "places", "drawing"];
function MyComponent({
setPolygons,
plottedPolygons,
operationType,
drawingEnabled,
setDrawingEnabled,
}) {
const { t } = useTranslation();
const mapRef = useRef(null);
const [zoom, setZoom] = useState(6);
const [shape, setShape] = useState(null);
const [center, setCenter] = useState({ lat: 23, lng: 54 });
const { isLoaded } = useJsApiLoader({
id: "script-loader",
googleMapsApiKey: "key",
language: "en",
region: "us",
libraries: libraries,
});
const onPolygonComplete = (polygon) => {
// Handle completed polygon
setDrawingEnabled(false);
const paths = polygon
.getPath()
.getArray()
.map((point) => ({
lat: point.lat(),
lng: point.lng(),
}));
// Make the polygon editable
polygon.setOptions({
draggable: false,
editable: true,
});
// Listen for polygon edits
window.google.maps.event.addListener(polygon.getPath(), "set_at", () => {
handlePolygonEdit(polygon.getPath().getArray());
});
window.google.maps.event.addListener(polygon.getPath(), "insert_at", () => {
handlePolygonEdit(polygon.getPath().getArray());
});
window.google.maps.event.addListener(polygon.getPath(), "remove_at", () => {
handlePolygonEdit(polygon.getPath().getArray());
});
setPolygons(paths);
//center map on polygon
const bounds = new window.google.maps.LatLngBounds();
polygon.getPath().forEach((element) => {
bounds.extend(element);
});
mapRef.current.fitBounds(bounds);
setZoom(mapRef.current.getZoom());
};
const handlePolygonEdit = (updatedPaths) => {
const paths = updatedPaths.map((point) => ({
lat: point.lat(),
lng: point.lng(),
}));
console.log("Updated Polygon Paths:", paths);
setPolygons(paths);
};
const onUnmount = useCallback(() => {
mapRef.current = null;
}, []);
// const handleOnLoad = (map) => (mapRef.current = map);
const handleOnLoad = (map) => {
mapRef.current = map;
// If in "edit" mode and there are plottedPolygons, fit bounds to show the polygon
if (operationType === "edit" && plottedPolygons.length > 0) {
const bounds = new window.google.maps.LatLngBounds();
plottedPolygons.forEach((polygon) => {
polygon.forEach((point) => {
bounds.extend(point);
});
});
mapRef.current.fitBounds(bounds);
}
};
const drawingManagerRef = useRef(null);
return (
<>
{isLoaded ? (
<GoogleMap
key="google-map"
ref={mapRef}
mapContainerClassName="App-map"
zoom={zoom}
version="weekly"
mapContainerStyle={{
width: "100%",
height: "500px",
}}
options={{
gestureHandling: "greedy",
disableDefaultUI: true,
keyboardShortcuts: false,
}}
center={center}
onLoad={handleOnLoad}
onUnmount={onUnmount}
>
<DrawingManager
ref={drawingManagerRef}
onPolygonComplete={onPolygonComplete}
onOverlayComplete={(e) => {
setShape(e.overlay);
}}
options={{
drawingMode:
drawingEnabled && operationType === "add"
? "polygon"
: null,
// drawingMode: drawingEnabled ? "polygon" : null,
drawingControl: drawingEnabled,
drawingControlOptions: {
position:
window.google.maps.ControlPosition.LEFT_CENTER,
drawingModes: ["polygon"],
},
markerOptions: {
draggable: false,
},
polylineOptions: {
editable: true,
draggable: false,
},
rectangleOptions: {
editable: true,
draggable: false,
},
circleOptions: {
editable: true,
draggable: false,
},
polygonOptions: {
editable: operationType === "edit", // Set to true only in edit mode
draggable: false,
fillOpacity: 0.5,
strokeWeight: 2,
strokeColor: "#0000FF",
zIndex: 999,
paths:
plottedPolygons.length > 0
? plottedPolygons[0]
: [],
},
}}
/>
</GoogleMap>
) : (
<div>loading</div>
)}
</>
);
}
export default MyComponent
<Polygon/>
component instead of the <DrawingManager/>
for pre-loaded pointsThe official API documentation is clear about the purpose of the DrawingManager
class, which is to,
"provide graphical interface for users to draw polygons, rectangles, polylines, circles, and markers on the map."
The purpose of the DrawingManager
is for the users to "manually" draw different kind of objects on the map. If you want to load a polygon with already existing data without the users having to draw them, the Google Maps API already had the Polygon objects.
If your goal is to make it editable even after rendering, then you can just set the editable
property of the PolylineOptions
to true
. As this will enable the user to edit the shape by dragging the control points shown at the vertices and on each segment. Ref: https://developers.google.com/maps/documentation/javascript/reference/polygon#PolylineOptions.editable
In your case, since you are using the react-google-maps/api
library, you can use the <Polyline/>
component. The library supports the editable
property so you should not have any issues implementing this.
It should look something like this:
import React from "react";
import { GoogleMap, useJsApiLoader, Polyline } from "@react-google-maps/api";
const containerStyle = {
width: "100%",
height: "100vh",
};
samplePolygon = [
{ lat: -3.746341, lng: -38.561108 },
{ lat: -3.742494, lng: -38.607967 },
{ lat: -3.81688, lng: -38.609995 },
{ lat: -3.815362, lng: -38.493357 },
{ lat: -3.746341, lng: -38.561108 },
];
const center = {
lat: -3.775,
lng: -38.55,
};
const zoom = 12;
function MyComponent() {
const { isLoaded } = useJsApiLoader({
id: "google-map-script",
googleMapsApiKey: "API_KEY_HERE",
});
const [map, setMap] = React.useState(null);
const onLoad = React.useCallback(function callback(map) {
setMap(map);
}, []);
const onUnmount = React.useCallback(function callback(map) {
setMap(null);
}, []);
return isLoaded ? (
<GoogleMap
mapContainerStyle={containerStyle}
center={center}
zoom={zoom}
onLoad={onLoad}
onUnmount={onUnmount}
>
<Polyline
onLoad={() => {
alert("Polyline Loaded");
}}
editable={true}
path={samplePolygon}
/>
<></>
</GoogleMap>
) : (
<></>
);
}
export default React.memo(MyComponent);
Here's a proof of concept codesandbox: Editable Polygon
NOTE: Use your own API Key