Commit f54bb471 authored by Volker Krause's avatar Volker Krause
Browse files

Allow to import favorite locations from GPX files

That is useful for example for importing favorites exported from
Nextcloud Maps.
parent 7afcc433
......@@ -110,6 +110,7 @@ if (ANDROID)
dialog-cancel
dialog-close
document-edit
document-import
document-open
document-save
documentinfo
......
......@@ -43,6 +43,14 @@ Kirigami.Page {
nameFilters: [i18n("GPX Files (*.gpx)")]
onAccepted: FavoriteLocationModel.exportToGpx(file)
}
Platform.FileDialog {
id: favoriteGpxImportDialog
fileMode: Platform.FileDialog.OpenFile
title: i18n("Import Favorite Locations")
folder: Platform.StandardPaths.writableLocation(StandardPaths.DocumentsLocation)
nameFilters: [i18n("GPX Files (*.gpx)")]
onAccepted: FavoriteLocationModel.importFromGpx(file)
}
actions.main: Kirigami.Action {
icon.name: "crosshairs"
......@@ -81,6 +89,11 @@ Kirigami.Page {
text: i18n("Export to GPX")
icon.name: "export-symbolic"
onTriggered: favoriteGpxExportDialog.open()
},
Kirigami.Action {
text: i18n("Import from GPX")
icon.name: "document-import"
onTriggered: favoriteGpxImportDialog.open()
}
]
......
......@@ -9,6 +9,10 @@
#include "json.h"
#include "logging.h"
#include <gpx/gpxreader.h>
#include <KItinerary/LocationUtil>
#include <KLocalizedString>
#include <QDebug>
......@@ -156,6 +160,19 @@ void FavoriteLocationModel::appendNewLocation()
saveLocations();
}
void FavoriteLocationModel::appendLocationIfMissing(FavoriteLocation &&loc)
{
for (const auto &l : m_locations) {
if (KItinerary::LocationUtil::distance(l.latitude(), l.longitude(), loc.latitude(), loc.longitude()) < 10) {
qCDebug(Log) << "Not importing" << loc.name() << "due to" << l.name() << "close by";
return;
}
}
beginInsertRows({}, rowCount(), rowCount());
m_locations.push_back(std::move(loc));
endInsertRows();
}
void FavoriteLocationModel::removeLocation(int row)
{
beginRemoveRows({}, row, row);
......@@ -272,3 +289,55 @@ void FavoriteLocationModel::exportToGpx(const QString &filePath) const
exporter.writeFavoriteLocation(fav);
}
}
void FavoriteLocationModel::importFromGpx(const QString &filePath)
{
if (filePath.isEmpty()) {
return;
}
QFile f(QUrl(filePath).isLocalFile() ? QUrl(filePath).toLocalFile() : filePath);
if (!f.open(QFile::ReadOnly)) {
qCWarning(Log) << f.errorString() << f.fileName();
return;
}
Gpx::Reader reader(&f);
while (reader.readNextStartElement()) {
if (reader.isRootElement()) {
continue;
}
if (reader.isWaypointStart()) {
FavoriteLocation loc;
loc.setLatitude(reader.latitude());
loc.setLongitude(reader.longitude());
QString name, type;
while (reader.readNext()) {
if (reader.isWaypointEnd()) {
break;
}
if (reader.isGpxName()) {
name = reader.gpxName();
} else if (reader.isGpxType()) {
type = reader.gpxType();
}
}
if (name.isEmpty()) {
continue;
}
if (!type.isEmpty()) {
loc.setName(type + QLatin1Char('/') + name);
} else {
loc.setName(name);
}
appendLocationIfMissing(std::move(loc));
continue;
}
reader.skipCurrentElement();
}
saveLocations();
}
......@@ -67,6 +67,8 @@ public:
/** Appends a new dummy location. */
Q_INVOKABLE void appendNewLocation();
/** Appends the given location if there is none yet that matches its position. */
void appendLocationIfMissing(FavoriteLocation &&loc);
/** Removes location at index @row. */
Q_INVOKABLE void removeLocation(int row);
......@@ -79,6 +81,8 @@ public:
/** Export to GPX. */
Q_INVOKABLE void exportToGpx(const QString &filePath) const;
/** Import from GPX. */
Q_INVOKABLE void importFromGpx(const QString &filePath);
int rowCount(const QModelIndex &parent = {}) const override;
QVariant data(const QModelIndex &index, int role) const override;
......
......@@ -2,6 +2,7 @@
# SPDX-License-Identifier: BSD-3-Clause
add_library(GpxIo STATIC
gpxreader.cpp
gpxwriter.cpp
)
target_include_directories(GpxIo PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>")
......
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "gpxreader.h"
bool Gpx::Reader::isRootElement() const
{
return isStartElement() && name() == QLatin1String("gpx");
}
bool Gpx::Reader::isWaypointStart() const
{
return isStartElement() && name() == QLatin1String("wpt");
}
bool Gpx::Reader::isWaypointEnd() const
{
return isEndElement() && name() == QLatin1String("wpt");
}
float Gpx::Reader::latitude() const
{
return attributes().value(QLatin1String("lat")).toFloat();
}
float Gpx::Reader::longitude() const
{
return attributes().value(QLatin1String("lon")).toFloat();
}
bool Gpx::Reader::isGpxName() const
{
return isStartElement() && name() == QLatin1String("name");
}
QString Gpx::Reader::gpxName()
{
return readElementText();
}
bool Gpx::Reader::isGpxType() const
{
return isStartElement() && name() == QLatin1String("type");
}
QString Gpx::Reader::gpxType()
{
return readElementText();
}
/*
SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef GPX_READER_H
#define GPX_READER_H
#include <QXmlStreamReader>
namespace Gpx {
/** GPX reader. */
class Reader : public QXmlStreamReader
{
public:
using QXmlStreamReader::QXmlStreamReader;
/** Returns @c true for the top-level element. */
bool isRootElement() const;
/** Returns @c true if we are positioned on a waypoint start element. */
bool isWaypointStart() const;
/** Returns @c true if we are positioned on a waypoint end element. */
bool isWaypointEnd() const;
/** Returns the latitude of the current waypoint, route point or track point. */
float latitude() const;
/** Returns the longitude of the current waypoint, route point or track point. */
float longitude() const;
/** Returns @c true if the current element is a name property. */
bool isGpxName() const;
/** Returns the value of a GPX name property. */
QString gpxName();
/** Returns @c true if the current element is a GPX type property. */
bool isGpxType() const;
/** Returns the value of a GPX type property. */
QString gpxType();
};
}
#endif // GPX_READER_H
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment