What should you have done by now?
- You should have at least followed the "Hello World" - Tutorial (better to do some more to become familiar with the possibilities)
- You should have followed the "Hello GLUE" - Tutorial (better to do some more to become familiar with the possibilities)
- You should have made yourself familiar with the PHP Getting Started library (you can follow the second Location Based or GLUE Tutorial)
What will you learn?
Scan the QR Code on the right with junaio to see what you are getting.
- Switching between GLUE and Location-Based Information
- Using server events in junaio
- Complex User interactions
Downloads:
Reference Images used
Get started:
-
This tutorial will combine a lot of the things that have been shown in any of the GLUE and Location Based Tutorials.
Imagine there are multiple Location Based POIs floating somewhere in the world. Each POI indicates a position of the treasure collecting pattern that can be found. Once the user is at a certain position, he can click the scan button and then scan the pattern to collect points. The tutorial will also show how to determine, whether the user really is at one of those positions (and does not just scan the pattern somewhere). Once the user has found all points, the game is over.
For experienced deverlopers, this tutorial can easily extended with a database to really store user information and make a highscore based on targets found or time needed to find all of them. The tracking of which POI has been found is done quite simple in this Tutorial, and will not remember once the channel is closed. This should be handled via a database in a real case. Have a look at the “Geo Trivia” Channel for a nice example of handling user information in a complex matter.
In the config.php you will find a define:
define('ALLOW_EVERYWHERE', false);
The Tutorial Channel you can look (scan the QR code) will have this stated as true, so everybody can try this at home before getting his or her hands dirty and scan the image over and over and you will find one treasure after another. The Tutorial will ignore this and only explain the “real way” – only being able to scan this at the position. Keep in mind that the package you download will show both ways.
- Here we go. Let’s start with the index.php. Make sure you define our shortcut, the WWW_ROOT again:
//WWW_ROOT will hold the path to the html folder -> makes resource referencing easier later on
define('WWW_ROOT', "http://".$_SERVER['HTTP_HOST'].dirname($_SERVER['SCRIPT_NAME'])); //path to online location
Also make sure, that the index.php links to the event.php once a pois/event is triggered, so this command may not be commented out:
include '../src/event.php';
- Next, we will define some locations, where treasures are hidden. Since we will need that couple of times, we will create a new file in the src folder and call it treasurelocations.class.php. In the constructor of the class, the locations are defined. The referenced classes will be needed later:
require_once '../library/poibuilder.class.php';
require_once '../library/Tools.php';
class Treasurelocations
{
//start by defining some positions of geo referenced POIs and give those names and thumbnails
private $treasureLocations = array();
public function __construct()
{
//start by defining some positions of geo referenced POIs and give those names and thumbnails
$this->treasureLocations = array(
array("37.772547,-122.418437,0", "San Francisco"),
array("48.138141,11.581535,0", "Munich"),
array("48.857261,2.3493,0", "Paris"),
array("40.73269,-73.995094,0", "New York"),
array("-33.92399,18.463669,0", "Cape Town"),
array("30.062557,31.246433,0", "Cairo"),
array("-37.813107,144.96304,0", "Melbourne"),
array("3.139145,101.689396,0", "Kuala Lumpur"),
array("35.685187,139.692306,0", "Tokio"),
array("19.019279,72.849541,0", "Mumbai"),
array("-12.042007,-77.040482,0", "Lima"),
array("51.493355,-0.127945,0", "London")
);
}
}
IMPORTANT: Reference this class in the index.php. You will see some require_once on top. Just add this line:
require_once '../src/treasurelocations.class.php';
- Those locations are the first thing the user will see when opening the channel. So he knows we to go to collect his or her first treasure. So in the search.php, return those POIs. Additionally there will be a “Scan” button as relative to screen. When a user is at a location, he needs to click this.
require_once '../library/poibuilder.class.php';
if(!empty($_GET['l']))
$position = explode(",", $_GET['l']);
else
trigger_error("user position (l) missing. For testing, please provide a 'l' GET parameter with your request. e.g. pois/search/?l=23.34534,11.56734,0");
//use the poiBuilder class
$jPoiBuilder = new JunaioBuilder($position, MAX_DISTANCE);
//create the xml start
$jPoiBuilder->start();
//return the geo referenced locations of POIs
$treasureLocationKeeper = new Treasurelocations();
$treasureLocationKeeper->outputTreasureLocation($position, $jPoiBuilder);
//the scan button
$poi = $jPoiBuilder->createGUIPOI(
"scan button", //name
"0.1,0", //position relative to screen
WWW_ROOT . "/resources/button.md2_enc", //mainresource(model)
WWW_ROOT . "/resources/button.png", //resource (texture)
".05", //size
"", //description
"", //thumb
"scan_", //id
"0,1.57,1.57", //orientation
NULL,
"click"
);
$jPoiBuilder->outputPOI($poi);
$jPoiBuilder->end();
This is quite a short output, but the logic happens elsewhere in this Tutorial. For example you can see there is just a method call to the Treaurelocations class. Also Note, the ID of the scan button is “scan_”. Later, we will have to split the ID to know at which locations the treasure has been found already. So when the user has found the treasure in San Francisco and Paris for example, the ID looks like “scan_0-2”. This is the reason why the channel will not remember anything you did once you leave the channel. Because everything is passed via the Poi IDs.
- The outputTreasureLocation() Method is fairly straight forward. The found variable will be set when this function is called after an event.
/**
*
* Will return all treasure locations to the client. The distance to the treasure is written in the description.
* Also if it was found by the user before, "FOUND" is added to the name
* @param array $position holding current users position as lat, lng and alt (=0)
* @param JunaioBuilder $jPoiBuilder Helper Class for creating junaio output
* @param string $found holding "-" - seperated the IDs of the treasures found
*/
public function outputTreasureLocation($position, $jPoiBuilder, $found = "")
{
$oTools = new Tools();
$treasuresFound = array();
//if at least one treasure has been found, fill the id in an array (makes searching easier)
if($found != "")
$treasuresFound = explode("-", $found);
//display the POIs as defined in the Constructor
foreach($this->treasureLocations as $i => $findPOI)
{
//title of the POI
$title = $findPOI[1];
//check if this POI has been found already
if(in_array($i, $treasuresFound))
$title = $findPOI[1] . " FOUND";
//get the distance between the user and the treasure
$poiLocation = explode(",", $findPOI[0]);
$distance = $oTools->distanceBetweenLLAsInMeters($position[0], $position[1], $poiLocation[0], $poiLocation[1]);
$distanceInKM = round($distance / 1000);
//create the POI
$poi = new SinglePOI();
$poi = $jPoiBuilder->createBasicLocationBasedPOI(
$title,
$findPOI[0],
"Find the treasure in " . $findPOI[1] . ". It is $distanceInKM" . "km away.",
WWW_ROOT . "/resources/icon.png",
WWW_ROOT . "/resources/thumb.png",
"$i",
"true"
);
//make sure that everyone is able to see the poi, even though he or she might be very far away
//output the POI
$jPoiBuilder->outputPOI($poi);
}
}
- So we are done with the first output. So far you have created a nice Location Based Channel, where you can guide your user to your “treasure image”. But users need to be able to scan the image once they are there. So I’ll show you what the event.php will look like and what happens once the scan button was clicked.
At the beginning we split the ID that was clicked to check for any information on treasures that might have been found. If the scan button was clicked, three main things happen: (1) tell junaio to no longer use GPS, but scan for images, (2) remove some POIs and (2) add some POIs.
In the start() method, you see a tracking XML returned, which tells junaio to check for images (1). The geo referenced POIs as well as the scan button are being removed (2). And a frame (to tell the user to scan something), a find button (if the user was wrong, he wants to go back to GPS mode) and an event trigger (send an event to the server, once the reference image was scanned) is sent to the client (3).
require_once '../library/poibuilder.class.php';
//use the poiBuilder class
$jPoiBuilder = new JunaioBuilder();
//get already scanned treasure finders. We will just add this to the event trigger and "find" button to remember this.
//of course, you could add things like this in a DB, would be more easy to manage and users could come back to their state
//an id will eventually look something like this "find_1-3-2"
$splitArray = explode("_", $_POST['id']);
//treasuresFound is a string looking like this eventually "1-3-2"
$treasuresFound = $splitArray[1];
//the user hit the scan button
if($_POST['type'] == "click" && strpos($_POST['id'], "scan") !== FALSE)
{
//(1) create the xml start to scan the glue pattern
$jPoiBuilder->start(WWW_ROOT . "/resources/tracking_tutorial.xml_enc");
//(2)remove the scan button
$jPoiBuilder->removePOIByID($_POST['id']);
//remove all treasure Locations
$treasureLocationKeeper = new Treasurelocations();
$treasureLocationKeeper->removeTreasureLocations($jPoiBuilder);
//(3)return the frame
$name = "scanBoarder";
$description = "";
$mainresource = WWW_ROOT ."/resources/scanFrame.md2_enc";
$resource = WWW_ROOT . "/resources/scanFrame.png";
$scale = "5";
$orientation = "1.57,1.57,1.57";
$behaviourArray = array();
$icon = "";//$resourcePath . "/icon_map.jpg";
$thumbnail = "";//$resourcePath . "/icon_map.jpg";
$id = "scanFrame";
$showNavigationButton = "false";
$interactionFeedback = "click";
$relativeToScreen = ".5,.5";
$poi = new SinglePOI();
$poi = $jPoiBuilder->createGUIPOI($name, $relativeToScreen, $mainresource, $resource, $scale, $description, $thumbnail, $id, $orientation, $behaviourArray, $interactionFeedback);
$jPoiBuilder->outputPOI($poi);
//return the find button
$poi = $jPoiBuilder->createGUIPOI(
"find button", //name
"0.1,0", //position relative to screen
WWW_ROOT . "/resources/button2.md2_enc", //mainresource(model)
WWW_ROOT . "/resources/button2.png", //resource (texture)
".05", //size
"", //description
"", //thumb
"find_" . $treasuresFound, //id
"0,1.57,1.57", //orientation
NULL,
"click"
);
$jPoiBuilder->outputPOI($poi);
//return the trigger event for actually finding the information
$poi = new SinglePOI();
$poi = $jPoiBuilder->createEventTrigger("foundSomething_" . $treasuresFound, 1);
$jPoiBuilder->outputPOI($poi);
$jPoiBuilder->end();
}
For removing the geo referenced POIs, just call:
$treasureLocationKeeper->removeTreasureLocations($jPoiBuilder);
Of course, add it to the Treasurelocations class.
/**
*
* Remove all georeference POIs as defined in the constructor
* @param JunaioBuilder $jPoiBuilder Helper Class for creating junaio output
*/
public function removeTreasureLocations($jPoiBuilder)
{
foreach($this->treasureLocations as $i => $findPOI)
$jPoiBuilder->removePOIByID("$i");
}
- First let’s look at switching back to the geo referenced treasure locations once the user clicks the find button. So the user can always switch back and forth between scanning and finding.
It’s an else if from the scan click. Once again (this time backwards): (1) tell junaio to no longer scan for images, but use GPS, (2) remove some POIs and (2) add some POIs.
So the trackingXML is returned that says use compass and GPS. You can download it here, when you are logged in (1), remove the scan stuff (2) and add the geo referenced stuff with the scan button (3).
else if($_POST['type'] == "click" && strpos($_POST['id'], "find") !== FALSE)
{
//(1)create the xml start to go back to finding
$jPoiBuilder->start(WWW_ROOT . "/resources/AS_TrackingDataLBS.xml_enc");
//(2)remove the scan informatino
$jPoiBuilder->removePOIByID("scanFrame");
$jPoiBuilder->removePOIByID($_POST['id']);
$jPoiBuilder->removePOIByID("foundSomething_" . $treasuresFound);
//get the current user’s location
if(!empty($_POST['l']))
$position = explode(",", $_POST['l']);
//(3)return the treasure locations
$treasureLocationKeeper = new Treasurelocations();
$treasureLocationKeeper->outputTreasureLocation($position, $jPoiBuilder, $treasuresFound);
//the scan button
$poi = $jPoiBuilder->createGUIPOI(
"scan button", //name
"0.1,0", //position relative to screen
WWW_ROOT . "/resources/button.md2_enc", //mainresource(model)
WWW_ROOT . "/resources/button.png", //resource (texture)
".05", //size
"", //description
"", //thumb
"scan_" . $treasuresFound, //id
"0,1.57,1.57", //orientation
NULL,
"click"
);
$jPoiBuilder->outputPOI($poi);
$jPoiBuilder->end();
}
- However, the interesting part happens, once the pattern is scanned. The main steps are similar to when clicking scan, since we want to get back to finding a new POI once one has been scanned. However, before the user sees this, he will be shown a micro website stating his result.
Actually a method of the Treasurelocations class gets the result. It returns an array, with the first value saying “newFound” (you have found a new treasure), “alreadyFound” (you have found this treasure already), “foundAll” (you are done, got them all) or “wrongLocation” (there is no treasure here). Second value are the treasures that have been found so far and if a treasure was found, which one it was as the third value.
Currently, when opening a website once the content is loaded, there is a customization added to a POI. This customization will be added to the scan button.
else if($_POST['type'] == "imagefound" && strpos($_POST['id'], "foundSomething") !== FALSE)
{
$jPoiBuilder->start(WWW_ROOT . "/resources/AS_TrackingDataLBS.xml_enc");
//get the current users location
if(!empty($_POST['l']))
$position = explode(",", $_POST['l']);
//the user scanned something -> check if the user is at a location, he or she has not found yet
//return array with "newFound OR "alreadyFound" OR "foundAll" OR "wrongLocation" AND all IDs found AND Name of treasure (if applicable)
//keep in mind: if ALLOW_EVERYWHERE is true (config.php), "alreadyFound" or "wrongLocation" will never be returned and is not checked -> so scanning x time at the same position will also result in "foundAll"
$treasureLocationKeeper = new Treasurelocations();
$return = $treasureLocationKeeper->getResult($position, $jPoiBuilder, $treasuresFound);
//remove the scan stuff and go back to find
$jPoiBuilder->removePOIByID("find_" . $treasuresFound);
$jPoiBuilder->removePOIByID("scanFrame");
$jPoiBuilder->removePOIByID("foundSomething_" . $treasuresFound);
//output treasure locations
$treasureLocationKeeper->outputTreasureLocation($position, $jPoiBuilder, $return[1]);
//the scan button
//the scan button has the customization attached to open the result url on idle
$poi = $jPoiBuilder->createGUIPOI(
"scan button", //name
"0.1,0", //position relative to screen
WWW_ROOT . "/resources/button.md2_enc", //mainresource(model)
WWW_ROOT . "/resources/button.png", //resource (texture)
".05", //size
"", //description
"", //thumb
"scan_" . $return[1], //id
"0,1.57,1.57", //orientation
NULL,
"click"
);
//attach a customization to start the URL on idle (so whenever the return reached the client)
$cust = new Customization();
$cust->setValue(WWW_ROOT . "/resources/return.php?return=" . $return[0] . "&found=" . urlencode($return[2]) . "&amount=" . count(explode("-", $return[1])));
$cust->setNodeID("idle");
$cust->setType("url");
$cust->setName("Results");
$poi->addCustomization($cust);
$jPoiBuilder->outputPOI($poi);
$jPoiBuilder->end();
}
The method for getting the results is actually quite straight forward:
/**
*
* Gets the result if a new treasure was found, the user have found them all,
* he is at a wrong location or the treasure was found already by him before
* @param array $position holding current users position as lat, lng and alt (=0)
* @param JunaioBuilder $jPoiBuilder Helper Class for creating junaio output
* @param string $found holding "-" - seperated the IDs of the treasures found
*/
public function getResult($position, $jPoiBuilder, $found = "")
{
$oTools = new Tools();
$treasuresFound = array();
//split the information about found treasures
if($found != "")
$treasuresFound = explode("-", $found);
//check if the user has found something new
foreach($this->treasureLocations as $i => $findPOI)
{
//get the distance between the user and the treasure
$poiLocation = explode(",", $findPOI[0]);
$distance = $oTools->distanceBetweenLLAsInMeters($position[0], $position[1], $poiLocation[0], $poiLocation[1]);
//check if the user is close to a location of a treasure
if($distance < DISTANCE_TO_FOUND)
{
//was this treasure found before?
if(in_array($i, $treasuresFound))
return array("alreadyFound", $found, $findPOI[1]);
else
{
//add this to the found ones
$treasuresFound[] = $i;
//check if we have found all
if(count($treasuresFound) + 1 >= count($this->treasureLocations))
return array("foundAll", implode("-", $treasuresFound), $findPOI[1]);
else
return array("newFound", implode("-", $treasuresFound), $findPOI[1]);
}
}
}
//nothing found
return array("wrongLocation", $found, "");
}
The DISTANCE_TO_FOUND is defined in the config.php. This allows for some offset between the treasure location and the users location for GPS inaccuracies.
- That finishes the Tutorial. Have fun creating really cool and exciting scavenger hunts based on this tutorial. Now you just need to upload it to the server, put you API key in the config.php and create a new channel pointing to the html folder of this package on your server.
Quickstart Guides
Location-Based
- Hello World!
- Trouble Shooting Hello World
- Images, videos and sounds
- Location-based 3D
- Animate T-Rex
- metaio Man vs. T-Rex
- Indoor location-based POIs
- Server events in junaio
- 360 degree views
- Images in front of you
- AR Shooter
junaio GLUE
- Hello GLUE!
- Trouble Shooting Hello GLUE
- 3D world interaction
- Movie textures in junaio
- More than 1 image
- Sometimes recognizing is enough
- junaio GLUE and events
More Tutorials
Scan this marker with
junaio to see what you are
getting in this tutorial.


