Layar Developer Documentation

Back to layar.com

Placing POis evenly relative to user's current location

Instead of placing POIs at a fixed physical location, POIs can be generated relative to user's current location. This means POIs' locations are dynamic and changed based on user's location. This is a good use case when POI content is not relevant to the physical world but something that the user can interact with no matter where s/he is.  For instance, the "Battle Los Angeles" layer where you will find some alien armies around you all the time!! (scary, isn't it ? :))

In this tutorial, we will showcase how to place POIs relative to user's current location. What we want to achieve is:

relative POIs - placing pois

In this tutorial, we will explain all the changes(in BOLD) based on the sample code and database settings from the third tutorial . Please check previous tutorials, if you are not familiar with the code.

Calculate POIs location

The key in this tutorial is to find each POI's location(lat and lon). In order to do so, we need to know the following items:

It refers to user's latitude and longitude in signed decimal degrees. They can be retrieved from the getPOI request.

Can be retrieved from the POI database. Once POIs are retrieved, the number of POIs can be calculated.

Bearing is the direction of a fixed point, or the path of a moving object, from a point of observation. Bearings are angles measured in degrees (°) from the north line in a clockwise direction (between 0° and 360°).

In our case, it is dependent on the search range, the "radius" parameter which is passed through the getPOI request.  In our case, we will put the POIs 50m shorter than the search range.

It is 6371km. 

We will use the following Formulas to calculate the POI latitude (lat2) and longitude(lon2):

Given: user's latitude (lat1), user's longitude (lon1), distance(in km) between the POI and the user (d), earth's radius in km (R), the bearing (in degree) of the POI from the user (θ).

Formula:

     lat2 = asin(sin(lat1)*cos(d/R) + cos(lat1)*sin(d/R)*cos(θ))

     lon2 = lon1 + atan2(sin(θ)*sin(d/R)*cos(lat1), cos(d/R)−sin(lat1)*sin(lat2))

NOTE: The input parameters (lat1, lon1, θ) should be converted from values in degrees to values in radians. On the other hand, the output lat2 andlon2  should be converted to values in signed decimal degrees.  We will not explain how these formulas are calculated, for more information, please visit http://www.movable-type.co.uk/scripts/latlong.html. Of course, this is not the only option out there, you can optimize/simplify the formula based on your needs.

Based on the formula provided above, we have created a function called getPoiLoc() which calculates the lat and lon of each POI. The input parameters are:

function getPoiLoc ( $lat, $lon, $brng, $dist ) {
  // Convert $lat, $lon and $brng into radian values.
  $lat = DegtoRad ($lat);
  $lon = DegtoRad ($lon);
  $brng = DegtoRad ($brng);
 
  // The earth's radius in km.  
  $R = 6371;
 
  // Calculate the latitude value (in radian) of the POI.
  $poi_lat = asin(sin($lat) * cos($dist/$R) + cos($lat) * sin($dist/$R) * cos($brng));
 
  // Calculate the longitude value (in radian) of the POI.
  $poi_lon = $lon + atan2(sin($brng) * sin($dist/$R) * cos($lat) ,
                    cos($dist/$R) - sin($lat) * sin($poi_lat));
 
  // Convert POI lat and lon from radians to signed decimal degrees.
  $poiloc['lat'] = RadtoDeg($poi_lat);
  $poiloc['lon'] = RadtoDeg($poi_lon);
 
  // return an array which contains the POI lat and lon in degrees.
  return $poiloc;
}//getPoiLoc

As you can see that we also created two functions (DegtoRad() and RadtoDeg()) to convert values from degrees to radians and vice verse. These can be found in the complete sample code provided below.

Add a new field (relativePoi) to the database

To determine which POIs should be returned, we added a new enum parameter "relativePoi" to "POI" table. It has two values "yes" or "no". By default, it is "yes" and it means this POI is relative to the user and should be returned in the query.

You can use the following sql statement to add this new column:

ALTER TABLE `POI` ADD relativePoi ENUM('yes','no') NOT NULL DEFAULT 'yes'

Now the POI table looks like:

relative POIs - POI table

Retrieve relative POIs and store calculated POI locations in the response array

Now we need to retrieve POIs that should be displayed and gather the right input for calculating the POI location. The changes are made in function getHotspots().

// Put received POIs into an associative array. The returned values are assigned to $response["hotspots"].
//
// Arguments:
//   db ; The handler of the database.
//   value ; An array which contains all the needed parameters retrieved from GetPOI request.
//
// Returns:
//   array ; An array of received POIs.
//
function getHotspots( $db, $value ) {
  // Define an empty $hotspots array.
  $hotspots = array();
/* Create the SQL query to retrieve POIs with relativePoi = 'yes'.
   The first 50 POIs are selected and returned.
*/
    
  // Use PDO::prepare() to prepare SQL statement.
  // $sql is returned as a PDO statement object.
  $sql = $db->prepare( '
              SELECT id,
                     imageURL,
                     title,
                     description,
                     footnote,
                     iconID,
                     objectID,
                     transformID
                FROM POI
               WHERE poiType = "geo"
                 AND relativePoi = "yes"
               LIMIT 0, 50 ' );

  // Use PDO::execute() to execute the prepared statement $sql.
  $sql->execute();
  // Use fetchAll to return an array containing all of the remaining rows in
  // the result set.
  // Use PDO::FETCH_ASSOC to fetch $sql query results and return each row as an
  // array indexed by column name.
  $rawPois = $sql->fetchAll(PDO::FETCH_ASSOC);
/* Process the $pois result */
  // if $rawPois array is not  empty
  if ($rawPois) {
 
    // Iterator for the response array.
    $i = 0;
    // Count the number of returned POIs from the query.
    $num = count($rawPois); 
    // Calculate the bearing difference between POIs. Divide 360 degrees evenly
    // by the number of POIs.
    $diff = (float)360 / $num;  
    // Calculate the distance in km between POIs and the user's current
    // location using function getDistance(); The POIs will be placed at a
    // distance 50m shorter than the search range.
    $distance = getDistance($value['radius']);
    // The earth's radius, 6371 km
    $R = 6371;

    // Put each POI information into $hotspots array.
    foreach ( $rawPois as $rawPoi ) {
      $poi = array();     

      $poi['id'] = $rawPoi['id'];
      $poi['imageURL'] = $rawPoi['imageURL'];
      
      // Calculate each POI's position relative to the user. The first POI
      // always has the same lat as user's lat.
      $brng = $diff * $i;
      // Calculate POI's lat and lon in signed demical degrees.
      $poiLoc = getPoiLoc ($value['lat'], $value['lon'], $brng, $distance);
      // Get anchor object information
      $poi['anchor']['geolocation']['lat'] = changetoFloat($poiLoc['lat']);
      $poi['anchor']['geolocation']['lon'] = changetoFloat($poiLoc['lon']);
      // get text object information
      $poi['text']['title'] = $rawPoi['title'];
      $poi['text']['description'] = $rawPoi['description'];
      $poi['text']['footnote'] = $rawPoi['footnote'];
      //User function getPOiActions() to return an array of actions associated
      //with the current POI
      $poi['actions'] = getPoiActions($db, $rawPoi);
      // Get object object information if iconID is not null
      if(count($rawPoi['iconID']) != 0)
        $poi['icon'] = getIcon($db , $rawPoi['iconID']);
      // Get object object information if objectID is not null
      if(count($rawPoi['objectID']) != 0)
        $poi['object'] = getObject($db, $rawPoi['objectID']);
      // Get transform object information if transformID is not null
      if(count($rawPoi['transformID']) != 0)
        $poi['transform'] = getTransform($db, $rawPoi['transformID']);
      // Put the poi into the $hotspots array.
      $hotspots[$i] = $poi;
      $i++;
    }//foreach
  }//if
  return $hotspots;
}//getHotspots

As you can see that we also created a new function called getDistance() to calculate the distance between the POI and the user. It is defined below:

// Calculate the distance between the POI and the user. It is 50m shorter than the search range (radius).   
//
// Arguments:
//   float radius ; The search range in meters.
//
// Returns:
//   float ; The distance between the POI and the user in km.
function getDistance ($radius) {
  // If $radius exists and it is not NULL.
  if (isset($radius)){
    If ($radius <= 50)
      return (float)$radius / 1000;   
  }else {
  // if $radius does not exist or the value is null. Return $radius as 1500m.
    $radius = 1500;
  }
  return (float)($radius - 50) / 1000;
}//getDistance

Play with it

Ok, we have mentioned above all the changes that you need to make. The rest should remain the same as the third tutorial. You can find the attached sample php code to play with. Once you understand how this example works, you can alter it to meet your need. Before wrapping up the tutorial, lets see how it looks like when I place 10 POIs relative to user location evenly.

relativePOIs - API test page preview