Added hillshading to vector maps
[GPXSee.git] / src / map / mapsforge / style.cpp
blobe229bb8ecb5f5f0426d6130fc65ba9098f9a1b07
1 #include <QFile>
2 #include <QXmlStreamReader>
3 #include <QUrl>
4 #include <QFileInfo>
5 #include <QImageReader>
6 #include "common/programpaths.h"
7 #include "style.h"
9 using namespace Mapsforge;
11 static QString resourcePath(const QString &src, const QString &dir)
13 QUrl url(src);
14 if (url.scheme().isEmpty())
15 return src;
16 else
17 return dir + "/" + url.toLocalFile();
20 static QImage image(const QString &path, int width, int height, int percent,
21 qreal ratio)
23 QImageReader ir(path, "svg");
25 if (ir.canRead()) {
26 QSize s(ir.size());
28 if (!height && !width) {
29 height = 20;
30 width = 20;
31 } else if (!width) {
32 width = s.height() / (s.height() / (double)height);
33 } else if (!height)
34 height = s.width() / (s.width() / (double)width);
36 if (percent != 100) {
37 width *= percent / 100.0;
38 height *= percent / 100.0;
41 ir.setScaledSize(QSize(width * ratio, height * ratio));
42 QImage img(ir.read());
43 img.setDevicePixelRatio(ratio);
44 return img;
45 } else
46 return QImage(path);
49 static QList<unsigned> keyList(const MapData &data, const QList<QByteArray> &in)
51 QList<unsigned> out;
53 for (int i = 0; i < in.size(); i++) {
54 if (in.at(i) == "*")
55 out.append(0);
56 else {
57 unsigned key = data.tagId(in.at(i));
58 if (key)
59 out.append(key);
63 return out;
66 static QList<QByteArray> valList(const QList<QByteArray> &in)
68 QList<QByteArray> out;
70 for (int i = 0; i < in.size(); i++) {
71 if (in.at(i) == "*")
72 out.append(QByteArray());
73 else
74 out.append(in.at(i));
77 return out;
80 const Style::Menu::Layer *Style::Menu::findLayer(const QString &id) const
82 for (int i = 0; i < _layers.size(); i++)
83 if (_layers.at(i).id() == id)
84 return &_layers.at(i);
86 qWarning("%s: layer not found", qPrintable(id));
88 return 0;
91 void Style::Menu::addCats(const Layer *layer, QSet<QString> &cats) const
93 if (!layer)
94 return;
96 if (!layer->parent().isNull())
97 addCats(findLayer(layer->parent()), cats);
99 for (int i = 0; i < layer->cats().size(); i++)
100 cats.insert(layer->cats().at(i));
102 for (int i = 0; i < layer->overlays().size(); i++) {
103 const Layer *overlay = findLayer(layer->overlays().at(i));
104 if (overlay && overlay->enabled())
105 addCats(overlay, cats);
109 QSet<QString> Style::Menu::cats() const
111 QSet<QString> c;
112 addCats(findLayer(_defaultvalue), c);
113 return c;
116 Style::Rule::Filter::Filter(const MapData &data, const QList<QByteArray> &keys,
117 const QList<QByteArray> &vals) : _neg(false)
119 _keys = keyList(data, keys);
121 QList<QByteArray> vc(vals);
122 if (vc.removeAll("~"))
123 _neg = true;
124 _vals = valList(vc);
127 bool Style::Rule::match(const QVector<MapData::Tag> &tags) const
129 for (int i = 0; i < _filters.size(); i++)
130 if (!_filters.at(i).match(tags))
131 return false;
133 return true;
136 bool Style::Rule::match(bool closed, const QVector<MapData::Tag> &tags) const
138 Closed cl = closed ? YesClosed : NoClosed;
140 if (_closed && cl != _closed)
141 return false;
143 for (int i = 0; i < _filters.size(); i++)
144 if (!_filters.at(i).match(tags))
145 return false;
147 return true;
150 bool Style::Rule::match(int zoom, bool closed,
151 const QVector<MapData::Tag> &tags) const
153 Closed cl = closed ? YesClosed : NoClosed;
155 if (!_zooms.contains(zoom))
156 return false;
157 if (_closed && cl != _closed)
158 return false;
160 for (int i = 0; i < _filters.size(); i++)
161 if (!_filters.at(i).match(tags))
162 return false;
164 return true;
167 bool Style::Rule::match(int zoom, const QVector<MapData::Tag> &tags) const
169 if (!_zooms.contains(zoom))
170 return false;
172 for (int i = 0; i < _filters.size(); i++)
173 if (!_filters.at(i).match(tags))
174 return false;
176 return true;
179 void Style::area(QXmlStreamReader &reader, const QString &dir, qreal ratio,
180 qreal baseStrokeWidth, const Rule &rule)
182 PathRender ri(rule, _paths.size() + _circles.size() + _hillShading.isValid());
183 const QXmlStreamAttributes &attr = reader.attributes();
184 QString file;
185 QColor fillColor;
186 int height = 0, width = 0, percent = 100;
187 bool ok;
189 ri._area = true;
190 if (attr.hasAttribute("fill"))
191 fillColor = QColor(attr.value("fill").toString());
192 if (attr.hasAttribute("stroke"))
193 ri._strokeColor = QColor(attr.value("stroke").toString());
194 if (attr.hasAttribute("stroke-width")) {
195 ri._strokeWidth = attr.value("stroke-width").toFloat(&ok)
196 * baseStrokeWidth;
197 if (!ok || ri._strokeWidth < 0) {
198 reader.raiseError("invalid stroke-width value");
199 return;
202 if (attr.hasAttribute("scale")) {
203 QString scale(attr.value("scale").toString());
204 if (scale == "all")
205 ri._scale = PathRender::Scale::All;
206 else if (scale == "none")
207 ri._scale = PathRender::Scale::None;
209 if (attr.hasAttribute("src"))
210 file = resourcePath(attr.value("src").toString(), dir);
211 if (attr.hasAttribute("symbol-height")) {
212 height = attr.value("symbol-height").toInt(&ok);
213 if (!ok || height < 0) {
214 reader.raiseError("invalid symbol-height value");
215 return;
218 if (attr.hasAttribute("symbol-width")) {
219 width = attr.value("symbol-width").toInt(&ok);
220 if (!ok || width < 0) {
221 reader.raiseError("invalid symbol-width value");
222 return;
225 if (attr.hasAttribute("symbol-percent")) {
226 percent = attr.value("symbol-percent").toInt(&ok);
227 if (!ok || percent < 0) {
228 reader.raiseError("invalid symbol-percent value");
229 return;
233 if (!file.isNull())
234 ri._brush = QBrush(image(file, width, height, percent, ratio));
235 else if (fillColor.isValid())
236 ri._brush = QBrush(fillColor);
238 if (ri.rule()._type == Rule::AnyType || ri.rule()._type == Rule::WayType)
239 _paths.append(ri);
241 reader.skipCurrentElement();
244 void Style::line(QXmlStreamReader &reader, qreal baseStrokeWidth,
245 const Rule &rule)
247 PathRender ri(rule, _paths.size() + _circles.size() + _hillShading.isValid());
248 const QXmlStreamAttributes &attr = reader.attributes();
249 bool ok;
251 ri._brush = Qt::NoBrush;
253 if (attr.hasAttribute("stroke"))
254 ri._strokeColor = QColor(attr.value("stroke").toString());
255 if (attr.hasAttribute("stroke-width")) {
256 ri._strokeWidth = attr.value("stroke-width").toFloat(&ok)
257 * baseStrokeWidth;
258 if (!ok || ri._strokeWidth < 0) {
259 reader.raiseError("invalid stroke-width value");
260 return;
263 if (attr.hasAttribute("stroke-dasharray")) {
264 QStringList l(attr.value("stroke-dasharray").toString().split(','));
265 ri._strokeDasharray.resize(l.size());
266 for (int i = 0; i < l.size(); i++) {
267 ri._strokeDasharray[i] = l.at(i).toDouble(&ok);
268 if (!ok || ri._strokeDasharray[i] < 0) {
269 reader.raiseError("invalid stroke-dasharray value");
270 return;
274 if (attr.hasAttribute("stroke-linecap")) {
275 QString cap(attr.value("stroke-linecap").toString());
276 if (cap == "butt")
277 ri._strokeCap = Qt::FlatCap;
278 else if (cap == "round")
279 ri._strokeCap = Qt::RoundCap;
280 else if (cap == "square")
281 ri._strokeCap = Qt::SquareCap;
283 if (attr.hasAttribute("stroke-linejoin")) {
284 QString join(attr.value("stroke-linejoin").toString());
285 if (join == "miter")
286 ri._strokeJoin = Qt::MiterJoin;
287 else if (join == "round")
288 ri._strokeJoin = Qt::RoundJoin;
289 else if (join == "bevel")
290 ri._strokeJoin = Qt::BevelJoin;
292 if (attr.hasAttribute("scale")) {
293 QString scale(attr.value("scale").toString());
294 if (scale == "all")
295 ri._scale = PathRender::Scale::All;
296 else if (scale == "none")
297 ri._scale = PathRender::Scale::None;
299 if (attr.hasAttribute("curve")) {
300 QString curve(attr.value("curve").toString());
301 if (curve == "cubic")
302 ri._curve = true;
304 if (attr.hasAttribute("dy")) {
305 ri._dy = attr.value("dy").toDouble(&ok) * baseStrokeWidth;
306 if (!ok) {
307 reader.raiseError("invalid dy value");
308 return;
312 if (ri.rule()._type == Rule::AnyType || ri.rule()._type == Rule::WayType)
313 _paths.append(ri);
315 reader.skipCurrentElement();
318 void Style::circle(QXmlStreamReader &reader, qreal baseStrokeWidth,
319 const Rule &rule)
321 CircleRender ri(rule, _paths.size() + _circles.size() + _hillShading.isValid());
322 const QXmlStreamAttributes &attr = reader.attributes();
323 bool ok;
324 QColor fillColor, strokeColor;
325 qreal strokeWidth = 0;
327 if (attr.hasAttribute("fill"))
328 fillColor = QColor(attr.value("fill").toString());
329 if (attr.hasAttribute("stroke"))
330 strokeColor = QColor(attr.value("stroke").toString());
331 if (attr.hasAttribute("stroke-width")) {
332 strokeWidth = attr.value("stroke-width").toFloat(&ok) * baseStrokeWidth;
333 if (!ok || strokeWidth < 0) {
334 reader.raiseError("invalid stroke-width value");
335 return;
338 if (attr.hasAttribute("radius")) {
339 ri._radius = attr.value("radius").toDouble(&ok) * baseStrokeWidth;
340 if (!ok || ri._radius <= 0) {
341 reader.raiseError("invalid radius value");
342 return;
344 } else {
345 reader.raiseError("missing radius");
346 return;
348 if (attr.hasAttribute("scale-radius")) {
349 if (attr.value("scale-radius").toString() == "true")
350 ri._scale = true;
353 ri._pen = (strokeColor.isValid() && strokeWidth > 0)
354 ? QPen(QBrush(strokeColor), strokeWidth) : Qt::NoPen;
355 ri._brush = fillColor.isValid() ? QBrush(fillColor) : Qt::NoBrush;
357 if (ri.rule()._type == Rule::AnyType || ri.rule()._type == Rule::NodeType)
358 _circles.append(ri);
360 reader.skipCurrentElement();
363 void Style::text(QXmlStreamReader &reader, const MapData &data, const Rule &rule,
364 QList<QList<TextRender>*> &lists)
366 TextRender ri(rule);
367 const QXmlStreamAttributes &attr = reader.attributes();
368 int fontSize = 9;
369 bool bold = false, italic = false;
370 QString fontFamily("Helvetica");
371 QFont::Capitalization capitalization = QFont::MixedCase;
372 bool ok;
374 if (attr.hasAttribute("k"))
375 ri._key = data.tagId(attr.value("k").toLatin1());
376 if (attr.hasAttribute("fill"))
377 ri._fillColor = QColor(attr.value("fill").toString());
378 if (attr.hasAttribute("stroke"))
379 ri._strokeColor = QColor(attr.value("stroke").toString());
380 if (attr.hasAttribute("stroke-width")) {
381 ri._strokeWidth = attr.value("stroke-width").toFloat(&ok);
382 if (!ok || ri._strokeWidth < 0) {
383 reader.raiseError("invalid stroke-width value");
384 return;
387 if (attr.hasAttribute("font-size")) {
388 fontSize = attr.value("font-size").toFloat(&ok);
389 if (!ok || fontSize < 0) {
390 reader.raiseError("invalid font-size value");
391 return;
394 if (attr.hasAttribute("font-style")) {
395 QString style(attr.value("font-style").toString());
396 if (style == "bold")
397 bold = true;
398 else if (style == "italic")
399 italic = true;
400 else if (style == "bold_italic") {
401 bold = true;
402 italic = true;
405 if (attr.hasAttribute("font-family")) {
406 QString family(attr.value("font-family").toString());
407 if (family == "monospace")
408 fontFamily = "Courier New";
409 else if (family == "serif")
410 fontFamily = "Times New Roman";
412 if (attr.hasAttribute("text-transform")) {
413 QString transform(attr.value("text-transform").toString());
414 if (transform == "uppercase")
415 capitalization = QFont::AllUppercase;
416 else if (transform == "lowercase")
417 capitalization = QFont::AllLowercase;
418 else if (transform == "capitalize")
419 capitalization = QFont::Capitalize;
421 if (attr.hasAttribute("priority")) {
422 ri._priority = attr.value("priority").toInt(&ok);
423 if (!ok) {
424 reader.raiseError("invalid priority value");
425 return;
428 if (attr.hasAttribute("symbol-id"))
429 ri._symbolId = attr.value("symbol-id").toString();
431 ri._font.setFamily(fontFamily);
432 ri._font.setPixelSize(fontSize);
433 ri._font.setBold(bold);
434 ri._font.setItalic(italic);
435 ri._font.setCapitalization(capitalization);
437 if (fontSize)
438 for (int i = 0; i < lists.size(); i++)
439 lists[i]->append(ri);
441 reader.skipCurrentElement();
444 void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,
445 const Rule &rule, QList<Symbol> &list)
447 Symbol ri(rule);
448 const QXmlStreamAttributes &attr = reader.attributes();
449 QString file;
450 int height = 0, width = 0, percent = 100;
451 bool ok;
453 if (attr.hasAttribute("src"))
454 file = resourcePath(attr.value("src").toString(), dir);
455 else {
456 reader.raiseError("missing src value");
457 return;
459 if (attr.hasAttribute("symbol-height")) {
460 height = attr.value("symbol-height").toInt(&ok);
461 if (!ok || height < 0) {
462 reader.raiseError("invalid symbol-height value");
463 return;
466 if (attr.hasAttribute("symbol-width")) {
467 width = attr.value("symbol-width").toInt(&ok);
468 if (!ok || width < 0) {
469 reader.raiseError("invalid symbol-width value");
470 return;
473 if (attr.hasAttribute("symbol-percent")) {
474 percent = attr.value("symbol-percent").toInt(&ok);
475 if (!ok || percent < 0) {
476 reader.raiseError("invalid symbol-percent value");
477 return;
480 if (attr.hasAttribute("priority")) {
481 ri._priority = attr.value("priority").toInt(&ok);
482 if (!ok) {
483 reader.raiseError("invalid priority value");
484 return;
487 if (attr.hasAttribute("rotate")) {
488 if (attr.value("rotate").toString() == "false")
489 ri._rotate = false;
491 if (attr.hasAttribute("id"))
492 ri._id = attr.value("id").toString();
494 ri._img = image(file, width, height, percent, ratio);
496 list.append(ri);
498 reader.skipCurrentElement();
501 void Style::rule(QXmlStreamReader &reader, const QString &dir,
502 const MapData &data, qreal ratio, qreal baseStrokeWidth,
503 const QSet<QString> &cats, const Rule &parent)
505 Rule r(parent);
506 const QXmlStreamAttributes &attr = reader.attributes();
507 bool ok;
509 if (attr.hasAttribute("cat")
510 && !cats.contains(attr.value("cat").toString())) {
511 reader.skipCurrentElement();
512 return;
515 if (attr.value("e").toString() == "way")
516 r.setType(Rule::WayType);
517 else if (attr.value("e").toString() == "node")
518 r.setType(Rule::NodeType);
520 if (attr.hasAttribute("zoom-min")) {
521 r.setMinZoom(attr.value("zoom-min").toInt(&ok));
522 if (!ok || r._zooms.min() < 0) {
523 reader.raiseError("invalid zoom-min value");
524 return;
527 if (attr.hasAttribute("zoom-max")) {
528 r.setMaxZoom(attr.value("zoom-max").toInt(&ok));
529 if (!ok || r._zooms.max() < 0) {
530 reader.raiseError("invalid zoom-max value");
531 return;
535 if (attr.hasAttribute("closed")) {
536 if (attr.value("closed").toString() == "yes")
537 r.setClosed(Rule::YesClosed);
538 else if (attr.value("closed").toString() == "no")
539 r.setClosed(Rule::NoClosed);
542 QList<QByteArray> keys(attr.value("k").toLatin1().split('|'));
543 QList<QByteArray> vals(attr.value("v").toLatin1().split('|'));
544 r.addFilter(Rule::Filter(data, keys, vals));
546 while (reader.readNextStartElement()) {
547 if (reader.name() == QLatin1String("rule"))
548 rule(reader, dir, data, ratio, baseStrokeWidth, cats, r);
549 else if (reader.name() == QLatin1String("area"))
550 area(reader, dir, ratio, baseStrokeWidth, r);
551 else if (reader.name() == QLatin1String("line"))
552 line(reader, baseStrokeWidth, r);
553 else if (reader.name() == QLatin1String("circle"))
554 circle(reader, baseStrokeWidth, r);
555 else if (reader.name() == QLatin1String("pathText")) {
556 QList<QList<TextRender>*> list;
557 list.append(&_pathLabels);
558 text(reader, data, r, list);
559 } else if (reader.name() == QLatin1String("caption")) {
560 QList<QList<TextRender>*> list;
561 if (r._type == Rule::WayType || r._type == Rule::AnyType)
562 list.append(&_areaLabels);
563 if (r._type == Rule::NodeType || r._type == Rule::AnyType)
564 list.append(&_pointLabels);
565 text(reader, data, r, list);
567 else if (reader.name() == QLatin1String("symbol"))
568 symbol(reader, dir, ratio, r, _symbols);
569 else if (reader.name() == QLatin1String("lineSymbol"))
570 symbol(reader, dir, ratio, r, _lineSymbols);
571 else
572 reader.skipCurrentElement();
576 void Style::hillshading(QXmlStreamReader &reader, const QSet<QString> &cats)
578 Rule r;
579 const QXmlStreamAttributes &attr = reader.attributes();
580 bool ok;
581 int layer = 5;
583 if (attr.hasAttribute("cat")
584 && !cats.contains(attr.value("cat").toString())) {
585 reader.skipCurrentElement();
586 return;
588 if (attr.hasAttribute("zoom-min")) {
589 r.setMinZoom(attr.value("zoom-min").toInt(&ok));
590 if (!ok || r._zooms.min() < 0) {
591 reader.raiseError("invalid zoom-min value");
592 return;
595 if (attr.hasAttribute("zoom-max")) {
596 r.setMaxZoom(attr.value("zoom-max").toInt(&ok));
597 if (!ok || r._zooms.max() < 0) {
598 reader.raiseError("invalid zoom-max value");
599 return;
602 if (attr.hasAttribute("layer")) {
603 layer = attr.value("layer").toInt(&ok);
604 if (!ok || layer < 0) {
605 reader.raiseError("invalid layer value");
606 return;
610 _hillShading = HillShadingRender(r, _paths.size() + _circles.size(), layer);
612 reader.skipCurrentElement();
615 QString Style::cat(QXmlStreamReader &reader)
617 const QXmlStreamAttributes &attr = reader.attributes();
619 if (!attr.hasAttribute("id")) {
620 reader.raiseError("Missing id attribute");
621 return QString();
624 QString id(attr.value("id").toString());
625 reader.skipCurrentElement();
627 return id;
630 Style::Menu::Layer Style::layer(QXmlStreamReader &reader)
632 const QXmlStreamAttributes &attr = reader.attributes();
633 if (!attr.hasAttribute("id")) {
634 reader.raiseError("Missing id attribute");
635 return Menu::Layer();
638 Menu::Layer l(attr.value("id").toString(),
639 attr.value("enabled").toString() == "true");
641 if (attr.hasAttribute("parent"))
642 l.setParent(attr.value("parent").toString());
644 while (reader.readNextStartElement()) {
645 if (reader.name() == QLatin1String("cat"))
646 l.addCat(cat(reader));
647 else if (reader.name() == QLatin1String("overlay"))
648 l.addOverlay(cat(reader));
649 else
650 reader.skipCurrentElement();
653 return l;
656 Style::Menu Style::stylemenu(QXmlStreamReader &reader)
658 const QXmlStreamAttributes &attr = reader.attributes();
659 if (!attr.hasAttribute("defaultvalue")) {
660 reader.raiseError("Missing defaultvalue attribute");
661 return Menu();
664 Style::Menu menu(attr.value("defaultvalue").toString());
666 while (reader.readNextStartElement()) {
667 if (reader.name() == QLatin1String("layer"))
668 menu.addLayer(layer(reader));
669 else
670 reader.skipCurrentElement();
673 return menu;
676 void Style::rendertheme(QXmlStreamReader &reader, const QString &dir,
677 const MapData &data, qreal ratio)
679 Rule r;
680 QSet<QString> cats;
681 qreal baseStrokeWidth = 1.0;
683 const QXmlStreamAttributes &attr = reader.attributes();
684 if (attr.hasAttribute("base-stroke-width")) {
685 bool ok;
686 baseStrokeWidth = attr.value("base-stroke-width").toFloat(&ok);
687 if (!ok || baseStrokeWidth < 0) {
688 reader.raiseError("invalid base-stroke-width value");
689 return;
693 while (reader.readNextStartElement()) {
694 if (reader.name() == QLatin1String("rule"))
695 rule(reader, dir, data, ratio, baseStrokeWidth, cats, r);
696 else if (reader.name() == QLatin1String("hillshading"))
697 hillshading(reader, cats);
698 else if (reader.name() == QLatin1String("stylemenu")) {
699 Menu menu(stylemenu(reader));
700 cats = menu.cats();
701 } else
702 reader.skipCurrentElement();
706 bool Style::loadXml(const QString &path, const MapData &data, qreal ratio)
708 QFile file(path);
709 if (!file.open(QFile::ReadOnly))
710 return false;
711 QXmlStreamReader reader(&file);
712 QFileInfo fi(path);
714 if (reader.readNextStartElement()) {
715 if (reader.name() == QLatin1String("rendertheme"))
716 rendertheme(reader, fi.absolutePath(), data, ratio);
717 else
718 reader.raiseError("Not a Mapsforge style file");
721 if (reader.error())
722 qWarning("%s:%lld %s", qPrintable(path), reader.lineNumber(),
723 qPrintable(reader.errorString()));
725 return !reader.error();
728 void Style::load(const MapData &data, qreal ratio)
730 QString path(ProgramPaths::renderthemeFile());
732 if (!QFileInfo::exists(path) || !loadXml(path, data, ratio))
733 loadXml(":/mapsforge/default.xml", data, ratio);
736 void Style::clear()
738 _paths.clear();
739 _circles.clear();
740 _pathLabels.clear();
741 _pointLabels.clear();
742 _areaLabels.clear();
743 _symbols.clear();
744 _lineSymbols.clear();
747 QList<const Style::PathRender *> Style::paths(int zoom, bool closed,
748 const QVector<MapData::Tag> &tags) const
750 QList<const PathRender*> ri;
752 for (int i = 0; i < _paths.size(); i++)
753 if (_paths.at(i).rule().match(zoom, closed, tags))
754 ri.append(&_paths.at(i));
756 return ri;
759 QList<const Style::CircleRender *> Style::circles(int zoom,
760 const QVector<MapData::Tag> &tags) const
762 QList<const CircleRender*> ri;
764 for (int i = 0; i < _circles.size(); i++)
765 if (_circles.at(i).rule().match(zoom, tags))
766 ri.append(&_circles.at(i));
768 return ri;
771 const Style::HillShadingRender *Style::hillShading(int zoom) const
773 return (_hillShading.isValid() && _hillShading.rule()._zooms.contains(zoom))
774 ? &_hillShading : 0;
777 QList<const Style::TextRender*> Style::pathLabels(int zoom) const
779 QList<const TextRender*> list;
781 for (int i = 0; i < _pathLabels.size(); i++)
782 if (_pathLabels.at(i).rule()._zooms.contains(zoom))
783 list.append(&_pathLabels.at(i));
785 return list;
788 QList<const Style::TextRender*> Style::pointLabels(int zoom) const
790 QList<const TextRender*> list;
792 for (int i = 0; i < _pointLabels.size(); i++)
793 if (_pointLabels.at(i).rule()._zooms.contains(zoom))
794 list.append(&_pointLabels.at(i));
796 return list;
799 QList<const Style::TextRender*> Style::areaLabels(int zoom) const
801 QList<const TextRender*> list;
803 for (int i = 0; i < _areaLabels.size(); i++)
804 if (_areaLabels.at(i).rule()._zooms.contains(zoom))
805 list.append(&_areaLabels.at(i));
807 return list;
810 QList<const Style::Symbol*> Style::pointSymbols(int zoom) const
812 QList<const Symbol*> list;
814 for (int i = 0; i < _symbols.size(); i++) {
815 const Symbol &symbol = _symbols.at(i);
816 const Rule &rule = symbol.rule();
817 if (rule._zooms.contains(zoom) && (rule._type == Rule::AnyType
818 || rule._type == Rule::NodeType))
819 list.append(&symbol);
822 return list;
825 QList<const Style::Symbol*> Style::lineSymbols(int zoom) const
827 QList<const Symbol*> list;
829 for (int i = 0; i < _lineSymbols.size(); i++) {
830 const Symbol &symbol = _lineSymbols.at(i);
831 if (symbol.rule()._zooms.contains(zoom))
832 list.append(&symbol);
835 return list;
838 QList<const Style::Symbol*> Style::areaSymbols(int zoom) const
840 QList<const Symbol*> list;
842 for (int i = 0; i < _symbols.size(); i++) {
843 const Symbol &symbol = _symbols.at(i);
844 const Rule &rule = symbol.rule();
845 if (rule._zooms.contains(zoom) && (rule._type == Rule::AnyType
846 || rule._type == Rule::WayType))
847 list.append(&symbol);
850 return list;
853 QPen Style::PathRender::pen(int zoom) const
855 if (_strokeColor.isValid()) {
856 qreal width = (_scale > None && zoom >= 12)
857 ? pow(1.5, zoom - 12) * _strokeWidth : _strokeWidth;
858 QPen p(QBrush(_strokeColor), width, Qt::SolidLine, _strokeCap,
859 _strokeJoin);
860 if (!_strokeDasharray.isEmpty()) {
861 QVector<qreal>pattern(_strokeDasharray);
862 for (int i = 0; i < _strokeDasharray.size(); i++) {
863 if (_scale > Stroke && zoom >= 12)
864 pattern[i] = (pow(1.5, zoom - 12) * pattern[i]);
865 // QPainter pattern is specified in units of the pens width!
866 pattern[i] /= width;
868 p.setDashPattern(pattern);
870 return p;
871 } else
872 return Qt::NoPen;
875 qreal Style::PathRender::dy(int zoom) const
877 return (_scale && zoom >= 12) ? pow(1.5, zoom - 12) * _dy : _dy;
880 qreal Style::CircleRender::radius(int zoom) const
882 return (_scale && zoom >= 12) ? pow(1.5, zoom - 12) * _radius : _radius;