[Merkaartor] geotagged images

Chris Browet cbro at semperpax.com
Wed Aug 20 10:51:28 BST 2008


Hi,

I tried your patch but had a SEGFAULT. I used a file I quickly geotagged
with Picassa, so I guess it must be problematic. I'm attaching it for
reference. I was using exiv2 0.17 on Gentoo.

Could you please put somewhere working track/jpg that I can test further?
I don't have any.

Some remarks already:

1) As a rule of thumb, features needing external libraries should be
optional, so please be sure to adapt the Merkaartor.pro accordingly (i.e. à
la OSMARENDER)
2) Merkaartor being a multi-platform project, please be sure that exiv2 is
available/compilable on windows under mingw; I don't know about Mac. Bart?
3) Would it be possible before the final patch to format the sources to
avoid the 1-line IFs? I somewhat find them unreadable.

Thanks already
- Chris -

2008/8/19 Timo Schlüßler <timo at schluessler.org>

>  For all who want to test my implementation, here is the patch. It's not
> ready at all but you can give it a try and send me bugs.
>
> You have to apply the patch and then add the files to the directory Map.
>
> regards
>
> Timo Schlüßler wrote:
>
> Hello,
>
> I think this would be a very great feature for merkaartor and I would
> like to implement it.
>
> But before starting with that, I want to ask you if you are (still)
> interested in a "geotagged images"-feature or if there's yet a plugin for
> merkaartor doing this.
>
> best regards,
> timo
>
>
> Hanno Böck wrote:
>
> Hi,
>
> I'm glad merkaartor seems to get better, as I don't really like josm (and I
> even less like potlach).
>
> However, there's one thing that I'm using for all my mapping activity and that
> is using geotagged images. That's the single reason I'm stuck with josm.
> I want a feature to show locations of jpeg images (by their exif gps tags) and
> show then on a click. josm has two plugins for that, I like the interface of
> AfPifoJ better than geotagged.
>
> Is this feature planned (or even still possible and I didn't find it)?
>
>
>
> ------------------------------
>
> _______________________________________________
> Merkaartor mailing listMerkaartor at openstreetmap.orghttp://lists.openstreetmap.org/cgi-bin/mailman/listinfo/merkaartor
>
>  ------------------------------
>
> _______________________________________________
> Merkaartor mailing listMerkaartor at openstreetmap.org
> http://lists.openstreetmap.org/listinfo/merkaartor
>
>
> Index: MainWindow.h
> ===================================================================
> --- MainWindow.h        (revision 9959)
> +++ MainWindow.h        (working copy)
> @@ -8,6 +8,7 @@
>  #include <QProgressBar>
>  #include <QLabel>
>
> +
>  class LayerDock;
>  class MapDocument;
>  class MapLayer;
> @@ -18,6 +19,7 @@
>  class DirtyDock;
>  class QGPS;
>  class FeaturePainter;
> +class GeoImage;
>
>  class MainWindow : public QMainWindow, public Ui::MainWindow
>  {
> Index: MainWindow.cpp
> ===================================================================
> --- MainWindow.cpp      (revision 9959)
> +++ MainWindow.cpp      (working copy)
> @@ -31,6 +31,7 @@
>  #include "Map/Road.h"
>  #include "Map/RoadManipulations.h"
>  #include "Map/TrackPoint.h"
> +#include "Map/GeoImage.h"
>  #include "PaintStyle/EditPaintStyle.h"
>  #include "PaintStyle/PaintStyleEditor.h"
>  #include "Sync/SyncOSM.h"
> @@ -391,6 +392,7 @@
>        "OpenStreetMap binary format (*.osb)\n" \
>        "Noni GPSPlot format (*.ngt)\n" \
>        "NMEA GPS log format (*.nmea *.nme)\n" \
> +       "Geotagged images (*.jpg)\n" \
>        "All Files (*)")
>  #define FILTER_IMPORT_SUPPORTED \
>        tr("Supported formats (*.gpx *.osm *.osb *.ngt *.nmea *.nme)\n" \
> @@ -538,7 +540,16 @@
>                return;
>
>        QStringList fileNames(fileList);
> +
> +       QStringList images = fileNames.filter(".jpg", Qt::CaseInsensitive);
> +       if (!images.isEmpty()) {
> +               GeoImage::loadImages(images, theDocument, theView);
> +               QString cur;
> +               foreach (cur, images) fileNames.removeAll(cur);
> +       }
>
> +       if (fileNames.isEmpty()) return;
> +
>        QApplication::setOverrideCursor(Qt::BusyCursor);
>        theLayers->setUpdatesEnabled(false);
>        view()->setUpdatesEnabled(false);
> @@ -549,6 +560,7 @@
>        while (it.hasNext())
>        {
>                const QString & fn = it.next();
> +
>                if (fn.endsWith(".mdc") == false)
>                        continue;
>
> Index: Merkaartor.pri
> ===================================================================
> --- Merkaartor.pri      (revision 9959)
> +++ Merkaartor.pri      (working copy)
> @@ -36,6 +36,7 @@
>  ./Map/Road.h \
>  ./Map/RoadManipulations.h \
>  ./Map/TrackPoint.h \
> +./Map/GeoImage.h \
>  ./Map/TrackSegment.h \
>  ./MapView.h \
>  ./PaintStyle/EditPaintStyle.h \
> @@ -84,6 +85,7 @@
>  ./Map/Road.cpp \
>  ./Map/RoadManipulations.cpp \
>  ./Map/TrackPoint.cpp \
> +./Map/GeoImage.cpp \
>  ./Map/TrackSegment.cpp \
>  ./MapView.cpp \
>  ./Interaction/CreateAreaInteraction.cpp \
> Index: Merkaartor.pro
> ===================================================================
> --- Merkaartor.pro      (revision 9959)
> +++ Merkaartor.pro      (working copy)
> @@ -109,3 +109,5 @@
>     }
>     QT += webkit
>  }
> +
> +LIBS += -lexiv2
> Index: Map/ImportGPX.cpp
> ===================================================================
> --- Map/ImportGPX.cpp   (revision 9959)
> +++ Map/ImportGPX.cpp   (working copy)
> @@ -88,6 +88,7 @@
>                if (!t.isNull() && t.tagName() == "trkpt")
>                {
>                        TrackPoint* Pt = importTrkPt(t,theDocument,
> theLayer, theList);
> +                       Pt->setParent(S);
>                        if (MakeSegment)
>                                S->add(Pt);
>                        progress.setValue(progress.value()+1);
> Index: Map/MapDocument.cpp
> ===================================================================
> --- Map/MapDocument.cpp (revision 9959)
> +++ Map/MapDocument.cpp (working copy)
> @@ -372,8 +372,8 @@
>        QVector<MapFeature*> theFeatures;
>        for (VisibleFeatureIterator i(this); !i.isEnd(); ++i) {
>                if (!layerType)
> -                       theFeatures.append(i.get());
> -               else
> +                       qDebug() << "append :)";
> +               else
>                        if (i.get()->layer()->className() == *layerType)
>                                theFeatures.append(i.get());
>        }
>
> #include "GeoImage.h"
>
> #include "Command/DocumentCommands.h"
>
> /*#include <QtGui/QStandardItemModel>
> #include <QtGui/QDialog>
> #include <QtGui/QListView>
> #include <QtGui/QDialogButtonBox>*/
> #include <QtGui/QInputDialog>
> #include <QtGui/QMessageBox>
>
> class TrackSegment;
>
> GeoImage::GeoImage(TrackPoint * Pt, QImage img)
> : TrackPoint(*Pt), Image(img)
> {
> }
>
> GeoImage::~GeoImage()
> {
> }
>
> void GeoImage::draw(QPainter& P, const Projection& theProjection)
> {
>
>        QPointF me = theProjection.project(position());
>        P.setPen(QPen(QColor(0, 0, 0), 2));
>        QRectF box(me - QPointF(10, 6), me + QPointF(10, 6));
>        P.drawRect(box);
>        bound = CoordBox(theProjection.inverse(box.topLeft()),
> theProjection.inverse(box.bottomRight()));
> }
> void GeoImage::drawFocus(QPainter& P, const Projection& theProjection)
> {
>        QPointF me = theProjection.project(position());
>        double aspect = (double)Image.height() / (double)Image.width();
>        QPointF middle(P.device()->width()*0.2, aspect *
> P.device()->width()*0.2);
>        P.drawImage(QRectF(me - middle, me + middle), Image);
> }
>
> /*CoordBox GeoImage::boundingBox(void)
> {
>        qDebug() << bound.topLeft().lat() << bound.topLeft().lon() <<
> bound.bottomRight().lat();
>        if (bound.isNull()) return TrackPoint::boundingBox();
>        else return bound;
> }*/
>
>
> void GeoImage::loadImages(QStringList fileNames, MapDocument *theDocument,
> MapView *theView)
> {
>        QString file;
>        QImage img;
>        QDateTime time;
>        bool foundTime;
>
>        Exiv2::Image::AutoPtr image;
>        Exiv2::ExifData *exifData;
>        Exiv2::ExifData::const_iterator i, end;
>
>        MapLayer *theLayer;
>        {
>                QStringList layers;
>                unsigned int i;
>                for (i=0;i<theDocument->layerSize();i++)
> layers.append(theDocument->getLayer(i)->name());
>
>                bool ok;
>                QString layer = QInputDialog::getItem(NULL,
> MainWindow::tr("Load geotagged Images"),
>                 MainWindow::tr("Select the layer to which the images
> belong:"), layers, 0, false, &ok);
>                if (ok && !layer.isEmpty())
>                        theLayer =
> theDocument->getLayer(layers.indexOf(layer));
>                else return;
>
>                /*
>                QDialog dlg;
>
>        QVBoxLayout layout(&dlg);
>           QLabel text("Select the layer the images belong to.");
>                QListView list(&dlg);
>                QStandardItemModel *model = new QStandardItemModel(&list);
>                list.setModel(model);
>                list.setSelectionMode(QAbstractItemView::SingleSelection);
>                QDialogButtonBox buttons(QDialogButtonBox::Ok |
> QDialogButtonBox::Cancel);
>                layout.addWidget(&text, 0);
>                layout.addWidget(&list, 1);
>                layout.addWidget(&buttons, 2, Qt::AlignRight);
>
>                dlg.setWindowTitle("Load geotagged Images");
>
>                unsigned int i;
>                qDebug() << theDocument->layerSize();
>                for (i=0;i<theDocument->layerSize();i++)
> model->appendRow(new QStandardItem(theDocument->getLayer(i)->name()));
>
>                dlg.connect(&buttons, SIGNAL(accepted()), &dlg,
> SLOT(accept()));
>                dlg.connect(&buttons, SIGNAL(rejected()), &dlg,
> SLOT(reject()));
>                if (dlg.exec() != QDialog::Accepted) return;
>
>                QModelIndexList selection =
> list.selectionModel()->selectedRows();
>                if (selection.size() != 1) return;
>                theLayer = theDocument->getLayer(selection.first().row());
>
>                delete model;
>                */
>        }
>
>        CommandList * theList = new CommandList(MainWindow::tr("Loading
> Images"));
>
>        bool ok;
>        int offset = QInputDialog::getInteger( NULL, MainWindow::tr("Set
> offset"),
>         MainWindow::tr("Specify an offset\n(positive values will position
> the images more to the end of the track):"),
>         0, 0, (int) ((unsigned int)-1 / 2), 1, &ok);
>        qDebug() << (unsigned int)-1 << (int)((unsigned int)-1 /2);
>        if (!ok) offset = 0;
>
>        foreach(file, fileNames) {
>                if (!img.load(file)) { if (warning(MainWindow::tr("Image
> broken"), MainWindow::tr("Cannot open image %1.").arg(file))) continue; else
> return;}
>                if (img.isNull()) continue;
>
>        image = Exiv2::ImageFactory::open(file.toAscii().constData());
>                if (image.get() == 0) { qWarning("Error with exiv2 in
> %s.\n", file.toAscii().constData()); continue; }
>                image->readMetadata();
>
>                exifData = &  image->exifData();
>                if (exifData->empty()) {
>                        qWarning("No exif data. Skipping.\n"); continue;
>                }
>                end = exifData->end();
>                foundTime = false;
>                for (i = exifData->begin(); i != end; i++) {
>                        if (i->key() == "Exif.Image.DateTime") {
>                                time =
> QDateTime::fromString(i->value().toString().c_str(), "yyyy:MM:dd hh:mm:ss");
>                                qDebug() << "time is" << time;
>                                foundTime = true;
>                        }
>                }
>                if (!foundTime) { if (warning(MainWindow::tr("No
> timestamp"), MainWindow::tr("Image doesn't provide any time information.")))
> continue; else return; }
>
>
>                time = time.addSecs(offset);
>
>                qDebug() << theLayer;
>                qDebug() << "theLayer" << theLayer->size();
>                MapFeature *feature = NULL, *bestFeature = NULL;
>                TrackPoint *Pt;
>                unsigned int secondsTo = (unsigned int)-1, a;
>                unsigned int u;
>                for (u=0; u<theLayer->size(); feature = theLayer->get(u++))
> {
>                        if (!feature) { qDebug() << "is this a bug???";
> continue; } // TODO
>                        if (!(Pt = dynamic_cast<TrackPoint*>(feature)))
> continue;
>                        a = time.secsTo(Pt->time());
>                        if (a < secondsTo) { secondsTo = a; bestFeature =
> feature; }
>                }
>                if (!bestFeature) { qDebug() << "no features found";
> continue; }
>
>                if (secondsTo >= 15)
>                        if ( QMessageBox::question(NULL,
> MainWindow::tr("Wrong image?"),
>                         MainWindow::tr("Image \"%1\" was taken %2 seconds
> before the next trackpoint was recorded.\n" \
>                         "Do you still want to use
> it?").arg(file).arg(secondsTo), QMessageBox::Yes | QMessageBox::No,
> QMessageBox::No ) != QMessageBox::Yes) continue;
>
>                TrackSegment *S;
>                int idx;
>                if (bestFeature->sizeParents() == 1 &&
>                 (Pt = dynamic_cast<TrackPoint*>(bestFeature)) &&
>                 (S =
> dynamic_cast<TrackSegment*>(bestFeature->getParent(0))) )
>                        idx = S->find(Pt);
>                else { qWarning() << "Can't access TrackPoint or it's parent
> or the parent is not a TrackSegment!\n"; continue; }
>
>                if (bestFeature->deleteChildren(theDocument, theList)) {
>                        std::vector<MapFeature*> Alternatives;
>                        theList->add(new RemoveFeatureCommand(theDocument,
> Pt, Alternatives));
>                }
>
>                GeoImage * GeoPoint = new GeoImage(Pt, QImage(file));
>                theList->add(new AddFeatureCommand(theLayer, GeoPoint,
> false));
>
>                S->add(GeoPoint, idx);
>        }
>        if (theList->size()) {
>                theDocument->addHistory(theList);
>                theView->invalidate(true, false);
>        }
>        else delete theList;
>
> }
>
> bool GeoImage::warning(QString title, QString message)
> {
>        if ( QMessageBox::warning ( NULL, title, message,
> QMessageBox::Ignore | QMessageBox::Cancel, QMessageBox::Ignore ) ==
> QMessageBox::Ignore) return true;
>        else return false;
> }
>
>
> #include "Map/TrackPoint.h"
> #include "Map/TrackSegment.h"
> #include "Map/Projection.h"
> #include "MapView.h"
> #include <QtGui/QPainter>
> #include <exiv2/image.hpp>
> #include <exiv2/exif.hpp>
> #include "TrackPoint.h"
>
> class GeoImage : public TrackPoint
> {
> public:
>        GeoImage(TrackPoint *Pt, QImage img);
>        virtual ~GeoImage();
>
>        static void loadImages(QStringList fileNames, MapDocument
> *theDocument, MapView *theView);
>
>        virtual void draw(QPainter& P, const Projection& theProjection);
>        virtual void drawFocus(QPainter& P, const Projection&
> theProjection);
>        //virtual CoordBox boundingBox(void);
>
> private:
>        QImage Image;
>        CoordBox bound;
>
>        static bool warning(QString title, QString message);
> };
>
>
> _______________________________________________
> Merkaartor mailing list
> Merkaartor at openstreetmap.org
> http://lists.openstreetmap.org/listinfo/merkaartor
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openstreetmap.org/pipermail/merkaartor/attachments/20080820/29791c00/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 103117.jpg
Type: image/jpeg
Size: 66927 bytes
Desc: not available
URL: <http://lists.openstreetmap.org/pipermail/merkaartor/attachments/20080820/29791c00/attachment.jpg>


More information about the Merkaartor mailing list