mirror of
https://github.com/tumic0/QtPBFImagePlugin.git
synced 2025-07-06 07:42:52 +02:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
032c699e8b | |||
a298cfc47d | |||
402e1e3adc | |||
254e93e583 | |||
95d4fee453 | |||
a6547d27d3 | |||
3e065f87fa | |||
61291bb202 | |||
154114dd2a | |||
c0a7d64a1d | |||
d8df5cb595 | |||
543bf63017 | |||
7ea19d260c | |||
0185cd904f | |||
ec6302d524 | |||
0916d6330c |
@ -10,6 +10,7 @@ env:
|
|||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update; fi
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install qt protobuf; fi
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install qt protobuf; fi
|
||||||
|
69
README.md
69
README.md
@ -8,15 +8,11 @@ PBF(MVT) vector tiles without (almost, see usage) any application modifications.
|
|||||||
|
|
||||||
Standard Mapbox GL Styles are used for styling the maps. Most relevant style
|
Standard Mapbox GL Styles are used for styling the maps. Most relevant style
|
||||||
features used by [Maputnik](http://editor.openmaptiles.org) are supported.
|
features used by [Maputnik](http://editor.openmaptiles.org) are supported.
|
||||||
The style is loaded from the
|
A default fallback style (OSM-Liberty) for OpenMapTiles is part of the plugin.
|
||||||
[$AppDataLocation](http://doc.qt.io/qt-5/qstandardpaths.html)/style/style.json
|
|
||||||
file on plugin load. If the style uses a sprite, the sprite JSON file must
|
|
||||||
be named sprite.json and the sprite image sprite.png and both files must be
|
|
||||||
placed in the same directory as the style itself. A default fallback style
|
|
||||||
(OSM-Liberty) for OpenMapTiles is part of the plugin.
|
|
||||||
|
|
||||||
"Plain" PBF files as well as gzip compressed files (as used in MBTiles) are
|
"Plain" PBF files as well as gzip compressed files (as used in MBTiles) are
|
||||||
supported by the plugin.
|
supported by the plugin. The tile size is (since version 2.0 of the plugin) 512px
|
||||||
|
to fit the styles and available data (OpenMapTiles, Mapbox tiles).
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
Due to a major design flaw in the Mapbox vector tiles specification - the zoom
|
Due to a major design flaw in the Mapbox vector tiles specification - the zoom
|
||||||
@ -33,10 +29,22 @@ The plugin supports vector scaling using QImageReader's setScaledSize() method,
|
|||||||
so when used like in the following example:
|
so when used like in the following example:
|
||||||
```cpp
|
```cpp
|
||||||
QImageReader reader(file, QString::number(zoom).toLatin1());
|
QImageReader reader(file, QString::number(zoom).toLatin1());
|
||||||
reader.setScaledSize(QSize(512, 512));
|
reader.setScaledSize(QSize(1024, 1024));
|
||||||
reader.read(&image);
|
reader.read(&image);
|
||||||
```
|
```
|
||||||
you will get 512x512px tiles with a pixel ratio of 2 (= HiDPI tiles).
|
you will get 1024x1024px tiles with a pixel ratio of 2 (= HiDPI tiles).
|
||||||
|
|
||||||
|
## Styles
|
||||||
|
The map style is loaded from the
|
||||||
|
[$AppDataLocation](http://doc.qt.io/qt-5/qstandardpaths.html)/style/style.json
|
||||||
|
file on plugin load. If the style uses a sprite, the sprite JSON file must
|
||||||
|
be named `sprite.json` and the sprite image `sprite.png` and both files must be
|
||||||
|
placed in the same directory as the style itself. *A style compatible with the
|
||||||
|
tiles data schema (Mapbox, OpenMapTiles, Tilezen) must be used.*
|
||||||
|
|
||||||
|
For a list of "ready to use" styles see the
|
||||||
|
[QtPBFImagePlugin-styles](https://github.com/tumic0/QtPBFImagePlugin-styles)
|
||||||
|
repository.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
### Requirements
|
### Requirements
|
||||||
@ -72,22 +80,45 @@ for most common distros available on OBS.
|
|||||||
style are ignored.
|
style are ignored.
|
||||||
* Text PBF features must have a unique id (OpenMapTiles >= v3.7) for the text layout
|
* Text PBF features must have a unique id (OpenMapTiles >= v3.7) for the text layout
|
||||||
algorithm to work properly.
|
algorithm to work properly.
|
||||||
|
* Expressions are not supported in the styles, only property functions are implemented.
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
[Changelog](https://build.opensuse.org/package/view_file/home:tumic:QtPBFImagePlugin/QtPBFImagePlugin/libqt5-qtpbfimageformat.changes)
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
A picture is worth a thousand words. Data and styles from https://openmaptiles.org.
|
A picture is worth a thousand words.
|
||||||
#### OSM-liberty
|
#### OpenMapTiles
|
||||||

|
|
||||||
|
* Data: [MapTiler](https://github.com/tumic0/GPXSee-maps/blob/master/World/MapTiler.tpl)
|
||||||
|
* Style: [OSM-liberty](https://github.com/tumic0/QtPBFImagePlugin-styles/blob/master/OpenMapTiles/osm-liberty/style.json)
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|

|
||||||
|
|
||||||
#### Klokantech-basic
|
#### Mapbox
|
||||||

|
|
||||||

|
* Data: [Mapbox](https://github.com/tumic0/GPXSee-maps/blob/master/World/Mapbox.tpl)
|
||||||

|
* Style: [Bright](https://github.com/tumic0/QtPBFImagePlugin-styles/blob/master/Mapbox/bright/style.json)
|
||||||

|
|
||||||

|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
#### Tilezen
|
||||||
|
|
||||||
|
* Data: [HERE](https://github.com/tumic0/GPXSee-maps/blob/master/World/here-vector.tpl)
|
||||||
|
* Style: [Apollo-Bright](https://github.com/tumic0/QtPBFImagePlugin-styles/blob/master/Tilezen/apollo-bright/style.json)
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
## Applications using QtPBFImagePlugin
|
## Applications using QtPBFImagePlugin
|
||||||
* [GPXSee](https://www.gpxsee.org)
|
* [GPXSee](https://www.gpxsee.org)
|
||||||
|
@ -37,6 +37,8 @@ SOURCES += src/pbfplugin.cpp \
|
|||||||
src/textitem.cpp
|
src/textitem.cpp
|
||||||
RESOURCES += pbfplugin.qrc
|
RESOURCES += pbfplugin.qrc
|
||||||
|
|
||||||
|
DEFINES += QT_NO_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
unix:!macx{
|
unix:!macx{
|
||||||
LIBS += -lprotobuf-lite \
|
LIBS += -lprotobuf-lite \
|
||||||
-lz
|
-lz
|
||||||
|
@ -40,7 +40,9 @@ static QColor interpolate(const QPair<qreal, QColor> &p0,
|
|||||||
qreal p1h, p1s, p1l, p1a;
|
qreal p1h, p1s, p1l, p1a;
|
||||||
p1.second.getHslF(&p1h, &p1s, &p1l, &p1a);
|
p1.second.getHslF(&p1h, &p1s, &p1l, &p1a);
|
||||||
|
|
||||||
return QColor::fromHslF(f(p0h, p1h, ratio), f(p0s, p1s, ratio),
|
/* Qt returns a hue of -1 for achromatic colors. We convert it to a hue of 1
|
||||||
|
using qAbs() to enable interpolation of grayscale colors */
|
||||||
|
return QColor::fromHslF(f(qAbs(p0h), qAbs(p1h), ratio), f(p0s, p1s, ratio),
|
||||||
f(p0l, p1l, ratio), f(p0a, p1a, ratio));
|
f(p0l, p1l, ratio), f(p0a, p1a, ratio));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
67
src/gzip.cpp
67
src/gzip.cpp
@ -1,35 +1,58 @@
|
|||||||
#include <QtEndian>
|
#include <QIODevice>
|
||||||
#include <QDebug>
|
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include "gzip.h"
|
#include "gzip.h"
|
||||||
|
|
||||||
|
#define CHUNK 16384
|
||||||
|
|
||||||
QByteArray Gzip::uncompress(const QByteArray &data)
|
QByteArray Gzip::uncompress(QIODevice *device, int limit)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
z_stream strm;
|
||||||
|
unsigned char in[CHUNK];
|
||||||
|
unsigned char out[CHUNK];
|
||||||
|
qint64 rs;
|
||||||
QByteArray uba;
|
QByteArray uba;
|
||||||
z_stream stream;
|
|
||||||
|
|
||||||
quint32 *size = (quint32*)(data.constData() + data.size() - sizeof(quint32));
|
|
||||||
uba.resize(qFromLittleEndian(*size));
|
|
||||||
|
|
||||||
stream.zalloc = Z_NULL;
|
strm.zalloc = Z_NULL;
|
||||||
stream.zfree = Z_NULL;
|
strm.zfree = Z_NULL;
|
||||||
stream.opaque = Z_NULL;
|
strm.opaque = Z_NULL;
|
||||||
|
strm.avail_in = 0;
|
||||||
|
strm.next_in = Z_NULL;
|
||||||
|
if (inflateInit2(&strm, MAX_WBITS + 16) != Z_OK)
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
stream.next_in = (Bytef*)data.constData();
|
do {
|
||||||
stream.avail_in = data.size();
|
rs = device->read((char*)in, CHUNK);
|
||||||
stream.next_out = (Bytef*)uba.data();
|
if (rs < 0) {
|
||||||
stream.avail_out = uba.size();
|
(void)inflateEnd(&strm);
|
||||||
|
return QByteArray();
|
||||||
|
} else if (rs == 0)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
strm.avail_in = (uInt)rs;
|
||||||
|
strm.next_in = in;
|
||||||
|
|
||||||
if (inflateInit2(&stream, MAX_WBITS + 16) != Z_OK)
|
do {
|
||||||
return uba;
|
strm.avail_out = CHUNK;
|
||||||
|
strm.next_out = out;
|
||||||
if (inflate(&stream, Z_NO_FLUSH) != Z_STREAM_END) {
|
ret = inflate(&strm, Z_NO_FLUSH);
|
||||||
qCritical() << "Invalid gzip data";
|
Q_ASSERT(ret != Z_STREAM_ERROR);
|
||||||
uba = QByteArray();
|
switch (ret) {
|
||||||
|
case Z_NEED_DICT:
|
||||||
|
case Z_DATA_ERROR:
|
||||||
|
case Z_MEM_ERROR:
|
||||||
|
(void)inflateEnd(&strm);
|
||||||
|
return QByteArray();
|
||||||
}
|
}
|
||||||
|
uba.append((char*)out, CHUNK - strm.avail_out);
|
||||||
inflateEnd(&stream);
|
if (limit && uba.size() >= limit) {
|
||||||
|
(void)inflateEnd(&strm);
|
||||||
return uba;
|
return uba;
|
||||||
}
|
}
|
||||||
|
} while (!strm.avail_out);
|
||||||
|
} while (ret != Z_STREAM_END);
|
||||||
|
|
||||||
|
(void)inflateEnd(&strm);
|
||||||
|
return (ret == Z_STREAM_END) ? uba : QByteArray();
|
||||||
|
}
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
||||||
|
class QIODevice;
|
||||||
|
|
||||||
namespace Gzip
|
namespace Gzip
|
||||||
{
|
{
|
||||||
QByteArray uncompress(const QByteArray &data);
|
QByteArray uncompress(QIODevice *device, int limit = 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // GZIP_H
|
#endif // GZIP_H
|
||||||
|
@ -10,22 +10,22 @@
|
|||||||
|
|
||||||
#define TILE_SIZE 512
|
#define TILE_SIZE 512
|
||||||
|
|
||||||
#define GZIP_MAGIC 0x1F8B0800
|
#define GZIP_MAGIC 0x1F8B
|
||||||
#define GZIP_MAGIC_MASK 0xFFFFFF00
|
#define GZIP_MAGIC_MASK 0xFFFF
|
||||||
#define PBF_MAGIC 0x1A000000
|
#define PBF_MAGIC 0x1A00
|
||||||
#define PBF_MAGIC_MASK 0xFF000000
|
#define PBF_MAGIC_MASK 0xFF00
|
||||||
|
|
||||||
static bool isMagic(quint32 magic, quint32 mask, quint32 value)
|
static bool isMagic(quint16 magic, quint16 mask, quint16 value)
|
||||||
{
|
{
|
||||||
return ((qFromBigEndian(value) & mask) == magic);
|
return ((qFromBigEndian(value) & mask) == magic);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isGZIPPBF(quint32 magic)
|
static bool isGZIPPBF(quint16 magic)
|
||||||
{
|
{
|
||||||
return isMagic(GZIP_MAGIC, GZIP_MAGIC_MASK, magic);
|
return isMagic(GZIP_MAGIC, GZIP_MAGIC_MASK, magic);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isPlainPBF(quint32 magic)
|
static bool isPlainPBF(quint16 magic)
|
||||||
{
|
{
|
||||||
return isMagic(PBF_MAGIC, PBF_MAGIC_MASK, magic);
|
return isMagic(PBF_MAGIC, PBF_MAGIC_MASK, magic);
|
||||||
}
|
}
|
||||||
@ -34,7 +34,7 @@ static bool isPlainPBF(quint32 magic)
|
|||||||
bool PBFHandler::canRead() const
|
bool PBFHandler::canRead() const
|
||||||
{
|
{
|
||||||
if (canRead(device())) {
|
if (canRead(device())) {
|
||||||
setFormat("pbf");
|
setFormat("mvt");
|
||||||
return true;
|
return true;
|
||||||
} else
|
} else
|
||||||
return false;
|
return false;
|
||||||
@ -42,27 +42,37 @@ bool PBFHandler::canRead() const
|
|||||||
|
|
||||||
bool PBFHandler::canRead(QIODevice *device)
|
bool PBFHandler::canRead(QIODevice *device)
|
||||||
{
|
{
|
||||||
quint32 magic;
|
quint16 magic;
|
||||||
qint64 size = device->peek((char*)&magic, sizeof(magic));
|
qint64 size = device->peek((char*)&magic, sizeof(magic));
|
||||||
if (size != sizeof(magic))
|
if (size != sizeof(magic))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return (isGZIPPBF(magic) || isPlainPBF(magic));
|
if (isPlainPBF(magic))
|
||||||
|
return true;
|
||||||
|
else if (isGZIPPBF(magic)) {
|
||||||
|
QByteArray ba(Gzip::uncompress(device, sizeof(magic)));
|
||||||
|
if (ba.size() < (int)sizeof(magic))
|
||||||
|
return false;
|
||||||
|
return isPlainPBF(*((quint16*)ba.constData()));
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PBFHandler::read(QImage *image)
|
bool PBFHandler::read(QImage *image)
|
||||||
{
|
{
|
||||||
quint32 magic;
|
quint16 magic;
|
||||||
if (device()->peek((char*)&magic, sizeof(magic)) != sizeof(magic))
|
if (device()->peek((char*)&magic, sizeof(magic)) != sizeof(magic))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
if (isGZIPPBF(magic))
|
if (isGZIPPBF(magic)) {
|
||||||
ba = Gzip::uncompress(device()->readAll());
|
ba = Gzip::uncompress(device());
|
||||||
else if (isPlainPBF(magic))
|
if (ba.isNull()) {
|
||||||
ba = device()->readAll();
|
qCritical() << "Invalid gzip data";
|
||||||
if (ba.isNull())
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
} else if (isPlainPBF(magic))
|
||||||
|
ba = device()->readAll();
|
||||||
vector_tile::Tile data;
|
vector_tile::Tile data;
|
||||||
if (!data.ParseFromArray(ba.constData(), ba.size())) {
|
if (!data.ParseFromArray(ba.constData(), ba.size())) {
|
||||||
qCritical() << "Invalid PBF data";
|
qCritical() << "Invalid PBF data";
|
||||||
|
@ -172,7 +172,7 @@ bool Style::Layer::Filter::match(const PBF::Feature &feature) const
|
|||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
case GeometryType:
|
case GeometryType:
|
||||||
return feature.type() == _kv.second.toUInt();
|
return feature.type() == _kv.second.toInt();
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user