generate_kml.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 import os
00003 import signal
00004 import sys
00005 import math 
00006 import threading
00007 import time
00008 import string
00009 
00010 import rospy
00011 import roslib; roslib.load_manifest('kml_util')
00012 import rosbag
00013 import tf
00014 from nav_msgs.msg import *
00015 
00016 from pykml.factory import nsmap
00017 from pykml.factory import KML_ElementMaker as KML
00018 from pykml.factory import GX_ElementMaker as GX
00019 from pykml.parser import Schema
00020 from lxml import etree
00021 
00022 import utm
00023 
00024 #-------------------------------------------------------------------------------
00025 #GLOBAL VARS
00026 
00027 
00028 #===============================================================================
00029 
00030 class KMLTourGenerator(object):
00031 
00032     #---------------------------------------------------------------------------
00033     def __init__(self, poseTopicName, utmZoneNumber):
00034         rospy.loginfo("[KMLTourGenerator] init()")
00035         self.prevLinVel = 0.0
00036         self.prevAngVel = 0.0
00037 
00038         self.hasFinished = False
00039 
00040         self.hasOdom = False
00041         self.currOdom = None
00042         self.prevOdom = None
00043         self.subPose = rospy.Subscriber(poseTopicName, Odometry, self.poseHandler)
00044 
00045         self.utmZoneNumber = utmZoneNumber
00046         self.utmZoneLetter = 'U'
00047         self.zOffset = 2.0
00048 
00049         self.planCoordListStr = ''
00050         self.execCoordListStr = ''
00051         
00052         self.totalTime = 0.0
00053         
00054         # define a variable for the Google Extensions namespace URL string
00055         self.gxns = '{' + nsmap['gx'] + '}'
00056 
00057         # start with a base KML tour and playlist
00058 
00059         self.anim_fld = KML.Folder(
00060                     KML.name('Animations'),
00061                     id='animations',
00062                 )
00063         
00064         self.path_fld = KML.Folder(
00065                     KML.name('Paths'),
00066                     id='paths',
00067                 )
00068 
00069         self.tour_doc = KML.kml(
00070             KML.Document(
00071                 KML.Style(
00072                     KML.IconStyle(
00073                         KML.scale(1.2),
00074                         KML.Icon(
00075                             KML.href("http://maps.google.com/mapfiles/kml/shapes/shaded_dot.png")
00076                         ),
00077                     ),
00078                     id='sn_shaded_dot',
00079                 ),
00080                 GX.Tour(
00081                     KML.name("Play me!"),
00082                     GX.Playlist(),
00083                 ),
00084                 self.anim_fld,
00085                 self.path_fld
00086             )
00087         )
00088 
00089 
00090     #---------------------------------------------------------------------------
00091     #use odometer information to compute some metrics
00092     def poseHandler(self, odom):
00093         self.hasOdom = True
00094         if (self.prevOdom == None):
00095             self.prevOdom = odom
00096         self.currOdom = odom
00097 
00098 
00099     #---------------------------------------------------------------------------
00100     def finish(self, kmlFilename):
00101         rospy.loginfo("[KMLTourGenerator] finish()")
00102         if (self.hasFinished):
00103             return
00104 
00105         self.path_fld.append(KML.Placemark(
00106             KML.name('plan-path'),
00107             KML.extrude("1"),
00108             KML.tessellate("1"),
00109             KML.altitudeMode("absolute"),
00110             KML.Style(
00111                 KML.LineStyle(
00112                     KML.color('7FFF0000'),
00113                     KML.width(5)
00114                 ),
00115                 KML.PolyStyle(
00116                     KML.color('7FFFFF00')
00117                 )
00118             ),
00119             KML.LineString(
00120                 KML.tessellate("1"),
00121                 KML.altitudeMode("absolute"),
00122                 KML.coordinates(self.planCoordListStr)
00123             )
00124         ))
00125 
00126         self.path_fld.append(KML.Placemark(
00127             KML.name('exec-path'),
00128             KML.altitudeMode("absolute"),
00129             KML.Style(
00130                 KML.LineStyle(
00131                     KML.color('7F0000FF'),
00132                     KML.width(5)
00133                 ),
00134                 KML.PolyStyle(
00135                     KML.color('7FFFFFFF')
00136                 )
00137             ),
00138             KML.LineString(
00139                 KML.extrude("1"),
00140                 KML.tessellate("1"),
00141                 KML.altitudeMode("absolute"),
00142                 KML.coordinates(self.execCoordListStr)
00143             )
00144         ))
00145         
00146         # check that the KML document is valid using the Google Extension XML Schema
00147         #assert(Schema("kml22gx.xsd").validate(self.tour_doc))
00148 
00149         #print etree.tostring(self.tour_doc, pretty_print=True)
00150 
00151         # output a KML file (named based on the Python script)
00152         outfile = file(kmlFilename,'w')
00153         outfile.write(etree.tostring(self.tour_doc, pretty_print=True))            
00154         self.hasFinished = True
00155 
00156 
00157     #---------------------------------------------------------------------------
00158     def update(self):                
00159 
00160         if (self.hasOdom):
00161             #----------------------------------
00162             dt = self.currOdom.header.stamp.to_sec() - self.prevOdom.header.stamp.to_sec()
00163 
00164             dx = self.currOdom.pose.pose.position.x - self.prevOdom.pose.pose.position.x
00165             dy = self.currOdom.pose.pose.position.y - self.prevOdom.pose.pose.position.y
00166             dz = self.currOdom.pose.pose.position.z - self.prevOdom.pose.pose.position.z
00167 
00168             d = math.sqrt(dx*dx + dy*dy + dz*dz)
00169 
00170             #v = d/dt
00171             #----------------------------------
00172             if ((dt > 10.0) or
00173                 ((dt > 1.0) and (d > 10.0))):
00174                 latLongPos = self.convertToLatLong(self.currOdom)
00175                 self.kml_addPathPoint(latLongPos, dt)
00176                 #self.kml_addAnimPoint(latLongPos, self.currOdom.header.stamp.to_sec())
00177                 self.totalTime = self.totalTime + dt
00178                 self.prevOdom = self.currOdom
00179                 rospy.loginfo("Total time = {0}".format(self.totalTime))
00180 
00181             self.hasOdom = False
00182 
00183     #---------------------------------------------------------------------------
00184     def convertToLatLong(self, odom):
00185         utm_north = odom.pose.pose.position.y
00186         utm_east  = odom.pose.pose.position.x
00187 
00188         rospy.loginfo("UTM: {0} N / {1} E".format(utm_north, utm_east))
00189         latlon = utm.to_latlon(utm_north, utm_east, self.utmZoneNumber, self.utmZoneLetter)
00190         #rospy.logerr(latlon)
00191                         
00192         latitude  = latlon[0]
00193         longitude = latlon[1]
00194         altitude  = -odom.pose.pose.position.z + self.zOffset
00195             
00196         ori = odom.pose.pose.orientation
00197         euler = tf.transformations.euler_from_quaternion(([ori.x, ori.y, ori.z, ori.w]))
00198         #rospy.logerr(euler)
00199         return (latitude, longitude, altitude, euler[0]*(180.0/math.pi), euler[1]*(180.0/math.pi), euler[2]*(180.0/math.pi))
00200 
00201 
00202     #---------------------------------------------------------------------------
00203     def kml_addPathPoint(self, pos, dt):
00204         #pos[0] : latitude
00205         #pos[1] : longitude
00206         #pos[2] : altitude
00207         #pos[3] : roll
00208         #pos[4] : pitch
00209         #pos[5] : yaw
00210         if (pos == None):
00211             return
00212         #rospy.logerr(pos)
00213         
00214         self.tour_doc.Document[self.gxns+"Tour"].Playlist.append(
00215             GX.FlyTo(
00216                 GX.duration(dt),
00217                 GX.flyToMode("smooth"),
00218                 #KML.Camera(
00219                 KML.LookAt(
00220                     KML.latitude(pos[0]),
00221                     KML.longitude(pos[1]),
00222                     KML.altitude(pos[2]),
00223                     KML.heading(pos[5]),
00224                     #KML.tilt(pos[4] + 90.0),
00225                     #KML.roll(pos[3]),
00226                     KML.tilt(pos[4] + 75.0),
00227                     KML.range(20.0),
00228                     KML.altitudeMode("absolute"),
00229                 ),
00230             ),
00231         )
00232         
00233         auxStr = ' {lon},{lat},{alt}\n'.format(lon=pos[1], lat=pos[0], alt=pos[2])
00234         #rospy.logerr(auxStr)
00235         self.execCoordListStr = self.execCoordListStr + auxStr
00236 
00237 
00238     def kml_addAnimPoint(self, pos, tm):
00239         #pos[0] : latitude
00240         #pos[1] : longitude
00241         #pos[2] : altitude
00242         #pos[3] : roll
00243         #pos[4] : pitch
00244         #pos[5] : yaw
00245         if (pos == None):
00246             return
00247         
00248         localTimeStr = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.localtime(tm))
00249         #rospy.logerr(localTimeStr)
00250         self.anim_fld.append(KML.Placemark(
00251             KML.TimeStamp(
00252                 KML.when(localTimeStr)
00253             ),
00254             KML.altitudeMode("absolute"),
00255             KML.styleUrl('#sn_shaded_dot'),
00256             KML.LookAt(
00257                 KML.longitude(pos[1]),
00258                 KML.latitude(pos[0]),
00259                 KML.altitude(pos[2]),
00260                 KML.heading(pos[5]),
00261                 KML.tilt(0.0),
00262                 KML.range(100.0),
00263                 KML.altitudeMode("absolute"),
00264             ),
00265             KML.Point(
00266                 KML.coordinates("{lon},{lat},{alt}".format(lon=pos[1], lat=pos[0], alt=pos[2]))
00267             )
00268         ))        
00269 
00270 
00271 #-------------------------------------------------------------------------------
00272 #-------------------------------------------------------------------------------
00273 
00274 
00275 if __name__ == '__main__':
00276     try:        
00277         rospy.init_node('kml_tour_generator')
00278 
00279         kmlTourGen = None
00280 
00281         #-----------------------------------------------------------------------
00282         kmlOutputFilename = rospy.get_param('/generate_kml/output_filename')
00283 
00284         #-----------------------------------------------------------------------
00285         #Topic name 
00286         pose_topic = rospy.get_param('/generate_kml/pose_topic')
00287         #UTM zone number used for coordinate transformation
00288         utmZoneNumber = rospy.get_param('/generate_kml/utm_zone_number')
00289 
00290         #-----------------------------------------------------------------------
00291         #Get pose information and build KML file
00292         kmlTourGen = KMLTourGenerator(pose_topic, utmZoneNumber)
00293 
00294         r = rospy.Rate(5.0)
00295         while (not rospy.is_shutdown()):
00296             kmlTourGen.update()
00297             r.sleep()        
00298                 
00299         kmlTourGen.finish(kmlOutputFilename)
00300 
00301     except rospy.ROSInterruptException:
00302         if (kmlTourGen != None):
00303             kmlTourGen.finish(kmlOutputFilename)
00304         pass
00305 
00306 


kml_util
Author(s):
autogenerated on Thu Jun 6 2019 21:45:09