From b1f104c2ec1c7019103572c0c1f8825d0d224c95 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Martin=20T=C5=AFma?= Date: Thu, 7 Sep 2023 09:31:23 +0200 Subject: [PATCH] Added support for ENC atlases (catalogues) --- gpxsee.pro | 5 + src/common/range.h | 1 - src/map/ENC/atlasdata.cpp | 94 +++++++++++ src/map/ENC/atlasdata.h | 65 ++++++++ src/map/ENC/iso8211.cpp | 107 +++++++------ src/map/ENC/iso8211.h | 88 +++++++---- src/map/ENC/mapdata.cpp | 208 ++++-------------------- src/map/ENC/mapdata.h | 67 +------- src/map/ENC/rastertile.cpp | 179 +++++++++++++-------- src/map/ENC/rastertile.h | 37 +++-- src/map/bitmapline.cpp | 7 + src/map/bitmapline.h | 4 + src/map/encatlas.cpp | 387 +++++++++++++++++++++++++++++++++++++++++++++ src/map/encatlas.h | 98 ++++++++++++ src/map/encjob.h | 42 +++++ src/map/encmap.cpp | 182 ++++++++++++++++++--- src/map/encmap.h | 97 +++++++----- src/map/maplist.cpp | 5 +- 18 files changed, 1211 insertions(+), 462 deletions(-) create mode 100644 src/map/ENC/atlasdata.cpp create mode 100644 src/map/ENC/atlasdata.h create mode 100644 src/map/encatlas.cpp create mode 100644 src/map/encatlas.h create mode 100644 src/map/encjob.h diff --git a/gpxsee.pro b/gpxsee.pro index 0a1ac7eb..cb58b027 100644 --- a/gpxsee.pro +++ b/gpxsee.pro @@ -122,12 +122,15 @@ HEADERS += src/common/config.h \ src/data/twonavparser.h \ src/map/ENC/attributes.h \ src/map/ENC/mapdata.h \ + src/map/ENC/atlasdata.h \ src/map/ENC/objects.h \ src/map/ENC/rastertile.h \ src/map/ENC/style.h \ src/map/IMG/section.h \ src/map/IMG/zoom.h \ src/map/conversion.h \ + src/map/encatlas.h \ + src/map/encjob.h \ src/map/encmap.h \ src/map/ENC/iso8211.h \ src/map/gemfmap.h \ @@ -335,10 +338,12 @@ SOURCES += src/main.cpp \ src/GUI/projectioncombobox.cpp \ src/GUI/passwordedit.cpp \ src/data/twonavparser.cpp \ + src/map/ENC/atlasdata.cpp \ src/map/ENC/mapdata.cpp \ src/map/ENC/rastertile.cpp \ src/map/ENC/style.cpp \ src/map/conversion.cpp \ + src/map/encatlas.cpp \ src/map/encmap.cpp \ src/map/ENC/iso8211.cpp \ src/map/gemfmap.cpp \ diff --git a/src/common/range.h b/src/common/range.h index 611f1309..28dc0e9f 100644 --- a/src/common/range.h +++ b/src/common/range.h @@ -18,7 +18,6 @@ public: int size() const {return (_max - _min);} int min() const {return _min;} int max() const {return _max;} - int mid() const {return _min + size()/2;} bool isValid() const {return size() >= 0;} bool isNull() const {return _min == 0 && _max == 0;} diff --git a/src/map/ENC/atlasdata.cpp b/src/map/ENC/atlasdata.cpp new file mode 100644 index 00000000..712df3b9 --- /dev/null +++ b/src/map/ENC/atlasdata.cpp @@ -0,0 +1,94 @@ +#include "atlasdata.h" + +using namespace ENC; + +bool AtlasData::pointCb(const QString *map, void *context) +{ + PointCTX *ctx = (PointCTX*)context; + + ctx->lock.lock(); + + MapData *cached = ctx->cache.object(map); + + if (!cached) { + MapData *data = new MapData(*map); + data->points(ctx->rect, ctx->points); + if (!ctx->cache.insert(map, data)) + delete data; + } else + cached->points(ctx->rect, ctx->points); + + ctx->lock.unlock(); + + return true; +} + +bool AtlasData::polyCb(const QString *map, void *context) +{ + PolyCTX *ctx = (PolyCTX*)context; + + ctx->lock.lock(); + + MapData *cached = ctx->cache.object(map); + + if (!cached) { + MapData *data = new MapData(*map); + data->polygons(ctx->rect, ctx->polygons); + data->lines(ctx->rect, ctx->lines); + if (!ctx->cache.insert(map, data)) + delete data; + } else { + cached->polygons(ctx->rect, ctx->polygons); + cached->lines(ctx->rect, ctx->lines); + } + + ctx->lock.unlock(); + + return true; +} + +AtlasData::~AtlasData() +{ + MapTree::Iterator it; + for (_tree.GetFirst(it); !_tree.IsNull(it); _tree.GetNext(it)) + delete _tree.GetAt(it); +} + +void AtlasData::addMap(const RectC &bounds, const QString &path) +{ + double min[2], max[2]; + + min[0] = bounds.left(); + min[1] = bounds.bottom(); + max[0] = bounds.right(); + max[1] = bounds.top(); + + _tree.Insert(min, max, new QString(path)); +} + +void AtlasData::polys(const RectC &rect, QList *polygons, + QList *lines) +{ + double min[2], max[2]; + PolyCTX polyCtx(rect, polygons, lines, _cache, _lock); + + min[0] = rect.left(); + min[1] = rect.bottom(); + max[0] = rect.right(); + max[1] = rect.top(); + + _tree.Search(min, max, polyCb, &polyCtx); +} + +void AtlasData::points(const RectC &rect, QList *points) +{ + double min[2], max[2]; + PointCTX pointCtx(rect, points, _cache, _lock); + + min[0] = rect.left(); + min[1] = rect.bottom(); + max[0] = rect.right(); + max[1] = rect.top(); + + _tree.Search(min, max, pointCb, &pointCtx); +} diff --git a/src/map/ENC/atlasdata.h b/src/map/ENC/atlasdata.h new file mode 100644 index 00000000..c9aaea45 --- /dev/null +++ b/src/map/ENC/atlasdata.h @@ -0,0 +1,65 @@ +#ifndef ENC_ATLASDATA_H +#define ENC_ATLASDATA_H + +#include +#include +#include "common/rtree.h" +#include "mapdata.h" + +namespace ENC { + +typedef QCache MapCache; + +class AtlasData +{ +public: + AtlasData(MapCache &cache, QMutex &lock) + : _cache(cache), _lock(lock) {} + ~AtlasData(); + + void addMap(const RectC &bounds, const QString &path); + + void polys(const RectC &rect, QList *polygons, + QList *lines); + void points(const RectC &rect, QList *points); + +private: + typedef RTree MapTree; + + struct PolyCTX + { + PolyCTX(const RectC &rect, QList *polygons, + QList *lines, MapCache &cache, QMutex &lock) + : rect(rect), polygons(polygons), lines(lines), cache(cache), + lock(lock) {} + + const RectC ▭ + QList *polygons; + QList *lines; + MapCache &cache; + QMutex &lock; + }; + + struct PointCTX + { + PointCTX(const RectC &rect, QList *points, + MapCache &cache, QMutex &lock) : rect(rect), points(points), + cache(cache), lock(lock) {} + + const RectC ▭ + QList *points; + MapCache &cache; + QMutex &lock; + }; + + static bool polyCb(const QString *map, void *context); + static bool pointCb(const QString *map, void *context); + + MapTree _tree; + MapCache &_cache; + QMutex &_lock; +}; + +} + +#endif // ENC_ATLASDATA_H diff --git a/src/map/ENC/iso8211.cpp b/src/map/ENC/iso8211.cpp index 8a6abcc1..7ec18b72 100644 --- a/src/map/ENC/iso8211.cpp +++ b/src/map/ENC/iso8211.cpp @@ -66,36 +66,27 @@ bool ISO8211::Field::subfield(const char *name, QByteArray *val, int idx) const return true; } -bool ISO8211::fieldType(const QString &str, int cnt, FieldType &type, int &size) +ISO8211::SubFieldDefinition ISO8211::fieldType(const QString &str, int cnt, + const QByteArray &tag) { - if (str == "A" || str == "I" || str == "R") { - type = String; - size = cnt; - } else if (str == "B") { - type = Array; - size = cnt / 8; - } else if (str == "b11") { - type = U8; - size = 1; - } else if (str == "b12") { - type = U16; - size = 2; - } else if (str == "b14") { - type = U32; - size = 4; - } else if (str == "b21") { - type = S8; - size = 1; - } else if (str == "b22") { - type = S16; - size = 2; - } else if (str == "b24") { - type = S32; - size = 4; - } else - return false; - - return true; + if (str == "A" || str == "I" || str == "R") + return SubFieldDefinition(tag, String, cnt); + else if (str == "B") + return SubFieldDefinition(tag, Array, cnt / 8); + else if (str == "b11") + return SubFieldDefinition(tag, U8, 1); + else if (str == "b12") + return SubFieldDefinition(tag, U16, 2); + else if (str == "b14") + return SubFieldDefinition(tag, U32, 4); + else if (str == "b21") + return SubFieldDefinition(tag, S8, 1); + else if (str == "b22") + return SubFieldDefinition(tag, S16, 2); + else if (str == "b24") + return SubFieldDefinition(tag, S32, 4); + else + return SubFieldDefinition(); } int ISO8211::readDR(QVector &fields) @@ -145,6 +136,8 @@ bool ISO8211::readDDA(const FieldDefinition &def, SubFields &fields) { static QRegularExpression re("(\\d*)(\\w+)\\(*(\\d*)\\)*"); QByteArray ba; + bool repeat = false; + QVector defs; ba.resize(def.size); if (!(_file.seek(def.pos) && _file.read(ba.data(), ba.size()) == ba.size())) @@ -152,7 +145,7 @@ bool ISO8211::readDDA(const FieldDefinition &def, SubFields &fields) QList list(ba.split('\x1f')); if (!list.at(1).isEmpty() && list.at(1).front() == '*') { - fields.setRepeat(true); + repeat = true; list[1].remove(0, 1); } QList tags(list.at(1).split('!')); @@ -161,7 +154,7 @@ bool ISO8211::readDDA(const FieldDefinition &def, SubFields &fields) QRegularExpressionMatchIterator it = re.globalMatch(list.at(2)); int tag = 0; - fields.resize(tags.size()); + defs.resize(tags.size()); while (it.hasNext()) { QRegularExpressionMatch match = it.next(); @@ -183,15 +176,17 @@ bool ISO8211::readDDA(const FieldDefinition &def, SubFields &fields) } for (uint i = 0; i < cnt; i++) { - SubFieldDefinition &f = fields[tag]; - f.tag = tags.at(tag); - if (!fieldType(typeStr, size, f.type, f.size)) + SubFieldDefinition sfd(fieldType(typeStr, size, tags.at(tag))); + if (sfd.type() == Unknown) return false; + defs[tag] = sfd; tag++; } } } + fields = SubFields(defs, repeat); + return true; } @@ -228,8 +223,7 @@ bool ISO8211::readDDR() return true; } -bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def, - const SubFields &fields, Data &data) +bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def, Data &data) { QByteArray ba; @@ -242,22 +236,19 @@ bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def, const char *dp = ba.constData(); const char *ep = ba.constData() + ba.size() - 1; - data.clear(); - data.setFields(&fields); - do { QVector row; - row.resize(fields.size()); + row.resize(data.fields()->size()); - for (int i = 0; i < fields.size(); i++) { - const SubFieldDefinition &f = fields.at(i); + for (int i = 0; i < data.fields()->size(); i++) { + const SubFieldDefinition &f = data.fields()->at(i); - switch (f.type) { + switch (f.type()) { case String: case Array: - if (f.size) { - row[i] = QVariant(QByteArray(dp, f.size)); - dp += f.size; + if (f.size()) { + row[i] = QVariant(QByteArray(dp, f.size())); + dp += f.size(); } else { sp = dp; while (dp < ep && *dp != '\x1f') @@ -290,11 +281,13 @@ bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def, row[i] = QVariant(UINT32(dp)); dp += 4; break; + default: + return false; } } data.append(row); - } while (fields.repeat() && dp < ep); + } while (data.fields()->repeat() && dp < ep); return true; } @@ -317,23 +310,33 @@ bool ISO8211::readRecord(Record &record) for (int i = 0; i < fields.size(); i++) { const FieldDefinition &def = fields.at(i); - Field &f = record[i]; FieldsMap::const_iterator it = _map.find(def.tag); if (it == _map.constEnd()) { - _errorString = QString("%1: unknown record") - .arg(QString(def.tag)); + _errorString = QString("%1: unknown record").arg(QString(def.tag)); return false; } - f.setTag(def.tag); + Data data(&it.value()); - if (!readUDA(pos, def, it.value(), f.rdata())) { + if (!readUDA(pos, def, data)) { _errorString = QString("Error reading %1 record") .arg(QString(def.tag)); return false; } + + record[i] = Field(def.tag, data); } return true; } + + +const ISO8211::Field *ISO8211::field(const Record &record, const QByteArray &name) +{ + for (int i = 0; i < record.size(); i++) + if (record.at(i).tag() == name) + return &record.at(i); + + return 0; +} diff --git a/src/map/ENC/iso8211.h b/src/map/ENC/iso8211.h index 14d3abcc..9ecd89d2 100644 --- a/src/map/ENC/iso8211.h +++ b/src/map/ENC/iso8211.h @@ -17,60 +17,87 @@ namespace ENC { class ISO8211 { public: - enum FieldType {String, Array, S8, S16, S32, U8, U16, U32}; + enum FieldType {Unknown, String, Array, S8, S16, S32, U8, U16, U32}; - struct FieldDefinition { + struct FieldDefinition + { QByteArray tag; int pos; int size; }; - struct SubFieldDefinition { - QByteArray tag; - FieldType type; - int size; + class SubFieldDefinition + { + public: + SubFieldDefinition() : _type(Unknown), _size(0) {} + SubFieldDefinition(const QByteArray &tag, FieldType type, int size) + : _tag(tag), _type(type), _size(size) {} + + const QByteArray &tag() const {return _tag;} + FieldType type() const {return _type;} + int size() const {return _size;} + + private: + QByteArray _tag; + FieldType _type; + int _size; }; - class SubFields : public QVector + class SubFields { public: - SubFields() : QVector(), _repeat(false) {} + SubFields() : _repeat(false) {} + SubFields(const QVector &defs, bool repeat) + : _defs(defs), _repeat(repeat) {} + + int size() const {return _defs.size();} + const SubFieldDefinition &at(int i) const {return _defs.at(i);} bool repeat() const {return _repeat;} - void setRepeat(bool repeat) {_repeat = repeat;} private: + QVector _defs; bool _repeat; }; - class Data : public QVector > + class Data { public: - Data() : QVector >(), _fields(0) {} + Data() : _fields(0) {} + Data(const SubFields *fields) : _fields(fields) {} - void setFields(const SubFields *fields) {_fields = fields;} + int size() const {return _data.size();} + const QVector &at(int i) const {return _data.at(i);} + const SubFields *fields() const {return _fields;} const QVariant *field(const QByteArray &name, int idx = 0) const { - const QVector &v = at(idx); + const QVector &v = _data.at(idx); for (int i = 0; i < _fields->size(); i++) - if (_fields->at(i).tag == name) + if (_fields->at(i).tag() == name) return &v.at(i); return 0; } private: + friend class ISO8211; + + void append(QVector &row) {_data.append(row);} + + QVector > _data; const SubFields *_fields; }; class Field { public: + Field() {} + Field(const QByteArray &tag, const Data &data) + : _tag(tag), _data(data) {} + const QByteArray &tag() const {return _tag;} - void setTag(const QByteArray &tag) {_tag = tag;} - Data &rdata() {return _data;} const Data &data() const {return _data;} bool subfield(const char *name, int *val, int idx = 0) const; @@ -82,17 +109,7 @@ public: Data _data; }; - class Record : public QVector - { - public: - const Field *field(const QByteArray &name) const - { - for (int i = 0; i < size(); i++) - if (at(i).tag() == name) - return &at(i); - return 0; - } - }; + typedef QVector Record; ISO8211(const QString &path) : _file(path) {} bool readDDR(); @@ -100,16 +117,17 @@ public: const QString &errorString() const {return _errorString;} + static const Field *field(const Record &record, const QByteArray &name); + private: typedef QMap FieldsMap; - static bool fieldType(const QString &str, int cnt, FieldType &type, - int &size); + static SubFieldDefinition fieldType(const QString &str, int cnt, + const QByteArray &tag); int readDR(QVector &fields); bool readDDA(const FieldDefinition &def, SubFields &fields); - bool readUDA(quint64 pos, const FieldDefinition &def, - const SubFields &fields, Data &data); + bool readUDA(quint64 pos, const FieldDefinition &def, Data &data); QFile _file; FieldsMap _map; @@ -119,20 +137,20 @@ private: #ifndef QT_NO_DEBUG inline QDebug operator<<(QDebug dbg, const ISO8211::FieldDefinition &def) { - dbg.nospace() << "Field(" << def.tag << ", " << def.size << ")"; + dbg.nospace() << "FieldDefinition(" << def.tag << ", " << def.size << ")"; return dbg.space(); } inline QDebug operator<<(QDebug dbg, const ISO8211::SubFieldDefinition &def) { - dbg.nospace() << "SubField(" << def.tag << ", " << def.type << ", " - << def.size << ")"; + dbg.nospace() << "SubField(" << def.tag() << ", " << def.type() << ", " + << def.size() << ")"; return dbg.space(); } inline QDebug operator<<(QDebug dbg, const ISO8211::Field &field) { - dbg.nospace() << "Field(" << field.tag() /*<< ", " << field.data()*/ << ")"; + dbg.nospace() << "Field(" << field.tag() << ")"; return dbg.space(); } #endif // QT_NO_DEBUG diff --git a/src/map/ENC/mapdata.cpp b/src/map/ENC/mapdata.cpp index 44bf8cd8..d7ecd228 100644 --- a/src/map/ENC/mapdata.cpp +++ b/src/map/ENC/mapdata.cpp @@ -141,32 +141,32 @@ static const ISO8211::Field *SGXD(const ISO8211::Record &r) { const ISO8211::Field *f; - if ((f = r.field("SG2D"))) + if ((f = ISO8211::field(r, "SG2D"))) return f; - else if ((f = r.field("SG3D"))) + else if ((f = ISO8211::field(r, "SG3D"))) return f; else return 0; } -static bool pointCb(MapData::Point *point, void *context) +static bool pointCb(const MapData::Point *point, void *context) { - QList *points = (QList*)context; - points->append(point); + QList *points = (QList*)context; + points->append(*point); return true; } -static bool lineCb(MapData::Line *line, void *context) +static bool lineCb(const MapData::Line *line, void *context) { - QList *lines = (QList*)context; - lines->append(line); + QList *lines = (QList*)context; + lines->append(*line); return true; } -static bool polygonCb(MapData::Poly *polygon, void *context) +static bool polygonCb(const MapData::Poly *polygon, void *context) { - QList *polygons = (QList*)context; - polygons->append(polygon); + QList *polygons = (QList*)context; + polygons->append(*polygon); return true; } @@ -306,7 +306,7 @@ QVector MapData::soundings(const ISO8211::Record &r, uint COMF, uint SOMF) { QVector s; - const ISO8211::Field *f = r.field("SG3D"); + const ISO8211::Field *f = ISO8211::field(r, "SG3D"); if (!f) return QVector(); @@ -328,7 +328,7 @@ QVector MapData::soundingGeometry(const ISO8211::Record &r, quint32 id; RecordMapIterator it; - const ISO8211::Field *FSPT = r.field("FSPT"); + const ISO8211::Field *FSPT = ISO8211::field(r, "FSPT"); if (!FSPT || FSPT->data().at(0).size() != 4) return QVector(); @@ -356,7 +356,7 @@ Coordinates MapData::pointGeometry(const ISO8211::Record &r, quint32 id; RecordMapIterator it; - const ISO8211::Field *FSPT = r.field("FSPT"); + const ISO8211::Field *FSPT = ISO8211::field(r, "FSPT"); if (!FSPT || FSPT->data().at(0).size() != 4) return Coordinates(); @@ -386,7 +386,7 @@ QVector MapData::lineGeometry(const ISO8211::Record &r, quint8 type; quint32 id; - const ISO8211::Field *FSPT = r.field("FSPT"); + const ISO8211::Field *FSPT = ISO8211::field(r, "FSPT"); if (!FSPT || FSPT->data().at(0).size() != 4) return QVector(); @@ -399,7 +399,7 @@ QVector MapData::lineGeometry(const ISO8211::Record &r, if (it == ve.constEnd()) return QVector(); const ISO8211::Record &FRID = it.value(); - const ISO8211::Field *VRPT = FRID.field("VRPT"); + const ISO8211::Field *VRPT = ISO8211::field(FRID, "VRPT"); if (!VRPT || VRPT->data().size() != 2) return QVector(); @@ -452,7 +452,7 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc, quint8 type; quint32 id; - const ISO8211::Field *FSPT = r.field("FSPT"); + const ISO8211::Field *FSPT = ISO8211::field(r, "FSPT"); if (!FSPT || FSPT->data().at(0).size() != 4) return Polygon(); @@ -471,7 +471,7 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc, if (it == ve.constEnd()) return Polygon(); const ISO8211::Record &FRID = it.value(); - const ISO8211::Field *VRPT = FRID.field("VRPT"); + const ISO8211::Field *VRPT = ISO8211::field(FRID, "VRPT"); if (!VRPT || VRPT->data().size() != 2) return Polygon(); @@ -490,6 +490,8 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc, const ISO8211::Field *vertexes = SGXD(FRID); if (ORNT == 2) { v.append(c[1]); + if (USAG == 3) + v.append(Coordinates()); if (vertexes) { for (int j = vertexes->data().size() - 1; j >= 0; j--) { const QVector &cv = vertexes->data().at(j); @@ -497,9 +499,13 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc, COMF)); } } + if (USAG == 3) + v.append(Coordinates()); v.append(c[0]); } else { v.append(c[0]); + if (USAG == 3) + v.append(Coordinates()); if (vertexes) { for (int j = 0; j < vertexes->data().size(); j++) { const QVector &cv = vertexes->data().at(j); @@ -507,6 +513,8 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc, COMF)); } } + if (USAG == 3) + v.append(Coordinates()); v.append(c[1]); } @@ -528,7 +536,7 @@ MapData::Attr MapData::pointAttr(const ISO8211::Record &r, uint OBJL) QVector params(2); uint subtype = 0; - const ISO8211::Field *ATTF = r.field("ATTF"); + const ISO8211::Field *ATTF = ISO8211::field(r, "ATTF"); if (!(ATTF && ATTF->data().at(0).size() == 2)) return Attr(); @@ -580,7 +588,7 @@ MapData::Attr MapData::lineAttr(const ISO8211::Record &r, uint OBJL) QVector params(1); uint subtype = 0; - const ISO8211::Field *ATTF = r.field("ATTF"); + const ISO8211::Field *ATTF = ISO8211::field(r, "ATTF"); if (!(ATTF && ATTF->data().at(0).size() == 2)) return Attr(); @@ -608,7 +616,7 @@ MapData::Attr MapData::polyAttr(const ISO8211::Record &r, uint OBJL) QVector params(1); uint subtype = 0; - const ISO8211::Field *ATTF = r.field("ATTF"); + const ISO8211::Field *ATTF = ISO8211::field(r, "ATTF"); if (!(ATTF && ATTF->data().at(0).size() == 2)) return Attr(); @@ -677,30 +685,6 @@ MapData::Poly *MapData::polyObject(const ISO8211::Record &r, } bool MapData::processRecord(const ISO8211::Record &record, - QVector &rv, uint &COMF, QString &name) -{ - if (record.size() < 2) - return false; - - const ISO8211::Field &f = record.at(1); - const QByteArray &ba = f.tag(); - - if (ba == "VRID") { - rv.append(record); - } else if (ba == "DSID") { - QByteArray DSNM; - if (!f.subfield("DSNM", &DSNM)) - return false; - name = DSNM; - } else if (ba == "DSPM") { - if (!f.subfield("COMF", &COMF)) - return false; - } - - return true; -} - -bool MapData::processRecord(const ISO8211::Record &record, QVector &fe, RecordMap &vi, RecordMap &vc, RecordMap &ve, RecordMap &vf, uint &COMF, uint &SOMF) { @@ -742,92 +726,11 @@ bool MapData::processRecord(const ISO8211::Record &record, return true; } -bool MapData::bounds(const ISO8211::Record &record, Rect &rect) -{ - bool xok, yok; - // edge geometries can be empty! - const ISO8211::Field *f = SGXD(record); - if (!f) - return true; - - for (int i = 0; i < f->data().size(); i++) { - const QVector &c = f->data().at(i); - rect.unite(c.at(1).toInt(&xok), c.at(0).toInt(&yok)); - if (!(xok && yok)) - return false; - } - - return true; -} - -bool MapData::bounds(const QVector &gv, Rect &b) -{ - Rect r; - - for (int i = 0; i < gv.size(); i++) { - if (!bounds(gv.at(i), r)) - return false; - b |= r; - } - - return true; -} - -MapData::MapData(const QString &path): _fileName(path) -{ - QVector gv; - ISO8211 ddf(_fileName); - ISO8211::Record record; - uint COMF = 1; - - if (!ddf.readDDR()) { - _errorString = ddf.errorString(); - return; - } - while (ddf.readRecord(record)) { - if (!processRecord(record, gv, COMF, _name)) { - _errorString = "Invalid S-57 record"; - return; - } - } - if (!ddf.errorString().isNull()) { - _errorString = ddf.errorString(); - return; - } - - Rect b; - if (!bounds(gv, b)) { - _errorString = "Error fetching geometries bounds"; - return; - } - RectC br(Coordinates(b.minX() / (double)COMF, b.maxY() / (double)COMF), - Coordinates(b.maxX() / (double)COMF, b.minY() / (double)COMF)); - if (!br.isValid()) - _errorString = "Invalid geometries bounds"; - else - _bounds = br; -} - -MapData::~MapData() -{ - LineTree::Iterator lit; - for (_lines.GetFirst(lit); !_lines.IsNull(lit); _lines.GetNext(lit)) - delete _lines.GetAt(lit); - - PolygonTree::Iterator ait; - for (_areas.GetFirst(ait); !_areas.IsNull(ait); _areas.GetNext(ait)) - delete _areas.GetAt(ait); - - PointTree::Iterator pit; - for (_points.GetFirst(pit); !_points.IsNull(pit); _points.GetNext(pit)) - delete _points.GetAt(pit); -} - -void MapData::load() +MapData::MapData(const QString &path) { RecordMap vi, vc, ve, vf; QVector fe; - ISO8211 ddf(_fileName); + ISO8211 ddf(path); ISO8211::Record record; uint PRIM, OBJL, COMF = 1, SOMF = 1; Poly *poly; @@ -886,25 +789,22 @@ void MapData::load() } } -void MapData::clear() +MapData::~MapData() { LineTree::Iterator lit; for (_lines.GetFirst(lit); !_lines.IsNull(lit); _lines.GetNext(lit)) delete _lines.GetAt(lit); - _lines.RemoveAll(); PolygonTree::Iterator ait; for (_areas.GetFirst(ait); !_areas.IsNull(ait); _areas.GetNext(ait)) delete _areas.GetAt(ait); - _areas.RemoveAll(); PointTree::Iterator pit; for (_points.GetFirst(pit); !_points.IsNull(pit); _points.GetNext(pit)) delete _points.GetAt(pit); - _points.RemoveAll(); } -void MapData::points(const RectC &rect, QList *points) const +void MapData::points(const RectC &rect, QList *points) const { double min[2], max[2]; @@ -912,7 +812,7 @@ void MapData::points(const RectC &rect, QList *points) const _points.Search(min, max, pointCb, points); } -void MapData::lines(const RectC &rect, QList *lines) const +void MapData::lines(const RectC &rect, QList *lines) const { double min[2], max[2]; @@ -920,48 +820,10 @@ void MapData::lines(const RectC &rect, QList *lines) const _lines.Search(min, max, lineCb, lines); } -void MapData::polygons(const RectC &rect, QList *polygons) const +void MapData::polygons(const RectC &rect, QList *polygons) const { double min[2], max[2]; rectcBounds(rect, min, max); _areas.Search(min, max, polygonCb, polygons); } - -Range MapData::zooms() const -{ - double size = qMin(_bounds.width(), _bounds.height()); - - if (size > 180) - return Range(0, 10); - else if (size > 90) - return Range(1, 11); - else if (size > 45) - return Range(2, 12); - else if (size > 22.5) - return Range(3, 13); - else if (size > 11.25) - return Range(4, 14); - else if (size > 5.625) - return Range(5, 15); - else if (size > 2.813) - return Range(6, 16); - else if (size > 1.406) - return Range(7, 17); - else if (size > 0.703) - return Range(8, 18); - else if (size > 0.352) - return Range(9, 19); - else if (size > 0.176) - return Range(10, 20); - else if (size > 0.088) - return Range(11, 20); - else if (size > 0.044) - return Range(12, 20); - else if (size > 0.022) - return Range(13, 20); - else if (size > 0.011) - return Range(14, 20); - else - return Range(15, 20); -} diff --git a/src/map/ENC/mapdata.h b/src/map/ENC/mapdata.h index 662e1327..785e0011 100644 --- a/src/map/ENC/mapdata.h +++ b/src/map/ENC/mapdata.h @@ -1,11 +1,9 @@ #ifndef ENC_MAPDATA_H #define ENC_MAPDATA_H -#include #include "common/rectc.h" #include "common/rtree.h" #include "common/polygon.h" -#include "common/range.h" #include "iso8211.h" namespace ENC { @@ -68,55 +66,11 @@ public: MapData(const QString &path); ~MapData(); - const QString &name() const {return _name;} - RectC bounds() const {return _bounds;} - Range zooms() const; - - void polygons(const RectC &rect, QList *polygons) const; - void lines(const RectC &rect, QList *lines) const; - void points(const RectC &rect, QList *points) const; - - void load(); - void clear(); - - bool isValid() const {return _bounds.isValid();} - QString errorString() const {return _errorString;} + void polygons(const RectC &rect, QList *polygons) const; + void lines(const RectC &rect, QList *lines) const; + void points(const RectC &rect, QList *points) const; private: - class Rect { - public: - Rect() - : _minX(INT_MAX), _maxX(INT_MIN), _minY(INT_MAX), _maxY(INT_MIN) {} - Rect(int minX, int maxX, int minY, int maxY) - : _minX(minX), _maxX(maxX), _minY(minY), _maxY(maxY) {} - - int minX() const {return _minX;} - int maxX() const {return _maxX;} - int minY() const {return _minY;} - int maxY() const {return _maxY;} - - void unite(int x, int y) { - if (x < _minX) - _minX = x; - if (x > _maxX) - _maxX = x; - if (y < _minY) - _minY = y; - if (y > _maxY) - _maxY = y; - } - - Rect &operator|=(const Rect &r) {*this = *this | r; return *this;} - Rect operator|(const Rect &r) const - { - return Rect(qMin(_minX, r._minX), qMax(_maxX, r._maxX), - qMin(_minY, r._minY), qMax(_maxY, r._maxY)); - } - - private: - int _minX, _maxX, _minY, _maxY; - }; - class Attr { public: Attr() : _subtype(0) {} @@ -144,9 +98,9 @@ private: typedef QMap RecordMap; typedef QMap::const_iterator RecordMapIterator; - typedef RTree PolygonTree; - typedef RTree LineTree; - typedef RTree PointTree; + typedef RTree PolygonTree; + typedef RTree LineTree; + typedef RTree PointTree; static QVector soundings(const ISO8211::Record &r, uint COMF, uint SOMF); @@ -168,21 +122,14 @@ private: const RecordMap &ve, uint COMF, uint OBJL); static Poly *polyObject(const ISO8211::Record &r, const RecordMap &vc, const RecordMap &ve, uint COMF,uint OBJL); - static bool bounds(const ISO8211::Record &record, Rect &rect); - static bool bounds(const QVector &gv, Rect &b); + static bool processRecord(const ISO8211::Record &record, QVector &fe, RecordMap &vi, RecordMap &vc, RecordMap &ve, RecordMap &vf, uint &COMF, uint &SOMF); - static bool processRecord(const ISO8211::Record &record, - QVector &rv, uint &COMF, QString &name); - QString _fileName; - QString _name; - RectC _bounds; PolygonTree _areas; LineTree _lines; PointTree _points; - QString _errorString; }; } diff --git a/src/map/ENC/rastertile.cpp b/src/map/ENC/rastertile.cpp index 8ec60f71..4145c09e 100644 --- a/src/map/ENC/rastertile.cpp +++ b/src/map/ENC/rastertile.cpp @@ -12,16 +12,11 @@ using namespace ENC; #define TEXT_EXTENT 160 #define TSSLPT_SIZE 0.005 /* ll */ -typedef QMap PointMap; +typedef QSet PointSet; static const float C1 = 0.866025f; /* sqrt(3)/2 */ static const QColor haloColor(Qt::white); -static struct { - bool operator()(MapData::Point* a, MapData::Point* b) const - {return *a < *b;} -} pointLess; - static QFont pixelSizeFont(int pixelSize) { QFont f; @@ -114,7 +109,9 @@ static bool showLabel(const QImage *img, const Range &range, int zoom, int type) if (type>>16 == I_DISMAR) return true; - if ((img || type>>16 == SOUNDG) && zoom < range.mid()) + int limit = (!range.size()) + ? range.min() : range.min() + (range.size() + 1) / 2; + if ((img || (type>>16 == SOUNDG)) && (zoom < limit)) return false; return true; @@ -127,9 +124,14 @@ QPainterPath RasterTile::painterPath(const Polygon &polygon) const for (int i = 0; i < polygon.size(); i++) { const QVector &subpath = polygon.at(i); - QVector p(subpath.size()); - for (int j = 0; j < subpath.size(); j++) - p[j] = ll2xy(subpath.at(j)); + QVector p; + p.reserve(subpath.size()); + + for (int j = 0; j < subpath.size(); j++) { + const Coordinates &c = subpath.at(j); + if (!c.isNull()) + p.append(ll2xy(c)); + } path.addPolygon(p); } @@ -147,6 +149,35 @@ QPolygonF RasterTile::polyline(const QVector &path) const return polygon; } +QVector RasterTile::polylineM(const QVector &path) const +{ + QVector polys; + QPolygonF polygon; + bool mask = false; + + polygon.reserve(path.size()); + + for (int i = 0; i < path.size(); i++) { + const Coordinates &c = path.at(i); + + if (c.isNull()) { + if (mask) + mask = false; + else { + polys.append(polygon); + polygon.clear(); + mask = true; + } + } else if (!mask) + polygon.append(ll2xy(c)); + } + + if (!polygon.isEmpty()) + polys.append(polygon); + + return polys; +} + QPolygonF RasterTile::tsslptArrow(const Coordinates &c, qreal angle) const { Coordinates t[3], r[4]; @@ -173,14 +204,14 @@ QPolygonF RasterTile::tsslptArrow(const Coordinates &c, qreal angle) const } void RasterTile::drawArrows(QPainter *painter, - const QList &polygons) + const QList &polygons) { for (int i = 0; i < polygons.size(); i++) { - const MapData::Poly *poly = polygons.at(i); + const MapData::Poly &poly = polygons.at(i); - if (poly->type()>>16 == TSSLPT) { - QPolygonF polygon(tsslptArrow(centroid(poly->path().first()), - deg2rad(180 - poly->param().toDouble()))); + if (poly.type()>>16 == TSSLPT) { + QPolygonF polygon(tsslptArrow(centroid(poly.path().first()), + deg2rad(180 - poly.param().toDouble()))); painter->setPen(QPen(QColor("#eb49eb"), 1)); painter->setBrush(QBrush("#80eb49eb")); @@ -190,45 +221,55 @@ void RasterTile::drawArrows(QPainter *painter, } void RasterTile::drawPolygons(QPainter *painter, - const QList &polygons) + const QList &polygons) { const Style &s = style(); for (int n = 0; n < s.drawOrder().size(); n++) { for (int i = 0; i < polygons.size(); i++) { - const MapData::Poly *poly = polygons.at(i); - if (poly->type() != s.drawOrder().at(n)) + const MapData::Poly &poly = polygons.at(i); + if (poly.type() != s.drawOrder().at(n)) continue; - const Style::Polygon &style = s.polygon(poly->type()); + const Style::Polygon &style = s.polygon(poly.type()); if (!style.img().isNull()) { - for (int i = 0; i < poly->path().size(); i++) - BitmapLine::draw(painter, polyline(poly->path().at(i)), + for (int i = 0; i < poly.path().size(); i++) + BitmapLine::draw(painter, polylineM(poly.path().at(i)), style.img()); } else { - painter->setPen(style.pen()); - painter->setBrush(style.brush()); - painter->drawPath(painterPath(poly->path())); + if (style.brush() != Qt::NoBrush) { + painter->setPen(Qt::NoPen); + painter->setBrush(style.brush()); + painter->drawPath(painterPath(poly.path())); + } + if (style.pen() != Qt::NoPen) { + painter->setPen(style.pen()); + for (int i = 0; i < poly.path().size(); i++) { + QVector outline(polylineM(poly.path().at(i))); + for (int j = 0; j < outline.size(); j++) + painter->drawPolyline(outline.at(j)); + } + } } } } } -void RasterTile::drawLines(QPainter *painter, const QList &lines) +void RasterTile::drawLines(QPainter *painter, const QList &lines) { const Style &s = style(); painter->setBrush(Qt::NoBrush); for (int i = 0; i < lines.size(); i++) { - const MapData::Line *line = lines.at(i); - const Style::Line &style = s.line(line->type()); + const MapData::Line &line = lines.at(i); + const Style::Line &style = s.line(line.type()); if (!style.img().isNull()) { - BitmapLine::draw(painter, polyline(line->path()), style.img()); + BitmapLine::draw(painter, polyline(line.path()), style.img()); } else if (style.pen() != Qt::NoPen) { painter->setPen(style.pen()); - painter->drawPolyline(polyline(line->path())); + painter->drawPolyline(polyline(line.path())); } } } @@ -240,25 +281,25 @@ void RasterTile::drawTextItems(QPainter *painter, textItems.at(i)->paint(painter); } -void RasterTile::processPolygons(const QList &polygons, +void RasterTile::processPolygons(const QList &polygons, QList &textItems) { const Style &s = style(); for (int i = 0; i < polygons.size(); i++) { - const MapData::Poly *poly = polygons.at(i); - uint type = poly->type()>>16; + const MapData::Poly &poly = polygons.at(i); + uint type = poly.type()>>16; if (!(type == HRBFAC || type == I_TRNBSN - || poly->type() == SUBTYPE(I_BERTHS, 6))) + || poly.type() == SUBTYPE(I_BERTHS, 6))) continue; - const Style::Point &style = s.point(poly->type()); + const Style::Point &style = s.point(poly.type()); const QImage *img = style.img().isNull() ? 0 : &style.img(); if (!img) continue; TextPointItem *item = new TextPointItem( - ll2xy(centroid(poly->path().first())).toPoint(), + ll2xy(centroid(poly.path().first())).toPoint(), 0, 0, img, 0, 0, 0, 0); if (item->isValid() && !item->collides(textItems)) textItems.append(item); @@ -267,40 +308,40 @@ void RasterTile::processPolygons(const QList &polygons, } } -void RasterTile::processPoints(QList &points, +void RasterTile::processPoints(QList &points, QList &textItems, QList &lights) { const Style &s = style(); - PointMap lightsMap, signalsMap; + PointSet lightsSet, signalsSet; int i; - std::sort(points.begin(), points.end(), pointLess); + std::sort(points.begin(), points.end()); /* Lights & Signals */ for (i = 0; i < points.size(); i++) { - const MapData::Point *point = points.at(i); - if (point->type()>>16 == LIGHTS) - lightsMap.insert(point->pos(), point); - else if (point->type()>>16 == FOGSIG) - signalsMap.insert(point->pos(), point); + const MapData::Point &point = points.at(i); + if (point.type()>>16 == LIGHTS) + lightsSet.insert(point.pos()); + else if (point.type()>>16 == FOGSIG) + signalsSet.insert(point.pos()); else break; } /* Everything else */ for ( ; i < points.size(); i++) { - const MapData::Point *point = points.at(i); - QPoint pos(ll2xy(point->pos()).toPoint()); - const Style::Point &style = s.point(point->type()); + const MapData::Point &point = points.at(i); + QPoint pos(ll2xy(point.pos()).toPoint()); + const Style::Point &style = s.point(point.type()); - const QString *label = point->label().isEmpty() ? 0 : &(point->label()); + const QString *label = point.label().isEmpty() ? 0 : &(point.label()); const QImage *img = style.img().isNull() ? 0 : &style.img(); - const QFont *fnt = showLabel(img, _data->zooms(), _zoom, point->type()) + const QFont *fnt = showLabel(img, _zoomRange, _zoom, point.type()) ? font(style.textFontSize()) : 0; const QColor *color = &style.textColor(); const QColor *hColor = style.haloColor().isValid() ? &style.haloColor() : 0; - double rotate = angle(point->type(), point->param()); + double rotate = angle(point.type(), point.param()); if ((!label || !fnt) && !img) continue; @@ -309,34 +350,34 @@ void RasterTile::processPoints(QList &points, hColor, 0, 2, rotate); if (item->isValid() && !item->collides(textItems)) { textItems.append(item); - if (lightsMap.contains(point->pos())) + if (lightsSet.contains(point.pos())) lights.append(new TextPointItem(pos, 0, 0, light(), 0, 0, 0, 0)); - if (signalsMap.contains(point->pos())) + if (signalsSet.contains(point.pos())) lights.append(new TextPointItem(pos, 0, 0, signal(), 0, 0, 0, 0)); } else delete item; } } -void RasterTile::processLines(const QList &lines, +void RasterTile::processLines(const QList &lines, QList &textItems) { const Style &s = style(); for (int i = 0; i < lines.size(); i++) { - const MapData::Line *line = lines.at(i); - const Style::Line &style = s.line(line->type()); + const MapData::Line &line = lines.at(i); + const Style::Line &style = s.line(line.type()); if (style.img().isNull() && style.pen() == Qt::NoPen) continue; - if (line->label().isEmpty() || style.textFontSize() == Style::None) + if (line.label().isEmpty() || style.textFontSize() == Style::None) continue; const QFont *fnt = font(style.textFontSize()); const QColor *color = &style.textColor(); - TextPathItem *item = new TextPathItem(polyline(line->path()), - &line->label(), _rect, fnt, color, 0); + TextPathItem *item = new TextPathItem(polyline(line.path()), + &line.label(), _rect, fnt, color, 0); if (item->isValid() && !item->collides(textItems)) textItems.append(item); else @@ -344,8 +385,8 @@ void RasterTile::processLines(const QList &lines, } } -void RasterTile::fetchData(QList &polygons, - QList &lines, QList &points) +void RasterTile::fetchData(QList &polygons, + QList &lines, QList &points) { QPoint ttl(_rect.topLeft()); @@ -354,22 +395,28 @@ void RasterTile::fetchData(QList &polygons, RectD polyRectD(_transform.img2proj(polyRect.topLeft()), _transform.img2proj(polyRect.bottomRight())); RectC polyRectC(polyRectD.toRectC(_proj, 20)); - _data->lines(polyRectC, &lines); - _data->polygons(polyRectC, &polygons); - QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT, ttl.y() - TEXT_EXTENT), QPointF(ttl.x() + _rect.width() + TEXT_EXTENT, ttl.y() + _rect.height() + TEXT_EXTENT)); RectD pointRectD(_transform.img2proj(pointRect.topLeft()), _transform.img2proj(pointRect.bottomRight())); - _data->points(pointRectD.toRectC(_proj, 20), &points); + RectC pointRectC(pointRectD.toRectC(_proj, 20)); + + if (_map) { + _map->lines(polyRectC, &lines); + _map->polygons(polyRectC, &polygons); + _map->points(pointRectC, &points); + } else { + _atlas->polys(polyRectC, &polygons, &lines); + _atlas->points(pointRectC, &points); + } } void RasterTile::render() { - QList lines; - QList polygons; - QList points; + QList lines; + QList polygons; + QList points; QList textItems, lights; _pixmap.setDevicePixelRatio(_ratio); diff --git a/src/map/ENC/rastertile.h b/src/map/ENC/rastertile.h index a5aa8bff..1e027cee 100644 --- a/src/map/ENC/rastertile.h +++ b/src/map/ENC/rastertile.h @@ -2,10 +2,12 @@ #define ENC_RASTERTILE_H #include +#include "common/range.h" #include "map/projection.h" #include "map/transform.h" #include "map/textpointitem.h" #include "mapdata.h" +#include "atlasdata.h" class TextItem; @@ -15,9 +17,14 @@ class RasterTile { public: RasterTile(const Projection &proj, const Transform &transform, - const MapData *data, int zoom, const QRect &rect, qreal ratio) - : _proj(proj), _transform(transform), _data(data), _zoom(zoom), - _rect(rect), _ratio(ratio), + const MapData *data, int zoom, const Range &zoomRange, const QRect &rect, + qreal ratio) : _proj(proj), _transform(transform), _map(data), _atlas(0), + _zoom(zoom), _zoomRange(zoomRange), _rect(rect), _ratio(ratio), + _pixmap(rect.width() * ratio, rect.height() * ratio), _valid(false) {} + RasterTile(const Projection &proj, const Transform &transform, + AtlasData *data, int zoom, const Range &zoomRange, const QRect &rect, + qreal ratio) : _proj(proj), _transform(transform), _map(0), _atlas(data), + _zoom(zoom), _zoomRange(zoomRange), _rect(rect), _ratio(ratio), _pixmap(rect.width() * ratio, rect.height() * ratio), _valid(false) {} int zoom() const {return _zoom;} @@ -28,30 +35,36 @@ public: void render(); private: - void fetchData(QList &polygons, QList &lines, - QList &points); + void fetchData(QList &polygons, QList &lines, + QList &points); QPointF ll2xy(const Coordinates &c) const {return _transform.proj2img(_proj.ll2xy(c));} QPainterPath painterPath(const Polygon &polygon) const; QPolygonF polyline(const QVector &path) const; + QVector polylineM(const QVector &path) const; QPolygonF tsslptArrow(const Coordinates &c, qreal angle) const; - void processPoints(QList &points, + void processPoints(QList &points, QList &textItems, QList &lights); - void processLines(const QList &lines, + void processLines(const QList &lines, QList &textItems); - void processPolygons(const QList &polygons, + void processPolygons(const QList &polygons, QList &textItems); void drawBitmapPath(QPainter *painter, const QImage &img, const Polygon &polygon); - void drawArrows(QPainter *painter, const QList &polygons); - void drawPolygons(QPainter *painter, const QList &polygons); - void drawLines(QPainter *painter, const QList &lines); + void drawArrows(QPainter *painter, const QList &polygons); + void drawPolygons(QPainter *painter, const QList &polygons); + void drawLines(QPainter *painter, const QList &lines); void drawTextItems(QPainter *painter, const QList &textItems); + static bool polyCb(MapData *data, void *context); + static bool pointCb(MapData *data, void *context); + Projection _proj; Transform _transform; - const MapData *_data; + const MapData *_map; + AtlasData *_atlas; int _zoom; + Range _zoomRange; QRect _rect; qreal _ratio; QPixmap _pixmap; diff --git a/src/map/bitmapline.cpp b/src/map/bitmapline.cpp index 4b670e5a..ab2189ee 100644 --- a/src/map/bitmapline.cpp +++ b/src/map/bitmapline.cpp @@ -39,3 +39,10 @@ void BitmapLine::draw(QPainter *painter, const QPolygonF &line, painter->restore(); } } + +void BitmapLine::draw(QPainter *painter, const QVector &lines, + const QImage &img) +{ + for (int i = 0; i < lines.size(); i++) + draw(painter, lines.at(i), img); +} diff --git a/src/map/bitmapline.h b/src/map/bitmapline.h index 37bf428d..dc11f0c4 100644 --- a/src/map/bitmapline.h +++ b/src/map/bitmapline.h @@ -1,6 +1,8 @@ #ifndef BITMAPLINE_H #define BITMAPLINE_H +#include + class QPainter; class QImage; class QPolygonF; @@ -8,6 +10,8 @@ class QPolygonF; namespace BitmapLine { void draw(QPainter *painter, const QPolygonF &line, const QImage &img); + void draw(QPainter *painter, const QVector &lines, + const QImage &img); } #endif // BITMAPLINE_H diff --git a/src/map/encatlas.cpp b/src/map/encatlas.cpp new file mode 100644 index 00000000..8107a83d --- /dev/null +++ b/src/map/encatlas.cpp @@ -0,0 +1,387 @@ +#include +#include +#include "common/wgs84.h" +#include "GUI/format.h" +#include "rectd.h" +#include "pcs.h" +#include "encjob.h" +#include "encatlas.h" + +using namespace ENC; + +#define TILE_SIZE 512 + +Range ENCAtlas::zooms(IntendedUsage usage) +{ + switch (usage) { + case Overview: + return Range(6, 7); + case General: + return Range(8, 9); + case Coastal: + return Range(10, 11); + case Approach: + return Range(12, 13); + case Harbour: + return Range(14, 18); + case Berthing: + return Range(19, 19); + + case River: + return Range(12, 17); + case RiverHarbour: + return Range(18, 18); + case RiverBerthing: + return Range(19, 19); + default: + return Range(0, 19); + }; +} + +ENCAtlas::IntendedUsage ENCAtlas::usage(const QString &path) +{ + QFileInfo fi(path); + QString basename(fi.baseName()); + + if (basename.size() != 8) + return Unknown; + int iu = basename.at(2).digitValue(); + if (iu < 1 || iu > 9) + return Unknown; + + return (IntendedUsage)iu; +} + +bool ENCAtlas::processRecord(const ISO8211::Record &record, QByteArray &file, + RectC &bounds) +{ + if (record.size() < 2) + return false; + + const ENC::ISO8211::Field &f = record.at(1); + const QByteArray &ba = f.tag(); + + if (ba == "CATD") { + QByteArray FILE, IMPL; + + if (!f.subfield("IMPL", &IMPL)) + return false; + if (!f.subfield("FILE", &FILE)) + return false; + + if (IMPL == "BIN" && FILE.endsWith("000")) { + QByteArray SLAT, WLON, NLAT, ELON; + + if (!f.subfield("SLAT", &SLAT)) + return false; + if (!f.subfield("WLON", &WLON)) + return false; + if (!f.subfield("NLAT", &NLAT)) + return false; + if (!f.subfield("ELON", &ELON)) + return false; + + bool ok1, ok2, ok3, ok4; + bounds = RectC(Coordinates(WLON.toDouble(&ok1), NLAT.toDouble(&ok2)), + Coordinates(ELON.toDouble(&ok3), SLAT.toDouble(&ok4))); + if (!(ok1 && ok2 && ok3 && ok4)) + return false; + + file = FILE.replace('\\', '/'); + + return true; + } + } + + return false; +} + +void ENCAtlas::addMap(const QDir &dir, const QByteArray &file, + const RectC &bounds) +{ + QString path(dir.absoluteFilePath(file)); + if (!QFileInfo::exists(path)) { + qWarning("%s: No such map file", qPrintable(path)); + return; + } + if (!bounds.isValid()) { + qWarning("%s: Invalid map bounds", qPrintable(path)); + return; + } + + IntendedUsage iu = usage(path); + auto it = _data.find(iu); + if (it == _data.end()) + it = _data.insert(iu, new AtlasData(_cache, _lock)); + + it.value()->addMap(bounds, path); + + _name = "ENC (" + Format::coordinates(bounds.center(), DecimalDegrees) + ")"; + _llBounds |= bounds; +} + +ENCAtlas::ENCAtlas(const QString &fileName, QObject *parent) + : Map(fileName, parent), _projection(PCS::pcs(3857)), + _tileRatio(1.0), _zoom(0), _valid(false) +{ + QDir dir(QFileInfo(fileName).absoluteDir()); + ISO8211 ddf(fileName); + ISO8211::Record record; + QByteArray file; + RectC bounds; + + if (!ddf.readDDR()) { + _errorString = ddf.errorString(); + return; + } + while (ddf.readRecord(record)) { + if (processRecord(record, file, bounds)) + addMap(dir, file, bounds); + } + if (!ddf.errorString().isNull()) { + _errorString = ddf.errorString(); + return; + } + + if (_data.isEmpty()) { + _errorString = "No usable ENC map found"; + return; + } + + _usage = _data.firstKey(); + _zoom = zooms(_usage).min(); + updateTransform(); + + _cache.setMaxCost(10); + + _valid = true; +} + +ENCAtlas::~ENCAtlas() +{ + qDeleteAll(_data); +} + +void ENCAtlas::load(const Projection &in, const Projection &out, + qreal deviceRatio, bool hidpi) +{ + Q_UNUSED(in); + Q_UNUSED(hidpi); + + _tileRatio = deviceRatio; + _projection = out; + + QPixmapCache::clear(); +} + +void ENCAtlas::unload() +{ + cancelJobs(true); + + _cache.clear(); +} + +int ENCAtlas::zoomFit(const QSize &size, const RectC &rect) +{ + if (rect.isValid()) { + RectD pr(rect, _projection, 10); + + for (auto it = _data.cbegin(); it != _data.cend(); ++it) { + Range z(zooms(it.key())); + + _usage = it.key(); + _zoom = z.min(); + + for (int i = z.min() + 1; i <= z.max(); i++) { + Transform t(transform(i)); + QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight())); + + if (size.width() < r.width() || size.height() < r.height()) { + updateTransform(); + return _zoom; + } + + _zoom = i; + } + } + } else { + IntendedUsage usage(_data.lastKey()); + _usage = usage; + _zoom = zooms(usage).max(); + } + + updateTransform(); + + return _zoom; +} + +int ENCAtlas::zoomIn() +{ + cancelJobs(false); + + if (_zoom + 1 <= zooms(_usage).max()) + _zoom++; + else { + auto it = _data.find(_usage); + if (++it != _data.end()) { + _usage = it.key(); + _zoom = zooms(it.key()).min(); + } + } + + updateTransform(); + + return _zoom; +} + +int ENCAtlas::zoomOut() +{ + cancelJobs(false); + + if (_zoom - 1 >= zooms(_usage).min()) + _zoom--; + else { + auto it = _data.find(_usage); + if (it != _data.begin()) { + --it; + _usage = it.key(); + _zoom = zooms(it.key()).max(); + } + } + + updateTransform(); + + return _zoom; +} + +void ENCAtlas::setZoom(int zoom) +{ + _zoom = zoom; + updateTransform(); +} + +Transform ENCAtlas::transform(int zoom) const +{ + int z = zoom + Util::log2i(TILE_SIZE); + + double scale = _projection.isGeographic() + ? 360.0 / (1< &tiles = _jobs.at(i)->tiles(); + for (int j = 0; j < tiles.size(); j++) { + const ENC::RasterTile &mt = tiles.at(j); + if (mt.zoom() == zoom && mt.xy() == xy) + return true; + } + } + + return false; +} + +void ENCAtlas::runJob(ENCJob *job) +{ + _jobs.append(job); + + connect(job, &ENCJob::finished, this, &ENCAtlas::jobFinished); + job->run(); +} + +void ENCAtlas::removeJob(ENCJob *job) +{ + _jobs.removeOne(job); + job->deleteLater(); +} + +void ENCAtlas::jobFinished(ENCJob *job) +{ + const QList &tiles = job->tiles(); + + for (int i = 0; i < tiles.size(); i++) { + const ENC::RasterTile &mt = tiles.at(i); + if (mt.isValid()) + QPixmapCache::insert(key(mt.zoom(), mt.xy()), mt.pixmap()); + } + + removeJob(job); + + emit tilesLoaded(); +} + +void ENCAtlas::cancelJobs(bool wait) +{ + for (int i = 0; i < _jobs.size(); i++) + _jobs.at(i)->cancel(wait); +} + +QString ENCAtlas::key(int zoom, const QPoint &xy) const +{ + return path() + "-" + QString::number(zoom) + "_" + + QString::number(xy.x()) + "_" + QString::number(xy.y()); +} + +void ENCAtlas::draw(QPainter *painter, const QRectF &rect, Flags flags) +{ + Q_UNUSED(flags); + QPointF tl(floor(rect.left() / TILE_SIZE) * TILE_SIZE, + floor(rect.top() / TILE_SIZE) * TILE_SIZE); + QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y()); + int width = ceil(s.width() / TILE_SIZE); + int height = ceil(s.height() / TILE_SIZE); + + QList tiles; + + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + QPoint ttl(tl.x() + i * TILE_SIZE, tl.y() + j * TILE_SIZE); + if (isRunning(_zoom, ttl)) + continue; + + QPixmap pm; + if (QPixmapCache::find(key(_zoom, ttl), &pm)) + painter->drawPixmap(ttl, pm); + else + tiles.append(RasterTile(_projection, _transform, + _data.value(_usage), _zoom, zooms(_usage), + QRect(ttl, QSize(TILE_SIZE, TILE_SIZE)), _tileRatio)); + } + } + + if (!tiles.isEmpty()) { + if (flags & Map::Block) { + QFuture future = QtConcurrent::map(tiles, &RasterTile::render); + future.waitForFinished(); + + for (int i = 0; i < tiles.size(); i++) { + const RasterTile &mt = tiles.at(i); + const QPixmap &pm = mt.pixmap(); + painter->drawPixmap(mt.xy(), pm); + QPixmapCache::insert(key(mt.zoom(), mt.xy()), pm); + } + } else + runJob(new ENCJob(tiles)); + } +} + +Map *ENCAtlas::create(const QString &path, bool *isDir) +{ + if (isDir) + *isDir = true; + + return new ENCAtlas(path); +} diff --git a/src/map/encatlas.h b/src/map/encatlas.h new file mode 100644 index 00000000..c1b6b351 --- /dev/null +++ b/src/map/encatlas.h @@ -0,0 +1,98 @@ +#ifndef ENCATLAS_H +#define ENCATLAS_H + +#include "common/range.h" +#include "map.h" +#include "projection.h" +#include "transform.h" +#include "ENC/iso8211.h" +#include "ENC/atlasdata.h" + +class ENCJob; +class QDir; + +class ENCAtlas : public Map +{ + Q_OBJECT + +public: + ENCAtlas(const QString &fileName, QObject *parent = 0); + ~ENCAtlas(); + + QString name() const {return _name;} + + QRectF bounds() {return _bounds;} + RectC llBounds(const Projection &) {return _llBounds;} + + int zoom() const {return _zoom;} + void setZoom(int zoom); + int zoomFit(const QSize &size, const RectC &br); + int zoomIn(); + int zoomOut(); + + QPointF ll2xy(const Coordinates &c) + {return _transform.proj2img(_projection.ll2xy(c));} + Coordinates xy2ll(const QPointF &p) + {return _projection.xy2ll(_transform.img2proj(p));} + + void draw(QPainter *painter, const QRectF &rect, Flags flags); + + void load(const Projection &in, const Projection &out, qreal deviceRatio, + bool hidpi); + void unload(); + + bool isValid() const {return _valid;} + QString errorString() const {return _errorString;} + + static Map *create(const QString &path, bool *isDir); + +private slots: + void jobFinished(ENCJob *job); + +private: + enum IntendedUsage { + Unknown = 0, + Overview = 1, + General = 2, + Coastal = 3, + Approach = 4, + Harbour = 5, + Berthing = 6, + River = 7, + RiverHarbour = 8, + RiverBerthing = 9 + }; + + Transform transform(int zoom) const; + void updateTransform(); + bool isRunning(int zoom, const QPoint &xy) const; + void runJob(ENCJob *job); + void removeJob(ENCJob *job); + void cancelJobs(bool wait); + QString key(int zoom, const QPoint &xy) const; + void addMap(const QDir &dir, const QByteArray &file, const RectC &bounds); + + static bool processRecord(const ENC::ISO8211::Record &record, + QByteArray &file, RectC &bounds); + static Range zooms(IntendedUsage usage); + static IntendedUsage usage(const QString &path); + + QString _name; + RectC _llBounds; + QRectF _bounds; + Projection _projection; + Transform _transform; + qreal _tileRatio; + QMap _data; + ENC::MapCache _cache; + QMutex _lock; + IntendedUsage _usage; + int _zoom; + + QList _jobs; + + bool _valid; + QString _errorString; +}; + +#endif // ENCATLAS_H diff --git a/src/map/encjob.h b/src/map/encjob.h new file mode 100644 index 00000000..8246337a --- /dev/null +++ b/src/map/encjob.h @@ -0,0 +1,42 @@ +#ifndef ENCJOB_H +#define ENCJOB_H + +#include +#include "ENC/rastertile.h" + +class ENCJob : public QObject +{ + Q_OBJECT + +public: + ENCJob(const QList &tiles) + : _tiles(tiles) {} + + void run() + { + connect(&_watcher, &QFutureWatcher::finished, this, + &ENCJob::handleFinished); + _future = QtConcurrent::map(_tiles, &ENC::RasterTile::render); + _watcher.setFuture(_future); + } + void cancel(bool wait) + { + _future.cancel(); + if (wait) + _future.waitForFinished(); + } + const QList &tiles() const {return _tiles;} + +signals: + void finished(ENCJob *job); + +private slots: + void handleFinished() {emit finished(this);} + +private: + QFutureWatcher _watcher; + QFuture _future; + QList _tiles; +}; + +#endif // ENCJOB_H diff --git a/src/map/encmap.cpp b/src/map/encmap.cpp index 18df7624..3a5c5d2e 100644 --- a/src/map/encmap.cpp +++ b/src/map/encmap.cpp @@ -4,6 +4,7 @@ #include "common/wgs84.h" #include "rectd.h" #include "pcs.h" +#include "encjob.h" #include "encmap.h" @@ -11,14 +12,153 @@ using namespace ENC; #define TILE_SIZE 512 +static Range zooms(const RectC &bounds) +{ + double size = qMin(bounds.width(), bounds.height()); + + if (size > 180) + return Range(0, 10); + else if (size > 90) + return Range(1, 11); + else if (size > 45) + return Range(2, 12); + else if (size > 22.5) + return Range(3, 13); + else if (size > 11.25) + return Range(4, 14); + else if (size > 5.625) + return Range(5, 15); + else if (size > 2.813) + return Range(6, 16); + else if (size > 1.406) + return Range(7, 17); + else if (size > 0.703) + return Range(8, 18); + else if (size > 0.352) + return Range(9, 19); + else if (size > 0.176) + return Range(10, 20); + else if (size > 0.088) + return Range(11, 20); + else if (size > 0.044) + return Range(12, 20); + else if (size > 0.022) + return Range(13, 20); + else if (size > 0.011) + return Range(14, 20); + else + return Range(15, 20); +} + +static const ISO8211::Field *SGXD(const ISO8211::Record &r) +{ + const ISO8211::Field *f; + + if ((f = ISO8211::field(r, "SG2D"))) + return f; + else if ((f = ISO8211::field(r, "SG3D"))) + return f; + else + return 0; +} + +bool ENCMap::bounds(const ISO8211::Record &record, Rect &rect) +{ + bool xok, yok; + // edge geometries can be empty! + const ISO8211::Field *f = SGXD(record); + if (!f) + return true; + + for (int i = 0; i < f->data().size(); i++) { + const QVector &c = f->data().at(i); + rect.unite(c.at(1).toInt(&xok), c.at(0).toInt(&yok)); + if (!(xok && yok)) + return false; + } + + return true; +} + +bool ENCMap::bounds(const QVector &gv, Rect &b) +{ + Rect r; + + for (int i = 0; i < gv.size(); i++) { + if (!bounds(gv.at(i), r)) + return false; + b |= r; + } + + return true; +} + +bool ENCMap::processRecord(const ISO8211::Record &record, + QVector &rv, uint &COMF, QString &name) +{ + if (record.size() < 2) + return false; + + const ISO8211::Field &f = record.at(1); + const QByteArray &ba = f.tag(); + + if (ba == "VRID") { + rv.append(record); + } else if (ba == "DSID") { + QByteArray DSNM; + if (!f.subfield("DSNM", &DSNM)) + return false; + name = DSNM; + } else if (ba == "DSPM") { + if (!f.subfield("COMF", &COMF)) + return false; + } + + return true; +} + ENCMap::ENCMap(const QString &fileName, QObject *parent) - : Map(fileName, parent), _data(fileName), _projection(PCS::pcs(3857)), - _tileRatio(1.0), _zoom(0) + : Map(fileName, parent), _data(0), _projection(PCS::pcs(3857)), + _tileRatio(1.0), _valid(false) { - if (_data.isValid()) { - _llBounds = _data.bounds(); - updateTransform(); + QVector gv; + ISO8211 ddf(fileName); + ISO8211::Record record; + uint COMF = 1; + + if (!ddf.readDDR()) { + _errorString = ddf.errorString(); + return; + } + while (ddf.readRecord(record)) { + if (!processRecord(record, gv, COMF, _name)) { + _errorString = "Invalid S-57 record"; + return; + } } + if (!ddf.errorString().isNull()) { + _errorString = ddf.errorString(); + return; + } + + Rect b; + if (!bounds(gv, b)) { + _errorString = "Error fetching geometries bounds"; + return; + } + Coordinates tl(b.minX() / (double)COMF, b.maxY() / (double)COMF); + Coordinates br(b.maxX() / (double)COMF, b.minY() / (double)COMF); + _llBounds = RectC(tl, br); + if (!_llBounds.isValid()) { + _errorString = "Invalid geometries bounds"; + return; + } + + _zooms = zooms(_llBounds); + _zoom = _zooms.min(); + updateTransform(); + + _valid = true; } void ENCMap::load(const Projection &in, const Projection &out, @@ -29,14 +169,17 @@ void ENCMap::load(const Projection &in, const Projection &out, _tileRatio = deviceRatio; _projection = out; - _data.load(); + Q_ASSERT(!_data); + _data = new MapData(path()); QPixmapCache::clear(); } void ENCMap::unload() { cancelJobs(true); - _data.clear(); + + delete _data; + _data = 0; } int ENCMap::zoomFit(const QSize &size, const RectC &rect) @@ -44,8 +187,8 @@ int ENCMap::zoomFit(const QSize &size, const RectC &rect) if (rect.isValid()) { RectD pr(rect, _projection, 10); - _zoom = _data.zooms().min(); - for (int i = _data.zooms().min() + 1; i <= _data.zooms().max(); i++) { + _zoom = _zooms.min(); + for (int i = _zooms.min() + 1; i <= _zooms.max(); i++) { Transform t(transform(i)); QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight())); if (size.width() < r.width() || size.height() < r.height()) @@ -53,7 +196,7 @@ int ENCMap::zoomFit(const QSize &size, const RectC &rect) _zoom = i; } } else - _zoom = _data.zooms().max(); + _zoom = _zooms.max(); updateTransform(); @@ -64,7 +207,7 @@ int ENCMap::zoomIn() { cancelJobs(false); - _zoom = qMin(_zoom + 1, _data.zooms().max()); + _zoom = qMin(_zoom + 1, _zooms.max()); updateTransform(); return _zoom; } @@ -73,7 +216,7 @@ int ENCMap::zoomOut() { cancelJobs(false); - _zoom = qMax(_zoom - 1, _data.zooms().min()); + _zoom = qMax(_zoom - 1, _zooms.min()); updateTransform(); return _zoom; } @@ -118,21 +261,21 @@ bool ENCMap::isRunning(int zoom, const QPoint &xy) const return false; } -void ENCMap::runJob(ENCMapJob *job) +void ENCMap::runJob(ENCJob *job) { _jobs.append(job); - connect(job, &ENCMapJob::finished, this, &ENCMap::jobFinished); + connect(job, &ENCJob::finished, this, &ENCMap::jobFinished); job->run(); } -void ENCMap::removeJob(ENCMapJob *job) +void ENCMap::removeJob(ENCJob *job) { _jobs.removeOne(job); job->deleteLater(); } -void ENCMap::jobFinished(ENCMapJob *job) +void ENCMap::jobFinished(ENCJob *job) { const QList &tiles = job->tiles(); @@ -180,8 +323,9 @@ void ENCMap::draw(QPainter *painter, const QRectF &rect, Flags flags) if (QPixmapCache::find(key(_zoom, ttl), &pm)) painter->drawPixmap(ttl, pm); else - tiles.append(RasterTile(_projection, _transform, &_data, - _zoom, QRect(ttl, QSize(TILE_SIZE, TILE_SIZE)), _tileRatio)); + tiles.append(RasterTile(_projection, _transform, _data, + _zoom, _zooms, QRect(ttl, QSize(TILE_SIZE, TILE_SIZE)), + _tileRatio)); } } @@ -197,7 +341,7 @@ void ENCMap::draw(QPainter *painter, const QRectF &rect, Flags flags) QPixmapCache::insert(key(mt.zoom(), mt.xy()), pm); } } else - runJob(new ENCMapJob(tiles)); + runJob(new ENCJob(tiles)); } } diff --git a/src/map/encmap.h b/src/map/encmap.h index 6a469112..2243cdbb 100644 --- a/src/map/encmap.h +++ b/src/map/encmap.h @@ -1,47 +1,16 @@ #ifndef ENCMAP_H #define ENCMAP_H +#include #include +#include "common/range.h" #include "map.h" #include "projection.h" #include "transform.h" #include "ENC/mapdata.h" -#include "ENC/rastertile.h" +#include "ENC/iso8211.h" -class ENCMapJob : public QObject -{ - Q_OBJECT - -public: - ENCMapJob(const QList &tiles) - : _tiles(tiles) {} - - void run() - { - connect(&_watcher, &QFutureWatcher::finished, this, - &ENCMapJob::handleFinished); - _future = QtConcurrent::map(_tiles, &ENC::RasterTile::render); - _watcher.setFuture(_future); - } - void cancel(bool wait) - { - _future.cancel(); - if (wait) - _future.waitForFinished(); - } - const QList &tiles() const {return _tiles;} - -signals: - void finished(ENCMapJob *job); - -private slots: - void handleFinished() {emit finished(this);} - -private: - QFutureWatcher _watcher; - QFuture _future; - QList _tiles; -}; +class ENCJob; class ENCMap : public Map { @@ -49,8 +18,9 @@ class ENCMap : public Map public: ENCMap(const QString &fileName, QObject *parent = 0); + ~ENCMap() {delete _data;} - QString name() const {return _data.name();} + QString name() const {return _name;} QRectF bounds() {return _bounds;} RectC llBounds(const Projection &) {return _llBounds;} @@ -72,32 +42,73 @@ public: void draw(QPainter *painter, const QRectF &rect, Flags flags); - bool isValid() const {return _data.isValid();} - QString errorString() const {return _data.errorString();} + bool isValid() const {return _valid;} + QString errorString() const {return _errorString;} static Map *create(const QString &path, bool *isMap); private slots: - void jobFinished(ENCMapJob *job); + void jobFinished(ENCJob *job); private: + class Rect { + public: + Rect() + : _minX(INT_MAX), _maxX(INT_MIN), _minY(INT_MAX), _maxY(INT_MIN) {} + Rect(int minX, int maxX, int minY, int maxY) + : _minX(minX), _maxX(maxX), _minY(minY), _maxY(maxY) {} + + int minX() const {return _minX;} + int maxX() const {return _maxX;} + int minY() const {return _minY;} + int maxY() const {return _maxY;} + + void unite(int x, int y) { + if (x < _minX) + _minX = x; + if (x > _maxX) + _maxX = x; + if (y < _minY) + _minY = y; + if (y > _maxY) + _maxY = y; + } + + Rect &operator|=(const Rect &r) {*this = *this | r; return *this;} + Rect operator|(const Rect &r) const + { + return Rect(qMin(_minX, r._minX), qMax(_maxX, r._maxX), + qMin(_minY, r._minY), qMax(_maxY, r._maxY)); + } + + private: + int _minX, _maxX, _minY, _maxY; + }; + Transform transform(int zoom) const; void updateTransform(); bool isRunning(int zoom, const QPoint &xy) const; - void runJob(ENCMapJob *job); - void removeJob(ENCMapJob *job); + void runJob(ENCJob *job); + void removeJob(ENCJob *job); void cancelJobs(bool wait); QString key(int zoom, const QPoint &xy) const; - ENC::MapData _data; + static bool bounds(const ENC::ISO8211::Record &record, Rect &rect); + static bool bounds(const QVector &gv, Rect &b); + static bool processRecord(const ENC::ISO8211::Record &record, + QVector &rv, uint &COMF, QString &name); + + QString _name; + ENC::MapData *_data; Projection _projection; Transform _transform; qreal _tileRatio; RectC _llBounds; QRectF _bounds; + Range _zooms; int _zoom; - QList _jobs; + QList _jobs; bool _valid; QString _errorString; diff --git a/src/map/maplist.cpp b/src/map/maplist.cpp index fec2aa93..47e581be 100644 --- a/src/map/maplist.cpp +++ b/src/map/maplist.cpp @@ -20,6 +20,7 @@ #include "gemfmap.h" #include "oruxmap.h" #include "encmap.h" +#include "encatlas.h" #include "invalidmap.h" #include "maplist.h" @@ -56,6 +57,7 @@ MapList::ParserMap MapList::parsers() map.insert("gemf", &GEMFMap::create); map.insert("otrk2.xml", &OruxMap::create); map.insert("000", &ENCMap::create); + map.insert("031", &ENCAtlas::create); return map; } @@ -153,7 +155,8 @@ QString MapList::formats() return qApp->translate("MapList", "Supported files") + " (" + filter().join(" ") + ");;" - + qApp->translate("MapList", "Electronic Navigational Charts") + " (*.000);;" + + qApp->translate("MapList", "Electronic Navigational Charts") + + " (*.000 *.031);;" + qApp->translate("MapList", "AlpineQuest maps") + " (*.aqm);;" + qApp->translate("MapList", "GEMF maps") + " (*.gemf);;" + qApp->translate("MapList", "Garmin IMG maps") -- 2.11.4.GIT