mirror of
https://github.com/tumic0/GPXSee.git
synced 2024-11-24 03:35:53 +01:00
Added support for ENC maps
This commit is contained in:
parent
a2e22cd93b
commit
f8d856b7ee
15
gpxsee.pro
15
gpxsee.pro
@ -116,7 +116,13 @@ HEADERS += src/common/config.h \
|
||||
src/GUI/passwordedit.h \
|
||||
src/data/style.h \
|
||||
src/data/twonavparser.h \
|
||||
src/map/ENC/mapdata.h \
|
||||
src/map/ENC/objects.h \
|
||||
src/map/ENC/rastertile.h \
|
||||
src/map/ENC/style.h \
|
||||
src/map/IMG/section.h \
|
||||
src/map/encmap.h \
|
||||
src/map/ENC/iso8211.h \
|
||||
src/map/gemfmap.h \
|
||||
src/map/oruxmap.h \
|
||||
src/map/osmdroidmap.h \
|
||||
@ -131,7 +137,7 @@ HEADERS += src/common/config.h \
|
||||
src/map/proj/krovak.h \
|
||||
src/map/proj/polarstereographic.h \
|
||||
src/map/proj/obliquestereographic.h \
|
||||
src/map/IMG/bitmapline.h \
|
||||
src/map/bitmapline.h \
|
||||
src/map/IMG/bitstream.h \
|
||||
src/map/IMG/deltastream.h \
|
||||
src/map/IMG/gmapdata.h \
|
||||
@ -322,6 +328,11 @@ SOURCES += src/main.cpp \
|
||||
src/GUI/projectioncombobox.cpp \
|
||||
src/GUI/passwordedit.cpp \
|
||||
src/data/twonavparser.cpp \
|
||||
src/map/ENC/mapdata.cpp \
|
||||
src/map/ENC/rastertile.cpp \
|
||||
src/map/ENC/style.cpp \
|
||||
src/map/encmap.cpp \
|
||||
src/map/ENC/iso8211.cpp \
|
||||
src/map/gemfmap.cpp \
|
||||
src/map/oruxmap.cpp \
|
||||
src/map/osmdroidmap.cpp \
|
||||
@ -335,7 +346,7 @@ SOURCES += src/main.cpp \
|
||||
src/map/proj/krovak.cpp \
|
||||
src/map/proj/polarstereographic.cpp \
|
||||
src/map/proj/obliquestereographic.cpp \
|
||||
src/map/IMG/bitmapline.cpp \
|
||||
src/map/bitmapline.cpp \
|
||||
src/map/IMG/bitstream.cpp \
|
||||
src/map/IMG/deltastream.cpp \
|
||||
src/map/IMG/gmapdata.cpp \
|
||||
|
@ -149,6 +149,7 @@
|
||||
<file alias="rock-dangerous.png">icons/IMG/marine/rock-dangerous.png</file>
|
||||
<file alias="wreck.png">icons/IMG/marine/wreck.png</file>
|
||||
<file alias="wreck-exposed.png">icons/IMG/marine/wreck-exposed.png</file>
|
||||
<file alias="wreck-dangerous.png">icons/IMG/marine/wreck-dangerous.png</file>
|
||||
<file alias="obstruction.png">icons/IMG/marine/obstruction.png</file>
|
||||
<file alias="obstruction-covers.png">icons/IMG/marine/obstruction-covers.png</file>
|
||||
<file alias="anchor-line.png">icons/IMG/marine/anchor-line.png</file>
|
||||
|
BIN
icons/IMG/marine/wreck-dangerous.png
Normal file
BIN
icons/IMG/marine/wreck-dangerous.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 277 B |
@ -1,15 +1,13 @@
|
||||
#include <QPen>
|
||||
#include <QPainter>
|
||||
#include <QResizeEvent>
|
||||
#include "common/util.h"
|
||||
#include "stylecombobox.h"
|
||||
|
||||
|
||||
#define MIN_LINE_LENGTH 60
|
||||
#define LINE_WIDTH_RATIO 7
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a))
|
||||
|
||||
|
||||
static Qt::PenStyle styles[] = {Qt::SolidLine, Qt::DashLine, Qt::DotLine,
|
||||
Qt::DashDotLine, Qt::DashDotDotLine};
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <QList>
|
||||
#include <QVector>
|
||||
#include <QDebug>
|
||||
#include "common/rectc.h"
|
||||
|
||||
class Polygon
|
||||
@ -55,8 +56,19 @@ private:
|
||||
return rect;
|
||||
}
|
||||
|
||||
friend QDebug operator<<(QDebug dbg, const Polygon &poly);
|
||||
|
||||
QList<QVector<Coordinates> > _paths;
|
||||
RectC _boundingRect;
|
||||
};
|
||||
|
||||
|
||||
#ifndef QT_NO_DEBUG
|
||||
inline QDebug operator<<(QDebug dbg, const Polygon &poly)
|
||||
{
|
||||
dbg.nospace() << "Polygon(" << poly._paths << ")";
|
||||
return dbg.space();
|
||||
}
|
||||
#endif // QT_NO_DEBUG
|
||||
|
||||
#endif // POLYGON_H
|
||||
|
@ -113,6 +113,16 @@ double Util::niceNum(double x, bool round)
|
||||
return nf * pow(10.0, expv);
|
||||
}
|
||||
|
||||
int Util::log2i(unsigned val)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
while (val >>= 1)
|
||||
ret++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString Util::file2name(const QString &path)
|
||||
{
|
||||
QFileInfo fi(displayName(path));
|
||||
|
@ -5,8 +5,12 @@
|
||||
|
||||
class QTemporaryDir;
|
||||
|
||||
#define ARRAY_SIZE(array) \
|
||||
(sizeof(array) / sizeof(array[0]))
|
||||
|
||||
namespace Util
|
||||
{
|
||||
int log2i(unsigned val);
|
||||
int str2int(const char *str, int len);
|
||||
double niceNum(double x, bool round);
|
||||
QString file2name(const QString &path);
|
||||
|
328
src/map/ENC/iso8211.cpp
Normal file
328
src/map/ENC/iso8211.cpp
Normal file
@ -0,0 +1,328 @@
|
||||
#include <QFile>
|
||||
#include <QRegularExpression>
|
||||
#include "common/util.h"
|
||||
#include "iso8211.h"
|
||||
|
||||
using namespace ENC;
|
||||
|
||||
struct DR {
|
||||
char RecordLength[5];
|
||||
char InterchangeLevel;
|
||||
char LeaderID;
|
||||
char ExtensionFlag;
|
||||
char Version;
|
||||
char ApplicationIndicator;
|
||||
char FieldControlLength[2];
|
||||
char BaseAddress[5];
|
||||
char ExtCharSetIndicator[3];
|
||||
char FieldLengthSize;
|
||||
char FieldPosSize;
|
||||
char Reserved;
|
||||
char FieldTagSize;
|
||||
};
|
||||
|
||||
bool ISO8211::Field::subfield(const char *name, int *val, int idx) const
|
||||
{
|
||||
const QVariant *v;
|
||||
bool ok;
|
||||
|
||||
v = data().field(name, idx);
|
||||
if (!v)
|
||||
return false;
|
||||
*val = v->toInt(&ok);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ISO8211::Field::subfield(const char *name, uint *val, int idx) const
|
||||
{
|
||||
const QVariant *v;
|
||||
bool ok;
|
||||
|
||||
v = data().field(name, idx);
|
||||
if (!v)
|
||||
return false;
|
||||
*val = v->toUInt(&ok);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool ISO8211::Field::subfield(const char *name, QByteArray *val, int idx) const
|
||||
{
|
||||
const QVariant *v;
|
||||
|
||||
v = data().field(name, idx);
|
||||
if (!v)
|
||||
return false;
|
||||
*val = v->toByteArray();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ISO8211::fieldType(const QString &str, int cnt, FieldType &type, int &size)
|
||||
{
|
||||
if (str == "A" || str == "I" || str == "R") {
|
||||
type = String;
|
||||
size = cnt;
|
||||
} else if (str == "B") {
|
||||
type = Array;
|
||||
size = cnt / 8;
|
||||
} else if (str == "b11") {
|
||||
type = U8;
|
||||
size = 1;
|
||||
} else if (str == "b12") {
|
||||
type = U16;
|
||||
size = 2;
|
||||
} else if (str == "b14") {
|
||||
type = U32;
|
||||
size = 4;
|
||||
} else if (str == "b21") {
|
||||
type = S8;
|
||||
size = 1;
|
||||
} else if (str == "b22") {
|
||||
type = S16;
|
||||
size = 2;
|
||||
} else if (str == "b24") {
|
||||
type = S32;
|
||||
size = 4;
|
||||
} else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ISO8211::readDR(QFile &file, QVector<FieldDefinition> &fields) const
|
||||
{
|
||||
DR ddr;
|
||||
QByteArray fieldLen, fieldPos;
|
||||
int len, lenSize, posSize, tagSize, offset;
|
||||
|
||||
if (file.read((char*)&ddr, sizeof(ddr)) != sizeof(ddr))
|
||||
return -1;
|
||||
|
||||
len = Util::str2int(ddr.RecordLength, sizeof(ddr.RecordLength));
|
||||
offset = Util::str2int(ddr.BaseAddress, sizeof(ddr.BaseAddress));
|
||||
lenSize = Util::str2int(&ddr.FieldLengthSize, sizeof(ddr.FieldLengthSize));
|
||||
posSize = Util::str2int(&ddr.FieldPosSize, sizeof(ddr.FieldPosSize));
|
||||
tagSize = Util::str2int(&ddr.FieldTagSize, sizeof(ddr.FieldTagSize));
|
||||
|
||||
if (len < 0 || offset < 0 || lenSize < 0 || posSize < 0 || tagSize < 0)
|
||||
return -1;
|
||||
|
||||
fields.resize((offset - 1 - sizeof(DR)) / (lenSize + posSize + tagSize));
|
||||
fieldLen.resize(lenSize);
|
||||
fieldPos.resize(posSize);
|
||||
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
FieldDefinition &r = fields[i];
|
||||
|
||||
r.tag.resize(tagSize);
|
||||
|
||||
if (file.read(r.tag.data(), tagSize) != tagSize
|
||||
|| file.read(fieldLen.data(), lenSize) != lenSize
|
||||
|| file.read(fieldPos.data(), posSize) != posSize)
|
||||
return -1;
|
||||
|
||||
r.pos = offset + Util::str2int(fieldPos.constData(), posSize);
|
||||
r.size = Util::str2int(fieldLen.constData(), lenSize);
|
||||
|
||||
if (r.pos < 0 || r.size < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
bool ISO8211::readDDA(QFile &file, const FieldDefinition &def, SubFields &fields)
|
||||
{
|
||||
static QRegularExpression re("(\\d*)(\\w+)\\(*(\\d*)\\)*");
|
||||
QByteArray ba;
|
||||
|
||||
ba.resize(def.size);
|
||||
if (!(file.seek(def.pos) && file.read(ba.data(), ba.size()) == ba.size()))
|
||||
return false;
|
||||
|
||||
QList<QByteArray> list(ba.split('\x1f'));
|
||||
if (!list.at(1).isEmpty() && list.at(1).front() == '*') {
|
||||
fields.setRepeat(true);
|
||||
list[1].remove(0, 1);
|
||||
}
|
||||
QList<QByteArray> tags(list.at(1).split('!'));
|
||||
|
||||
if (list.size() > 2) {
|
||||
QRegularExpressionMatchIterator it = re.globalMatch(list.at(2));
|
||||
int tag = 0;
|
||||
|
||||
fields.resize(tags.size());
|
||||
|
||||
while (it.hasNext()) {
|
||||
QRegularExpressionMatch match = it.next();
|
||||
QString cntStr(match.captured(1));
|
||||
QString typeStr(match.captured(2));
|
||||
QString sizeStr(match.captured(3));
|
||||
uint cnt = 1, size = 0;
|
||||
bool ok;
|
||||
|
||||
if (!cntStr.isEmpty()) {
|
||||
cnt = cntStr.toUInt(&ok);
|
||||
if (!ok)
|
||||
return false;
|
||||
}
|
||||
if (!sizeStr.isEmpty()) {
|
||||
size = sizeStr.toUInt(&ok);
|
||||
if (!ok)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < cnt; i++) {
|
||||
SubFieldDefinition &f = fields[tag];
|
||||
f.tag = tags.at(tag);
|
||||
if (!fieldType(typeStr, size, f.type, f.size))
|
||||
return false;
|
||||
tag++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ISO8211::readDDR(QFile &file)
|
||||
{
|
||||
QVector<FieldDefinition> fields;
|
||||
qint64 pos = file.pos();
|
||||
int len = readDR(file, fields);
|
||||
|
||||
if (len < 0) {
|
||||
_errorString = "Not a ENC file";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
SubFields def;
|
||||
if (!readDDA(file, fields.at(i), def)) {
|
||||
_errorString = QString("Error reading %1 DDA field")
|
||||
.arg(QString(fields.at(i).tag));
|
||||
return false;
|
||||
}
|
||||
_map.insert(fields.at(i).tag, def);
|
||||
}
|
||||
|
||||
if (file.pos() != pos + len || fields.size() < 2) {
|
||||
_errorString = "DDR format error";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ISO8211::readUDA(QFile &file, quint64 pos, const FieldDefinition &def,
|
||||
const SubFields &fields, Data &data) const
|
||||
{
|
||||
QByteArray ba;
|
||||
|
||||
ba.resize(def.size);
|
||||
if (!(file.seek(pos + def.pos)
|
||||
&& file.read(ba.data(), ba.size()) == ba.size()))
|
||||
return false;
|
||||
|
||||
const char *sp;
|
||||
const char *dp = ba.constData();
|
||||
const char *ep = ba.constData() + ba.size() - 1;
|
||||
|
||||
data.setFields(&fields);
|
||||
|
||||
do {
|
||||
QVector<QVariant> row;
|
||||
row.resize(fields.size());
|
||||
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
const SubFieldDefinition &f = fields.at(i);
|
||||
|
||||
switch (f.type) {
|
||||
case String:
|
||||
case Array:
|
||||
if (f.size) {
|
||||
row[i] = QVariant(QByteArray(dp, f.size));
|
||||
dp += f.size;
|
||||
} else {
|
||||
sp = dp;
|
||||
while (dp < ep && *dp != '\x1f')
|
||||
dp++;
|
||||
row[i] = QVariant(QByteArray(sp, dp - sp));
|
||||
dp++;
|
||||
}
|
||||
break;
|
||||
case S8:
|
||||
row[i] = QVariant(*((qint8*)dp));
|
||||
dp++;
|
||||
break;
|
||||
case S16:
|
||||
row[i] = QVariant(INT16(dp));
|
||||
dp += 2;
|
||||
break;
|
||||
case S32:
|
||||
row[i] = QVariant(INT32(dp));
|
||||
dp += 4;
|
||||
break;
|
||||
case U8:
|
||||
row[i] = QVariant(*((quint8*)dp));
|
||||
dp++;
|
||||
break;
|
||||
case U16:
|
||||
row[i] = QVariant(UINT16(dp));
|
||||
dp += 2;
|
||||
break;
|
||||
case U32:
|
||||
row[i] = QVariant(UINT32(dp));
|
||||
dp += 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data.append(row);
|
||||
} while (fields.repeat() && dp < ep);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ISO8211::readRecord(QFile &file, Record &record)
|
||||
{
|
||||
QVector<FieldDefinition> fields;
|
||||
qint64 pos = file.pos();
|
||||
int len = readDR(file, fields);
|
||||
|
||||
if (len < 0) {
|
||||
_errorString = "Error reading DR";
|
||||
return false;
|
||||
}
|
||||
|
||||
record.resize(fields.size());
|
||||
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
const FieldDefinition &def = fields.at(i);
|
||||
Field &f = record[i];
|
||||
|
||||
FieldsMap::const_iterator it = _map.find(def.tag);
|
||||
if (it == _map.constEnd()) {
|
||||
_errorString = QString("%1: unknown record")
|
||||
.arg(QString(def.tag));
|
||||
return false;
|
||||
}
|
||||
|
||||
f.setTag(def.tag);
|
||||
|
||||
if (!readUDA(file, pos, def, it.value(), f.rdata())) {
|
||||
_errorString = QString("Error reading %1 record")
|
||||
.arg(QString(def.tag));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (file.pos() != pos + len) {
|
||||
_errorString = "Record format error";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
148
src/map/ENC/iso8211.h
Normal file
148
src/map/ENC/iso8211.h
Normal file
@ -0,0 +1,148 @@
|
||||
#ifndef ENC_ISO8211_H
|
||||
#define ENC_ISO8211_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QVariant>
|
||||
#include <QDebug>
|
||||
|
||||
class QFile;
|
||||
|
||||
#define UINT32(x) \
|
||||
(((quint32)*(const uchar*)(x)) \
|
||||
| ((quint32)(*((const uchar*)(x) + 1)) << 8) \
|
||||
| ((quint32)(*((const uchar*)(x) + 2)) << 16) \
|
||||
| ((quint32)(*((const uchar*)(x) + 3)) << 24))
|
||||
|
||||
#define UINT16(x) \
|
||||
(((quint16)*(const uchar*)(x)) \
|
||||
| ((quint16)(*((const uchar*)(x) + 1)) << 8))
|
||||
|
||||
#define INT32(x) ((qint32)UINT32(x))
|
||||
#define INT16(x) ((qint16)UINT16(x))
|
||||
|
||||
namespace ENC {
|
||||
|
||||
class ISO8211
|
||||
{
|
||||
public:
|
||||
enum FieldType {String, Array, S8, S16, S32, U8, U16, U32};
|
||||
|
||||
struct FieldDefinition {
|
||||
QByteArray tag;
|
||||
int pos;
|
||||
int size;
|
||||
};
|
||||
|
||||
struct SubFieldDefinition {
|
||||
QByteArray tag;
|
||||
FieldType type;
|
||||
int size;
|
||||
};
|
||||
|
||||
class SubFields : public QVector<SubFieldDefinition>
|
||||
{
|
||||
public:
|
||||
SubFields() : QVector<SubFieldDefinition>(), _repeat(false) {}
|
||||
|
||||
bool repeat() const {return _repeat;}
|
||||
void setRepeat(bool repeat) {_repeat = repeat;}
|
||||
|
||||
private:
|
||||
bool _repeat;
|
||||
};
|
||||
|
||||
class Data : public QVector<QVector<QVariant> >
|
||||
{
|
||||
public:
|
||||
Data() : QVector<QVector<QVariant> >(), _fields(0) {}
|
||||
|
||||
void setFields(const SubFields *fields) {_fields = fields;}
|
||||
|
||||
const QVariant *field(const QByteArray &name, int idx = 0) const
|
||||
{
|
||||
const QVector<QVariant> &v = at(idx);
|
||||
|
||||
for (int i = 0; i < _fields->size(); i++)
|
||||
if (_fields->at(i).tag == name)
|
||||
return &v.at(i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const SubFields *_fields;
|
||||
};
|
||||
|
||||
class Field
|
||||
{
|
||||
public:
|
||||
const QByteArray &tag() const {return _tag;}
|
||||
void setTag(const QByteArray &tag) {_tag = tag;}
|
||||
Data &rdata() {return _data;}
|
||||
const Data &data() const {return _data;}
|
||||
|
||||
bool subfield(const char *name, int *val, int idx = 0) const;
|
||||
bool subfield(const char *name, uint *val, int idx = 0) const;
|
||||
bool subfield(const char *name, QByteArray *val, int idx = 0) const;
|
||||
|
||||
private:
|
||||
QByteArray _tag;
|
||||
Data _data;
|
||||
};
|
||||
|
||||
class Record : public QVector<Field>
|
||||
{
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
bool readDDR(QFile &file);
|
||||
bool readRecord(QFile &file, Record &record);
|
||||
|
||||
const QString &errorString() const {return _errorString;}
|
||||
|
||||
private:
|
||||
typedef QMap<QByteArray, SubFields> FieldsMap;
|
||||
|
||||
static bool fieldType(const QString &str, int cnt, FieldType &type,
|
||||
int &size);
|
||||
|
||||
int readDR(QFile &file, QVector<FieldDefinition> &fields) const;
|
||||
bool readDDA(QFile &file, const FieldDefinition &def, SubFields &fields);
|
||||
bool readUDA(QFile &file, quint64 pos, const FieldDefinition &def,
|
||||
const SubFields &fields, Data &data) const;
|
||||
|
||||
FieldsMap _map;
|
||||
QString _errorString;
|
||||
};
|
||||
|
||||
#ifndef QT_NO_DEBUG
|
||||
inline QDebug operator<<(QDebug dbg, const ISO8211::FieldDefinition &def)
|
||||
{
|
||||
dbg.nospace() << "Field(" << def.tag << ", " << def.size << ")";
|
||||
return dbg.space();
|
||||
}
|
||||
|
||||
inline QDebug operator<<(QDebug dbg, const ISO8211::SubFieldDefinition &def)
|
||||
{
|
||||
dbg.nospace() << "SubField(" << def.tag << ", " << def.type << ", "
|
||||
<< def.size << ")";
|
||||
return dbg.space();
|
||||
}
|
||||
|
||||
inline QDebug operator<<(QDebug dbg, const ISO8211::Field &field)
|
||||
{
|
||||
dbg.nospace() << "Field(" << field.tag() /*<< ", " << field.data()*/ << ")";
|
||||
return dbg.space();
|
||||
}
|
||||
#endif // QT_NO_DEBUG
|
||||
|
||||
}
|
||||
|
||||
#endif // ENC_ISO8211_H
|
683
src/map/ENC/mapdata.cpp
Normal file
683
src/map/ENC/mapdata.cpp
Normal file
@ -0,0 +1,683 @@
|
||||
#include <QFile>
|
||||
#include "common/util.h"
|
||||
#include "objects.h"
|
||||
#include "mapdata.h"
|
||||
|
||||
using namespace ENC;
|
||||
|
||||
#define RCNM_VI 110
|
||||
#define RCNM_VC 120
|
||||
#define RCNM_VE 130
|
||||
#define RCNM_VF 140
|
||||
|
||||
#define PRIM_P 1
|
||||
#define PRIM_L 2
|
||||
#define PRIM_A 3
|
||||
|
||||
static void warning(const ISO8211::Field &FRID, uint PRIM)
|
||||
{
|
||||
uint RCID = 0xFFFFFFFF;
|
||||
FRID.subfield("RCID", &RCID);
|
||||
|
||||
switch (PRIM) {
|
||||
case PRIM_P:
|
||||
qWarning("%u: invalid point feature", RCID);
|
||||
break;
|
||||
case PRIM_L:
|
||||
qWarning("%u: invalid line feature", RCID);
|
||||
break;
|
||||
case PRIM_A:
|
||||
qWarning("%u: invalid area feature", RCID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void rectcBounds(const RectC &rect, double min[2], double max[2])
|
||||
{
|
||||
min[0] = rect.left();
|
||||
min[1] = rect.bottom();
|
||||
max[0] = rect.right();
|
||||
max[1] = rect.top();
|
||||
}
|
||||
|
||||
static bool parseNAME(const ISO8211::Field *f, quint8 *type, quint32 *id,
|
||||
int idx = 0)
|
||||
{
|
||||
QByteArray ba(f->data().at(idx).at(0).toByteArray());
|
||||
if (ba.size() != 5)
|
||||
return false;
|
||||
|
||||
*type = (quint8)(*ba.constData());
|
||||
*id = UINT32(ba.constData() + 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const ISO8211::Field *SGXD(const ISO8211::Record &r)
|
||||
{
|
||||
const ISO8211::Field *f;
|
||||
|
||||
if ((f = r.field("SG2D")))
|
||||
return f;
|
||||
else if ((f = r.field("SG3D")))
|
||||
return f;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool pointCb(MapData::Point *point, void *context)
|
||||
{
|
||||
QList<MapData::Point*> *points = (QList<MapData::Point*>*)context;
|
||||
points->append(point);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool lineCb(MapData::Line *line, void *context)
|
||||
{
|
||||
QList<MapData::Line*> *lines = (QList<MapData::Line*>*)context;
|
||||
lines->append(line);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool polygonCb(MapData::Poly *polygon, void *context)
|
||||
{
|
||||
QList<MapData::Poly*> *polygons = (QList<MapData::Poly*>*)context;
|
||||
polygons->append(polygon);
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint depthLevel(const QVariant &DRVAL1)
|
||||
{
|
||||
QString str(DRVAL1.toString());
|
||||
double minDepth = str.isEmpty() ? -1 : str.toDouble();
|
||||
|
||||
if (minDepth < 0)
|
||||
return 0;
|
||||
else if (minDepth < 2)
|
||||
return 1;
|
||||
else if (minDepth < 5)
|
||||
return 2;
|
||||
else if (minDepth < 10)
|
||||
return 3;
|
||||
else if (minDepth < 20)
|
||||
return 4;
|
||||
else if (minDepth < 50)
|
||||
return 5;
|
||||
else
|
||||
return 6;
|
||||
}
|
||||
|
||||
RectC MapData::Line::bounds() const
|
||||
{
|
||||
RectC b;
|
||||
|
||||
for (int i = 0; i < _path.size(); i++)
|
||||
b = b.united(_path.at(i));
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
Coordinates MapData::coordinates(int x, int y) const
|
||||
{
|
||||
return Coordinates(x / (double)_COMF, y / (double)_COMF);
|
||||
}
|
||||
|
||||
Coordinates MapData::point(const ISO8211::Record &r)
|
||||
{
|
||||
const ISO8211::Field *f = SGXD(r);
|
||||
if (!f)
|
||||
return Coordinates();
|
||||
|
||||
int y = f->data().at(0).at(0).toInt();
|
||||
int x = f->data().at(0).at(1).toInt();
|
||||
|
||||
return coordinates(x, y);
|
||||
}
|
||||
|
||||
QVector<MapData::Sounding> MapData::soundings(const ISO8211::Record &r)
|
||||
{
|
||||
QVector<Sounding> s;
|
||||
const ISO8211::Field *f = r.field("SG3D");
|
||||
if (!f)
|
||||
return QVector<Sounding>();
|
||||
|
||||
s.reserve(f->data().size());
|
||||
for (int i = 0; i < f->data().size(); i++) {
|
||||
int y = f->data().at(i).at(0).toInt();
|
||||
int x = f->data().at(i).at(1).toInt();
|
||||
int z = f->data().at(i).at(2).toInt();
|
||||
s.append(Sounding(coordinates(x, y), z / (double)_SOMF));
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
QVector<MapData::Sounding> MapData::soundingGeometry(const ISO8211::Record &r)
|
||||
{
|
||||
quint8 type;
|
||||
quint32 id;
|
||||
RecordMapIterator it;
|
||||
|
||||
const ISO8211::Field *FSPT = r.field("FSPT");
|
||||
if (!FSPT || FSPT->data().at(0).size() != 4)
|
||||
return QVector<Sounding>();
|
||||
|
||||
if (!parseNAME(FSPT, &type, &id))
|
||||
return QVector<Sounding>();
|
||||
|
||||
if (type == RCNM_VI)
|
||||
it = _vi.find(id);
|
||||
else if (type == RCNM_VC)
|
||||
it = _vc.find(id);
|
||||
else
|
||||
return QVector<Sounding>();
|
||||
if (it == _ve.constEnd())
|
||||
return QVector<Sounding>();
|
||||
|
||||
const ISO8211::Record &FRID = it.value();
|
||||
|
||||
return soundings(FRID);
|
||||
}
|
||||
|
||||
Coordinates MapData::pointGeometry(const ISO8211::Record &r)
|
||||
{
|
||||
quint8 type;
|
||||
quint32 id;
|
||||
RecordMapIterator it;
|
||||
|
||||
const ISO8211::Field *FSPT = r.field("FSPT");
|
||||
if (!FSPT || FSPT->data().at(0).size() != 4)
|
||||
return Coordinates();
|
||||
|
||||
if (!parseNAME(FSPT, &type, &id))
|
||||
return Coordinates();
|
||||
|
||||
if (type == RCNM_VI)
|
||||
it = _vi.find(id);
|
||||
else if (type == RCNM_VC)
|
||||
it = _vc.find(id);
|
||||
else
|
||||
return Coordinates();
|
||||
if (it == _ve.constEnd())
|
||||
return Coordinates();
|
||||
|
||||
const ISO8211::Record &FRID = it.value();
|
||||
|
||||
return point(FRID);
|
||||
}
|
||||
|
||||
QVector<Coordinates> MapData::lineGeometry(const ISO8211::Record &r)
|
||||
{
|
||||
QVector<Coordinates> path;
|
||||
Coordinates c[2];
|
||||
uint ORNT, MASK;
|
||||
quint8 type;
|
||||
quint32 id;
|
||||
|
||||
const ISO8211::Field *FSPT = r.field("FSPT");
|
||||
if (!FSPT || FSPT->data().at(0).size() != 4)
|
||||
return QVector<Coordinates>();
|
||||
|
||||
for (int i = 0; i < FSPT->data().size(); i++) {
|
||||
if (!parseNAME(FSPT, &type, &id, i) || type != RCNM_VE)
|
||||
return QVector<Coordinates>();
|
||||
ORNT = FSPT->data().at(i).at(1).toUInt();
|
||||
MASK = FSPT->data().at(i).at(3).toUInt();
|
||||
Q_ASSERT(MASK != 1);
|
||||
|
||||
RecordMapIterator it = _ve.find(id);
|
||||
if (it == _ve.constEnd())
|
||||
return QVector<Coordinates>();
|
||||
const ISO8211::Record &FRID = it.value();
|
||||
const ISO8211::Field *VRPT = FRID.field("VRPT");
|
||||
if (!VRPT || VRPT->data().size() != 2)
|
||||
return QVector<Coordinates>();
|
||||
|
||||
for (int j = 0; j < 2; j++) {
|
||||
if (!parseNAME(VRPT, &type, &id, j) || type != RCNM_VC)
|
||||
return QVector<Coordinates>();
|
||||
|
||||
RecordMapIterator jt = _vc.find(id);
|
||||
if (jt == _vc.constEnd())
|
||||
return QVector<Coordinates>();
|
||||
c[j] = point(jt.value());
|
||||
if (c[j].isNull())
|
||||
return QVector<Coordinates>();
|
||||
}
|
||||
|
||||
const ISO8211::Field *vertexes = SGXD(FRID);
|
||||
if (ORNT == 2) {
|
||||
path.append(c[1]);
|
||||
if (vertexes) {
|
||||
for (int j = vertexes->data().size() - 1; j >= 0; j--) {
|
||||
const QVector<QVariant> &cv = vertexes->data().at(j);
|
||||
path.append(coordinates(cv.at(1).toInt(), cv.at(0).toInt()));
|
||||
}
|
||||
}
|
||||
path.append(c[0]);
|
||||
} else {
|
||||
path.append(c[0]);
|
||||
if (vertexes) {
|
||||
for (int j = 0; j < vertexes->data().size(); j++) {
|
||||
const QVector<QVariant> &cv = vertexes->data().at(j);
|
||||
path.append(coordinates(cv.at(1).toInt(), cv.at(0).toInt()));
|
||||
}
|
||||
}
|
||||
path.append(c[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
Polygon MapData::polyGeometry(const ISO8211::Record &r)
|
||||
{
|
||||
Polygon path;
|
||||
QVector<Coordinates> v;
|
||||
Coordinates c[2];
|
||||
uint ORNT, USAG, MASK;
|
||||
quint8 type;
|
||||
quint32 id;
|
||||
|
||||
const ISO8211::Field *FSPT = r.field("FSPT");
|
||||
if (!FSPT || FSPT->data().at(0).size() != 4)
|
||||
return Polygon();
|
||||
|
||||
for (int i = 0; i < FSPT->data().size(); i++) {
|
||||
if (!parseNAME(FSPT, &type, &id, i) || type != RCNM_VE)
|
||||
return Polygon();
|
||||
ORNT = FSPT->data().at(i).at(1).toUInt();
|
||||
USAG = FSPT->data().at(i).at(2).toUInt();
|
||||
MASK = FSPT->data().at(i).at(3).toUInt();
|
||||
Q_ASSERT(MASK != 1);
|
||||
|
||||
if (USAG == 2 && path.isEmpty()) {
|
||||
path.append(v);
|
||||
v.clear();
|
||||
}
|
||||
|
||||
RecordMapIterator it = _ve.find(id);
|
||||
if (it == _ve.constEnd())
|
||||
return Polygon();
|
||||
const ISO8211::Record &FRID = it.value();
|
||||
const ISO8211::Field *VRPT = FRID.field("VRPT");
|
||||
if (!VRPT || VRPT->data().size() != 2)
|
||||
return Polygon();
|
||||
|
||||
for (int j = 0; j < 2; j++) {
|
||||
if (!parseNAME(VRPT, &type, &id, j) || type != RCNM_VC)
|
||||
return Polygon();
|
||||
|
||||
RecordMapIterator jt = _vc.find(id);
|
||||
if (jt == _vc.constEnd())
|
||||
return Polygon();
|
||||
c[j] = point(jt.value());
|
||||
if (c[j].isNull())
|
||||
return Polygon();
|
||||
}
|
||||
|
||||
const ISO8211::Field *vertexes = SGXD(FRID);
|
||||
if (ORNT == 2) {
|
||||
v.append(c[1]);
|
||||
if (vertexes) {
|
||||
for (int j = vertexes->data().size() - 1; j >= 0; j--) {
|
||||
const QVector<QVariant> &cv = vertexes->data().at(j);
|
||||
v.append(coordinates(cv.at(1).toInt(), cv.at(0).toInt()));
|
||||
}
|
||||
}
|
||||
v.append(c[0]);
|
||||
} else {
|
||||
v.append(c[0]);
|
||||
if (vertexes) {
|
||||
for (int j = 0; j < vertexes->data().size(); j++) {
|
||||
const QVector<QVariant> &cv = vertexes->data().at(j);
|
||||
v.append(coordinates(cv.at(1).toInt(), cv.at(0).toInt()));
|
||||
}
|
||||
}
|
||||
v.append(c[1]);
|
||||
}
|
||||
|
||||
if (USAG == 2 && v.first() == v.last()) {
|
||||
path.append(v);
|
||||
v.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (!v.isEmpty())
|
||||
path.append(v);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
MapData::Attr MapData::pointAttr(const ISO8211::Record &r, uint OBJL)
|
||||
{
|
||||
QString label;
|
||||
uint subtype = 0;
|
||||
|
||||
const ISO8211::Field *ATTF = r.field("ATTF");
|
||||
if (!(ATTF && ATTF->data().at(0).size() == 2))
|
||||
return Attr();
|
||||
|
||||
for (int i = 0; i < ATTF->data().size(); i++) {
|
||||
const QVector<QVariant> &av = ATTF->data().at(i);
|
||||
uint key = av.at(0).toUInt();
|
||||
|
||||
if (key == 116)
|
||||
label = av.at(1).toString();
|
||||
if ((OBJL == HRBFAC && key == 30) || (OBJL == LNDMRK && key == 35)
|
||||
|| (OBJL == WRECKS && key == 71))
|
||||
subtype = av.at(1).toString().toUInt();
|
||||
}
|
||||
|
||||
return Attr(subtype, label);
|
||||
}
|
||||
|
||||
MapData::Attr MapData::lineAttr(const ISO8211::Record &r, uint OBJL)
|
||||
{
|
||||
QString label;
|
||||
uint subtype = 0;
|
||||
|
||||
const ISO8211::Field *ATTF = r.field("ATTF");
|
||||
if (!(ATTF && ATTF->data().at(0).size() == 2))
|
||||
return Attr();
|
||||
|
||||
for (int i = 0; i < ATTF->data().size(); i++) {
|
||||
const QVector<QVariant> &av = ATTF->data().at(i);
|
||||
uint key = av.at(0).toUInt();
|
||||
|
||||
if (key == 116)
|
||||
label = av.at(1).toString();
|
||||
if ((OBJL == DEPCNT && key == 174))
|
||||
label = av.at(1).toString();
|
||||
}
|
||||
|
||||
return Attr(subtype, label);
|
||||
}
|
||||
|
||||
MapData::Attr MapData::polyAttr(const ISO8211::Record &r, uint OBJL)
|
||||
{
|
||||
if (OBJL != DEPARE)
|
||||
return Attr();
|
||||
|
||||
const ISO8211::Field *ATTF = r.field("ATTF");
|
||||
if (!(ATTF && ATTF->data().at(0).size() == 2))
|
||||
return Attr();
|
||||
|
||||
for (int i = 0; i < ATTF->data().size(); i++) {
|
||||
const QVector<QVariant> &av = ATTF->data().at(i);
|
||||
if (av.at(0).toUInt() == 87)
|
||||
return Attr(depthLevel(av.at(1)));
|
||||
}
|
||||
|
||||
return Attr();
|
||||
}
|
||||
|
||||
MapData::Point *MapData::pointObject(const Sounding &s)
|
||||
{
|
||||
return new Point(SOUNDG<<16, s.c, QString::number(s.depth));
|
||||
}
|
||||
|
||||
MapData::Point *MapData::pointObject(const ISO8211::Record &r, uint OBJL)
|
||||
{
|
||||
Coordinates c(pointGeometry(r));
|
||||
Attr attr(pointAttr(r, OBJL));
|
||||
|
||||
return (c.isNull() ? 0 : new Point(OBJL<<16|attr.subtype(), c,
|
||||
attr.label()));
|
||||
}
|
||||
|
||||
MapData::Line *MapData::lineObject(const ISO8211::Record &r, uint OBJL)
|
||||
{
|
||||
QVector<Coordinates> path(lineGeometry(r));
|
||||
Attr attr(lineAttr(r, OBJL));
|
||||
|
||||
return (path.isEmpty() ? 0 : new Line(OBJL<<16|attr.subtype(), path,
|
||||
attr.label()));
|
||||
}
|
||||
|
||||
MapData::Poly *MapData::polyObject(const ISO8211::Record &r, uint OBJL)
|
||||
{
|
||||
Polygon path(polyGeometry(r));
|
||||
Attr attr(polyAttr(r, OBJL));
|
||||
|
||||
return (path.isEmpty() ? 0 : new Poly(OBJL<<16|attr.subtype(), path));
|
||||
}
|
||||
|
||||
bool MapData::processRecord(const ISO8211::Record &record)
|
||||
{
|
||||
const ISO8211::Field &f = record.at(1);
|
||||
const QByteArray &ba = f.tag();
|
||||
|
||||
if (ba == "VRID") {
|
||||
int RCNM;
|
||||
uint RCID;
|
||||
|
||||
if (!(f.subfield("RCNM", &RCNM) && f.subfield("RCID", &RCID)))
|
||||
return false;
|
||||
|
||||
switch (RCNM) {
|
||||
case RCNM_VI:
|
||||
_vi.insert(RCID, record);
|
||||
break;
|
||||
case RCNM_VC:
|
||||
_vc.insert(RCID, record);
|
||||
break;
|
||||
case RCNM_VE:
|
||||
_ve.insert(RCID, record);
|
||||
break;
|
||||
case RCNM_VF:
|
||||
_vf.insert(RCID, record);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if (ba == "FRID") {
|
||||
_fe.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) && f.subfield("SOMF", &_SOMF)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MapData::bounds(const ISO8211::Record &record, Rect &rect)
|
||||
{
|
||||
bool xok, yok;
|
||||
const ISO8211::Field *f = SGXD(record);
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
MapData::Rect MapData::bounds(const RecordMap &map)
|
||||
{
|
||||
Rect b, r;
|
||||
|
||||
for (RecordMapIterator it = map.constBegin(); it != map.constEnd(); ++it)
|
||||
if (bounds(it.value(), r))
|
||||
b |= r;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
RectC MapData::bounds() const
|
||||
{
|
||||
const RecordMap *maps[] = {&_vi, &_vc, &_ve, &_vf};
|
||||
Rect b;
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(maps); i++)
|
||||
b |= bounds(*maps[i]);
|
||||
|
||||
return RectC(
|
||||
Coordinates(b.minX() / (double)_COMF, b.maxY() / (double)_COMF),
|
||||
Coordinates(b.maxX() / (double)_COMF, b.minY() / (double)_COMF));
|
||||
}
|
||||
|
||||
MapData::MapData(const QString &path): _valid(false)
|
||||
{
|
||||
QFile file(path);
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
_errorString = file.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_ddf.readDDR(file)) {
|
||||
_errorString = _ddf.errorString();
|
||||
return;
|
||||
}
|
||||
while (!file.atEnd()) {
|
||||
ISO8211::Record record;
|
||||
if (!_ddf.readRecord(file, record)) {
|
||||
_errorString = _ddf.errorString();
|
||||
return;
|
||||
}
|
||||
if (!processRecord(record))
|
||||
return;
|
||||
}
|
||||
|
||||
_valid = true;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
uint PRIM, OBJL;
|
||||
Poly *poly;
|
||||
Line *line;
|
||||
Point *point;
|
||||
double min[2], max[2];
|
||||
|
||||
for (int i = 0; i < _fe.size(); i++) {
|
||||
const ISO8211::Record &r = _fe.at(i);
|
||||
const ISO8211::Field &f = r.at(1);
|
||||
|
||||
if (f.data().at(0).size() != 7)
|
||||
continue;
|
||||
PRIM = f.data().at(0).at(2).toUInt();
|
||||
OBJL = f.data().at(0).at(4).toUInt();
|
||||
|
||||
switch (PRIM) {
|
||||
case PRIM_P:
|
||||
if (OBJL == SOUNDG) {
|
||||
QVector<Sounding> s(soundingGeometry(r));
|
||||
for (int i = 0; i < s.size(); i++) {
|
||||
point = pointObject(s.at(i));
|
||||
min[0] = point->pos().lon();
|
||||
min[1] = point->pos().lat();
|
||||
max[0] = point->pos().lon();
|
||||
max[1] = point->pos().lat();
|
||||
_points.Insert(min, max, point);
|
||||
}
|
||||
} else {
|
||||
if ((point = pointObject(r, OBJL))) {
|
||||
min[0] = point->pos().lon();
|
||||
min[1] = point->pos().lat();
|
||||
max[0] = point->pos().lon();
|
||||
max[1] = point->pos().lat();
|
||||
_points.Insert(min, max, point);
|
||||
} else
|
||||
warning(f, PRIM);
|
||||
}
|
||||
break;
|
||||
case PRIM_L:
|
||||
if ((line = lineObject(r, OBJL))) {
|
||||
rectcBounds(line->bounds(), min, max);
|
||||
_lines.Insert(min, max, line);
|
||||
} else
|
||||
warning(f, PRIM);
|
||||
break;
|
||||
case PRIM_A:
|
||||
if ((poly = polyObject(r, OBJL))) {
|
||||
rectcBounds(poly->bounds(), min, max);
|
||||
_areas.Insert(min, max, poly);
|
||||
} else
|
||||
warning(f, PRIM);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MapData::clear()
|
||||
{
|
||||
LineTree::Iterator lit;
|
||||
for (_lines.GetFirst(lit); !_lines.IsNull(lit); _lines.GetNext(lit))
|
||||
delete _lines.GetAt(lit);
|
||||
_lines.RemoveAll();
|
||||
|
||||
PolygonTree::Iterator ait;
|
||||
for (_areas.GetFirst(ait); !_areas.IsNull(ait); _areas.GetNext(ait))
|
||||
delete _areas.GetAt(ait);
|
||||
_areas.RemoveAll();
|
||||
|
||||
PointTree::Iterator pit;
|
||||
for (_points.GetFirst(pit); !_points.IsNull(pit); _points.GetNext(pit))
|
||||
delete _points.GetAt(pit);
|
||||
_points.RemoveAll();
|
||||
}
|
||||
|
||||
void MapData::points(const RectC &rect, QList<Point*> *points)
|
||||
{
|
||||
double min[2], max[2];
|
||||
|
||||
min[0] = rect.left();
|
||||
min[1] = rect.bottom();
|
||||
max[0] = rect.right();
|
||||
max[1] = rect.top();
|
||||
|
||||
_points.Search(min, max, pointCb, points);
|
||||
}
|
||||
|
||||
void MapData::lines(const RectC &rect, QList<Line*> *lines)
|
||||
{
|
||||
double min[2], max[2];
|
||||
|
||||
min[0] = rect.left();
|
||||
min[1] = rect.bottom();
|
||||
max[0] = rect.right();
|
||||
max[1] = rect.top();
|
||||
|
||||
_lines.Search(min, max, lineCb, lines);
|
||||
}
|
||||
|
||||
void MapData::polygons(const RectC &rect, QList<Poly*> *polygons)
|
||||
{
|
||||
double min[2], max[2];
|
||||
|
||||
min[0] = rect.left();
|
||||
min[1] = rect.bottom();
|
||||
max[0] = rect.right();
|
||||
max[1] = rect.top();
|
||||
|
||||
_areas.Search(min, max, polygonCb, polygons);
|
||||
}
|
182
src/map/ENC/mapdata.h
Normal file
182
src/map/ENC/mapdata.h
Normal file
@ -0,0 +1,182 @@
|
||||
#ifndef ENC_MAPDATA_H
|
||||
#define ENC_MAPDATA_H
|
||||
|
||||
#include <climits>
|
||||
#include <QPainterPath>
|
||||
#include "common/rectc.h"
|
||||
#include "common/rtree.h"
|
||||
#include "common/polygon.h"
|
||||
#include "iso8211.h"
|
||||
|
||||
namespace ENC {
|
||||
|
||||
class MapData
|
||||
{
|
||||
public:
|
||||
class Poly {
|
||||
public:
|
||||
Poly(uint type, const Polygon &path)
|
||||
: _type(type), _path(path) {}
|
||||
|
||||
RectC bounds() const {return _path.boundingRect();}
|
||||
const Polygon &path() const {return _path;}
|
||||
uint type() const {return _type;}
|
||||
|
||||
private:
|
||||
uint _type;
|
||||
Polygon _path;
|
||||
};
|
||||
|
||||
class Line {
|
||||
public:
|
||||
Line(uint type, const QVector<Coordinates> &path, const QString &label)
|
||||
: _type(type), _path(path), _label(label) {}
|
||||
|
||||
RectC bounds() const;
|
||||
const QVector<Coordinates> &path() const {return _path;}
|
||||
uint type() const {return _type;}
|
||||
const QString &label() const {return _label;}
|
||||
|
||||
private:
|
||||
uint _type;
|
||||
QVector<Coordinates> _path;
|
||||
QString _label;
|
||||
};
|
||||
|
||||
class Point {
|
||||
public:
|
||||
Point(uint type, const Coordinates &c, const QString &label)
|
||||
: _type(type), _pos(c), _label(label)
|
||||
{
|
||||
uint hash = (uint)qHash(QPair<double,double>(c.lon(), c.lat()));
|
||||
_id = ((quint64)type)<<32 | hash;
|
||||
}
|
||||
|
||||
const Coordinates &pos() const {return _pos;}
|
||||
uint type() const {return _type;}
|
||||
const QString &label() const {return _label;}
|
||||
|
||||
bool operator<(const Point &other) const
|
||||
{return _id < other._id;}
|
||||
|
||||
private:
|
||||
uint _type;
|
||||
Coordinates _pos;
|
||||
QString _label;
|
||||
quint64 _id;
|
||||
};
|
||||
|
||||
MapData(const QString &path);
|
||||
~MapData();
|
||||
|
||||
const QString &name() const {return _name;}
|
||||
RectC bounds() const;
|
||||
|
||||
void polygons(const RectC &rect, QList<Poly*> *polygons);
|
||||
void lines(const RectC &rect, QList<Line*> *lines);
|
||||
void points(const RectC &rect, QList<Point*> *points);
|
||||
|
||||
void load();
|
||||
void clear();
|
||||
|
||||
bool isValid() const {return _valid;}
|
||||
QString errorString() const {return _errorString;}
|
||||
|
||||
private:
|
||||
class Rect {
|
||||
public:
|
||||
Rect()
|
||||
: _minX(INT_MAX), _maxX(INT_MIN), _minY(INT_MAX), _maxY(INT_MIN) {}
|
||||
Rect(int minX, int maxX, int minY, int maxY)
|
||||
: _minX(minX), _maxX(maxX), _minY(minY), _maxY(maxY) {}
|
||||
|
||||
int minX() const {return _minX;}
|
||||
int maxX() const {return _maxX;}
|
||||
int minY() const {return _minY;}
|
||||
int maxY() const {return _maxY;}
|
||||
|
||||
void unite(int x, int y) {
|
||||
if (x < _minX)
|
||||
_minX = x;
|
||||
if (x > _maxX)
|
||||
_maxX = x;
|
||||
if (y < _minY)
|
||||
_minY = y;
|
||||
if (y > _maxY)
|
||||
_maxY = y;
|
||||
}
|
||||
|
||||
Rect &operator|=(const Rect &r) {*this = *this | r; return *this;}
|
||||
Rect operator|(const Rect &r) const
|
||||
{
|
||||
return Rect(qMin(_minX, r._minX), qMax(_maxX, r._maxX),
|
||||
qMin(_minY, r._minY), qMax(_maxY, r._maxY));
|
||||
}
|
||||
|
||||
private:
|
||||
int _minX, _maxX, _minY, _maxY;
|
||||
};
|
||||
|
||||
class Attr {
|
||||
public:
|
||||
Attr() : _subtype(0) {}
|
||||
Attr(uint subtype, const QString &label = QString())
|
||||
: _subtype(subtype), _label(label) {}
|
||||
|
||||
unsigned subtype() const {return _subtype;}
|
||||
const QString &label() const {return _label;}
|
||||
|
||||
private:
|
||||
unsigned _subtype;
|
||||
QString _label;
|
||||
};
|
||||
|
||||
struct Sounding {
|
||||
Sounding(const Coordinates &c, double depth) : c(c), depth(depth) {}
|
||||
|
||||
Coordinates c;
|
||||
double depth;
|
||||
};
|
||||
|
||||
typedef QMap<uint, ISO8211::Record> RecordMap;
|
||||
typedef QMap<uint, ISO8211::Record>::const_iterator RecordMapIterator;
|
||||
typedef RTree<Poly*, double, 2> PolygonTree;
|
||||
typedef RTree<Line*, double, 2> LineTree;
|
||||
typedef RTree<Point*, double, 2> PointTree;
|
||||
|
||||
bool processRecord(const ISO8211::Record &record);
|
||||
Attr pointAttr(const ISO8211::Record &r, uint OBJL);
|
||||
Attr lineAttr(const ISO8211::Record &r, uint OBJL);
|
||||
Attr polyAttr(const ISO8211::Record &r, uint OBJL);
|
||||
QVector<Sounding> soundingGeometry(const ISO8211::Record &r);
|
||||
Coordinates pointGeometry(const ISO8211::Record &r);
|
||||
QVector<Coordinates> lineGeometry(const ISO8211::Record &r);
|
||||
Polygon polyGeometry(const ISO8211::Record &r);
|
||||
Point *pointObject(const Sounding &s);
|
||||
Point *pointObject(const ISO8211::Record &r, uint OBJL);
|
||||
Line *lineObject(const ISO8211::Record &r, uint OBJL);
|
||||
Poly *polyObject(const ISO8211::Record &r, uint OBJL);
|
||||
Coordinates coordinates(int x, int y) const;
|
||||
Coordinates point(const ISO8211::Record &r);
|
||||
QVector<Sounding> soundings(const ISO8211::Record &r);
|
||||
|
||||
static bool bounds(const ISO8211::Record &record, Rect &rect);
|
||||
static Rect bounds(const RecordMap &map);
|
||||
|
||||
QString _name;
|
||||
RecordMap _vi, _vc, _ve, _vf;
|
||||
QVector<ISO8211::Record> _fe;
|
||||
uint _COMF, _SOMF;
|
||||
ISO8211 _ddf;
|
||||
|
||||
PolygonTree _areas;
|
||||
LineTree _lines;
|
||||
PointTree _points;
|
||||
|
||||
bool _valid;
|
||||
QString _errorString;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ENC_MAPDATA_H
|
45
src/map/ENC/objects.h
Normal file
45
src/map/ENC/objects.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef ENC_OBJECTS_H
|
||||
#define ENC_OBJECTS_H
|
||||
|
||||
#define ACHARE 4
|
||||
#define BCNCAR 5
|
||||
#define BCNISD 6
|
||||
#define BCNLAT 7
|
||||
#define BCNSAW 8
|
||||
#define BCNSPP 9
|
||||
#define BRIDGE 11
|
||||
#define BUISGL 12
|
||||
#define BUAARE 13
|
||||
#define BOYCAR 14
|
||||
#define BOYINB 15
|
||||
#define BOYISD 16
|
||||
#define BOYLAT 17
|
||||
#define BOYSAW 18
|
||||
#define BOYSPP 19
|
||||
#define CBLOHD 21
|
||||
#define CBLSUB 22
|
||||
#define COALNE 30
|
||||
#define DEPARE 42
|
||||
#define DEPCNT 43
|
||||
#define DMPGRD 48
|
||||
#define DYKCON 49
|
||||
#define FAIRWY 51
|
||||
#define HRBFAC 64
|
||||
#define LAKARE 69
|
||||
#define LNDARE 71
|
||||
#define LNDELV 72
|
||||
#define LNDMRK 74
|
||||
#define LIGHTS 75
|
||||
#define NAVLNE 85
|
||||
#define OBSTRN 86
|
||||
#define PILPNT 90
|
||||
#define PIPSOL 94
|
||||
#define PONTON 95
|
||||
#define RIVERS 114
|
||||
#define SLCONS 122
|
||||
#define SOUNDG 129
|
||||
#define UWTROC 153
|
||||
#define WRECKS 159
|
||||
#define M_COVR 302
|
||||
|
||||
#endif // ENC_OBJECTS_H
|
210
src/map/ENC/rastertile.cpp
Normal file
210
src/map/ENC/rastertile.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
#include <QPainter>
|
||||
#include "map/bitmapline.h"
|
||||
#include "map/textpointitem.h"
|
||||
#include "map/textpathitem.h"
|
||||
#include "style.h"
|
||||
#include "rastertile.h"
|
||||
|
||||
using namespace ENC;
|
||||
|
||||
#define ICON_PADDING 2
|
||||
|
||||
static const QColor haloColor(Qt::white);
|
||||
|
||||
static struct {
|
||||
bool operator()(MapData::Point* a, MapData::Point* b) const
|
||||
{return *a < *b;}
|
||||
} pointLess;
|
||||
|
||||
static QFont pixelSizeFont(int pixelSize)
|
||||
{
|
||||
QFont f;
|
||||
f.setPixelSize(pixelSize);
|
||||
return f;
|
||||
}
|
||||
|
||||
static QFont *font(Style::FontSize size)
|
||||
{
|
||||
/* The fonts must be initialized on first usage (after the QGuiApplication
|
||||
instance is created) */
|
||||
static QFont large = pixelSizeFont(16);
|
||||
static QFont normal = pixelSizeFont(12);
|
||||
static QFont small = pixelSizeFont(10);
|
||||
|
||||
switch (size) {
|
||||
case Style::None:
|
||||
return 0;
|
||||
case Style::Large:
|
||||
return &large;
|
||||
case Style::Small:
|
||||
return &small;
|
||||
default:
|
||||
return &normal;
|
||||
}
|
||||
}
|
||||
|
||||
static const Style& style()
|
||||
{
|
||||
static Style s;
|
||||
return s;
|
||||
}
|
||||
|
||||
QPainterPath RasterTile::painterPath(const Polygon &polygon) const
|
||||
{
|
||||
QPainterPath path;
|
||||
|
||||
for (int i = 0; i < polygon.size(); i++) {
|
||||
const QVector<Coordinates> &subpath = polygon.at(i);
|
||||
|
||||
path.moveTo(ll2xy(subpath.first()));
|
||||
for (int j = 1; j < subpath.size(); j++)
|
||||
path.lineTo(ll2xy(subpath.at(j)));
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
QPolygonF RasterTile::polyline(const QVector<Coordinates> &path) const
|
||||
{
|
||||
QPolygonF polygon;
|
||||
polygon.reserve(path.size());
|
||||
|
||||
for (int i = 0; i < path.size(); i++)
|
||||
polygon.append(ll2xy(path.at(i)));
|
||||
|
||||
return polygon;
|
||||
}
|
||||
|
||||
void RasterTile::drawPolygons(QPainter *painter)
|
||||
{
|
||||
const Style &s = style();
|
||||
|
||||
for (int n = 0; n < s.drawOrder().size(); n++) {
|
||||
for (int i = 0; i < _polygons.size(); i++) {
|
||||
const MapData::Poly *poly = _polygons.at(i);
|
||||
if (poly->type() != s.drawOrder().at(n))
|
||||
continue;
|
||||
const Style::Polygon &style = s.polygon(poly->type());
|
||||
|
||||
painter->setPen(style.pen());
|
||||
painter->setBrush(style.brush());
|
||||
painter->drawPath(painterPath(poly->path()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RasterTile::drawLines(QPainter *painter)
|
||||
{
|
||||
const Style &s = style();
|
||||
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
|
||||
for (int i = 0; i < _lines.size(); i++) {
|
||||
const MapData::Line *line = _lines.at(i);
|
||||
const Style::Line &style = s.line(line->type());
|
||||
|
||||
if (style.background() == Qt::NoPen)
|
||||
continue;
|
||||
|
||||
painter->setPen(style.background());
|
||||
painter->drawPolyline(polyline(line->path()));
|
||||
}
|
||||
|
||||
for (int i = 0; i < _lines.size(); i++) {
|
||||
const MapData::Line *line = _lines.at(i);
|
||||
const Style::Line &style = s.line(line->type());
|
||||
|
||||
if (!style.img().isNull()) {
|
||||
BitmapLine::draw(painter, polyline(line->path()), style.img());
|
||||
} else if (style.foreground() != Qt::NoPen) {
|
||||
painter->setPen(style.foreground());
|
||||
painter->drawPolyline(polyline(line->path()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RasterTile::drawTextItems(QPainter *painter,
|
||||
const QList<TextItem*> &textItems)
|
||||
{
|
||||
for (int i = 0; i < textItems.size(); i++)
|
||||
textItems.at(i)->paint(painter);
|
||||
}
|
||||
|
||||
void RasterTile::processPoints(QList<TextItem*> &textItems)
|
||||
{
|
||||
const Style &s = style();
|
||||
|
||||
std::sort(_points.begin(), _points.end(), pointLess);
|
||||
|
||||
for (int i = 0; i < _points.size(); i++) {
|
||||
const MapData::Point *point = _points.at(i);
|
||||
const Style::Point &style = s.point(point->type());
|
||||
|
||||
const QString *label = point->label().isEmpty() ? 0 : &(point->label());
|
||||
const QImage *img = style.img().isNull() ? 0 : &style.img();
|
||||
const QFont *fnt = font(style.textFontSize());
|
||||
const QColor *color = &style.textColor();
|
||||
|
||||
if ((!label || !fnt) && !img)
|
||||
continue;
|
||||
|
||||
TextPointItem *item = new TextPointItem(ll2xy(point->pos()).toPoint(),
|
||||
label, fnt, img, color, &haloColor, 0, ICON_PADDING);
|
||||
if (item->isValid() && !item->collides(textItems))
|
||||
textItems.append(item);
|
||||
else
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void RasterTile::processLines(QList<TextItem*> &textItems)
|
||||
{
|
||||
const Style &s = style();
|
||||
|
||||
for (int i = 0; i < _lines.size(); i++) {
|
||||
const MapData::Line *line = _lines.at(i);
|
||||
const Style::Line &style = s.line(line->type());
|
||||
|
||||
if (style.img().isNull() && style.foreground() == Qt::NoPen)
|
||||
continue;
|
||||
if (line->label().isEmpty() || style.textFontSize() == Style::None)
|
||||
continue;
|
||||
|
||||
const QFont *fnt = font(style.textFontSize());
|
||||
const QColor *color = &style.textColor();
|
||||
|
||||
TextPathItem *item = new TextPathItem(polyline(line->path()),
|
||||
&line->label(), _rect, fnt, color, 0);
|
||||
if (item->isValid() && !item->collides(textItems))
|
||||
textItems.append(item);
|
||||
else
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
void RasterTile::render()
|
||||
{
|
||||
QList<TextItem*> textItems;
|
||||
|
||||
_pixmap.setDevicePixelRatio(_ratio);
|
||||
_pixmap.fill(Qt::transparent);
|
||||
|
||||
processPoints(textItems);
|
||||
processLines(textItems);
|
||||
|
||||
QPainter painter(&_pixmap);
|
||||
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.translate(-_rect.x(), -_rect.y());
|
||||
|
||||
drawPolygons(&painter);
|
||||
drawLines(&painter);
|
||||
|
||||
drawTextItems(&painter, textItems);
|
||||
|
||||
qDeleteAll(textItems);
|
||||
|
||||
//painter.setPen(Qt::red);
|
||||
//painter.setBrush(Qt::NoBrush);
|
||||
//painter.drawRect(QRect(_rect.topLeft(), _pixmap.size()));
|
||||
}
|
55
src/map/ENC/rastertile.h
Normal file
55
src/map/ENC/rastertile.h
Normal file
@ -0,0 +1,55 @@
|
||||
#ifndef ENC_RASTERTILE_H
|
||||
#define ENC_RASTERTILE_H
|
||||
|
||||
#include <QPixmap>
|
||||
#include "map/projection.h"
|
||||
#include "map/transform.h"
|
||||
#include "mapdata.h"
|
||||
|
||||
class TextItem;
|
||||
|
||||
namespace ENC {
|
||||
|
||||
class RasterTile
|
||||
{
|
||||
public:
|
||||
RasterTile(const Projection &proj, const Transform &transform, int zoom,
|
||||
const QRect &rect, qreal ratio, const QList<MapData::Line*> &lines,
|
||||
const QList<MapData::Poly*> &polygons, const QList<MapData::Point*> &points)
|
||||
: _proj(proj), _transform(transform), _zoom(zoom), _rect(rect),
|
||||
_ratio(ratio), _pixmap(rect.width() * ratio, rect.height() * ratio),
|
||||
_lines(lines), _polygons(polygons), _points(points) {}
|
||||
|
||||
int zoom() const {return _zoom;}
|
||||
QPoint xy() const {return _rect.topLeft();}
|
||||
const QPixmap &pixmap() const {return _pixmap;}
|
||||
|
||||
void render();
|
||||
|
||||
private:
|
||||
QPointF ll2xy(const Coordinates &c) const
|
||||
{return _transform.proj2img(_proj.ll2xy(c));}
|
||||
QPainterPath painterPath(const Polygon &polygon) const;
|
||||
QPolygonF polyline(const QVector<Coordinates> &path) const;
|
||||
void processPoints(QList<TextItem*> &textItems);
|
||||
void processLines(QList<TextItem*> &textItems);
|
||||
void drawBitmapPath(QPainter *painter, const QImage &img,
|
||||
const Polygon &polygon);
|
||||
void drawPolygons(QPainter *painter);
|
||||
void drawLines(QPainter *painter);
|
||||
void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems);
|
||||
|
||||
Projection _proj;
|
||||
Transform _transform;
|
||||
int _zoom;
|
||||
QRect _rect;
|
||||
qreal _ratio;
|
||||
QPixmap _pixmap;
|
||||
QList<MapData::Line*> _lines;
|
||||
QList<MapData::Poly*> _polygons;
|
||||
QList<MapData::Point*> _points;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ENC_RASTERTILE_H
|
123
src/map/ENC/style.cpp
Normal file
123
src/map/ENC/style.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include "objects.h"
|
||||
#include "style.h"
|
||||
|
||||
using namespace ENC;
|
||||
|
||||
#define TYPE(t) ((t)<<16)
|
||||
#define SUBTYPE(t, s) (((t)<<16)|(s))
|
||||
|
||||
void Style::defaultPolygonStyle()
|
||||
{
|
||||
_polygons[TYPE(M_COVR)] = Polygon(QBrush("#ffffff"));
|
||||
_polygons[TYPE(LNDARE)] = Polygon(QBrush("#e8e064"));
|
||||
_polygons[TYPE(BUISGL)] = Polygon(QBrush("#d98b21"));
|
||||
_polygons[TYPE(BRIDGE)] = Polygon(QBrush("#a58140"));
|
||||
_polygons[SUBTYPE(DEPARE, 0)] = Polygon(QBrush("#98c064"));
|
||||
_polygons[SUBTYPE(DEPARE, 1)] = Polygon(QBrush("#a0a0ff"));
|
||||
_polygons[SUBTYPE(DEPARE, 2)] = Polygon(QBrush("#b0b0ff"));
|
||||
_polygons[SUBTYPE(DEPARE, 3)] = Polygon(QBrush("#c0c0ff"));
|
||||
_polygons[SUBTYPE(DEPARE, 4)] = Polygon(QBrush("#c0d0ff"));
|
||||
_polygons[SUBTYPE(DEPARE, 5)] = Polygon(QBrush("#c0e0ff"));
|
||||
_polygons[SUBTYPE(DEPARE, 6)] = Polygon(QBrush("#ffffff"));
|
||||
_polygons[TYPE(DMPGRD)] = Polygon(QBrush(QColor("#a3a3a3"), Qt::Dense3Pattern));
|
||||
_polygons[TYPE(FAIRWY)] = Polygon(QBrush("#fcb4fc"));
|
||||
_polygons[TYPE(OBSTRN)] = Polygon(Qt::NoBrush, QPen(QColor("#000000"), 1.5,
|
||||
Qt::DotLine));
|
||||
_polygons[TYPE(PONTON)] = Polygon(QBrush("#333333"));
|
||||
_polygons[TYPE(SLCONS)] = Polygon(Qt::NoBrush, QPen(QColor("#333333"), 1.5,
|
||||
Qt::DashLine));
|
||||
_polygons[TYPE(ACHARE)] = Polygon(Qt::NoBrush, QPen(QColor("#e728e7"), 1,
|
||||
Qt::DashDotLine));
|
||||
_polygons[TYPE(LAKARE)] = Polygon(QBrush("#9fc4e1"),
|
||||
QPen(QColor("#000000"), 1));
|
||||
_polygons[TYPE(RIVERS)] = Polygon(QBrush("#9fc4e1"));
|
||||
|
||||
_drawOrder
|
||||
<< TYPE(M_COVR) << TYPE(LNDARE) << TYPE(BUISGL) << SUBTYPE(DEPARE, 0)
|
||||
<< SUBTYPE(DEPARE, 1) << SUBTYPE(DEPARE, 2) << SUBTYPE(DEPARE, 3)
|
||||
<< SUBTYPE(DEPARE, 4) << SUBTYPE(DEPARE, 5) << SUBTYPE(DEPARE, 6)
|
||||
<< TYPE(LAKARE) << TYPE(RIVERS) << TYPE(FAIRWY) << TYPE(BRIDGE)
|
||||
<< TYPE(SLCONS) << TYPE(PONTON) << TYPE(DMPGRD) << TYPE(OBSTRN)
|
||||
<< TYPE(ACHARE);
|
||||
}
|
||||
|
||||
void Style::defaultLineStyle()
|
||||
{
|
||||
_lines[TYPE(DEPCNT)] = Line(QPen(QColor("#659aef"), 1, Qt::SolidLine));
|
||||
_lines[TYPE(DEPCNT)].setTextColor(QColor("#558adf"));
|
||||
_lines[TYPE(DEPCNT)].setTextFontSize(Small);
|
||||
_lines[TYPE(CBLOHD)] = Line(QImage(":/IMG/cable-line.png"));
|
||||
_lines[TYPE(BRIDGE)] = Line(QPen(QColor("#a58140"), 3, Qt::SolidLine));
|
||||
_lines[TYPE(CBLSUB)] = Line(QImage(":/IMG/cable.png"));
|
||||
_lines[TYPE(CBLSUB)].setTextFontSize(Small);
|
||||
_lines[TYPE(PIPSOL)] = Line(QImage(":/IMG/pipeline.png"));
|
||||
_lines[TYPE(NAVLNE)] = Line(QPen(QColor("#eb49eb"), 1, Qt::DashLine));
|
||||
_lines[TYPE(COALNE)] = Line(QPen(QColor("#000000"), 1, Qt::SolidLine));
|
||||
_lines[TYPE(SLCONS)] = Line(QPen(QColor("#000000"), 2, Qt::SolidLine));
|
||||
_lines[TYPE(PONTON)] = Line(QPen(QColor("#333333"), 1, Qt::SolidLine));
|
||||
_lines[TYPE(DYKCON)] = Line(QPen(QColor("#333333"), 2, Qt::SolidLine));
|
||||
_lines[TYPE(RIVERS)] = Line(QPen(QColor("#000000"), 1, Qt::SolidLine));
|
||||
}
|
||||
|
||||
void Style::defaultPointStyle()
|
||||
{
|
||||
_points[TYPE(BUAARE)].setTextFontSize(Large);
|
||||
_points[TYPE(SOUNDG)].setTextFontSize(Small);
|
||||
_points[TYPE(LIGHTS)] = Point(QImage(":/IMG/light-major.png"), Small);
|
||||
_points[TYPE(BOYCAR)] = Point(QImage(":/IMG/buoy.png"), Small);
|
||||
_points[TYPE(BOYINB)] = Point(QImage(":/IMG/buoy.png"), Small);
|
||||
_points[TYPE(BOYISD)] = Point(QImage(":/IMG/buoy.png"), Small);
|
||||
_points[TYPE(BOYLAT)] = Point(QImage(":/IMG/buoy.png"), Small);
|
||||
_points[TYPE(BOYSAW)] = Point(QImage(":/IMG/buoy.png"), Small);
|
||||
_points[TYPE(BOYSPP)] = Point(QImage(":/IMG/buoy.png"), Small);
|
||||
_points[TYPE(BCNISD)] = Point(QImage(":/IMG/beacon.png"), Small);
|
||||
_points[TYPE(BCNLAT)] = Point(QImage(":/IMG/beacon.png"), Small);
|
||||
_points[TYPE(BCNSAW)] = Point(QImage(":/IMG/beacon.png"), Small);
|
||||
_points[TYPE(BCNSPP)] = Point(QImage(":/IMG/beacon.png"), Small);
|
||||
_points[SUBTYPE(LNDMRK, 3)] = Point(QImage(":/IMG/chimney.png"));
|
||||
_points[SUBTYPE(LNDMRK, 20)] = Point(QImage(":/IMG/church.png"));
|
||||
_points[SUBTYPE(LNDMRK, 17)] = Point(QImage(":/IMG/tower.png"));
|
||||
_points[TYPE(LNDELV)] = Point(QImage(":/IMG/triangulation-point.png"));
|
||||
_points[TYPE(OBSTRN)] = Point(QImage(":/IMG/obstruction.png"), Small);
|
||||
_points[TYPE(WRECKS)] = Point(QImage(":/IMG/wreck.png"), Small);
|
||||
_points[SUBTYPE(WRECKS, 1)] = Point(QImage(":/IMG/wreck.png"), Small);
|
||||
_points[SUBTYPE(WRECKS, 2)] = Point(QImage(":/IMG/wreck-dangerous.png"),
|
||||
Small);
|
||||
_points[SUBTYPE(WRECKS, 3)] = Point(QImage(":/IMG/wreck.png"), Small);
|
||||
_points[SUBTYPE(WRECKS, 4)] = Point(QImage(":/IMG/wreck.png"), Small);
|
||||
_points[SUBTYPE(WRECKS, 5)] = Point(QImage(":/IMG/wreck-exposed.png"));
|
||||
_points[TYPE(UWTROC)] = Point(QImage(":/IMG/rock-dangerous.png"), Small);
|
||||
_points[SUBTYPE(HRBFAC, 5)] = Point(QImage(":/IMG/yacht-harbor.png"));
|
||||
_points[TYPE(PILPNT)] = Point(QImage(":/IMG/pile.png"), Small);
|
||||
}
|
||||
|
||||
Style::Style()
|
||||
{
|
||||
defaultPolygonStyle();
|
||||
defaultLineStyle();
|
||||
defaultPointStyle();
|
||||
}
|
||||
|
||||
const Style::Line &Style::line(quint32 type) const
|
||||
{
|
||||
static Line null;
|
||||
|
||||
QMap<uint, Line>::const_iterator it = _lines.find(type);
|
||||
return (it == _lines.constEnd()) ? null : *it;
|
||||
}
|
||||
|
||||
const Style::Polygon &Style::polygon(quint32 type) const
|
||||
{
|
||||
static Polygon null;
|
||||
|
||||
QMap<uint, Polygon>::const_iterator it = _polygons.find(type);
|
||||
return (it == _polygons.constEnd()) ? null : *it;
|
||||
}
|
||||
|
||||
const Style::Point &Style::point(quint32 type) const
|
||||
{
|
||||
static Point null;
|
||||
|
||||
QMap<uint, Point>::const_iterator it = _points.find(type);
|
||||
return (it == _points.constEnd()) ? null : *it;
|
||||
}
|
104
src/map/ENC/style.h
Normal file
104
src/map/ENC/style.h
Normal file
@ -0,0 +1,104 @@
|
||||
#ifndef ENC_STYLE_H
|
||||
#define ENC_STYLE_H
|
||||
|
||||
#include <QPen>
|
||||
#include <QBrush>
|
||||
#include <QMap>
|
||||
|
||||
namespace ENC {
|
||||
|
||||
class Style
|
||||
{
|
||||
public:
|
||||
enum FontSize {
|
||||
None,
|
||||
Small,
|
||||
Normal,
|
||||
Large,
|
||||
};
|
||||
|
||||
class Polygon {
|
||||
public:
|
||||
Polygon() : _brush(Qt::NoBrush), _pen(Qt::NoPen) {}
|
||||
Polygon(const QBrush &brush, const QPen &pen = Qt::NoPen)
|
||||
: _brush(brush)
|
||||
{
|
||||
_pen = (pen == Qt::NoPen) ? QPen(_brush, 0) : pen;
|
||||
}
|
||||
|
||||
const QPen &pen() const {return _pen;}
|
||||
const QBrush &brush() const {return _brush;}
|
||||
|
||||
private:
|
||||
QBrush _brush;
|
||||
QPen _pen;
|
||||
};
|
||||
|
||||
class Line {
|
||||
public:
|
||||
Line() : _foreground(Qt::NoPen), _background(Qt::NoPen),
|
||||
_textFontSize(None) {}
|
||||
Line(const QPen &foreground, const QPen &background = Qt::NoPen)
|
||||
: _foreground(foreground), _background(background),
|
||||
_textFontSize(None) {}
|
||||
Line(const QImage &img)
|
||||
: _foreground(Qt::NoPen), _background(Qt::NoPen),
|
||||
_textFontSize(None), _img(img.convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied)) {}
|
||||
|
||||
void setTextColor(const QColor &color) {_textColor = color;}
|
||||
void setTextFontSize(FontSize size) {_textFontSize = size;}
|
||||
|
||||
const QPen &foreground() const {return _foreground;}
|
||||
const QPen &background() const {return _background;}
|
||||
const QColor &textColor() const {return _textColor;}
|
||||
FontSize textFontSize() const {return _textFontSize;}
|
||||
const QImage &img() const {return _img;}
|
||||
|
||||
private:
|
||||
QPen _foreground, _background;
|
||||
QColor _textColor;
|
||||
FontSize _textFontSize;
|
||||
QImage _img;
|
||||
};
|
||||
|
||||
class Point {
|
||||
public:
|
||||
Point() : _textColor(Qt::black), _textFontSize(Normal) {}
|
||||
Point(const QImage &img, FontSize fontSize = Normal)
|
||||
: _textColor(Qt::black), _textFontSize(fontSize), _img(img) {}
|
||||
|
||||
void setTextColor(const QColor &color) {_textColor = color;}
|
||||
void setTextFontSize(FontSize size) {_textFontSize = size;}
|
||||
|
||||
const QColor &textColor() const {return _textColor;}
|
||||
FontSize textFontSize() const {return _textFontSize;}
|
||||
const QImage &img() const {return _img;}
|
||||
|
||||
private:
|
||||
QColor _textColor;
|
||||
FontSize _textFontSize;
|
||||
QImage _img;
|
||||
};
|
||||
|
||||
Style();
|
||||
|
||||
const Line &line(uint type) const;
|
||||
const Polygon &polygon(uint type) const;
|
||||
const Point &point(quint32 type) const;
|
||||
const QVector<uint> &drawOrder() const {return _drawOrder;}
|
||||
|
||||
private:
|
||||
void defaultPolygonStyle();
|
||||
void defaultLineStyle();
|
||||
void defaultPointStyle();
|
||||
|
||||
QMap<uint, Line> _lines;
|
||||
QMap<uint, Polygon> _polygons;
|
||||
QMap<uint, Point> _points;
|
||||
QVector<uint> _drawOrder;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // ENC_STYLE_H
|
@ -1,11 +1,10 @@
|
||||
#include "common/util.h"
|
||||
#include "bitstream.h"
|
||||
#include "nodfile.h"
|
||||
|
||||
using namespace Garmin;
|
||||
using namespace IMG;
|
||||
|
||||
#define ARRAY_SIZE(array) \
|
||||
(sizeof(array) / sizeof(array[0]))
|
||||
#define DELTA(val, llBits, maxBits) \
|
||||
(((int)((val) << (32 - (llBits))) >> (32 - (llBits))) << (32 - (maxBits)))
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "map/imgmap.h"
|
||||
#include "map/textpathitem.h"
|
||||
#include "map/textpointitem.h"
|
||||
#include "bitmapline.h"
|
||||
#include "map/bitmapline.h"
|
||||
#include "style.h"
|
||||
#include "lblfile.h"
|
||||
#include "rastertile.h"
|
||||
@ -357,7 +357,7 @@ void RasterTile::processStreetNames(QList<TextItem*> &textItems)
|
||||
? &style.textColor() : 0;
|
||||
|
||||
TextPathItem *item = new TextPathItem(poly.points,
|
||||
&poly.label.text(), _rect, fnt, color);
|
||||
&poly.label.text(), _rect, fnt, color, &haloColor);
|
||||
if (item->isValid() && !item->collides(textItems))
|
||||
textItems.append(item);
|
||||
else
|
||||
|
@ -37,7 +37,6 @@ public:
|
||||
private:
|
||||
QBrush _brush;
|
||||
QPen _pen;
|
||||
QColor _textColor;
|
||||
};
|
||||
|
||||
class Line {
|
||||
|
186
src/map/encmap.cpp
Normal file
186
src/map/encmap.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
#include <QPainter>
|
||||
#include <QPixmapCache>
|
||||
#include <QtConcurrent>
|
||||
#include "common/range.h"
|
||||
#include "common/wgs84.h"
|
||||
#include "ENC/rastertile.h"
|
||||
#include "rectd.h"
|
||||
#include "pcs.h"
|
||||
#include "encmap.h"
|
||||
|
||||
#define TILE_SIZE 512
|
||||
#define TEXT_EXTENT 160
|
||||
|
||||
using namespace ENC;
|
||||
|
||||
static Range ZOOMS = Range(0, 20);
|
||||
|
||||
ENCMap::ENCMap(const QString &fileName, QObject *parent)
|
||||
: Map(fileName, parent), _data(fileName), _projection(PCS::pcs(3857)),
|
||||
_tileRatio(1.0), _zoom(0)
|
||||
{
|
||||
if (_data.isValid()) {
|
||||
_llBounds = _data.bounds();
|
||||
updateTransform();
|
||||
}
|
||||
}
|
||||
|
||||
void ENCMap::load()
|
||||
{
|
||||
_data.load();
|
||||
}
|
||||
|
||||
void ENCMap::unload()
|
||||
{
|
||||
_data.clear();
|
||||
}
|
||||
|
||||
int ENCMap::zoomFit(const QSize &size, const RectC &rect)
|
||||
{
|
||||
if (rect.isValid()) {
|
||||
RectD pr(rect, _projection, 10);
|
||||
|
||||
_zoom = ZOOMS.min();
|
||||
for (int i = ZOOMS.min() + 1; i <= ZOOMS.max(); i++) {
|
||||
Transform t(transform(i));
|
||||
QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight()));
|
||||
if (size.width() < r.width() || size.height() < r.height())
|
||||
break;
|
||||
_zoom = i;
|
||||
}
|
||||
} else
|
||||
_zoom = ZOOMS.max();
|
||||
|
||||
updateTransform();
|
||||
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
int ENCMap::zoomIn()
|
||||
{
|
||||
_zoom = qMin(_zoom + 1, ZOOMS.max());
|
||||
updateTransform();
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
int ENCMap::zoomOut()
|
||||
{
|
||||
_zoom = qMax(_zoom - 1, ZOOMS.min());
|
||||
updateTransform();
|
||||
return _zoom;
|
||||
}
|
||||
|
||||
void ENCMap::setZoom(int zoom)
|
||||
{
|
||||
_zoom = zoom;
|
||||
updateTransform();
|
||||
}
|
||||
|
||||
Transform ENCMap::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 ENCMap::updateTransform()
|
||||
{
|
||||
_transform = transform(_zoom);
|
||||
|
||||
RectD prect(_llBounds, _projection);
|
||||
_bounds = QRectF(_transform.proj2img(prect.topLeft()),
|
||||
_transform.proj2img(prect.bottomRight()));
|
||||
}
|
||||
|
||||
QString ENCMap::key(int zoom, const QPoint &xy) const
|
||||
{
|
||||
return path() + "-" + QString::number(zoom) + "_"
|
||||
+ QString::number(xy.x()) + "_" + QString::number(xy.y());
|
||||
}
|
||||
|
||||
void ENCMap::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);
|
||||
|
||||
QPixmap pm;
|
||||
if (QPixmapCache::find(key(_zoom, ttl), &pm))
|
||||
painter->drawPixmap(ttl, pm);
|
||||
else {
|
||||
QList<MapData::Poly*> polygons;
|
||||
QList<MapData::Line*> lines;
|
||||
QList<MapData::Point*> points;
|
||||
|
||||
QRectF polyRect(ttl, QPointF(ttl.x() + TILE_SIZE,
|
||||
ttl.y() + TILE_SIZE));
|
||||
polyRect &= _bounds;
|
||||
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
|
||||
_transform.img2proj(polyRect.bottomRight()));
|
||||
RectC polyRectC(polyRectD.toRectC(_projection, 20));
|
||||
_data.lines(polyRectC, &lines);
|
||||
_data.polygons(polyRectC, &polygons);
|
||||
|
||||
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT,
|
||||
ttl.y() - TEXT_EXTENT), QPointF(ttl.x() + TILE_SIZE
|
||||
+ TEXT_EXTENT, ttl.y() + TILE_SIZE + TEXT_EXTENT));
|
||||
pointRect &= _bounds;
|
||||
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
|
||||
_transform.img2proj(pointRect.bottomRight()));
|
||||
_data.points(pointRectD.toRectC(_projection, 20), &points);
|
||||
|
||||
tiles.append(RasterTile(_projection, _transform, _zoom,
|
||||
QRect(ttl, QSize(TILE_SIZE, TILE_SIZE)), _tileRatio,
|
||||
lines, polygons, points));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void ENCMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio)
|
||||
{
|
||||
Q_UNUSED(mapRatio);
|
||||
|
||||
_tileRatio = deviceRatio;
|
||||
}
|
||||
|
||||
void ENCMap::setOutputProjection(const Projection &projection)
|
||||
{
|
||||
if (projection == _projection)
|
||||
return;
|
||||
|
||||
_projection = projection;
|
||||
updateTransform();
|
||||
QPixmapCache::clear();
|
||||
}
|
||||
|
||||
Map *ENCMap::create(const QString &path, const Projection &, bool *isMap)
|
||||
{
|
||||
if (isMap)
|
||||
*isMap = false;
|
||||
|
||||
return new ENCMap(path);
|
||||
}
|
63
src/map/encmap.h
Normal file
63
src/map/encmap.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef ENCMAP_H
|
||||
#define ENCMAP_H
|
||||
|
||||
#include "map.h"
|
||||
#include "projection.h"
|
||||
#include "transform.h"
|
||||
#include "ENC/mapdata.h"
|
||||
|
||||
class QFile;
|
||||
|
||||
class ENCMap : public Map
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ENCMap(const QString &fileName, QObject *parent = 0);
|
||||
|
||||
QString name() const {return _data.name();}
|
||||
|
||||
QRectF bounds() {return _bounds;}
|
||||
RectC llBounds() {return _llBounds;}
|
||||
|
||||
int zoom() const {return _zoom;}
|
||||
void setZoom(int zoom);
|
||||
int zoomFit(const QSize &size, const RectC &rect);
|
||||
int zoomIn();
|
||||
int zoomOut();
|
||||
|
||||
void load();
|
||||
void unload();
|
||||
void setOutputProjection(const Projection &projection);
|
||||
void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio);
|
||||
|
||||
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);
|
||||
|
||||
bool isValid() const {return _data.isValid();}
|
||||
QString errorString() const {return _data.errorString();}
|
||||
|
||||
static Map *create(const QString &path, const Projection &, bool *isMap);
|
||||
|
||||
private:
|
||||
Transform transform(int zoom) const;
|
||||
void updateTransform();
|
||||
QString key(int zoom, const QPoint &xy) const;
|
||||
|
||||
ENC::MapData _data;
|
||||
Projection _projection;
|
||||
Transform _transform;
|
||||
qreal _tileRatio;
|
||||
RectC _llBounds;
|
||||
QRectF _bounds;
|
||||
int _zoom;
|
||||
|
||||
bool _valid;
|
||||
QString _errorString;
|
||||
};
|
||||
|
||||
#endif // ENCMAP_H
|
@ -19,6 +19,7 @@
|
||||
#include "osmdroidmap.h"
|
||||
#include "gemfmap.h"
|
||||
#include "oruxmap.h"
|
||||
#include "encmap.h"
|
||||
#include "invalidmap.h"
|
||||
#include "maplist.h"
|
||||
|
||||
@ -54,6 +55,7 @@ MapList::ParserMap MapList::parsers()
|
||||
map.insert("sqlite", &OsmdroidMap::create);
|
||||
map.insert("gemf", &GEMFMap::create);
|
||||
map.insert("otrk2.xml", &OruxMap::create);
|
||||
map.insert("000", &ENCMap::create);
|
||||
|
||||
return map;
|
||||
}
|
||||
@ -153,6 +155,7 @@ QString MapList::formats()
|
||||
return
|
||||
qApp->translate("MapList", "Supported files")
|
||||
+ " (" + filter().join(" ") + ");;"
|
||||
+ qApp->translate("MapList", "Electronic Navigational Charts") + " (*.000);;"
|
||||
+ qApp->translate("MapList", "AlpineQuest maps") + " (*.aqm);;"
|
||||
+ qApp->translate("MapList", "GEMF maps") + " (*.gemf);;"
|
||||
+ qApp->translate("MapList", "Garmin IMG maps")
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <QPainter>
|
||||
#include <QPixmapCache>
|
||||
#include "common/wgs84.h"
|
||||
#include "common/util.h"
|
||||
#include "pcs.h"
|
||||
#include "rectd.h"
|
||||
#include "mapsforgemap.h"
|
||||
@ -10,16 +11,6 @@ using namespace Mapsforge;
|
||||
|
||||
#define TEXT_EXTENT 160
|
||||
|
||||
static int log2i(unsigned val)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
while (val >>= 1)
|
||||
ret++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MapsforgeMap::MapsforgeMap(const QString &fileName, QObject *parent)
|
||||
: Map(fileName, parent), _data(fileName), _zoom(0),
|
||||
_projection(PCS::pcs(3857)), _tileRatio(1.0)
|
||||
@ -87,7 +78,7 @@ void MapsforgeMap::setZoom(int zoom)
|
||||
|
||||
Transform MapsforgeMap::transform(int zoom) const
|
||||
{
|
||||
int z = zoom + log2i(_data.tileSize());
|
||||
int z = zoom + Util::log2i(_data.tileSize());
|
||||
|
||||
double scale = _projection.isGeographic()
|
||||
? 360.0 / (1<<z) : (2.0 * M_PI * WGS84_RADIUS) / (1<<z);
|
||||
|
@ -1,12 +1,9 @@
|
||||
#include <QFile>
|
||||
#include "common/util.h"
|
||||
#include "gcs.h"
|
||||
#include "pcs.h"
|
||||
#include "prjfile.h"
|
||||
|
||||
|
||||
#define ARRAY_SIZE(array) \
|
||||
(sizeof(array) / sizeof(array[0]))
|
||||
|
||||
static Projection::Method projectionMethod(const QString &name)
|
||||
{
|
||||
static const struct {
|
||||
|
@ -237,8 +237,9 @@ static bool reverse(const QPainterPath &path)
|
||||
}
|
||||
|
||||
TextPathItem::TextPathItem(const QPolygonF &line, const QString *label,
|
||||
const QRect &tileRect, const QFont *font, const QColor *color)
|
||||
: TextItem(label), _font(font), _color(color), _haloColor(0)
|
||||
const QRect &tileRect, const QFont *font, const QColor *color,
|
||||
const QColor *haloColor) : TextItem(label), _font(font), _color(color),
|
||||
_haloColor(haloColor)
|
||||
{
|
||||
qreal cw = font->pixelSize() * 0.6;
|
||||
qreal textWidth = _text->size() * cw;
|
||||
@ -290,29 +291,32 @@ void TextPathItem::paint(QPainter *painter) const
|
||||
QTransform t = painter->transform();
|
||||
|
||||
painter->setFont(*_font);
|
||||
painter->setPen(_haloColor ? *_haloColor : Qt::white);
|
||||
|
||||
for (int i = 0; i < _text->size(); i++) {
|
||||
QPointF point = _path.pointAtPercent(percent);
|
||||
qreal angle = _path.angleAtPercent(percent);
|
||||
QChar c = _text->at(i);
|
||||
if (_haloColor) {
|
||||
painter->setPen(*_haloColor);
|
||||
|
||||
painter->translate(point);
|
||||
painter->rotate(-angle);
|
||||
painter->drawText(QPoint(-1, fm.descent() - 1), c);
|
||||
painter->drawText(QPoint(1, fm.descent() + 1), c);
|
||||
painter->drawText(QPoint(-1, fm.descent() + 1), c);
|
||||
painter->drawText(QPoint(1, fm.descent() -1), c);
|
||||
painter->drawText(QPoint(0, fm.descent() - 1), c);
|
||||
painter->drawText(QPoint(0, fm.descent() + 1), c);
|
||||
painter->drawText(QPoint(-1, fm.descent()), c);
|
||||
painter->drawText(QPoint(1, fm.descent()), c);
|
||||
painter->setTransform(t);
|
||||
for (int i = 0; i < _text->size(); i++) {
|
||||
QPointF point = _path.pointAtPercent(percent);
|
||||
qreal angle = _path.angleAtPercent(percent);
|
||||
QChar c = _text->at(i);
|
||||
|
||||
int width = fm.horizontalAdvance(_text->at(i));
|
||||
percent += ((qreal)width / (qreal)textWidth) * factor;
|
||||
painter->translate(point);
|
||||
painter->rotate(-angle);
|
||||
painter->drawText(QPoint(-1, fm.descent() - 1), c);
|
||||
painter->drawText(QPoint(1, fm.descent() + 1), c);
|
||||
painter->drawText(QPoint(-1, fm.descent() + 1), c);
|
||||
painter->drawText(QPoint(1, fm.descent() -1), c);
|
||||
painter->drawText(QPoint(0, fm.descent() - 1), c);
|
||||
painter->drawText(QPoint(0, fm.descent() + 1), c);
|
||||
painter->drawText(QPoint(-1, fm.descent()), c);
|
||||
painter->drawText(QPoint(1, fm.descent()), c);
|
||||
painter->setTransform(t);
|
||||
|
||||
int width = fm.horizontalAdvance(_text->at(i));
|
||||
percent += ((qreal)width / (qreal)textWidth) * factor;
|
||||
}
|
||||
percent = (1.0 - factor) / 2.0;
|
||||
}
|
||||
percent = (1.0 - factor) / 2.0;
|
||||
|
||||
painter->setPen(_color ? *_color : Qt::black);
|
||||
for (int i = 0; i < _text->size(); i++) {
|
||||
|
@ -10,7 +10,8 @@ class TextPathItem : public TextItem
|
||||
public:
|
||||
TextPathItem() : TextItem(0), _font(0), _color(0) {}
|
||||
TextPathItem(const QPolygonF &line, const QString *label,
|
||||
const QRect &tileRect, const QFont *font, const QColor *color);
|
||||
const QRect &tileRect, const QFont *font, const QColor *color,
|
||||
const QColor *haloColor);
|
||||
TextPathItem(const QPainterPath &line, const QString *label,
|
||||
const QRect &tileRect, const QFont *font, const QColor *color,
|
||||
const QColor *haloColor);
|
||||
|
Loading…
Reference in New Issue
Block a user