mirror of
https://github.com/tumic0/GPXSee.git
synced 2024-11-27 21:24:47 +01:00
Added support for ENC atlases (catalogues)
This commit is contained in:
parent
864326210a
commit
b1f104c2ec
@ -122,12 +122,15 @@ HEADERS += src/common/config.h \
|
|||||||
src/data/twonavparser.h \
|
src/data/twonavparser.h \
|
||||||
src/map/ENC/attributes.h \
|
src/map/ENC/attributes.h \
|
||||||
src/map/ENC/mapdata.h \
|
src/map/ENC/mapdata.h \
|
||||||
|
src/map/ENC/atlasdata.h \
|
||||||
src/map/ENC/objects.h \
|
src/map/ENC/objects.h \
|
||||||
src/map/ENC/rastertile.h \
|
src/map/ENC/rastertile.h \
|
||||||
src/map/ENC/style.h \
|
src/map/ENC/style.h \
|
||||||
src/map/IMG/section.h \
|
src/map/IMG/section.h \
|
||||||
src/map/IMG/zoom.h \
|
src/map/IMG/zoom.h \
|
||||||
src/map/conversion.h \
|
src/map/conversion.h \
|
||||||
|
src/map/encatlas.h \
|
||||||
|
src/map/encjob.h \
|
||||||
src/map/encmap.h \
|
src/map/encmap.h \
|
||||||
src/map/ENC/iso8211.h \
|
src/map/ENC/iso8211.h \
|
||||||
src/map/gemfmap.h \
|
src/map/gemfmap.h \
|
||||||
@ -335,10 +338,12 @@ SOURCES += src/main.cpp \
|
|||||||
src/GUI/projectioncombobox.cpp \
|
src/GUI/projectioncombobox.cpp \
|
||||||
src/GUI/passwordedit.cpp \
|
src/GUI/passwordedit.cpp \
|
||||||
src/data/twonavparser.cpp \
|
src/data/twonavparser.cpp \
|
||||||
|
src/map/ENC/atlasdata.cpp \
|
||||||
src/map/ENC/mapdata.cpp \
|
src/map/ENC/mapdata.cpp \
|
||||||
src/map/ENC/rastertile.cpp \
|
src/map/ENC/rastertile.cpp \
|
||||||
src/map/ENC/style.cpp \
|
src/map/ENC/style.cpp \
|
||||||
src/map/conversion.cpp \
|
src/map/conversion.cpp \
|
||||||
|
src/map/encatlas.cpp \
|
||||||
src/map/encmap.cpp \
|
src/map/encmap.cpp \
|
||||||
src/map/ENC/iso8211.cpp \
|
src/map/ENC/iso8211.cpp \
|
||||||
src/map/gemfmap.cpp \
|
src/map/gemfmap.cpp \
|
||||||
|
@ -18,7 +18,6 @@ public:
|
|||||||
int size() const {return (_max - _min);}
|
int size() const {return (_max - _min);}
|
||||||
int min() const {return _min;}
|
int min() const {return _min;}
|
||||||
int max() const {return _max;}
|
int max() const {return _max;}
|
||||||
int mid() const {return _min + size()/2;}
|
|
||||||
|
|
||||||
bool isValid() const {return size() >= 0;}
|
bool isValid() const {return size() >= 0;}
|
||||||
bool isNull() const {return _min == 0 && _max == 0;}
|
bool isNull() const {return _min == 0 && _max == 0;}
|
||||||
|
94
src/map/ENC/atlasdata.cpp
Normal file
94
src/map/ENC/atlasdata.cpp
Normal file
@ -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<MapData::Poly> *polygons,
|
||||||
|
QList<MapData::Line> *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<MapData::Point> *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);
|
||||||
|
}
|
65
src/map/ENC/atlasdata.h
Normal file
65
src/map/ENC/atlasdata.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#ifndef ENC_ATLASDATA_H
|
||||||
|
#define ENC_ATLASDATA_H
|
||||||
|
|
||||||
|
#include <QCache>
|
||||||
|
#include <QMutex>
|
||||||
|
#include "common/rtree.h"
|
||||||
|
#include "mapdata.h"
|
||||||
|
|
||||||
|
namespace ENC {
|
||||||
|
|
||||||
|
typedef QCache<const QString*, MapData> 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<MapData::Poly> *polygons,
|
||||||
|
QList<MapData::Line> *lines);
|
||||||
|
void points(const RectC &rect, QList<MapData::Point> *points);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef RTree<const QString*, double, 2> MapTree;
|
||||||
|
|
||||||
|
struct PolyCTX
|
||||||
|
{
|
||||||
|
PolyCTX(const RectC &rect, QList<MapData::Poly> *polygons,
|
||||||
|
QList<MapData::Line> *lines, MapCache &cache, QMutex &lock)
|
||||||
|
: rect(rect), polygons(polygons), lines(lines), cache(cache),
|
||||||
|
lock(lock) {}
|
||||||
|
|
||||||
|
const RectC ▭
|
||||||
|
QList<MapData::Poly> *polygons;
|
||||||
|
QList<MapData::Line> *lines;
|
||||||
|
MapCache &cache;
|
||||||
|
QMutex &lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PointCTX
|
||||||
|
{
|
||||||
|
PointCTX(const RectC &rect, QList<MapData::Point> *points,
|
||||||
|
MapCache &cache, QMutex &lock) : rect(rect), points(points),
|
||||||
|
cache(cache), lock(lock) {}
|
||||||
|
|
||||||
|
const RectC ▭
|
||||||
|
QList<MapData::Point> *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
|
@ -66,36 +66,27 @@ bool ISO8211::Field::subfield(const char *name, QByteArray *val, int idx) const
|
|||||||
return true;
|
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") {
|
if (str == "A" || str == "I" || str == "R")
|
||||||
type = String;
|
return SubFieldDefinition(tag, String, cnt);
|
||||||
size = cnt;
|
else if (str == "B")
|
||||||
} else if (str == "B") {
|
return SubFieldDefinition(tag, Array, cnt / 8);
|
||||||
type = Array;
|
else if (str == "b11")
|
||||||
size = cnt / 8;
|
return SubFieldDefinition(tag, U8, 1);
|
||||||
} else if (str == "b11") {
|
else if (str == "b12")
|
||||||
type = U8;
|
return SubFieldDefinition(tag, U16, 2);
|
||||||
size = 1;
|
else if (str == "b14")
|
||||||
} else if (str == "b12") {
|
return SubFieldDefinition(tag, U32, 4);
|
||||||
type = U16;
|
else if (str == "b21")
|
||||||
size = 2;
|
return SubFieldDefinition(tag, S8, 1);
|
||||||
} else if (str == "b14") {
|
else if (str == "b22")
|
||||||
type = U32;
|
return SubFieldDefinition(tag, S16, 2);
|
||||||
size = 4;
|
else if (str == "b24")
|
||||||
} else if (str == "b21") {
|
return SubFieldDefinition(tag, S32, 4);
|
||||||
type = S8;
|
else
|
||||||
size = 1;
|
return SubFieldDefinition();
|
||||||
} else if (str == "b22") {
|
|
||||||
type = S16;
|
|
||||||
size = 2;
|
|
||||||
} else if (str == "b24") {
|
|
||||||
type = S32;
|
|
||||||
size = 4;
|
|
||||||
} else
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ISO8211::readDR(QVector<FieldDefinition> &fields)
|
int ISO8211::readDR(QVector<FieldDefinition> &fields)
|
||||||
@ -145,6 +136,8 @@ bool ISO8211::readDDA(const FieldDefinition &def, SubFields &fields)
|
|||||||
{
|
{
|
||||||
static QRegularExpression re("(\\d*)(\\w+)\\(*(\\d*)\\)*");
|
static QRegularExpression re("(\\d*)(\\w+)\\(*(\\d*)\\)*");
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
|
bool repeat = false;
|
||||||
|
QVector<SubFieldDefinition> defs;
|
||||||
|
|
||||||
ba.resize(def.size);
|
ba.resize(def.size);
|
||||||
if (!(_file.seek(def.pos) && _file.read(ba.data(), ba.size()) == ba.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<QByteArray> list(ba.split('\x1f'));
|
QList<QByteArray> list(ba.split('\x1f'));
|
||||||
if (!list.at(1).isEmpty() && list.at(1).front() == '*') {
|
if (!list.at(1).isEmpty() && list.at(1).front() == '*') {
|
||||||
fields.setRepeat(true);
|
repeat = true;
|
||||||
list[1].remove(0, 1);
|
list[1].remove(0, 1);
|
||||||
}
|
}
|
||||||
QList<QByteArray> tags(list.at(1).split('!'));
|
QList<QByteArray> tags(list.at(1).split('!'));
|
||||||
@ -161,7 +154,7 @@ bool ISO8211::readDDA(const FieldDefinition &def, SubFields &fields)
|
|||||||
QRegularExpressionMatchIterator it = re.globalMatch(list.at(2));
|
QRegularExpressionMatchIterator it = re.globalMatch(list.at(2));
|
||||||
int tag = 0;
|
int tag = 0;
|
||||||
|
|
||||||
fields.resize(tags.size());
|
defs.resize(tags.size());
|
||||||
|
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
QRegularExpressionMatch match = it.next();
|
QRegularExpressionMatch match = it.next();
|
||||||
@ -183,15 +176,17 @@ bool ISO8211::readDDA(const FieldDefinition &def, SubFields &fields)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (uint i = 0; i < cnt; i++) {
|
for (uint i = 0; i < cnt; i++) {
|
||||||
SubFieldDefinition &f = fields[tag];
|
SubFieldDefinition sfd(fieldType(typeStr, size, tags.at(tag)));
|
||||||
f.tag = tags.at(tag);
|
if (sfd.type() == Unknown)
|
||||||
if (!fieldType(typeStr, size, f.type, f.size))
|
|
||||||
return false;
|
return false;
|
||||||
|
defs[tag] = sfd;
|
||||||
tag++;
|
tag++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fields = SubFields(defs, repeat);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,8 +223,7 @@ bool ISO8211::readDDR()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def,
|
bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def, Data &data)
|
||||||
const SubFields &fields, Data &data)
|
|
||||||
{
|
{
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
|
|
||||||
@ -242,22 +236,19 @@ bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def,
|
|||||||
const char *dp = ba.constData();
|
const char *dp = ba.constData();
|
||||||
const char *ep = ba.constData() + ba.size() - 1;
|
const char *ep = ba.constData() + ba.size() - 1;
|
||||||
|
|
||||||
data.clear();
|
|
||||||
data.setFields(&fields);
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
QVector<QVariant> row;
|
QVector<QVariant> row;
|
||||||
row.resize(fields.size());
|
row.resize(data.fields()->size());
|
||||||
|
|
||||||
for (int i = 0; i < fields.size(); i++) {
|
for (int i = 0; i < data.fields()->size(); i++) {
|
||||||
const SubFieldDefinition &f = fields.at(i);
|
const SubFieldDefinition &f = data.fields()->at(i);
|
||||||
|
|
||||||
switch (f.type) {
|
switch (f.type()) {
|
||||||
case String:
|
case String:
|
||||||
case Array:
|
case Array:
|
||||||
if (f.size) {
|
if (f.size()) {
|
||||||
row[i] = QVariant(QByteArray(dp, f.size));
|
row[i] = QVariant(QByteArray(dp, f.size()));
|
||||||
dp += f.size;
|
dp += f.size();
|
||||||
} else {
|
} else {
|
||||||
sp = dp;
|
sp = dp;
|
||||||
while (dp < ep && *dp != '\x1f')
|
while (dp < ep && *dp != '\x1f')
|
||||||
@ -290,11 +281,13 @@ bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def,
|
|||||||
row[i] = QVariant(UINT32(dp));
|
row[i] = QVariant(UINT32(dp));
|
||||||
dp += 4;
|
dp += 4;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.append(row);
|
data.append(row);
|
||||||
} while (fields.repeat() && dp < ep);
|
} while (data.fields()->repeat() && dp < ep);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -317,23 +310,33 @@ bool ISO8211::readRecord(Record &record)
|
|||||||
|
|
||||||
for (int i = 0; i < fields.size(); i++) {
|
for (int i = 0; i < fields.size(); i++) {
|
||||||
const FieldDefinition &def = fields.at(i);
|
const FieldDefinition &def = fields.at(i);
|
||||||
Field &f = record[i];
|
|
||||||
|
|
||||||
FieldsMap::const_iterator it = _map.find(def.tag);
|
FieldsMap::const_iterator it = _map.find(def.tag);
|
||||||
if (it == _map.constEnd()) {
|
if (it == _map.constEnd()) {
|
||||||
_errorString = QString("%1: unknown record")
|
_errorString = QString("%1: unknown record").arg(QString(def.tag));
|
||||||
.arg(QString(def.tag));
|
|
||||||
return false;
|
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")
|
_errorString = QString("Error reading %1 record")
|
||||||
.arg(QString(def.tag));
|
.arg(QString(def.tag));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record[i] = Field(def.tag, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
|
@ -17,60 +17,87 @@ namespace ENC {
|
|||||||
class ISO8211
|
class ISO8211
|
||||||
{
|
{
|
||||||
public:
|
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;
|
QByteArray tag;
|
||||||
int pos;
|
int pos;
|
||||||
int size;
|
int size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SubFieldDefinition {
|
class SubFieldDefinition
|
||||||
QByteArray tag;
|
|
||||||
FieldType type;
|
|
||||||
int size;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SubFields : public QVector<SubFieldDefinition>
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SubFields() : QVector<SubFieldDefinition>(), _repeat(false) {}
|
SubFieldDefinition() : _type(Unknown), _size(0) {}
|
||||||
|
SubFieldDefinition(const QByteArray &tag, FieldType type, int size)
|
||||||
|
: _tag(tag), _type(type), _size(size) {}
|
||||||
|
|
||||||
bool repeat() const {return _repeat;}
|
const QByteArray &tag() const {return _tag;}
|
||||||
void setRepeat(bool repeat) {_repeat = repeat;}
|
FieldType type() const {return _type;}
|
||||||
|
int size() const {return _size;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QByteArray _tag;
|
||||||
|
FieldType _type;
|
||||||
|
int _size;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SubFields
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SubFields() : _repeat(false) {}
|
||||||
|
SubFields(const QVector<SubFieldDefinition> &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;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVector<SubFieldDefinition> _defs;
|
||||||
bool _repeat;
|
bool _repeat;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Data : public QVector<QVector<QVariant> >
|
class Data
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Data() : QVector<QVector<QVariant> >(), _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<QVariant> &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 QVariant *field(const QByteArray &name, int idx = 0) const
|
||||||
{
|
{
|
||||||
const QVector<QVariant> &v = at(idx);
|
const QVector<QVariant> &v = _data.at(idx);
|
||||||
|
|
||||||
for (int i = 0; i < _fields->size(); i++)
|
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 &v.at(i);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class ISO8211;
|
||||||
|
|
||||||
|
void append(QVector<QVariant> &row) {_data.append(row);}
|
||||||
|
|
||||||
|
QVector<QVector<QVariant> > _data;
|
||||||
const SubFields *_fields;
|
const SubFields *_fields;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Field
|
class Field
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Field() {}
|
||||||
|
Field(const QByteArray &tag, const Data &data)
|
||||||
|
: _tag(tag), _data(data) {}
|
||||||
|
|
||||||
const QByteArray &tag() const {return _tag;}
|
const QByteArray &tag() const {return _tag;}
|
||||||
void setTag(const QByteArray &tag) {_tag = tag;}
|
|
||||||
Data &rdata() {return _data;}
|
|
||||||
const Data &data() const {return _data;}
|
const Data &data() const {return _data;}
|
||||||
|
|
||||||
bool subfield(const char *name, int *val, int idx = 0) const;
|
bool subfield(const char *name, int *val, int idx = 0) const;
|
||||||
@ -82,17 +109,7 @@ public:
|
|||||||
Data _data;
|
Data _data;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Record : public QVector<Field>
|
typedef QVector<Field> Record;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ISO8211(const QString &path) : _file(path) {}
|
ISO8211(const QString &path) : _file(path) {}
|
||||||
bool readDDR();
|
bool readDDR();
|
||||||
@ -100,16 +117,17 @@ public:
|
|||||||
|
|
||||||
const QString &errorString() const {return _errorString;}
|
const QString &errorString() const {return _errorString;}
|
||||||
|
|
||||||
|
static const Field *field(const Record &record, const QByteArray &name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef QMap<QByteArray, SubFields> FieldsMap;
|
typedef QMap<QByteArray, SubFields> FieldsMap;
|
||||||
|
|
||||||
static bool fieldType(const QString &str, int cnt, FieldType &type,
|
static SubFieldDefinition fieldType(const QString &str, int cnt,
|
||||||
int &size);
|
const QByteArray &tag);
|
||||||
|
|
||||||
int readDR(QVector<FieldDefinition> &fields);
|
int readDR(QVector<FieldDefinition> &fields);
|
||||||
bool readDDA(const FieldDefinition &def, SubFields &fields);
|
bool readDDA(const FieldDefinition &def, SubFields &fields);
|
||||||
bool readUDA(quint64 pos, const FieldDefinition &def,
|
bool readUDA(quint64 pos, const FieldDefinition &def, Data &data);
|
||||||
const SubFields &fields, Data &data);
|
|
||||||
|
|
||||||
QFile _file;
|
QFile _file;
|
||||||
FieldsMap _map;
|
FieldsMap _map;
|
||||||
@ -119,20 +137,20 @@ private:
|
|||||||
#ifndef QT_NO_DEBUG
|
#ifndef QT_NO_DEBUG
|
||||||
inline QDebug operator<<(QDebug dbg, const ISO8211::FieldDefinition &def)
|
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();
|
return dbg.space();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QDebug operator<<(QDebug dbg, const ISO8211::SubFieldDefinition &def)
|
inline QDebug operator<<(QDebug dbg, const ISO8211::SubFieldDefinition &def)
|
||||||
{
|
{
|
||||||
dbg.nospace() << "SubField(" << def.tag << ", " << def.type << ", "
|
dbg.nospace() << "SubField(" << def.tag() << ", " << def.type() << ", "
|
||||||
<< def.size << ")";
|
<< def.size() << ")";
|
||||||
return dbg.space();
|
return dbg.space();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QDebug operator<<(QDebug dbg, const ISO8211::Field &field)
|
inline QDebug operator<<(QDebug dbg, const ISO8211::Field &field)
|
||||||
{
|
{
|
||||||
dbg.nospace() << "Field(" << field.tag() /*<< ", " << field.data()*/ << ")";
|
dbg.nospace() << "Field(" << field.tag() << ")";
|
||||||
return dbg.space();
|
return dbg.space();
|
||||||
}
|
}
|
||||||
#endif // QT_NO_DEBUG
|
#endif // QT_NO_DEBUG
|
||||||
|
@ -141,32 +141,32 @@ static const ISO8211::Field *SGXD(const ISO8211::Record &r)
|
|||||||
{
|
{
|
||||||
const ISO8211::Field *f;
|
const ISO8211::Field *f;
|
||||||
|
|
||||||
if ((f = r.field("SG2D")))
|
if ((f = ISO8211::field(r, "SG2D")))
|
||||||
return f;
|
return f;
|
||||||
else if ((f = r.field("SG3D")))
|
else if ((f = ISO8211::field(r, "SG3D")))
|
||||||
return f;
|
return f;
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool pointCb(MapData::Point *point, void *context)
|
static bool pointCb(const MapData::Point *point, void *context)
|
||||||
{
|
{
|
||||||
QList<MapData::Point*> *points = (QList<MapData::Point*>*)context;
|
QList<MapData::Point> *points = (QList<MapData::Point>*)context;
|
||||||
points->append(point);
|
points->append(*point);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool lineCb(MapData::Line *line, void *context)
|
static bool lineCb(const MapData::Line *line, void *context)
|
||||||
{
|
{
|
||||||
QList<MapData::Line*> *lines = (QList<MapData::Line*>*)context;
|
QList<MapData::Line> *lines = (QList<MapData::Line>*)context;
|
||||||
lines->append(line);
|
lines->append(*line);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool polygonCb(MapData::Poly *polygon, void *context)
|
static bool polygonCb(const MapData::Poly *polygon, void *context)
|
||||||
{
|
{
|
||||||
QList<MapData::Poly*> *polygons = (QList<MapData::Poly*>*)context;
|
QList<MapData::Poly> *polygons = (QList<MapData::Poly>*)context;
|
||||||
polygons->append(polygon);
|
polygons->append(*polygon);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,7 +306,7 @@ QVector<MapData::Sounding> MapData::soundings(const ISO8211::Record &r,
|
|||||||
uint COMF, uint SOMF)
|
uint COMF, uint SOMF)
|
||||||
{
|
{
|
||||||
QVector<Sounding> s;
|
QVector<Sounding> s;
|
||||||
const ISO8211::Field *f = r.field("SG3D");
|
const ISO8211::Field *f = ISO8211::field(r, "SG3D");
|
||||||
if (!f)
|
if (!f)
|
||||||
return QVector<Sounding>();
|
return QVector<Sounding>();
|
||||||
|
|
||||||
@ -328,7 +328,7 @@ QVector<MapData::Sounding> MapData::soundingGeometry(const ISO8211::Record &r,
|
|||||||
quint32 id;
|
quint32 id;
|
||||||
RecordMapIterator it;
|
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)
|
if (!FSPT || FSPT->data().at(0).size() != 4)
|
||||||
return QVector<Sounding>();
|
return QVector<Sounding>();
|
||||||
|
|
||||||
@ -356,7 +356,7 @@ Coordinates MapData::pointGeometry(const ISO8211::Record &r,
|
|||||||
quint32 id;
|
quint32 id;
|
||||||
RecordMapIterator it;
|
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)
|
if (!FSPT || FSPT->data().at(0).size() != 4)
|
||||||
return Coordinates();
|
return Coordinates();
|
||||||
|
|
||||||
@ -386,7 +386,7 @@ QVector<Coordinates> MapData::lineGeometry(const ISO8211::Record &r,
|
|||||||
quint8 type;
|
quint8 type;
|
||||||
quint32 id;
|
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)
|
if (!FSPT || FSPT->data().at(0).size() != 4)
|
||||||
return QVector<Coordinates>();
|
return QVector<Coordinates>();
|
||||||
|
|
||||||
@ -399,7 +399,7 @@ QVector<Coordinates> MapData::lineGeometry(const ISO8211::Record &r,
|
|||||||
if (it == ve.constEnd())
|
if (it == ve.constEnd())
|
||||||
return QVector<Coordinates>();
|
return QVector<Coordinates>();
|
||||||
const ISO8211::Record &FRID = it.value();
|
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)
|
if (!VRPT || VRPT->data().size() != 2)
|
||||||
return QVector<Coordinates>();
|
return QVector<Coordinates>();
|
||||||
|
|
||||||
@ -452,7 +452,7 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc,
|
|||||||
quint8 type;
|
quint8 type;
|
||||||
quint32 id;
|
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)
|
if (!FSPT || FSPT->data().at(0).size() != 4)
|
||||||
return Polygon();
|
return Polygon();
|
||||||
|
|
||||||
@ -471,7 +471,7 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc,
|
|||||||
if (it == ve.constEnd())
|
if (it == ve.constEnd())
|
||||||
return Polygon();
|
return Polygon();
|
||||||
const ISO8211::Record &FRID = it.value();
|
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)
|
if (!VRPT || VRPT->data().size() != 2)
|
||||||
return Polygon();
|
return Polygon();
|
||||||
|
|
||||||
@ -490,6 +490,8 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc,
|
|||||||
const ISO8211::Field *vertexes = SGXD(FRID);
|
const ISO8211::Field *vertexes = SGXD(FRID);
|
||||||
if (ORNT == 2) {
|
if (ORNT == 2) {
|
||||||
v.append(c[1]);
|
v.append(c[1]);
|
||||||
|
if (USAG == 3)
|
||||||
|
v.append(Coordinates());
|
||||||
if (vertexes) {
|
if (vertexes) {
|
||||||
for (int j = vertexes->data().size() - 1; j >= 0; j--) {
|
for (int j = vertexes->data().size() - 1; j >= 0; j--) {
|
||||||
const QVector<QVariant> &cv = vertexes->data().at(j);
|
const QVector<QVariant> &cv = vertexes->data().at(j);
|
||||||
@ -497,9 +499,13 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc,
|
|||||||
COMF));
|
COMF));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (USAG == 3)
|
||||||
|
v.append(Coordinates());
|
||||||
v.append(c[0]);
|
v.append(c[0]);
|
||||||
} else {
|
} else {
|
||||||
v.append(c[0]);
|
v.append(c[0]);
|
||||||
|
if (USAG == 3)
|
||||||
|
v.append(Coordinates());
|
||||||
if (vertexes) {
|
if (vertexes) {
|
||||||
for (int j = 0; j < vertexes->data().size(); j++) {
|
for (int j = 0; j < vertexes->data().size(); j++) {
|
||||||
const QVector<QVariant> &cv = vertexes->data().at(j);
|
const QVector<QVariant> &cv = vertexes->data().at(j);
|
||||||
@ -507,6 +513,8 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc,
|
|||||||
COMF));
|
COMF));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (USAG == 3)
|
||||||
|
v.append(Coordinates());
|
||||||
v.append(c[1]);
|
v.append(c[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,7 +536,7 @@ MapData::Attr MapData::pointAttr(const ISO8211::Record &r, uint OBJL)
|
|||||||
QVector<QByteArray> params(2);
|
QVector<QByteArray> params(2);
|
||||||
uint subtype = 0;
|
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))
|
if (!(ATTF && ATTF->data().at(0).size() == 2))
|
||||||
return Attr();
|
return Attr();
|
||||||
|
|
||||||
@ -580,7 +588,7 @@ MapData::Attr MapData::lineAttr(const ISO8211::Record &r, uint OBJL)
|
|||||||
QVector<QByteArray> params(1);
|
QVector<QByteArray> params(1);
|
||||||
uint subtype = 0;
|
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))
|
if (!(ATTF && ATTF->data().at(0).size() == 2))
|
||||||
return Attr();
|
return Attr();
|
||||||
|
|
||||||
@ -608,7 +616,7 @@ MapData::Attr MapData::polyAttr(const ISO8211::Record &r, uint OBJL)
|
|||||||
QVector<QByteArray> params(1);
|
QVector<QByteArray> params(1);
|
||||||
uint subtype = 0;
|
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))
|
if (!(ATTF && ATTF->data().at(0).size() == 2))
|
||||||
return Attr();
|
return Attr();
|
||||||
|
|
||||||
@ -676,30 +684,6 @@ MapData::Poly *MapData::polyObject(const ISO8211::Record &r,
|
|||||||
attr.params()));
|
attr.params()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MapData::processRecord(const ISO8211::Record &record,
|
|
||||||
QVector<ISO8211::Record> &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,
|
bool MapData::processRecord(const ISO8211::Record &record,
|
||||||
QVector<ISO8211::Record> &fe, RecordMap &vi, RecordMap &vc, RecordMap &ve,
|
QVector<ISO8211::Record> &fe, RecordMap &vi, RecordMap &vc, RecordMap &ve,
|
||||||
RecordMap &vf, uint &COMF, uint &SOMF)
|
RecordMap &vf, uint &COMF, uint &SOMF)
|
||||||
@ -742,92 +726,11 @@ bool MapData::processRecord(const ISO8211::Record &record,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MapData::bounds(const ISO8211::Record &record, Rect &rect)
|
MapData::MapData(const QString &path)
|
||||||
{
|
|
||||||
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<QVariant> &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<ISO8211::Record> &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<ISO8211::Record> 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()
|
|
||||||
{
|
{
|
||||||
RecordMap vi, vc, ve, vf;
|
RecordMap vi, vc, ve, vf;
|
||||||
QVector<ISO8211::Record> fe;
|
QVector<ISO8211::Record> fe;
|
||||||
ISO8211 ddf(_fileName);
|
ISO8211 ddf(path);
|
||||||
ISO8211::Record record;
|
ISO8211::Record record;
|
||||||
uint PRIM, OBJL, COMF = 1, SOMF = 1;
|
uint PRIM, OBJL, COMF = 1, SOMF = 1;
|
||||||
Poly *poly;
|
Poly *poly;
|
||||||
@ -886,25 +789,22 @@ void MapData::load()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapData::clear()
|
MapData::~MapData()
|
||||||
{
|
{
|
||||||
LineTree::Iterator lit;
|
LineTree::Iterator lit;
|
||||||
for (_lines.GetFirst(lit); !_lines.IsNull(lit); _lines.GetNext(lit))
|
for (_lines.GetFirst(lit); !_lines.IsNull(lit); _lines.GetNext(lit))
|
||||||
delete _lines.GetAt(lit);
|
delete _lines.GetAt(lit);
|
||||||
_lines.RemoveAll();
|
|
||||||
|
|
||||||
PolygonTree::Iterator ait;
|
PolygonTree::Iterator ait;
|
||||||
for (_areas.GetFirst(ait); !_areas.IsNull(ait); _areas.GetNext(ait))
|
for (_areas.GetFirst(ait); !_areas.IsNull(ait); _areas.GetNext(ait))
|
||||||
delete _areas.GetAt(ait);
|
delete _areas.GetAt(ait);
|
||||||
_areas.RemoveAll();
|
|
||||||
|
|
||||||
PointTree::Iterator pit;
|
PointTree::Iterator pit;
|
||||||
for (_points.GetFirst(pit); !_points.IsNull(pit); _points.GetNext(pit))
|
for (_points.GetFirst(pit); !_points.IsNull(pit); _points.GetNext(pit))
|
||||||
delete _points.GetAt(pit);
|
delete _points.GetAt(pit);
|
||||||
_points.RemoveAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapData::points(const RectC &rect, QList<Point*> *points) const
|
void MapData::points(const RectC &rect, QList<Point> *points) const
|
||||||
{
|
{
|
||||||
double min[2], max[2];
|
double min[2], max[2];
|
||||||
|
|
||||||
@ -912,7 +812,7 @@ void MapData::points(const RectC &rect, QList<Point*> *points) const
|
|||||||
_points.Search(min, max, pointCb, points);
|
_points.Search(min, max, pointCb, points);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapData::lines(const RectC &rect, QList<Line*> *lines) const
|
void MapData::lines(const RectC &rect, QList<Line> *lines) const
|
||||||
{
|
{
|
||||||
double min[2], max[2];
|
double min[2], max[2];
|
||||||
|
|
||||||
@ -920,48 +820,10 @@ void MapData::lines(const RectC &rect, QList<Line*> *lines) const
|
|||||||
_lines.Search(min, max, lineCb, lines);
|
_lines.Search(min, max, lineCb, lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapData::polygons(const RectC &rect, QList<Poly*> *polygons) const
|
void MapData::polygons(const RectC &rect, QList<Poly> *polygons) const
|
||||||
{
|
{
|
||||||
double min[2], max[2];
|
double min[2], max[2];
|
||||||
|
|
||||||
rectcBounds(rect, min, max);
|
rectcBounds(rect, min, max);
|
||||||
_areas.Search(min, max, polygonCb, polygons);
|
_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);
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
#ifndef ENC_MAPDATA_H
|
#ifndef ENC_MAPDATA_H
|
||||||
#define ENC_MAPDATA_H
|
#define ENC_MAPDATA_H
|
||||||
|
|
||||||
#include <climits>
|
|
||||||
#include "common/rectc.h"
|
#include "common/rectc.h"
|
||||||
#include "common/rtree.h"
|
#include "common/rtree.h"
|
||||||
#include "common/polygon.h"
|
#include "common/polygon.h"
|
||||||
#include "common/range.h"
|
|
||||||
#include "iso8211.h"
|
#include "iso8211.h"
|
||||||
|
|
||||||
namespace ENC {
|
namespace ENC {
|
||||||
@ -68,55 +66,11 @@ public:
|
|||||||
MapData(const QString &path);
|
MapData(const QString &path);
|
||||||
~MapData();
|
~MapData();
|
||||||
|
|
||||||
const QString &name() const {return _name;}
|
void polygons(const RectC &rect, QList<Poly> *polygons) const;
|
||||||
RectC bounds() const {return _bounds;}
|
void lines(const RectC &rect, QList<Line> *lines) const;
|
||||||
Range zooms() const;
|
void points(const RectC &rect, QList<Point> *points) const;
|
||||||
|
|
||||||
void polygons(const RectC &rect, QList<Poly*> *polygons) const;
|
|
||||||
void lines(const RectC &rect, QList<Line*> *lines) const;
|
|
||||||
void points(const RectC &rect, QList<Point*> *points) const;
|
|
||||||
|
|
||||||
void load();
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
bool isValid() const {return _bounds.isValid();}
|
|
||||||
QString errorString() const {return _errorString;}
|
|
||||||
|
|
||||||
private:
|
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 {
|
class Attr {
|
||||||
public:
|
public:
|
||||||
Attr() : _subtype(0) {}
|
Attr() : _subtype(0) {}
|
||||||
@ -144,9 +98,9 @@ private:
|
|||||||
|
|
||||||
typedef QMap<uint, ISO8211::Record> RecordMap;
|
typedef QMap<uint, ISO8211::Record> RecordMap;
|
||||||
typedef QMap<uint, ISO8211::Record>::const_iterator RecordMapIterator;
|
typedef QMap<uint, ISO8211::Record>::const_iterator RecordMapIterator;
|
||||||
typedef RTree<Poly*, double, 2> PolygonTree;
|
typedef RTree<const Poly*, double, 2> PolygonTree;
|
||||||
typedef RTree<Line*, double, 2> LineTree;
|
typedef RTree<const Line*, double, 2> LineTree;
|
||||||
typedef RTree<Point*, double, 2> PointTree;
|
typedef RTree<const Point*, double, 2> PointTree;
|
||||||
|
|
||||||
static QVector<Sounding> soundings(const ISO8211::Record &r, uint COMF,
|
static QVector<Sounding> soundings(const ISO8211::Record &r, uint COMF,
|
||||||
uint SOMF);
|
uint SOMF);
|
||||||
@ -168,21 +122,14 @@ private:
|
|||||||
const RecordMap &ve, uint COMF, uint OBJL);
|
const RecordMap &ve, uint COMF, uint OBJL);
|
||||||
static Poly *polyObject(const ISO8211::Record &r, const RecordMap &vc,
|
static Poly *polyObject(const ISO8211::Record &r, const RecordMap &vc,
|
||||||
const RecordMap &ve, uint COMF,uint OBJL);
|
const RecordMap &ve, uint COMF,uint OBJL);
|
||||||
static bool bounds(const ISO8211::Record &record, Rect &rect);
|
|
||||||
static bool bounds(const QVector<ISO8211::Record> &gv, Rect &b);
|
|
||||||
static bool processRecord(const ISO8211::Record &record,
|
static bool processRecord(const ISO8211::Record &record,
|
||||||
QVector<ISO8211::Record> &fe, RecordMap &vi, RecordMap &vc, RecordMap &ve,
|
QVector<ISO8211::Record> &fe, RecordMap &vi, RecordMap &vc, RecordMap &ve,
|
||||||
RecordMap &vf, uint &COMF, uint &SOMF);
|
RecordMap &vf, uint &COMF, uint &SOMF);
|
||||||
static bool processRecord(const ISO8211::Record &record,
|
|
||||||
QVector<ISO8211::Record> &rv, uint &COMF, QString &name);
|
|
||||||
|
|
||||||
QString _fileName;
|
|
||||||
QString _name;
|
|
||||||
RectC _bounds;
|
|
||||||
PolygonTree _areas;
|
PolygonTree _areas;
|
||||||
LineTree _lines;
|
LineTree _lines;
|
||||||
PointTree _points;
|
PointTree _points;
|
||||||
QString _errorString;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,16 +12,11 @@ using namespace ENC;
|
|||||||
#define TEXT_EXTENT 160
|
#define TEXT_EXTENT 160
|
||||||
#define TSSLPT_SIZE 0.005 /* ll */
|
#define TSSLPT_SIZE 0.005 /* ll */
|
||||||
|
|
||||||
typedef QMap<Coordinates, const MapData::Point*> PointMap;
|
typedef QSet<Coordinates> PointSet;
|
||||||
|
|
||||||
static const float C1 = 0.866025f; /* sqrt(3)/2 */
|
static const float C1 = 0.866025f; /* sqrt(3)/2 */
|
||||||
static const QColor haloColor(Qt::white);
|
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)
|
static QFont pixelSizeFont(int pixelSize)
|
||||||
{
|
{
|
||||||
QFont f;
|
QFont f;
|
||||||
@ -114,7 +109,9 @@ static bool showLabel(const QImage *img, const Range &range, int zoom, int type)
|
|||||||
if (type>>16 == I_DISMAR)
|
if (type>>16 == I_DISMAR)
|
||||||
return true;
|
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 false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -127,9 +124,14 @@ QPainterPath RasterTile::painterPath(const Polygon &polygon) const
|
|||||||
for (int i = 0; i < polygon.size(); i++) {
|
for (int i = 0; i < polygon.size(); i++) {
|
||||||
const QVector<Coordinates> &subpath = polygon.at(i);
|
const QVector<Coordinates> &subpath = polygon.at(i);
|
||||||
|
|
||||||
QVector<QPointF> p(subpath.size());
|
QVector<QPointF> p;
|
||||||
for (int j = 0; j < subpath.size(); j++)
|
p.reserve(subpath.size());
|
||||||
p[j] = ll2xy(subpath.at(j));
|
|
||||||
|
for (int j = 0; j < subpath.size(); j++) {
|
||||||
|
const Coordinates &c = subpath.at(j);
|
||||||
|
if (!c.isNull())
|
||||||
|
p.append(ll2xy(c));
|
||||||
|
}
|
||||||
path.addPolygon(p);
|
path.addPolygon(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,6 +149,35 @@ QPolygonF RasterTile::polyline(const QVector<Coordinates> &path) const
|
|||||||
return polygon;
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<QPolygonF> RasterTile::polylineM(const QVector<Coordinates> &path) const
|
||||||
|
{
|
||||||
|
QVector<QPolygonF> 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
|
QPolygonF RasterTile::tsslptArrow(const Coordinates &c, qreal angle) const
|
||||||
{
|
{
|
||||||
Coordinates t[3], r[4];
|
Coordinates t[3], r[4];
|
||||||
@ -173,14 +204,14 @@ QPolygonF RasterTile::tsslptArrow(const Coordinates &c, qreal angle) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RasterTile::drawArrows(QPainter *painter,
|
void RasterTile::drawArrows(QPainter *painter,
|
||||||
const QList<MapData::Poly*> &polygons)
|
const QList<MapData::Poly> &polygons)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < polygons.size(); i++) {
|
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) {
|
if (poly.type()>>16 == TSSLPT) {
|
||||||
QPolygonF polygon(tsslptArrow(centroid(poly->path().first()),
|
QPolygonF polygon(tsslptArrow(centroid(poly.path().first()),
|
||||||
deg2rad(180 - poly->param().toDouble())));
|
deg2rad(180 - poly.param().toDouble())));
|
||||||
|
|
||||||
painter->setPen(QPen(QColor("#eb49eb"), 1));
|
painter->setPen(QPen(QColor("#eb49eb"), 1));
|
||||||
painter->setBrush(QBrush("#80eb49eb"));
|
painter->setBrush(QBrush("#80eb49eb"));
|
||||||
@ -190,45 +221,55 @@ void RasterTile::drawArrows(QPainter *painter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RasterTile::drawPolygons(QPainter *painter,
|
void RasterTile::drawPolygons(QPainter *painter,
|
||||||
const QList<MapData::Poly*> &polygons)
|
const QList<MapData::Poly> &polygons)
|
||||||
{
|
{
|
||||||
const Style &s = style();
|
const Style &s = style();
|
||||||
|
|
||||||
for (int n = 0; n < s.drawOrder().size(); n++) {
|
for (int n = 0; n < s.drawOrder().size(); n++) {
|
||||||
for (int i = 0; i < polygons.size(); i++) {
|
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() != s.drawOrder().at(n))
|
if (poly.type() != s.drawOrder().at(n))
|
||||||
continue;
|
continue;
|
||||||
const Style::Polygon &style = s.polygon(poly->type());
|
const Style::Polygon &style = s.polygon(poly.type());
|
||||||
|
|
||||||
if (!style.img().isNull()) {
|
if (!style.img().isNull()) {
|
||||||
for (int i = 0; i < poly->path().size(); i++)
|
for (int i = 0; i < poly.path().size(); i++)
|
||||||
BitmapLine::draw(painter, polyline(poly->path().at(i)),
|
BitmapLine::draw(painter, polylineM(poly.path().at(i)),
|
||||||
style.img());
|
style.img());
|
||||||
} else {
|
} else {
|
||||||
painter->setPen(style.pen());
|
if (style.brush() != Qt::NoBrush) {
|
||||||
painter->setBrush(style.brush());
|
painter->setPen(Qt::NoPen);
|
||||||
painter->drawPath(painterPath(poly->path()));
|
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<QPolygonF> 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<MapData::Line*> &lines)
|
void RasterTile::drawLines(QPainter *painter, const QList<MapData::Line> &lines)
|
||||||
{
|
{
|
||||||
const Style &s = style();
|
const Style &s = style();
|
||||||
|
|
||||||
painter->setBrush(Qt::NoBrush);
|
painter->setBrush(Qt::NoBrush);
|
||||||
|
|
||||||
for (int i = 0; i < lines.size(); i++) {
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
const MapData::Line *line = lines.at(i);
|
const MapData::Line &line = lines.at(i);
|
||||||
const Style::Line &style = s.line(line->type());
|
const Style::Line &style = s.line(line.type());
|
||||||
|
|
||||||
if (!style.img().isNull()) {
|
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) {
|
} else if (style.pen() != Qt::NoPen) {
|
||||||
painter->setPen(style.pen());
|
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);
|
textItems.at(i)->paint(painter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterTile::processPolygons(const QList<MapData::Poly*> &polygons,
|
void RasterTile::processPolygons(const QList<MapData::Poly> &polygons,
|
||||||
QList<TextItem*> &textItems)
|
QList<TextItem*> &textItems)
|
||||||
{
|
{
|
||||||
const Style &s = style();
|
const Style &s = style();
|
||||||
|
|
||||||
for (int i = 0; i < polygons.size(); i++) {
|
for (int i = 0; i < polygons.size(); i++) {
|
||||||
const MapData::Poly *poly = polygons.at(i);
|
const MapData::Poly &poly = polygons.at(i);
|
||||||
uint type = poly->type()>>16;
|
uint type = poly.type()>>16;
|
||||||
|
|
||||||
if (!(type == HRBFAC || type == I_TRNBSN
|
if (!(type == HRBFAC || type == I_TRNBSN
|
||||||
|| poly->type() == SUBTYPE(I_BERTHS, 6)))
|
|| poly.type() == SUBTYPE(I_BERTHS, 6)))
|
||||||
continue;
|
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();
|
const QImage *img = style.img().isNull() ? 0 : &style.img();
|
||||||
if (!img)
|
if (!img)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
TextPointItem *item = new TextPointItem(
|
TextPointItem *item = new TextPointItem(
|
||||||
ll2xy(centroid(poly->path().first())).toPoint(),
|
ll2xy(centroid(poly.path().first())).toPoint(),
|
||||||
0, 0, img, 0, 0, 0, 0);
|
0, 0, img, 0, 0, 0, 0);
|
||||||
if (item->isValid() && !item->collides(textItems))
|
if (item->isValid() && !item->collides(textItems))
|
||||||
textItems.append(item);
|
textItems.append(item);
|
||||||
@ -267,40 +308,40 @@ void RasterTile::processPolygons(const QList<MapData::Poly*> &polygons,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterTile::processPoints(QList<MapData::Point*> &points,
|
void RasterTile::processPoints(QList<MapData::Point> &points,
|
||||||
QList<TextItem*> &textItems, QList<TextItem*> &lights)
|
QList<TextItem*> &textItems, QList<TextItem*> &lights)
|
||||||
{
|
{
|
||||||
const Style &s = style();
|
const Style &s = style();
|
||||||
PointMap lightsMap, signalsMap;
|
PointSet lightsSet, signalsSet;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
std::sort(points.begin(), points.end(), pointLess);
|
std::sort(points.begin(), points.end());
|
||||||
|
|
||||||
/* Lights & Signals */
|
/* Lights & Signals */
|
||||||
for (i = 0; i < points.size(); i++) {
|
for (i = 0; i < points.size(); i++) {
|
||||||
const MapData::Point *point = points.at(i);
|
const MapData::Point &point = points.at(i);
|
||||||
if (point->type()>>16 == LIGHTS)
|
if (point.type()>>16 == LIGHTS)
|
||||||
lightsMap.insert(point->pos(), point);
|
lightsSet.insert(point.pos());
|
||||||
else if (point->type()>>16 == FOGSIG)
|
else if (point.type()>>16 == FOGSIG)
|
||||||
signalsMap.insert(point->pos(), point);
|
signalsSet.insert(point.pos());
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Everything else */
|
/* Everything else */
|
||||||
for ( ; i < points.size(); i++) {
|
for ( ; i < points.size(); i++) {
|
||||||
const MapData::Point *point = points.at(i);
|
const MapData::Point &point = points.at(i);
|
||||||
QPoint pos(ll2xy(point->pos()).toPoint());
|
QPoint pos(ll2xy(point.pos()).toPoint());
|
||||||
const Style::Point &style = s.point(point->type());
|
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 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;
|
? font(style.textFontSize()) : 0;
|
||||||
const QColor *color = &style.textColor();
|
const QColor *color = &style.textColor();
|
||||||
const QColor *hColor = style.haloColor().isValid()
|
const QColor *hColor = style.haloColor().isValid()
|
||||||
? &style.haloColor() : 0;
|
? &style.haloColor() : 0;
|
||||||
double rotate = angle(point->type(), point->param());
|
double rotate = angle(point.type(), point.param());
|
||||||
|
|
||||||
if ((!label || !fnt) && !img)
|
if ((!label || !fnt) && !img)
|
||||||
continue;
|
continue;
|
||||||
@ -309,34 +350,34 @@ void RasterTile::processPoints(QList<MapData::Point*> &points,
|
|||||||
hColor, 0, 2, rotate);
|
hColor, 0, 2, rotate);
|
||||||
if (item->isValid() && !item->collides(textItems)) {
|
if (item->isValid() && !item->collides(textItems)) {
|
||||||
textItems.append(item);
|
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));
|
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));
|
lights.append(new TextPointItem(pos, 0, 0, signal(), 0, 0, 0, 0));
|
||||||
} else
|
} else
|
||||||
delete item;
|
delete item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterTile::processLines(const QList<MapData::Line*> &lines,
|
void RasterTile::processLines(const QList<MapData::Line> &lines,
|
||||||
QList<TextItem*> &textItems)
|
QList<TextItem*> &textItems)
|
||||||
{
|
{
|
||||||
const Style &s = style();
|
const Style &s = style();
|
||||||
|
|
||||||
for (int i = 0; i < lines.size(); i++) {
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
const MapData::Line *line = lines.at(i);
|
const MapData::Line &line = lines.at(i);
|
||||||
const Style::Line &style = s.line(line->type());
|
const Style::Line &style = s.line(line.type());
|
||||||
|
|
||||||
if (style.img().isNull() && style.pen() == Qt::NoPen)
|
if (style.img().isNull() && style.pen() == Qt::NoPen)
|
||||||
continue;
|
continue;
|
||||||
if (line->label().isEmpty() || style.textFontSize() == Style::None)
|
if (line.label().isEmpty() || style.textFontSize() == Style::None)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const QFont *fnt = font(style.textFontSize());
|
const QFont *fnt = font(style.textFontSize());
|
||||||
const QColor *color = &style.textColor();
|
const QColor *color = &style.textColor();
|
||||||
|
|
||||||
TextPathItem *item = new TextPathItem(polyline(line->path()),
|
TextPathItem *item = new TextPathItem(polyline(line.path()),
|
||||||
&line->label(), _rect, fnt, color, 0);
|
&line.label(), _rect, fnt, color, 0);
|
||||||
if (item->isValid() && !item->collides(textItems))
|
if (item->isValid() && !item->collides(textItems))
|
||||||
textItems.append(item);
|
textItems.append(item);
|
||||||
else
|
else
|
||||||
@ -344,8 +385,8 @@ void RasterTile::processLines(const QList<MapData::Line*> &lines,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterTile::fetchData(QList<MapData::Poly*> &polygons,
|
void RasterTile::fetchData(QList<MapData::Poly> &polygons,
|
||||||
QList<MapData::Line*> &lines, QList<MapData::Point*> &points)
|
QList<MapData::Line> &lines, QList<MapData::Point> &points)
|
||||||
{
|
{
|
||||||
QPoint ttl(_rect.topLeft());
|
QPoint ttl(_rect.topLeft());
|
||||||
|
|
||||||
@ -354,22 +395,28 @@ void RasterTile::fetchData(QList<MapData::Poly*> &polygons,
|
|||||||
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
|
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
|
||||||
_transform.img2proj(polyRect.bottomRight()));
|
_transform.img2proj(polyRect.bottomRight()));
|
||||||
RectC polyRectC(polyRectD.toRectC(_proj, 20));
|
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),
|
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT, ttl.y() - TEXT_EXTENT),
|
||||||
QPointF(ttl.x() + _rect.width() + TEXT_EXTENT, ttl.y() + _rect.height()
|
QPointF(ttl.x() + _rect.width() + TEXT_EXTENT, ttl.y() + _rect.height()
|
||||||
+ TEXT_EXTENT));
|
+ TEXT_EXTENT));
|
||||||
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
|
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
|
||||||
_transform.img2proj(pointRect.bottomRight()));
|
_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()
|
void RasterTile::render()
|
||||||
{
|
{
|
||||||
QList<MapData::Line*> lines;
|
QList<MapData::Line> lines;
|
||||||
QList<MapData::Poly*> polygons;
|
QList<MapData::Poly> polygons;
|
||||||
QList<MapData::Point*> points;
|
QList<MapData::Point> points;
|
||||||
QList<TextItem*> textItems, lights;
|
QList<TextItem*> textItems, lights;
|
||||||
|
|
||||||
_pixmap.setDevicePixelRatio(_ratio);
|
_pixmap.setDevicePixelRatio(_ratio);
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
#define ENC_RASTERTILE_H
|
#define ENC_RASTERTILE_H
|
||||||
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
#include "common/range.h"
|
||||||
#include "map/projection.h"
|
#include "map/projection.h"
|
||||||
#include "map/transform.h"
|
#include "map/transform.h"
|
||||||
#include "map/textpointitem.h"
|
#include "map/textpointitem.h"
|
||||||
#include "mapdata.h"
|
#include "mapdata.h"
|
||||||
|
#include "atlasdata.h"
|
||||||
|
|
||||||
class TextItem;
|
class TextItem;
|
||||||
|
|
||||||
@ -15,9 +17,14 @@ class RasterTile
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RasterTile(const Projection &proj, const Transform &transform,
|
RasterTile(const Projection &proj, const Transform &transform,
|
||||||
const MapData *data, int zoom, const QRect &rect, qreal ratio)
|
const MapData *data, int zoom, const Range &zoomRange, const QRect &rect,
|
||||||
: _proj(proj), _transform(transform), _data(data), _zoom(zoom),
|
qreal ratio) : _proj(proj), _transform(transform), _map(data), _atlas(0),
|
||||||
_rect(rect), _ratio(ratio),
|
_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) {}
|
_pixmap(rect.width() * ratio, rect.height() * ratio), _valid(false) {}
|
||||||
|
|
||||||
int zoom() const {return _zoom;}
|
int zoom() const {return _zoom;}
|
||||||
@ -28,30 +35,36 @@ public:
|
|||||||
void render();
|
void render();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void fetchData(QList<MapData::Poly*> &polygons, QList<MapData::Line*> &lines,
|
void fetchData(QList<MapData::Poly> &polygons, QList<MapData::Line> &lines,
|
||||||
QList<MapData::Point*> &points);
|
QList<MapData::Point> &points);
|
||||||
QPointF ll2xy(const Coordinates &c) const
|
QPointF ll2xy(const Coordinates &c) const
|
||||||
{return _transform.proj2img(_proj.ll2xy(c));}
|
{return _transform.proj2img(_proj.ll2xy(c));}
|
||||||
QPainterPath painterPath(const Polygon &polygon) const;
|
QPainterPath painterPath(const Polygon &polygon) const;
|
||||||
QPolygonF polyline(const QVector<Coordinates> &path) const;
|
QPolygonF polyline(const QVector<Coordinates> &path) const;
|
||||||
|
QVector<QPolygonF> polylineM(const QVector<Coordinates> &path) const;
|
||||||
QPolygonF tsslptArrow(const Coordinates &c, qreal angle) const;
|
QPolygonF tsslptArrow(const Coordinates &c, qreal angle) const;
|
||||||
void processPoints(QList<MapData::Point *> &points,
|
void processPoints(QList<MapData::Point> &points,
|
||||||
QList<TextItem*> &textItems, QList<TextItem *> &lights);
|
QList<TextItem*> &textItems, QList<TextItem *> &lights);
|
||||||
void processLines(const QList<MapData::Line *> &lines,
|
void processLines(const QList<MapData::Line> &lines,
|
||||||
QList<TextItem*> &textItems);
|
QList<TextItem*> &textItems);
|
||||||
void processPolygons(const QList<MapData::Poly *> &polygons,
|
void processPolygons(const QList<MapData::Poly> &polygons,
|
||||||
QList<TextItem*> &textItems);
|
QList<TextItem*> &textItems);
|
||||||
void drawBitmapPath(QPainter *painter, const QImage &img,
|
void drawBitmapPath(QPainter *painter, const QImage &img,
|
||||||
const Polygon &polygon);
|
const Polygon &polygon);
|
||||||
void drawArrows(QPainter *painter, const QList<MapData::Poly*> &polygons);
|
void drawArrows(QPainter *painter, const QList<MapData::Poly> &polygons);
|
||||||
void drawPolygons(QPainter *painter, const QList<MapData::Poly *> &polygons);
|
void drawPolygons(QPainter *painter, const QList<MapData::Poly> &polygons);
|
||||||
void drawLines(QPainter *painter, const QList<MapData::Line *> &lines);
|
void drawLines(QPainter *painter, const QList<MapData::Line> &lines);
|
||||||
void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems);
|
void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems);
|
||||||
|
|
||||||
|
static bool polyCb(MapData *data, void *context);
|
||||||
|
static bool pointCb(MapData *data, void *context);
|
||||||
|
|
||||||
Projection _proj;
|
Projection _proj;
|
||||||
Transform _transform;
|
Transform _transform;
|
||||||
const MapData *_data;
|
const MapData *_map;
|
||||||
|
AtlasData *_atlas;
|
||||||
int _zoom;
|
int _zoom;
|
||||||
|
Range _zoomRange;
|
||||||
QRect _rect;
|
QRect _rect;
|
||||||
qreal _ratio;
|
qreal _ratio;
|
||||||
QPixmap _pixmap;
|
QPixmap _pixmap;
|
||||||
|
@ -39,3 +39,10 @@ void BitmapLine::draw(QPainter *painter, const QPolygonF &line,
|
|||||||
painter->restore();
|
painter->restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BitmapLine::draw(QPainter *painter, const QVector<QPolygonF> &lines,
|
||||||
|
const QImage &img)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < lines.size(); i++)
|
||||||
|
draw(painter, lines.at(i), img);
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#ifndef BITMAPLINE_H
|
#ifndef BITMAPLINE_H
|
||||||
#define BITMAPLINE_H
|
#define BITMAPLINE_H
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
class QPainter;
|
class QPainter;
|
||||||
class QImage;
|
class QImage;
|
||||||
class QPolygonF;
|
class QPolygonF;
|
||||||
@ -8,6 +10,8 @@ class QPolygonF;
|
|||||||
namespace BitmapLine
|
namespace BitmapLine
|
||||||
{
|
{
|
||||||
void draw(QPainter *painter, const QPolygonF &line, const QImage &img);
|
void draw(QPainter *painter, const QPolygonF &line, const QImage &img);
|
||||||
|
void draw(QPainter *painter, const QVector<QPolygonF> &lines,
|
||||||
|
const QImage &img);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // BITMAPLINE_H
|
#endif // BITMAPLINE_H
|
||||||
|
387
src/map/encatlas.cpp
Normal file
387
src/map/encatlas.cpp
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
#include <QPainter>
|
||||||
|
#include <QPixmapCache>
|
||||||
|
#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<<z) : (2.0 * M_PI * WGS84_RADIUS) / (1<<z);
|
||||||
|
PointD topLeft(_projection.ll2xy(_llBounds.topLeft()));
|
||||||
|
return Transform(ReferencePoint(PointD(0, 0), topLeft),
|
||||||
|
PointD(scale, scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ENCAtlas::updateTransform()
|
||||||
|
{
|
||||||
|
_transform = transform(_zoom);
|
||||||
|
|
||||||
|
RectD prect(_llBounds, _projection);
|
||||||
|
_bounds = QRectF(_transform.proj2img(prect.topLeft()),
|
||||||
|
_transform.proj2img(prect.bottomRight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ENCAtlas::isRunning(int zoom, const QPoint &xy) const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _jobs.size(); i++) {
|
||||||
|
const QList<ENC::RasterTile> &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<ENC::RasterTile> &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<RasterTile> 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<void> 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);
|
||||||
|
}
|
98
src/map/encatlas.h
Normal file
98
src/map/encatlas.h
Normal file
@ -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<IntendedUsage, ENC::AtlasData*> _data;
|
||||||
|
ENC::MapCache _cache;
|
||||||
|
QMutex _lock;
|
||||||
|
IntendedUsage _usage;
|
||||||
|
int _zoom;
|
||||||
|
|
||||||
|
QList<ENCJob*> _jobs;
|
||||||
|
|
||||||
|
bool _valid;
|
||||||
|
QString _errorString;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ENCATLAS_H
|
42
src/map/encjob.h
Normal file
42
src/map/encjob.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#ifndef ENCJOB_H
|
||||||
|
#define ENCJOB_H
|
||||||
|
|
||||||
|
#include <QtConcurrent>
|
||||||
|
#include "ENC/rastertile.h"
|
||||||
|
|
||||||
|
class ENCJob : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ENCJob(const QList<ENC::RasterTile> &tiles)
|
||||||
|
: _tiles(tiles) {}
|
||||||
|
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
connect(&_watcher, &QFutureWatcher<void>::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<ENC::RasterTile> &tiles() const {return _tiles;}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void finished(ENCJob *job);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleFinished() {emit finished(this);}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QFutureWatcher<void> _watcher;
|
||||||
|
QFuture<void> _future;
|
||||||
|
QList<ENC::RasterTile> _tiles;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ENCJOB_H
|
@ -4,6 +4,7 @@
|
|||||||
#include "common/wgs84.h"
|
#include "common/wgs84.h"
|
||||||
#include "rectd.h"
|
#include "rectd.h"
|
||||||
#include "pcs.h"
|
#include "pcs.h"
|
||||||
|
#include "encjob.h"
|
||||||
#include "encmap.h"
|
#include "encmap.h"
|
||||||
|
|
||||||
|
|
||||||
@ -11,14 +12,153 @@ using namespace ENC;
|
|||||||
|
|
||||||
#define TILE_SIZE 512
|
#define TILE_SIZE 512
|
||||||
|
|
||||||
ENCMap::ENCMap(const QString &fileName, QObject *parent)
|
static Range zooms(const RectC &bounds)
|
||||||
: Map(fileName, parent), _data(fileName), _projection(PCS::pcs(3857)),
|
|
||||||
_tileRatio(1.0), _zoom(0)
|
|
||||||
{
|
{
|
||||||
if (_data.isValid()) {
|
double size = qMin(bounds.width(), bounds.height());
|
||||||
_llBounds = _data.bounds();
|
|
||||||
updateTransform();
|
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<QVariant> &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<ISO8211::Record> &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<ISO8211::Record> &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(0), _projection(PCS::pcs(3857)),
|
||||||
|
_tileRatio(1.0), _valid(false)
|
||||||
|
{
|
||||||
|
QVector<ISO8211::Record> 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,
|
void ENCMap::load(const Projection &in, const Projection &out,
|
||||||
@ -29,14 +169,17 @@ void ENCMap::load(const Projection &in, const Projection &out,
|
|||||||
|
|
||||||
_tileRatio = deviceRatio;
|
_tileRatio = deviceRatio;
|
||||||
_projection = out;
|
_projection = out;
|
||||||
_data.load();
|
Q_ASSERT(!_data);
|
||||||
|
_data = new MapData(path());
|
||||||
QPixmapCache::clear();
|
QPixmapCache::clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ENCMap::unload()
|
void ENCMap::unload()
|
||||||
{
|
{
|
||||||
cancelJobs(true);
|
cancelJobs(true);
|
||||||
_data.clear();
|
|
||||||
|
delete _data;
|
||||||
|
_data = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ENCMap::zoomFit(const QSize &size, const RectC &rect)
|
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()) {
|
if (rect.isValid()) {
|
||||||
RectD pr(rect, _projection, 10);
|
RectD pr(rect, _projection, 10);
|
||||||
|
|
||||||
_zoom = _data.zooms().min();
|
_zoom = _zooms.min();
|
||||||
for (int i = _data.zooms().min() + 1; i <= _data.zooms().max(); i++) {
|
for (int i = _zooms.min() + 1; i <= _zooms.max(); i++) {
|
||||||
Transform t(transform(i));
|
Transform t(transform(i));
|
||||||
QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight()));
|
QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight()));
|
||||||
if (size.width() < r.width() || size.height() < r.height())
|
if (size.width() < r.width() || size.height() < r.height())
|
||||||
@ -53,7 +196,7 @@ int ENCMap::zoomFit(const QSize &size, const RectC &rect)
|
|||||||
_zoom = i;
|
_zoom = i;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
_zoom = _data.zooms().max();
|
_zoom = _zooms.max();
|
||||||
|
|
||||||
updateTransform();
|
updateTransform();
|
||||||
|
|
||||||
@ -64,7 +207,7 @@ int ENCMap::zoomIn()
|
|||||||
{
|
{
|
||||||
cancelJobs(false);
|
cancelJobs(false);
|
||||||
|
|
||||||
_zoom = qMin(_zoom + 1, _data.zooms().max());
|
_zoom = qMin(_zoom + 1, _zooms.max());
|
||||||
updateTransform();
|
updateTransform();
|
||||||
return _zoom;
|
return _zoom;
|
||||||
}
|
}
|
||||||
@ -73,7 +216,7 @@ int ENCMap::zoomOut()
|
|||||||
{
|
{
|
||||||
cancelJobs(false);
|
cancelJobs(false);
|
||||||
|
|
||||||
_zoom = qMax(_zoom - 1, _data.zooms().min());
|
_zoom = qMax(_zoom - 1, _zooms.min());
|
||||||
updateTransform();
|
updateTransform();
|
||||||
return _zoom;
|
return _zoom;
|
||||||
}
|
}
|
||||||
@ -118,21 +261,21 @@ bool ENCMap::isRunning(int zoom, const QPoint &xy) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ENCMap::runJob(ENCMapJob *job)
|
void ENCMap::runJob(ENCJob *job)
|
||||||
{
|
{
|
||||||
_jobs.append(job);
|
_jobs.append(job);
|
||||||
|
|
||||||
connect(job, &ENCMapJob::finished, this, &ENCMap::jobFinished);
|
connect(job, &ENCJob::finished, this, &ENCMap::jobFinished);
|
||||||
job->run();
|
job->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ENCMap::removeJob(ENCMapJob *job)
|
void ENCMap::removeJob(ENCJob *job)
|
||||||
{
|
{
|
||||||
_jobs.removeOne(job);
|
_jobs.removeOne(job);
|
||||||
job->deleteLater();
|
job->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ENCMap::jobFinished(ENCMapJob *job)
|
void ENCMap::jobFinished(ENCJob *job)
|
||||||
{
|
{
|
||||||
const QList<ENC::RasterTile> &tiles = job->tiles();
|
const QList<ENC::RasterTile> &tiles = job->tiles();
|
||||||
|
|
||||||
@ -180,8 +323,9 @@ void ENCMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
|
|||||||
if (QPixmapCache::find(key(_zoom, ttl), &pm))
|
if (QPixmapCache::find(key(_zoom, ttl), &pm))
|
||||||
painter->drawPixmap(ttl, pm);
|
painter->drawPixmap(ttl, pm);
|
||||||
else
|
else
|
||||||
tiles.append(RasterTile(_projection, _transform, &_data,
|
tiles.append(RasterTile(_projection, _transform, _data,
|
||||||
_zoom, QRect(ttl, QSize(TILE_SIZE, TILE_SIZE)), _tileRatio));
|
_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);
|
QPixmapCache::insert(key(mt.zoom(), mt.xy()), pm);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
runJob(new ENCMapJob(tiles));
|
runJob(new ENCJob(tiles));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,47 +1,16 @@
|
|||||||
#ifndef ENCMAP_H
|
#ifndef ENCMAP_H
|
||||||
#define ENCMAP_H
|
#define ENCMAP_H
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
|
#include "common/range.h"
|
||||||
#include "map.h"
|
#include "map.h"
|
||||||
#include "projection.h"
|
#include "projection.h"
|
||||||
#include "transform.h"
|
#include "transform.h"
|
||||||
#include "ENC/mapdata.h"
|
#include "ENC/mapdata.h"
|
||||||
#include "ENC/rastertile.h"
|
#include "ENC/iso8211.h"
|
||||||
|
|
||||||
class ENCMapJob : public QObject
|
class ENCJob;
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
ENCMapJob(const QList<ENC::RasterTile> &tiles)
|
|
||||||
: _tiles(tiles) {}
|
|
||||||
|
|
||||||
void run()
|
|
||||||
{
|
|
||||||
connect(&_watcher, &QFutureWatcher<void>::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<ENC::RasterTile> &tiles() const {return _tiles;}
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void finished(ENCMapJob *job);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void handleFinished() {emit finished(this);}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QFutureWatcher<void> _watcher;
|
|
||||||
QFuture<void> _future;
|
|
||||||
QList<ENC::RasterTile> _tiles;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ENCMap : public Map
|
class ENCMap : public Map
|
||||||
{
|
{
|
||||||
@ -49,8 +18,9 @@ class ENCMap : public Map
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
ENCMap(const QString &fileName, QObject *parent = 0);
|
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;}
|
QRectF bounds() {return _bounds;}
|
||||||
RectC llBounds(const Projection &) {return _llBounds;}
|
RectC llBounds(const Projection &) {return _llBounds;}
|
||||||
@ -72,32 +42,73 @@ public:
|
|||||||
|
|
||||||
void draw(QPainter *painter, const QRectF &rect, Flags flags);
|
void draw(QPainter *painter, const QRectF &rect, Flags flags);
|
||||||
|
|
||||||
bool isValid() const {return _data.isValid();}
|
bool isValid() const {return _valid;}
|
||||||
QString errorString() const {return _data.errorString();}
|
QString errorString() const {return _errorString;}
|
||||||
|
|
||||||
static Map *create(const QString &path, bool *isMap);
|
static Map *create(const QString &path, bool *isMap);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void jobFinished(ENCMapJob *job);
|
void jobFinished(ENCJob *job);
|
||||||
|
|
||||||
private:
|
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;
|
Transform transform(int zoom) const;
|
||||||
void updateTransform();
|
void updateTransform();
|
||||||
bool isRunning(int zoom, const QPoint &xy) const;
|
bool isRunning(int zoom, const QPoint &xy) const;
|
||||||
void runJob(ENCMapJob *job);
|
void runJob(ENCJob *job);
|
||||||
void removeJob(ENCMapJob *job);
|
void removeJob(ENCJob *job);
|
||||||
void cancelJobs(bool wait);
|
void cancelJobs(bool wait);
|
||||||
QString key(int zoom, const QPoint &xy) const;
|
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<ENC::ISO8211::Record> &gv, Rect &b);
|
||||||
|
static bool processRecord(const ENC::ISO8211::Record &record,
|
||||||
|
QVector<ENC::ISO8211::Record> &rv, uint &COMF, QString &name);
|
||||||
|
|
||||||
|
QString _name;
|
||||||
|
ENC::MapData *_data;
|
||||||
Projection _projection;
|
Projection _projection;
|
||||||
Transform _transform;
|
Transform _transform;
|
||||||
qreal _tileRatio;
|
qreal _tileRatio;
|
||||||
RectC _llBounds;
|
RectC _llBounds;
|
||||||
QRectF _bounds;
|
QRectF _bounds;
|
||||||
|
Range _zooms;
|
||||||
int _zoom;
|
int _zoom;
|
||||||
|
|
||||||
QList<ENCMapJob*> _jobs;
|
QList<ENCJob*> _jobs;
|
||||||
|
|
||||||
bool _valid;
|
bool _valid;
|
||||||
QString _errorString;
|
QString _errorString;
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "gemfmap.h"
|
#include "gemfmap.h"
|
||||||
#include "oruxmap.h"
|
#include "oruxmap.h"
|
||||||
#include "encmap.h"
|
#include "encmap.h"
|
||||||
|
#include "encatlas.h"
|
||||||
#include "invalidmap.h"
|
#include "invalidmap.h"
|
||||||
#include "maplist.h"
|
#include "maplist.h"
|
||||||
|
|
||||||
@ -56,6 +57,7 @@ MapList::ParserMap MapList::parsers()
|
|||||||
map.insert("gemf", &GEMFMap::create);
|
map.insert("gemf", &GEMFMap::create);
|
||||||
map.insert("otrk2.xml", &OruxMap::create);
|
map.insert("otrk2.xml", &OruxMap::create);
|
||||||
map.insert("000", &ENCMap::create);
|
map.insert("000", &ENCMap::create);
|
||||||
|
map.insert("031", &ENCAtlas::create);
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
@ -153,7 +155,8 @@ QString MapList::formats()
|
|||||||
return
|
return
|
||||||
qApp->translate("MapList", "Supported files")
|
qApp->translate("MapList", "Supported files")
|
||||||
+ " (" + filter().join(" ") + ");;"
|
+ " (" + 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", "AlpineQuest maps") + " (*.aqm);;"
|
||||||
+ qApp->translate("MapList", "GEMF maps") + " (*.gemf);;"
|
+ qApp->translate("MapList", "GEMF maps") + " (*.gemf);;"
|
||||||
+ qApp->translate("MapList", "Garmin IMG maps")
|
+ qApp->translate("MapList", "Garmin IMG maps")
|
||||||
|
Loading…
Reference in New Issue
Block a user