Mapping A Drive
I wanted to demonstrate how easy it is to record a video as you drive along, and then be able to playback that video with an overlay of your position on a map. This could enable further functionality, like the ability to report potholes or "code" a road to get a safety rating etc.
The result of my work can be found at routeshoot.programster.org.
Similar Posts
Steps
Get Your Hardware Setup
The first thing you need is the ability to record video footage and GPS in an "open" format that we can easily work with. I have yet to find a dashcam that allows this, but luckily the free Routeshoot app does exactly what I need. It's just a shame that my phone is rather cheap with an "okay" camera, limited storage, and a fairly rubbish mounting system.
Record A Drive
Go take a video recording of a drive. I would suggest taking a few short drives trying to do this as you get used to the app. I found that I had thought it was recording when it wasn't more than once.
Pull The Data off the Phone
Once you have successfully recorded a drive, we need to get the data off the phone.
I used the Ftp Server android app to turn my phone into an FTP server.
I then used Filezilla on my desktop to connect to the phone and fetch the files from /Android/media/com.wilsonpymmay.routeshoot
.
You will have an mp4 and kml file for each recording. The kml file that belongs with the mp4 file will have the exact same filename with the .kml extension appended to the end.
Convert the KML File
Now we need to convert the KML file into a more useable JSON format to work with. I created the script below to do this:
$xmlString = file_get_contents('iGV_20170924123608.mp4.kml');
$xmlObj=simplexml_load_string($xmlString);
$placemarks = array();
foreach ($xmlObj as $document)
{
$counter = 0;
foreach ($document as $name => $element)
{
if ($name == "Placemark")
{
$placemark = $element;
if (isset($element->ExtendedData))
{
$extendedData = $element->ExtendedData;
if (isset($element->ExtendedData->SchemaData->SimpleData))
{
$simpleData = $extendedData->SchemaData->SimpleData;
if (isset($simpleData[0]))
{
$placemarkArray = array(
'Lat' => floatval((string)$simpleData[1]),
'Lon' => floatval((string)$simpleData[2]),
'Bearing' => floatval((string)$simpleData[3]),
'Speed' => floatval((string)$simpleData[4]),
'UID' => intval((string)$simpleData[5]),
'Altitude' => floatval((string)$simpleData[6]),
'UTC_Date' => (string)$simpleData[7],
'UTC_Time' => (string)$simpleData[8],
'Distance' => floatval((string)$simpleData[9]),
'X' => floatval((string)$simpleData[10]),
'Y' => floatval((string)$simpleData[11]),
'Z' => floatval((string)$simpleData[12]),
);
$placemarks[] = $placemarkArray;
}
}
}
}
}
}
$output = json_encode($placemarks, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
file_put_contents('iGV_20170924123608.mp4.json', $output);
The output of which looks like:
[
{
"Lat": 51.34397,
"Lon": -0.473886,
"Bearing": 0,
"Speed": 0,
"UID": 1,
"Altitude": 61,
"UTC_Date": "24/09/2017",
"UTC_Time": "12:36:09",
"Distance": 0.278152,
"X": 10.036493,
"Y": 0.373495,
"Z": -0.78051
},
{
"Lat": 51.34397,
"Lon": -0.473884,
"Bearing": 0,
"Speed": 0,
"UID": 2,
"Altitude": 61,
"UTC_Date": "24/09/2017",
"UTC_Time": "12:36:10",
"Distance": 0.459539,
"X": 9.672575,
"Y": 0.430956,
"Z": -1.326388
},
Convert Video
Your phone is probably pretty bad at encoding video in real-time. This means that the video is highly uncompressed and would use a lot of storage and bandwidth if you were to use it directly. The solution for us is to convert the video to VP9 and use that. This format can be played back in a browser, yet is roughly the same compression as the newer h.265/HEVC format. My website is using the video with a 3000kbps bitrate, but 2000 kbps would probably be enough. Using 3000kbps, my 20 minute video went from 1.6GiB to JUST 455 MiB. A 2000kbps version is just 304 MiB.
Get a Google Maps API Key
Now you need to get yourself an API key for google maps.
Deploy Your Website
Install apache/nginx on a server to turn it into a webserver, and put the GPS json file on there along with the encoded video.
Then copy and paste the code below to become your index.html
file and change it to suit your needs.
You just need to update the paths to point to your video file and GPS files and may want to change the CSS styling along with putting your google maps key in where it says MY_GOOGLE_MAPS_KEY
.
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
background-color: #2C3539;
}
.main-box {
display:block;
width:1280px;
height:720px;
position: absolute;
left: 50%;
top: 50%;
margin-top: -360px;
margin-left: -640px;
}
#map {
position: absolute;
top: 0px;
left: 0px;
height: 200px;
width: 400px;
border: 1px solid black;
margin-left: auto;
margin-right: auto;
z-index: 1;
}
#player {
border: 1px solid black;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key=MY_GOOGLE_MAPS_KEY&callback=initMap"></script>
</head>
<body>
<div class="main-box">
<div id="map"></div>
<!-- video of journey -->
<video id="player" width="1280" height="720" controls>
<source src="/trips/tesco/full.3000K.webm" type="video/webm">
Your browser does not support the video tag.
</video>
</div>
<script>
var carLocation = {
"lat": 51.339797,
"lng": -0.486179
};
var mapConfig = {
"zoom": 17,
"center": carLocation
};
var map;
var markerConfig
var carMarker;
var carIcon;
var g_mapData = [];
function initMap()
{
map = new google.maps.Map(document.getElementById('map'), mapConfig);
carIcon = {
"path": google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
"scale": 5,
"rotation": 260.100006,
"strokeWeight": 3,
"fillColor": "blue",
"fillOpacity": 1
};
markerConfig = {
"position": carLocation,
"map": map,
"icon": carIcon
};
carMarker = new google.maps.Marker(markerConfig);
}
//https://www.w3schools.com/tags/av_prop_currenttime.asp -->
function iteration()
{
// get video position
var player = document.getElementById("player");
var relevantData = g_mapData[Math.floor(player.currentTime)];
var newPosition = {
"lat": relevantData.Lat,
"lng": relevantData.Lon
};
carIcon.rotation = relevantData.Bearing;
carMarker.setIcon(carIcon);
carMarker.setPosition(newPosition);
map.setCenter(newPosition);
}
$(document).ready(function(){
// get the GPS data for the trip
$.getJSON("/trips/tesco/gps.json", function(data) {
g_mapData = data;
setInterval(function(){ iteration(); }, 1000);
});
});
</script>
</body>
</html>
The end result should look something like below:
Conclusion
You have now mapped your own drive. At this point you can easily take the project further by adding functionality like being able to stop the video and press a button to report a pothole, which would send off the relevant GPS coordinates to somebody.
First published: 16th August 2018