diff --git a/pbfplugin.qrc b/pbfplugin.qrc index 11c11d4..afb387e 100644 --- a/pbfplugin.qrc +++ b/pbfplugin.qrc @@ -3,5 +3,7 @@ style/style.json style/sprite.json style/sprite.png + style/sprite@2x.json + style/sprite@2x.png diff --git a/src/pbf.cpp b/src/pbf.cpp index 8743577..a3d1cb7 100644 --- a/src/pbf.cpp +++ b/src/pbf.cpp @@ -156,18 +156,20 @@ static void drawFeature(const Feature &feature, Style *style, int styleLayer, } static void drawLayer(const Layer &layer, Style *style, int styleLayer, - Tile &tile, const QPointF &scale) + Tile &tile) { if (layer.data()->version() > 2) return; - QSizeF factor(tile.size().width() / scale.x() / (qreal)layer.data()->extent(), - tile.size().height() / scale.y() / (qreal)layer.data()->extent()); + QSizeF factor(tile.size().width() / tile.scale().x() / + (qreal)layer.data()->extent(), tile.size().height() / tile.scale().y() + / (qreal)layer.data()->extent()); + tile.painter().save(); style->setupLayer(tile, styleLayer); - for (int i = 0; i < layer.features().size(); i++) drawFeature(layer.features().at(i), style, styleLayer, factor, tile); + tile.painter().restore(); } bool PBF::render(const QByteArray &data, int zoom, Style *style, @@ -192,8 +194,6 @@ bool PBF::render(const QByteArray &data, int zoom, Style *style, layers.insert(name, Layer(&layer)); } - t.painter().save(); - // Process source layers in order of style layers for (int i = 0; i < style->sourceLayers().size(); i++) { QMap::const_iterator it = layers.find( @@ -201,10 +201,9 @@ bool PBF::render(const QByteArray &data, int zoom, Style *style, if (it == layers.constEnd()) continue; - drawLayer(*it, style, i, t, scale); + drawLayer(*it, style, i, t); } - t.painter().restore(); t.text().render(&t.painter()); return true; diff --git a/src/sprites.cpp b/src/sprites.cpp index 8ed48d7..3c14363 100644 --- a/src/sprites.cpp +++ b/src/sprites.cpp @@ -9,12 +9,19 @@ Loading the sprites atlas image must be deferred until all image plugins are loaded, otherwise reading the image will cause a deadlock! */ -static QImage &atlas(const QString &fileName) +static const QImage *atlas(const QString &fileName) { static QImage *img = new QImage(fileName); - return *img; + return img; } +static const QImage *atlas2x(const QString &fileName) +{ + static QImage *img = new QImage(fileName); + return img; +} + + Sprites::Sprite::Sprite(const QJsonObject &json) { int x, y, width, height; @@ -42,7 +49,17 @@ Sprites::Sprite::Sprite(const QJsonObject &json) bool Sprites::load(const QString &jsonFile, const QString &imageFile) { _imageFile = imageFile; + return load(jsonFile, _sprites); +} +bool Sprites::load2x(const QString &jsonFile, const QString &imageFile) +{ + _image2xFile = imageFile; + return load(jsonFile, _sprites2x); +} + +bool Sprites::load(const QString &jsonFile, QMap &map) +{ QFile file(jsonFile); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qCritical() << jsonFile << ": error opening file"; @@ -64,7 +81,7 @@ bool Sprites::load(const QString &jsonFile, const QString &imageFile) if (val.isObject()) { Sprite s(val.toObject()); if (s.rect().isValid()) - _sprites.insert(it.key(), s); + map.insert(it.key(), s); else qWarning() << it.key() << ": invalid sprite definition"; } else @@ -74,20 +91,36 @@ bool Sprites::load(const QString &jsonFile, const QString &imageFile) return true; } -QImage Sprites::icon(const QString &name) const +QImage Sprites::icon(const QString &name, bool hidpi) const { - if (_imageFile.isNull()) - return QImage(); - const QImage &img = atlas(_imageFile); - if (img.isNull()) + qreal ratio; + const QImage *img; + const QMap *map; + + if (hidpi && !_image2xFile.isNull()) { + img = atlas2x(_image2xFile); + map = &_sprites2x; + ratio = 2; + } else if (!_imageFile.isNull()) { + img = atlas(_imageFile); + map = &_sprites; + ratio = 1; + } else return QImage(); - QMap::const_iterator it = _sprites.find(name); - if (it == _sprites.constEnd()) + if (img->isNull()) return QImage(); - if (!img.rect().contains(it->rect())) + + QMap::const_iterator it = map->find(name); + if (it == map->constEnd()) return QImage(); - return img.copy(it->rect()); + if (!img->rect().contains(it->rect())) + return QImage(); + + QImage ret(img->copy(it->rect())); + ret.setDevicePixelRatio(ratio); + + return ret; } diff --git a/src/sprites.h b/src/sprites.h index a029f58..fb256e2 100644 --- a/src/sprites.h +++ b/src/sprites.h @@ -9,8 +9,9 @@ class Sprites { public: bool load(const QString &jsonFile, const QString &imageFile); + bool load2x(const QString &jsonFile, const QString &imageFile); - QImage icon(const QString &name) const; + QImage icon(const QString &name, bool hidpi) const; private: class Sprite { @@ -22,8 +23,10 @@ private: QRect _rect; }; - QMap _sprites; - QString _imageFile; + bool load(const QString &jsonFile, QMap &map); + + QMap _sprites, _sprites2x; + QString _imageFile, _image2xFile; }; #endif // SPRITES_H diff --git a/src/style.cpp b/src/style.cpp index 40336f7..da51976 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -223,7 +223,8 @@ QPen Style::Layer::Paint::pen(Type type, int zoom) const return pen; } -QBrush Style::Layer::Paint::brush(Type type, int zoom, const Sprites &sprites) const +QBrush Style::Layer::Paint::brush(Type type, int zoom, const Sprites &sprites, + bool hidpi) const { QColor color; QBrush brush(Qt::NoBrush); @@ -236,7 +237,7 @@ QBrush Style::Layer::Paint::brush(Type type, int zoom, const Sprites &sprites) c brush = QBrush(color); pattern = _fillPattern.value(zoom); if (!pattern.isNull()) - brush.setTextureImage(sprites.icon(pattern)); + brush.setTextureImage(sprites.icon(pattern, hidpi)); break; case Background: color = _backgroundColor.value(zoom); @@ -244,7 +245,7 @@ QBrush Style::Layer::Paint::brush(Type type, int zoom, const Sprites &sprites) c brush = QBrush(color); pattern = _fillPattern.value(zoom); if (!pattern.isNull()) - brush.setTextureImage(sprites.icon(pattern)); + brush.setTextureImage(sprites.icon(pattern, hidpi)); break; default: break; @@ -448,7 +449,8 @@ void Style::Layer::setPathPainter(Tile &tile, const Sprites &sprites) const pen.setJoinStyle(_layout.lineJoin(zoom)); pen.setCapStyle(_layout.lineCap(zoom)); - QBrush brush(_paint.brush(_type, zoom, sprites)); + bool hidpi = qMax(tile.scale().x(), tile.scale().y()) > 1.0 ? true : false; + QBrush brush(_paint.brush(_type, zoom, sprites, hidpi)); p.setRenderHint(QPainter::Antialiasing, _paint.antialias(_type, zoom)); p.setPen(pen); @@ -478,7 +480,8 @@ void Style::Layer::addSymbol(Tile &tile, const QPainterPath &path, return; QString icon = _layout.icon(tile.zoom(), tags); - tile.text().addLabel(text, sprites.icon(icon), path); + bool hidpi = qMax(tile.scale().x(), tile.scale().y()) > 1.0 ? true : false; + tile.text().addLabel(text, sprites.icon(icon, hidpi), path); } bool Style::load(const QString &fileName) @@ -509,7 +512,9 @@ bool Style::load(const QString &fileName) for (int i = 0; i < _layers.size(); i++) _sourceLayers.append(_layers.at(i).sourceLayer()); + QDir styleDir = QFileInfo(fileName).absoluteDir(); + QString spritesJSON(styleDir.filePath("sprite.json")); if (QFileInfo::exists(spritesJSON)) { QString spritesImg(styleDir.filePath("sprite.png")); @@ -519,6 +524,15 @@ bool Style::load(const QString &fileName) qCritical() << spritesImg << ": no such file"; } + QString sprites2xJSON(styleDir.filePath("sprite@2x.json")); + if (QFileInfo::exists(sprites2xJSON)) { + QString sprites2xImg(styleDir.filePath("sprite@2x.png")); + if (QFileInfo::exists(sprites2xImg)) + _sprites.load2x(sprites2xJSON, sprites2xImg); + else + qCritical() << sprites2xImg << ": no such file"; + } + return true; } diff --git a/src/style.h b/src/style.h index 9abf544..4d3e2f3 100644 --- a/src/style.h +++ b/src/style.h @@ -136,7 +136,8 @@ private: Paint(const QJsonObject &json); QPen pen(Layer::Type type, int zoom) const; - QBrush brush(Layer::Type type, int zoom, const Sprites &sprites) const; + QBrush brush(Layer::Type type, int zoom, const Sprites &sprites, + bool hidpi) const; qreal opacity(Layer::Type type, int zoom) const; bool antialias(Layer::Type type, int zoom) const; QString fillPattern(int zoom) const diff --git a/src/text.cpp b/src/text.cpp index a7897b1..b3bb041 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include "text.h" #include "textpointitem.h" diff --git a/src/text.h b/src/text.h index 91cce98..8e22937 100644 --- a/src/text.h +++ b/src/text.h @@ -34,8 +34,7 @@ public: Auto }; - Text(const QSize &size) - : _sceneRect(QRectF(QPointF(0, 0), size)) {} + Text(const QSize &size) : _sceneRect(QRectF(QPointF(0, 0), size)) {} ~Text(); void setFont(const QFont &font) {_font = font;} diff --git a/src/textpointitem.cpp b/src/textpointitem.cpp index fd58c58..1e25a68 100644 --- a/src/textpointitem.cpp +++ b/src/textpointitem.cpp @@ -71,7 +71,8 @@ QRectF TextPointItem::fuzzyBoundingRect(const QString &str, QRectF TextPointItem::computeTextRect(BoundingRectFunction brf) const { - QRectF iconRect = _icon.isNull() ? QRectF() : _icon.rect(); + QRectF iconRect = _icon.isNull() ? QRectF() + : QRectF(QPointF(0, 0), QSizeF(_icon.size()) / _icon.devicePixelRatioF()); QRectF textRect = brf(text(), font(), _maxWidth); switch (_anchor) { @@ -108,7 +109,8 @@ TextPointItem::TextPointItem(const QString &text, const QPointF &pos, _boundingRect = computeTextRect(fuzzyBoundingRect); if (!_icon.isNull()) { - QRectF iconRect(_icon.rect()); + QRectF iconRect(QPointF(0, 0), QSizeF(_icon.size()) + / _icon.devicePixelRatioF()); iconRect.moveCenter(pos); _boundingRect |= iconRect; } @@ -125,8 +127,9 @@ void TextPointItem::paint(QPainter *painter) const if (!_icon.isNull()) { textRect = computeTextRect(exactBoundingRect); - painter->drawImage(_pos - QPointF(_icon.width() / 2, - _icon.height() / 2), _icon); + painter->drawImage(_pos - QPointF(_icon.width() + / _icon.devicePixelRatioF() / 2, _icon.height() + / _icon.devicePixelRatioF() / 2), _icon); } else textRect = computeTextRect(fuzzyBoundingRect); diff --git a/src/tile.h b/src/tile.h index 1ba16e0..fbde564 100644 --- a/src/tile.h +++ b/src/tile.h @@ -8,18 +8,22 @@ class Tile { public: Tile(QImage *img, int zoom, const QPointF &scale) - : _zoom(zoom), _size(img->size()), _text(QSize(img->size().width() - / scale.x(), img->size().height() / scale.y())), _painter(img) + : _zoom(zoom), _size(img->size()), _scale(scale), + _text(QSize(img->size().width() / scale.x(), + img->size().height() / scale.y())), _painter(img) {_painter.scale(scale.x(), scale.y());} int zoom() const {return _zoom;} - QSize size() const {return _size;} + const QSize &size() const {return _size;} + const QPointF &scale() const {return _scale;} + Text &text() {return _text;} QPainter &painter() {return _painter;} private: int _zoom; QSize _size; + QPointF _scale; Text _text; QPainter _painter; }; diff --git a/style/sprite@2x.json b/style/sprite@2x.json new file mode 100644 index 0000000..630c4b1 --- /dev/null +++ b/style/sprite@2x.json @@ -0,0 +1,1682 @@ +{ + "aerialway_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 568, + "y": 254 + }, + "aerialway_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 160, + "y": 380 + }, + "airfield_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 598, + "y": 254 + }, + "airfield_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 198, + "y": 380 + }, + "airport_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 628, + "y": 254 + }, + "airport_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 236, + "y": 380 + }, + "alcohol_shop_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 762, + "y": 420 + }, + "alcohol_shop_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 128, + "y": 0 + }, + "america_football_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 796, + "y": 420 + }, + "america_football_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 170, + "y": 0 + }, + "amusement_park_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 830, + "y": 420 + }, + "amusement_park_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 212, + "y": 0 + }, + "aquarium_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 864, + "y": 420 + }, + "aquarium_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 0, + "y": 128 + }, + "art_gallery_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 898, + "y": 420 + }, + "art_gallery_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 42, + "y": 128 + }, + "attraction_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 932, + "y": 420 + }, + "attraction_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 84, + "y": 128 + }, + "bakery_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 966, + "y": 420 + }, + "bakery_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 126, + "y": 128 + }, + "bank_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 454, + "y": 458 + }, + "bank_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 168, + "y": 128 + }, + "bar_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 488, + "y": 458 + }, + "bar_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 210, + "y": 128 + }, + "baseball_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 522, + "y": 458 + }, + "baseball_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 0, + "y": 170 + }, + "basketball_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 556, + "y": 458 + }, + "basketball_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 42, + "y": 170 + }, + "beer_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 590, + "y": 458 + }, + "beer_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 84, + "y": 170 + }, + "bicycle_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 624, + "y": 458 + }, + "bicycle_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 126, + "y": 170 + }, + "bicycle_rental_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 658, + "y": 458 + }, + "bicycle_rental_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 168, + "y": 170 + }, + "building_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 658, + "y": 254 + }, + "building_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 274, + "y": 380 + }, + "bus_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 688, + "y": 254 + }, + "bus_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 312, + "y": 380 + }, + "butcher_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 692, + "y": 458 + }, + "butcher_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 210, + "y": 170 + }, + "cafe_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 726, + "y": 458 + }, + "cafe_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 0, + "y": 212 + }, + "campsite_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 760, + "y": 458 + }, + "campsite_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 42, + "y": 212 + }, + "car_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 718, + "y": 254 + }, + "car_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 350, + "y": 380 + }, + "castle_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 794, + "y": 458 + }, + "castle_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 84, + "y": 212 + }, + "cemetery_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 828, + "y": 458 + }, + "cemetery_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 126, + "y": 212 + }, + "cinema_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 862, + "y": 458 + }, + "cinema_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 168, + "y": 212 + }, + "circle-stroked_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 748, + "y": 254 + }, + "circle-stroked_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 388, + "y": 380 + }, + "circle_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 778, + "y": 254 + }, + "circle_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 426, + "y": 380 + }, + "clothing_store_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 896, + "y": 458 + }, + "clothing_store_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 210, + "y": 212 + }, + "college_11": { + "height": 32, + "pixelRatio": 2, + "width": 32, + "x": 946, + "y": 212 + }, + "college_15": { + "height": 40, + "pixelRatio": 2, + "width": 40, + "x": 0, + "y": 380 + }, + "commercial_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 808, + "y": 254 + }, + "commercial_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 464, + "y": 380 + }, + "cricket_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 930, + "y": 458 + }, + "cricket_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 252, + "y": 128 + }, + "cross_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 838, + "y": 254 + }, + "cross_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 0, + "y": 420 + }, + "dam_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 868, + "y": 254 + }, + "dam_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 38, + "y": 420 + }, + "danger_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 964, + "y": 458 + }, + "danger_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 294, + "y": 128 + }, + "default_1": { + "height": 36, + "pixelRatio": 2, + "width": 36, + "x": 304, + "y": 458 + }, + "default_2": { + "height": 36, + "pixelRatio": 2, + "width": 50, + "x": 340, + "y": 458 + }, + "default_3": { + "height": 36, + "pixelRatio": 2, + "width": 64, + "x": 390, + "y": 458 + }, + "default_4": { + "height": 36, + "pixelRatio": 2, + "width": 78, + "x": 494, + "y": 420 + }, + "default_5": { + "height": 36, + "pixelRatio": 2, + "width": 90, + "x": 572, + "y": 420 + }, + "default_6": { + "height": 36, + "pixelRatio": 2, + "width": 100, + "x": 662, + "y": 420 + }, + "dentist_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 502, + "y": 380 + }, + "dentist_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 336, + "y": 128 + }, + "doctor_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 536, + "y": 380 + }, + "doctor_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 378, + "y": 128 + }, + "dog_park_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 570, + "y": 380 + }, + "dog_park_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 420, + "y": 128 + }, + "dot_10": { + "height": 20, + "pixelRatio": 2, + "width": 20, + "x": 998, + "y": 458 + }, + "dot_11": { + "height": 22, + "pixelRatio": 2, + "width": 22, + "x": 1000, + "y": 420 + }, + "dot_9": { + "height": 18, + "pixelRatio": 2, + "width": 18, + "x": 954, + "y": 296 + }, + "drinking-water_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 604, + "y": 380 + }, + "drinking_water_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 462, + "y": 128 + }, + "embassy_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 638, + "y": 380 + }, + "embassy_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 252, + "y": 170 + }, + "entrance_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 898, + "y": 254 + }, + "entrance_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 76, + "y": 420 + }, + "fast_food_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 672, + "y": 380 + }, + "fast_food_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 294, + "y": 170 + }, + "ferry_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 928, + "y": 254 + }, + "ferry_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 114, + "y": 420 + }, + "fire-station_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 706, + "y": 380 + }, + "fire-station_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 336, + "y": 170 + }, + "fuel_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 958, + "y": 254 + }, + "fuel_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 152, + "y": 420 + }, + "garden_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 740, + "y": 380 + }, + "garden_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 378, + "y": 170 + }, + "gift_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 774, + "y": 380 + }, + "gift_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 420, + "y": 170 + }, + "golf_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 808, + "y": 380 + }, + "golf_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 462, + "y": 170 + }, + "grocery_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 842, + "y": 380 + }, + "grocery_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 252, + "y": 212 + }, + "hairdresser_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 876, + "y": 380 + }, + "hairdresser_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 294, + "y": 212 + }, + "harbor_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 988, + "y": 254 + }, + "harbor_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 190, + "y": 420 + }, + "heart_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 910, + "y": 380 + }, + "heart_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 336, + "y": 212 + }, + "heliport_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 504, + "y": 296 + }, + "heliport_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 228, + "y": 420 + }, + "hospital_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 944, + "y": 380 + }, + "hospital_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 378, + "y": 212 + }, + "ice_cream_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 978, + "y": 380 + }, + "ice_cream_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 420, + "y": 212 + }, + "industry_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 534, + "y": 296 + }, + "industry_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 266, + "y": 420 + }, + "information_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 504, + "y": 128 + }, + "information_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 462, + "y": 212 + }, + "laundry_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 538, + "y": 128 + }, + "laundry_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 254, + "y": 0 + }, + "library_11": { + "height": 32, + "pixelRatio": 2, + "width": 32, + "x": 978, + "y": 212 + }, + "library_15": { + "height": 40, + "pixelRatio": 2, + "width": 40, + "x": 40, + "y": 380 + }, + "lighthouse_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 572, + "y": 128 + }, + "lighthouse_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 296, + "y": 0 + }, + "lodging_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 606, + "y": 128 + }, + "lodging_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 338, + "y": 0 + }, + "marker_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 564, + "y": 296 + }, + "marker_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 304, + "y": 420 + }, + "monument_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 640, + "y": 128 + }, + "monument_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 380, + "y": 0 + }, + "mountain_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 674, + "y": 128 + }, + "mountain_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 422, + "y": 0 + }, + "museum_11": { + "height": 32, + "pixelRatio": 2, + "width": 32, + "x": 504, + "y": 254 + }, + "museum_15": { + "height": 40, + "pixelRatio": 2, + "width": 40, + "x": 80, + "y": 380 + }, + "music_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 708, + "y": 128 + }, + "music_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 464, + "y": 0 + }, + "park_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 742, + "y": 128 + }, + "park_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 0, + "y": 254 + }, + "parking_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 594, + "y": 296 + }, + "parking_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 342, + "y": 420 + }, + "parking_garage_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 624, + "y": 296 + }, + "parking_garage_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 380, + "y": 420 + }, + "pedestrian_polygon": { + "height": 128, + "pixelRatio": 2, + "width": 128, + "x": 0, + "y": 0 + }, + "pharmacy_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 776, + "y": 128 + }, + "pharmacy_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 42, + "y": 254 + }, + "picnic_site_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 810, + "y": 128 + }, + "picnic_site_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 84, + "y": 254 + }, + "pitch_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 844, + "y": 128 + }, + "pitch_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 126, + "y": 254 + }, + "place_of_worship_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 878, + "y": 128 + }, + "place_of_worship_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 168, + "y": 254 + }, + "playground_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 912, + "y": 128 + }, + "playground_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 210, + "y": 254 + }, + "police_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 946, + "y": 128 + }, + "police_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 252, + "y": 254 + }, + "post_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 980, + "y": 128 + }, + "post_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 294, + "y": 254 + }, + "prison_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 504, + "y": 170 + }, + "prison_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 336, + "y": 254 + }, + "railway_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 654, + "y": 296 + }, + "railway_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 418, + "y": 420 + }, + "railway_light_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 684, + "y": 296 + }, + "railway_light_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 456, + "y": 420 + }, + "railway_metro_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 714, + "y": 296 + }, + "railway_metro_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 0, + "y": 458 + }, + "ranger_station_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 538, + "y": 170 + }, + "ranger_station_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 378, + "y": 254 + }, + "religious_christian_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 572, + "y": 170 + }, + "religious_christian_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 420, + "y": 254 + }, + "religious_jewish_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 606, + "y": 170 + }, + "religious_jewish_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 462, + "y": 254 + }, + "religious_muslim_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 640, + "y": 170 + }, + "religious_muslim_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 0, + "y": 296 + }, + "restaurant_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 674, + "y": 170 + }, + "restaurant_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 42, + "y": 296 + }, + "roadblock_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 708, + "y": 170 + }, + "roadblock_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 84, + "y": 296 + }, + "rocket_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 742, + "y": 170 + }, + "rocket_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 126, + "y": 296 + }, + "school_11": { + "height": 32, + "pixelRatio": 2, + "width": 32, + "x": 536, + "y": 254 + }, + "school_15": { + "height": 40, + "pixelRatio": 2, + "width": 40, + "x": 120, + "y": 380 + }, + "shelter_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 776, + "y": 170 + }, + "shelter_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 168, + "y": 296 + }, + "shop_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 810, + "y": 170 + }, + "shop_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 210, + "y": 296 + }, + "skiing_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 844, + "y": 170 + }, + "skiing_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 252, + "y": 296 + }, + "soccer_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 878, + "y": 170 + }, + "soccer_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 294, + "y": 296 + }, + "square-stroke_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 744, + "y": 296 + }, + "square-stroke_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 38, + "y": 458 + }, + "square_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 774, + "y": 296 + }, + "square_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 76, + "y": 458 + }, + "stadium_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 912, + "y": 170 + }, + "stadium_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 336, + "y": 296 + }, + "star-stroke_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 804, + "y": 296 + }, + "star-stroke_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 114, + "y": 458 + }, + "star_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 834, + "y": 296 + }, + "star_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 152, + "y": 458 + }, + "suitcase_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 946, + "y": 170 + }, + "suitcase_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 378, + "y": 296 + }, + "sushi_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 980, + "y": 170 + }, + "sushi_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 420, + "y": 296 + }, + "swimming_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 504, + "y": 212 + }, + "swimming_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 462, + "y": 296 + }, + "telephone_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 538, + "y": 212 + }, + "telephone_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 0, + "y": 338 + }, + "tennis_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 572, + "y": 212 + }, + "tennis_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 42, + "y": 338 + }, + "theatre_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 606, + "y": 212 + }, + "theatre_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 84, + "y": 338 + }, + "toilet_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 640, + "y": 212 + }, + "toilet_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 126, + "y": 338 + }, + "town-hall_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 674, + "y": 212 + }, + "town-hall_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 168, + "y": 338 + }, + "triangle_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 864, + "y": 296 + }, + "triangle_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 190, + "y": 458 + }, + "triangle_stroked_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 894, + "y": 296 + }, + "triangle_stroked_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 228, + "y": 458 + }, + "veterinary_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 708, + "y": 212 + }, + "veterinary_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 210, + "y": 338 + }, + "volcano_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 742, + "y": 212 + }, + "volcano_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 252, + "y": 338 + }, + "warehouse_11": { + "height": 30, + "pixelRatio": 2, + "width": 30, + "x": 924, + "y": 296 + }, + "warehouse_15": { + "height": 38, + "pixelRatio": 2, + "width": 38, + "x": 266, + "y": 458 + }, + "waste_basket_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 776, + "y": 212 + }, + "waste_basket_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 294, + "y": 338 + }, + "water_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 810, + "y": 212 + }, + "water_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 336, + "y": 338 + }, + "wetland_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 844, + "y": 212 + }, + "wetland_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 378, + "y": 338 + }, + "wheelchair_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 878, + "y": 212 + }, + "wheelchair_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 420, + "y": 338 + }, + "zoo_11": { + "height": 34, + "pixelRatio": 2, + "width": 34, + "x": 912, + "y": 212 + }, + "zoo_15": { + "height": 42, + "pixelRatio": 2, + "width": 42, + "x": 462, + "y": 338 + } +} \ No newline at end of file diff --git a/style/sprite@2x.png b/style/sprite@2x.png new file mode 100644 index 0000000..9458527 Binary files /dev/null and b/style/sprite@2x.png differ