> ## Documentation Index
> Fetch the complete documentation index at: https://docs.here.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a GPX recording app

<style>
  {'.beta-banner { color: #000000; background: linear-gradient(270deg, #65EBE2 0%, #6B9CFF 100%); font-weight: bold; padding: 5px; font-family: FiraGo, sans-serif; }'}
</style>

<p className="beta-banner">Positioning is only available with the Navigate license.</p>

Use HERE Positioning to build a digital diary for your adventure trips.

<HTMLBlock>
  {`
  <div align="center"><p>
    <img src="https://files.readme.io/e2d545a0bcccad1bc1f6ff19a709ca54416918fbfa29339f1b242289bac8af9b-android-himalayas_trekking.jpg" width="100%" />
    <figcaption>Way to Chandrashilla trek in the Garhwal Himalayan range, 5000 m AMSL</figcaption>
  </p></div>
  `}
</HTMLBlock>

As adventure enthusiasts, we all know the excitement of documenting our hiking and trekking trips to show our friends and family. While traditional methods such as cameras can provide a visual record of these experiences, they do not capture the exact path taken. In this section, we'll use the [HERE SDK for Android (Navigate)](android-get-started.md) to record and visualize our audacious trips.

You can use the resulting app as a starting point to create your own customized applications that leverages the HERE SDK's many features and functionalities. Furthermore, the HERE SDK is shipped with ready-made code that you can find on [GitHub](https://github.com/heremaps/here-sdk-examples).

Note that our focus will be on recording GPS signals, rather than counting steps because we want to see our hiking path on the HERE map.

Now, let's delve into the use of the HERE SDK to build our "HikingDiary" app.

## Get and show locations

With the code snippets from the HERE SDK found on this [GitHub](https://github.com/heremaps/here-sdk-examples) repo, we can easily copy & paste the code to determine our location by integrating the [HEREBackgroundPositioningServiceProvider](https://github.com/heremaps/here-sdk-examples/blob/master/examples/latest/navigate/android/Java/HikingDiary/app/src/main/java/com/here/hikingdiary/HEREBackgroundPositioningServiceProvider.java) into our app. The `HEREBackgroundPositioningServiceProvider` uses [HEREBackgroundPositioningService](https://github.com/heremaps/here-sdk-examples/blob/master/examples/latest/navigate/android/Java/HikingDiary/app/src/main/java/com/here/hikingdiary/backgroundpositioning/HEREBackgroundPositioningService.java) as a background service listener to listen to positioning updates even in the background.

While hiking, we usually put our device into the pocket and leave it there. Therefore, to continue tracking our hike, we enable background updates for the app by default.

The`HEREBackgroundPositioningService` utilizes the `LocationEngine` provided by the HERE SDK that uses the Android platform positioning, and provides high-precision location data from a variety of sources such as GPS, network location providers, other Global Navigation Satellite System (GNSS) receivers.

As a next step, we can integrate the copied `HEREBackgroundPositioningServiceProvider` by integrating the `LocationListener` in our main class, we named it `HikingApp.java`, and create a `HEREBackgroundPositioningServiceProvider` instance in that main class to get started:

```java
// A class to receive location events from the device.
public HEREBackgroundPositioningServiceProvider hereBackgroundPositioningServiceProvider;

// Sets listener to receive locations from HERE Positioning.
hereBackgroundPositioningServiceProvider = new HEREBackgroundPositioningServiceProvider(activity, locationListener);

...

// Receives location updates from LocationListener.
func onLocationUpdated(_ location: heresdk.Location) {
  ...
}
```

Note: `LocationListener` is invoked on the main thread and called each time a new `Location` is available.

Do not forget to add the service class `HEREBackgroundPositioningService` in the `AndroidManifest.xml` file.

```xml
  <service
    android:name="com.here.hikingdiary.backgroundpositioning.HEREBackgroundPositioningService"
    android:exported="false"/>
```

You can set the accuracy precision of the location updates via accuracy parameter. The `navigation` setting is the highest precision possible.

You can find the final implementation of our `HikingApp` class on [GitHub](https://github.com/heremaps/here-sdk-examples/blob/master/examples/latest/navigate/android/Java/HikingDiary/app/src/main/java/com/here/hikingdiary/HikingApp.java).

### Add the required permissions

Location tracking raises some privacy concerns, as it may reveal sensitive information about the user's whereabouts and activities. To address these concerns, many mobile platforms require apps to obtain explicit user consent before accessing location data and provide options to control the frequency and granularity of the location tracking. It is important for app developers to handle location data responsibly and in accordance with the privacy regulations and best practices. Hence, before you start using the `LocationEngine` in your app, you need to add the required permissions to the app's `AndroidManifest.xml` file:

```xml
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.INTERNET"/>

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
```

You can find the resulting [AndroidManifest.xml](https://github.com/heremaps/here-sdk-examples/blob/master/examples/latest/navigate/android/Java/HikingDiary/app/src/main/AndroidManifest.xml) file on GitHub.

## Handle location accuracy

Now that we are able to receive locations, we have to check the accuracy of the location:

The `Location` object that we receive from `onLocationUpdated` describes the location of the user in the world at a given time and it is just an estimation of the true geographic coordinates. Even if you yourself are stationary, the GNSS satellites are not stationary but travel at a huge speed in the space, which may cause the location estimate to move around. For example, when meditating or having a meal while trekking, the location estimate will vary around my actual location. In such scenarios, the app should not update positions because this way you can travel miles even sitting at home.

Below illustration visualizes the problem with the fluctuation of the received GPS signals:

<HTMLBlock>
  {`
  <div align="center"><p>
    <img src="https://files.readme.io/074df59fa01d5807280ccd02b467a94e6964d7c712ed26ef1060d544805b3bf7-android-default_filter.jpg" width="360" style="box-shadow: 0 0 10px"/>
    <figcaption>Illustration: GPS signal visualization.</figcaption>
  </p></div>
  `}
</HTMLBlock>

The hiking man denotes the location of the user (moving or non-moving). The circle surrounding the man is the accuracy circle whose radius is defined by `horizontalAccuracyInMeters` at any time t1. The blue circles are examples of possible locations at any time t1 with 68% probability of lying inside the horizontal accuracy circle, and 32% probability of lying outside the accuracy circle. For example, if the `horizontalAccuracyInMeters` is 10 meters then it is 68% likely that the true geographic coordinates can lie within the radius of 10 meters. As the hiking man transitions from time t1 to t2, the `horizontalAccuracyInMeters` value decreases, resulting in a reduced accuracy circle. However, the probability of finding the true geographical coordinates within the accuracy circle increases. Hence, we can conclude that smaller accuracy circles have better accuracy than the bigger one.

With a naive implementation, we could just accept the locations that are below a certain accuracy threshold.

This can lead to imprecise distance calculation, as illustrated below:

<HTMLBlock>
  {`
  <div align="center"><p>
    <img src="https://files.readme.io/5d4ba1e742d133ae5396cde856c887dea80500089b848564e0ae0c61ef1ccac6-android-actual_distance_illustration.jpg" width="700" style="box-shadow: 0 0 10px"/>
    <figcaption>Illustration: Problems with distance calculation.</figcaption>
  </p></div>
  `}
</HTMLBlock>

The black lines are the calculated distances based on the GPS signals and the blue lines are the actual distances that are impossible to determine, unless we roll out a rope behind us and track the real path.

How to solve this problem?

## Create location filtering algorithm

To solve the above problems, we need to create a location filtering algorithm that accepts locations only when moving. For this, we will create a new `DistanceAccuracyLocationFilter` class and provide a filtering strategy based on the horizontal accuracy of the location estimate and the distance to our previous location estimate.

For this, we need two types of filters:

1. DistanceFilter: Filters the locations based on a distance threshold.
2. AccuracyFilter: Filters the locations based on the horizontal accuracy of the location estimate, and only includes the readings with a certain level of accuracy. To create this filter we will use the `horizontalAccuracyInMeters` property which is found in the `Location` object provided by the HERE SDK. It gives the estimated horizontal accuracy in meters and tells us that the true geographic coordinates will lie within this radius of uncertainty with a probability of 68% (see first illustration above).

Let's look how we can implement such a distance filter. Firstly, we need to define a `distanceThresholdInMeters` property, let's say 15 meters. We will only accept the GPS signal if the estimated location is beyond the `distanceThresholdInMeters` from the last accepted location. Below illustration visualizes the idea of our planned mechanism:

<HTMLBlock>
  {`
  <div align="center"><p>
    <img src="https://files.readme.io/aa814041fa1abd5ffe775a57dc77831ecf30d03f22102e939e650d79ca5cdf39-android-distance_threshold_illustration.jpg" width="700" style="box-shadow: 0 0 10px"/>
    <figcaption>Illustration: Distance threshold visualization.</figcaption>
  </p></div>
  `}
</HTMLBlock>

Each circle represents a single location estimate with a varying accuracy circle. The crossed circles are below the distance threshold and therefore discarded.

On top of the distance filter, we also need to consider the accuracy of each GPS signal (see above). For this we define a `accuracyRadiusThresholdInMeters` property. Let's say it is 10 meters. Therefore, each GPS signal with a higher value than `accuracyRadiusThresholdInMeters` will be filtered out. In our first illustration above, this would mean that all blue GPS signals are accepted and the red ones are discarded.

Now, we are ready to go for the implementation of our algorithm using the two filter mechanisms:

```java
public interface LocationFilterInterface {
  boolean checkIfLocationCanBeUsed(Location location);
}

public class DistanceAccuracyLocationFilter implements LocationFilterInterface {
  // These two parameters define if incoming location updates are considered to be good enough.
  // In the field, the GPS signal can be very unreliable, so we need to filter out inaccurate signals.
  private static final double ACCURACY_RADIUS_THRESHOLD_IN_METERS = 10.0;
  private static final double DISTANCE_THRESHOLD_IN_METERS = 15.0;
  private GeoCoordinates lastAcceptedGeoCoordinates;

  @Override
  public boolean checkIfLocationCanBeUsed(Location location) {
      if (isAccuracyGoodEnough(location) && isDistanceFarEnough(location)) {
          lastAcceptedGeoCoordinates = location.coordinates;
          return true;
      }
      return false;
  }

  // Checks if the accuracy of the received GPS signal is good enough.
  private boolean isAccuracyGoodEnough(Location location) {
      Double horizontalAccuracyInMeters = location.horizontalAccuracyInMeters;
      if (horizontalAccuracyInMeters == null) {
          return false;
      }

      // If the location lies within the radius of ACCURACY_RADIUS_THRESHOLD_IN_METERS then we accept it.
      if (horizontalAccuracyInMeters <= ACCURACY_RADIUS_THRESHOLD_IN_METERS) {
          return true;
      }
      return false;
  }

  // Checks if last accepted location is farther away than xx meters.
  // If it is, the new location will be accepted.
  // This way we can filter out signals that are caused by a non-moving user.
  private boolean isDistanceFarEnough(Location location) {
      if (lastAcceptedGeoCoordinates == null) {
          // We always accept the first location.
          lastAcceptedGeoCoordinates = location.coordinates;
          return true;
      }

      double distance = location.coordinates.distanceTo(lastAcceptedGeoCoordinates);
      if (distance >= DISTANCE_THRESHOLD_IN_METERS) {
          return true;
      }
      return false;
  }
}
```

The [DistanceAccuracyLocationFilter](https://github.com/heremaps/here-sdk-examples/blob/master/examples/latest/navigate/android/Java/HikingDiary/app/src/main/java/com/here/hikingdiary/locationfilter/DistanceAccuracyLocationFilter.java) algorithm can be also found on the HERE SDK GitHub repo.

Now, we can create an instance of `DistanceAccuracyLocationFilter` and filter out the incoming location signals and update the locations on the map:

```java
private LocationFilterInterface locationFilter;

...

// Filter out undesired location signals.
locationFilter = new DistanceAccuracyLocationFilter();

...

public void onLocationUpdated(@NonNull Location location) {
    if (locationFilter.checkIfLocationCanBeUsed(location)) {
      // Use the location.
    }
}
```

You can also create your own location filter algorithm by adopting the [LocationFilterInterface](https://github.com/heremaps/here-sdk-examples/blob/master/examples/latest/navigate/android/Java/HikingDiary/app/src/main/java/com/here/hikingdiary/locationfilter/LocationFilterInterface.java) protocol. For example, you can implement a bypass filter like this:

```java
// The DefaultLocationFilter class implements the LocationFilterInterface and
// allows every location signal to pass in order to visualize the raw GPS signals on the map.
public class DefaultLocationFilter implements LocationFilterInterface {
    @Override
    public boolean checkIfLocationCanBeUsed(Location location) {
        return true;
    }
}
```

With the `LocationFilterInterface` you can customize the app to use different algorithms for location filtering without changing the core functionality of the app. Note that our filtering algorithm above is kept as simple as possible, it can be improved as per your needs.

## Record location updates

Once we have the location updates, the next step is to record them. For this, we use a `GPXTrackWriter` code snippet from the HERE SDK GitHub that allows us to create a `GPXTrack`. This `GPXTrack` can be stored and loaded with the `GPXDocument` class. The `GPXDocument` contains all the GPX tracks and is saved in the [GPX](https://www.topografix.com/gpx.asp) file format. Hence, once saved, it can be easily shared with other applications that understand the GPX file format.

GPX is a de facto standard data format for GPS data exchange, including waypoints, tracks, and routes. GPX files contain geographical information in XML format and are easy to process with various software programs and GPS devices.

The HERE SDK can help with GPX data by providing tools for reading, displaying, and manipulating GPX data in a variety of ways.

For recording the location updates, create an instance of [GPXManager](https://github.com/heremaps/here-sdk-examples/blob/master/examples/latest/navigate/android/Java/HikingDiary/app/src/main/java/com/here/hikingdiary/GPXManager.java) to manage our GPX operations:

```java
private GPXTrackWriter gpxTrackWriter = new GPXTrackWriter();
private GPXManager gpxManager;

// Create a GPXDocument file with named as myGPXDocument.
gpxManager = new GPXManager("myGPXDocument.gpx", context);
```

Now, we can write location updates to a `GPXTrack` inside the `onLocationUpdated()` method:

```java
// Add location updates to a GPX track.
gpxTrackWriter.onLocationUpdated(location)
```

Finally, to store the hiking trip, we use this one-liner code that stores the GPX track in a `GPXDocument`:

```java
// Permanently store the trip on the device.
gpxManager.saveGPXTrack(gpxTrackWriter.getTrack());
```

In order to load our trips, call:

```java
GPXTrack gpxTrack = gpxManager.getGPXTrack(index);
if (gpxTrack == null) {
    return;
}
```

You can record and store multiple hiking trips as multiple `GPXTrack` in a single `GPXDocument`.

## Finalize the app

Now, in order to complete our app, we show the travelled path with a `MapPolyline` on the map. The implementation of a `MapPolyline`follows the [Map Items Guide](android-map-items.md#add-map-polylines) that can be found in the [Developer Guide for the HERE SDK](android-get-started.md). It can be also seen in the [`HikingApp.java`](https://github.com/heremaps/here-sdk-examples/blob/41f258e5af2617703f39b8614d456e3bd741e928/examples/latest/navigate/android/HikingDiary/app/src/main/java/com/here/hikingdiary/HikingApp.java#L154) class.

In order to extend the polyline during the trip, we take the latest list of coordinates from the `gpxManager` instance, since it already contains the latest location updates. With this list we can create a new `geoPolyline` instance that we set to our existing `mapPolyline` instance:

```java
// Update the polyline shape that shows the travelled path of the user.
mapPolyline.setGeometry(geoPolyline);
```

This will immediately extend the polyline that is already shown on the map view.

Note that in this tutorial we did not covered every step of the app development process. To get a more complete picture, take a look at this [Get started](android-integrate-here-sdk.md) guide.

You can find the complete Android project on [GitHub](https://github.com/heremaps/here-sdk-examples/tree/master/examples/latest/navigate/android/Java/HikingDiary).

Below you can see the finished app:

<HTMLBlock>
  {`
  <div align="center">
  <table>
    <tr align="center">
      <td align=center style="width: 33%">
        <img src="https://files.readme.io/f9953c7a0d48414fdc390fe5cf5b0e6b7da3fd826661d8113ecbbf707fca3505-android-hikeapp_satellite_screenshot.jpg" width="350" style="box-shadow: 0 0 10px" />
        <figcaption>Screenshot: HikingDiary app with SATELLITE map scheme.</figcaption>
      </td>
      <td align=center style="width: 33%">
        <img src="https://files.readme.io/95a7a60811096cb9d149b5f8cc19b6e1584fee81afe9a56fcccae0d22f7b27b9-android-hikeapp_raster_screenshot.jpg" width="350" style="box-shadow: 0 0 10px" />
        <figcaption>Screenshot: HikingDiary app with outdoor raster layer.</figcaption>
      </td>
    </tr>
  </table>
  </div>
  `}
</HTMLBlock>

The HERE SDK provides various map schemes. By default, in our app we show the `SATELLITE` map scheme. However, you can change the `MapScheme` and switch to a different map scheme. To know more about the available `MapScheme` options, you can refer to the API Reference of the HERE SDK.

Also, in our app, you can enable an outdoor raster layer on top of the map view by sliding a switch at the top-right corner of the main view. Note that this is a third-party raster layer that can be suitable for hiking trips, as it shows height lines and more detailed paths. Here, we use an outdoor map from [thunderforest.com](https://www.thunderforest.com). More information about custom raster layers can be found in the [Developer Guide for the HERE SDK](android-custom-map-styles.md#add-custom-raster-layers).

### Next steps

There are multiple ways to enhance our little "HikingDiary" app. Below are some ideas:

* Use the `altitude` property of the `GeoCoordinates` object to include a height profile of your trip.
* Integrate more map schemes, for example, the HERE SDK offers a `TERRAIN` scheme that provides hill shading.
* Store your trips with the overall duration and timestamps where you took rests.
* Include an export option of the stored GPX file to share it with other applications.

Since we released the app as an open source project on [GitHub](https://github.com/heremaps/here-sdk-examples/tree/master/examples/latest/navigate/android/Java/HikingDiary), contributions are welcome! Feel free to submit pull requests with bug fixes, new features, and overall improvements.

Happy hiking!