1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-10-06 06:43:22 +02:00

Support all variants of TrekBuddy maps/atlases

Added support for gmi calibration files and arbitrary named tba/map/gmi files.
This commit is contained in:
Martin Tůma 2023-09-13 20:02:24 +02:00
parent b38cf31920
commit 722f3acb1e
7 changed files with 313 additions and 71 deletions

View File

@ -134,6 +134,7 @@ HEADERS += src/common/config.h \
src/map/encmap.h \
src/map/ENC/iso8211.h \
src/map/gemfmap.h \
src/map/gmifile.h \
src/map/oruxmap.h \
src/map/osmdroidmap.h \
src/map/proj/polyconic.h \
@ -347,6 +348,7 @@ SOURCES += src/main.cpp \
src/map/encmap.cpp \
src/map/ENC/iso8211.cpp \
src/map/gemfmap.cpp \
src/map/gmifile.cpp \
src/map/oruxmap.cpp \
src/map/osmdroidmap.cpp \
src/map/proj/polyconic.cpp \

View File

@ -27,6 +27,34 @@ static bool yCmp(OziMap *m1, OziMap *m2)
return TL(m1).y() > TL(m2).y();
}
static QString calibrationFile(const QString &path)
{
QDir dir(path);
QFileInfoList files = dir.entryInfoList(QDir::Files);
for (int i = 0; i < files.size(); i++) {
const QFileInfo &fi = files.at(i);
QString suffix(fi.suffix().toLower());
if (suffix == "map" || suffix == "gmi")
return fi.absoluteFilePath();
}
return QString();
}
static QString tbaFile(const QStringList &files)
{
for (int i = 0; i < files.size(); i++) {
QFileInfo fi(files.at(i));
if (fi.path() == "." && fi.suffix().toLower() == "tba")
return files.at(i);
}
return QString();
}
void Atlas::computeZooms()
{
std::sort(_maps.begin(), _maps.end(), resCmp);
@ -86,7 +114,11 @@ Atlas::Atlas(const QString &fileName, bool TAR, QObject *parent)
_errorString = "Error reading tar file";
return;
}
QString tbaFileName = fi.completeBaseName() + ".tba";
QString tbaFileName(tbaFile(tar.files()));
if (tbaFileName.isNull()) {
_errorString = "No tba file found";
return;
}
ba = tar.file(tbaFileName);
} else {
QFile tbaFile(fileName);
@ -97,7 +129,7 @@ Atlas::Atlas(const QString &fileName, bool TAR, QObject *parent)
ba = tbaFile.readAll();
}
if (!ba.startsWith("Atlas 1.0")) {
_errorString = "Missing or invalid tba file";
_errorString = "Invalid tba file";
return;
}
@ -108,20 +140,23 @@ Atlas::Atlas(const QString &fileName, bool TAR, QObject *parent)
QFileInfoList maps = zdir.entryInfoList(QDir::Dirs
| QDir::NoDotAndDotDot);
for (int i = 0; i < maps.count(); i++) {
QString mapFile = maps.at(i).absoluteFilePath() + "/"
+ maps.at(i).fileName() + ".map";
OziMap *map;
if (tar.isOpen())
map = new OziMap(mapFile, tar, this);
else
map = new OziMap(mapFile, TAR, this);
if (TAR)
map = new OziMap(maps.at(i).absoluteFilePath(), tar, this);
else {
QString cf(calibrationFile(maps.at(i).absoluteFilePath()));
if (cf.isNull()) {
_errorString = "No calibration file found";
return;
}
map = new OziMap(cf, this);
}
if (map->isValid())
_maps.append(map);
else {
_errorString = QString("Error loading map: %1: %2")
.arg(mapFile, map->errorString());
_errorString = QString("%1: %2")
.arg(map->path(), map->errorString());
return;
}
}

97
src/map/gmifile.cpp Normal file
View File

@ -0,0 +1,97 @@
#include "common/csv.h"
#include "pcs.h"
#include "gmifile.h"
static CalibrationPoint calibrationPoint(const QByteArray line)
{
bool xOk, yOk, lonOk, latOk;
QList<QByteArray> list = line.split(';');
if (list.size() != 4)
return CalibrationPoint();
int x = list.at(0).toInt(&xOk);
int y = list.at(1).toInt(&yOk);
double lon = list.at(2).toDouble(&lonOk);
double lat = list.at(3).toDouble(&latOk);
return (xOk && yOk && latOk && lonOk)
? CalibrationPoint(PointD(x, y), Coordinates(lon, lat))
: CalibrationPoint();
}
bool GmiFile::parse(QIODevice &device, QList<CalibrationPoint> &points)
{
int ln = 1;
int width, height;
bool ok;
if (!device.open(QIODevice::ReadOnly)) {
_errorString = device.errorString();
return false;
}
while (!device.atEnd()) {
QByteArray line = device.readLine(4096);
if (ln == 1) {
if (!line.startsWith("Map Calibration data file")) {
_errorString = "Not a GMI file";
return false;
}
} else if (ln == 2)
_image = line.trimmed();
else if (ln == 3) {
width = line.toInt(&ok);
if (!ok || ok <= 0) {
_errorString = "Invalid image width";
return false;
}
} else if (ln == 4) {
height = line.toInt(&ok);
if (!ok || ok <= 0) {
_errorString = "Invalid image height";
return false;
}
_size = QSize(width, height);
} else {
CalibrationPoint cp(calibrationPoint(line));
if (cp.isValid())
points.append(cp);
else
break;
}
ln++;
}
device.close();
return (points.size() >= 2);
}
bool GmiFile::computeTransformation(const QList<CalibrationPoint> &points)
{
QList<ReferencePoint> rp;
Projection proj(GCS::WGS84());
for (int i = 0; i < points.size(); i++)
rp.append(points.at(i).rp(proj));
_transform = Transform(rp);
if (!_transform.isValid()) {
_errorString = _transform.errorString();
return false;
}
return true;
}
GmiFile::GmiFile(QIODevice &file)
{
QList<CalibrationPoint> points;
if (!parse(file, points))
return;
if (!computeTransformation(points))
return;
}

33
src/map/gmifile.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef GMIFILE_H
#define GMIFILE_H
#include "transform.h"
#include "calibrationpoint.h"
class QIODevice;
class GCS;
class GmiFile
{
public:
GmiFile(QIODevice &file);
bool isValid() const {return !_image.isNull() && _transform.isValid();}
const QString &errorString() const {return _errorString;}
const Transform &transform() const {return _transform;}
const QString &image() const {return _image;}
const QSize &size() const {return _size;}
private:
bool parse(QIODevice &device, QList<CalibrationPoint> &points);
bool computeTransformation(const QList<CalibrationPoint> &points);
QString _image;
QSize _size;
Transform _transform;
QString _errorString;
};
#endif // GMIFILE_H

View File

@ -43,6 +43,7 @@ MapList::ParserMap MapList::parsers()
map.insert("rtmap", &RMap::create);
map.insert("map", &MapsforgeMap::create);
map.insert("map", &OziMap::createMAP);
map.insert("gmi", &OziMap::createMAP);
map.insert("kap", &BSBMap::create);
map.insert("kmz", &KMZMap::create);
map.insert("aqm", &AQMMap::create);
@ -173,7 +174,8 @@ QString MapList::formats()
+ qApp->translate("MapList", "Osmdroid SQLite maps") + " (*.sqlite);;"
+ qApp->translate("MapList", "Locus/OsmAnd/RMaps SQLite maps")
+ " (*.sqlitedb);;"
+ qApp->translate("MapList", "TrekBuddy maps/atlases") + " (*.tar *.tba);;"
+ qApp->translate("MapList", "TrekBuddy maps/atlases")
+ " (*.tar *.tba *.gmi *.map);;"
+ qApp->translate("MapList", "GeoTIFF images") + " (*.tif *.tiff);;"
+ qApp->translate("MapList", "World-file georeferenced images")
+ " (*.wld *.jgw *.gfw *.pgw *.tfw);;"

View File

@ -12,42 +12,68 @@
#include "ozf.h"
#include "image.h"
#include "mapfile.h"
#include "gmifile.h"
#include "rectd.h"
#include "ozimap.h"
static QString mapFile(const QStringList &files)
QString OziMap::calibrationFile(const QStringList &files, const QString path,
CalibrationType &type)
{
for (int i = 0; i < files.size(); i++) {
QFileInfo fi(files.at(i));
if (fi.path() == "." && fi.suffix() == "map")
QString suffix(fi.suffix().toLower());
if (path.endsWith(fi.path())) {
if (suffix == "map") {
type = MAP;
return files.at(i);
} else if (suffix == "gmi") {
type = GMI;
return files.at(i);
}
}
}
type = Unknown;
return QString();
}
OziMap::OziMap(const QString &fileName, bool TAR, QObject *parent)
OziMap::OziMap(const QString &fileName, QObject *parent)
: Map(fileName, parent), _img(0), _tar(0), _ozf(0), _zoom(0), _mapRatio(1.0),
_valid(false)
_hasProj(true), _valid(false)
{
QFileInfo fi(fileName);
QString suffix(fi.suffix().toLower());
if (suffix == "tar") {
CalibrationType type;
if (TAR) {
_tar = new Tar(fileName);
if (!_tar->open()) {
_errorString = "Error reading tar file";
return;
}
QStringList files(_tar->files());
QString mapFileName(mapFile(files));
if (mapFileName.isNull()) {
_errorString = "No map file found in tar file";
return;
}
QString cf(calibrationFile(files, ".", type));
QByteArray ba(_tar->file(mapFileName));
if (type == GMI) {
QByteArray ba(_tar->file(cf));
QBuffer buffer(&ba);
GmiFile gmi(buffer);
if (!gmi.isValid()) {
_errorString = gmi.errorString();
return;
} else {
_name = Util::file2name(fileName);
_map.size = gmi.size();
_map.path = gmi.image();
_transform = gmi.transform();
_projection = Projection(GCS::WGS84());
_hasProj = false;
}
} else if (type == MAP) {
QByteArray ba(_tar->file(cf));
QBuffer buffer(&ba);
MapFile mf(buffer);
if (!mf.isValid()) {
@ -60,6 +86,10 @@ OziMap::OziMap(const QString &fileName, bool TAR, QObject *parent)
_projection = mf.projection();
_transform = mf.transform();
}
} else {
_errorString = "No calibration file found";
return;
}
if (!setTileInfo(files))
return;
@ -67,6 +97,8 @@ OziMap::OziMap(const QString &fileName, bool TAR, QObject *parent)
_tar->close();
} else {
QFile file(fileName);
if (suffix == "map") {
MapFile mf(file);
if (!mf.isValid()) {
_errorString = mf.errorString();
@ -78,6 +110,23 @@ OziMap::OziMap(const QString &fileName, bool TAR, QObject *parent)
_projection = mf.projection();
_transform = mf.transform();
}
} else if (suffix == "gmi") {
GmiFile gmi(file);
if (!gmi.isValid()) {
_errorString = gmi.errorString();
return;
} else {
_name = Util::file2name(fileName);
_map.size = gmi.size();
_map.path = gmi.image();
_transform = gmi.transform();
_projection = Projection(GCS::WGS84());
_hasProj = false;
}
} else {
_errorString = "Unknown file type";
return;
}
QDir set(fi.absolutePath() + "/" + "set");
if (set.exists()) {
@ -92,21 +141,15 @@ OziMap::OziMap(const QString &fileName, bool TAR, QObject *parent)
_valid = true;
}
OziMap::OziMap(const QString &fileName, Tar &tar, QObject *parent)
: Map(fileName, parent), _img(0), _tar(0), _ozf(0), _zoom(0), _mapRatio(1.0),
_valid(false)
OziMap::OziMap(const QString &dirName, Tar &tar, QObject *parent)
: Map(dirName, parent), _img(0), _tar(0), _ozf(0), _zoom(0), _mapRatio(1.0),
_hasProj(true), _valid(false)
{
QFileInfo fi(fileName);
QFileInfo map(fi.absolutePath());
QFileInfo layer(map.absolutePath());
QString mapFile = layer.fileName() + "/" + map.fileName() + "/"
+ fi.fileName();
CalibrationType type;
QString cf(calibrationFile(tar.files(), dirName, type));
QByteArray ba = tar.file(mapFile);
if (ba.isNull()) {
_errorString = "Map file not found";
return;
}
if (type == MAP) {
QByteArray ba = tar.file(cf);
QBuffer buffer(&ba);
MapFile mf(buffer);
if (!mf.isValid()) {
@ -118,7 +161,28 @@ OziMap::OziMap(const QString &fileName, Tar &tar, QObject *parent)
_map.size = mf.size();
_projection = mf.projection();
_transform = mf.transform();
_tar = new Tar(fi.absolutePath() + "/" + fi.completeBaseName() + ".tar");
} else if (type == GMI) {
QByteArray ba = tar.file(cf);
QBuffer buffer(&ba);
GmiFile gmi(buffer);
if (!gmi.isValid()) {
_errorString = gmi.errorString();
return;
}
_name = Util::file2name(cf);
_map.size = gmi.size();
_transform = gmi.transform();
_projection = Projection(GCS::WGS84());
_hasProj = false;
} else {
_errorString = "No calibration file found";
return;
}
QFileInfo fi(cf);
QDir dir(dirName);
_tar = new Tar(dir.absoluteFilePath(fi.completeBaseName() + ".tar"));
if (!_tar->open()) {
_errorString = _tar->fileName() + ": error reading tar file";
@ -221,10 +285,11 @@ bool OziMap::setTileInfo(const QStringList &tiles, const QString &path)
void OziMap::load(const Projection &in, const Projection &out,
qreal deviceRatio, bool hidpi)
{
Q_UNUSED(in);
Q_UNUSED(out);
_mapRatio = hidpi ? deviceRatio : 1.0;
if (!_hasProj)
_projection = in;
if (_tar) {
Q_ASSERT(!_tar->isOpen());
@ -413,7 +478,7 @@ Map *OziMap::createTAR(const QString &path, bool *isDir)
if (isDir)
*isDir = false;
return new OziMap(path, true);
return new OziMap(path);
}
Map *OziMap::createMAP(const QString &path, bool *isDir)
@ -421,5 +486,5 @@ Map *OziMap::createMAP(const QString &path, bool *isDir)
if (isDir)
*isDir = false;
return new OziMap(path, false);
return new OziMap(path);
}

View File

@ -14,8 +14,8 @@ class OziMap : public Map
Q_OBJECT
public:
OziMap(const QString &fileName, bool TAR, QObject *parent = 0);
OziMap(const QString &fileName, Tar &tar, QObject *parent = 0);
OziMap(const QString &fileName, QObject *parent = 0);
OziMap(const QString &dirName, Tar &tar, QObject *parent = 0);
~OziMap();
QString name() const {return _name;}
@ -51,6 +51,10 @@ public:
static Map *createMAP(const QString &path, bool *isDir);
private:
enum CalibrationType {
Unknown, MAP, GMI
};
struct ImageInfo {
QSize size;
QString path;
@ -67,6 +71,9 @@ private:
void rescale(int zoom);
static QString calibrationFile(const QStringList &files, const QString path,
CalibrationType &type);
QString _name;
Projection _projection;
Transform _transform;
@ -77,6 +84,7 @@ private:
int _zoom;
QPointF _scale;
qreal _mapRatio;
bool _hasProj;
bool _valid;
QString _errorString;