diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-19 18:22:05 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-19 18:22:05 +0000 |
commit | 57e10fedbcb8c3e8c6590ff0935dbf017ce5587f (patch) | |
tree | 3000a7649ca4e40e43f9e7feed963236a0b0f56b /kipi-plugins/gpssync | |
download | kipi-plugins-57e10fedbcb8c3e8c6590ff0935dbf017ce5587f.tar.gz kipi-plugins-57e10fedbcb8c3e8c6590ff0935dbf017ce5587f.zip |
Import abandoned KDE3 version of kipi plugins
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/libraries/kipi-plugins@1077221 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kipi-plugins/gpssync')
27 files changed, 4728 insertions, 0 deletions
diff --git a/kipi-plugins/gpssync/Makefile.am b/kipi-plugins/gpssync/Makefile.am new file mode 100644 index 0000000..c5fcc64 --- /dev/null +++ b/kipi-plugins/gpssync/Makefile.am @@ -0,0 +1,35 @@ +INCLUDES = $(KIPI_PLUGINS_COMMON_INCLUDE) $(LIBKEXIV2_CFLAGS) $(LIBKIPI_CFLAGS) $(all_includes) + +METASOURCES = AUTO + +# Install this plugin in the KDE modules directory +kde_module_LTLIBRARIES = kipiplugin_gpssync.la + +kipiplugin_gpssync_la_DEPENDENCIES = $(LIBKIPI_LIBS_DEP) $(LIBKEXIV2_LIBS_DEP) + +# Srcs for the plugin +kipiplugin_gpssync_la_SOURCES = plugin_gpssync.cpp gpssyncdialog.cpp gpslistviewitem.cpp \ + gpsbabelbinary.cpp gpsdataparser.cpp gpseditdialog.cpp \ + gpsmapwidget.cpp kmlexport.cpp kmlexportconfig.cpp \ + kmlgpsdataparser.cpp + +# Libs needed by the plugin +kipiplugin_gpssync_la_LIBADD = $(LIBKEXIV2_LIBS) -lkipiplugins $(LIBKIPI_LIBS) $(LIB_KHTML) \ + $(LIB_KPARTS) $(LIB_KIO) $(LIB_KDEUI) $(LIB_KDECORE) $(LIB_QT) \ + $(LIB_KFILE) + +# LD flags for the plugin +kipiplugin_gpssync_la_LDFLAGS = $(KIPI_PLUGINS_COMMON_LDFLAGS) -module $(KDE_PLUGIN) $(all_libraries) + +# Install the desktop file needed to detect the plugin +kde_services_DATA = kipiplugin_gpssync.desktop + +# Icons set for the plugin. +kipiplugin_gpssyncicondir = $(kde_datadir)/kipiplugin_gpssync/icons +kipiplugin_gpssyncicon_ICON = AUTO + + +# i18n translation messages +messages: rc.cpp + $(XGETTEXT) *.cpp *.h -o $(podir)/kipiplugin_gpssync.pot + diff --git a/kipi-plugins/gpssync/getlonlat.php b/kipi-plugins/gpssync/getlonlat.php new file mode 100644 index 0000000..da76386 --- /dev/null +++ b/kipi-plugins/gpssync/getlonlat.php @@ -0,0 +1,186 @@ +<?php +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-22 + * Description : a php script to show GPS locator world map + * this script is used by GPSSync kipi-plugin. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * Notes : This script use Google Map API version 2: + * http://www.google.com/apis/maps/documentation + * This script must be copied to host kipi-plugins + * web project page. + * This script accept these values from url: + * - 'altitude' : picture altitude. + * - 'longitude' : picture longitude. + * - 'width' : width of map. + * - 'height' : height of map. + * - 'zoom' : map zoom level. + * - 'maptype' : type of map (G_NORMAL_MAP, G_SATELLITE_MAP, G_HYBRID_MAP) + * - 'filename' : photo file name as string. + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ +?> +<!DOCTYPE html + PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<title>GPSSync Kipi-plugin Geographical Location Editor</title> +<script src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAAy_Vv5rc03ctmYvwfsuTH6RSK29CRGKrdb78LNYpP1_riKtR3zRRxy4unyuWAi2vp7m1isLwuHObXDg" +type="text/javascript"> +</script> +<style type="text/css"> + @import url("http://www.google.com/uds/css/gsearch.css"); + @import url("http://www.google.com/uds/solutions/localsearch/gmlocalsearch.css"); +</style> +<script src="http://www.google.com/uds/api?file=uds.js&v=1.0" type="text/javascript"></script> +<script src="http://www.google.com/uds/solutions/localsearch/gmlocalsearch.js" type="text/javascript"></script> + +<style type="text/css"> + /*<![CDATA[*/ + body { + padding: 0px; + margin: 0px; + } + /*]]>*/ +</style> + +<script type="text/javascript"> + +//<![CDATA[ +function loadMap() +{ + var map = new GMap2(document.getElementById("map")); + var searchoptions = { + suppressInitialResultSelection : true + }; + + var markeroptions = { + autoPan : true, + draggable : true, +<?php + $filename = $_GET['filename']; + if ($filename != "") echo "title : \"$filename\""; +?> + }; + + map.addControl(new GLargeMapControl()); + map.addControl(new GMapTypeControl()); + map.addControl(new GScaleControl()); + map.addControl(new google.maps.LocalSearch(searchoptions), new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(10,20))); + +<?php + $maptype = $_GET['maptype']; + if ($maptype == "") $maptype = "G_NORMAL_MAP"; + + echo "map.setCenter(new GLatLng("; + echo $_GET['latitude']; + echo ", "; + echo $_GET['longitude']; + echo "), "; + echo $_GET['zoom']; + echo ", "; + echo $maptype; + echo ");\n"; + + echo "var marker = new GMarker(new GLatLng("; + echo $_GET['latitude']; + echo ", "; + echo $_GET['longitude']; + echo "), markeroptions"; + echo ");\n"; + + echo "map.addOverlay(marker)"; +?> + + GEvent.addListener(map, "click", + function(overlay, point) + { + if (point) + { + marker.setPoint(point); + msg = "(lat:" + point.lat() + ", lon:" + point.lng() + ")"; + window.status=msg; + } + } + ); + + GEvent.addListener(marker, "drag", + function() + { + var point = marker.getPoint(); + msg = "(lat:" + point.lat() + ", lon:" + point.lng() + ")"; + window.status=msg; + } + ); + + GEvent.addListener(marker, "dragend", + function() + { + var point = marker.getPoint(); + msg = "(lat:" + point.lat() + ", lon:" + point.lng() + ")"; + window.status=msg; + } + ); + + GEvent.addListener(map, "zoomend", + function(oldLevel, newLevel) + { + msg = "newZoomLevel:" + newLevel; + window.status=msg; + } + ); + + GEvent.addListener(map, "maptypechanged", + function() + { + var myMapType = map.getCurrentMapType(); + if (myMapType == G_SATELLITE_TYPE) {msg = "newMapType:G_SATELLITE_TYPE";} + if (myMapType == G_MAP_TYPE) {msg = "newMapType:G_MAP_TYPE";} + if (myMapType == G_HYBRID_TYPE) {msg = "newMapType:G_HYBRID_TYPE";} + window.status=msg; + } + ); +} +{ + window.addEventListener("load", + function() + { + loadMap(); // Firefox and standard browsers + } + , false); +} +//]]> + +</script> +</head> + +<body onLoad="loadMap()"> + +<?php + echo "<div id=\"map\" "; + echo "style=\"width: "; + echo $_GET['width']; + echo "px; height: "; + echo $_GET['height']; + echo "px;\">"; +?> + +</div> +</body> +</html> diff --git a/kipi-plugins/gpssync/getlonlatalt.php b/kipi-plugins/gpssync/getlonlatalt.php new file mode 100644 index 0000000..91f2bac --- /dev/null +++ b/kipi-plugins/gpssync/getlonlatalt.php @@ -0,0 +1,206 @@ +<?php +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-22 + * Description : a php script to show GPS locator world map + * this script is used by GPSSync kipi-plugin. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * Copyright 2008 by Gerhard Kulzer <gerhard at kulzer dot net> + * + * Notes : This script use Google Map API version 2: + * http://www.google.com/apis/maps/documentation + * This script must be copied to host kipi-plugins + * web project page. + * This script accept these values from url: + * - 'altitude' : picture altitude. + * - 'longitude' : picture longitude. + * - 'width' : width of map. + * - 'height' : height of map. + * - 'zoom' : map zoom level. + * - 'maptype' : type of map (G_NORMAL_MAP, G_SATELLITE_MAP, G_HYBRID_MAP) + * - 'filename' : photo file name as string. + + * Notes on the service topocoding : + * here is how topoGetAltitude behaves: + * 1. You call *topoGetAltitude*( lat, lon, action, context, timeout ) + * 2. As soon as the server sends back the altitude, the asynchronous call + * to action(altitude,context) is performed. + * Here you can operate with the altitude information. And the context + * variable contains any useful data that you also want to pass in, + * it can be for example the reference to an element where you want the altitude to be assigned. + * So for example you can pass the marker as a context. + * Note that you can ommit the context if you don't need it. + * 3. If the server response does not arrive (timeout), the asynchronous + * call to action(null,context) is performed. + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +?> +<!DOCTYPE html + PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<title>GPSSync Kipi-plugin Geographical Location Editor</title> +<script src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAAy_Vv5rc03ctmYvwfsuTH6RSK29CRGKrdb78LNYpP1_riKtR3zRRxy4unyuWAi2vp7m1isLwuHObXDg" +type="text/javascript"> +</script> +<script src="http://www.google.com/uds/api?file=uds.js&v=1.0" type="text/javascript"></script> +<script src="http://www.google.com/uds/solutions/localsearch/gmlocalsearch.js" type="text/javascript"></script> +<script type="text/javascript" src="http://topocoding.com/api/getapi_v1.php?key=ILOGFVOBCUOSRHC"></script> +<style type="text/css"> + @import url("http://www.google.com/uds/css/gsearch.css"); + @import url("http://www.google.com/uds/solutions/localsearch/gmlocalsearch.css"); + + /*<![CDATA[*/ + body { + padding: 0px; + margin: 0px; + } + /*]]>*/ +</style> + +<script type="text/javascript"> + +//<![CDATA[ + +function loadMap() +{ + var map = new GMap2(document.getElementById("map")); + var searchoptions = { + suppressInitialResultSelection : true + }; + + var markeroptions = { + autoPan : true, + draggable : true, +<?php +$topoKey = 'COMBBKMQQYCKMMK'; +include( 'topocoding.inc' ); + + // Gets data from URL parameters + $filename = $_GET['filename']; + if ($filename != "") echo "title : \"$filename\""; + +?> + }; + + map.addControl(new GLargeMapControl()); + map.addControl(new GMapTypeControl()); + map.addControl(new GScaleControl()); + map.addControl(new google.maps.LocalSearch(searchoptions), new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(10,20))); + +<?php + $maptype = $_GET['maptype']; + if ($maptype == "") $maptype = "G_NORMAL_MAP"; + + echo "map.setCenter(new GLatLng("; + echo $_GET['latitude']; + echo ", "; + echo $_GET['longitude']; + echo "), "; + echo $_GET['zoom']; + echo ", "; + echo $maptype; + echo ");\n"; + + echo "var marker = new GMarker(new GLatLng("; + echo $_GET['latitude']; + echo ", "; + echo $_GET['longitude']; + echo "), markeroptions"; + echo ");\n"; + echo "map.addOverlay(marker)"; +?> + + GEvent.addListener(map, "click", + function(overlay, point) + { + if (point) + { + marker.setPoint(point); + topoGetAltitude( point.lat(), point.lng(), function( altitude ) { window.status = "(lat:" + point.lat() + ", lon:" + point.lng() + ", alt:" + altitude + ")" ; } ); + } + } + ); + + GEvent.addListener(marker, "drag", + function() + { + var point = marker.getPoint(); + topoGetAltitude( point.lat(), point.lng(), function( altitude ) { window.status = "(lat:" + point.lat() + ", lon:" + point.lng() + ", alt:" + altitude + ")" ; } ); + } + ); + + GEvent.addListener(marker, "dragend", + function() + { + var point = marker.getPoint(); + topoGetAltitude( point.lat(), point.lng(), function( altitude ) { window.status = "(lat:" + point.lat() + ", lon:" + point.lng() + ", alt:" + altitude + ")" ; } ); + } + ); + + GEvent.addListener(map, "zoomend", + function(oldLevel, newLevel) + { + msg = "newZoomLevel:" + newLevel; + window.status=msg; + } + ); + + GEvent.addListener(map, "maptypechanged", + function() + { + var myMapType = map.getCurrentMapType(); + if (myMapType == G_SATELLITE_TYPE) {msg = "newMapType:G_SATELLITE_TYPE";} + if (myMapType == G_MAP_TYPE) {msg = "newMapType:G_MAP_TYPE";} + if (myMapType == G_HYBRID_TYPE) {msg = "newMapType:G_HYBRID_TYPE";} + window.status=msg; + } + ); +} +{ + window.addEventListener("load", + function() + { + loadMap(); // Firefox and standard browsers + } + , false); +} +//]]> + +</script> +</head> + +<body onLoad="loadMap()"> +<div> +<?php +// print_r ( topoGetAltitudes( array( array( 'latitude', 'longitude' ) ) ) ); +?> +</div> +<?php + echo "<div id=\"map\" "; + echo "style=\"width: "; + echo $_GET['width']; + echo "px; height: "; + echo $_GET['height']; + echo "px;\">"; +?> + +</div> +</body> +</html> diff --git a/kipi-plugins/gpssync/gpsbabelbinary.cpp b/kipi-plugins/gpssync/gpsbabelbinary.cpp new file mode 100644 index 0000000..5dd6ab2 --- /dev/null +++ b/kipi-plugins/gpssync/gpsbabelbinary.cpp @@ -0,0 +1,125 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-19 + * Description : Autodetect gpsbabel binary program and version + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// KDE includes + +#include <kapplication.h> +#include <kprocess.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kglobal.h> +#include <kdebug.h> + +// Local includes + +#include "gpsbabelbinary.h" +#include "gpsbabelbinary.moc" + +namespace KIPIGPSSyncPlugin +{ + +class GPSBabelBinaryPriv +{ +public: + + GPSBabelBinaryPriv() + { + available = false; + version = QString::null; + } + + bool available; + + QString version; +}; + +GPSBabelBinary::GPSBabelBinary() + : QObject() +{ + d = new GPSBabelBinaryPriv; + checkSystem(); +} + +GPSBabelBinary::~GPSBabelBinary() +{ + delete d; +} + +void GPSBabelBinary::checkSystem() +{ + KProcess process; + process.clearArguments(); + process << path() << "-V"; + + connect(&process, SIGNAL(receivedStdout(KProcess *, char*, int)), + this, SLOT(slotReadStdoutFromGPSBabel(KProcess*, char*, int))); + + d->available = process.start(KProcess::Block, KProcess::Stdout); +} + +void GPSBabelBinary::slotReadStdoutFromGPSBabel(KProcess*, char* buffer, int buflen) +{ + // The gpsbabel output look like this : GPSBabel Version 1.2.5 + QString headerStarts("GPSBabel Version "); + + QString stdOut = QString::fromLocal8Bit(buffer, buflen); + QString firstLine = stdOut.section('\n', 1, 1); + + if (firstLine.startsWith(headerStarts)) + { + d->version = firstLine.remove(0, headerStarts.length()); + kdDebug( 51001 ) << "Found gpsbabel version: " << version() << endl; + } +} + +const char *GPSBabelBinary::path() +{ + return "gpsbabel"; +} + +bool GPSBabelBinary::isAvailable() const +{ + return d->available; +} + +QString GPSBabelBinary::version() const +{ + return d->version; +} + +bool GPSBabelBinary::versionIsRight() const +{ + if (d->version.isNull() || !isAvailable()) + return false; + + if (d->version.toFloat() >= minimalVersion().toFloat()) + return true; + + return false; +} + +QString GPSBabelBinary::minimalVersion() const +{ + return QString("1.2.5"); +} + +} // namespace KIPIGPSSyncPlugin diff --git a/kipi-plugins/gpssync/gpsbabelbinary.h b/kipi-plugins/gpssync/gpsbabelbinary.h new file mode 100644 index 0000000..3dc95ab --- /dev/null +++ b/kipi-plugins/gpssync/gpsbabelbinary.h @@ -0,0 +1,66 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-19 + * Description : Autodetect gpsbabel binary program and version + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef GPSBABELBINARY_H +#define GPSBABELBINARY_H + +// Qt includes. + +#include <qstring.h> +#include <qobject.h> + +class KProcess; + +namespace KIPIGPSSyncPlugin +{ + +class GPSBabelBinaryPriv; + +class GPSBabelBinary : public QObject +{ + Q_OBJECT + +public: + + GPSBabelBinary(); + ~GPSBabelBinary(); + + static const char *path(); + bool isAvailable() const; + QString version() const; + bool versionIsRight() const; + QString minimalVersion() const; + + void checkSystem(); + +private slots: + + void slotReadStdoutFromGPSBabel(KProcess*, char*, int); + +private: + + GPSBabelBinaryPriv *d; +}; + +} // namespace KIPIGPSSyncPlugin + +#endif // GPSBABELBINARY_H diff --git a/kipi-plugins/gpssync/gpsdatacontainer.h b/kipi-plugins/gpssync/gpsdatacontainer.h new file mode 100644 index 0000000..2413950 --- /dev/null +++ b/kipi-plugins/gpssync/gpsdatacontainer.h @@ -0,0 +1,78 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-19 + * Description : GPS data container. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef GPSDATACONTAINER_H +#define GPSDATACONTAINER_H + +// Qt includes. + +namespace KIPIGPSSyncPlugin +{ + +class GPSDataContainer +{ +public: + + GPSDataContainer(): m_interpolated(false), m_altitude(0.0), + m_latitude(0.0), m_longitude(0.0) + {}; + + GPSDataContainer(double altitude, double latitude, + double longitude, bool interpolated) + : m_interpolated(interpolated), m_altitude(altitude), + m_latitude(latitude), m_longitude(longitude) + {}; + + ~GPSDataContainer() + {}; + + GPSDataContainer& operator=(const GPSDataContainer& data) + { + m_interpolated = data.isInterpolated(); + m_altitude = data.altitude(); + m_latitude = data.latitude(); + m_longitude = data.longitude(); + return *this; + }; + + void setInterpolated(bool ite) { m_interpolated = ite; }; + void setAltitude(double alt) { m_altitude = alt; }; + void setLatitude(double lat) { m_latitude = lat; }; + void setLongitude(double lng) { m_longitude = lng; }; + + bool isInterpolated() const { return m_interpolated; }; + double altitude() const { return m_altitude; }; + double latitude() const { return m_latitude; }; + double longitude() const { return m_longitude; }; + +private: + + bool m_interpolated; + + double m_altitude; + double m_latitude; + double m_longitude; +}; + +} // NameSpace KIPIGPSSyncPlugin + +#endif // GPSDATACONTAINER_H diff --git a/kipi-plugins/gpssync/gpsdataparser.cpp b/kipi-plugins/gpssync/gpsdataparser.cpp new file mode 100644 index 0000000..2124f1d --- /dev/null +++ b/kipi-plugins/gpssync/gpsdataparser.cpp @@ -0,0 +1,275 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-19 + * Description : GPS data file parser. + * (GPX format http://www.topografix.com/gpx.asp). + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// C++ includes. + +#include <cmath> +#include <cstdlib> + +// Qt includes. + +#include <qstring.h> +#include <qstringlist.h> +#include <qfile.h> +#include <qdom.h> +#include <qtextstream.h> + +// KDE includes. + +#include <kdebug.h> + +// Local includes. + +#include "gpsdataparser.h" + +namespace KIPIGPSSyncPlugin +{ + +GPSDataParser::GPSDataParser() +{ + clear(); +} + +void GPSDataParser::clear() +{ + m_GPSDataMap.clear(); +} + +int GPSDataParser::numPoints() +{ + return m_GPSDataMap.count(); +} + +bool GPSDataParser::matchDate(const QDateTime& photoDateTime, int maxGapTime, int timeZone, + bool interpolate, int interpolationDstTime, + GPSDataContainer& gpsData) +{ + // GPS device are sync in time by satelite using GMT time. + // If the camera time is different than GMT time, we need to convert it to GMT time + // Using the time zone. + QDateTime cameraGMTDateTime = photoDateTime.addSecs(timeZone*(-1)); + + kdDebug() << "cameraGMTDateTime: " << cameraGMTDateTime << endl; + + // We trying to find the right date in the GPS points list. + bool findItem = false; + int nbSecItem = maxGapTime; + int nbSecs; + + for (GPSDataMap::Iterator it = m_GPSDataMap.begin(); + it != m_GPSDataMap.end(); ++it ) + { + // Here we check a possible accuracy in seconds between the + // Camera GMT time and the GPS device GMT time. + + nbSecs = abs(cameraGMTDateTime.secsTo( it.key() )); + + // We tring to find the minimal accuracy. + if( nbSecs < maxGapTime && nbSecs < nbSecItem) + { + gpsData = m_GPSDataMap[it.key()]; + findItem = true; + nbSecItem = nbSecs; + } + } + + if (findItem) return true; + + // If we can't find it, we will trying to interpolate the GPS point. + + if (interpolate) + { + // The interpolate GPS point will be separate by at the maximum of 'interpolationDstTime' + // seconds before and after the next and previous real GPS point found. + + QDateTime prevDateTime = findPrevDate(cameraGMTDateTime, interpolationDstTime); + QDateTime nextDateTime = findNextDate(cameraGMTDateTime, interpolationDstTime); + + if (!nextDateTime.isNull() && !prevDateTime.isNull()) + { + GPSDataContainer prevGPSPoint = m_GPSDataMap[prevDateTime]; + GPSDataContainer nextGPSPoint = m_GPSDataMap[nextDateTime]; + + double alt1 = prevGPSPoint.altitude(); + double lon1 = prevGPSPoint.longitude(); + double lat1 = prevGPSPoint.latitude(); + uint t1 = prevDateTime.toTime_t(); + double alt2 = nextGPSPoint.altitude(); + double lon2 = nextGPSPoint.longitude(); + double lat2 = nextGPSPoint.latitude(); + uint t2 = nextDateTime.toTime_t(); + uint tCor = cameraGMTDateTime.toTime_t(); + + if (tCor-t1 != 0) + { + gpsData.setAltitude(alt1 + (alt2-alt1) * (tCor-t1)/(t2-t1)); + gpsData.setLatitude(lat1 + (lat2-lat1) * (tCor-t1)/(t2-t1)); + gpsData.setLongitude(lon1 + (lon2-lon1) * (tCor-t1)/(t2-t1)); + gpsData.setInterpolated(true); + return true; + } + } + } + + return false; +} + +QDateTime GPSDataParser::findNextDate(const QDateTime& dateTime, int secs) +{ + // We will find the item in GPS data list where the time is + // at the maximum bigger than 'secs' mn of the value to match. + QDateTime itemFound = dateTime.addSecs(secs); + bool found = false; + + for (GPSDataMap::Iterator it = m_GPSDataMap.begin(); + it != m_GPSDataMap.end(); ++it ) + { + if (it.key() > dateTime) + { + if (it.key() < itemFound) + { + itemFound = it.key(); + found = true; + } + } + } + + if (found) + return itemFound; + + return QDateTime(); +} + +QDateTime GPSDataParser::findPrevDate(const QDateTime& dateTime, int secs) +{ + // We will find the item in GPS data list where the time is + // at the maximum smaller than 'secs' mn of the value to match. + QDateTime itemFound = dateTime.addSecs((-1)*secs); + bool found = false; + + for (GPSDataMap::Iterator it = m_GPSDataMap.begin(); + it != m_GPSDataMap.end(); ++it ) + { + if (it.key() < dateTime) + { + if (it.key() > itemFound) + { + itemFound = it.key(); + found = true; + } + } + } + + if (found) + return itemFound; + + return QDateTime(); +} + +bool GPSDataParser::loadGPXFile(const KURL& url) +{ + QFile gpxfile(url.path()); + + if (!gpxfile.open(IO_ReadOnly)) + return false; + + QDomDocument gpxDoc("gpx"); + if (!gpxDoc.setContent(&gpxfile)) + return false; + + QDomElement gpxDocElem = gpxDoc.documentElement(); + if (gpxDocElem.tagName()!="gpx") + return false; + + for (QDomNode nTrk = gpxDocElem.firstChild(); + !nTrk.isNull(); nTrk = nTrk.nextSibling()) + { + QDomElement trkElem = nTrk.toElement(); + if (trkElem.isNull()) continue; + if (trkElem.tagName() != "trk") continue; + + for (QDomNode nTrkseg = trkElem.firstChild(); + !nTrkseg.isNull(); nTrkseg = nTrkseg.nextSibling()) + { + QDomElement trksegElem = nTrkseg.toElement(); + if (trksegElem.isNull()) continue; + if (trksegElem.tagName() != "trkseg") continue; + + for (QDomNode nTrkpt = trksegElem.firstChild(); + !nTrkpt.isNull(); nTrkpt = nTrkpt.nextSibling()) + { + QDomElement trkptElem = nTrkpt.toElement(); + if (trkptElem.isNull()) continue; + if (trkptElem.tagName() != "trkpt") continue; + + QDateTime ptDateTime; + double ptAltitude = 0.0; + double ptLatitude = 0.0; + double ptLongitude = 0.0; + + // Get GPS position. If not available continue to next point. + QString lat = trkptElem.attribute("lat"); + QString lon = trkptElem.attribute("lon"); + if (lat.isEmpty() || lon.isEmpty()) continue; + + ptLatitude = lat.toDouble(); + ptLongitude = lon.toDouble(); + + // Get metadata of track point (altitude and time stamp) + for (QDomNode nTrkptMeta = trkptElem.firstChild(); + !nTrkptMeta.isNull(); nTrkptMeta = nTrkptMeta.nextSibling()) + { + QDomElement trkptMetaElem = nTrkptMeta.toElement(); + if (trkptMetaElem.isNull()) continue; + if (trkptMetaElem.tagName() == QString("time")) + { + // Get GPS point time stamp. If not available continue to next point. + QString time = trkptMetaElem.text(); + if (time.isEmpty()) continue; + ptDateTime = QDateTime::fromString(time, Qt::ISODate); + } + if (trkptMetaElem.tagName() == QString("ele")) + { + // Get GPS point altitude. If not available continue to next point. + QString ele = trkptMetaElem.text(); + if (!ele.isEmpty()) + ptAltitude = ele.toDouble(); + } + } + + if (ptDateTime.isNull()) + continue; + + GPSDataContainer gpsData(ptAltitude, ptLatitude, ptLongitude, false); + m_GPSDataMap.insert( ptDateTime, gpsData ); + } + } + } + + kdDebug( 51001 ) << "GPX File " << url.fileName() + << " parsed with " << numPoints() + << " points extracted" << endl; + return true; +} + +} // NameSpace KIPIGPSSyncPlugin diff --git a/kipi-plugins/gpssync/gpsdataparser.h b/kipi-plugins/gpssync/gpsdataparser.h new file mode 100644 index 0000000..121a97e --- /dev/null +++ b/kipi-plugins/gpssync/gpsdataparser.h @@ -0,0 +1,74 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-19 + * Description : GPS data file parser. + * (GPX format http://www.topografix.com/gpx.asp). + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef GPSDATAPARSER_H +#define GPSDATAPARSER_H + +// Qt includes. + +#include <qdatetime.h> +#include <qmap.h> + +// KDE includes. + +#include <kurl.h> + +// Local includes. + +#include "gpsdatacontainer.h" + +namespace KIPIGPSSyncPlugin +{ + +class GPSDataParser +{ + +public: + + GPSDataParser(); + ~GPSDataParser(){}; + + bool loadGPXFile(const KURL& url); + + void clear(); + int numPoints(); + bool matchDate(const QDateTime& photoDateTime, int maxGapTime, int timeZone, + bool interpolate, int interpolationDstTime, + GPSDataContainer& gpsData); + +private: + + // Methods used to perform interpolation. + QDateTime findNextDate(const QDateTime& dateTime, int secs); + QDateTime findPrevDate(const QDateTime& dateTime, int secs); + +protected: + + typedef QMap<QDateTime, GPSDataContainer> GPSDataMap; + + GPSDataMap m_GPSDataMap; +}; + +} // NameSpace KIPIGPSSyncPlugin + +#endif // GPSDATAPARSER_H diff --git a/kipi-plugins/gpssync/gpseditdialog.cpp b/kipi-plugins/gpssync/gpseditdialog.cpp new file mode 100644 index 0000000..032aecd --- /dev/null +++ b/kipi-plugins/gpssync/gpseditdialog.cpp @@ -0,0 +1,339 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-22 + * Description : a dialog to edit GPS positions + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// Qt includes. + +#include <qtimer.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qpushbutton.h> +#include <qvalidator.h> + +// KDE includes. + +#include <klocale.h> +#include <khelpmenu.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <kapplication.h> +#include <klineedit.h> +#include <kmessagebox.h> +#include <khtmlview.h> +#include <kpopupmenu.h> + +// Local includes. + +#include "kpaboutdata.h" +#include "pluginsversion.h" +#include "gpsmapwidget.h" +#include "gpseditdialog.h" +#include "gpseditdialog.moc" + +namespace KIPIGPSSyncPlugin +{ + +class GPSEditDialogDialogPrivate +{ + +public: + + GPSEditDialogDialogPrivate() + { + altitudeInput = 0; + latitudeInput = 0; + longitudeInput = 0; + worldMap = 0; + about = 0; + goButton = 0; + hasGPSInfo = false; + } + + bool hasGPSInfo; + + QPushButton *goButton; + + KLineEdit *altitudeInput; + KLineEdit *latitudeInput; + KLineEdit *longitudeInput; + + KIPIPlugins::KPAboutData *about; + + GPSDataContainer gpsData; + + GPSMapWidget *worldMap; +}; + +GPSEditDialog::GPSEditDialog(QWidget* parent, const GPSDataContainer& gpsData, + const QString& fileName, bool hasGPSInfo) + : KDialogBase(Plain, i18n("%1 - Edit Geographical Coordinates").arg(fileName), + Help|Ok|Cancel, Ok, + parent, 0, true, false) +{ + d = new GPSEditDialogDialogPrivate; + d->hasGPSInfo = hasGPSInfo; + d->gpsData = gpsData; + + QGridLayout* grid = new QGridLayout(plainPage(), 8, 3, 0, spacingHint()); + + QLabel *message = new QLabel(i18n("<p>Use the map on the right to select the location where " + "the picture have been taken. Click with left mouse button or move the marker " + "on the map to get the GPS coordinates.<p>"), plainPage()); + + QLabel *altitudeLabel = new QLabel(i18n("Altitude:"), plainPage()); + QLabel *latitudeLabel = new QLabel(i18n("Latitude:"), plainPage()); + QLabel *longitudeLabel = new QLabel(i18n("Longitude:"), plainPage()); + + d->altitudeInput = new KLineEdit(plainPage()); + d->latitudeInput = new KLineEdit(plainPage()); + d->longitudeInput = new KLineEdit(plainPage()); + + QPushButton *altResetButton = new QPushButton(SmallIcon("clear_left"), QString::null, plainPage()); + QPushButton *latResetButton = new QPushButton(SmallIcon("clear_left"), QString::null, plainPage()); + QPushButton *lonResetButton = new QPushButton(SmallIcon("clear_left"), QString::null, plainPage()); + + d->altitudeInput->setValidator(new QDoubleValidator(-20000.0, 20000.0, 1, this)); + d->latitudeInput->setValidator(new QDoubleValidator(-90.0, 90.0, 12, this)); + d->longitudeInput->setValidator(new QDoubleValidator(-180.0, 180.0, 12, this)); + + d->goButton = new QPushButton(i18n("Goto Location"), plainPage()); + d->goButton->setEnabled(false); + + d->worldMap = new GPSMapWidget(plainPage()); + d->worldMap->setFileName(fileName); + d->worldMap->show(); + + grid->addMultiCellWidget(message, 0, 0, 0, 2); + grid->addMultiCellWidget(altitudeLabel, 1, 1, 0, 2); + grid->addMultiCellWidget(d->altitudeInput, 2, 2, 0, 1); + grid->addMultiCellWidget(altResetButton, 2, 2, 2, 2); + grid->addMultiCellWidget(latitudeLabel, 3, 3, 0, 2); + grid->addMultiCellWidget(d->latitudeInput, 4, 4, 0, 1); + grid->addMultiCellWidget(latResetButton, 4, 4, 2, 2); + grid->addMultiCellWidget(longitudeLabel, 5, 5, 0, 2); + grid->addMultiCellWidget(d->longitudeInput, 6, 6, 0, 1); + grid->addMultiCellWidget(lonResetButton, 6, 6, 2, 2); + grid->addMultiCellWidget(d->goButton, 7, 7, 0, 1); + grid->addMultiCellWidget(d->worldMap->view(), 0, 8, 3, 3); + grid->setColStretch(0, 3); + grid->setColStretch(3, 10); + grid->setRowStretch(8, 10); + + // --------------------------------------------------------------- + // About data and help button. + + d->about = new KIPIPlugins::KPAboutData(I18N_NOOP("GPS Sync"), + 0, + KAboutData::License_GPL, + I18N_NOOP("A Plugin to synchronize pictures metadata with a GPS device"), + "(c) 2006-2008, Gilles Caulier"); + + d->about->addAuthor("Gilles Caulier", I18N_NOOP("Author and Maintainer"), + "caulier dot gilles at gmail dot com"); + + KHelpMenu* helpMenu = new KHelpMenu(this, d->about, false); + helpMenu->menu()->removeItemAt(0); + helpMenu->menu()->insertItem(i18n("Plugin Handbook"), + this, SLOT(slotHelp()), 0, -1, 0); + actionButton(Help)->setPopup( helpMenu->menu() ); + + // --------------------------------------------------------------- + + connect(altResetButton, SIGNAL(released()), + d->altitudeInput, SLOT(clear())); + + connect(latResetButton, SIGNAL(released()), + d->latitudeInput, SLOT(clear())); + + connect(lonResetButton, SIGNAL(released()), + d->longitudeInput, SLOT(clear())); + + connect(d->altitudeInput, SIGNAL(textChanged(const QString&)), + this, SLOT(slotGPSPositionChanged())); + + connect(d->latitudeInput, SIGNAL(textChanged(const QString&)), + this, SLOT(slotGPSPositionChanged())); + + connect(d->longitudeInput, SIGNAL(textChanged(const QString&)), + this, SLOT(slotGPSPositionChanged())); + + connect(d->worldMap, SIGNAL(signalNewGPSLocationFromMap(const QString&, const QString&, const QString&)), + this, SLOT(slotNewGPSLocationFromMap(const QString&, const QString&, const QString&))); + + connect(d->goButton, SIGNAL(released()), + this, SLOT(slotGotoLocation())); + + // --------------------------------------------------------------- + + readSettings(); + QTimer::singleShot(0, this, SLOT(slotUpdateWorldMap())); +} + +GPSEditDialog::~GPSEditDialog() +{ + delete d->about; + delete d; +} + +void GPSEditDialog::slotHelp() +{ + KApplication::kApplication()->invokeHelp("gpssync", "kipi-plugins"); +} + +void GPSEditDialog::closeEvent(QCloseEvent *e) +{ + if (!e) return; + saveSettings(); + e->accept(); +} + +void GPSEditDialog::slotGPSPositionChanged() +{ + d->goButton->setEnabled(true); +} + +void GPSEditDialog::slotGotoLocation() +{ + if (!checkGPSLocation()) return; + d->worldMap->setGPSPosition(d->latitudeInput->text(), d->longitudeInput->text()); + slotUpdateWorldMap(); +} + +void GPSEditDialog::slotUpdateWorldMap() +{ + d->worldMap->resized(); +} + +void GPSEditDialog::resizeEvent(QResizeEvent *e) +{ + if (!e) return; + slotUpdateWorldMap(); +} + +void GPSEditDialog::slotCancel() +{ + saveSettings(); + KDialogBase::slotCancel(); +} + +void GPSEditDialog::readSettings() +{ + KConfig config("kipirc"); + config.setGroup("GPS Sync Settings"); + resize(configDialogSize(config, QString("GPS Edit Dialog"))); + + d->worldMap->setZoomLevel(config.readNumEntry("Zoom Level", 8)); + d->worldMap->setMapType(config.readEntry("Map Type", QString("G_MAP_TYPE"))); + + d->altitudeInput->blockSignals(true); + d->latitudeInput->blockSignals(true); + d->longitudeInput->blockSignals(true); + + if (d->hasGPSInfo) + { + d->altitudeInput->setText(QString::number(d->gpsData.altitude(), 'g', 12)); + d->latitudeInput->setText(QString::number(d->gpsData.latitude(), 'g', 12)); + d->longitudeInput->setText(QString::number(d->gpsData.longitude(), 'g', 12)); + } + else + { + d->altitudeInput->setText(QString::number(config.readDoubleNumEntry("GPS Last Altitude", 0.0), 'g', 12)); + d->latitudeInput->setText(QString::number(config.readDoubleNumEntry("GPS Last Latitude", 0.0), 'g', 12)); + d->longitudeInput->setText(QString::number(config.readDoubleNumEntry("GPS Last Longitude", 0.0), 'g', 12)); + } + + d->altitudeInput->blockSignals(false); + d->latitudeInput->blockSignals(false); + d->longitudeInput->blockSignals(false); + + d->worldMap->setGPSPosition(d->latitudeInput->text(), d->longitudeInput->text()); + d->worldMap->resized(); +} + +void GPSEditDialog::saveSettings() +{ + KConfig config("kipirc"); + config.setGroup("GPS Sync Settings"); + saveDialogSize(config, QString("GPS Edit Dialog")); + config.writeEntry("GPS Last Latitude", d->latitudeInput->text().toDouble()); + config.writeEntry("GPS Last Longitude", d->longitudeInput->text().toDouble()); + config.writeEntry("GPS Last Altitude", d->altitudeInput->text().toDouble()); + config.writeEntry("Zoom Level", d->worldMap->zoomLevel()); + config.writeEntry("Map Type", d->worldMap->mapType()); + config.sync(); +} + +GPSDataContainer GPSEditDialog::getGPSInfo() +{ + return GPSDataContainer(d->altitudeInput->text().toDouble(), + d->latitudeInput->text().toDouble(), + d->longitudeInput->text().toDouble(), + false); +} + +bool GPSEditDialog::checkGPSLocation() +{ + bool ok; + + d->altitudeInput->text().toDouble(&ok); + if (!ok) + { + KMessageBox::error(this, i18n("Altitude value is not correct!"), + i18n("Edit Geographical Coordinates")); + return false; + } + + d->latitudeInput->text().toDouble(&ok); + if (!ok) + { + KMessageBox::error(this, i18n("Latitude value is not correct!"), + i18n("Edit Geographical Coordinates")); + return false; + } + + d->longitudeInput->text().toDouble(&ok); + if (!ok) + { + KMessageBox::error(this, i18n("Longitude value is not correct!"), + i18n("Edit Geographical Coordinates")); + return false; + } + + return true; +} + +void GPSEditDialog::slotOk() +{ + if (!checkGPSLocation()) return; + saveSettings(); + accept(); +} + +void GPSEditDialog::slotNewGPSLocationFromMap(const QString& lat, const QString& lon, const QString& alt) +{ + d->latitudeInput->setText(lat); + d->longitudeInput->setText(lon); + d->altitudeInput->setText(alt); + d->goButton->setEnabled(false); +} + +} // namespace KIPIGPSSyncPlugin diff --git a/kipi-plugins/gpssync/gpseditdialog.h b/kipi-plugins/gpssync/gpseditdialog.h new file mode 100644 index 0000000..5ce243d --- /dev/null +++ b/kipi-plugins/gpssync/gpseditdialog.h @@ -0,0 +1,79 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-22 + * Description : a dialog to edit GPS positions + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef GPSEDITDIALOG_H +#define GPSEDITDIALOG_H + +// KDE includes. + +#include <kdialogbase.h> + +// Local includes. + +#include "gpsdatacontainer.h" + +namespace KIPIGPSSyncPlugin +{ + +class GPSEditDialogDialogPrivate; + +class GPSEditDialog : public KDialogBase +{ + Q_OBJECT + +public: + + GPSEditDialog(QWidget* parent, const GPSDataContainer& gpsData, + const QString& fileName, bool hasGPSInfo); + ~GPSEditDialog(); + + GPSDataContainer getGPSInfo(); + +protected slots: + + void slotOk(); + void slotCancel(); + void slotNewGPSLocationFromMap(const QString& lat, const QString& lon, const QString& alt); + void slotUpdateWorldMap(); + void slotGotoLocation(); + void slotGPSPositionChanged(); + void slotHelp(); + +protected: + + void resizeEvent(QResizeEvent *); + void closeEvent(QCloseEvent *); + +private: + + void readSettings(); + void saveSettings(); + bool checkGPSLocation(); + +private: + + GPSEditDialogDialogPrivate *d; +}; + +} // namespace KIPIGPSSyncPlugin + +#endif /* GPSEDITDIALOG_H */ diff --git a/kipi-plugins/gpssync/gpslistviewitem.cpp b/kipi-plugins/gpssync/gpslistviewitem.cpp new file mode 100644 index 0000000..8951c87 --- /dev/null +++ b/kipi-plugins/gpssync/gpslistviewitem.cpp @@ -0,0 +1,246 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-19 + * Description : GPS file list view item. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// Qt includes. + +#include <qpainter.h> +#include <qfileinfo.h> + +// KDE includes. + +#include <klocale.h> +#include <kiconloader.h> + +// LibKExiv2 includes. + +#include <libkexiv2/kexiv2.h> + +// Local includes. + +#include "gpslistviewitem.h" + +namespace KIPIGPSSyncPlugin +{ + +class GPSListViewItemPriv +{ +public: + + GPSListViewItemPriv() + { + enabled = false; + dirty = false; + erase = false; + hasGPSInfo = false; + } + + bool enabled; + bool dirty; + bool erase; + bool hasGPSInfo; + + QDateTime date; + + KURL url; + + GPSDataContainer gpsData; +}; + +GPSListViewItem::GPSListViewItem(KListView *view, QListViewItem *after, const KURL& url) + : KListViewItem(view, after) +{ + d = new GPSListViewItemPriv; + d->url = url; + + setEnabled(false); + setPixmap(0, SmallIcon( "file_broken", KIcon::SizeLarge, KIcon::DisabledState )); + setText(1, d->url.fileName()); + + KExiv2Iface::KExiv2 exiv2Iface; + exiv2Iface.load(d->url.path()); + setDateTime(exiv2Iface.getImageDateTime()); + double alt, lat, lng; + d->hasGPSInfo = exiv2Iface.getGPSInfo(alt, lat, lng); + if (hasGPSInfo()) + setGPSInfo(GPSDataContainer(alt, lat, lng, false), false); +} + +GPSListViewItem::~GPSListViewItem() +{ + delete d; +} + +void GPSListViewItem::setGPSInfo(const GPSDataContainer& gpsData, bool dirty, bool addedManually) +{ + setEnabled(true); + d->dirty = dirty; + d->gpsData = gpsData; + d->erase = false; + d->hasGPSInfo = true; + setText(3, QString::number(d->gpsData.latitude(), 'g', 12)); + setText(4, QString::number(d->gpsData.longitude(), 'g', 12)); + setText(5, QString::number(d->gpsData.altitude(), 'g', 12)); + + if (isDirty()) + { + QString status; + + if (d->gpsData.isInterpolated()) + status = i18n("Interpolated"); + else + { + if (addedManually) + status = i18n("Added"); + else + status = i18n("Found"); + } + + setText(6, status); + } + + repaint(); +} + +GPSDataContainer GPSListViewItem::GPSInfo() const +{ + return d->gpsData; +} + +void GPSListViewItem::eraseGPSInfo() +{ + d->erase = true; + d->dirty = true; + setText(6, i18n("Deleted!")); + repaint(); +} + +void GPSListViewItem::setDateTime(const QDateTime& date) +{ + if (date.isValid()) + { + d->date = date; + setText(2, date.toString(Qt::LocalDate)); + } + else + { + setText(2, i18n("Not available")); + } +} + +QDateTime GPSListViewItem::dateTime() const +{ + return d->date; +} + +KURL GPSListViewItem::url() +{ + return d->url; +} + +bool GPSListViewItem::hasGPSInfo() +{ + return d->hasGPSInfo; +} + +bool GPSListViewItem::isInterpolated() +{ + return d->gpsData.isInterpolated(); +} + +void GPSListViewItem::writeGPSInfoToFile() +{ + if (isEnabled() && isDirty()) + { + setPixmap(1, SmallIcon("run")); + KExiv2Iface::KExiv2 exiv2Iface; + bool ret = exiv2Iface.load(d->url.path()); + + if (d->erase) + ret &= exiv2Iface.removeGPSInfo(); + else + { + ret &= exiv2Iface.setGPSInfo(d->gpsData.altitude(), + d->gpsData.latitude(), + d->gpsData.longitude()); + } + + ret &= exiv2Iface.save(d->url.path()); + + if (ret) + setPixmap(1, SmallIcon("ok")); + else + setPixmap(1, SmallIcon("cancel")); + + d->dirty = false; + } +} + +void GPSListViewItem::setEnabled(bool e) +{ + d->enabled = e; + repaint(); +} + +bool GPSListViewItem::isEnabled() +{ + return d->enabled; +} + +bool GPSListViewItem::isDirty() +{ + return d->dirty; +} + +void GPSListViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment) +{ + if (isEnabled()) + { + if ( isDirty() && !d->erase && column >= 3 && column <= 5 ) + { + QColorGroup _cg( cg ); + QColor c = _cg.text(); + _cg.setColor( QColorGroup::Text, Qt::red ); + KListViewItem::paintCell( p, _cg, column, width, alignment ); + _cg.setColor( QColorGroup::Text, c ); + } + else if ( isDirty() && d->erase && column == 6) + { + QColorGroup _cg( cg ); + QColor c = _cg.text(); + _cg.setColor( QColorGroup::Text, Qt::red ); + KListViewItem::paintCell( p, _cg, column, width, alignment ); + _cg.setColor( QColorGroup::Text, c ); + } + else + KListViewItem::paintCell(p, cg, column, width, alignment); + } + else + { + QColorGroup _cg( cg ); + QColor c = _cg.text(); + _cg.setColor( QColorGroup::Text, Qt::gray ); + KListViewItem::paintCell( p, _cg, column, width, alignment ); + _cg.setColor( QColorGroup::Text, c ); + } +} + +} // NameSpace KIPIGPSSyncPlugin diff --git a/kipi-plugins/gpssync/gpslistviewitem.h b/kipi-plugins/gpssync/gpslistviewitem.h new file mode 100644 index 0000000..b4483c5 --- /dev/null +++ b/kipi-plugins/gpssync/gpslistviewitem.h @@ -0,0 +1,83 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-19 + * Description : GPS file list view item. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef GPSLISTVIEWITEM_H +#define GPSLISTVIEWITEM_H + +// Qt includes. + +#include <qdatetime.h> +#include <qstring.h> + +// KDE includes. + +#include <kurl.h> +#include <klistview.h> + +// Local includes. + +#include "gpsdatacontainer.h" + +class QPainter; +class QColorGroup; +class QPixmap; + +namespace KIPIGPSSyncPlugin +{ +class GPSListViewItemPriv; + +class GPSListViewItem : public KListViewItem +{ + +public: + + GPSListViewItem(KListView *view, QListViewItem *after, const KURL& url); + ~GPSListViewItem(); + + void setGPSInfo(const GPSDataContainer& gpsData, bool dirty=true, bool addedManually=false); + GPSDataContainer GPSInfo() const; + void eraseGPSInfo(); + + void setDateTime(const QDateTime &date); + QDateTime dateTime() const; + + void setEnabled(bool e); + bool isEnabled(); + bool isDirty(); + bool isInterpolated(); + bool hasGPSInfo(); + + KURL url(); + void writeGPSInfoToFile(); + +protected: + + void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment); + +private: + + GPSListViewItemPriv *d; +}; + +} // NameSpace KIPIGPSSyncPlugin + +#endif /* GPSLISTVIEWITEM_H */ diff --git a/kipi-plugins/gpssync/gpsmapwidget.cpp b/kipi-plugins/gpssync/gpsmapwidget.cpp new file mode 100644 index 0000000..92484cd --- /dev/null +++ b/kipi-plugins/gpssync/gpsmapwidget.cpp @@ -0,0 +1,188 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-28 + * Description : a widget to display a GPS web map locator. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * Copyright 2008 by Gerhard Kulzer <gerhard at kulzer dot net> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// KDE includes. + +#include <kdebug.h> +#include <khtmlview.h> +#include <kurl.h> + +// Local includes. + +#include "gpsmapwidget.h" +#include "gpsmapwidget.moc" + +namespace KIPIGPSSyncPlugin +{ + +class GPSMapWidgetPrivate +{ + +public: + + GPSMapWidgetPrivate() + { + gpsLocalorUrl = QString("http://digikam3rdparty.free.fr/gpslocator/getlonlatalt.php"); + } + + QString gpsLocalorUrl; + QString latitude; + QString longitude; + QString altitude; + QString zoomLevel; + QString mapType; + QString fileName; +}; + +GPSMapWidget::GPSMapWidget(QWidget* parent) + : KHTMLPart(parent) +{ + d = new GPSMapWidgetPrivate; + + setJScriptEnabled(true); + setDNDEnabled(false); + setEditable(false); + + view()->setVScrollBarMode(QScrollView::AlwaysOff); + view()->setHScrollBarMode(QScrollView::AlwaysOff); + view()->setMinimumSize(480, 360); +} + +GPSMapWidget::~GPSMapWidget() +{ + delete d; +} + +void GPSMapWidget::setFileName(const QString& fileName) +{ + d->fileName = fileName; +} + +QString GPSMapWidget::fileName() +{ + return d->fileName; +} + +void GPSMapWidget::setGPSPosition(const QString& lat, const QString& lon) +{ + d->latitude = lat; + d->longitude = lon; +} + +void GPSMapWidget::setMapType(const QString& mapType) +{ + d->mapType = mapType; +} + +QString GPSMapWidget::mapType() +{ + return d->mapType; +} + +void GPSMapWidget::setZoomLevel(int zoomLevel) +{ + d->zoomLevel = QString::number(zoomLevel); +} + +int GPSMapWidget::GPSMapWidget::zoomLevel() +{ + return d->zoomLevel.toInt(); +} + +void GPSMapWidget::extractGPSPositionfromStatusbar(const QString& txt) +{ + QString status = txt; + status.remove(0, 5); + status.truncate(status.length()-1); + d->latitude = status.section(",", 0, 0); + d->longitude = status.section(",", 1, 1); + d->altitude = status.section(",", 2, 2); + d->longitude.remove(0, 5); + d->altitude.remove(0, 5); + emit signalNewGPSLocationFromMap(d->latitude, d->longitude, d->altitude); +} + +void GPSMapWidget::khtmlMouseMoveEvent(khtml::MouseMoveEvent *e) +{ + QString status = jsStatusBarText(); + + // If a new point to the map have been moved, the Status + // string is like : "(lat:25.5894748, lon:47.6897455478, alt:211)" + if (status.startsWith(QString("(lat:"))) + extractGPSPositionfromStatusbar(status); + + KHTMLPart::khtmlMouseMoveEvent(e); +} + +void GPSMapWidget::khtmlMouseReleaseEvent(khtml::MouseReleaseEvent *e) +{ + QString status = jsStatusBarText(); + + // If a new point to the map have been moved, the Status + // string is like : "(lat:25.5894748, lon:47.6897455478, alt:211)" + if (status.startsWith(QString("(lat:"))) + extractGPSPositionfromStatusbar(status); + + // If a new map zoom level have been selected, the Status + // string is like : "newZoomLevel:5" + if (status.startsWith(QString("newZoomLevel:"))) + { + status.remove(0, 13); + d->zoomLevel = status; + } + + // If a new map type have been selected, the Status + // string is like : "newMapType:G_SATELLITE_TYPE" + if (status.startsWith(QString("newMapType:"))) + { + status.remove(0, 11); + d->mapType = status; + } + + KHTMLPart::khtmlMouseReleaseEvent(e); +} + +void GPSMapWidget::resized() +{ + QString url = d->gpsLocalorUrl; + url.append("?latitude="); + url.append(d->latitude); + url.append("&longitude="); + url.append(d->longitude); + url.append("&altitude="); + url.append(d->altitude); + url.append("&width="); + url.append(QString::number(view()->width())); + url.append("&height="); + url.append(QString::number(view()->height())); + url.append("&zoom="); + url.append(d->zoomLevel); + url.append("&maptype="); + url.append(d->mapType); + url.append("&filename="); + url.append(d->fileName); + openURL(KURL(url)); + kdDebug( 51001 ) << url << endl; +} + +} // namespace KIPIGPSSyncPlugin diff --git a/kipi-plugins/gpssync/gpsmapwidget.h b/kipi-plugins/gpssync/gpsmapwidget.h new file mode 100644 index 0000000..3ca2e52 --- /dev/null +++ b/kipi-plugins/gpssync/gpsmapwidget.h @@ -0,0 +1,82 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-09-28 + * Description : a widget to display a GPS web map locator. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * Copyright 2008 by Gerhard Kulzer <gerhard at kulzer dot net> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef GPSMAPWIDGET_H +#define GPSMAPWIDGET_H + +// Qt includes. + +#include <qstring.h> + +// KDE includes. + +#include <khtml_part.h> + +namespace KIPIGPSSyncPlugin +{ + +class GPSMapWidgetPrivate; + +class GPSMapWidget : public KHTMLPart +{ + Q_OBJECT + +public: + + GPSMapWidget(QWidget* parent); + ~GPSMapWidget(); + + void setGPSPosition(const QString& lat, const QString& lon); + + void setZoomLevel(int zoomLevel); + int zoomLevel(); + + void setMapType(const QString& mapType); + QString mapType(); + + void setFileName(const QString& fileName); + QString fileName(); + + void resized(); + +signals: + + void signalNewGPSLocationFromMap(const QString&, const QString&, const QString&); + +protected: + + void khtmlMouseMoveEvent(khtml::MouseMoveEvent*); + void khtmlMouseReleaseEvent(khtml::MouseReleaseEvent *); + +private: + + void extractGPSPositionfromStatusbar(const QString& txt); + +private: + + GPSMapWidgetPrivate *d; +}; + +} // namespace KIPIGPSSyncPlugin + +#endif /* GPSMAPWIDGET_H */ diff --git a/kipi-plugins/gpssync/gpssyncdialog.cpp b/kipi-plugins/gpssync/gpssyncdialog.cpp new file mode 100644 index 0000000..eed7154 --- /dev/null +++ b/kipi-plugins/gpssync/gpssyncdialog.cpp @@ -0,0 +1,548 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-05-16 + * Description : a plugin to synchronize pictures with + * a GPS device. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// Qt includes. + +#include <qcombobox.h> +#include <qlayout.h> +#include <qlabel.h> +#include <qvgroupbox.h> +#include <qgrid.h> +#include <qpushbutton.h> +#include <qwhatsthis.h> +#include <qcheckbox.h> + +// KDE includes. + +#include <kdebug.h> +#include <klocale.h> +#include <kapplication.h> +#include <khelpmenu.h> +#include <ksqueezedtextlabel.h> +#include <kiconloader.h> +#include <kpopupmenu.h> +#include <kstandarddirs.h> +#include <klistview.h> +#include <kfiledialog.h> +#include <kconfig.h> +#include <kmessagebox.h> +#include <kglobalsettings.h> +#include <knuminput.h> +#include <kseparator.h> +#include <kio/previewjob.h> + +// Local includes. + +#include "kpaboutdata.h" +#include "pluginsversion.h" +#include "gpslistviewitem.h" +#include "gpsdataparser.h" +#include "gpseditdialog.h" +#include "gpssyncdialog.h" +#include "gpssyncdialog.moc" + +namespace KIPIGPSSyncPlugin +{ + +class GPSSyncDialogPriv +{ +public: + + GPSSyncDialogPriv() + { + listView = 0; + interface = 0; + maxGapInput = 0; + gpxFileName = 0; + gpxPointsLabel = 0; + timeZoneCB = 0; + interpolateBox = 0; + maxTimeInput = 0; + maxTimeLabel = 0; + about = 0; + } + + QLabel *gpxPointsLabel; + QLabel *maxTimeLabel; + + QComboBox *timeZoneCB; + + QCheckBox *interpolateBox; + + KListView *listView; + + KIntSpinBox *maxGapInput; + KIntSpinBox *maxTimeInput; + + KSqueezedTextLabel *gpxFileName; + + KIPI::Interface *interface; + + KIPIPlugins::KPAboutData *about; + + GPSDataParser gpxParser; +}; + +GPSSyncDialog::GPSSyncDialog( KIPI::Interface* interface, QWidget* parent) + : KDialogBase(Plain, i18n("Geolocation"), + Help|User1|User2|User3|Apply|Close, Close, + parent, 0, true, false) +{ + d = new GPSSyncDialogPriv; + d->interface = interface; + + setButtonText(User1, i18n("Correlate")); + setButtonText(User2, i18n("Edit...")); + setButtonText(User3, i18n("Remove")); + + setButtonTip(User1, i18n("Correlate in time and interpolate distance of data from GPX file with all images on the list.")); + setButtonTip(User2, i18n("Manually edit GPS coordinates of selected images from the list.")); + setButtonTip(User3, i18n("Remove GPS coordinates of selected images from the list.")); + + enableButton(User1, false); + enableButton(User2, true); + enableButton(User3, true); + + QGridLayout *mainLayout = new QGridLayout(plainPage(), 3, 1, 0, marginHint()); + + // -------------------------------------------------------------- + + d->listView = new KListView(plainPage()); + d->listView->addColumn( i18n("Thumbnail") ); + d->listView->addColumn( i18n("File Name") ); + d->listView->addColumn( i18n("Camera time stamp") ); + d->listView->addColumn( i18n("Latitude") ); + d->listView->addColumn( i18n("Longitude") ); + d->listView->addColumn( i18n("Altitude") ); + d->listView->addColumn( i18n("Status") ); + d->listView->setResizeMode(QListView::AllColumns); + d->listView->setAllColumnsShowFocus(true); + d->listView->setSorting(-1); + d->listView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + d->listView->setSelectionMode(QListView::Extended); + d->listView->setMinimumWidth(450); + + // --------------------------------------------------------------- + + QGroupBox *settingsBox = new QGroupBox(0, Qt::Vertical, i18n("Settings"), plainPage()); + QGridLayout *settingsBoxLayout = new QGridLayout(settingsBox->layout(), 8, 1, + KDialog::spacingHint()); + + QPushButton *loadGPXButton = new QPushButton(i18n("Load GPX File..."), settingsBox); + + QLabel *gpxFileLabel = new QLabel(i18n("Current GPX file:"), settingsBox); + d->gpxFileName = new KSqueezedTextLabel(i18n("No GPX file"), settingsBox); + d->gpxPointsLabel = new QLabel(settingsBox); + KSeparator *line = new KSeparator(Horizontal, settingsBox); + + QLabel *maxGapLabel = new QLabel(i18n("Max. time gap (sec.):"), settingsBox); + d->maxGapInput = new KIntSpinBox(0, 1000000, 1, 30, 10, settingsBox); + QWhatsThis::add(d->maxGapInput, i18n("<p>Sets the maximum difference in " + "seconds from a GPS track point to the image time to be matched. " + "If the time difference exceeds this setting, no match will be attempted.")); + + QLabel *timeZoneLabel = new QLabel(i18n("Time zone:"), settingsBox); + d->timeZoneCB = new QComboBox( false, settingsBox ); + + // See list of time zomes over the world : + // http://en.wikipedia.org/wiki/List_of_time_zones + // NOTE: Combo box strings are not i18n. + d->timeZoneCB->insertItem("GMT-12:00"); + d->timeZoneCB->insertItem("GMT-11:00"); + d->timeZoneCB->insertItem("GMT-10:00"); + d->timeZoneCB->insertItem("GMT-09:30"); + d->timeZoneCB->insertItem("GMT-09:00"); + d->timeZoneCB->insertItem("GMT-08:00"); + d->timeZoneCB->insertItem("GMT-07:00"); + d->timeZoneCB->insertItem("GMT-06:00"); + d->timeZoneCB->insertItem("GMT-05:30"); + d->timeZoneCB->insertItem("GMT-05:00"); + d->timeZoneCB->insertItem("GMT-04:30"); + d->timeZoneCB->insertItem("GMT-04:00"); + d->timeZoneCB->insertItem("GMT-03:30"); + d->timeZoneCB->insertItem("GMT-03:00"); + d->timeZoneCB->insertItem("GMT-02:00"); + d->timeZoneCB->insertItem("GMT-01:00"); + d->timeZoneCB->insertItem("GMT+00:00"); + d->timeZoneCB->insertItem("GMT+01:00"); + d->timeZoneCB->insertItem("GMT+02:00"); + d->timeZoneCB->insertItem("GMT+03:00"); + d->timeZoneCB->insertItem("GMT+03:30"); + d->timeZoneCB->insertItem("GMT+04:00"); + d->timeZoneCB->insertItem("GMT+05:00"); + d->timeZoneCB->insertItem("GMT+05:30"); // See B.K.O # 149491 + d->timeZoneCB->insertItem("GMT+05:45"); + d->timeZoneCB->insertItem("GMT+06:00"); + d->timeZoneCB->insertItem("GMT+06:30"); + d->timeZoneCB->insertItem("GMT+07:00"); + d->timeZoneCB->insertItem("GMT+08:00"); + d->timeZoneCB->insertItem("GMT+08:45"); + d->timeZoneCB->insertItem("GMT+09:00"); + d->timeZoneCB->insertItem("GMT+09:30"); + d->timeZoneCB->insertItem("GMT+10:00"); + d->timeZoneCB->insertItem("GMT+10:30"); + d->timeZoneCB->insertItem("GMT+11:00"); + d->timeZoneCB->insertItem("GMT+11:30"); + d->timeZoneCB->insertItem("GMT+12:00"); + d->timeZoneCB->insertItem("GMT+12:45"); + d->timeZoneCB->insertItem("GMT+13:00"); + d->timeZoneCB->insertItem("GMT+14:00"); + QWhatsThis::add(d->timeZoneCB, i18n("<p>Sets the time zone the camera was set to " + "during photo shooting, so that the time stamps of the images " + "can be converted to GMT to match the GPS time reference.\n" + "Note: positive offsets count eastwards from zero longitude (GMT), " + "they are 'ahead of time'.")); + + d->interpolateBox = new QCheckBox(i18n("Interpolate"), settingsBox); + QWhatsThis::add(d->interpolateBox, i18n("<p>Set this option to interpolate GPS track points " + "which are not closely matched to the GPX data file.")); + + d->maxTimeLabel = new QLabel(i18n("Difference in min.:"), settingsBox); + d->maxTimeInput = new KIntSpinBox(0, 240, 1, 15, 10, settingsBox); + QWhatsThis::add(d->maxTimeInput, i18n("<p>Sets the maximum time difference in minutes (240 max.)" + " to interpolate GPX file points to image time data.")); + + settingsBoxLayout->addMultiCellWidget(loadGPXButton, 0, 0, 0, 1); + settingsBoxLayout->addMultiCellWidget(gpxFileLabel, 1, 1, 0, 1); + settingsBoxLayout->addMultiCellWidget(d->gpxFileName, 2, 2, 0, 1); + settingsBoxLayout->addMultiCellWidget(d->gpxPointsLabel, 3, 3, 0, 1); + settingsBoxLayout->addMultiCellWidget(line, 4, 4, 0, 1); + settingsBoxLayout->addMultiCellWidget(maxGapLabel, 5, 5, 0, 0); + settingsBoxLayout->addMultiCellWidget(d->maxGapInput, 5, 5, 1, 1); + settingsBoxLayout->addMultiCellWidget(timeZoneLabel, 6, 6, 0, 0); + settingsBoxLayout->addMultiCellWidget(d->timeZoneCB, 6, 6, 1, 1); + settingsBoxLayout->addMultiCellWidget(d->interpolateBox, 7, 7, 0, 1); + settingsBoxLayout->addMultiCellWidget(d->maxTimeLabel, 8, 8, 0, 0); + settingsBoxLayout->addMultiCellWidget(d->maxTimeInput, 8, 8, 1, 1); + + // --------------------------------------------------------------- + + mainLayout->addMultiCellWidget(d->listView, 0, 2, 0, 1); + mainLayout->addMultiCellWidget(settingsBox, 0, 1, 2, 2); + mainLayout->setColStretch(1, 10); + mainLayout->setRowStretch(2, 10); + + // --------------------------------------------------------------- + // About data and help button. + + d->about = new KIPIPlugins::KPAboutData(I18N_NOOP("GPS Sync"), + 0, + KAboutData::License_GPL, + I18N_NOOP("A plugin to synchronize images' metadata with a GPS device"), + "(c) 2006-2008, Gilles Caulier"); + + d->about->addAuthor("Gilles Caulier", I18N_NOOP("Author and Maintainer"), + "caulier dot gilles at gmail dot com"); + + KHelpMenu* helpMenu = new KHelpMenu(this, d->about, false); + helpMenu->menu()->removeItemAt(0); + helpMenu->menu()->insertItem(i18n("Plugin Handbook"), + this, SLOT(slotHelp()), 0, -1, 0); + actionButton(Help)->setPopup( helpMenu->menu() ); + + // --------------------------------------------------------------- + + connect(loadGPXButton, SIGNAL(released()), + this, SLOT(slotLoadGPXFile())); + + connect(d->interpolateBox, SIGNAL(toggled(bool)), + d->maxTimeLabel, SLOT(setEnabled(bool))); + + connect(d->interpolateBox, SIGNAL(toggled(bool)), + d->maxTimeInput, SLOT(setEnabled(bool))); + + readSettings(); +} + +GPSSyncDialog::~GPSSyncDialog() +{ + delete d->about; + delete d; +} + +void GPSSyncDialog::slotHelp() +{ + KApplication::kApplication()->invokeHelp("gpssync", "kipi-plugins"); +} + +void GPSSyncDialog::setImages( const KURL::List& images ) +{ + for( KURL::List::ConstIterator it = images.begin(); it != images.end(); ++it ) + new GPSListViewItem(d->listView, d->listView->lastItem(), *it); + + KIO::PreviewJob *thumbnailJob = KIO::filePreview(images, 64); + + connect(thumbnailJob, SIGNAL(gotPreview(const KFileItem*, const QPixmap&)), + this, SLOT(slotGotThumbnail(const KFileItem*, const QPixmap&))); +} + +void GPSSyncDialog::slotLoadGPXFile() +{ + KURL loadGPXFile = KFileDialog::getOpenURL(KGlobalSettings::documentPath(), + i18n("%1|GPS Exchange Format").arg("*.gpx"), this, + i18n("Select GPX File to Load") ); + if( loadGPXFile.isEmpty() ) + return; + + d->gpxParser.clear(); + bool ret = d->gpxParser.loadGPXFile(loadGPXFile); + + if (!ret) + { + KMessageBox::error(this, i18n("Cannot parse %1 GPX file!") + .arg(loadGPXFile.fileName()), i18n("GPS Sync")); + enableButton(User1, false); + return; + } + + if (d->gpxParser.numPoints() <= 0) + { + KMessageBox::sorry(this, i18n("The %1 GPX file do not have a date-time track to use!") + .arg(loadGPXFile.fileName()), i18n("GPS Sync")); + enableButton(User1, false); + return; + } + + d->gpxFileName->setText(loadGPXFile.fileName()); + d->gpxPointsLabel->setText(i18n("Points parsed: %1").arg(d->gpxParser.numPoints())); + enableButton(User1, true); + slotUser1(); +} + +void GPSSyncDialog::closeEvent(QCloseEvent *e) +{ + if (!e) return; + if (!promptUserClose()) + { + e->ignore(); + return; + } + + saveSettings(); + e->accept(); +} + +void GPSSyncDialog::slotClose() +{ + if (!promptUserClose()) return; + saveSettings(); + KDialogBase::slotClose(); +} + +bool GPSSyncDialog::promptUserClose() +{ + // Check if one item is dirty in the list. + + QListViewItemIterator it( d->listView ); + int dirty = 0; + + while ( it.current() ) + { + GPSListViewItem *item = (GPSListViewItem*) it.current(); + if (item->isDirty()) + dirty++; + + ++it; + } + + if (dirty > 0) + { + QString msg = i18n("1 image from the list is not updated.", + "%n images from the list are not updated.", dirty); + + if (KMessageBox::No == KMessageBox::warningYesNo(this, + i18n("<p>%1\n" + "Do you really want to close this window without applying changes?</p>") + .arg(msg))) + return false; + } + + return true; +} + +void GPSSyncDialog::readSettings() +{ + KConfig config("kipirc"); + config.setGroup("GPS Sync Settings"); + d->maxGapInput->setValue(config.readNumEntry("Max Gap Time", 30)); + d->timeZoneCB->setCurrentItem(config.readNumEntry("Time Zone", 16)); // GMT+00:00 + d->interpolateBox->setChecked(config.readBoolEntry("Interpolate", false)); + d->maxTimeInput->setValue(config.readNumEntry("Max Inter Dist Time", 15)); + + d->maxTimeLabel->setEnabled(d->interpolateBox->isChecked()); + d->maxTimeInput->setEnabled(d->interpolateBox->isChecked()); + resize(configDialogSize(config, QString("GPS Sync Dialog"))); +} + +void GPSSyncDialog::saveSettings() +{ + KConfig config("kipirc"); + config.setGroup("GPS Sync Settings"); + config.writeEntry("Max Gap Time", d->maxGapInput->value() ); + config.writeEntry("Time Zone", d->timeZoneCB->currentItem() ); + config.writeEntry("Interpolate", d->interpolateBox->isChecked() ); + config.writeEntry("Max Inter Dist Time", d->maxTimeInput->value() ); + saveDialogSize(config, QString("GPS Sync Dialog")); + config.sync(); +} + +// Correlate the GPS positions from Pictures using a GPX file data. +void GPSSyncDialog::slotUser1() +{ + int itemsUpdated = 0; + + QListViewItemIterator it( d->listView ); + while ( it.current() ) + { + GPSListViewItem *item = dynamic_cast<GPSListViewItem*>(it.current()); + GPSDataContainer gpsData; + QString tz = d->timeZoneCB->currentText(); + int hh = QString(QString(tz[4])+QString(tz[5])).toInt(); + int mm = QString(QString(tz[7])+QString(tz[8])).toInt(); + int offset = hh*3600 + mm*60; + + if (tz[3] == QChar('-')) + offset = (-1)*offset; + + if (d->gpxParser.matchDate(item->dateTime(), + d->maxGapInput->value(), + offset, + d->interpolateBox->isChecked(), + d->maxTimeInput->value()*60, + gpsData)) + { + item->setGPSInfo(gpsData); + itemsUpdated++; + } + ++it; + } + + if (itemsUpdated == 0) + { + KMessageBox::sorry(this, i18n("Cannot find pictures to correlate with GPX file data."), + i18n("GPS Sync")); + return; + } + + QString msg = i18n("GPS data of 1 image have been updated on the list using the GPX data file.", + "GPS data of %n images have been updated on the list using the GPX data file.", + itemsUpdated); + msg += '\n'; + msg += i18n("Press Apply button to update images metadata."); + + KMessageBox::information(this, msg, i18n("GPS Sync")); +} + +// Start the GPS coordinates editor dialog. +void GPSSyncDialog::slotUser2() +{ + if (!d->listView->currentItem()) + { + KMessageBox::information(this, i18n("Please, select at least one image from " + "the list to edit GPS coordinates manually."), i18n("GPS Sync")); + return; + } + + GPSListViewItem *item = dynamic_cast<GPSListViewItem*>(d->listView->currentItem()); + + GPSEditDialog dlg(this, item->GPSInfo(), + item->url().fileName(), + item->hasGPSInfo()); + + if (dlg.exec() == KDialogBase::Accepted) + { + QListViewItemIterator it(d->listView); + + while (it.current()) + { + if (it.current()->isSelected()) + { + GPSListViewItem *selItem = dynamic_cast<GPSListViewItem*>(it.current()); + selItem->setGPSInfo(dlg.getGPSInfo(), true, true); + } + ++it; + } + } +} + +// Remove GPS coordinates from pictures. +void GPSSyncDialog::slotUser3() +{ + if (!d->listView->currentItem()) + { + KMessageBox::information(this, i18n("Please, select at least one image from " + "the list to remove GPS coordinates."), i18n("GPS Sync")); + return; + } + + QListViewItemIterator it(d->listView); + + while (it.current()) + { + if (it.current()->isSelected()) + { + GPSListViewItem *selItem = dynamic_cast<GPSListViewItem*>(it.current()); + selItem->eraseGPSInfo(); + } + ++it; + } +} + +void GPSSyncDialog::slotApply() +{ + KURL::List images; + + QListViewItemIterator it( d->listView ); + while ( it.current() ) + { + GPSListViewItem *selItem = dynamic_cast<GPSListViewItem*>(it.current()); + d->listView->setSelected(selItem, true); + d->listView->ensureItemVisible(selItem); + selItem->writeGPSInfoToFile(); + images.append(selItem->url()); + ++it; + kapp->processEvents(); + } + + // We use kipi interface refreshImages() method to tell to host than + // metadata from pictures have changed and need to be re-readed. + + d->interface->refreshImages(images); +} + +void GPSSyncDialog::slotGotThumbnail(const KFileItem *item, const QPixmap& pix) +{ + QListViewItemIterator it(d->listView); + + while (it.current()) + { + GPSListViewItem *selItem = dynamic_cast<GPSListViewItem*>(it.current()); + if (selItem->url() == item->url()) + { + selItem->setPixmap(0, pix); + } + ++it; + } +} + +} // NameSpace KIPIGPSSyncPlugin diff --git a/kipi-plugins/gpssync/gpssyncdialog.h b/kipi-plugins/gpssync/gpssyncdialog.h new file mode 100644 index 0000000..053e97a --- /dev/null +++ b/kipi-plugins/gpssync/gpssyncdialog.h @@ -0,0 +1,87 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-05-16 + * Description : a plugin to synchronize pictures with + * a GPS device. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef GPSSYNCDIALOG_H +#define GPSSYNCDIALOG_H + +// KDE includes. + +#include <kdialogbase.h> +#include <kurl.h> + +// LibKipi includes. + +#include <libkipi/interface.h> + +class QListViewItem; + +class KFileItem; + +namespace KIPIGPSSyncPlugin +{ + +class GPSSyncDialogPriv; + +class GPSSyncDialog :public KDialogBase +{ + Q_OBJECT + +public: + + GPSSyncDialog(KIPI::Interface* interface, QWidget* parent); + ~GPSSyncDialog(); + + void setImages(const KURL::List& images); + +protected: + + void closeEvent(QCloseEvent *); + +protected slots: + + void slotApply(); + void slotHelp(); + void slotClose(); + void slotUser1(); + void slotUser2(); + void slotUser3(); + +private slots: + + void slotGotThumbnail(const KFileItem*, const QPixmap&); + void slotLoadGPXFile(); + +private: + + bool promptUserClose(); + void readSettings(); + void saveSettings(); + +private: + + GPSSyncDialogPriv *d; +}; + +} // NameSpace KIPIGPSSyncPlugin + +#endif /* GPSSYNCDIALOG_H */ diff --git a/kipi-plugins/gpssync/hi16-action-gpsimagetag.png b/kipi-plugins/gpssync/hi16-action-gpsimagetag.png Binary files differnew file mode 100644 index 0000000..2d11450 --- /dev/null +++ b/kipi-plugins/gpssync/hi16-action-gpsimagetag.png diff --git a/kipi-plugins/gpssync/hi32-action-gpsimagetag.png b/kipi-plugins/gpssync/hi32-action-gpsimagetag.png Binary files differnew file mode 100644 index 0000000..64da47f --- /dev/null +++ b/kipi-plugins/gpssync/hi32-action-gpsimagetag.png diff --git a/kipi-plugins/gpssync/kipiplugin_gpssync.desktop b/kipi-plugins/gpssync/kipiplugin_gpssync.desktop new file mode 100644 index 0000000..77cab68 --- /dev/null +++ b/kipi-plugins/gpssync/kipiplugin_gpssync.desktop @@ -0,0 +1,40 @@ +[Desktop Entry] +Comment=KIPI plugin to Geolocalize pictures +Comment[ca]=Connector del KIPI per a geolocalitzar fotografies +Comment[da]=KIPI-plugin til at lokalisere billeder geografisk +Comment[de]=Ein KIPI-Modul zur geographischen Zuordnung von Bildern +Comment[el]=Πρόσθετο του KIPI για γεωτοποθέτηση εικόνων +Comment[es]=Complemento de KIPI para geolocalizar imágenes +Comment[et]=KIPI piltide geolokatsiooni plugin +Comment[fr]=Module externe KIPI pour géolocaliser les images +Comment[is]=KIPI íforrit til að GPS-staðsetja myndir +Comment[it]=Plugin di KIPI per geolocalizzare le immagini +Comment[ja]=Kipi ジオロケーションプラグイン +Comment[nds]=KIPI-Moduul för Standöörd vun Biller +Comment[nl]=KIPI-plugin voor het geolokaliseren van afbeeldingen +Comment[pa]=ਭੂ-ਚਿੱਤਰਾਂ ਲਈ KIPI ਪਲੱਗਇਨ +Comment[pl]=Wtyczka KIPI - Geolokalizacja zdjęć +Comment[pt]='Plugin' do KIPI para localizar geograficamente as imagens +Comment[pt_BR]=Plugin para Geolocalização de Figuras do KIPI +Comment[sr]=KIPI прикључак за гео-локализацију слика +Comment[sr@Latn]=KIPI priključak za geo-lokalizaciju slika +Comment[sv]=KIPI-insticksprogram för att lokalisera bilder geografiskt +Comment[xx]=xxKIPI plugin to Geolocalize picturesxx +Comment[zh_CN]=KIPI 地理化图片插件 +Encoding=UTF-8 +Icon= +Name=GPSSync +Name[ca]=Sincronització GPS +Name[da]=GPS-synkronisering +Name[de]=GPS-Abgleich +Name[el]=Συγχρονισμός GPS +Name[fi]=GPS-synkronointi +Name[pt]=Sincronização GPS +Name[sv]=GPS-synkronisering +Name[xx]=xxGPSSyncxx +Name[zh_CN]=GPS 同步 +ServiceTypes=KIPI/Plugin +Type=Service +X-KDE-Library=kipiplugin_gpssync +X-KIPI-ReqFeatures=ImagesHasTime +author=Gilles Caulier, caulier dot gilles at gmail dot com diff --git a/kipi-plugins/gpssync/kmlexport.cpp b/kipi-plugins/gpssync/kmlexport.cpp new file mode 100644 index 0000000..5262140 --- /dev/null +++ b/kipi-plugins/gpssync/kmlexport.cpp @@ -0,0 +1,523 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-05-16 + * Description : a tool to export GPS data to KML file. + * + * Copyright (C) 2006-2007 by Stephane Pontier <shadow dot walker at free dot fr> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +extern "C" +{ +#include <unistd.h> +} + +// Qt includes. + +#include <qpainter.h> +#include <qregexp.h> + +// KDE includes. + +#include <kapplication.h> +#include <kconfig.h> +#include <kio/job.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> + +// Libkexiv2 includes. + +#include <libkexiv2/kexiv2.h> + +// LibKipi includes + +#include <libkipi/plugin.h> +#include <libkipi/batchprogressdialog.h> +#include <libkipi/imageinfo.h> + +// Local includes. + +#include "kmlexport.h" + +namespace KIPIGPSSyncPlugin +{ + +kmlExport::kmlExport(KIPI::Interface* interface) +{ + m_interface = interface; + QWidget* parent = KApplication::kApplication()->mainWidget(); + m_progressDialog = new KIPI::BatchProgressDialog(parent, i18n("Generating KML file...")); +} + +kmlExport::~kmlExport() +{ + delete m_progressDialog; +} + +/*! + \fn kmlExport::createDir(QDir dir) + */ +bool kmlExport::createDir(QDir dir) +{ + if (dir.exists()) return true; + + QDir parent = dir; + parent.cdUp(); + bool ok = createDir(parent); + if (!ok) + { + logError(i18n("Could not create '%1").arg(parent.path())); + return false; + } + return parent.mkdir(dir.dirName()); +} + +/*! +\fn kmlExport::webifyFileName(const QString &fileName) + */ +QString kmlExport::webifyFileName(const QString &fileName) +{ + QString webFileName=fileName.lower(); + + // Remove potentially troublesome chars + webFileName=webFileName.replace(QRegExp("[^-0-9a-z]+"), "_"); + + return webFileName; +} + +/*! + \fn kmlExport::generateSquareThumbnail(const QImage& fullImage, int size) + */ +QImage kmlExport::generateSquareThumbnail(const QImage& fullImage, int size) +{ + QImage image = fullImage.smoothScale(size, size, QImage::ScaleMax); + + if (image.width() == size && image.height() == size) + { + return image; + } + QPixmap croppedPix(size, size); + QPainter painter(&croppedPix); + + int sx=0, sy=0; + if (image.width()>size) + { + sx=(image.width() - size)/2; + } + else + { + sy=(image.height() - size)/2; + } + painter.drawImage(0, 0, image, sx, sy, size, size); + painter.end(); + + return croppedPix.convertToImage(); +} + +/*! + \fn kmlExport::generateBorderedThumbnail(const QImage& fullImage, int size) + */ +QImage kmlExport::generateBorderedThumbnail(const QImage& fullImage, int size) +{ + int image_border = 3; + + // getting an image minus the border + QImage image = fullImage.smoothScale(size -(2*image_border), size - (2*image_border), QImage::ScaleMax); + + QPixmap croppedPix(image.width() + (2*image_border), image.height() + (2*image_border)); + QPainter painter(&croppedPix); + + QColor BrushColor(255,255,255); + painter.fillRect(0,0,image.width() + (2*image_border),image.height() + (2*image_border),BrushColor); + /*! @todo add a corner to the thumbnail and a hotspot to the kml element */ + + painter.drawImage(image_border, image_border, image ); + painter.end(); + + return croppedPix.convertToImage(); +} + +/*! +\fn kmlExport::generateImagesthumb(KIPI::Interface* interface, const KURL& imageURL, QDomElement &kmlAlbum ) + */ +void kmlExport::generateImagesthumb(KIPI::Interface* interface, const KURL& imageURL, QDomElement &kmlAlbum ) +{ + KIPI::Interface* mInterface = interface; + KIPI::ImageInfo info = mInterface->info(imageURL); + + // Load image + QString path = imageURL.path(); + QFile imageFile(path); + if (!imageFile.open(IO_ReadOnly)) + { + logWarning(i18n("Could not read image '%1'").arg(path)); + return; + } + + QString imageFormat = QImageIO::imageFormat(&imageFile); + if (imageFormat.isEmpty()) + { + logWarning(i18n("Format of image '%1' is unknown").arg(path)); + return; + } + imageFile.close(); + imageFile.open(IO_ReadOnly); + + QByteArray imageData = imageFile.readAll(); + QImage image; + if (!image.loadFromData(imageData) ) + { + logWarning(i18n("Error loading image '%1'").arg(path)); + return; + } + + // Process images + /** FIXME depending the soft used, angle could return a good value (digikam) or a value of 0 (gwenview) + * and, in some case the picture is not rotated as it should be. + */ + if ( info.angle() != 0 ) + { + QWMatrix matrix; + matrix.rotate( info.angle() ); + image = image.xForm( matrix ); + } + image = image.smoothScale(m_size, m_size, QImage::ScaleMax); + + QImage icon; + if (m_optimize_googlemap) + { + icon = generateSquareThumbnail(image,m_googlemapSize); + } + else + { + // icon = image.smoothScale(m_iconSize, m_iconSize, QImage::ScaleMax); + icon = generateBorderedThumbnail(image, m_iconSize); + } + + // Save images + /** @todo remove the extension of the file + * it's appear with digikam but not with gwenview + * which already seems to strip the extension + */ + QString baseFileName = webifyFileName(info.title()); + // baseFileName = mUniqueNameHelper.makeNameUnique(baseFileName); + QString fullFileName; + fullFileName = baseFileName + '.' + imageFormat.lower(); + QString destPath = m_tempDestDir + m_imageDir + fullFileName; + if (!image.save(destPath, imageFormat.ascii(), 85)) + { + // if not able to save the image, it's pointless to create a placemark + logWarning(i18n("Could not save image '%1' to '%2'").arg(path).arg(destPath)); + } + else + { + //logInfo(i18n("Creation of picture '%1'").arg(fullFileName)); + KExiv2Iface::KExiv2 exiv2Iface; + exiv2Iface.load(imageURL.path()); + double alt, lat, lng; + exiv2Iface.getGPSInfo(alt, lat, lng); + QDomElement kmlPlacemark = addKmlElement(kmlAlbum, "Placemark"); + addKmlTextElement(kmlPlacemark,"name",fullFileName); + // location and altitude + QDomElement kmlGeometry = addKmlElement(kmlPlacemark, "Point"); + + if (alt) + { + addKmlTextElement(kmlGeometry, "coordinates", QString("%1,%2,%3").arg(lng).arg(lat).arg(alt)); + } + else + { + addKmlTextElement(kmlGeometry, "coordinates", QString("%1,%2").arg(lng).arg(lat)); + } + + if (m_altitudeMode == 2 ) + { + addKmlTextElement(kmlGeometry, "altitudeMode", "absolute"); + } + else if (m_altitudeMode == 1 ) + { + addKmlTextElement(kmlGeometry, "altitudeMode", "relativeToGround"); + } + else + { + addKmlTextElement(kmlGeometry, "altitudeMode", "clampToGround"); + } + addKmlTextElement(kmlGeometry, "extrude", "1"); + + // we try to load exif value if any otherwise, try the application db + /** we need to take the DateTimeOriginal + * if we refer to http://www.exif.org/Exif2-2.PDF + * (standard)DateTime: is The date and time of image creation. In this standard it is the date and time the file was changed + * DateTimeOriginal: The date and time when the original image data was generated. + * For a DSC the date and time the picture was taken are recorded. + * DateTimeDigitized: The date and time when the image was stored as digital data. + * So for: + * - a DSC: the right time is the DateTimeDigitized which is also DateTimeOriginal + * if the picture has been modified the (standard)DateTime should change. + * - a scanned picture, the right time is the DateTimeOriginal which should also be the the DateTime + * the (standard)DateTime should be the same except if the picture is modified + * - a panorama created from several pictures, the right time is the DateTimeOriginal (average of DateTimeOriginal actually) + * The (standard)DateTime is the creation date of the panorama. + * it's seems the time to take into acccount is the DateTimeOriginal. + * but the exiv2Iface.getImageDateTime() return the (standard)DateTime first + * libkexiv2 seems to take Original dateTime first so it shoul be alright now. + */ + QDateTime datetime = exiv2Iface.getImageDateTime(); + if (datetime.isValid()) + { + QDomElement kmlTimeStamp = addKmlElement(kmlPlacemark, "TimeStamp"); + addKmlTextElement(kmlTimeStamp, "when", datetime.toString("yyyy-MM-ddThh:mm:ssZ")); + } + else if ( mInterface->hasFeature(KIPI::ImagesHasTime)) + { + QDomElement kmlTimeStamp = addKmlElement(kmlGeometry, "TimeStamp"); + addKmlTextElement(kmlTimeStamp, "when", (info.time()).toString("yyyy-MM-ddThh:mm:ssZ")); + } + QString my_description; + if (m_optimize_googlemap) + { + my_description = "<img src=\"" + m_UrlDestDir + m_imageDir + fullFileName + "\">"; + } + else + { + my_description = "<img src=\"" + m_imageDir + fullFileName + "\">"; + } + if ( m_interface->hasFeature( KIPI::ImagesHasComments ) ) + { + my_description += "<br/>" + info.description() ; + } + addKmlTextElement(kmlPlacemark, "description", my_description); + logInfo(i18n("Creation of placemark '%1'").arg(fullFileName)); + + // Save icon + QString iconFileName = "thumb_" + baseFileName + '.' + imageFormat.lower(); + QString destPath = m_tempDestDir + m_imageDir + iconFileName; + if (!icon.save(destPath, imageFormat.ascii(), 85)) + { + logWarning(i18n("Could not save icon for image '%1' to '%2'").arg(path).arg(destPath)); + } + else + { + //logInfo(i18n("Creation of icon '%1'").arg(iconFileName)); + // style et icon + QDomElement kmlStyle = addKmlElement(kmlPlacemark, "Style"); + QDomElement kmlIconStyle = addKmlElement(kmlStyle, "IconStyle"); + QDomElement kmlIcon = addKmlElement(kmlIconStyle, "Icon"); + if (m_optimize_googlemap) + { + addKmlTextElement(kmlIcon, "href", m_UrlDestDir + m_imageDir + iconFileName); + } + else + { + addKmlTextElement(kmlIcon, "href", m_imageDir + iconFileName); + } + QDomElement kmlBallonStyle = addKmlElement(kmlStyle, "BalloonStyle"); + addKmlTextElement(kmlBallonStyle, "text", "$[description]"); + } + } +} + +/*! +\fn kmlExport::addTrack(QDomElement &kmlAlbum) + */ +void kmlExport::addTrack(QDomElement &kmlAlbum) +{ + if( m_GPXFile.isEmpty() ) + { + logWarning(i18n("No GPX file Chosen!")); + return; + } + + m_gpxParser.clear(); + bool ret = m_gpxParser.loadGPXFile(m_GPXFile); + + if (!ret) + { + logError(i18n("Cannot parse %1 GPX file!").arg(m_GPXFile)); + return; + } + + if (m_gpxParser.numPoints() <= 0) + { + logError(i18n("The %1 GPX file do not have a date-time track to use!") + .arg(m_GPXFile)); + return; + } + + // create a folder that will contain tracks and points + QDomElement kmlFolder = addKmlElement(kmlAlbum, "Folder"); + addKmlTextElement(kmlFolder, "name", i18n("Tracks")); + + if (!m_optimize_googlemap) + { + // style of points and track + QDomElement kmlTrackStyle = addKmlElement(kmlAlbum, "Style"); + kmlTrackStyle.setAttribute("id","track"); + QDomElement kmlIconStyle = addKmlElement(kmlTrackStyle, "IconStyle"); + QDomElement kmlIcon = addKmlElement(kmlIconStyle, "Icon"); + //! FIXME is there a way to be sure of the location of the icon? + addKmlTextElement(kmlIcon, "href", "http://maps.google.com/mapfiles/kml/pal4/icon60.png"); + + m_gpxParser.CreateTrackPoints(kmlFolder, *kmlDocument, m_TimeZone - 12, m_GPXAltitudeMode); + } + + // linetrack style + QDomElement kmlLineTrackStyle = addKmlElement(kmlAlbum, "Style"); + kmlLineTrackStyle.setAttribute("id","linetrack"); + QDomElement kmlLineStyle = addKmlElement(kmlLineTrackStyle, "LineStyle"); + // the KML color is not #RRGGBB but AABBGGRR + QString KMLColorValue = QString("%1%2%3%4") + .arg((int)m_GPXOpacity*256/100, 2, 16) + .arg((&m_GPXColor)->blue(), 2, 16) + .arg((&m_GPXColor)->green(), 2, 16) + .arg((&m_GPXColor)->red(), 2, 16); + addKmlTextElement(kmlLineStyle, "color", KMLColorValue); + addKmlTextElement(kmlLineStyle, "width", QString("%1").arg(m_LineWidth) ); + + m_gpxParser.CreateTrackLine(kmlAlbum, *kmlDocument, m_GPXAltitudeMode); +} + +/*! + \fn kmlExport::generate() + */ +void kmlExport::generate() +{ + //! @todo perform a test here before to continue. + createDir(m_tempDestDir + m_imageDir); + + m_progressDialog->show(); + KIPI::ImageCollection selection = m_interface->currentSelection(); + KIPI::ImageCollection album = m_interface->currentAlbum(); + // create the document, and it's root + kmlDocument = new QDomDocument(""); + QDomImplementation impl; + QDomProcessingInstruction instr = kmlDocument->createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\""); + kmlDocument->appendChild(instr); + QDomElement kmlRoot = kmlDocument->createElementNS( "http://earth.google.com/kml/2.1","kml"); + kmlDocument->appendChild( kmlRoot ); + + QDomElement kmlAlbum = addKmlElement( kmlRoot, "Document"); + QDomElement kmlName= addKmlTextElement( kmlAlbum, "name", album.name()); + QDomElement kmlDescription = addKmlHtmlElement( kmlAlbum, "description", "Created with kmlexport <a href=\"http://www.kipi-plugins.org/\">kipi-plugin</a>"); + + if (m_GPXtracks) + { + addTrack(kmlAlbum); + } + + KURL::List images = selection.images(); + int defectImage = 0; + int pos = 1; + int count = images.count(); + KURL::List::ConstIterator imagesEnd (images.constEnd()); + for( KURL::List::ConstIterator selIt = images.constBegin(); selIt != imagesEnd; ++selIt, ++pos) + { + KExiv2Iface::KExiv2 exiv2Iface; + KIPI::ImageInfo info = m_interface->info( *selIt ); + // exiv2 load from url not image + KURL url = *selIt; + exiv2Iface.load(url.path()); + double alt, lat, lng; + bool hasGPSInfo = exiv2Iface.getGPSInfo(alt, lat, lng); + if ( hasGPSInfo ) + { + // generation de l'image et de l'icone + generateImagesthumb(m_interface,url,kmlAlbum); + } + else + { + logWarning(i18n("No position data for '%1'").arg(info.title())); + defectImage++; + } + m_progressDialog->setProgress(pos, count); + qApp->processEvents(); + } + + if (defectImage) + { + /** @todo if defectImage==count there are no pictures exported, does it worst to continue? */ + QWidget* parent = KApplication::kApplication()->mainWidget(); + KMessageBox::information(parent, i18n("No position data for 1 picture", + "No position data for %n pictures", defectImage)); + } + + /** @todo change to kml or kmz if compressed */ + QFile file( m_tempDestDir + m_KMLFileName + ".kml"); + /** @todo handle file opening problems */ + file.open( IO_WriteOnly ); + QTextStream stream( &file ); // we will serialize the data into the file + stream << kmlDocument->toString(); + file.close(); + + delete kmlDocument; + + KIO::moveAs(m_tempDestDir,m_baseDestDir,false); + logInfo(i18n("Move to final directory")); + m_progressDialog->close(); +} + +/*! + \fn kmlExport::getConfig() + */ +int kmlExport::getConfig() +{ + KConfig config("kipirc"); + config.setGroup("KMLExport Settings"); + + m_localTarget = config.readBoolEntry("localTarget"); + m_optimize_googlemap = config.readBoolEntry("optimize_googlemap"); + m_iconSize = config.readNumEntry("iconSize"); + // googlemapSize = config.readNumEntry("googlemapSize"); + m_size = config.readNumEntry("size"); + + // UrlDestDir have to have the trailing + m_baseDestDir = config.readEntry("baseDestDir"); + m_UrlDestDir = config.readEntry("UrlDestDir"); + m_altitudeMode = config.readNumEntry("Altitude Mode", 0); + m_KMLFileName = config.readEntry("KMLFileName"); + m_GPXtracks = config.readBoolEntry("UseGPXTracks"); + m_GPXFile = config.readEntry("GPXFile"); + m_TimeZone = config.readNumEntry("Time Zone", 12); + m_LineWidth = config.readNumEntry("Line Width", 4); + m_GPXColor = config.readEntry("Track Color", "#17eeee" ); + m_GPXOpacity = config.readNumEntry("Track Opacity", 64 ); + m_GPXAltitudeMode = config.readNumEntry("GPX Altitude Mode", 0); + + KStandardDirs dir; + m_tempDestDir = dir.saveLocation("tmp", "kipi-kmlrexportplugin-" + QString::number(getpid()) + '/'); + m_imageDir = "images/"; + m_googlemapSize = 32; + return 1; +} + +void kmlExport::logInfo(const QString& msg) +{ + m_progressDialog->addedAction(msg, KIPI::ProgressMessage); +} + +void kmlExport::logError(const QString& msg) +{ + m_progressDialog->addedAction(msg, KIPI::ErrorMessage); +} + +void kmlExport::logWarning(const QString& msg) +{ + m_progressDialog->addedAction(msg, KIPI::WarningMessage); + // mWarnings=true; +} + +} //namespace KIPIGPSSyncPlugin diff --git a/kipi-plugins/gpssync/kmlexport.h b/kipi-plugins/gpssync/kmlexport.h new file mode 100644 index 0000000..0dc32f2 --- /dev/null +++ b/kipi-plugins/gpssync/kmlexport.h @@ -0,0 +1,195 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-05-16 + * Description : a tool to export GPS data to KML file. + * + * Copyright (C) 2006-2007 by Stephane Pontier <shadow dot walker at free dot fr> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef KIPIKMLEXPORTKMLEXPORT_H +#define KIPIKMLEXPORTKMLEXPORT_H + +// Qt includes. + +#include <qcolor.h> +#include <qdir.h> +#include <qdom.h> + +// Local includes. + +#include "kmlgpsdataparser.h" + +class QImage; + +namespace KIPI +{ + class BatchProgressDialog; + class Interface; +} + +namespace KIPIGPSSyncPlugin +{ + +/** +Exporter to KML + + @author KIPI dev. team +*/ +class kmlExport +{ + +public: + + kmlExport(KIPI::Interface* interface); + ~kmlExport(); + + bool createDir(QDir dir); + + /*! generate the kml element for pictures with tumbnails + * @param interface the kipi interface + * @param KURL the URL of the picture + * @param kmlAlbum the album used + */ + void generateImagesthumb(KIPI::Interface* interface, const KURL&, QDomElement &kmlAlbum); + + /*! Produce a web-friendly file name + * otherwise, while google earth works fine, maps.google.com may not find pictures and thumbnail + * thank htmlexport + * @param the filename + * @return the webifyed filename + */ + QString webifyFileName(const QString &fileName); + + /*! Generate a square thumbnail from @fullImage of @size x @size pixels + * @param fullImage the original image + * @param size the size of the thumbnail + * @return the thumbnail + */ + QImage generateSquareThumbnail(const QImage& fullImage, int size); + + /*! Generate a square thumbnail from @fullImage of @size x @size pixels + * with a white border + * @param fullImage the original image + * @param size the size of the thumbnail + * @return the thumbnail + */ + QImage generateBorderedThumbnail(const QImage& fullImage, int size); + void addTrack(QDomElement &kmlAlbum); + void generate(); + int getConfig(); + +public: + + bool m_localTarget; + bool m_optimize_googlemap; + bool m_GPXtracks; + + int m_iconSize; + int m_googlemapSize; + int m_size; + int m_altitudeMode; + int m_TimeZone; + int m_LineWidth; + int m_GPXOpacity; + int m_GPXAltitudeMode; + + /** directory used in kmldocument structure */ + QString m_imageDir; + QString m_GPXFile; + QString m_UrlDestDir; + + /** temporary directory where everything will be created */ + QString m_tempDestDir; + + /** directory selected by user*/ + QString m_baseDestDir; + + QString m_imgdir; + QString m_KMLFileName; + + QColor m_GPXColor; + + KIPI::Interface *m_interface; + +private: + + /*! the root document, used to create all QDomElements */ + QDomDocument *kmlDocument; + + /*! the GPS parsed data */ + KMLGPSDataParser m_gpxParser; + + KIPI::BatchProgressDialog *m_progressDialog; + +private: + + void logInfo(const QString& msg); + void logError(const QString& msg); + void logWarning(const QString& msg); + + /*! + * \fn KIPIKMLExport::kmlExport::addKmlElement(QDomElement target, QString tag) + * Add a new element + * @param target the parent element to which add the element + * @param tag the new element name + * @return the New element + */ + QDomElement addKmlElement(QDomElement &target, QString tag) + { + QDomElement kmlElement = kmlDocument->createElement( tag ); + target.appendChild( kmlElement ); + return kmlElement; + } + + /*! + * \fn KIPIKMLExport::kmlExport::addKmlTextElement(QDomElement target, QString tag, QString text) + * Add a new element with a text + * @param target the parent element to which add the element + * @param tag the new element name + * @param text the text content of the new element + * @return the New element + */ + QDomElement addKmlTextElement(QDomElement &target, QString tag, QString text) + { + QDomElement kmlElement = kmlDocument->createElement( tag ); + target.appendChild( kmlElement ); + QDomText kmlTextElement = kmlDocument->createTextNode( text ); + kmlElement.appendChild( kmlTextElement ); + return kmlElement; + } + + /*! + * \fn KIPIKMLExport::kmlExport::addKmlHtmlElement(QDomElement target, QString tag, QString text) + * Add a new element with html content (html entities are escaped and text is wrapped in a CDATA section) + * @param target the parent element to which add the element + * @param tag the new element name + * @param text the HTML content of the new element + * @return the New element + */ + QDomElement addKmlHtmlElement(QDomElement &target, QString tag, QString text) + { + QDomElement kmlElement = kmlDocument->createElement( tag ); + target.appendChild( kmlElement ); + QDomText kmlTextElement = kmlDocument->createCDATASection( text ); + kmlElement.appendChild( kmlTextElement ); + return kmlElement; + } +}; + +} // namespace KIPIGPSSyncPlugin + +#endif // KIPIKMLEXPORTKMLEXPORT_H diff --git a/kipi-plugins/gpssync/kmlexportconfig.cpp b/kipi-plugins/gpssync/kmlexportconfig.cpp new file mode 100644 index 0000000..7e991ef --- /dev/null +++ b/kipi-plugins/gpssync/kmlexportconfig.cpp @@ -0,0 +1,479 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-05-16 + * Description : a tool to export GPS data to KML file. + * + * Copyright (C) 2006-2007 by Stephane Pontier <shadow dot walker at free dot fr> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// Qt includes. + +#include <qbuttongroup.h> +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qgroupbox.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qlineedit.h> +#include <qpushbutton.h> +#include <qradiobutton.h> +#include <qtooltip.h> +#include <qwhatsthis.h> + +// KDE includes. + +#include <kapplication.h> +#include <kconfig.h> +#include <kcolorbutton.h> +#include <kdialog.h> +#include <khelpmenu.h> +#include <klocale.h> +#include <klineedit.h> +#include <knuminput.h> +#include "kpaboutdata.h" +#include <kpopupmenu.h> +#include <kstandarddirs.h> +#include <kurlrequester.h> + +// Local includes. + +#include "pluginsversion.h" +#include "kmlexportconfig.h" +#include "kmlexportconfig.moc" + +namespace KIPIGPSSyncPlugin +{ + +/* + * Constructs a KIPIKMLExport::KMLExportConfig which is a child of 'parent', with the + * name 'name'.' + */ +KMLExportConfig::KMLExportConfig( QWidget* parent, const char* name) + : KDialogBase(Plain, i18n("KML Export"), + Help|Ok|Cancel, Ok, + parent, 0, true, false) +{ + if ( !name ) setName( "KMLExportConfig" ); + setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)0, + (QSizePolicy::SizeType)0, 0, 0, sizePolicy().hasHeightForWidth() ) ); + KMLExportConfigLayout = new QGridLayout ( plainPage(), 1, 3, 0, spacingHint()); + + // -------------------------------------------------------------- + // Target preferences + TargetPreferenceGroupBox = new QGroupBox(0, Qt::Vertical, i18n( "Target Preferences" ), plainPage()); + TargetPreferenceGroupBoxLayout = new QGridLayout( TargetPreferenceGroupBox->layout(), 6, 5, KDialog::spacingHint()); + TargetPreferenceGroupBoxLayout->setAlignment( Qt::AlignTop ); + + // target type + buttonGroupTargetType = new QButtonGroup(0, Qt::Vertical, i18n( "Target Type" ), TargetPreferenceGroupBox, "buttonGroupTargetType" ); + buttonGroupTargetTypeLayout = new QGridLayout( buttonGroupTargetType->layout(), 2, 1, KDialog::spacingHint() ); + buttonGroupTargetTypeLayout->setAlignment( Qt::AlignTop ); + + LocalTargetRadioButton_ = new QRadioButton( i18n( "&Local or web target used by GoogleEarth" ), + buttonGroupTargetType, "LocalTargetRadioButton_" ); + LocalTargetRadioButton_->setChecked( true ); + buttonGroupTargetTypeLayout->addWidget( LocalTargetRadioButton_, 0, 0 ); + + GoogleMapTargetRadioButton_ = new QRadioButton( i18n( "Web target used by GoogleMap" ), + buttonGroupTargetType, "GoogleMapTargetRadioButton_" ); + buttonGroupTargetTypeLayout->addWidget( GoogleMapTargetRadioButton_, 1, 0 ); + QToolTip::add( GoogleMapTargetRadioButton_, i18n( + "When using GoogleMap, all image must have complete URL, icons are squared and when drawing a track, only linetrack is exported" ) ); + + // -------------------------------------------------------------- + // target preference, suite + TargetPreferenceGroupBoxLayout->addMultiCellWidget( buttonGroupTargetType, 0, 1, 0, 4 ); + + QLabel *AltitudeLabel_ = new QLabel(i18n("Picture altitude" ), + TargetPreferenceGroupBox, "AltitudeLabel_"); + TargetPreferenceGroupBoxLayout->addMultiCellWidget( AltitudeLabel_, 2, 2, 0, 4 ); + AltitudeCB_ = new QComboBox( false, TargetPreferenceGroupBox ); + AltitudeCB_->insertItem(i18n("clamp to ground")); + AltitudeCB_->insertItem(i18n("relative to ground")); + AltitudeCB_->insertItem(i18n("absolute")); + QWhatsThis::add(AltitudeCB_, i18n("<p>Specifies how pictures are displayed" + "<dl><dt>clamp to ground (default)</dt>" + "<dd>Indicates to ignore an altitude specification</dd>" + "<dt>relative to ground</dt>" + "<dd>Sets the altitude of the element relative to the actual ground " + "elevation of a particular location.</dd>" + "<dt>absolute</dt>" + "<dd>Sets the altitude of the coordinate relative to sea level, regardless " + "of the actual elevation of the terrain beneath the element.</dd></dl>")); + TargetPreferenceGroupBoxLayout->addMultiCellWidget( AltitudeCB_, 2, 2, 2, 4 ); + + destinationDirectoryLabel_ = new QLabel( i18n( "Destination directory" ), + TargetPreferenceGroupBox, "destinationDirectoryLabel_" ); + TargetPreferenceGroupBoxLayout->addMultiCellWidget( destinationDirectoryLabel_, 3, 3, 0, 2 ); + + // DestinationDirectory_ = new QLineEdit( TargetPreferenceGroupBox, "DestinationDirectory_" ); + DestinationDirectory_= new KURLRequester( TargetPreferenceGroupBox, "DestinationDirectory_"); + DestinationDirectory_->setCaption(i18n("Select a directory to save the kml file and pictures")); + DestinationDirectory_->setMode(KFile::Directory | KFile::LocalOnly ); + TargetPreferenceGroupBoxLayout->addMultiCellWidget( DestinationDirectory_, 3, 3, 3, 4 ); + + DestinationUrlLabel_ = new QLabel( i18n( "Destination URL" ), + TargetPreferenceGroupBox, "DestinationUrlLabel_" ); + TargetPreferenceGroupBoxLayout->addMultiCellWidget( DestinationUrlLabel_, 4, 4, 0, 1 ); + + DestinationURL_ = new QLineEdit( TargetPreferenceGroupBox, "DestinationURL_" ); + + TargetPreferenceGroupBoxLayout->addMultiCellWidget( DestinationURL_, 4, 4, 2, 4 ); + FileNameLabel_ = new QLabel( i18n( "File name" ), + TargetPreferenceGroupBox, "FileNameLabel_" ); + TargetPreferenceGroupBoxLayout->addWidget( FileNameLabel_, 5, 0 ); + + FileName_ = new QLineEdit( TargetPreferenceGroupBox, "FileName_" ); + TargetPreferenceGroupBoxLayout->addMultiCellWidget( FileName_, 5, 5, 1, 4 ); + + KMLExportConfigLayout->addWidget( TargetPreferenceGroupBox, 0, 0 ); + + // -------------------------------------------------------------- + // Sizes + QGroupBox *SizeGroupBox = new QGroupBox(0, Qt::Vertical, i18n( "Sizes" ), plainPage() ); + SizeGroupBox->setColumnLayout(0, Qt::Vertical ); + SizeGroupBoxLayout = new QGridLayout( SizeGroupBox->layout(), 2, 3, KDialog::spacingHint() ); + SizeGroupBoxLayout->setAlignment( Qt::AlignTop ); + + IconSizeLabel = new QLabel( i18n( "Icon size" ), + SizeGroupBox, "IconSizeLabel" ); + SizeGroupBoxLayout->addWidget( IconSizeLabel, 0, 0 ); + + IconSizeInput_ = new KIntNumInput( SizeGroupBox, "IconSizeInput_" ); + IconSizeInput_->setValue( 33 ); + SizeGroupBoxLayout->addWidget( IconSizeInput_, 0, 1 ); + + spacer3 = new QSpacerItem( 191, 21, QSizePolicy::Expanding, QSizePolicy::Minimum ); + SizeGroupBoxLayout->addItem( spacer3, 0, 2 ); + + ImageSizeLabel = new QLabel( i18n( "Image size" ), + SizeGroupBox, "ImageSizeLabel" ); + SizeGroupBoxLayout->addWidget( ImageSizeLabel, 1, 0 ); + + ImageSizeInput_ = new KIntNumInput( SizeGroupBox, "ImageSizeInput_" ); + ImageSizeInput_->setValue( 320 ); + SizeGroupBoxLayout->addWidget( ImageSizeInput_, 1, 1 ); + + spacer4 = new QSpacerItem( 191, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); + SizeGroupBoxLayout->addItem( spacer4, 1, 2 ); + + KMLExportConfigLayout->addWidget( SizeGroupBox, 1, 0 ); + + // -------------------------------------------------------------- + // GPX Tracks + QGroupBox *GPXTracksGroupBox = new QGroupBox(0, Qt::Vertical, i18n( "GPX Tracks" ), plainPage()); + QGridLayout *GPXTracksGroupBoxLayout = new QGridLayout(GPXTracksGroupBox->layout(), 5, 4, + KDialog::spacingHint()); + GPXTracksGroupBoxLayout->setAlignment( Qt::AlignTop ); + + // add a gpx track checkbox + GPXTracksCheckBox_ = new QCheckBox( i18n( "Draw GPX track" ), GPXTracksGroupBox, "GPXTracksCheckBox"); + GPXTracksGroupBoxLayout->addMultiCellWidget( GPXTracksCheckBox_, 0, 0, 0, 3); + + // file selector + GPXFileLabel_ = new QLabel( i18n( "GPX file" ), + GPXTracksGroupBox, "GPXFileLabel_" ); + GPXTracksGroupBoxLayout->addMultiCellWidget( GPXFileLabel_, 1, 1, 0, 0); + + GPXFileKURLRequester_ = new KURLRequester( GPXTracksGroupBox, "GPXFileKURLRequester" ); + GPXFileKURLRequester_->setFilter(i18n("%1|GPS Exchange Format").arg("*.gpx")); + GPXFileKURLRequester_->setCaption(i18n("Select GPX File to Load")); + GPXTracksGroupBoxLayout->addMultiCellWidget( GPXFileKURLRequester_, 1, 1, 1, 3); + + timeZoneLabel_ = new QLabel(i18n("Time zone"), GPXTracksGroupBox); + GPXTracksGroupBoxLayout->addMultiCellWidget( timeZoneLabel_, 2, 2, 0, 0); + timeZoneCB = new QComboBox( false, GPXTracksGroupBox ); + timeZoneCB->insertItem(i18n("GMT-12:00"), 0); + timeZoneCB->insertItem(i18n("GMT-11:00"), 1); + timeZoneCB->insertItem(i18n("GMT-10:00"), 2); + timeZoneCB->insertItem(i18n("GMT-09:00"), 3); + timeZoneCB->insertItem(i18n("GMT-08:00"), 4); + timeZoneCB->insertItem(i18n("GMT-07:00"), 5); + timeZoneCB->insertItem(i18n("GMT-06:00"), 6); + timeZoneCB->insertItem(i18n("GMT-05:00"), 7); + timeZoneCB->insertItem(i18n("GMT-04:00"), 8); + timeZoneCB->insertItem(i18n("GMT-03:00"), 9); + timeZoneCB->insertItem(i18n("GMT-02:00"), 10); + timeZoneCB->insertItem(i18n("GMT-01:00"), 11); + timeZoneCB->insertItem(i18n("GMT"), 12); + timeZoneCB->insertItem(i18n("GMT+01:00"), 13); + timeZoneCB->insertItem(i18n("GMT+02:00"), 14); + timeZoneCB->insertItem(i18n("GMT+03:00"), 15); + timeZoneCB->insertItem(i18n("GMT+04:00"), 16); + timeZoneCB->insertItem(i18n("GMT+05:00"), 17); + timeZoneCB->insertItem(i18n("GMT+06:00"), 18); + timeZoneCB->insertItem(i18n("GMT+07:00"), 19); + timeZoneCB->insertItem(i18n("GMT+08:00"), 20); + timeZoneCB->insertItem(i18n("GMT+09:00"), 21); + timeZoneCB->insertItem(i18n("GMT+10:00"), 22); + timeZoneCB->insertItem(i18n("GMT+11:00"), 23); + timeZoneCB->insertItem(i18n("GMT+12:00"), 24); + timeZoneCB->insertItem(i18n("GMT+13:00"), 25); + timeZoneCB->insertItem(i18n("GMT+14:00"), 26); + QWhatsThis::add(timeZoneCB, i18n("<p>Sets the time zone of the camera during " + "picture shooting, so that the time stamps of the GPS " + "can be converted to match the local time")); + GPXTracksGroupBoxLayout->addMultiCellWidget( timeZoneCB, 2, 2, 1, 3); + + GPXLineWidthLabel_ = new QLabel( i18n( "Track width" ), + GPXTracksGroupBox, "GPXLineWidthLabel_" ); + GPXTracksGroupBoxLayout->addMultiCellWidget( GPXLineWidthLabel_, 3, 3, 0, 0); + GPXLineWidthInput_ = new KIntNumInput( GPXTracksGroupBox, "GPXLineWidthInput_" ); + GPXLineWidthInput_->setValue( 4 ); + GPXTracksGroupBoxLayout->addMultiCellWidget( GPXLineWidthInput_, 3, 3, 1, 3); + + GPXColorLabel_ = new QLabel( i18n( "Track color" ), + GPXTracksGroupBox, "GPXColorLabel_" ); + GPXTracksGroupBoxLayout->addWidget( GPXColorLabel_, 4, 0 ); + GPXTrackColor_ = new KColorButton(QColor("#ffffff"), GPXTracksGroupBox); + GPXTracksGroupBoxLayout->addWidget( GPXTrackColor_, 4, 1 ); + + GPXTracksOpacityInput_ = new KIntNumInput( GPXTracksGroupBox , "GPXTracksOpacityInput_" ); + GPXTracksOpacityInput_->setRange( 0, 100, 1, false ); + GPXTracksOpacityInput_->setValue( 100 ); + GPXTracksOpacityInput_->setLabel( i18n( "Opacity:" ), AlignVCenter); + GPXTracksOpacityInput_->setSuffix( QString::fromAscii( "%" ) ); + GPXTracksGroupBoxLayout->addMultiCellWidget( GPXTracksOpacityInput_, 4, 4, 2, 3); + + GPXAltitudeLabel_ = new QLabel( i18n( "Track altitude" ), + GPXTracksGroupBox, "GPXAltitudeLabel_" ); + GPXTracksGroupBoxLayout->addMultiCellWidget( GPXAltitudeLabel_, 5, 5, 0, 0); + GPXAltitudeCB_ = new QComboBox( false, GPXTracksGroupBox ); + GPXAltitudeCB_->insertItem(i18n("clamp to ground")); + GPXAltitudeCB_->insertItem(i18n("relative to ground")); + GPXAltitudeCB_->insertItem(i18n("absolute")); + QWhatsThis::add(GPXAltitudeCB_, i18n("<p>Specifies how the points are displayed" + "<dl><dt>clamp to ground (default)</dt>" + "<dd>Indicates to ignore an altitude specification</dd>" + "<dt>relative to ground</dt>" + "<dd>Sets the altitude of the element relative to the actual ground " + "elevation of a particular location.</dd>" + "<dt>absolute</dt>" + "<dd>Sets the altitude of the coordinate relative to sea level, " + "regardless of the actual elevation of the terrain beneath " + "the element.</dd></dl>")); + GPXTracksGroupBoxLayout->addMultiCellWidget( GPXAltitudeCB_, 5, 5, 1, 3); + + KMLExportConfigLayout->addWidget( GPXTracksGroupBox, 2, 0 ); + + connect( GoogleMapTargetRadioButton_, SIGNAL( toggled(bool) ), + this, SLOT( GoogleMapTargetRadioButton__toggled(bool) ) ); + + connect( GPXTracksCheckBox_, SIGNAL( toggled(bool) ), + this, SLOT( KMLTracksCheckButton__toggled(bool) ) ); + + // -------------------------------------------------------------- + // About data and help button. + m_about = new KIPIPlugins::KPAboutData(I18N_NOOP("KML Export"), + 0, + KAboutData::License_GPL, + I18N_NOOP("A Kipi plugin for kml exporting"), + "(c) 2006-2007, Stéphane Pontier"); + + m_about->addAuthor("Stéphane Pontier", I18N_NOOP("Author"), + "shadow.walker@free.fr"); + + KHelpMenu* helpMenu = new KHelpMenu(this, m_about, false); + helpMenu->menu()->removeItemAt(0); + helpMenu->menu()->insertItem(i18n("Plugin Handbook"), this, SLOT(slotHelp()), 0, -1, 0); + actionButton(Help)->setPopup( helpMenu->menu() ); + + // -------------------------------------------------------------- + // Configuration file management + + config_ = new KConfig("kipirc"); + config_->setGroup("KMLExport Settings"); + + readSettings(); + + // -------------------------------------------------------------- + // Just to initialize the UI + GoogleMapTargetRadioButton__toggled(true); + KMLTracksCheckButton__toggled(false); +} + +/* + * Destroys the object and frees any allocated resources + */ +KMLExportConfig::~KMLExportConfig() +{ + // no need to delete child widgets, Qt does it all for us + if(config_) + delete config_; + delete m_about; +} + +void KMLExportConfig::slotOk() + //void KMLExportConfig::slotOkClicked() +{ + saveSettings(); + + emit okButtonClicked(); + accept(); +} + +void KMLExportConfig::slotHelp() +{ + KApplication::kApplication()->invokeHelp("KMLExport", "kipi-plugins"); +} + +void KMLExportConfig::GoogleMapTargetRadioButton__toggled(bool) +{ + if (GoogleMapTargetRadioButton_->isChecked()) + { + DestinationUrlLabel_->setEnabled(true); + DestinationURL_->setEnabled(true); + IconSizeLabel->setEnabled(false); + IconSizeInput_->setEnabled(false); + } + else + { + DestinationUrlLabel_->setEnabled(false); + DestinationURL_->setEnabled(false); + IconSizeLabel->setEnabled(true); + IconSizeInput_->setEnabled(true); + } +} + +void KMLExportConfig::KMLTracksCheckButton__toggled(bool) +{ + if (GPXTracksCheckBox_->isChecked()) + { + GPXFileKURLRequester_->setEnabled(true); + GPXFileLabel_->setEnabled(true); + timeZoneCB->setEnabled(true); + GPXColorLabel_->setEnabled(true); + GPXAltitudeLabel_->setEnabled(true); + timeZoneLabel_->setEnabled(true); + GPXAltitudeCB_->setEnabled(true); + GPXTrackColor_->setEnabled(true); + GPXLineWidthLabel_->setEnabled(true); + GPXLineWidthInput_->setEnabled(true); + GPXTracksOpacityInput_->setEnabled(true); + } + else + { + GPXFileKURLRequester_->setEnabled(false); + GPXFileLabel_->setEnabled(false); + timeZoneCB->setEnabled(false); + GPXColorLabel_->setEnabled(false); + GPXAltitudeLabel_->setEnabled(false); + timeZoneLabel_->setEnabled(false); + GPXAltitudeCB_->setEnabled(false); + GPXTrackColor_->setEnabled(false); + GPXLineWidthLabel_->setEnabled(false); + GPXLineWidthInput_->setEnabled(false); + GPXTracksOpacityInput_->setEnabled(false); + } +} + +void KMLExportConfig::saveSettings() +{ + if (!config_) return; + + config_->writeEntry("localTarget", LocalTargetRadioButton_->isChecked()); + config_->writeEntry("optimize_googlemap", GoogleMapTargetRadioButton_->isChecked()); + config_->writeEntry("iconSize", IconSizeInput_->value()); + config_->writeEntry("size", ImageSizeInput_->value()); + QString destination = DestinationDirectory_->url(); + if (!destination.endsWith("/")) + { + destination.append("/"); + } + config_->writeEntry("baseDestDir",destination); + QString url = DestinationURL_->text(); + if (!url.endsWith("/")) + { + url.append("/"); + } + config_->writeEntry("UrlDestDir",url); + config_->writeEntry("KMLFileName",FileName_->text()); + config_->writeEntry("Altitude Mode", AltitudeCB_->currentItem() ); + + config_->writeEntry("UseGPXTracks", GPXTracksCheckBox_->isChecked()); + + config_->writeEntry("GPXFile", GPXFileKURLRequester_->lineEdit()->originalText()); + config_->writeEntry("Time Zone", timeZoneCB->currentItem() ); + config_->writeEntry("Line Width", GPXLineWidthInput_->value()); + config_->writeEntry("Track Color", GPXTrackColor_->color().name () ); + config_->writeEntry("Track Opacity", GPXTracksOpacityInput_->value() ); + config_->writeEntry("GPX Altitude Mode", GPXAltitudeCB_->currentItem() ); + + config_->sync(); +} + +void KMLExportConfig::readSettings() +{ + bool localTarget; + bool optimize_googlemap; + int iconSize; + // int googlemapSize; + int size; + QString UrlDestDir; + QString baseDestDir; + QString KMLFileName; + int AltitudeMode; + + bool GPXtracks; + QString GPXFile; + int TimeZone; + int LineWidth; + QString GPXColor; + int GPXOpacity; + int GPXAltitudeMode; + + localTarget = config_->readBoolEntry("localTarget", true); + optimize_googlemap = config_->readBoolEntry("optimize_googlemap", false); + iconSize = config_->readNumEntry("iconSize", 33); + // not saving this size as it should not change + // googlemapSize = config_->readNumEntry("googlemapSize", 32); + size = config_->readNumEntry("size", 320); + // UrlDestDir have to have the trailing / + baseDestDir = config_->readEntry("baseDestDir", "/tmp/"); + UrlDestDir = config_->readEntry("UrlDestDir", "http://www.example.com/"); + KMLFileName = config_->readEntry("KMLFileName", "kmldocument"); + AltitudeMode = config_->readNumEntry("Altitude Mode", 0); + + GPXtracks = config_->readBoolEntry("UseGPXTracks", false); + GPXFile = config_->readEntry("GPXFile", ""); + TimeZone = config_->readNumEntry("Time Zone", 12); + LineWidth = config_->readNumEntry("Line Width", 4); + GPXColor = config_->readEntry("Track Color", "#17eeee" ); + GPXOpacity = config_->readNumEntry("Track Opacity", 64 ); + GPXAltitudeMode = config_->readNumEntry("GPX Altitude Mode", 0); + + // -- Apply Settings to widgets ------------------------------ + + LocalTargetRadioButton_->setChecked(localTarget); + GoogleMapTargetRadioButton_->setChecked(optimize_googlemap); + + IconSizeInput_->setValue(iconSize); + ImageSizeInput_->setValue(size); + + AltitudeCB_->setCurrentItem(AltitudeMode); + DestinationDirectory_->setURL(baseDestDir); + DestinationURL_->setText(UrlDestDir); + FileName_->setText(KMLFileName); + + timeZoneCB->setCurrentItem(TimeZone); + GPXLineWidthInput_->setValue(LineWidth); + GPXTrackColor_->setColor(GPXColor); + GPXTracksOpacityInput_->setValue(GPXOpacity); + GPXAltitudeCB_->setCurrentItem(GPXAltitudeMode); +} + +} //namespace KIPIGPSSyncPlugin diff --git a/kipi-plugins/gpssync/kmlexportconfig.h b/kipi-plugins/gpssync/kmlexportconfig.h new file mode 100644 index 0000000..54841b7 --- /dev/null +++ b/kipi-plugins/gpssync/kmlexportconfig.h @@ -0,0 +1,138 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-05-16 + * Description : a tool to export GPS data to KML file. + * + * Copyright (C) 2006-2007 by Stephane Pontier <shadow dot walker at free dot fr> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef KMLEXPORTCONFIG_H +#define KMLEXPORTCONFIG_H + +// KDE includes. + +#include <kdialogbase.h> + +class QButtonGroup; +class QCheckBox; +class QComboBox; +class QGridLayout; +class QGroupBox; +class QLabel; +class QLineEdit; +class QRadioButton; +class QSpacerItem; + +class KColorButton; +class KIntNumInput; +class KURLRequester; + +namespace KIPIPlugins +{ + class KPAboutData; +} + +namespace KIPIGPSSyncPlugin +{ + +class KMLExportConfig : public KDialogBase +{ + Q_OBJECT + +public: + + explicit KMLExportConfig( QWidget* parent = 0, const char* name = 0); + ~KMLExportConfig(); + +public: + + QLabel *ImageSizeLabel; + QLabel *IconSizeLabel; + QLabel *destinationDirectoryLabel_; + QLabel *FileNameLabel_; + QLabel *DestinationUrlLabel_; + QLabel *GPXFileLabel_; + QLabel *timeZoneLabel_; + QLabel *GPXLineWidthLabel_; + QLabel *GPXColorLabel_; + QLabel *GPXTracksOpacityLabel_; + QLabel *GPXAltitudeLabel_; + + QGroupBox *TargetPreferenceGroupBox; + + QButtonGroup *buttonGroupTargetType; + + QRadioButton *LocalTargetRadioButton_; + QRadioButton *GoogleMapTargetRadioButton_; + + QLineEdit *DestinationURL_; + QLineEdit *FileName_; + + QCheckBox *GPXTracksCheckBox_; + + QComboBox *AltitudeCB_; + QComboBox *timeZoneCB; + QComboBox *GPXAltitudeCB_; + + KColorButton *GPXTrackColor_; + + KURLRequester *DestinationDirectory_; + KURLRequester *GPXFileKURLRequester_; + + KIntNumInput *ImageSizeInput_; + KIntNumInput *IconSizeInput_; + KIntNumInput *GPXTracksOpacityInput_; + KIntNumInput *GPXLineWidthInput_; + +public slots: + + void GoogleMapTargetRadioButton__toggled(bool); + void KMLTracksCheckButton__toggled(bool); + +signals: + + void okButtonClicked(); // Signal needed by plugin_kmlexport class + +protected: + + QSpacerItem *spacer3; + QSpacerItem *spacer4; + + QGridLayout *KMLExportConfigLayout; + QGridLayout *SizeGroupBoxLayout; + QGridLayout *TargetPreferenceGroupBoxLayout; + QGridLayout *buttonGroupTargetTypeLayout; + + KConfig *config_; + + KIPIPlugins::KPAboutData *m_about; + +protected: + + void saveSettings(); + void readSettings(); + +protected slots: + + void slotOk(); + // void slotOkClicked(); + void slotHelp(); +}; + +} // namespace + +#endif // KMLEXPORTCONFIG_H diff --git a/kipi-plugins/gpssync/kmlgpsdataparser.cpp b/kipi-plugins/gpssync/kmlgpsdataparser.cpp new file mode 100644 index 0000000..4a796a9 --- /dev/null +++ b/kipi-plugins/gpssync/kmlgpsdataparser.cpp @@ -0,0 +1,141 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-05-16 + * Description : a tool to export GPS data to KML file. + * + * Copyright (C) 2006-2007 by Stephane Pontier <shadow dot walker at free dot fr> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// Local includes. + +#include "kmlgpsdataparser.h" + +// KDE includes. + +#include <kdebug.h> +#include <klocale.h> + +namespace KIPIGPSSyncPlugin +{ + +KMLGPSDataParser::KMLGPSDataParser() + : GPSDataParser() +{ +} + +KMLGPSDataParser::~KMLGPSDataParser() +{ +} + +QString KMLGPSDataParser::lineString() +{ + QString line = ""; + // cache the end to not recalculate it with large number of points + GPSDataMap::ConstIterator end (m_GPSDataMap.constEnd()); + for (GPSDataMap::ConstIterator it = m_GPSDataMap.constBegin(); + it != end; ++it ) + { + line += QString("%1,%2,%3 ").arg(it.data().longitude()).arg(it.data().latitude()).arg(it.data().altitude()); + } + return line; +} + +/*! +\fn void KIPIGPSSyncPlugin::KMLGPSDataParser::CreateTrackLine(QDomElement &parent, QDomDocument &root, int altitudeMode) + */ +void KIPIGPSSyncPlugin::KMLGPSDataParser::CreateTrackLine(QDomElement &parent, QDomDocument &root, int altitudeMode) +{ + kmlDocument = &root; + + // add the linetrack + QDomElement kmlPlacemark = addKmlElement(parent, "Placemark"); + addKmlTextElement(kmlPlacemark, "name", i18n("Track")); + QDomElement kmlLineString = addKmlElement(kmlPlacemark, "LineString"); + addKmlTextElement(kmlLineString, "coordinates", lineString()); + addKmlTextElement(kmlPlacemark, "styleUrl", "#linetrack"); + if (altitudeMode == 2 ) + { + addKmlTextElement(kmlLineString, "altitudeMode", "absolute"); + } + else if (altitudeMode == 1 ) + { + addKmlTextElement(kmlLineString, "altitudeMode", "relativeToGround"); + } + else + { + addKmlTextElement(kmlLineString, "altitudeMode", "clampToGround"); + } +} + +/*! +\fn void KIPIGPSSyncPlugin::KMLGPSDataParser::CreateTrackPoints(QDomElement &parent, QDomDocument &root, int timeZone, int altitudeMode) + */ +void KIPIGPSSyncPlugin::KMLGPSDataParser::CreateTrackPoints(QDomElement &parent, QDomDocument &root, + int timeZone, int altitudeMode) +{ + kmlDocument = &root; + kdDebug( 51001 ) << "creation d'un trackpoint" << endl; + + // create the points + QDomElement kmlPointsFolder = addKmlElement(parent, "Folder"); + addKmlTextElement(kmlPointsFolder, "name", i18n("Points")); + addKmlTextElement(kmlPointsFolder, "visibility", "0"); + addKmlTextElement(kmlPointsFolder, "open", "0"); + int i = 0; + // cache the end to not recalculate it with large number of points + GPSDataMap::ConstIterator end (m_GPSDataMap.constEnd()); + for (GPSDataMap::ConstIterator it = m_GPSDataMap.constBegin(); + it != end; ++it, i++) + { + QDomElement kmlPointPlacemark = addKmlElement(kmlPointsFolder, "Placemark"); + addKmlTextElement(kmlPointPlacemark, "name", QString("%1 %2 ").arg(i18n("Point")).arg(i)); + addKmlTextElement(kmlPointPlacemark, "styleUrl", "#track"); + QDomElement kmlTimeStamp = addKmlElement(kmlPointPlacemark, "TimeStamp"); + // GPS device are sync in time by satellite using GMT time. + // If the camera time is different than GMT time, we want to + // convert the GPS time to localtime of the picture to be display + // in the same timeframe + QDateTime GPSLocalizedTime = it.key().addSecs(timeZone*3600); + + addKmlTextElement(kmlTimeStamp, "when", GPSLocalizedTime.toString("yyyy-MM-ddThh:mm:ssZ")); + QDomElement kmlGeometry = addKmlElement(kmlPointPlacemark, "Point"); + addKmlTextElement(kmlPointPlacemark, "visibility", "0"); + if (it.data().latitude()) + { + addKmlTextElement(kmlGeometry, "coordinates", + QString("%1,%2,%3").arg(it.data().longitude()).arg(it.data().latitude()).arg(it.data().altitude())); + } + else + { + addKmlTextElement(kmlGeometry, "coordinates", QString("%1,%2").arg(it.data().longitude()).arg(it.data().latitude())); + } + if (altitudeMode == 2 ) + { + addKmlTextElement(kmlGeometry, "altitudeMode", "absolute"); + } + else if (altitudeMode == 1 ) + { + addKmlTextElement(kmlGeometry, "altitudeMode", "relativeToGround"); + } + else + { + addKmlTextElement(kmlGeometry, "altitudeMode", "clampToGround"); + } + } +} + +} // namespace KIPIGPSSyncPlugin diff --git a/kipi-plugins/gpssync/kmlgpsdataparser.h b/kipi-plugins/gpssync/kmlgpsdataparser.h new file mode 100644 index 0000000..e3e271e --- /dev/null +++ b/kipi-plugins/gpssync/kmlgpsdataparser.h @@ -0,0 +1,107 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-05-16 + * Description : a tool to export GPS data to KML file. + * + * Copyright (C) 2006-2007 by Stephane Pontier <shadow dot walker at free dot fr> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef KIPIGPSSYNCPLUGINKMLGPSDATAPARSER_H +#define KIPIGPSSYNCPLUGINKMLGPSDATAPARSER_H + +// Local includes. + +#include "gpsdataparser.h" + +// Qt includes. + +#include <qdom.h> + +namespace KIPIGPSSyncPlugin +{ + +/*! a classe derivated from GPSDataParser mainly to transform GPS data to KML + * @author Stéphane Pontier shadow.walker@free.fr + */ +class KMLGPSDataParser : public GPSDataParser +{ + +public: + + KMLGPSDataParser(); + ~KMLGPSDataParser(); + + /*! KIPIGPSSyncPlugin::KMLGPSDataParser::lineString() + * @return the string containing the time ordered point (lon,lat,alt) + */ + QString lineString(); + /*! Create a KML Element that will contain the linetrace of the GPS + * @param parent the QDomElement to which the track will be added + * @param root the QDomDocument used to create all elements + * @param altitudeMode altitude mode of the line and points + */ + + void CreateTrackLine(QDomElement &parent, QDomDocument &root, int altitudeMode); + /*! Create a KML Element that will contain the points and of the GPS + * @param parent the QDomElement to which the track will be added + * @param root the QDomDocument used to create all elements + * @param timeZone the Timezone of the pictures + * @param altitudeMode altitude mode of the line and points + */ + void CreateTrackPoints(QDomElement &parent, QDomDocument &root, int timeZone, int altitudeMode); + +private: + + /*! @todo maybe initialize it in the constructor */ + /*! the root document, used to create all QDomElements */ + QDomDocument *kmlDocument; + + /*! + \fn QDomElement KIPIGPSSyncPlugin::KMLGPSDataParser::addKmlElement(QDomElement target, QString tag) + * Add a new element + * @param target the parent element to which add the element + * @param tag the new element name + * @return the New element + */ + QDomElement addKmlElement(QDomElement &target, QString tag) + { + QDomElement kmlElement = kmlDocument->createElement( tag ); + target.appendChild( kmlElement ); + return kmlElement; + } + + /*! + \fn QDomElement KIPIGPSSyncPlugin::KMLGPSDataParser::addKmlTextElement(QDomElement target, QString tag, QString text) + * Add a new element with a text + * @param target the parent element to which add the element + * @param tag the new element name + * @param text the text content of the new element + * @return the New element + */ + QDomElement addKmlTextElement(QDomElement &target, QString tag, QString text) + { + QDomElement kmlElement = kmlDocument->createElement( tag ); + target.appendChild( kmlElement ); + QDomText kmlTextElement = kmlDocument->createTextNode( text ); + kmlElement.appendChild( kmlTextElement ); + return kmlElement; + } +}; + +} // namespace KIPIGPSSyncPlugin + +#endif // KIPIGPSSYNCPLUGINKMLGPSDATAPARSER_H diff --git a/kipi-plugins/gpssync/plugin_gpssync.cpp b/kipi-plugins/gpssync/plugin_gpssync.cpp new file mode 100644 index 0000000..15c7f51 --- /dev/null +++ b/kipi-plugins/gpssync/plugin_gpssync.cpp @@ -0,0 +1,342 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-05-16 + * Description : a plugin to synchronize pictures with + * a GPS device. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// Qt includes. + +#include <qfileinfo.h> + +// KDE includes. + +#include <klocale.h> +#include <kaction.h> +#include <kapplication.h> +#include <kgenericfactory.h> +#include <klibloader.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kmessagebox.h> + +// LibKIPI includes. + +#include <libkipi/imagecollection.h> + +// LibKExiv2 includes. + +#include <libkexiv2/kexiv2.h> + +// Local includes. + +#include "gpsbabelbinary.h" +#include "gpsdatacontainer.h" +#include "gpseditdialog.h" +#include "gpssyncdialog.h" +#include "kmlexport.h" +#include "kmlexportconfig.h" +#include "plugin_gpssync.h" +#include "plugin_gpssync.moc" + +typedef KGenericFactory<Plugin_GPSSync> Factory; + +K_EXPORT_COMPONENT_FACTORY( kipiplugin_gpssync, Factory("kipiplugin_gpssync")) + +Plugin_GPSSync::Plugin_GPSSync(QObject *parent, const char*, const QStringList&) + : KIPI::Plugin( Factory::instance(), parent, "GPSSync") +{ + kdDebug( 51001 ) << "Plugin_GPSSync plugin loaded" << endl; +} + +void Plugin_GPSSync::setup( QWidget* widget ) +{ + KIPI::Plugin::setup( widget ); + + m_action_geolocation = new KActionMenu(i18n("Geolocation"), + 0, + actionCollection(), + "geolocation"); + + m_action_geolocation->insert(new KAction (i18n("Correlator..."), + "gpsimagetag", + 0, + this, + SLOT(slotGPSSync()), + actionCollection(), + "gpssync")); + + m_action_geolocation->insert(new KAction (i18n("Edit Coordinates..."), + 0, + 0, + this, + SLOT(slotGPSEdit()), + actionCollection(), + "gpsedit")); + + m_action_geolocation->insert(new KAction (i18n("Remove Coordinates..."), + 0, + 0, + this, + SLOT(slotGPSRemove()), + actionCollection(), + "gpsremove")); + + addAction( m_action_geolocation ); + + // this is our action shown in the menubar/toolbar of the mainwindow + m_actionKMLExport = new KAction (i18n("KML Export..."), + "www", // icon + 0, // do never set shortcuts from plugins. + this, + SLOT(slotKMLExport()), + actionCollection(), + "kmlexport"); + + addAction( m_actionKMLExport ); + + m_interface = dynamic_cast< KIPI::Interface* >( parent() ); + + if ( !m_interface ) + { + kdError( 51000 ) << "Kipi interface is null!" << endl; + return; + } + + KIPI::ImageCollection selection = m_interface->currentSelection(); + m_action_geolocation->setEnabled( selection.isValid() && !selection.images().isEmpty() ); + + connect( m_interface, SIGNAL(selectionChanged(bool)), + m_action_geolocation, SLOT(setEnabled(bool))); +} + +bool Plugin_GPSSync::checkBinaries(QString &gpsBabelVersion) +{ + KIPIGPSSyncPlugin::GPSBabelBinary gpsBabelBinary; + gpsBabelVersion = gpsBabelBinary.version(); + + if (!gpsBabelBinary.isAvailable()) + { + KMessageBox::information( + kapp->activeWindow(), + i18n("<qt><p>Unable to find the gpsbabel executable:<br> " + "This program is required by this plugin to support GPS data file decoding. " + "Please install gpsbabel as a package from your distributor " + "or <a href=\"%1\">download the source</a>.</p>" + "<p>Note: at least, gpsbabel version %2 is required by this plugin.</p></qt>") + .arg("http://www.gpsbabel.org") + .arg(gpsBabelBinary.minimalVersion()), + QString::null, + QString::null, + KMessageBox::Notify | KMessageBox::AllowLink); + return false; + } + + if (!gpsBabelBinary.versionIsRight()) + { + KMessageBox::information( + kapp->activeWindow(), + i18n("<qt><p>gpsbabel executable is not up to date:<br> " + "The version %1 of gpsbabel have been found on your computer. " + "This version is too old to run properly with this plugin. " + "Please update gpsbabel as a package from your distributor " + "or <a href=\"%2\">download the source</a>.</p>" + "<p>Note: at least, gpsbabel version %3 is required by this " + "plugin</p></qt>") + .arg(gpsBabelVersion) + .arg("http://www.gpsbabel.org") + .arg(gpsBabelBinary.minimalVersion()), + QString::null, + QString::null, + KMessageBox::Notify | KMessageBox::AllowLink); + return false; + } + + return true; +} + +void Plugin_GPSSync::slotGPSSync() +{ + KIPI::ImageCollection images = m_interface->currentSelection(); + + if ( !images.isValid() || images.images().isEmpty() ) + return; + + /* NOTE: this plugin do not use yet GPSBabel to convert GPS data file to GPX + QString gpsBabelVersion; + if (!checkBinaries(gpsBabelVersion)) + return; + */ + + KIPIGPSSyncPlugin::GPSSyncDialog *dialog = new KIPIGPSSyncPlugin::GPSSyncDialog( + m_interface, kapp->activeWindow()); + + dialog->setImages( images.images() ); + dialog->show(); +} + +void Plugin_GPSSync::slotGPSEdit() +{ + KIPI::ImageCollection images = m_interface->currentSelection(); + + if ( !images.isValid() || images.images().isEmpty() ) + return; + + KURL img = images.images().first(); + KExiv2Iface::KExiv2 exiv2Iface; + exiv2Iface.load(img.path()); + double alt, lat, lng; + bool hasGPSInfo = exiv2Iface.getGPSInfo(alt, lat, lng); + KIPIGPSSyncPlugin::GPSDataContainer gpsData(alt, lat, lng, false); + + KIPIGPSSyncPlugin::GPSEditDialog dlg(kapp->activeWindow(), + gpsData, img.fileName(), hasGPSInfo); + + if (dlg.exec() == KDialogBase::Accepted) + { + gpsData = dlg.getGPSInfo(); + KURL::List imageURLs = images.images(); + KURL::List updatedURLs; + QStringList errorFiles; + + for( KURL::List::iterator it = imageURLs.begin() ; + it != imageURLs.end(); ++it) + { + KURL url = *it; + + bool ret = true; + ret &= exiv2Iface.load(url.path()); + if (ret) + { + ret &= exiv2Iface.setGPSInfo(gpsData.altitude(), + gpsData.latitude(), + gpsData.longitude()); + ret &= exiv2Iface.save(url.path()); + } + + if (!ret) + errorFiles.append(url.fileName()); + else + updatedURLs.append(url); + } + + // We use kipi interface refreshImages() method to tell to host than + // metadata from pictures have changed and need to be re-readed. + + m_interface->refreshImages(updatedURLs); + + if (!errorFiles.isEmpty()) + { + KMessageBox::errorList( + kapp->activeWindow(), + i18n("Unable to save geographical coordinates into:"), + errorFiles, + i18n("Edit Geographical Coordinates")); + } + } +} + +void Plugin_GPSSync::slotGPSRemove() +{ + KIPI::ImageCollection images = m_interface->currentSelection(); + + if ( !images.isValid() || images.images().isEmpty() ) + return; + + if (KMessageBox::warningYesNo( + kapp->activeWindow(), + i18n("Geographical coordinates will be definitively removed from all selected images.\n" + "Do you want to continue ?"), + i18n("Remove Geographical Coordinates")) != KMessageBox::Yes) + return; + + KURL::List imageURLs = images.images(); + KURL::List updatedURLs; + QStringList errorFiles; + + for( KURL::List::iterator it = imageURLs.begin() ; + it != imageURLs.end(); ++it) + { + KURL url = *it; + + bool ret = true; + KExiv2Iface::KExiv2 exiv2Iface; + ret &= exiv2Iface.load(url.path()); + ret &= exiv2Iface.removeGPSInfo(); + ret &= exiv2Iface.save(url.path()); + + if (!ret) + errorFiles.append(url.fileName()); + else + updatedURLs.append(url); + } + + // We use kipi interface refreshImages() method to tell to host than + // metadata from pictures have changed and need to be re-readed. + + m_interface->refreshImages(updatedURLs); + + if (!errorFiles.isEmpty()) + { + KMessageBox::errorList( + kapp->activeWindow(), + i18n("Unable to remove geographical coordinates from:"), + errorFiles, + i18n("Remove Geographical Coordinates")); + } +} + +void Plugin_GPSSync::slotKMLExport() +{ + KIPI::ImageCollection selection = m_interface->currentSelection(); + + if ( !selection.isValid() ) + { + kdDebug( 51000) << "No Selection!" << endl; + } + else + { + KIPIGPSSyncPlugin::KMLExportConfig *kmlExportConfigGui = new KIPIGPSSyncPlugin::KMLExportConfig( + kapp->activeWindow(), i18n("KMLExport").ascii()); + connect(kmlExportConfigGui, SIGNAL(okButtonClicked()), + this, SLOT(slotKMLGenerate())); + kmlExportConfigGui->show(); + } +} + +void Plugin_GPSSync::slotKMLGenerate() +{ + KIPI::ImageCollection selection = m_interface->currentSelection(); + KIPIGPSSyncPlugin::kmlExport myExport(m_interface); + if(!myExport.getConfig()) + return; + myExport.generate(); +} + +KIPI::Category Plugin_GPSSync::category( KAction* action ) const +{ + if ( action == m_action_geolocation ) + return KIPI::IMAGESPLUGIN; + if ( action == m_actionKMLExport ) + return KIPI::EXPORTPLUGIN; + + kdWarning( 51000 ) << "Unrecognized action for plugin category identification" << endl; + return KIPI::IMAGESPLUGIN; // no warning from compiler, please +} diff --git a/kipi-plugins/gpssync/plugin_gpssync.h b/kipi-plugins/gpssync/plugin_gpssync.h new file mode 100644 index 0000000..c12105c --- /dev/null +++ b/kipi-plugins/gpssync/plugin_gpssync.h @@ -0,0 +1,66 @@ +/* ============================================================ + * + * This file is a part of kipi-plugins project + * http://www.kipi-plugins.org + * + * Date : 2006-05-16 + * Description : a plugin to synchronize pictures with + * a GPS device. + * + * Copyright 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +#ifndef PLUGIN_GPSSYNC_H +#define PLUGIN_GPSSYNC_H + +// LibKIPI includes. + +#include <libkipi/plugin.h> + +class KActionMenu; +class KAction; + +class Plugin_GPSSync : public KIPI::Plugin +{ + Q_OBJECT + +public: + + Plugin_GPSSync(QObject *parent, const char* name, const QStringList &args); + + virtual KIPI::Category category( KAction* action ) const; + virtual void setup( QWidget* ); + +protected slots: + + void slotGPSSync(); + void slotGPSEdit(); + void slotGPSRemove(); + void slotKMLGenerate(); + void slotKMLExport(); + +private: + + bool checkBinaries(QString &gpsBabelVersion); + +private: + + KActionMenu *m_action_geolocation; + KAction *m_actionKMLExport; + + KIPI::Interface *m_interface; +}; + +#endif // PLUGIN_GPSSYNC_H |