3
#include "Command/DocumentCommands.h"
4
#include "Command/RoadCommands.h"
5
#include "Maps/Coord.h"
6
#include "Maps/Painting.h"
8
#include "Maps/TrackPoint.h"
9
#include "Utils/LineF.h"
10
#include "Utils/MDiscardableDialog.h"
12
#include <QtGui/QPainter>
13
#include <QtGui/QPainterPath>
14
#include <QMessageBox>
15
#include <QProgressDialog>
21
#include <ggl/ggl.hpp>
22
#include <ggl/geometries/cartesian2d.hpp>
24
#include <ggl/geometries/adapted/std_as_linestring.hpp>
32
: SmoothedUpToDate(false), BBox(Coord(0,0),Coord(0,0)), BBoxUpToDate(false), Area(0), Distance(0), Width(0),
33
wasPathComplete(false), ProjectionRevision(0)
36
std::vector<TrackPointPtr> Nodes;
37
QList<Coord> Smoothed;
38
bool SmoothedUpToDate;
46
bool NotEverythingDownloaded;
48
RenderPriority theRenderPriority;
51
int ProjectionRevision;
54
void updateSmoothed(bool DoSmooth);
55
void addSmoothedBezier(int i, int j, int k, int l);
58
void RoadPrivate::addSmoothedBezier(int i, int j, int k, int l)
60
Coord A(Nodes[i]->position());
61
Coord B(Nodes[j]->position());
62
Coord C(Nodes[k]->position());
63
Coord D(Nodes[l]->position());
65
Coord Ctrl1(B+(C-A)*(1/6));
66
Coord Ctrl2(C-(D-B)*(1/6));
69
Smoothed.push_back(Ctrl1);
70
Smoothed.push_back(Ctrl2);
71
Smoothed.push_back(C);
74
void RoadPrivate::updateSmoothed(bool DoSmooth)
76
SmoothedUpToDate = true;
78
if ( (Nodes.size() < 3) || !DoSmooth )
80
Smoothed.push_back(Nodes[0]->position());
81
addSmoothedBezier(0,0,1,2);
82
for (unsigned int i=1; i<Nodes.size()-2; ++i)
83
addSmoothedBezier(i-1,i,i+1,i+2);
84
int Last = Nodes.size()-1;
85
addSmoothedBezier(Last-2,Last-1,Last,Last);
93
Road::Road(const Road& other)
94
: MapFeature(other), p(new RoadPrivate)
100
for (unsigned int i=0; i<p->Nodes.size(); ++i)
102
p->Nodes[i]->unsetParentFeature(this);
106
void Road::setLayer(MapLayer* L)
109
for (unsigned int i=0; i<p->Nodes.size(); ++i)
111
p->Nodes[i]->setParentFeature(this);
113
for (unsigned int i=0; i<p->Nodes.size(); ++i)
115
p->Nodes[i]->unsetParentFeature(this);
116
MapFeature::setLayer(L);
119
void Road::partChanged(MapFeature*, int ChangeId)
124
p->BBoxUpToDate = false;
125
MetaUpToDate = false;
126
p->SmoothedUpToDate = false;
127
p->wasPathComplete = false;
128
notifyParents(ChangeId);
131
QString Road::description() const
133
QString s(tagValue("name",""));
135
return QString("%1 (%2)").arg(s).arg(id());
136
return QString("%1").arg(id());
139
RenderPriority Road::renderPriority()
141
// FIWME Segments of a road with different layers are wrongly painted (rounded corners)
142
return p->theRenderPriority;
145
RenderPriority Road::getRenderPriority()
147
return p->theRenderPriority;
150
void Road::add(TrackPoint* Pt)
153
layer()->indexRemove(p->BBox, this);
154
p->Nodes.push_back(Pt);
155
Pt->setParentFeature(this);
156
p->BBoxUpToDate = false;
157
MetaUpToDate = false;
158
p->SmoothedUpToDate = false;
159
p->wasPathComplete = false;
161
CoordBox bb = boundingBox();
162
layer()->indexAdd(bb, this);
166
void Road::add(TrackPoint* Pt, int Idx)
169
layer()->indexRemove(p->BBox, this);
170
p->Nodes.push_back(Pt);
171
std::rotate(p->Nodes.begin()+Idx,p->Nodes.end()-1,p->Nodes.end());
172
Pt->setParentFeature(this);
173
p->BBoxUpToDate = false;
174
MetaUpToDate = false;
175
p->SmoothedUpToDate = false;
176
p->wasPathComplete = false;
178
CoordBox bb = boundingBox();
179
layer()->indexAdd(bb, this);
183
int Road::find(MapFeature* Pt) const
185
for (unsigned int i=0; i<p->Nodes.size(); ++i)
186
if (p->Nodes[i] == Pt)
188
return p->Nodes.size();
191
void Road::remove(int idx)
194
layer()->indexRemove(p->BBox, this);
196
TrackPoint* Pt = p->Nodes[idx];
197
Pt->unsetParentFeature(this);
199
p->Nodes.erase(p->Nodes.begin()+idx);
200
p->BBoxUpToDate = false;
201
MetaUpToDate = false;
202
p->SmoothedUpToDate = false;
203
p->wasPathComplete = false;
205
CoordBox bb = boundingBox();
206
layer()->indexAdd(bb, this);
210
void Road::remove(MapFeature* F)
212
for (int i=p->Nodes.size(); i; --i)
213
if (p->Nodes[i-1] == F)
217
int Road::size() const
219
return p->Nodes.size();
222
TrackPoint* Road::getNode(int idx)
224
return p->Nodes[idx];
227
const TrackPoint* Road::getNode(int idx) const
229
return p->Nodes[idx];
232
const std::vector<TrackPointPtr>& Road::getNodes() const
238
MapFeature* Road::get(int idx)
240
return p->Nodes[idx];
243
const MapFeature* Road::get(int idx) const
245
return p->Nodes[idx];
248
bool Road::isNull() const
250
return (p->Nodes.size() == 0);
253
bool Road::notEverythingDownloaded()
255
if (MetaUpToDate == false)
258
return p->NotEverythingDownloaded;
261
CoordBox Road::boundingBox() const
263
if (!p->BBoxUpToDate)
267
p->BBox = CoordBox(p->Nodes[0]->position(),p->Nodes[0]->position());
268
for (unsigned int i=1; i<p->Nodes.size(); ++i)
269
p->BBox.merge(p->Nodes[i]->position());
272
p->BBox = CoordBox(Coord(0,0),Coord(0,0));
273
p->BBoxUpToDate = true;
278
void Road::updateMeta()
282
p->IsCoastline = false;
284
p->NotEverythingDownloaded = false;
285
if (lastUpdated() == MapFeature::NotYetDownloaded)
286
p->NotEverythingDownloaded = true;
288
for (unsigned int i=0; i<p->Nodes.size(); ++i)
289
if (p->Nodes.at(i) && p->Nodes.at(i)->notEverythingDownloaded()) {
290
p->NotEverythingDownloaded = true;
294
if (p->Nodes.size() == 0)
300
bool isArea = (p->Nodes[0] == p->Nodes[p->Nodes.size()-1]);
302
for (unsigned int i=0; (i+1)<p->Nodes.size(); ++i)
304
if (p->Nodes[i] && p->Nodes[i+1]) {
305
const Coord & here = p->Nodes[i]->position();
306
const Coord & next = p->Nodes[i+1]->position();
308
p->Distance += next.distanceFrom(here);
310
//p->Area += here.lat() * next.lon() - next.lat() * here.lon();
315
p->Area = p->Distance;
316
p->theRenderPriority = RenderPriority(RenderPriority::IsArea,-fabs(p->Area));
318
qreal Priority = tagValue("layer","0").toInt();
321
// dummy number to get a deterministic feature sort
322
Priority *= sin(intToRad(boundingBox().lonDiff()));
323
p->theRenderPriority = RenderPriority(RenderPriority::IsLinear,Priority);
326
if (tagValue("natural","") == "coastline")
327
p->IsCoastline = true;
333
double Road::distance()
335
if (MetaUpToDate == false)
341
bool Road::isCoastline()
343
if (MetaUpToDate == false)
346
return p->IsCoastline;
349
bool Road::isClosed() const
351
return (p->Nodes.size() && p->Nodes[0] == p->Nodes[p->Nodes.size()-1]);
356
if (MetaUpToDate == false)
362
void Road::draw(QPainter&, MapView* )
366
void Road::drawHover(QPainter& thePainter, MapView* theView, bool solid)
368
// FIXME Selected route
372
QFont F(thePainter.font());
375
F.setWeight(QFont::Black);
376
thePainter.setFont(F);
377
QPen TP(MerkaartorPreferences::instance()->getHoverColor());
378
TP.setWidth(MerkaartorPreferences::instance()->getHoverWidth());
380
TP.setDashPattern(M_PREFS->getParentDashes());
382
thePainter.setPen(TP);
383
thePainter.setBrush(Qt::NoBrush);
384
//QRect clipRect = thePainter.clipRegion().boundingRect().adjusted(int(-20), int(-20), int(20), int(20));
385
//buildPath(theProjection, clipRect);
386
thePainter.drawPath(theView->transform().map(p->thePath));
388
TP.setWidth(MerkaartorPreferences::instance()->getHoverWidth()*3);
389
TP.setCapStyle(Qt::RoundCap);
390
thePainter.setPen(TP);
392
buildPolygonFromRoad(this,theView->projection(),Pl);
393
thePainter.drawPoints(theView->transform().map(Pl));
395
if (M_PREFS->getShowParents()) {
396
for (int i=0; i<sizeParents(); ++i)
397
if (!getParent(i)->isDeleted())
398
getParent(i)->drawHover(thePainter, theView, false);
403
void Road::drawHighlight(QPainter& thePainter, MapView* theView, bool solid)
405
// FIXME Selected route
409
QFont F(thePainter.font());
412
F.setWeight(QFont::Black);
413
thePainter.setFont(F);
414
QPen TP(MerkaartorPreferences::instance()->getHighlightColor());
415
TP.setWidth(MerkaartorPreferences::instance()->getHighlightWidth());
417
TP.setDashPattern(M_PREFS->getParentDashes());
419
thePainter.setPen(TP);
420
thePainter.setBrush(Qt::NoBrush);
421
//QRect clipRect = thePainter.clipRegion().boundingRect().adjusted(int(-20), int(-20), int(20), int(20));
422
//buildPath(theProjection, clipRect);
423
thePainter.drawPath(theView->transform().map(p->thePath));
425
TP.setWidth(MerkaartorPreferences::instance()->getHighlightWidth()*3);
426
TP.setCapStyle(Qt::RoundCap);
427
thePainter.setPen(TP);
429
buildPolygonFromRoad(this,theView->projection(),Pl);
430
thePainter.drawPoints(theView->transform().map(Pl));
432
// if (M_PREFS->getShowParents()) {
433
// for (int i=0; i<sizeParents(); ++i)
434
// if (!getParent(i)->isDeleted())
435
// getParent(i)->drawHover(thePainter, theView, false);
440
void Road::drawFocus(QPainter& thePainter, MapView* theView, bool solid)
442
// FIXME Selected route
446
QFont F(thePainter.font());
449
F.setWeight(QFont::Black);
450
thePainter.setFont(F);
451
QPen TP(MerkaartorPreferences::instance()->getFocusColor());
452
TP.setWidth(MerkaartorPreferences::instance()->getFocusWidth());
454
TP.setDashPattern(M_PREFS->getParentDashes());
456
thePainter.setPen(TP);
457
thePainter.setBrush(Qt::NoBrush);
458
//QRect clipRect = thePainter.clipRegion().boundingRect().adjusted(int(-20), int(-20), int(20), int(20));
459
//buildPath(theProjection, clipRect);
460
thePainter.drawPath(theView->transform().map(p->thePath));
462
TP.setWidth(MerkaartorPreferences::instance()->getFocusWidth()*3);
463
TP.setCapStyle(Qt::RoundCap);
464
thePainter.setPen(TP);
466
buildPolygonFromRoad(this,theView->projection(),Pl);
467
thePainter.drawPoints(theView->transform().map(Pl));
469
if (M_PREFS->getShowParents()) {
470
for (int i=0; i<sizeParents(); ++i)
471
if (!getParent(i)->isDeleted())
472
getParent(i)->drawFocus(thePainter, theView, false);
477
double Road::pixelDistance(const QPointF& Target, double ClearEndDistance, const Projection& theProjection, const QTransform& theTransform) const
479
double Best = 1000000;
480
for (unsigned int i=0; i<p->Nodes.size(); ++i)
482
if (p->Nodes.at(i)) {
483
double x = ::distance(Target,theTransform.map(theProjection.project(p->Nodes.at(i))));
484
if (x<ClearEndDistance)
488
if (smoothed().size())
489
for (int i=3; i <p->Smoothed.size(); i += 3)
492
theTransform.map(theProjection.project(p->Smoothed[i-3])),
493
theTransform.map(theProjection.project(p->Smoothed[i-2])),
494
theTransform.map(theProjection.project(p->Smoothed[i-1])),
495
theTransform.map(theProjection.project(p->Smoothed[i])));
496
double D = F.distance(Target);
497
if (D < ClearEndDistance)
501
for (unsigned int i=1; i<p->Nodes.size(); ++i)
503
if (p->Nodes.at(i) && p->Nodes.at(i-1)) {
504
LineF F(theTransform.map(theProjection.project(p->Nodes.at(i-1))),theTransform.map(theProjection.project(p->Nodes.at(i))));
505
double D = F.capDistance(Target);
506
if (D < ClearEndDistance)
513
void Road::cascadedRemoveIfUsing(MapDocument* theDocument, MapFeature* aFeature, CommandList* theList, const QList<MapFeature*>& Proposals)
515
for (unsigned int i=0; i<p->Nodes.size();) {
516
if (p->Nodes[i] == aFeature)
518
QList<TrackPoint*> Alternatives;
519
for (int j=0; j<Proposals.size(); ++j)
521
TrackPoint* Pt = dynamic_cast<TrackPoint*>(Proposals[j]);
523
Alternatives.push_back(Pt);
525
if ( (p->Nodes.size() == 1) && (Alternatives.size() == 0) ) {
527
theList->add(new RemoveFeatureCommand(theDocument,this));
531
for (int j=0; j<Alternatives.size(); ++j)
533
if (i < p->Nodes.size())
535
if (p->Nodes[i+j] != Alternatives[j])
538
theList->add(new RoadAddTrackPointCommand(this, Alternatives[j], i+j,theDocument->getDirtyOrOriginLayer(layer())));
539
else if (p->Nodes[i+j-1] != Alternatives[j])
540
theList->add(new RoadAddTrackPointCommand(this, Alternatives[j], i+j,theDocument->getDirtyOrOriginLayer(layer())));
544
theList->add(new RoadRemoveTrackPointCommand(this, (TrackPoint*)aFeature,theDocument->getDirtyOrOriginLayer(layer())));
551
QPainterPath Road::getPath()
557
void Road::buildPath(const Projection &theProjection, const QTransform& /*theTransform*/, const QRectF& cr)
561
if (p->Nodes.size() < 2)
564
// Ensure nodes' projection is up-to-date
565
for (unsigned int i=0; i<p->Nodes.size(); ++i)
566
theProjection.project(p->Nodes[i]);
568
QPointF pbl = theProjection.project(p->BBox.bottomLeft());
569
QPointF ptr = theProjection.project(p->BBox.topRight());
571
make<point_2d>(pbl.x(), pbl.y()),
572
make<point_2d>(ptr.x(), ptr.y())
574
box_2d clipRect (make<point_2d>(cr.bottomLeft().x(), cr.topRight().y()), make<point_2d>(cr.topRight().x(), cr.bottomLeft().y()));
575
bool toClip = !ggl::within(roadRect, clipRect);
577
if (!p->wasPathComplete || p->ProjectionRevision != theProjection.projectionRevision()) {
578
p->thePath = QPainterPath();
580
p->thePath.moveTo(p->Nodes.at(0)->projection());
581
for (unsigned int i=1; i<p->Nodes.size(); ++i) {
582
p->thePath.lineTo(p->Nodes.at(i)->projection());
584
p->ProjectionRevision = theProjection.projectionRevision();
585
p->wasPathComplete = true;
588
p->thePath = QPainterPath();
589
p->wasPathComplete = false;
593
// for (unsigned int i=0; i<p->Nodes.size(); ++i) {
594
// QPointF P = p->Nodes[i]->projection();
595
// append(in, make<point_2d>(P.x(), P.y()));
598
std::vector<linestring_2d> clipped;
599
intersection <linestring_2d, box_2d, /*linestring_2d*/ std::vector<TrackPointPtr>, std::back_insert_iterator <std::vector<linestring_2d> > >
600
(clipRect, /*in*/ p->Nodes, std::back_inserter(clipped));
602
for (std::vector<linestring_2d>::const_iterator it = clipped.begin(); it != clipped.end(); it++)
604
if (!(*it).empty()) {
605
p->thePath.moveTo(QPointF((*it)[0].x(), (*it)[0].y()));
607
for (linestring_2d::const_iterator itl = (*it).begin()+1; itl != (*it).end(); itl++)
609
p->thePath.lineTo(QPointF((*itl).x(), (*itl).y()));
616
for (unsigned int i=0; i<p->Nodes.size(); ++i) {
617
QPointF P = p->Nodes[i]->projection();
618
append(in, make<point_2d>(P.x(), P.y()));
622
std::vector<polygon_2d> clipped;
623
intersection <polygon_2d, box_2d, polygon_2d /*std::vector<TrackPointPtr>*/, std::back_insert_iterator <std::vector<polygon_2d> > >
624
(clipRect, in /*p->Nodes*/, std::back_inserter(clipped));
626
for (std::vector<polygon_2d>::const_iterator it = clipped.begin(); it != clipped.end(); it++)
628
if (!(*it).outer().empty()) {
629
p->thePath.moveTo(QPointF((*it).outer()[0].x(), (*it).outer()[0].y()));
631
for (ring_2d::const_iterator itl = (*it).outer().begin()+1; itl != (*it).outer().end()-1; itl++)
633
p->thePath.lineTo(QPointF((*itl).x(), (*itl).y()));
635
p->thePath.lineTo(QPointF((*it).outer()[0].x(), (*it).outer()[0].y()));
639
//p->thePath = theTransform.map(p->thePath);
642
void Road::buildPath(Projection const &theProjection, const QRect& clipRect)
644
p->thePath = QPainterPath();
645
if (!p->Nodes.size())
648
bool lastPointVisible = true;
649
QPoint lastPoint = theProjection.project(p->Nodes[0]);
650
QPoint aP = lastPoint;
652
double PixelPerM = theProjection.pixelPerM();
653
double WW = PixelPerM*widthOf()*10+10;
656
if (M_PREFS->getDrawingHack()) {
657
if (!clipRect.contains(aP)) {
658
aP.setX(qMax(clipRect.left(), aP.x()));
659
aP.setX(qMin(clipRect.right(), aP.x()));
660
aP.setY(qMax(clipRect.top(), aP.y()));
661
aP.setY(qMin(clipRect.bottom(), aP.y()));
662
lastPointVisible = false;
665
p->thePath.moveTo(aP);
666
QPoint firstPoint = aP;
667
if (smoothed().size())
669
for (int i=3; i<smoothed().size(); i+=3)
671
theProjection.project(smoothed()[i-2]),
672
theProjection.project(smoothed()[i-1]),
673
theProjection.project(smoothed()[i]));
676
for (int j=1; j<size(); ++j) {
677
aP = theProjection.project(p->Nodes[j]);
678
if (M_PREFS->getDrawingHack()) {
679
QLine l(lastPoint, aP);
680
if (!clipRect.contains(aP)) {
681
if (!lastPointVisible) {
683
if (QRectInterstects(clipRect, l, a, b)) {
684
p->thePath.lineTo(a);
689
aP.setX(qMax(clipRect.left(), aP.x()));
690
aP.setX(qMin(clipRect.right(), aP.x()));
691
aP.setY(qMax(clipRect.top(), aP.y()));
692
aP.setY(qMin(clipRect.bottom(), aP.y()));
696
QRectInterstects(clipRect, l, a, b);
700
lastPointVisible = false;
702
if (!lastPointVisible) {
704
QRectInterstects(clipRect, l, a, b);
705
p->thePath.lineTo(a);
708
lastPointVisible = true;
711
p->thePath.lineTo(aP);
713
if (area() > 0.0 && !lastPointVisible)
714
p->thePath.lineTo(firstPoint);
719
bool Road::deleteChildren(MapDocument* theDocument, CommandList* theList)
721
if (lastUpdated() == MapFeature::OSMServerConflict)
724
MDiscardableMessage dlg(NULL,
725
MainWindow::tr("Delete Children."),
726
MainWindow::tr("Do you want to delete the children nodes also?"));
727
if (dlg.check() == QDialog::Accepted) {
728
QList<MapFeature*> Alternatives;
729
QMap<MapFeature*, int> ToDelete;
730
for (int i=(int)p->Nodes.size()-1; i>=0; --i) {
731
TrackPoint* N = p->Nodes[i];
732
if (N->sizeParents() < 2) {
736
QList<MapFeature*> ToDeleteKeys = ToDelete.uniqueKeys();
737
for (int i=0; i<ToDeleteKeys.size(); ++i) {
738
if (!ToDeleteKeys[i]->isDeleted())
739
theList->add(new RemoveFeatureCommand(theDocument, ToDeleteKeys[i], Alternatives));
745
#define DEFAULTWIDTH 6
748
double Road::widthOf()
753
QString s(tagValue("width",QString()));
755
p->Width = s.toDouble();
756
QString h = tagValue("highway",QString());
757
if ( (h == "motorway") || (h=="motorway_link") )
758
p->Width = 4*LANEWIDTH; // 3 lanes plus emergency
759
else if ( (h == "trunk") || (h=="trunk_link") )
760
p->Width = 3*LANEWIDTH; // 2 lanes plus emergency
761
else if ( (h == "primary") || (h=="primary_link") )
762
p->Width = 2*LANEWIDTH; // 2 lanes
763
else if (h == "secondary")
764
p->Width = 2*LANEWIDTH; // 2 lanes
765
else if (h == "tertiary")
766
p->Width = 1.5*LANEWIDTH; // shared middle lane
767
else if (h == "cycleway")
769
p->Width = DEFAULTWIDTH;
774
void Road::setTag(const QString& key, const QString& value, bool addToTagList)
776
MapFeature::setTag(key, value, addToTagList);
777
MetaUpToDate = false;
781
void Road::setTag(int index, const QString& key, const QString& value, bool addToTagList)
783
MapFeature::setTag(index, key, value, addToTagList);
784
MetaUpToDate = false;
788
void Road::clearTags()
790
MapFeature::clearTags();
791
MetaUpToDate = false;
795
void Road::clearTag(const QString& k)
797
MapFeature::clearTag(k);
798
MetaUpToDate = false;
802
bool Road::toGPX(QDomElement xParent, QProgressDialog & progress, bool forExport)
806
QDomElement e = xParent.ownerDocument().createElement("rte");
807
xParent.appendChild(e);
810
e.setAttribute("xml:id", xmlId());
811
QString s = tagValue("name","");
813
QDomElement c = xParent.ownerDocument().createElement("name");
815
QDomText v = c.ownerDocument().createTextNode(s);
818
s = tagValue("_comment_","");
820
QDomElement c = xParent.ownerDocument().createElement("cmt");
822
QDomText v = c.ownerDocument().createTextNode(s);
825
s = tagValue("_description_","");
827
QDomElement c = xParent.ownerDocument().createElement("desc");
829
QDomText v = c.ownerDocument().createTextNode(s);
833
for (int i=0; i<size(); ++i) {
834
dynamic_cast <TrackPoint*> (get(i))->toGPX(e, progress, forExport);
840
QString Road::toXML(int lvl, QProgressDialog * progress)
842
if (!size()) return "";
845
progress->setValue(progress->value()+1);
847
QString S(lvl*2, ' ');
848
S += QString("<way id=\"%1\">\n").arg(stripToOSMId(id()));
850
S += QString((lvl+1)*2, ' ') + QString("<nd ref=\"%1\"/>\n").arg(stripToOSMId(get(0)->id()));
851
for (int i=1; i<size(); ++i)
852
if (get(i)->id() != get(i-1)->id())
853
S += QString((lvl+1)*2, ' ') + QString("<nd ref=\"%1\"/>\n").arg(stripToOSMId(get(i)->id()));
854
S += tagsToXML(lvl+1);
855
S += QString(lvl*2, ' ') + "</way>\n";
859
bool Road::toXML(QDomElement xParent, QProgressDialog & progress)
863
QDomElement e = xParent.ownerDocument().createElement("way");
864
xParent.appendChild(e);
866
e.setAttribute("id", xmlId());
867
e.setAttribute("timestamp", time().toString(Qt::ISODate)+"Z");
868
e.setAttribute("user", user());
869
e.setAttribute("actor", (int)lastUpdated());
870
e.setAttribute("version", versionNumber());
872
e.setAttribute("deleted","true");
875
QDomElement n = xParent.ownerDocument().createElement("nd");
877
n.setAttribute("ref", get(0)->xmlId());
879
for (int i=1; i<size(); ++i) {
880
if (get(i)->xmlId() != get(i-1)->xmlId()) {
881
QDomElement n = xParent.ownerDocument().createElement("nd");
884
n.setAttribute("ref", get(i)->xmlId());
891
progress.setValue(progress.value()+1);
895
Road * Road::fromXML(MapDocument* d, MapLayer * L, const QDomElement e)
897
QString id = e.attribute("id");
898
if (!id.startsWith('{') && !id.startsWith('-'))
900
QDateTime time = QDateTime::fromString(e.attribute("timestamp").left(19), Qt::ISODate);
901
QString user = e.attribute("user");
902
bool Deleted = (e.attribute("deleted") == "true");
903
int Version = e.attribute("version").toInt();
904
MapFeature::ActorType A;
905
if (e.hasAttribute("actor"))
906
A = (MapFeature::ActorType)(e.attribute("actor", "2").toInt());
908
if ((L == d->getDirtyOrOriginLayer()))
909
A = MapFeature::User;
911
A = MapFeature::OSMServer;
913
Road* R = dynamic_cast<Road*>(d->getFeature(id));
918
R->setLastUpdated(A);
921
if (R->layer() != L) {
922
R->layer()->remove(R);
925
if (R->lastUpdated() == MapFeature::NotYetDownloaded)
926
R->setLastUpdated(A);
930
R->setDeleted(Deleted);
931
R->setVersionNumber(Version);
933
QDomElement c = e.firstChildElement();
935
if (c.tagName() == "nd") {
936
QString nId = c.attribute("ref");
937
if (!nId.startsWith('{') && !nId.startsWith('-'))
939
//TrackPoint* Part = MapFeature::getTrackPointOrCreatePlaceHolder(d, L, NULL, c.attribute("ref"));
940
TrackPoint* Part = dynamic_cast<TrackPoint*>(d->getFeature(nId));
943
Part = new TrackPoint(Coord(0,0));
945
Part->setLastUpdated(MapFeature::NotYetDownloaded);
950
c = c.nextSiblingElement();
953
MapFeature::tagsFromXML(d, R, e);
958
MapFeature::TrafficDirectionType trafficDirection(const Road* R)
960
// TODO some duplication with Way trafficDirection
962
int idx=R->findKey("oneway");
963
if (idx<R->tagSize())
965
d = R->tagValue(idx);
966
if ( (d == "yes") || (d == "1") || (d == "true")) return MapFeature::OneWay;
967
if (d == "-1") return MapFeature::OtherWay;
968
if ((d == "no") || (d == "false") || (d == "0")) return MapFeature::BothWays;
971
idx=R->findKey("junction");
972
if (idx<R->tagSize())
974
d = R->tagValue(idx);
975
if(d=="roundabout") return MapFeature::OneWay;
976
//TODO For motorway and motorway_link, this is still discussed on the wiki.
978
return MapFeature::UnknownDirection;
981
int findSnapPointIndex(const Road* R, Coord& P)
983
if (R->smoothed().size())
985
BezierF L(R->smoothed()[0],R->smoothed()[1],R->smoothed()[2],R->smoothed()[3]);
987
double BestDistance = L.distance(toQt(P));
988
for (int i=3; i<R->smoothed().size(); i+=3)
990
BezierF L(R->smoothed()[i-3],R->smoothed()[i-2],R->smoothed()[i-1],R->smoothed()[i]);
991
double Distance = L.distance(toQt(P));
992
if (Distance < BestDistance)
995
BestDistance = Distance;
998
BezierF B(R->smoothed()[BestIdx-3],R->smoothed()[BestIdx-2],R->smoothed()[BestIdx-1],R->smoothed()[BestIdx]);
999
P = toCoord(B.project(toQt(P)));
1002
LineF L(R->getNode(0)->position(),R->getNode(1)->position());
1004
double BestDistance = L.capDistance(P);
1005
for (int i = 2; i<R->size(); ++i)
1007
LineF L(R->getNode(i-1)->position(),R->getNode(i)->position());
1008
double Distance = L.capDistance(P);
1009
if (Distance < BestDistance)
1012
BestDistance = Distance;
1015
LineF F(R->getNode(BestIdx-1)->position(),R->getNode(BestIdx)->position());
1016
P = F.project(Coord(P));
1020
const QList<Coord>& Road::smoothed() const
1022
if (!p->SmoothedUpToDate)
1023
p->updateSmoothed(tagValue("smooth","") == "yes");
1027
QString Road::toHtml()
1029
QString distanceLabel;
1030
if (distance() < 1.0)
1031
distanceLabel = QString("%1 m").arg(int(distance() * 1000));
1033
distanceLabel = QString("%1 km").arg(distance(), 0, 'f', 3);
1037
D += "<i>"+QApplication::translate("MapFeature", "Length")+": </i>" + distanceLabel;
1039
D += "<i>"+QApplication::translate("MapFeature", "Size")+": </i>" + QApplication::translate("MapFeature", "%1 nodes").arg(size());
1040
CoordBox bb = boundingBox();
1042
D += "<i>"+QApplication::translate("MapFeature", "Topleft")+": </i>" + QString::number(intToAng(bb.topLeft().lat()), 'f', 4) + " / " + QString::number(intToAng(bb.topLeft().lon()), 'f', 4);
1044
D += "<i>"+QApplication::translate("MapFeature", "Botright")+": </i>" + QString::number(intToAng(bb.bottomRight().lat()), 'f', 4) + " / " + QString::number(intToAng(bb.bottomRight().lon()), 'f', 4);
1046
QString type = isClosed() ? QApplication::translate("MapFeature", "Area") : QApplication::translate("MapFeature", "Way");
1047
return MapFeature::toMainHtml(type, "way").arg(D);
1050
void Road::toBinary(QDataStream& ds, QHash <QString, quint64>& theIndex)
1058
theIndex[type + QString::number(idToLong())] = ds.device()->pos();
1062
for (int i=0; i < sz; ++i) {
1063
TrackPoint* N = CAST_NODE(get(i));
1064
if (N->sizeParents() > 1)
1065
ds << (qint8)'N' << (qint64)(N->idToLong());
1067
ds << (qint8)'C' << N->position().lat() << N->position().lon();
1071
Road* Road::fromBinary(MapDocument* d, OsbMapLayer* L, QDataStream& ds, qint8 c, qint64 id)
1085
for (int i=0; i < fSize; ++i) {
1093
strId = QString::number(id);
1095
strId = QString("way_%1").arg(QString::number(id));
1097
Road* R = dynamic_cast<Road*>(d->getFeature(strId));
1101
R->setLastUpdated(MapFeature::OSMServer);
1103
if (R->lastUpdated() == MapFeature::NotYetDownloaded) {
1104
R->setLastUpdated(MapFeature::OSMServer);
1108
for (int i=0; i < fSize; ++i) {
1116
TrackPoint* N = NULL;
1117
TrackPoint* firstPoint = NULL;
1118
for (int i=0; i < fSize; ++i) {
1125
N = new TrackPoint(Coord(lat, lon));
1130
N = CAST_NODE(d->getFeature(QString("node_%1").arg(refId)));
1138
if (type == 'A' && firstPoint)
1146
bool Road::isExtrimity(TrackPoint* node)
1148
if (p->Nodes[0] == node)
1151
if (p->Nodes[p->Nodes.size()-1] == node)
1157
Road * Road::GetSingleParentRoad(MapFeature * mapFeature)
1159
int parents = mapFeature->sizeParents();
1164
Road * parentRoad = NULL;
1167
for (i=0; i<parents; i++)
1169
MapFeature * parent = mapFeature->getParent(i);
1170
Road * road = CAST_WAY(parent);
1175
if (parentRoad && road != parentRoad)
1178
//FIXME This test shouldn't be necessary, but there is at least a case where the road has a NULL layer. The root cause must be found.
1179
if (!(road->isDeleted()) && road->layer() && road->layer()->isEnabled())
1186
Road * Road::GetSingleParentRoadInner(MapFeature * mapFeature)
1188
int parents = mapFeature->sizeParents();
1193
Road * parentRoad = NULL;
1194
TrackPoint* trackPoint = dynamic_cast<TrackPoint*>(mapFeature);
1197
for (i=0; i<parents; i++)
1199
MapFeature * parent = mapFeature->getParent(i);
1200
Road * road = dynamic_cast<Road*>(parent);
1205
if (road->isExtrimity(trackPoint) && !road->isClosed())
1208
if (parentRoad && road != parentRoad)
1211
//FIXME This test shouldn't be necessary, but there is at least a case where the road has a NULL layer. The root cause must be found.
1212
if (!(road->isDeleted()) && road->layer() && road->layer()->isEnabled())