Exemple #1
0
def main():
    # Parse the options
    parser = ArgumentParser()
    parser.add_argument("args", metavar="PHOTO", nargs='*', help='photos to be processed')
    parser.add_argument("-g", "--gps", dest="gps", required=True,
                      help="The input GPS track file in .gpx format", metavar="FILE")
    parser.add_argument("-p", "--photos", dest="photos",
                      help="The directory of photos", metavar="DIR")
    # MPickering added next option; this offset is added to the JPG values (which don't have
    # native timezone information)
    parser.add_argument("-t", "--timediff", dest="timediff", type=int, default=0,
                      help="Add this number of hours to the JPEG times")
    parser.add_argument("-o", "--output", dest="output",
                      help="The output filename for the GPX file", metavar="FILE")
    parser.add_argument("-u", "--update-photos", action="store_true",
                      dest="updatephotos", help="Update the photos with GPS information")
    parser.add_argument("-v", "--verbose",
                      action="store_true", dest="verbose")  # not used; could be useful
    parser.add_argument("-i", "--interpolate", action="store_true", dest="interpolate",
                      help="interpolate coordinates linearily between closest track points")
    parser.add_argument("--threshold", dest="threshold", type=int, default=5*60,
                      help="threshold in seconds that a track point may differ from a photos timestamp still allowing them to get associated; set to -1 to allow arbitrary threshold.")
    options = parser.parse_args()
    args = options.args

    if options.threshold==-1: options.threshold = float("inf")

    # Load and Parse the GPX file to retrieve all the trackpoints
    xmldoc = minidom.parse(options.gps)
    gpx = xmldoc.getElementsByTagName("gpx")
    # get all trackpoints, irrespective of their track
    trackpointElements = gpx[0].getElementsByTagName("trkpt")

    photos = []
    trackpoints = []

    # Iterate over the trackpoints; put them in a list sorted by time
    for pt in trackpointElements:
        timeElement = pt.getElementsByTagName("time")
        time = ""
        if timeElement:
            timeString = timeElement[0].firstChild.data
            # times are in xsd:dateTime:  <time>2006-12-20T15:01:06Z</time>
            time = mktime(strptime(timeString[0:len(timeString)-1], "%Y-%m-%dT%H:%M:%S"))
            trackpoint = Trackpoint()
            trackpoint.lat = float(pt.attributes["lat"].value)
            trackpoint.lon = float(pt.attributes["lon"].value)
            trackpoint.ele = float(pt.getElementsByTagName("ele")[0].firstChild.data)
            trackpoint.time = time
            trackpoints.append(trackpoint)
    trackpoints.sort(key=lambda obj:obj.time)

    # prepare the list of photos
    if options.photos:
        photolist = filter(lambda x: os.path.isfile(x) and               \
          os.path.splitext(x)[1].lower() == '.jpg',                      \
          [os.path.join(options.photos, photo) for photo in os.listdir(options.photos)])
    else:
        photolist = args
    photolist.sort()

    for file in photolist:
        photo = Photo()
        photo.filename = file
        photo.shortfilename = os.path.split(file)[1]
        # Parse the EXIF data and find the closest matching trackpoint
        tags = getExif(photo)
        try:
            photo.time = mktime(strptime(bytes.decode(tags[b'Image timestamp']), "%Y:%m:%d %H:%M:%S"))
            # account for time difference (GPX uses UTC; EXIF uses local time)
            photo.time += options.timediff * 3600
            photo.trackpoint = findNearestTrackpoint(trackpoints, photo.time, options.interpolate, options.threshold)
            if photo.trackpoint:
                photos.append(photo)
        except:
            # picture may have been unreadable, may not have had timestamp, etc.
            print(photo.filename, traceback.format_exc())


    # ready to output the photo listing
    impl = getDOMImplementation()
    gpxdoc = impl.createDocument(None, "gpx", None)
    doc_element = gpxdoc.documentElement

    # <gpx> (top-level) element attributes
    doc_element.setAttribute("version", "1.0")
    doc_element.setAttribute("creator", "gpspoint_to_gpx.py")
    doc_element.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
    doc_element.setAttribute("xmlns", "http://www.topografix.com/GPX/1/0")
    doc_element.setAttribute("xsi:schemaLocation",   \
      "http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd")

    # <gpx> contains <time> element
    time_element = gpxdoc.createElement("time")
    time_element.appendChild(gpxdoc.createTextNode(   \
                                strftime("%Y-%m-%dT%H:%M:%SZ", gmtime())))
    doc_element.appendChild(time_element)

    # <bounds> element needs to go next; we'll fill in the attributes later
    bounds_element = gpxdoc.createElement("bounds")
    doc_element.appendChild(bounds_element)

    # header is complete; now need to iterate over the photomap dictionary
    # and add waypoints
    minlat = 90.0
    maxlat = -90.0
    minlon = 180.0
    maxlon = -180.0

    for photo in photos:
        lat = photo.trackpoint.lat
        lon = photo.trackpoint.lon
        ele = photo.trackpoint.ele

        # track minimum and maximum for the <bounds> element
        if (lat < minlat):
            minlat = lat
        if (lat > maxlat):
            maxlat = lat
        if (lon < minlon):
            minlon = lon
        if (lon > maxlon):
            maxlon = lon

        wpt_element = gpxdoc.createElement("wpt")
        doc_element.appendChild(wpt_element)

        wpt_element.setAttribute("lat", str(lat))
        wpt_element.setAttribute("lon", str(lon))

        ele_element = gpxdoc.createElement("ele")
        wpt_element.appendChild(ele_element)
        ele_element.appendChild(gpxdoc.createTextNode(str(ele)))

        name_element = gpxdoc.createElement("name")
        wpt_element.appendChild(name_element)
        name_element.appendChild(gpxdoc.createTextNode(photo.shortfilename))

        cmt_element = gpxdoc.createElement("cmt")
        wpt_element.appendChild(cmt_element)
        cmt_element.appendChild(gpxdoc.createTextNode(photo.shortfilename))

        # use filename as the description
        # we could check the photo comment, if it exists, and use that...
        desc_element = gpxdoc.createElement("desc")
        wpt_element.appendChild(desc_element)
        desc_element.appendChild(gpxdoc.createTextNode(photo.shortfilename))

        # now, assemble and execute the exiv2 command
        if options.updatephotos:
            setExif(photo);

    # finish the bounds element
    bounds_element.setAttribute("minlat", str(minlat))
    bounds_element.setAttribute("minlon", str(minlon))
    bounds_element.setAttribute("maxlat", str(maxlat))
    bounds_element.setAttribute("maxlon", str(maxlon))

    # dump the document
    if options.output:
        outfile = open(options.output, "w")
    else:
        outfile = sys.stdout

    try:
        PrettyPrint(gpxdoc, outfile)
    except:
        outfile.write(gpxdoc.toprettyxml("  "))

    outfile.close()