3

I am trying to create a video from a track in GPX or KML format from Google Street view images.

If I enter the data of the coordinates of a location, I can generate a JPG with that image.

My problem is that when I want it to be several points, then I can't do it.

I have tried to see if through the loaded page control I can do it, but it only does it with the last point that I send you.

Any idea how to do it?

from PyQt5.QtWidgets import QWidget, QApplication,QMainWindow,QFileDialog
from PyQt5.QtWidgets import QLineEdit, QVBoxLayout, QHBoxLayout,QSplitter,QPushButton
from PyQt5.QtCore import QUrl,QRect,QRectF
from PyQt5.QtGui import QIcon

from PyQt5.QtWebEngineWidgets import QWebEngineView,QWebEngineProfile,QWebEngineSettings
import sys
import time


class Video_Google_Street(QMainWindow):
    def __init__(self):
        super().__init__()
        self.showMaximized()
        
        # Create GUI
        self.centralwidget = QWidget(self)
        self.layoutWidget = QWidget(self.centralwidget)
        self.horizontalLayout = QVBoxLayout(self.centralwidget)
        self.web_view=QWebEngineView(self.layoutWidget)
        self.file_Gpx = QPushButton(self.layoutWidget)
        self.file_Gpx.setText('GPX')
        self.pb_fixed_point = QPushButton(self.layoutWidget)
        self.pb_fixed_point.setText('Fixed Point')
        self.pb_list_points = QPushButton(self.layoutWidget)
        self.pb_list_points.setText('List of Points')
        
        self.setCentralWidget(self.centralwidget)
        self.horizontalLayout.addWidget(self.web_view)
        self.horizontalLayout.addWidget(self.file_Gpx )
        self.horizontalLayout.addWidget(self.pb_fixed_point)
        self.horizontalLayout.addWidget(self.pb_list_points)
        
        # Create Slots
        # Slot when page is loaded
        self.web_view.loadProgress.connect(self.webLoading)
        self.file_Gpx.clicked.connect(self.select_track)
        self.pb_fixed_point.clicked.connect(self.fixed_point)
        self.pb_list_points.clicked.connect(self.list_points)
        
        self.loaded=0 # For control how many times Google Street give page loaded. Give 4 or 6 times
                
    def webLoading(self,event):
        if event==100: # For control how many times Google Street give page loaded. Give 4 or 6 times
            self.loaded+=1
            print(self.loaded)
            if self.loaded==6: # When give 6 loaded pages, then I grab the image
                height=self.web_view.page().contentsSize().height()
                width=self.web_view.page().contentsSize().width()
                self.web_view.grab(QRect(0,0,width,height)).save(self.file_name_jpg, b'JPG')
                self.loaded=0 # Restart Counter
    
    def fixed_point(self):
        # For test one fixed point
        page='https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=42.2410144284368,0.965725583955646&heading=186.20048419497363&pitch=0'
        url = QUrl(page)        
        self.file_name_jpg='c:/temp/jpg/Fixed_Point.jpg'
        self.web_view.page().load(url)
        
    def list_points(self):
        # For test a list of points
        points=[[42.2410144284368, 0.965725583955646], [42.2409574314952, 0.965720470994711], [42.240896243602, 0.965715777128935], [42.2408291045576, 0.965709993615747], [42.2407593671232, 0.965707311406732], [42.240682002157, 0.965706137940288], [42.2406058106571, 0.965705132111907], [42.2405290324241, 0.965702198445797], [42.2404542658478, 0.96570186316967], [42.2403736319393, 0.965694990009069], [42.2402878012508, 0.965689290314913], [42.2402046527714, 0.965683842077851], [42.2401182353497, 0.965680154040456], [42.2400250285864, 0.965676885098219], [42.2399284690619, 0.965670766308904], [42.239836268127, 0.965669928118587], [42.2397397086024, 0.965669760480523], [42.2396446578205, 0.965668670833111], [42.2395426500589, 0.965667329728603], [42.2394416481256, 0.965666742995381], [42.2393352817744, 0.965665988624096], [42.239225897938, 0.965662971138954], [42.2391161788255, 0.965662384405732], [42.2390032745898, 0.965660456568003], [42.238890286535, 0.965660959482193], [42.2387754544616, 0.965657522901893], [42.2386588621885, 0.965651236474514], [42.2385448683053, 0.965645201504231], [42.2384282760322, 0.965633047744632], [42.2383137792349, 0.965623240917921], [42.2381979413331, 0.965607399120927], [42.2380880545825, 0.965586444362998], [42.2379776649177, 0.965560711920261], [42.2378623299301, 0.965525507926941], [42.2377466596663, 0.965477395802736], [42.2376317437738, 0.965427858754992], [42.2375180851668, 0.965373795479536], [42.2374019119889, 0.965305985882878], [42.2372817993164, 0.965243289247155], [42.2371708229184, 0.965186543762684], [42.2370388079435, 0.965118734166026], [42.2369178570807, 0.965059977024794], [42.2367867641151, 0.965002393350005], [42.2366627119482, 0.964955454692245], [42.2365273442119, 0.964910443872213], [42.2363934852183, 0.96487800590694], [42.2362439520657, 0.964843891561031], [42.2360911499709, 0.964822098612785], [42.235938096419, 0.964814722537994], [42.2357739787549, 0.96481841057539], [42.2356092743576, 0.964833246544003], [42.2354392055422, 0.964861242100596], [42.235277434811, 0.96489611081779], [42.2350895125419, 0.964943552389741], [42.2349150013179, 0.964991580694914], [42.2347342036664, 0.965042291209102], [42.2345569264144, 0.965098952874541], [42.2343808226287, 0.965155195444822], [42.2342006955296, 0.965212108567357]]
        self.file_name='List_Of_Points____' # Name used to save capture in JPG file
        self.load_street_view(points)
        
    def select_track(self):
        # For test with a GPX or KML file
        #self.file_name_gpx='c:/temp/jpg/GRMN6969.gpx'
        self.file_name_gpx,_ = QFileDialog.getOpenFileName(self, 'Selecciona Fichero Track','c:/' , 'TRACK (*.gpx *.kml)')
        self.read_track(self.file_name_gpx)

    def read_track(self, file_name):
        # Read a KML or GPX and convert in a list of points
        from xml.dom import minidom

        if file_name:
            points=[]
            doc = minidom.parse(file_name)
            if file_name[-3:].upper()=='KML':
                coordinates = doc.getElementsByTagName('coordinates')[0].firstChild.data
                for element in coordinates.split():
                    lat=float(element.split(',')[0])
                    lon=float(element.split(',')[1])
                    points.append([lat,lon])
                    
            elif file_name[-3:].upper()=='GPX':
                track = doc.getElementsByTagName("trkpt")
                for punto in track:
                    lat=float(punto.getAttribute("lat"))
                    lon=float(punto.getAttribute("lon"))
                    points.append([lat,lon])
                    
            self.load_street_view(points)        
        
    def load_street_view(self, points):
        # Read a list of points and
        # Load a web page of Street view
        
        for position in range(len(points)):
            lat=points[position][0]
            lon=points[position][1]
            if not position==len(points)-1: # If it's the last point i can't calculate bearing
                bearing=self.cal_bearing(lat,lon,points[position+1][0],points[position+1][1])
                
            self.file_name_jpg=self.file_name[:-4]+"_"+str(position).zfill(5)+'.jpg'

            # For testing use. See terms of use of Google
            #page=('https://www.google.com/maps/@?api=1&map_action=pano&viewpoint={},{}&heading={}&pitch=0').format(str(lat),str(lon),bearing)
            
            # Using Google API
            page=('https://maps.googleapis.com/maps/api/streetview?size=600x300&location={},{}&heading={}&pitch=0&key=YOUR_API_KEY').format(str(lat),str(lon),bearing)    
            url = QUrl(page)   
            self.loaded=0 
            self.web_view.page().load(url)  
        
    def cal_bearing(self,lat1,lon1,lat2,lon2):
        """ Calculate bearing between two coordinates """
        from math import sin, cos, sqrt, atan2, radians, degrees 
        lat1 = radians(lat1) 
        lon1 = radians(lon1) 
        lat2 = radians(lat2) 
        lon2 = radians(lon2) 
        dLon = lon2 - lon1
        y = sin(dLon) * cos(lat2)
        x = cos(lat1)* sin(lat2) - sin(lat1)*cos(lat2)*cos(dLon)
        initial_bearing = atan2(x, y)
        # Now we have the initial bearing but math.atan2 return values
        # from -180° to + 180° which is not what we want for a compass bearing
        # The solution is to normalize the initial bearing as shown below
        initial_bearing = degrees(initial_bearing)
        compass_bearing = (initial_bearing + 360) % 360
        compass_bearing -=80 # I don't know why, but I have to correct with -80
        return compass_bearing
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = Video_Google_Street()
    win.show()
    sys.exit(app.exec_())
    

1 Answer 1

1

I find this "home made" solution:

from PyQt5.QtWidgets import QWidget, QApplication,QMainWindow,QFileDialog
from PyQt5.QtWidgets import  QVBoxLayout, QPushButton
from PyQt5.QtCore import QUrl,QRect

from PyQt5.QtWebEngineWidgets import QWebEngineView
import sys,os

# Por Hacer
# Rehacer ficheros no creados bien
# Crear GPX con el tiempo correcto para el DashCamm
# Poder establecer los Frames por segundo

class Video_Google_Street(QMainWindow):
    def __init__(self,file_name=None,dir_destiny=None):
        super().__init__()
        self.showMaximized()
        
        # Create GUI
        self.centralwidget = QWidget(self)
        self.layoutWidget = QWidget(self.centralwidget)
        self.horizontalLayout = QVBoxLayout(self.centralwidget)
        self.web_view=QWebEngineView(self.layoutWidget)
        self.file_Gpx = QPushButton(self.layoutWidget)
        self.file_Gpx.setText('GPX')
        self.pb_fixed_point = QPushButton(self.layoutWidget)
        self.pb_fixed_point.setText('Fixed Point')
        self.pb_list_points = QPushButton(self.layoutWidget)
        self.pb_list_points.setText('List of Points')
        
        self.setCentralWidget(self.centralwidget)
        self.horizontalLayout.addWidget(self.pb_list_points)
        self.horizontalLayout.addWidget(self.file_Gpx )
        #self.horizontalLayout.addWidget(self.pb_fixed_point)
        self.horizontalLayout.addWidget(self.web_view)
        
        # Create Slots
        # Slot when page is loaded
        self.web_view.loadProgress.connect(self.webLoading)
        self.file_Gpx.clicked.connect(self.select_track)
        self.pb_fixed_point.clicked.connect(self.fixed_point)
        self.pb_list_points.clicked.connect(self.list_points)
        
        self.loaded=0 # For control how many times Google Street give page loaded. Give 4 or 6 times
        self.position=-1 # For control position of point processed
        
        # If not exit directory destiny ask for.
        if not dir_destiny:
            self.dir_destiny=QFileDialog.getExistingDirectory(self, "Select Directory Destiny of Pictures")
        else:
            self.dir_destiny=dir_destiny
        
        # If file GPX exist, then start to create JPG
        if file_name:
            self.file_name_gpx=file_name
            self.read_track(self.file_name_gpx)
                
    def webLoading(self,event):
        # Called when page is loading - Event percentaje of loading
        
        if event==100: # For control how many times Google Street give page loaded. Give 4 or 6 times
            # Increase number of times is loaded
            self.loaded+=1
            print(self.loaded)
            # For me give 5 loaded pages when really image is created, then I grab the image
            if self.loaded==5: 
                # Get size of screen/widget to create JPG
                height=self.web_view.page().contentsSize().height()
                width=self.web_view.page().contentsSize().width()

                # Create secuential name of JPG
                self.file_name_jpg=self.dir_destiny+'/'+ os.path.basename(self.file_name_gpx).split('.')[0]+"_"+str(self.position).zfill(5)+'.jpg'
                
                # Capture Image
                self.web_view.grab(QRect(0,0,width,height)).save(self.file_name_jpg, b'JPG')
                
                self.loaded=0 # Restart Counter
                print(self.file_name_jpg)
                
                # If there is a list then continue calling more points
                if self.position>-1:
                    # If it is not the final point
                    if self.position<len(self.points):
                        # Save JPG with GPS Coordinates
                        self.save_exif(self.file_name_jpg,self.points[self.position][0],self.points[self.position][1])
                        # Call to load another Point
                        self.load_street_view() 
                        # Increase for next point
                        self.position+=1
                    else:
                        # Join JPG to create a Video
                        self.create_video()
    
    def fixed_point(self):
        # For test one fixed point
        page='https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=42.2410144284368,0.965725583955646&heading=186.20048419497363&pitch=0'
        url = QUrl(page)        
        self.file_name_jpg='c:/temp/jpg/Fixed_Point.jpg'
        self.web_view.page().load(url)
        
    def list_points(self):
        # For test a list of points
        points=[[42.2410144284368, 0.965725583955646], [42.2409574314952, 0.965720470994711], [42.240896243602, 0.965715777128935], [42.2408291045576, 0.965709993615747], [42.2407593671232, 0.965707311406732], [42.240682002157, 0.965706137940288], [42.2406058106571, 0.965705132111907], [42.2405290324241, 0.965702198445797], [42.2404542658478, 0.96570186316967], [42.2403736319393, 0.965694990009069], [42.2402878012508, 0.965689290314913], [42.2402046527714, 0.965683842077851], [42.2401182353497, 0.965680154040456], [42.2400250285864, 0.965676885098219], [42.2399284690619, 0.965670766308904], [42.239836268127, 0.965669928118587], [42.2397397086024, 0.965669760480523], [42.2396446578205, 0.965668670833111], [42.2395426500589, 0.965667329728603], [42.2394416481256, 0.965666742995381], [42.2393352817744, 0.965665988624096], [42.239225897938, 0.965662971138954], [42.2391161788255, 0.965662384405732], [42.2390032745898, 0.965660456568003], [42.238890286535, 0.965660959482193], [42.2387754544616, 0.965657522901893], [42.2386588621885, 0.965651236474514], [42.2385448683053, 0.965645201504231], [42.2384282760322, 0.965633047744632], [42.2383137792349, 0.965623240917921], [42.2381979413331, 0.965607399120927], [42.2380880545825, 0.965586444362998], [42.2379776649177, 0.965560711920261], [42.2378623299301, 0.965525507926941], [42.2377466596663, 0.965477395802736], [42.2376317437738, 0.965427858754992], [42.2375180851668, 0.965373795479536], [42.2374019119889, 0.965305985882878], [42.2372817993164, 0.965243289247155], [42.2371708229184, 0.965186543762684], [42.2370388079435, 0.965118734166026], [42.2369178570807, 0.965059977024794], [42.2367867641151, 0.965002393350005], [42.2366627119482, 0.964955454692245], [42.2365273442119, 0.964910443872213], [42.2363934852183, 0.96487800590694], [42.2362439520657, 0.964843891561031], [42.2360911499709, 0.964822098612785], [42.235938096419, 0.964814722537994], [42.2357739787549, 0.96481841057539], [42.2356092743576, 0.964833246544003], [42.2354392055422, 0.964861242100596], [42.235277434811, 0.96489611081779], [42.2350895125419, 0.964943552389741], [42.2349150013179, 0.964991580694914], [42.2347342036664, 0.965042291209102], [42.2345569264144, 0.965098952874541], [42.2343808226287, 0.965155195444822], [42.2342006955296, 0.965212108567357]]
        points=[[42.2410144284368, 0.965725583955646], [42.2409574314952, 0.965720470994711], [42.240896243602, 0.965715777128935], [42.2408291045576, 0.965709993615747], [42.2407593671232, 0.965707311406732], [42.240682002157, 0.965706137940288]]
        self.file_name_gpx='c:/temp/jpg/List_Of_Points' # Name used to save capture in JPG file
        self.load_street_view(points,first=True)
        
    def select_track(self):
        # For test with a GPX or KML file
        
        self.file_name_gpx,_ = QFileDialog.getOpenFileName(self, 'Selecciona Fichero Track','c:/' , 'TRACK (*.gpx *.kml)')
        self.read_track(self.file_name_gpx)

    def read_track(self, file_name):
        # Read a KML or GPX and convert in a list of points
        from xml.dom import minidom
        

        if self.file_name_gpx:
            points=[]
            doc = minidom.parse(self.file_name_gpx)
            if self.file_name_gpx[-3:].upper()=='KML':
                coordinates = doc.getElementsByTagName('coordinates')[0].firstChild.data
                for element in coordinates.split():
                    lat=float(element.split(',')[0])
                    lon=float(element.split(',')[1])
                    points.append([lat,lon])
                    
            elif self.file_name_gpx[-3:].upper()=='GPX':
                track = doc.getElementsByTagName("trkpt")
                for punto in track:
                    lat=float(punto.getAttribute("lat"))
                    lon=float(punto.getAttribute("lon"))
                    points.append([lat,lon])
                    
            self.load_street_view(points,first=True)        

    def load_street_view(self, points=None,first=False):
        # Read a list of points and
        # Load a web page of Street view
        
        if first:
            self.points=points
            self.position=0

        lat=self.points[self.position][0]
        lon=self.points[self.position][1]
        if not self.position==len(self.points)-1: # If it's the last point i can't calculate bearing
            self.bearing=self.cal_bearing(lat,lon,self.points[self.position+1][0],self.points[self.position+1][1])

        # Using Google API
        page=('https://maps.googleapis.com/maps/api/streetview?size=600x300&location={},{}&heading={}&pitch=0&key=YOUR_API_KEY').format(str(lat),str(lon),self.bearing)

        # For testing use. See terms of use of Google
        page=('https://www.google.com/maps/@?api=1&map_action=pano&viewpoint={},{}&heading={}&pitch=0').format(str(lat),str(lon),self.bearing)
        
        url = QUrl(page)   
        self.loaded=0 
        self.web_view.page().load(url)  
        
    def cal_bearing(self,lat1,lon1,lat2,lon2):
        """ Calculate bearing between two coordinates """
        from math import sin, cos, sqrt, atan2, radians, degrees 
        lat1 = radians(lat1) 
        lon1 = radians(lon1) 
        lat2 = radians(lat2) 
        lon2 = radians(lon2) 
        dLon = lon2 - lon1
        x = sin(dLon) * cos(lat2)
        y = cos(lat1)* sin(lat2) - sin(lat1)*cos(lat2)*cos(dLon)
        initial_bearing = atan2(x, y)
        # Now we have the initial bearing but math.atan2 return values
        # from -180° to + 180° which is not what we want for a compass bearing
        # The solution is to normalize the initial bearing as shown below
        initial_bearing = degrees(initial_bearing)
        compass_bearing = (initial_bearing + 360) % 360
        return compass_bearing

    def save_exif(self,fichero,lat,lon):
        # Save GPS data in JPG EXIF File
        from pyexiv2 import Image
        img = Image(fichero)
        exif_lat=str(int(lat))+"/1 "+str(int(lat % 1 * 60))+"/1 "+str(int(round((lat % 1 * 60)%1*60,4)*100))+"/100"
        exif_lon=str(int(lon))+"/1 "+str(int(lon % 1 * 60))+"/1 "+str(int(round((lon % 1 * 60)%1*60,4)*100))+"/100"
        if int(lat)<0:
            exif_lat_ref='S'
        else:
            exif_lat_ref='N'

        if int(lon)<0:
            exif_lon_ref='W'
        else:
            exif_lon_ref='E'

        description=''
        img.modify_exif({'Exif.Image.ImageDescription':description,'Exif.Image.Make':'Python Google Earth','Exif.GPSInfo.GPSLatitudeRef': exif_lat_ref,'Exif.GPSInfo.GPSLatitude': exif_lat,'Exif.GPSInfo.GPSLongitudeRef': exif_lon_ref, 'Exif.GPSInfo.GPSLongitude': exif_lon})

    def create_video(self):
        # Call to FFMPEG to join JPG and create a Video
        cwd=os.getcwd()
        os.chdir(self.dir_destiny)
        output_video=self.dir_destiny+'/'+ os.path.basename(self.file_name_gpx).split('.')[0]+'.MP4'       
        order='''ffmpeg -framerate 1/0.5 -i {}_%5d.jpg -vf "crop=trunc(iw/2)*2:trunc(ih/2)*2" {}'''.format(os.path.basename(self.file_name_gpx).split('.')[0],output_video)
        os.system(order)
        os.chdir(cwd)
        self.exit()

    def exit(self):
        self.close()
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    # Name of GPX File
    file_name_gpx='c:/temp/jpg/GRMN6969.gpx'
    file_name_gpx=None
    # Directory where create JPG
    dir_destiny='c:/temp/jpg/generated'
    win = Video_Google_Street(file_name_gpx,dir_destiny)
    win.show()
    sys.exit(app.exec_())
    

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.