Skip to content

Exercise 10 - Show Golf Courses in a Google Maps

Project name : E10 Golf Courses

Note

If you are afread to use Google Maps (billing), you can try to use any other Maps API's what you can find from the internet. Here are a few other Maps to use. If you found other possibilites, send those links to me and I will update this list.

Remember that you can always check your API requests and estimated charges from Google Cloud Platform, if you are going to use a Google Maps. Read more information here Google Maps Platform Billing - free trial.

Warning

JAMK/IT is not responsible any cost what might be caused to you with Google Maps SDK or any other SDK's!

Introduction

In this exercise you will learn to use Google Maps in your Android application. With the Maps SDK for Android, you can add maps based on Google Maps data to your application. The API automatically handles access to Google Maps servers, data downloading, map display, and response to map gestures.

 

Golf course JSON data

Your target is to create Android application, which shows some of the Finnish Golf Courses in a Google Maps. You can use a following JSON file in your application https://ptm.fi/materials/golfcourses/golf_courses.json.

Structure of JSON data:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "info":"SGKY:N JÄSENKENTÄT",
  "courses": [
    {
      "type":"Kulta",
      "lat":62.2653926,
      "lng":22.6415612,
      "course":"Alastaro Golf",
      "address":"Golfkentäntie 195, 32560 Virttaa",
      "phone":"(02) 724 7824",
      "email":"minna.nenonen@alastarogolf.fi",
      "web":"http://alastarogolf.fi/",
      "image":"kuvat/kulta.jpg",
      "text":"Alastaro Golfin Virttaankankaan..."
    },
  ...

Create a new project

  • Start Android Studio
  • Select Google Maps Activity
  • Create a new project – E10 Golf Courses

Follow instructions given in previous exercise to get your Map working in your app.

Run and test your project

Run and test your project. Google Maps should be visible and it should be pointed to Sidney by default.

!Image 01

Load Golf Courses JSON data

Open the build.gradle file from your app module and add a Volley library inside dependencies block. Volley is an HTTP library that makes networking for Android apps easier and most importantly, faster. Volley is available on GitHub.

1
2
3
4
dependencies {
  ...
  implementation 'com.android.volley:volley:1.2.1'
}

Comment (or remove) all the Sydney code in the onMapReady function. Create an own loadData function and call it from the onMapReady function. Now data will be loaded after Google Maps is ready.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
private fun loadData() {
  val queue = Volley.newRequestQueue(this)
  // 1. code here
  // create JSON request object
  val jsonObjectRequest = JsonObjectRequest(
    Request.Method.GET, url, null,
      { response ->
        // JSON loaded successfully
        // 2. code here
      },
      { error ->
        // Error loading JSON
      }
  )
  // Add the request to the RequestQueue
  queue.add(jsonObjectRequest)
  // 3. Add custom info window adapter here
}

Note

When you are importing needed classes, check that you are importing those from Volley library.

Add a few variables (to section 1. in above code) , which will be used to store loaded JSON data. All the golf courses will be stored to golfCourses array. Different golf course types, in this application, will use a different marker colors. courseTypes map will be used in this purpose.

1
2
3
4
5
6
7
8
val url = "https://ptm.fi/materials/golfcourses/golf_courses.json"
var golfCourses: JSONArray
var courseTypes: Map<String, Float> = mapOf(
  "?" to BitmapDescriptorFactory.HUE_VIOLET,
  "Etu" to BitmapDescriptorFactory.HUE_BLUE,
  "Kulta" to BitmapDescriptorFactory.HUE_GREEN,
  "Kulta/Etu" to BitmapDescriptorFactory.HUE_YELLOW
)

After JSON is loaded successfully (to section 2. in above code), it can be parsed. Loop through all of the object in JSONArray. Get all course data and create a new Google Maps Marker. Pass data to marker via tag. It will be used later with InfoWindowAdapter to show data in marker. After that, camera will be moved to point center of Finland.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
golfCourses = response.getJSONArray("courses")
  // loop through all objects
  for (i in 0 until golfCourses.length()){
    // get course data
    val course = golfCourses.getJSONObject(i)
    val lat = course["lat"].toString().toDouble()
    val lng = course["lng"].toString().toDouble()
    val latLng= LatLng(lat, lng)
    val type = course["type"].toString()
    val title = course["course"].toString()
    val address = course["address"].toString()
    val phone = course["phone"].toString()
    val email = course["email"].toString()
    val webUrl = course["web"].toString()

    if (courseTypes.containsKey(type)){
      val m = mMap.addMarker(
        MarkerOptions()
          .position(latLng)
          .title(title)
          .icon(BitmapDescriptorFactory
              .defaultMarker(courseTypes.getOrDefault(type, BitmapDescriptorFactory.HUE_RED)
            )
        )
      )
      // pass data to marker via Tag
      val list = listOf(address, phone, email, webUrl)
      m!!.tag = list
    } else {
      Log.d("GolfCourses", "This course type does not exist in evaluation $type")
    }
  }
  mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(65.5, 26.0),5.0F))

Run and test your project

Run your project. Now a Golf course markers should be visible.

!Image 02

Custom Info Window Adapter

An info window allows you to display information to the user when they tap on a marker. Only one info window is displayed at a time. If a user clicks on a marker, the current info window will be closed and the new info window will be displayed. Note that if the user clicks on a marker that is currently showing an info window, that info window closes and re-opens.

Layout

Create a new info_window.xml layout file for the info window. This info window will show golf course name, address, phone, email and web page address. You can use TextView elements in your layout XML file. You can use for example LinearLayout as a root element and vertical orientation to layout all of your TextView elements in a row. Use a following id's in your layout items: titleTextView, addressTextView, phoneTextView, emailTextView and webTextView.

Click to see a solution - first try to create a layout by yourself
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/titleTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:textSize="16sp"
        android:textStyle="bold" />
    <TextView
        android:id="@+id/addressTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/phoneTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/emailTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/webTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

Adapter

Create a new inner CustomInfoWindowAdapter class to your MainActivity. This class should extends from GoogleMap.InfoWindowAdapter.

  • Info Window layout will be loaded from info_window.xml resource.
  • getInfoWindow function need to be declared but it is not used.
  • getInfoContents function will find all the id's from the layout and assign correct data. All the data will be get here via Marker (which has been clicked by the user). Title is a string and rest of the data is passed via tag as a list (look above Kotlin code and line 27).
Click to see a solution - first try to code it by yourself
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
internal inner class CustomInfoWindowAdapter : GoogleMap.InfoWindowAdapter {
  private val contents: View = layoutInflater.inflate(R.layout.info_window, null)

  override fun getInfoWindow(marker: Marker): View? {
      return null
  }

  override fun getInfoContents(marker: Marker): View {
    // UI elements
    val titleTextView = contents.findViewById<TextView>(R.id.titleTextView)
    val addressTextView = contents.findViewById<TextView>(R.id.addressTextView)
    val phoneTextView = contents.findViewById<TextView>(R.id.phoneTextView)
    val emailTextView = contents.findViewById<TextView>(R.id.emailTextView)
    val webTextView = contents.findViewById<TextView>(R.id.webTextView)
    // title
    titleTextView.text = marker.title.toString()
    // get data from Tag list
    if (marker.tag is List<*>){
      val list: List<String> = marker.tag as List<String>
      addressTextView.text = list[0]
      phoneTextView.text = list[1]
      emailTextView.text = list[2]
      webTextView.text = list[3]
    }
    return contents
  }
}

Use created Adapter in MainActivity

Connect your CustomInfoWindowAdapter to Google Maps in your loadData function (to section 3. in loadData code).

1
2
// 3. Add custom info window adapter here
mMap.setInfoWindowAdapter(CustomInfoWindowAdapter())

Run and test your project

Run and test your project. Info Window will be visible, when a marker is clicked.

!Image 03

Test and push

Test your application in emulator, take screenshots and commit/push your project and screenshots back to JAMKIT/GitLab. Remember move your exercise/issue ticket from Doing to In Review in Issues Board and write your learning comments to issue comments.