Map control

Version 0.9 - December 12th, 2016

📘

Sourcecode

Please see the UgMapControl project for the source code created in this chapter. See Installing examples for information on how to download the examples.

Introduction

Many mobile apps need a map control. We wrapped the native map control from iOS and Android respectively and access its functionality using one common interface and API. Functionality includes

  • positioning map control freely
  • zooming in and out by setting the size of the area that the control displays
  • moving to certain location
  • using annotations to position image, i.e. drop your location pin

Overview of map classes and structures

SCDPlatformLocationCoordinate

Property/methodDescriptionComment/Example
latitude:NSNumberSets the lattitude
longitude:NSNumberSets the longitude
let location = SCDPlatformLocationCoordinate(latitude:44.061334,longitude:-79.454920)

SCDWidgetsMapWidget

Property/methodDescriptionComment/Example
mapTypeSet the map typeSCDWidgetsMapType.standard,satellite,hybrid
setRegion( SCDPlatformLocationCoordinate, latitudinalMeters:Int, longitudinalMeters:Int)Sets the region you want to displayUses SCDWidgetsMapRegion
userLocationCurrent user locationType is SCDPlatformLocationCoordinate
isShowUserLocation : BoolChecks if user location can be shown

SCDWidgetsMapRegion

Property/ methodDescriptionComment / Example
centerCenters the region around this coordinatespecify location using SCDPlatformLocationCoordinate
latitudinalMetersWidth of the region in meters1000 (map control will cover 1000 meters horizontally)
longitudinalMetersHeight of the region in meters1000 (map control will cover 1000 meters vertically)
self.mapWidget.setRegion(location, latitudinalMeters:1000,longitudinalMeters:1000)

Minimal map application

  • Drag and drop the map control on the main page
  • Drag three buttons below the map control and name them Torronto, Current Location and Sushi
  • Drag three buttons below the map control and name them Standard, Satellite and Hybrid

Add this minimal code to initialize the map control to the main.page.swift (easiest is to replace existing code)

import ScadeKit

class MainPageAdapter: SCDLatticePageAdapter {

    var mapWidget : SCDWidgetsMapWidget!
    
    // set coordinates
    let perfectlySoftLocation = SCDPlatformLocationCoordinate(latitude:44.061334,longitude:-79.454920)
    let bestSushiLocation = SCDPlatformLocationCoordinate(latitude:43.655156,longitude:-79.385293)
    
    // page adapter initialization
    override func load(_ path: String) {        
        super.load(path)
        
        self.mapWidget = self.page!.getWidgetByName("mapwidget1") as! SCDWidgetsMapWidget
        
        let button = self.page!.getWidgetByName("btnToronto") as! SCDWidgetsButton
        button.onClick.append(SCDWidgetsEventHandler{ _ in self.self.goto(coordinates:self.perfectlySoftLocation) })
        
        let btnCurrLoc = self.page!.getWidgetByName("btnCurrLoc") as! SCDWidgetsButton
        btnCurrLoc.onClick.append(SCDWidgetsEventHandler{_ in self.gotoCurrentLocation()})
        
        let btnSushi = self.page!.getWidgetByName("btnSushi") as! SCDWidgetsButton
        btnSushi.onClick.append(SCDWidgetsEventHandler{_ in self.goto(coordinates:self.bestSushiLocation)})
        
        // let add buttons for setting map type
        let btnStandard = self.page!.getWidgetByName("btnStandard") as! SCDWidgetsButton
        btnStandard.onClick.append(SCDWidgetsEventHandler{_ in self.setMapType(btnStandard.name)})
        
        let btnHybrid = self.page!.getWidgetByName("btnSatellite") as! SCDWidgetsButton
        btnHybrid.onClick.append(SCDWidgetsEventHandler{_ in self.setMapType(btnHybrid.name)})
    
        let btnSatellite = self.page!.getWidgetByName("btnHybrid") as! SCDWidgetsButton
        btnSatellite.onClick.append(SCDWidgetsEventHandler{_ in self.setMapType(btnSatellite.name)})
        
    }
    
    func setMapType(_ name:String) {
        
        switch(name) {
            case "btnHybrid":
                self.mapWidget.mapType = SCDWidgetsMapType.hybrid
            case "btnSatellite":
                self.mapWidget.mapType = SCDWidgetsMapType.satellite
            case "btnStandard":
                self.mapWidget.mapType = SCDWidgetsMapType.standard 
            default:
                print("not covered")
        }
    }
    
    func goto(coordinates:SCDPlatformLocationCoordinate) {
 self.mapWidget.setRegion(coordinates,latitudinalMeters:1000,longitudinalMeters:1000)
    }
}

Setting the map type

The type of the map can easily be set using SCDWidgetsMapType.

func setMapType(_ name:String) {
 switch(name) {
  case "btnHybrid":
    self.mapWidget.mapType = SCDWidgetsMapType.hybrid
  case "btnSatellite":
    self.mapWidget.mapType = SCDWidgetsMapType.satellite
  case "btnStandard":
    self.mapWidget.mapType = SCDWidgetsMapType.standard 
   default:
    print("not covered")
 }
}

Setting the zoom level

Setting the zoom level is easy. Just set new values for latitudinalMeters and longitudinalMeters, the zoom respectively around the center coordinate.

// Use the meters metric to set the zoom level. Make sure to use a new instance
// set new region
    self.mapWidget.setRegion(coordinates,latitudinalMeters:1000,longitudinalMeters:1000)

Getting and going to current location

The current location is stored in the map widget's userLocation variable. However, you should first check the permission using isShowUserLocation() before going to the current location using moveToUserLocation()

func gotoCurrentLocation() {
// currently, the desktop returns isShowUserLocation==false
  if(mapWidget.isShowUserLocation) {
    let curr = mapWidget.userLocation
    print(curr.latitude)
    print(curr.longitude)
    mapWidget.moveToUserLocation()
  }
}

Map annotations

📘

Annotations position images and are zoom agnostic

Annotations are a common concept on both iOS and Android. The most common use case is for instance setting a pin to indicate a location Info

Follow these steps:

  1. Use a SVG graphic to represent the annotation, i.e. the pin
  2. Always set the width and height, these parameters are mandatory
  3. Use the xhref field to set the relative location of the SVG file
  4. Create an annotation at a specific location using the location field
  5. and assign the image in the drawing field
  6. Finally, add the annotation to the widget's list of annotations: .annotations
func setPinAsAnnotaton(coordinate:SCDPlatformLocationCoordinate, imagePath:String) {

    // We will further simplify the API
    let svgImage = SCDSvgImage(width:SCDSvgUnit(value:25),height:SCDSvgUnit(value:25))
    svgImage.xhref = imagePath

    let ann = SCDWidgetsMapAnnotation(location:coordinate)
    ann.drawing = svgImage

    self.mapWidget.annotations.append(ann)
}

Map overlays

📘

Overlays are images on a map that adjust to the zoom level

Depending on weather you zoom in or out, the image adjusts to the current zoom and increases or decreases in size.

Follow these steps:

  1. Convert your usual coordinates into the coordinate system of your mobile os using the convert method
  2. Create a scalable image. We use a circle in this example. Make sure to use a color.
  3. Create a map overlay and set the image to use using the drawing field
  4. Append the overlay to the list of overlays
func setOverlayAroundSushiPlace() {

  // get the map specific coordinates
  let coor2d = mapWidget.convert(fromGeoLocation: bestSushiLocation)

  // Create overlay circle of radius 1000m 
  let overlayCircle = SCDSvgCircle(cx:SCDSvgUnit(value:coor2d.x),cy:SCDSvgUnit(value:coor2d.y),r:SCDSvgUnit(value:1000))
  overlayCircle.fill = SCDSvgRGBColor(a:0.2,r:1,g:0,b:0)

  // Create an overlay
  let overlay = SCDWidgetsMapOverlay()
  overlay.drawing = overlayCircle

  // Add overlay onto the map
  mapWidget.overlays.append(overlay)

}

Troubleshooting

GoogleMaps in China

Please understand that Google Maps is not or only partially supported in China and the MapWidget won't work on Android