Use SVG Maki icos instead of PNGs
[GPXSee.git] / src / map / ENC / rastertile.cpp
bloba269eee901f322ad43df68c52cf14e012ec4290e
1 #include <QtMath>
2 #include <QPainter>
3 #include "common/linec.h"
4 #include "map/bitmapline.h"
5 #include "map/textpathitem.h"
6 #include "map/rectd.h"
7 #include "style.h"
8 #include "rastertile.h"
10 using namespace ENC;
12 #define TEXT_EXTENT 160
13 #define TSSLPT_SIZE 0.005 /* ll */
15 typedef QSet<Coordinates> PointSet;
17 static const float C1 = 0.866025f; /* sqrt(3)/2 */
18 static const QColor tsslptPen = QColor("#eb49eb");
19 static const QColor tsslptBrush = QColor("#80eb49eb");
21 static const Style *style(qreal ratio)
23 static ENC::Style s(ratio);
24 return &s;
27 static double area(const QVector<Coordinates> &polygon)
29 double area = 0;
31 for (int i = 0; i < polygon.size() - 1; i++) {
32 const Coordinates &pi = polygon.at(i);
33 const Coordinates &pj = polygon.at(i+1);
34 area += pi.lon() * pj.lat();
35 area -= pi.lat() * pj.lon();
37 area /= 2.0;
39 return area;
42 static Coordinates centroid(const QVector<Coordinates> &polygon)
44 Q_ASSERT(polygon.size() > 3);
45 Q_ASSERT(polygon.first() == polygon.last());
47 double cx = 0, cy = 0;
48 double factor = 1.0 / (6.0 * area(polygon));
50 for (int i = 0; i < polygon.size() - 1; i++) {
51 const Coordinates &pi = polygon.at(i);
52 const Coordinates &pj = polygon.at(i+1);
53 double f = (pi.lon() * pj.lat() - pj.lon() * pi.lat());
54 cx += (pi.lon() + pj.lon()) * f;
55 cy += (pi.lat() + pj.lat()) * f;
58 return Coordinates(cx * factor, cy * factor);
61 static double angle(uint type, const QVariant &param)
63 uint bt = type>>16;
65 return (bt == RDOCAL || bt == I_RDOCAL || bt == CURENT)
66 ? 90 + param.toDouble() : NAN;
69 static bool showLabel(const QImage *img, const Range &range, int zoom, int type)
71 if (type>>16 == I_DISMAR)
72 return true;
74 int limit = (!range.size())
75 ? range.min() : range.min() + (range.size() + 1) / 2;
76 if ((img || (type>>16 == SOUNDG)) && (zoom < limit))
77 return false;
79 return true;
82 QPainterPath RasterTile::painterPath(const Polygon &polygon) const
84 QPainterPath path;
86 for (int i = 0; i < polygon.size(); i++) {
87 const QVector<Coordinates> &subpath = polygon.at(i);
89 QVector<QPointF> p;
90 p.reserve(subpath.size());
92 for (int j = 0; j < subpath.size(); j++) {
93 const Coordinates &c = subpath.at(j);
94 if (!c.isNull())
95 p.append(ll2xy(c));
97 path.addPolygon(p);
100 return path;
103 QPolygonF RasterTile::polyline(const QVector<Coordinates> &path) const
105 QPolygonF polygon;
106 polygon.reserve(path.size());
108 for (int i = 0; i < path.size(); i++)
109 polygon.append(ll2xy(path.at(i)));
111 return polygon;
114 QVector<QPolygonF> RasterTile::polylineM(const QVector<Coordinates> &path) const
116 QVector<QPolygonF> polys;
117 QPolygonF polygon;
118 bool mask = false;
120 polygon.reserve(path.size());
122 for (int i = 0; i < path.size(); i++) {
123 const Coordinates &c = path.at(i);
125 if (c.isNull()) {
126 if (mask)
127 mask = false;
128 else {
129 polys.append(polygon);
130 polygon.clear();
131 mask = true;
133 } else if (!mask)
134 polygon.append(ll2xy(c));
137 if (!polygon.isEmpty())
138 polys.append(polygon);
140 return polys;
143 QPolygonF RasterTile::tsslptArrow(const Coordinates &c, qreal angle) const
145 Coordinates t[3], r[4];
146 QPolygonF polygon;
148 t[0] = c;
149 t[1] = Coordinates(t[0].lon() - qCos(angle - M_PI/3) * TSSLPT_SIZE,
150 t[0].lat() - qSin(angle - M_PI/3) * TSSLPT_SIZE);
151 t[2] = Coordinates(t[0].lon() - qCos(angle - M_PI + M_PI/3) * TSSLPT_SIZE,
152 t[0].lat() - qSin(angle - M_PI + M_PI/3) * TSSLPT_SIZE);
154 LineC l(t[1], t[2]);
155 r[0] = l.pointAt(0.25);
156 r[1] = l.pointAt(0.75);
157 r[2] = Coordinates(r[0].lon() - C1 * TSSLPT_SIZE * qCos(angle - M_PI/2),
158 r[0].lat() - C1 * TSSLPT_SIZE * qSin(angle - M_PI/2));
159 r[3] = Coordinates(r[1].lon() - C1 * TSSLPT_SIZE * qCos(angle - M_PI/2),
160 r[1].lat() - C1 * TSSLPT_SIZE * qSin(angle - M_PI/2));
162 polygon << ll2xy(t[0]) << ll2xy(t[2]) << ll2xy(r[1]) << ll2xy(r[3])
163 << ll2xy(r[2]) << ll2xy(r[0]) << ll2xy(t[1]);
165 return polygon;
168 void RasterTile::drawArrows(QPainter *painter,
169 const QList<MapData::Poly> &polygons)
171 for (int i = 0; i < polygons.size(); i++) {
172 const MapData::Poly &poly = polygons.at(i);
174 if (poly.type()>>16 == TSSLPT) {
175 QPolygonF polygon(tsslptArrow(centroid(poly.path().first()),
176 deg2rad(180 - poly.param().toDouble())));
178 painter->setPen(QPen(tsslptPen, 1));
179 painter->setBrush(QBrush(tsslptBrush));
180 painter->drawPolygon(polygon);
185 void RasterTile::drawPolygons(QPainter *painter,
186 const QList<MapData::Poly> &polygons)
188 for (int n = 0; n < _style->drawOrder().size(); n++) {
189 for (int i = 0; i < polygons.size(); i++) {
190 const MapData::Poly &poly = polygons.at(i);
191 if (poly.type() != _style->drawOrder().at(n))
192 continue;
193 const Style::Polygon &style = _style->polygon(poly.type());
195 if (!style.img().isNull()) {
196 for (int i = 0; i < poly.path().size(); i++)
197 BitmapLine::draw(painter, polylineM(poly.path().at(i)),
198 style.img());
199 } else {
200 if (style.brush() != Qt::NoBrush) {
201 painter->setPen(Qt::NoPen);
202 painter->setBrush(style.brush());
203 painter->drawPath(painterPath(poly.path()));
205 if (style.pen() != Qt::NoPen) {
206 painter->setPen(style.pen());
207 for (int i = 0; i < poly.path().size(); i++) {
208 QVector<QPolygonF> outline(polylineM(poly.path().at(i)));
209 for (int j = 0; j < outline.size(); j++)
210 painter->drawPolyline(outline.at(j));
218 void RasterTile::drawLines(QPainter *painter, const QList<MapData::Line> &lines)
220 painter->setBrush(Qt::NoBrush);
222 for (int i = 0; i < lines.size(); i++) {
223 const MapData::Line &line = lines.at(i);
224 const Style::Line &style = _style->line(line.type());
226 if (!style.img().isNull()) {
227 BitmapLine::draw(painter, polyline(line.path()), style.img());
228 } else if (style.pen() != Qt::NoPen) {
229 painter->setPen(style.pen());
230 painter->drawPolyline(polyline(line.path()));
235 void RasterTile::drawTextItems(QPainter *painter,
236 const QList<TextItem*> &textItems)
238 for (int i = 0; i < textItems.size(); i++)
239 textItems.at(i)->paint(painter);
242 void RasterTile::processPolygons(const QList<MapData::Poly> &polygons,
243 QList<TextItem*> &textItems)
245 for (int i = 0; i < polygons.size(); i++) {
246 const MapData::Poly &poly = polygons.at(i);
247 uint type = poly.type()>>16;
249 if (!(type == HRBFAC || type == I_TRNBSN
250 || poly.type() == SUBTYPE(I_BERTHS, 6)))
251 continue;
252 const Style::Point &style = _style->point(poly.type());
253 const QImage *img = style.img().isNull() ? 0 : &style.img();
254 if (!img)
255 continue;
257 TextPointItem *item = new TextPointItem(
258 ll2xy(centroid(poly.path().first())).toPoint(),
259 0, 0, img, 0, 0, 0, 0);
260 if (item->isValid() && !item->collides(textItems))
261 textItems.append(item);
262 else
263 delete item;
267 void RasterTile::processPoints(QList<MapData::Point> &points,
268 QList<TextItem*> &textItems, QList<TextItem*> &lights)
270 PointSet lightsSet, signalsSet;
271 int i;
273 std::sort(points.begin(), points.end());
275 /* Lights & Signals */
276 for (i = 0; i < points.size(); i++) {
277 const MapData::Point &point = points.at(i);
278 if (point.type()>>16 == LIGHTS)
279 lightsSet.insert(point.pos());
280 else if (point.type()>>16 == FOGSIG)
281 signalsSet.insert(point.pos());
282 else
283 break;
286 /* Everything else */
287 for ( ; i < points.size(); i++) {
288 const MapData::Point &point = points.at(i);
289 QPoint pos(ll2xy(point.pos()).toPoint());
290 const Style::Point &style = _style->point(point.type());
292 const QString *label = point.label().isEmpty() ? 0 : &(point.label());
293 const QImage *img = style.img().isNull() ? 0 : &style.img();
294 const QFont *fnt = showLabel(img, _zoomRange, _zoom, point.type())
295 ? _style->font(style.textFontSize()) : 0;
296 const QColor *color = &style.textColor();
297 const QColor *hColor = style.haloColor().isValid()
298 ? &style.haloColor() : 0;
299 double rotate = angle(point.type(), point.param());
301 if ((!label || !fnt) && !img)
302 continue;
304 TextPointItem *item = new TextPointItem(pos, label, fnt, img, color,
305 hColor, 0, 2, rotate);
306 if (item->isValid() && !item->collides(textItems)) {
307 textItems.append(item);
308 if (lightsSet.contains(point.pos()))
309 lights.append(new TextPointItem(pos, 0, 0, _style->light(), 0,
310 0, 0, 0));
311 if (signalsSet.contains(point.pos()))
312 lights.append(new TextPointItem(pos, 0, 0, _style->signal(), 0,
313 0, 0, 0));
314 } else
315 delete item;
319 void RasterTile::processLines(const QList<MapData::Line> &lines,
320 QList<TextItem*> &textItems)
322 for (int i = 0; i < lines.size(); i++) {
323 const MapData::Line &line = lines.at(i);
324 const Style::Line &style = _style->line(line.type());
326 if (style.img().isNull() && style.pen() == Qt::NoPen)
327 continue;
328 if (line.label().isEmpty() || style.textFontSize() == Style::None)
329 continue;
331 const QFont *fnt = _style->font(style.textFontSize());
332 const QColor *color = &style.textColor();
334 TextPathItem *item = new TextPathItem(polyline(line.path()),
335 &line.label(), _rect, fnt, color, 0);
336 if (item->isValid() && !item->collides(textItems))
337 textItems.append(item);
338 else
339 delete item;
343 void RasterTile::fetchData(QList<MapData::Poly> &polygons,
344 QList<MapData::Line> &lines, QList<MapData::Point> &points)
346 QPoint ttl(_rect.topLeft());
348 QRectF polyRect(ttl, QPointF(ttl.x() + _rect.width(), ttl.y()
349 + _rect.height()));
350 RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
351 _transform.img2proj(polyRect.bottomRight()));
352 RectC polyRectC(polyRectD.toRectC(_proj, 20));
353 QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT, ttl.y() - TEXT_EXTENT),
354 QPointF(ttl.x() + _rect.width() + TEXT_EXTENT, ttl.y() + _rect.height()
355 + TEXT_EXTENT));
356 RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
357 _transform.img2proj(pointRect.bottomRight()));
358 RectC pointRectC(pointRectD.toRectC(_proj, 20));
360 if (_map) {
361 _map->lines(polyRectC, &lines);
362 _map->polygons(polyRectC, &polygons);
363 _map->points(pointRectC, &points);
364 } else {
365 _atlas->polys(polyRectC, &polygons, &lines);
366 _atlas->points(pointRectC, &points);
370 void RasterTile::render()
372 QList<MapData::Line> lines;
373 QList<MapData::Poly> polygons;
374 QList<MapData::Point> points;
375 QList<TextItem*> textItems, lights;
377 _pixmap.setDevicePixelRatio(_ratio);
378 _pixmap.fill(Qt::transparent);
380 fetchData(polygons, lines, points);
382 processPolygons(polygons, textItems);
383 processPoints(points, textItems, lights);
384 processLines(lines, textItems);
386 QPainter painter(&_pixmap);
387 painter.setRenderHint(QPainter::SmoothPixmapTransform);
388 painter.setRenderHint(QPainter::Antialiasing);
389 painter.translate(-_rect.x(), -_rect.y());
391 drawPolygons(&painter, polygons);
392 drawLines(&painter, lines);
393 drawArrows(&painter, polygons);
395 drawTextItems(&painter, lights);
396 drawTextItems(&painter, textItems);
398 qDeleteAll(textItems);
399 qDeleteAll(lights);
401 //painter.setPen(Qt::red);
402 //painter.setBrush(Qt::NoBrush);
403 //painter.drawRect(QRect(_rect.topLeft(), _pixmap.size()));
405 _valid = true;
408 RasterTile::RasterTile(const Projection &proj, const Transform &transform,
409 const MapData *data, int zoom, const Range &zoomRange, const QRect &rect,
410 qreal ratio) :
411 _proj(proj), _transform(transform), _map(data), _atlas(0), _zoom(zoom),
412 _zoomRange(zoomRange), _rect(rect), _ratio(ratio),
413 _pixmap(rect.width() * ratio, rect.height() * ratio), _valid(false)
415 _style = style(ratio);
418 RasterTile::RasterTile(const Projection &proj, const Transform &transform,
419 AtlasData *data, int zoom, const Range &zoomRange, const QRect &rect,
420 qreal ratio) :
421 _proj(proj), _transform(transform), _map(0), _atlas(data), _zoom(zoom),
422 _zoomRange(zoomRange), _rect(rect), _ratio(ratio),
423 _pixmap(rect.width() * ratio, rect.height() * ratio), _valid(false)
425 _style = style(ratio);