6#include "GeoLineStringGraphicsItem.h"
8#include "GeoDataColorStyle.h"
9#include "GeoDataLabelStyle.h"
10#include "GeoDataLineStyle.h"
11#include "GeoDataPlacemark.h"
12#include "GeoDataPolyStyle.h"
13#include "GeoDataStyle.h"
14#include "GeoPainter.h"
15#include "MarbleDebug.h"
16#include "OsmPlacemarkData.h"
17#include "StyleBuilder.h"
20#include <QPainterPathStroker>
26const GeoDataStyle *GeoLineStringGraphicsItem::s_previousStyle =
nullptr;
27bool GeoLineStringGraphicsItem::s_paintInline =
true;
28bool GeoLineStringGraphicsItem::s_paintOutline =
true;
30GeoLineStringGraphicsItem::GeoLineStringGraphicsItem(
const GeoDataPlacemark *placemark,
const GeoDataLineString *lineString)
31 : GeoGraphicsItem(placemark)
32 , m_lineString(lineString)
33 , m_renderLineString(lineString)
34 , m_renderLabel(false)
36 , m_name(placemark->
name())
38 QString const category = StyleBuilder::visualCategoryName(placemark->visualCategory());
42 if (!m_name.isEmpty()) {
45 setPaintLayers(paintLayers);
48GeoLineStringGraphicsItem::~GeoLineStringGraphicsItem()
50 qDeleteAll(m_cachedPolygons);
53void GeoLineStringGraphicsItem::setLineString(
const GeoDataLineString *lineString)
55 m_lineString = lineString;
56 m_renderLineString = lineString;
59const GeoDataLineString *GeoLineStringGraphicsItem::lineString()
const
67 return GeoDataLineString();
70 Q_ASSERT(!lineStrings_.
isEmpty());
71 auto lineStrings = lineStrings_;
72 GeoDataLineString result = *lineStrings.
first();
73 lineStrings.pop_front();
74 for (
bool matched =
true; matched && !lineStrings.isEmpty();) {
76 for (
auto lineString : lineStrings) {
77 if (canMerge(result.first(), lineString->first())) {
80 result << *lineString;
81 lineStrings.removeOne(lineString);
84 }
else if (canMerge(result.last(), lineString->first())) {
85 result.remove(result.size() - 1);
86 result << *lineString;
87 lineStrings.removeOne(lineString);
90 }
else if (canMerge(result.first(), lineString->last())) {
91 GeoDataLineString behind = result;
95 lineStrings.removeOne(lineString);
98 }
else if (canMerge(result.last(), lineString->last())) {
99 GeoDataLineString behind = *lineString;
103 lineStrings.removeOne(lineString);
110 return GeoDataLineString();
113 return lineStrings.isEmpty() ? result : GeoDataLineString();
116void GeoLineStringGraphicsItem::setMergedLineString(
const GeoDataLineString &mergedLineString)
118 m_mergedLineString = mergedLineString;
119 m_renderLineString = mergedLineString.isEmpty() ? m_lineString : &m_mergedLineString;
122const GeoDataLatLonAltBox &GeoLineStringGraphicsItem::latLonAltBox()
const
124 return m_renderLineString->latLonAltBox();
127void GeoLineStringGraphicsItem::paint(GeoPainter *painter,
const ViewportParams *viewport,
const QString &layer,
int tileLevel)
129 setRenderContext(RenderContext(tileLevel));
132 qDeleteAll(m_cachedPolygons);
133 m_cachedPolygons.clear();
135 painter->polygonsFromLineString(*m_renderLineString, m_cachedPolygons);
136 if (m_cachedPolygons.empty()) {
139 if (painter->mapQuality() == HighQuality || painter->mapQuality() == PrintQuality) {
140 paintOutline(painter, viewport);
143 if (m_cachedPolygons.empty()) {
146 paintInline(painter, viewport);
148 if (!m_cachedPolygons.empty()) {
150 paintLabel(painter, viewport);
154 qDeleteAll(m_cachedPolygons);
155 m_cachedPolygons.clear();
157 painter->polygonsFromLineString(*m_renderLineString, m_cachedPolygons);
158 if (m_cachedPolygons.empty()) {
161 if (s_previousStyle != style().data()) {
162 configurePainterForLine(painter, viewport,
false);
164 s_previousStyle = style().data();
165 for (
const QPolygonF *itPolygon : std::as_const(m_cachedPolygons)) {
166 painter->drawPolyline(*itPolygon);
171bool GeoLineStringGraphicsItem::contains(
const QPoint &screenPosition,
const ViewportParams *)
const
173 if (m_penWidth <= 0.0) {
177 if (m_cachedRegion.isNull()) {
179 for (
auto polygon : m_cachedPolygons) {
183 qreal
const margin = 6.0;
184 stroker.
setWidth(m_penWidth + margin);
188 return m_cachedRegion.contains(screenPosition);
194 for (
auto relation : relations) {
195 auto const ref = relation->osmData().tagValue(QStringLiteral(
"ref"));
196 if (relation->isVisible() && !ref.isEmpty()) {
197 names[relation->relationType()] << ref;
201 m_name = feature()->name();
204 for (
auto iter = names.
begin(); iter != names.
end(); ++iter) {
206 switch (iter.key()) {
207 case GeoDataRelation::UnknownType:
208 case GeoDataRelation::RouteRoad:
210 case GeoDataRelation::RouteDetour:
211 value = tr(
"Detour");
213 case GeoDataRelation::RouteFerry:
214 value = tr(
"Ferry Route");
216 case GeoDataRelation::RouteTrain:
219 case GeoDataRelation::RouteSubway:
220 value = tr(
"Subway");
222 case GeoDataRelation::RouteTram:
225 case GeoDataRelation::RouteBus:
228 case GeoDataRelation::RouteTrolleyBus:
229 value = tr(
"Trolley Bus");
231 case GeoDataRelation::RouteBicycle:
232 value = tr(
"Bicycle Route");
234 case GeoDataRelation::RouteMountainbike:
235 value = tr(
"Mountainbike Route");
237 case GeoDataRelation::RouteFoot:
238 value = tr(
"Walking Route");
240 case GeoDataRelation::RouteHiking:
241 value = tr(
"Hiking Route");
243 case GeoDataRelation::RouteHorse:
244 value = tr(
"Bridleway");
246 case GeoDataRelation::RouteInlineSkates:
247 value = tr(
"Inline Skates Route");
249 case GeoDataRelation::RouteSkiDownhill:
250 value = tr(
"Downhill Piste");
252 case GeoDataRelation::RouteSkiNordic:
253 value = tr(
"Nordic Ski Trail");
255 case GeoDataRelation::RouteSkitour:
256 value = tr(
"Skitour");
258 case GeoDataRelation::RouteSled:
259 value = tr(
"Sled Trail");
264 std::sort(references.
begin(), references.
end());
265 auto const last = std::unique(references.
begin(), references.
end());
266 references.
erase(last, references.
end());
267 auto const routes = references.
join(QStringLiteral(
", "));
268 namesList << (value.
isEmpty() ? routes : QStringLiteral(
"%1 %2").arg(value, routes));
270 auto const allRoutes = namesList.
join(QStringLiteral(
"; "));
271 if (feature()->
name().isEmpty()) {
274 m_name = QStringLiteral(
"%1 (%2)").arg(feature()->
name(), allRoutes);
279void GeoLineStringGraphicsItem::paintInline(GeoPainter *painter,
const ViewportParams *viewport)
281 if ((!viewport->resolves(m_renderLineString->latLonAltBox(), 2))) {
285 if (s_previousStyle != style().data()) {
286 s_paintInline = configurePainterForLine(painter, viewport,
false);
288 s_previousStyle = style().data();
291 m_renderLabel = painter->pen().widthF() >= 6.0f;
292 m_penWidth = painter->pen().widthF();
293 for (
const QPolygonF *itPolygon : std::as_const(m_cachedPolygons)) {
294 painter->drawPolyline(*itPolygon);
299void GeoLineStringGraphicsItem::paintOutline(GeoPainter *painter,
const ViewportParams *viewport)
const
301 if ((!viewport->resolves(m_renderLineString->latLonAltBox(), 2))) {
305 if (s_previousStyle != style().data()) {
306 s_paintOutline = configurePainterForLine(painter, viewport,
true);
308 s_previousStyle = style().data();
310 if (s_paintOutline) {
311 for (
const QPolygonF *itPolygon : m_cachedPolygons) {
312 painter->drawPolyline(*itPolygon);
317void GeoLineStringGraphicsItem::paintLabel(GeoPainter *painter,
const ViewportParams *viewport)
const
319 if ((!viewport->resolves(m_renderLineString->latLonAltBox(), 2))) {
323 LabelPositionFlags labelPositionFlags = NoLabel;
324 bool isValid = configurePainterForLabel(painter, viewport, labelPositionFlags);
327 GeoDataStyle::ConstPtr style = this->style();
336 const GeoDataLabelStyle &labelStyle = style->labelStyle();
337 painter->drawLabelsForPolygons(m_cachedPolygons, m_name, FollowLine, labelStyle.paintedColor());
341bool GeoLineStringGraphicsItem::configurePainterForLine(GeoPainter *painter,
const ViewportParams *viewport,
const bool isOutline)
const
343 QPen currentPen = painter->pen();
344 GeoDataStyle::ConstPtr style = this->style();
346 painter->setPen(
QPen());
350 if (isOutline && !style->polyStyle().outline()) {
354 const GeoDataLineStyle &lineStyle = style->lineStyle();
363 const QColor linePaintedColor = (!isOutline && (lineStyle.cosmeticOutline() && lineStyle.penStyle() ==
Qt::SolidLine))
364 ? style->polyStyle().paintedColor()
365 : lineStyle.paintedColor();
366 if (currentPen.
color() != linePaintedColor) {
367 if (linePaintedColor.
alpha() == 255) {
368 currentPen.
setColor(linePaintedColor);
371 QColor penColor = linePaintedColor;
372 if (penColor.
alpha() != 0) {
375 if (currentPen.
color() != penColor) {
379 currentPen.
setColor(linePaintedColor);
384 const float lineWidth = lineStyle.width();
385 const float linePhysicalWidth = lineStyle.physicalWidth();
386 float newLineWidth = lineWidth;
387 if (linePhysicalWidth != 0.0) {
388 const float scaledLinePhysicalWidth = float(viewport->radius()) / EARTH_RADIUS * linePhysicalWidth;
389 newLineWidth = scaledLinePhysicalWidth > lineWidth ? scaledLinePhysicalWidth : lineWidth;
392 if (!isOutline && lineStyle.cosmeticOutline() && lineStyle.penStyle() ==
Qt::SolidLine) {
393 if (newLineWidth > 2.5) {
398 const qreal lineDrawThreshold = isOutline ? 2.5 : 0.5;
402 if (currentPen.
widthF() != newLineWidth && newLineWidth != 0.0) {
403 if (newLineWidth < lineDrawThreshold) {
404 if (painter->pen().style() !=
Qt::NoPen) {
417 if (painter->mapQuality() == HighQuality || painter->mapQuality() == PrintQuality) {
424 if (currentPen.
dashPattern() != lineStyle.dashPattern()) {
432 if (painter->pen() != currentPen) {
433 painter->setPen(currentPen);
439 if (lineStyle.background()) {
440 QBrush brush = painter->background();
441 brush.
setColor(style->polyStyle().paintedColor());
442 painter->setBackground(brush);
458bool GeoLineStringGraphicsItem::configurePainterForLabel(GeoPainter *painter,
const ViewportParams *viewport, LabelPositionFlags &labelPositionFlags)
const
460 QPen currentPen = painter->pen();
461 GeoDataStyle::ConstPtr style = this->style();
463 painter->setPen(
QPen());
465 const GeoDataLineStyle &lineStyle = style->lineStyle();
475 const float lineWidth = lineStyle.width();
476 const float linePhysicalWidth = lineStyle.physicalWidth();
477 float newLineWidth = lineWidth;
478 if (linePhysicalWidth != 0.0) {
479 const float scaledLinePhysicalWidth = float(viewport->radius()) / EARTH_RADIUS * linePhysicalWidth;
480 newLineWidth = scaledLinePhysicalWidth > lineWidth ? scaledLinePhysicalWidth : lineWidth;
485 if (currentPen.
widthF() != newLineWidth && newLineWidth != 0.0) {
486 if (newLineWidth < 6.0) {
492 if (painter->pen() != currentPen) {
493 painter->setPen(currentPen);
506 const GeoDataLabelStyle &labelStyle = style->labelStyle();
507 painter->setFont(labelStyle.font());
508 switch (labelStyle.alignment()) {
509 case GeoDataLabelStyle::Corner:
510 case GeoDataLabelStyle::Right:
511 labelPositionFlags |= LineStart;
513 case GeoDataLabelStyle::Center:
514 labelPositionFlags |= LineCenter;
522bool GeoLineStringGraphicsItem::canMerge(
const GeoDataCoordinates &a,
const GeoDataCoordinates &b)
524 return a.sphericalDistanceTo(b) * EARTH_RADIUS < 0.1;
This file contains the headers for ViewportParams.
bool isValid(QStringView ifopt)
QString name(StandardAction id)
Category category(StandardShortcut id)
Binds a QML item to a specific geodetic location in screen coordinates.
@ HighQuality
High quality (e.g. antialiasing for lines)
@ PrintQuality
Print quality.
void setColor(Qt::GlobalColor color)
bool isEmpty() const const
iterator erase(const_iterator begin, const_iterator end)
bool isEmpty() const const
T value(qsizetype i) const const
void addPolygon(const QPolygonF &polygon)
QPolygonF toFillPolygon(const QTransform &matrix) const const
QPainterPath createStroke(const QPainterPath &path) const const
void setWidth(qreal width)
QColor color() const const
QList< qreal > dashPattern() const const
void setCapStyle(Qt::PenCapStyle style)
void setColor(const QColor &color)
void setDashPattern(const QList< qreal > &pattern)
void setStyle(Qt::PenStyle style)
void setWidthF(qreal width)
qreal widthF() const const
QPolygon toPolygon() const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString join(QChar separator) const const