~ubuntu-branches/ubuntu/saucy/merkaartor/saucy

« back to all changes in this revision

Viewing changes to src/Maps/Road.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bernd Zeimetz
  • Date: 2009-09-13 00:52:12 UTC
  • mto: (1.2.7 upstream) (0.1.3 upstream) (3.1.7 sid)
  • mto: This revision was merged to the branch mainline in revision 10.
  • Revision ID: james.westby@ubuntu.com-20090913005212-pjecal8zxm07x0fj
ImportĀ upstreamĀ versionĀ 0.14+svnfixes~20090912

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include "Maps/Road.h"
 
2
 
 
3
#include "Command/DocumentCommands.h"
 
4
#include "Command/RoadCommands.h"
 
5
#include "Maps/Coord.h"
 
6
#include "Maps/Painting.h"
 
7
#include "MapView.h"
 
8
#include "Maps/TrackPoint.h"
 
9
#include "Utils/LineF.h"
 
10
#include "Utils/MDiscardableDialog.h"
 
11
 
 
12
#include <QtGui/QPainter>
 
13
#include <QtGui/QPainterPath>
 
14
#include <QMessageBox>
 
15
#include <QProgressDialog>
 
16
 
 
17
#include <algorithm>
 
18
#include <QList>
 
19
 
 
20
#ifndef _MOBILE
 
21
#include <ggl/ggl.hpp>
 
22
#include <ggl/geometries/cartesian2d.hpp>
 
23
 
 
24
#include <ggl/geometries/adapted/std_as_linestring.hpp>
 
25
#endif
 
26
 
 
27
 
 
28
class RoadPrivate
 
29
{
 
30
        public:
 
31
                RoadPrivate()
 
32
                : SmoothedUpToDate(false), BBox(Coord(0,0),Coord(0,0)), BBoxUpToDate(false), Area(0), Distance(0), Width(0),
 
33
                        wasPathComplete(false), ProjectionRevision(0)
 
34
                {
 
35
                }
 
36
                std::vector<TrackPointPtr> Nodes;
 
37
                QList<Coord> Smoothed;
 
38
                bool SmoothedUpToDate;
 
39
                CoordBox BBox;
 
40
                bool BBoxUpToDate;
 
41
 
 
42
                bool IsCoastline;
 
43
                double Area;
 
44
                double Distance;
 
45
                double Width;
 
46
        bool NotEverythingDownloaded;
 
47
                bool wasPathComplete;
 
48
                RenderPriority theRenderPriority;
 
49
                QPainterPath thePath;
 
50
#ifndef _MOBILE
 
51
                int ProjectionRevision;
 
52
#endif
 
53
 
 
54
                void updateSmoothed(bool DoSmooth);
 
55
                void addSmoothedBezier(int i, int j, int k, int l);
 
56
};
 
57
 
 
58
void RoadPrivate::addSmoothedBezier(int i, int j, int k, int l)
 
59
{
 
60
        Coord A(Nodes[i]->position());
 
61
        Coord B(Nodes[j]->position());
 
62
        Coord C(Nodes[k]->position());
 
63
        Coord D(Nodes[l]->position());
 
64
 
 
65
        Coord Ctrl1(B+(C-A)*(1/6));
 
66
        Coord Ctrl2(C-(D-B)*(1/6));
 
67
 
 
68
 
 
69
        Smoothed.push_back(Ctrl1);
 
70
        Smoothed.push_back(Ctrl2);
 
71
        Smoothed.push_back(C);
 
72
}
 
73
 
 
74
void RoadPrivate::updateSmoothed(bool DoSmooth)
 
75
{
 
76
        SmoothedUpToDate = true;
 
77
        Smoothed.clear();
 
78
        if ( (Nodes.size() < 3) || !DoSmooth )
 
79
                return;
 
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);
 
86
}
 
87
 
 
88
Road::Road(void)
 
89
: p(new RoadPrivate)
 
90
{
 
91
}
 
92
 
 
93
Road::Road(const Road& other)
 
94
: MapFeature(other), p(new RoadPrivate)
 
95
{
 
96
}
 
97
 
 
98
Road::~Road(void)
 
99
{
 
100
        for (unsigned int i=0; i<p->Nodes.size(); ++i)
 
101
                if (p->Nodes[i])
 
102
                        p->Nodes[i]->unsetParentFeature(this);
 
103
        delete p;
 
104
}
 
105
 
 
106
void Road::setLayer(MapLayer* L)
 
107
{
 
108
        if (L)
 
109
                for (unsigned int i=0; i<p->Nodes.size(); ++i)
 
110
                        if (p->Nodes[i])
 
111
                                p->Nodes[i]->setParentFeature(this);
 
112
        else
 
113
                for (unsigned int i=0; i<p->Nodes.size(); ++i)
 
114
                        if (p->Nodes[i])
 
115
                                p->Nodes[i]->unsetParentFeature(this);
 
116
        MapFeature::setLayer(L);
 
117
}
 
118
 
 
119
void Road::partChanged(MapFeature*, int ChangeId)
 
120
{
 
121
        if (isDeleted())
 
122
                return;
 
123
 
 
124
        p->BBoxUpToDate = false;
 
125
        MetaUpToDate = false;
 
126
        p->SmoothedUpToDate = false;
 
127
        p->wasPathComplete = false;
 
128
        notifyParents(ChangeId);
 
129
}
 
130
 
 
131
QString Road::description() const
 
132
{
 
133
        QString s(tagValue("name",""));
 
134
        if (!s.isEmpty())
 
135
                return QString("%1 (%2)").arg(s).arg(id());
 
136
        return QString("%1").arg(id());
 
137
}
 
138
 
 
139
RenderPriority Road::renderPriority() 
 
140
{
 
141
        // FIWME Segments of a road with different layers are wrongly painted (rounded corners)
 
142
        return p->theRenderPriority;
 
143
}
 
144
 
 
145
RenderPriority Road::getRenderPriority()
 
146
{
 
147
        return p->theRenderPriority;
 
148
}
 
149
 
 
150
void Road::add(TrackPoint* Pt)
 
151
{
 
152
        if (layer())
 
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;
 
160
        if (layer()) {
 
161
                CoordBox bb = boundingBox();
 
162
                layer()->indexAdd(bb, this);
 
163
        }
 
164
}
 
165
 
 
166
void Road::add(TrackPoint* Pt, int Idx)
 
167
{
 
168
        if (layer())
 
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;
 
177
        if (layer()) {
 
178
                CoordBox bb = boundingBox();
 
179
                layer()->indexAdd(bb, this);
 
180
        }
 
181
}
 
182
 
 
183
int Road::find(MapFeature* Pt) const
 
184
{
 
185
        for (unsigned int i=0; i<p->Nodes.size(); ++i)
 
186
                if (p->Nodes[i] == Pt)
 
187
                        return i;
 
188
        return p->Nodes.size();
 
189
}
 
190
 
 
191
void Road::remove(int idx)
 
192
{
 
193
        if (layer())
 
194
                layer()->indexRemove(p->BBox, this);
 
195
        if (p->Nodes[idx]) {
 
196
                TrackPoint* Pt = p->Nodes[idx];
 
197
                Pt->unsetParentFeature(this);
 
198
        }
 
199
        p->Nodes.erase(p->Nodes.begin()+idx);
 
200
        p->BBoxUpToDate = false;
 
201
        MetaUpToDate = false;
 
202
        p->SmoothedUpToDate = false;
 
203
        p->wasPathComplete = false;
 
204
        if (layer()) {
 
205
                CoordBox bb = boundingBox();
 
206
                layer()->indexAdd(bb, this);
 
207
        }
 
208
}
 
209
 
 
210
void Road::remove(MapFeature* F)
 
211
{
 
212
        for (int i=p->Nodes.size(); i; --i)
 
213
                if (p->Nodes[i-1] == F)
 
214
                        remove(i-1);
 
215
}
 
216
 
 
217
int Road::size() const
 
218
{
 
219
        return p->Nodes.size();
 
220
}
 
221
 
 
222
TrackPoint* Road::getNode(int idx)
 
223
{
 
224
        return p->Nodes[idx];
 
225
}
 
226
 
 
227
const TrackPoint* Road::getNode(int idx) const
 
228
{
 
229
        return p->Nodes[idx];
 
230
}
 
231
 
 
232
const std::vector<TrackPointPtr>& Road::getNodes() const
 
233
{
 
234
        return p->Nodes;
 
235
}
 
236
 
 
237
 
 
238
MapFeature* Road::get(int idx)
 
239
{
 
240
        return p->Nodes[idx];
 
241
}
 
242
 
 
243
const MapFeature* Road::get(int idx) const
 
244
{
 
245
        return p->Nodes[idx];
 
246
}
 
247
 
 
248
bool Road::isNull() const
 
249
{
 
250
        return (p->Nodes.size() == 0);
 
251
}
 
252
 
 
253
bool Road::notEverythingDownloaded()
 
254
{
 
255
        if (MetaUpToDate == false)
 
256
                updateMeta();
 
257
 
 
258
        return p->NotEverythingDownloaded;
 
259
}
 
260
 
 
261
CoordBox Road::boundingBox() const
 
262
{
 
263
        if (!p->BBoxUpToDate)
 
264
        {
 
265
                if (p->Nodes.size())
 
266
                {
 
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());
 
270
                }
 
271
                else
 
272
                        p->BBox = CoordBox(Coord(0,0),Coord(0,0));
 
273
                p->BBoxUpToDate = true;
 
274
        }
 
275
        return p->BBox;
 
276
}
 
277
 
 
278
void Road::updateMeta()
 
279
{
 
280
        p->Area = 0;
 
281
        p->Distance = 0;
 
282
        p->IsCoastline = false;
 
283
 
 
284
        p->NotEverythingDownloaded = false;
 
285
        if (lastUpdated() == MapFeature::NotYetDownloaded)
 
286
                p->NotEverythingDownloaded = true;
 
287
        else
 
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;
 
291
                                break;
 
292
                        }
 
293
 
 
294
        if (p->Nodes.size() == 0)
 
295
        {
 
296
                MetaUpToDate = true;
 
297
                return;
 
298
        }
 
299
 
 
300
        bool isArea = (p->Nodes[0] == p->Nodes[p->Nodes.size()-1]);
 
301
 
 
302
        for (unsigned int i=0; (i+1)<p->Nodes.size(); ++i)
 
303
        {
 
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();
 
307
 
 
308
                        p->Distance += next.distanceFrom(here);
 
309
                        //if (isArea)
 
310
                                //p->Area += here.lat() * next.lon() - next.lat() * here.lon();
 
311
                }
 
312
        }
 
313
 
 
314
        if (isArea) {
 
315
                p->Area = p->Distance;
 
316
                p->theRenderPriority = RenderPriority(RenderPriority::IsArea,-fabs(p->Area));
 
317
        } else {
 
318
                qreal Priority = tagValue("layer","0").toInt();
 
319
                if (Priority >= 0)
 
320
                        Priority++;
 
321
                // dummy number to get a deterministic feature sort
 
322
                Priority *= sin(intToRad(boundingBox().lonDiff()));
 
323
                p->theRenderPriority = RenderPriority(RenderPriority::IsLinear,Priority);
 
324
        }
 
325
 
 
326
        if (tagValue("natural","") == "coastline")
 
327
                p->IsCoastline = true;
 
328
 
 
329
    
 
330
        MetaUpToDate = true;
 
331
}
 
332
 
 
333
double Road::distance()
 
334
{
 
335
        if (MetaUpToDate == false)
 
336
                updateMeta();
 
337
 
 
338
        return p->Distance;
 
339
}
 
340
 
 
341
bool Road::isCoastline()
 
342
{
 
343
        if (MetaUpToDate == false)
 
344
                updateMeta();
 
345
 
 
346
        return p->IsCoastline;
 
347
}
 
348
 
 
349
bool Road::isClosed() const
 
350
{
 
351
        return (p->Nodes.size() && p->Nodes[0] == p->Nodes[p->Nodes.size()-1]);
 
352
}
 
353
 
 
354
double Road::area()
 
355
{
 
356
        if (MetaUpToDate == false)
 
357
                updateMeta();
 
358
 
 
359
        return p->Area;
 
360
}
 
361
 
 
362
void Road::draw(QPainter&, MapView* )
 
363
{
 
364
}
 
365
 
 
366
void Road::drawHover(QPainter& thePainter, MapView* theView, bool solid)
 
367
{
 
368
        // FIXME Selected route
 
369
        if (!size())
 
370
                return;
 
371
 
 
372
        QFont F(thePainter.font());
 
373
        F.setPointSize(10);
 
374
        F.setBold(true);
 
375
        F.setWeight(QFont::Black);
 
376
        thePainter.setFont(F);
 
377
        QPen TP(MerkaartorPreferences::instance()->getHoverColor());
 
378
        TP.setWidth(MerkaartorPreferences::instance()->getHoverWidth());
 
379
        if (!solid) {
 
380
                TP.setDashPattern(M_PREFS->getParentDashes());
 
381
        }
 
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));
 
387
        if (solid) {
 
388
                TP.setWidth(MerkaartorPreferences::instance()->getHoverWidth()*3);
 
389
                TP.setCapStyle(Qt::RoundCap);
 
390
                thePainter.setPen(TP);
 
391
                QPolygonF Pl;
 
392
                buildPolygonFromRoad(this,theView->projection(),Pl);
 
393
                thePainter.drawPoints(theView->transform().map(Pl));
 
394
 
 
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);
 
399
                }
 
400
        }
 
401
}
 
402
 
 
403
void Road::drawHighlight(QPainter& thePainter, MapView* theView, bool solid)
 
404
{
 
405
        // FIXME Selected route
 
406
        if (!size())
 
407
                return;
 
408
 
 
409
        QFont F(thePainter.font());
 
410
        F.setPointSize(10);
 
411
        F.setBold(true);
 
412
        F.setWeight(QFont::Black);
 
413
        thePainter.setFont(F);
 
414
        QPen TP(MerkaartorPreferences::instance()->getHighlightColor());
 
415
        TP.setWidth(MerkaartorPreferences::instance()->getHighlightWidth());
 
416
        if (!solid) {
 
417
                TP.setDashPattern(M_PREFS->getParentDashes());
 
418
        }
 
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));
 
424
        if (solid) {
 
425
                TP.setWidth(MerkaartorPreferences::instance()->getHighlightWidth()*3);
 
426
                TP.setCapStyle(Qt::RoundCap);
 
427
                thePainter.setPen(TP);
 
428
                QPolygonF Pl;
 
429
                buildPolygonFromRoad(this,theView->projection(),Pl);
 
430
                thePainter.drawPoints(theView->transform().map(Pl));
 
431
 
 
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);
 
436
//              }
 
437
        }
 
438
}
 
439
 
 
440
void Road::drawFocus(QPainter& thePainter, MapView* theView, bool solid)
 
441
{
 
442
        // FIXME Selected route
 
443
        if (!size())
 
444
                return;
 
445
 
 
446
        QFont F(thePainter.font());
 
447
        F.setPointSize(10);
 
448
        F.setBold(true);
 
449
        F.setWeight(QFont::Black);
 
450
        thePainter.setFont(F);
 
451
        QPen TP(MerkaartorPreferences::instance()->getFocusColor());
 
452
        TP.setWidth(MerkaartorPreferences::instance()->getFocusWidth());
 
453
        if (!solid) {
 
454
                TP.setDashPattern(M_PREFS->getParentDashes());
 
455
        }
 
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));
 
461
        if (solid) {
 
462
                TP.setWidth(MerkaartorPreferences::instance()->getFocusWidth()*3);
 
463
                TP.setCapStyle(Qt::RoundCap);
 
464
                thePainter.setPen(TP);
 
465
                QPolygonF Pl;
 
466
                buildPolygonFromRoad(this,theView->projection(),Pl);
 
467
                thePainter.drawPoints(theView->transform().map(Pl));
 
468
 
 
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);
 
473
                }
 
474
        }
 
475
}
 
476
 
 
477
double Road::pixelDistance(const QPointF& Target, double ClearEndDistance, const Projection& theProjection, const QTransform& theTransform) const
 
478
{
 
479
        double Best = 1000000;
 
480
        for (unsigned int i=0; i<p->Nodes.size(); ++i)
 
481
        {
 
482
                if (p->Nodes.at(i)) {
 
483
                        double x = ::distance(Target,theTransform.map(theProjection.project(p->Nodes.at(i))));
 
484
                        if (x<ClearEndDistance)
 
485
                                return Best;
 
486
                }
 
487
        }
 
488
        if (smoothed().size())
 
489
                for (int i=3; i <p->Smoothed.size(); i += 3)
 
490
                {
 
491
                        BezierF F(
 
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)
 
498
                                Best = D;
 
499
                }
 
500
        else
 
501
                for (unsigned int i=1; i<p->Nodes.size(); ++i)
 
502
                {
 
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)
 
507
                                        Best = D;
 
508
                        }
 
509
                }
 
510
        return Best;
 
511
}
 
512
 
 
513
void Road::cascadedRemoveIfUsing(MapDocument* theDocument, MapFeature* aFeature, CommandList* theList, const QList<MapFeature*>& Proposals)
 
514
{
 
515
        for (unsigned int i=0; i<p->Nodes.size();) {
 
516
                if (p->Nodes[i] == aFeature)
 
517
                {
 
518
                        QList<TrackPoint*> Alternatives;
 
519
                        for (int j=0; j<Proposals.size(); ++j)
 
520
                        {
 
521
                                TrackPoint* Pt = dynamic_cast<TrackPoint*>(Proposals[j]);
 
522
                                if (Pt)
 
523
                                        Alternatives.push_back(Pt);
 
524
                        }
 
525
                        if ( (p->Nodes.size() == 1) && (Alternatives.size() == 0) ) {
 
526
                                if (!isDeleted())
 
527
                                        theList->add(new RemoveFeatureCommand(theDocument,this));
 
528
                        }
 
529
                        else
 
530
                        {
 
531
                                for (int j=0; j<Alternatives.size(); ++j)
 
532
                                {
 
533
                                        if (i < p->Nodes.size())
 
534
                                        {
 
535
                                                if (p->Nodes[i+j] != Alternatives[j])
 
536
                                                {
 
537
                                                        if ((i+j) == 0)
 
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())));
 
541
                                                }
 
542
                                        }
 
543
                                }
 
544
                                theList->add(new RoadRemoveTrackPointCommand(this, (TrackPoint*)aFeature,theDocument->getDirtyOrOriginLayer(layer())));
 
545
                        }
 
546
                }
 
547
                ++i;
 
548
        }
 
549
}
 
550
 
 
551
QPainterPath Road::getPath()
 
552
{
 
553
        return p->thePath;
 
554
}
 
555
 
 
556
#ifndef _MOBILE
 
557
void Road::buildPath(const Projection &theProjection, const QTransform& /*theTransform*/, const QRectF& cr)
 
558
{
 
559
        using namespace ggl;
 
560
 
 
561
        if (p->Nodes.size() < 2)
 
562
                return;
 
563
 
 
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]);
 
567
 
 
568
        QPointF pbl = theProjection.project(p->BBox.bottomLeft());
 
569
    QPointF ptr = theProjection.project(p->BBox.topRight());
 
570
    box_2d roadRect (
 
571
        make<point_2d>(pbl.x(), pbl.y()), 
 
572
        make<point_2d>(ptr.x(), ptr.y())
 
573
        );
 
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);
 
576
    if (!toClip) {
 
577
                if (!p->wasPathComplete || p->ProjectionRevision != theProjection.projectionRevision()) {
 
578
                        p->thePath = QPainterPath();
 
579
 
 
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());
 
583
                        }
 
584
                        p->ProjectionRevision = theProjection.projectionRevision();
 
585
                        p->wasPathComplete = true;
 
586
        }
 
587
    } else {
 
588
                p->thePath = QPainterPath();
 
589
                p->wasPathComplete = false;
 
590
 
 
591
            if (area() <= 0.0) {
 
592
//              linestring_2d in;
 
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()));
 
596
//              }
 
597
 
 
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));
 
601
 
 
602
                for (std::vector<linestring_2d>::const_iterator it = clipped.begin(); it != clipped.end(); it++)
 
603
                {
 
604
                        if (!(*it).empty()) {
 
605
                                p->thePath.moveTo(QPointF((*it)[0].x(), (*it)[0].y()));
 
606
                        }
 
607
                        for (linestring_2d::const_iterator itl = (*it).begin()+1; itl != (*it).end(); itl++)
 
608
                        {
 
609
                                p->thePath.lineTo(QPointF((*itl).x(), (*itl).y()));
 
610
                        }
 
611
                }
 
612
            } 
 
613
            else 
 
614
            {
 
615
                        polygon_2d in;
 
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()));
 
619
                        }
 
620
                correct(in);
 
621
 
 
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));
 
625
 
 
626
                for (std::vector<polygon_2d>::const_iterator it = clipped.begin(); it != clipped.end(); it++)
 
627
                {
 
628
                        if (!(*it).outer().empty()) {
 
629
                                p->thePath.moveTo(QPointF((*it).outer()[0].x(), (*it).outer()[0].y()));
 
630
                        }
 
631
                        for (ring_2d::const_iterator itl = (*it).outer().begin()+1; itl != (*it).outer().end()-1; itl++)
 
632
                        {
 
633
                                p->thePath.lineTo(QPointF((*itl).x(), (*itl).y()));
 
634
                        }
 
635
                        p->thePath.lineTo(QPointF((*it).outer()[0].x(), (*it).outer()[0].y()));
 
636
                }
 
637
        }
 
638
        }
 
639
        //p->thePath = theTransform.map(p->thePath);
 
640
}
 
641
#else
 
642
void Road::buildPath(Projection const &theProjection, const QRect& clipRect)
 
643
{
 
644
        p->thePath = QPainterPath();
 
645
        if (!p->Nodes.size())
 
646
                return;
 
647
 
 
648
        bool lastPointVisible = true;
 
649
        QPoint lastPoint = theProjection.project(p->Nodes[0]);
 
650
        QPoint aP = lastPoint;
 
651
 
 
652
        double PixelPerM = theProjection.pixelPerM();
 
653
        double WW = PixelPerM*widthOf()*10+10;
 
654
 
 
655
 
 
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;
 
663
                }
 
664
        }
 
665
        p->thePath.moveTo(aP);
 
666
        QPoint firstPoint = aP;
 
667
        if (smoothed().size())
 
668
        {
 
669
                for (int i=3; i<smoothed().size(); i+=3)
 
670
                        p->thePath.cubicTo(
 
671
                                theProjection.project(smoothed()[i-2]),
 
672
                                theProjection.project(smoothed()[i-1]),
 
673
                                theProjection.project(smoothed()[i]));
 
674
        }
 
675
        else
 
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) {
 
682
                                                QPoint a, b;
 
683
                                                if (QRectInterstects(clipRect, l, a, b)) {
 
684
                                                        p->thePath.lineTo(a);
 
685
                                                        lastPoint = aP;
 
686
                                                        aP = b;
 
687
                                                } else {
 
688
                                                        lastPoint = aP;
 
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()));
 
693
                                                }
 
694
                                        } else {
 
695
                                                QPoint a, b;
 
696
                                                QRectInterstects(clipRect, l, a, b);
 
697
                                                lastPoint = aP;
 
698
                                                aP = a;
 
699
                                        }
 
700
                                        lastPointVisible = false;
 
701
                                } else {
 
702
                                        if (!lastPointVisible) {
 
703
                                                QPoint a, b;
 
704
                                                QRectInterstects(clipRect, l, a, b);
 
705
                                                p->thePath.lineTo(a);
 
706
                                        }
 
707
                                        lastPoint = aP;
 
708
                                        lastPointVisible = true;
 
709
                                }
 
710
                        }
 
711
                        p->thePath.lineTo(aP);
 
712
                }
 
713
                if (area() > 0.0 && !lastPointVisible)
 
714
                        p->thePath.lineTo(firstPoint);
 
715
}
 
716
#endif
 
717
 
 
718
 
 
719
bool Road::deleteChildren(MapDocument* theDocument, CommandList* theList)
 
720
{
 
721
        if (lastUpdated() == MapFeature::OSMServerConflict)
 
722
                return true;
 
723
 
 
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) {
 
733
                                ToDelete[N] = i;
 
734
                        }
 
735
                }
 
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));
 
740
                }
 
741
        }
 
742
        return true;
 
743
}
 
744
 
 
745
#define DEFAULTWIDTH 6
 
746
#define LANEWIDTH 4
 
747
 
 
748
double Road::widthOf()
 
749
{
 
750
        if (p->Width)
 
751
                return p->Width;
 
752
 
 
753
        QString s(tagValue("width",QString()));
 
754
        if (!s.isNull())
 
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")
 
768
                p->Width =  1.5;
 
769
        p->Width = DEFAULTWIDTH;
 
770
 
 
771
        return p->Width;
 
772
}
 
773
 
 
774
void Road::setTag(const QString& key, const QString& value, bool addToTagList)
 
775
{
 
776
        MapFeature::setTag(key, value, addToTagList);
 
777
        MetaUpToDate = false;
 
778
        p->Width = 0;
 
779
}
 
780
 
 
781
void Road::setTag(int index, const QString& key, const QString& value, bool addToTagList)
 
782
{
 
783
        MapFeature::setTag(index, key, value, addToTagList);
 
784
        MetaUpToDate = false;
 
785
        p->Width = 0;
 
786
}
 
787
 
 
788
void Road::clearTags()
 
789
{
 
790
        MapFeature::clearTags();
 
791
        MetaUpToDate = false;
 
792
        p->Width = 0;
 
793
}
 
794
 
 
795
void Road::clearTag(const QString& k)
 
796
{
 
797
        MapFeature::clearTag(k);
 
798
        MetaUpToDate = false;
 
799
        p->Width = 0;
 
800
}
 
801
 
 
802
bool Road::toGPX(QDomElement xParent, QProgressDialog & progress, bool forExport)
 
803
{
 
804
        bool OK = true;
 
805
 
 
806
        QDomElement e = xParent.ownerDocument().createElement("rte");
 
807
        xParent.appendChild(e);
 
808
 
 
809
        if (!forExport)
 
810
                e.setAttribute("xml:id", xmlId());
 
811
        QString s = tagValue("name","");
 
812
        if (!s.isEmpty()) {
 
813
                QDomElement c = xParent.ownerDocument().createElement("name");
 
814
                e.appendChild(c);
 
815
                QDomText v = c.ownerDocument().createTextNode(s);
 
816
                c.appendChild(v);
 
817
        }
 
818
        s = tagValue("_comment_","");
 
819
        if (!s.isEmpty()) {
 
820
                QDomElement c = xParent.ownerDocument().createElement("cmt");
 
821
                e.appendChild(c);
 
822
                QDomText v = c.ownerDocument().createTextNode(s);
 
823
                c.appendChild(v);
 
824
        }
 
825
        s = tagValue("_description_","");
 
826
        if (!s.isEmpty()) {
 
827
                QDomElement c = xParent.ownerDocument().createElement("desc");
 
828
                e.appendChild(c);
 
829
                QDomText v = c.ownerDocument().createTextNode(s);
 
830
                c.appendChild(v);
 
831
        }
 
832
 
 
833
        for (int i=0; i<size(); ++i) {
 
834
                dynamic_cast <TrackPoint*> (get(i))->toGPX(e, progress, forExport);
 
835
        }
 
836
 
 
837
        return OK;
 
838
}
 
839
 
 
840
QString Road::toXML(int lvl, QProgressDialog * progress)
 
841
{
 
842
        if (!size()) return "";
 
843
 
 
844
        if (progress)
 
845
                progress->setValue(progress->value()+1);
 
846
 
 
847
        QString S(lvl*2, ' ');
 
848
        S += QString("<way id=\"%1\">\n").arg(stripToOSMId(id()));
 
849
 
 
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";
 
856
        return S;
 
857
}
 
858
 
 
859
bool Road::toXML(QDomElement xParent, QProgressDialog & progress)
 
860
{
 
861
        bool OK = true;
 
862
 
 
863
        QDomElement e = xParent.ownerDocument().createElement("way");
 
864
        xParent.appendChild(e);
 
865
 
 
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());
 
871
        if (isDeleted())
 
872
                e.setAttribute("deleted","true");
 
873
 
 
874
        if (size()) {
 
875
                QDomElement n = xParent.ownerDocument().createElement("nd");
 
876
                e.appendChild(n);
 
877
                n.setAttribute("ref", get(0)->xmlId());
 
878
 
 
879
                for (int i=1; i<size(); ++i) {
 
880
                        if (get(i)->xmlId() != get(i-1)->xmlId()) {
 
881
                                QDomElement n = xParent.ownerDocument().createElement("nd");
 
882
                                e.appendChild(n);
 
883
 
 
884
                                n.setAttribute("ref", get(i)->xmlId());
 
885
                        }
 
886
                }
 
887
        }
 
888
 
 
889
        tagsToXML(e);
 
890
 
 
891
        progress.setValue(progress.value()+1);
 
892
        return OK;
 
893
}
 
894
 
 
895
Road * Road::fromXML(MapDocument* d, MapLayer * L, const QDomElement e)
 
896
{
 
897
        QString id = e.attribute("id");
 
898
        if (!id.startsWith('{') && !id.startsWith('-'))
 
899
                id = "way_" + id;
 
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());
 
907
        else
 
908
                if ((L == d->getDirtyOrOriginLayer()))
 
909
                        A = MapFeature::User;
 
910
                else
 
911
                        A = MapFeature::OSMServer;
 
912
 
 
913
        Road* R = dynamic_cast<Road*>(d->getFeature(id));
 
914
 
 
915
        if (!R) {
 
916
                R = new Road();
 
917
                R->setId(id);
 
918
                R->setLastUpdated(A);
 
919
                L->add(R);
 
920
        } else {
 
921
                if (R->layer() != L) {
 
922
                        R->layer()->remove(R);
 
923
                        L->add(R);
 
924
                }
 
925
                if (R->lastUpdated() == MapFeature::NotYetDownloaded)
 
926
                        R->setLastUpdated(A);
 
927
        }
 
928
        R->setTime(time);
 
929
        R->setUser(user);
 
930
        R->setDeleted(Deleted);
 
931
        R->setVersionNumber(Version);
 
932
 
 
933
        QDomElement c = e.firstChildElement();
 
934
        while(!c.isNull()) {
 
935
                if (c.tagName() == "nd") {
 
936
                        QString nId = c.attribute("ref");
 
937
                        if (!nId.startsWith('{') && !nId.startsWith('-'))
 
938
                                nId = "node_" + nId;
 
939
                        //TrackPoint* Part = MapFeature::getTrackPointOrCreatePlaceHolder(d, L, NULL, c.attribute("ref"));
 
940
                        TrackPoint* Part = dynamic_cast<TrackPoint*>(d->getFeature(nId));
 
941
                        if (!Part)
 
942
                        {
 
943
                                Part = new TrackPoint(Coord(0,0));
 
944
                                Part->setId(nId);
 
945
                                Part->setLastUpdated(MapFeature::NotYetDownloaded);
 
946
                                L->add(Part);
 
947
                        }
 
948
                        R->add(Part);
 
949
                }
 
950
                c = c.nextSiblingElement();
 
951
        }
 
952
 
 
953
        MapFeature::tagsFromXML(d, R, e);
 
954
 
 
955
        return R;
 
956
}
 
957
 
 
958
MapFeature::TrafficDirectionType trafficDirection(const Road* R)
 
959
{
 
960
        // TODO some duplication with Way trafficDirection
 
961
        QString d;
 
962
        int idx=R->findKey("oneway");
 
963
        if (idx<R->tagSize())
 
964
        {
 
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;
 
969
        }
 
970
 
 
971
        idx=R->findKey("junction");
 
972
        if (idx<R->tagSize())
 
973
        {
 
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.
 
977
        }
 
978
        return MapFeature::UnknownDirection;
 
979
}
 
980
 
 
981
int findSnapPointIndex(const Road* R, Coord& P)
 
982
{
 
983
        if (R->smoothed().size())
 
984
        {
 
985
                BezierF L(R->smoothed()[0],R->smoothed()[1],R->smoothed()[2],R->smoothed()[3]);
 
986
                int BestIdx = 3;
 
987
                double BestDistance = L.distance(toQt(P));
 
988
                for (int i=3; i<R->smoothed().size(); i+=3)
 
989
                {
 
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)
 
993
                        {
 
994
                                BestIdx = i;
 
995
                                BestDistance = Distance;
 
996
                        }
 
997
                }
 
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)));
 
1000
                return BestIdx/3;
 
1001
        }
 
1002
        LineF L(R->getNode(0)->position(),R->getNode(1)->position());
 
1003
        int BestIdx = 1;
 
1004
        double BestDistance = L.capDistance(P);
 
1005
        for (int i = 2; i<R->size(); ++i)
 
1006
        {
 
1007
                LineF L(R->getNode(i-1)->position(),R->getNode(i)->position());
 
1008
                double Distance = L.capDistance(P);
 
1009
                if (Distance < BestDistance)
 
1010
                {
 
1011
                        BestIdx = i;
 
1012
                        BestDistance = Distance;
 
1013
                }
 
1014
        }
 
1015
        LineF F(R->getNode(BestIdx-1)->position(),R->getNode(BestIdx)->position());
 
1016
        P = F.project(Coord(P));
 
1017
        return BestIdx;
 
1018
}
 
1019
 
 
1020
const QList<Coord>& Road::smoothed() const
 
1021
{
 
1022
        if (!p->SmoothedUpToDate)
 
1023
                p->updateSmoothed(tagValue("smooth","") == "yes");
 
1024
        return p->Smoothed;
 
1025
}
 
1026
 
 
1027
QString Road::toHtml()
 
1028
{
 
1029
        QString distanceLabel;
 
1030
        if (distance() < 1.0)
 
1031
                distanceLabel = QString("%1 m").arg(int(distance() * 1000));
 
1032
        else
 
1033
                distanceLabel = QString("%1 km").arg(distance(), 0, 'f', 3);
 
1034
 
 
1035
        QString D;
 
1036
 
 
1037
        D += "<i>"+QApplication::translate("MapFeature", "Length")+": </i>" + distanceLabel;
 
1038
        D += "<br/>";
 
1039
        D += "<i>"+QApplication::translate("MapFeature", "Size")+": </i>" + QApplication::translate("MapFeature", "%1 nodes").arg(size());
 
1040
        CoordBox bb = boundingBox();
 
1041
        D += "<br/>";
 
1042
        D += "<i>"+QApplication::translate("MapFeature", "Topleft")+": </i>" + QString::number(intToAng(bb.topLeft().lat()), 'f', 4) + " / " + QString::number(intToAng(bb.topLeft().lon()), 'f', 4);
 
1043
        D += "<br/>";
 
1044
        D += "<i>"+QApplication::translate("MapFeature", "Botright")+": </i>" + QString::number(intToAng(bb.bottomRight().lat()), 'f', 4) + " / " + QString::number(intToAng(bb.bottomRight().lon()), 'f', 4);
 
1045
 
 
1046
        QString type = isClosed() ? QApplication::translate("MapFeature", "Area") : QApplication::translate("MapFeature", "Way");
 
1047
        return MapFeature::toMainHtml(type, "way").arg(D);
 
1048
}
 
1049
 
 
1050
void Road::toBinary(QDataStream& ds, QHash <QString, quint64>& theIndex)
 
1051
{
 
1052
        char type = 'R';
 
1053
        qint32 sz = size();
 
1054
        if (isClosed()) {
 
1055
                type = 'A';
 
1056
                sz--;
 
1057
        }
 
1058
        theIndex[type  + QString::number(idToLong())] = ds.device()->pos();
 
1059
        ds << (qint8)type;
 
1060
        ds << idToLong();
 
1061
        ds << (qint32)sz;
 
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());
 
1066
                else
 
1067
                        ds << (qint8)'C' << N->position().lat() << N->position().lon();
 
1068
        }
 
1069
}
 
1070
 
 
1071
Road* Road::fromBinary(MapDocument* d, OsbMapLayer* L, QDataStream& ds, qint8 c, qint64 id)
 
1072
{
 
1073
        char type = c;
 
1074
 
 
1075
        qint32  fSize;
 
1076
        QString strId;
 
1077
        qint32 lat, lon;
 
1078
 
 
1079
        quint8 nodeType;
 
1080
        qint64 refId;
 
1081
 
 
1082
        ds >> fSize;
 
1083
 
 
1084
        if (!L) {
 
1085
                for (int i=0; i < fSize; ++i) {
 
1086
                        ds >> nodeType;
 
1087
                        ds >> refId;
 
1088
                }
 
1089
                return NULL;
 
1090
        }
 
1091
 
 
1092
        if (id < 1)
 
1093
                strId = QString::number(id);
 
1094
        else
 
1095
                strId = QString("way_%1").arg(QString::number(id));
 
1096
 
 
1097
        Road* R = dynamic_cast<Road*>(d->getFeature(strId));
 
1098
        if (!R) {
 
1099
                R = new Road();
 
1100
                R->setId(strId);
 
1101
                R->setLastUpdated(MapFeature::OSMServer);
 
1102
        } else {
 
1103
                if (R->lastUpdated() == MapFeature::NotYetDownloaded) {
 
1104
                        R->setLastUpdated(MapFeature::OSMServer);
 
1105
                        L->remove(R);
 
1106
                }
 
1107
                else  {
 
1108
                        for (int i=0; i < fSize; ++i) {
 
1109
                                ds >> nodeType;
 
1110
                                ds >> refId;
 
1111
                        }
 
1112
                        return R;
 
1113
                }
 
1114
        }
 
1115
 
 
1116
        TrackPoint* N = NULL;
 
1117
        TrackPoint* firstPoint = NULL;
 
1118
        for (int i=0; i < fSize; ++i) {
 
1119
                ds >> nodeType;
 
1120
                switch (nodeType) {
 
1121
                        case 'C':
 
1122
                                ds >> lat;
 
1123
                                ds >> lon;
 
1124
 
 
1125
                                N = new TrackPoint(Coord(lat, lon));
 
1126
                                N->setParent(R);
 
1127
                                break;
 
1128
                        case 'N':
 
1129
                                ds >> refId;
 
1130
                                N = CAST_NODE(d->getFeature(QString("node_%1").arg(refId)));
 
1131
                                Q_ASSERT(N);
 
1132
 
 
1133
                }
 
1134
                R->add(N);
 
1135
                if (!firstPoint)
 
1136
                        firstPoint = N;
 
1137
        }
 
1138
        if (type == 'A' && firstPoint)
 
1139
                R->add(firstPoint);
 
1140
 
 
1141
        L->add(R);
 
1142
 
 
1143
        return R;
 
1144
}
 
1145
 
 
1146
bool Road::isExtrimity(TrackPoint* node)
 
1147
{
 
1148
        if (p->Nodes[0] == node)
 
1149
                return true;
 
1150
 
 
1151
        if (p->Nodes[p->Nodes.size()-1] == node)
 
1152
                return true;
 
1153
 
 
1154
        return false;
 
1155
}
 
1156
 
 
1157
Road * Road::GetSingleParentRoad(MapFeature * mapFeature)
 
1158
{
 
1159
        int parents = mapFeature->sizeParents();
 
1160
 
 
1161
        if (parents == 0)
 
1162
                return NULL;
 
1163
 
 
1164
        Road * parentRoad = NULL;
 
1165
 
 
1166
        int i;
 
1167
        for (i=0; i<parents; i++)
 
1168
        {
 
1169
                MapFeature * parent = mapFeature->getParent(i);
 
1170
                Road * road = CAST_WAY(parent);
 
1171
 
 
1172
                if (road == NULL)
 
1173
                        continue;
 
1174
 
 
1175
                if (parentRoad && road != parentRoad)
 
1176
                        return NULL;
 
1177
 
 
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())
 
1180
                        parentRoad = road;
 
1181
        }
 
1182
 
 
1183
        return parentRoad;
 
1184
}
 
1185
 
 
1186
Road * Road::GetSingleParentRoadInner(MapFeature * mapFeature)
 
1187
{
 
1188
        int parents = mapFeature->sizeParents();
 
1189
 
 
1190
        if (parents == 0)
 
1191
                return NULL;
 
1192
 
 
1193
        Road * parentRoad = NULL;
 
1194
        TrackPoint* trackPoint = dynamic_cast<TrackPoint*>(mapFeature);
 
1195
 
 
1196
        int i;
 
1197
        for (i=0; i<parents; i++)
 
1198
        {
 
1199
                MapFeature * parent = mapFeature->getParent(i);
 
1200
                Road * road = dynamic_cast<Road*>(parent);
 
1201
 
 
1202
                if (road == NULL)
 
1203
                        continue;
 
1204
 
 
1205
                if (road->isExtrimity(trackPoint) && !road->isClosed())
 
1206
                        continue;
 
1207
                
 
1208
                if (parentRoad && road != parentRoad)
 
1209
                        return NULL;
 
1210
 
 
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())
 
1213
                        parentRoad = road;
 
1214
        }
 
1215
 
 
1216
        return parentRoad;
 
1217
}
 
1218