🗺 iOS / macOS MapKit Swift — Drawing Shapes on Map

Jackie Leonardy 鄭
4 min readDec 12, 2021

--

iOS MapKit is a powerful API from Apple for iOS, iPadOS, macOS, all of the available Operating System depending on the required version on each of them. MapKit is not only used to display Map on your device, but you could also customize your data, drawing traveled routes, or drawing custom shapes on your map.

In this medium, we’ll discover the basic of drawing shapes and went through the implementation on SwiftUI. So, to make it short, the code base for the MapKit could be found here, or if you’re integrating your code from macOS (AppKit) to SwiftUI, please read this first.

1. Geocoding with GeoJSON

Before working with Geocoding / drawings on our map, we should get to know GeoJSON. According to geojson.org. GeoJSON is a format for encoding a variety of geographic data structures, simply it’s an extension from a regular JSON.

But what differ GeoJSON to JSON is the rules on each of dataports format. Since GeoJSON is a well-specified standard, while a generic JSON file need not follow any standard for data organization. Using the standardized format, the GeoJSON decoder from any programming language will be able to decode the data and turn it into drawable shapes based on the longitude and latitude provided.

Below here is the standardized data format for GeoJSON.

Full Source Code: https://github.com/ans-4175/peta-indonesia-geojson/blob/master/indonesia-prov.geojson

Let’s focus on the array inside features. As we can see, the format must include:

  • Geometry: an array representing arrays of coordinate points (longitude, and latitude)
  • Type: a key representing the type of drawing we want to display. There are a lot of geometry type we could use, where the value of the type member must be one of: "Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon", or "GeometryCollection"and it affects our drawing result on the map.
  • Properties: a regular JSON format we could customize any data we want.

Note that the type Polygon has it’s own GeoJSON rule, as well as Point, LineString, and so on.

For the detail of each type member, you could read it here or here.

2. Creating GeoJSON Map Project

We’ve already talked about GeoJSON, but how do we know which coordinate to be drawn on? Well, there are lots of tools we could use to draw and get the GeoJSON data. Here, I used the MapBox Studio to draw and display the shape into GeoJSON Data.

You could be either create your own Geo Shape, but I recommend you to use the existing project. Just like where I use and modify the GeoJSON retrieved from github.

GeoJSON from https://github.com/ans-4175/peta-indonesia-geojson/blob/master/indonesia-prov.geojson

All of created points and lines will be transformed into a GeoJSON, and here we could modify the data inside the properties array as much as we want. Below is my modified GeoJSON used in my project.

3. Let’s implement it in Swift!

Alright, we’ve gone through about what is GeoJSON, how to draw and get the data on Map. Now, is the time to implement it on our Project. The data flow will implement look like this:

  • Read our GeoJSON data
  • Decode and store it in a Codable object
  • Loop through each Object and render it in a MKPolygon type Overlay
  • Change the overlay color while rendering.

a. Read our GeoJSON data

— To read our GeoJSON data, we will use GeoJSON Decoder to read our GeoJSON data into MKGeoJSONObject.

b. Decode and store it in a Codable object

After decoding our GeoJSON file, we will access the feature json to retrieve the geometry and properties data by using MKGeoJSONFeature by looping through our geoJSON object arrays. But before that we need to prepare a Codable Object to store our data.

Then extract to get the geometry and property data. Put in mind that our feature we use is MKPolygon, so we need to check and convert it into a MKPolygon object first. This apply to other features.

c. Render each Feature into an Overlay Shape

This part is quite tricky, so I developed my own way of thinking by creating a custom function to render our GeoJSON object into our map and also another objects to help me store the overlay shape object.

Objects to Store and Track our Create Overlay Shape

Here I create a render function that will be used in our loadGeoJSON function, where we will call this function in while looping through our MKGeoJSONFeature, let’s call and attach this function.

d. Change Shape Overlay Color

As you can see, after creating our MKOverlay we store it into a Singleton MapOverlays Class and then we call the self.mapView.addOverlay(overlay) to add the Shape Overlay to our map where it will trigger a delegate function func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay). So, in this function we can configure our shape overlay like:

  • Add shape fill
  • Change stroke color
  • Adjust line width
  • Add title and subtitle (store detail information for each shape)

Here are the full function to load, read, and draw Polygon Shape.

Full loadGeoJSON function

After adjusting Coordinate Region, setting MapView, and drawing our GeoJSON shape, it should be displayed like this.

It turns out to be not that hard though, right…? 😤

--

--