Create your first Placenote App from scratch

Build a simple app that will let us map an area and save a sphere persistently in the same position.

Step 1: Create a new AR project and install Placenote

Follow the instructions in the previous page - Installing the CocoaPod, to set up a brand new AR project and install the Placenote CocoaPod to it. When you're done with this, you should be able to build the ARKit project with the "include PlacenoteSDK" statement in your View Controller, like this

Step 2: Initialize Placenote and verify your API Key

Let's add the very first line of code, to initialize Placenote in your project. Go to your AppDelegate.swift file and import PlacenoteSDK at the top of the file.

import PlacenoteSDK

Then find this function (usually the first function in the file):

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

And add the initialization command to it like this.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
LibPlacenote.instance.initialize(apiKey: "My_API_Key", onInitialized: {(initialized: Bool?) -> Void in
if (initialized!) {
print ("SDK Initialized")
}
else {
print ("SDK Could not be initialized")
}
})
return true
}

Step 3: Add the Placenote Delegate functions

In your ViewController.swift file, make your ViewController class a delegate of PNDelegate by adding this to your class definition.

class ViewController: UIViewController, ARSCNViewDelegate, ARSessionDelegate, PNDelegate {

When you set your ViewController to be a PNDelegate, you will see an error that says,

"ViewController does not confirm to the protocol 'PNDelegate'

The screenshot below shows what you'll see.

Click on "Fix" to resolve this issue, and you'll notice that 3 new function stubs have been added to your View Controller. We'll use this later in the tutorial.

The functions added to your project are listed here.

func onPose(_ outputPose: matrix_float4x4, _ arkitPose: matrix_float4x4) {
}
func onStatusChange(_ prevStatus: LibPlacenote.MappingStatus, _ currStatus: LibPlacenote.MappingStatus) {
}
func onLocalized() {
}

Step 4: Configure some required Placenote variables

Within viewDidLoad(), add your ViewController to be the multiDelegate of PlacenoteSDK as the ViewController initializes.

LibPlacenote.instance.multiDelegate += self

Make sure you have also added ViewController as a session delegate for ARKit like this:

sceneView.session.delegate = self

Next, Placenote SDK has a utility called CameraManager that allows the PlacenoteSDK to manage the position of the ARKit Camera between ARKit Sessions. You activate it by simply declaring the object and passing in the ARKit Scene and Camera Object.

//Initialize the camManager variable in your class
private var camManager: CameraManager? = nil;
//Add this to the end of your viewDidLoad function
if let camera: SCNNode = sceneView?.pointOfView {
camManager = CameraManager(scene: sceneView.scene, cam: camera)
}

Finally, to visualize point clouds created by Placenote, you can initialize a feature point visualizer in the class like this

private var ptViz: FeaturePointVisualizer? = nil;

and initialize it within your viewDidLoad() function.

// Add this to your viewDidLoad() function
ptViz = FeaturePointVisualizer(inputScene: sceneView.scene);
ptViz?.enableFeaturePoints()

When it’s all put together, your viewDidLoad() or viewDidAppear() should look something like this

override func viewDidLoad() {
super.viewDidLoad()
// configuration
LibPlacenote.instance.multiDelegate += self
// set up AR session
self.sceneView.session.run(configuration)
self.sceneView.autoenablesDefaultLighting = true
self.sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
sceneView.session.delegate = self
ptViz = FeaturePointVisualizer(inputScene: sceneView.scene);
ptViz?.enableFeaturePoints()
if let camera: SCNNode = sceneView?.pointOfView {
camManager = CameraManager(scene: sceneView.scene, cam: camera)
}
}

Step 4: Mapping and Localizing

You are now ready to render your sphere and use PlacenoteSDK to remember where it was! Doing this is a simple 3 step process.

  1. Start a Mapping Session and place content

  2. Save the map and retrieve a relevant Map ID

  3. Load a map with a Map ID and start a Localization Session.

After Step 3, PlacenoteSDK attempts to relocalize your device with with the loaded map and when it finds a match, it shifts the ARCamera to the correct position. Let’s look at how to get this working.

Start a mapping session

We start a Placenote mapping session by calling startSession(), like this.

LibPlacenote.instance.startSession()

However, we need to trigger this session somehow. In this example we'll use a button and start mapping when a user clicks on a button. When the button in clicked, we'll render a sphere at (0,0,0) and start mapping.

To set this up, add a button to your Main Storyboard and within its IBAction, call this function to render a sphere.

// Create a sphere at (0,0,0) and apply a diffuse green color to it
@IBAction func startMappingAndRenderSphere(_ sender: Any) {
// start placenote mapping
LibPlacenote.instance.startSession()
renderSphere()
}
func renderSphere() {
// render a sphere at (0,0,0)
let geometry:SCNGeometry = SCNSphere(radius: 0.05) //units, meters
let geometryNode = SCNNode(geometry: geometry)
geometryNode.position = SCNVector3(x:0.0, y:0.0, z:0.0)
geometryNode.geometry?.firstMaterial?.diffuse.contents = UIColor.green
sceneView.scene.rootNode.addChildNode(geometryNode)
}

The goal of this app will be to prove that once we render a sphere and save the map, we can reload the map and relocalize the scene exactly where we left it. (i.e. We'll make the sphere appear in the same place every time.)

When a mapping session is started, we must send Placenote the image frames coming from the camera so they can be processed. We’ll do this in the ARKit session call back. As a reminder, we initialized this using sceneView.session.delegate = self inside viewDidLoad().

Add this function to your ViewController.swift

// send AR frame to placenote
func session(_ session: ARSession, didUpdate: ARFrame) {
LibPlacenote.instance.setARFrame(frame: didUpdate)
}

Save the map

Next, you’ll want to save the map so we can recall it later. You do this by calling saveMap. We can use another button to stop mapping and save the map.

@IBAction func saveMap(_ sender: Any) {
//save the map and stop session
LibPlacenote.instance.saveMap(
savedCb: { (mapID: String?) -> Void in
print ("MapId: " + mapID!)
LibPlacenote.instance.stopSession()
},
uploadProgressCb: {(completed: Bool, faulted: Bool, percentage: Float) -> Void in
print("Map Uploading...")
if(completed){
print("Map upload done!!!")
}
})
}

Note how the saveMap function returns a unique Map ID. We just output it to the console here, but you could also save it programmatically or retrieve it later. The map is saved and uploaded to the cloud for access on any device running with your API Key.

Reload a map

Now let’s look at how to retrieve the map every time you turn on the app. For this example, let’s create a third button and within it's IBAction, make a call to loadMap() using the mapID we just generated. You'll need to manually paste the MapID that was printed to the console when you created and saved the map on your device in the previous step.

The way LoadMap works is that it downloads the Map (if it's not already downloaded) on your phone. When the "completed" callback is received, we call startSession() again. This time Placenote SDK will try to localize the scene for you and update the camera position.

@IBAction func loadMap(_ sender: Any) {
// we're now loading a map
LibPlacenote.instance.loadMap(mapId: "Paste Map ID here",
downloadProgressCb: {(completed: Bool, faulted: Bool, percentage: Float) -> Void in
if (completed) {
LibPlacenote.instance.startSession()
}
})
}

For now, we just copy paste the Map ID as it was output to the console when we saved the map. However, in most circumstances you will save this mapID locally or on your app’s backend to retrieve any time.

Now, remember when you set up a delegate with a function called onLocalized()? The moment PlacenoteSDK finds the map you created before, the onLocalized callback is fired. When this happens, you can render the sphere exactly at (0,0,0) like this.

func onLocalized() {
renderSphere()
}

Let's run this app in place now! No matter where we start, we find the sphere appearing in the exact same position we initially placed it.