1 #include "FeatureManipulations.h"
2 
3 #include "Global.h"
4 #include "Coord.h"
5 #include "DocumentCommands.h"
6 #include "FeatureCommands.h"
7 #include "WayCommands.h"
8 #include "RelationCommands.h"
9 #include "NodeCommands.h"
10 #include "Document.h"
11 #include "Features.h"
12 #include "PropertiesDock.h"
13 #include "Projection.h"
14 #include "ImportExportOSC.h"
15 
16 #include "Utils.h"
17 #include "LineF.h"
18 
19 #include <QtCore/QString>
20 #include <QMessageBox>
21 
22 #include <algorithm>
23 
24 static void mergeNodes(Document* theDocument, CommandList* theList, Node *node1, Node *node2);
25 
isNear(Node * a,Node * b)26 static bool isNear(Node* a, Node* b)
27 {
28     // For now, only if exactly same position
29     // Future: use distance threshold?
30     return a->position() == b->position();
31 }
32 
canJoin(Way * R1,Way * R2)33 bool canJoin(Way* R1, Way* R2)
34 {
35     if ( (R1->size() == 0) || (R2->size() == 0) )
36         return true;
37     Node* Start1 = R1->getNode(0);
38     Node* End1 = R1->getNode(R1->size()-1);
39     Node* Start2 = R2->getNode(0);
40     Node* End2 = R2->getNode(R2->size()-1);
41     return (Start1 == Start2) ||
42         (Start1 == End2) ||
43         (End1 == Start2) ||
44         (End1 == End2) ||
45         isNear(Start1, Start2) ||
46         isNear(Start1, End2) ||
47         isNear(End1, Start2) ||
48         isNear(End1, End2);
49 }
50 
canBreak(Way * R1,Way * R2)51 bool canBreak(Way* R1, Way* R2)
52 {
53     if ( (R1->size() == 0) || (R2->size() == 0) )
54         return false;
55     for (int i=0; i<R1->size(); i++)
56         for (int j=0; j<R2->size(); j++)
57             if (R1->get(i) == R2->get(j))
58                 return true;
59     return false;
60 }
61 
canJoinRoads(PropertiesDock * theDock)62 bool canJoinRoads(PropertiesDock* theDock)
63 {
64     QHash<Coord,int> ends;
65     for (int i=0; i<theDock->selectionSize(); ++i)
66         if (Way* R = CAST_WAY(theDock->selection(i))) {
67             if (R->isClosed()) continue;
68             if (!R->size()) continue;
69             Coord start = R->getNode(0)->position();
70             Coord end = R->getNodes().back()->position();
71             ++ends[start];
72             ++ends[end];
73         }
74 
75     return ends.values().contains(2);
76 }
77 
canBreakRoads(PropertiesDock * theDock)78 bool canBreakRoads(PropertiesDock* theDock)
79 {
80     QList<Way*> Input;
81     for (int i=0; i<theDock->selectionSize(); ++i)
82         if (Way* R = CAST_WAY(theDock->selection(i)))
83             Input.push_back(R);
84     for (int i=0; i<Input.size(); ++i)
85         for (int j=i+1; j<Input.size(); ++j)
86             if (canBreak(Input[i],Input[j]))
87                 return true;
88     return false;
89 }
90 
canDetachNodes(PropertiesDock * theDock)91 bool canDetachNodes(PropertiesDock* theDock)
92 {
93     QList<Way*> Roads, Result;
94     QList<Node*> Points;
95     for (int i=0; i<theDock->selectionSize(); ++i)
96         if (Way* R = CAST_WAY(theDock->selection(i)))
97             Roads.push_back(R);
98         else if (Node* Pt = CAST_NODE(theDock->selection(i)))
99             Points.push_back(Pt);
100 
101     if (Roads.size() > 1 && Points.size() > 1)
102         return false;
103 
104     if (Roads.size() == 0 && Points.size()) {
105         for (int i=0; i<Points.size(); ++i) {
106             Way * R = Way::GetSingleParentRoad(Points[i]);
107             if (R)
108                 return true;
109         }
110         return false;
111     }
112 
113     if (Roads.size() && Points.size() == 1)
114         return true;
115 
116     if (Roads.size() == 1 && Points.size())
117         return true;
118 
119     return false;
120 }
121 
reversePoints(Document * theDocument,CommandList * theList,Way * R)122 void reversePoints(Document* theDocument, CommandList* theList, Way* R)
123 {
124     Layer *layer=theDocument->getDirtyOrOriginLayer(R->layer());
125     for (int i=R->size()-2; i>=0; --i)
126     {
127         Node* Pt = R->getNode(i);
128         theList->add(new WayRemoveNodeCommand(R,i,layer));
129         theList->add(new WayAddNodeCommand(R,Pt,layer));
130     }
131 }
132 
133 
distanceFrom(QLineF ab,const Coord & c)134 static qreal distanceFrom(QLineF ab, const Coord& c)
135 {
136     // distance in metres from c to line segment ab
137     qreal ab_len2 = ab.dx() * ab.dx() + ab.dy() * ab.dy();
138     QPointF dp;
139     if (ab_len2) {
140         QLineF ac(ab.p1(), c);
141         qreal u = (ac.dx() * ab.dx() + ac.dy() * ab.dy()) / ab_len2;
142         if (u < 0.0) u = 0.0;
143         else if (u > 1.0) u = 1.0;
144         dp = ab.pointAt(u);
145     } else {
146         dp = ab.p1();
147     }
148     Coord dc(dp);
149     return dc.distanceFrom(c);
150 }
151 
simplifyWay(Document * doc,Layer * layer,CommandList * theList,Way * w,int start,int end,qreal threshold)152 void simplifyWay(Document *doc, Layer *layer, CommandList *theList, Way *w, int start, int end, qreal threshold)
153 {
154     // Douglas-Peucker reduction of uninteresting points in way, subject to maximum error in metres
155 
156     // TODO Performance: Use path-hull algorithm described at http://www.cs.ubc.ca/cgi-bin/tr/1992/TR-92-07.ps
157     if (end - start <= 1)
158         // no removable nodes
159         return;
160 
161     QLineF segment(w->getNode(start)->position(), w->getNode(end)->position());
162     qreal maxdist = -1;
163     int maxpos = 0;
164     for (int i = start+1;  i < end;  i++) {
165         qreal d = distanceFrom(segment, w->getNode(i)->position());
166         if (d > maxdist) {
167             maxdist = d;
168             maxpos = i;
169         }
170     }
171     // maxdist is in kilometres
172     if (maxpos && maxdist * 1000 > threshold) {
173         simplifyWay(doc, layer, theList, w, maxpos, end, threshold);
174         simplifyWay(doc, layer, theList, w, start, maxpos, threshold);
175     } else {
176         for (int i = end - 1;  i > start;  i--) {
177             Feature *n = w->get(i);
178             if (!doc->isDownloadedSafe(n->boundingBox()) && n->hasOSMId())
179                 continue;
180             theList->add(new WayRemoveNodeCommand(w, i, layer));
181             theList->add(new RemoveFeatureCommand(doc, n));
182         }
183     }
184 }
185 QSet<QString> uninterestingKeys;
isNodeInteresting(Node * n)186 bool isNodeInteresting(Node *n)
187 {
188     for (int i = 0; i < n->tagSize();  i++)
189         if (!uninterestingKeys.contains(n->tagKey(i)))
190             return true;
191     return false;
192 }
193 
simplifyRoads(Document * theDocument,CommandList * theList,PropertiesDock * theDock,qreal threshold)194 void simplifyRoads(Document* theDocument, CommandList* theList, PropertiesDock* theDock, qreal threshold)
195 {
196     if (uninterestingKeys.isEmpty())
197         uninterestingKeys << "source";
198 
199     for (int i = 0;  i < theDock->selectionSize();  ++i)
200         if (Way* w = CAST_WAY(theDock->selection(i))) {
201             Layer *layer = theDocument->getDirtyOrOriginLayer(w->layer());
202             int end = w->size() - 1;
203             for (int i = end;  i >= 0;  i--) {
204                 Node *n = w->getNode(i);
205                 if (i == 0 || n->sizeParents() > 1 || isNodeInteresting(n)) {
206                     simplifyWay(theDocument, layer, theList, w, i, end, threshold);
207                     end = i;
208                 }
209             }
210         }
211 }
212 
appendPoints(Document * theDocument,CommandList * L,Way * Dest,Way * Src,bool prepend,bool reverse)213 static void appendPoints(Document* theDocument, CommandList* L, Way* Dest, Way* Src, bool prepend, bool reverse)
214 {
215     Layer *layer = theDocument->getDirtyOrOriginLayer(Dest->layer());
216     int srclen = Src->size();
217     int destpos = prepend ? 0 : Dest->size();
218     for (int i=1; i<srclen; ++i) {
219         int j = (reverse ? srclen-i : i) - (prepend != reverse ? 1 : 0);
220         Node* Pt = Src->getNode(j);
221         if (!Pt->layer() || layer != Pt->layer()) {
222             L->add(new AddFeatureCommand(layer, Pt, false));
223         }
224         L->add(new WayAddNodeCommand(Dest, Pt, destpos++, layer));
225     }
226 }
227 
join(Document * theDocument,CommandList * L,Way * R1,Way * R2)228 static Way* join(Document* theDocument, CommandList* L, Way* R1, Way* R2)
229 {
230     QList<Feature*> Alternatives;
231     if (R1->size() == 0)
232     {
233         Feature::mergeTags(theDocument,L,R2,R1);
234         L->add(new RemoveFeatureCommand(theDocument,R1,Alternatives));
235         return R2;
236     }
237     if (R2->size() == 0)
238     {
239         Feature::mergeTags(theDocument,L,R1,R2);
240         L->add(new RemoveFeatureCommand(theDocument,R2,Alternatives));
241         return R1;
242     }
243     Node* Start1 = R1->getNode(0);
244     Node* End1 = R1->getNode(R1->size()-1);
245     Node* Start2 = R2->getNode(0);
246     Node* End2 = R2->getNode(R2->size()-1);
247 
248     bool prepend = false;       // set true if R2 meets beginning of R1
249     bool reverse = false;       // set true if R2 is opposite direction to R1
250 
251     if (End1 == Start2) {
252         // nothing to do, but skip the other tests
253     } else if (End1 == End2) {
254         reverse = true;
255     } else if (Start1 == Start2) {
256         prepend = true;
257         reverse = true;
258     } else if (Start1 == End2) {
259         prepend = true;
260     } else if (isNear(End1, Start2)) {
261         mergeNodes(theDocument, L, End1, Start2);
262     } else if (isNear(End1, End2)) {
263         mergeNodes(theDocument, L, End1, End2);
264         reverse = true;
265     } else if (isNear(Start1, Start2)) {
266         mergeNodes(theDocument, L, Start1, Start2);
267         prepend = true;
268         reverse = true;
269     } else if (isNear(Start1, End2)) {
270         mergeNodes(theDocument, L, Start1, End2);
271         prepend = true;
272     }
273 
274     appendPoints(theDocument, L, R1, R2, prepend, reverse);
275     Feature::mergeTags(theDocument,L,R1,R2);
276     L->add(new RemoveFeatureCommand(theDocument,R2,Alternatives));
277 
278     // Auto-merge nearly-closed ways
279     Node *StartResult = R1->getNode(0);
280     Node *EndResult = R1->getNode(R1->size()-1);
281     if (StartResult != EndResult && isNear(StartResult, EndResult))
282         mergeNodes(theDocument, L, StartResult, EndResult);
283     return R1;
284 }
285 
286 template<typename T>
replaceItem(QList<T> & list,T oldItem,T newItem)287 static void replaceItem(QList<T> &list, T oldItem, T newItem)
288 {
289     int i = list.indexOf(oldItem);
290     if (i >= 0)
291         list.replace(i, newItem);
292 }
293 
joinRoads(Document * theDocument,CommandList * theList,PropertiesDock * theDock)294 void joinRoads(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
295 {
296     QList<Feature*> selection = theDock->selection();
297     QHash<Coord,QList<Way*> > ends;
298 
299     foreach (Feature *F, selection)
300         if (Way* R = CAST_WAY(F))
301             if (!R->isClosed()) {
302                 Coord start = R->getNode(0)->position();
303                 ends[start] << R;
304                 Coord end = R->getNodes().back()->position();
305                 ends[end] << R;
306             }
307 
308     foreach (Coord c, ends.keys()) {
309         QList<Way*> ways = ends[c];
310         if (ways.count() != 2) continue;
311         Way *R1 = ways[0];
312         Way *R2 = ways[1];
313         if (R1 == R2) {
314             Node *firstNode = R1->getNode(0);
315             Node *lastNode = R1->getNode(R1->size()-1);
316             if (firstNode != lastNode && R1->size() >= 4 && firstNode->position() == lastNode->position()) {
317                 // almost-area; close it properly
318                 mergeNodes(theDocument, theList, R1->getNode(0), R1->getNode(R1->size()-1));
319             }
320         } else {
321             R1 = join(theDocument, theList, R1, R2);
322             // replace R2 with R1 for subsequent operations
323             replaceItem(ends[R2->getNode(0)->position()], R2, R1);
324             replaceItem(ends[R2->getNodes().back()->position()], R2, R1);
325             selection.removeOne(R2);
326         }
327     }
328     theDock->setSelection(selection);
329 }
330 
handleWaysplitSpecialRestriction(Document * theDocument,CommandList * theList,Way * FirstPart,Way * NextPart,Relation * theRel)331 static bool handleWaysplitSpecialRestriction(Document* theDocument, CommandList* theList, Way* FirstPart, Way* NextPart, Relation* theRel)
332 {
333     if (theRel->tagValue("type","") != "restriction")
334         return false;
335 
336     for (int k=0; k < theRel->size(); k++)
337     {
338         // check for via
339         if (theRel->getRole(k) == "via")
340         {
341             if (theRel->get(k) == FirstPart)
342             {
343                 // whoops, we just split a via way, just add the new way, too
344                 theList->add(new RelationAddFeatureCommand(theRel, theRel->getRole(k), NextPart, k+1, theDocument->getDirtyOrOriginLayer(FirstPart->layer())));
345                 // we just added a member, so get over it
346                 k++;
347             }
348             else if ((theRel->get(k))->getType() == IFeature::Point)
349             {
350                 // this seems to be a via node, check if it is member the nextPart
351                 if (NextPart->find(theRel->get(k)) < NextPart->size())
352                 {
353                     // yes it is, so remove First Part and add Next Part to it
354                     int idx = theRel->find(FirstPart);
355                     theList->add(new RelationAddFeatureCommand(theRel, theRel->getRole(idx), NextPart, idx+1, theDocument->getDirtyOrOriginLayer(FirstPart->layer())));
356                     theList->add(new RelationRemoveFeatureCommand(theRel, idx, theDocument->getDirtyOrOriginLayer(FirstPart->layer())));
357                 }
358             }
359             else if ((theRel->get(k))->getType() & IFeature::LineString)
360             {
361                 // this is a way, check the nodes
362                 Way* W = CAST_WAY(theRel->get(k));
363                 for (int l=0; l<W->size(); l++)
364                 {
365             // check if this node is member the nextPart
366                     if (NextPart->find(W->get(l)) < NextPart->size())
367                     {
368                         // yes it is, so remove First Part and add Next Part to it
369                         int idx = theRel->find(FirstPart);
370                         theList->add(new RelationAddFeatureCommand(theRel, theRel->getRole(idx), NextPart, idx+1, theDocument->getDirtyOrOriginLayer(FirstPart->layer())));
371                         theList->add(new RelationRemoveFeatureCommand(theRel, idx, theDocument->getDirtyOrOriginLayer(FirstPart->layer())));
372                         break;
373                     }
374                 }
375             }
376         }
377     }
378     return true;
379 }
380 
handleWaysplitRelations(Document * theDocument,CommandList * theList,Way * FirstPart,Way * NextPart)381 static void handleWaysplitRelations(Document* theDocument, CommandList* theList, Way* FirstPart, Way* NextPart)
382 {
383     /* since we may delete First Part from some Relations here, we first build a list of the relations to check */
384     QList<Relation*> checkList;
385 
386     for (int j=0; j < FirstPart->sizeParents(); j++) {
387         checkList.append(CAST_RELATION(FirstPart->getParent(j)));
388     }
389 
390     for (int j=0; j < checkList.count(); j++) {
391         Relation* L = checkList.at(j);
392         if (!handleWaysplitSpecialRestriction(theDocument, theList, FirstPart, NextPart, L))
393         {
394             int idx = L->find(FirstPart);
395             theList->add(new RelationAddFeatureCommand(L, L->getRole(idx), NextPart, idx+1, theDocument->getDirtyOrOriginLayer(FirstPart->layer())));
396         }
397     }
398 }
399 
400 
splitRoad(Document * theDocument,CommandList * theList,Way * In,const QList<Node * > & Points,QList<Way * > & Result)401 static void splitRoad(Document* theDocument, CommandList* theList, Way* In, const QList<Node*>& Points, QList<Way*>& Result)
402 {
403     int pos = 0;
404     if (In->isClosed()) {  // Special case: If area, rotate the area so that the start node is the first point of splitting
405 
406         QList<Node*> Target;
407         for (int i=0; i < Points.size(); i++)
408             if ((pos = In->find(Points[i])) != In->size()) {
409                 for (int j=pos+1; j<In->size(); ++j)
410                     Target.push_back(In->getNode(j));
411                 for (int j=1; j<= pos; ++j)
412                     Target.push_back(In->getNode(j));
413                 break;
414             }
415         if (pos == In->size())
416             return;
417 
418         if (Points.size() == 1) // Special case: For a 1 point area splitting, de-close the road, i.e. duplicate the selected node
419         {
420             Node* N = g_backend.allocNode(theDocument->getDirtyOrOriginLayer(In->layer()), *(In->getNode(pos)));
421             theList->add(new AddFeatureCommand(theDocument->getDirtyOrOriginLayer(In->layer()),N,true));
422 
423             Target.prepend(N);
424         } else // otherwise, just close the modified area
425             Target.prepend(In->getNode(pos));
426 
427         // Now, reconstruct the road/area
428         while (In->size())
429             theList->add(new WayRemoveNodeCommand(In,(int)0,theDocument->getDirtyOrOriginLayer(In->layer())));
430 
431         for (int i=0; i<Target.size(); ++i)
432             theList->add(new WayAddNodeCommand(In,Target[i],theDocument->getDirtyOrOriginLayer(In->layer())));
433 
434         if (Points.size() == 1) {  // For 1-point, we are done
435             Result.push_back(In);
436             return;
437         }
438     }
439 
440     Way* FirstPart     = In;
441     bool FirstWayValid = false;
442     for (int i=1; (i+1)<FirstPart->size(); ++i)
443     {
444         if (std::find(Points.begin(),Points.end(),FirstPart->get(i)) != Points.end() and FirstPart->get(i) != FirstPart->get(i+1) \
445                 and (FirstWayValid or FirstPart->get(0) != FirstPart->get(i))) {
446             Way* NextPart = g_backend.allocWay(theDocument->getDirtyOrOriginLayer(In->layer()));
447             theList->add(new AddFeatureCommand(theDocument->getDirtyOrOriginLayer(In->layer()),NextPart,true));
448             copyTags(NextPart,FirstPart);
449             theList->add(new WayAddNodeCommand(NextPart, FirstPart->getNode(i), theDocument->getDirtyOrOriginLayer(In->layer())));
450             while ( (i+1) < FirstPart->size() )
451             {
452                 theList->add(new WayAddNodeCommand(NextPart, FirstPart->getNode(i+1), theDocument->getDirtyOrOriginLayer(In->layer())));
453                 theList->add(new WayRemoveNodeCommand(FirstPart,i+1,theDocument->getDirtyOrOriginLayer(In->layer())));
454             }
455             handleWaysplitRelations(theDocument, theList, FirstPart, NextPart);
456 
457             FirstWayValid = true;
458             Result.push_back(FirstPart);
459             FirstPart = NextPart;
460             i=0;
461         }
462     }
463     Result.push_back(FirstPart);
464 
465 }
466 
splitRoads(Document * theDocument,CommandList * theList,PropertiesDock * theDock)467 void splitRoads(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
468 {
469     QList<Way*> Roads, Result;
470     QList<Node*> Points;
471     for (int i=0; i<theDock->selectionSize(); ++i)
472         if (Way* R = CAST_WAY(theDock->selection(i)))
473             Roads.push_back(R);
474         else if (Node* Pt = CAST_NODE(theDock->selection(i)))
475             Points.push_back(Pt);
476 
477     if (Roads.size() == 0 && Points.size() == 1)
478     {
479         Way * R = Way::GetSingleParentRoadInner(Points[0]);
480         if (R)
481             Roads.push_back(R);
482     }
483 
484     for (int i=0; i<Roads.size(); ++i)
485         splitRoad(theDocument, theList,Roads[i],Points, Result);
486     theDock->setSelection(Result);
487 }
488 
489 /* Split road by the two nodes and return the way joining them, if there is one. */
cutoutRoad(Document * theDocument,CommandList * theList,PropertiesDock *,Node * N1,Node * N2)490 Way *cutoutRoad(Document* theDocument, CommandList* theList, PropertiesDock* /* theDock */, Node *N1, Node *N2) {
491     QList<Way*> Roads, Result;
492     QList<Node*> Points;
493 
494 	Way *R1 = Way::GetSingleParentRoadInner( N1 );
495 	Way *R2 = Way::GetSingleParentRoadInner( N2 );
496 
497 	if (R1)
498 		Roads.push_back(R1);
499 	if (R2)
500 		Roads.push_back(R2);
501 
502     Points.push_back(N1);
503     Points.push_back(N2);
504 
505     for (int i=0; i<Roads.size(); ++i)
506         splitRoad(theDocument, theList, Roads[i], Points, Result);
507 
508     for (int i = 0; i < Result.size(); ++i) {
509         if (Result[i]->isExtrimity(N1) && Result[i]->isExtrimity(N2))
510             return Result[i];
511     }
512     return NULL;
513 }
514 
breakRoad(Document * theDocument,CommandList * theList,Way * R,Node * Pt)515 static void breakRoad(Document* theDocument, CommandList* theList, Way* R, Node* Pt)
516 {
517     for (int i=0; i<R->size(); ++i) {
518         if (R->get(i) == Pt) {
519             Node* New = g_backend.allocNode(theDocument->getDirtyOrOriginLayer(R->layer()), *Pt);
520             theList->add(new AddFeatureCommand(theDocument->getDirtyOrOriginLayer(R->layer()),New,true));
521             copyTags(New,Pt);
522             theList->add(new WayRemoveNodeCommand(R,i,theDocument->getDirtyOrOriginLayer(R->layer())));
523             theList->add(new WayAddNodeCommand(R,New,i,theDocument->getDirtyOrOriginLayer(R->layer())));
524         }
525     }
526     if (!Pt->sizeParents())
527         theList->add(new RemoveFeatureCommand(theDocument,Pt));
528 }
529 
breakRoads(Document * theDocument,CommandList * theList,PropertiesDock * theDock)530 void breakRoads(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
531 {
532     QList<Way*> Roads, Result;
533     QList<Node*> Points;
534     for (int i=0; i<theDock->selectionSize(); ++i)
535         if (Way* R = CAST_WAY(theDock->selection(i)))
536             Roads.push_back(R);
537         else if (Node* Pt = CAST_NODE(theDock->selection(i)))
538             Points.push_back(Pt);
539 
540     if (Roads.size() == 0 && Points.size() == 1)
541     {
542         for (int i=0; i<Points[0]->sizeParents() ; ++i) {
543             Way * R = CAST_WAY(Points[0]->getParent(i));
544             if (R && !R->isDeleted())
545                 Roads.push_back(R);
546         }
547     }
548 
549     if (Roads.size() == 1 && Points.size() ) {
550         splitRoad(theDocument, theList,Roads[0],Points, Result);
551         if (Roads[0]->area() > 0.0) {
552             for (int i=0; i<Points.size(); ++i)
553                 breakRoad(theDocument, theList, Roads[0],Points[i]);
554         } else {
555             Roads = Result;
556         }
557     }
558 
559     for (int i=0; i<Roads.size(); ++i)
560         for (int j=0; j<Roads[i]->size(); ++j)
561             for (int k=i+1; k<Roads.size(); ++k)
562                 breakRoad(theDocument, theList, Roads[k],CAST_NODE(Roads[i]->get(j)));
563 }
564 
canCreateJunction(PropertiesDock * theDock)565 bool canCreateJunction(PropertiesDock* theDock)
566 {
567     return createJunction(NULL, NULL, theDock, false);
568 }
569 
createJunction(Document * theDocument,CommandList * theList,PropertiesDock * theDock,bool doIt)570 int createJunction(Document* theDocument, CommandList* theList, PropertiesDock* theDock, bool doIt)
571 {
572     //TODO test that the junction do not already exists!
573 
574     QList<Way*> Roads, Result;
575     for (int i=0; i<theDock->selectionSize(); ++i)
576         if (Way* R = CAST_WAY(theDock->selection(i)))
577             Roads.push_back(R);
578 
579     if (Roads.size() < 2)
580         return 0;
581 
582     Way* R1 = Roads[0];
583     Way* R2 = Roads[1];
584 
585     return Way::createJunction(theDocument, theList, R1, R2, doIt);
586 }
587 
588 #define STREET_NUMBERS_LENGTH .0000629
589 #define STREET_NUMBERS_ANGLE 30.0
590 
createStreetNumbers(Document * theDocument,CommandList * theList,Way * theRoad,bool Left)591 void createStreetNumbers(Document* theDocument, CommandList* theList, Way* theRoad, bool Left)
592 {
593     QString streetName = theRoad->tagValue("name", "");
594     QLineF l, l2, nv;
595 
596     Node* N;
597     Way* R = g_backend.allocWay(theDocument->getDirtyOrOriginLayer());
598     theList->add(new AddFeatureCommand(theDocument->getDirtyOrOriginLayer(),R,true));
599     theList->add(new SetTagCommand(R, "addr:interpolation", ""));
600     theList->add(new SetTagCommand(R, "addr:street", streetName));
601     QPointF prevPoint;
602 
603     for (int j=0; j < theRoad->size(); j++) {
604         if (j == 0) {
605             l2 = QLineF(theRoad->getNode(j)->position(), theRoad->getNode(j+1)->position());
606             l = l2;
607             l.setAngle(l2.angle() + 180.);
608             prevPoint = l.p2();
609         } else
610         if (j == theRoad->size()-1) {
611             l = QLineF(theRoad->getNode(j)->position(), theRoad->getNode(j-1)->position());
612             l2 = l;
613             l2.setAngle(l.angle() + 180.);
614         } else {
615             l = QLineF(theRoad->getNode(j)->position(), theRoad->getNode(j-1)->position());
616             l2 = QLineF(theRoad->getNode(j)->position(), theRoad->getNode(j+1)->position());
617         }
618         nv = l.normalVector().unitVector();
619 
620         qreal theAngle = (l.angle() - l2.angle());
621         if (theAngle < 0.0) theAngle = 360. + theAngle;
622         theAngle /= 2.;
623         nv.setAngle(l2.angle() + theAngle);
624         nv.setLength(STREET_NUMBERS_LENGTH/sin(angToRad(theAngle)));
625         if (Left)
626             nv.setAngle(nv.angle() + 180.0);
627 
628         QLineF lto(prevPoint, nv.p2());
629         lto.setLength(lto.length()+STREET_NUMBERS_LENGTH);
630         QPointF pto;
631 
632         bool intersectedTo = false;
633         for (int k=0; k < theRoad->getNode(j)->sizeParents(); ++k) {
634             Way* I = CAST_WAY(theRoad->getNode(j)->getParent(k));
635             if (!I || I == theRoad || I->isDeleted())
636                 continue;
637 
638             for (int m=0; m < I->size()-1; ++m) {
639                 QLineF l3 = QLineF(I->getNode(m)->position(), I->getNode(m+1)->position());
640                 QPointF theIntersection;
641                 if (lto.intersect(l3, &theIntersection) == QLineF::BoundedIntersection) {
642                     intersectedTo = true;
643                     QLineF lt = QLineF(prevPoint, theIntersection);
644                     if (lt.length() < lto.length())
645                         lto = lt;
646                 }
647             }
648         }
649 
650         if (j != 0) {
651             QLineF lfrom = QLineF(nv.p2(), prevPoint);
652             lfrom.setLength(lfrom.length()*2.);
653             QPointF pfrom;
654 
655             bool intersectedFrom = false;
656             for (int k=0; k < theRoad->getNode(j-1)->sizeParents(); ++k) {
657                 Way* I = CAST_WAY(theRoad->getNode(j-1)->getParent(k));
658                 if (!I || I == theRoad || I->isDeleted())
659                     continue;
660 
661                 for (int m=0; m < I->size()-1; ++m) {
662                     QLineF l3 = QLineF(I->getNode(m)->position(), I->getNode(m+1)->position());
663                     QPointF theIntersection;
664                     if (lfrom.intersect(l3, &theIntersection) == QLineF::BoundedIntersection) {
665                         intersectedFrom = true;
666                         QLineF lt = QLineF(nv.p2(), theIntersection);
667                         if (lt.length() < lfrom.length())
668                             lfrom = lt;
669                     }
670                 }
671             }
672             if (intersectedFrom) {
673                 lfrom.setLength(lfrom.length() - STREET_NUMBERS_LENGTH);
674                 pfrom = lfrom.p2();
675 
676                 R = g_backend.allocWay(theDocument->getDirtyOrOriginLayer());
677                 theList->add(new AddFeatureCommand(theDocument->getDirtyOrOriginLayer(),R,true));
678                 theList->add(new SetTagCommand(R, "addr:interpolation", ""));
679                 theList->add(new SetTagCommand(R, "addr:street", streetName));
680 
681                 N = g_backend.allocNode(theDocument->getDirtyOrOriginLayer(), Coord(pfrom));
682                 theList->add(new AddFeatureCommand(theDocument->getDirtyOrOriginLayer(),N,true));
683                 theList->add(new WayAddNodeCommand(R, N, theDocument->getDirtyOrOriginLayer(R->layer())));
684                 theList->add(new SetTagCommand(N, "addr:housenumber", ""));
685             } else {
686                 pfrom = prevPoint;
687             }
688         }
689 
690         if (intersectedTo) {
691             if (j != 0) {
692                 lto.setLength(lto.length() - STREET_NUMBERS_LENGTH);
693                 pto = lto.p2();
694 
695                 N = g_backend.allocNode(theDocument->getDirtyOrOriginLayer(), Coord(pto));
696                 theList->add(new AddFeatureCommand(theDocument->getDirtyOrOriginLayer(),N,true));
697                 theList->add(new WayAddNodeCommand(R, N, theDocument->getDirtyOrOriginLayer(R->layer())));
698                 theList->add(new SetTagCommand(N, "addr:housenumber", ""));
699             }
700         } else {
701             if (theAngle < 85. || theAngle > 95. || j== 0 || j == theRoad->size()-1) {
702                 N = g_backend.allocNode(theDocument->getDirtyOrOriginLayer(), Coord(nv.p2()));
703                 theList->add(new AddFeatureCommand(theDocument->getDirtyOrOriginLayer(),N,true));
704                 theList->add(new WayAddNodeCommand(R, N, theDocument->getDirtyOrOriginLayer(R->layer())));
705                 theList->add(new SetTagCommand(N, "addr:housenumber", ""));
706             }
707 
708             pto = nv.p2();
709         }
710         prevPoint = nv.p2();
711     }
712 }
713 
addStreetNumbers(Document * theDocument,CommandList * theList,PropertiesDock * theDock)714 void addStreetNumbers(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
715 {
716     QList<Way*> Roads;
717     for (int i=0; i<theDock->selectionSize(); ++i)
718         if (Way* R = CAST_WAY(theDock->selection(i)))
719             Roads.push_back(R);
720 
721     if (Roads.isEmpty())
722         return;
723 
724     QList<Way*>::const_iterator it = Roads.constBegin();
725     for (;it != Roads.constEnd(); ++it) {
726         if((*it)->size() < 2)
727             continue;
728 
729         createStreetNumbers(theDocument, theList, (*it), false);
730         createStreetNumbers(theDocument, theList, (*it), true);
731     }
732 
733     if (Roads.size() == 1)
734         theList->setFeature(Roads.at(0));
735 }
736 
alignNodes(Document * theDocument,CommandList * theList,PropertiesDock * theDock)737 void alignNodes(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
738 {
739     if (theDock->selectionSize() < 3) //thre must be at least 3 nodes to align something
740         return;
741 
742     //We build a list of selected nodes
743     QList<Node*> Nodes;
744     for (int i=0; i<theDock->selectionSize(); ++i)
745         if (Node* N = CAST_NODE(theDock->selection(i)))
746             Nodes.push_back(N);
747 
748     //we check that we have at least 3 nodes and the first two can give a line
749     if(Nodes.size() < 3)
750         return;
751     if(Nodes[0]->position() == Nodes[1]->position())
752         return;
753 
754     //we do the alignement
755     Coord pos(0,0);
756     const Coord p1(Nodes[0]->position());
757     const Coord p2(Nodes[1]->position()-p1);
758     const qreal slope = angle(p2);
759     for (int i=2; i<Nodes.size(); ++i) {
760         pos=Nodes[i]->position()-p1;
761         rotate(pos,-slope);
762         pos.setY(0);
763         rotate(pos,slope);
764         pos=pos+p1;
765         theList->add(new MoveNodeCommand( Nodes[i], pos, theDocument->getDirtyOrOriginLayer(Nodes[i]->layer()) ));
766     }
767 }
768 
bingExtract(Document * theDocument,CommandList * theList,PropertiesDock * theDock,CoordBox vp)769 void bingExtract(Document* theDocument, CommandList* theList, PropertiesDock* theDock, CoordBox vp)
770 {
771     //We build a list of selected nodes
772     QList<Node*> Nodes;
773     for (int i=0; i<theDock->selectionSize(); ++i)
774         if (Node* N = CAST_NODE(theDock->selection(i)))
775             Nodes.push_back(N);
776 
777     //we check that we have at least 2 nodes
778     if(Nodes.size() < 2)
779         return;
780     if(Nodes[0]->position() == Nodes[1]->position())
781         return;
782 
783     //http://3667a17de9b94ccf8fd278f9de62dae4.cloudapp.net/
784     QString u("http://magicshop.cloudapp.net/DetectRoad.svc/detect/?");
785     u.append(QString("pt1=%1,%2").arg(COORD2STRING(Nodes[0]->position().y())).arg(COORD2STRING(Nodes[0]->position().x())));
786     u.append(QString("&pt2=%1,%2").arg(COORD2STRING(Nodes[1]->position().y())).arg(COORD2STRING(Nodes[1]->position().x())));
787     u.append(QString("&bbox=%1,%2,%3,%4")
788              .arg(COORD2STRING(vp.top()))
789              .arg(COORD2STRING(vp.left()))
790              .arg(COORD2STRING(vp.bottom()))
791              .arg(COORD2STRING(vp.right()))
792              );
793 
794     QString sXml;
795     if (!Utils::sendBlockingNetRequest(QUrl(u), sXml)) {
796         QMessageBox::critical(0, QApplication::tr("Bing Road Detect"), QApplication::tr("Cannot get output."));
797         return;
798     }
799     qDebug() << sXml;
800 
801     //Import the OSC
802     Document* newDoc;
803     QDomDocument xmlDoc;
804     xmlDoc.setContent(sXml);
805     if (!(newDoc = Document::getDocumentFromXml(&xmlDoc))) {
806         QMessageBox::critical(0, QApplication::tr("Bing Road Detect"), QApplication::tr("No valid data."));
807         return;
808     }
809 
810     if (!newDoc->size()) {
811         QMessageBox::critical(0, QApplication::tr("Bing Road Detect"), QApplication::tr("Cannot parse output."));
812     } else {
813         DrawingLayer* newLayer = new DrawingLayer( "Bing Road Extract" );
814         newLayer->setUploadable(false);
815         theDocument->add(newLayer);
816         QList<Feature*> theFeats = theDocument->mergeDocument(newDoc, newLayer, theList);
817 
818         // Merge in initial nodes
819         Node * N0 = NULL;
820         Node * N1 = NULL;
821         qreal Best0 = 180., Best1 = 180.;
822 
823         foreach (Feature* F, theFeats) {
824             if (CHECK_NODE(F)) {
825                 Node* N = STATIC_CAST_NODE(F);
826                 qreal B = distance(Nodes[0]->position(), N->position());
827                 if (B < Best0) {
828                     N0 = N;
829                     Best0 = B;
830                 }
831                 B = distance(Nodes[1]->position(), N->position());
832                 if (B < Best1) {
833                     N1 = N;
834                     Best1 = B;
835                 }
836             }
837         }
838         if (N0)
839             mergeNodes(theDocument, theList, Nodes[0], N0);
840         if (N1)
841             mergeNodes(theDocument, theList, Nodes[1], N1);
842     }
843     delete newDoc;
844 }
845 
spreadNodes(Document * theDocument,CommandList * theList,PropertiesDock * theDock)846 void spreadNodes(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
847 {
848     // There must be at least 3 nodes to align something
849     if (theDock->selectionSize() < 3)
850         return;
851 
852     // We build a list of selected nodes
853     // Sort by distance along the line between the first two nodes
854     QList<Node*> Nodes;
855     QList<float> Metrics;
856     Coord p;
857     Coord delta;
858     for (int i=0; i<theDock->selectionSize(); ++i) {
859         if (Node* N = CAST_NODE(theDock->selection(i))) {
860             Coord pos(N->position());
861             if (Nodes.size() == 0) {
862                 p = pos;
863                 Nodes.push_back(N);
864                 Metrics.push_back(0.0f);
865             } else if (Nodes.size() == 1) {
866                 delta = pos - p;
867                 // First two nodes must form a line
868                 if (delta.isNull())
869                     return;
870                 Nodes.push_back(N);
871                 Metrics.push_back(delta.x()*delta.x() + delta.y()*delta.y());
872             } else {
873                 pos = pos - p;
874                 qreal metric = pos.x()*delta.x() + pos.y()*delta.y();
875                 // This could be done more efficiently with a binary search
876                 for (int j = 0; j < Metrics.size(); ++j) {
877                     if (metric < Metrics[j]) {
878                         Nodes.insert(j, N);
879                         Metrics.insert(j, metric);
880                         goto inserted;
881                     }
882                 }
883                 Nodes.push_back(N);
884                 Metrics.push_back(metric);
885 inserted:
886                 ;
887             }
888         }
889     }
890 
891     // We check that we have at least 3 nodes
892     if(Nodes.size() < 3)
893         return;
894 
895     // Do the spreading between the extremes
896     p = Nodes[0]->position();
897     delta = (Nodes[Nodes.size()-1]->position() - p) / (Nodes.size()-1);
898 
899     for (int i=1; i<Nodes.size()-1; ++i) {
900         p = p + delta;
901         theList->add(new MoveNodeCommand( Nodes[i], p, theDocument->getDirtyOrOriginLayer(Nodes[i]->layer()) ));
902     }
903 }
904 
mergeNodes(Document * theDocument,CommandList * theList,Node * node1,Node * node2)905 static void mergeNodes(Document* theDocument, CommandList* theList, Node *node1, Node *node2)
906 {
907     QList<Feature*> alt;
908     alt.append(node1);
909     Feature::mergeTags(theDocument, theList, node1, node2);
910     theList->add(new RemoveFeatureCommand(theDocument, node2, alt));
911 }
912 
mergeNodes(Document * theDocument,CommandList * theList,PropertiesDock * theDock)913 void mergeNodes(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
914 {
915     if (theDock->selectionSize() <= 1)
916         return;
917     QList<Node*> Nodes;
918     QList<Feature*> alt;
919     for (int i=0; i<theDock->selectionSize(); ++i)
920         if (Node* N = CAST_NODE(theDock->selection(i)))
921             Nodes.push_back(N);
922     Node* merged = Nodes[0];
923     alt.push_back(merged);
924     for (int i=1; i<Nodes.size(); ++i) {
925         Feature::mergeTags(theDocument, theList, merged, Nodes[i]);
926         theList->add(new RemoveFeatureCommand(theDocument, Nodes[i], alt));
927     }
928 }
929 
detachNode(Document * theDocument,CommandList * theList,PropertiesDock * theDock)930 void detachNode(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
931 {
932     QList<Way*> Roads, Result;
933     QList<Node*> Points;
934     for (int i=0; i<theDock->selectionSize(); ++i)
935         if (Way* R = CAST_WAY(theDock->selection(i)))
936             Roads.push_back(R);
937         else if (Node* Pt = CAST_NODE(theDock->selection(i)))
938             Points.push_back(Pt);
939 
940     if (Roads.size() > 1 && Points.size() > 1)
941         return;
942 
943     if (Roads.size() == 0 && Points.size())
944     {
945         for (int i=0; i<Points.size(); ++i) {
946             Way * R = Way::GetSingleParentRoad(Points[i]);
947             if (R)
948                 theList->add(new WayRemoveNodeCommand(R, Points[i],
949                     theDocument->getDirtyOrOriginLayer(R)));
950         }
951     }
952 
953     if (Roads.size() > 1 && Points.size() == 1)
954     {
955         for (int i=0; i<Roads.size(); ++i) {
956             if (Roads[i]->find(Points[0]) < Roads[i]->size())
957                 theList->add(new WayRemoveNodeCommand(Roads[i], Points[0],
958                     theDocument->getDirtyOrOriginLayer(Roads[i])));
959         }
960     }
961 
962     if (Roads.size() == 1 && Points.size())
963     {
964         for (int i=0; i<Points.size(); ++i) {
965             if (Roads[0]->find(Points[i]) < Roads[0]->size())
966                 theList->add(new WayRemoveNodeCommand(Roads[0], Points[i],
967                     theDocument->getDirtyOrOriginLayer(Roads[0])));
968         }
969     }
970 }
971 
commitFeatures(Document * theDocument,CommandList * theList,PropertiesDock * theDock)972 void commitFeatures(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
973 {
974     QSet<Feature*> Features;
975     QQueue<Feature*> ToAdd;
976 
977     for (int i=0; i<theDock->selectionSize(); ++i)
978         if (!theDock->selection(i)->isUploadable() && !theDock->selection(i)->isSpecial())
979             ToAdd.enqueue(theDock->selection(i));
980 
981     while (!ToAdd.isEmpty()) {
982         Feature *feature = ToAdd.dequeue();
983         Features.insert(feature);
984         for (int j=0; j < feature->size(); ++j) {
985             Feature *member = feature->get(j);
986             if (!Features.contains(member)) {
987                 ToAdd.enqueue(member);
988             }
989         }
990     }
991 
992     if (Features.size()) {
993         Layer *layer = theDocument->getDirtyLayer();
994         foreach (Feature *feature, Features)
995             theList->add(new AddFeatureCommand(layer,feature,true));
996     }
997 }
998 
addRelationMember(Document * theDocument,CommandList * theList,PropertiesDock * theDock)999 void addRelationMember(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
1000 {
1001     Relation* theRelation = NULL;
1002     QList<Feature*> Features;
1003     for (int i=0; i<theDock->selectionSize(); ++i)
1004         if ((theDock->selection(i)->getClass() == "Relation") && !theRelation)
1005             theRelation = CAST_RELATION(theDock->selection(i));
1006         else
1007             Features.push_back(theDock->selection(i));
1008 
1009     if (!(theRelation && Features.size())) return;
1010 
1011     for (int i=0; i<Features.size(); ++i) {
1012         theList->add(new RelationAddFeatureCommand(theRelation, "", Features[i], theDocument->getDirtyOrOriginLayer(theRelation->layer())));
1013     }
1014 }
1015 
removeRelationMember(Document * theDocument,CommandList * theList,PropertiesDock * theDock)1016 void removeRelationMember(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
1017 {
1018     Relation* theRelation = NULL;
1019     QList<Feature*> Features;
1020     for (int i=0; i<theDock->selectionSize(); ++i)
1021         if ((theDock->selection(i)->getClass() == "Relation") && !theRelation)
1022             theRelation = CAST_RELATION(theDock->selection(i));
1023         else
1024             Features.push_back(theDock->selection(i));
1025 
1026     if (!theRelation && Features.size() == 1)
1027         theRelation = Feature::GetSingleParentRelation(Features[0]);
1028     if (!(theRelation && Features.size())) return;
1029 
1030     int idx;
1031     for (int i=0; i<Features.size(); ++i) {
1032         if ((idx = theRelation->find(Features[i])) != theRelation->size())
1033             theList->add(new RelationRemoveFeatureCommand(theRelation, idx, theDocument->getDirtyOrOriginLayer(theRelation->layer())));
1034     }
1035 }
1036 
addToMultipolygon(Document * theDocument,CommandList * theList,PropertiesDock * theDock)1037 void addToMultipolygon(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
1038 {
1039     Relation* theRelation = NULL;
1040     QList<Way*> theWays;
1041     for (int i=0; i<theDock->selectionSize(); ++i) {
1042         if (!theRelation && CHECK_RELATION(theDock->selection(i)) && theDock->selection(i)->tagValue("type", "") == "multipolygon")
1043             theRelation = STATIC_CAST_RELATION(theDock->selection(i));
1044         if (CHECK_WAY(theDock->selection(i)))
1045             theWays << STATIC_CAST_WAY(theDock->selection(i));
1046     }
1047 
1048     if (!theWays.size())
1049         return;
1050 
1051     if (theRelation) {
1052         for (int i=0; i<theRelation->size(); ++i) {
1053             if (CHECK_WAY(theRelation->get(i)) && !theWays.contains(STATIC_CAST_WAY(theRelation->get(i)))) {
1054                 theWays << STATIC_CAST_WAY(theRelation->get(i));
1055             }
1056         }
1057     }
1058 
1059     Way* outer = theWays[0];
1060     for (int i=1; i<theWays.size(); ++i) {
1061         if (outer->boundingBox().contains(theWays[i]->boundingBox()))
1062             continue;
1063         if (theWays[i]->boundingBox().contains(outer->boundingBox()))
1064             outer = theWays[i];
1065         else
1066             if (theWays[i]->boundingBox().intersects(outer->boundingBox()))
1067                 outer = NULL;
1068     }
1069     if (outer) {
1070         if (!theRelation) {
1071             theRelation = g_backend.allocRelation(theDocument->getDirtyOrOriginLayer());
1072             theList->add(new AddFeatureCommand(theDocument->getDirtyOrOriginLayer(),theRelation,true));
1073         } else {
1074             for (int i=0; i<theRelation->size(); ++i)
1075                 theList->add(new RelationRemoveFeatureCommand(theRelation, 0));
1076         }
1077         theRelation->setTag("type", "multipolygon");
1078     } else
1079         return;
1080 
1081     theList->add(new RelationAddFeatureCommand(theRelation, "outer", outer));
1082     for (int i=0; i<theWays.size(); ++i) {
1083         if (theWays[i] != outer) {
1084             theList->add(new RelationAddFeatureCommand(theRelation, "inner", theWays[i]));
1085         }
1086     }
1087 }
1088 
1089 /* Subdivide theRoad between index and index+1 into divisions segments.
1090  * divisions-1 new nodes are created starting at index index+1.
1091  */
subdivideRoad(Document * theDocument,CommandList * theList,Way * theRoad,unsigned int index,unsigned int divisions)1092 static void subdivideRoad(Document* theDocument, CommandList* theList,
1093                           Way* theRoad, unsigned int index, unsigned int divisions)
1094 {
1095     Node* N0 = theRoad->getNode(index);
1096     Node* N1 = theRoad->getNode(index+1);
1097     Coord nodeBase = N0->position();
1098     Coord nodeDelta = (N1->position() - nodeBase) / divisions;
1099     for (unsigned int i = 1; i < divisions; ++i) {
1100         nodeBase = nodeBase + nodeDelta;
1101         Node* newNode = g_backend.allocNode(theDocument->getDirtyOrOriginLayer(theRoad), nodeBase);
1102         theList->add(new AddFeatureCommand(theDocument->getDirtyOrOriginLayer(theRoad), newNode, true));
1103         theList->add(new WayAddNodeCommand(theRoad, newNode, index + i, theDocument->getDirtyOrOriginLayer(theRoad)));
1104     }
1105 }
1106 
canSubdivideRoad(PropertiesDock * theDock,Way ** outTheRoad,unsigned int * outEdge)1107 bool canSubdivideRoad(PropertiesDock* theDock, Way** outTheRoad, unsigned int* outEdge)
1108 {
1109     // Get the selected way and nodes
1110     Way* theRoad = NULL;
1111     Node* theNodes[2] = { NULL, NULL };
1112     for (int i = 0; i < theDock->selectionSize(); ++i) {
1113         if ((theDock->selection(i)->getClass() == "Way") && !theRoad)
1114             theRoad = CAST_WAY(theDock->selection(i));
1115         else if (theDock->selection(i)->getClass() == "Node") {
1116             if (!theNodes[0])
1117                 theNodes[0] = CAST_NODE(theDock->selection(i));
1118             else if (!theNodes[1])
1119                 theNodes[1] = CAST_NODE(theDock->selection(i));
1120         }
1121     }
1122 
1123     // If the way has only two nodes, use them
1124     if (theRoad && theRoad->size() == 2) {
1125         theNodes[0] = theRoad->getNode(0);
1126         theNodes[1] = theRoad->getNode(1);
1127         // Now this would just be silly
1128         if (theNodes[0] == theNodes[1])
1129             return false;
1130     }
1131 
1132     // A way and 2 nodes
1133     if (!theRoad || !theNodes[0] || !theNodes[1])
1134         return false;
1135 
1136     // Nodes must be adjacent in the way
1137     int numNodes = theRoad->size();
1138     int nodeIndex0 = -1;
1139     for (int i = 0; i < numNodes-1; ++i) {
1140         Node* N0 = theRoad->getNode(i);
1141         Node* N1 = theRoad->getNode(i+1);
1142         if ((N0 == theNodes[0] && N1 == theNodes[1]) ||
1143             (N0 == theNodes[1] && N1 == theNodes[0])) {
1144             nodeIndex0 = i;
1145             break;
1146         }
1147     }
1148 
1149     if (nodeIndex0 < 0)
1150         return false;
1151 
1152     if (outTheRoad)
1153         *outTheRoad = theRoad;
1154     if (outEdge)
1155         *outEdge = nodeIndex0;
1156 
1157     return true;
1158 }
1159 
subdivideRoad(Document * theDocument,CommandList * theList,PropertiesDock * theDock,unsigned int divisions)1160 void subdivideRoad(Document* theDocument, CommandList* theList, PropertiesDock* theDock, unsigned int divisions)
1161 {
1162     // Subdividing into 1 division is no-op
1163     if (divisions < 2)
1164         return;
1165 
1166     // Get the selected way and nodes
1167     Way* theRoad;
1168     unsigned int edge;
1169     if (!canSubdivideRoad(theDock, &theRoad, &edge))
1170         return;
1171     subdivideRoad(theDocument, theList, theRoad, edge, divisions);
1172 }
1173 
1174 /* Remove nodes between theNodes in theArea into a separate way newArea.
1175  * newArea's first node will be theNodes[0], and it's last nodes will be
1176  * theNodes[1] and theNodes[0].
1177  */
splitArea(Document * theDocument,CommandList * theList,Way * theArea,unsigned int nodes[2],Way ** outNewArea)1178 static bool splitArea(Document* theDocument, CommandList* theList,
1179                       Way* theArea, unsigned int nodes[2], Way** outNewArea)
1180 {
1181     // Make sure nodes are in order
1182     if (nodes[0] > nodes[1])
1183         qSwap(nodes[0], nodes[1]);
1184 
1185     // And not next to one another
1186     if (nodes[0] + 1 == nodes[1] ||
1187             (nodes[0] == 0 && nodes[1] == (unsigned int)theArea->size() - 2)) {
1188         qDebug() << "Nodes must not be adjacent";
1189         return false;
1190     }
1191 
1192     Node* theNodes[2];
1193     for (int i = 0; i < 2; ++i)
1194         theNodes[i] = theArea->getNode(nodes[i]);
1195 
1196     // Extract nodes between nodes[0] and nodes[1] into a separate area
1197     // and remove the nodes from the original area
1198     Way* newArea = g_backend.allocWay(theDocument->getDirtyOrOriginLayer(theArea));
1199     theList->add(new AddFeatureCommand(theDocument->getDirtyOrOriginLayer(theArea), newArea, true));
1200     copyTags(newArea, theArea);
1201     theList->add(new WayAddNodeCommand(newArea, theNodes[0], theDocument->getDirtyOrOriginLayer(theArea)));
1202     for (unsigned int i = nodes[0]+1; i < nodes[1]; ++i) {
1203         theList->add(new WayAddNodeCommand(newArea, theArea->getNode(nodes[0]+1),
1204                                            theDocument->getDirtyOrOriginLayer(theArea)));
1205         theList->add(new WayRemoveNodeCommand(theArea, theArea->getNode(nodes[0]+1),
1206                                               theDocument->getDirtyOrOriginLayer(theArea)));
1207     }
1208     theList->add(new WayAddNodeCommand(newArea, theNodes[1], theDocument->getDirtyOrOriginLayer(theArea)));
1209     theList->add(new WayAddNodeCommand(newArea, theNodes[0], theDocument->getDirtyOrOriginLayer(theArea)));
1210     handleWaysplitRelations(theDocument, theList, theArea, newArea);
1211 
1212     if (outNewArea)
1213         *outNewArea = newArea;
1214 
1215     return true;
1216 }
1217 
1218 // Cycle the node index in an area so it's >= 0, < size-1 (never the last node).
cycleNodeIndex(Way * area,int index)1219 static int cycleNodeIndex(Way* area, int index)
1220 {
1221     if (index >= area->size()-1)
1222         index -= area->size()-1;
1223     else if (index < 0)
1224         index += area->size()-1;
1225     return index;
1226 }
1227 
1228 // Find the index of a node in an area as close as possible to expected.
1229 // Return -1 if node isn't in area. This is useful for tracing around shared
1230 // nodes of two areas, where we want the indexes to be continuous so we can
1231 // detect where the edges depart from one another.
nodeIndexInArea(Way * area,Node * node,int expected)1232 static int nodeIndexInArea(Way* area, Node* node, int expected)
1233 {
1234     // parent list is gonna be shorter, so check this first
1235     for (int i = 0; i < node->sizeParents(); ++i)
1236         if (node->getParent(i) == area) {
1237             // now we know it's on the list, find the index
1238             if (area->getNode(expected) == node)
1239                 return expected;
1240             int forward = expected;
1241             int backward = expected;
1242             for (int j = 0; j < (area->size()-1)/2; ++j) {
1243                 forward = cycleNodeIndex(area, forward + 1);
1244                 if (area->getNode(forward) == node)
1245                     return forward;
1246                 backward = cycleNodeIndex(area, backward - 1);
1247                 if (area->getNode(backward) == node)
1248                     return backward;
1249             }
1250             break;
1251         }
1252     return -1;
1253 }
1254 
1255 // Determine if two area node indexes are adjacent, and in a particular
1256 // direction if direction is 1 or -1. If they are adjacent, direction is set to
1257 // 1 or -1. This is useful tracing the shared nodes between two areas as the
1258 // direction is initialised to 0 and determined on the first check, and then
1259 // enforced on later checks.
nodeIndexesAdjacent(Way * way,unsigned int n1,unsigned int n2,int * direction)1260 static bool nodeIndexesAdjacent(Way* way, unsigned int n1, unsigned int n2, int* direction)
1261 {
1262     int dir = n2 - n1;
1263     if (abs(dir) > 1) {
1264         // wrap?
1265         if (!n1)
1266             n1 = way->size() - 1;
1267         else if (!n2)
1268             n2 = way->size() - 1;
1269         else
1270             return false;
1271         dir = n2 - n1;
1272     }
1273     if (abs(dir) != 1)
1274         return false;
1275 
1276     // in the correct direction?
1277     if (!*direction) {
1278         *direction = dir;
1279         return true;
1280     } else
1281         return *direction == dir;
1282 }
1283 
1284 // Join selected areas which are sharing edges. Multiple nodes adjacent in both
1285 // areas must be shared for them to be joined.
1286 // FIXME This does not currently produce multipolygons when the areas form an
1287 // enclosed area. Instead the edge will run around the outside, cut into the
1288 // middle and run around the inner part, and then cut back out the same way.
joinAreas(Document * theDocument,CommandList * theList,PropertiesDock * theDock)1289 void joinAreas(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
1290 {
1291     // Collect the set of selected areas
1292     QSet<Way*> areas;
1293     for (int i = 0; i < theDock->selectionSize(); ++i)
1294         if (theDock->selection(i)->getClass() == "Way") {
1295             Way* area = CAST_WAY(theDock->selection(i));
1296             if (area->isClosed())
1297                 areas << area;
1298         }
1299 
1300 findNextJoin:
1301     // Go through areas in the set
1302     while (areas.size() > 1) {
1303         Way* area = *areas.begin();
1304         // Go through nodes shared between multiple features
1305         for (int i = 0; i < area->size() - 1; ++i) {
1306             Node* node = area->getNode(i);
1307             if (node->sizeParents() <= 1)
1308                 continue;
1309             // Go through parents of the node that are areas in our set
1310             for (int p1 = 0; p1 < node->sizeParents(); ++p1) {
1311                 if (!(node->getParent(p1)->getType() & IFeature::Polygon))
1312                     continue;
1313                 Way* otherArea = CAST_WAY(node->getParent(p1));
1314                 if (otherArea == area || !areas.contains(otherArea))
1315                     continue;
1316                 // Look for the first shared mutually adjacent node.
1317                 for (int n = 0; n < otherArea->size()-1; ++n) {
1318                     if (otherArea->getNode(n) != node)
1319                         continue;
1320                     int otherFirst = n;
1321                     int otherLast = otherFirst;
1322                     int otherDirection = 0;
1323                     int first = i;
1324                     int count = 1;
1325                     // We want node i to be the first shared node, so if the
1326                     // one before it is also shared and adjacent we can safely
1327                     // ignore it as we'll hit it again later
1328                     int lastOtherIndex = nodeIndexInArea(otherArea, area->getNode(cycleNodeIndex(area, i-1)), n);
1329                     if (lastOtherIndex != -1 && nodeIndexesAdjacent(otherArea, n, lastOtherIndex, &otherDirection))
1330                         continue;
1331                     // Continue forwards to see how many nodes are shared with
1332                     // the other area and adjacent. The first nodeIndexesAdjacent
1333                     // will set otherDirection and later nodes have to keep
1334                     // going in that direction.
1335                     for (int j = 1; j < area->size(); ++j) {
1336                         int jIndex = (i + j) % (area->size()-1);
1337                         int otherIndex = nodeIndexInArea(otherArea, area->getNode(jIndex), cycleNodeIndex(otherArea, otherLast + otherDirection));
1338                         // is next node in same area?
1339                         if (otherIndex == -1)
1340                             break;
1341                         // must be adjacent to last node (and in same direction)
1342                         if (!nodeIndexesAdjacent(otherArea, otherLast, otherIndex, &otherDirection))
1343                             break;
1344                         otherLast = otherIndex;
1345                         ++count;
1346                     }
1347                     if (count < 2)
1348                         continue;
1349 
1350                     // If all of area's nodes are shared (area is enclosed
1351                     // inside otherArea), then it is better to swap area and
1352                     // otherArea so that area is the one that gets removed.
1353                     if (count == area->size()) {
1354                         qSwap(area, otherArea);
1355                         qSwap(first, otherFirst);
1356                     }
1357 
1358                     // Remove nodes along the shared edge from area. No need to
1359                     // remove from otherArea, it'll get removed anyway.
1360                     for (int del = 0; del < count-2; ++del) {
1361                         int delIndex = cycleNodeIndex(area, first+1);
1362                         theList->add(new WayRemoveNodeCommand(area, delIndex, theDocument->getDirtyOrOriginLayer(area->layer())));
1363                         if (first > delIndex)
1364                             --first;
1365                     }
1366 
1367                     // If otherArea is completely enclosed then the end nodes
1368                     // of the shared edge are the same and are now adjacent, so
1369                     // remove one of them.
1370                     if (count == otherArea->size())
1371                         theList->add(new WayRemoveNodeCommand(area, first, theDocument->getDirtyOrOriginLayer(area->layer())));
1372                     // Seal the crack left over from an enclosed otherArea
1373                     // until the nodes on either side of 'first' diverge.
1374                     while (area->size() > 3 &&
1375                            area->getNode(cycleNodeIndex(area, first-1)) == area->getNode(cycleNodeIndex(area, first+1))) {
1376                         theList->add(new WayRemoveNodeCommand(area, first, theDocument->getDirtyOrOriginLayer(area->layer())));
1377                         theList->add(new WayRemoveNodeCommand(area, first, theDocument->getDirtyOrOriginLayer(area->layer())));
1378                         first = cycleNodeIndex(area, first-1);
1379                     }
1380                     // Add the remaining nodes of otherArea that aren't shared
1381                     // to area.
1382                     for (int add = 1; add < otherArea->size()-count; ++add) {
1383                         int index = cycleNodeIndex(otherArea, otherFirst - otherDirection*add);
1384                         theList->add(new WayAddNodeCommand(area, otherArea->getNode(index), ++first, theDocument->getDirtyOrOriginLayer(area->layer())));
1385                     }
1386                     // Merge tags and remove otherArea.
1387                     Feature::mergeTags(theDocument, theList, area, otherArea);
1388                     otherArea->deleteChildren(theDocument, theList);
1389                     theList->add(new RemoveFeatureCommand(theDocument, otherArea));
1390                     areas.remove(otherArea);
1391                     // Remove otherArea from selection.
1392                     QList<Feature*> sel = theDock->selection();
1393                     sel.removeOne(otherArea);
1394                     theDock->setSelection(sel);
1395                     // Restart the process again.
1396                     goto findNextJoin;
1397                 }
1398             }
1399         }
1400         areas.remove(area);
1401     }
1402 }
1403 
canSplitArea(PropertiesDock * theDock,Way ** outTheArea,unsigned int outNodes[2])1404 bool canSplitArea(PropertiesDock* theDock, Way** outTheArea, unsigned int outNodes[2])
1405 {
1406     if (theDock->selectionSize() != 3)
1407         return false;
1408 
1409     // Get the selected way and nodes
1410     Way* theArea = NULL;
1411     Node* theNodes[2] = { NULL, NULL };
1412     for (int i = 0; i < theDock->selectionSize(); ++i) {
1413         if ((theDock->selection(i)->getClass() == "Way") && !theArea)
1414             theArea = CAST_WAY(theDock->selection(i));
1415         else if (theDock->selection(i)->getClass() == "Node") {
1416             if (!theNodes[0])
1417                 theNodes[0] = CAST_NODE(theDock->selection(i));
1418             else if (!theNodes[1])
1419                 theNodes[1] = CAST_NODE(theDock->selection(i));
1420         }
1421     }
1422 
1423     // A way and 2 nodes
1424     if (!theArea || !theNodes[0] || !theNodes[1])
1425         return false;
1426 
1427     // Way must be closed
1428     if (!theArea->isClosed())
1429         return false;
1430 
1431     // Nodes must belong to way
1432     unsigned int numNodes = theArea->size();
1433     unsigned int nodeIndex[2];
1434     for (int i = 0; i < 2; ++i) {
1435         nodeIndex[i] = theArea->find(theNodes[i]);
1436         if (nodeIndex[i] >= numNodes)
1437             return false;
1438     }
1439 
1440     // Make sure nodes are in order
1441     if (nodeIndex[0] > nodeIndex[1])
1442         qSwap(nodeIndex[0], nodeIndex[1]);
1443 
1444     // And not next to one another
1445     if (nodeIndex[0] + 1 == nodeIndex[1] ||
1446             (nodeIndex[0] == 0 && nodeIndex[1] == (unsigned int)theArea->size() - 2))
1447         return false;
1448 
1449 
1450     if (outTheArea)
1451         *outTheArea = theArea;
1452     if (outNodes) {
1453         outNodes[0] = nodeIndex[0];
1454         outNodes[1] = nodeIndex[1];
1455     }
1456 
1457     return true;
1458 }
1459 
splitArea(Document * theDocument,CommandList * theList,PropertiesDock * theDock)1460 void splitArea(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
1461 {
1462     Way* theArea;
1463     unsigned int nodes[2];
1464     if (!canSplitArea(theDock, &theArea, nodes))
1465         return;
1466 
1467     Way* newArea;
1468     if (!splitArea(theDocument, theList, theArea, nodes, &newArea))
1469         return;
1470 
1471     theDock->setSelection(QList<Way*>() << theArea << newArea);
1472 }
1473 
terraceArea(Document * theDocument,CommandList * theList,Way * theArea,unsigned int sides[2],unsigned int divisions,int startNode,QList<Feature * > * outAreas)1474 static void terraceArea(Document* theDocument, CommandList* theList,
1475                         Way* theArea, unsigned int sides[2],
1476                         unsigned int divisions, int startNode,
1477                         QList<Feature*>* outAreas)
1478 {
1479     // We're adding nodes so ordering is important
1480     if (sides[0] > sides[1])
1481         qSwap(sides[0], sides[1]);
1482 
1483     bool reverse = (startNode >= 0 && ((5 + startNode - sides[1]) & 2));
1484 
1485     // Subdivide both sides
1486     subdivideRoad(theDocument, theList, theArea, sides[1], divisions);
1487     subdivideRoad(theDocument, theList, theArea, sides[0], divisions);
1488 
1489     // Split apart
1490     for (unsigned int i = sides[0] + divisions - 1; i > sides[0]; --i) {
1491         Way* newArea;
1492         unsigned int nodes[2] = { i, i + 3 };
1493         splitArea(theDocument, theList, theArea, nodes, &newArea);
1494         if (newArea && outAreas) {
1495             if (reverse)
1496                 outAreas->push_front(newArea);
1497             else
1498                 outAreas->push_back(newArea);
1499         }
1500     }
1501     if (outAreas) {
1502         if (reverse)
1503             outAreas->push_front(theArea);
1504         else
1505             outAreas->push_back(theArea);
1506     }
1507 }
1508 
canTerraceArea(PropertiesDock * theDock,Way ** outTheArea,int * startNode)1509 bool canTerraceArea(PropertiesDock* theDock, Way** outTheArea, int* startNode)
1510 {
1511     // Get the selected area
1512     Way* theArea = NULL;
1513     Node* theNode = NULL;
1514     for (int i = 0; i < theDock->selectionSize(); ++i)
1515         if ((theDock->selection(i)->getClass() == "Way") && !theArea) {
1516             theArea = CAST_WAY(theDock->selection(i));
1517         } else if (startNode && theDock->selection(i)->getClass() == "Node") {
1518             theNode = CAST_NODE(theDock->selection(i));
1519         }
1520     if (!theArea || !theArea->isClosed())
1521         return false;
1522 
1523     if (startNode) {
1524         if (theNode)
1525             *startNode = theArea->find(theNode);
1526         else
1527             *startNode = -1;
1528     }
1529 
1530     // Only work with 4 edges for now
1531     if (theArea->size() != 5)
1532         return false;
1533 
1534     if (outTheArea)
1535         *outTheArea = theArea;
1536 
1537     return true;
1538 }
1539 
terraceArea(Document * theDocument,CommandList * theList,PropertiesDock * theDock,unsigned int divisions)1540 void terraceArea(Document* theDocument, CommandList* theList, PropertiesDock* theDock, unsigned int divisions)
1541 {
1542     Way* theArea;
1543     int startNode;
1544     if (!canTerraceArea(theDock, &theArea, &startNode))
1545         return;
1546 
1547     qreal longestLen = 0.0f;
1548     unsigned int sides[2];
1549     sides[0] = 0;
1550     for (int i = 0; i < theArea->size()-1; ++i) {
1551         const Coord p1(theArea->getNode(i)->position());
1552         const Coord p2(theArea->getNode(i+1)->position());
1553         const qreal len = p1.distanceFrom(p2);
1554         if (len > longestLen) {
1555             longestLen = len;
1556             sides[0] = i;
1557         }
1558     }
1559     sides[1] = (sides[0] + 2) % (theArea->size() - 1);
1560 
1561     QList<Feature*> areas;
1562     terraceArea(theDocument, theList, theArea, sides, divisions, startNode, &areas);
1563 
1564     theDock->setSelection(areas);
1565 }
1566 
1567 // Remove repeated (adjacent) nodes in ways.
removeRepeatsInRoads(Document * theDocument,CommandList * theList,PropertiesDock * theDock)1568 static void removeRepeatsInRoads(Document* theDocument, CommandList* theList, PropertiesDock* theDock)
1569 {
1570     for (int i = 0; i < theDock->selectionSize(); ++i) {
1571         if (theDock->selection(i)->getClass() == "Way") {
1572             Way *way = CAST_WAY(theDock->selection(i));
1573             Node *last = 0;
1574             for (int j = 0; j < way->size();) {
1575                 Node *node = way->getNode(j);
1576                 if (node == last) {
1577                     theList->add(new WayRemoveNodeCommand(way, j, theDocument->getDirtyOrOriginLayer(way->layer())));
1578                     continue;
1579                 }
1580                 last = node;
1581                 ++j;
1582             }
1583         }
1584     }
1585 }
1586 
1587 // Align a set of midpoints in a specific direction
axisAlignAlignMidpoints(QVector<QPointF> & midpoints,const QVector<qreal> weights,const QPointF & dir,int begin,int end)1588 static void axisAlignAlignMidpoints(QVector<QPointF> &midpoints, const QVector<qreal> weights, const QPointF &dir, int begin, int end)
1589 {
1590     // get weighted average midpoint
1591     const int n = midpoints.size();
1592     QPointF avg(0.0, 0.0);
1593     qreal total_weight = 0.0;
1594     for (int j = begin; j < end; ++j) {
1595         int i = j % n;
1596         avg += weights[i]*midpoints[i];
1597         total_weight += weights[i];
1598     }
1599     avg /= total_weight;
1600 
1601     // project midpoints onto dir line through avg
1602     for (int j = begin; j < end; ++j) {
1603         int i = j % n;
1604         QPointF rel_mid = midpoints[i] - avg;
1605         qreal dot = dir.x()*rel_mid.x() + dir.y()*rel_mid.y();
1606         // assumes dir is normalised
1607         midpoints[i] = avg + dir*dot;
1608     }
1609 }
1610 
1611 // Axis align uses binary fixed point for angles
1612 static const int angle_shift = 20;
1613 
1614 // returns true on success
axisAlignGetWays(PropertiesDock * theDock,QList<Way * > & theWays)1615 static bool axisAlignGetWays(/* in */ PropertiesDock *theDock,
1616                              /* out */ QList<Way *> &theWays)
1617 {
1618     QList<Feature *> selection = theDock->selection();
1619     // we can't use foreach since we append to selection inside the loop
1620     for (; !selection.empty(); selection.pop_front()) {
1621         Feature *F = selection.first();
1622         if (F->getClass() == "Way")
1623             theWays << CAST_WAY(F);
1624         else if (F->getClass() == "Relation")
1625             // expand relation members onto the end of the selection list
1626             for (int i = 0; i < F->size(); ++i)
1627                 selection << F->get(i);
1628     }
1629     return !theWays.empty();
1630 }
1631 
1632 // base the decision purely on what types of features are selected
canAxisAlignRoads(PropertiesDock * theDock)1633 bool canAxisAlignRoads(PropertiesDock* theDock)
1634 {
1635     QList<Way *> theWays;
1636     return axisAlignGetWays(theDock, theWays);
1637 }
1638 
1639 // returns true on success
axisAlignPreprocess(PropertiesDock * theDock,const Projection & proj,int axes,QList<Way * > & theWays,QVector<int> & edge_angles,QVector<int> & edge_axis,QVector<qreal> & edge_weight,qreal & total_weight,QVector<QPointF> & midpoints)1640 static bool axisAlignPreprocess(/* in */ PropertiesDock *theDock, const Projection &proj, int axes,
1641                                 /* out */ QList<Way *> &theWays, QVector<int> &edge_angles, QVector<int> &edge_axis,
1642                                           QVector<qreal> &edge_weight, qreal &total_weight, QVector<QPointF> &midpoints)
1643 {
1644     if (!axisAlignGetWays(theDock, theWays))
1645         return false;
1646 
1647     // manipulate angles as fixed point values with a unit of the angle between axes
1648     int nedges = 0;
1649     foreach (Way *theWay, theWays)
1650         nedges += theWay->size()-1;
1651     edge_angles.resize(nedges);
1652     edge_axis.resize(nedges);
1653     edge_weight.resize(nedges);
1654     midpoints.resize(nedges);
1655     total_weight = 0.0;
1656 
1657     // Look at nodes and find angles etc
1658     int i = 0;
1659     foreach (Way *theWay, theWays) {
1660         Node *n1;
1661         Node *n2 = theWay->getNode(0);
1662         QPointF p1;
1663         QPointF p2 = proj.project(n2->position());
1664         for (int j = 0; j < theWay->size()-1; ++j, ++i) {
1665             n1 = n2;
1666             n2 = theWay->getNode(j + 1);
1667             p1 = p2;
1668             p2 = proj.project(n2->position());
1669             if (n1 == n2 || p1 == p2) {
1670                 qWarning() << "ERROR: duplicate nodes found during axis align in" << theWay->id().numId;
1671                 return false;
1672             }
1673             midpoints[i] = (p1 + p2) * 0.5;
1674             // weight towards longer edges rather than lots of smaller edges
1675             edge_weight[i] = n1->position().distanceFrom(n2->position());
1676             edge_weight[i] *= edge_weight[i];
1677             total_weight += edge_weight[i];
1678             // calculate angle of edge
1679             qreal ang = QLineF(p1, p2).angle();
1680             edge_angles[i] = ang * ((axes << angle_shift) / 360.0);
1681             if (edge_angles[i] < 0) {
1682                 edge_angles[i] += axes<<angle_shift;
1683             }
1684             edge_axis[i] = -1;
1685         }
1686     }
1687     return true;
1688 }
1689 
1690 // QVectors must be same size
1691 // return true on success
axisAlignCluster(const QVector<int> & edge_angles,const QVector<qreal> & edge_weight,qreal total_weight,unsigned int axes,int & theta,QVector<int> & edge_axis)1692 static bool axisAlignCluster(/* in */  const QVector<int> &edge_angles, const QVector<qreal> &edge_weight,
1693                                        qreal total_weight, unsigned int axes,
1694                              /* out */ int &theta, QVector<int> &edge_axis)
1695 {
1696     // Use a variant of K-Means Clustering with regular spaced clusters to find
1697     // the best angle for the first axis.
1698     const int nedges = edge_angles.size();
1699     bool changed;
1700     theta = 0;
1701     // This should always terminate pretty quickly, but for robustness it's better to warn than hang.
1702     unsigned int safety = 100;
1703     while (--safety) {
1704         // reassign edges to axes, stopping when nothing changes
1705         changed = false;
1706         for (int i = 0; i < nedges; ++i) {
1707             int angle = edge_angles[i] - theta;
1708             int new_axis = ((angle >> (angle_shift - 1)) + 1) >> 1;
1709             if (new_axis >= (int)axes)
1710                 new_axis -= axes;
1711             else if (new_axis < 0)
1712                 new_axis += axes;
1713             if (new_axis != edge_axis[i]) {
1714                 edge_axis[i] = new_axis;
1715                 changed = true;
1716             }
1717         }
1718         if (!changed)
1719             break;
1720         // adjust angle of first axis by weighted mean of angles between edges and their assigned axes
1721         qreal dtheta = 0;
1722         for (int i = 0; i < nedges; ++i) {
1723             int diff = (edge_angles[i] - theta - (edge_axis[i] << angle_shift)) % (1 << angle_shift);
1724             if (diff > ((1<<angle_shift)>>1))
1725                 diff -= (1<<angle_shift);
1726             else if (diff < -((1<<angle_shift)>>1))
1727                 diff += (1<<angle_shift);
1728             dtheta += edge_weight[i]*diff;
1729         }
1730         dtheta /= total_weight;
1731         theta += dtheta;
1732     }
1733     if (!safety)
1734         qWarning() << "WARNING: align axes clustering loop exceeded safety limit";
1735 
1736     return safety;
1737 }
1738 
1739 // QVectors must be same size
axisAlignCalcVariance(const QVector<int> & edge_angles,const QVector<int> & edge_axis,int theta)1740 static qreal axisAlignCalcVariance(const QVector<int> &edge_angles, const QVector<int> &edge_axis, int theta)
1741 {
1742     const int nedges = edge_angles.size();
1743     qreal variance = 0.0;
1744 
1745     for (int i = 0; i < nedges; ++i) {
1746         int diff = (edge_angles[i] - theta - (edge_axis[i] << angle_shift)) % (1 << angle_shift);
1747         if (diff > ((1<<angle_shift)>>1))
1748             diff -= (1<<angle_shift);
1749         else if (diff < -((1<<angle_shift)>>1))
1750             diff += (1<<angle_shift);
1751         qreal diff_f = (double)diff / ((1<<angle_shift)>>1);
1752         variance += diff_f*diff_f;
1753     }
1754     variance /= nedges;
1755     return variance;
1756 }
1757 
1758 // Guesses the number of axes in a shape.
1759 // returns 0 on failure.
axisAlignGuessAxes(PropertiesDock * theDock,const Projection & proj,unsigned int max_axes)1760 unsigned int axisAlignGuessAxes(PropertiesDock* theDock, const Projection &proj, unsigned int max_axes)
1761 {
1762     QList<Way *> theWays;
1763     QVector<int> edge_angles;
1764     QVector<int> edge_axis;
1765     QVector<qreal> edge_weight;
1766     QVector<QPointF> midpoints;
1767     qreal total_weight;
1768     qreal min_var;
1769     int min_var_axes = 0;
1770     for (unsigned int axes = 3; axes <= max_axes; ++axes) {
1771         if (!axisAlignPreprocess(theDock, proj, axes,
1772                                  theWays, edge_angles, edge_axis, edge_weight, total_weight, midpoints))
1773             return 0;
1774         int theta;
1775         axisAlignCluster(edge_angles, edge_weight, total_weight, axes, theta, edge_axis);
1776         qreal var = axisAlignCalcVariance(edge_angles, edge_axis, theta);
1777         // prefer a lower number of axes
1778         var *= sqrt((float)axes);
1779         // we want the number of axes with the lowest weighted variance
1780         if (!min_var_axes || var < min_var) {
1781             min_var = var;
1782             min_var_axes = axes;
1783         }
1784     }
1785 
1786     return min_var_axes;
1787 }
1788 
1789 // maximum number of iterations
1790 #define AXIS_ALIGN_MAX_ITS          10000
1791 // threshold of (node movement/distance from midpoint)^2
1792 #define AXIS_ALIGN_FAR_THRESHOLD    (1e-6)  // 1/1000th ^2
1793 
1794 // don't add theList to history if result isn't success
axisAlignRoads(Document * theDocument,CommandList * theList,PropertiesDock * theDock,const Projection & proj,unsigned int axes)1795 AxisAlignResult axisAlignRoads(Document* theDocument, CommandList* theList, PropertiesDock* theDock, const Projection &proj, unsigned int axes)
1796 {
1797     // Make sure nodes aren't pointlessly repeated or angles would be undefined
1798     removeRepeatsInRoads(theDocument, theList, theDock);
1799 
1800     // manipulate angles as fixed point values with a unit of the angle between axes
1801     QList<Way *> theWays;
1802     QVector<int> edge_angles;
1803     QVector<int> edge_axis;
1804     QVector<qreal> edge_weight;
1805     QVector<QPointF> midpoints;
1806     qreal total_weight;
1807     if (!axisAlignPreprocess(theDock, proj, axes,
1808                              theWays, edge_angles, edge_axis, edge_weight, total_weight, midpoints)) {
1809         // should never happen, repeated nodes already removed
1810         Q_ASSERT(0);
1811         theList->undo();
1812         return AxisAlignFail;
1813     }
1814 
1815     int theta;
1816     axisAlignCluster(edge_angles, edge_weight, total_weight, axes, theta, edge_axis);
1817 
1818     // Calculate axis direction vectors
1819     QVector<QPointF> axis_vectors(axes);
1820     for (unsigned int i = 0; i < axes; ++i) {
1821         int angle = theta + (i<<angle_shift);
1822         axis_vectors[i].setX(cos(M_PI*2/(axes<<angle_shift)*angle));
1823         axis_vectors[i].setY(-sin(M_PI*2/(axes<<angle_shift)*angle));
1824     }
1825 
1826     // If adjacent edges are in the same axis we can't intersect them to find
1827     // where the node should go. Align midpoints of adjacent edges on the same
1828     // axis so that we can just project the nodes onto the axis and make the
1829     // result axis aligned. Note that this does not handle adjacent edges which
1830     // have opposite axes and are therefore parallel (when axes is even).
1831     int i = 0;
1832     foreach (Way *theWay, theWays) {
1833         int start = i;
1834         int n = theWay->size()-1;
1835         int last_axis = -1;
1836         int first_in_seq = -1;
1837         int excess = 0;
1838         for (; i < start + n; ++i) {
1839             int axis = edge_axis[i];
1840             if (axis != last_axis) {
1841                 if (first_in_seq == -1) {
1842                     // If edges sharing axis cross the join between ends, we'll
1843                     // come back to it anyway.
1844                     excess = i;
1845                     if (i != 0 || !theWay->isClosed() || edge_axis[start+n-1] != axis)
1846                         first_in_seq = i;
1847                 } else {
1848                     if (i - first_in_seq > 1) {
1849                         axisAlignAlignMidpoints(midpoints, edge_weight, axis_vectors[last_axis], first_in_seq, i);
1850                     }
1851                     first_in_seq = i;
1852                 }
1853                 last_axis = axis;
1854             }
1855         }
1856         if (n + excess - first_in_seq > 1)
1857             axisAlignAlignMidpoints(midpoints, edge_weight, axis_vectors[last_axis], first_in_seq, n+excess);
1858     }
1859 
1860     // Get the positions of each unique node
1861     QHash<Node *, QPointF> node_pos;
1862     bool dups = false;
1863     foreach (Way *theWay, theWays) {
1864         for (int i = 0; i < theWay->size(); ++i) {
1865             Node *N = theWay->getNode(i);
1866             if (!dups && node_pos.contains(N) && !(theWay->isClosed() && i == theWay->size()-1))
1867                 dups = true;
1868             node_pos[N] = proj.project(N->position());
1869         }
1870     }
1871 
1872     // If nodes are repeated then they will be moved more than once, so we
1873     // iterate and allow the nodes to converge to a point.
1874     qreal last_movement = -1.0;
1875     qreal movement = 0.0;
1876     for (int it = 0; ; ++it) {
1877         i = 0;
1878         bool moved_far = false;
1879         // Move nodes (only in node_pos) onto the intersection of the neighbouring
1880         // edge's axes through their midpoints. Where neighbouring edges are on the
1881         // same axis (and so there is no intersection), project directly onto the
1882         // axis through the mean midpoint.
1883         foreach (Way *theWay, theWays) {
1884             int index0, index1, max;
1885             int start = i;
1886             int n = theWay->size()-1;
1887             if (theWay->isClosed()) {
1888                 index1 = start+n-1;
1889                 max = n;
1890             } else {
1891                 index1 = -1;
1892                 max = theWay->size();
1893             }
1894             for (int j = 0; j < max; ++j) {
1895                 index0 = index1;
1896                 index1 = ((j < n) ? i++ : -1);
1897                 Node *N = theWay->getNode(j);
1898                 QPointF old_pos = node_pos[N];
1899                 QPointF new_pos;
1900                 QPointF mid;
1901                 if (index0 >= 0 && index1 >= 0 && edge_axis[index0] != edge_axis[index1]) {
1902                     // axes different, so probably safe to intersect
1903                     QLineF l0(midpoints[index0], midpoints[index0] + axis_vectors[edge_axis[index0]]);
1904                     QLineF l1(midpoints[index1], midpoints[index1] + axis_vectors[edge_axis[index1]]);
1905                     if (l0.intersect(l1, &new_pos) == QLineF::NoIntersection) {
1906                         theList->undo();
1907                         return AxisAlignSharpAngles;
1908                     }
1909 
1910                     if (dups && !moved_far)
1911                         mid = midpoints[index0];
1912                 } else {
1913                     // Axes are the same so there's no intersection point. Just
1914                     // project onto axis through average midpoint.
1915                     int index = ((index0 >= 0) ? index0 : index1);
1916                     QPointF midpoint = midpoints[index];
1917                     if (index != index1 && index1 >= 0)
1918                         midpoint = (midpoint + midpoints[index1]) / 2;
1919                     QPointF rel_pos = old_pos - midpoint;
1920                     QPointF dir = axis_vectors[edge_axis[index]];
1921                     qreal dot = dir.x()*rel_pos.x() + dir.y()*rel_pos.y();
1922                     new_pos = midpoint + dir*dot;
1923 
1924                     if (dups && !moved_far)
1925                         mid = midpoints[index];
1926                 }
1927                 if (dups) {
1928                     // If we're iterating, only move the node half the distance
1929                     // towards the new position, so that different edges can
1930                     // compete with one another to move the node.
1931                     new_pos = (new_pos + old_pos) / 2;
1932 
1933                     // find how far the node has moved (squared to avoid a sqrt)
1934                     QPointF delta = new_pos - old_pos;
1935                     qreal moved_sq = delta.x()*delta.x() + delta.y()*delta.y();
1936                     movement += moved_sq;
1937 
1938                     // has the node moved "far"?
1939                     if (!moved_far) {
1940                         delta = new_pos - mid;
1941                         qreal size_sq = delta.x()*delta.x() + delta.y()*delta.y();
1942                         if (moved_sq > size_sq * AXIS_ALIGN_FAR_THRESHOLD)
1943                             moved_far = true;
1944                     }
1945                 }
1946                 node_pos[N] = new_pos;
1947             }
1948         }
1949 
1950         // We're done when no nodes have moved very far.
1951         if (!moved_far)
1952             dups = false;
1953         if (!dups || it >= AXIS_ALIGN_MAX_ITS)
1954             break;
1955         // Every so often check that the movement has decreased since last
1956         // time. If it hasn't then it's likely that the ways are impossible to
1957         // align.
1958         if ((it & 0xf) == 0xf) {
1959             if (last_movement >= 0.0 && movement >= last_movement)
1960                 break;
1961             last_movement = movement;
1962             movement = 0.0;
1963         }
1964 
1965         // tweak midpoints
1966         i = 0;
1967         foreach (Way *theWay, theWays) {
1968             Node *n2 = theWay->getNode(0);
1969             QPointF p1;
1970             QPointF p2 = node_pos[n2];
1971             for (int j = 0; j < theWay->size()-1; ++j, ++i) {
1972                 n2 = theWay->getNode(j + 1);
1973                 p1 = p2;
1974                 p2 = node_pos[n2];
1975                 midpoints[i] = (p1 + p2) * 0.5;
1976             }
1977         }
1978     }
1979     // dups is set to false when converged
1980     if (dups) {
1981         theList->undo();
1982         return AxisAlignFail;
1983     }
1984     // Commit the changes
1985     foreach (Way *theWay, theWays) {
1986         for (int i = 0; i < theWay->size(); ++i) {
1987             Node *N = theWay->getNode(i);
1988             theList->add(new MoveNodeCommand(N, proj.inverse(node_pos[N]), theDocument->getDirtyOrOriginLayer(N->layer())));
1989         }
1990     }
1991     return AxisAlignSuccess;
1992 }
1993