Build

Live Updating Streams of GPS Coordinates: PubNub Signals

5 min read Oscar Castro on Sep 3, 2019

Not all message sizes are the same. For example, the size for sending a text message is much greater than sending GPS coordinates. Not only that, text messages must be encrypted and stored in history for later retrieval while coordinates are useful for a brief moment of time. It seems fair that larger payloads should be priced higher than smaller payloads. With this in mind, PubNub has launched Signals.

PubNub Signals

PubNub Signals is small message payload messaging, no greater than 64 bytes, that offers a low-cost data delivery rate while still leveraging PubNub’s secure and reliable Data Stream Network. Some use cases for Signals include:

  1. Typing indicators in a chat app
  2. GPS lat/long updates (what we’ll cover in this tutorial)
  3. Sensor streaming updates for IoT devices
  4. Stats for gaming applications

When to Use Signals Instead of Publish?

If your application needs to send a stream of messages to instruct or inform your application, where the content of each message is not as important as the stream of data, then use Signals. Signals uses the core Pub/Sub features such as channels groups and Access Manager.

Unlike Publish, there are some limitations to using Signals:

  1. Messages sent with Signals are not replicated to all global data centers
  2. Messages are not stored in PubNub History for later retrieval; only the most recent message can be fetched
  3. Mobile Push notifications can’t be invoked
  4. Access to Functions is disabled by default and can only be enabled by PubNub Support

Geolocation App with PubNub Signals

To get a better understanding of what you can do with PubNub Signals, let’s go over a

in Android. For this app, a marker will initially be placed in San Francisco. The user can move and place the marker anywhere they please, and the coordinates will be sent to a global channel using Signals. The marker will update for everyone connected to the channel.

Geolocation App Screenshot

Note: This app DOES NOT use your location!

Get Your API Keys

Sign up for your free PubNub API keys. You can send up to 1 million free messages a month! Use the form below to get your keys:

Once you have your keys, add them to initiPubNub() in MainActivity.java:

 // Variables
 private GoogleMap mMap;
 private PubNub pubnub;
 private Marker marker;
 private Boolean isMarkerPlaced = false;

// Initialize PubNub and set up a listener
public void initPubNub(){
  PNConfiguration pnConfiguration = new PNConfiguration();
  pnConfiguration.setPublishKey("Your_Pub_Key_Here");
  pnConfiguration.setSubscribeKey("Your_Sub_Key_Here");
  pnConfiguration.setSecure(true);
  pubnub = new PubNub(pnConfiguration);
 ...
}

Besides the Pub/Sub keys, you also need to get a Google Maps API key. Once you have the key, go to the debug directory, under src, and add it to the file google_maps_api.xml.

<resources>
    <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">YOUR_KEY_HERE</string>
</resources>

Now that we have all the keys set up in the app, let’s go over the app.

Set Up PubNub’s Event Listener

After initializing PubNub, we set up an event listener to listen for messages that arrive on the channel. Inside the listener, we implement the method signal().

// Listen to messages that arrive in the channel
pubnub.addListener(new SubscribeCallback() {
  @Override
  public void status(PubNub pub, PNStatus status) {

  }

  @Override
  public void message(PubNub pub, final PNMessageResult message) {

  }

  @Override
  public void presence(PubNub pub, PNPresenceEventResult presence) {

  }

  @Override
  public void signal(PubNub pubnub, final PNMessageResult signal) {
    System.out.println("Message: " + signal.getMessage());

    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        try {
          JsonArray payload = signal.getMessage().getAsJsonArray();
          placeMarker(payload);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    });
  }
});

Since we are no longer publishing the GPS coordinates with PubNub Publish, the method message() is empty. Inside of the signal() method, the message payload is received, converted into a JsonArray, and placeMarker() is called to update the current marker with the new coordinates in the map. We’ll come back to this method shortly.

Below addListener(), subscribe to the global channel geolocation_channel .

// Subscribe to the global channel
pubnub.subscribe()
      .channels(Arrays.asList("geolocation_channel"))
      .execute();

Next, let’s set up the marker on the map and add a drag and drop event listener for the marker.

Set Up Google Maps Event Listener

As mentioned earlier, the marker is initially placed in San Francisco. To add the marker in the map, placeMarker() is called with the marker coordinates as the argument. Before we get to this method, let’s complete the method onMapReady() which is triggered when Google Maps is ready to use.

public void onMapReady(GoogleMap googleMap) {
  mMap = googleMap;

  // Initial payload coordinates in S.F.♥︎
  JsonArray payload = new JsonArray();
  payload.add(37.782486);
  payload.add(-122.395344);
  placeMarker(payload);
}

We need to add an event listener that is triggered when the marker is dragged anywhere in the map. Add the following code to onMapReady():

// Initial payload coordinates in S.F.♥︎
...

// Listener for when marker is dragged to another location
mMap.setOnMarkerDragListener(new GoogleMap.OnMarkerDragListener() {
  @Override
  public void onMarkerDrag(Marker arg0) {
    Log.d("Marker", "Dragging");
  }

  @Override
  public void onMarkerDragEnd(Marker arg0) {
    Log.d("Marker", "Finished");
  }

  @Override
  public void onMarkerDragStart(Marker arg0) {
    Log.d("Marker", "Started");
  }
});

Once the user has placed the marker in a new location, the Lat/Long coordinates are sent to the global channel to update the marker placement for everyone connected. To do so, we get the new position of the marker, add it to a JsonArray, and send the payload to the channel. Since we can only send 64 bytes, we format the coordinate values up to 6 digits after the decimal point, which is the same format that Google Maps uses. We do all of this logic inside of onMarkerDragEnd():

@Override
public void onMarkerDragEnd(Marker arg0) {
  Log.d("Marker", "Finished");

  // Get coordinate values up to 6 decimal numbers
  DecimalFormat decimalFormat = new DecimalFormat("0.######");
  JsonArray payload = new JsonArray();
  payload.add(decimalFormat.format(marker.getPosition().latitude)); // Get lat coord.
  payload.add(decimalFormat.format(marker.getPosition().longitude)); // Get long coord.

  // Message Payload size for PubNub Signals is limited to 64 bytes
  pubnub.signal()
        .channel("geolocation_channel")
        .message(payload)
        .async(new PNCallback<PNPublishResult>() {
          @Override
          public void onResponse(PNPublishResult result, PNStatus status) {
            // Error
            if(status.isError()) {
              System.out.println("Error- pub status code: " + status.getStatusCode());
            }
          }
        });
}

If the payload is successfully sent and received on the channel, the marker is placed on the map.

Place the Marker on the Map

In the app, we call placeMarker() twice to add the marker in the initial location and to update the marker in a new location. In order for the method to know which of the two things to do, we use the boolean variable isMarkerPlaced. This variable is initialized to false. It is set to true once the maker has been placed.

// Place marker on the map in the specified location
public void placeMarker(JsonArray loc){
  //LatLng only accepts arguments of type Double
  Double lat = loc.get(0).getAsDouble();
  Double lng = loc.get(1).getAsDouble();
  LatLng newLoc = new LatLng(lat,lng);

  if(isMarkerPlaced){
    // Change position of the marker
    marker.setPosition(newLoc);
    marker.setTitle(newLoc.toString());
  }
  else{
    // Add a marker to the map in the specified location
    marker = mMap.addMarker(new MarkerOptions().position(newLoc).title(newLoc.toString()).draggable(true));
    isMarkerPlaced = true;
  }

  // Move the camera to the location of the marker
  mMap.moveCamera(CameraUpdateFactory.newLatLng(newLoc));
}

Run the Geolocation App

In Android Studio, run the app in two emulators to see the markers change location in real time!

Now that you know how to send GPS coordinates using PubNub Signals, check out other ways to use Signals, such as implementing Typing Indicators in a Chat App.

Have suggestions or questions about the content of this post? Reach out at devrel@pubnub.com.

0