Changeset 5c1abde2 in subsurface


Ignore:
Timestamp:
Feb 6, 2014, 11:29:23 AM (4 years ago)
Author:
Dirk Hohndel <dirk@…>
Branches:
Mtest, android_test, dcDownload, forTomaz, foratdotde, gitMerge, gitdebug, gitpictures, ios, master, mergeKirigamiPort, pictimeshift, resolutionIndependence, spinner, testTomaz, testing, v4.2-branch, v4.4-branch, v4.5-branch
Children:
96e35e5
Parents:
c84ef31
git-author:
Robert C. Helling <helling@…> (01/27/14 05:44:26)
git-committer:
Dirk Hohndel <dirk@…> (02/06/14 11:29:23)
Message:

Include images in profile

This adds an entry to the dive list context menu to load images. The user
can select image files and set a time offset to align camera and dive
computer clocks.

Using the exif time stamp the images are tried to match to the times of
the selected dives (with a grace period of an hour before and after the
dive). Upon success an event of type 123 is created per image with the
string value being the path to the image. Those images are displayed as
thumbnails in the profile. If the matching dive does not yet have a geo
location specified but the image provides one it is copied to the dive
(making the camera a poor man's companion app).

This patch includes easyexif https://code.google.com/p/easyexif/ which is
originally under a New BSD License to parse the image meta data.

This commit includes a new test dive dives/test31.xml with a matching
image wreck.jpg to try out the functionallity.

Obvious to do's:
Have images on the map
Have the images clickable
Have a proper picture viewer
Give visual reference for image time shifting.
Use the new profile

Signed-off-by: Robert C. Helling <helling@…>
Signed-off-by: Dirk Hohndel <dirk@…>

Files:
5 added
9 edited

Legend:

Unmodified
Added
Removed
  • dive.h

    r1920093 r5c1abde2  
    615615        return dive;
    616616}
     617
     618#ifdef __cplusplus
     619extern "C" {
     620#endif
     621
    617622extern struct dive *find_dive_including(timestamp_t when);
    618623extern bool dive_within_time_range(struct dive *dive, timestamp_t when, timestamp_t offset);
     
    692697extern void set_autogroup(bool value);
    693698extern int total_weight(struct dive *);
     699
     700#ifdef __cplusplus
     701}
     702#endif
    694703
    695704#define DIVE_ERROR_PARSE 1
  • file.c

    r1920093 r5c1abde2  
    157157}
    158158
    159 static timestamp_t parse_date(const char *date)
     159timestamp_t parse_date(const char *date)
    160160{
    161161        int hour, min, sec;
  • file.h

    r4b12f28c r5c1abde2  
    1010extern int try_to_open_cochran(const char *filename, struct memblock *mem, GError **error);
    1111#endif
     12
     13#ifdef __cplusplus
     14extern "C" {
     15#endif
    1216extern int readfile(const char *filename, struct memblock *mem);
     17extern timestamp_t parse_date(const char *date);
     18#ifdef __cplusplus
     19}
     20#endif
    1321
    1422#endif
  • qt-ui/divelistview.cpp

    r6cc65f5e r5c1abde2  
    1111#include "subsurfacewebservices.h"
    1212#include "../display.h"
     13#include "exif.h"
     14#include "../file.h"
    1315#include <QApplication>
    1416#include <QHeaderView>
     
    2224#include <QMenu>
    2325#include <QFileDialog>
     26#include <string>
     27#include <iostream>
     28
    2429
    2530DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelection(false),
     
    736741                popup.addAction(tr("export As UDDF"), this, SLOT(exportSelectedDivesAsUDDF()));
    737742                popup.addAction(tr("shift times"), this, SLOT(shiftTimes()));
     743                popup.addAction(tr("load images"), this, SLOT(loadImages()));
    738744        }
    739745        if (d)
     
    795801}
    796802
     803void DiveListView::loadImages()
     804{
     805        struct memblock mem;
     806        EXIFInfo exif;
     807        int code;
     808        time_t imagetime;
     809        QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open Image Files"), lastUsedImageDir(), tr("Image Files (*.jpg *.jpeg *.pnm *.tif *.tiff)"));
     810
     811        if (fileNames.isEmpty())
     812                return;
     813
     814        updateLastUsedImageDir(QFileInfo(fileNames[0]).dir().path());
     815
     816        ShiftImageTimesDialog* shiftDialog = ShiftImageTimesDialog::instance();
     817        shiftDialog->exec();
     818
     819        for (int i = 0; i < fileNames.size(); ++i) {
     820                struct tm tm;
     821                int year, month, day, hour, min, sec;
     822                readfile(fileNames.at(i).toUtf8().data(), &mem);
     823                code = exif.parseFrom((const unsigned char *) mem.buffer, (unsigned) mem.size);
     824                free(mem.buffer);
     825                sscanf(exif.DateTime.c_str(), "%d:%d:%d %d:%d:%d", &year, &month, &day, &hour, &min, &sec);
     826                tm.tm_year = year;
     827                tm.tm_mon = month - 1;
     828                tm.tm_mday = day;
     829                tm.tm_hour = hour;
     830                tm.tm_min = min;
     831                tm.tm_sec = sec;
     832                imagetime = utc_mktime(&tm) + shiftDialog->amount;
     833                int j = 0;
     834                struct dive *dive;
     835                for_each_dive(j, dive){
     836                        if (!dive->selected)
     837                                continue;
     838                        if (dive->when - 3600 < imagetime && dive->when + dive->duration.seconds + 3600 > imagetime){
     839                                if (dive->when > imagetime) {
     840                                        ;  // Before dive
     841                                        add_event(&(dive->dc), 0, 123, 0, 0, fileNames.at(i).toUtf8().data());
     842                                        mainWindow()->refreshDisplay();
     843                                        mark_divelist_changed(true);
     844                                }
     845                                else if (dive->when + dive->duration.seconds < imagetime){
     846                                        ;  // After dive
     847                                        add_event(&(dive->dc), dive->duration.seconds, 123, 0, 0, fileNames.at(i).toUtf8().data());
     848                                        mainWindow()->refreshDisplay();
     849                                        mark_divelist_changed(true);
     850                                }
     851                                else {
     852                                        add_event(&(dive->dc), imagetime - dive->when, 123, 0, 0, fileNames.at(i).toUtf8().data());
     853                                        mainWindow()->refreshDisplay();
     854                                        mark_divelist_changed(true);
     855                                }
     856                                if (!dive->latitude.udeg && !IS_FP_SAME(exif.GeoLocation.Latitude, 0.0)){
     857                                        dive->latitude.udeg = lrint(1000000.0 * exif.GeoLocation.Latitude);
     858                                        dive->longitude.udeg = lrint(1000000.0 * exif.GeoLocation.Longitude);
     859                                        mark_divelist_changed(true);
     860                                        mainWindow()->refreshDisplay();
     861                                }
     862                        }
     863                }
     864        }
     865}
     866
    797867void DiveListView::uploadToDivelogsDE()
    798868{
    799869        DivelogsDeWebServices::instance()->prepareDivesForUpload();
    800870}
     871
     872QString DiveListView::lastUsedImageDir()
     873{
     874        QSettings settings;
     875        QString lastImageDir = QDir::homePath();
     876
     877        settings.beginGroup("FileDialog");
     878        if (settings.contains("LastImageDir"))
     879                if (QDir::setCurrent(settings.value("LastImageDir").toString()))
     880                        lastImageDir = settings.value("LastIamgeDir").toString();
     881        return lastImageDir;
     882}
     883
     884void DiveListView::updateLastUsedImageDir(const QString& dir)
     885{
     886        QSettings s;
     887        s.beginGroup("FileDialog");
     888        s.setValue("LastImageDir", dir);
     889}
  • qt-ui/divelistview.h

    r58697125 r5c1abde2  
    5050        void exportSelectedDivesAsUDDF();
    5151        void shiftTimes();
     52        void loadImages();
    5253        void uploadToDivelogsDE();
    5354
     
    7273        int lastVisibleColumn();
    7374        void selectTrip ( dive_trip_t* trip );
     75        QString lastUsedImageDir();
     76        void updateLastUsedImageDir(const QString& s);
    7477};
    7578
  • qt-ui/profilegraphics.cpp

    rc1ed9ba r5c1abde2  
    10191019        scene()->addItem(item);
    10201020
     1021        if (ev->type == 123){
     1022                QPixmap picture;
     1023                picture.load(ev->name);
     1024                scene()->addPixmap(picture.scaledToHeight(100, Qt::SmoothTransformation))->setPos(x, y + 10);
     1025        }
     1026
     1027
    10211028        /* we display the event on screen - so translate (with the correct context for events) */
    10221029        QString name = gettextFromC::instance()->tr(ev->name);
  • qt-ui/simplewidgets.cpp

    ra27f67c r5c1abde2  
    157157        connect(ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*)));
    158158}
     159ShiftImageTimesDialog* ShiftImageTimesDialog::instance()
     160{
     161        static ShiftImageTimesDialog* self = new ShiftImageTimesDialog(mainWindow());
     162        return self;
     163}
     164
     165void ShiftImageTimesDialog::buttonClicked(QAbstractButton* button)
     166{
     167        if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) {
     168                amount = ui.timeEdit->time().hour() * 3600 + ui.timeEdit->time().minute() * 60;
     169                if (ui.backwards->isChecked())
     170                        amount *= -1;
     171        }
     172}
     173
     174ShiftImageTimesDialog::ShiftImageTimesDialog(QWidget *parent): QDialog(parent)
     175{
     176        ui.setupUi(this);
     177        connect(ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*)));
     178}
    159179
    160180bool isGnome3Session()
  • qt-ui/simplewidgets.h

    rb22f1da r5c1abde2  
    1010#include "ui_renumber.h"
    1111#include "ui_shifttimes.h"
     12#include "ui_shiftimagetimes.h"
    1213
    1314class MinMaxAvgWidget : public QWidget{
     
    5556};
    5657
     58class ShiftImageTimesDialog : public QDialog {
     59        Q_OBJECT
     60public:
     61        static ShiftImageTimesDialog *instance();
     62        int amount;
     63private slots:
     64        void buttonClicked(QAbstractButton *button);
     65private:
     66        explicit ShiftImageTimesDialog(QWidget *parent);
     67        Ui::ShiftImageTimesDialog ui;
     68};
     69
    5770bool isGnome3Session();
    5871
  • subsurface.pro

    rc1ed9ba r5c1abde2  
    5555        qt-ui/subsurfacewebservices.h \
    5656        qt-ui/tableview.h \
     57        qt-ui/exif.h \
    5758        sha1.h \
    5859        statistics.h \
     
    115116        qt-ui/subsurfacewebservices.cpp \
    116117        qt-ui/tableview.cpp \
     118        qt-ui/exif.cpp \
    117119        save-xml.c \
    118120        sha1.c \
     
    154156        qt-ui/renumber.ui \
    155157        qt-ui/shifttimes.ui \
     158        qt-ui/shiftimagetimes.ui \
    156159        qt-ui/webservices.ui \
    157160        qt-ui/tableview.ui \
Note: See TracChangeset for help on using the changeset viewer.