00001
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
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
00055 self.gxns = '{' + nsmap['gx'] + '}'
00056
00057
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
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
00147
00148
00149
00150
00151
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
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
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
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
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
00205
00206
00207
00208
00209
00210 if (pos == None):
00211 return
00212
00213
00214 self.tour_doc.Document[self.gxns+"Tour"].Playlist.append(
00215 GX.FlyTo(
00216 GX.duration(dt),
00217 GX.flyToMode("smooth"),
00218
00219 KML.LookAt(
00220 KML.latitude(pos[0]),
00221 KML.longitude(pos[1]),
00222 KML.altitude(pos[2]),
00223 KML.heading(pos[5]),
00224
00225
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
00235 self.execCoordListStr = self.execCoordListStr + auxStr
00236
00237
00238 def kml_addAnimPoint(self, pos, tm):
00239
00240
00241
00242
00243
00244
00245 if (pos == None):
00246 return
00247
00248 localTimeStr = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.localtime(tm))
00249
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
00286 pose_topic = rospy.get_param('/generate_kml/pose_topic')
00287
00288 utmZoneNumber = rospy.get_param('/generate_kml/utm_zone_number')
00289
00290
00291
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