1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-27 21:24:47 +01:00

Added support for ENC maps

This commit is contained in:
Martin Tůma 2022-11-04 09:03:36 +01:00
parent a2e22cd93b
commit f8d856b7ee
28 changed files with 2204 additions and 47 deletions

View File

@ -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 \

View File

@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

View File

@ -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};

View File

@ -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

View File

@ -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));

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View File

@ -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)))

View File

@ -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

View File

@ -37,7 +37,6 @@ public:
private:
QBrush _brush;
QPen _pen;
QColor _textColor;
};
class Line {

186
src/map/encmap.cpp Normal file
View 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
View 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

View File

@ -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")

View File

@ -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);

View File

@ -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 {

View File

@ -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,7 +291,9 @@ void TextPathItem::paint(QPainter *painter) const
QTransform t = painter->transform();
painter->setFont(*_font);
painter->setPen(_haloColor ? *_haloColor : Qt::white);
if (_haloColor) {
painter->setPen(*_haloColor);
for (int i = 0; i < _text->size(); i++) {
QPointF point = _path.pointAtPercent(percent);
@ -313,6 +316,7 @@ void TextPathItem::paint(QPainter *painter) const
percent += ((qreal)width / (qreal)textWidth) * factor;
}
percent = (1.0 - factor) / 2.0;
}
painter->setPen(_color ? *_color : Qt::black);
for (int i = 0; i < _text->size(); i++) {

View File

@ -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);