Android Question Get location data (lat and lng) from .mp4 file

RB Smissaert

Well-Known Member
Licensed User
Longtime User
Managed to do this with the JPEGUtils library from Agraham for .jpg files:

B4X:
    Dim arrLatLng(2) As Float
    'Exif is the JpegUtils library from AGraham
    Exif.Initialize(tFP.strFolderName, tFP.strFileName)
    Exif.getLatLong(arrLatLng)
    Log(arrLatLng(0) & ", " & arrLatLng(1))

But not sure how to do this for video files (.mp4 files).
The JPEGUtils library will give 0 and 0, although the lat/lng data is available in the mp4 file.

RBS
 

TILogistic

Expert
Licensed User
Longtime User
B4A.

use javaobject api android.media.MediaMetadataRetriever

Not all videos contain GPS data.
Works on Android API 10+.
The file must exist and have read permissions.

test:
B4X:
Sub GetVideoLocation(FilePath As String) As String
    Dim jo As JavaObject
    jo.InitializeNewInstance("android.media.MediaMetadataRetriever", Null)
    jo.RunMethod("setDataSource", Array(FilePath))
    
    ' Extract location metadata
    Dim location As String = jo.RunMethod("extractMetadata", Array(jo.GetField("METADATA_KEY_LOCATION")))

    or
    
    Dim location As String = jo.RunMethod("extractMetadata", Array(23)) ' 23 = METADATA_KEY_LOCATION 
    
    If location <> Null Then
        ' Location format is usually "lat,long"
        Return location
    Else
        Return "No location data found"
    End If
End Sub
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
B4A.

use javaobject api android.media.MediaMetadataRetriever

Not all videos contain GPS data.
Works on Android API 10+.
The file must exist and have read permissions.

test:
B4X:
Sub GetVideoLocation(FilePath As String) As String
    Dim jo As JavaObject
    jo.InitializeNewInstance("android.media.MediaMetadataRetriever", Null)
    jo.RunMethod("setDataSource", Array(FilePath))
   
    ' Extract location metadata
    Dim location As String = jo.RunMethod("extractMetadata", Array(jo.GetField("METADATA_KEY_LOCATION")))

    or
   
    Dim location As String = jo.RunMethod("extractMetadata", Array(23)) ' 23 = METADATA_KEY_LOCATION
   
    If location <> Null Then
        ' Location format is usually "lat,long"
        Return location
    Else
        Return "No location data found"
    End If
End Sub
Thanks, will try that.

RBS
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
B4A.

use javaobject api android.media.MediaMetadataRetriever

Not all videos contain GPS data.
Works on Android API 10+.
The file must exist and have read permissions.

test:
B4X:
Sub GetVideoLocation(FilePath As String) As String
    Dim jo As JavaObject
    jo.InitializeNewInstance("android.media.MediaMetadataRetriever", Null)
    jo.RunMethod("setDataSource", Array(FilePath))
   
    ' Extract location metadata
    Dim location As String = jo.RunMethod("extractMetadata", Array(jo.GetField("METADATA_KEY_LOCATION")))

    or
   
    Dim location As String = jo.RunMethod("extractMetadata", Array(23)) ' 23 = METADATA_KEY_LOCATION
   
    If location <> Null Then
        ' Location format is usually "lat,long"
        Return location
    Else
        Return "No location data found"
    End If
End Sub
Tried this Java code, but not sure the returned lat/lng data is precise enough.
This is what I get:
20260211_144243.mp4: +52.6139-002.1460/

I take it to get the float/double values I split this on 00 ?

RBS
 
Upvote 0

jahswant

Well-Known Member
Licensed User
Longtime User
TRY THIS CODE TO PARSE IT.

B4X:
'Put this in a code module (or anywhere accessible)

Type LatLng (Lat As Double, Lng As Double)

'Example:
'Dim s As String = "+52.6139-002.1460/"
'Dim p As LatLng = ParseIso6709LatLng(s)
'Log($"Lat=${p.Lat}, Lng=${p.Lng}"$)

Public Sub ParseIso6709LatLng(s As String) As LatLng
    Dim res As LatLng

    If s = Null Or s.Length < 4 Then Return res

    'Remove spaces and trailing slash if present
    s = s.Trim
    If s.EndsWith("/") Then s = s.SubString2(0, s.Length - 1)

    'Find where longitude starts: the next '+' or '-' after position 0
    Dim idx As Int = FindSecondSignIndex(s)
    If idx = -1 Then Return res

    Dim latStr As String = s.SubString2(0, idx)
    Dim lngStr As String = s.SubString(idx)

    'Convert to Double
    Try
        res.Lat = latStr
        res.Lng = lngStr
    Catch
        'If parsing fails, return 0,0
    End Try

    Return res
End Sub

Private Sub FindSecondSignIndex(s As String) As Int
    'Search from index 1 so we don't pick the first sign (lat sign)
    For i = 1 To s.Length - 1
        Dim ch As String = s.CharAt(i)
        If ch = "+" Or ch = "-" Then Return i
    Next
    Return -1
End Sub
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
Tried this Java code, but not sure the returned lat/lng data is precise enough.
This is what I get:
20260211_144243.mp4: +52.6139-002.1460/

I take it to get the float/double values I split this on 00 ?

RBS
Ignore this, the lat/lng values are fine. Not sure why the string values suggested different.

RBS
TRY THIS CODE TO PARSE IT.

B4X:
'Put this in a code module (or anywhere accessible)

Type LatLng (Lat As Double, Lng As Double)

'Example:
'Dim s As String = "+52.6139-002.1460/"
'Dim p As LatLng = ParseIso6709LatLng(s)
'Log($"Lat=${p.Lat}, Lng=${p.Lng}"$)

Public Sub ParseIso6709LatLng(s As String) As LatLng
    Dim res As LatLng

    If s = Null Or s.Length < 4 Then Return res

    'Remove spaces and trailing slash if present
    s = s.Trim
    If s.EndsWith("/") Then s = s.SubString2(0, s.Length - 1)

    'Find where longitude starts: the next '+' or '-' after position 0
    Dim idx As Int = FindSecondSignIndex(s)
    If idx = -1 Then Return res

    Dim latStr As String = s.SubString2(0, idx)
    Dim lngStr As String = s.SubString(idx)

    'Convert to Double
    Try
        res.Lat = latStr
        res.Lng = lngStr
    Catch
        'If parsing fails, return 0,0
    End Try

    Return res
End Sub

Private Sub FindSecondSignIndex(s As String) As Int
    'Search from index 1 so we don't pick the first sign (lat sign)
    For i = 1 To s.Length - 1
        Dim ch As String = s.CharAt(i)
        If ch = "+" Or ch = "-" Then Return i
    Next
    Return -1
End Sub

I think I got this working OK now.
The code is meant for matching up lat/lng data with time data recorded outside the actual journey time.
Tested this now and it seems to be working fine, but will need some more testing.

B4X:
Sub AddImagesToJourney(iJourney_ID As Int, strFolder As String)
    
    Dim i As Int
    Dim n As Int
    Dim strSQL As String
    Dim strSQL2 As String
    Dim dMinDistance As Double
    Dim dLowestDistance As Double
    Dim oRS As ResultSet
    Dim oRS2 As ResultSet
    Dim oRS3 As ResultSet
    Dim lstFilesFolders As List
    Dim lstFiles As List
    Dim strFile As String
    Dim mapImageFilesInJourney As Map
    Dim arrLatLng(2) As Float
    Dim arrSplitString() As String
    Dim lDateTime As Long
    
    lstFilesFolders.Initialize
    lstFiles.Initialize
    mapImageFilesInJourney.Initialize
    
    strSQL = "select min(latitude), min(longitude), max(latitude), max(longitude) from journey_recordings where journey_id = ? and not latitude is null and not longitude is null"
    oRS = cMP.cConn.SQLNon_Clinical.ExecQuery2(strSQL, Array As String(iJourney_ID))
    oRS.Position = 0
    
    strSQL = "select image_file from journey_recordings where journey_id = ? and not image_file is null"
    oRS2 = cMP.cConn.SQLNon_Clinical.ExecQuery2(strSQL, Array As String(iJourney_ID))
    
    Do While oRS2.NextRow
        mapImageFilesInJourney.Put(oRS2.GetString2(0), 0)
    Loop
    
    If strFolder.Length = 0 Then
        strFolder = File.DirRootExternal & "/DCIM/Camera"
    End If
    
    'there are some 1282 pictures and videos in this folder
    lstFilesFolders = File.ListFiles(strFolder)
    
    For i = 0 To lstFilesFolders.Size - 1
        strFile = lstFilesFolders.Get(i)
        If File.IsDirectory(strFolder, strFile) = False Then
            If strFile.EndsWith(".mp4") Or strFile.EndsWith(".jpg") Then
                If strFile.StartsWith("20") Then
                    If cFunctions.CInt(strFile.SubString2(0, 4)) > 2025 Then
                        'As image files taken during the journey, can be matched on date-time data
                        '-------------------------------------------------------------------------
                        If mapImageFilesInJourney.ContainsKey(strFile) = False Then
                            lstFiles.Add(strFile)
                        End If
                    End If
                End If
            End If
        End If
    Next
    
    If lstFiles.Size = 0 Then Return
    
    strSQL = "select date_time, latitude, longitude from journey_recordings where journey_id = ?"
    oRS3 = cMP.cConn.SQLNon_Clinical.ExecQuery2(strSQL, Array As String(iJourney_ID))
    
    strSQL2 = "update journey_recordings set image_file = ? where journey_id = ? and date_time = ?"
    
    For i = 0 To lstFiles.Size - 1
        
        strFile = lstFiles.Get(i)
        
        If strFile.EndsWith(".mp4") Then
            Dim rs As ResumableSub = GetVideoLocation(File.Combine(strFolder, strFile))
            Wait For (rs) Complete (strVideoLatLng As String)
            
            If strVideoLatLng.SubString(1).Contains("-") Then
                arrSplitString = Regex.Split("-", strVideoLatLng)
                arrLatLng(0) = arrSplitString(0)
                arrLatLng(1) = 0 - arrSplitString(1).SubString2(0, arrSplitString(1).Length - 2)
            Else
                arrSplitString = Regex.Split("+", strVideoLatLng)
                arrLatLng(0) = arrSplitString(0)
                arrLatLng(1) = arrSplitString(1).SubString2(0, arrSplitString(1).Length - 2)
            End If
        Else
            Exif.Initialize(strFolder, strFile)
            Exif.getLatLong(arrLatLng)
        End If
        
        If arrLatLng(0) > oRS.GetDouble2(0) Then
            If arrLatLng(0) < oRS.GetDouble2(2) Then
                If arrLatLng(1) > oRS.GetDouble2(1) Then
                    If arrLatLng(1) < oRS.GetDouble2(3) Then
                        oRS3.Position = -1
                        dMinDistance = 1000000
                        Do While oRS3.NextRow
                            Dim rs As ResumableSub = MapUtilities.DistVincenty2(oRS3.GetDouble2(1), oRS3.GetDouble2(2), arrLatLng(0), arrLatLng(1))
                            Wait For (rs) Complete (dDistance As Double)
                            If dDistance < dMinDistance Then
                                dMinDistance = dDistance
                                lDateTime = oRS3.GetLong2(0)
                                dLowestDistance = dDistance
                            End If
                        Loop
                        If dLowestDistance < 3 Then
                            n = n + 1
                            Log(strFile & ": dLowestDistance: " & dLowestDistance & ", lDateTime: " & lDateTime)
                            cMP.cConn.SQLNon_Clinical.ExecNonQuery2(strSQL2, Array As String(strFile, iJourney_ID, lDateTime))
                        End If
                    End If
                End If
            End If
        End If
        
    Next
    
    Log("n: " & n)
    
End Sub

RBS
 
Upvote 0

TILogistic

Expert
Licensed User
Longtime User
? get Latitud and Longitud

B4X:
    Dim location As String = "+52.6139-002.1460/"
    If location <> Null Then
        location = location.Replace("/", "")
        Dim m As Matcher = Regex.Matcher("([+-]\d+\.\d+)", location)
  
        Dim lat As Double
        Dim lon As Double
  
        If m.Find Then lat = m.Group(1)
        If m.Find Then lon = m.Group(1)
  
        Log("Latitud: " & lat)
        Log("Longitud: " & lon)
    End If

1772058477268.png


1772058691982.png
 
Upvote 0

TILogistic

Expert
Licensed User
Longtime User
I see you're using Vincenty's distance calculation formula (WGS84 ellipsoid). Well done! It's more accurate.

If I understand your code correctly, you're validating the location of your MP4 file against the database records.

To improve validation in a pad signature app I'm developing, I also use the datetime from the MP4 file's metadata.

This is to determine if the user signed the document at the location and datetime recorded in the database.

With the location and datetime, you can validate other parameters, such as time, distance, speed, etc.

Use:

This is just a suggestion.


1772064076334.png
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
ISO-6709 is not quite as simple as suggested. and android's documentation only says that the location
should be specified in accordance with ISO6709, not necessarily in the format shown in the documentation,
which is given as a "for instance".
 
Upvote 0

emexes

Expert
Licensed User
Longtime User
This is what I get:
20260211_144243.mp4: +52.6139-002.1460/

I take it to get the float/double values I split this on 00 ?

I would split it on the positive/negative signs. I was wondering why the latitude wasn't zero-padded to three digits like the longitude is, then I realised that latitude is maximum 90 (2 digits) cf longitude is maximum 180 or 360 (3 digits).

On a side note, if you key in those coordinates to Google Maps (latitude +52.6139 longitude -002.1460) then it comes up with a canalside pub which I might well have had lunch at in 1982 when I spent a week on the canals with - lol, bit of a random connection - the step/host-father of a Finnish friend of my English mother from their nurse training in England around 1960.
 
Upvote 0

TILogistic

Expert
Licensed User
Longtime User
On a side note, if you key in those coordinates to Google Maps (latitude +52.6139 longitude -002.1460) then it comes up with a canalside pub which I might well have had lunch at in 1982 when I spent a week on the canals with - lol, bit of a random connection - the step/host-father of a Finnish friend of my English mother from their nurse training in England around 1960.
Yes, you left a souvenir on the bar table.

 
Upvote 0
Top