1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-06-28 12:09:15 +02:00

Added support for Mapsforge maps

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

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

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

View File

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

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

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

View File

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

View File

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

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

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

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

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

View File

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

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

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