4 #include "map/textpathitem.h"
5 #include "map/textpointitem.h"
6 #include "map/bitmapline.h"
8 #include "map/hillshading.h"
12 #include "rastertile.h"
16 #define TEXT_EXTENT 160
17 #define ICON_PADDING 2
20 (rect.size().width() * rect.size().height())
22 #define HIDPI_IMG(dir, basename, ratio) \
24 ? QImage(dir "/" basename "@2x.png") \
25 : QImage(dir "/" basename ".png"))
30 static const QColor
textColor(Qt::black
);
31 static const QColor
haloColor(Qt::white
);
32 static const QColor
shieldColor(Qt::white
);
33 static const QColor
shieldBgColor1(0xdd, 0x3e, 0x3e);
34 static const QColor
shieldBgColor2(0x37, 0x99, 0x47);
35 static const QColor
shieldBgColor3(0x4a, 0x7f, 0xc1);
37 static const QColor
*shieldBgColor(Shield::Type type
)
40 case Shield::USInterstate
:
42 return &shieldBgColor1
;
43 case Shield::USShield
:
45 return &shieldBgColor2
;
48 return &shieldBgColor3
;
54 static int minShieldZoom(Shield::Type type
)
57 case Shield::USInterstate
:
60 case Shield::USShield
:
71 static qreal
area(const QVector
<QPointF
> &polygon
)
75 for (int i
= 0; i
< polygon
.size(); i
++) {
76 int j
= (i
+ 1) % polygon
.size();
77 area
+= polygon
.at(i
).x() * polygon
.at(j
).y();
78 area
-= polygon
.at(i
).y() * polygon
.at(j
).x();
85 static QPointF
centroid(const QVector
<QPointF
> &polygon
)
88 qreal factor
= 1.0 / (6.0 * area(polygon
));
90 for (int i
= 0; i
< polygon
.size(); i
++) {
91 int j
= (i
+ 1) % polygon
.size();
92 qreal f
= (polygon
.at(i
).x() * polygon
.at(j
).y() - polygon
.at(j
).x()
94 cx
+= (polygon
.at(i
).x() + polygon
.at(j
).x()) * f
;
95 cy
+= (polygon
.at(i
).y() + polygon
.at(j
).y()) * f
;
98 return QPointF(cx
* factor
, cy
* factor
);
101 static bool rectNearPolygon(const QPolygonF
&polygon
, const QRectF
&rect
)
103 return (polygon
.boundingRect().contains(rect
)
104 && (polygon
.containsPoint(rect
.topLeft(), Qt::OddEvenFill
)
105 || polygon
.containsPoint(rect
.topRight(), Qt::OddEvenFill
)
106 || polygon
.containsPoint(rect
.bottomLeft(), Qt::OddEvenFill
)
107 || polygon
.containsPoint(rect
.bottomRight(), Qt::OddEvenFill
)));
110 const QFont
*RasterTile::poiFont(Style::FontSize size
, int zoom
, bool extended
)
113 size
= Style::Normal
;
121 return _data
->style()->font(Style::ExtraSmall
);
125 void RasterTile::ll2xy(QList
<MapData::Poly
> &polys
)
127 for (int i
= 0; i
< polys
.size(); i
++) {
128 MapData::Poly
&poly
= polys
[i
];
129 for (int j
= 0; j
< poly
.points
.size(); j
++) {
130 QPointF
&p
= poly
.points
[j
];
131 p
= ll2xy(Coordinates(p
.x(), p
.y()));
136 void RasterTile::ll2xy(QList
<MapData::Point
> &points
)
138 for (int i
= 0; i
< points
.size(); i
++) {
139 QPointF
p(ll2xy(points
.at(i
).coordinates
));
140 points
[i
].coordinates
= Coordinates(p
.x(), p
.y());
144 void RasterTile::drawPolygons(QPainter
*painter
,
145 const QList
<MapData::Poly
> &polygons
)
147 QCache
<const LBLFile
*, SubFile::Handle
> hc(16);
149 for (int n
= 0; n
< _data
->style()->drawOrder().size(); n
++) {
150 for (int i
= 0; i
< polygons
.size(); i
++) {
151 const MapData::Poly
&poly
= polygons
.at(i
);
152 if (poly
.type
!= _data
->style()->drawOrder().at(n
))
155 if (poly
.raster
.isValid()) {
156 RectC
r(poly
.raster
.rect());
157 QPointF
tl(ll2xy(r
.topLeft()));
158 QPointF
br(ll2xy(r
.bottomRight()));
159 QSizeF
size(QRectF(tl
, br
).size());
162 SubFile::Handle
*hdl
= hc
.object(poly
.raster
.lbl());
164 hdl
= new SubFile::Handle(poly
.raster
.lbl());
167 QPixmap
pm(poly
.raster
.lbl()->image(*hdl
, poly
.raster
.id()));
169 hc
.insert(poly
.raster
.lbl(), hdl
);
170 qreal sx
= size
.width() / (qreal
)pm
.width();
171 qreal sy
= size
.height() / (qreal
)pm
.height();
174 painter
->scale(sx
, sy
);
175 painter
->drawPixmap(QPointF(tl
.x() / sx
, tl
.y() / sy
), pm
);
178 //painter->setPen(Qt::blue);
179 //painter->setBrush(Qt::NoBrush);
180 //painter->drawRect(QRectF(tl, br));
182 const Style::Polygon
&style
= _data
->style()->polygon(poly
.type
);
184 painter
->setPen(style
.pen());
185 painter
->setBrush(style
.brush());
186 painter
->drawPolygon(poly
.points
);
192 void RasterTile::drawLines(QPainter
*painter
, const QList
<MapData::Poly
> &lines
)
194 painter
->setBrush(Qt::NoBrush
);
196 for (int i
= 0; i
< lines
.size(); i
++) {
197 const MapData::Poly
&poly
= lines
.at(i
);
198 const Style::Line
&style
= _data
->style()->line(poly
.type
);
200 if (style
.background() == Qt::NoPen
)
203 painter
->setPen(style
.background());
204 painter
->drawPolyline(poly
.points
);
207 for (int i
= 0; i
< lines
.size(); i
++) {
208 const MapData::Poly
&poly
= lines
.at(i
);
209 const Style::Line
&style
= _data
->style()->line(poly
.type
);
211 if (!style
.img().isNull())
212 BitmapLine::draw(painter
, poly
.points
, style
.img());
213 else if (style
.foreground() != Qt::NoPen
) {
214 painter
->setPen(style
.foreground());
215 painter
->drawPolyline(poly
.points
);
220 void RasterTile::drawTextItems(QPainter
*painter
,
221 const QList
<TextItem
*> &textItems
)
223 for (int i
= 0; i
< textItems
.size(); i
++)
224 textItems
.at(i
)->paint(painter
);
227 static void removeDuplicitLabel(QList
<TextItem
*> &labels
, const QString
&text
,
228 const QRectF
&tileRect
)
230 for (int i
= 0; i
< labels
.size(); i
++) {
231 TextItem
*item
= labels
.at(i
);
232 if (tileRect
.contains(item
->boundingRect()) && *(item
->text()) == text
) {
240 void RasterTile::processPolygons(const QList
<MapData::Poly
> &polygons
,
241 QList
<TextItem
*> &textItems
)
244 QList
<TextItem
*> labels
;
246 for (int i
= 0; i
< polygons
.size(); i
++) {
247 const MapData::Poly
&poly
= polygons
.at(i
);
248 bool exists
= set
.contains(poly
.label
.text());
250 if (poly
.label
.text().isEmpty())
253 if (_zoom
<= 23 && (Style::isWaterArea(poly
.type
)
254 || Style::isMilitaryArea(poly
.type
)
255 || Style::isNatureReserve(poly
.type
))) {
256 const Style::Polygon
&style
= _data
->style()->polygon(poly
.type
);
257 TextPointItem
*item
= new TextPointItem(
258 centroid(poly
.points
).toPoint(), &poly
.label
.text(), poiFont(),
259 0, &style
.brush().color(), &haloColor
);
260 if (item
->isValid() && !item
->collides(textItems
)
261 && !item
->collides(labels
)
262 && !(exists
&& _rect
.contains(item
->boundingRect().toRect()))
263 && rectNearPolygon(poly
.points
, item
->boundingRect())) {
265 removeDuplicitLabel(labels
, poly
.label
.text(), _rect
);
267 set
.insert(poly
.label
.text());
274 textItems
.append(labels
);
277 void RasterTile::processLines(QList
<MapData::Poly
> &lines
,
278 QList
<TextItem
*> &textItems
, const QImage (&arrows
)[2])
280 std::stable_sort(lines
.begin(), lines
.end());
283 processStreetNames(lines
, textItems
, arrows
);
284 processShields(lines
, textItems
);
287 void RasterTile::processStreetNames(const QList
<MapData::Poly
> &lines
,
288 QList
<TextItem
*> &textItems
, const QImage (&arrows
)[2])
290 for (int i
= 0; i
< lines
.size(); i
++) {
291 const MapData::Poly
&poly
= lines
.at(i
);
292 const Style::Line
&style
= _data
->style()->line(poly
.type
);
294 if (style
.img().isNull() && style
.foreground() == Qt::NoPen
)
297 const QFont
*fnt
= _data
->style()->font(style
.text().size(),
299 const QColor
*color
= style
.text().color().isValid()
300 ? &style
.text().color() : 0;
301 const QColor
*hColor
= Style::isContourLine(poly
.type
) ? 0 : &haloColor
;
302 const QImage
*img
= poly
.oneway
303 ? Style::isWaterLine(poly
.type
)
304 ? &arrows
[WATER
] : &arrows
[ROAD
] : 0;
305 const QString
*label
= poly
.label
.text().isEmpty()
306 ? 0 : &poly
.label
.text();
308 if (!img
&& (!label
|| !fnt
))
311 TextPathItem
*item
= new TextPathItem(poly
.points
, label
, _rect
, fnt
,
313 if (item
->isValid() && !item
->collides(textItems
))
314 textItems
.append(item
);
319 TextPathItem
*item
= new TextPathItem(poly
.points
, 0, _rect
, 0,
321 if (item
->isValid() && !item
->collides(textItems
))
322 textItems
.append(item
);
330 void RasterTile::processShields(const QList
<MapData::Poly
> &lines
,
331 QList
<TextItem
*> &textItems
)
333 for (int type
= FIRST_SHIELD
; type
<= LAST_SHIELD
; type
++) {
334 if (minShieldZoom(static_cast<Shield::Type
>(type
)) > _zoom
)
337 QHash
<Shield
, QPolygonF
> shields
;
338 QHash
<Shield
, const Shield
*> sp
;
340 for (int i
= 0; i
< lines
.size(); i
++) {
341 const MapData::Poly
&poly
= lines
.at(i
);
342 const Shield
&shield
= poly
.label
.shield();
343 if (!shield
.isValid() || shield
.type() != type
344 || !Style::isMajorRoad(poly
.type
))
347 QPolygonF
&p
= shields
[shield
];
348 for (int j
= 0; j
< poly
.points
.size(); j
++)
349 p
.append(poly
.points
.at(j
));
351 sp
.insert(shield
, &shield
);
354 for (QHash
<Shield
, QPolygonF
>::const_iterator it
= shields
.constBegin();
355 it
!= shields
.constEnd(); ++it
) {
356 const QPolygonF
&p
= it
.value();
357 QRectF
rect(p
.boundingRect() & _rect
);
358 if (AREA(rect
) < AREA(QRect(0, 0, _rect
.width()/4, _rect
.width()/4)))
361 QMap
<qreal
, int> map
;
362 QPointF center
= rect
.center();
363 for (int j
= 0; j
< p
.size(); j
++) {
364 QLineF
l(p
.at(j
), center
);
365 map
.insert(l
.length(), j
);
368 QMap
<qreal
, int>::const_iterator jt
= map
.constBegin();
370 TextPointItem
*item
= new TextPointItem(
371 p
.at(jt
.value()).toPoint(), &(sp
.value(it
.key())->text()),
372 poiFont(), 0, &shieldColor
, 0, shieldBgColor(it
.key().type()));
376 if (!item
->collides(textItems
)
377 && _rect
.contains(item
->boundingRect().toRect())) {
381 if (++jt
== map
.constEnd())
383 item
->setPos(p
.at(jt
.value()).toPoint());
387 textItems
.append(item
);
394 void RasterTile::processPoints(QList
<MapData::Point
> &points
,
395 QList
<TextItem
*> &textItems
)
397 std::sort(points
.begin(), points
.end());
399 for (int i
= 0; i
< points
.size(); i
++) {
400 const MapData::Point
&point
= points
.at(i
);
401 const Style::Point
&style
= _data
->style()->point(point
.type
);
402 bool poi
= Style::isPOI(point
.type
);
404 const QString
*label
= point
.label
.text().isEmpty()
405 ? 0 : &(point
.label
.text());
406 const QImage
*img
= style
.img().isNull() ? 0 : &style
.img();
407 const QFont
*fnt
= poi
408 ? poiFont(style
.text().size(), _zoom
, point
.classLabel
)
409 : _data
->style()->font(style
.text().size());
410 const QColor
*color
= style
.text().color().isValid()
411 ? &style
.text().color() : &textColor
;
412 const QColor
*hcolor
= Style::isDepthPoint(point
.type
)
415 if ((!label
|| !fnt
) && !img
)
418 TextPointItem
*item
= new TextPointItem(QPoint(point
.coordinates
.lon(),
419 point
.coordinates
.lat()), label
, fnt
, img
, color
, hcolor
, 0,
421 if (item
->isValid() && !item
->collides(textItems
))
422 textItems
.append(item
);
428 void RasterTile::fetchData(QList
<MapData::Poly
> &polygons
,
429 QList
<MapData::Poly
> &lines
, QList
<MapData::Point
> &points
)
431 QPoint
ttl(_rect
.topLeft());
433 QRectF
polyRect(ttl
, QPointF(ttl
.x() + _rect
.width(), ttl
.y()
435 RectD
polyRectD(_transform
.img2proj(polyRect
.topLeft()),
436 _transform
.img2proj(polyRect
.bottomRight()));
437 _data
->polys(polyRectD
.toRectC(_proj
, 20), _zoom
,
440 QRectF
pointRect(QPointF(ttl
.x() - TEXT_EXTENT
, ttl
.y() - TEXT_EXTENT
),
441 QPointF(ttl
.x() + _rect
.width() + TEXT_EXTENT
, ttl
.y() + _rect
.height()
443 RectD
pointRectD(_transform
.img2proj(pointRect
.topLeft()),
444 _transform
.img2proj(pointRect
.bottomRight()));
445 _data
->points(pointRectD
.toRectC(_proj
, 20), _zoom
, &points
);
448 Matrix
RasterTile::elevation() const
450 Matrix
m(_rect
.height() + 2, _rect
.width() + 2);
452 int left
= _rect
.left() - 1;
453 int right
= _rect
.right() + 1;
454 int top
= _rect
.top() - 1;
455 int bottom
= _rect
.bottom() + 1;
458 for (int y
= top
; y
<= bottom
; y
++) {
459 for (int x
= left
; x
<= right
; x
++)
460 m
.m(y
- top
, x
- left
) = DEM::elevation(xy2ll(QPointF(x
, y
)));
467 void RasterTile::render()
469 QImage
img(_rect
.width() * _ratio
, _rect
.height() * _ratio
,
470 QImage::Format_ARGB32_Premultiplied
);
471 QList
<MapData::Poly
> polygons
;
472 QList
<MapData::Poly
> lines
;
473 QList
<MapData::Point
> points
;
474 QList
<TextItem
*> textItems
;
477 arrows
[ROAD
] = HIDPI_IMG(":/map", "arrow", _ratio
);
478 arrows
[WATER
] = HIDPI_IMG(":/map", "water-arrow", _ratio
);
480 fetchData(polygons
, lines
, points
);
485 processPoints(points
, textItems
);
486 processPolygons(polygons
, textItems
);
487 processLines(lines
, textItems
, arrows
);
489 img
.setDevicePixelRatio(_ratio
);
490 img
.fill(Qt::transparent
);
492 QPainter
painter(&img
);
493 painter
.setRenderHint(QPainter::SmoothPixmapTransform
);
494 painter
.setRenderHint(QPainter::Antialiasing
);
495 painter
.translate(-_rect
.x(), -_rect
.y());
497 drawPolygons(&painter
, polygons
);
498 if (_hillShading
&& _zoom
>= 18 && _zoom
<= 24)
499 painter
.drawImage(_rect
.x(), _rect
.y(), HillShading::render(elevation()));
500 drawLines(&painter
, lines
);
501 drawTextItems(&painter
, textItems
);
503 qDeleteAll(textItems
);
505 _pixmap
= QPixmap::fromImage(img
);
507 //painter.setPen(Qt::red);
508 //painter.setRenderHint(QPainter::Antialiasing, false);
509 //painter.drawRect(_rect);