{"_id":"56a83b999ec7660d002e07c4","parentDoc":null,"__v":99,"category":{"_id":"571d5ae118b3c10e003e55cd","version":"56a83b989ec7660d002e07c1","__v":0,"project":"56a83b979ec7660d002e07be","sync":{"url":"","isSync":false},"reference":false,"createdAt":"2016-04-24T23:46:41.975Z","from_sync":false,"order":0,"slug":"tutorials","title":"Tutorials"},"project":"56a83b979ec7660d002e07be","user":"56a83a6070a9440d00ef5ef8","version":{"_id":"56a83b989ec7660d002e07c1","project":"56a83b979ec7660d002e07be","__v":9,"createdAt":"2016-01-27T03:38:00.333Z","releaseDate":"2016-01-27T03:38:00.333Z","categories":["56a83b989ec7660d002e07c2","56a83c282036420d002d21e1","56a96de8f834950d0037b35a","56a9706013a69a0d00a778c3","56a970ec2d8fd90d0036eec7","56a971a62bb3910d000ee934","56a973372d8fd90d0036eece","56a978dc2bb3910d000ee93f","571d5ae118b3c10e003e55cd"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"Beta","version_clean":"1.0.0","version":"1.0"},"updates":[],"next":{"pages":[],"description":""},"createdAt":"2016-01-27T03:38:01.888Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":0,"body":"<div class=\"corner-ribbon top-left sticky blue\"><a style=\"color: white;\" href=\"https://pathfinder.readme.io/blog/pathfinder-now-in-public-beta\">Public Beta!</a></div>\n\nTo integrate your web or mobile application with Pathfinder, you will need to do two things:\n1. Sign up at https://thepathfinder.xyz and [register an application](doc:registering-an-application).\n2. Identify and include the [platform-appropriate SDK](doc:platform-support) in your app.\n\nWe also recommend that you are familiar with the Pathfinder model and related terms before using this tutorial.\n\n\nIn 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.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Adding a Pathfinder SDK dependency\"\n}\n[/block]\nTo 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](https://bintray.com/csse497/thepathfinder/pathfinder-android/view) or [Maven Central](http://search.maven.org/#artifactdetails|xyz.thepathfinder|pathfinder-android|0.0.15|jar). The iOS SDK is available on [CocoaPods](https://cocoapods.org/pods/thepathfinder), and Javascript SDK is available on Bower.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Using Gradle\\n// Add this to your build.gradle file in the dependencies section\\ncompile 'xyz.thepathfinder:pathfinder-android:1.0.0'\\n// Gson is needed if you intended to create a commodity or transport\\ncompile 'com.google.code.gson:gson:2.5'\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"// bower.json\\n{\\n  \\\"name\\\": \\\"my web app\\\",\\n  \\\"dependencies\\\": {\\n\\t\\t\\\"pathfinder.js\\\": \\\"1.0.0\\\"\\n  }\\n}\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"// Podfile\\npod 'thepathfinder', '~> 1.0.0'\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Using Pathfinder in your application\"\n}\n[/block]\nTo 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. \n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Java and Oracle's JDK SSL Certificate Problems\",\n  \"body\": \"Note, if you are using OpenJDK this warning does not apply to you.\\n\\nIf 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.\\n\\n[Download Let’s Encrypt Authority X1 and X3 Certificates](https://letsencrypt.org/certificates/) and [add them to your keystore](http://alvinalexander.com/java/java-using-keytool-import-certificate-keystore) to fix the problem.\"\n}\n[/block]\n\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"Pathfinder pathfinder = new Pathfinder(\\\"YOUR_APPLICATION_ID\\\", \\\"USER_ID_TOKEN\\\");\\n\\n// Synchronously connect to the Pathfinder server.\\n// Use this when first creating an application to find common \\n// SSL certificate problems.\\npathfinder.connect(false);\\n\\n// Asynchronously connect to the Pathfinder server\\npathfinder.connect()\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"var pathfinder = new Pathfinder(APP_ID, ID_TOKEN);\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"import thepathfinder\\n\\nlet pathfinder = Pathfinder(applicationIdentifier: myPathfinderAppId)\\npathfinder.connectAndAuthenticateWithPathfinderAuth(userGoogleIDToken) {\\n  (success: Boolean) -> Void in\\n    // your code here\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Getting a cluster\"\n}\n[/block]\nTo 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. \n\nAll 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. \n\nWhile 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. \n\n- [Android/Java Listeners Tutorial](doc:androidjava-listeners)\n- [JavaScript Callbacks Tutorial](doc:javascript-callbacks) \n- [IOS Delegates Tutorial](doc:ios-delegates) \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// After creating a pathfinder object you can get a cluster\\nCluster defaultCluster = pathfinder.getDefaultCluster();\\n\\n// Which is the same as \\ndefaultCluster = pathfinder.getCluster(\\\"/root\\\");\\n\\n// Asynchronously fetch the cluster's data\\ndefaultCluster.connect();\\n\\n// Using paths\\nCluster seattleCluster = pathfinder.getCluster(\\\"/root/washington/seattle\\\");\\n\\n// Add a cluster listener, MyClusterListener is a class you create that extends ClusterListener\\nseattleCluster.addListener(new MyClusterListener());\\nseattleCluster.connect(); // don't forget to connect to the cluster\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"pathfinder.getDefaultCluster(clusterId, function(cluster){\\n  // do stuff with cluster\\n});\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"let defaultCluster = pathfinder.cluster()\\nlet nycCluster = pathfinder.cluster(\\\"/root/eastcoast/nyc\\\")\\n\\ndefaultCluster.connect() { (cluster: Cluster) -> Void in\\n\\t// Note that here cluster == defaultCluster.\\n}\\n\\n// The delegate can be set at any time\\nnycCluster.delegate = ClusterDelegate()\\nnycCluster.connect()\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Creating a commodity\"\n}\n[/block]\nOnce you have a cluster you can add commodities to that cluster. Check out the [commodity documentation](doc:commodities-clusters-and-transports) for more information on commodities.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Use Gson's JSON object to create metadata\\nJsonObject metadata = new JsonObject();\\nmetadata.addProperty(\\\"capacity\\\", 5);\\n\\n// Locally create a commodity\\nCommodity commodity = seattleCluster.createCommodity(47.563711, -122.313555, 47.563501, -122.319499, CommodityStatus.INACTIVE, metadata);\\n\\n// You can also add listeners to commodities, MyCommodityListener extends CommodityListener\\ncommodity.addListener(new MyCommodityListener());\\n        \\n// Create the commodity on the Pathfinder server\\ncommodity.create();\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"var metadata = { capacity: 5 };\\n\\npathfinder.createCommodity(\\n  47.563711, -122.313555,\\n  47.563501, -122.319499\\n  metadata,\\n  \\\"Waiting\\\",\\n  clusterId\\n);\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"let startLocation = CLLocationCoordinate2D(latitude: 40.71, longitude: 74.00)\\nlet endLocation = CLLocationCoordinate2D(latitude: 41.34, longitude: 74.39)\\nlet metadata = [\\\"person\\\": 2, \\\"bike\\\": 0]\\n\\npathfinder.cluster(\\\"/root/eastcoast/nyc\\\").connect() { (cluster: Cluster) -> Void in\\n\\tcluster.createCommodity(start: startLocation, destination: endLocation, metadata: metadata)\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nThe top cluster is called the default cluster or the root cluster.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Subscribing to a commodity\"\n}\n[/block]\nAfter 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.\n\nSubscribing 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.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Use a commodity listener to receive updates\\ncommodity.subscribe();\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"commodity.subscribe(function(com){\\n  console.log(com.status);\\n});\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"commodity.subscribe()\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nRoute 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*.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"commodity.routeSubscribe();\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"commodity.routeSubscribe(function(com){\\n  for(var i = 0; i < com.actions.length; i++){\\n  \\tconsole.log(com.actions[i]); \\n  }\\n});\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"// The iOS SDK does not differentiate between status subscriptions and route subscriptions.\\ncommodity.subscribe()\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Picking Up a commodity\"\n}\n[/block]\nOnce, 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.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"commodity.updatePickedUp(transport);\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"transport.pickUp(commodity);\\n// or\\ntransport.pickUp(commodityId);\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"transport.completeNextRouteAction()\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Dropping Off a commodity\"\n}\n[/block]\nDropping off a commodity is very similar to picking one up. This will update the transport's route to not include dropping off the commodity.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"commodity.updateDroppedOff();\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"transport.completeNextRouteAction()\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Updating a commodity\"\n}\n[/block]\nA 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.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"commodity.updateStartLocation(45.21312, -125.2131);\\ncommodity.updateEndLocation(53.132132, -12.432);\\ncommodity.updateStatus(CommodityStatus.WAITING);\\n        \\nJsonObject commodityMetadata = new JsonObject(); // Gson JsonObject\\n\\ncommodityMetadata.addProperty(\\\"bikes\\\", 4);\\ncommodityMetadata.addProperty(\\\"people\\\", 5);\\n       \\ncommodity.updateMetadata(commodityMetadata);\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"commodity.update(45.22,-125.23, null, null, \\\"Offline\\\");\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"commodity.update(Commodity.Status.Inactive)\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Cancelling a Commodity\"\n}\n[/block]\nSometimes you might want to remove a commodity from being picked up. To do this update the commodity's status to *cancelled*.\nAs 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.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"commodity.updateStatus(CommodityStatus.CANCELLED);\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"commodity.update(null,null,null,null,\\\"Cancelled\\\");\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"commodity.update(Commodity.Status.Cancelled)\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nAs 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.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Creating a transport\"\n}\n[/block]\nCreating a transport is very similar to creating a commodity. Check out the [transport documentation](doc:commodities-clusters-and-transports) for more information on transports.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Use Gson's JSON object to create metadata\\nJsonObject metadata = new JsonObject();\\nmetadata.addProperty(\\\"capacity\\\", 10);\\n\\n// Locally create a transport\\nTransport transport = seattleCluster.createTransport(47.56383, -122.31490, TransportStatus.OFFLINE, metadata);\\n\\n// You can also add listeners to transports, MyTransportListener extends TransportListener\\ntransport.addListener(new MyTransportListener());\\n\\n// Create the transport on the Pathfinder server\\ntransport.create();\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"pathfinder.createTransport(\\n  34.22, -112.321,\\n  \\\"Online\\\",\\n  \\\"/root/myCluster\\\"\\n);\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"let metadata = [\\\"person\\\": 4, \\\"bike\\\": 2, \\\"mpg\\\": 15]\\n\\n// Note that the device location will be used. Location for transports cannot be set manually.\\npathfinder.cluster(\\\"/root/eastcoast/nyc\\\").connect() { (cluster: Cluster) -> Void in\\n\\tcluster.createTransport(status: startLocation, metadata: metadata)\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Subscribing to a transport\"\n}\n[/block]\nAfter 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 *waiting*eceive updates from the Pathfinder server when anything interesting happens to the transport. To do this you may subscribe or route subscribe to the transport.\n\nSubscribing 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.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Use a transport listener to receive updates\\ntransport.subscribe();\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"transport.subscribe(function(trans){\\n  // do stuff with transport\\n});\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"transport.subscribe()\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nRoute subscribing only gives you updates to changes in the transports's route. Note, no route is generated if the transport's status is *offline*.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"transport.routeSubscribe();\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"transport.subscribe(function(route){\\n  // do stuff with route\\n});\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"transport.subscribe()\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Updating a transport's location\"\n}\n[/block]\nMake 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.\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Automatic location updates\",\n  \"body\": \"The iOS SDK will automatically update your transports location. This requires that your app request the Locations permission.\"\n}\n[/block]\n\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"transport.updateLocation(45.634, -124.3252);\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"transport.update(32.1232,-143.12);\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"// The iOS SDK updates your transport location automatically.\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Updating a transport's status\"\n}\n[/block]\nEach 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.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Update a transport's status to offline\\ntransport.updateStatus(TransportStatus.OFFLINE);\\n\\n// Update a transport's status to online\\ntransport.updateStatus(TransportStatus.ONLINE);\",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"transport.update(null,null,\\\"Online\\\");\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"transport.goOnline()\\n\\ntransport.goOffline()\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Creating subclusters\"\n}\n[/block]\nAs 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.\n\nEach 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.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Following from the getting cluster section\\nCluster defaultCluster = pathfinder.getDefaultCluster();\\n\\n// Locally creates the subcluster\\nCluster texasCluster = defaultCluster.createSubcluster(\\\"texas\\\");\\n\\n// Listen for updates from the Pathfinder Server\\n// MyClusterListener extends ClusterListener\\ntexasCluster.addListener(new MyClusterListener());\\n\\n// Creates the subcluster on the Pathfinder server\\ntexasCluster.create();        \",\n      \"language\": \"java\",\n      \"name\": \"Android/Java\"\n    },\n    {\n      \"code\": \"pathfinder.createCluster('/root/newCluster');\",\n      \"language\": \"javascript\"\n    },\n    {\n      \"code\": \"// This feature is not supported in iOS due to lack of demand.\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]","excerpt":"","slug":"getting-started","type":"basic","title":"Getting Started"}
<div class="corner-ribbon top-left sticky blue"><a style="color: white;" href="https://pathfinder.readme.io/blog/pathfinder-now-in-public-beta">Public Beta!</a></div> 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](doc:registering-an-application). 2. Identify and include the [platform-appropriate SDK](doc:platform-support) 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. [block:api-header] { "type": "basic", "title": "Adding a Pathfinder SDK dependency" } [/block] 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](https://bintray.com/csse497/thepathfinder/pathfinder-android/view) or [Maven Central](http://search.maven.org/#artifactdetails|xyz.thepathfinder|pathfinder-android|0.0.15|jar). The iOS SDK is available on [CocoaPods](https://cocoapods.org/pods/thepathfinder), and Javascript SDK is available on Bower. [block:code] { "codes": [ { "code": "// Using Gradle\n// Add this to your build.gradle file in the dependencies section\ncompile 'xyz.thepathfinder:pathfinder-android:1.0.0'\n// Gson is needed if you intended to create a commodity or transport\ncompile 'com.google.code.gson:gson:2.5'", "language": "java", "name": "Android/Java" }, { "code": "// bower.json\n{\n \"name\": \"my web app\",\n \"dependencies\": {\n\t\t\"pathfinder.js\": \"1.0.0\"\n }\n}", "language": "javascript" }, { "code": "// Podfile\npod 'thepathfinder', '~> 1.0.0'", "language": "swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Using Pathfinder in your application" } [/block] 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. [block:callout] { "type": "warning", "title": "Java and Oracle's JDK SSL Certificate Problems", "body": "Note, if you are using OpenJDK this warning does not apply to you.\n\nIf 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.\n\n[Download Let’s Encrypt Authority X1 and X3 Certificates](https://letsencrypt.org/certificates/) and [add them to your keystore](http://alvinalexander.com/java/java-using-keytool-import-certificate-keystore) to fix the problem." } [/block] [block:code] { "codes": [ { "code": "Pathfinder pathfinder = new Pathfinder(\"YOUR_APPLICATION_ID\", \"USER_ID_TOKEN\");\n\n// Synchronously connect to the Pathfinder server.\n// Use this when first creating an application to find common \n// SSL certificate problems.\npathfinder.connect(false);\n\n// Asynchronously connect to the Pathfinder server\npathfinder.connect()", "language": "java", "name": "Android/Java" }, { "code": "var pathfinder = new Pathfinder(APP_ID, ID_TOKEN);", "language": "javascript" }, { "code": "import thepathfinder\n\nlet pathfinder = Pathfinder(applicationIdentifier: myPathfinderAppId)\npathfinder.connectAndAuthenticateWithPathfinderAuth(userGoogleIDToken) {\n (success: Boolean) -> Void in\n // your code here\n}", "language": "swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Getting a cluster" } [/block] 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. - [Android/Java Listeners Tutorial](doc:androidjava-listeners) - [JavaScript Callbacks Tutorial](doc:javascript-callbacks) - [IOS Delegates Tutorial](doc:ios-delegates) [block:code] { "codes": [ { "code": "// After creating a pathfinder object you can get a cluster\nCluster defaultCluster = pathfinder.getDefaultCluster();\n\n// Which is the same as \ndefaultCluster = pathfinder.getCluster(\"/root\");\n\n// Asynchronously fetch the cluster's data\ndefaultCluster.connect();\n\n// Using paths\nCluster seattleCluster = pathfinder.getCluster(\"/root/washington/seattle\");\n\n// Add a cluster listener, MyClusterListener is a class you create that extends ClusterListener\nseattleCluster.addListener(new MyClusterListener());\nseattleCluster.connect(); // don't forget to connect to the cluster", "language": "java", "name": "Android/Java" }, { "code": "pathfinder.getDefaultCluster(clusterId, function(cluster){\n // do stuff with cluster\n});", "language": "javascript" }, { "code": "let defaultCluster = pathfinder.cluster()\nlet nycCluster = pathfinder.cluster(\"/root/eastcoast/nyc\")\n\ndefaultCluster.connect() { (cluster: Cluster) -> Void in\n\t// Note that here cluster == defaultCluster.\n}\n\n// The delegate can be set at any time\nnycCluster.delegate = ClusterDelegate()\nnycCluster.connect()", "language": "swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Creating a commodity" } [/block] Once you have a cluster you can add commodities to that cluster. Check out the [commodity documentation](doc:commodities-clusters-and-transports) for more information on commodities. [block:code] { "codes": [ { "code": "// Use Gson's JSON object to create metadata\nJsonObject metadata = new JsonObject();\nmetadata.addProperty(\"capacity\", 5);\n\n// Locally create a commodity\nCommodity commodity = seattleCluster.createCommodity(47.563711, -122.313555, 47.563501, -122.319499, CommodityStatus.INACTIVE, metadata);\n\n// You can also add listeners to commodities, MyCommodityListener extends CommodityListener\ncommodity.addListener(new MyCommodityListener());\n \n// Create the commodity on the Pathfinder server\ncommodity.create();", "language": "java", "name": "Android/Java" }, { "code": "var metadata = { capacity: 5 };\n\npathfinder.createCommodity(\n 47.563711, -122.313555,\n 47.563501, -122.319499\n metadata,\n \"Waiting\",\n clusterId\n);", "language": "javascript" }, { "code": "let startLocation = CLLocationCoordinate2D(latitude: 40.71, longitude: 74.00)\nlet endLocation = CLLocationCoordinate2D(latitude: 41.34, longitude: 74.39)\nlet metadata = [\"person\": 2, \"bike\": 0]\n\npathfinder.cluster(\"/root/eastcoast/nyc\").connect() { (cluster: Cluster) -> Void in\n\tcluster.createCommodity(start: startLocation, destination: endLocation, metadata: metadata)\n}", "language": "swift" } ] } [/block] The top cluster is called the default cluster or the root cluster. [block:api-header] { "type": "basic", "title": "Subscribing to a commodity" } [/block] 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. [block:code] { "codes": [ { "code": "// Use a commodity listener to receive updates\ncommodity.subscribe();", "language": "java", "name": "Android/Java" }, { "code": "commodity.subscribe(function(com){\n console.log(com.status);\n});", "language": "javascript" }, { "code": "commodity.subscribe()", "language": "swift" } ] } [/block] 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*. [block:code] { "codes": [ { "code": "commodity.routeSubscribe();", "language": "java", "name": "Android/Java" }, { "code": "commodity.routeSubscribe(function(com){\n for(var i = 0; i < com.actions.length; i++){\n \tconsole.log(com.actions[i]); \n }\n});", "language": "javascript" }, { "code": "// The iOS SDK does not differentiate between status subscriptions and route subscriptions.\ncommodity.subscribe()", "language": "swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Picking Up a commodity" } [/block] 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. [block:code] { "codes": [ { "code": "commodity.updatePickedUp(transport);", "language": "java", "name": "Android/Java" }, { "code": "transport.pickUp(commodity);\n// or\ntransport.pickUp(commodityId);", "language": "javascript" }, { "code": "transport.completeNextRouteAction()", "language": "swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Dropping Off a commodity" } [/block] 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. [block:code] { "codes": [ { "code": "commodity.updateDroppedOff();", "language": "java", "name": "Android/Java" }, { "code": "", "language": "javascript" }, { "code": "transport.completeNextRouteAction()", "language": "swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Updating a commodity" } [/block] 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. [block:code] { "codes": [ { "code": "commodity.updateStartLocation(45.21312, -125.2131);\ncommodity.updateEndLocation(53.132132, -12.432);\ncommodity.updateStatus(CommodityStatus.WAITING);\n \nJsonObject commodityMetadata = new JsonObject(); // Gson JsonObject\n\ncommodityMetadata.addProperty(\"bikes\", 4);\ncommodityMetadata.addProperty(\"people\", 5);\n \ncommodity.updateMetadata(commodityMetadata);", "language": "java", "name": "Android/Java" }, { "code": "commodity.update(45.22,-125.23, null, null, \"Offline\");", "language": "javascript" }, { "code": "commodity.update(Commodity.Status.Inactive)", "language": "swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Cancelling a Commodity" } [/block] 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. [block:code] { "codes": [ { "code": "commodity.updateStatus(CommodityStatus.CANCELLED);", "language": "java", "name": "Android/Java" }, { "code": "commodity.update(null,null,null,null,\"Cancelled\");", "language": "javascript" }, { "code": "commodity.update(Commodity.Status.Cancelled)", "language": "swift" } ] } [/block] 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. [block:api-header] { "type": "basic", "title": "Creating a transport" } [/block] Creating a transport is very similar to creating a commodity. Check out the [transport documentation](doc:commodities-clusters-and-transports) for more information on transports. [block:code] { "codes": [ { "code": "// Use Gson's JSON object to create metadata\nJsonObject metadata = new JsonObject();\nmetadata.addProperty(\"capacity\", 10);\n\n// Locally create a transport\nTransport transport = seattleCluster.createTransport(47.56383, -122.31490, TransportStatus.OFFLINE, metadata);\n\n// You can also add listeners to transports, MyTransportListener extends TransportListener\ntransport.addListener(new MyTransportListener());\n\n// Create the transport on the Pathfinder server\ntransport.create();", "language": "java", "name": "Android/Java" }, { "code": "pathfinder.createTransport(\n 34.22, -112.321,\n \"Online\",\n \"/root/myCluster\"\n);", "language": "javascript" }, { "code": "let metadata = [\"person\": 4, \"bike\": 2, \"mpg\": 15]\n\n// Note that the device location will be used. Location for transports cannot be set manually.\npathfinder.cluster(\"/root/eastcoast/nyc\").connect() { (cluster: Cluster) -> Void in\n\tcluster.createTransport(status: startLocation, metadata: metadata)\n}", "language": "swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Subscribing to a transport" } [/block] 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 *waiting*eceive 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. [block:code] { "codes": [ { "code": "// Use a transport listener to receive updates\ntransport.subscribe();", "language": "java", "name": "Android/Java" }, { "code": "transport.subscribe(function(trans){\n // do stuff with transport\n});", "language": "javascript" }, { "code": "transport.subscribe()", "language": "swift" } ] } [/block] 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*. [block:code] { "codes": [ { "code": "transport.routeSubscribe();", "language": "java", "name": "Android/Java" }, { "code": "transport.subscribe(function(route){\n // do stuff with route\n});", "language": "javascript" }, { "code": "transport.subscribe()", "language": "swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Updating a transport's location" } [/block] 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. [block:callout] { "type": "warning", "title": "Automatic location updates", "body": "The iOS SDK will automatically update your transports location. This requires that your app request the Locations permission." } [/block] [block:code] { "codes": [ { "code": "transport.updateLocation(45.634, -124.3252);", "language": "java", "name": "Android/Java" }, { "code": "transport.update(32.1232,-143.12);", "language": "javascript" }, { "code": "// The iOS SDK updates your transport location automatically.", "language": "swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Updating a transport's status" } [/block] 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. [block:code] { "codes": [ { "code": "// Update a transport's status to offline\ntransport.updateStatus(TransportStatus.OFFLINE);\n\n// Update a transport's status to online\ntransport.updateStatus(TransportStatus.ONLINE);", "language": "java", "name": "Android/Java" }, { "code": "transport.update(null,null,\"Online\");", "language": "javascript" }, { "code": "transport.goOnline()\n\ntransport.goOffline()", "language": "swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Creating subclusters" } [/block] 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. [block:code] { "codes": [ { "code": "// Following from the getting cluster section\nCluster defaultCluster = pathfinder.getDefaultCluster();\n\n// Locally creates the subcluster\nCluster texasCluster = defaultCluster.createSubcluster(\"texas\");\n\n// Listen for updates from the Pathfinder Server\n// MyClusterListener extends ClusterListener\ntexasCluster.addListener(new MyClusterListener());\n\n// Creates the subcluster on the Pathfinder server\ntexasCluster.create(); ", "language": "java", "name": "Android/Java" }, { "code": "pathfinder.createCluster('/root/newCluster');", "language": "javascript" }, { "code": "// This feature is not supported in iOS due to lack of demand.", "language": "swift" } ] } [/block]