A Brief How-To On Freehand Sketching On Google Maps

A Brief How-To On Freehand Sketching On Google Maps

How to draw a freehand sketch on Google Maps instead of drawing a rigid polygon to mark a particular area.

Listen to this article

In this modern digital era, everything has been automated from sending mails to visiting the world at the click of a button powered by VR technologies. It's a known fact the almost every product/service provider make use of Google Maps in their applications to conduct research on users' behaviour. Google provides various services when it comes to Maps. This article shows how to draw a freehand sketch and get border co-ordinates on Google Maps using APIs.

This article assumes that the reader has a basic working knowledge of React along with knowledge of Google APIs.

Setting up Google API

Google provides various map APIs. For our application, we would be using Maps JavaScript API. The following are the steps to enable Google Maps into our project:

  • Create a project in the Google Console and enable Maps JavaScript API.

  • Next, generate and save an API key.

React App

For this article, I have created a React app using create-react-app with TypeScript along with google-map-react package for the map component.

Implementation

  • Include the API key in the configuration file. You will notice that we would be requiring some special libraries to achieve our goal geometry, drawing. You can execute this by using the following code snippet:

    // src/services/config.ts
    const config = {
    googleMaps: {
      apiKey: '', // Paste your Google API key
      libraries: ['geometry', 'drawing'],
    },
    };
    
  • Next, mount the Google Map component onto the DOM by providing the default center's coordinates and then zoom in.

  • The google-map-react package provides us many props to customize the map component and the most important in this project is the yesIWantToUseGoogleMapApiInternals prop. We need to enable this for our functionality to work as shown:
// src/Components/GoogleMap/index.tsx
<GoogleMapReact
  ref={(ref: any) => (this.mapView = ref)}
  bootstrapURLKeys={{
    key: config.googleMaps.apiKey,
    libraries: config.googleMaps.libraries,
  }}
  defaultCenter={this.props.center}
  defaultZoom={this.props.zoom}
  draggable={!this.state.isDrawable}
  yesIWantToUseGoogleMapApiInternals
/>
  • Once the map is ready, we can drag it to move it around. The only condition being that it should be static when we draw on it. That's where the draggable prop comes in. We can manually control the prop using our local state by using this.

  • You will notice a DRAW ON MAP button at the top of the map. We need to click it to disable the map drag while enabling the Google Event for listeners. A representation of this is shown below: Screenshot 2021-05-15 at 3.01.41 AM.png

  • We need to add an event listener on the GoogleMapDom to listen to the mousedown event when we want to draw on the map. The code to execute this is given below:

    _enableDrawableHelper = () => {
    // Clear the old Listeners if exits and attach a new 'mousedown' listener on Map
    if (this.mapView) {
      this.mapView.maps_.event.clearListeners(
        this.mapView.googleMapDom_,
        'mousedown',
      );
      this.mapView.maps_.event.addDomListener(
        this.mapView.googleMapDom_,
        'mousedown',
        this._drawFreeHand,
      );
    }
    };
    
  • We can create a Polyline which is provided by Google Maps along with some default properties like width, color, map component, etc. once the mousedown event is triggered. Use the following code:
    const poly = new this.mapView.maps_.Polyline({
    clickable: false,
    map: this.mapView.map_,
    strokeColor: '#42A5F5',
    strokeWeight: 3,
    });
    
  • The purpose of creating a Polyline is to see the line drawn by the user. Once the Polyline is created, we can add a mousemove event listener on the Google Map to track mouse movements. Get the relevant coordinates and assign those coordinates to the Polyline to see the drawn line. Use the code given below to execute this:

    // Added mousemove listener to track the user's mouse movement
    const move = this.mapView.maps_.event.addListener(
    this.mapView.map_,
    'mousemove',
    (e: any) => {
      poly.getPath().push(e.latLng);
    },
    );
    

    Screenshot 2021-05-15 at 3.02.48 AM.png

  • The next step is to get back to the starting point so that a complete polygon is formed. To achieve that, we need to add a mouseup listener to the Google Map. Once the event is triggered, we can draw the complete path using the Polyline.

  • To proceed, we need to clear the Polyline from the map and create a polygon using the path provided by the Polyline. Since a polygon is always a closed geometrical shape, the end-point of the Polyline will automatically connect to the start-point of the Polyline drawn. Use the following code:

    // Added mouseup listener to check when the user releases the mouse button
    this.mapView.maps_.event.addListenerOnce(
    this.mapView.map_,
    'mouseup',
    (e: any) => {
      this.mapView.maps_.event.removeListener(move);
      const path = poly.getPath();
      poly.setMap(null);
      const polygon = new this.mapView.maps_.Polygon({
        clickable: false,
        fillColor: '#42A5F5',
        fillOpacity: 0.25,
        geodesic: true,
        map: this.mapView.map_,
        path,
        strokeColor: '#42A5F5',
        strokeWeight: 3,
      });
      this._polyComplete(polygon);
      this.mapView.maps_.event.clearListeners(
        this.mapView.googleMapDom_,
        'mousedown',
      );
    },
    );
    

    Screenshot 2021-05-15 at 3.03.18 AM.png

  • Once the polygon is complete, we can remove all the listeners from the map.

  • We still need to get the proper coordinates from the polygon so that we can send them to the API. Use the code given below:

    // Helper method to get the coordinates across the polygon line
    _polyComplete = (poly: any) => {
    this.polygonControllers = poly;
    let bounds = '';
    const paths = poly.getPaths();
    paths.forEach((path: any) => {
      const ar = path.getArray();
      for (let i = 0, l = ar.length; i < l; i++) {
        const lat = ar[i].lat();
        const lng = ar[i].lng();
        bounds += `${lng} ${lat},`;
      }
      // Appending the first coords as last to make a complete polygon
      if (ar[0]) {
        bounds += `${ar[0].lng()} ${ar[0].lat()}`;
      }
    });
    // Send the bounds to api 
    _sendToApi(bounds)
    };
    

    Screenshot 2021-05-15 at 3.03.58 AM.png

  • The reason we set our polygon to a local variable is to facilitate the clearing of the existing polygon to draw a new one. To do this, we just need to remove the polygon's map and use this.polygonControllers.setMap(null);.

This is a working demonstration of what the end-product should look like: map-sketch-demo.gif

Use cases

There are many use cases for this functionality, one of them is real estate. Let take an example of a user looking to buy a new home in HSR Layout, Bangalore. Since the users are very particular about where or which place they are looking for, it's difficult for the companies to understand the user's requirements. Instead of giving all the available houses in HSR, this functionality can be used to find out what exactly the user is looking for and give suggestions based on the findings. We can also enable users to mark multiple areas. A pictorial representation is shown below: Screenshot 2021-05-15 at 3.11.19 AM.png

Conclusion

This implementation was made possible mainly because of the DOM event listeners provided by the browsers. The browsers are so powerful that they trigger multiple events based on the user interactions based on which we identify the necessary events and add listeners. We can achieve multiple geometry drawings and other functionalities like heat signatures, Street Views, etc. by attaching DOM event listeners on Maps utilising Google Map APIs. Our current world is moving toward a digital future in all aspects. Many organizations (like Google, Amazon, Facebook, etc.) are developing various services and open source software that can be used to improve applications. This is one of my projects using Google's Map APIs. To know more, click here

Hope you look forward to such similar articles!

#reactjs#google#google-maps#events
 
Share this