1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-06-27 19:49:15 +02:00

Added support for Mapsforge maps

This commit is contained in:
2021-04-10 15:27:40 +02:00
parent 8fe0f836ae
commit 44a5e5de81
208 changed files with 5761 additions and 475 deletions

View File

@ -39,22 +39,16 @@ AreaItem::AreaItem(const Area &area, Map *map, GraphicsItem *parent)
setAcceptHoverEvents(true);
}
QPainterPath AreaItem::painterPath(const Polygon &polygon)
{
QPainterPath path;
const QVector<Coordinates> &lr = polygon.first();
path.moveTo(_map->ll2xy(lr.first()));
for (int i = 1; i < lr.size(); i++)
path.lineTo(_map->ll2xy(lr.at(i)));
path.closeSubpath();
for (int i = 0; i < polygon.size(); i++) {
const QVector<Coordinates> &subpath = polygon.at(i);
for (int i = 1; i < polygon.size(); i++) {
const QVector<Coordinates> &lr = polygon.at(i);
path.moveTo(_map->ll2xy(lr.first()));
for (int j = 1; j < lr.size(); j++)
path.lineTo(_map->ll2xy(lr.at(j)));
path.moveTo(_map->ll2xy(subpath.first()));
for (int j = 1; j < subpath.size(); j++)
path.lineTo(_map->ll2xy(subpath.at(j)));
path.closeSubpath();
}

View File

@ -9,7 +9,6 @@
#include "common/rectc.h"
#include "common/config.h"
#include "data/waypoint.h"
#include "data/polygon.h"
#include "map/projection.h"
#include "searchpointer.h"
#include "units.h"

62
src/common/polygon.h Normal file
View File

@ -0,0 +1,62 @@
#ifndef POLYGON_H
#define POLYGON_H
#include <QList>
#include <QVector>
#include "common/rectc.h"
class Polygon
{
public:
Polygon() {}
Polygon(const QVector<Coordinates> &path)
{
_paths.reserve(1);
_paths.append(path);
_boundingRect = boundingRect(path);
}
Polygon(const RectC &rect)
{
QVector<Coordinates> v(4);
v[0] = Coordinates(rect.left(), rect.top());
v[1] = Coordinates(rect.right(), rect.top());
v[2] = Coordinates(rect.right(), rect.bottom());
v[3] = Coordinates(rect.left(), rect.bottom());
_paths.reserve(1);
_paths.append(v);
_boundingRect = RectC(v.at(0), v.at(2));
}
void append(const QVector<Coordinates> &path)
{
_paths.append(path);
_boundingRect |= boundingRect(path);
}
void reserve(int size) {_paths.reserve(size);}
int size() const {return _paths.size();}
bool isEmpty() const {return _paths.isEmpty();}
const QVector<Coordinates> &at(int i) const {return _paths.at(i);}
const QVector<Coordinates> &first() const {return _paths.first();}
const QVector<Coordinates> &last() const {return _paths.last();}
const RectC &boundingRect() const {return _boundingRect;}
private:
static RectC boundingRect(const QVector<Coordinates> &path)
{
RectC rect;
for (int i = 0; i < path.size(); i++)
rect = rect.united(path.at(i));
return rect;
}
QList<QVector<Coordinates> > _paths;
RectC _boundingRect;
};
#endif // POLYGON_H

View File

@ -15,6 +15,7 @@
#define GCS_FILE "gcs.csv"
#define PCS_FILE "pcs.csv"
#define TYP_FILE "style.typ"
#define RENDERTHEME_FILE "style.xml"
QString ProgramPaths::mapDir(bool writable)
@ -102,3 +103,9 @@ QString ProgramPaths::typFile()
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
STYLE_DIR "/" TYP_FILE, QStandardPaths::LocateFile);
}
QString ProgramPaths::renderthemeFile()
{
return QStandardPaths::locate(QStandardPaths::AppDataLocation,
STYLE_DIR "/" RENDERTHEME_FILE, QStandardPaths::LocateFile);
}

View File

@ -16,6 +16,7 @@ namespace ProgramPaths
QString gcsFile();
QString pcsFile();
QString typFile();
QString renderthemeFile();
}
#endif // PROGRAMPATHS_H

View File

@ -3,7 +3,7 @@
#include <QString>
#include <QList>
#include "polygon.h"
#include "common/polygon.h"
class Area
{
@ -11,15 +11,10 @@ public:
Area() {}
Area(const RectC &rect)
{
QVector<Coordinates> v(4);
v[0] = Coordinates(rect.left(), rect.top());
v[1] = Coordinates(rect.right(), rect.top());
v[2] = Coordinates(rect.right(), rect.bottom());
v[3] = Coordinates(rect.left(), rect.bottom());
Polygon polygon(rect);
_polygons.reserve(1);
_polygons.append(v);
_boundingRect = RectC(v.at(0), v.at(2));
_polygons.append(polygon);
_boundingRect = polygon.boundingRect();
}
Area(const Polygon &polygon)
{
@ -38,8 +33,9 @@ public:
if (_polygons.isEmpty())
return false;
for (int i = 0; i < _polygons.size(); i++)
if (!_polygons.at(i).isValid())
if (_polygons.at(i).isEmpty() || _polygons.at(i).first().size() < 3)
return false;
return true;
}

View File

@ -143,8 +143,7 @@ bool GeoJSONParser::polygon(const QJsonArray &coordinates, ::Polygon &pg)
}
const QJsonArray lr(coordinates.at(i).toArray());
pg.append(QVector<Coordinates>());
QVector<Coordinates> &data = pg.last();
QVector<Coordinates> data;
for (int j = 0; j < lr.size(); j++) {
QJsonArray point(lr.at(j).toArray());
@ -156,6 +155,8 @@ bool GeoJSONParser::polygon(const QJsonArray &coordinates, ::Polygon &pg)
data.append(Coordinates(point.at(0).toDouble(),
point.at(1).toDouble()));
}
pg.append(data);
}
return true;

View File

@ -266,20 +266,22 @@ void KMLParser::polygon(Area &area)
Polygon polygon;
while (_reader.readNextStartElement()) {
QVector<Coordinates> path;
if (_reader.name() == QLatin1String("outerBoundaryIs")) {
if (!polygon.isEmpty()) {
_reader.raiseError("Multiple polygon outerBoundaryIss");
return;
}
polygon.append(QVector<Coordinates>());
boundary(polygon.last());
boundary(path);
polygon.append(path);
} else if (_reader.name() == QLatin1String("innerBoundaryIs")) {
if (polygon.isEmpty()) {
_reader.raiseError("Missing polygon outerBoundaryIs");
return;
}
polygon.append(QVector<Coordinates>());
boundary(polygon.last());
boundary(path);
polygon.append(path);
} else
_reader.skipCurrentElement();
}

View File

@ -1,38 +0,0 @@
#ifndef POLYGON_H
#define POLYGON_H
#include <QList>
#include <QVector>
#include "common/coordinates.h"
#include "common/rectc.h"
class Polygon : public QList<QVector<Coordinates> >
{
public:
Polygon() {}
Polygon(const QVector<Coordinates> &c)
{
reserve(1);
append(c);
}
bool isValid() const
{
return !isEmpty() && first().size() >= 3;
}
RectC boundingRect() const
{
RectC rect;
if (isEmpty() || first().size() < 3)
return rect;
for (int i = 0; i < first().size(); i++)
rect = rect.united(first().at(i));
return rect;
}
};
#endif // POLYGON_H

View File

@ -1,5 +1,6 @@
#include "bitstream.h"
using namespace IMG;
bool BitStream1::flush()
{

View File

@ -1,8 +1,10 @@
#ifndef BITSTREAM_H
#define BITSTREAM_H
#ifndef IMG_BITSTREAM_H
#define IMG_BITSTREAM_H
#include "subfile.h"
namespace IMG {
class BitStream1 {
public:
BitStream1(const SubFile &file, SubFile::Handle &hdl, quint32 length)
@ -128,4 +130,6 @@ bool BitStream4R::read(int bits, T &val)
return true;
}
#endif // BITSTREAM_H
}
#endif // IMG_BITSTREAM_H

View File

@ -1,6 +1,8 @@
#include "deltastream.h"
using namespace IMG;
static int bitSize(quint8 baseSize, bool variableSign, bool extraBit)
{
int bits = 2;

View File

@ -1,8 +1,10 @@
#ifndef DELTASTREAM_H
#define DELTASTREAM_H
#ifndef IMG_DELTASTREAM_H
#define IMG_DELTASTREAM_H
#include "bitstream.h"
namespace IMG {
class DeltaStream : public BitStream1 {
public:
DeltaStream(const SubFile &file, SubFile::Handle &hdl, quint32 length,
@ -25,4 +27,6 @@ private:
quint32 _lonBits, _latBits, _readBits;
};
#endif // DELTASTREAM_H
}
#endif // IMG_DELTASTREAM_H

View File

@ -1,8 +1,9 @@
#include <QXmlStreamReader>
#include <QDir>
#include "vectortile.h"
#include "gmap.h"
#include "gmapdata.h"
using namespace IMG;
static SubFile::Type tileType(const QString &suffix)
{
@ -22,7 +23,7 @@ static SubFile::Type tileType(const QString &suffix)
return SubFile::Unknown;
}
void GMAP::subProduct(QXmlStreamReader &reader, QString &dataDir,
void GMAPData::subProduct(QXmlStreamReader &reader, QString &dataDir,
QString &baseMap)
{
while (reader.readNextStartElement()) {
@ -35,7 +36,7 @@ void GMAP::subProduct(QXmlStreamReader &reader, QString &dataDir,
}
}
void GMAP::mapProduct(QXmlStreamReader &reader, QString &dataDir,
void GMAPData::mapProduct(QXmlStreamReader &reader, QString &dataDir,
QString &typFile, QString &baseMap)
{
while (reader.readNextStartElement()) {
@ -50,7 +51,7 @@ void GMAP::mapProduct(QXmlStreamReader &reader, QString &dataDir,
}
}
bool GMAP::readXML(const QString &path, QString &dataDir, QString &typFile,
bool GMAPData::readXML(const QString &path, QString &dataDir, QString &typFile,
QString &baseMap)
{
QFile file(path);
@ -74,7 +75,7 @@ bool GMAP::readXML(const QString &path, QString &dataDir, QString &typFile,
return true;
}
bool GMAP::loadTile(const QDir &dir, bool baseMap)
bool GMAPData::loadTile(const QDir &dir, bool baseMap)
{
VectorTile *tile = new VectorTile();
@ -110,7 +111,7 @@ bool GMAP::loadTile(const QDir &dir, bool baseMap)
return true;
}
GMAP::GMAP(const QString &fileName) : _fileName(fileName)
GMAPData::GMAPData(const QString &fileName) : _fileName(fileName)
{
QString dataDirPath, typFilePath, baseMapPath;
if (!readXML(fileName, dataDirPath, typFilePath, baseMapPath))
@ -146,12 +147,12 @@ GMAP::GMAP(const QString &fileName) : _fileName(fileName)
_valid = true;
}
GMAP::~GMAP()
GMAPData::~GMAPData()
{
qDeleteAll(_files);
}
bool GMAP::isGMAP(const QString &path)
bool GMAPData::isGMAP(const QString &path)
{
QFile file(path);

View File

@ -1,16 +1,18 @@
#ifndef GMAP_H
#define GMAP_H
#ifndef IMG_GMAP_H
#define IMG_GMAP_H
#include "mapdata.h"
class QXmlStreamReader;
class QDir;
class GMAP : public MapData
namespace IMG {
class GMAPData : public MapData
{
public:
GMAP(const QString &fileName);
~GMAP();
GMAPData(const QString &fileName);
~GMAPData();
const QString &fileName() const {return _fileName;}
@ -29,4 +31,6 @@ private:
QList<const QString*> _files;
};
#endif // GMAP_H
}
#endif // IMG_GMAP_H

View File

@ -1,6 +1,8 @@
#include "rgnfile.h"
#include "huffmanbuffer.h"
using namespace IMG;
bool HuffmanBuffer::load(const RGNFile *rgn, SubFile::Handle &rgnHdl)
{
quint32 recordSize, recordOffset = rgn->dictOffset();

View File

@ -1,9 +1,11 @@
#ifndef HUFFMANBUFFER_H
#define HUFFMANBUFFER_H
#ifndef IMG_HUFFMANBUFFER_H
#define IMG_HUFFMANBUFFER_H
#include <QByteArray>
#include "subfile.h"
namespace IMG {
class RGNFile;
class HuffmanBuffer : public QByteArray
@ -18,4 +20,6 @@ private:
quint8 _id;
};
#endif // HUFFMANBUFFER_H
}
#endif // IMG_HUFFMANBUFFER_H

View File

@ -1,5 +1,7 @@
#include "huffmanstream.h"
using namespace IMG;
bool HuffmanStreamF::init(bool line)
{
if (line) {

View File

@ -1,9 +1,11 @@
#ifndef HUFFMANSTREAM_H
#define HUFFMANSTREAM_H
#ifndef IMG_HUFFMANSTREAM_H
#define IMG_HUFFMANSTREAM_H
#include "bitstream.h"
#include "huffmantable.h"
namespace IMG {
template <class BitStream>
class HuffmanStream {
public:
@ -138,4 +140,6 @@ public:
bool init(int lonSign, int latSign, quint32 data, quint32 dataSize);
};
#endif // HUFFMANSTREAM_H
}
#endif // IMG_HUFFMANSTREAM_H

View File

@ -2,6 +2,8 @@
#include "huffmantable.h"
using namespace IMG;
static inline quint32 readVUint32(const quint8 *buffer, quint32 bytes)
{
quint32 val = 0;

View File

@ -1,8 +1,10 @@
#ifndef HUFFMANTABLE_H
#define HUFFMANTABLE_H
#ifndef IMG_HUFFMANTABLE_H
#define IMG_HUFFMANTABLE_H
#include "huffmanbuffer.h"
namespace IMG {
class RGNFile;
class HuffmanTable {
@ -23,4 +25,6 @@ private:
quint16 _s22;
};
#endif // HUFFMANTABLE_H
}
#endif // IMG_HUFFMANTABLE_H

View File

@ -2,6 +2,7 @@
#include "subfile.h"
#include "huffmantext.h"
using namespace IMG;
static inline quint32 readVUint32(const quint8 *buffer, quint32 bytes)
{

View File

@ -1,8 +1,10 @@
#ifndef HUFFMANTEXT_H
#define HUFFMANTEXT_H
#ifndef IMG_HUFFMANTEXT_H
#define IMG_HUFFMANTEXT_H
#include "huffmanbuffer.h"
namespace IMG {
class HuffmanText
{
public:
@ -32,4 +34,6 @@ private:
quint8 *_bp4;
};
#endif // HUFFMANTEXT_H
}
#endif // IMG_HUFFMANTEXT_H

View File

@ -2,8 +2,9 @@
#include <QtEndian>
#include <QFile>
#include "vectortile.h"
#include "img.h"
#include "imgdata.h"
using namespace IMG;
typedef QMap<QByteArray, VectorTile*> TileMap;
@ -25,7 +26,7 @@ static SubFile::Type tileType(const char str[3])
return SubFile::Unknown;
}
IMG::IMG(const QString &fileName) : _fileName(fileName)
IMGData::IMGData(const QString &fileName) : _fileName(fileName)
{
#define CHECK(condition) \
if (!(condition)) { \
@ -182,7 +183,7 @@ IMG::IMG(const QString &fileName) : _fileName(fileName)
_valid = true;
}
qint64 IMG::read(QFile &file, char *data, qint64 maxSize) const
qint64 IMGData::read(QFile &file, char *data, qint64 maxSize) const
{
qint64 ret = file.read(data, maxSize);
if (_key)
@ -191,7 +192,7 @@ qint64 IMG::read(QFile &file, char *data, qint64 maxSize) const
return ret;
}
template<class T> bool IMG::readValue(QFile &file, T &val) const
template<class T> bool IMGData::readValue(QFile &file, T &val) const
{
T data;
@ -203,7 +204,7 @@ template<class T> bool IMG::readValue(QFile &file, T &val) const
return true;
}
bool IMG::readBlock(QFile &file, int blockNum, char *data) const
bool IMGData::readBlock(QFile &file, int blockNum, char *data) const
{
if (!file.seek((quint64)blockNum << _blockBits))
return false;

View File

@ -1,14 +1,16 @@
#ifndef IMG_H
#define IMG_H
#ifndef IMG_IMGDATA_H
#define IMG_IMGDATA_H
#include "mapdata.h"
class QFile;
class IMG : public MapData
namespace IMG {
class IMGData : public MapData
{
public:
IMG(const QString &fileName);
IMGData(const QString &fileName);
const QString &fileName() const {return _fileName;}
@ -24,4 +26,6 @@ private:
unsigned _blockBits;
};
#endif // IMG_H
}
#endif // IMG_IMGDATA_H

View File

@ -1,43 +1,14 @@
#ifndef LABEL_H
#define LABEL_H
#ifndef IMG_LABEL_H
#define IMG_LABEL_H
#include <QString>
#include <QDebug>
#include "common/config.h"
#include "shield.h"
#define FIRST_SHIELD Label::Shield::USInterstate
#define LAST_SHIELD Label::Shield::Oval
namespace IMG {
class Label {
public:
class Shield
{
public:
enum Type {
None,
USInterstate,
USShield,
USRound,
Hbox,
Box,
Oval
};
Shield() : _type(None) {}
Shield(Type type, const QString &name) : _type(type), _text(name) {}
Type type() const {return _type;}
const QString &text() const {return _text;}
bool isValid() const {return _type > None && !_text.isEmpty();}
bool operator==(const Shield &other) const
{return _type == other._type && _text == other._text;}
private:
Type _type;
QString _text;
};
Label() {}
Label(const QString &text, const Shield &shield = Shield())
: _text(text), _shield(shield) {}
@ -53,19 +24,10 @@ private:
Shield _shield;
};
inline HASH_T qHash(const Label::Shield &shield)
{
return qHash(shield.text()) ^ qHash(shield.type());
}
#ifndef QT_NO_DEBUG
inline QDebug operator<<(QDebug dbg, const Label::Shield &shield)
{
dbg.nospace() << "Shield(" << shield.type() << ", " << shield.text() << ")";
return dbg.space();
}
inline QDebug operator<<(QDebug dbg, const Label &label)
inline QDebug operator<<(QDebug dbg, const IMG::Label &label)
{
dbg.nospace() << "Label(";
if (label.shield().isValid())
@ -75,4 +37,4 @@ inline QDebug operator<<(QDebug dbg, const Label &label)
}
#endif // QT_NO_DEBUG
#endif // LABEL_H
#endif // IMG_LABEL_H

View File

@ -2,6 +2,8 @@
#include "rgnfile.h"
#include "lblfile.h"
using namespace IMG;
enum Charset {Normal, Symbol, Special};
static quint8 NORMAL_CHARS[] = {
@ -130,7 +132,7 @@ void LBLFile::clear()
Label LBLFile::label6b(Handle &hdl, quint32 offset, bool capitalize) const
{
Label::Shield::Type shieldType = Label::Shield::None;
Shield::Type shieldType = Shield::None;
QByteArray label, shieldLabel;
QByteArray *bap = &label;
Charset curCharSet = Normal;
@ -149,8 +151,7 @@ Label LBLFile::label6b(Handle &hdl, quint32 offset, bool capitalize) const
if (c[cpt] > 0x2f || (curCharSet == Normal && c[cpt] == 0x1d)) {
QString text(QString::fromLatin1(label));
return Label(capitalize && isAllUpperCase(text)
? capitalized(text) : text, Label::Shield(shieldType,
shieldLabel));
? capitalized(text) : text, Shield(shieldType, shieldLabel));
}
switch (curCharSet) {
case Normal:
@ -159,8 +160,7 @@ Label LBLFile::label6b(Handle &hdl, quint32 offset, bool capitalize) const
else if (c[cpt] == 0x1b)
curCharSet = Special;
else if (c[cpt] >= 0x2a && c[cpt] <= 0x2f) {
shieldType = static_cast<Label::Shield::Type>
(c[cpt] - 0x29);
shieldType = static_cast<Shield::Type>(c[cpt] - 0x29);
bap = &shieldLabel;
} else if (bap == &shieldLabel
&& NORMAL_CHARS[c[cpt]] == ' ')
@ -183,7 +183,7 @@ Label LBLFile::label6b(Handle &hdl, quint32 offset, bool capitalize) const
Label LBLFile::str2label(const QVector<quint8> &str, bool capitalize) const
{
Label::Shield::Type shieldType = Label::Shield::None;
Shield::Type shieldType = Shield::None;
QByteArray label, shieldLabel;
QByteArray *bap = &label;
@ -201,7 +201,7 @@ Label LBLFile::str2label(const QVector<quint8> &str, bool capitalize) const
else
bap->append(' ');
} else if (c < 0x07) {
shieldType = static_cast<Label::Shield::Type>(c);
shieldType = static_cast<Shield::Type>(c);
bap = &shieldLabel;
} else if (bap == &shieldLabel && c == 0x20) {
bap = &label;
@ -212,7 +212,7 @@ Label LBLFile::str2label(const QVector<quint8> &str, bool capitalize) const
QString text(_codec.toString(label));
return Label(capitalize && isAllUpperCase(text) ? capitalized(text) : text,
Label::Shield(shieldType, _codec.toString(shieldLabel)));
Shield(shieldType, _codec.toString(shieldLabel)));
}
Label LBLFile::label8b(Handle &hdl, quint32 offset, bool capitalize) const

View File

@ -1,18 +1,20 @@
#ifndef LBLFILE_H
#define LBLFILE_H
#ifndef IMG_LBLFILE_H
#define IMG_LBLFILE_H
#include <QPixmap>
#include "common/textcodec.h"
#include "subfile.h"
#include "label.h"
namespace IMG {
class HuffmanText;
class RGNFile;
class LBLFile : public SubFile
{
public:
LBLFile(const IMG *img)
LBLFile(const IMGData *img)
: SubFile(img), _huffmanText(0), _table(0), _rasters(0), _offset(0),
_size(0), _poiOffset(0), _poiSize(0), _imgOffsetIdSize(0),
_poiMultiplier(0), _multiplier(0), _encoding(0) {}
@ -65,4 +67,6 @@ private:
quint8 _encoding;
};
#endif // LBLFILE_H
}
#endif // IMG_LBLFILE_H

View File

@ -4,40 +4,11 @@
#include "mapdata.h"
using namespace IMG;
#define CACHED_SUBDIVS_COUNT 2048 // ~32MB for both caches together
struct PolyCTX
{
PolyCTX(const RectC &rect, int bits, bool baseMap,
QList<MapData::Poly> *polygons, QList<MapData::Poly> *lines,
QCache<const SubDiv*, MapData::Polys> *polyCache)
: rect(rect), bits(bits), baseMap(baseMap), polygons(polygons),
lines(lines), polyCache(polyCache) {}
const RectC &rect;
int bits;
bool baseMap;
QList<MapData::Poly> *polygons;
QList<MapData::Poly> *lines;
QCache<const SubDiv*, MapData::Polys> *polyCache;
};
struct PointCTX
{
PointCTX(const RectC &rect, int bits, bool baseMap,
QList<MapData::Point> *points,
QCache<const SubDiv*, QList<MapData::Point> > *pointCache)
: rect(rect), bits(bits), baseMap(baseMap), points(points),
pointCache(pointCache) {}
const RectC &rect;
int bits;
bool baseMap;
QList<MapData::Point> *points;
QCache<const SubDiv*, QList<MapData::Point> > *pointCache;
};
inline bool polyCb(VectorTile *tile, void *context)
bool MapData::polyCb(VectorTile *tile, void *context)
{
PolyCTX *ctx = (PolyCTX*)context;
tile->polys(ctx->rect, ctx->bits, ctx->baseMap, ctx->polygons, ctx->lines,
@ -45,7 +16,7 @@ inline bool polyCb(VectorTile *tile, void *context)
return true;
}
inline bool pointCb(VectorTile *tile, void *context)
bool MapData::pointCb(VectorTile *tile, void *context)
{
PointCTX *ctx = (PointCTX*)context;
tile->points(ctx->rect, ctx->bits, ctx->baseMap, ctx->points,

View File

@ -1,5 +1,5 @@
#ifndef MAPDATA_H
#define MAPDATA_H
#ifndef IMG_MAPDATA_H
#define IMG_MAPDATA_H
#include <QList>
#include <QPointF>
@ -11,6 +11,9 @@
#include "label.h"
#include "raster.h"
namespace IMG {
class Style;
class SubDiv;
class SubFile;
@ -89,6 +92,40 @@ private:
QList<Poly> lines;
};
struct PolyCTX
{
PolyCTX(const RectC &rect, int bits, bool baseMap,
QList<MapData::Poly> *polygons, QList<MapData::Poly> *lines,
QCache<const SubDiv*, MapData::Polys> *polyCache)
: rect(rect), bits(bits), baseMap(baseMap), polygons(polygons),
lines(lines), polyCache(polyCache) {}
const RectC &rect;
int bits;
bool baseMap;
QList<MapData::Poly> *polygons;
QList<MapData::Poly> *lines;
QCache<const SubDiv*, MapData::Polys> *polyCache;
};
struct PointCTX
{
PointCTX(const RectC &rect, int bits, bool baseMap,
QList<MapData::Point> *points,
QCache<const SubDiv*, QList<MapData::Point> > *pointCache)
: rect(rect), bits(bits), baseMap(baseMap), points(points),
pointCache(pointCache) {}
const RectC &rect;
int bits;
bool baseMap;
QList<MapData::Point> *points;
QCache<const SubDiv*, QList<MapData::Point> > *pointCache;
};
static bool polyCb(VectorTile *tile, void *context);
static bool pointCb(VectorTile *tile, void *context);
QCache<const SubDiv*, Polys> _polyCache;
QCache<const SubDiv*, QList<Point> > _pointCache;
@ -96,18 +133,20 @@ private:
friend struct PolyCTX;
};
}
#ifndef QT_NO_DEBUG
inline QDebug operator<<(QDebug dbg, const MapData::Point &point)
inline QDebug operator<<(QDebug dbg, const IMG::MapData::Point &point)
{
dbg.nospace() << "Point(" << point.type << ", " << point.label << ")";
return dbg.space();
}
inline QDebug operator<<(QDebug dbg, const MapData::Poly &poly)
inline QDebug operator<<(QDebug dbg, const IMG::MapData::Poly &poly)
{
dbg.nospace() << "Poly(" << poly.type << ", " << poly.label << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG
#endif // MAPDATA_H
#endif // IMG_MAPDATA_H

View File

@ -6,6 +6,7 @@
#include "rgnfile.h"
#include "netfile.h"
using namespace IMG;
static bool readAdjCounts(BitStream4R &bs, QVector<quint16> &cnts, quint16 &mask)
{

View File

@ -1,9 +1,11 @@
#ifndef NETFILE_H
#define NETFILE_H
#ifndef IMG_NETFILE_H
#define IMG_NETFILE_H
#include "subfile.h"
#include "nodfile.h"
namespace IMG {
class LBLFile;
class RGNFile;
class SubDiv;
@ -12,8 +14,9 @@ class HuffmanTable;
class NETFile : public SubFile
{
public:
NETFile(const IMG *img) : SubFile(img), _huffmanTable(0), _tp(0), _offset(0),
_size(0), _linksOffset(0), _linksSize(0), _shift(0), _linksShift(0) {}
NETFile(const IMGData *img) : SubFile(img), _huffmanTable(0), _tp(0),
_offset(0), _size(0), _linksOffset(0), _linksSize(0), _shift(0),
_linksShift(0) {}
NETFile(const QString *path) : SubFile(path), _huffmanTable(0), _tp(0),
_offset(0), _size(0), _linksOffset(0), _linksSize(0), _shift(0),
_linksShift(0) {}
@ -41,4 +44,6 @@ private:
quint8 _shift, _linksShift;
};
#endif // NETFILE_H
}
#endif // IMG_NETFILE_H

View File

@ -1,6 +1,7 @@
#include "bitstream.h"
#include "nodfile.h"
using namespace IMG;
#define ARRAY_SIZE(array) \
(sizeof(array) / sizeof(array[0]))

View File

@ -1,8 +1,10 @@
#ifndef NODFILE_H
#define NODFILE_H
#ifndef IMG_NODFILE_H
#define IMG_NODFILE_H
#include "subfile.h"
namespace IMG {
class NODFile : public SubFile
{
public:
@ -53,7 +55,7 @@ public:
bool eog;
};
NODFile(const IMG *img) : SubFile(img), _indexOffset(0), _indexSize(0),
NODFile(const IMGData *img) : SubFile(img), _indexOffset(0), _indexSize(0),
_indexFlags(0), _blockOffset(0), _blockSize(0), _indexRecordSize(0),
_blockRecordSize(0), _blockShift(0), _nodeShift(0), _indexIdSize(0) {}
NODFile(const QString *path) : SubFile(path), _indexOffset(0), _indexSize(0),
@ -95,4 +97,6 @@ private:
quint8 _blockShift, _nodeShift, _indexIdSize;
};
#endif // NETFILE_H
}
#endif // IMG_NETFILE_H

View File

@ -1,11 +1,13 @@
#ifndef RASTER_H
#define RASTER_H
#ifndef IMG_RASTER_H
#define IMG_RASTER_H
#include <QRect>
#include <QDebug>
#include "common/rectc.h"
#include "common/garmin.h"
namespace IMG {
class LBLFile;
class Raster {
@ -29,12 +31,14 @@ private:
QRect _rect;
};
}
#ifndef QT_NO_DEBUG
inline QDebug operator<<(QDebug dbg, const Raster &raster)
inline QDebug operator<<(QDebug dbg, const IMG::Raster &raster)
{
dbg.nospace() << "Raster(" << raster.rect() << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG
#endif // RASTER_H
#endif // IMG_RASTER_H

View File

@ -1,13 +1,14 @@
#include <QFont>
#include <QPainter>
#include "map/imgmap.h"
#include "textpathitem.h"
#include "textpointitem.h"
#include "map/textpathitem.h"
#include "map/textpointitem.h"
#include "bitmapline.h"
#include "style.h"
#include "lblfile.h"
#include "rastertile.h"
using namespace IMG;
#define AREA(rect) \
(rect.size().width() * rect.size().height())
@ -90,34 +91,34 @@ static QFont *poiFont(Style::FontSize size = Style::Normal)
}
}
static const QColor *shieldBgColor(Label::Shield::Type type)
static const QColor *shieldBgColor(Shield::Type type)
{
switch (type) {
case Label::Shield::USInterstate:
case Label::Shield::Hbox:
case Shield::USInterstate:
case Shield::Hbox:
return &shieldBgColor1;
case Label::Shield::USShield:
case Label::Shield::Box:
case Shield::USShield:
case Shield::Box:
return &shieldBgColor2;
case Label::Shield::USRound:
case Label::Shield::Oval:
case Shield::USRound:
case Shield::Oval:
return &shieldBgColor3;
default:
return 0;
}
}
static int minShieldZoom(Label::Shield::Type type)
static int minShieldZoom(Shield::Type type)
{
switch (type) {
case Label::Shield::USInterstate:
case Label::Shield::Hbox:
case Shield::USInterstate:
case Shield::Hbox:
return 17;
case Label::Shield::USShield:
case Label::Shield::Box:
case Shield::USShield:
case Shield::Box:
return 19;
case Label::Shield::USRound:
case Label::Shield::Oval:
case Shield::USRound:
case Shield::Oval:
return 20;
default:
return 0;
@ -378,15 +379,15 @@ void RasterTile::processShields(const QRect &tileRect,
QList<TextItem*> &textItems)
{
for (int type = FIRST_SHIELD; type <= LAST_SHIELD; type++) {
if (minShieldZoom(static_cast<Label::Shield::Type>(type)) > _zoom)
if (minShieldZoom(static_cast<Shield::Type>(type)) > _zoom)
continue;
QHash<Label::Shield, QPolygonF> shields;
QHash<Label::Shield, const Label::Shield*> sp;
QHash<Shield, QPolygonF> shields;
QHash<Shield, const Shield*> sp;
for (int i = 0; i < _lines.size(); i++) {
const MapData::Poly &poly = _lines.at(i);
const Label::Shield &shield = poly.label.shield();
const Shield &shield = poly.label.shield();
if (!shield.isValid() || shield.type() != type
|| !Style::isMajorRoad(poly.type))
continue;
@ -398,8 +399,8 @@ void RasterTile::processShields(const QRect &tileRect,
sp.insert(shield, &shield);
}
for (QHash<Label::Shield, QPolygonF>::const_iterator it
= shields.constBegin(); it != shields.constEnd(); ++it) {
for (QHash<Shield, QPolygonF>::const_iterator it = shields.constBegin();
it != shields.constEnd(); ++it) {
const QPolygonF &p = it.value();
QRectF rect(p.boundingRect() & tileRect);
if (AREA(rect) < AREA(QRect(0, 0, _img.width()/4, _img.width()/4)))

View File

@ -1,13 +1,16 @@
#ifndef RASTERTILE_H
#define RASTERTILE_H
#ifndef IMG_RASTERTILE_H
#define IMG_RASTERTILE_H
#include <QImage>
#include "mapdata.h"
class QPainter;
class TextItem;
class Style;
class IMGMap;
class TextItem;
namespace IMG {
class Style;
class RasterTile
{
@ -50,4 +53,6 @@ private:
QList<MapData::Point> _points;
};
#endif // RASTERTILE_H
}
#endif // IMG_RASTERTILE_H

View File

@ -8,6 +8,7 @@
#include "nodfile.h"
#include "rgnfile.h"
using namespace IMG;
#define MASK(bits) ((2U << ((bits) - 1U)) - 1U)

View File

@ -1,9 +1,11 @@
#ifndef RGNFILE_H
#define RGNFILE_H
#ifndef IMG_RGNFILE_H
#define IMG_RGNFILE_H
#include "subfile.h"
#include "subdiv.h"
namespace IMG {
class LBLFile;
class NETFile;
class NODFile;
@ -20,7 +22,7 @@ public:
RoadReference = 0x10
};
RGNFile(const IMG *img)
RGNFile(const IMGData *img)
: SubFile(img), _huffmanTable(0), _offset(0), _size(0), _polygonsOffset(0),
_polygonsSize(0), _linesOffset(0), _linesSize(0), _pointsOffset(0),
_pointsSize(0) {}
@ -86,4 +88,6 @@ private:
quint32 _pointsGblFlags;
};
#endif // RGNFILE_H
}
#endif // IMG_RGNFILE_H

55
src/map/IMG/shield.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef IMG_SHIELD_H
#define IMG_SHIELD_H
#include <QString>
#include "common/config.h"
#define FIRST_SHIELD Shield::USInterstate
#define LAST_SHIELD Shield::Oval
namespace IMG {
class Shield
{
public:
enum Type {
None,
USInterstate,
USShield,
USRound,
Hbox,
Box,
Oval
};
Shield() : _type(None) {}
Shield(Type type, const QString &name) : _type(type), _text(name) {}
Type type() const {return _type;}
const QString &text() const {return _text;}
bool isValid() const {return _type > None && !_text.isEmpty();}
bool operator==(const Shield &other) const
{return _type == other._type && _text == other._text;}
private:
Type _type;
QString _text;
};
inline HASH_T qHash(const IMG::Shield &shield)
{
return ::qHash(shield.text()) ^ ::qHash(shield.type());
}
}
#ifndef QT_NO_DEBUG
inline QDebug operator<<(QDebug dbg, const IMG::Shield &shield)
{
dbg.nospace() << "Shield(" << shield.type() << ", " << shield.text() << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG
#endif // IMG_SHIELD_H

View File

@ -2,6 +2,7 @@
#include <QPainter>
#include "style.h"
using namespace IMG;
void Style::defaultPolygonStyle()
{
@ -238,137 +239,137 @@ void Style::defaultPointStyle()
_points[TYPE(0x03)].setTextFontSize(Large);
// POI
_points[0x2a00] = Point(QImage(":/restaurant-11.png"));
_points[0x2a01] = Point(QImage(":/restaurant-11.png"));
_points[0x2a02] = Point(QImage(":/restaurant-noodle-11.png"));
_points[0x2a03] = Point(QImage(":/bbq-11.png"));
_points[0x2a04] = Point(QImage(":/restaurant-noodle-11.png"));
_points[0x2a05] = Point(QImage(":/bakery-11.png"));
_points[0x2a06] = Point(QImage(":/restaurant-11.png"));
_points[0x2a07] = Point(QImage(":/fast-food-11.png"));
_points[0x2a08] = Point(QImage(":/restaurant-pizza-11.png"));
_points[0x2a09] = Point(QImage(":/restaurant-11.png"));
_points[0x2a0a] = Point(QImage(":/restaurant-pizza-11.png"));
_points[0x2a0b] = Point(QImage(":/restaurant-seafood-11.png"));
_points[0x2a0c] = Point(QImage(":/restaurant-11.png"));
_points[0x2a0d] = Point(QImage(":/bakery-11.png"));
_points[0x2a0e] = Point(QImage(":/cafe-11.png"));
_points[0x2a0f] = Point(QImage(":/restaurant-11.png"));
_points[0x2a10] = Point(QImage(":/restaurant-11.png"));
_points[0x2a11] = Point(QImage(":/restaurant-11.png"));
_points[0x2a12] = Point(QImage(":/restaurant-11.png"));
_points[0x2a13] = Point(QImage(":/restaurant-11.png"));
_points[0x2a00] = Point(QImage(":/POI/restaurant-11.png"));
_points[0x2a01] = Point(QImage(":/POI/restaurant-11.png"));
_points[0x2a02] = Point(QImage(":/POI/restaurant-noodle-11.png"));
_points[0x2a03] = Point(QImage(":/POI/bbq-11.png"));
_points[0x2a04] = Point(QImage(":/POI/restaurant-noodle-11.png"));
_points[0x2a05] = Point(QImage(":/POI/bakery-11.png"));
_points[0x2a06] = Point(QImage(":/POI/restaurant-11.png"));
_points[0x2a07] = Point(QImage(":/POI/fast-food-11.png"));
_points[0x2a08] = Point(QImage(":/POI/restaurant-pizza-11.png"));
_points[0x2a09] = Point(QImage(":/POI/restaurant-11.png"));
_points[0x2a0a] = Point(QImage(":/POI/restaurant-pizza-11.png"));
_points[0x2a0b] = Point(QImage(":/POI/restaurant-seafood-11.png"));
_points[0x2a0c] = Point(QImage(":/POI/restaurant-11.png"));
_points[0x2a0d] = Point(QImage(":/POI/bakery-11.png"));
_points[0x2a0e] = Point(QImage(":/POI/cafe-11.png"));
_points[0x2a0f] = Point(QImage(":/POI/restaurant-11.png"));
_points[0x2a10] = Point(QImage(":/POI/restaurant-11.png"));
_points[0x2a11] = Point(QImage(":/POI/restaurant-11.png"));
_points[0x2a12] = Point(QImage(":/POI/restaurant-11.png"));
_points[0x2a13] = Point(QImage(":/POI/restaurant-11.png"));
_points[0x2b01] = Point(QImage(":/lodging-11.png"));
_points[0x2b02] = Point(QImage(":/lodging-11.png"));
_points[0x2b03] = Point(QImage(":/campsite-11.png"));
_points[0x2b04] = Point(QImage(":/village-11.png"));
_points[0x2b06] = Point(QImage(":/shelter-11.png"));
_points[0x2b01] = Point(QImage(":/POI/lodging-11.png"));
_points[0x2b02] = Point(QImage(":/POI/lodging-11.png"));
_points[0x2b03] = Point(QImage(":/POI/campsite-11.png"));
_points[0x2b04] = Point(QImage(":/POI/village-11.png"));
_points[0x2b06] = Point(QImage(":/POI/shelter-11.png"));
_points[0x2c01] = Point(QImage(":/amusement-park-11.png"));
_points[0x2c02] = Point(QImage(":/museum-11.png"));
_points[0x2c03] = Point(QImage(":/library-11.png"));
_points[0x2c04] = Point(QImage(":/landmark-11.png"));
_points[0x2c05] = Point(QImage(":/school-11.png"));
_points[0x2c06] = Point(QImage(":/garden-11.png"));
_points[0x2c07] = Point(QImage(":/zoo-11.png"));
_points[0x2c08] = Point(QImage(":/soccer-11.png"));
_points[0x2c0a] = Point(QImage(":/bar-11.png"));
_points[0x2c0b] = Point(QImage(":/place-of-worship-11.png"));
_points[0x2c0d] = Point(QImage(":/religious-muslim-11.png"));
_points[0x2c0e] = Point(QImage(":/religious-christian-11.png"));
_points[0x2c10] = Point(QImage(":/religious-jewish-11.png"));
_points[0x2d01] = Point(QImage(":/theatre-11.png"));
_points[0x2d02] = Point(QImage(":/bar-11.png"));
_points[0x2d03] = Point(QImage(":/cinema-11.png"));
_points[0x2d04] = Point(QImage(":/casino-11.png"));
_points[0x2d05] = Point(QImage(":/golf-11.png"));
_points[0x2d06] = Point(QImage(":/skiing-11.png"));
_points[0x2d07] = Point(QImage(":/bowling-alley-11.png"));
_points[0x2d09] = Point(QImage(":/swimming-11.png"));
_points[0x2d0a] = Point(QImage(":/fitness-centre-11.png"));
_points[0x2d0b] = Point(QImage(":/airfield-11.png"));
_points[0x2c01] = Point(QImage(":/POI/amusement-park-11.png"));
_points[0x2c02] = Point(QImage(":/POI/museum-11.png"));
_points[0x2c03] = Point(QImage(":/POI/library-11.png"));
_points[0x2c04] = Point(QImage(":/POI/landmark-11.png"));
_points[0x2c05] = Point(QImage(":/POI/school-11.png"));
_points[0x2c06] = Point(QImage(":/POI/garden-11.png"));
_points[0x2c07] = Point(QImage(":/POI/zoo-11.png"));
_points[0x2c08] = Point(QImage(":/POI/soccer-11.png"));
_points[0x2c0a] = Point(QImage(":/POI/bar-11.png"));
_points[0x2c0b] = Point(QImage(":/POI/place-of-worship-11.png"));
_points[0x2c0d] = Point(QImage(":/POI/religious-muslim-11.png"));
_points[0x2c0e] = Point(QImage(":/POI/religious-christian-11.png"));
_points[0x2c10] = Point(QImage(":/POI/religious-jewish-11.png"));
_points[0x2d01] = Point(QImage(":/POI/theatre-11.png"));
_points[0x2d02] = Point(QImage(":/POI/bar-11.png"));
_points[0x2d03] = Point(QImage(":/POI/cinema-11.png"));
_points[0x2d04] = Point(QImage(":/POI/casino-11.png"));
_points[0x2d05] = Point(QImage(":/POI/golf-11.png"));
_points[0x2d06] = Point(QImage(":/POI/skiing-11.png"));
_points[0x2d07] = Point(QImage(":/POI/bowling-alley-11.png"));
_points[0x2d09] = Point(QImage(":/POI/swimming-11.png"));
_points[0x2d0a] = Point(QImage(":/POI/fitness-centre-11.png"));
_points[0x2d0b] = Point(QImage(":/POI/airfield-11.png"));
_points[0x2e02] = Point(QImage(":/grocery-11.png"));
_points[0x2e03] = Point(QImage(":/shop-11.png"));
_points[0x2e05] = Point(QImage(":/pharmacy-11.png"));
_points[0x2e06] = Point(QImage(":/convenience-11.png"));
_points[0x2e07] = Point(QImage(":/clothing-store-11.png"));
_points[0x2e08] = Point(QImage(":/garden-centre-11.png"));
_points[0x2e09] = Point(QImage(":/furniture-11.png"));
_points[0x2e0a] = Point(QImage(":/shop-11.png"));
_points[0x2e0c] = Point(QImage(":/shop-11.png"));
_points[0x2e02] = Point(QImage(":/POI/grocery-11.png"));
_points[0x2e03] = Point(QImage(":/POI/shop-11.png"));
_points[0x2e05] = Point(QImage(":/POI/pharmacy-11.png"));
_points[0x2e06] = Point(QImage(":/POI/convenience-11.png"));
_points[0x2e07] = Point(QImage(":/POI/clothing-store-11.png"));
_points[0x2e08] = Point(QImage(":/POI/garden-centre-11.png"));
_points[0x2e09] = Point(QImage(":/POI/furniture-11.png"));
_points[0x2e0a] = Point(QImage(":/POI/shop-11.png"));
_points[0x2e0c] = Point(QImage(":/POI/shop-11.png"));
_points[0x2f01] = Point(QImage(":/fuel-11.png"));
_points[0x2f02] = Point(QImage(":/car-rental-11.png"));
_points[0x2f03] = Point(QImage(":/car-repair-11.png"));
_points[0x2f04] = Point(QImage(":/airport-11.png"));
_points[0x2f05] = Point(QImage(":/post-11.png"));
_points[0x2f06] = Point(QImage(":/bank-11.png"));
_points[0x2f07] = Point(QImage(":/car-11.png"));
_points[0x2f08] = Point(QImage(":/bus-11.png"));
_points[0x2f09] = Point(QImage(":/harbor-11.png"));
_points[0x2f0b] = Point(QImage(":/parking-11.png"));
_points[0x2f01] = Point(QImage(":/POI/fuel-11.png"));
_points[0x2f02] = Point(QImage(":/POI/car-rental-11.png"));
_points[0x2f03] = Point(QImage(":/POI/car-repair-11.png"));
_points[0x2f04] = Point(QImage(":/POI/airport-11.png"));
_points[0x2f05] = Point(QImage(":/POI/post-11.png"));
_points[0x2f06] = Point(QImage(":/POI/bank-11.png"));
_points[0x2f07] = Point(QImage(":/POI/car-11.png"));
_points[0x2f08] = Point(QImage(":/POI/bus-11.png"));
_points[0x2f09] = Point(QImage(":/POI/harbor-11.png"));
_points[0x2f0b] = Point(QImage(":/POI/parking-11.png"));
_points[0x2f0b].setTextFontSize(None);
_points[0x2f0c] = Point(QImage(":/toilet-11.png"));
_points[0x2f0c] = Point(QImage(":/POI/toilet-11.png"));
_points[0x2f0c].setTextFontSize(None);
_points[0x2f10] = Point(QImage(":/hairdresser-11.png"));
_points[0x2f10] = Point(QImage(":/POI/hairdresser-11.png"));
_points[0x2f12].setTextFontSize(None);
_points[0x2f13] = Point(QImage(":/hardware-11.png"));
_points[0x2f17] = Point(QImage(":/bus-11.png"));
_points[0x2f13] = Point(QImage(":/POI/hardware-11.png"));
_points[0x2f17] = Point(QImage(":/POI/bus-11.png"));
_points[0x3001] = Point(QImage(":/police-11.png"));
_points[0x3002] = Point(QImage(":/hospital-11.png"));
_points[0x3003] = Point(QImage(":/town-hall-11.png"));
_points[0x3006] = Point(QImage(":/entrance-alt1-11.png"));
_points[0x3007] = Point(QImage(":/town-hall-11.png"));
_points[0x3008] = Point(QImage(":/fire-station-11.png"));
_points[0x3001] = Point(QImage(":/POI/police-11.png"));
_points[0x3002] = Point(QImage(":/POI/hospital-11.png"));
_points[0x3003] = Point(QImage(":/POI/town-hall-11.png"));
_points[0x3006] = Point(QImage(":/POI/entrance-alt1-11.png"));
_points[0x3007] = Point(QImage(":/POI/town-hall-11.png"));
_points[0x3008] = Point(QImage(":/POI/fire-station-11.png"));
_points[0x4000] = Point(QImage(":/golf-11.png"));
_points[0x4300] = Point(QImage(":/harbor-11.png"));
_points[0x4400] = Point(QImage(":/fuel-11.png"));
_points[0x4500] = Point(QImage(":/restaurant-11.png"));
_points[0x4600] = Point(QImage(":/bar-11.png"));
_points[0x4900] = Point(QImage(":/park-11.png"));
_points[0x4a00] = Point(QImage(":/picnic-site-11.png"));
_points[0x4c00] = Point(QImage(":/information-11.png"));
_points[0x4800] = Point(QImage(":/campsite-11.png"));
_points[0x4a00] = Point(QImage(":/picnic-site-11.png"));
_points[0x4b00] = Point(QImage(":/hospital-11.png"));
_points[0x4c00] = Point(QImage(":/information-11.png"));
_points[0x4d00] = Point(QImage(":/parking-11.png"));
_points[0x4000] = Point(QImage(":/POI/golf-11.png"));
_points[0x4300] = Point(QImage(":/POI/harbor-11.png"));
_points[0x4400] = Point(QImage(":/POI/fuel-11.png"));
_points[0x4500] = Point(QImage(":/POI/restaurant-11.png"));
_points[0x4600] = Point(QImage(":/POI/bar-11.png"));
_points[0x4900] = Point(QImage(":/POI/park-11.png"));
_points[0x4a00] = Point(QImage(":/POI/picnic-site-11.png"));
_points[0x4c00] = Point(QImage(":/POI/information-11.png"));
_points[0x4800] = Point(QImage(":/POI/campsite-11.png"));
_points[0x4a00] = Point(QImage(":/POI/picnic-site-11.png"));
_points[0x4b00] = Point(QImage(":/POI/hospital-11.png"));
_points[0x4c00] = Point(QImage(":/POI/information-11.png"));
_points[0x4d00] = Point(QImage(":/POI/parking-11.png"));
_points[0x4d00].setTextFontSize(None);
_points[0x4e00] = Point(QImage(":/toilet-11.png"));
_points[0x4e00] = Point(QImage(":/POI/toilet-11.png"));
_points[0x4e00].setTextFontSize(None);
_points[0x5000] = Point(QImage(":/drinking-water-11.png"));
_points[0x5000] = Point(QImage(":/POI/drinking-water-11.png"));
_points[0x5000].setTextFontSize(None);
_points[0x5100] = Point(QImage(":/telephone-11.png"));
_points[0x5200] = Point(QImage(":/viewpoint-11.png"));
_points[0x5300] = Point(QImage(":/skiing-11.png"));
_points[0x5400] = Point(QImage(":/swimming-11.png"));
_points[0x5500] = Point(QImage(":/dam-11.png"));
_points[0x5700] = Point(QImage(":/danger-11.png"));
_points[0x5800] = Point(QImage(":/roadblock-11.png"));
_points[0x5900] = Point(QImage(":/airport-11.png"));
_points[0x5901] = Point(QImage(":/airport-11.png"));
_points[0x5904] = Point(QImage(":/heliport-11.png"));
_points[0x5100] = Point(QImage(":/POI/telephone-11.png"));
_points[0x5200] = Point(QImage(":/POI/viewpoint-11.png"));
_points[0x5300] = Point(QImage(":/POI/skiing-11.png"));
_points[0x5400] = Point(QImage(":/POI/swimming-11.png"));
_points[0x5500] = Point(QImage(":/POI/dam-11.png"));
_points[0x5700] = Point(QImage(":/POI/danger-11.png"));
_points[0x5800] = Point(QImage(":/POI/roadblock-11.png"));
_points[0x5900] = Point(QImage(":/POI/airport-11.png"));
_points[0x5901] = Point(QImage(":/POI/airport-11.png"));
_points[0x5904] = Point(QImage(":/POI/heliport-11.png"));
_points[0x6401] = Point(QImage(":/bridge-11.png"));
_points[0x6402] = Point(QImage(":/building-alt1-11.png"));
_points[0x6403] = Point(QImage(":/cemetery-11.png"));
_points[0x6404] = Point(QImage(":/religious-christian-11.png"));
_points[0x6407] = Point(QImage(":/dam-11.png"));
_points[0x6408] = Point(QImage(":/hospital-11.png"));
_points[0x6409] = Point(QImage(":/dam-11.png"));
_points[0x640d] = Point(QImage(":/communications-tower-11.png"));
_points[0x640e] = Point(QImage(":/park-11.png"));
_points[0x640f] = Point(QImage(":/post-11.png"));
_points[0x6411] = Point(QImage(":/communications-tower-11.png"));
_points[0x6401] = Point(QImage(":/POI/bridge-11.png"));
_points[0x6402] = Point(QImage(":/POI/building-alt1-11.png"));
_points[0x6403] = Point(QImage(":/POI/cemetery-11.png"));
_points[0x6404] = Point(QImage(":/POI/religious-christian-11.png"));
_points[0x6407] = Point(QImage(":/POI/dam-11.png"));
_points[0x6408] = Point(QImage(":/POI/hospital-11.png"));
_points[0x6409] = Point(QImage(":/POI/dam-11.png"));
_points[0x640d] = Point(QImage(":/POI/communications-tower-11.png"));
_points[0x640e] = Point(QImage(":/POI/park-11.png"));
_points[0x640f] = Point(QImage(":/POI/post-11.png"));
_points[0x6411] = Point(QImage(":/POI/communications-tower-11.png"));
_points[0x6508] = Point(QImage(":/waterfall-11.png"));
_points[0x6513] = Point(QImage(":/wetland-11.png"));
_points[0x6604] = Point(QImage(":/beach-11.png"));
_points[0x6616] = Point(QImage(":/mountain-11.png"));
_points[0x6508] = Point(QImage(":/POI/waterfall-11.png"));
_points[0x6513] = Point(QImage(":/POI/wetland-11.png"));
_points[0x6604] = Point(QImage(":/POI/beach-11.png"));
_points[0x6616] = Point(QImage(":/POI/mountain-11.png"));
// NT types

View File

@ -1,5 +1,5 @@
#ifndef STYLE_H
#define STYLE_H
#ifndef IMG_STYLE_H
#define IMG_STYLE_H
#include <QPen>
#include <QBrush>
@ -8,6 +8,8 @@
#define TYPE(t) ((t)<<8)
namespace IMG {
class Style
{
public:
@ -173,10 +175,12 @@ private:
QList<quint32> _drawOrder;
};
}
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const Style::Polygon &polygon);
QDebug operator<<(QDebug dbg, const Style::Line &line);
QDebug operator<<(QDebug dbg, const Style::Point &point);
QDebug operator<<(QDebug dbg, const IMG::Style::Polygon &polygon);
QDebug operator<<(QDebug dbg, const IMG::Style::Line &line);
QDebug operator<<(QDebug dbg, const IMG::Style::Point &point);
#endif // QT_NO_DEBUG
#endif // STYLE_H
#endif // IMG_STYLE_H

View File

@ -1,10 +1,12 @@
#ifndef SUBDIV_H
#define SUBDIV_H
#ifndef IMG_SUBDIV_H
#define IMG_SUBDIV_H
#include <QtGlobal>
#include "common/coordinates.h"
#include "common/garmin.h"
namespace IMG {
class SubDiv {
public:
class Segment {
@ -155,4 +157,6 @@ private:
};
};
#endif // SUBDIV_H
}
#endif // IMG_SUBDIV_H

View File

@ -1,7 +1,8 @@
#include <cstring>
#include "img.h"
#include "imgdata.h"
#include "subfile.h"
using namespace IMG;
#define mod2n(x, m) ((x) & ((m) - 1));

View File

@ -1,13 +1,15 @@
#ifndef SUBFILE_H
#define SUBFILE_H
#ifndef IMG_SUBFILE_H
#define IMG_SUBFILE_H
#include <QVector>
#include <QFile>
#include "img.h"
#include "imgdata.h"
#define BLOCK_BITS 12 /* 4096 bytes */
namespace IMG {
class SubFile
{
public:
@ -43,7 +45,7 @@ public:
int _pos;
};
SubFile(const IMG *img)
SubFile(const IMGData *img)
: _gmpOffset(0), _img(img), _blocks(new QVector<quint16>()), _path(0) {}
SubFile(const SubFile *gmp, quint32 offset) : _gmpOffset(offset),
_img(gmp->_img), _blocks(gmp->_blocks), _path(gmp->_path) {}
@ -153,9 +155,11 @@ protected:
quint32 _gmpOffset;
private:
const IMG *_img;
const IMGData *_img;
QVector<quint16> *_blocks;
const QString *_path;
};
#endif // SUBFILE_H
}
#endif // IMG_SUBFILE_H

View File

@ -2,6 +2,7 @@
#include "subdiv.h"
#include "trefile.h"
using namespace IMG;
static inline double RB(qint32 val)
{
@ -329,12 +330,3 @@ QList<SubDiv*> TREFile::subdivs(const RectC &rect, int bits, bool baseMap)
return list;
}
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const TREFile::MapLevel &level)
{
dbg.nospace() << "MapLevel(" << level.level << ", " << level.bits << ", "
<< level.subdivs << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG

View File

@ -1,5 +1,5 @@
#ifndef TREFILE_H
#define TREFILE_H
#ifndef IMG_TREFILE_H
#define IMG_TREFILE_H
#include <QVector>
#include <QDebug>
@ -8,12 +8,14 @@
#include "common/rtree.h"
#include "subfile.h"
namespace IMG {
class SubDiv;
class TREFile : public SubFile
{
public:
TREFile(const IMG *img) : SubFile(img) {}
TREFile(const IMGData *img) : SubFile(img) {}
TREFile(const QString *path) : SubFile(path) {}
TREFile(const SubFile *gmp, quint32 offset) : SubFile(gmp, offset) {}
~TREFile();
@ -49,8 +51,6 @@ private:
int readExtEntry(Handle &hdl, quint32 &polygons, quint32 &lines,
quint32 &points);
friend QDebug operator<<(QDebug dbg, const MapLevel &level);
RectC _bounds;
QVector<MapLevel> _levels;
quint32 _subdivOffset;
@ -62,8 +62,6 @@ private:
QMap<int, SubDivTree*> _subdivs;
};
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const TREFile::MapLevel &level);
#endif // QT_NO_DEBUG
}
#endif // TREFILE_H
#endif // IMG_TREFILE_H

View File

@ -1,5 +1,6 @@
#include "vectortile.h"
using namespace IMG;
static void copyPolys(const RectC &rect, QList<MapData::Poly> *src,
QList<MapData::Poly> *dst)

View File

@ -1,5 +1,5 @@
#ifndef VECTORTILE_H
#define VECTORTILE_H
#ifndef IMG_VECTORTILE_H
#define IMG_VECTORTILE_H
#include "trefile.h"
#include "rgnfile.h"
@ -7,6 +7,8 @@
#include "netfile.h"
#include "nodfile.h"
namespace IMG {
class VectorTile {
public:
VectorTile()
@ -82,8 +84,10 @@ private:
int _loaded;
};
}
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const VectorTile &tile);
QDebug operator<<(QDebug dbg, const IMG::VectorTile &tile);
#endif // QT_NO_DEBUG
#endif // VECTORTILE_H
#endif // IMG_VECTORTILE_H

View File

@ -192,16 +192,14 @@ bool AQMMap::readHeader()
return false;
if (_bounds.isNull()) {
double minX = OSM::index2mercator(qMin((1<<zoom) - 1,
qMax(0, bounds.left())), zoom);
double minY = OSM::index2mercator(qMin((1<<zoom) - 1,
qMax(0, bounds.top())), zoom);
double maxX = OSM::index2mercator(qMin((1<<zoom) - 1,
qMax(0, bounds.right())) + 1, zoom);
double maxY = OSM::index2mercator(qMin((1<<zoom) - 1,
qMax(0, bounds.bottom())) + 1, zoom);
Coordinates tl(OSM::m2ll(QPointF(minX, -minY)));
Coordinates br(OSM::m2ll(QPointF(maxX, -maxY)));
int minX = qMin((1<<zoom) - 1, qMax(0, bounds.left()));
int minY = qMin((1<<zoom) - 1, qMax(0, bounds.top()));
int maxX = qMin((1<<zoom) - 1, qMax(0, bounds.right())) + 1;
int maxY = qMin((1<<zoom) - 1, qMax(0, bounds.bottom())) + 1;
Coordinates tl(OSM::tile2ll(QPoint(minX, minY), zoom));
tl.rlat() = -tl.lat();
Coordinates br(OSM::tile2ll(QPoint(maxX, maxY), zoom));
br.rlat() = -br.lat();
// Workaround of broken zoom levels 0 and 1 due to numerical
// instability
tl.rlat() = qMin(tl.lat(), OSM::BOUNDS.top());

View File

@ -5,14 +5,15 @@
#include "common/rectc.h"
#include "common/range.h"
#include "common/wgs84.h"
#include "IMG/img.h"
#include "IMG/gmap.h"
#include "IMG/imgdata.h"
#include "IMG/gmapdata.h"
#include "IMG/rastertile.h"
#include "osm.h"
#include "pcs.h"
#include "rectd.h"
#include "imgmap.h"
using namespace IMG;
#define TILE_SIZE 384
#define TEXT_EXTENT 160
@ -24,7 +25,7 @@ static QList<MapData*> overlays(const QString &fileName)
for (int i = 1; i < 32; i++) {
QString ol(fileName + "." + QString::number(i));
if (QFileInfo(ol).isFile()) {
MapData *data = new IMG(ol);
MapData *data = new IMGData(ol);
if (data->isValid())
list.append(data);
else {
@ -42,10 +43,10 @@ static QList<MapData*> overlays(const QString &fileName)
IMGMap::IMGMap(const QString &fileName, QObject *parent)
: Map(fileName, parent), _projection(PCS::pcs(3857)), _valid(false)
{
if (GMAP::isGMAP(fileName))
_data.append(new GMAP(fileName));
if (GMAPData::isGMAP(fileName))
_data.append(new GMAPData(fileName));
else {
_data.append(new IMG(fileName));
_data.append(new IMGData(fileName));
_data.append(overlays(fileName));
}
@ -164,7 +165,7 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
QRectF polyRect(ttl, QPointF(ttl.x() + TILE_SIZE,
ttl.y() + TILE_SIZE));
polyRect &= bounds();
polyRect &= _bounds;
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
_transform.img2proj(polyRect.bottomRight()));
_data.at(n)->polys(polyRectD.toRectC(_projection, 20), _zoom,
@ -173,7 +174,7 @@ void IMGMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT,
ttl.y() - TEXT_EXTENT), QPointF(ttl.x() + TILE_SIZE
+ TEXT_EXTENT, ttl.y() + TILE_SIZE + TEXT_EXTENT));
pointRect &= bounds();
pointRect &= _bounds;
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
_transform.img2proj(pointRect.bottomRight()));
_data.at(n)->points(pointRectD.toRectC(_projection, 20),

View File

@ -45,7 +45,7 @@ private:
Transform transform(int zoom) const;
void updateTransform();
QList<MapData *> _data;
QList<IMG::MapData *> _data;
int _zoom;
Projection _projection;
Transform _transform;

View File

@ -1,6 +1,7 @@
#include <QFileInfo>
#include <QDir>
#include <QApplication>
#include "IMG/gmapdata.h"
#include "atlas.h"
#include "ozimap.h"
#include "jnxmap.h"
@ -9,11 +10,11 @@
#include "mbtilesmap.h"
#include "rmap.h"
#include "imgmap.h"
#include "IMG/gmap.h"
#include "bsbmap.h"
#include "kmzmap.h"
#include "aqmmap.h"
#include "sqlitemap.h"
#include "mapsforgemap.h"
#include "invalidmap.h"
#include "maplist.h"
@ -31,7 +32,7 @@ Map *MapList::loadFile(const QString &path, bool *isDir)
} else if (suffix == "xml") {
if (MapSource::isMap(path)) {
map = MapSource::loadMap(path);
} else if (GMAP::isGMAP(path)) {
} else if (IMG::GMAPData::isGMAP(path)) {
map = new IMGMap(path);
if (isDir)
*isDir = true;
@ -46,7 +47,12 @@ Map *MapList::loadFile(const QString &path, bool *isDir)
map = new RMap(path);
else if (suffix == "img")
map = new IMGMap(path);
else if (suffix == "map" || suffix == "tar")
else if (suffix == "map") {
if (Mapsforge::MapData::isMapsforge(path))
map = new MapsforgeMap(path);
else
map = new OziMap(path);
} else if (suffix == "tar")
map = new OziMap(path);
else if (suffix == "kap")
map = new BSBMap(path);
@ -112,6 +118,7 @@ QString MapList::formats()
+ qApp->translate("MapList", "Garmin JNX maps") + " (*.jnx);;"
+ qApp->translate("MapList", "BSB nautical charts") + " (*.kap);;"
+ qApp->translate("MapList", "KMZ maps") + " (*.kmz);;"
+ qApp->translate("MapList", "Mapsforge maps") + " (*.map);;"
+ qApp->translate("MapList", "OziExplorer maps") + " (*.map);;"
+ qApp->translate("MapList", "MBTiles maps") + " (*.mbtiles);;"
+ qApp->translate("MapList", "TwoNav maps") + " (*.rmap *.rtmap);;"

22
src/map/mapsforge/cmp.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef MAPSFORGE_CMP_H
#define MAPSFORGE_CMP_H
#include <QByteArray>
#include <cstring>
namespace Mapsforge {
inline bool cmp(const QByteArray &b1, const QByteArray &b2)
{
int len = b1.length();
if (!len)
return true;
if (len != b2.length())
return false;
return !memcmp(b1.constData(), b2.constData(), len);
}
}
#endif // MAPSFORGE_CMP_H

View File

@ -0,0 +1,714 @@
#include <cstring>
#include <QtEndian>
#include <QFile>
#include <QDataStream>
#include <QColor>
#include "map/osm.h"
#include "subfile.h"
#include "mapdata.h"
using namespace Mapsforge;
#define MAGIC "mapsforge binary OSM"
#define MD(val) ((val) / 1e6)
#define OFFSET_MASK 0x7FFFFFFFFFL
static uint pointType(const QVector<MapData::Tag> &tags)
{
for (int i = 0; i < tags.size(); i++) {
const MapData::Tag &tag = tags.at(i);
if (tag.key == "place") {
if (tag.value == "country")
return 4;
else if (tag.value == "city")
return 3;
else if (tag.value == "town")
return 2;
else if (tag.value == "village")
return 1;
else
return 0;
}
}
return 0;
}
static void setPointId(MapData::Point &p)
{
uint hash = (uint)qHash(QPair<double,double>((uint)qHash(
QPair<double, double>(p.coordinates.lon(), p.coordinates.lat())),
(uint)qHash(p.label)));
uint type = pointType(p.tags);
p.id = ((quint64)type)<<32 | hash;
}
static void copyPaths(const RectC &rect, const QList<MapData::Path> *src,
QList<MapData::Path> *dst)
{
for (int i = 0; i < src->size(); i++)
if (rect.intersects(src->at(i).poly.boundingRect()))
dst->append(src->at(i));
}
static void copyPoints(const RectC &rect, const QList<MapData::Point> *src,
QList<MapData::Point> *dst)
{
for (int i = 0; i < src->size(); i++)
if (rect.contains(src->at(i).coordinates))
dst->append(src->at(i));
}
static double distance(const Coordinates &c1, const Coordinates &c2)
{
return hypot(c1.lon() - c2.lon(), c1.lat() - c2.lat());
}
static bool isClosed(const Polygon &poly)
{
if (poly.isEmpty() || poly.first().isEmpty())
return false;
return (distance(poly.first().first(), poly.first().last()) < 0.000000001);
}
static bool readTags(SubFile &subfile, int count,
const QVector<MapData::Tag> &tags, QVector<MapData::Tag> &list)
{
QVector<quint32> ids(count);
list.resize(count);
for (int i = 0; i < count; i++) {
if (!subfile.readVUInt32(ids[i]))
return false;
if (ids[i] >= (quint32)tags.size())
return false;
}
for (int i = 0; i < count; i++) {
const MapData::Tag &tag = tags.at(ids.at(i));
if (tag.value.length() == 2 && tag.value.at(0) == '%') {
QByteArray value;
if (tag.value.at(1) == 'b') {
quint8 b;
if (!subfile.readByte(b))
return false;
value.setNum(b);
} else if (tag.value.at(1) == 'i') {
qint32 u;
if (!subfile.readInt32(u))
return false;
if (tag.key.contains(":colour"))
value = QColor((quint32)u).name().toLatin1();
else
value.setNum(u);
} else if (tag.value.at(1) == 'f') {
quint32 u;
if (!subfile.readUInt32(u))
return false;
float *f = (float *)&u;
value.setNum(*f);
} else if (tag.value.at(1) == 'h') {
quint16 s;
if (!subfile.readUInt16(s))
return false;
value.setNum(s);
} else if (tag.value.at(1) == 's') {
if (!subfile.readString(value))
return false;
} else
value = tag.value;
list[i] = MapData::Tag(tag.key, value);
} else
list[i] = tag;
}
return true;
}
static bool readSingleDelta(SubFile &subfile, const Coordinates &c,
int count, QVector<Coordinates> &nodes)
{
qint32 mdLat, mdLon;
if (!(subfile.readVInt32(mdLat) && subfile.readVInt32(mdLon)))
return false;
double lat = c.lat() + MD(mdLat);
double lon = c.lon() + MD(mdLon);
nodes.reserve(count);
nodes.append(Coordinates(lon, lat));
for (int i = 1; i < count; i++) {
if (!(subfile.readVInt32(mdLat) && subfile.readVInt32(mdLon)))
return false;
lat = lat + MD(mdLat);
lon = lon + MD(mdLon);
nodes.append(Coordinates(lon, lat));
}
return true;
}
static bool readDoubleDelta(SubFile &subfile, const Coordinates &c,
int count, QVector<Coordinates> &nodes)
{
qint32 mdLat, mdLon;
if (!(subfile.readVInt32(mdLat) && subfile.readVInt32(mdLon)))
return false;
double lat = c.lat() + MD(mdLat);
double lon = c.lon() + MD(mdLon);
double prevLat = 0;
double prevLon = 0;
nodes.reserve(count);
nodes.append(Coordinates(lon, lat));
for (int i = 1; i < count; i++) {
if (!(subfile.readVInt32(mdLat) && subfile.readVInt32(mdLon)))
return false;
double singleLat = MD(mdLat) + prevLat;
double singleLon = MD(mdLon) + prevLon;
lat += singleLat;
lon += singleLon;
nodes.append(Coordinates(lon, lat));
prevLat = singleLat;
prevLon = singleLon;
}
return true;
}
static bool readPolygon(SubFile &subfile, const Coordinates &c,
bool doubleDelta, Polygon &polygon)
{
quint32 blocks, nodes;
if (!subfile.readVUInt32(blocks))
return false;
polygon.reserve(blocks);
for (quint32 i = 0; i < blocks; i++) {
if (!subfile.readVUInt32(nodes) || !nodes)
return false;
QVector<Coordinates> path;
if (doubleDelta) {
if (!readDoubleDelta(subfile, c, nodes, path))
return false;
} else {
if (!readSingleDelta(subfile, c, nodes, path))
return false;
}
polygon.append(path);
}
return true;
}
static bool readOffset(QDataStream &stream, quint64 &offset)
{
quint8 b0, b1, b2, b3, b4;
stream >> b0 >> b1 >> b2 >> b3 >> b4;
offset = b4 | ((quint64)b3) << 8 | ((quint64)b2) << 16
| ((quint64)b1) << 24 | ((quint64)b0) << 32;
return (stream.status() == QDataStream::Ok);
}
bool MapData::readSubFiles()
{
QDataStream stream(&_file);
for (int i = 0; i < _subFiles.size(); i++) {
const SubFileInfo &f = _subFiles.at(i);
quint64 offset, nextOffset;
stream.device()->seek(f.offset);
QPoint tl(OSM::ll2tile(_bounds.topLeft(), f.base));
QPoint br(OSM::ll2tile(_bounds.bottomRight(), f.base));
if (!readOffset(stream, offset) || (offset & OFFSET_MASK) > f.size)
return false;
_tiles.append(new TileTree());
for (int h = tl.y(); h <= br.y(); h++) {
for (int w = tl.x(); w <= br.x(); w++) {
if (!(h == br.y() && w == br.x())) {
if (!readOffset(stream, nextOffset)
|| (nextOffset & OFFSET_MASK) > f.size)
return false;
if (nextOffset == offset)
continue;
}
Coordinates ttl(OSM::tile2ll(QPoint(w, h), f.base));
ttl = Coordinates(ttl.lon(), -ttl.lat());
Coordinates tbr(OSM::tile2ll(QPoint(w + 1, h + 1), f.base));
tbr = Coordinates(tbr.lon(), -tbr.lat());
RectC bounds(ttl, tbr);
double min[2], max[2];
min[0] = bounds.left();
min[1] = bounds.bottom();
max[0] = bounds.right();
max[1] = bounds.top();
_tiles.last()->Insert(min, max, new VectorTile(offset, bounds));
offset = nextOffset;
}
}
}
return true;
}
bool MapData::readHeader()
{
char magic[sizeof(MAGIC) - 1];
quint32 hdrSize, version;
quint64 fileSize, date;
qint32 minLat, minLon, maxLat, maxLon;
quint16 tags;
quint8 flags, zooms;
QByteArray projection, tag;
if (_file.read(magic, sizeof(magic)) < (int)sizeof(magic)
|| memcmp(magic, MAGIC, sizeof(magic)))
return false;
if (_file.read((char*)&hdrSize, sizeof(hdrSize)) < (qint64)sizeof(hdrSize))
return false;
SubFile subfile(_file, sizeof(magic) + sizeof(hdrSize),
qFromBigEndian(hdrSize));
if (!subfile.seek(0))
return false;
if (!(subfile.readUInt32(version) && subfile.readUInt64(fileSize)
&& subfile.readUInt64(date) && subfile.readInt32(minLat)
&& subfile.readInt32(minLon) && subfile.readInt32(maxLat)
&& subfile.readInt32(maxLon) && subfile.readUInt16(_tileSize)
&& subfile.readString(projection) && subfile.readByte(flags)))
return false;
if (projection != "Mercator") {
_errorString = projection + ": invalid/unsupported projection";
return false;
}
if (flags & 0x80) {
_errorString = "DEBUG maps not supported";
return false;
}
if (flags & 0x40) {
qint32 startLon, startLat;
if (!(subfile.readInt32(startLat) && subfile.readInt32(startLon)))
return false;
}
if (flags & 0x20) {
quint8 startZoom;
if (!subfile.readByte(startZoom))
return false;
}
if (flags & 0x10) {
QByteArray lang;
if (!subfile.readString(lang))
return false;
}
if (flags & 0x08) {
QByteArray comment;
if (!subfile.readString(comment))
return false;
}
if (flags & 0x04) {
QByteArray createdBy;
if (!subfile.readString(createdBy))
return false;
}
if (!subfile.readUInt16(tags))
return false;
_pointTags.resize(tags);
for (quint16 i = 0; i < tags; i++) {
if (!subfile.readString(tag))
return false;
_pointTags[i] = tag;
}
if (!subfile.readUInt16(tags))
return false;
_pathTags.resize(tags);
for (quint16 i = 0; i < tags; i++) {
if (!subfile.readString(tag))
return false;
_pathTags[i] = tag;
}
if (!subfile.readByte(zooms))
return false;
_subFiles.resize(zooms);
for (quint8 i = 0; i < zooms; i++) {
if (!(subfile.readByte(_subFiles[i].base)
&& subfile.readByte(_subFiles[i].min)
&& subfile.readByte(_subFiles[i].max)
&& subfile.readUInt64(_subFiles[i].offset)
&& subfile.readUInt64(_subFiles[i].size)))
return false;
}
_bounds = RectC(Coordinates(MD(minLon), MD(maxLat)),
Coordinates(MD(maxLon), MD(minLat)));
return true;
}
MapData::MapData(const QString &fileName) : _file(fileName)
{
if (!_file.open(QFile::ReadOnly)) {
_errorString = _file.errorString();
return;
}
if (!readHeader())
return;
_file.close();
_pathCache.setMaxCost(256);
_pointCache.setMaxCost(256);
_valid = true;
}
MapData::~MapData()
{
clearTiles();
}
RectC MapData::bounds() const
{
/* Align the map bounds with the OSM tiles to prevent area overlap artifacts
at least when using EPSG:3857 projection. */
int zoom = _subFiles.last().base;
QPoint tl(OSM::mercator2tile(OSM::ll2m(_bounds.topLeft()), zoom));
QPoint br(OSM::mercator2tile(OSM::ll2m(_bounds.bottomRight()), zoom));
Coordinates ctl(OSM::tile2ll(tl, zoom));
ctl.rlat() = -ctl.lat();
Coordinates cbr(OSM::tile2ll(br, zoom));
cbr.rlat() = -cbr.lat();
return RectC(ctl, cbr);
}
void MapData::load()
{
if (_file.open(QIODevice::ReadOnly))
readSubFiles();
}
void MapData::clear()
{
_file.close();
_pathCache.clear();
_pointCache.clear();
clearTiles();
}
void MapData::clearTiles()
{
TileTree::Iterator it;
for (int i = 0; i < _tiles.size(); i++) {
TileTree *t = _tiles.at(i);
for (t->GetFirst(it); !t->IsNull(it); t->GetNext(it))
delete t->GetAt(it);
}
qDeleteAll(_tiles);
_tiles.clear();
}
bool MapData::pathCb(VectorTile *tile, void *context)
{
PathCTX *ctx = (PathCTX*)context;
ctx->data->paths(tile, ctx->rect, ctx->zoom, ctx->list);
return true;
}
bool MapData::pointCb(VectorTile *tile, void *context)
{
PointCTX *ctx = (PointCTX*)context;
ctx->data->points(tile, ctx->rect, ctx->zoom, ctx->list);
return true;
}
int MapData::level(int zoom) const
{
for (int i = 0; i < _subFiles.size(); i++)
if (zoom <= _subFiles.at(i).max)
return i;
return _subFiles.size() - 1;
}
void MapData::points(const RectC &rect, int zoom, QList<Point> *list)
{
int l(level(zoom));
PointCTX ctx(this, rect, zoom, list);
double min[2], max[2];
min[0] = rect.left();
min[1] = rect.bottom();
max[0] = rect.right();
max[1] = rect.top();
_tiles.at(l)->Search(min, max, pointCb, &ctx);
}
void MapData::points(const VectorTile *tile, const RectC &rect, int zoom,
QList<Point> *list)
{
Key key(tile, zoom);
QList<Point> *cached = _pointCache.object(key);
if (!cached) {
QList<Point> *p = new QList<Point>();
if (readPoints(tile, zoom, p)) {
copyPoints(rect, p, list);
_pointCache.insert(key, p);
} else
delete p;
} else
copyPoints(rect, cached, list);
}
void MapData::paths(const RectC &rect, int zoom, QList<Path> *list)
{
int l(level(zoom));
PathCTX ctx(this, rect, zoom, list);
double min[2], max[2];
min[0] = rect.left();
min[1] = rect.bottom();
max[0] = rect.right();
max[1] = rect.top();
_tiles.at(l)->Search(min, max, pathCb, &ctx);
}
void MapData::paths(const VectorTile *tile, const RectC &rect, int zoom,
QList<Path> *list)
{
Key key(tile, zoom);
QList<Path> *cached = _pathCache.object(key);
if (!cached) {
QList<Path> *p = new QList<Path>();
if (readPaths(tile, zoom, p)) {
copyPaths(rect, p, list);
_pathCache.insert(key, p);
} else
delete p;
} else
copyPaths(rect, cached, list);
}
bool MapData::readPaths(const VectorTile *tile, int zoom, QList<Path> *list)
{
const SubFileInfo &info = _subFiles.at(level(zoom));
SubFile subfile(_file, info.offset, info.size);
int rows = info.max - info.min + 1;
QVector<unsigned> paths(rows);
quint32 blocks, unused, val, cnt = 0;
quint16 bitmap;
quint8 flags;
QByteArray label, houseNumber, reference;
if (!subfile.seek(tile->offset & OFFSET_MASK))
return false;
for (int i = 0; i < rows; i++) {
if (!(subfile.readVUInt32(unused) && subfile.readVUInt32(val)))
return false;
cnt += val;
paths[i] = cnt;
}
if (!subfile.readVUInt32(val))
return false;
if (!subfile.seek(subfile.pos() + val))
return false;
for (unsigned i = 0; i < paths[zoom - info.min]; i++) {
Path p;
if (!(subfile.readVUInt32(unused) && subfile.readUInt16(bitmap)
&& subfile.readByte(flags)))
return false;
p.layer = flags >> 4;
int tags = flags & 0x0F;
if (!readTags(subfile, tags, _pathTags, p.tags))
return false;
if (!subfile.readByte(flags))
return false;
if (flags & 0x80) {
if (!subfile.readString(label))
return false;
p.label = label.split('\r').first();
}
if (flags & 0x40) {
if (!subfile.readString(houseNumber))
return false;
}
if (flags & 0x20) {
if (!subfile.readString(reference))
return false;
p.reference = reference;
}
if (flags & 0x10) {
qint32 unused;
if (!(subfile.readVInt32(unused) && subfile.readVInt32(unused)))
return false;
}
if (flags & 0x08) {
if (!subfile.readVUInt32(blocks))
return false;
} else
blocks = 1;
Q_ASSERT(blocks);
for (unsigned j = 0; j < blocks; j++) {
if (!readPolygon(subfile, tile->pos, flags & 0x04, p.poly))
return false;
p.closed = isClosed(p.poly);
list->append(p);
}
}
return true;
}
bool MapData::readPoints(const VectorTile *tile, int zoom, QList<Point> *list)
{
const SubFileInfo &info = _subFiles.at(level(zoom));
SubFile subfile(_file, info.offset, info.size);
int rows = info.max - info.min + 1;
QVector<unsigned> points(rows);
quint32 val, unused, cnt = 0;
quint8 flags;
QByteArray label, houseNumber;
if (!subfile.seek(tile->offset & OFFSET_MASK))
return false;
for (int i = 0; i < rows; i++) {
if (!(subfile.readVUInt32(val) && subfile.readVUInt32(unused)))
return false;
cnt += val;
points[i] = cnt;
}
if (!subfile.readVUInt32(unused))
return false;
for (unsigned i = 0; i < points[zoom - info.min]; i++) {
qint32 lat, lon;
if (!(subfile.readVInt32(lat) && subfile.readVInt32(lon)))
return false;
Point p(Coordinates(tile->pos.lon() + MD(lon),
tile->pos.lat() + MD(lat)));
if (!subfile.readByte(flags))
return false;
p.layer = flags >> 4;
int tags = flags & 0x0F;
if (!readTags(subfile, tags, _pointTags, p.tags))
return false;
if (!subfile.readByte(flags))
return false;
if (flags & 0x80) {
if (!subfile.readString(label))
return false;
p.label = label.split('\r').first();
}
if (flags & 0x40) {
if (!subfile.readString(houseNumber))
return false;
}
if (flags & 0x20) {
qint32 unused;
if (!subfile.readVInt32(unused))
return false;
}
setPointId(p);
list->append(p);
}
return true;
}
bool MapData::isMapsforge(const QString &path)
{
QFile file(path);
char magic[sizeof(MAGIC) - 1];
if (!file.open(QFile::ReadOnly))
return false;
if (file.read(magic, sizeof(magic)) < (qint64)sizeof(magic))
return false;
return !memcmp(magic, MAGIC, sizeof(magic));
}
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const Mapsforge::MapData::Tag &tag)
{
dbg.nospace() << "Tag(" << tag.key << ", " << tag.value << ")";
return dbg.space();
}
QDebug operator<<(QDebug dbg, const MapData::Path &path)
{
dbg.nospace() << "Path(" << path.poly.boundingRect() << ", " << path.label
<< ", " << path.tags << ")";
return dbg.space();
}
QDebug operator<<(QDebug dbg, const MapData::Point &point)
{
dbg.nospace() << "Point(" << point.coordinates << "," << point.label
<< ", " << point.tags << ")";
return dbg.space();
}
#endif // QT_NO_DEBUG

183
src/map/mapsforge/mapdata.h Normal file
View File

@ -0,0 +1,183 @@
#ifndef MAPSFORGE_MAPDATA_H
#define MAPSFORGE_MAPDATA_H
#include <QFile>
#include <QCache>
#include <QPainterPath>
#include "common/config.h"
#include "common/rectc.h"
#include "common/rtree.h"
#include "common/range.h"
#include "common/polygon.h"
#include "cmp.h"
namespace Mapsforge {
class MapData
{
public:
MapData(const QString &path);
~MapData();
struct Tag {
Tag() {}
Tag(const QByteArray &key, const QByteArray &value)
: key(key), value(value) {}
Tag(const QByteArray &str)
{
QList<QByteArray> l(str.split('='));
if (l.size() == 2) {
key = l.at(0);
value = l.at(1);
}
}
bool operator==(const Tag &other) const
{return cmp(key, other.key) && cmp(value, other.value);}
QByteArray key;
QByteArray value;
};
struct Point {
Point(const Coordinates &c) : coordinates(c) {}
Coordinates coordinates;
QString label;
QVector<Tag> tags;
int layer;
quint64 id;
bool operator<(const Point &other) const
{return id > other.id;}
};
struct Path {
Polygon poly;
QString label;
QString reference;
QVector<Tag> tags;
int layer;
bool closed;
QPainterPath path;
bool operator<(const Path &other) const
{return layer < other.layer;}
};
RectC bounds() const;
Range zooms() const
{return Range(_subFiles.first().min, _subFiles.last().max);}
int tileSize() const {return _tileSize;}
void points(const RectC &rect, int zoom, QList<Point> *list);
void paths(const RectC &rect, int zoom, QList<Path> *list);
void load();
void clear();
bool isValid() const {return _valid;}
QString errorString() const {return _errorString;}
static bool isMapsforge(const QString &path);
private:
struct SubFileInfo {
quint8 base;
quint8 min;
quint8 max;
quint64 offset;
quint64 size;
};
struct VectorTile {
VectorTile(size_t offset, const RectC &rect)
: offset(offset), pos(rect.topLeft()) {}
size_t offset;
Coordinates pos;
};
struct PathCTX {
PathCTX(MapData *data, const RectC &rect, int zoom, QList<Path> *list)
: data(data), rect(rect), zoom(zoom), list(list) {}
MapData *data;
const RectC &rect;
int zoom;
QList<Path> *list;
};
struct PointCTX {
PointCTX(MapData *data, const RectC &rect, int zoom, QList<Point> *list)
: data(data), rect(rect), zoom(zoom), list(list) {}
MapData *data;
const RectC &rect;
int zoom;
QList<Point> *list;
};
struct Key {
Key(const VectorTile *tile, int zoom) : tile(tile), zoom(zoom) {}
bool operator==(const Key &other) const
{return tile == other.tile && zoom == other.zoom;}
const VectorTile *tile;
int zoom;
};
typedef RTree<VectorTile *, double, 2> TileTree;
bool readHeader();
bool readSubFiles();
void clearTiles();
int level(int zoom) const;
void paths(const VectorTile *tile, const RectC &rect, int zoom,
QList<Path> *list);
void points(const VectorTile *tile, const RectC &rect, int zoom,
QList<Point> *list);
bool readPaths(const VectorTile *tile, int zoom, QList<Path> *list);
bool readPoints(const VectorTile *tile, int zoom, QList<Point> *list);
static bool pathCb(VectorTile *tile, void *context);
static bool pointCb(VectorTile *tile, void *context);
friend HASH_T qHash(const MapData::Key &key);
QFile _file;
RectC _bounds;
quint16 _tileSize;
QVector<Tag> _pointTags, _pathTags;
QVector<SubFileInfo> _subFiles;
QList<TileTree*> _tiles;
QCache<Key, QList<Path> > _pathCache;
QCache<Key, QList<Point> > _pointCache;
bool _valid;
QString _errorString;
};
inline HASH_T qHash(const MapData::Key &key)
{
return ::qHash(key.tile) ^ ::qHash(key.zoom);
}
inline HASH_T qHash(const MapData::Tag &tag)
{
return ::qHash(tag.key) ^ ::qHash(tag.value);
}
}
#ifndef QT_NO_DEBUG
QDebug operator<<(QDebug dbg, const Mapsforge::MapData::Tag &tag);
QDebug operator<<(QDebug dbg, const Mapsforge::MapData::Path &path);
QDebug operator<<(QDebug dbg, const Mapsforge::MapData::Point &point);
#endif // QT_NO_DEBUG
#endif // MAPSFORGE_MAPDATA_H

View File

@ -0,0 +1,184 @@
#include <QPainter>
#include <QCache>
#include "common/programpaths.h"
#include "map/mapsforgemap.h"
#include "map/textpathitem.h"
#include "map/textpointitem.h"
#include "rastertile.h"
using namespace Mapsforge;
static const Style& style()
{
static Style s(ProgramPaths::renderthemeFile());
return s;
}
void RasterTile::processPoints(QList<TextItem*> &textItems)
{
const Style &s = style();
for (int i = 0; i < _points.size(); i++) {
const MapData::Point &point = _points.at(i);
const QString *label = point.label.isEmpty() ? 0 : &(point.label);
const Style::TextRender *ti = 0;
const Style::Symbol *si = 0;
if (label) {
for (int i = 0; i < s.pointLabels().size(); i++) {
const Style::TextRender &ri = s.pointLabels().at(i);
if (ri.rule().match(_zoom, point.tags)) {
ti = &ri;
break;
}
}
}
for (int i = 0; i < s.symbols().size(); i++) {
const Style::Symbol &ri = s.symbols().at(i);
if (ri.rule().match(_zoom, point.tags)) {
si = &ri;
break;
}
}
if (!ti && !si)
continue;
const QImage *img = si ? &si->img() : 0;
const QFont *font = ti ? &ti->font() : 0;
const QColor *color = ti ? &ti->fillColor() : 0;
TextPointItem *item = new TextPointItem(
ll2xy(point.coordinates).toPoint(), label, font, img, color, 0, false);
if (item->isValid() && !item->collides(textItems))
textItems.append(item);
else
delete item;
}
}
void RasterTile::processStreetNames(const QRect &tileRect,
QList<TextItem*> &textItems)
{
const Style &s = style();
for (int i = 0; i < s.pathLabels().size(); i++) {
const Style::TextRender &ri = s.pathLabels().at(i);
for (int j = 0; j < _paths.size(); j++) {
MapData::Path &path = _paths[j];
if (path.label.isEmpty())
continue;
if (!path.path.elementCount())
continue;
if (!ri.rule().match(_zoom, path.closed, path.tags))
continue;
TextPathItem *item = new TextPathItem(path.path,
&path.label, tileRect, &ri.font(), &ri.fillColor(),
&ri.strokeColor());
if (item->isValid() && !item->collides(textItems))
textItems.append(item);
else
delete item;
}
}
}
void RasterTile::drawTextItems(QPainter *painter,
const QList<TextItem*> &textItems)
{
for (int i = 0; i < textItems.size(); i++)
textItems.at(i)->paint(painter);
}
QPainterPath RasterTile::painterPath(const Polygon &polygon) const
{
QPainterPath path;
for (int i = 0; i < polygon.size(); i++) {
const QVector<Coordinates> &subpath = polygon.at(i);
path.moveTo(ll2xy(subpath.first()));
for (int j = 1; j < subpath.size(); j++)
path.lineTo(ll2xy(subpath.at(j)));
}
return path;
}
QVector<RasterTile::PathInstruction> RasterTile::pathInstructions()
{
QCache<Key, QVector<const Style::PathRender *> > cache(1024);
QVector<PathInstruction> instructions;
const Style &s = style();
for (int i = 0 ; i < _paths.size(); i++) {
MapData::Path &path = _paths[i];
Key key(_zoom, path.closed, path.tags);
QVector<const Style::PathRender*> *cached = cache.object(key);
if (!cached) {
QVector<const Style::PathRender*> *ri
= new QVector<const Style::PathRender*>();
s.match(_zoom, path.closed, path.tags, ri);
for (int j = 0; j < ri->size(); j++)
instructions.append(PathInstruction(ri->at(j), &path));
cache.insert(key, ri);
} else {
for (int j = 0; j < cached->size(); j++)
instructions.append(PathInstruction(cached->at(j), &path));
}
}
std::sort(instructions.begin(), instructions.end());
return instructions;
}
void RasterTile::drawPaths(QPainter *painter)
{
QVector<PathInstruction> instructions(pathInstructions());
for (int i = 0; i < instructions.size(); i++) {
PathInstruction &is = instructions[i];
const Style::PathRender *ri = is.render();
painter->setPen(ri->pen(_zoom));
painter->setBrush(ri->brush());
if (!is.path()->path.elementCount())
is.path()->path = painterPath(is.path()->poly);
painter->drawPath(is.path()->path);
}
}
void RasterTile::render()
{
std::sort(_points.begin(), _points.end());
QList<TextItem*> textItems;
QRect tileRect(_xy, _img.size());
_img.fill(Qt::transparent);
QPainter painter(&_img);
painter.setRenderHint(QPainter::Antialiasing);
painter.translate(-_xy.x(), -_xy.y());
drawPaths(&painter);
processPoints(textItems);
processStreetNames(tileRect, textItems);
drawTextItems(&painter, textItems);
//painter.setPen(Qt::red);
//painter.setBrush(Qt::NoBrush);
//painter.drawRect(QRect(_xy, _img.size()));
qDeleteAll(textItems);
}

View File

@ -0,0 +1,98 @@
#ifndef MAPSFORGE_RASTERTILE_H
#define MAPSFORGE_RASTERTILE_H
#include <QImage>
#include "map/projection.h"
#include "map/transform.h"
#include "style.h"
#include "mapdata.h"
#include "cmp.h"
class MapsforgeMap;
class TextItem;
namespace Mapsforge {
class RasterTile
{
public:
RasterTile(const Projection &proj, const Transform &transform, int zoom,
const QRect &rect, const QString &key, const QList<MapData::Path> &paths,
const QList<MapData::Point> &points)
: _proj(proj), _transform(transform), _zoom(zoom), _xy(rect.topLeft()),
_key(key), _img(rect.size(), QImage::Format_ARGB32_Premultiplied),
_paths(paths), _points(points) {}
const QString &key() const {return _key;}
const QPoint &xy() const {return _xy;}
const QImage &img() const {return _img;}
void render();
private:
class PathInstruction
{
public:
PathInstruction(const Style::PathRender *render, MapData::Path *path)
: _render(render), _path(path) {}
bool operator<(const PathInstruction &other) const
{
if (_path->layer == other._path->layer)
return _render->zOrder() < other._render->zOrder();
else
return (_path->layer < other._path->layer);
}
const Style::PathRender *render() const {return _render;}
MapData::Path *path() {return _path;}
private:
const Style::PathRender *_render;
MapData::Path *_path;
};
struct Key {
Key(int zoom, bool closed, const QVector<MapData::Tag> &tags)
: zoom(zoom), closed(closed), tags(tags) {}
bool operator==(const Key &other) const
{
return zoom == other.zoom && closed == other.closed
&& tags == other.tags;
}
int zoom;
bool closed;
const QVector<MapData::Tag> &tags;
};
friend HASH_T qHash(const RasterTile::Key &key);
friend HASH_T qHash(const RasterTile::PathInstruction &pi);
QVector<PathInstruction> pathInstructions();
QPointF ll2xy(const Coordinates &c) const
{return _transform.proj2img(_proj.ll2xy(c));}
void processPoints(QList<TextItem*> &textItems);
void processStreetNames(const QRect &tileRect, QList<TextItem*> &textItems);
QPainterPath painterPath(const Polygon &polygon) const;
void drawTextItems(QPainter *painter, const QList<TextItem*> &textItems);
void drawPaths(QPainter *painter);
Projection _proj;
Transform _transform;
int _zoom;
QPoint _xy;
QString _key;
QImage _img;
QList<MapData::Path> _paths;
QList<MapData::Point> _points;
};
inline HASH_T qHash(const RasterTile::Key &key)
{
return ::qHash(key.zoom) ^ ::qHash(key.tags);
}
}
#endif // MAPSFORGE_RASTERTILE_H

357
src/map/mapsforge/style.cpp Normal file
View File

@ -0,0 +1,357 @@
#include <QFile>
#include <QXmlStreamReader>
#include <QUrl>
#include <QFileInfo>
#include <QImageReader>
#include "style.h"
using namespace Mapsforge;
static QString resourcePath(const QString &src, const QString &dir)
{
QUrl url(src);
if (url.scheme().isEmpty())
return src;
else
return dir + "/" + url.toLocalFile();
}
static QImage image(const QString &path, int width, int height)
{
QImageReader ir(path, "svg");
if (ir.canRead()) {
if (!height && !width) {
height = 20;
width = 20;
} else if (!width) {
width = ir.size().height() / (ir.size().height() / (double)height);
} else if (!height)
height = ir.size().width() / (ir.size().width() / (double)width);
ir.setScaledSize(QSize(width, height));
return ir.read();
} else
return QImage(path);
}
bool Style::Rule::match(int zoom, bool closed,
const QVector<MapData::Tag> &tags) const
{
Closed cl = closed ? YesClosed : NoClosed;
if (_type && WayType != _type)
return false;
if (!_zooms.contains(zoom))
return false;
if (_closed && cl != _closed)
return false;
for (int i = 0; i < _filters.size(); i++)
if (!_filters.at(i).match(tags))
return false;
return true;
}
bool Style::Rule::match(int zoom, const QVector<MapData::Tag> &tags) const
{
if (_type && NodeType != _type)
return false;
if (!_zooms.contains(zoom))
return false;
for (int i = 0; i < _filters.size(); i++)
if (!_filters.at(i).match(tags))
return false;
return true;
}
void Style::area(QXmlStreamReader &reader, const QString &dir, const Rule &rule)
{
PathRender ri(rule, _paths.size());
const QXmlStreamAttributes &attr = reader.attributes();
QString file;
int height = 0, width = 0;
if (attr.hasAttribute("fill"))
ri._fillColor = QColor(attr.value("fill").toString());
if (attr.hasAttribute("stroke"))
ri._strokeColor = QColor(attr.value("stroke").toString());
if (attr.hasAttribute("stroke-width"))
ri._strokeWidth = attr.value("stroke-width").toFloat();
if (attr.hasAttribute("src"))
file = resourcePath(attr.value("src").toString(), dir);
if (attr.hasAttribute("symbol-height"))
height = attr.value("symbol-height").toInt();
if (attr.hasAttribute("symbol-width"))
width = attr.value("symbol-width").toInt();
if (!file.isNull())
ri._fillImage = image(file, width, height);
_paths.append(ri);
reader.skipCurrentElement();
}
void Style::line(QXmlStreamReader &reader, const Rule &rule)
{
PathRender ri(rule, _paths.size());
const QXmlStreamAttributes &attr = reader.attributes();
if (attr.hasAttribute("stroke"))
ri._strokeColor = QColor(attr.value("stroke").toString());
if (attr.hasAttribute("stroke-width"))
ri._strokeWidth = attr.value("stroke-width").toFloat();
if (attr.hasAttribute("stroke-dasharray")) {
QStringList l(attr.value("stroke-dasharray").toString().split(','));
ri._strokeDasharray.resize(l.size());
for (int i = 0; i < l.size(); i++)
ri._strokeDasharray[i] = l.at(i).toDouble();
}
if (attr.hasAttribute("stroke-linecap")) {
QString cap(attr.value("stroke-linecap").toString());
if (cap == "butt")
ri._strokeCap = Qt::FlatCap;
else if (cap == "round")
ri._strokeCap = Qt::RoundCap;
else if (cap == "square")
ri._strokeCap = Qt::SquareCap;
}
if (attr.hasAttribute("stroke-linejoin")) {
QString join(attr.value("stroke-linejoin").toString());
if (join == "miter")
ri._strokeJoin = Qt::MiterJoin;
else if (join == "round")
ri._strokeJoin = Qt::RoundJoin;
else if (join == "bevel")
ri._strokeJoin = Qt::BevelJoin;
}
_paths.append(ri);
reader.skipCurrentElement();
}
void Style::text(QXmlStreamReader &reader, const Rule &rule,
QList<TextRender> &list)
{
TextRender ri(rule);
const QXmlStreamAttributes &attr = reader.attributes();
int fontSize = 9;
bool bold = false, italic = false;
if (attr.hasAttribute("fill"))
ri._fillColor = QColor(attr.value("fill").toString());
if (attr.hasAttribute("stroke"))
ri._strokeColor = QColor(attr.value("stroke").toString());
if (attr.hasAttribute("font-size"))
fontSize = attr.value("font-size").toInt();
if (attr.hasAttribute("font-style")) {
QString style(attr.value("font-style").toString());
if (style == "bold")
bold = true;
else if (style == "italic")
italic = true;
else if (style == "bold_italic") {
bold = true;
italic = true;
}
}
ri._font.setPixelSize(fontSize);
ri._font.setBold(bold);
ri._font.setItalic(italic);
list.append(ri);
reader.skipCurrentElement();
}
void Style::symbol(QXmlStreamReader &reader, const QString &dir,
const Rule &rule)
{
Symbol ri(rule);
const QXmlStreamAttributes &attr = reader.attributes();
QString file;
int height = 0, width = 0;
if (attr.hasAttribute("src"))
file = resourcePath(attr.value("src").toString(), dir);
if (attr.hasAttribute("symbol-height"))
height = attr.value("symbol-height").toInt();
if (attr.hasAttribute("symbol-width"))
width = attr.value("symbol-width").toInt();
if (!file.isNull())
ri._img = image(file, width, height);
_symbols.append(ri);
reader.skipCurrentElement();
}
void Style::rule(QXmlStreamReader &reader, const QString &dir,
const QSet<QString> &cats, const Rule &parent)
{
Rule r(parent);
const QXmlStreamAttributes &attr = reader.attributes();
if (attr.hasAttribute("cat")
&& !cats.contains(attr.value("cat").toString())) {
reader.skipCurrentElement();
return;
}
if (attr.value("e") == "way")
r.setType(Rule::WayType);
else if (attr.value("e") == "node")
r.setType(Rule::NodeType);
if (attr.hasAttribute("zoom-min"))
r.setMinZoom(attr.value("zoom-min").toInt());
if (attr.hasAttribute("zoom-max"))
r.setMaxZoom(attr.value("zoom-max").toInt());
if (attr.hasAttribute("closed")) {
if (attr.value("closed").toString() == "yes")
r.setClosed(Rule::YesClosed);
else if (attr.value("closed").toString() == "no")
r.setClosed(Rule::NoClosed);
}
QList<QByteArray> keys(attr.value("k").toLatin1().split('|'));
QList<QByteArray> vals(attr.value("v").toLatin1().split('|'));
r.addFilter(Rule::Filter(keys, vals));
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("rule"))
rule(reader, dir, cats, r);
else if (reader.name() == QLatin1String("area"))
area(reader, dir, r);
else if (reader.name() == QLatin1String("line"))
line(reader, r);
else if (reader.name() == QLatin1String("pathText"))
text(reader, r, _pathLabels);
else if (reader.name() == QLatin1String("caption"))
text(reader, r, _pointLabels);
else if (reader.name() == QLatin1String("symbol"))
symbol(reader, dir, r);
else
reader.skipCurrentElement();
}
}
void Style::cat(QXmlStreamReader &reader, QSet<QString> &cats)
{
const QXmlStreamAttributes &attr = reader.attributes();
cats.insert(attr.value("id").toString());
reader.skipCurrentElement();
}
void Style::layer(QXmlStreamReader &reader, QSet<QString> &cats)
{
const QXmlStreamAttributes &attr = reader.attributes();
bool enabled = (attr.value("enabled").toString() == "true");
while (reader.readNextStartElement()) {
if (enabled && reader.name() == QLatin1String("cat"))
cat(reader, cats);
else
reader.skipCurrentElement();
}
}
void Style::stylemenu(QXmlStreamReader &reader, QSet<QString> &cats)
{
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("layer"))
layer(reader, cats);
else
reader.skipCurrentElement();
}
}
void Style::rendertheme(QXmlStreamReader &reader, const QString &dir)
{
Rule r;
QSet<QString> cats;
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("rule"))
rule(reader, dir, cats, r);
else if (reader.name() == QLatin1String("stylemenu"))
stylemenu(reader, cats);
else
reader.skipCurrentElement();
}
}
bool Style::loadXml(const QString &path)
{
QFile file(path);
if (!file.open(QFile::ReadOnly))
return false;
QXmlStreamReader reader(&file);
QFileInfo fi(path);
if (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("rendertheme"))
rendertheme(reader, fi.absolutePath());
else
reader.raiseError("Not a Mapsforge style file");
}
if (reader.error())
qWarning("%s:%lld %s", qPrintable(path), reader.lineNumber(),
qPrintable(reader.errorString()));
return !reader.error();
}
Style::Style(const QString &path)
{
if (!QFileInfo::exists(path) || !loadXml(path))
loadXml(":/mapsforge/default.xml");
}
void Style::match(int zoom, bool closed, const QVector<MapData::Tag> &tags,
QVector<const Style::PathRender*> *ri) const
{
for (int i = 0; i < _paths.size(); i++)
if (_paths.at(i).rule().match(zoom, closed, tags))
ri->append(&_paths.at(i));
}
QPen Style::PathRender::pen(int zoom) const
{
qreal width = (zoom >= 12)
? pow(1.5, zoom - 12) * _strokeWidth : _strokeWidth;
if (_strokeColor.isValid()) {
QPen p(QBrush(_strokeColor), width, Qt::SolidLine, _strokeCap,
_strokeJoin);
if (!_strokeDasharray.isEmpty()) {
QVector<qreal>pattern(_strokeDasharray);
for (int i = 0; i < _strokeDasharray.size(); i++)
pattern[i] /= width;
p.setDashPattern(pattern);
}
return p;
} else
return Qt::NoPen;
}
QBrush Style::PathRender::brush() const
{
if (!_fillImage.isNull())
return QBrush(_fillImage);
else if (_fillColor.isValid())
return QBrush(_fillColor);
else
return Qt::NoBrush;
}

230
src/map/mapsforge/style.h Normal file
View File

@ -0,0 +1,230 @@
#ifndef MAPSFORGE_STYLE_H
#define MAPSFORGE_STYLE_H
#include <QString>
#include <QList>
#include <QPen>
#include <QFont>
#include "mapdata.h"
class QXmlStreamReader;
namespace Mapsforge {
class Style
{
public:
class Rule {
public:
Rule() : _type(AnyType), _closed(AnyClosed), _zooms(0, 127) {}
bool match(int zoom, bool closed,
const QVector<MapData::Tag> &tags) const;
bool match(int zoom, const QVector<MapData::Tag> &tags) const;
private:
enum Type {
AnyType = 0,
NodeType = 1,
WayType = 2,
InvalidType = 3
};
enum Closed {
AnyClosed = 0,
YesClosed = 1,
NoClosed = 2,
InvalidClosed = 3
};
class Filter {
public:
Filter(const QList<QByteArray> &keys, const QList<QByteArray> &vals)
: _neg(false)
{
_keys = list(keys);
QList<QByteArray> vc(vals);
if (vc.removeAll("~"))
_neg = true;
_vals = list(vals);
}
bool match(const QVector<MapData::Tag> &tags) const
{
if (_neg) {
if (!keyMatches(tags))
return true;
return valueMatches(tags);
} else
return (keyMatches(tags) && valueMatches(tags));
}
bool isTautology() const
{
return (!_neg && _keys.contains(QByteArray())
&& _vals.contains(QByteArray()));
}
private:
static QList<QByteArray> list(const QList<QByteArray> &in)
{
QList<QByteArray> out;
for (int i = 0; i < in.size(); i++) {
if (in.at(i) == "*")
out.append(QByteArray());
else
out.append(in.at(i));
}
return out;
}
bool keyMatches(const QVector<MapData::Tag> &tags) const
{
for (int i = 0; i < _keys.size(); i++)
for (int j = 0; j < tags.size(); j++)
if (cmp(_keys.at(i), tags.at(j).key))
return true;
return false;
}
bool valueMatches(const QVector<MapData::Tag> &tags) const
{
for (int i = 0; i < _vals.size(); i++)
for (int j = 0; j < tags.size(); j++)
if (cmp(_vals.at(i), tags.at(j).value))
return true;
return false;
}
QList<QByteArray> _keys;
QList<QByteArray> _vals;
bool _neg;
};
void setType(Type type)
{
_type = static_cast<Type>(static_cast<int>(type)
| static_cast<int>(_type));
}
void setMinZoom(int zoom) {_zooms.setMin(qMax(zoom, _zooms.min()));}
void setMaxZoom(int zoom) {_zooms.setMax(qMin(zoom, _zooms.max()));}
void setClosed(Closed closed)
{
_closed = static_cast<Closed>(static_cast<int>(closed)
| static_cast<int>(_closed));
}
void addFilter(const Filter &filter)
{
if (!filter.isTautology())
_filters.append(filter);
}
bool match(int zoom, Type type, Closed closed,
const QVector<MapData::Tag> &tags) const;
friend class Style;
Type _type;
Closed _closed;
Range _zooms;
QVector<Filter> _filters;
};
class Render
{
public:
Render(const Rule &rule) : _rule(rule) {}
const Rule &rule() const {return _rule;}
private:
Rule _rule;
};
class PathRender : public Render
{
public:
PathRender(const Rule &rule, int zOrder) : Render(rule),
_zOrder(zOrder), _strokeWidth(0), _strokeCap(Qt::RoundCap),
_strokeJoin(Qt::RoundJoin) {}
int zOrder() const {return _zOrder;}
QPen pen(int zoom) const;
QBrush brush() const;
private:
friend class Style;
int _zOrder;
QColor _fillColor, _strokeColor;
qreal _strokeWidth;
QVector<qreal> _strokeDasharray;
Qt::PenCapStyle _strokeCap;
Qt::PenJoinStyle _strokeJoin;
QImage _fillImage;
};
class TextRender : public Render
{
public:
TextRender(const Rule &rule)
: Render(rule), _fillColor(Qt::black), _strokeColor(Qt::white) {}
const QFont &font() const {return _font;}
const QColor &fillColor() const {return _fillColor;}
const QColor &strokeColor() const {return _strokeColor;}
private:
friend class Style;
QColor _fillColor, _strokeColor;
QFont _font;
};
class Symbol : public Render
{
public:
Symbol(const Rule &rule) : Render(rule) {}
const QImage &img() const {return _img;}
private:
friend class Style;
QImage _img;
};
Style(const QString &path);
void match(int zoom, bool closed, const QVector<MapData::Tag> &tags,
QVector<const PathRender *> *ri) const;
const QList<TextRender> &pathLabels() const {return _pathLabels;}
const QList<TextRender> &pointLabels() const {return _pointLabels;}
const QList<Symbol> &symbols() const {return _symbols;}
private:
QList<PathRender> _paths;
QList<TextRender> _pathLabels, _pointLabels;
QList<Symbol> _symbols;
bool loadXml(const QString &path);
void rendertheme(QXmlStreamReader &reader, const QString &dir);
void layer(QXmlStreamReader &reader, QSet<QString> &cats);
void stylemenu(QXmlStreamReader &reader, QSet<QString> &cats);
void cat(QXmlStreamReader &reader, QSet<QString> &cats);
void rule(QXmlStreamReader &reader, const QString &dir,
const QSet<QString> &cats, const Rule &parent);
void area(QXmlStreamReader &reader, const QString &dir, const Rule &rule);
void line(QXmlStreamReader &reader, const Rule &rule);
void text(QXmlStreamReader &reader, const Rule &rule,
QList<TextRender> &list);
void symbol(QXmlStreamReader &reader, const QString &dir, const Rule &rule);
};
}
#endif // MAPSFORGE_STYLE_H

View File

@ -0,0 +1,51 @@
#include <cstring>
#include "subfile.h"
using namespace Mapsforge;
#define mod2n(x, m) ((x) & ((m) - 1));
bool SubFile::seek(quint64 pos)
{
Q_ASSERT(pos < _size);
int blockNum = pos >> BLOCK_BITS;
if (_blockNum != blockNum) {
quint64 seek = ((quint64)blockNum << BLOCK_BITS) + _offset;
if (seek >= _offset + _size || !_file.seek(seek))
return false;
if (_file.read((char*)_data, sizeof(_data)) < 0)
return false;
_blockNum = blockNum;
}
_blockPos = mod2n(pos, 1U<<BLOCK_BITS);
_pos = pos;
return true;
}
bool SubFile::read(char *buff, quint32 size)
{
while (size) {
quint32 remaining = sizeof(_data) - _blockPos;
if (size < remaining) {
memcpy(buff, _data + _blockPos, size);
_blockPos += size;
_pos += size;
return true;
} else {
memcpy(buff, _data + _blockPos, remaining);
buff += remaining;
size -= remaining;
_blockPos = 0;
_pos += remaining;
if (!seek(_pos))
return false;
}
}
return true;
}

130
src/map/mapsforge/subfile.h Normal file
View File

@ -0,0 +1,130 @@
#ifndef MAPSFORGE_SUBFILE_H
#define MAPSFORGE_SUBFILE_H
#include <QFile>
#define BLOCK_BITS 12 /* 4096 bytes */
namespace Mapsforge {
class SubFile
{
public:
SubFile(QFile &file, quint64 offset, quint64 size)
: _file(file), _offset(offset), _size(size), _pos(-1),
_blockNum(-1), _blockPos(-1) {}
quint64 pos() const {return _pos;}
bool seek(quint64 pos);
bool read(char *buff, quint32 size);
bool readByte(quint8 &val)
{
val = _data[_blockPos++];
_pos++;
return (_blockPos >= (int)sizeof(_data)) ? seek(_pos) : true;
}
template<typename T>
bool readUInt16(T &val)
{
quint8 b0, b1;
if (!(readByte(b0) && readByte(b1)))
return false;
val = b1 | ((quint16)b0) << 8;
return true;
}
bool readUInt32(quint32 &val)
{
quint8 b0, b1, b2, b3;
if (!(readByte(b0) && readByte(b1) && readByte(b2) && readByte(b3)))
return false;
val = b3 | ((quint32)b2) << 8 | ((quint32)b1) << 16 | ((quint32)b0) << 24;
return true;
}
bool readUInt64(quint64 &val)
{
quint8 b0, b1, b2, b3, b4, b5, b6, b7;
if (!(readByte(b0) && readByte(b1) && readByte(b2) && readByte(b3)
&& readByte(b4) && readByte(b5) && readByte(b6) && readByte(b7)))
return false;
val = b7 | ((quint64)b6) << 8 | ((quint64)b5) << 16
| ((quint64)b4) << 24 | ((quint64)b3) << 32 | ((quint64)b2) << 40
| ((quint64)b1) << 48 | ((quint64)b0) << 56;
return true;
}
bool readInt32(qint32 &val)
{
return readUInt32(reinterpret_cast<quint32&>(val));
}
bool readVUInt32(quint32 &val)
{
int shift = 0;
quint8 b;
val = 0;
do {
if (!readByte(b))
return false;
val |= (quint32)(b & 0x7F) << shift;
shift += 7;
} while (b & 0x80);
return true;
}
bool readVInt32(qint32 &val)
{
int shift = 0;
quint8 b;
val = 0;
while (true) {
if (!readByte(b))
return false;
if (b & 0x80) {
val |= (qint32)(b & 0x7F) << shift;
shift += 7;
} else {
val |= (qint32)(b & 0x3F) << shift;
if (b & 0x40)
val = -val;
break;
}
}
return true;
}
bool readString(QByteArray &str)
{
quint32 len;
if (!readVUInt32(len))
return false;
str.resize(len);
if (!read(str.data(), len))
return false;
return true;
}
private:
QFile &_file;
quint8 _data[1U<<BLOCK_BITS];
quint64 _offset;
quint64 _size;
qint64 _pos;
int _blockNum;
int _blockPos;
};
}
#endif // MAPSFORGE_SUBFILE_H

199
src/map/mapsforgemap.cpp Normal file
View File

@ -0,0 +1,199 @@
#include <QPainter>
#include <QTemporaryDir>
#include "common/wgs84.h"
#include "pcs.h"
#include "rectd.h"
#include "mapsforgemap.h"
using namespace Mapsforge;
#define TEXT_EXTENT 160
static int log2i(unsigned val)
{
int ret = 0;
while (val >>= 1)
ret++;
return ret;
}
MapsforgeMap::MapsforgeMap(const QString &fileName, QObject *parent)
: Map(fileName, parent), _data(fileName), _zoom(0), _projection(PCS::pcs(3857))
{
_zoom = _data.zooms().min();
updateTransform();
}
void MapsforgeMap::load()
{
_data.load();
}
void MapsforgeMap::unload()
{
_data.clear();
}
int MapsforgeMap::zoomFit(const QSize &size, const RectC &rect)
{
if (rect.isValid()) {
RectD pr(rect, _projection, 10);
_zoom = _data.zooms().min();
for (int i = _data.zooms().min() + 1; i <= _data.zooms().max(); i++) {
Transform t(transform(i));
QRectF r(t.proj2img(pr.topLeft()), t.proj2img(pr.bottomRight()));
if (size.width() < r.width() || size.height() < r.height())
break;
_zoom = i;
}
} else
_zoom = _data.zooms().max();
updateTransform();
return _zoom;
}
int MapsforgeMap::zoomIn()
{
_zoom = qMin(_zoom + 1, _data.zooms().max());
updateTransform();
return _zoom;
}
int MapsforgeMap::zoomOut()
{
_zoom = qMax(_zoom - 1, _data.zooms().min());
updateTransform();
return _zoom;
}
void MapsforgeMap::setZoom(int zoom)
{
_zoom = zoom;
updateTransform();
}
Transform MapsforgeMap::transform(int zoom) const
{
int z = zoom + log2i(_data.tileSize());
double scale = _projection.isGeographic()
? 360.0 / (1<<z) : (2.0 * M_PI * WGS84_RADIUS) / (1<<z);
PointD topLeft(_projection.ll2xy(_data.bounds().topLeft()));
return Transform(ReferencePoint(PointD(0, 0), topLeft),
PointD(scale, scale));
}
void MapsforgeMap::updateTransform()
{
_transform = transform(_zoom);
RectD prect(_data.bounds(), _projection);
_bounds = QRectF(_transform.proj2img(prect.topLeft()),
_transform.proj2img(prect.bottomRight()));
// Adjust the bounds of world maps to avoid problems with wrapping
if (_data.bounds().left() <= -180.0 || _data.bounds().right() >= 180.0)
_bounds.adjust(0.5, 0, -0.5, 0);
}
bool MapsforgeMap::isRunning(const QString &key) const
{
return _running.contains(key);
}
void MapsforgeMap::addRunning(const QList<RasterTile> &tiles)
{
for (int i = 0; i < tiles.size(); i++)
_running.insert(tiles.at(i).key());
}
void MapsforgeMap::removeRunning(const QList<RasterTile> &tiles)
{
for (int i = 0; i < tiles.size(); i++)
_running.remove(tiles.at(i).key());
}
void MapsforgeMap::jobFinished(const QList<RasterTile> &tiles)
{
removeRunning(tiles);
emit tilesLoaded();
}
void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags)
{
Q_UNUSED(flags);
QPointF tl(floor(rect.left() / _data.tileSize()) * _data.tileSize(),
floor(rect.top() / _data.tileSize()) * _data.tileSize());
QSizeF s(rect.right() - tl.x(), rect.bottom() - tl.y());
int width = ceil(s.width() / _data.tileSize());
int height = ceil(s.height() / _data.tileSize());
QList<RasterTile> tiles;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
QPixmap pm;
QPoint ttl(tl.x() + i * _data.tileSize(), tl.y() + j
* _data.tileSize());
QString key = path() + "-" + QString::number(_zoom) + "_"
+ QString::number(ttl.x()) + "_" + QString::number(ttl.y());
if (isRunning(key))
continue;
if (QPixmapCache::find(key, &pm))
painter->drawPixmap(ttl, pm);
else {
QList<MapData::Path> paths;
QList<MapData::Point> points;
/* Add a "sub-pixel" margin to assure the tile areas do not
overlap on the border lines. This prevents areas overlap
artifacts at least when using the EPSG:3857 projection. */
QRectF pathRect(QPointF(ttl.x() + 0.5, ttl.y() + 0.5),
QPointF(ttl.x() + _data.tileSize() - 0.5, ttl.y()
+ _data.tileSize() - 0.5));
pathRect &= _bounds;
RectD pathRectD(_transform.img2proj(pathRect.topLeft()),
_transform.img2proj(pathRect.bottomRight()));
_data.paths(pathRectD.toRectC(_projection, 20), _zoom, &paths);
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT, ttl.y()
- TEXT_EXTENT), QPointF(ttl.x() + _data.tileSize()
+ TEXT_EXTENT, ttl.y() + _data.tileSize() + TEXT_EXTENT));
pointRect &= _bounds;
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
_transform.img2proj(pointRect.bottomRight()));
_data.points(pointRectD.toRectC(_projection, 20), _zoom, &points);
tiles.append(RasterTile(_projection, _transform, _zoom,
QRect(ttl, QSize(_data.tileSize(), _data.tileSize())), key,
paths, points));
}
}
}
if (!tiles.isEmpty()) {
MapsforgeMapJob *job = new MapsforgeMapJob(tiles);
connect(job, &MapsforgeMapJob::finished, this,
&MapsforgeMap::jobFinished);
addRunning(tiles);
job->run();
}
}
void MapsforgeMap::setOutputProjection(const Projection &projection)
{
if (projection == _projection)
return;
_projection = projection;
updateTransform();
QPixmapCache::clear();
}

106
src/map/mapsforgemap.h Normal file
View File

@ -0,0 +1,106 @@
#ifndef MAPSFORGEMAP_H
#define MAPSFORGEMAP_H
#include <QtConcurrent>
#include <QPixmapCache>
#include "mapsforge/mapdata.h"
#include "mapsforge/rastertile.h"
#include "projection.h"
#include "transform.h"
#include "map.h"
class MapsforgeMapJob : public QObject
{
Q_OBJECT
public:
MapsforgeMapJob(const QList<Mapsforge::RasterTile> &tiles) : _tiles(tiles)
{
connect(&_watcher, &QFutureWatcher<void>::finished, this,
&MapsforgeMapJob::handleFinished);
}
void run()
{
_future = QtConcurrent::map(_tiles, &Mapsforge::RasterTile::render);
_watcher.setFuture(_future);
}
signals:
void finished(const QList<Mapsforge::RasterTile> &);
private slots:
void handleFinished()
{
for (int i = 0; i < _tiles.size(); i++) {
Mapsforge::RasterTile &mt = _tiles[i];
QPixmap pm(QPixmap::fromImage(mt.img()));
if (pm.isNull())
continue;
QPixmapCache::insert(mt.key(), pm);
}
emit finished(_tiles);
deleteLater();
}
private:
QFutureWatcher<void> _watcher;
QFuture<void> _future;
QList<Mapsforge::RasterTile> _tiles;
};
class MapsforgeMap : public Map
{
Q_OBJECT
public:
MapsforgeMap(const QString &fileName, QObject *parent = 0);
QRectF bounds() {return _bounds;}
RectC llBounds() {return _data.bounds();}
int zoom() const {return _zoom;}
void setZoom(int zoom);
int zoomFit(const QSize &size, const RectC &rect);
int zoomIn();
int zoomOut();
void load();
void unload();
void setOutputProjection(const Projection &projection);
QPointF ll2xy(const Coordinates &c)
{return _transform.proj2img(_projection.ll2xy(c));}
Coordinates xy2ll(const QPointF &p)
{return _projection.xy2ll(_transform.img2proj(p));}
void draw(QPainter *painter, const QRectF &rect, Flags flags);
bool isValid() const {return _data.isValid();}
QString errorString() const {return _data.errorString();}
private slots:
void jobFinished(const QList<Mapsforge::RasterTile> &tiles);
private:
Transform transform(int zoom) const;
void updateTransform();
bool isRunning(const QString &key) const;
void addRunning(const QList<Mapsforge::RasterTile> &tiles);
void removeRunning(const QList<Mapsforge::RasterTile> &tiles);
Mapsforge::MapData _data;
int _zoom;
Projection _projection;
Transform _transform;
QRectF _bounds;
QSet<QString> _running;
};
#endif // MAPSFORGEMAP_H

View File

@ -87,22 +87,19 @@ MBTilesMap::MBTilesMap(const QString &fileName, QObject *parent)
_zoom = _zooms.max();
{
int z = _zooms.min();
QString sql = QString("SELECT min(tile_column), min(tile_row), "
"max(tile_column), max(tile_row) FROM tiles WHERE zoom_level = %1")
.arg(_zooms.min());
QSqlQuery query(sql, _db);
query.first();
double minX = OSM::index2mercator(qMin((1<<_zooms.min()) - 1,
qMax(0, query.value(0).toInt())), _zooms.min());
double minY = OSM::index2mercator(qMin((1<<_zooms.min()) - 1,
qMax(0, query.value(1).toInt())), _zooms.min());
double maxX = OSM::index2mercator(qMin((1<<_zooms.min()) - 1,
qMax(0, query.value(2).toInt())) + 1, _zooms.min());
double maxY = OSM::index2mercator(qMin((1<<_zooms.min()) - 1,
qMax(0, query.value(3).toInt())) + 1, _zooms.min());
Coordinates tl(OSM::m2ll(QPointF(minX, maxY)));
Coordinates br(OSM::m2ll(QPointF(maxX, minY)));
int minX = qMin((1<<z) - 1, qMax(0, query.value(0).toInt()));
int minY = qMin((1<<z) - 1, qMax(0, query.value(1).toInt()));
int maxX = qMin((1<<z) - 1, qMax(0, query.value(2).toInt())) + 1;
int maxY = qMin((1<<z) - 1, qMax(0, query.value(3).toInt())) + 1;
Coordinates tl(OSM::tile2ll(QPoint(minX, maxY), z));
Coordinates br(OSM::tile2ll(QPoint(maxX, minY), z));
// Workaround of broken zoom levels 0 and 1 due to numerical instability
tl.rlat() = qMin(tl.lat(), OSM::BOUNDS.top());
br.rlat() = qMax(br.lat(), OSM::BOUNDS.bottom());

View File

@ -5,6 +5,11 @@
#define EPSILON 1e-6
static double index2mercator(int index, int zoom)
{
return rad2deg(-M_PI + 2 * M_PI * ((double)index / (1<<zoom)));
}
QPointF OSM::ll2m(const Coordinates &c)
{
return QPointF(c.lon(), rad2deg(log(tan(M_PI_4 + deg2rad(c.lat())/2.0))));
@ -21,9 +26,9 @@ QPoint OSM::mercator2tile(const QPointF &m, int zoom)
qFloor((1.0 - (m.y() / 180.0)) / 2.0 * (1<<zoom)));
}
double OSM::index2mercator(int index, int zoom)
QPointF OSM::tile2mercator(const QPoint &p, int zoom)
{
return rad2deg(-M_PI + 2 * M_PI * ((double)index / (1<<zoom)));
return QPointF(index2mercator(p.x(), zoom), index2mercator(p.y(), zoom));
}
qreal OSM::zoom2scale(int zoom, int tileSize)

View File

@ -15,10 +15,15 @@ namespace OSM
QPointF ll2m(const Coordinates &c);
Coordinates m2ll(const QPointF &p);
QPoint mercator2tile(const QPointF &m, int zoom);
double index2mercator(int index, int zoom);
QPointF tile2mercator(const QPoint &p, int zoom);
qreal zoom2scale(int zoom, int tileSize);
int scale2zoom(qreal scale, int tileSize);
qreal resolution(const QPointF &p, int zoom, int tileSize);
inline Coordinates tile2ll(const QPoint &p, int zoom)
{return m2ll(tile2mercator(p, zoom));}
inline QPoint ll2tile(const Coordinates &c, int zoom)
{return mercator2tile(ll2m(c), zoom);}
}
#endif // OSM_H

View File

@ -73,21 +73,20 @@ SqliteMap::SqliteMap(const QString &fileName, QObject *parent)
_zoom = _zooms.max();
{
int z = _zooms.min();
QString sql = QString("SELECT min(x), min(y), max(x), max(y) FROM tiles"
" WHERE z = %1").arg(17 - _zooms.min());
" WHERE z = %1").arg(17 - z);
QSqlQuery query(sql, _db);
query.first();
double minX = OSM::index2mercator(qMin((1<<_zooms.min()) - 1,
qMax(0, query.value(0).toInt())), _zooms.min());
double minY = OSM::index2mercator(qMin((1<<_zooms.min()) - 1,
qMax(0, query.value(1).toInt())), _zooms.min());
double maxX = OSM::index2mercator(qMin((1<<_zooms.min()) - 1,
qMax(0, query.value(2).toInt())) + 1, _zooms.min());
double maxY = OSM::index2mercator(qMin((1<<_zooms.min()) - 1,
qMax(0, query.value(3).toInt())) + 1, _zooms.min());
Coordinates tl(OSM::m2ll(QPointF(minX, -minY)));
Coordinates br(OSM::m2ll(QPointF(maxX, -maxY)));
int minX = qMin((1<<z) - 1, qMax(0, query.value(0).toInt()));
int minY = qMin((1<<z) - 1, qMax(0, query.value(1).toInt()));
int maxX = qMin((1<<z) - 1, qMax(0, query.value(2).toInt())) + 1;
int maxY = qMin((1<<z) - 1, qMax(0, query.value(3).toInt())) + 1;
Coordinates tl(OSM::tile2ll(QPoint(minX, minY), z));
tl.rlat() = -tl.lat();
Coordinates br(OSM::tile2ll(QPoint(maxX, maxY), z));
br.rlat() = -br.lat();
// Workaround of broken zoom levels 0 and 1 due to numerical instability
tl.rlat() = qMin(tl.lat(), OSM::BOUNDS.top());
br.rlat() = qMax(br.lat(), OSM::BOUNDS.bottom());

View File

@ -147,8 +147,58 @@ static QList<QLineF> lineString(const QPolygonF &path,
return lines;
}
static QList<QLineF> lineString(const QPainterPath &path,
const QRectF &boundingRect)
{
QList<QLineF> lines;
int start = -1, end = -1;
static QPainterPath textPath(const QPolygonF &path, qreal textWidth,
for (int i = 0; i < path.elementCount(); i++) {
if (boundingRect.contains(path.elementAt(i))) {
start = i;
break;
}
}
for (int i = path.elementCount() - 1; i >= 0; i--) {
if (boundingRect.contains(path.elementAt(i))) {
end = i;
break;
}
}
if (start < 0) {
QPointF p1, p2;
for (int i = 1; i < path.elementCount(); i++) {
QLineF l(path.elementAt(i-1), path.elementAt(i));
if (intersection(l, boundingRect, &p1, &p2)) {
lines.append(QLineF(p1, p2));
break;
}
}
} else {
QPointF p;
if (start > 0) {
QLineF l(path.elementAt(start-1), path.elementAt(start));
if (intersection(l, boundingRect, &p))
lines.append(QLineF(p, path.elementAt(start)));
}
for (int i = start + 1; i <= end; i++)
lines.append(QLineF(path.elementAt(i-1), path.elementAt(i)));
if (end < path.elementCount() - 1) {
QLineF l(path.elementAt(end), path.elementAt(end+1));
if (intersection(l, boundingRect, &p))
lines.append(QLineF(path.elementAt(end), p));
}
}
return lines;
}
template<class T>
static QPainterPath textPath(const T &path, qreal textWidth,
qreal charWidth, const QRectF &tileRect)
{
QList<QLineF> lines(lineString(path, tileRect));
@ -186,10 +236,31 @@ static bool reverse(const QPainterPath &path)
return (angle > 90 && angle < 270) ? true : false;
}
TextPathItem::TextPathItem(const QPolygonF &line, const QString *label,
const QRect &tileRect, const QFont *font, const QColor *color)
: TextItem(label), _font(font), _color(color)
: TextItem(label), _font(font), _color(color), _outlineColor(0)
{
qreal cw = font->pixelSize() * 0.6;
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();
}
TextPathItem::TextPathItem(const QPainterPath &line, const QString *label,
const QRect &tileRect, const QFont *font, const QColor *color,
const QColor *outlineColor) : TextItem(label), _font(font), _color(color),
_outlineColor(outlineColor)
{
qreal cw = font->pixelSize() * 0.6;
qreal textWidth = _text->size() * cw;
@ -219,7 +290,7 @@ void TextPathItem::paint(QPainter *painter) const
QTransform t = painter->transform();
painter->setFont(*_font);
painter->setPen(Qt::white);
painter->setPen(_outlineColor ? *_outlineColor : Qt::white);
for (int i = 0; i < _text->size(); i++) {
QPointF point = _path.pointAtPercent(percent);

View File

@ -11,6 +11,9 @@ public:
TextPathItem() : TextItem(0), _font(0), _color(0) {}
TextPathItem(const QPolygonF &line, const QString *label,
const QRect &tileRect, const QFont *font, const QColor *color);
TextPathItem(const QPainterPath &line, const QString *label,
const QRect &tileRect, const QFont *font, const QColor *color,
const QColor *outlineColor);
bool isValid() const {return !_path.isEmpty();}
@ -21,6 +24,7 @@ public:
private:
const QFont *_font;
const QColor *_color;
const QColor *_outlineColor;
QPainterPath _path;
QRectF _rect;
QPainterPath _shape;

View File

@ -17,8 +17,8 @@ static void expand(QRect &rect, int width)
TextPointItem::TextPointItem(const QPoint &point, const QString *text,
const QFont *font, const QImage *img, const QColor *color,
const QColor *bgColor) : TextItem(font ? text : 0), _font(font), _img(img),
_color(color), _bgColor(bgColor)
const QColor *bgColor, bool padding) : TextItem(font ? text : 0),
_font(font), _img(img), _color(color), _bgColor(bgColor)
{
if (_text) {
QFontMetrics fm(*_font);
@ -30,18 +30,19 @@ TextPointItem::TextPointItem(const QPoint &point, const QString *text,
expand(_textRect, _font->pixelSize() * MIN_BOX_WIDTH);
}
setPos(point);
setPos(point, padding);
}
void TextPointItem::setPos(const QPoint &point)
void TextPointItem::setPos(const QPoint &point, bool padding)
{
QPainterPath shape;
QRect iconRect;
if (_img) {
iconRect = QRect(QPoint(point.x() - _img->width()/2, point.y()
int xOffset = padding ? _img->width() : _img->width() / 2;
iconRect = QRect(QPoint(point.x() - xOffset, point.y()
- _img->height()/2), _img->size());
_textRect.moveTopLeft(QPoint(point.x() + _img->width(), point.y()
_textRect.moveTopLeft(QPoint(point.x() + _img->width()/2, point.y()
- _textRect.height()/2));
} else
_textRect.moveCenter(point);

View File

@ -16,7 +16,8 @@ class TextPointItem : public TextItem
public:
TextPointItem() : TextItem(0), _font(0), _img(0) {}
TextPointItem(const QPoint &point, const QString *text, const QFont *font,
const QImage *img, const QColor *color, const QColor *bgColor = 0);
const QImage *img, const QColor *color, const QColor *bgColor = 0,
bool padding = true);
bool isValid() const {return !_rect.isEmpty();}
@ -24,7 +25,7 @@ public:
QPainterPath shape() const {return _shape;}
void paint(QPainter *painter) const;
void setPos(const QPoint &point);
void setPos(const QPoint &point, bool padding = true);
private:
const QFont *_font;