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:
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
62
src/common/polygon.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ namespace ProgramPaths
|
||||
QString gcsFile();
|
||||
QString pcsFile();
|
||||
QString typFile();
|
||||
QString renderthemeFile();
|
||||
}
|
||||
|
||||
#endif // PROGRAMPATHS_H
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
@ -1,5 +1,6 @@
|
||||
#include "bitstream.h"
|
||||
|
||||
using namespace IMG;
|
||||
|
||||
bool BitStream1::flush()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "deltastream.h"
|
||||
|
||||
|
||||
using namespace IMG;
|
||||
|
||||
static int bitSize(quint8 baseSize, bool variableSign, bool extraBit)
|
||||
{
|
||||
int bits = 2;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "huffmanstream.h"
|
||||
|
||||
using namespace IMG;
|
||||
|
||||
bool HuffmanStreamF::init(bool line)
|
||||
{
|
||||
if (line) {
|
||||
|
@ -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
|
||||
|
@ -2,6 +2,8 @@
|
||||
#include "huffmantable.h"
|
||||
|
||||
|
||||
using namespace IMG;
|
||||
|
||||
static inline quint32 readVUint32(const quint8 *buffer, quint32 bytes)
|
||||
{
|
||||
quint32 val = 0;
|
||||
|
@ -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
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "subfile.h"
|
||||
#include "huffmantext.h"
|
||||
|
||||
using namespace IMG;
|
||||
|
||||
static inline quint32 readVUint32(const quint8 *buffer, quint32 bytes)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ▭
|
||||
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 ▭
|
||||
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,
|
||||
|
@ -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 ▭
|
||||
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 ▭
|
||||
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
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "rgnfile.h"
|
||||
#include "netfile.h"
|
||||
|
||||
using namespace IMG;
|
||||
|
||||
static bool readAdjCounts(BitStream4R &bs, QVector<quint16> &cnts, quint16 &mask)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "bitstream.h"
|
||||
#include "nodfile.h"
|
||||
|
||||
using namespace IMG;
|
||||
|
||||
#define ARRAY_SIZE(array) \
|
||||
(sizeof(array) / sizeof(array[0]))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)))
|
||||
|
@ -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
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "nodfile.h"
|
||||
#include "rgnfile.h"
|
||||
|
||||
using namespace IMG;
|
||||
|
||||
#define MASK(bits) ((2U << ((bits) - 1U)) - 1U)
|
||||
|
||||
|
@ -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
55
src/map/IMG/shield.h
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "vectortile.h"
|
||||
|
||||
using namespace IMG;
|
||||
|
||||
static void copyPolys(const RectC &rect, QList<MapData::Poly> *src,
|
||||
QList<MapData::Poly> *dst)
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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
22
src/map/mapsforge/cmp.h
Normal 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
|
714
src/map/mapsforge/mapdata.cpp
Normal file
714
src/map/mapsforge/mapdata.cpp
Normal 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
183
src/map/mapsforge/mapdata.h
Normal 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 ▭
|
||||
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 ▭
|
||||
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
|
184
src/map/mapsforge/rastertile.cpp
Normal file
184
src/map/mapsforge/rastertile.cpp
Normal 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);
|
||||
}
|
98
src/map/mapsforge/rastertile.h
Normal file
98
src/map/mapsforge/rastertile.h
Normal 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
357
src/map/mapsforge/style.cpp
Normal 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
230
src/map/mapsforge/style.h
Normal 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
|
51
src/map/mapsforge/subfile.cpp
Normal file
51
src/map/mapsforge/subfile.cpp
Normal 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
130
src/map/mapsforge/subfile.h
Normal 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
199
src/map/mapsforgemap.cpp
Normal 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
106
src/map/mapsforgemap.h
Normal 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
|
@ -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());
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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);
|
@ -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;
|
@ -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);
|
@ -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;
|
Reference in New Issue
Block a user