1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-07-17 12:24:24 +02:00

Added support for ENC atlases (catalogues)

This commit is contained in:
2023-09-07 09:31:23 +02:00
parent 864326210a
commit b1f104c2ec
18 changed files with 1215 additions and 466 deletions

94
src/map/ENC/atlasdata.cpp Normal file
View File

@ -0,0 +1,94 @@
#include "atlasdata.h"
using namespace ENC;
bool AtlasData::pointCb(const QString *map, void *context)
{
PointCTX *ctx = (PointCTX*)context;
ctx->lock.lock();
MapData *cached = ctx->cache.object(map);
if (!cached) {
MapData *data = new MapData(*map);
data->points(ctx->rect, ctx->points);
if (!ctx->cache.insert(map, data))
delete data;
} else
cached->points(ctx->rect, ctx->points);
ctx->lock.unlock();
return true;
}
bool AtlasData::polyCb(const QString *map, void *context)
{
PolyCTX *ctx = (PolyCTX*)context;
ctx->lock.lock();
MapData *cached = ctx->cache.object(map);
if (!cached) {
MapData *data = new MapData(*map);
data->polygons(ctx->rect, ctx->polygons);
data->lines(ctx->rect, ctx->lines);
if (!ctx->cache.insert(map, data))
delete data;
} else {
cached->polygons(ctx->rect, ctx->polygons);
cached->lines(ctx->rect, ctx->lines);
}
ctx->lock.unlock();
return true;
}
AtlasData::~AtlasData()
{
MapTree::Iterator it;
for (_tree.GetFirst(it); !_tree.IsNull(it); _tree.GetNext(it))
delete _tree.GetAt(it);
}
void AtlasData::addMap(const RectC &bounds, const QString &path)
{
double min[2], max[2];
min[0] = bounds.left();
min[1] = bounds.bottom();
max[0] = bounds.right();
max[1] = bounds.top();
_tree.Insert(min, max, new QString(path));
}
void AtlasData::polys(const RectC &rect, QList<MapData::Poly> *polygons,
QList<MapData::Line> *lines)
{
double min[2], max[2];
PolyCTX polyCtx(rect, polygons, lines, _cache, _lock);
min[0] = rect.left();
min[1] = rect.bottom();
max[0] = rect.right();
max[1] = rect.top();
_tree.Search(min, max, polyCb, &polyCtx);
}
void AtlasData::points(const RectC &rect, QList<MapData::Point> *points)
{
double min[2], max[2];
PointCTX pointCtx(rect, points, _cache, _lock);
min[0] = rect.left();
min[1] = rect.bottom();
max[0] = rect.right();
max[1] = rect.top();
_tree.Search(min, max, pointCb, &pointCtx);
}

65
src/map/ENC/atlasdata.h Normal file
View File

@ -0,0 +1,65 @@
#ifndef ENC_ATLASDATA_H
#define ENC_ATLASDATA_H
#include <QCache>
#include <QMutex>
#include "common/rtree.h"
#include "mapdata.h"
namespace ENC {
typedef QCache<const QString*, MapData> MapCache;
class AtlasData
{
public:
AtlasData(MapCache &cache, QMutex &lock)
: _cache(cache), _lock(lock) {}
~AtlasData();
void addMap(const RectC &bounds, const QString &path);
void polys(const RectC &rect, QList<MapData::Poly> *polygons,
QList<MapData::Line> *lines);
void points(const RectC &rect, QList<MapData::Point> *points);
private:
typedef RTree<const QString*, double, 2> MapTree;
struct PolyCTX
{
PolyCTX(const RectC &rect, QList<MapData::Poly> *polygons,
QList<MapData::Line> *lines, MapCache &cache, QMutex &lock)
: rect(rect), polygons(polygons), lines(lines), cache(cache),
lock(lock) {}
const RectC &rect;
QList<MapData::Poly> *polygons;
QList<MapData::Line> *lines;
MapCache &cache;
QMutex &lock;
};
struct PointCTX
{
PointCTX(const RectC &rect, QList<MapData::Point> *points,
MapCache &cache, QMutex &lock) : rect(rect), points(points),
cache(cache), lock(lock) {}
const RectC &rect;
QList<MapData::Point> *points;
MapCache &cache;
QMutex &lock;
};
static bool polyCb(const QString *map, void *context);
static bool pointCb(const QString *map, void *context);
MapTree _tree;
MapCache &_cache;
QMutex &_lock;
};
}
#endif // ENC_ATLASDATA_H

View File

@ -66,36 +66,27 @@ bool ISO8211::Field::subfield(const char *name, QByteArray *val, int idx) const
return true;
}
bool ISO8211::fieldType(const QString &str, int cnt, FieldType &type, int &size)
ISO8211::SubFieldDefinition ISO8211::fieldType(const QString &str, int cnt,
const QByteArray &tag)
{
if (str == "A" || str == "I" || str == "R") {
type = String;
size = cnt;
} else if (str == "B") {
type = Array;
size = cnt / 8;
} else if (str == "b11") {
type = U8;
size = 1;
} else if (str == "b12") {
type = U16;
size = 2;
} else if (str == "b14") {
type = U32;
size = 4;
} else if (str == "b21") {
type = S8;
size = 1;
} else if (str == "b22") {
type = S16;
size = 2;
} else if (str == "b24") {
type = S32;
size = 4;
} else
return false;
return true;
if (str == "A" || str == "I" || str == "R")
return SubFieldDefinition(tag, String, cnt);
else if (str == "B")
return SubFieldDefinition(tag, Array, cnt / 8);
else if (str == "b11")
return SubFieldDefinition(tag, U8, 1);
else if (str == "b12")
return SubFieldDefinition(tag, U16, 2);
else if (str == "b14")
return SubFieldDefinition(tag, U32, 4);
else if (str == "b21")
return SubFieldDefinition(tag, S8, 1);
else if (str == "b22")
return SubFieldDefinition(tag, S16, 2);
else if (str == "b24")
return SubFieldDefinition(tag, S32, 4);
else
return SubFieldDefinition();
}
int ISO8211::readDR(QVector<FieldDefinition> &fields)
@ -145,6 +136,8 @@ bool ISO8211::readDDA(const FieldDefinition &def, SubFields &fields)
{
static QRegularExpression re("(\\d*)(\\w+)\\(*(\\d*)\\)*");
QByteArray ba;
bool repeat = false;
QVector<SubFieldDefinition> defs;
ba.resize(def.size);
if (!(_file.seek(def.pos) && _file.read(ba.data(), ba.size()) == ba.size()))
@ -152,7 +145,7 @@ bool ISO8211::readDDA(const FieldDefinition &def, SubFields &fields)
QList<QByteArray> list(ba.split('\x1f'));
if (!list.at(1).isEmpty() && list.at(1).front() == '*') {
fields.setRepeat(true);
repeat = true;
list[1].remove(0, 1);
}
QList<QByteArray> tags(list.at(1).split('!'));
@ -161,7 +154,7 @@ bool ISO8211::readDDA(const FieldDefinition &def, SubFields &fields)
QRegularExpressionMatchIterator it = re.globalMatch(list.at(2));
int tag = 0;
fields.resize(tags.size());
defs.resize(tags.size());
while (it.hasNext()) {
QRegularExpressionMatch match = it.next();
@ -183,15 +176,17 @@ bool ISO8211::readDDA(const FieldDefinition &def, SubFields &fields)
}
for (uint i = 0; i < cnt; i++) {
SubFieldDefinition &f = fields[tag];
f.tag = tags.at(tag);
if (!fieldType(typeStr, size, f.type, f.size))
SubFieldDefinition sfd(fieldType(typeStr, size, tags.at(tag)));
if (sfd.type() == Unknown)
return false;
defs[tag] = sfd;
tag++;
}
}
}
fields = SubFields(defs, repeat);
return true;
}
@ -228,8 +223,7 @@ bool ISO8211::readDDR()
return true;
}
bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def,
const SubFields &fields, Data &data)
bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def, Data &data)
{
QByteArray ba;
@ -242,22 +236,19 @@ bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def,
const char *dp = ba.constData();
const char *ep = ba.constData() + ba.size() - 1;
data.clear();
data.setFields(&fields);
do {
QVector<QVariant> row;
row.resize(fields.size());
row.resize(data.fields()->size());
for (int i = 0; i < fields.size(); i++) {
const SubFieldDefinition &f = fields.at(i);
for (int i = 0; i < data.fields()->size(); i++) {
const SubFieldDefinition &f = data.fields()->at(i);
switch (f.type) {
switch (f.type()) {
case String:
case Array:
if (f.size) {
row[i] = QVariant(QByteArray(dp, f.size));
dp += f.size;
if (f.size()) {
row[i] = QVariant(QByteArray(dp, f.size()));
dp += f.size();
} else {
sp = dp;
while (dp < ep && *dp != '\x1f')
@ -290,11 +281,13 @@ bool ISO8211::readUDA(quint64 pos, const FieldDefinition &def,
row[i] = QVariant(UINT32(dp));
dp += 4;
break;
default:
return false;
}
}
data.append(row);
} while (fields.repeat() && dp < ep);
} while (data.fields()->repeat() && dp < ep);
return true;
}
@ -317,23 +310,33 @@ bool ISO8211::readRecord(Record &record)
for (int i = 0; i < fields.size(); i++) {
const FieldDefinition &def = fields.at(i);
Field &f = record[i];
FieldsMap::const_iterator it = _map.find(def.tag);
if (it == _map.constEnd()) {
_errorString = QString("%1: unknown record")
.arg(QString(def.tag));
_errorString = QString("%1: unknown record").arg(QString(def.tag));
return false;
}
f.setTag(def.tag);
Data data(&it.value());
if (!readUDA(pos, def, it.value(), f.rdata())) {
if (!readUDA(pos, def, data)) {
_errorString = QString("Error reading %1 record")
.arg(QString(def.tag));
return false;
}
record[i] = Field(def.tag, data);
}
return true;
}
const ISO8211::Field *ISO8211::field(const Record &record, const QByteArray &name)
{
for (int i = 0; i < record.size(); i++)
if (record.at(i).tag() == name)
return &record.at(i);
return 0;
}

View File

@ -17,60 +17,87 @@ namespace ENC {
class ISO8211
{
public:
enum FieldType {String, Array, S8, S16, S32, U8, U16, U32};
enum FieldType {Unknown, String, Array, S8, S16, S32, U8, U16, U32};
struct FieldDefinition {
struct FieldDefinition
{
QByteArray tag;
int pos;
int size;
};
struct SubFieldDefinition {
QByteArray tag;
FieldType type;
int size;
};
class SubFields : public QVector<SubFieldDefinition>
class SubFieldDefinition
{
public:
SubFields() : QVector<SubFieldDefinition>(), _repeat(false) {}
SubFieldDefinition() : _type(Unknown), _size(0) {}
SubFieldDefinition(const QByteArray &tag, FieldType type, int size)
: _tag(tag), _type(type), _size(size) {}
bool repeat() const {return _repeat;}
void setRepeat(bool repeat) {_repeat = repeat;}
const QByteArray &tag() const {return _tag;}
FieldType type() const {return _type;}
int size() const {return _size;}
private:
QByteArray _tag;
FieldType _type;
int _size;
};
class SubFields
{
public:
SubFields() : _repeat(false) {}
SubFields(const QVector<SubFieldDefinition> &defs, bool repeat)
: _defs(defs), _repeat(repeat) {}
int size() const {return _defs.size();}
const SubFieldDefinition &at(int i) const {return _defs.at(i);}
bool repeat() const {return _repeat;}
private:
QVector<SubFieldDefinition> _defs;
bool _repeat;
};
class Data : public QVector<QVector<QVariant> >
class Data
{
public:
Data() : QVector<QVector<QVariant> >(), _fields(0) {}
Data() : _fields(0) {}
Data(const SubFields *fields) : _fields(fields) {}
void setFields(const SubFields *fields) {_fields = fields;}
int size() const {return _data.size();}
const QVector<QVariant> &at(int i) const {return _data.at(i);}
const SubFields *fields() const {return _fields;}
const QVariant *field(const QByteArray &name, int idx = 0) const
{
const QVector<QVariant> &v = at(idx);
const QVector<QVariant> &v = _data.at(idx);
for (int i = 0; i < _fields->size(); i++)
if (_fields->at(i).tag == name)
if (_fields->at(i).tag() == name)
return &v.at(i);
return 0;
}
private:
friend class ISO8211;
void append(QVector<QVariant> &row) {_data.append(row);}
QVector<QVector<QVariant> > _data;
const SubFields *_fields;
};
class Field
{
public:
Field() {}
Field(const QByteArray &tag, const Data &data)
: _tag(tag), _data(data) {}
const QByteArray &tag() const {return _tag;}
void setTag(const QByteArray &tag) {_tag = tag;}
Data &rdata() {return _data;}
const Data &data() const {return _data;}
bool subfield(const char *name, int *val, int idx = 0) const;
@ -82,17 +109,7 @@ public:
Data _data;
};
class Record : public QVector<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;
}
};
typedef QVector<Field> Record;
ISO8211(const QString &path) : _file(path) {}
bool readDDR();
@ -100,16 +117,17 @@ public:
const QString &errorString() const {return _errorString;}
static const Field *field(const Record &record, const QByteArray &name);
private:
typedef QMap<QByteArray, SubFields> FieldsMap;
static bool fieldType(const QString &str, int cnt, FieldType &type,
int &size);
static SubFieldDefinition fieldType(const QString &str, int cnt,
const QByteArray &tag);
int readDR(QVector<FieldDefinition> &fields);
bool readDDA(const FieldDefinition &def, SubFields &fields);
bool readUDA(quint64 pos, const FieldDefinition &def,
const SubFields &fields, Data &data);
bool readUDA(quint64 pos, const FieldDefinition &def, Data &data);
QFile _file;
FieldsMap _map;
@ -119,20 +137,20 @@ private:
#ifndef QT_NO_DEBUG
inline QDebug operator<<(QDebug dbg, const ISO8211::FieldDefinition &def)
{
dbg.nospace() << "Field(" << def.tag << ", " << def.size << ")";
dbg.nospace() << "FieldDefinition(" << def.tag << ", " << def.size << ")";
return dbg.space();
}
inline QDebug operator<<(QDebug dbg, const ISO8211::SubFieldDefinition &def)
{
dbg.nospace() << "SubField(" << def.tag << ", " << def.type << ", "
<< def.size << ")";
dbg.nospace() << "SubField(" << def.tag() << ", " << def.type() << ", "
<< def.size() << ")";
return dbg.space();
}
inline QDebug operator<<(QDebug dbg, const ISO8211::Field &field)
{
dbg.nospace() << "Field(" << field.tag() /*<< ", " << field.data()*/ << ")";
dbg.nospace() << "Field(" << field.tag() << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG

View File

@ -141,32 +141,32 @@ static const ISO8211::Field *SGXD(const ISO8211::Record &r)
{
const ISO8211::Field *f;
if ((f = r.field("SG2D")))
if ((f = ISO8211::field(r, "SG2D")))
return f;
else if ((f = r.field("SG3D")))
else if ((f = ISO8211::field(r, "SG3D")))
return f;
else
return 0;
}
static bool pointCb(MapData::Point *point, void *context)
static bool pointCb(const MapData::Point *point, void *context)
{
QList<MapData::Point*> *points = (QList<MapData::Point*>*)context;
points->append(point);
QList<MapData::Point> *points = (QList<MapData::Point>*)context;
points->append(*point);
return true;
}
static bool lineCb(MapData::Line *line, void *context)
static bool lineCb(const MapData::Line *line, void *context)
{
QList<MapData::Line*> *lines = (QList<MapData::Line*>*)context;
lines->append(line);
QList<MapData::Line> *lines = (QList<MapData::Line>*)context;
lines->append(*line);
return true;
}
static bool polygonCb(MapData::Poly *polygon, void *context)
static bool polygonCb(const MapData::Poly *polygon, void *context)
{
QList<MapData::Poly*> *polygons = (QList<MapData::Poly*>*)context;
polygons->append(polygon);
QList<MapData::Poly> *polygons = (QList<MapData::Poly>*)context;
polygons->append(*polygon);
return true;
}
@ -306,7 +306,7 @@ QVector<MapData::Sounding> MapData::soundings(const ISO8211::Record &r,
uint COMF, uint SOMF)
{
QVector<Sounding> s;
const ISO8211::Field *f = r.field("SG3D");
const ISO8211::Field *f = ISO8211::field(r, "SG3D");
if (!f)
return QVector<Sounding>();
@ -328,7 +328,7 @@ QVector<MapData::Sounding> MapData::soundingGeometry(const ISO8211::Record &r,
quint32 id;
RecordMapIterator it;
const ISO8211::Field *FSPT = r.field("FSPT");
const ISO8211::Field *FSPT = ISO8211::field(r, "FSPT");
if (!FSPT || FSPT->data().at(0).size() != 4)
return QVector<Sounding>();
@ -356,7 +356,7 @@ Coordinates MapData::pointGeometry(const ISO8211::Record &r,
quint32 id;
RecordMapIterator it;
const ISO8211::Field *FSPT = r.field("FSPT");
const ISO8211::Field *FSPT = ISO8211::field(r, "FSPT");
if (!FSPT || FSPT->data().at(0).size() != 4)
return Coordinates();
@ -386,7 +386,7 @@ QVector<Coordinates> MapData::lineGeometry(const ISO8211::Record &r,
quint8 type;
quint32 id;
const ISO8211::Field *FSPT = r.field("FSPT");
const ISO8211::Field *FSPT = ISO8211::field(r, "FSPT");
if (!FSPT || FSPT->data().at(0).size() != 4)
return QVector<Coordinates>();
@ -399,7 +399,7 @@ QVector<Coordinates> MapData::lineGeometry(const ISO8211::Record &r,
if (it == ve.constEnd())
return QVector<Coordinates>();
const ISO8211::Record &FRID = it.value();
const ISO8211::Field *VRPT = FRID.field("VRPT");
const ISO8211::Field *VRPT = ISO8211::field(FRID, "VRPT");
if (!VRPT || VRPT->data().size() != 2)
return QVector<Coordinates>();
@ -452,7 +452,7 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc,
quint8 type;
quint32 id;
const ISO8211::Field *FSPT = r.field("FSPT");
const ISO8211::Field *FSPT = ISO8211::field(r, "FSPT");
if (!FSPT || FSPT->data().at(0).size() != 4)
return Polygon();
@ -471,7 +471,7 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc,
if (it == ve.constEnd())
return Polygon();
const ISO8211::Record &FRID = it.value();
const ISO8211::Field *VRPT = FRID.field("VRPT");
const ISO8211::Field *VRPT = ISO8211::field(FRID, "VRPT");
if (!VRPT || VRPT->data().size() != 2)
return Polygon();
@ -490,6 +490,8 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc,
const ISO8211::Field *vertexes = SGXD(FRID);
if (ORNT == 2) {
v.append(c[1]);
if (USAG == 3)
v.append(Coordinates());
if (vertexes) {
for (int j = vertexes->data().size() - 1; j >= 0; j--) {
const QVector<QVariant> &cv = vertexes->data().at(j);
@ -497,9 +499,13 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc,
COMF));
}
}
if (USAG == 3)
v.append(Coordinates());
v.append(c[0]);
} else {
v.append(c[0]);
if (USAG == 3)
v.append(Coordinates());
if (vertexes) {
for (int j = 0; j < vertexes->data().size(); j++) {
const QVector<QVariant> &cv = vertexes->data().at(j);
@ -507,6 +513,8 @@ Polygon MapData::polyGeometry(const ISO8211::Record &r, const RecordMap &vc,
COMF));
}
}
if (USAG == 3)
v.append(Coordinates());
v.append(c[1]);
}
@ -528,7 +536,7 @@ MapData::Attr MapData::pointAttr(const ISO8211::Record &r, uint OBJL)
QVector<QByteArray> params(2);
uint subtype = 0;
const ISO8211::Field *ATTF = r.field("ATTF");
const ISO8211::Field *ATTF = ISO8211::field(r, "ATTF");
if (!(ATTF && ATTF->data().at(0).size() == 2))
return Attr();
@ -580,7 +588,7 @@ MapData::Attr MapData::lineAttr(const ISO8211::Record &r, uint OBJL)
QVector<QByteArray> params(1);
uint subtype = 0;
const ISO8211::Field *ATTF = r.field("ATTF");
const ISO8211::Field *ATTF = ISO8211::field(r, "ATTF");
if (!(ATTF && ATTF->data().at(0).size() == 2))
return Attr();
@ -608,7 +616,7 @@ MapData::Attr MapData::polyAttr(const ISO8211::Record &r, uint OBJL)
QVector<QByteArray> params(1);
uint subtype = 0;
const ISO8211::Field *ATTF = r.field("ATTF");
const ISO8211::Field *ATTF = ISO8211::field(r, "ATTF");
if (!(ATTF && ATTF->data().at(0).size() == 2))
return Attr();
@ -676,30 +684,6 @@ MapData::Poly *MapData::polyObject(const ISO8211::Record &r,
attr.params()));
}
bool MapData::processRecord(const ISO8211::Record &record,
QVector<ISO8211::Record> &rv, uint &COMF, QString &name)
{
if (record.size() < 2)
return false;
const ISO8211::Field &f = record.at(1);
const QByteArray &ba = f.tag();
if (ba == "VRID") {
rv.append(record);
} else if (ba == "DSID") {
QByteArray DSNM;
if (!f.subfield("DSNM", &DSNM))
return false;
name = DSNM;
} else if (ba == "DSPM") {
if (!f.subfield("COMF", &COMF))
return false;
}
return true;
}
bool MapData::processRecord(const ISO8211::Record &record,
QVector<ISO8211::Record> &fe, RecordMap &vi, RecordMap &vc, RecordMap &ve,
RecordMap &vf, uint &COMF, uint &SOMF)
@ -742,92 +726,11 @@ bool MapData::processRecord(const ISO8211::Record &record,
return true;
}
bool MapData::bounds(const ISO8211::Record &record, Rect &rect)
{
bool xok, yok;
// edge geometries can be empty!
const ISO8211::Field *f = SGXD(record);
if (!f)
return true;
for (int i = 0; i < f->data().size(); i++) {
const QVector<QVariant> &c = f->data().at(i);
rect.unite(c.at(1).toInt(&xok), c.at(0).toInt(&yok));
if (!(xok && yok))
return false;
}
return true;
}
bool MapData::bounds(const QVector<ISO8211::Record> &gv, Rect &b)
{
Rect r;
for (int i = 0; i < gv.size(); i++) {
if (!bounds(gv.at(i), r))
return false;
b |= r;
}
return true;
}
MapData::MapData(const QString &path): _fileName(path)
{
QVector<ISO8211::Record> gv;
ISO8211 ddf(_fileName);
ISO8211::Record record;
uint COMF = 1;
if (!ddf.readDDR()) {
_errorString = ddf.errorString();
return;
}
while (ddf.readRecord(record)) {
if (!processRecord(record, gv, COMF, _name)) {
_errorString = "Invalid S-57 record";
return;
}
}
if (!ddf.errorString().isNull()) {
_errorString = ddf.errorString();
return;
}
Rect b;
if (!bounds(gv, b)) {
_errorString = "Error fetching geometries bounds";
return;
}
RectC br(Coordinates(b.minX() / (double)COMF, b.maxY() / (double)COMF),
Coordinates(b.maxX() / (double)COMF, b.minY() / (double)COMF));
if (!br.isValid())
_errorString = "Invalid geometries bounds";
else
_bounds = br;
}
MapData::~MapData()
{
LineTree::Iterator lit;
for (_lines.GetFirst(lit); !_lines.IsNull(lit); _lines.GetNext(lit))
delete _lines.GetAt(lit);
PolygonTree::Iterator ait;
for (_areas.GetFirst(ait); !_areas.IsNull(ait); _areas.GetNext(ait))
delete _areas.GetAt(ait);
PointTree::Iterator pit;
for (_points.GetFirst(pit); !_points.IsNull(pit); _points.GetNext(pit))
delete _points.GetAt(pit);
}
void MapData::load()
MapData::MapData(const QString &path)
{
RecordMap vi, vc, ve, vf;
QVector<ISO8211::Record> fe;
ISO8211 ddf(_fileName);
ISO8211 ddf(path);
ISO8211::Record record;
uint PRIM, OBJL, COMF = 1, SOMF = 1;
Poly *poly;
@ -886,25 +789,22 @@ void MapData::load()
}
}
void MapData::clear()
MapData::~MapData()
{
LineTree::Iterator lit;
for (_lines.GetFirst(lit); !_lines.IsNull(lit); _lines.GetNext(lit))
delete _lines.GetAt(lit);
_lines.RemoveAll();
PolygonTree::Iterator ait;
for (_areas.GetFirst(ait); !_areas.IsNull(ait); _areas.GetNext(ait))
delete _areas.GetAt(ait);
_areas.RemoveAll();
PointTree::Iterator pit;
for (_points.GetFirst(pit); !_points.IsNull(pit); _points.GetNext(pit))
delete _points.GetAt(pit);
_points.RemoveAll();
}
void MapData::points(const RectC &rect, QList<Point*> *points) const
void MapData::points(const RectC &rect, QList<Point> *points) const
{
double min[2], max[2];
@ -912,7 +812,7 @@ void MapData::points(const RectC &rect, QList<Point*> *points) const
_points.Search(min, max, pointCb, points);
}
void MapData::lines(const RectC &rect, QList<Line*> *lines) const
void MapData::lines(const RectC &rect, QList<Line> *lines) const
{
double min[2], max[2];
@ -920,48 +820,10 @@ void MapData::lines(const RectC &rect, QList<Line*> *lines) const
_lines.Search(min, max, lineCb, lines);
}
void MapData::polygons(const RectC &rect, QList<Poly*> *polygons) const
void MapData::polygons(const RectC &rect, QList<Poly> *polygons) const
{
double min[2], max[2];
rectcBounds(rect, min, max);
_areas.Search(min, max, polygonCb, polygons);
}
Range MapData::zooms() const
{
double size = qMin(_bounds.width(), _bounds.height());
if (size > 180)
return Range(0, 10);
else if (size > 90)
return Range(1, 11);
else if (size > 45)
return Range(2, 12);
else if (size > 22.5)
return Range(3, 13);
else if (size > 11.25)
return Range(4, 14);
else if (size > 5.625)
return Range(5, 15);
else if (size > 2.813)
return Range(6, 16);
else if (size > 1.406)
return Range(7, 17);
else if (size > 0.703)
return Range(8, 18);
else if (size > 0.352)
return Range(9, 19);
else if (size > 0.176)
return Range(10, 20);
else if (size > 0.088)
return Range(11, 20);
else if (size > 0.044)
return Range(12, 20);
else if (size > 0.022)
return Range(13, 20);
else if (size > 0.011)
return Range(14, 20);
else
return Range(15, 20);
}

View File

@ -1,11 +1,9 @@
#ifndef ENC_MAPDATA_H
#define ENC_MAPDATA_H
#include <climits>
#include "common/rectc.h"
#include "common/rtree.h"
#include "common/polygon.h"
#include "common/range.h"
#include "iso8211.h"
namespace ENC {
@ -68,55 +66,11 @@ public:
MapData(const QString &path);
~MapData();
const QString &name() const {return _name;}
RectC bounds() const {return _bounds;}
Range zooms() const;
void polygons(const RectC &rect, QList<Poly*> *polygons) const;
void lines(const RectC &rect, QList<Line*> *lines) const;
void points(const RectC &rect, QList<Point*> *points) const;
void load();
void clear();
bool isValid() const {return _bounds.isValid();}
QString errorString() const {return _errorString;}
void polygons(const RectC &rect, QList<Poly> *polygons) const;
void lines(const RectC &rect, QList<Line> *lines) const;
void points(const RectC &rect, QList<Point> *points) const;
private:
class Rect {
public:
Rect()
: _minX(INT_MAX), _maxX(INT_MIN), _minY(INT_MAX), _maxY(INT_MIN) {}
Rect(int minX, int maxX, int minY, int maxY)
: _minX(minX), _maxX(maxX), _minY(minY), _maxY(maxY) {}
int minX() const {return _minX;}
int maxX() const {return _maxX;}
int minY() const {return _minY;}
int maxY() const {return _maxY;}
void unite(int x, int y) {
if (x < _minX)
_minX = x;
if (x > _maxX)
_maxX = x;
if (y < _minY)
_minY = y;
if (y > _maxY)
_maxY = y;
}
Rect &operator|=(const Rect &r) {*this = *this | r; return *this;}
Rect operator|(const Rect &r) const
{
return Rect(qMin(_minX, r._minX), qMax(_maxX, r._maxX),
qMin(_minY, r._minY), qMax(_maxY, r._maxY));
}
private:
int _minX, _maxX, _minY, _maxY;
};
class Attr {
public:
Attr() : _subtype(0) {}
@ -144,9 +98,9 @@ private:
typedef QMap<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;
typedef RTree<const Poly*, double, 2> PolygonTree;
typedef RTree<const Line*, double, 2> LineTree;
typedef RTree<const Point*, double, 2> PointTree;
static QVector<Sounding> soundings(const ISO8211::Record &r, uint COMF,
uint SOMF);
@ -168,21 +122,14 @@ private:
const RecordMap &ve, uint COMF, uint OBJL);
static Poly *polyObject(const ISO8211::Record &r, const RecordMap &vc,
const RecordMap &ve, uint COMF,uint OBJL);
static bool bounds(const ISO8211::Record &record, Rect &rect);
static bool bounds(const QVector<ISO8211::Record> &gv, Rect &b);
static bool processRecord(const ISO8211::Record &record,
QVector<ISO8211::Record> &fe, RecordMap &vi, RecordMap &vc, RecordMap &ve,
RecordMap &vf, uint &COMF, uint &SOMF);
static bool processRecord(const ISO8211::Record &record,
QVector<ISO8211::Record> &rv, uint &COMF, QString &name);
QString _fileName;
QString _name;
RectC _bounds;
PolygonTree _areas;
LineTree _lines;
PointTree _points;
QString _errorString;
};
}

View File

@ -12,16 +12,11 @@ using namespace ENC;
#define TEXT_EXTENT 160
#define TSSLPT_SIZE 0.005 /* ll */
typedef QMap<Coordinates, const MapData::Point*> PointMap;
typedef QSet<Coordinates> PointSet;
static const float C1 = 0.866025f; /* sqrt(3)/2 */
static const QColor haloColor(Qt::white);
static struct {
bool operator()(MapData::Point* a, MapData::Point* b) const
{return *a < *b;}
} pointLess;
static QFont pixelSizeFont(int pixelSize)
{
QFont f;
@ -114,7 +109,9 @@ static bool showLabel(const QImage *img, const Range &range, int zoom, int type)
if (type>>16 == I_DISMAR)
return true;
if ((img || type>>16 == SOUNDG) && zoom < range.mid())
int limit = (!range.size())
? range.min() : range.min() + (range.size() + 1) / 2;
if ((img || (type>>16 == SOUNDG)) && (zoom < limit))
return false;
return true;
@ -127,9 +124,14 @@ QPainterPath RasterTile::painterPath(const Polygon &polygon) const
for (int i = 0; i < polygon.size(); i++) {
const QVector<Coordinates> &subpath = polygon.at(i);
QVector<QPointF> p(subpath.size());
for (int j = 0; j < subpath.size(); j++)
p[j] = ll2xy(subpath.at(j));
QVector<QPointF> p;
p.reserve(subpath.size());
for (int j = 0; j < subpath.size(); j++) {
const Coordinates &c = subpath.at(j);
if (!c.isNull())
p.append(ll2xy(c));
}
path.addPolygon(p);
}
@ -147,6 +149,35 @@ QPolygonF RasterTile::polyline(const QVector<Coordinates> &path) const
return polygon;
}
QVector<QPolygonF> RasterTile::polylineM(const QVector<Coordinates> &path) const
{
QVector<QPolygonF> polys;
QPolygonF polygon;
bool mask = false;
polygon.reserve(path.size());
for (int i = 0; i < path.size(); i++) {
const Coordinates &c = path.at(i);
if (c.isNull()) {
if (mask)
mask = false;
else {
polys.append(polygon);
polygon.clear();
mask = true;
}
} else if (!mask)
polygon.append(ll2xy(c));
}
if (!polygon.isEmpty())
polys.append(polygon);
return polys;
}
QPolygonF RasterTile::tsslptArrow(const Coordinates &c, qreal angle) const
{
Coordinates t[3], r[4];
@ -173,14 +204,14 @@ QPolygonF RasterTile::tsslptArrow(const Coordinates &c, qreal angle) const
}
void RasterTile::drawArrows(QPainter *painter,
const QList<MapData::Poly*> &polygons)
const QList<MapData::Poly> &polygons)
{
for (int i = 0; i < polygons.size(); i++) {
const MapData::Poly *poly = polygons.at(i);
const MapData::Poly &poly = polygons.at(i);
if (poly->type()>>16 == TSSLPT) {
QPolygonF polygon(tsslptArrow(centroid(poly->path().first()),
deg2rad(180 - poly->param().toDouble())));
if (poly.type()>>16 == TSSLPT) {
QPolygonF polygon(tsslptArrow(centroid(poly.path().first()),
deg2rad(180 - poly.param().toDouble())));
painter->setPen(QPen(QColor("#eb49eb"), 1));
painter->setBrush(QBrush("#80eb49eb"));
@ -190,45 +221,55 @@ void RasterTile::drawArrows(QPainter *painter,
}
void RasterTile::drawPolygons(QPainter *painter,
const QList<MapData::Poly*> &polygons)
const QList<MapData::Poly> &polygons)
{
const Style &s = style();
for (int n = 0; n < s.drawOrder().size(); n++) {
for (int i = 0; i < polygons.size(); i++) {
const MapData::Poly *poly = polygons.at(i);
if (poly->type() != s.drawOrder().at(n))
const MapData::Poly &poly = polygons.at(i);
if (poly.type() != s.drawOrder().at(n))
continue;
const Style::Polygon &style = s.polygon(poly->type());
const Style::Polygon &style = s.polygon(poly.type());
if (!style.img().isNull()) {
for (int i = 0; i < poly->path().size(); i++)
BitmapLine::draw(painter, polyline(poly->path().at(i)),
for (int i = 0; i < poly.path().size(); i++)
BitmapLine::draw(painter, polylineM(poly.path().at(i)),
style.img());
} else {
painter->setPen(style.pen());
painter->setBrush(style.brush());
painter->drawPath(painterPath(poly->path()));
if (style.brush() != Qt::NoBrush) {
painter->setPen(Qt::NoPen);
painter->setBrush(style.brush());
painter->drawPath(painterPath(poly.path()));
}
if (style.pen() != Qt::NoPen) {
painter->setPen(style.pen());
for (int i = 0; i < poly.path().size(); i++) {
QVector<QPolygonF> outline(polylineM(poly.path().at(i)));
for (int j = 0; j < outline.size(); j++)
painter->drawPolyline(outline.at(j));
}
}
}
}
}
}
void RasterTile::drawLines(QPainter *painter, const QList<MapData::Line*> &lines)
void RasterTile::drawLines(QPainter *painter, const QList<MapData::Line> &lines)
{
const Style &s = style();
painter->setBrush(Qt::NoBrush);
for (int i = 0; i < lines.size(); i++) {
const MapData::Line *line = lines.at(i);
const Style::Line &style = s.line(line->type());
const MapData::Line &line = lines.at(i);
const Style::Line &style = s.line(line.type());
if (!style.img().isNull()) {
BitmapLine::draw(painter, polyline(line->path()), style.img());
BitmapLine::draw(painter, polyline(line.path()), style.img());
} else if (style.pen() != Qt::NoPen) {
painter->setPen(style.pen());
painter->drawPolyline(polyline(line->path()));
painter->drawPolyline(polyline(line.path()));
}
}
}
@ -240,25 +281,25 @@ void RasterTile::drawTextItems(QPainter *painter,
textItems.at(i)->paint(painter);
}
void RasterTile::processPolygons(const QList<MapData::Poly*> &polygons,
void RasterTile::processPolygons(const QList<MapData::Poly> &polygons,
QList<TextItem*> &textItems)
{
const Style &s = style();
for (int i = 0; i < polygons.size(); i++) {
const MapData::Poly *poly = polygons.at(i);
uint type = poly->type()>>16;
const MapData::Poly &poly = polygons.at(i);
uint type = poly.type()>>16;
if (!(type == HRBFAC || type == I_TRNBSN
|| poly->type() == SUBTYPE(I_BERTHS, 6)))
|| poly.type() == SUBTYPE(I_BERTHS, 6)))
continue;
const Style::Point &style = s.point(poly->type());
const Style::Point &style = s.point(poly.type());
const QImage *img = style.img().isNull() ? 0 : &style.img();
if (!img)
continue;
TextPointItem *item = new TextPointItem(
ll2xy(centroid(poly->path().first())).toPoint(),
ll2xy(centroid(poly.path().first())).toPoint(),
0, 0, img, 0, 0, 0, 0);
if (item->isValid() && !item->collides(textItems))
textItems.append(item);
@ -267,40 +308,40 @@ void RasterTile::processPolygons(const QList<MapData::Poly*> &polygons,
}
}
void RasterTile::processPoints(QList<MapData::Point*> &points,
void RasterTile::processPoints(QList<MapData::Point> &points,
QList<TextItem*> &textItems, QList<TextItem*> &lights)
{
const Style &s = style();
PointMap lightsMap, signalsMap;
PointSet lightsSet, signalsSet;
int i;
std::sort(points.begin(), points.end(), pointLess);
std::sort(points.begin(), points.end());
/* Lights & Signals */
for (i = 0; i < points.size(); i++) {
const MapData::Point *point = points.at(i);
if (point->type()>>16 == LIGHTS)
lightsMap.insert(point->pos(), point);
else if (point->type()>>16 == FOGSIG)
signalsMap.insert(point->pos(), point);
const MapData::Point &point = points.at(i);
if (point.type()>>16 == LIGHTS)
lightsSet.insert(point.pos());
else if (point.type()>>16 == FOGSIG)
signalsSet.insert(point.pos());
else
break;
}
/* Everything else */
for ( ; i < points.size(); i++) {
const MapData::Point *point = points.at(i);
QPoint pos(ll2xy(point->pos()).toPoint());
const Style::Point &style = s.point(point->type());
const MapData::Point &point = points.at(i);
QPoint pos(ll2xy(point.pos()).toPoint());
const Style::Point &style = s.point(point.type());
const QString *label = point->label().isEmpty() ? 0 : &(point->label());
const QString *label = point.label().isEmpty() ? 0 : &(point.label());
const QImage *img = style.img().isNull() ? 0 : &style.img();
const QFont *fnt = showLabel(img, _data->zooms(), _zoom, point->type())
const QFont *fnt = showLabel(img, _zoomRange, _zoom, point.type())
? font(style.textFontSize()) : 0;
const QColor *color = &style.textColor();
const QColor *hColor = style.haloColor().isValid()
? &style.haloColor() : 0;
double rotate = angle(point->type(), point->param());
double rotate = angle(point.type(), point.param());
if ((!label || !fnt) && !img)
continue;
@ -309,34 +350,34 @@ void RasterTile::processPoints(QList<MapData::Point*> &points,
hColor, 0, 2, rotate);
if (item->isValid() && !item->collides(textItems)) {
textItems.append(item);
if (lightsMap.contains(point->pos()))
if (lightsSet.contains(point.pos()))
lights.append(new TextPointItem(pos, 0, 0, light(), 0, 0, 0, 0));
if (signalsMap.contains(point->pos()))
if (signalsSet.contains(point.pos()))
lights.append(new TextPointItem(pos, 0, 0, signal(), 0, 0, 0, 0));
} else
delete item;
}
}
void RasterTile::processLines(const QList<MapData::Line*> &lines,
void RasterTile::processLines(const QList<MapData::Line> &lines,
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());
const MapData::Line &line = lines.at(i);
const Style::Line &style = s.line(line.type());
if (style.img().isNull() && style.pen() == Qt::NoPen)
continue;
if (line->label().isEmpty() || style.textFontSize() == Style::None)
if (line.label().isEmpty() || style.textFontSize() == Style::None)
continue;
const QFont *fnt = font(style.textFontSize());
const QColor *color = &style.textColor();
TextPathItem *item = new TextPathItem(polyline(line->path()),
&line->label(), _rect, fnt, color, 0);
TextPathItem *item = new TextPathItem(polyline(line.path()),
&line.label(), _rect, fnt, color, 0);
if (item->isValid() && !item->collides(textItems))
textItems.append(item);
else
@ -344,8 +385,8 @@ void RasterTile::processLines(const QList<MapData::Line*> &lines,
}
}
void RasterTile::fetchData(QList<MapData::Poly*> &polygons,
QList<MapData::Line*> &lines, QList<MapData::Point*> &points)
void RasterTile::fetchData(QList<MapData::Poly> &polygons,
QList<MapData::Line> &lines, QList<MapData::Point> &points)
{
QPoint ttl(_rect.topLeft());
@ -354,22 +395,28 @@ void RasterTile::fetchData(QList<MapData::Poly*> &polygons,
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
_transform.img2proj(polyRect.bottomRight()));
RectC polyRectC(polyRectD.toRectC(_proj, 20));
_data->lines(polyRectC, &lines);
_data->polygons(polyRectC, &polygons);
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT, ttl.y() - TEXT_EXTENT),
QPointF(ttl.x() + _rect.width() + TEXT_EXTENT, ttl.y() + _rect.height()
+ TEXT_EXTENT));
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
_transform.img2proj(pointRect.bottomRight()));
_data->points(pointRectD.toRectC(_proj, 20), &points);
RectC pointRectC(pointRectD.toRectC(_proj, 20));
if (_map) {
_map->lines(polyRectC, &lines);
_map->polygons(polyRectC, &polygons);
_map->points(pointRectC, &points);
} else {
_atlas->polys(polyRectC, &polygons, &lines);
_atlas->points(pointRectC, &points);
}
}
void RasterTile::render()
{
QList<MapData::Line*> lines;
QList<MapData::Poly*> polygons;
QList<MapData::Point*> points;
QList<MapData::Line> lines;
QList<MapData::Poly> polygons;
QList<MapData::Point> points;
QList<TextItem*> textItems, lights;
_pixmap.setDevicePixelRatio(_ratio);

View File

@ -2,10 +2,12 @@
#define ENC_RASTERTILE_H
#include <QPixmap>
#include "common/range.h"
#include "map/projection.h"
#include "map/transform.h"
#include "map/textpointitem.h"
#include "mapdata.h"
#include "atlasdata.h"
class TextItem;
@ -15,9 +17,14 @@ class RasterTile
{
public:
RasterTile(const Projection &proj, const Transform &transform,
const MapData *data, int zoom, const QRect &rect, qreal ratio)
: _proj(proj), _transform(transform), _data(data), _zoom(zoom),
_rect(rect), _ratio(ratio),
const MapData *data, int zoom, const Range &zoomRange, const QRect &rect,
qreal ratio) : _proj(proj), _transform(transform), _map(data), _atlas(0),
_zoom(zoom), _zoomRange(zoomRange), _rect(rect), _ratio(ratio),
_pixmap(rect.width() * ratio, rect.height() * ratio), _valid(false) {}
RasterTile(const Projection &proj, const Transform &transform,
AtlasData *data, int zoom, const Range &zoomRange, const QRect &rect,
qreal ratio) : _proj(proj), _transform(transform), _map(0), _atlas(data),
_zoom(zoom), _zoomRange(zoomRange), _rect(rect), _ratio(ratio),
_pixmap(rect.width() * ratio, rect.height() * ratio), _valid(false) {}
int zoom() const {return _zoom;}
@ -28,30 +35,36 @@ public:
void render();
private:
void fetchData(QList<MapData::Poly*> &polygons, QList<MapData::Line*> &lines,
QList<MapData::Point*> &points);
void fetchData(QList<MapData::Poly> &polygons, QList<MapData::Line> &lines,
QList<MapData::Point> &points);
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;
QVector<QPolygonF> polylineM(const QVector<Coordinates> &path) const;
QPolygonF tsslptArrow(const Coordinates &c, qreal angle) const;
void processPoints(QList<MapData::Point *> &points,
void processPoints(QList<MapData::Point> &points,
QList<TextItem*> &textItems, QList<TextItem *> &lights);
void processLines(const QList<MapData::Line *> &lines,
void processLines(const QList<MapData::Line> &lines,
QList<TextItem*> &textItems);
void processPolygons(const QList<MapData::Poly *> &polygons,
void processPolygons(const QList<MapData::Poly> &polygons,
QList<TextItem*> &textItems);
void drawBitmapPath(QPainter *painter, const QImage &img,
const Polygon &polygon);
void drawArrows(QPainter *painter, const QList<MapData::Poly*> &polygons);
void drawPolygons(QPainter *painter, const QList<MapData::Poly *> &polygons);
void drawLines(QPainter *painter, const QList<MapData::Line *> &lines);
void drawArrows(QPainter *painter, const QList<MapData::Poly> &polygons);
void drawPolygons(QPainter *painter, const QList<MapData::Poly> &polygons);
void drawLines(QPainter *painter, const QList<MapData::Line> &lines);
void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems);
static bool polyCb(MapData *data, void *context);
static bool pointCb(MapData *data, void *context);
Projection _proj;
Transform _transform;
const MapData *_data;
const MapData *_map;
AtlasData *_atlas;
int _zoom;
Range _zoomRange;
QRect _rect;
qreal _ratio;
QPixmap _pixmap;