Getting Started

To integrate your web or mobile application with Pathfinder, you will need to do two things:

  1. Sign up at https://thepathfinder.xyz and register an application.
  2. Identify and include the platform-appropriate SDK in your app.

We also recommend that you are familiar with the Pathfinder model and related terms before using this tutorial.

In this tutorial, the reader is guided through a series of steps in order to add Pathfinder functionality to an unfinished project. This project has two components, one for users who want to be delivered and another for drivers who pick up the users.

Adding a Pathfinder SDK dependency

To access one of our SDKs you must add a dependency to your project. We currently have SDKs for Android/Java, iOS, JavaScript. The Android/Java SDK is available on JCenter or Maven Central. The iOS SDK is available on CocoaPods, and Javascript SDK is available on Bower.

// Using Gradle
// Add this to your build.gradle file in the dependencies section
compile 'xyz.thepathfinder:pathfinder-android:1.0.0'
// Gson is needed if you intended to create a commodity or transport
compile 'com.google.code.gson:gson:2.5'
// bower.json
{
  "name": "my web app",
  "dependencies": {
		"pathfinder.js": "1.0.0"
  }
}
// Podfile
pod 'thepathfinder', '~> 1.0.0'

Using Pathfinder in your application

To use each of our SDKs you must create a pathfinder object. When created it will take care of the authentication sequence and open a WebSocket to the API server.

🚧

Java and Oracle's JDK SSL Certificate Problems

Note, if you are using OpenJDK this warning does not apply to you.

If you are using our Android/Java SDK and Oracle's JDK make sure to add the Let's Encrypt certificate to your keystore. Currently, Let's Encrypt's certificate is not trusted by default in Oracle's JDK, so if you do not add the certificate to your keystore you will not be able to connect the Pathfinder server. To tell if you have not correctly added the certificate to your keystore use pathfinder.connect(false). If it is incorrect you will receive a java.lang.RuntimeException: javax.websocket.DeploymentException: SSL handshake has failed in the output of your program.

Download Let’s Encrypt Authority X1 and X3 Certificates and add them to your keystore to fix the problem.

Pathfinder pathfinder = new Pathfinder("YOUR_APPLICATION_ID", "USER_ID_TOKEN");

// Synchronously connect to the Pathfinder server.
// Use this when first creating an application to find common 
// SSL certificate problems.
pathfinder.connect(false);

// Asynchronously connect to the Pathfinder server
pathfinder.connect()
var pathfinder = new Pathfinder(APP_ID, ID_TOKEN);
import thepathfinder

let pathfinder = Pathfinder(applicationIdentifier: myPathfinderAppId)
pathfinder.connectAndAuthenticateWithPathfinderAuth(userGoogleIDToken) {
  (success: Boolean) -> Void in
    // your code here
}

Getting a cluster

To access a cluster's data you must first get the cluster from the SDK and then use connect to fetch its data from the Pathfinder server. When getting a cluster from the SDK you can either use paths or get the default cluster. The default cluster is top cluster in an application which is represented by "/root" when using paths.

All paths use the cluster's name and its parent cluster's names separated by a "/". Therefore, a path with "/root/washington/seattle" means the default cluster has a subcluster named washington and washington has a subcluster named seattle.

While the cluster is unconnected its data is not the same as on the Pathfinder server. Use the connect function to asynchronously fetch the cluster's data from the server. You can listen for the connection request to complete using our language specific listeners.

// After creating a pathfinder object you can get a cluster
Cluster defaultCluster = pathfinder.getDefaultCluster();

// Which is the same as 
defaultCluster = pathfinder.getCluster("/root");

// Asynchronously fetch the cluster's data
defaultCluster.connect();

// Using paths
Cluster seattleCluster = pathfinder.getCluster("/root/washington/seattle");

// Add a cluster listener, MyClusterListener is a class you create that extends ClusterListener
seattleCluster.addListener(new MyClusterListener());
seattleCluster.connect(); // don't forget to connect to the cluster
pathfinder.getDefaultCluster(clusterId, function(cluster){
  // do stuff with cluster
});
let defaultCluster = pathfinder.cluster()
let nycCluster = pathfinder.cluster("/root/eastcoast/nyc")

defaultCluster.connect() { (cluster: Cluster) -> Void in
	// Note that here cluster == defaultCluster.
}

// The delegate can be set at any time
nycCluster.delegate = ClusterDelegate()
nycCluster.connect()

Creating a commodity

Once you have a cluster you can add commodities to that cluster. Check out the commodity documentation for more information on commodities.

// Use Gson's JSON object to create metadata
JsonObject metadata = new JsonObject();
metadata.addProperty("capacity", 5);

// Locally create a commodity
Commodity commodity = seattleCluster.createCommodity(47.563711, -122.313555, 47.563501, -122.319499, CommodityStatus.INACTIVE, metadata);

// You can also add listeners to commodities, MyCommodityListener extends CommodityListener
commodity.addListener(new MyCommodityListener());
        
// Create the commodity on the Pathfinder server
commodity.create();
var metadata = { capacity: 5 };

pathfinder.createCommodity(
  47.563711, -122.313555,
  47.563501, -122.319499
  metadata,
  "Waiting",
  clusterId
);
let startLocation = CLLocationCoordinate2D(latitude: 40.71, longitude: 74.00)
let endLocation = CLLocationCoordinate2D(latitude: 41.34, longitude: 74.39)
let metadata = ["person": 2, "bike": 0]

pathfinder.cluster("/root/eastcoast/nyc").connect() { (cluster: Cluster) -> Void in
	cluster.createCommodity(start: startLocation, destination: endLocation, metadata: metadata)
}

The top cluster is called the default cluster or the root cluster.

Subscribing to a commodity

After creating a commodity, you might want to receive updates from the Pathfinder server when anything interesting happens to the commodity. To do this you may subscribe or route subscribe to the commodity.

Subscribing gives you access to any changes in the state of commodity immediately after the Pathfinder server receives them. This way you can keep multiple users in sync without having to poll the Pathfinder server periodically.

// Use a commodity listener to receive updates
commodity.subscribe();
commodity.subscribe(function(com){
  console.log(com.status);
});
commodity.subscribe()

Route subscribing only gives you updates to changes in the commodity's route. A route is only assigned once a transport has been routed to pick up your commodity. Note, no new route is generated if no transports can pick up the commodity or the commodity's status is not waiting.

commodity.routeSubscribe();
commodity.routeSubscribe(function(com){
  for(var i = 0; i < com.actions.length; i++){
  	console.log(com.actions[i]); 
  }
});
// The iOS SDK does not differentiate between status subscriptions and route subscriptions.
commodity.subscribe()

Picking Up a commodity

Once, a transport picks up a commodity you should let others listening know by using the following code. This will update the transport's route to not include picking up the commodity.

commodity.updatePickedUp(transport);
transport.pickUp(commodity);
// or
transport.pickUp(commodityId);
transport.completeNextRouteAction()

Dropping Off a commodity

Dropping off a commodity is very similar to picking one up. This will update the transport's route to not include dropping off the commodity.

commodity.updateDroppedOff();
transport.completeNextRouteAction()

Updating a commodity

A commodity can update its pick up and drop off locations, along with its status and metadata. To do this use one of the several methods provided by one our SDKs.

commodity.updateStartLocation(45.21312, -125.2131);
commodity.updateEndLocation(53.132132, -12.432);
commodity.updateStatus(CommodityStatus.WAITING);
        
JsonObject commodityMetadata = new JsonObject(); // Gson JsonObject

commodityMetadata.addProperty("bikes", 4);
commodityMetadata.addProperty("people", 5);
       
commodity.updateMetadata(commodityMetadata);
commodity.update(45.22,-125.23, null, null, "Offline");
commodity.update(Commodity.Status.Inactive)

Cancelling a Commodity

Sometimes you might want to remove a commodity from being picked up. To do this update the commodity's status to cancelled.
As you have seen above, you can create commodities and transports inside a cluster but you can also create clusters inside of other clusters called subclusters.

commodity.updateStatus(CommodityStatus.CANCELLED);
commodity.update(null,null,null,null,"Cancelled");
commodity.update(Commodity.Status.Cancelled)

As you have seen above, you can create commodities and transports inside a cluster but you can also create clusters inside of other clusters called subclusters.

Creating a transport

Creating a transport is very similar to creating a commodity. Check out the transport documentation for more information on transports.

// Use Gson's JSON object to create metadata
JsonObject metadata = new JsonObject();
metadata.addProperty("capacity", 10);

// Locally create a transport
Transport transport = seattleCluster.createTransport(47.56383, -122.31490, TransportStatus.OFFLINE, metadata);

// You can also add listeners to transports, MyTransportListener extends TransportListener
transport.addListener(new MyTransportListener());

// Create the transport on the Pathfinder server
transport.create();
pathfinder.createTransport(
  34.22, -112.321,
  "Online",
  "/root/myCluster"
);
let metadata = ["person": 4, "bike": 2, "mpg": 15]

// Note that the device location will be used. Location for transports cannot be set manually.
pathfinder.cluster("/root/eastcoast/nyc").connect() { (cluster: Cluster) -> Void in
	cluster.createTransport(status: startLocation, metadata: metadata)
}

Subscribing to a transport

After creating a transport, you might want to rno new route is generated if no transports can pick up the commodity or the commodity's status is not waitingeceive updates from the Pathfinder server when anything interesting happens to the transport. To do this you may subscribe or route subscribe to the transport.

Subscribing gives you access to any changes in the state of transport immediately after the Pathfinder server receives them. This way you can keep multiple users in sync without having to poll the Pathfinder server periodically.

// Use a transport listener to receive updates
transport.subscribe();
transport.subscribe(function(trans){
  // do stuff with transport
});
transport.subscribe()

Route subscribing only gives you updates to changes in the transports's route. Note, no route is generated if the transport's status is offline.

transport.routeSubscribe();
transport.subscribe(function(route){
  // do stuff with route
});
transport.subscribe()

Updating a transport's location

Make sure to update your transport's location somewhat frequently, this way the Pathfinder service can keep everyone up to date on the transport's location and provide the best routes possible.

🚧

Automatic location updates

The iOS SDK will automatically update your transports location. This requires that your app request the Locations permission.

transport.updateLocation(45.634, -124.3252);
transport.update(32.1232,-143.12);
// The iOS SDK updates your transport location automatically.

Updating a transport's status

Each transport can have one of two statuses, offline and online. When a transport has a status of offline it will not be routed to pick up commodities. When a transport has a status of online it will be routed to pick up commodities.

// Update a transport's status to offline
transport.updateStatus(TransportStatus.OFFLINE);

// Update a transport's status to online
transport.updateStatus(TransportStatus.ONLINE);
transport.update(null,null,"Online");
transport.goOnline()

transport.goOffline()

Creating subclusters

As you have seen above, you can create commodities and transports inside a cluster but you can also create clusters inside of other clusters, these clusters are called subclusters. Subclusters are used to organize commodities and transports into logical groups. Note, the top cluster is called the default cluster or the root cluster.

Each cluster is independently routed from its parent and subclusters. So if you add a commodity to the root cluster it will not reroute all its subclusters and if you add a commodity to a subcluster it will not reroute its parent clusters, it will only reroute the cluster the commodity was added to.

// Following from the getting cluster section
Cluster defaultCluster = pathfinder.getDefaultCluster();

// Locally creates the subcluster
Cluster texasCluster = defaultCluster.createSubcluster("texas");

// Listen for updates from the Pathfinder Server
// MyClusterListener extends ClusterListener
texasCluster.addListener(new MyClusterListener());

// Creates the subcluster on the Pathfinder server
texasCluster.create();
pathfinder.createCluster('/root/newCluster');
// This feature is not supported in iOS due to lack of demand.