Page Example

Gps consumer Sample Overview

Mobile native

The GPS Consumer sample application demonstrates how to implement a location-based application utilizing the geolocation data provided by a GPS service and a geolocation boundary provided by the Location API.

The following figure illustrates the application views.

Figure: GPS Consumer screens

No service Inside the boundary Outside the boundary

The application is responsible for displaying geolocation and satellite information acquired from the GPS service through a message port. The coordinates (longitude and latitude) are acquired at 1-second intervals, whereas the satellites information is fetched every 5 seconds. These interval values are determined by the GPS service.

The application main window consists of 3 areas:

  • Information: displays the longitude, latitude, and number of visible satellites
  • Message: displays textual information based on the geolocation data received from the GPS service and cross-boundary calculation
    • Boundary area exceeded: if the current position is outside the defined boundary
    • Inside boundary area: if the current position is inside the defined boundary
  • Map: displays the local area with the current position marked. At the initial position, the circular boundary is set with a radius equal to 30 m. The current position marker is updated according to the acquired geolocation data.

Implementation

Message Port Module

The message port module is used for communication between the GPS service and consumer applications. The GPS consumer application must register a local port with a specified name to receive data from the GPS service. To do that, the message_port_register_local_port() function is invoked. The function takes the local port name as the first parameter and a callback function to be called when a message is received as the second. If the port is created successfully, a local message port ID is returned; otherwise, the function returns a negative error value.

int local_port_id = message_port_register_local_port(LOCAL_PORT_NAME, _msg_port_cb, NULL);
if (local_port_id < 0)
{
   dlog_print(DLOG_ERROR, LOG_TAG, "Failed to register port, error: %d", local_port_id);
	
   return false;
}

When a message is received on the previously registered local port, the _msg_port_cb() function is invoked. The message in a form of a bundle is parsed here to check the message type and content. The _bundle_str_error_check_get() function is a simple helper that retrieves a string value associated with a given key and logs the appropriate message in case an error occurs. After the message content is acquired, it is passed to another function to be handled depending on the message type.

static void
_msg_port_cb(int local_port_id, const char *remote_app_id, const char *remote_port, bool trusted, bundle *message, void *user_data)
{
   char *msg_type = NULL;
   char *latitude_str = NULL, *longitude_str = NULL;
   char *satellites_count_str = NULL;

   if (!_bundle_str_error_check_get(message, MESSAGE_TYPE_STR, &msg_type))
   {
      return;
   }

   if (!strncmp(msg_type, MESSAGE_TYPE_SATELLITES_UPDATE, strlen(msg_type)))
   {
      if (!_bundle_str_error_check_get(message, MESSAGE_SATELLITES_COUNT_STR, &satellites_count_str))
      {
         return;
      }
      dlog_print(DLOG_INFO, LOG_TAG, "Received message from %s: satellites_count %s", remote_app_id, satellites_count_str);
      _satellites_update(satellites_count_str);
   }
   else if (!strncmp(msg_type, MESSAGE_TYPE_POSITION_UPDATE, strlen(msg_type)))
   {
      if (!_bundle_str_error_check_get(message, MESSAGE_LATITUDE_STR, &latitude_str) ||
         !_bundle_str_error_check_get(message, MESSAGE_LONGITUDE_STR, &longitude_str))
      {
         return;
      }
      dlog_print(DLOG_INFO, LOG_TAG, "Received message from %s: position data: %s %s", remote_app_id, latitude_str, longitude_str);
      _position_update(latitude_str, longitude_str);
   }
}
static bool
_bundle_str_error_check_get(bundle *message, char *msg_type, char **message_content)
{
   if (bundle_get_str(message, msg_type, message_content) != BUNDLE_ERROR_NONE)
   {
      dlog_print(DLOG_ERROR, LOG_TAG, "Failed to get message: %s", msg_type);
		
      return false;
   }

   return true;
}

When the GPS consumer application receives a message with the initial coordinates, it uses the values to create a map with the circle boundary as a center point defined by the coordinates.

static void
_circle_init(char *circle_latitude_str, char *circle_longitude_str)
{
   double circle_latitude, circle_longitude;

   if (!circle_latitude_str || !circle_longitude_str)
   {
      dlog_print(DLOG_ERROR, LOG_TAG, "Invalid parameter");

      return;
   }

   sscanf(circle_latitude_str, "%lf", &circle_latitude);
   sscanf(circle_longitude_str, "%lf", &circle_longitude);

   view_manager_map_with_circle_boundary_create(circle_longitude, circle_latitude);
}

When the first position update message is received, the coordinates are used to create a map with the circle boundary whose center point is defined by the coordinate values. After that, when the position update message is received, the coordinates are passed to the view manager to update the current location marker on the map as well as to check whether the current coordinates are inside the previously defined boundary.

static void
_position_update(char *latitude_str, char *longitude_str)
{
   double latitude, longitude;

   if (!latitude_str || !longitude_str)
   {
      dlog_print(DLOG_ERROR, LOG_TAG, "Invalid parameter");

      return;
   }

   sscanf(latitude_str, "%lf", &latitude);
   sscanf(longitude_str, "%lf", &longitude);

   view_manager_map_position_update(longitude, latitude);
}

When the satellite update message is received, the satellite count is passed to the view manager to handle the displayed satellite value.

static void
_satellites_update(char *satellites_count_str)
{
   view_manager_satellites_count_update(satellites_count_str);
}

View Manager Module

The application UI consists of a window object with a standard setup with an EDJE-defined layout.

The map is created after the first position update message is received. The zoom level is set to ZOOM_LEVEL (18), which is currently the maximum supported value.

static Evas_Object*
_map_create(Evas_Object *parent)
{
   Evas_Object *map = elm_map_add(parent);
   if (!map)
   {
      dlog_print(DLOG_ERROR, LOG_TAG, "Failed to create map");

      return NULL;
   }

   evas_object_size_hint_weight_set(map, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
   evas_object_size_hint_align_set(map, EVAS_HINT_FILL, EVAS_HINT_FILL);
   evas_object_show(map);

   elm_map_zoom_set(map, ZOOM_LEVEL);

   return map;
}

After the map has been created, a circle boundary is created with the center defined by the received coordinates and the radius equal to 30 m. The boundary consists of 2 elements:

  • Map overlay, which is a graphic representation of the boundary. It is created directly on the map using the elm_map_overlay_circle_add() function. The map object, circle center longitude and latitude, as well as the pixel length of the radius are passed to this function. In this example, with the zoom level set to 18 and radius length equal to 30 meters, the pixel length of the radius is set to BOUNDARY_CIRCLE_RADIUS_REL (0.0002604).

    The minimum zoom level with which the overlay is displayed is set with the elm_map_overlay_displayed_zoom_min_set() function.

    // Create circle boundary
    dlog_print(DLOG_INFO, LOG_TAG, "Circle boundary overlay at %lf, %lf", circle_longitude, circle_latitude);
    
    s_view_data.boundary_overlay = elm_map_overlay_circle_add(s_view_data.map, circle_longitude, circle_latitude, BOUNDARY_CIRCLE_RADIUS_REL);
    if (!s_view_data.boundary_overlay)
    {
       dlog_print(DLOG_ERROR, LOG_TAG, "Failed to create boundary overlay");
    
       return;
    }
    
    elm_map_overlay_displayed_zoom_min_set(s_view_data.boundary_overlay, ZOOM_LEVEL);
    
  • Circle location bounds, which define a geographical boundary that can be used to track the device entering and leaving a specified region. The location center coordinates in a form of the location_coords_s structure and the radius (in meters) are passed to the location_bounds_create_circle() function to create the location bounds. The third function parameter is the newly created location bounds.
    // Create location bounds - BOUNDARY_CIRCLE_RADIUS [m] circle with center defined by initial coordinates
    circle_coords.longitude = circle_longitude;
    circle_coords.latitude = circle_latitude;
    
    dlog_print(DLOG_INFO, LOG_TAG, "center: %lf %lf, radius %lf", circle_coords.longitude, circle_coords.latitude, BOUNDARY_CIRCLE_RADIUS_M);
    int ret = location_bounds_create_circle(circle_coords, BOUNDARY_CIRCLE_RADIUS_M, &s_view_data.bounds);
    if (ret != LOCATIONS_ERROR_NONE)
    {
       dlog_print(DLOG_ERROR, LOG_TAG, "Failed to create circle bound: error %d", ret);
    
       return;
    }
    dlog_print(DLOG_INFO, LOG_TAG, "Circle bounds created - %.1fm radius, center at %f, %f", BOUNDARY_CIRCLE_RADIUS_M, circle_coords.longitude, circle_coords.latitude);
          

When the position update message is received, the view_manager_map_position_update() function is invoked to apply the current position coordinate changes to the displayed map. Another map overlay is created indicating the device position on the map. The minimum zoom level with which the overlay is displayed is set with the elm_map_overlay_displayed_zoom_min_set() function.

If the overlay already exists, its position is changed to match the current coordinates using the elm_map_overlay_region_set() function.

The region that is shown on the map is changed so that the current position marker is at the center of the map. It is done with the elm_map_region_bring_in() function, which takes the map object and new map center coordinates as parameters.

// Set position overlay to new coordinates
if (!s_view_data.pos_overlay)
{
   // Create position overlay
   s_view_data.pos_overlay = elm_map_overlay_add(s_view_data.map, longitude, latitude);
   if (!s_view_data.pos_overlay)
   {
      dlog_print(DLOG_ERROR, LOG_TAG, "Failed to create position overlay");

      return;
   }

   elm_map_overlay_displayed_zoom_min_set(s_view_data.pos_overlay, ZOOM_LEVEL);
   elm_map_overlay_show(s_view_data.pos_overlay);
}
else
{
   // Move position overlay
   elm_map_overlay_region_set(s_view_data.pos_overlay, longitude, latitude);
}

// Show current region
elm_map_region_bring_in(s_view_data.map, longitude, latitude);

To check whether the given coordinates are within the boundary circle, the location_bounds_contains_coordinates() function is invoked. It takes the location bounds object and the location_coords_s structure as parameters and returns a bool value (true if the bounds contain the specified coordinates, false otherwise).

Depending on the result, a message is set either to MESSAGE_BOUNDARY_INSIDE (Inside boundary area) or MESSAGE_BOUNDARY_OUTSIDE (Boundary area exceeded).

coords.longitude = longitude;
coords.latitude = latitude;

contains = location_bounds_contains_coordinates(s_view_data.bounds, coords);

contains ? view_manager_message_update(MESSAGE_BOUNDARY_INSIDE) : view_manager_message_update(MESSAGE_BOUNDARY_OUTSIDE);

When the application is closed, the view resources must be released. The elm_map_overlay_del() function is used to delete the created map overlays. Also, the location bounds must be released using the location_bounds_destroy() function.

elm_map_overlay_del(s_view_data.pos_overlay);
elm_map_overlay_del(s_view_data.boundary_overlay);

location_bounds_destroy(s_view_data.bounds);