2020-04-26 01:17:54 +02:00
|
|
|
#include <QFont>
|
|
|
|
#include <QPainter>
|
2021-07-19 22:51:00 +02:00
|
|
|
#include <QCache>
|
2024-02-22 21:34:34 +01:00
|
|
|
#include "common/dem.h"
|
2021-04-10 15:27:40 +02:00
|
|
|
#include "map/textpathitem.h"
|
|
|
|
#include "map/textpointitem.h"
|
2022-11-04 09:03:36 +01:00
|
|
|
#include "map/bitmapline.h"
|
2023-05-19 19:33:22 +02:00
|
|
|
#include "map/rectd.h"
|
2024-02-21 08:49:09 +01:00
|
|
|
#include "map/hillshading.h"
|
2020-04-26 01:17:54 +02:00
|
|
|
#include "style.h"
|
2021-01-27 21:18:06 +01:00
|
|
|
#include "lblfile.h"
|
2020-04-26 01:17:54 +02:00
|
|
|
#include "rastertile.h"
|
|
|
|
|
2021-04-10 15:27:40 +02:00
|
|
|
using namespace IMG;
|
2020-04-26 01:17:54 +02:00
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
#define TEXT_EXTENT 160
|
2022-03-26 17:20:16 +01:00
|
|
|
#define ICON_PADDING 2
|
|
|
|
|
2020-04-26 01:17:54 +02:00
|
|
|
#define AREA(rect) \
|
|
|
|
(rect.size().width() * rect.size().height())
|
|
|
|
|
2024-02-12 22:38:42 +01:00
|
|
|
#define HIDPI_IMG(dir, basename, ratio) \
|
|
|
|
(((ratio) > 1.0) \
|
|
|
|
? QImage(dir "/" basename "@2x.png") \
|
|
|
|
: QImage(dir "/" basename ".png"))
|
|
|
|
|
2023-08-03 00:39:56 +02:00
|
|
|
#define ROAD 0
|
|
|
|
#define WATER 1
|
|
|
|
|
2021-04-18 12:20:07 +02:00
|
|
|
static const QColor textColor(Qt::black);
|
|
|
|
static const QColor haloColor(Qt::white);
|
2020-04-26 01:17:54 +02:00
|
|
|
static const QColor shieldColor(Qt::white);
|
2024-02-09 02:29:51 +01:00
|
|
|
static const QColor shieldBgColor1(0xdd, 0x3e, 0x3e);
|
|
|
|
static const QColor shieldBgColor2(0x37, 0x99, 0x47);
|
|
|
|
static const QColor shieldBgColor3(0x4a, 0x7f, 0xc1);
|
2020-04-26 01:17:54 +02:00
|
|
|
|
2021-04-10 15:27:40 +02:00
|
|
|
static const QColor *shieldBgColor(Shield::Type type)
|
2020-04-26 01:17:54 +02:00
|
|
|
{
|
|
|
|
switch (type) {
|
2021-04-10 15:27:40 +02:00
|
|
|
case Shield::USInterstate:
|
|
|
|
case Shield::Hbox:
|
2020-04-26 01:17:54 +02:00
|
|
|
return &shieldBgColor1;
|
2021-04-10 15:27:40 +02:00
|
|
|
case Shield::USShield:
|
|
|
|
case Shield::Box:
|
2020-04-26 01:17:54 +02:00
|
|
|
return &shieldBgColor2;
|
2021-04-10 15:27:40 +02:00
|
|
|
case Shield::USRound:
|
|
|
|
case Shield::Oval:
|
2020-04-26 01:17:54 +02:00
|
|
|
return &shieldBgColor3;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-10 15:27:40 +02:00
|
|
|
static int minShieldZoom(Shield::Type type)
|
2020-04-26 01:17:54 +02:00
|
|
|
{
|
|
|
|
switch (type) {
|
2021-04-10 15:27:40 +02:00
|
|
|
case Shield::USInterstate:
|
|
|
|
case Shield::Hbox:
|
2020-04-26 01:17:54 +02:00
|
|
|
return 17;
|
2021-04-10 15:27:40 +02:00
|
|
|
case Shield::USShield:
|
|
|
|
case Shield::Box:
|
2020-04-26 01:17:54 +02:00
|
|
|
return 19;
|
2021-04-10 15:27:40 +02:00
|
|
|
case Shield::USRound:
|
|
|
|
case Shield::Oval:
|
2020-04-26 01:17:54 +02:00
|
|
|
return 20;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static qreal area(const QVector<QPointF> &polygon)
|
|
|
|
{
|
|
|
|
qreal area = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < polygon.size(); i++) {
|
|
|
|
int j = (i + 1) % polygon.size();
|
|
|
|
area += polygon.at(i).x() * polygon.at(j).y();
|
|
|
|
area -= polygon.at(i).y() * polygon.at(j).x();
|
|
|
|
}
|
|
|
|
area /= 2.0;
|
|
|
|
|
2021-04-11 11:37:43 +02:00
|
|
|
return area;
|
2020-04-26 01:17:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static QPointF centroid(const QVector<QPointF> &polygon)
|
|
|
|
{
|
|
|
|
qreal cx = 0, cy = 0;
|
|
|
|
qreal factor = 1.0 / (6.0 * area(polygon));
|
|
|
|
|
|
|
|
for (int i = 0; i < polygon.size(); i++) {
|
|
|
|
int j = (i + 1) % polygon.size();
|
|
|
|
qreal f = (polygon.at(i).x() * polygon.at(j).y() - polygon.at(j).x()
|
|
|
|
* polygon.at(i).y());
|
|
|
|
cx += (polygon.at(i).x() + polygon.at(j).x()) * f;
|
|
|
|
cy += (polygon.at(i).y() + polygon.at(j).y()) * f;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QPointF(cx * factor, cy * factor);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool rectNearPolygon(const QPolygonF &polygon, const QRectF &rect)
|
|
|
|
{
|
|
|
|
return (polygon.boundingRect().contains(rect)
|
|
|
|
&& (polygon.containsPoint(rect.topLeft(), Qt::OddEvenFill)
|
|
|
|
|| polygon.containsPoint(rect.topRight(), Qt::OddEvenFill)
|
|
|
|
|| polygon.containsPoint(rect.bottomLeft(), Qt::OddEvenFill)
|
|
|
|
|| polygon.containsPoint(rect.bottomRight(), Qt::OddEvenFill)));
|
|
|
|
}
|
|
|
|
|
2023-12-21 01:13:36 +01:00
|
|
|
const QFont *RasterTile::poiFont(Style::FontSize size, int zoom, bool extended)
|
|
|
|
{
|
|
|
|
if (zoom > 25)
|
|
|
|
size = Style::Normal;
|
|
|
|
else if (extended)
|
|
|
|
size = Style::None;
|
|
|
|
|
|
|
|
switch (size) {
|
|
|
|
case Style::None:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return _data->style()->font(Style::ExtraSmall);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-15 22:37:49 +01:00
|
|
|
void RasterTile::ll2xy(QList<MapData::Poly> &polys)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < polys.size(); i++) {
|
|
|
|
MapData::Poly &poly = polys[i];
|
|
|
|
for (int j = 0; j < poly.points.size(); j++) {
|
|
|
|
QPointF &p = poly.points[j];
|
2022-06-02 18:31:40 +02:00
|
|
|
p = ll2xy(Coordinates(p.x(), p.y()));
|
2020-11-15 22:37:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RasterTile::ll2xy(QList<MapData::Point> &points)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < points.size(); i++) {
|
2022-06-02 18:31:40 +02:00
|
|
|
QPointF p(ll2xy(points.at(i).coordinates));
|
2020-11-15 22:37:49 +01:00
|
|
|
points[i].coordinates = Coordinates(p.x(), p.y());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
void RasterTile::drawPolygons(QPainter *painter,
|
|
|
|
const QList<MapData::Poly> &polygons)
|
2020-04-26 01:17:54 +02:00
|
|
|
{
|
2021-07-19 22:51:00 +02:00
|
|
|
QCache<const LBLFile *, SubFile::Handle> hc(16);
|
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
for (int n = 0; n < _data->style()->drawOrder().size(); n++) {
|
|
|
|
for (int i = 0; i < polygons.size(); i++) {
|
|
|
|
const MapData::Poly &poly = polygons.at(i);
|
|
|
|
if (poly.type != _data->style()->drawOrder().at(n))
|
2020-04-26 01:17:54 +02:00
|
|
|
continue;
|
|
|
|
|
2021-01-25 21:37:07 +01:00
|
|
|
if (poly.raster.isValid()) {
|
|
|
|
RectC r(poly.raster.rect());
|
2022-06-02 18:31:40 +02:00
|
|
|
QPointF tl(ll2xy(r.topLeft()));
|
|
|
|
QPointF br(ll2xy(r.bottomRight()));
|
2021-06-04 21:04:09 +02:00
|
|
|
QSizeF size(QRectF(tl, br).size());
|
2021-01-25 21:37:07 +01:00
|
|
|
|
2021-07-19 22:51:00 +02:00
|
|
|
bool insert = false;
|
|
|
|
SubFile::Handle *hdl = hc.object(poly.raster.lbl());
|
|
|
|
if (!hdl) {
|
|
|
|
hdl = new SubFile::Handle(poly.raster.lbl());
|
|
|
|
insert = true;
|
|
|
|
}
|
|
|
|
QPixmap pm(poly.raster.lbl()->image(*hdl, poly.raster.id()));
|
|
|
|
if (insert)
|
|
|
|
hc.insert(poly.raster.lbl(), hdl);
|
2021-06-04 21:04:09 +02:00
|
|
|
qreal sx = size.width() / (qreal)pm.width();
|
|
|
|
qreal sy = size.height() / (qreal)pm.height();
|
2021-01-27 21:18:06 +01:00
|
|
|
|
2021-01-31 10:30:06 +01:00
|
|
|
painter->save();
|
|
|
|
painter->scale(sx, sy);
|
2021-03-19 08:42:20 +01:00
|
|
|
painter->drawPixmap(QPointF(tl.x() / sx, tl.y() / sy), pm);
|
2021-01-31 10:30:06 +01:00
|
|
|
painter->restore();
|
2021-01-25 21:37:07 +01:00
|
|
|
|
|
|
|
//painter->setPen(Qt::blue);
|
|
|
|
//painter->setBrush(Qt::NoBrush);
|
|
|
|
//painter->drawRect(QRectF(tl, br));
|
|
|
|
} else {
|
2023-05-19 19:33:22 +02:00
|
|
|
const Style::Polygon &style = _data->style()->polygon(poly.type);
|
2021-01-25 21:37:07 +01:00
|
|
|
|
|
|
|
painter->setPen(style.pen());
|
|
|
|
painter->setBrush(style.brush());
|
|
|
|
painter->drawPolygon(poly.points);
|
|
|
|
}
|
2020-04-26 01:17:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
void RasterTile::drawLines(QPainter *painter, const QList<MapData::Poly> &lines)
|
2020-04-26 01:17:54 +02:00
|
|
|
{
|
|
|
|
painter->setBrush(Qt::NoBrush);
|
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
for (int i = 0; i < lines.size(); i++) {
|
|
|
|
const MapData::Poly &poly = lines.at(i);
|
|
|
|
const Style::Line &style = _data->style()->line(poly.type);
|
2020-04-26 01:17:54 +02:00
|
|
|
|
|
|
|
if (style.background() == Qt::NoPen)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
painter->setPen(style.background());
|
|
|
|
painter->drawPolyline(poly.points);
|
|
|
|
}
|
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
for (int i = 0; i < lines.size(); i++) {
|
|
|
|
const MapData::Poly &poly = lines.at(i);
|
|
|
|
const Style::Line &style = _data->style()->line(poly.type);
|
2020-04-26 01:17:54 +02:00
|
|
|
|
|
|
|
if (!style.img().isNull())
|
|
|
|
BitmapLine::draw(painter, poly.points, style.img());
|
|
|
|
else if (style.foreground() != Qt::NoPen) {
|
|
|
|
painter->setPen(style.foreground());
|
|
|
|
painter->drawPolyline(poly.points);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RasterTile::drawTextItems(QPainter *painter,
|
|
|
|
const QList<TextItem*> &textItems)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < textItems.size(); i++)
|
|
|
|
textItems.at(i)->paint(painter);
|
|
|
|
}
|
|
|
|
|
2020-10-17 14:26:59 +02:00
|
|
|
static void removeDuplicitLabel(QList<TextItem *> &labels, const QString &text,
|
|
|
|
const QRectF &tileRect)
|
|
|
|
{
|
2020-10-17 20:59:58 +02:00
|
|
|
for (int i = 0; i < labels.size(); i++) {
|
|
|
|
TextItem *item = labels.at(i);
|
2020-10-17 14:26:59 +02:00
|
|
|
if (tileRect.contains(item->boundingRect()) && *(item->text()) == text) {
|
2020-10-17 20:59:58 +02:00
|
|
|
labels.removeAt(i);
|
2020-10-17 14:30:06 +02:00
|
|
|
delete item;
|
2020-10-17 14:26:59 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
void RasterTile::processPolygons(const QList<MapData::Poly> &polygons,
|
|
|
|
QList<TextItem*> &textItems)
|
2020-04-26 01:17:54 +02:00
|
|
|
{
|
2020-10-17 14:26:59 +02:00
|
|
|
QSet<QString> set;
|
|
|
|
QList<TextItem *> labels;
|
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
for (int i = 0; i < polygons.size(); i++) {
|
|
|
|
const MapData::Poly &poly = polygons.at(i);
|
2020-10-17 14:26:59 +02:00
|
|
|
bool exists = set.contains(poly.label.text());
|
2020-04-26 01:17:54 +02:00
|
|
|
|
|
|
|
if (poly.label.text().isEmpty())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (_zoom <= 23 && (Style::isWaterArea(poly.type)
|
|
|
|
|| Style::isMilitaryArea(poly.type)
|
|
|
|
|| Style::isNatureReserve(poly.type))) {
|
2023-05-19 19:33:22 +02:00
|
|
|
const Style::Polygon &style = _data->style()->polygon(poly.type);
|
2020-04-26 01:17:54 +02:00
|
|
|
TextPointItem *item = new TextPointItem(
|
2021-04-18 12:20:07 +02:00
|
|
|
centroid(poly.points).toPoint(), &poly.label.text(), poiFont(),
|
|
|
|
0, &style.brush().color(), &haloColor);
|
2020-04-26 01:17:54 +02:00
|
|
|
if (item->isValid() && !item->collides(textItems)
|
2020-10-22 01:16:23 +02:00
|
|
|
&& !item->collides(labels)
|
2022-05-10 01:05:30 +02:00
|
|
|
&& !(exists && _rect.contains(item->boundingRect().toRect()))
|
2020-10-17 14:26:59 +02:00
|
|
|
&& rectNearPolygon(poly.points, item->boundingRect())) {
|
|
|
|
if (exists)
|
2022-05-10 01:05:30 +02:00
|
|
|
removeDuplicitLabel(labels, poly.label.text(), _rect);
|
2020-10-17 14:26:59 +02:00
|
|
|
else
|
|
|
|
set.insert(poly.label.text());
|
|
|
|
labels.append(item);
|
|
|
|
} else
|
2020-04-26 01:17:54 +02:00
|
|
|
delete item;
|
|
|
|
}
|
|
|
|
}
|
2020-10-17 14:26:59 +02:00
|
|
|
|
|
|
|
textItems.append(labels);
|
2020-04-26 01:17:54 +02:00
|
|
|
}
|
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
void RasterTile::processLines(QList<MapData::Poly> &lines,
|
2023-08-01 23:38:33 +02:00
|
|
|
QList<TextItem*> &textItems, const QImage (&arrows)[2])
|
2020-04-26 01:17:54 +02:00
|
|
|
{
|
2023-05-19 19:33:22 +02:00
|
|
|
std::stable_sort(lines.begin(), lines.end());
|
2020-04-26 01:17:54 +02:00
|
|
|
|
|
|
|
if (_zoom >= 22)
|
2023-08-01 23:38:33 +02:00
|
|
|
processStreetNames(lines, textItems, arrows);
|
2023-05-19 19:33:22 +02:00
|
|
|
processShields(lines, textItems);
|
2020-04-26 01:17:54 +02:00
|
|
|
}
|
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
void RasterTile::processStreetNames(const QList<MapData::Poly> &lines,
|
2023-08-01 23:38:33 +02:00
|
|
|
QList<TextItem*> &textItems, const QImage (&arrows)[2])
|
2020-04-26 01:17:54 +02:00
|
|
|
{
|
2023-05-19 19:33:22 +02:00
|
|
|
for (int i = 0; i < lines.size(); i++) {
|
|
|
|
const MapData::Poly &poly = lines.at(i);
|
|
|
|
const Style::Line &style = _data->style()->line(poly.type);
|
2020-04-26 01:17:54 +02:00
|
|
|
|
|
|
|
if (style.img().isNull() && style.foreground() == Qt::NoPen)
|
|
|
|
continue;
|
|
|
|
|
2024-02-21 08:49:09 +01:00
|
|
|
const QFont *fnt = _data->style()->font(style.text().size(),
|
2023-12-21 01:13:36 +01:00
|
|
|
Style::Small);
|
2024-02-21 08:49:09 +01:00
|
|
|
const QColor *color = style.text().color().isValid()
|
2024-02-27 07:20:09 +01:00
|
|
|
? &style.text().color() : Style::isContourLine(poly.type)
|
|
|
|
? 0 : &textColor;
|
2023-07-13 11:59:53 +02:00
|
|
|
const QColor *hColor = Style::isContourLine(poly.type) ? 0 : &haloColor;
|
2023-07-31 23:36:14 +02:00
|
|
|
const QImage *img = poly.oneway
|
|
|
|
? Style::isWaterLine(poly.type)
|
2023-08-03 00:39:56 +02:00
|
|
|
? &arrows[WATER] : &arrows[ROAD] : 0;
|
2023-08-01 23:21:49 +02:00
|
|
|
const QString *label = poly.label.text().isEmpty()
|
|
|
|
? 0 : &poly.label.text();
|
|
|
|
|
2024-02-27 07:20:09 +01:00
|
|
|
if (!img && (!label || !fnt || !color))
|
2023-08-01 23:21:49 +02:00
|
|
|
continue;
|
2020-04-26 01:17:54 +02:00
|
|
|
|
2023-08-01 23:21:49 +02:00
|
|
|
TextPathItem *item = new TextPathItem(poly.points, label, _rect, fnt,
|
|
|
|
color, hColor, img);
|
2020-04-26 01:17:54 +02:00
|
|
|
if (item->isValid() && !item->collides(textItems))
|
|
|
|
textItems.append(item);
|
2023-07-13 11:59:53 +02:00
|
|
|
else {
|
2020-04-26 01:17:54 +02:00
|
|
|
delete item;
|
2023-07-13 11:59:53 +02:00
|
|
|
|
|
|
|
if (img) {
|
|
|
|
TextPathItem *item = new TextPathItem(poly.points, 0, _rect, 0,
|
|
|
|
0, 0, img);
|
|
|
|
if (item->isValid() && !item->collides(textItems))
|
|
|
|
textItems.append(item);
|
|
|
|
else
|
|
|
|
delete item;
|
|
|
|
}
|
|
|
|
}
|
2020-04-26 01:17:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
void RasterTile::processShields(const QList<MapData::Poly> &lines,
|
|
|
|
QList<TextItem*> &textItems)
|
2020-04-26 01:17:54 +02:00
|
|
|
{
|
|
|
|
for (int type = FIRST_SHIELD; type <= LAST_SHIELD; type++) {
|
2021-04-10 15:27:40 +02:00
|
|
|
if (minShieldZoom(static_cast<Shield::Type>(type)) > _zoom)
|
2020-04-26 01:17:54 +02:00
|
|
|
continue;
|
|
|
|
|
2021-04-10 15:27:40 +02:00
|
|
|
QHash<Shield, QPolygonF> shields;
|
|
|
|
QHash<Shield, const Shield*> sp;
|
2020-04-26 01:17:54 +02:00
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
for (int i = 0; i < lines.size(); i++) {
|
|
|
|
const MapData::Poly &poly = lines.at(i);
|
2021-04-10 15:27:40 +02:00
|
|
|
const Shield &shield = poly.label.shield();
|
2020-04-26 01:17:54 +02:00
|
|
|
if (!shield.isValid() || shield.type() != type
|
|
|
|
|| !Style::isMajorRoad(poly.type))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
QPolygonF &p = shields[shield];
|
|
|
|
for (int j = 0; j < poly.points.size(); j++)
|
|
|
|
p.append(poly.points.at(j));
|
|
|
|
|
|
|
|
sp.insert(shield, &shield);
|
|
|
|
}
|
|
|
|
|
2021-04-10 15:27:40 +02:00
|
|
|
for (QHash<Shield, QPolygonF>::const_iterator it = shields.constBegin();
|
|
|
|
it != shields.constEnd(); ++it) {
|
2020-04-26 01:17:54 +02:00
|
|
|
const QPolygonF &p = it.value();
|
2022-05-10 01:05:30 +02:00
|
|
|
QRectF rect(p.boundingRect() & _rect);
|
2024-02-21 08:49:09 +01:00
|
|
|
if (AREA(rect) < AREA(QRect(0, 0, _rect.width()/4, _rect.width()/4)))
|
2020-04-26 01:17:54 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
QMap<qreal, int> map;
|
|
|
|
QPointF center = rect.center();
|
|
|
|
for (int j = 0; j < p.size(); j++) {
|
|
|
|
QLineF l(p.at(j), center);
|
|
|
|
map.insert(l.length(), j);
|
|
|
|
}
|
|
|
|
|
|
|
|
QMap<qreal, int>::const_iterator jt = map.constBegin();
|
|
|
|
|
|
|
|
TextPointItem *item = new TextPointItem(
|
|
|
|
p.at(jt.value()).toPoint(), &(sp.value(it.key())->text()),
|
2021-04-18 12:20:07 +02:00
|
|
|
poiFont(), 0, &shieldColor, 0, shieldBgColor(it.key().type()));
|
2020-04-26 01:17:54 +02:00
|
|
|
|
|
|
|
bool valid = false;
|
|
|
|
while (true) {
|
|
|
|
if (!item->collides(textItems)
|
2022-05-10 01:05:30 +02:00
|
|
|
&& _rect.contains(item->boundingRect().toRect())) {
|
2020-04-26 01:17:54 +02:00
|
|
|
valid = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (++jt == map.constEnd())
|
|
|
|
break;
|
|
|
|
item->setPos(p.at(jt.value()).toPoint());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (valid)
|
|
|
|
textItems.append(item);
|
|
|
|
else
|
|
|
|
delete item;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
void RasterTile::processPoints(QList<MapData::Point> &points,
|
|
|
|
QList<TextItem*> &textItems)
|
2020-04-26 01:17:54 +02:00
|
|
|
{
|
2023-05-19 19:33:22 +02:00
|
|
|
std::sort(points.begin(), points.end());
|
2020-04-26 01:17:54 +02:00
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
for (int i = 0; i < points.size(); i++) {
|
|
|
|
const MapData::Point &point = points.at(i);
|
|
|
|
const Style::Point &style = _data->style()->point(point.type);
|
2020-10-16 00:03:26 +02:00
|
|
|
bool poi = Style::isPOI(point.type);
|
2020-04-26 01:17:54 +02:00
|
|
|
|
|
|
|
const QString *label = point.label.text().isEmpty()
|
|
|
|
? 0 : &(point.label.text());
|
|
|
|
const QImage *img = style.img().isNull() ? 0 : &style.img();
|
2020-10-16 00:03:26 +02:00
|
|
|
const QFont *fnt = poi
|
2024-02-21 08:49:09 +01:00
|
|
|
? poiFont(style.text().size(), _zoom, point.classLabel)
|
|
|
|
: _data->style()->font(style.text().size());
|
|
|
|
const QColor *color = style.text().color().isValid()
|
|
|
|
? &style.text().color() : &textColor;
|
2023-05-12 10:19:52 +02:00
|
|
|
const QColor *hcolor = Style::isDepthPoint(point.type)
|
|
|
|
? 0 : &haloColor;
|
2020-04-26 01:17:54 +02:00
|
|
|
|
|
|
|
if ((!label || !fnt) && !img)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
TextPointItem *item = new TextPointItem(QPoint(point.coordinates.lon(),
|
2023-05-12 10:19:52 +02:00
|
|
|
point.coordinates.lat()), label, fnt, img, color, hcolor, 0,
|
2022-03-26 17:20:16 +01:00
|
|
|
ICON_PADDING);
|
2020-04-26 01:17:54 +02:00
|
|
|
if (item->isValid() && !item->collides(textItems))
|
|
|
|
textItems.append(item);
|
|
|
|
else
|
|
|
|
delete item;
|
|
|
|
}
|
|
|
|
}
|
2023-05-19 19:33:22 +02:00
|
|
|
|
|
|
|
void RasterTile::fetchData(QList<MapData::Poly> &polygons,
|
|
|
|
QList<MapData::Poly> &lines, QList<MapData::Point> &points)
|
|
|
|
{
|
|
|
|
QPoint ttl(_rect.topLeft());
|
|
|
|
|
|
|
|
QRectF polyRect(ttl, QPointF(ttl.x() + _rect.width(), ttl.y()
|
|
|
|
+ _rect.height()));
|
|
|
|
RectD polyRectD(_transform.img2proj(polyRect.topLeft()),
|
|
|
|
_transform.img2proj(polyRect.bottomRight()));
|
|
|
|
_data->polys(polyRectD.toRectC(_proj, 20), _zoom,
|
|
|
|
&polygons, &lines);
|
|
|
|
|
|
|
|
QRectF pointRect(QPointF(ttl.x() - TEXT_EXTENT, ttl.y() - TEXT_EXTENT),
|
|
|
|
QPointF(ttl.x() + _rect.width() + TEXT_EXTENT, ttl.y() + _rect.height()
|
|
|
|
+ TEXT_EXTENT));
|
|
|
|
RectD pointRectD(_transform.img2proj(pointRect.topLeft()),
|
|
|
|
_transform.img2proj(pointRect.bottomRight()));
|
|
|
|
_data->points(pointRectD.toRectC(_proj, 20), _zoom, &points);
|
|
|
|
}
|
|
|
|
|
2024-02-21 08:49:09 +01:00
|
|
|
Matrix RasterTile::elevation() const
|
|
|
|
{
|
|
|
|
Matrix m(_rect.height() + 2, _rect.width() + 2);
|
|
|
|
|
|
|
|
int left = _rect.left() - 1;
|
|
|
|
int right = _rect.right() + 1;
|
|
|
|
int top = _rect.top() - 1;
|
|
|
|
int bottom = _rect.bottom() + 1;
|
|
|
|
|
2024-02-21 18:40:28 +01:00
|
|
|
QVector<Coordinates> ll;
|
|
|
|
ll.reserve(m.w() * m.h());
|
2024-02-21 08:49:09 +01:00
|
|
|
for (int y = top; y <= bottom; y++) {
|
|
|
|
for (int x = left; x <= right; x++)
|
2024-02-21 18:40:28 +01:00
|
|
|
ll.append(xy2ll(QPointF(x, y)));
|
2024-02-21 08:49:09 +01:00
|
|
|
}
|
2024-02-21 18:40:28 +01:00
|
|
|
|
|
|
|
DEM::lock();
|
|
|
|
for (int i = 0; i < ll.size(); i++)
|
|
|
|
m.m(i) = DEM::elevation(ll.at(i));
|
2024-02-21 08:49:09 +01:00
|
|
|
DEM::unlock();
|
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2023-05-19 19:33:22 +02:00
|
|
|
void RasterTile::render()
|
|
|
|
{
|
2024-02-21 08:49:09 +01:00
|
|
|
QImage img(_rect.width() * _ratio, _rect.height() * _ratio,
|
|
|
|
QImage::Format_ARGB32_Premultiplied);
|
2023-05-19 19:33:22 +02:00
|
|
|
QList<MapData::Poly> polygons;
|
|
|
|
QList<MapData::Poly> lines;
|
|
|
|
QList<MapData::Point> points;
|
|
|
|
QList<TextItem*> textItems;
|
2023-08-01 23:38:33 +02:00
|
|
|
QImage arrows[2];
|
|
|
|
|
2024-02-12 22:38:42 +01:00
|
|
|
arrows[ROAD] = HIDPI_IMG(":/map", "arrow", _ratio);
|
|
|
|
arrows[WATER] = HIDPI_IMG(":/map", "water-arrow", _ratio);
|
2023-05-19 19:33:22 +02:00
|
|
|
|
|
|
|
fetchData(polygons, lines, points);
|
|
|
|
ll2xy(polygons);
|
|
|
|
ll2xy(lines);
|
|
|
|
ll2xy(points);
|
|
|
|
|
|
|
|
processPoints(points, textItems);
|
|
|
|
processPolygons(polygons, textItems);
|
2023-08-01 23:38:33 +02:00
|
|
|
processLines(lines, textItems, arrows);
|
2023-05-19 19:33:22 +02:00
|
|
|
|
2024-02-21 08:49:09 +01:00
|
|
|
img.setDevicePixelRatio(_ratio);
|
|
|
|
img.fill(Qt::transparent);
|
2023-05-19 19:33:22 +02:00
|
|
|
|
2024-02-21 08:49:09 +01:00
|
|
|
QPainter painter(&img);
|
2023-05-19 19:33:22 +02:00
|
|
|
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
painter.translate(-_rect.x(), -_rect.y());
|
|
|
|
|
|
|
|
drawPolygons(&painter, polygons);
|
2024-02-21 08:49:09 +01:00
|
|
|
if (_hillShading && _zoom >= 18 && _zoom <= 24)
|
|
|
|
painter.drawImage(_rect.x(), _rect.y(), HillShading::render(elevation()));
|
2023-05-19 19:33:22 +02:00
|
|
|
drawLines(&painter, lines);
|
|
|
|
drawTextItems(&painter, textItems);
|
|
|
|
|
|
|
|
qDeleteAll(textItems);
|
|
|
|
|
|
|
|
//painter.setPen(Qt::red);
|
|
|
|
//painter.setRenderHint(QPainter::Antialiasing, false);
|
2023-06-04 23:56:00 +02:00
|
|
|
//painter.drawRect(_rect);
|
2024-02-23 09:25:41 +01:00
|
|
|
|
2024-02-23 09:45:41 +01:00
|
|
|
_pixmap.convertFromImage(img);
|
2023-05-19 19:33:22 +02:00
|
|
|
}
|