mirror of
https://github.com/tumic0/GPXSee.git
synced 2024-11-27 21:24:47 +01:00
Added support for Garmin IMG maps
This commit is contained in:
parent
82923a4529
commit
0594774570
30
gpxsee.pro
30
gpxsee.pro
@ -84,6 +84,9 @@ HEADERS += src/common/config.h \
|
|||||||
src/GUI/mapview.h \
|
src/GUI/mapview.h \
|
||||||
src/GUI/font.h \
|
src/GUI/font.h \
|
||||||
src/GUI/areaitem.h \
|
src/GUI/areaitem.h \
|
||||||
|
src/map/IMG/bitmapline.h \
|
||||||
|
src/map/IMG/textpathitem.h \
|
||||||
|
src/map/IMG/textpointitem.h \
|
||||||
src/map/projection.h \
|
src/map/projection.h \
|
||||||
src/map/ellipsoid.h \
|
src/map/ellipsoid.h \
|
||||||
src/map/datum.h \
|
src/map/datum.h \
|
||||||
@ -163,7 +166,18 @@ HEADERS += src/common/config.h \
|
|||||||
src/map/calibrationpoint.h \
|
src/map/calibrationpoint.h \
|
||||||
src/map/color.h \
|
src/map/color.h \
|
||||||
src/data/exifparser.h \
|
src/data/exifparser.h \
|
||||||
src/data/imageinfo.h
|
src/data/imageinfo.h \
|
||||||
|
src/map/imgmap.h \
|
||||||
|
src/map/IMG/img.h \
|
||||||
|
src/map/IMG/subfile.h \
|
||||||
|
src/map/IMG/trefile.h \
|
||||||
|
src/map/IMG/rgnfile.h \
|
||||||
|
src/map/IMG/lblfile.h \
|
||||||
|
src/map/IMG/vectortile.h \
|
||||||
|
src/map/IMG/subdiv.h \
|
||||||
|
src/map/IMG/units.h \
|
||||||
|
src/map/IMG/style.h \
|
||||||
|
src/map/IMG/netfile.h
|
||||||
SOURCES += src/main.cpp \
|
SOURCES += src/main.cpp \
|
||||||
src/common/coordinates.cpp \
|
src/common/coordinates.cpp \
|
||||||
src/common/rectc.cpp \
|
src/common/rectc.cpp \
|
||||||
@ -215,6 +229,9 @@ SOURCES += src/main.cpp \
|
|||||||
src/GUI/gearratiographitem.cpp \
|
src/GUI/gearratiographitem.cpp \
|
||||||
src/GUI/mapview.cpp \
|
src/GUI/mapview.cpp \
|
||||||
src/GUI/areaitem.cpp \
|
src/GUI/areaitem.cpp \
|
||||||
|
src/map/IMG/bitmapline.cpp \
|
||||||
|
src/map/IMG/textpathitem.cpp \
|
||||||
|
src/map/IMG/textpointitem.cpp \
|
||||||
src/map/maplist.cpp \
|
src/map/maplist.cpp \
|
||||||
src/map/onlinemap.cpp \
|
src/map/onlinemap.cpp \
|
||||||
src/map/downloader.cpp \
|
src/map/downloader.cpp \
|
||||||
@ -280,7 +297,16 @@ SOURCES += src/main.cpp \
|
|||||||
src/map/obliquestereographic.cpp \
|
src/map/obliquestereographic.cpp \
|
||||||
src/GUI/coordinatesitem.cpp \
|
src/GUI/coordinatesitem.cpp \
|
||||||
src/map/rmap.cpp \
|
src/map/rmap.cpp \
|
||||||
src/data/exifparser.cpp
|
src/data/exifparser.cpp \
|
||||||
|
src/map/imgmap.cpp \
|
||||||
|
src/map/IMG/img.cpp \
|
||||||
|
src/map/IMG/subfile.cpp \
|
||||||
|
src/map/IMG/trefile.cpp \
|
||||||
|
src/map/IMG/rgnfile.cpp \
|
||||||
|
src/map/IMG/lblfile.cpp \
|
||||||
|
src/map/IMG/vectortile.cpp \
|
||||||
|
src/map/IMG/style.cpp \
|
||||||
|
src/map/IMG/netfile.cpp
|
||||||
|
|
||||||
greaterThan(QT_MAJOR_VERSION, 4) {
|
greaterThan(QT_MAJOR_VERSION, 4) {
|
||||||
HEADERS += src/data/geojsonparser.h
|
HEADERS += src/data/geojsonparser.h
|
||||||
|
@ -28,6 +28,11 @@ public:
|
|||||||
double left() const {return _tl.lon();}
|
double left() const {return _tl.lon();}
|
||||||
double right() const {return _br.lon();}
|
double right() const {return _br.lon();}
|
||||||
|
|
||||||
|
void setLeft(double val) {_tl.rlon() = val;}
|
||||||
|
void setRight(double val) {_br.rlon() = val;}
|
||||||
|
void setTop(double val) {_tl.rlat() = val;}
|
||||||
|
void setBottom(double val) {_br.rlat() = val;}
|
||||||
|
|
||||||
RectC operator|(const RectC &r) const;
|
RectC operator|(const RectC &r) const;
|
||||||
RectC &operator|=(const RectC &r) {*this = *this | r; return *this;}
|
RectC &operator|=(const RectC &r) {*this = *this | r; return *this;}
|
||||||
RectC operator&(const RectC &r) const;
|
RectC operator&(const RectC &r) const;
|
||||||
@ -35,6 +40,13 @@ public:
|
|||||||
|
|
||||||
RectC united(const Coordinates &c) const;
|
RectC united(const Coordinates &c) const;
|
||||||
|
|
||||||
|
bool intersects(const RectC &r) const
|
||||||
|
{return (right() >= r.left() && bottom() <= r.top() && left() <= r.right()
|
||||||
|
&& top() >= r.bottom());}
|
||||||
|
bool contains(const Coordinates&c) const
|
||||||
|
{return (c.lon() >= left() && c.lon() <= right() && c.lat() <= top()
|
||||||
|
&& c.lat() >= bottom());}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Coordinates _tl, _br;
|
Coordinates _tl, _br;
|
||||||
};
|
};
|
||||||
|
39
src/map/IMG/bitmapline.cpp
Normal file
39
src/map/IMG/bitmapline.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include <QPainter>
|
||||||
|
#include <QImage>
|
||||||
|
#include "bitmapline.h"
|
||||||
|
|
||||||
|
static QImage img2line(const QImage &img, int width)
|
||||||
|
{
|
||||||
|
Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied);
|
||||||
|
QImage res(width, img.height(), QImage::Format_ARGB32_Premultiplied);
|
||||||
|
const int srcBpl = img.bytesPerLine();
|
||||||
|
const int dstBpl = res.bytesPerLine();
|
||||||
|
const uchar *srcBits = img.bits();
|
||||||
|
uchar *dstBits = res.bits();
|
||||||
|
|
||||||
|
for (int i = 0; i < img.height(); i++) {
|
||||||
|
const uchar *srcLine = srcBits + srcBpl * i;
|
||||||
|
uchar *dstLine = dstBits + dstBpl * i;
|
||||||
|
|
||||||
|
for (int j = dstBpl; j > 0; j -= srcBpl, dstLine += srcBpl)
|
||||||
|
memcpy(dstLine, srcLine, qMin(j, srcBpl));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BitmapLine::draw(QPainter *painter, const QPolygonF &line,
|
||||||
|
const QImage &img)
|
||||||
|
{
|
||||||
|
for (int i = 1; i < line.size(); i++) {
|
||||||
|
QLineF segment(line.at(i-1).x(), line.at(i-1).y(), line.at(i).x(),
|
||||||
|
line.at(i).y());
|
||||||
|
|
||||||
|
painter->save();
|
||||||
|
painter->translate(segment.p1());
|
||||||
|
painter->rotate(-segment.angle());
|
||||||
|
painter->drawImage(0, -img.height()/2, img2line(img, segment.length()));
|
||||||
|
painter->restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
13
src/map/IMG/bitmapline.h
Normal file
13
src/map/IMG/bitmapline.h
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef BITMAPLINE_H
|
||||||
|
#define BITMAPLINE_H
|
||||||
|
|
||||||
|
class QPainter;
|
||||||
|
class QImage;
|
||||||
|
class QPolygonF;
|
||||||
|
|
||||||
|
namespace BitmapLine
|
||||||
|
{
|
||||||
|
void draw(QPainter *painter, const QPolygonF &line, const QImage &img);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BITMAPLINE_H
|
249
src/map/IMG/img.cpp
Normal file
249
src/map/IMG/img.cpp
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
#include <QSet>
|
||||||
|
#include <QtEndian>
|
||||||
|
#include "vectortile.h"
|
||||||
|
#include "img.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define CHECK(condition) \
|
||||||
|
if (!(condition)) { \
|
||||||
|
_errorString = "Invalid/corrupted IMG file"; \
|
||||||
|
return; \
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CTX
|
||||||
|
{
|
||||||
|
CTX(const RectC &rect, int bits, QList<IMG::Poly> *polygons,
|
||||||
|
QList<IMG::Poly> *lines, QList<IMG::Point> *points)
|
||||||
|
: rect(rect), bits(bits), polygons(polygons), lines(lines),
|
||||||
|
points(points) {}
|
||||||
|
|
||||||
|
const RectC ▭
|
||||||
|
int bits;
|
||||||
|
QList<IMG::Poly> *polygons;
|
||||||
|
QList<IMG::Poly> *lines;
|
||||||
|
QList<IMG::Point> *points;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
static QDebug operator<<(QDebug dbg, const QMap<QString, VectorTile*> &map)
|
||||||
|
{
|
||||||
|
dbg.nospace() << "TileMap(";
|
||||||
|
for (QMap<QString, VectorTile*>::const_iterator it = map.constBegin();
|
||||||
|
it != map.constEnd(); ++it)
|
||||||
|
dbg << "(" << it.key() << ", " << *(*it) << ")";
|
||||||
|
dbg << ")";
|
||||||
|
return dbg.space();
|
||||||
|
}
|
||||||
|
#endif // QT_NO_DEBUG
|
||||||
|
|
||||||
|
IMG::IMG(const QString &fileName) : _file(fileName), _valid(false)
|
||||||
|
{
|
||||||
|
if (!_file.open(QFile::ReadOnly)) {
|
||||||
|
_errorString = _file.errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read IMG header
|
||||||
|
char signature[7], identifier[7];
|
||||||
|
_file.read((char*)&_key, 1) && _file.seek(0x10)
|
||||||
|
&& read(signature, sizeof(signature)) && _file.seek(0x41)
|
||||||
|
&& read(identifier, sizeof(identifier));
|
||||||
|
if (memcmp(signature, "DSKIMG", sizeof(signature))
|
||||||
|
|| memcmp(identifier, "GARMIN", sizeof(identifier))) {
|
||||||
|
_errorString = "Not a Garmin IMG file";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char d1[20], d2[31];
|
||||||
|
quint8 e1, e2;
|
||||||
|
CHECK(_file.seek(0x49) && read(d1, sizeof(d1)) && _file.seek(0x61)
|
||||||
|
&& readValue(e1) && readValue(e2) && _file.seek(0x65)
|
||||||
|
&& read(d2, sizeof(d2)));
|
||||||
|
QByteArray nba(QByteArray(d1, sizeof(d1)) + QByteArray(d2, sizeof(d2)));
|
||||||
|
_name = QString(nba).trimmed();
|
||||||
|
_blockSize = 1 << (e1 + e2);
|
||||||
|
|
||||||
|
// Read the FAT table
|
||||||
|
quint8 flag;
|
||||||
|
quint64 offset = 0x200;
|
||||||
|
// Skip unused FAT blocks if any
|
||||||
|
while (true) {
|
||||||
|
CHECK(_file.seek(offset) && readValue(flag));
|
||||||
|
if (flag)
|
||||||
|
break;
|
||||||
|
offset += 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read first FAT block with FAT table size
|
||||||
|
char name[8], type[3];
|
||||||
|
quint32 size;
|
||||||
|
quint16 part;
|
||||||
|
CHECK(_file.seek(offset + 12) && readValue(size));
|
||||||
|
offset += 512;
|
||||||
|
int cnt = (size - offset) / 512;
|
||||||
|
|
||||||
|
|
||||||
|
QMap<QString, VectorTile*> tileMap;
|
||||||
|
QMap<QString, SubFile*> TYPMap;
|
||||||
|
|
||||||
|
// Read FAT blocks describing the IMG sub-files
|
||||||
|
for (int i = 0; i < cnt; i++) {
|
||||||
|
quint16 block;
|
||||||
|
CHECK(_file.seek(offset) && readValue(flag) && read(name, sizeof(name))
|
||||||
|
&& read(type, sizeof(type)) && readValue(size) && readValue(part));
|
||||||
|
SubFile::Type tt = SubFile::type(type);
|
||||||
|
|
||||||
|
if (tt == SubFile::GMP) {
|
||||||
|
_errorString = "NT maps not supported";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString fn(QByteArray(name, sizeof(name)));
|
||||||
|
if (VectorTile::isTileFile(tt)) {
|
||||||
|
VectorTile *tile;
|
||||||
|
QMap<QString, VectorTile*>::iterator it = tileMap.find(fn);
|
||||||
|
if (it == tileMap.end()) {
|
||||||
|
tile = new VectorTile();
|
||||||
|
tileMap.insert(fn, tile);
|
||||||
|
} else
|
||||||
|
tile = *it;
|
||||||
|
|
||||||
|
SubFile *file = part ? tile->file(tt)
|
||||||
|
: tile->addFile(this, tt, size);
|
||||||
|
CHECK(file);
|
||||||
|
|
||||||
|
_file.seek(offset + 0x20);
|
||||||
|
for (int i = 0; i < 240; i++) {
|
||||||
|
CHECK(readValue(block));
|
||||||
|
if (block == 0xFFFF)
|
||||||
|
break;
|
||||||
|
file->addBlock(block);
|
||||||
|
}
|
||||||
|
} else if (tt == SubFile::TYP) {
|
||||||
|
SubFile *typ;
|
||||||
|
QMap<QString, SubFile*>::iterator it = TYPMap.find(fn);
|
||||||
|
if (it == TYPMap.end()) {
|
||||||
|
typ = new SubFile(this, size);
|
||||||
|
TYPMap.insert(fn, typ);
|
||||||
|
} else
|
||||||
|
typ = *it;
|
||||||
|
|
||||||
|
_file.seek(offset + 0x20);
|
||||||
|
for (int i = 0; i < 240; i++) {
|
||||||
|
CHECK(readValue(block));
|
||||||
|
if (block == 0xFFFF)
|
||||||
|
break;
|
||||||
|
typ->addBlock(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create tile tree
|
||||||
|
QSet<int> bits;
|
||||||
|
for (QMap<QString, VectorTile*>::iterator it = tileMap.begin();
|
||||||
|
it != tileMap.end(); ++it) {
|
||||||
|
CHECK((*it)->init());
|
||||||
|
|
||||||
|
double min[2], max[2];
|
||||||
|
min[0] = (*it)->bounds().left();
|
||||||
|
min[1] = (*it)->bounds().bottom();
|
||||||
|
max[0] = (*it)->bounds().right();
|
||||||
|
max[1] = (*it)->bounds().top();
|
||||||
|
_tileTree.Insert(min, max, *it);
|
||||||
|
|
||||||
|
_bounds |= (*it)->bounds();
|
||||||
|
|
||||||
|
for (int i = 0; i < (*it)->bits().count(); i++)
|
||||||
|
bits.insert((*it)->bits().at(i));
|
||||||
|
}
|
||||||
|
_bits = bits.toList();
|
||||||
|
qSort(_bits);
|
||||||
|
|
||||||
|
// Read TYP file if any
|
||||||
|
if (!TYPMap.isEmpty()) {
|
||||||
|
SubFile *typ = TYPMap.values().first();
|
||||||
|
_style = Style(typ);
|
||||||
|
qDeleteAll(TYPMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMG::~IMG()
|
||||||
|
{
|
||||||
|
TileTree::Iterator it;
|
||||||
|
for (_tileTree.GetFirst(it); !_tileTree.IsNull(it); _tileTree.GetNext(it))
|
||||||
|
delete _tileTree.GetAt(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool cb(VectorTile *tile, void *context)
|
||||||
|
{
|
||||||
|
CTX *ctx = (CTX*)context;
|
||||||
|
tile->objects(ctx->rect, ctx->bits, ctx->polygons, ctx->lines, ctx->points);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMG::objects(const RectC &rect, int bits, QList<Poly> *polygons,
|
||||||
|
QList<Poly> *lines, QList<Point> *points) const
|
||||||
|
{
|
||||||
|
int mb = _bits.first();
|
||||||
|
for (int i = 0; i < _bits.size(); i++) {
|
||||||
|
if (_bits.at(i) > bits)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
mb = _bits.at(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTX ctx(rect, mb, polygons, lines, points);
|
||||||
|
double min[2], max[2];
|
||||||
|
|
||||||
|
min[0] = rect.left();
|
||||||
|
min[1] = rect.bottom();
|
||||||
|
max[0] = rect.right();
|
||||||
|
max[1] = rect.top();
|
||||||
|
|
||||||
|
_tileTree.Search(min, max, cb, &ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 IMG::read(char *data, qint64 maxSize)
|
||||||
|
{
|
||||||
|
qint64 ret = _file.read(data, maxSize);
|
||||||
|
if (_key)
|
||||||
|
for (int i = 0; i < ret; i++)
|
||||||
|
data[i] ^= _key;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T> bool IMG::readValue(T &val)
|
||||||
|
{
|
||||||
|
T data;
|
||||||
|
|
||||||
|
if (read((char*)&data, sizeof(T)) < (qint64)sizeof(T))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (sizeof(T) > 1)
|
||||||
|
val = qFromLittleEndian(data);
|
||||||
|
else
|
||||||
|
val = data;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray IMG::readBlock(int blockNum)
|
||||||
|
{
|
||||||
|
QByteArray *block = _blockCache[blockNum];
|
||||||
|
if (!block) {
|
||||||
|
if (!_file.seek((qint64)blockNum * (qint64)_blockSize))
|
||||||
|
return QByteArray();
|
||||||
|
QByteArray *ba = new QByteArray();
|
||||||
|
ba->resize(_blockSize);
|
||||||
|
if (read(ba->data(), _blockSize) < _blockSize) {
|
||||||
|
delete ba;
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
_blockCache.insert(blockNum, ba);
|
||||||
|
return *ba;
|
||||||
|
} else
|
||||||
|
return *block;
|
||||||
|
}
|
88
src/map/IMG/img.h
Normal file
88
src/map/IMG/img.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#ifndef IMG_H
|
||||||
|
#define IMG_H
|
||||||
|
|
||||||
|
#include <QRect>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QCache>
|
||||||
|
#include <QDebug>
|
||||||
|
#include "common/rtree.h"
|
||||||
|
#include "common/rectc.h"
|
||||||
|
#include "common/range.h"
|
||||||
|
#include "style.h"
|
||||||
|
|
||||||
|
class VectorTile;
|
||||||
|
class SubFile;
|
||||||
|
|
||||||
|
class IMG
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Poly {
|
||||||
|
/* QPointF insted of Coordinates for performance reasons (no need to
|
||||||
|
duplicate all the vectors for drawing). Note, that we do not want to
|
||||||
|
ll2xy() the points in the IMG class as this can not be done in
|
||||||
|
parallel. */
|
||||||
|
QVector<QPointF> points;
|
||||||
|
QString label;
|
||||||
|
quint32 type;
|
||||||
|
|
||||||
|
bool operator<(const Poly &other) const
|
||||||
|
{return type > other.type;}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
Point() : id(0) {}
|
||||||
|
|
||||||
|
Coordinates coordinates;
|
||||||
|
QString label;
|
||||||
|
quint32 type;
|
||||||
|
bool poi;
|
||||||
|
quint64 id;
|
||||||
|
|
||||||
|
bool operator<(const Point &other) const
|
||||||
|
{return id < other.id;}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
IMG(const QString &fileName);
|
||||||
|
~IMG();
|
||||||
|
|
||||||
|
const QString &name() const {return _name;}
|
||||||
|
const RectC &bounds() const {return _bounds;}
|
||||||
|
Range zooms() const {return Range(_bits.first(), _bits.last());}
|
||||||
|
|
||||||
|
bool isValid() const {return _valid;}
|
||||||
|
const QString &errorString() const {return _errorString;}
|
||||||
|
|
||||||
|
void objects(const RectC &rect, int bits, QList<Poly> *polygons,
|
||||||
|
QList<Poly> *lines, QList<Point> *points) const;
|
||||||
|
|
||||||
|
const Style &style() const {return _style;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class SubFile;
|
||||||
|
|
||||||
|
typedef RTree<VectorTile*, double, 2> TileTree;
|
||||||
|
|
||||||
|
int blockSize() const {return _blockSize;}
|
||||||
|
QByteArray readBlock(int blockNum);
|
||||||
|
qint64 read(char *data, qint64 maxSize);
|
||||||
|
template<class T> bool readValue(T &val);
|
||||||
|
|
||||||
|
QFile _file;
|
||||||
|
quint8 _key;
|
||||||
|
int _blockSize;
|
||||||
|
QCache<int, QByteArray> _blockCache;
|
||||||
|
|
||||||
|
TileTree _tileTree;
|
||||||
|
|
||||||
|
QString _name;
|
||||||
|
RectC _bounds;
|
||||||
|
QList<int> _bits;
|
||||||
|
Style _style;
|
||||||
|
|
||||||
|
bool _valid;
|
||||||
|
QString _errorString;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IMG_H
|
173
src/map/IMG/lblfile.cpp
Normal file
173
src/map/IMG/lblfile.cpp
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
#include <QTextCodec>
|
||||||
|
#include "lblfile.h"
|
||||||
|
|
||||||
|
enum Charset {Normal, Symbol, Special};
|
||||||
|
|
||||||
|
static quint8 NORMAL_CHARS[] = {
|
||||||
|
' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
||||||
|
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
||||||
|
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||||
|
'X', 'Y', 'Z', '~', '~', '~', '~', '~',
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||||
|
'8', '9', '~', '~', '~', '~', '~', '~'
|
||||||
|
};
|
||||||
|
|
||||||
|
static quint8 SYMBOL_CHARS[] = {
|
||||||
|
'@', '!', '"', '#', '$', '%', '&', '\'',
|
||||||
|
'(', ')', '*', '+', ',', '-', '.', '/',
|
||||||
|
'~', '~', '~', '~', '~', '~', '~', '~',
|
||||||
|
'~', '~', ':', ';', '<', '=', '>', '?',
|
||||||
|
'~', '~', '~', '~', '~', '~', '~', '~',
|
||||||
|
'~', '~', '~', '[', '\\', ']', '^', '_'
|
||||||
|
};
|
||||||
|
|
||||||
|
static quint8 SPECIAL_CHARS[] = {
|
||||||
|
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
|
||||||
|
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||||
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
|
||||||
|
'x', 'y', 'z', '~', '~', '~', '~', '~',
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||||
|
'8', '9', '~', '~', '~', '~', '~', '~'
|
||||||
|
};
|
||||||
|
|
||||||
|
static QString capitalize(const QString &str)
|
||||||
|
{
|
||||||
|
if (str.isEmpty())
|
||||||
|
return str;
|
||||||
|
for (int i = 0; i < str.size(); i++)
|
||||||
|
if (str.at(i).isLetter() && !str.at(i).isUpper())
|
||||||
|
return str;
|
||||||
|
|
||||||
|
QString ret(str);
|
||||||
|
for (int i = 0; i < str.size(); i++)
|
||||||
|
if (i && !str.at(i-1).isSpace())
|
||||||
|
ret[i] = str.at(i).toLower();
|
||||||
|
else
|
||||||
|
ret[i] = str.at(i);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LBLFile::init()
|
||||||
|
{
|
||||||
|
Handle hdl;
|
||||||
|
quint16 codepage;
|
||||||
|
|
||||||
|
if (!(seek(hdl, 0x15) && readUInt32(hdl, _offset)
|
||||||
|
&& readUInt32(hdl, _size) && readByte(hdl, _multiplier)
|
||||||
|
&& readByte(hdl, _encoding) && seek(hdl, 0x57)
|
||||||
|
&& readUInt32(hdl, _poiOffset) && readUInt32(hdl, _poiSize)
|
||||||
|
&& seek(hdl, 0xAA) && readUInt16(hdl, codepage)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_multiplier = 1<<_multiplier;
|
||||||
|
|
||||||
|
if (codepage == 65001)
|
||||||
|
_codec = QTextCodec::codecForName("UTF-8");
|
||||||
|
else if (codepage == 0)
|
||||||
|
_codec = 0;
|
||||||
|
else
|
||||||
|
_codec = QTextCodec::codecForName(QString("CP%1").arg(codepage)
|
||||||
|
.toLatin1());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LBLFile::label6b(Handle &hdl, quint32 offset) const
|
||||||
|
{
|
||||||
|
QByteArray result;
|
||||||
|
Charset curCharSet = Normal;
|
||||||
|
quint8 b1, b2, b3;
|
||||||
|
|
||||||
|
if (!seek(hdl, offset))
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (!(readByte(hdl, b1) && readByte(hdl, b2) && readByte(hdl, b3)))
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
int c[]= {b1>>2, (b1&0x3)<<4|b2>>4, (b2&0xF)<<2|b3>>6, b3&0x3F};
|
||||||
|
|
||||||
|
for (int cpt = 0; cpt < 4; cpt++) {
|
||||||
|
if (c[cpt] > 0x2F)
|
||||||
|
return QString::fromLatin1(result);
|
||||||
|
switch (curCharSet) {
|
||||||
|
case Normal:
|
||||||
|
if (c[cpt] == 0x1c)
|
||||||
|
curCharSet = Symbol;
|
||||||
|
else if (c[cpt] == 0x1b)
|
||||||
|
curCharSet = Special;
|
||||||
|
else if(c[cpt] == 0x1d)
|
||||||
|
result.append('|');
|
||||||
|
else if (c[cpt] == 0x1f)
|
||||||
|
result.append(' ');
|
||||||
|
else if (c[cpt] == 0x1e)
|
||||||
|
result.append(' ');
|
||||||
|
else
|
||||||
|
result.append(NORMAL_CHARS[c[cpt]]);
|
||||||
|
break;
|
||||||
|
case Symbol:
|
||||||
|
result.append(SYMBOL_CHARS[c[cpt]]);
|
||||||
|
curCharSet = Normal;
|
||||||
|
break;
|
||||||
|
case Special:
|
||||||
|
result.append(SPECIAL_CHARS[c[cpt]]);
|
||||||
|
curCharSet = Normal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LBLFile::label8b(Handle &hdl, quint32 offset) const
|
||||||
|
{
|
||||||
|
QByteArray result;
|
||||||
|
quint8 c;
|
||||||
|
|
||||||
|
if (!seek(hdl, offset))
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (!readByte(hdl, c))
|
||||||
|
return QString();
|
||||||
|
if (!c)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (c >= 0x1B && c <= 0x1F)
|
||||||
|
result.append(' ');
|
||||||
|
else if (c > 0x07)
|
||||||
|
result.append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _codec ? _codec->toUnicode(result) : QString::fromLatin1(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString LBLFile::label(Handle &hdl, quint32 offset, bool poi)
|
||||||
|
{
|
||||||
|
if (!_init) {
|
||||||
|
if (!(_init = init()))
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 labelOffset;
|
||||||
|
if (poi) {
|
||||||
|
quint32 poiOffset;
|
||||||
|
if (!(seek(hdl, _poiOffset + offset) && readUInt24(hdl, poiOffset)))
|
||||||
|
return QString();
|
||||||
|
labelOffset = _offset + (poiOffset & 0x3FFFFF) * _multiplier;
|
||||||
|
} else
|
||||||
|
labelOffset = _offset + offset * _multiplier;
|
||||||
|
|
||||||
|
if (labelOffset > _offset + _size)
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
switch (_encoding) {
|
||||||
|
case 6:
|
||||||
|
return capitalize(label6b(hdl, labelOffset));
|
||||||
|
case 9:
|
||||||
|
case 10:
|
||||||
|
return capitalize(label8b(hdl, labelOffset));
|
||||||
|
default:
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
32
src/map/IMG/lblfile.h
Normal file
32
src/map/IMG/lblfile.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef LBLFILE_H
|
||||||
|
#define LBLFILE_H
|
||||||
|
|
||||||
|
#include "subfile.h"
|
||||||
|
|
||||||
|
class QTextCodec;
|
||||||
|
|
||||||
|
class LBLFile : public SubFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LBLFile(IMG *img, quint32 size) : SubFile(img, size), _init(false) {}
|
||||||
|
|
||||||
|
QString label(Handle &hdl, quint32 offset, bool poi = false);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool init();
|
||||||
|
|
||||||
|
QString label6b(Handle &hdl, quint32 offset) const;
|
||||||
|
QString label8b(Handle &hdl, quint32 offset) const;
|
||||||
|
|
||||||
|
quint32 _offset;
|
||||||
|
quint32 _size;
|
||||||
|
quint32 _poiOffset;
|
||||||
|
quint32 _poiSize;
|
||||||
|
quint8 _multiplier;
|
||||||
|
quint8 _encoding;
|
||||||
|
QTextCodec *_codec;
|
||||||
|
|
||||||
|
bool _init;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LBLFILE_H
|
30
src/map/IMG/netfile.cpp
Normal file
30
src/map/IMG/netfile.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include "netfile.h"
|
||||||
|
|
||||||
|
bool NETFile::init()
|
||||||
|
{
|
||||||
|
Handle hdl;
|
||||||
|
|
||||||
|
if (!(seek(hdl, 0x15) && readUInt32(hdl, _offset)
|
||||||
|
&& readUInt32(hdl, _size) && readByte(hdl, _multiplier)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_multiplier = 1<<_multiplier;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NETFile::lblOffset(Handle &hdl, quint32 netOffset, quint32 &lblOffset)
|
||||||
|
{
|
||||||
|
if (!_init) {
|
||||||
|
if (!(_init = init()))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(seek(hdl, _offset + netOffset * _multiplier)
|
||||||
|
&& readUInt24(hdl, lblOffset)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
lblOffset &= 0x3FFFFF;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
23
src/map/IMG/netfile.h
Normal file
23
src/map/IMG/netfile.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef NETFILE_H
|
||||||
|
#define NETFILE_H
|
||||||
|
|
||||||
|
#include "subfile.h"
|
||||||
|
|
||||||
|
class NETFile : public SubFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NETFile(IMG *img, quint32 size) : SubFile(img, size), _init(false) {}
|
||||||
|
|
||||||
|
bool lblOffset(Handle &hdl, quint32 netOffset, quint32 &lblOffset);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool init();
|
||||||
|
|
||||||
|
quint32 _offset;
|
||||||
|
quint32 _size;
|
||||||
|
quint8 _multiplier;
|
||||||
|
|
||||||
|
bool _init;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NETFILE_H
|
516
src/map/IMG/rgnfile.cpp
Normal file
516
src/map/IMG/rgnfile.cpp
Normal file
@ -0,0 +1,516 @@
|
|||||||
|
#include "trefile.h"
|
||||||
|
#include "units.h"
|
||||||
|
#include "lblfile.h"
|
||||||
|
#include "netfile.h"
|
||||||
|
#include "rgnfile.h"
|
||||||
|
|
||||||
|
|
||||||
|
bool RGNFile::BitStream::read(int bits, quint32 &val)
|
||||||
|
{
|
||||||
|
val = 0;
|
||||||
|
|
||||||
|
for (int pos = 0; pos < bits; ) {
|
||||||
|
if (!_remaining) {
|
||||||
|
if (!_length || !_file.readByte(_hdl, _data))
|
||||||
|
return false;
|
||||||
|
_remaining = 8;
|
||||||
|
_length--;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 get = bits - pos;
|
||||||
|
if (get >= _remaining) {
|
||||||
|
val |= _data << pos;
|
||||||
|
pos += _remaining;
|
||||||
|
_remaining = 0;
|
||||||
|
} else {
|
||||||
|
quint32 mask = (1<<get) - 1;
|
||||||
|
val |= (_data & mask)<<pos;
|
||||||
|
_data >>= get;
|
||||||
|
_remaining -= get;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RGNFile::BitStream::readDelta(int bits, int sign, bool extraBit,
|
||||||
|
qint32 &delta)
|
||||||
|
{
|
||||||
|
quint32 value;
|
||||||
|
int bo = 0;
|
||||||
|
|
||||||
|
if (!read(bits, value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (extraBit) {
|
||||||
|
value>>=1;
|
||||||
|
bo = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sign) {
|
||||||
|
qint32 signMask = 1 << (bits - bo - 1);
|
||||||
|
if (value & signMask) {
|
||||||
|
qint32 comp = value ^ signMask;
|
||||||
|
if (comp)
|
||||||
|
delta = comp - signMask;
|
||||||
|
else {
|
||||||
|
qint32 other;
|
||||||
|
if (!readDelta(bits - bo, sign, false, other))
|
||||||
|
return false;
|
||||||
|
if (other < 0)
|
||||||
|
delta = 1 - signMask + other;
|
||||||
|
else
|
||||||
|
delta = signMask - 1 + other;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delta = value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delta = value * sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RGNFile::BitStream::finish()
|
||||||
|
{
|
||||||
|
while (_length--)
|
||||||
|
if (!_file.readByte(_hdl, _data))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool RGNFile::init()
|
||||||
|
{
|
||||||
|
Handle hdl;
|
||||||
|
|
||||||
|
if (!(seek(hdl, 0x15) && readUInt32(hdl, _offset)
|
||||||
|
&& readUInt32(hdl, _size) && readUInt32(hdl, _polygonsOffset)
|
||||||
|
&& readUInt32(hdl, _polygonsSize) && seek(hdl, 0x39)
|
||||||
|
&& readUInt32(hdl, _linesOffset) && readUInt32(hdl, _linesSize)
|
||||||
|
&& seek(hdl, 0x55) && readUInt32(hdl, _pointsOffset)
|
||||||
|
&& readUInt32(hdl, _pointsSize)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (_offset + _size > size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RGNFile::sign(BitStream &bs, int &val)
|
||||||
|
{
|
||||||
|
quint32 bit;
|
||||||
|
val = 0;
|
||||||
|
|
||||||
|
if (!bs.read(1, bit))
|
||||||
|
return false;
|
||||||
|
if (bit) {
|
||||||
|
if (!bs.read(1, bit))
|
||||||
|
return false;
|
||||||
|
val = bit ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RGNFile::bitSize(quint8 baseSize, bool variableSign, bool extraBit)
|
||||||
|
{
|
||||||
|
int bits = 2;
|
||||||
|
if (baseSize <= 9)
|
||||||
|
bits += baseSize;
|
||||||
|
else
|
||||||
|
bits += 2 * baseSize - 9;
|
||||||
|
|
||||||
|
if (variableSign)
|
||||||
|
bits++;
|
||||||
|
if (extraBit)
|
||||||
|
bits++;
|
||||||
|
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RGNFile::polyObjects(const RectC &rect, Handle &hdl, const SubDiv *subdiv,
|
||||||
|
const Segment &segment, LBLFile *lbl, Handle &lblHdl, NETFile *net,
|
||||||
|
Handle &netHdl, QList<IMG::Poly> *polys) const
|
||||||
|
{
|
||||||
|
if (segment.start() == segment.end())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!seek(hdl, segment.start()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
quint32 labelPtr;
|
||||||
|
quint8 type, len8, bitstreamInfo;
|
||||||
|
qint16 lon, lat;
|
||||||
|
quint16 len;
|
||||||
|
|
||||||
|
while (hdl.pos < (int)segment.end()) {
|
||||||
|
IMG::Poly poly;
|
||||||
|
|
||||||
|
if (!(readByte(hdl, type) && readUInt24(hdl, labelPtr)
|
||||||
|
&& readInt16(hdl, lon) && readInt16(hdl, lat)))
|
||||||
|
return false;
|
||||||
|
if (type & 0x80) {
|
||||||
|
if (!readUInt16(hdl, len))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!readByte(hdl, len8))
|
||||||
|
return false;
|
||||||
|
len = len8;
|
||||||
|
}
|
||||||
|
if (!readByte(hdl, bitstreamInfo))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
poly.type = (segment.type() == Segment::Polygon)
|
||||||
|
? ((quint32)(type & 0x7F)) << 8 : ((quint32)(type & 0x3F)) << 8;
|
||||||
|
|
||||||
|
RectC br;
|
||||||
|
QPoint pos(subdiv->lon() + ((qint32)lon<<(24-subdiv->bits())),
|
||||||
|
subdiv->lat() + ((qint32)lat<<(24-subdiv->bits())));
|
||||||
|
Coordinates c(toWGS84(pos.x()), toWGS84(pos.y()));
|
||||||
|
br = br.united(c);
|
||||||
|
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||||
|
|
||||||
|
BitStream bs(*this, hdl, len);
|
||||||
|
int lonSign, latSign;
|
||||||
|
if (!sign(bs, lonSign) || !sign(bs, latSign))
|
||||||
|
return false;
|
||||||
|
bool extraBit = labelPtr & 0x400000;
|
||||||
|
int lonBits = bitSize(bitstreamInfo & 0x0F, !lonSign, extraBit);
|
||||||
|
int latBits = bitSize(bitstreamInfo >> 4, !latSign, false);
|
||||||
|
|
||||||
|
while (bs.hasNext(lonBits + latBits)) {
|
||||||
|
qint32 lonDelta, latDelta;
|
||||||
|
|
||||||
|
if (!(bs.readDelta(lonBits, lonSign, extraBit, lonDelta)
|
||||||
|
&& bs.readDelta(latBits, latSign, false, latDelta)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pos.rx() += lonDelta<<(24-subdiv->bits());
|
||||||
|
pos.ry() += latDelta<<(24-subdiv->bits());
|
||||||
|
|
||||||
|
Coordinates c(toWGS84(pos.x()), toWGS84(pos.y()));
|
||||||
|
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||||
|
br = br.united(c);
|
||||||
|
}
|
||||||
|
if (!bs.finish())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!rect.intersects(br))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (lbl && (labelPtr & 0x3FFFFF)) {
|
||||||
|
if (labelPtr & 0x800000) {
|
||||||
|
quint32 lblOff;
|
||||||
|
if (net && net->lblOffset(netHdl, labelPtr & 0x3FFFFF, lblOff))
|
||||||
|
poly.label = lbl->label(lblHdl, lblOff);
|
||||||
|
} else
|
||||||
|
poly.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
polys->append(poly);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RGNFile::extPolyObjects(const RectC &rect, Handle &hdl,
|
||||||
|
const SubDiv *subdiv, const Segment &segment, LBLFile *lbl, Handle &lblHdl,
|
||||||
|
QList<IMG::Poly> *polys) const
|
||||||
|
{
|
||||||
|
quint32 labelPtr;
|
||||||
|
quint8 type, subtype, len8, len82, bitstreamInfo;
|
||||||
|
qint16 lon, lat;
|
||||||
|
quint16 len;
|
||||||
|
|
||||||
|
|
||||||
|
if (!seek(hdl, segment.start()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (hdl.pos < (int)segment.end()) {
|
||||||
|
IMG::Poly poly;
|
||||||
|
|
||||||
|
if (!(readByte(hdl, type) && readByte(hdl, subtype)
|
||||||
|
&& readInt16(hdl, lon) && readInt16(hdl, lat) && readByte(hdl, len8)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (subtype & 0x80) {
|
||||||
|
qWarning("Polygons/lines with extra bytes not supported");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len8 & 0x01)
|
||||||
|
len = (len8>>1) - 1;
|
||||||
|
else {
|
||||||
|
if (!readByte(hdl, len82))
|
||||||
|
return false;
|
||||||
|
len = ((len8 | ((quint16)len82<<8))>>2) - 1;
|
||||||
|
}
|
||||||
|
if (!readByte(hdl, bitstreamInfo))
|
||||||
|
return false;
|
||||||
|
poly.type = 0x10000 + (quint16(type) << 8) + (subtype & 0x1F);
|
||||||
|
|
||||||
|
RectC br;
|
||||||
|
QPoint pos(subdiv->lon() + ((qint32)lon<<(24-subdiv->bits())),
|
||||||
|
subdiv->lat() + ((qint32)lat<<(24-subdiv->bits())));
|
||||||
|
Coordinates c(toWGS84(pos.x()), toWGS84(pos.y()));
|
||||||
|
br = br.united(c);
|
||||||
|
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||||
|
|
||||||
|
BitStream bs(*this, hdl, len);
|
||||||
|
int lonSign, latSign;
|
||||||
|
if (!sign(bs, lonSign) || !sign(bs, latSign))
|
||||||
|
return false;
|
||||||
|
quint32 extraBit;
|
||||||
|
bs.read(1, extraBit);
|
||||||
|
int lonBits = bitSize(bitstreamInfo & 0x0F, !lonSign, extraBit);
|
||||||
|
int latBits = bitSize(bitstreamInfo >> 4, !latSign, extraBit);
|
||||||
|
|
||||||
|
while (bs.hasNext(lonBits + latBits)) {
|
||||||
|
qint32 lonDelta, latDelta;
|
||||||
|
|
||||||
|
if (!(bs.readDelta(lonBits, lonSign, false, lonDelta)
|
||||||
|
&& bs.readDelta(latBits, latSign, false, latDelta)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pos.rx() += lonDelta<<(24-subdiv->bits());
|
||||||
|
pos.ry() += latDelta<<(24-subdiv->bits());
|
||||||
|
|
||||||
|
Coordinates c(toWGS84(pos.x()), toWGS84(pos.y()));
|
||||||
|
poly.points.append(QPointF(c.lon(), c.lat()));
|
||||||
|
br = br.united(c);
|
||||||
|
}
|
||||||
|
if (!bs.finish())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (subtype & 0x20) {
|
||||||
|
if (!readUInt24(hdl, labelPtr))
|
||||||
|
return false;
|
||||||
|
if (lbl)
|
||||||
|
poly.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rect.intersects(br))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
polys->append(poly);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RGNFile::pointObjects(const RectC &rect, Handle &hdl, const SubDiv *subdiv,
|
||||||
|
const Segment &segment, LBLFile *lbl, Handle &lblHdl,
|
||||||
|
QList<IMG::Point> *points) const
|
||||||
|
{
|
||||||
|
quint8 type, subtype;
|
||||||
|
qint16 lon, lat;
|
||||||
|
quint32 labelPtr;
|
||||||
|
|
||||||
|
if (!seek(hdl, segment.start()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (hdl.pos < (int)segment.end()) {
|
||||||
|
IMG::Point point;
|
||||||
|
|
||||||
|
if (!(readByte(hdl, type) && readUInt24(hdl, labelPtr)
|
||||||
|
&& readInt16(hdl, lon) && readInt16(hdl, lat)))
|
||||||
|
return false;
|
||||||
|
if (labelPtr & 0x800000) {
|
||||||
|
if (!readByte(hdl, subtype))
|
||||||
|
return false;
|
||||||
|
} else
|
||||||
|
subtype = 0;
|
||||||
|
|
||||||
|
point.type = (quint16)type<<8 | subtype;
|
||||||
|
|
||||||
|
qint16 lonOffset = lon<<(24-subdiv->bits());
|
||||||
|
qint16 latOffset = lat<<(24-subdiv->bits());
|
||||||
|
point.coordinates = Coordinates(toWGS84(subdiv->lon() + lonOffset),
|
||||||
|
toWGS84(subdiv->lat() + latOffset));
|
||||||
|
|
||||||
|
if (!rect.contains(point.coordinates))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
point.poi = labelPtr & 0x400000;
|
||||||
|
if (lbl && (labelPtr & 0x3FFFFF)) {
|
||||||
|
point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, point.poi);
|
||||||
|
point.id = ((quint64)lbl->offset())<<24 | (labelPtr & 0x3FFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
points->append(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RGNFile::extPointObjects(const RectC &rect, Handle &hdl,
|
||||||
|
const SubDiv *subdiv, const Segment &segment, LBLFile *lbl, Handle &lblHdl,
|
||||||
|
QList<IMG::Point> *points) const
|
||||||
|
{
|
||||||
|
quint8 type, subtype;
|
||||||
|
qint16 lon, lat;
|
||||||
|
quint32 labelPtr;
|
||||||
|
|
||||||
|
if (!seek(hdl, segment.start()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (hdl.pos < (int)segment.end()) {
|
||||||
|
IMG::Point point;
|
||||||
|
|
||||||
|
if (!(readByte(hdl, type) && readByte(hdl, subtype)
|
||||||
|
&& readInt16(hdl, lon) && readInt16(hdl, lat)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (subtype & 0x80) {
|
||||||
|
qWarning("Points with extra bytes not supported");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
point.type = 0x10000 | (((quint32)type)<<8) | (subtype & 0x1F);
|
||||||
|
|
||||||
|
qint16 lonOffset = lon<<(24-subdiv->bits());
|
||||||
|
qint16 latOffset = lat<<(24-subdiv->bits());
|
||||||
|
point.coordinates = Coordinates(toWGS84(subdiv->lon() + lonOffset),
|
||||||
|
toWGS84(subdiv->lat() + latOffset));
|
||||||
|
|
||||||
|
if (subtype & 0x20) {
|
||||||
|
if (!readUInt24(hdl, labelPtr))
|
||||||
|
return false;
|
||||||
|
point.poi = labelPtr & 0x400000;
|
||||||
|
if (lbl && (labelPtr & 0x3FFFFF)) {
|
||||||
|
point.label = lbl->label(lblHdl, labelPtr & 0x3FFFFF, point.poi);
|
||||||
|
point.id = ((quint64)lbl->offset())<<24 | (labelPtr & 0x3FFFFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rect.contains(point.coordinates))
|
||||||
|
points->append(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RGNFile::objects(const RectC &rect, const SubDiv *subdiv, LBLFile *lbl,
|
||||||
|
NETFile *net, QList<IMG::Poly> *polygons, QList<IMG::Poly> *lines,
|
||||||
|
QList<IMG::Point> *points)
|
||||||
|
{
|
||||||
|
Handle rgnHdl, lblHdl, netHdl;
|
||||||
|
|
||||||
|
if (!_init) {
|
||||||
|
if (!(_init = init()))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<RGNFile::Segment> seg(segments(rgnHdl, subdiv));
|
||||||
|
for (int i = 0; i < seg.size(); i++) {
|
||||||
|
switch (seg.at(i).type()) {
|
||||||
|
case Segment::Point:
|
||||||
|
case Segment::IndexedPoint:
|
||||||
|
if (points)
|
||||||
|
pointObjects(rect, rgnHdl, subdiv, seg.at(i), lbl, lblHdl,
|
||||||
|
points);
|
||||||
|
break;
|
||||||
|
case Segment::Line:
|
||||||
|
if (lines)
|
||||||
|
polyObjects(rect, rgnHdl, subdiv, seg.at(i), lbl, lblHdl,
|
||||||
|
net, netHdl, lines);
|
||||||
|
break;
|
||||||
|
case Segment::Polygon:
|
||||||
|
if (polygons)
|
||||||
|
polyObjects(rect, rgnHdl, subdiv, seg.at(i), lbl, lblHdl,
|
||||||
|
net, netHdl, polygons);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RGNFile::extObjects(const RectC &rect, const SubDiv *subdiv, LBLFile *lbl,
|
||||||
|
QList<IMG::Poly> *polygons, QList<IMG::Poly> *lines,
|
||||||
|
QList<IMG::Point> *points)
|
||||||
|
{
|
||||||
|
Handle rgnHdl, lblHdl;
|
||||||
|
|
||||||
|
if (!_init) {
|
||||||
|
if (!(_init = init()))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (polygons && subdiv->polygonsOffset() != subdiv->polygonsEnd()) {
|
||||||
|
quint32 start = _polygonsOffset + subdiv->polygonsOffset();
|
||||||
|
quint32 end = subdiv->polygonsEnd()
|
||||||
|
? _polygonsOffset + subdiv->polygonsEnd()
|
||||||
|
: _polygonsOffset + _polygonsSize;
|
||||||
|
extPolyObjects(rect, rgnHdl, subdiv, Segment(start, end,
|
||||||
|
Segment::Polygon), lbl, lblHdl, polygons);
|
||||||
|
}
|
||||||
|
if (lines && subdiv->linesOffset() != subdiv->linesEnd()) {
|
||||||
|
quint32 start = _linesOffset + subdiv->linesOffset();
|
||||||
|
quint32 end = subdiv->linesEnd()
|
||||||
|
? _linesOffset + subdiv->linesEnd()
|
||||||
|
: _linesOffset + _linesSize;
|
||||||
|
extPolyObjects(rect, rgnHdl, subdiv, Segment(start, end, Segment::Line),
|
||||||
|
lbl, lblHdl, lines);
|
||||||
|
}
|
||||||
|
if (points && subdiv->pointsOffset() != subdiv->pointsEnd()) {
|
||||||
|
quint32 start = _pointsOffset + subdiv->pointsOffset();
|
||||||
|
quint32 end = subdiv->pointsEnd()
|
||||||
|
? _pointsOffset + subdiv->pointsEnd()
|
||||||
|
: _pointsOffset + _linesSize;
|
||||||
|
extPointObjects(rect, rgnHdl, subdiv, Segment(start, end,
|
||||||
|
Segment::Point), lbl, lblHdl, points);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<RGNFile::Segment> RGNFile::segments(Handle &hdl, const SubDiv *subdiv)
|
||||||
|
const
|
||||||
|
{
|
||||||
|
if (subdiv->offset() == subdiv->end() || !(subdiv->objects() & 0xF0))
|
||||||
|
return QVector<Segment>();
|
||||||
|
|
||||||
|
quint32 offset = _offset + subdiv->offset();
|
||||||
|
|
||||||
|
int no = 0;
|
||||||
|
for (quint8 mask = 0x10; mask; mask <<= 1)
|
||||||
|
if (subdiv->objects() & mask)
|
||||||
|
no++;
|
||||||
|
|
||||||
|
if (!seek(hdl, offset))
|
||||||
|
return QVector<Segment>();
|
||||||
|
|
||||||
|
QVector<Segment> ret;
|
||||||
|
quint32 start = offset + 2 * (no - 1);
|
||||||
|
quint16 po;
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
for (quint8 mask = 0x10; mask; mask <<= 1) {
|
||||||
|
if (subdiv->objects() & mask) {
|
||||||
|
if (cnt) {
|
||||||
|
if (!readUInt16(hdl, po))
|
||||||
|
return QVector<Segment>();
|
||||||
|
start = offset + po;
|
||||||
|
}
|
||||||
|
if (!ret.isEmpty())
|
||||||
|
ret.last().setEnd(start);
|
||||||
|
ret.append(Segment(start, (Segment::Type)mask));
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.last().setEnd(subdiv->end() ? _offset + subdiv->end() : _offset + _size);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
QDebug operator<<(QDebug dbg, const RGNFile::Segment &segment)
|
||||||
|
{
|
||||||
|
dbg.nospace() << "Segment(" << segment.start() << ", " << segment.end()
|
||||||
|
<< ", " << segment.type() << ")";
|
||||||
|
return dbg.space();
|
||||||
|
}
|
||||||
|
#endif // QT_NO_DEBUG
|
107
src/map/IMG/rgnfile.h
Normal file
107
src/map/IMG/rgnfile.h
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#ifndef RGNFILE_H
|
||||||
|
#define RGNFILE_H
|
||||||
|
|
||||||
|
#include "img.h"
|
||||||
|
#include "subfile.h"
|
||||||
|
#include "subdiv.h"
|
||||||
|
|
||||||
|
class LBLFile;
|
||||||
|
class NETFile;
|
||||||
|
|
||||||
|
class RGNFile : public SubFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RGNFile(IMG *img, quint32 size) : SubFile(img, size), _init(false) {}
|
||||||
|
|
||||||
|
void objects(const RectC &rect, const SubDiv *subdiv, LBLFile *lbl,
|
||||||
|
NETFile *net, QList<IMG::Poly> *polygons, QList<IMG::Poly> *lines,
|
||||||
|
QList<IMG::Point> *points);
|
||||||
|
void extObjects(const RectC &rect, const SubDiv *subdiv, LBLFile *lbl,
|
||||||
|
QList<IMG::Poly> *polygons, QList<IMG::Poly> *lines,
|
||||||
|
QList<IMG::Point> *points);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Segment {
|
||||||
|
public:
|
||||||
|
enum Type {
|
||||||
|
Point = 0x10,
|
||||||
|
IndexedPoint = 0x20,
|
||||||
|
Line = 0x40,
|
||||||
|
Polygon = 0x80
|
||||||
|
};
|
||||||
|
|
||||||
|
Segment() : _start(0), _end(0), _type(Point) {}
|
||||||
|
Segment(quint32 start, Type type)
|
||||||
|
: _start(start), _end(0), _type(type) {}
|
||||||
|
Segment(quint32 start, quint32 end, Type type)
|
||||||
|
: _start(start), _end(end), _type(type) {}
|
||||||
|
|
||||||
|
void setEnd(quint32 end) {_end = end;}
|
||||||
|
|
||||||
|
quint32 start() const {return _start;}
|
||||||
|
quint32 end() const {return _end;}
|
||||||
|
Type type() const {return _type;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
quint32 _start;
|
||||||
|
quint32 _end;
|
||||||
|
Type _type;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BitStream {
|
||||||
|
public:
|
||||||
|
BitStream(const SubFile &file, Handle &hdl, quint16 length)
|
||||||
|
: _file(file), _hdl(hdl), _length(length), _remaining(0) {}
|
||||||
|
|
||||||
|
bool read(int bits, quint32 &val);
|
||||||
|
bool readDelta(int bits, int sign, bool extraBit, qint32 &delta);
|
||||||
|
bool hasNext(int bits) const
|
||||||
|
{return _length * 8 + _remaining >= (quint32)bits;}
|
||||||
|
bool finish();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SubFile &_file;
|
||||||
|
Handle &_hdl;
|
||||||
|
quint16 _length;
|
||||||
|
quint32 _remaining;
|
||||||
|
quint8 _data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool sign(BitStream &bs, int &val);
|
||||||
|
static int bitSize(quint8 baseSize, bool variableSign, bool extraBit);
|
||||||
|
|
||||||
|
bool init();
|
||||||
|
QVector<Segment> segments(Handle &hdl, const SubDiv *subdiv) const;
|
||||||
|
bool polyObjects(const RectC &rect, Handle &hdl, const SubDiv *subdiv,
|
||||||
|
const Segment &segment, LBLFile *lbl, Handle &lblHdl, NETFile *net,
|
||||||
|
Handle &netHdl, QList<IMG::Poly> *polys) const;
|
||||||
|
bool pointObjects(const RectC &rect, Handle &hdl, const SubDiv *subdiv,
|
||||||
|
const Segment &segment, LBLFile *lbl, Handle &lblHdl,
|
||||||
|
QList<IMG::Point> *points) const;
|
||||||
|
bool extPolyObjects(const RectC &rect, Handle &hdl, const SubDiv *subdiv,
|
||||||
|
const Segment &segment, LBLFile *lbl, Handle &lblHdl,
|
||||||
|
QList<IMG::Poly> *polys) const;
|
||||||
|
bool extPointObjects(const RectC &rect, Handle &hdl, const SubDiv *subdiv,
|
||||||
|
const Segment &segment, LBLFile *lbl, Handle &lblHdl,
|
||||||
|
QList<IMG::Point> *points) const;
|
||||||
|
|
||||||
|
friend QDebug operator<<(QDebug dbg, const RGNFile::Segment &segment);
|
||||||
|
|
||||||
|
quint32 _offset;
|
||||||
|
quint32 _size;
|
||||||
|
|
||||||
|
quint32 _polygonsOffset;
|
||||||
|
quint32 _polygonsSize;
|
||||||
|
quint32 _linesOffset;
|
||||||
|
quint32 _linesSize;
|
||||||
|
quint32 _pointsOffset;
|
||||||
|
quint32 _pointsSize;
|
||||||
|
|
||||||
|
bool _init;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
QDebug operator<<(QDebug dbg, const RGNFile::Segment &segment);
|
||||||
|
#endif // QT_NO_DEBUG
|
||||||
|
|
||||||
|
#endif // RGNFILE_H
|
845
src/map/IMG/style.cpp
Normal file
845
src/map/IMG/style.cpp
Normal file
@ -0,0 +1,845 @@
|
|||||||
|
#include <QImage>
|
||||||
|
#include "style.h"
|
||||||
|
|
||||||
|
#define TYPE(t) ((t)<<8)
|
||||||
|
|
||||||
|
void Style::defaultPolygonStyle()
|
||||||
|
{
|
||||||
|
_polygons[TYPE(0x01)] = Style::Polygon(QBrush("#dfd3b5"));
|
||||||
|
_polygons[TYPE(0x02)] = Style::Polygon(QBrush("#dfd3b5"));
|
||||||
|
_polygons[TYPE(0x03)] = Style::Polygon(QBrush("#dfd3b5"));
|
||||||
|
_polygons[TYPE(0x04)] = Style::Polygon(QBrush("#ff4040", Qt::BDiagPattern),
|
||||||
|
QPen(QColor("#ff4040")));
|
||||||
|
_polygons[TYPE(0x05)] = Style::Polygon(QBrush("#d6d4ce"));
|
||||||
|
_polygons[TYPE(0x06)] = Style::Polygon(QBrush("#d6d4ce"));
|
||||||
|
_polygons[TYPE(0x07)] = Style::Polygon(QBrush("#d6d4ce"));
|
||||||
|
_polygons[TYPE(0x08)] = Style::Polygon(QBrush("#d6d4ce"));
|
||||||
|
_polygons[TYPE(0x09)] = Style::Polygon(QBrush("#d6d4ce"));
|
||||||
|
_polygons[TYPE(0x0a)] = Style::Polygon(QBrush("#d6d4ce"));
|
||||||
|
_polygons[TYPE(0x0b)] = Style::Polygon(QBrush("#d6d4ce"));
|
||||||
|
_polygons[TYPE(0x0c)] = Style::Polygon(QBrush("#d6d4ce"));
|
||||||
|
_polygons[TYPE(0x0d)] = Style::Polygon(QBrush("#f8e3be"));
|
||||||
|
_polygons[TYPE(0x0e)] = Style::Polygon(QBrush("#ffffff"));
|
||||||
|
_polygons[TYPE(0x0f)] = Style::Polygon(QBrush("#e6e2d9"));
|
||||||
|
_polygons[TYPE(0x10)] = Style::Polygon(QBrush("#e6e2d9"));
|
||||||
|
_polygons[TYPE(0x11)] = Style::Polygon(QBrush("#e6e2d9"));
|
||||||
|
_polygons[TYPE(0x12)] = Style::Polygon(QBrush("#e6e2d9"));
|
||||||
|
_polygons[TYPE(0x13)] = Style::Polygon(QBrush("#dbd0b6"),
|
||||||
|
QPen(QColor("#cdccc4"), 1));
|
||||||
|
_polygons[TYPE(0x14)] = Style::Polygon(QBrush(Qt::NoBrush),
|
||||||
|
QPen(QColor("#9ac269"), 2, Qt::DashLine));
|
||||||
|
_polygons[TYPE(0x15)] = Style::Polygon(QBrush(Qt::NoBrush),
|
||||||
|
QPen(QColor("#9ac269"), 2, Qt::DashLine));
|
||||||
|
_polygons[TYPE(0x17)] = Style::Polygon(QBrush("#d4ebb8"));
|
||||||
|
_polygons[TYPE(0x18)] = Style::Polygon(QBrush("#d4ebb8"));
|
||||||
|
_polygons[TYPE(0x19)] = Style::Polygon(QBrush("#d6d4ce"));
|
||||||
|
_polygons[TYPE(0x1a)] = Style::Polygon(QBrush("#000000", Qt::Dense6Pattern),
|
||||||
|
QPen(QColor("#cdccc4"), 1));
|
||||||
|
_polygons[TYPE(0x1e)] = Style::Polygon(QBrush(Qt::NoBrush),
|
||||||
|
QPen(QColor("#9ac269"), 1, Qt::DashLine));
|
||||||
|
_polygons[TYPE(0x1f)] = Style::Polygon(QBrush(Qt::NoBrush),
|
||||||
|
QPen(QColor("#9ac269"), 1, Qt::DashLine));
|
||||||
|
_polygons[TYPE(0x28)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x29)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x32)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x3b)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x3c)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x3d)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x3e)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x3f)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x40)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x41)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x42)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x43)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x44)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x45)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x46)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x47)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x48)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x49)] = Style::Polygon(QBrush("#9fc4e1"));
|
||||||
|
_polygons[TYPE(0x4b)] = Style::Polygon(QBrush("#f1f0e5"), QPen("#f1f0e5"));
|
||||||
|
_polygons[TYPE(0x4a)] = Style::Polygon(QBrush("#f1f0e5"), QPen("#f1f0e5"));
|
||||||
|
_polygons[TYPE(0x4c)] = Style::Polygon(QBrush("#9fc4e1", Qt::Dense6Pattern));
|
||||||
|
_polygons[TYPE(0x4d)] = Style::Polygon(QBrush("#ddf1fd"));
|
||||||
|
_polygons[TYPE(0x4e)] = Style::Polygon(QBrush("#e3edc1"));
|
||||||
|
_polygons[TYPE(0x4f)] = Style::Polygon(QBrush("#d4ebb8"));
|
||||||
|
_polygons[TYPE(0x50)] = Style::Polygon(QBrush("#d4ebb8"));
|
||||||
|
_polygons[TYPE(0x51)] = Style::Polygon(QBrush("#9fc4e1", Qt::Dense4Pattern));
|
||||||
|
_polygons[TYPE(0x52)] = Style::Polygon(QBrush("#d4ebb8"));
|
||||||
|
|
||||||
|
_drawOrder << TYPE(0x4b) << TYPE(0x4a) << TYPE(0x01) << TYPE(0x02)
|
||||||
|
<< TYPE(0x03) << TYPE(0x17) << TYPE(0x18) << TYPE(0x19) << TYPE(0x1a)
|
||||||
|
<< TYPE(0x28) << TYPE(0x29) << TYPE(0x32) << TYPE(0x3b) << TYPE(0x3c)
|
||||||
|
<< TYPE(0x3d) << TYPE(0x3e) << TYPE(0x3f) << TYPE(0x40) << TYPE(0x41)
|
||||||
|
<< TYPE(0x42) << TYPE(0x43) << TYPE(0x44) << TYPE(0x45) << TYPE(0x46)
|
||||||
|
<< TYPE(0x47) << TYPE(0x48) << TYPE(0x49) << TYPE(0x4c) << TYPE(0x4d)
|
||||||
|
<< TYPE(0x4e) << TYPE(0x4f) << TYPE(0x50) << TYPE(0x51) << TYPE(0x52)
|
||||||
|
<< TYPE(0x14) << TYPE(0x15) << TYPE(0x1e) << TYPE(0x1f) << TYPE(0x04)
|
||||||
|
<< TYPE(0x05) << TYPE(0x06) << TYPE(0x07) << TYPE(0x08) << TYPE(0x09)
|
||||||
|
<< TYPE(0x0a) << TYPE(0x0b) << TYPE(0x0c) << TYPE(0x0d) << TYPE(0x0e)
|
||||||
|
<< TYPE(0x0f) << TYPE(0x10) << TYPE(0x11) << TYPE(0x12) << TYPE(0x13);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Style::defaultLineStyle()
|
||||||
|
{
|
||||||
|
QVector<qreal> pattern;
|
||||||
|
pattern << 4 << 4;
|
||||||
|
QPen rr(QColor("#717171"), 3, Qt::CustomDashLine);
|
||||||
|
rr.setDashPattern(pattern);
|
||||||
|
|
||||||
|
_lines[TYPE(0x01)] = Style::Line(QPen(QColor("#9bd772"), 2, Qt::SolidLine),
|
||||||
|
QPen(QColor("#72a35a"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
_lines[TYPE(0x02)] = Style::Line(QPen(QColor("#ffcc78"), 2, Qt::SolidLine),
|
||||||
|
QPen(QColor("#e8a541"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
_lines[TYPE(0x03)] = Style::Line(QPen(QColor("#ffcc78"), 2, Qt::SolidLine),
|
||||||
|
QPen(QColor("#e8a541"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
_lines[TYPE(0x04)] = Style::Line(QPen(QColor("#faef75"), 3, Qt::SolidLine),
|
||||||
|
QPen(QColor("#dbd27b"), 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
_lines[TYPE(0x05)] = Style::Line(QPen(QColor("#ffffff"), 3, Qt::SolidLine),
|
||||||
|
QPen(QColor("#d5cdc0"), 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
_lines[TYPE(0x06)] = Style::Line(QPen(QColor("#ffffff"), 3, Qt::SolidLine),
|
||||||
|
QPen(QColor("#d5cdc0"), 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
_lines[TYPE(0x07)] = Style::Line(QPen(QColor("#ffffff"), 2, Qt::SolidLine),
|
||||||
|
QPen(QColor("#d5cdc0"), 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
_lines[TYPE(0x08)] = Style::Line(QPen(QColor("#ffcc78"), 2, Qt::SolidLine),
|
||||||
|
QPen(QColor("#e8a541"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
_lines[TYPE(0x09)] = Style::Line(QPen(QColor("#9bd772"), 2, Qt::SolidLine),
|
||||||
|
QPen(QColor("#72a35a"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
_lines[TYPE(0x0a)] = Style::Line(QPen(QColor("#aba083"), 1, Qt::DashLine));
|
||||||
|
_lines[TYPE(0x0b)] = Style::Line(QPen(QColor("#ffcc78"), 2, Qt::SolidLine),
|
||||||
|
QPen(QColor("#e8a541"), 6, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
_lines[TYPE(0x0c)] = Style::Line(QPen(QColor("#ffffff"), 3, Qt::SolidLine),
|
||||||
|
QPen(QColor("#d5cdc0"), 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
_lines[TYPE(0x14)] = Style::Line(rr, QPen(Qt::white, 3, Qt::SolidLine,
|
||||||
|
Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
_lines[TYPE(0x16)] = Style::Line(QPen(QColor("#aba083"), 1, Qt::DotLine));
|
||||||
|
_lines[TYPE(0x18)] = Style::Line(QPen(QColor("#9fc4e1"), 2, Qt::SolidLine));
|
||||||
|
_lines[TYPE(0x18)].setTextColor(QColor("#9fc4e1"));
|
||||||
|
//_lines[TYPE(0x1a)] = Style::Line(QPen(QColor("#7697b7"), 1, Qt::DashLine));
|
||||||
|
_lines[TYPE(0x1b)] = Style::Line(QPen(QColor("#7697b7"), 1, Qt::DashLine));
|
||||||
|
_lines[TYPE(0x1e)] = Style::Line(QPen(QColor("#505145"), 2, Qt::DashDotLine));
|
||||||
|
_lines[TYPE(0x1f)] = Style::Line(QPen(QColor("#9fc4e1"), 3, Qt::SolidLine));
|
||||||
|
_lines[TYPE(0x1f)].setTextColor(QColor("#9fc4e1"));
|
||||||
|
_lines[TYPE(0x21)] = Style::Line(QPen(QColor("#cacfc0"), 1, Qt::SolidLine));
|
||||||
|
_lines[TYPE(0x21)].setTextColor(QColor("#62695a"));
|
||||||
|
_lines[TYPE(0x21)].setTextFontSize(Style::Small);
|
||||||
|
_lines[TYPE(0x22)] = Style::Line(QPen(QColor("#cacfc0"), 1.5, Qt::SolidLine));
|
||||||
|
_lines[TYPE(0x22)].setTextColor(QColor("#62695a"));
|
||||||
|
_lines[TYPE(0x22)].setTextFontSize(Style::Small);
|
||||||
|
_lines[TYPE(0x24)] = Style::Line(QPen(QColor("#55aaff"), 1, Qt::SolidLine));
|
||||||
|
_lines[TYPE(0x24)].setTextColor(QColor("#55aaff"));
|
||||||
|
_lines[TYPE(0x24)].setTextFontSize(Style::Small);
|
||||||
|
_lines[TYPE(0x25)] = Style::Line(QPen(QColor("#55aaff"), 1.5, Qt::SolidLine));
|
||||||
|
_lines[TYPE(0x25)].setTextColor(QColor("#55aaff"));
|
||||||
|
_lines[TYPE(0x25)].setTextFontSize(Style::Small);
|
||||||
|
_lines[TYPE(0x26)] = Style::Line(QPen(QColor("#9fc4e1"), 2, Qt::DotLine));
|
||||||
|
_lines[TYPE(0x27)] = Style::Line(QPen(QColor("#ffffff"), 4, Qt::SolidLine),
|
||||||
|
QPen(QColor("#d5cdc0"), 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
//_lines[TYPE(0x28)] = Style::Line(QPen(QColor("#5a5a5a"), 1, Qt::SolidLine));
|
||||||
|
_lines[TYPE(0x29)] = Style::Line(QPen(QColor("#5a5a5a"), 1, Qt::SolidLine));
|
||||||
|
_lines[TYPE(0x29)].setTextFontSize(Style::None);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readBitmap(SubFile *file, SubFile::Handle &hdl, QImage &img,
|
||||||
|
int bpp)
|
||||||
|
{
|
||||||
|
if (!bpp)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (int y = 0; y < img.height(); y++) {
|
||||||
|
for (int x = 0; x < img.width(); x += 8/bpp) {
|
||||||
|
quint8 color;
|
||||||
|
if (!file->readByte(hdl, color))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < 8/bpp && x + i < img.width(); i++) {
|
||||||
|
int value = (i > 0) ? (color >>= bpp) : color;
|
||||||
|
|
||||||
|
if (bpp == 4)
|
||||||
|
value = value & 0xf;
|
||||||
|
else if (bpp == 2)
|
||||||
|
value = value & 0x3;
|
||||||
|
else if (bpp == 1)
|
||||||
|
value = value & 0x1;
|
||||||
|
|
||||||
|
img.setPixel(x + i, y, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readColor(SubFile *file, SubFile::Handle &hdl, QColor &color)
|
||||||
|
{
|
||||||
|
quint8 b, g, r;
|
||||||
|
|
||||||
|
if (!(file->readByte(hdl, b) && file->readByte(hdl, g)
|
||||||
|
&& file->readByte(hdl, r)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
color = qRgb(r, g, b);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool skipLocalization(SubFile *file, SubFile::Handle &hdl)
|
||||||
|
{
|
||||||
|
quint8 t8, n = 1;
|
||||||
|
quint16 len;
|
||||||
|
|
||||||
|
if (!file->readByte(hdl, t8))
|
||||||
|
return false;
|
||||||
|
len = t8;
|
||||||
|
if (!(t8 & 0x01)) {
|
||||||
|
n = 2;
|
||||||
|
if (!file->readByte(hdl, t8))
|
||||||
|
return false;
|
||||||
|
len |= t8 << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
len -= n;
|
||||||
|
while (len > 0) {
|
||||||
|
if (!file->readByte(hdl, t8))
|
||||||
|
return false;
|
||||||
|
len -= 2 * n;
|
||||||
|
while (len > 0) {
|
||||||
|
if (!file->readByte(hdl, t8))
|
||||||
|
return false;
|
||||||
|
len -= 2 * n;
|
||||||
|
if (!t8)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Style::itemInfo(SubFile *file, SubFile::Handle &hdl,
|
||||||
|
const Section §ion, ItemInfo &info)
|
||||||
|
{
|
||||||
|
quint16 t16_1, t16_2;
|
||||||
|
quint8 t8;
|
||||||
|
|
||||||
|
if (section.arrayItemSize == 5) {
|
||||||
|
if (!(file->readUInt16(hdl, t16_1) && file->readUInt16(hdl, t16_2)
|
||||||
|
&& file->readByte(hdl, t8)))
|
||||||
|
return false;
|
||||||
|
info.offset = t16_2 | (t8<<16);
|
||||||
|
} else if (section.arrayItemSize == 4) {
|
||||||
|
if (!(file->readUInt16(hdl, t16_1) && file->readUInt16(hdl, t16_2)))
|
||||||
|
return false;
|
||||||
|
info.offset = t16_2;
|
||||||
|
} else if (section.arrayItemSize == 3) {
|
||||||
|
if (!(file->readUInt16(hdl, t16_1) && file->readByte(hdl, t8)))
|
||||||
|
return false;
|
||||||
|
info.offset = t8;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
t16_2 = (t16_1 >> 5) | (( t16_1 & 0x1f) << 11);
|
||||||
|
info.type = t16_2 & 0x7F;
|
||||||
|
info.subtype = t16_1 & 0x1F;
|
||||||
|
info.extended = t16_1 & 0x2000;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Style::parsePolygons(SubFile *file, SubFile::Handle &hdl,
|
||||||
|
const Section §ion)
|
||||||
|
{
|
||||||
|
for (quint32 i = 0; i < section.arraySize / section.arrayItemSize; i++) {
|
||||||
|
if (!file->seek(hdl, section.arrayOffset + i * section.arrayItemSize))
|
||||||
|
return false;
|
||||||
|
ItemInfo info;
|
||||||
|
if (!itemInfo(file, hdl, section, info))
|
||||||
|
return false;
|
||||||
|
quint32 type = info.extended
|
||||||
|
? 0x10000 | (info.type << 8) | info.subtype : (info.type << 8);
|
||||||
|
|
||||||
|
quint8 t8, flags;
|
||||||
|
if (!(file->seek(hdl, section.offset + info.offset)
|
||||||
|
&& file->readByte(hdl, t8)))
|
||||||
|
return false;
|
||||||
|
flags = t8 & 0x0F;
|
||||||
|
|
||||||
|
QColor c1, c2, c3, c4;
|
||||||
|
QImage img(32, 32, QImage::Format_Indexed8);
|
||||||
|
|
||||||
|
switch (flags) {
|
||||||
|
case 0x01:
|
||||||
|
if (!(readColor(file, hdl, c1) && readColor(file, hdl, c2)
|
||||||
|
&& readColor(file, hdl, c3) && readColor(file, hdl, c4)))
|
||||||
|
return false;
|
||||||
|
_polygons[type] = Style::Polygon(QBrush(c1), QPen(c3, 2));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x06:
|
||||||
|
case 0x07:
|
||||||
|
if (!readColor(file, hdl, c1))
|
||||||
|
return false;
|
||||||
|
_polygons[type] = Style::Polygon(QBrush(c1));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x08:
|
||||||
|
if (!(readColor(file, hdl, c1) && readColor(file, hdl, c2)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
img.setColorCount(2);
|
||||||
|
img.setColor(0, c2.rgb());
|
||||||
|
img.setColor(1, c1.rgb());
|
||||||
|
if (!readBitmap(file, hdl, img, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_polygons[type] = Style::Polygon(QBrush(img));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x09:
|
||||||
|
if (!(readColor(file, hdl, c1) && readColor(file, hdl, c2)
|
||||||
|
&& readColor(file, hdl, c3) && readColor(file, hdl, c4)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
img.setColorCount(2);
|
||||||
|
img.setColor(0, c2.rgb());
|
||||||
|
img.setColor(1, c1.rgb());
|
||||||
|
if (!readBitmap(file, hdl, img, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_polygons[type] = Style::Polygon(QBrush(img));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x0B:
|
||||||
|
case 0x0D:
|
||||||
|
if (!(readColor(file, hdl, c1) && readColor(file, hdl, c2)
|
||||||
|
&& readColor(file, hdl, c3)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
img.setColorCount(2);
|
||||||
|
img.setColor(0, (flags == 0x0B) ? qRgba(255, 255, 255, 0)
|
||||||
|
: c2.rgb());
|
||||||
|
img.setColor(1, c1.rgb());
|
||||||
|
if (!readBitmap(file, hdl, img, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_polygons[type] = Style::Polygon(QBrush(img));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x0E:
|
||||||
|
if (!readColor(file, hdl, c1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
img.setColorCount(2);
|
||||||
|
img.setColor(0, qRgba(255, 255, 255, 0));
|
||||||
|
img.setColor(1, c1.rgb());
|
||||||
|
if (!readBitmap(file, hdl, img, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_polygons[type] = Style::Polygon(QBrush(img));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x0F:
|
||||||
|
if (!(readColor(file, hdl, c1) && readColor(file, hdl, c2)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
img.setColorCount(2);
|
||||||
|
img.setColor(0, qRgba(255, 255, 255, 0));
|
||||||
|
img.setColor(1, c1.rgb());
|
||||||
|
if (!readBitmap(file, hdl, img, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_polygons[type] = Style::Polygon(QBrush(img));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Style::parseLines(SubFile *file, SubFile::Handle &hdl,
|
||||||
|
const Section §ion)
|
||||||
|
{
|
||||||
|
for (quint32 i = 0; i < section.arraySize / section.arrayItemSize; i++) {
|
||||||
|
if (!file->seek(hdl, section.arrayOffset + i * section.arrayItemSize))
|
||||||
|
return false;
|
||||||
|
ItemInfo info;
|
||||||
|
if (!itemInfo(file, hdl, section, info))
|
||||||
|
return false;
|
||||||
|
quint32 type = info.extended
|
||||||
|
? 0x10000 | (info.type << 8) | info.subtype : (info.type << 8);
|
||||||
|
|
||||||
|
quint8 t8_1, t8_2, flags, rows;
|
||||||
|
if (!(file->seek(hdl, section.offset + info.offset)
|
||||||
|
&& file->readByte(hdl, t8_1) && file->readByte(hdl, t8_2)))
|
||||||
|
return false;
|
||||||
|
flags = t8_1 & 0x07;
|
||||||
|
rows = t8_1 >> 3;
|
||||||
|
bool localization = t8_2 & 0x01;
|
||||||
|
bool textColor = t8_2 & 0x04;
|
||||||
|
|
||||||
|
QColor c1, c2, c3, c4;
|
||||||
|
quint8 w1, w2;
|
||||||
|
|
||||||
|
switch (flags) {
|
||||||
|
case 0x00:
|
||||||
|
if (!(readColor(file, hdl, c1) && readColor(file, hdl, c2)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (rows) {
|
||||||
|
QImage img(32, rows, QImage::Format_Indexed8);
|
||||||
|
img.setColorCount(2);
|
||||||
|
img.setColor(0, c2.rgb());
|
||||||
|
img.setColor(1, c1.rgb());
|
||||||
|
if (!readBitmap(file, hdl, img, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lines[type] = Style::Line(img);
|
||||||
|
} else {
|
||||||
|
if (!(file->readByte(hdl, w1) && file->readByte(hdl, w2)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lines[type] = (w2 > w1)
|
||||||
|
? Style::Line(QPen(c1, w1, Qt::SolidLine, Qt::RoundCap,
|
||||||
|
Qt::RoundJoin), QPen(c2, w2, Qt::SolidLine, Qt::RoundCap,
|
||||||
|
Qt::RoundJoin))
|
||||||
|
: Style::Line(QPen(c1, w1, Qt::SolidLine, Qt::RoundCap,
|
||||||
|
Qt::RoundJoin));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x01:
|
||||||
|
if (!(readColor(file, hdl, c1) && readColor(file, hdl, c2)
|
||||||
|
&& readColor(file, hdl, c3) && readColor(file, hdl, c4)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (rows) {
|
||||||
|
QImage img(32, rows, QImage::Format_Indexed8);
|
||||||
|
img.setColorCount(2);
|
||||||
|
img.setColor(0, c2.rgb());
|
||||||
|
img.setColor(1, c1.rgb());
|
||||||
|
if (!readBitmap(file, hdl, img, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lines[type] = Style::Line(img);
|
||||||
|
} else {
|
||||||
|
if (!(file->readByte(hdl, w1) && file->readByte(hdl, w2)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lines[type] = (w2 > w1)
|
||||||
|
? Style::Line(QPen(c1, w1, Qt::SolidLine, Qt::RoundCap,
|
||||||
|
Qt::RoundJoin), QPen(c2, w2, Qt::SolidLine, Qt::RoundCap,
|
||||||
|
Qt::RoundJoin))
|
||||||
|
: Style::Line(QPen(c1, w1, Qt::SolidLine, Qt::RoundCap,
|
||||||
|
Qt::RoundJoin));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x03:
|
||||||
|
if (!(readColor(file, hdl, c1) && readColor(file, hdl, c2)
|
||||||
|
&& readColor(file, hdl, c3)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (rows) {
|
||||||
|
QImage img(32, rows, QImage::Format_Indexed8);
|
||||||
|
img.setColorCount(2);
|
||||||
|
img.setColor(0, qRgba(255, 255, 255, 0));
|
||||||
|
img.setColor(1, c1.rgb());
|
||||||
|
if (!readBitmap(file, hdl, img, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lines[type] = Style::Line(img);
|
||||||
|
} else {
|
||||||
|
if (!(file->readByte(hdl, w1) && file->readByte(hdl, w2)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lines[type] = Style::Line(QPen(c1, w1, Qt::SolidLine,
|
||||||
|
Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x05:
|
||||||
|
if (!(readColor(file, hdl, c1) && readColor(file, hdl, c2)
|
||||||
|
&& readColor(file, hdl, c3)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (rows) {
|
||||||
|
QImage img(32, rows, QImage::Format_Indexed8);
|
||||||
|
img.setColorCount(2);
|
||||||
|
img.setColor(0, c2.rgb());
|
||||||
|
img.setColor(1, c1.rgb());
|
||||||
|
if (!readBitmap(file, hdl, img, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lines[type] = Style::Line(img);
|
||||||
|
} else {
|
||||||
|
if (!(file->readByte(hdl, w1) && file->readByte(hdl, w2)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lines[type] = (w2 > w1)
|
||||||
|
? Style::Line(QPen(c1, w1, Qt::SolidLine, Qt::RoundCap,
|
||||||
|
Qt::RoundJoin), QPen(c2, w2, Qt::SolidLine, Qt::RoundCap,
|
||||||
|
Qt::RoundJoin))
|
||||||
|
: Style::Line(QPen(c1, w1, Qt::SolidLine, Qt::RoundCap,
|
||||||
|
Qt::RoundJoin));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x06:
|
||||||
|
if (!readColor(file, hdl, c1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (rows) {
|
||||||
|
QImage img(32, rows, QImage::Format_Indexed8);
|
||||||
|
img.setColorCount(2);
|
||||||
|
img.setColor(0, qRgba(255, 255, 255, 0));
|
||||||
|
img.setColor(1, c1.rgb());
|
||||||
|
if (!readBitmap(file, hdl, img, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lines[type] = Style::Line(img);
|
||||||
|
} else {
|
||||||
|
if (!file->readByte(hdl, w1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lines[type] = Style::Line(QPen(c1, w1, Qt::SolidLine,
|
||||||
|
Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x07:
|
||||||
|
if (!(readColor(file, hdl, c1) && readColor(file, hdl, c2)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (rows) {
|
||||||
|
QImage img(32, rows, QImage::Format_Indexed8);
|
||||||
|
img.setColorCount(2);
|
||||||
|
img.setColor(0, qRgba(255,255,255,0));
|
||||||
|
img.setColor(1, c1.rgb());
|
||||||
|
if (!readBitmap(file, hdl, img, 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lines[type] = Style::Line(img);
|
||||||
|
} else {
|
||||||
|
if (!file->readByte(hdl, w1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lines[type] = Style::Line(QPen(c1, w1, Qt::SolidLine,
|
||||||
|
Qt::RoundCap, Qt::RoundJoin));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isContourLine(type)) {
|
||||||
|
Line &l = _lines[type];
|
||||||
|
l.setTextColor(l.foreground().color());
|
||||||
|
l.setTextFontSize(Small);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localization && !skipLocalization(file, hdl))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (textColor) {
|
||||||
|
quint8 labelFlags;
|
||||||
|
if (!file->readByte(hdl, labelFlags))
|
||||||
|
return false;
|
||||||
|
if (labelFlags & 0x08) {
|
||||||
|
if (!readColor(file, hdl, c1))
|
||||||
|
return false;
|
||||||
|
_lines[type].setTextColor(c1);
|
||||||
|
}
|
||||||
|
_lines[type].setTextFontSize((FontSize)(labelFlags & 0x07));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int colors2bpp(quint8 colors, quint8 flags)
|
||||||
|
{
|
||||||
|
switch (flags) {
|
||||||
|
case 0x00:
|
||||||
|
if (colors < 3)
|
||||||
|
return colors;
|
||||||
|
else if (colors == 3)
|
||||||
|
return 2;
|
||||||
|
else if (colors < 16)
|
||||||
|
return 4;
|
||||||
|
else
|
||||||
|
return 8;
|
||||||
|
case 0x10:
|
||||||
|
if (colors == 0)
|
||||||
|
return 1;
|
||||||
|
else if (colors < 3)
|
||||||
|
return 2;
|
||||||
|
else if (colors < 15)
|
||||||
|
return 4;
|
||||||
|
else
|
||||||
|
return 8;
|
||||||
|
case 0x20:
|
||||||
|
if (colors == 0)
|
||||||
|
return -1;
|
||||||
|
else if (colors < 3)
|
||||||
|
return colors;
|
||||||
|
else if (colors < 4)
|
||||||
|
return 2;
|
||||||
|
else if (colors < 16)
|
||||||
|
return 4;
|
||||||
|
else
|
||||||
|
return 8;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readColorTable(SubFile *file, SubFile::Handle &hdl, QImage& img,
|
||||||
|
int colors, int bpp, bool transparent)
|
||||||
|
{
|
||||||
|
img.setColorCount(colors);
|
||||||
|
|
||||||
|
if (transparent) {
|
||||||
|
quint8 byte;
|
||||||
|
quint32 bits = 0, reg = 0, mask = 0x000000FF;
|
||||||
|
|
||||||
|
for (int i = 0; i < colors; i++) {
|
||||||
|
while (bits < 28) {
|
||||||
|
if (!file->readByte(hdl, byte))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mask = 0x000000FF << bits;
|
||||||
|
reg = reg & (~mask);
|
||||||
|
reg = reg | (byte << bits);
|
||||||
|
bits += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.setColor(i, qRgba((reg >> 16) & 0x0FF, (reg >> 8) & 0x0FF,
|
||||||
|
reg & 0x0FF, ~((reg >> 24) & 0x0F) << 4));
|
||||||
|
|
||||||
|
reg = reg >> 28;
|
||||||
|
bits -= 28;
|
||||||
|
}
|
||||||
|
for (int i = colors; i < 1<<bpp; i++)
|
||||||
|
img.setColor(i, qRgba(0, 0, 0, 0));
|
||||||
|
} else {
|
||||||
|
QColor color;
|
||||||
|
|
||||||
|
for (int i = 0; i < colors; i++) {
|
||||||
|
if (!readColor(file, hdl, color))
|
||||||
|
return false;
|
||||||
|
img.setColor(i, color.rgb());
|
||||||
|
}
|
||||||
|
for (int i = colors; i < 1<<bpp; i++)
|
||||||
|
img.setColor(i, qRgba(0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Style::parsePoints(SubFile *file, SubFile::Handle &hdl,
|
||||||
|
const Section §ion)
|
||||||
|
{
|
||||||
|
for (quint32 i = 0; i < section.arraySize / section.arrayItemSize; i++) {
|
||||||
|
if (!file->seek(hdl, section.arrayOffset + i * section.arrayItemSize))
|
||||||
|
return false;
|
||||||
|
ItemInfo info;
|
||||||
|
if (!itemInfo(file, hdl, section, info))
|
||||||
|
return false;
|
||||||
|
quint32 type = info.extended
|
||||||
|
? 0x10000 | (info.type << 8) | info.subtype
|
||||||
|
: (info.type << 8) | info.subtype;
|
||||||
|
|
||||||
|
quint8 t8_1, width, height, numColors, imgType;
|
||||||
|
if (!(file->seek(hdl, section.offset + info.offset)
|
||||||
|
&& file->readByte(hdl, t8_1) && file->readByte(hdl, width)
|
||||||
|
&& file->readByte(hdl, height) && file->readByte(hdl, numColors)
|
||||||
|
&& file->readByte(hdl, imgType)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool localization = t8_1 & 0x04;
|
||||||
|
bool textColor = t8_1 & 0x08;
|
||||||
|
|
||||||
|
int bpp = colors2bpp(numColors, imgType);
|
||||||
|
if (bpp < 0)
|
||||||
|
continue;
|
||||||
|
QImage img(width, height, QImage::Format_Indexed8);
|
||||||
|
if (!readColorTable(file, hdl, img, numColors, bpp, imgType == 0x20))
|
||||||
|
return false;
|
||||||
|
if (!readBitmap(file, hdl, img, bpp))
|
||||||
|
return false;
|
||||||
|
_points[type] = Point(img);
|
||||||
|
|
||||||
|
if (t8_1 & 0x02) {
|
||||||
|
if (!(file->readByte(hdl, numColors)
|
||||||
|
&& file->readByte(hdl, imgType)))
|
||||||
|
return false;
|
||||||
|
if ((bpp = colors2bpp(numColors, imgType)) < 0)
|
||||||
|
continue;
|
||||||
|
if (!readColorTable(file, hdl, img, numColors, bpp, imgType == 0x20))
|
||||||
|
return false;
|
||||||
|
if (!readBitmap(file, hdl, img, bpp))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localization && !skipLocalization(file, hdl))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (textColor) {
|
||||||
|
quint8 labelFlags;
|
||||||
|
QColor color;
|
||||||
|
if (!file->readByte(hdl, labelFlags))
|
||||||
|
return false;
|
||||||
|
if (labelFlags & 0x08) {
|
||||||
|
if (!readColor(file, hdl, color))
|
||||||
|
return false;
|
||||||
|
_points[type].setTextColor(color);
|
||||||
|
}
|
||||||
|
_points[type].setTextFontSize((FontSize)(labelFlags & 0x07));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Style::parseDrawOrder(SubFile *file, SubFile::Handle &hdl,
|
||||||
|
const Section &order)
|
||||||
|
{
|
||||||
|
QList<quint32> drawOrder;
|
||||||
|
|
||||||
|
if (!file->seek(hdl, order.arrayOffset))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (quint32 i = 0; i < order.arraySize / order.arrayItemSize; i++) {
|
||||||
|
quint8 type;
|
||||||
|
quint32 subtype;
|
||||||
|
|
||||||
|
if (!(file->readByte(hdl, type) && file->readUInt32(hdl, subtype)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!subtype)
|
||||||
|
drawOrder.append(((quint32)type) << 8);
|
||||||
|
else {
|
||||||
|
for (int j = 0; j < 32; j++) {
|
||||||
|
quint32 mask = 1 << j;
|
||||||
|
if (subtype & mask)
|
||||||
|
drawOrder.append(0x010000 | (((quint32)type) << 8) | j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_drawOrder = drawOrder;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Style::parseTYPFile(SubFile *file)
|
||||||
|
{
|
||||||
|
SubFile::Handle hdl;
|
||||||
|
Section points, lines, polygons, order;
|
||||||
|
quint16 tmp16, codepage;
|
||||||
|
|
||||||
|
if (!(file->seek(hdl, 0x15) && file->readUInt16(hdl, codepage)
|
||||||
|
&& file->readUInt32(hdl, points.offset)
|
||||||
|
&& file->readUInt32(hdl, points.size)
|
||||||
|
&& file->readUInt32(hdl, lines.offset)
|
||||||
|
&& file->readUInt32(hdl, lines.size)
|
||||||
|
&& file->readUInt32(hdl, polygons.offset)
|
||||||
|
&& file->readUInt32(hdl, polygons.size)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!(file->readUInt16(hdl, tmp16) && file->readUInt16(hdl, tmp16)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!(file->readUInt32(hdl, points.arrayOffset)
|
||||||
|
&& file->readUInt16(hdl, points.arrayItemSize)
|
||||||
|
&& file->readUInt32(hdl, points.arraySize)
|
||||||
|
&& file->readUInt32(hdl, lines.arrayOffset)
|
||||||
|
&& file->readUInt16(hdl, lines.arrayItemSize)
|
||||||
|
&& file->readUInt32(hdl, lines.arraySize)
|
||||||
|
&& file->readUInt32(hdl, polygons.arrayOffset)
|
||||||
|
&& file->readUInt16(hdl, polygons.arrayItemSize)
|
||||||
|
&& file->readUInt32(hdl, polygons.arraySize)
|
||||||
|
&& file->readUInt32(hdl, order.arrayOffset)
|
||||||
|
&& file->readUInt16(hdl, order.arrayItemSize)
|
||||||
|
&& file->readUInt32(hdl, order.arraySize)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!(parsePoints(file, hdl, points) && parseLines(file, hdl, lines)
|
||||||
|
&& parsePolygons(file, hdl, polygons)
|
||||||
|
&& parseDrawOrder(file, hdl, order))) {
|
||||||
|
qWarning("%s: Invalid TYP file, using default style",
|
||||||
|
qPrintable(file->imgName()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Style::Style(SubFile *typ)
|
||||||
|
{
|
||||||
|
defaultLineStyle();
|
||||||
|
defaultPolygonStyle();
|
||||||
|
|
||||||
|
if (typ)
|
||||||
|
parseTYPFile(typ);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Style::Line &Style::line(quint32 type) const
|
||||||
|
{
|
||||||
|
static Line null;
|
||||||
|
|
||||||
|
QMap<quint32, 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<quint32, 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<quint16, Point>::const_iterator it = _points.find(type);
|
||||||
|
return (it == _points.constEnd()) ? null : *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Style::isContourLine(quint32 type)
|
||||||
|
{
|
||||||
|
return (type == TYPE(0x20) || type == TYPE(0x21) || type == TYPE(0x22)
|
||||||
|
|| type == TYPE(23) || type == TYPE(24) || type == TYPE(25));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
static QString penColor(const QPen &pen)
|
||||||
|
{
|
||||||
|
return (pen == Qt::NoPen) ? "None" : pen.color().name();
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString brushColor(const QBrush &brush)
|
||||||
|
{
|
||||||
|
return (brush == Qt::NoBrush) ? "None" : brush.color().name();
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug dbg, const Style::Polygon &polygon)
|
||||||
|
{
|
||||||
|
dbg.nospace() << "Polygon(" << brushColor(polygon.brush()) << ", "
|
||||||
|
<< penColor(polygon.pen()) << ")";
|
||||||
|
return dbg.space();
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug dbg, const Style::Line &line)
|
||||||
|
{
|
||||||
|
dbg.nospace() << "Line(" << penColor(line.foreground()) << ", "
|
||||||
|
<< penColor(line.background()) << ", " << !line.img().isNull() << ")";
|
||||||
|
return dbg.space();
|
||||||
|
}
|
||||||
|
#endif // QT_NO_DEBUG
|
135
src/map/IMG/style.h
Normal file
135
src/map/IMG/style.h
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#ifndef STYLE_H
|
||||||
|
#define STYLE_H
|
||||||
|
|
||||||
|
#include <QPen>
|
||||||
|
#include <QBrush>
|
||||||
|
#include <QDebug>
|
||||||
|
#include "subfile.h"
|
||||||
|
|
||||||
|
|
||||||
|
class Style
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum FontSize {
|
||||||
|
NotSet = 0,
|
||||||
|
None = 1,
|
||||||
|
Small = 2,
|
||||||
|
Normal = 3,
|
||||||
|
Large = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
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(NotSet) {}
|
||||||
|
Line(const QPen &foreground, const QPen &background = Qt::NoPen)
|
||||||
|
: _foreground(foreground), _background(background),
|
||||||
|
_textFontSize(NotSet) {}
|
||||||
|
Line(const QImage &img)
|
||||||
|
: _foreground(Qt::NoPen), _background(Qt::NoPen),
|
||||||
|
_textFontSize(NotSet), _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() : _textFontSize(NotSet) {}
|
||||||
|
Point(const QImage &img) : _textFontSize(NotSet), _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(SubFile *typ = 0);
|
||||||
|
|
||||||
|
const Line &line(quint32 type) const;
|
||||||
|
const Polygon &polygon(quint32 type) const;
|
||||||
|
const Point &point(quint32 type) const;
|
||||||
|
const QList<quint32> &drawOrder() const {return _drawOrder;}
|
||||||
|
|
||||||
|
static bool isContourLine(quint32 type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Section {
|
||||||
|
quint32 offset;
|
||||||
|
quint32 size;
|
||||||
|
quint32 arrayOffset;
|
||||||
|
quint32 arraySize;
|
||||||
|
quint16 arrayItemSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ItemInfo {
|
||||||
|
quint32 offset;
|
||||||
|
quint8 type;
|
||||||
|
quint8 subtype;
|
||||||
|
bool extended;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool parseTYPFile(SubFile *file);
|
||||||
|
bool parsePoints(SubFile *file, SubFile::Handle &hdl,
|
||||||
|
const Section §ion);
|
||||||
|
bool parseLines(SubFile *file, SubFile::Handle &hdl,
|
||||||
|
const Section §ion);
|
||||||
|
bool parsePolygons(SubFile *file, SubFile::Handle &hdl,
|
||||||
|
const Section §ion);
|
||||||
|
bool parseDrawOrder(SubFile *file, SubFile::Handle &hdl,
|
||||||
|
const Section §ion);
|
||||||
|
bool itemInfo(SubFile *file, SubFile::Handle &hdl,
|
||||||
|
const Section §ion, ItemInfo &info);
|
||||||
|
void defaultPolygonStyle();
|
||||||
|
void defaultLineStyle();
|
||||||
|
|
||||||
|
QMap<quint32, Line> _lines;
|
||||||
|
QMap<quint32, Polygon> _polygons;
|
||||||
|
QMap<quint16, Point> _points;
|
||||||
|
QList<quint32> _drawOrder;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
QDebug operator<<(QDebug dbg, const Style::Polygon &polygon);
|
||||||
|
QDebug operator<<(QDebug dbg, const Style::Line &line);
|
||||||
|
#endif // QT_NO_DEBUG
|
||||||
|
|
||||||
|
#endif // STYLE_H
|
61
src/map/IMG/subdiv.h
Normal file
61
src/map/IMG/subdiv.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#ifndef SUBDIV_H
|
||||||
|
#define SUBDIV_H
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include "common/coordinates.h"
|
||||||
|
#include "units.h"
|
||||||
|
|
||||||
|
class SubDiv {
|
||||||
|
public:
|
||||||
|
SubDiv(quint32 offset, qint32 lon, qint32 lat, int bits, quint8 objects)
|
||||||
|
: _offset(offset), _end(0), _lon(lon), _lat(lat), _bits(bits),
|
||||||
|
_objects(objects), _polygonsOffset(0), _polygonsEnd(0), _linesOffset(0),
|
||||||
|
_linesEnd(0), _pointsOffset(0), _pointsEnd(0) {}
|
||||||
|
void setEnd(quint32 end) {_end = end;}
|
||||||
|
|
||||||
|
quint32 offset() const {return _offset;}
|
||||||
|
quint32 end() const {return _end;}
|
||||||
|
qint32 lon() const {return _lon;}
|
||||||
|
qint32 lat() const {return _lat;}
|
||||||
|
quint8 bits() const {return _bits;}
|
||||||
|
quint8 objects() const {return _objects;}
|
||||||
|
|
||||||
|
// Extended types objects (TRE7)
|
||||||
|
void setExtOffsets(quint32 polygon, quint32 line, quint32 point)
|
||||||
|
{_polygonsOffset = polygon; _linesOffset = line; _pointsOffset = point;}
|
||||||
|
void setExtEnds(quint32 polygon, quint32 line, quint32 point)
|
||||||
|
{_polygonsEnd = polygon; _linesEnd = line; _pointsEnd = point;}
|
||||||
|
|
||||||
|
quint32 polygonsOffset() const {return _polygonsOffset;}
|
||||||
|
quint32 polygonsEnd() const {return _polygonsEnd;}
|
||||||
|
quint32 linesOffset() const {return _linesOffset;}
|
||||||
|
quint32 linesEnd() const {return _linesEnd;}
|
||||||
|
quint32 pointsOffset() const {return _pointsOffset;}
|
||||||
|
quint32 pointsEnd() const {return _pointsEnd;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
quint32 _offset;
|
||||||
|
quint32 _end;
|
||||||
|
qint32 _lon, _lat;
|
||||||
|
quint8 _bits;
|
||||||
|
quint8 _objects;
|
||||||
|
|
||||||
|
quint32 _polygonsOffset;
|
||||||
|
quint32 _polygonsEnd;
|
||||||
|
quint32 _linesOffset;
|
||||||
|
quint32 _linesEnd;
|
||||||
|
quint32 _pointsOffset;
|
||||||
|
quint32 _pointsEnd;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
inline QDebug operator<<(QDebug dbg, const SubDiv &subdiv)
|
||||||
|
{
|
||||||
|
Coordinates c(toWGS84(subdiv.lon()), toWGS84(subdiv.lat()));
|
||||||
|
dbg.nospace() << "SubDiv(" << c << ", " << subdiv.offset()
|
||||||
|
<< ", " << subdiv.end() << ", " << subdiv.objects() << ")";
|
||||||
|
return dbg.space();
|
||||||
|
}
|
||||||
|
#endif // QT_NO_DEBUG
|
||||||
|
|
||||||
|
#endif // SUBDIV_H
|
76
src/map/IMG/subfile.cpp
Normal file
76
src/map/IMG/subfile.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#include "img.h"
|
||||||
|
#include "subfile.h"
|
||||||
|
|
||||||
|
SubFile::Type SubFile::type(const char str[3])
|
||||||
|
{
|
||||||
|
if (!memcmp(str, "TRE", 3))
|
||||||
|
return TRE;
|
||||||
|
else if (!memcmp(str, "RGN", 3))
|
||||||
|
return RGN;
|
||||||
|
else if (!memcmp(str, "LBL", 3))
|
||||||
|
return LBL;
|
||||||
|
else if (!memcmp(str, "TYP", 3))
|
||||||
|
return TYP;
|
||||||
|
else if (!memcmp(str, "GMP", 3))
|
||||||
|
return GMP;
|
||||||
|
else if (!memcmp(str, "NET", 3))
|
||||||
|
return NET;
|
||||||
|
else
|
||||||
|
return Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SubFile::isValid() const
|
||||||
|
{
|
||||||
|
return ((quint32)_img->blockSize() * (quint32)_blocks.size() - _size
|
||||||
|
< (quint32)_img->blockSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SubFile::seek(Handle &handle, quint32 pos) const
|
||||||
|
{
|
||||||
|
quint32 blockSize = _img->blockSize();
|
||||||
|
int blockNum = pos / blockSize;
|
||||||
|
|
||||||
|
if (handle.blockNum != blockNum) {
|
||||||
|
if (blockNum >= _blocks.size())
|
||||||
|
return false;
|
||||||
|
handle.data = _img->readBlock(_blocks.at(blockNum));
|
||||||
|
if (handle.data.isNull())
|
||||||
|
return false;
|
||||||
|
handle.blockNum = blockNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.blockPos = pos % blockSize;
|
||||||
|
handle.pos = pos;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SubFile::readByte(Handle &handle, quint8 &val) const
|
||||||
|
{
|
||||||
|
val = handle.data.at(handle.blockPos++);
|
||||||
|
handle.pos++;
|
||||||
|
return (handle.blockPos >= _img->blockSize())
|
||||||
|
? seek(handle, handle.pos) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &SubFile::imgName() const
|
||||||
|
{
|
||||||
|
return _img->name();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
QDebug operator<<(QDebug dbg, const SubFile &file)
|
||||||
|
{
|
||||||
|
bool continuous = true;
|
||||||
|
for (int i = 1; i < file._blocks.size(); i++) {
|
||||||
|
if (file._blocks.at(i) != file._blocks.at(i-1) + 1) {
|
||||||
|
continuous = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg.nospace() << "SubFile(" << file._size << ", " << file._blocks.size()
|
||||||
|
<< ", " << continuous << ")";
|
||||||
|
return dbg.space();
|
||||||
|
}
|
||||||
|
#endif // QT_NO_DEBUG
|
98
src/map/IMG/subfile.h
Normal file
98
src/map/IMG/subfile.h
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#ifndef SUBFILE_H
|
||||||
|
#define SUBFILE_H
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
class IMG;
|
||||||
|
|
||||||
|
class SubFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Type {Unknown, TRE, RGN, LBL, NET, TYP, GMP};
|
||||||
|
|
||||||
|
struct Handle
|
||||||
|
{
|
||||||
|
Handle() : blockNum(-1), blockPos(-1), pos(-1) {}
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
int blockNum;
|
||||||
|
int blockPos;
|
||||||
|
int pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
SubFile(IMG *img, quint32 size) : _img(img), _size(size) {}
|
||||||
|
|
||||||
|
void addBlock(quint16 block) {_blocks.append(block);}
|
||||||
|
bool isValid() const;
|
||||||
|
|
||||||
|
quint32 size() const {return _size;}
|
||||||
|
bool seek(Handle &handle, quint32 pos) const;
|
||||||
|
bool readByte(Handle &handle, quint8 &val) const;
|
||||||
|
|
||||||
|
bool readUInt16(Handle &handle, quint16 &val) const
|
||||||
|
{
|
||||||
|
quint8 b0, b1;
|
||||||
|
if (!(readByte(handle, b0) && readByte(handle, b1)))
|
||||||
|
return false;
|
||||||
|
val = b0 | ((quint16)b1) << 8;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readInt16(Handle &handle, qint16 &val) const
|
||||||
|
{
|
||||||
|
if (!readUInt16(handle, (quint16&)val))
|
||||||
|
return false;
|
||||||
|
if((quint16)val > 0x7FFF)
|
||||||
|
val = (val & 0x7FFF) - 0x8000;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readUInt24(Handle &handle, quint32 &val) const
|
||||||
|
{
|
||||||
|
quint8 b0, b1, b2;
|
||||||
|
if (!(readByte(handle, b0) && readByte(handle, b1)
|
||||||
|
&& readByte(handle, b2)))
|
||||||
|
return false;
|
||||||
|
val = b0 | ((quint32)b1) << 8 | ((quint32)b2) << 16;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readInt24(Handle &handle, qint32 &val) const
|
||||||
|
{
|
||||||
|
if (!readUInt24(handle, (quint32&)val))
|
||||||
|
return false;
|
||||||
|
if (val > 0x7FFFFF)
|
||||||
|
val = (val & 0x7FFFFF) - 0x800000;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readUInt32(Handle &handle, quint32 &val) const
|
||||||
|
{
|
||||||
|
quint8 b0, b1, b2, b3;
|
||||||
|
if (!(readByte(handle, b0) && readByte(handle, b1)
|
||||||
|
&& readByte(handle, b2) && readByte(handle, b3)))
|
||||||
|
return false;
|
||||||
|
val = b0 | ((quint32)b1) << 8 | ((quint32)b2) << 16
|
||||||
|
| ((quint32)b3) << 24;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 offset() const {return _blocks.first();}
|
||||||
|
const QString &imgName() const;
|
||||||
|
|
||||||
|
static Type type(const char str[3]);
|
||||||
|
|
||||||
|
friend QDebug operator<<(QDebug dbg, const SubFile &file);
|
||||||
|
|
||||||
|
private:
|
||||||
|
IMG *_img;
|
||||||
|
quint32 _size;
|
||||||
|
QVector<quint16> _blocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
QDebug operator<<(QDebug dbg, const SubFile &file);
|
||||||
|
#endif // QT_NO_DEBUG
|
||||||
|
|
||||||
|
#endif // SUBFILE_H
|
224
src/map/IMG/textpathitem.cpp
Normal file
224
src/map/IMG/textpathitem.cpp
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
#include <QFont>
|
||||||
|
#include <QPainter>
|
||||||
|
#include "textpathitem.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_TEXT_ANGLE 30
|
||||||
|
|
||||||
|
static bool intersection(const QLineF &line, const QRectF &rect,
|
||||||
|
QPointF *p)
|
||||||
|
{
|
||||||
|
if (line.intersect(QLineF(rect.topLeft(), rect.topRight()), p)
|
||||||
|
== QLineF::BoundedIntersection)
|
||||||
|
return true;
|
||||||
|
if (line.intersect(QLineF(rect.topLeft(), rect.bottomLeft()), p)
|
||||||
|
== QLineF::BoundedIntersection)
|
||||||
|
return true;
|
||||||
|
if (line.intersect(QLineF(rect.bottomRight(), rect.bottomLeft()), p)
|
||||||
|
== QLineF::BoundedIntersection)
|
||||||
|
return true;
|
||||||
|
if (line.intersect(QLineF(rect.bottomRight(), rect.topRight()), p)
|
||||||
|
== QLineF::BoundedIntersection)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QPainterPath subpath(const QList<QLineF> &lines, int start, int end,
|
||||||
|
qreal cut)
|
||||||
|
{
|
||||||
|
qreal ss = 0, es = 0;
|
||||||
|
int si = start, ei = end;
|
||||||
|
|
||||||
|
for (int i = start; i <= end; i++) {
|
||||||
|
qreal len = lines.at(i).length();
|
||||||
|
if (ss + len < cut / 2) {
|
||||||
|
ss += len;
|
||||||
|
si++;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (int i = end; i >= start; i--) {
|
||||||
|
qreal len = lines.at(i).length();
|
||||||
|
if (es + len < cut / 2) {
|
||||||
|
es += len;
|
||||||
|
ei--;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLineF sl(lines.at(si).p2(), lines.at(si).p1());
|
||||||
|
sl.setLength(sl.length() - (cut / 2 - ss));
|
||||||
|
QLineF el(lines.at(ei));
|
||||||
|
el.setLength(el.length() - (cut / 2 - es));
|
||||||
|
|
||||||
|
QPainterPath p(sl.p2());
|
||||||
|
for (int i = si; i <= ei; i++)
|
||||||
|
p.lineTo(lines.at(i).p2());
|
||||||
|
p.setElementPositionAt(p.elementCount() - 1, el.p2().x(), el.p2().y());
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QList<QLineF> lineString(const QPolygonF &path,
|
||||||
|
const QRectF &boundingRect)
|
||||||
|
{
|
||||||
|
QList<QLineF> lines;
|
||||||
|
int start = 0, end = path.count() - 1;
|
||||||
|
QPointF p;
|
||||||
|
|
||||||
|
for (int i = 0; i < path.count(); i++) {
|
||||||
|
if (boundingRect.contains(path.at(i))) {
|
||||||
|
start = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = path.count() - 1; i >= 0; i--) {
|
||||||
|
if (boundingRect.contains(path.at(i))) {
|
||||||
|
end = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start > 0) {
|
||||||
|
QLineF l(path.at(start-1), path.at(start));
|
||||||
|
if (intersection(l, boundingRect, &p))
|
||||||
|
lines.append(QLineF(p, path.at(start)));
|
||||||
|
}
|
||||||
|
for (int i = start + 1; i <= end; i++)
|
||||||
|
lines.append(QLineF(path.at(i-1), path.at(i)));
|
||||||
|
if (end < path.count() - 1) {
|
||||||
|
QLineF l(path.at(end), path.at(end+1));
|
||||||
|
if (intersection(l, boundingRect, &p))
|
||||||
|
lines.append(QLineF(path.at(end), p));
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QPainterPath textPath(const QPolygonF &path, qreal textWidth,
|
||||||
|
qreal charWidth, const QRectF &tileRect)
|
||||||
|
{
|
||||||
|
QList<QLineF> lines(lineString(path, tileRect));
|
||||||
|
qreal length = 0;
|
||||||
|
qreal angle = lines.first().angle();
|
||||||
|
int last = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
|
qreal sl = lines.at(i).length();
|
||||||
|
qreal a = lines.at(i).angle();
|
||||||
|
|
||||||
|
if (!tileRect.contains(lines.at(i).p2()) || sl < charWidth
|
||||||
|
|| qAbs(angle - a) > MAX_TEXT_ANGLE) {
|
||||||
|
if (length > textWidth)
|
||||||
|
return subpath(lines, last, i - 1, length - textWidth);
|
||||||
|
last = i;
|
||||||
|
length = 0;
|
||||||
|
} else
|
||||||
|
length += sl;
|
||||||
|
|
||||||
|
angle = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (length > textWidth)
|
||||||
|
? subpath(lines, last, lines.size() - 1, length - textWidth)
|
||||||
|
: QPainterPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool reverse(const QPainterPath &path)
|
||||||
|
{
|
||||||
|
QLineF l(path.elementAt(0), path.elementAt(1));
|
||||||
|
qreal angle = l.angle();
|
||||||
|
return (angle > 90 && angle < 270) ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TextPathItem::TextPathItem(const QPolygonF &line, const QString *label,
|
||||||
|
const QRect &tileRect, const QFont *font, const QColor *color)
|
||||||
|
: _text(label), _font(font), _color(color)
|
||||||
|
{
|
||||||
|
qreal cw = font->pixelSize() * 0.7;
|
||||||
|
qreal textWidth = _text->size() * cw;
|
||||||
|
qreal mw = font->pixelSize() / 2;
|
||||||
|
_path = textPath(line, textWidth, cw, tileRect.adjusted(mw, mw, -mw, -mw));
|
||||||
|
if (_path.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (reverse(_path))
|
||||||
|
_path = _path.toReversed();
|
||||||
|
|
||||||
|
QPainterPathStroker s;
|
||||||
|
s.setWidth(font->pixelSize());
|
||||||
|
s.setCapStyle(Qt::FlatCap);
|
||||||
|
_shape = s.createStroke(_path).simplified();
|
||||||
|
_rect = _shape.boundingRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextPathItem::collides(const QVector<TextPathItem> &list) const
|
||||||
|
{
|
||||||
|
if (_rect.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
const TextPathItem &other = list.at(i);
|
||||||
|
if (other._rect.isEmpty() || !_rect.intersects(other._rect))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (other._shape.intersects(_shape))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextPathItem::paint(QPainter *painter) const
|
||||||
|
{
|
||||||
|
QFontMetrics fm(*_font);
|
||||||
|
int textWidth = fm.width(*_text);
|
||||||
|
|
||||||
|
qreal factor = (textWidth) / qMax(_path.length(), (qreal)textWidth);
|
||||||
|
qreal percent = (1.0 - factor) / 2.0;
|
||||||
|
|
||||||
|
QTransform t = painter->transform();
|
||||||
|
|
||||||
|
painter->setFont(*_font);
|
||||||
|
painter->setPen(Qt::white);
|
||||||
|
|
||||||
|
for (int i = 0; i < _text->size(); i++) {
|
||||||
|
QPointF point = _path.pointAtPercent(percent);
|
||||||
|
qreal angle = _path.angleAtPercent(percent);
|
||||||
|
|
||||||
|
painter->translate(point);
|
||||||
|
painter->rotate(-angle);
|
||||||
|
painter->drawText(QPoint(-1, fm.descent() - 1), _text->at(i));
|
||||||
|
painter->drawText(QPoint(1, fm.descent() + 1), _text->at(i));
|
||||||
|
painter->drawText(QPoint(-1, fm.descent() + 1), _text->at(i));
|
||||||
|
painter->drawText(QPoint(1, fm.descent() -1), _text->at(i));
|
||||||
|
painter->drawText(QPoint(0, fm.descent() - 1), _text->at(i));
|
||||||
|
painter->drawText(QPoint(0, fm.descent() + 1), _text->at(i));
|
||||||
|
painter->drawText(QPoint(-1, fm.descent()), _text->at(i));
|
||||||
|
painter->drawText(QPoint(1, fm.descent()), _text->at(i));
|
||||||
|
painter->setTransform(t);
|
||||||
|
|
||||||
|
int width = fm.charWidth(*_text, i);
|
||||||
|
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++) {
|
||||||
|
QPointF point = _path.pointAtPercent(percent);
|
||||||
|
qreal angle = _path.angleAtPercent(percent);
|
||||||
|
|
||||||
|
painter->translate(point);
|
||||||
|
painter->rotate(-angle);
|
||||||
|
painter->drawText(QPoint(0, fm.descent()), _text->at(i));
|
||||||
|
painter->setTransform(t);
|
||||||
|
|
||||||
|
int width = fm.charWidth(*_text, i);
|
||||||
|
percent += ((qreal)width / (qreal)textWidth) * factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
//painter->setPen(Qt::red);
|
||||||
|
//painter->drawPath(_shape);
|
||||||
|
}
|
28
src/map/IMG/textpathitem.h
Normal file
28
src/map/IMG/textpathitem.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef TEXTPATHITEM_H
|
||||||
|
#define TEXTPATHITEM_H
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
#include <QPainterPath>
|
||||||
|
#include "img.h"
|
||||||
|
|
||||||
|
class TextPathItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TextPathItem() : _text(0), _font(0), _color(0) {}
|
||||||
|
TextPathItem(const QPolygonF &line, const QString *label,
|
||||||
|
const QRect &tileRect, const QFont *font, const QColor *color);
|
||||||
|
|
||||||
|
bool isValid() const {return !_path.isEmpty();}
|
||||||
|
bool collides(const QVector<TextPathItem> &list) const;
|
||||||
|
void paint(QPainter *painter) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString *_text;
|
||||||
|
const QFont *_font;
|
||||||
|
const QColor *_color;
|
||||||
|
QPainterPath _path;
|
||||||
|
QRectF _rect;
|
||||||
|
QPainterPath _shape;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TEXTPATHITEM_H
|
76
src/map/IMG/textpointitem.cpp
Normal file
76
src/map/IMG/textpointitem.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#include <QFont>
|
||||||
|
#include <QFontMetrics>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QPainter>
|
||||||
|
#include "textpointitem.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define FLAGS (Qt::AlignCenter | Qt::TextWordWrap | Qt::TextDontClip)
|
||||||
|
#define MAX_TEXT_WIDTH 8
|
||||||
|
|
||||||
|
TextPointItem::TextPointItem(const QPoint &point, const QString *text,
|
||||||
|
const QFont *font, const QImage *img, const QColor *color)
|
||||||
|
: _text(text), _font(font), _img(img), _color(color)
|
||||||
|
{
|
||||||
|
QRect iconRect;
|
||||||
|
|
||||||
|
if (text) {
|
||||||
|
QFontMetrics fm(*font);
|
||||||
|
int limit = font->pixelSize() * MAX_TEXT_WIDTH;
|
||||||
|
_textRect = fm.boundingRect(QRect(0, 0, limit, 0), FLAGS, *text);
|
||||||
|
}
|
||||||
|
if (img) {
|
||||||
|
iconRect = QRect(QPoint(point.x() - img->width()/2, point.y()
|
||||||
|
- img->height()/2), img->size());
|
||||||
|
_textRect.moveTopLeft(QPoint(point.x() + img->width(), point.y()
|
||||||
|
- _textRect.height()/2));
|
||||||
|
} else
|
||||||
|
_textRect.moveCenter(point);
|
||||||
|
|
||||||
|
_rect = _textRect | iconRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextPointItem::collides(const QVector<TextPointItem> &list) const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < list.size(); i++)
|
||||||
|
if (list.at(i)._rect.intersects(_rect))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextPointItem::paint(QPainter *painter) const
|
||||||
|
{
|
||||||
|
if (_img)
|
||||||
|
painter->drawImage(QPoint(_rect.left(), _rect.center().y()
|
||||||
|
- _img->height()/2), *_img);
|
||||||
|
|
||||||
|
if (_text) {
|
||||||
|
QImage img(_textRect.size(), QImage::Format_ARGB32_Premultiplied);
|
||||||
|
img.fill(Qt::transparent);
|
||||||
|
QPainter ip(&img);
|
||||||
|
ip.setPen(Qt::white);
|
||||||
|
ip.setFont(*_font);
|
||||||
|
ip.drawText(img.rect(), FLAGS, *_text);
|
||||||
|
|
||||||
|
painter->drawImage(_textRect.x() - 1, _textRect.y() - 1, img);
|
||||||
|
painter->drawImage(_textRect.x() + 1, _textRect.y() + 1, img);
|
||||||
|
painter->drawImage(_textRect.x() - 1, _textRect.y() + 1, img);
|
||||||
|
painter->drawImage(_textRect.x(), _textRect.y() - 1, img);
|
||||||
|
painter->drawImage(_textRect.x(), _textRect.y() + 1, img);
|
||||||
|
painter->drawImage(_textRect.x() - 1, _textRect.y(), img);
|
||||||
|
painter->drawImage(_textRect.x() + 1, _textRect.y(), img);
|
||||||
|
|
||||||
|
if (_color) {
|
||||||
|
painter->setFont(*_font);
|
||||||
|
painter->setPen(*_color);
|
||||||
|
painter->drawText(_textRect, FLAGS, *_text);
|
||||||
|
} else {
|
||||||
|
img.invertPixels();
|
||||||
|
painter->drawImage(_textRect, img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//painter->setPen(Qt::red);
|
||||||
|
//painter->drawRect(_rect);
|
||||||
|
}
|
31
src/map/IMG/textpointitem.h
Normal file
31
src/map/IMG/textpointitem.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef TEXTPOINTITEM_H
|
||||||
|
#define TEXTPOINTITEM_H
|
||||||
|
|
||||||
|
#include <QRect>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
class QPainter;
|
||||||
|
class QFont;
|
||||||
|
class QImage;
|
||||||
|
class QColor;
|
||||||
|
|
||||||
|
class TextPointItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TextPointItem() : _text(0), _font(0), _img(0) {}
|
||||||
|
TextPointItem(const QPoint &point, const QString *text, const QFont *font,
|
||||||
|
const QImage *img, const QColor *color);
|
||||||
|
|
||||||
|
bool collides(const QVector<TextPointItem> &list) const;
|
||||||
|
void paint(QPainter *painter) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString *_text;
|
||||||
|
const QFont *_font;
|
||||||
|
const QImage *_img;
|
||||||
|
const QColor *_color;
|
||||||
|
QRect _rect, _textRect;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TEXTPOINTITEM_H
|
222
src/map/IMG/trefile.cpp
Normal file
222
src/map/IMG/trefile.cpp
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
#include "subdiv.h"
|
||||||
|
#include "units.h"
|
||||||
|
#include "trefile.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct MapLevel {
|
||||||
|
quint8 level;
|
||||||
|
quint8 bits;
|
||||||
|
quint16 subdivs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
static QDebug operator<<(QDebug dbg, const MapLevel &ml)
|
||||||
|
{
|
||||||
|
bool inherited = ml.level & 0x80 ? true : false;
|
||||||
|
dbg.nospace() << "MapLevel(" << (ml.level & 0x7F) << ", " << inherited
|
||||||
|
<< ", " << ml.bits << ", " << ml.subdivs << ")";
|
||||||
|
return dbg.space();
|
||||||
|
}
|
||||||
|
#endif // QT_NO_DEBUG
|
||||||
|
|
||||||
|
static void unlock(quint8 *dst, const quint8 *src, quint32 size, quint32 key)
|
||||||
|
{
|
||||||
|
static const unsigned char shuf[] = {
|
||||||
|
0xb, 0xc, 0xa, 0x0,
|
||||||
|
0x8, 0xf, 0x2, 0x1,
|
||||||
|
0x6, 0x4, 0x9, 0x3,
|
||||||
|
0xd, 0x5, 0x7, 0xe
|
||||||
|
};
|
||||||
|
|
||||||
|
int sum = shuf[((key >> 24) + (key >> 16) + (key >> 8) + key) & 0xf];
|
||||||
|
|
||||||
|
for (quint32 i = 0, ringctr = 16; i < size; i++) {
|
||||||
|
quint32 upper = src[i] >> 4;
|
||||||
|
quint32 lower = src[i];
|
||||||
|
|
||||||
|
upper -= sum;
|
||||||
|
upper -= key >> ringctr;
|
||||||
|
upper -= shuf[(key >> ringctr) & 0xf];
|
||||||
|
ringctr = ringctr ? ringctr - 4 : 16;
|
||||||
|
|
||||||
|
lower -= sum;
|
||||||
|
lower -= key >> ringctr;
|
||||||
|
lower -= shuf[(key >> ringctr) & 0xf];
|
||||||
|
ringctr = ringctr ? ringctr - 4 : 16;
|
||||||
|
|
||||||
|
dst[i] = ((upper << 4) & 0xf0) | (lower & 0xf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TREFile::~TREFile()
|
||||||
|
{
|
||||||
|
SubDivTree::Iterator jt;
|
||||||
|
|
||||||
|
for (QMap<int, SubDivTree*>::iterator it = _subdivs.begin();
|
||||||
|
it != _subdivs.end(); ++it) {
|
||||||
|
SubDivTree *tree = *it;
|
||||||
|
for (tree->GetFirst(jt); !tree->IsNull(jt); tree->GetNext(jt))
|
||||||
|
delete tree->GetAt(jt);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (QMap<int, SubDivTree*>::iterator it = _subdivs.begin();
|
||||||
|
it != _subdivs.end(); ++it)
|
||||||
|
delete *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TREFile::init()
|
||||||
|
{
|
||||||
|
Handle hdl;
|
||||||
|
quint8 locked;
|
||||||
|
quint16 hdrLen;
|
||||||
|
|
||||||
|
if (!(isValid() && seek(hdl, 0) && readUInt16(hdl, hdrLen)
|
||||||
|
&& seek(hdl, 0x0D) && readByte(hdl, locked)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Tile bounds
|
||||||
|
qint32 north, east, south, west;
|
||||||
|
if (!(seek(hdl, 0x15) && readInt24(hdl, north) && readInt24(hdl, east)
|
||||||
|
&& readInt24(hdl, south) && readInt24(hdl, west)))
|
||||||
|
return false;
|
||||||
|
_bounds = RectC(Coordinates(toWGS84(west), toWGS84(north)),
|
||||||
|
Coordinates(toWGS84(east), toWGS84(south)));
|
||||||
|
|
||||||
|
quint32 levelsOffset, levelsSize, subdivOffset, subdivSize;
|
||||||
|
if (!(readUInt32(hdl, levelsOffset) && readUInt32(hdl, levelsSize)
|
||||||
|
&& readUInt32(hdl, subdivOffset) && readUInt32(hdl, subdivSize)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
quint32 extOffset, extSize = 0;
|
||||||
|
quint16 extItemSize = 0;
|
||||||
|
if (hdrLen > 0x9A) {
|
||||||
|
if (!(seek(hdl, 0x7C) && readUInt32(hdl, extOffset)
|
||||||
|
&& readUInt32(hdl, extSize) && readUInt16(hdl, extItemSize)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tile levels
|
||||||
|
if (levelsSize > 64 || !seek(hdl, levelsOffset))
|
||||||
|
return false;
|
||||||
|
quint8 levels[64];
|
||||||
|
for (quint32 i = 0; i < levelsSize; i++)
|
||||||
|
if (!readByte(hdl, levels[i]))
|
||||||
|
return false;
|
||||||
|
if (locked) {
|
||||||
|
quint32 key;
|
||||||
|
quint8 unlocked[64];
|
||||||
|
if (!seek(hdl, 0xAA) || !readUInt32(hdl, key))
|
||||||
|
return false;
|
||||||
|
unlock(unlocked, levels, levelsSize, key);
|
||||||
|
memcpy(levels, unlocked, levelsSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 levelsCount = levelsSize / 4;
|
||||||
|
QVector<MapLevel> ml(levelsCount);
|
||||||
|
QMap<int, int> level2bits;
|
||||||
|
|
||||||
|
for (quint32 i = 0; i < levelsCount; i++) {
|
||||||
|
quint8 *zoom = levels + (i * 4);
|
||||||
|
ml[i].level = *zoom;
|
||||||
|
ml[i].bits = *(zoom + 1);
|
||||||
|
ml[i].subdivs = *(zoom + 2) | (quint16)(*(zoom + 3)) << 8;
|
||||||
|
if ((ml[i].level & 0xF) > 15 || ml[i].bits > 24)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
level2bits.insert(ml[i].level & 0xF, ml[i].bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subdivisions
|
||||||
|
if (!seek(hdl, subdivOffset))
|
||||||
|
return false;
|
||||||
|
SubDiv *s = 0;
|
||||||
|
QList<SubDiv*> sl;
|
||||||
|
|
||||||
|
for (int i = 0; i < ml.size(); i++) {
|
||||||
|
if (!(ml.at(i).level & 0x80))
|
||||||
|
_subdivs.insert(ml.at(i).bits, new SubDivTree());
|
||||||
|
|
||||||
|
for (int j = 0; j < ml.at(i).subdivs; j++) {
|
||||||
|
quint32 offset;
|
||||||
|
qint32 lon, lat;
|
||||||
|
quint8 objects;
|
||||||
|
quint16 width, height, nextLevel;
|
||||||
|
|
||||||
|
if (!(readUInt24(hdl, offset) && readByte(hdl, objects)
|
||||||
|
&& readInt24(hdl, lon) && readInt24(hdl, lat)
|
||||||
|
&& readUInt16(hdl, width) && readUInt16(hdl, height)))
|
||||||
|
return false;
|
||||||
|
if (i != (int)levelsCount - 1)
|
||||||
|
if (!readUInt16(hdl, nextLevel))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (s)
|
||||||
|
s->setEnd(offset);
|
||||||
|
|
||||||
|
if (ml.at(i).level & 0x80) {
|
||||||
|
sl.append(0);
|
||||||
|
s = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
width &= 0x7FFF;
|
||||||
|
width <<= (24 - ml.at(i).bits);
|
||||||
|
height <<= (24 - ml.at(i).bits);
|
||||||
|
|
||||||
|
s = new SubDiv(offset, lon, lat, ml.at(i).bits, objects);
|
||||||
|
sl.append(s);
|
||||||
|
|
||||||
|
double min[2], max[2];
|
||||||
|
RectC bounds(Coordinates(toWGS84(lon - width),
|
||||||
|
toWGS84(lat + height + 1)), Coordinates(toWGS84(lon + width + 1),
|
||||||
|
toWGS84(lat - height)));
|
||||||
|
|
||||||
|
min[0] = bounds.left();
|
||||||
|
min[1] = bounds.bottom();
|
||||||
|
max[0] = bounds.right();
|
||||||
|
max[1] = bounds.top();
|
||||||
|
_subdivs[ml.at(i).bits]->Insert(min, max, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// objects with extended types (TRE7)
|
||||||
|
if (extSize && extItemSize == 13) {
|
||||||
|
quint32 polygons, lines, points;
|
||||||
|
quint8 kinds;
|
||||||
|
if (!seek(hdl, extOffset))
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < sl.size(); i++) {
|
||||||
|
if (!(readUInt32(hdl, polygons) && readUInt32(hdl, lines)
|
||||||
|
&& readUInt32(hdl, points) && readByte(hdl, kinds)))
|
||||||
|
return false;
|
||||||
|
if (i && sl.at(i-1))
|
||||||
|
sl.at(i-1)->setExtEnds(polygons, lines, points);
|
||||||
|
if (sl.at(i))
|
||||||
|
sl.at(i)->setExtOffsets(polygons, lines, points);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool cb(SubDiv *subdiv, void *context)
|
||||||
|
{
|
||||||
|
QList<SubDiv*> *list = (QList<SubDiv*>*)context;
|
||||||
|
list->append(subdiv);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<SubDiv*> TREFile::subdivs(const RectC &rect, int bits) const
|
||||||
|
{
|
||||||
|
QList<SubDiv*> list;
|
||||||
|
double min[2], max[2];
|
||||||
|
|
||||||
|
min[0] = rect.left();
|
||||||
|
min[1] = rect.bottom();
|
||||||
|
max[0] = rect.right();
|
||||||
|
max[1] = rect.top();
|
||||||
|
|
||||||
|
_subdivs.value(bits)->Search(min, max, cb, &list);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
37
src/map/IMG/trefile.h
Normal file
37
src/map/IMG/trefile.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef TREFILE_H
|
||||||
|
#define TREFILE_H
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QRect>
|
||||||
|
#include "common/rectc.h"
|
||||||
|
#include "common/rtree.h"
|
||||||
|
#include "subfile.h"
|
||||||
|
|
||||||
|
class SubDiv;
|
||||||
|
|
||||||
|
class TREFile : public SubFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TREFile(IMG *img, quint32 size) : SubFile(img, size) {}
|
||||||
|
~TREFile();
|
||||||
|
|
||||||
|
bool init();
|
||||||
|
|
||||||
|
const RectC &bounds() const {return _bounds;}
|
||||||
|
const QList<int> bits() const {return _subdivs.keys();}
|
||||||
|
|
||||||
|
QList<SubDiv*> subdivs(const RectC &rect, int bits) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef RTree<SubDiv*, double, 2> SubDivTree;
|
||||||
|
|
||||||
|
bool parsePoly(Handle hdl, quint32 pos, const QMap<int, int> &level2bits,
|
||||||
|
QMap<quint32, int> &map);
|
||||||
|
bool parsePoints(Handle hdl, quint32 pos, const QMap<int, int> &level2bits);
|
||||||
|
|
||||||
|
RectC _bounds;
|
||||||
|
QMap<int, SubDivTree*> _subdivs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TREFILE_H
|
11
src/map/IMG/units.h
Normal file
11
src/map/IMG/units.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef UNITS_H
|
||||||
|
#define UNITS_H
|
||||||
|
|
||||||
|
inline double toWGS84(qint32 coord)
|
||||||
|
{
|
||||||
|
return (coord < 0x800000)
|
||||||
|
? (double)coord * 360.0 / (double)(1<<24)
|
||||||
|
: (double)(coord - 0x1000000) * 360.0 / (double)(1<<24);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // UNITS_H
|
79
src/map/IMG/vectortile.cpp
Normal file
79
src/map/IMG/vectortile.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include "vectortile.h"
|
||||||
|
|
||||||
|
SubFile *VectorTile::file(SubFile::Type type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case SubFile::TRE:
|
||||||
|
return _tre;
|
||||||
|
case SubFile::RGN:
|
||||||
|
return _rgn;
|
||||||
|
case SubFile::LBL:
|
||||||
|
return _lbl;
|
||||||
|
case SubFile::NET:
|
||||||
|
return _net;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SubFile *VectorTile::addFile(IMG *img, SubFile::Type type, quint32 size)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case SubFile::TRE:
|
||||||
|
_tre = new TREFile(img, size);
|
||||||
|
return _tre;
|
||||||
|
case SubFile::RGN:
|
||||||
|
_rgn = new RGNFile(img, size);
|
||||||
|
return _rgn;
|
||||||
|
case SubFile::LBL:
|
||||||
|
_lbl = new LBLFile(img, size);
|
||||||
|
return _lbl;
|
||||||
|
case SubFile::NET:
|
||||||
|
_net = new NETFile(img, size);
|
||||||
|
return _net;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VectorTile::init()
|
||||||
|
{
|
||||||
|
if (!(_tre && _tre->init() && _rgn && _rgn->isValid()))
|
||||||
|
return false;
|
||||||
|
if (_lbl && !_lbl->isValid())
|
||||||
|
return false;
|
||||||
|
if (_net && !_net->isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VectorTile::objects(const RectC &rect, int bits,
|
||||||
|
QList<IMG::Poly> *polygons, QList<IMG::Poly> *lines,
|
||||||
|
QList<IMG::Point> *points) const
|
||||||
|
{
|
||||||
|
QList<SubDiv*> subdivs = _tre->subdivs(rect, bits);
|
||||||
|
for (int i = 0; i < subdivs.size(); i++) {
|
||||||
|
_rgn->objects(rect, subdivs.at(i), _lbl, _net, polygons, lines, points);
|
||||||
|
_rgn->extObjects(rect, subdivs.at(i), _lbl, polygons, lines,
|
||||||
|
points);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
QDebug operator<<(QDebug dbg, const VectorTile &tile)
|
||||||
|
{
|
||||||
|
dbg.nospace() << "VectorTile(";
|
||||||
|
if (tile._tre)
|
||||||
|
dbg << "TRE: " << *(tile._tre);
|
||||||
|
if (tile._rgn)
|
||||||
|
dbg << ", RGN: " << *(tile._rgn);
|
||||||
|
if (tile._lbl)
|
||||||
|
dbg << ", LBL: " << *(tile._lbl);
|
||||||
|
if (tile._net)
|
||||||
|
dbg << ", NET: " << *(tile._net);
|
||||||
|
dbg << ")";
|
||||||
|
|
||||||
|
return dbg.space();
|
||||||
|
}
|
||||||
|
#endif // QT_NO_DEBUG
|
46
src/map/IMG/vectortile.h
Normal file
46
src/map/IMG/vectortile.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#ifndef VECTORTILE_H
|
||||||
|
#define VECTORTILE_H
|
||||||
|
|
||||||
|
#include "img.h"
|
||||||
|
#include "trefile.h"
|
||||||
|
#include "trefile.h"
|
||||||
|
#include "rgnfile.h"
|
||||||
|
#include "lblfile.h"
|
||||||
|
#include "netfile.h"
|
||||||
|
|
||||||
|
class VectorTile {
|
||||||
|
public:
|
||||||
|
VectorTile() : _tre(0), _rgn(0), _lbl(0), _net(0) {}
|
||||||
|
~VectorTile() {delete _tre; delete _rgn; delete _lbl; delete _net;}
|
||||||
|
|
||||||
|
bool init();
|
||||||
|
|
||||||
|
const RectC &bounds() const {return _tre->bounds();}
|
||||||
|
const QList<int> bits() const {return _tre->bits();}
|
||||||
|
|
||||||
|
SubFile *file(SubFile::Type type);
|
||||||
|
SubFile *addFile(IMG *img, SubFile::Type type, quint32 size);
|
||||||
|
|
||||||
|
void objects(const RectC &rect, int bits, QList<IMG::Poly> *polygons,
|
||||||
|
QList<IMG::Poly> *lines, QList<IMG::Point> *points) const;
|
||||||
|
|
||||||
|
friend QDebug operator<<(QDebug dbg, const VectorTile &tile);
|
||||||
|
|
||||||
|
static bool isTileFile(SubFile::Type type)
|
||||||
|
{
|
||||||
|
return (type == SubFile::TRE || type == SubFile::LBL
|
||||||
|
|| type == SubFile::RGN || type == SubFile::NET);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TREFile *_tre;
|
||||||
|
RGNFile *_rgn;
|
||||||
|
LBLFile *_lbl;
|
||||||
|
NETFile *_net;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef QT_NO_DEBUG
|
||||||
|
QDebug operator<<(QDebug dbg, const VectorTile &tile);
|
||||||
|
#endif // QT_NO_DEBUG
|
||||||
|
|
||||||
|
#endif // VECTORTILE_H
|
368
src/map/imgmap.cpp
Normal file
368
src/map/imgmap.cpp
Normal file
@ -0,0 +1,368 @@
|
|||||||
|
#include <QFile>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPixmapCache>
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
#include <QtCore>
|
||||||
|
#else // QT_VERSION < 5
|
||||||
|
#include <QtConcurrent>
|
||||||
|
#endif // QT_VERSION < 5
|
||||||
|
#include "common/rectc.h"
|
||||||
|
#include "common/wgs84.h"
|
||||||
|
#include "IMG/textpathitem.h"
|
||||||
|
#include "IMG/textpointitem.h"
|
||||||
|
#include "IMG/bitmapline.h"
|
||||||
|
#include "pcs.h"
|
||||||
|
#include "rectd.h"
|
||||||
|
#include "imgmap.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define TILE_SIZE 256
|
||||||
|
#define TEXT_EXTENT 256
|
||||||
|
|
||||||
|
#define LARGE_FONT_SIZE 14
|
||||||
|
#define NORMAL_FONT_SIZE 12
|
||||||
|
#define SMALL_FONT_SIZE 10
|
||||||
|
#define POI_FONT_SIZE 9
|
||||||
|
|
||||||
|
#define LINE_TEXT_MIN_ZOOM 23
|
||||||
|
#define POI_MIN_ZOOM 25
|
||||||
|
#define POI_TEXT_MIN_ZOOM 26
|
||||||
|
|
||||||
|
class RasterTile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RasterTile() : _map(0) {}
|
||||||
|
RasterTile(IMGMap *map, const QPoint &xy, const QString &key)
|
||||||
|
: _map(map), _xy(xy), _key(key),
|
||||||
|
_img(TILE_SIZE, TILE_SIZE, QImage::Format_ARGB32_Premultiplied) {}
|
||||||
|
|
||||||
|
const QString &key() const {return _key;}
|
||||||
|
const QPoint &xy() const {return _xy;}
|
||||||
|
QImage &img() {return _img;}
|
||||||
|
QList<IMG::Poly> &polygons() {return _polygons;}
|
||||||
|
QList<IMG::Poly> &lines() {return _lines;}
|
||||||
|
QList<IMG::Point> &points() {return _points;}
|
||||||
|
|
||||||
|
void load()
|
||||||
|
{
|
||||||
|
_img.fill(Qt::transparent);
|
||||||
|
|
||||||
|
QPainter painter(&_img);
|
||||||
|
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
painter.translate(-_xy.x(), -_xy.y());
|
||||||
|
|
||||||
|
_map->drawPolygons(&painter, _polygons);
|
||||||
|
_map->drawLines(&painter, _lines, _xy);
|
||||||
|
_map->drawPoints(&painter, _points);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
IMGMap *_map;
|
||||||
|
QPoint _xy;
|
||||||
|
QString _key;
|
||||||
|
QImage _img;
|
||||||
|
QList<IMG::Poly> _polygons;
|
||||||
|
QList<IMG::Poly> _lines;
|
||||||
|
QList<IMG::Point> _points;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void convertUnits(QString &str)
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
int number = str.toInt(&ok);
|
||||||
|
if (ok)
|
||||||
|
str = QString::number(qRound(number * 0.3048));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IMGMap::IMGMap(const QString &fileName, QObject *parent)
|
||||||
|
: Map(parent), _fileName(fileName), _img(fileName),
|
||||||
|
_projection(PCS::pcs(3857)), _valid(false)
|
||||||
|
{
|
||||||
|
if (!_img.isValid()) {
|
||||||
|
_errorString = _img.errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_zooms = Range(_img.zooms().min() - 1, 28);
|
||||||
|
_zoom = _zooms.min();
|
||||||
|
|
||||||
|
updateTransform();
|
||||||
|
|
||||||
|
_largeFont.setPixelSize(LARGE_FONT_SIZE);
|
||||||
|
_normalFont.setPixelSize(NORMAL_FONT_SIZE);
|
||||||
|
_smallFont.setPixelSize(SMALL_FONT_SIZE);
|
||||||
|
_poiFont.setPixelSize(POI_FONT_SIZE);
|
||||||
|
|
||||||
|
_valid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRectF IMGMap::bounds()
|
||||||
|
{
|
||||||
|
RectD prect(_img.bounds(), _projection);
|
||||||
|
return QRectF(_transform.proj2img(prect.topLeft()),
|
||||||
|
_transform.proj2img(prect.bottomRight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
int IMGMap::zoomFit(const QSize &size, const RectC &rect)
|
||||||
|
{
|
||||||
|
if (rect.isValid()) {
|
||||||
|
QPointF sc((rect.right() - rect.left()) / size.width(),
|
||||||
|
(rect.top() - rect.bottom()) / size.height());
|
||||||
|
double resolution = qMax(qAbs(sc.x()), qAbs(sc.y()));
|
||||||
|
|
||||||
|
_zoom = _zooms.min();
|
||||||
|
for (int i = _zooms.min(); i <= _zooms.max(); i++) {
|
||||||
|
if (360.0 / (1<<i) < resolution)
|
||||||
|
break;
|
||||||
|
_zoom = i;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
_zoom = _zooms.max();
|
||||||
|
|
||||||
|
updateTransform();
|
||||||
|
|
||||||
|
return _zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IMGMap::zoomIn()
|
||||||
|
{
|
||||||
|
_zoom = qMin(_zoom + 1, _zooms.max());
|
||||||
|
updateTransform();
|
||||||
|
return _zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IMGMap::zoomOut()
|
||||||
|
{
|
||||||
|
_zoom = qMax(_zoom - 1, _zooms.min());
|
||||||
|
updateTransform();
|
||||||
|
return _zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMGMap::updateTransform()
|
||||||
|
{
|
||||||
|
double scale = (2.0 * M_PI * WGS84_RADIUS) / (1<<_zoom);;
|
||||||
|
PointD topLeft(_projection.ll2xy(_img.bounds().topLeft()));
|
||||||
|
_transform = Transform(ReferencePoint(PointD(0, 0), topLeft),
|
||||||
|
PointD(scale, scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF IMGMap::ll2xy(const Coordinates &c)
|
||||||
|
{
|
||||||
|
return _transform.proj2img(_projection.ll2xy(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
Coordinates IMGMap::xy2ll(const QPointF &p)
|
||||||
|
{
|
||||||
|
return _projection.xy2ll(_transform.img2proj(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void IMGMap::drawPolygons(QPainter *painter, QList<IMG::Poly> &polygons)
|
||||||
|
{
|
||||||
|
for (int n = 0; n < _img.style().drawOrder().size(); n++) {
|
||||||
|
for (int i = 0; i < polygons.size(); i++) {
|
||||||
|
IMG::Poly &poly = polygons[i];
|
||||||
|
if (poly.type != _img.style().drawOrder().at(n))
|
||||||
|
continue;
|
||||||
|
const Style::Polygon &style = _img.style().polygon(poly.type);
|
||||||
|
|
||||||
|
for (int j = 0; j < poly.points.size(); j++) {
|
||||||
|
QPointF &p = poly.points[j];
|
||||||
|
p = ll2xy(Coordinates(p.x(), p.y()));
|
||||||
|
}
|
||||||
|
|
||||||
|
painter->setPen(style.pen());
|
||||||
|
painter->setBrush(style.brush());
|
||||||
|
painter->drawPolygon(poly.points);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMGMap::drawLines(QPainter *painter, QList<IMG::Poly> &lines,
|
||||||
|
const QPoint &tile)
|
||||||
|
{
|
||||||
|
qStableSort(lines);
|
||||||
|
|
||||||
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
|
IMG::Poly &poly = lines[i];
|
||||||
|
for (int j = 0; j < poly.points.size(); j++) {
|
||||||
|
QPointF &p = poly.points[j];
|
||||||
|
p = ll2xy(Coordinates(p.x(), p.y()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
painter->setBrush(Qt::NoBrush);
|
||||||
|
|
||||||
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
|
const IMG::Poly &poly = lines.at(i);
|
||||||
|
const Style::Line &style = _img.style().line(poly.type);
|
||||||
|
|
||||||
|
if (style.background() == Qt::NoPen)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
painter->setPen(style.background());
|
||||||
|
painter->drawPolyline(poly.points);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
|
const IMG::Poly &poly = lines.at(i);
|
||||||
|
const Style::Line &style = _img.style().line(poly.type);
|
||||||
|
|
||||||
|
if (!style.img().isNull())
|
||||||
|
BitmapLine::draw(painter, poly.points, style.img());
|
||||||
|
else if (style.foreground() != Qt::NoPen) {
|
||||||
|
painter->setPen(style.foreground());
|
||||||
|
painter->drawPolyline(poly.points);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_zoom < LINE_TEXT_MIN_ZOOM)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QVector<TextPathItem> items;
|
||||||
|
|
||||||
|
for (int i = 0; i < lines.size(); i++) {
|
||||||
|
IMG::Poly &poly = lines[i];
|
||||||
|
const Style::Line &style = _img.style().line(poly.type);
|
||||||
|
|
||||||
|
if (style.img().isNull() && style.foreground() == Qt::NoPen)
|
||||||
|
continue;
|
||||||
|
if (poly.label.isEmpty() || style.textFontSize() == Style::None)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Style::isContourLine(poly.type))
|
||||||
|
convertUnits(poly.label);
|
||||||
|
|
||||||
|
const QFont *font;
|
||||||
|
switch (style.textFontSize()) {
|
||||||
|
case Style::Large:
|
||||||
|
font = &_largeFont;
|
||||||
|
break;
|
||||||
|
case Style::Small:
|
||||||
|
font = &_smallFont;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
font = &_normalFont;
|
||||||
|
}
|
||||||
|
const QColor *color = style.textColor().isValid()
|
||||||
|
? &style.textColor() : 0;
|
||||||
|
|
||||||
|
TextPathItem item(poly.points, &poly.label, QRect(tile,
|
||||||
|
QSize(TILE_SIZE, TILE_SIZE)), font, color);
|
||||||
|
if (item.isValid() && !item.collides(items))
|
||||||
|
items.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < items.size(); i++)
|
||||||
|
items.at(i).paint(painter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMGMap::drawPoints(QPainter *painter, QList<IMG::Point> &points)
|
||||||
|
{
|
||||||
|
qSort(points);
|
||||||
|
|
||||||
|
QVector<TextPointItem> items;
|
||||||
|
|
||||||
|
for (int i = 0; i < points.size(); i++) {
|
||||||
|
const IMG::Point &point = points.at(i);
|
||||||
|
const Style::Point &style = _img.style().point(point.type);
|
||||||
|
|
||||||
|
if (point.poi && _zoom < POI_MIN_ZOOM)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const QString *label = ((point.poi && _zoom < POI_TEXT_MIN_ZOOM)
|
||||||
|
|| point.label.isEmpty()) ? 0 : &(point.label);
|
||||||
|
const QImage *img = style.img().isNull() ? 0 : &style.img();
|
||||||
|
const QFont *font = 0;
|
||||||
|
if (point.poi)
|
||||||
|
font = &_poiFont;
|
||||||
|
else {
|
||||||
|
switch (style.textFontSize()) {
|
||||||
|
case Style::None:
|
||||||
|
label = 0;
|
||||||
|
break;
|
||||||
|
case Style::Normal:
|
||||||
|
font = &_normalFont;
|
||||||
|
break;
|
||||||
|
case Style::Small:
|
||||||
|
font = &_smallFont;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
font = &_largeFont;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const QColor *color = style.textColor().isValid()
|
||||||
|
? &style.textColor() : 0;
|
||||||
|
|
||||||
|
if (!label && !img)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
TextPointItem item(ll2xy(point.coordinates).toPoint(), label, font, img,
|
||||||
|
color);
|
||||||
|
if (!item.collides(items))
|
||||||
|
items.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < items.size(); i++)
|
||||||
|
items.at(i).paint(painter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void render(RasterTile &tile)
|
||||||
|
{
|
||||||
|
tile.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IMGMap::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++) {
|
||||||
|
QPixmap pm;
|
||||||
|
QPoint ttl(tl.x() + i * TILE_SIZE, tl.y() + j * TILE_SIZE);
|
||||||
|
QString key = _fileName + "-" + QString::number(_zoom) + "_"
|
||||||
|
+ QString::number(ttl.x()) + "_" + QString::number(ttl.y());
|
||||||
|
if (QPixmapCache::find(key, pm))
|
||||||
|
painter->drawPixmap(ttl, pm);
|
||||||
|
else {
|
||||||
|
tiles.append(RasterTile(this, ttl, key));
|
||||||
|
RasterTile &tile = tiles.last();
|
||||||
|
|
||||||
|
RectD polyRect(_transform.img2proj(ttl), _transform.img2proj(
|
||||||
|
QPointF(ttl.x() + TILE_SIZE, ttl.y() + TILE_SIZE)));
|
||||||
|
_img.objects(polyRect.toRectC(_projection, 4), _zoom,
|
||||||
|
&(tile.polygons()), &(tile.lines()), 0);
|
||||||
|
RectD pointRect(_transform.img2proj(QPointF(ttl.x() - TEXT_EXTENT,
|
||||||
|
ttl.y() - TEXT_EXTENT)), _transform.img2proj(QPointF(ttl.x()
|
||||||
|
+ TILE_SIZE + TEXT_EXTENT, ttl.y() + TILE_SIZE + TEXT_EXTENT)));
|
||||||
|
_img.objects(pointRect.toRectC(_projection, 4), _zoom,
|
||||||
|
0, 0, &(tile.points()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<void> future = QtConcurrent::map(tiles, render);
|
||||||
|
future.waitForFinished();
|
||||||
|
|
||||||
|
for (int i = 0; i < tiles.size(); i++) {
|
||||||
|
RasterTile &mt = tiles[i];
|
||||||
|
QPixmap pm(QPixmap::fromImage(mt.img()));
|
||||||
|
if (pm.isNull())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QPixmapCache::insert(mt.key(), pm);
|
||||||
|
|
||||||
|
painter->drawPixmap(mt.xy(), pm);
|
||||||
|
}
|
||||||
|
}
|
59
src/map/imgmap.h
Normal file
59
src/map/imgmap.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef IMGMAP_H
|
||||||
|
#define IMGMAP_H
|
||||||
|
|
||||||
|
#include <QFont>
|
||||||
|
#include "map.h"
|
||||||
|
#include "projection.h"
|
||||||
|
#include "transform.h"
|
||||||
|
#include "common/range.h"
|
||||||
|
#include "IMG/img.h"
|
||||||
|
|
||||||
|
|
||||||
|
class IMGMap : public Map
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
IMGMap(const QString &fileName, QObject *parent = 0);
|
||||||
|
|
||||||
|
QString name() const {return _img.name();}
|
||||||
|
|
||||||
|
QRectF bounds();
|
||||||
|
|
||||||
|
virtual int zoom() const {return _zoom;}
|
||||||
|
virtual void setZoom(int zoom) {_zoom = zoom;}
|
||||||
|
virtual int zoomFit(const QSize &, const RectC &);
|
||||||
|
virtual int zoomIn();
|
||||||
|
virtual int zoomOut();
|
||||||
|
|
||||||
|
QPointF ll2xy(const Coordinates &c);
|
||||||
|
Coordinates xy2ll(const QPointF &p);
|
||||||
|
|
||||||
|
void draw(QPainter *painter, const QRectF &rect, Flags flags);
|
||||||
|
|
||||||
|
bool isValid() const {return _valid;}
|
||||||
|
QString errorString() const {return _errorString;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class RasterTile;
|
||||||
|
|
||||||
|
void updateTransform();
|
||||||
|
void drawPolygons(QPainter *painter, QList<IMG::Poly> &polygons);
|
||||||
|
void drawLines(QPainter *painter, QList<IMG::Poly> &lines,
|
||||||
|
const QPoint &tile);
|
||||||
|
void drawPoints(QPainter *painter, QList<IMG::Point> &points);
|
||||||
|
|
||||||
|
QString _fileName;
|
||||||
|
IMG _img;
|
||||||
|
int _zoom;
|
||||||
|
Range _zooms;
|
||||||
|
Projection _projection;
|
||||||
|
Transform _transform;
|
||||||
|
|
||||||
|
QFont _largeFont, _normalFont, _smallFont, _poiFont;
|
||||||
|
|
||||||
|
bool _valid;
|
||||||
|
QString _errorString;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // IMGMAP_H
|
@ -2,12 +2,12 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include "atlas.h"
|
#include "atlas.h"
|
||||||
#include "ozimap.h"
|
#include "ozimap.h"
|
||||||
#include "onlinemap.h"
|
|
||||||
#include "jnxmap.h"
|
#include "jnxmap.h"
|
||||||
#include "geotiffmap.h"
|
#include "geotiffmap.h"
|
||||||
#include "mapsource.h"
|
#include "mapsource.h"
|
||||||
#include "mbtilesmap.h"
|
#include "mbtilesmap.h"
|
||||||
#include "rmap.h"
|
#include "rmap.h"
|
||||||
|
#include "imgmap.h"
|
||||||
#include "maplist.h"
|
#include "maplist.h"
|
||||||
|
|
||||||
|
|
||||||
@ -61,6 +61,8 @@ bool MapList::loadFile(const QString &path, bool *atlas, bool dir)
|
|||||||
map = new MBTilesMap(path, this);
|
map = new MBTilesMap(path, this);
|
||||||
else if (suffix == "rmap" || suffix == "rtmap")
|
else if (suffix == "rmap" || suffix == "rtmap")
|
||||||
map = new RMap(path, this);
|
map = new RMap(path, this);
|
||||||
|
else if (suffix == "img")
|
||||||
|
map = new IMGMap(path, this);
|
||||||
else
|
else
|
||||||
map = new OziMap(path, this);
|
map = new OziMap(path, this);
|
||||||
|
|
||||||
@ -117,10 +119,11 @@ QString MapList::formats()
|
|||||||
{
|
{
|
||||||
return
|
return
|
||||||
tr("Supported files")
|
tr("Supported files")
|
||||||
+ " (*.jnx *.map *.mbtiles *.rmap *.rtmap *.tar *.tba *.tif *.tiff *.xml);;"
|
+ " (*.img *.jnx *.map *.mbtiles *.rmap *.rtmap *.tar *.tba *.tif *.tiff *.xml);;"
|
||||||
+ tr("MBTiles maps") + " (*.mbtiles);;"
|
+ tr("Garmin IMG maps") + " (*.img);;"
|
||||||
+ tr("Garmin JNX maps") + " (*.jnx);;"
|
+ tr("Garmin JNX maps") + " (*.jnx);;"
|
||||||
+ tr("OziExplorer maps") + " (*.map);;"
|
+ tr("OziExplorer maps") + " (*.map);;"
|
||||||
|
+ tr("MBTiles maps") + " (*.mbtiles);;"
|
||||||
+ tr("TrekBuddy maps/atlases") + " (*.tar *.tba);;"
|
+ tr("TrekBuddy maps/atlases") + " (*.tar *.tba);;"
|
||||||
+ tr("GeoTIFF images") + " (*.tif *.tiff);;"
|
+ tr("GeoTIFF images") + " (*.tif *.tiff);;"
|
||||||
+ tr("TwoNav maps") + " (*.rmap *.rtmap);;"
|
+ tr("TwoNav maps") + " (*.rmap *.rtmap);;"
|
||||||
@ -130,7 +133,7 @@ QString MapList::formats()
|
|||||||
QStringList MapList::filter()
|
QStringList MapList::filter()
|
||||||
{
|
{
|
||||||
QStringList filter;
|
QStringList filter;
|
||||||
filter << "*.jnx" << "*.map" << "*.tba" << "*.tar" << "*.xml" << "*.tif"
|
filter << "*.img" << "*.jnx" << "*.map" << "*.tba" << "*.tar" << "*.xml"
|
||||||
<< "*.tiff" << "*.mbtiles" << "*.rmap";
|
<< "*.tif" << "*.tiff" << "*.mbtiles" << "*.rmap" << "*.rtmap" << "*.img";
|
||||||
return filter;
|
return filter;
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
#include "rectd.h"
|
#include "rectd.h"
|
||||||
|
|
||||||
|
|
||||||
#define SAMPLE_POINTS 100
|
|
||||||
|
|
||||||
static void growRect(const Projection &proj, const Coordinates &c, RectD &rect)
|
static void growRect(const Projection &proj, const Coordinates &c, RectD &rect)
|
||||||
{
|
{
|
||||||
if (c.isNull())
|
if (c.isNull())
|
||||||
@ -26,23 +24,44 @@ static void growRect(const Projection &proj, const Coordinates &c, RectD &rect)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RectD::RectD(const RectC &rect, const Projection &proj)
|
static void growRect(const Projection &proj, const PointD &p, RectC &rect)
|
||||||
|
{
|
||||||
|
if (p.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Coordinates c(proj.xy2ll(p));
|
||||||
|
|
||||||
|
if (rect.isNull())
|
||||||
|
rect = RectC(c, c);
|
||||||
|
else {
|
||||||
|
if (c.lon() < rect.left())
|
||||||
|
rect.setLeft(c.lon());
|
||||||
|
if (c.lon() > rect.right())
|
||||||
|
rect.setRight(c.lon());
|
||||||
|
if (c.lat() < rect.bottom())
|
||||||
|
rect.setBottom(c.lat());
|
||||||
|
if (c.lat() > rect.top())
|
||||||
|
rect.setTop(c.lat());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RectD::RectD(const RectC &rect, const Projection &proj, int samples)
|
||||||
{
|
{
|
||||||
RectD prect;
|
RectD prect;
|
||||||
double dx = (rect.right() - rect.left()) / SAMPLE_POINTS;
|
double dx = (rect.right() - rect.left()) / samples;
|
||||||
double dy = (rect.top() - rect.bottom()) / SAMPLE_POINTS;
|
double dy = (rect.top() - rect.bottom()) / samples;
|
||||||
|
|
||||||
growRect(proj, rect.topLeft(), prect);
|
growRect(proj, rect.topLeft(), prect);
|
||||||
|
|
||||||
if (dx > 0) {
|
if (dx > 0) {
|
||||||
for (int i = 0; i <= SAMPLE_POINTS; i++) {
|
for (int i = 0; i <= samples; i++) {
|
||||||
double x = rect.left() + i * dx;
|
double x = rect.left() + i * dx;
|
||||||
growRect(proj, Coordinates(x, rect.bottom()), prect);
|
growRect(proj, Coordinates(x, rect.bottom()), prect);
|
||||||
growRect(proj, Coordinates(x, rect.top()), prect);
|
growRect(proj, Coordinates(x, rect.top()), prect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dy > 0) {
|
if (dy > 0) {
|
||||||
for (int i = 0; i <= SAMPLE_POINTS; i++ ) {
|
for (int i = 0; i <= samples; i++ ) {
|
||||||
double y = rect.bottom() + i * dy;
|
double y = rect.bottom() + i * dy;
|
||||||
growRect(proj, Coordinates(rect.left(), y), prect);
|
growRect(proj, Coordinates(rect.left(), y), prect);
|
||||||
growRect(proj, Coordinates(rect.right(), y), prect);
|
growRect(proj, Coordinates(rect.right(), y), prect);
|
||||||
@ -51,3 +70,29 @@ RectD::RectD(const RectC &rect, const Projection &proj)
|
|||||||
|
|
||||||
*this = prect;
|
*this = prect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RectC RectD::toRectC(const Projection &proj, int samples) const
|
||||||
|
{
|
||||||
|
RectC ret;
|
||||||
|
double dx = (right() - left()) / samples;
|
||||||
|
double dy = (top() - bottom()) / samples;
|
||||||
|
|
||||||
|
growRect(proj, topLeft(), ret);
|
||||||
|
|
||||||
|
if (dx > 0) {
|
||||||
|
for (int i = 0; i <= samples; i++) {
|
||||||
|
double x = left() + i * dx;
|
||||||
|
growRect(proj, PointD(x, bottom()), ret);
|
||||||
|
growRect(proj, PointD(x, top()), ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dy > 0) {
|
||||||
|
for (int i = 0; i <= samples; i++ ) {
|
||||||
|
double y = bottom() + i * dy;
|
||||||
|
growRect(proj, PointD(left(), y), ret);
|
||||||
|
growRect(proj, PointD(right(), y), ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -12,7 +12,7 @@ public:
|
|||||||
RectD() {}
|
RectD() {}
|
||||||
RectD(const PointD &topLeft, const PointD &bottomRight)
|
RectD(const PointD &topLeft, const PointD &bottomRight)
|
||||||
: _tl(topLeft), _br(bottomRight) {}
|
: _tl(topLeft), _br(bottomRight) {}
|
||||||
RectD(const RectC &rect, const Projection &proj);
|
RectD(const RectC &rect, const Projection &proj, int samples = 100);
|
||||||
|
|
||||||
PointD topLeft() const {return _tl;}
|
PointD topLeft() const {return _tl;}
|
||||||
PointD bottomRight() const {return _br;}
|
PointD bottomRight() const {return _br;}
|
||||||
@ -37,6 +37,8 @@ public:
|
|||||||
bool isNull() const {return _tl.isNull() && _br.isNull();}
|
bool isNull() const {return _tl.isNull() && _br.isNull();}
|
||||||
bool isValid() const {return !(_tl.isNull() || _br.isNull());}
|
bool isValid() const {return !(_tl.isNull() || _br.isNull());}
|
||||||
|
|
||||||
|
RectC toRectC(const Projection &proj, int samples = 100) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PointD _tl, _br;
|
PointD _tl, _br;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user