Web and Android app showing routes from GPX files on a map.
Web app runs at https://map.milanlaslop.dev. Android app downloadable from that web.
- Kotlin, Anko library
Implementation Details and Challenges
The Pigeon Maps library is used for displaying and manipulating with the map. The library provides a map component with all common features, which works out-of-the-box. Moreover it supports creating any custom overlays - that’s how I could add the routes (lines) to the map.
Web + Android
The Android application additionally contains the possibility to download GPX files from a Strava account. This is done by an automated browsing of the Strava web site in a hidden
The Android application can load GPX files from the filesystem (storage), without even requiring
READ_EXTERNAL_STORAGE permission. This can be done by opening the file chooser provided by the system (
Intent.ACTION_OPEN_DOCUMENT_TREE) and reading the result using
DocumentsContract. After the user chooses some directory in the provided file chooser, the application automatically obtains the rights to read the corresponding files.
Performance was the most challenging aspect. I especially dealt with:
Performance of the XML (GPX) Parsing
Instead of DOM parsers, I focused on event-emitting (SAX-like) parsers - which I assumed would be faster and less memory-consuming. I tried sax and Saxophone. Saxophone turned out to be faster (yes, their Benchmark tells the same).
I/O Performance of Files Loading
The web application needed to access file data provided by the Android application. At first, I did it the simplest way - through
- there is some additional time spent by the processing of the call itself, containing some data conversions
After some experimenting, I discovered another way how to provide files - through
shouldInterceptRequest (the web application would basically access some URL, and Android code would respond with a stream).
Map Overlays (Lines)
The lines for the routes are SVG images in the HTML DOM, containing a lot of point specifications. When moving the map, only one thing changes in the DOM - SVG transformation specifying the translation (position) of the image. The browsers seem to understand this and do not try to re-draw or rebuild something unnecessarily. Of course some work had to be done around correct usages of React to achieve an optimal behavior (correct decomposition to components, correct design of props and state for React to not re-check or re-render the components unnecessarily).
Zooming is a more complex operation - images are re-built because:
- different zoom levels require different detail (precision and count of the line segments)
- the projection (from latitude+longitude to the pixels on the 2D map) has to be re-calculated
The Android app supports switching to full-screen mode. Thus, the UI layout was designed in a way that even displays with notch or round edges can accommodate the UI (at least on some devices).