Please note! All the code snippets in this section have been provided for Android. If you use iOS, Cordova or ReactNative you’ll find it easy to translate them appropriately.

Situm Dashboard allows you to define the wayfinding paths in your building. Based on this information, Situm SDK is able to compute the route from any point A to any point B. This section will teach you the basics about routing.

Routing concepts #

Before we dive in, it will be useful to understand a few concepts first. In Situm, wayfinding paths are configured in Situm Dashboard as a graph, where the edges represent all the passable paths in the building (and the nodes are just the start/end of each edge).

Each edge can have a number of properties (more info here): they can be accessible (suitable for people in wheelchairs, e.g. they avoid elevators), they can represent one-way or two way paths, they can interconnect different floors (representing elevators, escalators, etc.), etc.

When this graph has been configured, Situm SDK is able to compute the route between any two points within the same venue (A and B). Conceptually, this route will be composed of a set of data structures.

StructureDescriptionMore info
Points (nodes)The set of ordered (consecutive) graph nodes that the route traversesComputing your first route
Steps (edges)The set of ordered (consecutive) graph edges that connect those nodes.Extracting route points
SegmentA list of consecutive points that are in the same floor. Extracting route steps (edges)

Computing your first route #

Computing a route with Situm SDK can be break down in 4 steps:

  • Determine the building identifier of the building where you want to retrieve the route.
  • Retrieve the information of that building.
  • Define the points A & B. For each of them, you will need to specify: its floor identifier, its WSG84 coordinates (latitude and longitude).
    • In native Android & iOS, you will also need to provide its cartesian coordinates (X and Y). Situm SDK includes a handy tool, the CoordinateConverter (Android, iOS), that will allow you to compute WSG84 coordinates from cartesian ones, and viceversa.
  • Create the DirectionsRequest object (Android, iOS, Cordova, ReactNative), that defines how the route should be computed: origin, destination, whether it should avoid non accessible paths, etc.
  • Compute the route using the DirectionsManager (or in Situm jargon, requestDirections: Android, iOS, Cordova, ReactNative)!

Internally, the route computation process goes like this:

  1. Among all the edges, finds the one that is closer to point A. Then, proceeds to find the exact point in that edge that is closer to A. Let’s call this point A’.
  2. Performs the same process for point B. Let’s call the resulting point B’.
  3. Computes the shortest route between points A’ and B’.
  4. Generates a route that goes from point A to A’ (a straight line), from point A’ to B’ (computed in step 3), and finally from point B’ to B (another straight line).

The output will be a Route object (Android, iOS, Cordova, ReactNative) that will contain all the relevant information: route nodes or points, edges, segments, indications, etc. The following example shows an Android simple example that shows you how to do it.

package com.example.helloworld;

import androidx.appcompat.app.AppCompatActivity;
import es.situm.sdk.location.util.CoordinateConverter;

import android.os.Bundle;
import android.util.Log;

import es.situm.sdk.SitumSdk;
import es.situm.sdk.directions.DirectionsRequest;
import es.situm.sdk.error.Error;
import es.situm.sdk.model.cartography.BuildingInfo;
import es.situm.sdk.model.cartography.Point;
import es.situm.sdk.model.directions.Indication;
import es.situm.sdk.model.directions.Route;
import es.situm.sdk.model.directions.RouteStep;
import es.situm.sdk.model.location.Coordinate;
import es.situm.sdk.utils.Handler;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = MainActivity.class.getSimpleName();

    private CoordinateConverter coordinateConverter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        SitumSdk.init(this);

        //We will compute routes in building 10194
        String buildingId="10194";

        //First of all, we need to retrieve the information of the building ...
        SitumSdk.communicationManager().fetchBuildingInfo(buildingId, new Handler<BuildingInfo>() {

            @Override
            public void onSuccess(BuildingInfo buildingInfo) {

                //We define the data points A & B between  which we will compute the route
                String floorIdA="29685";
                Coordinate coordinateA = new Coordinate(43.3434333899059, -8.42761197887612);

                String floorIdB="29686";
                Coordinate coordinateB = new Coordinate(43.3451052729103, -8.42827590527984);

                // With this information, we build the coordinate converter ...
                coordinateConverter = new CoordinateConverter(buildingInfo.getBuilding().getDimensions(),buildingInfo.getBuilding().getCenter(),buildingInfo.getBuilding().getRotation());

                // ... which allows us to build the 2 points (A & B) indicating both the global & cartesian coordinates
                Point pointA = new Point(buildingId, floorIdA,  coordinateA, coordinateConverter.toCartesianCoordinate(coordinateA) );
                Point pointB = new Point(buildingId, floorIdB,  coordinateB, coordinateConverter.toCartesianCoordinate(coordinateB) );

                // The DirectionsRequest allows us to configure the route calculation: source point, destination point, other options...
                DirectionsRequest directionsRequest = new DirectionsRequest.Builder().from(pointA, null).to(pointB).build();

                //Finally, we compute the route
                SitumSdk.directionsManager().requestDirections(directionsRequest, new Handler<Route>() {
                    @Override
                    public void onSuccess(Route route) {
                        Log.i(TAG, "Computed route " + route);
                    }

                    @Override
                    public void onFailure(Error error) {
                        Log.e(TAG, "Error" + error);
                    }
                });

            }

            @Override
            public void onFailure(Error error) {
                Log.e(TAG, "Error " + error);
            }
        });
        
    }
    
}

You may modify the appropriate variables to define your own A&B points. Then, if you execute the previous example, you will get a log similar to this:

I/MainActivity: Computed route 
Route{request=DirectionsRequest{from=Point{buildingIdentifier='10194', floorIdentifier='29685', cartesianCoordinate=CartesianCoordinate{x=134,90, y=160,15}, coordinate=Coordinate{latitude=43,343433, longitude=-8,427612}, isOutdoor=false}, bearingFrom=null, to=Point{buildingIdentifier='10194', floorIdentifier='29686', cartesianCoordinate=CartesianCoordinate{x=324,82, y=123,66}, coordinate=Coordinate{latitude=43,345105, longitude=-8,428276}, isOutdoor=false}, accessibilityMode=CHOOSE_SHORTEST, minimizeFloorChanges=false, options: es.situm.sdk.directions.DirectionsOptions@1876eef},
...
points=[Point{buildingIdentifier='10194', floorIdentifier='29685', cartesianCoordinate=CartesianCoordinate{x=134,90, y=160,15}, coordinate=Coordinate{latitude=43,343433, longitude=-8,427612}, isOutdoor=false}, DefaultNode{id=-1} Point{buildingIdentifier='10194', floorIdentifier='29685', cartesianCoordinate=CartesianCoordinate{x=146,26, y=159,26}, coordinate=Coordinate{latitude=43,343528, longitude=-8,427666}, isOutdoor=false}, DefaultNode{id=43} Point{buildingIdentifier='10194', floorIdentifier='29685', cartesianCoordinate=CartesianCoordinate{x=143,13, y=119,11}, coordinate=Coordinate{latitude=43,343667, longitude=-8,427207}, isOutdoor=false}, DefaultNode{id=51} 


....

Success! You have computed your first route. What should you do next?

Extracting route points #

We’ve seen that a route is essentially a set of ordered nodes, connected by edges, that go from point A to B. In the previous log output, you may have noticed that we’ve printed out a set of points. These are the nodes of the route in the right order from the beginning (point A) to the end (point B).

Situm SDK allows you to extract a list of route points (Android, iOS, Cordova, ReactNative). For instance, in Android you may do:

...

SitumSdk.directionsManager().requestDirections(directionsRequest, new Handler<Route>() {

   @Override
   public void onSuccess(Route route) {

      for (Point point: route.getPoints()){
         Log.i(TAG, "Point's building ID " + point.getBuildingIdentifier());
         Log.i(TAG, "Point's floor ID " + point.getFloorIdentifier());
         Log.i(TAG, "Point's Latitude " + point.getCoordinate().getLatitude()  + " Longitude " + point.getCoordinate().getLongitude());
         Log.i(TAG, "Point's Cartesian X " + point.getCartesianCoordinate().getX()  + " Cartesian Y " + point.getCartesianCoordinate().getY());
         Log.i(TAG, "-----");
      }
   }

  ...
}

After execution, you will see the following log:

I/MainActivity: Point's building ID 10194
    Point's floor ID 29686
    Point's Latitude 43.344254545619556 Longitude -8.427709954367506
    Point's Cartesian X 219.771 Cartesian Y 125.749
    -----
    Point's building ID 10194
    Point's floor ID 29686
    Point's Latitude 43.34426453022196 Longitude -8.427803794047842
    Point's Cartesian X 224.218 Cartesian Y 132.022
    -----

...

Each point contains the relevant location information (building identifier, floor identifier, coordinates, etc.). Take a look at the Point Android reference to have an idea of the nature of this information.

Extracting route steps (edges) #

In the previous example, we’ve seen how to extract the ordered nodes or points that conform a route. What about the steps or edges connecting those points? Situm SDK allows to retrieve them as well (Android, iOS, Cordova, not available in ReactNative). For instance, in Android you may do:

...

SitumSdk.directionsManager().requestDirections(directionsRequest, new Handler<Route>() {

    @Override
    public void onSuccess(Route route) {

        for (RouteStep step : route.getSteps()) {

            Log.i(TAG, "Step Id: " + step.getId());
            Log.i(TAG, "Distance (meters) to end of route: " + step.getDistanceToGoal());
            Log.i(TAG, "Distance (meters) to next floor change: " + step.getDistanceToFloorChange());
            Log.i(TAG, "Length (meters) of this edge/step: " + step.getDistance());

            Point from = step.getFrom();
            Point to = step.getTo();
            Log.i(TAG, "FromPoint: BuildingId "+ from.getFloorIdentifier() + " FloorId " + from.getFloorIdentifier() + " Latitude "+ from.getCoordinate().getLatitude() + " Longitude " + from.getCoordinate().getLongitude());
            Log.i(TAG, "ToPoint: BuildingId "+ to.getFloorIdentifier() + " FloorId " + to.getFloorIdentifier() + " Latitude "+ to.getCoordinate().getLatitude() + " Longitude " + to.getCoordinate().getLongitude());

            Log.i(TAG, "----");
        }

    ...
}

After execution, you will see the following log:

I/MainActivity: Step Id: 1
    Distance (meters) to end of route: 283.61143215971987
    Distance (meters) to next floor change: 55.85009662344499
    Length (meters) of this edge/step: 11.391021886334167
    FromPoint: BuildingId 10194 FloorId 29685 Latitude 43.3434333899059 Longitude -8.42761197887612
    ToPoint: BuildingId 10194 FloorId 29685 Latitude 43.34352806065825 Longitude -8.427665920512453
    ----
    Step Id: 2
    Distance (meters) to end of route: 272.2204102733857
    Distance (meters) to next floor change: 44.459074737110825
    Length (meters) of this edge/step: 40.271730829572306
    FromPoint: BuildingId 10194 FloorId 29685 Latitude 43.34352806065825 Longitude -8.427665920512453
    ToPoint: BuildingId 10194 FloorId 29685 Latitude 43.343667245037864 Longitude -8.427207329649177
    ----

...

Each step contains the relevant location information: origin point (“from”), endpoint (“to”), several pre-computed distances (length of edge, distance to next floor change, distance to end destination), etc.

Extracting route segments (set of steps within the same floor) #

The most common operation we might want to perform with a route computed by Situm SDK is to visualize it on top of the building floorplans.

One way to do this is to draw a polyline that traverses all the route points, retrieved as shown in the previous sections. Usually, we will visualize just one floorplan at a time, but the list of points as retrieved in the previous sections contains points across all floors traversed by the route. Therefore, we need to:

  • Filter out the data points so we just visualize those contained in the floor that we want to show.
  • Make sure we don’t incorrectly connect route points when visualizing them. Sometimes, a route may start in a certain floor (e.g. floor 0), go through other floor(s), only to come back to the original floor. A common mistake is to just throw all the “floor 0” points in a list and draw the polyline: therefore, the polyline will incorrectly join points that should not be tied together.

Situm facilitates these operations by providing a way to retrieve segments (Android, iOS, Cordova, ReactNative). Each segment is a list of consecutive route points that are in the same floor. Non-consecutive points within the same floor will be split in different segments. This way, we can just draw one polyline per segment (within the desired floor) and avoid any further trouble.

For example, let’s say a route has 8 data points (P1, P2, P3, P4, P5, P6, P7, P8), being:

  • P1, P2 and P3 in Floor 0.
  • P4 and P5 in Floor 1.
  • P6, P7 and P8 in Floor 0.

Then, we would have 3 segments: 1) segment 1 (P1, P2, P3), 2) segment 2 (P4, P5), segment 3 (P6, P7, P8).

Let’s see some Android code in order to understand this concept:

...

SitumSdk.directionsManager().requestDirections(directionsRequest, new Handler<Route>() {
@Override
    public void onSuccess(Route route) {
        for (RouteSegment segment: route.getSegments()){
    
            Log.i(TAG, "Segment");
            Log.i(TAG, "Floor Id: " + segment.getFloorIdentifier());
    
            for (Point point: segment.getPoints()){
                Log.i(TAG, "Point: BuildingId "+ point.getFloorIdentifier() + " FloorId " + point.getFloorIdentifier() + " Latitude "+ point.getCoordinate().getLatitude() + " Longitude " + point.getCoordinate().getLongitude());
            }
    
            Log.i(TAG, "----");
        }
    }

    ...
}

After execution, you will see the following log:

I/MainActivity: Segment
    Floor Id: 29685
    Point: BuildingId 10194 FloorId 29685 Latitude 43.3434333899059 Longitude -8.42761197887612
    Point: BuildingId 10194 FloorId 29685 Latitude 43.34352806065825 Longitude -8.427665920512453
    Point: BuildingId 10194 FloorId 29685 Latitude 43.343667245037864 Longitude -8.427207329649177
    Point: BuildingId 10194 FloorId 29685 Latitude 43.343702404166145 Longitude -8.427225934618166
    ----
    Segment
    Floor Id: 29686
    Point: BuildingId 10194 FloorId 29686 Latitude 43.34372707052083 Longitude -8.427236219233354
I/MainActivity: Point: BuildingId 10194 FloorId 29686 Latitude 43.343686078274054 Longitude -8.427222991801575
    Point: BuildingId 10194 FloorId 29686 Latitude 43.34371318522387 Longitude -8.427139721270171
    Point: BuildingId 10194 FloorId 29686 Latitude 43.34377374892849 Longitude -8.4271796783137

...

Note that each segment indicates its floor identifier, and then contains the list of consecutive points within that list.

Configuring the type of route (accessibility) #

Situm SDK features 3 types of routes:

  • Shortest route (default).
  • Accessible route (avoids paths that are not suitable for wheelchairs, such as stairs).
  • Routes where all floor changes are non-accessible (e.g. avoid routes that use elevators).

Each type of route is explained in detail here (with graphical examples). You may configure the type of route in Situm SDK using the DirectionsRequest accesibilityMode helper (Android, iOS, Cordova, React Native) and data type (Android, iOS, Cordova, React Native). The following Android example shows you how to configure the accessibility mode and log it when the route is computed:

DirectionsRequest directionsRequest = 
   new DirectionsRequest.Builder().from(pointA, null).to(pointB).
   // We want to compute an accessible route
   accessibilityMode(DirectionsRequest.AccessibilityMode.ONLY_ACCESSIBLE).
   build();
   
   SitumSdk.directionsManager().
      requestDirections(directionsRequest, new Handler<Route>() {

   @Override
   public void onSuccess(Route route) {

      // The accessibility mode of the computed route will be ONLY_ACCESSIBLE
      Log.i(TAG, "Accessibility mode "  + route.getAccessibilityMode());
   }
    
   ...

});

Retrieving indications #

Aside from the points & segments of the route (which you can display on top of a floorplan), Situm SDK also computes the navigation indications in textual format. You may retrieve the whole set of indications using the Route getIndications helper (Android, iOS, Cordova, React Native). This helper provides a list of Indication data structures (Android, iOS, Cordova, React Native) with several properties, the most important being:

For instance, if the user is heading towards the advance direction of the route, Situm will generate a “Go ahead for 20 meters” instruction (or any other distance). In another case, Situm will generate a “Turn left”, “Turn around” or “Turn right” indication.

The indication changes depending on the user orientation with respect to the advance direction

Based on these properties, you can generate human-friendly textual indications for the whole route. If you prefer, in native Android/iOS, Situm SDK also provides a toText helper method (Android, iOS) that generates these texts for you in the smartphone locale (english, spanish, portuguese, japanese and arabic supported at the moment). You may try it out by modifying the previous example as follows:

public class MainActivity extends AppCompatActivity {
    ...
    Context myContext = this;

    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        
        SitumSdk.communicationManager().
            fetchBuildingInfo(buildingId, new Handler<BuildingInfo>() {
            @Override
            public void onSuccess(BuildingInfo buildingInfo) {
                 
                ...
                SitumSdk.directionsManager().
                    requestDirections(directionsRequest, new Handler<Route>() {
                    
                    @Override
                    public void onSuccess(Route route) {
                        for (Indication indication: route.getIndications()){
                            
                            // We print the indication internals for inspection
                            Log.i(TAG, indication.toString());
                            // ... and the human-friendly text generated
                            Log.i(TAG, indication.toText(myContext));

                        }
                    }
                    @Override
                    public void onFailure(Error error) {
                        Log.e(TAG, "Error" + error);
                    }
                });
            }
            @Override
            public void onFailure(Error error) {
                Log.e(TAG, "Error " + error);
            }
        });

    }

}

Which outputs a log like the following:

Indication{stepIdxOrigin=1, stepIdxDestination=1, indicationType=GO_AHEAD, orientationType=STRAIGHT, orientation=0.0, distance=4.623364020533007, needLevelChange=false, distanceToNextLevel=0, nextLevel=null}
Go ahead for 5 metres
--
Indication{stepIdxOrigin=2, stepIdxDestination=2, indicationType=TURN, orientationType=RIGHT, orientation=-1.570796326794899, distance=40.46055375754866, needLevelChange=false, distanceToNextLevel=0, nextLevel=null}
Turn right and go ahead for 41 metres
--
Indication{stepIdxOrigin=3, stepIdxDestination=3, indicationType=TURN, orientationType=LEFT, orientation=1.678919122474429, distance=7.953652054245258, needLevelChange=false, distanceToNextLevel=0, nextLevel=null}
Turn left and go ahead for 8 metres
--
Indication{stepIdxOrigin=4, stepIdxDestination=4, indicationType=CHANGE_FLOOR, orientationType=RIGHT, orientation=-2.288264812469845, distance=1.0, needLevelChange=true, distanceToNextLevel=1, nextLevel=1}
Go up to floor 1
--
Indication{stepIdxOrigin=5, stepIdxDestination=5, indicationType=GO_AHEAD, orientationType=STRAIGHT, orientation=0.0, distance=4.67879909805923, needLevelChange=false, distanceToNextLevel=0, nextLevel=null}
Go ahead for 5 metres
--
Indication{stepIdxOrigin=6, stepIdxDestination=6, indicationType=TURN, orientationType=LEFT, orientation=1.7590508678750336, distance=7.393135870522071, needLevelChange=false, distanceToNextLevel=0, nextLevel=null}
Turn left and go ahead for 8 metres
--
Indication{stepIdxOrigin=7, stepIdxDestination=10, indicationType=TURN, orientationType=LEFT, orientation=1.9429090871105288, distance=76.97345631265134, needLevelChange=false, distanceToNextLevel=0, nextLevel=null}
Turn left and go ahead for 77 metres
--
Indication{stepIdxOrigin=11, stepIdxDestination=11, indicationType=TURN, orientationType=RIGHT, orientation=-1.218588055921534, distance=16.402837864223372, needLevelChange=false, distanceToNextLevel=0, nextLevel=null}
Turn right and go ahead for 17 metres
--
Indication{stepIdxOrigin=12, stepIdxDestination=17, indicationType=TURN, orientationType=LEFT, orientation=0.8838334129694063, distance=102.90426567863084, needLevelChange=false, distanceToNextLevel=0, nextLevel=null}
Turn left and go ahead for 103 metres
--
Indication{stepIdxOrigin=18, stepIdxDestination=18, indicationType=TURN, orientationType=RIGHT, orientation=-1.5707963267948981, distance=21.192595211547314, needLevelChange=false, distanceToNextLevel=0, nextLevel=null}
Turn right and go ahead for 22 metres

The previous log is very interesting because it shows how the text generation works (so you may replicate it if you want). For example:

  • The 1st indication is of type “GO_AHEAD” and the distance is “4.62 meters”, so naturally the indication is “Go ahead for 5 meters”.
  • The 2nd indication is of type “TURN” and orientationType “LEFT”. Since distance is “7.95 meters”, we conclude that the user should “Turn right and go ahead for 41 meters” (in a way, this is 2 indications in one).

Suscríbete a nuestro boletín

INFORMACIÓN BÁSICA SOBRE PROTECCIÓN DE DATOS

Responsable del tratamiento: SITUM TECHNOLOGIES, S.L.
Contacto: Responsable del tratamiento: situm@situm.es
Responsable de protección: dpo@situm.es
Finalidad y base legal: Gestionar el envío de newsletter de SITUM sólo con consentimiento.
Legitimación: Consentimiento expreso del interesado.
Destinatarios: Los datos no serán cedidos a terceros salvo obligación legal.
Plazo de conservación: Mientras la parte interesada permanezca suscrita al newsletter (en cada newsletter enviado por Situm estará disponible un link para darse de baja).
Derechos: El interesado podrá revocar en cualquier momento su consentimiento, así como ejercitar los derechos de oposición, acceso, conservación, rectificación, limitación, supresión de datos y no ser objeto de una decisión basada únicamente en el tratamiento automatizado de datos, dirigiéndose por escrito a SITUM en las direcciones indicadas.
Información Adicional: Puede consultar la información adicional y detallada sobre Protección de Datos en nuestra política de privacidad.

Por favor, descarga tu copia aquí.

Muchas gracias por descargar nuestro whitepaper. No dudes en contactar con nosotros si quieres saber más sobre cómo nuestras soluciones pueden ayudar a tu negocio.


Cerrar ventana