1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-28 05:34:47 +01:00

Added support for Garmin IMG maps

This commit is contained in:
Martin Tůma 2019-05-10 18:56:19 +02:00
parent 82923a4529
commit 0594774570
31 changed files with 3769 additions and 15 deletions

View File

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

View File

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

View 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
View 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
View 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 &rect;
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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 &section, 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 &section)
{
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 &section)
{
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 &section)
{
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
View 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 &section);
bool parseLines(SubFile *file, SubFile::Handle &hdl,
const Section &section);
bool parsePolygons(SubFile *file, SubFile::Handle &hdl,
const Section &section);
bool parseDrawOrder(SubFile *file, SubFile::Handle &hdl,
const Section &section);
bool itemInfo(SubFile *file, SubFile::Handle &hdl,
const Section &section, 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
View 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
View 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
View 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

View 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);
}

View 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

View 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);
}

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

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

View File

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

View File

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

View File

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