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;
 
   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;
 
This file contains the headers for ViewportParams.
 
A 3d point representation.
 
qreal sphericalDistanceTo(const GeoDataCoordinates &other) const
This method calculates the shortest distance between two points on a sphere.
 
A LineString that allows to store a contiguous set of line segments.
 
a class representing a point of interest on the map
 
an addressable style group
 
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