diff --git a/pbfplugin.pro b/pbfplugin.pro index 7badc33..7321506 100644 --- a/pbfplugin.pro +++ b/pbfplugin.pro @@ -19,7 +19,8 @@ HEADERS += src/pbfhandler.h \ src/textpathitem.h \ src/textpointitem.h \ src/font.h \ - src/textitem.h + src/textitem.h \ + src/sprites.h SOURCES += src/pbfplugin.cpp \ src/pbfhandler.cpp \ src/gzip.cpp \ @@ -30,7 +31,8 @@ SOURCES += src/pbfplugin.cpp \ src/function.cpp \ src/textpathitem.cpp \ src/textpointitem.cpp \ - src/font.cpp + src/font.cpp \ + src/sprites.cpp RESOURCES += pbfplugin.qrc unix:!macx{ diff --git a/pbfplugin.qrc b/pbfplugin.qrc index ed8fc7b..11c11d4 100644 --- a/pbfplugin.qrc +++ b/pbfplugin.qrc @@ -1,5 +1,7 @@ style/style.json + style/sprite.json + style/sprite.png diff --git a/src/pbf.cpp b/src/pbf.cpp index acc340e..971a441 100644 --- a/src/pbf.cpp +++ b/src/pbf.cpp @@ -116,7 +116,7 @@ static inline QPoint parameters(quint32 v1, quint32 v2) static void drawFeature(const Feature &feature, Style *style, int styleLayer, const QSizeF &factor, Tile &tile) { - if (!style->match(styleLayer, feature.tags())) + if (!style->match(tile.zoom(), styleLayer, feature.tags())) return; QPoint cursor; @@ -151,7 +151,7 @@ static void drawFeature(const Feature &feature, Style *style, int styleLayer, } } - style->drawFeature(styleLayer, path, feature.tags(), tile); + style->drawFeature(tile, styleLayer, path, feature.tags()); } static void drawLayer(const Layer &layer, Style *style, int styleLayer, @@ -163,7 +163,9 @@ static void drawLayer(const Layer &layer, Style *style, int styleLayer, QSizeF factor(tile.size().width() / scale.x() / (qreal)layer.data()->extent(), tile.size().height() / scale.y() / (qreal)layer.data()->extent()); - style->setPainter(styleLayer, tile); + style->setPainter(tile, styleLayer); + style->setTextProperties(tile, styleLayer); + for (int i = 0; i < layer.features().size(); i++) drawFeature(layer.features().at(i), style, styleLayer, factor, tile); } @@ -177,9 +179,8 @@ bool PBF::render(const QByteArray &data, int zoom, Style *style, return false; } - Tile t(image, scale); + Tile t(image, zoom, scale); - style->setZoom(zoom); style->drawBackground(t); // Prepare source layers diff --git a/src/sprites.cpp b/src/sprites.cpp new file mode 100644 index 0000000..69e8fc0 --- /dev/null +++ b/src/sprites.cpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include "sprites.h" + + +/* + 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 QImage *img = new QImage(fileName); + return *img; +} + +Sprites::Sprite::Sprite(const QJsonObject &json) +{ + int x, y, width, height; + + if (json.contains("x") && json["x"].isDouble()) + x = json["x"].toInt(); + else + return; + if (json.contains("y") && json["y"].isDouble()) + y = json["y"].toInt(); + else + return; + if (json.contains("width") && json["width"].isDouble()) + width = json["width"].toInt(); + else + return; + if (json.contains("height") && json["height"].isDouble()) + height = json["height"].toInt(); + else + return; + + _rect = QRect(x, y, width, height); +} + +bool Sprites::load(const QString &jsonFile, const QString &imageFile) +{ + _imageFile = imageFile; + + QFile file(jsonFile); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCritical() << jsonFile << ": error opening file"; + return false; + } + QByteArray ba(file.readAll()); + + QJsonParseError error; + QJsonDocument doc(QJsonDocument::fromJson(ba, &error)); + if (doc.isNull()) { + qCritical() << jsonFile << ":" << error.errorString(); + return false; + } + + QJsonObject json(doc.object()); + for (QJsonObject::const_iterator it = json.constBegin(); + it != json.constEnd(); it++) { + QJsonValue val(*it); + if (val.isObject()) + _sprites.insert(it.key(), Sprite(val.toObject())); + else + qWarning() << it.key() << ": invalid sprite definition"; + } + + return true; +} + +QImage Sprites::icon(const QString &name) const +{ + if (_imageFile.isNull()) + return QImage(); + const QImage &img = atlas(_imageFile); + if (img.isNull()) + return QImage(); + + QMap::const_iterator it = _sprites.find(name); + if (it == _sprites.constEnd()) + return QImage(); + + if (!img.rect().contains(it->rect())) { + qWarning() << it->rect() << ": invalid sprite rect"; + return QImage(); + } + + return img.copy(it->rect()); +} diff --git a/src/sprites.h b/src/sprites.h new file mode 100644 index 0000000..a029f58 --- /dev/null +++ b/src/sprites.h @@ -0,0 +1,29 @@ +#ifndef SPRITES_H +#define SPRITES_H + +#include +#include +#include + +class Sprites +{ +public: + bool load(const QString &jsonFile, const QString &imageFile); + + QImage icon(const QString &name) const; + +private: + class Sprite { + public: + Sprite(const QJsonObject &json); + const QRect &rect() const {return _rect;} + + private: + QRect _rect; + }; + + QMap _sprites; + QString _imageFile; +}; + +#endif // SPRITES_H diff --git a/src/style.cpp b/src/style.cpp index 87b7981..c591f98 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include "text.h" #include "color.h" @@ -168,6 +170,32 @@ bool Style::Layer::Filter::match(const QVariantHash &tags) const } } +Style::Layer::Template::Template(const QString &str) : _field(str) +{ + int pos = 0; + + while ((pos = _rx.indexIn(_field, pos)) != -1) { + QString match = _rx.capturedTexts().first(); + _keys.append(match.mid(1, match.size() - 2)); + pos += _rx.matchedLength(); + } +} + +QString Style::Layer::Template::value(const QVariantHash &tags) const +{ + QString text(_field); + + for (int i = 0; i < _keys.size(); i++) { + const QString &key = _keys.at(i); + const QVariant val = tags.value(key); + text.replace(QString("{%1}").arg(key), val.toString()); + } + + return text; +} + +QRegExp Style::Layer::Template::_rx = QRegExp("\\{[^\\}]*\\}"); + Style::Layer::Paint::Paint(const QJsonObject &json) : _fillOpacity(1.0), _lineOpacity(1.0), _lineWidth(1.0) { @@ -275,7 +303,7 @@ bool Style::Layer::Paint::antialias(Layer::Type type, int zoom) const Style::Layer::Layout::Layout(const QJsonObject &json) : _textSize(16), _textMaxWidth(10), _textMaxAngle(45), _lineCap(Qt::FlatCap), _lineJoin(Qt::MiterJoin), _font("Open Sans"), _capitalize(false), - _viewportAlignment(false) + _viewportAlignment(false), _textAnchor(Text::Center) { // line if (json.contains("line-cap") && json["line-cap"].isString()) { @@ -292,17 +320,8 @@ Style::Layer::Layout::Layout(const QJsonObject &json) } // text - if (!(json.contains("text-field") && json["text-field"].isString())) - return; - _textField = json["text-field"].toString(); - - QRegExp rx("\\{[^\\}]*\\}"); - int pos = 0; - while ((pos = rx.indexIn(_textField, pos)) != -1) { - QString match = rx.capturedTexts().first(); - _keys.append(match.mid(1, match.size() - 2)); - pos += rx.matchedLength(); - } + if (json.contains("text-field") && json["text-field"].isString()) + _text = Template(json["text-field"].toString()); jsonFloat(json, "text-size", _textSize); jsonFloat(json, "text-max-width", _textMaxWidth); @@ -311,11 +330,27 @@ Style::Layer::Layout::Layout(const QJsonObject &json) _font = Font::fromJsonArray(json["text-font"].toArray()); if (json.contains("text-transform") && json["text-transform"].isString()) _capitalize = json["text-transform"].toString() == "uppercase"; - if (json.contains("text-rotation-alignment") && json["text-rotation-alignment"].isString()) if (json["text-rotation-alignment"].toString() == "viewport") _viewportAlignment = true; + if (json.contains("text-anchor") && json["text-anchor"].isString()) { + QString anchor(json["text-anchor"].toString()); + if (anchor == "center") + _textAnchor = Text::Center; + else if (anchor == "left") + _textAnchor = Text::Left; + else if (anchor == "right") + _textAnchor = Text::Right; + else if (anchor == "top") + _textAnchor = Text::Top; + else if (anchor == "bottom") + _textAnchor = Text::Bottom; + } + + // icon + if (json.contains("icon-image") && json["icon-image"].isString()) + _icon = Template(json["icon-image"].toString()); } QFont Style::Layer::Layout::font(int zoom) const @@ -374,58 +409,61 @@ bool Style::Layer::match(int zoom, const QVariantHash &tags) const return _filter.match(tags); } -void Style::Layer::setPathPainter(int zoom, Tile &tile) const +void Style::Layer::setPathPainter(Tile &tile) const { - QPen pen(_paint.pen(_type, zoom)); - QBrush brush(_paint.brush(_type, zoom)); + QPen pen(_paint.pen(_type, tile.zoom())); + QBrush brush(_paint.brush(_type, tile.zoom())); pen.setJoinStyle(_layout.lineJoin()); pen.setCapStyle(_layout.lineCap()); QPainter &p = tile.painter(); - p.setRenderHint(QPainter::Antialiasing, _paint.antialias(_type, zoom)); + p.setRenderHint(QPainter::Antialiasing, _paint.antialias(_type, tile.zoom())); p.setPen(pen); p.setBrush(brush); - p.setOpacity(_paint.opacity(_type, zoom)); + p.setOpacity(_paint.opacity(_type, tile.zoom())); } -void Style::Layer::setSymbolPainter(int zoom, Tile &tile) const +void Style::Layer::setSymbolPainter(Tile &tile) const { - QPen pen(_paint.pen(_type, zoom)); - QFont font(_layout.font(zoom)); + QPen pen(_paint.pen(_type, tile.zoom())); + QFont font(_layout.font(tile.zoom())); QPainter &p = tile.painter(); p.setPen(pen); p.setFont(font); } -void Style::Layer::addSymbol(int zoom, const QPainterPath &path, - const QVariantHash &tags, Tile &tile) const +void Style::Layer::setTextProperties(Tile &tile) const { - if (_layout.keys().isEmpty()) - return; + Text::Properties prop; + prop.maxWidth = _layout.maxTextWidth(tile.zoom()); + prop.maxAngle = _layout.maxTextAngle(tile.zoom()); + prop.anchor = _layout.textAnchor(); - QString text(_layout.field()); - for (int i = 0; i < _layout.keys().size(); i++) { - const QString &key = _layout.keys().at(i); - const QVariant val = tags.value(key); - text.replace(QString("{%1}").arg(key), _layout.capitalize() - ? val.toString().toUpper() : val.toString()); - } + tile.text().setProperties(prop); +} +void Style::Layer::addSymbol(Tile &tile, const QPainterPath &path, + const QVariantHash &tags, const Sprites &sprites) const +{ + QString text = _layout.text().value(tags); QString tt(text.trimmed()); if (tt.isEmpty()) return; + if (_layout.capitalize()) + tt = tt.toUpper(); + + QString icon = _layout.icon().value(tags); if (_layout.viewportAlignment()) - tile.text().addLabel(tt, path.elementAt(0), tile.painter(), - _layout.maxTextWidth(zoom), false); + tile.text().addLabel(tt, path.elementAt(0), tile.painter(), false, + sprites.icon(icon)); else if (path.elementCount() == 1 && path.elementAt(0).isMoveTo()) - tile.text().addLabel(tt, path.elementAt(0), tile.painter(), - _layout.maxTextWidth(zoom), true); + tile.text().addLabel(tt, path.elementAt(0), tile.painter(), true, + sprites.icon(icon)); else - tile.text().addLabel(tt, path, tile.painter(), - _layout.maxTextAngle(zoom)); + tile.text().addLabel(tt, path, tile.painter()); } bool Style::load(const QString &fileName) @@ -450,53 +488,69 @@ bool Style::load(const QString &fileName) QJsonArray layers = json["layers"].toArray(); for (int i = 0; i < layers.size(); i++) if (layers[i].isObject()) - _styles.append(Layer(layers[i].toObject())); + _layers.append(Layer(layers[i].toObject())); } - for (int i = 0; i < _styles.size(); i++) - _sourceLayers.append(_styles.at(i).sourceLayer()); + 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")); + if (QFileInfo::exists(spritesImg)) + _sprites.load(spritesJSON, spritesImg); + else + qCritical() << spritesImg << ": no such file"; + } return true; } -bool Style::match(int layer, const QVariantHash &tags) +bool Style::match(int zoom, int layer, const QVariantHash &tags) const { - return _styles.at(layer).match(_zoom, tags); + return _layers.at(layer).match(zoom, tags); } -void Style::setPainter(int layer, Tile &tile) +void Style::setTextProperties(Tile &tile, int layer) const { - const Layer &sl = _styles.at(layer); + const Layer &sl = _layers.at(layer); + sl.setTextProperties(tile); +} + +void Style::setPainter(Tile &tile, int layer) const +{ + const Layer &sl = _layers.at(layer); if (sl.isPath()) - sl.setPathPainter(_zoom, tile); + sl.setPathPainter(tile); else if (sl.isSymbol()) - sl.setSymbolPainter(_zoom, tile); + sl.setSymbolPainter(tile); } -void Style::drawFeature(int layer, const QPainterPath &path, - const QVariantHash &tags, Tile &tile) +void Style::drawFeature(Tile &tile, int layer, const QPainterPath &path, + const QVariantHash &tags) const { - const Layer &sl = _styles.at(layer); + const Layer &sl = _layers.at(layer); if (sl.isPath()) tile.painter().drawPath(path); else if (sl.isSymbol()) - sl.addSymbol(_zoom, path, tags, tile); + sl.addSymbol(tile, path, tags, _sprites); } -void Style::drawBackground(Tile &tile) +void Style::drawBackground(Tile &tile) const { QRectF rect(QPointF(0, 0), tile.size()); QPainterPath path; path.addRect(rect); - if (_styles.isEmpty()) { + if (_layers.isEmpty()) { tile.painter().setBrush(Qt::lightGray); tile.painter().setPen(Qt::NoPen); tile.painter().drawRect(rect); - } else if (_styles.first().isBackground()) { - _styles.first().setPathPainter(_zoom, tile); + } else if (_layers.first().isBackground()) { + _layers.first().setPathPainter(tile); tile.painter().drawPath(path); } diff --git a/src/style.h b/src/style.h index 1b84b25..1c292d3 100644 --- a/src/style.h +++ b/src/style.h @@ -9,7 +9,9 @@ #include #include #include +#include "text.h" #include "function.h" +#include "sprites.h" class QPainter; @@ -23,15 +25,15 @@ public: bool load(const QString &fileName); - void setZoom(int zoom) {_zoom = zoom;} - const QStringList &sourceLayers() const {return _sourceLayers;} - bool match(int layer, const QVariantHash &tags); - void drawBackground(Tile &tile); - void setPainter(int layer, Tile &tile); - void drawFeature(int layer, const QPainterPath &path, - const QVariantHash &tags, Tile &tile); + bool match(int zoom, int layer, const QVariantHash &tags) const; + + void drawBackground(Tile &tile) const; + void setPainter(Tile &tile, int layer) const; + void setTextProperties(Tile &tile, int layer) const; + void drawFeature(Tile &tile, int layer, const QPainterPath &path, + const QVariantHash &tags) const; private: class Layer { @@ -45,10 +47,11 @@ private: bool isSymbol() const {return (_type == Symbol);} bool match(int zoom, const QVariantHash &tags) const; - void setPathPainter(int zoom, Tile &tile) const; - void setSymbolPainter(int zoom, Tile &tile) const; - void addSymbol(int zoom, const QPainterPath &path, - const QVariantHash &tags, Tile &tile) const; + void setPathPainter(Tile &tile) const; + void setSymbolPainter(Tile &tile) const; + void setTextProperties(Tile &tile) const; + void addSymbol(Tile &tile, const QPainterPath &path, + const QVariantHash &tags, const Sprites &sprites) const; private: enum Type { @@ -80,12 +83,25 @@ private: QVector _filters; }; + class Template { + public: + Template() {} + Template(const QString &str); + + QString value(const QVariantHash &tags) const; + + private: + static QRegExp _rx; + QStringList _keys; + QString _field; + }; + class Layout { public: Layout() : _textSize(16), _textMaxWidth(10), _textMaxAngle(45), _lineCap(Qt::FlatCap), _lineJoin(Qt::MiterJoin), - _font("Open Sans"), _capitalize(false), _viewportAlignment(false) - {} + _font("Open Sans"), _capitalize(false), _viewportAlignment(false), + _textAnchor(Text::Center) {} Layout(const QJsonObject &json); bool capitalize() const {return _capitalize;} @@ -93,16 +109,17 @@ private: {return _textMaxWidth.value(zoom);} qreal maxTextAngle(int zoom) const {return _textMaxAngle.value(zoom);} - const QString &field() const {return _textField;} - const QStringList &keys() const {return _keys;} + const Template &text() const {return _text;} + const Template &icon() const {return _icon;} QFont font(int zoom) const; Qt::PenCapStyle lineCap() const {return _lineCap;} Qt::PenJoinStyle lineJoin() const {return _lineJoin;} bool viewportAlignment() const {return _viewportAlignment;} + Text::Anchor textAnchor() const {return _textAnchor;} private: - QStringList _keys; - QString _textField; + Template _text; + Template _icon; FunctionF _textSize; FunctionF _textMaxWidth; FunctionF _textMaxAngle; @@ -111,6 +128,7 @@ private: QFont _font; bool _capitalize; bool _viewportAlignment; + Text::Anchor _textAnchor; }; class Paint { @@ -144,9 +162,9 @@ private: Paint _paint; }; - int _zoom; - QVector _styles; + QVector _layers; QStringList _sourceLayers; + Sprites _sprites; }; #endif // STYLE_H diff --git a/src/text.cpp b/src/text.cpp index bbb5e52..4bcc202 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -150,28 +150,27 @@ void Text::render(QPainter *painter) const } void Text::addLabel(const QString &text, const QPointF &pos, - const QPainter &painter, qreal maxTextWidth, bool overlap) + const QPainter &painter, bool overlap, const QImage &icon) { if (text.isEmpty()) return; - TextPointItem *ti; - - ti = new TextPointItem(text, pos, painter.font(), maxTextWidth); + TextPointItem *ti = new TextPointItem(text, pos, painter.font(), + _properties, icon); if (!overlap && !_sceneRect.contains(ti->boundingRect())) { delete ti; return; } - ti->setPen(painter.pen()); addItem(ti); + QList ci = collidingItems(ti); for (int i = 0; i < ci.size(); i++) ci[i]->setVisible(false); } void Text::addLabel(const QString &text, const QPainterPath &path, - const QPainter &painter, qreal maxAngle) + const QPainter &painter) { if (path.isEmpty()) return; @@ -182,7 +181,7 @@ void Text::addLabel(const QString &text, const QPainterPath &path, if (textWidth > path.length()) return; - QPainterPath tp(textPath(path, textWidth, maxAngle, + QPainterPath tp(textPath(path, textWidth, _properties.maxAngle, painter.font().pixelSize() / 2, _sceneRect)); if (tp.isEmpty()) return; diff --git a/src/text.h b/src/text.h index 26c84a9..99c3107 100644 --- a/src/text.h +++ b/src/text.h @@ -1,20 +1,40 @@ #ifndef TEXT_H #define TEXT_H -#include "textitem.h" +#include + +class TextItem; class Text { public: + enum Anchor { + Center, + Left, + Right, + Top, + Bottom + }; + + struct Properties { + int maxWidth; + int maxAngle; + Anchor anchor; + }; + + Text(const QSize &size) : _sceneRect(QRectF(QPointF(0, 0), size)) {} ~Text(); - void render(QPainter *painter) const; + void setProperties(const Properties &prop) + {_properties = prop;} void addLabel(const QString &text, const QPointF &pos, - const QPainter &painter, qreal maxTextWidth, bool overlap); + const QPainter &painter, bool overlap, const QImage &icon); void addLabel(const QString &text, const QPainterPath &path, - const QPainter &painter, qreal maxAngle); + const QPainter &painter); + + void render(QPainter *painter) const; private: void addItem(TextItem *item) {_items.append(item);} @@ -22,6 +42,7 @@ private: QRectF _sceneRect; QList _items; + Properties _properties; }; #endif // TEXT_H diff --git a/src/textpointitem.cpp b/src/textpointitem.cpp index ac97bed..a663e43 100644 --- a/src/textpointitem.cpp +++ b/src/textpointitem.cpp @@ -1,13 +1,11 @@ -#include +#include #include #include "textpointitem.h" #define FLAGS (Qt::AlignCenter | Qt::TextWordWrap | Qt::TextDontClip) -#ifdef USE_EXACT_TEXT_RECT - -static QRectF textBoundingRect(const QString &str, const QFont &font, +static QRectF exactBoundingRect(const QString &str, const QFont &font, int maxTextWidth) { QFontMetrics fm(font); @@ -26,8 +24,7 @@ static QRectF textBoundingRect(const QString &str, const QFont &font, return br; } -#else // USE_EXACT_TEXT_RECT -static QRectF textBoundingRect(const QString &str, const QFont &font, +static QRectF fuzzyBoundingRect(const QString &str, const QFont &font, int maxTextWidth) { int limit = font.pixelSize() * maxTextWidth; @@ -67,23 +64,68 @@ static QRectF textBoundingRect(const QString &str, const QFont &font, return QRectF(0, 0, width, lines * lh); } -#endif // USE_EXACT_TEXT_RECT + + +QRectF TextPointItem::computeTextRect(BoundingRectFunction brf) const +{ + QRectF iconRect = _icon.isNull() ? QRectF() : _icon.rect(); + QRectF textRect = brf(text(), _font, _properties.maxWidth); + + switch (_properties.anchor) { + case Text::Center: + textRect.moveCenter(_pos); + break; + case Text::Left: + textRect.moveTopLeft(_pos - QPointF(-iconRect.width() / 2, + textRect.height() / 2)); + break; + case Text::Right: + textRect.moveTopRight(_pos - QPointF(iconRect.width() / 2, + textRect.height() / 2)); + break; + case Text::Bottom: + textRect.moveTopLeft(_pos - QPointF(textRect.width() / 2, + iconRect.height() / 2)); + break; + case Text::Top: + textRect.moveTopLeft(_pos - QPointF(textRect.width() / 2, + -iconRect.height() / 2)); + break; + } + + return textRect; +} TextPointItem::TextPointItem(const QString &text, const QPointF &pos, - const QFont &font, int maxTextWidth) : TextItem(text), _font(font) + const QFont &font, const Text::Properties &prop, const QImage &icon) + : TextItem(text), _pos(pos), _font(font), _icon(icon), _properties(prop) { - _boundingRect = textBoundingRect(text, font, maxTextWidth); + _boundingRect = computeTextRect(fuzzyBoundingRect); + + if (!_icon.isNull()) { + QRectF iconRect(_icon.rect()); + iconRect.moveCenter(pos); + _boundingRect |= iconRect; + } - _boundingRect.moveCenter(pos); _shape.addRect(_boundingRect); } void TextPointItem::paint(QPainter *painter) const { - painter->setFont(_font); - painter->setPen(_pen); - painter->drawText(_boundingRect, FLAGS, text()); - //painter->setPen(Qt::red); //painter->drawRect(_boundingRect); + + QRectF textRect; + + if (!_icon.isNull()) { + textRect = computeTextRect(exactBoundingRect); + painter->drawImage(_pos - QPointF(_icon.width() / 2, + _icon.height() / 2), _icon); + } else + textRect = computeTextRect(fuzzyBoundingRect); + + painter->setFont(_font); + painter->setPen(_pen); + painter->drawText(textRect, FLAGS, text()); } diff --git a/src/textpointitem.h b/src/textpointitem.h index 4a51fc3..a0f33f6 100644 --- a/src/textpointitem.h +++ b/src/textpointitem.h @@ -5,12 +5,13 @@ #include #include #include "textitem.h" +#include "text.h" class TextPointItem : public TextItem { public: TextPointItem(const QString &text, const QPointF &pos, const QFont &font, - int maxTextWidth); + const Text::Properties &prop, const QImage &icon); QRectF boundingRect() const {return _boundingRect;} QPainterPath shape() const {return _shape;} @@ -19,10 +20,16 @@ public: void setPen(const QPen &pen) {_pen = pen;} private: + typedef QRectF (*BoundingRectFunction)(const QString &, const QFont &, int); + QRectF computeTextRect(BoundingRectFunction brf) const; + + QPointF _pos; QPainterPath _shape; QRectF _boundingRect; QFont _font; QPen _pen; + QImage _icon; + Text::Properties _properties; }; #endif // TEXTPOINTITEM_H diff --git a/src/tile.h b/src/tile.h index 7aaa56b..1ba16e0 100644 --- a/src/tile.h +++ b/src/tile.h @@ -7,16 +7,18 @@ class Tile { public: - Tile(QImage *img, const QPointF &scale) - : _size(img->size()), _text(QSize(img->size().width() / scale.x(), - img->size().height() / scale.y())), _painter(img) + 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) {_painter.scale(scale.x(), scale.y());} + int zoom() const {return _zoom;} QSize size() const {return _size;} Text &text() {return _text;} QPainter &painter() {return _painter;} private: + int _zoom; QSize _size; Text _text; QPainter _painter; diff --git a/style/sprite.json b/style/sprite.json new file mode 100644 index 0000000..ffe03b5 --- /dev/null +++ b/style/sprite.json @@ -0,0 +1,1682 @@ +{ + "aerialway_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 284, + "y": 127 + }, + "aerialway_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 80, + "y": 190 + }, + "airfield_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 299, + "y": 127 + }, + "airfield_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 99, + "y": 190 + }, + "airport_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 314, + "y": 127 + }, + "airport_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 118, + "y": 190 + }, + "alcohol_shop_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 381, + "y": 210 + }, + "alcohol_shop_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 64, + "y": 0 + }, + "america_football_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 398, + "y": 210 + }, + "america_football_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 85, + "y": 0 + }, + "amusement_park_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 415, + "y": 210 + }, + "amusement_park_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 106, + "y": 0 + }, + "aquarium_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 432, + "y": 210 + }, + "aquarium_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 0, + "y": 64 + }, + "art_gallery_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 449, + "y": 210 + }, + "art_gallery_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 21, + "y": 64 + }, + "attraction_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 466, + "y": 210 + }, + "attraction_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 42, + "y": 64 + }, + "bakery_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 483, + "y": 210 + }, + "bakery_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 63, + "y": 64 + }, + "bank_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 227, + "y": 229 + }, + "bank_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 84, + "y": 64 + }, + "bar_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 244, + "y": 229 + }, + "bar_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 105, + "y": 64 + }, + "baseball_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 261, + "y": 229 + }, + "baseball_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 0, + "y": 85 + }, + "basketball_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 278, + "y": 229 + }, + "basketball_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 21, + "y": 85 + }, + "beer_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 295, + "y": 229 + }, + "beer_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 42, + "y": 85 + }, + "bicycle_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 312, + "y": 229 + }, + "bicycle_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 63, + "y": 85 + }, + "bicycle_rental_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 329, + "y": 229 + }, + "bicycle_rental_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 84, + "y": 85 + }, + "building_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 329, + "y": 127 + }, + "building_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 137, + "y": 190 + }, + "bus_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 344, + "y": 127 + }, + "bus_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 156, + "y": 190 + }, + "butcher_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 346, + "y": 229 + }, + "butcher_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 105, + "y": 85 + }, + "cafe_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 363, + "y": 229 + }, + "cafe_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 0, + "y": 106 + }, + "campsite_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 380, + "y": 229 + }, + "campsite_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 21, + "y": 106 + }, + "car_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 359, + "y": 127 + }, + "car_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 175, + "y": 190 + }, + "castle_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 397, + "y": 229 + }, + "castle_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 42, + "y": 106 + }, + "cemetery_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 414, + "y": 229 + }, + "cemetery_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 63, + "y": 106 + }, + "cinema_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 431, + "y": 229 + }, + "cinema_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 84, + "y": 106 + }, + "circle-stroked_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 374, + "y": 127 + }, + "circle-stroked_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 194, + "y": 190 + }, + "circle_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 389, + "y": 127 + }, + "circle_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 213, + "y": 190 + }, + "clothing_store_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 448, + "y": 229 + }, + "clothing_store_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 105, + "y": 106 + }, + "college_11": { + "height": 16, + "pixelRatio": 1, + "width": 16, + "x": 473, + "y": 106 + }, + "college_15": { + "height": 20, + "pixelRatio": 1, + "width": 20, + "x": 0, + "y": 190 + }, + "commercial_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 404, + "y": 127 + }, + "commercial_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 232, + "y": 190 + }, + "cricket_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 465, + "y": 229 + }, + "cricket_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 126, + "y": 64 + }, + "cross_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 419, + "y": 127 + }, + "cross_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 0, + "y": 210 + }, + "dam_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 434, + "y": 127 + }, + "dam_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 19, + "y": 210 + }, + "danger_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 482, + "y": 229 + }, + "danger_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 147, + "y": 64 + }, + "default_1": { + "height": 18, + "pixelRatio": 1, + "width": 18, + "x": 152, + "y": 229 + }, + "default_2": { + "height": 18, + "pixelRatio": 1, + "width": 25, + "x": 170, + "y": 229 + }, + "default_3": { + "height": 18, + "pixelRatio": 1, + "width": 32, + "x": 195, + "y": 229 + }, + "default_4": { + "height": 18, + "pixelRatio": 1, + "width": 39, + "x": 247, + "y": 210 + }, + "default_5": { + "height": 18, + "pixelRatio": 1, + "width": 45, + "x": 286, + "y": 210 + }, + "default_6": { + "height": 18, + "pixelRatio": 1, + "width": 50, + "x": 331, + "y": 210 + }, + "dentist_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 251, + "y": 190 + }, + "dentist_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 168, + "y": 64 + }, + "doctor_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 268, + "y": 190 + }, + "doctor_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 189, + "y": 64 + }, + "dog_park_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 285, + "y": 190 + }, + "dog_park_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 210, + "y": 64 + }, + "dot_10": { + "height": 10, + "pixelRatio": 1, + "width": 10, + "x": 499, + "y": 229 + }, + "dot_11": { + "height": 11, + "pixelRatio": 1, + "width": 11, + "x": 500, + "y": 210 + }, + "dot_9": { + "height": 9, + "pixelRatio": 1, + "width": 9, + "x": 477, + "y": 148 + }, + "drinking-water_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 302, + "y": 190 + }, + "drinking_water_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 231, + "y": 64 + }, + "embassy_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 319, + "y": 190 + }, + "embassy_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 126, + "y": 85 + }, + "entrance_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 449, + "y": 127 + }, + "entrance_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 38, + "y": 210 + }, + "fast_food_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 336, + "y": 190 + }, + "fast_food_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 147, + "y": 85 + }, + "ferry_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 464, + "y": 127 + }, + "ferry_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 57, + "y": 210 + }, + "fire-station_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 353, + "y": 190 + }, + "fire-station_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 168, + "y": 85 + }, + "fuel_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 479, + "y": 127 + }, + "fuel_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 76, + "y": 210 + }, + "garden_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 370, + "y": 190 + }, + "garden_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 189, + "y": 85 + }, + "gift_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 387, + "y": 190 + }, + "gift_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 210, + "y": 85 + }, + "golf_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 404, + "y": 190 + }, + "golf_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 231, + "y": 85 + }, + "grocery_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 421, + "y": 190 + }, + "grocery_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 126, + "y": 106 + }, + "hairdresser_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 438, + "y": 190 + }, + "hairdresser_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 147, + "y": 106 + }, + "harbor_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 494, + "y": 127 + }, + "harbor_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 95, + "y": 210 + }, + "heart_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 455, + "y": 190 + }, + "heart_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 168, + "y": 106 + }, + "heliport_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 252, + "y": 148 + }, + "heliport_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 114, + "y": 210 + }, + "hospital_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 472, + "y": 190 + }, + "hospital_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 189, + "y": 106 + }, + "ice_cream_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 489, + "y": 190 + }, + "ice_cream_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 210, + "y": 106 + }, + "industry_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 267, + "y": 148 + }, + "industry_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 133, + "y": 210 + }, + "information_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 252, + "y": 64 + }, + "information_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 231, + "y": 106 + }, + "laundry_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 269, + "y": 64 + }, + "laundry_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 127, + "y": 0 + }, + "library_11": { + "height": 16, + "pixelRatio": 1, + "width": 16, + "x": 489, + "y": 106 + }, + "library_15": { + "height": 20, + "pixelRatio": 1, + "width": 20, + "x": 20, + "y": 190 + }, + "lighthouse_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 286, + "y": 64 + }, + "lighthouse_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 148, + "y": 0 + }, + "lodging_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 303, + "y": 64 + }, + "lodging_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 169, + "y": 0 + }, + "marker_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 282, + "y": 148 + }, + "marker_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 152, + "y": 210 + }, + "monument_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 320, + "y": 64 + }, + "monument_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 190, + "y": 0 + }, + "mountain_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 337, + "y": 64 + }, + "mountain_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 211, + "y": 0 + }, + "museum_11": { + "height": 16, + "pixelRatio": 1, + "width": 16, + "x": 252, + "y": 127 + }, + "museum_15": { + "height": 20, + "pixelRatio": 1, + "width": 20, + "x": 40, + "y": 190 + }, + "music_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 354, + "y": 64 + }, + "music_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 232, + "y": 0 + }, + "park_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 371, + "y": 64 + }, + "park_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 0, + "y": 127 + }, + "parking_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 297, + "y": 148 + }, + "parking_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 171, + "y": 210 + }, + "parking_garage_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 312, + "y": 148 + }, + "parking_garage_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 190, + "y": 210 + }, + "pedestrian_polygon": { + "height": 64, + "pixelRatio": 1, + "width": 64, + "x": 0, + "y": 0 + }, + "pharmacy_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 388, + "y": 64 + }, + "pharmacy_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 21, + "y": 127 + }, + "picnic_site_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 405, + "y": 64 + }, + "picnic_site_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 42, + "y": 127 + }, + "pitch_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 422, + "y": 64 + }, + "pitch_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 63, + "y": 127 + }, + "place_of_worship_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 439, + "y": 64 + }, + "place_of_worship_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 84, + "y": 127 + }, + "playground_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 456, + "y": 64 + }, + "playground_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 105, + "y": 127 + }, + "police_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 473, + "y": 64 + }, + "police_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 126, + "y": 127 + }, + "post_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 490, + "y": 64 + }, + "post_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 147, + "y": 127 + }, + "prison_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 252, + "y": 85 + }, + "prison_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 168, + "y": 127 + }, + "railway_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 327, + "y": 148 + }, + "railway_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 209, + "y": 210 + }, + "railway_light_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 342, + "y": 148 + }, + "railway_light_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 228, + "y": 210 + }, + "railway_metro_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 357, + "y": 148 + }, + "railway_metro_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 0, + "y": 229 + }, + "ranger_station_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 269, + "y": 85 + }, + "ranger_station_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 189, + "y": 127 + }, + "religious_christian_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 286, + "y": 85 + }, + "religious_christian_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 210, + "y": 127 + }, + "religious_jewish_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 303, + "y": 85 + }, + "religious_jewish_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 231, + "y": 127 + }, + "religious_muslim_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 320, + "y": 85 + }, + "religious_muslim_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 0, + "y": 148 + }, + "restaurant_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 337, + "y": 85 + }, + "restaurant_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 21, + "y": 148 + }, + "roadblock_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 354, + "y": 85 + }, + "roadblock_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 42, + "y": 148 + }, + "rocket_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 371, + "y": 85 + }, + "rocket_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 63, + "y": 148 + }, + "school_11": { + "height": 16, + "pixelRatio": 1, + "width": 16, + "x": 268, + "y": 127 + }, + "school_15": { + "height": 20, + "pixelRatio": 1, + "width": 20, + "x": 60, + "y": 190 + }, + "shelter_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 388, + "y": 85 + }, + "shelter_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 84, + "y": 148 + }, + "shop_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 405, + "y": 85 + }, + "shop_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 105, + "y": 148 + }, + "skiing_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 422, + "y": 85 + }, + "skiing_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 126, + "y": 148 + }, + "soccer_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 439, + "y": 85 + }, + "soccer_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 147, + "y": 148 + }, + "square-stroke_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 372, + "y": 148 + }, + "square-stroke_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 19, + "y": 229 + }, + "square_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 387, + "y": 148 + }, + "square_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 38, + "y": 229 + }, + "stadium_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 456, + "y": 85 + }, + "stadium_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 168, + "y": 148 + }, + "star-stroke_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 402, + "y": 148 + }, + "star-stroke_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 57, + "y": 229 + }, + "star_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 417, + "y": 148 + }, + "star_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 76, + "y": 229 + }, + "suitcase_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 473, + "y": 85 + }, + "suitcase_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 189, + "y": 148 + }, + "sushi_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 490, + "y": 85 + }, + "sushi_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 210, + "y": 148 + }, + "swimming_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 252, + "y": 106 + }, + "swimming_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 231, + "y": 148 + }, + "telephone_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 269, + "y": 106 + }, + "telephone_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 0, + "y": 169 + }, + "tennis_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 286, + "y": 106 + }, + "tennis_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 21, + "y": 169 + }, + "theatre_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 303, + "y": 106 + }, + "theatre_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 42, + "y": 169 + }, + "toilet_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 320, + "y": 106 + }, + "toilet_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 63, + "y": 169 + }, + "town-hall_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 337, + "y": 106 + }, + "town-hall_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 84, + "y": 169 + }, + "triangle_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 432, + "y": 148 + }, + "triangle_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 95, + "y": 229 + }, + "triangle_stroked_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 447, + "y": 148 + }, + "triangle_stroked_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 114, + "y": 229 + }, + "veterinary_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 354, + "y": 106 + }, + "veterinary_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 105, + "y": 169 + }, + "volcano_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 371, + "y": 106 + }, + "volcano_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 126, + "y": 169 + }, + "warehouse_11": { + "height": 15, + "pixelRatio": 1, + "width": 15, + "x": 462, + "y": 148 + }, + "warehouse_15": { + "height": 19, + "pixelRatio": 1, + "width": 19, + "x": 133, + "y": 229 + }, + "waste_basket_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 388, + "y": 106 + }, + "waste_basket_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 147, + "y": 169 + }, + "water_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 405, + "y": 106 + }, + "water_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 168, + "y": 169 + }, + "wetland_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 422, + "y": 106 + }, + "wetland_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 189, + "y": 169 + }, + "wheelchair_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 439, + "y": 106 + }, + "wheelchair_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 210, + "y": 169 + }, + "zoo_11": { + "height": 17, + "pixelRatio": 1, + "width": 17, + "x": 456, + "y": 106 + }, + "zoo_15": { + "height": 21, + "pixelRatio": 1, + "width": 21, + "x": 231, + "y": 169 + } +} \ No newline at end of file diff --git a/style/sprite.png b/style/sprite.png new file mode 100644 index 0000000..762589d Binary files /dev/null and b/style/sprite.png differ