Thursday, February 11, 2021

PhoneMapper

I recently came across a neat Android app called MacroDroid. It is an automation utility that lets you make macros - basically scripts, that get triggered at different events; very akin to AutoHotkey in Windows. There were a lot of features in this little app. I was pretty impressed. I tinkered around with it for some time and one day I thought why not make a location tracker for myself. So, I made a macro that would log the location, along with various other properties of the phone (the battery level, uptime, etc) every hour to a local file on the phone. Soon, after a couple of days I had accrued maybe a hundred records. Then I wondered how it would look if I was able to visualize all that data. I searched around the internet for anything of the sort but any program able to do that would have to be fed the collected records in a set format - one that it expected and that would entail some degree of data-cleaning. So, I decided to make an app of my own for the express purpose of reading the log recorded by my macro and displaying it on a map.

For the aforementioned purpose, I instinctively went to the Google Maps API but turns out it cannot be used without a billing address. Even people who do pay to use it have qualms about how expensive it is compared to other, similar services. So, I investigated some more and decided that OpenStreetMap would be my best bet. I made a boilerplate webpage using its tile server in leaflet.js and was starting to get the hang of it - starting from drawing a map centered at a given latlong location to placing markers on the map but I had to be able to read the log file uploaded to Google Drive from my phone before drawing the map. At first, I thought maybe I had to use the Google Drive API for it as well but I found this post. So, the direct download link problem was set. But when I actually proceeded with the fetch() API in JavaScript, CORS held me back. Apparently, the browser doesn't let reception of a resource sent by the server if the sender is not the same origin as the server. In my case, I had launched the web page locally, i.e. by opening the corresponding html file, so Chrome would not let me view the log file from Google Drive because the request for the file originated from a file:// source and not an https:// one and the CORS policy that web browsers follow forbids that (if I'm correct). So, it was clear that I had to either host a website running this frontend code or alternatively opt for the Electron framework for a desktop app. Surely, that had to work, I imagined. But the last time I made something in Electron, it was absolutely massive. So, I steered clear of it. Instead, I chose C# - surely a language this popular and in such rapid development must have a very stable library for displaying maps.

Well, C#, I found out, while for sure having a lot of libraries for a variety of different purposes, was pretty lean in the area of drawing maps. Fortunately, I stumbled upon GMap.net, a mapping library available also via NuGet package installer that provides a convenient component for drawing maps in Windows Forms, WPF applications etc. Though the documentation was virtually non-existent, this was all I needed to get started. Everything went ahead smoothly in C# and now, a couple of days later, here I am. A functional version 1.0 is here and this is what it looks like:


The program should be pretty self-explanatory but I've also included instructions in the Help menu regarding the setting up of the log file URL and the format it expects the file to be in, along with part of the MacroDroid macro that I personally use.

GitHub

Download


My Musings During Development:

References:

https://stackoverflow.com/questions/44888219/windows-form-display-dynamic-google-map-instead-of-static-map-in-net

http://www.independent-software.com/gmap-net-beginners-tutorial-maps-markers-polygons-routes-updated-for-vs2015-and-gmap1-7.html

https://stackoverflow.com/questions/44992766/is-there-any-good-gmap-net-documentation-out-there-for-c-sharp-windows-form

http://www.independent-software.com/gmap-net-beginners-tutorial-adding-clickable-markers-to-your-map-updates-for-vs2015-and-gmap-1-7.html

https://www.c-sharpcorner.com/article/serialization-and-deserialization-in-c-sharp/

https://docs.microsoft.com/en-us/dotnet/api/system.io.filestream?view=netframework-4.7.2

https://stackoverflow.com/questions/3029675/listview-with-copy-paste


Feb 8, 2021 | 10:40 PM

-----------------------

I found that loading the table data from the contents of the downloaded file to the listview table itself wasn't that heavy computationally on the GUI of the app since I was using async/await for the download process. But still the app felt laggy during the population of the listview table. So I switched on the CPU profiler tool in Visual Studio Diagnostic Tools and it showed me that the majority of the CPU hogging was due to a seemingly innocent method for resizing the columns of the listview table:

listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);

All it does is it resizes the widths of the table's columns based on the header size/contents of the columns'. Amazingly, upon commenting out this command, the app felt way snappier. There was no visible lag or unresponsiveness while populating the table with the downloaded rows.


Feb 9, 2021 | 09:08 PM

-----------------------

Enabling AutoCompleteMode/AutoCompleteCustom/AutoCompleteSource properties of a toolstrip textbox seems to mess with its KeyPress event with the Enter key. Other keys trigger the event correctly though.


Feb 10, 2021 | 10:19 PM

-----------------------

Icon attribution:

<div>Icons made by <a href="https://www.freepik.com" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a></div>


Feb 11, 2021 | 12:45 PM

-----------------------

I had originally envisioned a web app for this project. But because of the same origin CORS policy enforced by browsers, I was unable to download the log file from google drive while running the html file from hard disk. I then thought of Electron as an alternative but the last time I made something using Electron, the output executable size was massive(~70MB I think). So, I settled on a desktop app in C#. But in the end, its dependencies made it so that the final size of the output (the main program executable along with the dependency dll files and corresponding resources) is around 16MB. I now think that maybe the Electron idea wasn't that bad after all. I know it is a significant difference between 70MB and 16MB but the dependencies are a headache to distribute without an installer. And I don't have much experience of producing installers. I'd much rather have a standalone executable.


Feb 11, 2021 | 02:52 PM

-----------------------

I only learnt of GitHub Releases a couple of minutes ago. Gonna use it more often. Neater way of uploading your compiled binaries.