Saturday, May 9, 2015

Free Hand Drawing on Google's Map View

Recently, I got a chance to work on a project, where we needed to provide an ability to users of an iOS app to draw on Maps. Basically, we decided to go with Google Maps, because of its overall coverage and its popularity. Hence, we used Google Maps SDK for iOS and we wanted to implement "Free Hand Drawing on the Google Maps".
To do so, I started searching around the internet for some good resources, but weren't able to find any good one that could just be used like Plug and Play. We had to do a lot of handling on our own and hence decided to write a blog post and a sample project just to make it useful for others, so that others in the same path don't struggle much like me.

If you are only planning to draw a Polygon on the Map (As most of them do), then may be you don't have to read the entire post. Instead just follow these steps:

(i) Copy & Paste these Folders into your project: "SARMapDrawView", "Categories" & "Models".

(ii) Since, "SARMapDrawView" is just a subclass of UIView, just initialise and place "SARMapDrawView" onto your View Controller     mapDrawView = [[SARMapDrawView alloc]initWithFrame:self.view.bounds];or if your using storyboard then just add an UIView to your controller and name its class to "SARMapDrawView".

(iii) Now, when you are ready to draw your polygon on the map, just call "enableDrawing" method on the view, now "SARMapDrawView" is in the Drawing mode, so if you just scribble or draw any shape with your finger on the map, it will explicitly draw a Polygon, by joining the Starting and the ending point of your touch. and you will receive the drawn Polygon(GMSPolygon) object in Block Callback ("polygonDrawnBlock") [Only if you are listening to it].

If you are too lazy like me who doesn't like following step by step guide like this, then just have a look into the Sample project on GitHub and i have done the same thing described above.

Also, don't forget to add the main dependency, Google Maps SDK to your project.
Here's the Reference to adding Google Maps iOS SDK to your project.
To get your API Key from Google to access their maps on your app.

Well, just following up with the above should get you up and running with drawing Polygons on the Google Maps View.

Oh, you're still reading this, then probably you're still interested in knowing about it works behind or just curious to know how well i am describing it.
Well, i would like to warn you before proceeding further. I am not very good at explaining things since it has been a long back that i have stopped writing.

If you're still On then let's not waste time and jump straight into the context.
For sure, I will make it as short as possible.

As I said before, "SARMapDrawView" is an UIView, which basically handles all the UITouch events that occurs on the screen and we have "GMSMapView" placed above it occupying the entire bounds of the view, which is again a subclass of an UIView and a main component of Google Maps SDK used to display the actual map.

So, Here we go...

We override all the UITouch event delegate methods in our subclass("SARMapDrawView") to detect user interaction with the view.
Once, our view is in drawing mode and the user interaction of map view is disabled, we start receiving touch events for our view. Once user taps on the view we receive the starting point in the "touchesBegan:withEvent:" delegate and store that point in the screen and when user drags his finger around the screen, we receive events and thus convert them to the points or so said coordinates of the screen or the Map view, since map view occupies the entire screen, as follows:

    CGPoint location = [touch locationInView:self.mapView];

Using this point/screen-coordinate, we can ask Google maps to provide us the Latitude and Longitude of that point by the following line:

    CLLocationCoordinate2D coordinate = [self.mapView.projection coordinateForPoint:location];

Now, we store these coordinates and when user drags along the screen, we need the user to give a feel of Free Hand Drawing. To do this we must draw Polylines with the received points/coordinates.

So, when the user drags his finger around the screen, we get consecutive  coordinates and we draw polylines in "touchesMoved:withEvent" method with the current coordinate and the previous coordinate, doing so whenever user moves his finger we receive the coordinates on the map view and thus draw a polyline on it. Refer "addCoordinate:replaceLastObject:inCoordinates" method for the drawing API's.

There is an issue when you draw Polylines in the Map View in this way:
The joint between the 2 polylines are not filled unless they are redrawn as a Single Polyline by giving out all the points/coordinates/path of those 2 polylines.

In our example, finally when user lifts up the finger, we draw the Polygon and thus we don't face the above issue, else it does looks cluttered(Unfilled Polylines, straight lines) when the Map view is zoomed in. So, in case of Polygon all the smoothing, filling, curving is done by the Google Maps SDK.