source: subversion/applications/editors/potlatch/way.as @ 18898

Last change on this file since 18898 was 18823, checked in by richard, 10 years ago

Potlatch 1.3a

File size: 55.0 KB
Line 
1
2        // =====================================================================================
3        // OOP classes - OSMWay
4
5        // ---- Initialise
6       
7        function OSMWay() {
8                this.resetBBox();
9                this.path=new Array();                  // list of nodes
10                this.attr=new Array();                  // hash of tags
11                this.mergedways=new Array();    // list of ways merged into this
12                this.deletednodes=new Object(); // hash of nodes deleted from this
13        };
14
15        OSMWay.prototype=new MovieClip();
16        OSMWay.prototype.clean=true;                            // altered since last upload?
17        OSMWay.prototype.uploading=false;                       // currently uploading?
18        OSMWay.prototype.locked=false;                          // locked against upload?
19        OSMWay.prototype.version=0;                                     // version number?
20        OSMWay.prototype.uid=0;                                         // user ID (used for TIGER detection)
21        OSMWay.prototype.historic=false;                        // is this an undeleted, not-uploaded way?
22        OSMWay.prototype.checkconnections=false;        // check shared nodes on reload
23
24        // ---- Load from remote server
25
26        OSMWay.prototype.load=function() {
27                responder = function() { };
28                responder.onResult = function(result) {
29                        _root.waysreceived+=1;
30                        var code=result.shift(); var msg=result.shift(); if (code) { handleError(code,msg,result); return; }
31                        var w=result[0];
32                        if (length(result[1])==0) { removeMovieClip(_root.map.ways[w]); 
33                                                                                removeMovieClip(_root.map.areas[w]); return; }
34                        var i,id,x,y,prepoint,redrawws;
35                        _root.map.ways[w].clean=true;
36                        _root.map.ways[w].locked=false;
37                        _root.map.ways[w].historic=false;
38                        _root.map.ways[w].version=result[3];
39                        _root.map.ways[w].uid=result[4];
40                        _root.map.ways[w].removeNodeIndex();
41                        _root.map.ways[w].path=[];
42                        _root.map.ways[w].resetBBox();
43                        for (i=0; i<result[1].length; i++) {
44                                x =result[1][i][0];
45                                y =result[1][i][1];
46                                id=result[1][i][2];
47                                _root.map.ways[w].updateBBox(x,y);
48                                x=long2coord(x); y=lat2coord(y);
49                                if (nodes[id]) {
50                                        // already exists: move node in other ways if required
51                                        // ** maybe we should take out 'w'? not sure
52                                        if (_root.map.ways[w].checkconnections) { nodes[id].moveTo(x,y,w); }
53                                } else {
54                                        // doesn't exist, so create new node
55                                        _root.nodes[id]=new Node(id,x,y,result[1][i][3],result[1][i][4]);
56                                        _root.nodes[id].clean=true;
57                                        if (id==prenode) { prepoint=i; }
58                                }
59                                _root.map.ways[w].path.push(_root.nodes[id]);
60                                _root.nodes[id].addWay(w);
61                                if (_root.nodes[id].ways[_root.wayselected] && w!=_root.wayselected) { redrawws=true; }
62                        }
63                        _root.map.ways[w].attr=result[2];
64                        _root.map.ways[w].redraw();
65                        if (redrawws)  { _root.ws.highlightPoints(5000,"anchor"); }     // need to redraw [_]s if new connections loaded
66                        if (w==preway) { _root.map.ways[w].select(); preway=undefined; }
67                        if (prepoint)  { _root.map.ways[w].select(); 
68                                                         _root.map.anchors[prepoint].select();
69                                                         prenode=prepoint=undefined; }
70                        _root.map.ways[w].clearPOIs();
71                        _root.map.ways[w].checkconnections=false;
72                };
73                remote_read.call('getway',responder,Math.floor(this._name));
74        };
75
76        OSMWay.prototype.loadFromDeleted=function(timestamp) {
77                delresponder=function() { };
78                delresponder.onResult=function(result) {
79                        var code=result.shift(); var msg=result.shift(); if (code) { handleError(code,msg,result); return; }
80                        var i,id,n;
81                        var w=_root.map.ways[result[0]];
82                        var oldpath=new Object();               // make sure to delete any nodes not in reverted way
83                        for (i=0; i<w.path.length; i++) { oldpath[w.path[i].id]=w.path[i].version; }
84                        if (result[4]) { w.historic=false; w.clean=true;  }
85                                  else { w.historic=true;  w.clean=false; }
86                        w.version=result[3];
87                        w.removeNodeIndex();
88                        w.path=[];
89                        w.resetBBox();
90                        for (i=0; i<result[1].length; i++) {
91                                n=result[1][i];
92                                x=n[0]; y=n[1]; id=n[2];                                        // 3:current version, 4:tags, 5:reuse?
93                                if (id<0) { id=--_root.newnodeid; }                     // assign negative IDs to anything moved
94                                delete oldpath[id];
95                                w.updateBBox(x,y);
96                                x=long2coord(x); y=lat2coord(y);
97                                if (nodes[id]) {
98                                        if (x!=nodes[id].x || y!=nodes[id].y) {
99                                                // ** also needs to check that tags haven't changed
100                                                nodes[id].moveTo(x,y,w);
101                                                nodes[id].attr=n[4];
102                                                nodes[id].version=n[3];
103                                                w.clean=false;
104                                        }
105                                        if (n[5]) { nodes[id].clean=true; }             // is visible and current version
106                                } else {
107                                        _root.nodes[id]=new Node(id,x,y,n[4],n[3]);
108                                }
109                                w.path.push(_root.nodes[id]);
110                                _root.nodes[id].addWay(result[0]);
111                        }
112                        for (i in oldpath) { if (i>0) { w.deletednodes[i]=oldpath[i]; } }
113                        w.attr=result[2];
114                        if (w==ws) { w.select(); }
115                                  else { w.locked=true; }
116                        w.redraw();
117                        w.clearPOIs();
118                };
119                remote_read.call('getway_old',delresponder,Number(this._name),timestamp);
120        };
121
122        OSMWay.prototype.clearPOIs=function() {
123                // check if any way nodes are POIs, delete the POIs if so
124                var i,z;
125                z=this.path;
126                for (i in z) {
127                        if (_root.map.pois[this.path[i].id]) { removeMovieClip(_root.map.pois[this.path[i].id]); }
128                }
129        };
130
131        // ---- Draw line
132
133        OSMWay.prototype.redraw=function(skip,skiponeway) {
134                this.createEmptyMovieClip("taggednodes",3);
135
136                if (skip && !this.attr["oneway"]) {
137                        // We're at the same scale as previously, so don't redraw
138                        // ** will refactor this when we do proper POI icons
139                        for (var i=0; i<this.path.length; i+=1) {
140                                if (this.path[i].tagged) {
141                                        this.taggednodes.attachMovie("poiinway",i,i);
142                                        this.taggednodes[i]._x=this.path[i].x;
143                                        this.taggednodes[i]._y=this.path[i].y;
144                                        this.taggednodes[i]._xscale=this.taggednodes[i]._yscale=taggedscale;
145                                }
146                        }
147                } else {
148
149                        // Either the line has changed, or we've changed scale
150                        this.createEmptyMovieClip("line",1);                                    // clear line
151                        this.createEmptyMovieClip("arrows",2);                                  //  |
152                        var linealpha=100; // -50*(this.locked==true);
153                        var casingx=(scale<17 || preferences.data.thinlines) ? 1.5 : 1.3; var casingcol=0x222222;
154       
155                        // Set stroke
156       
157                        var f=this.getFill();
158                        if              (this.locked)                                    { this.line.lineStyle(linewidth,0xFF0000,linealpha,false,"none"); }
159                        else if (colours[this.attr["highway"]])  { this.line.lineStyle(linewidth,colours[this.attr["highway" ]],linealpha,false,"none"); }
160                        else if (colours[this.attr["waterway"]]) { this.line.lineStyle(linewidth,colours[this.attr["waterway"]],linealpha,false,"none"); }
161                        else if (colours[this.attr["railway"]])  { this.line.lineStyle(linewidth,colours[this.attr["railway" ]],linealpha,false,"none"); }
162                        else {
163                                var c=0xAAAAAA; var z=this.attr;
164                                for (var i in z) { if (i!='created_by' && this.attr[i]!='' && this.attr[i].substr(0,6)!='(type ') { c=0x707070; } }
165                                this.line.lineStyle((f>-1 || this.attr['boundary']) ? areawidth : linewidth,c,linealpha,false,"none");
166                        }
167                       
168                        // Draw fill/casing
169       
170                        var br=false;
171                        if (preferences.data.tiger && this.uid==7168 && this.version==1 && this.clean && this.attr["tiger:tlid"]) {
172                casingx=2; casingcol=0xFF00FF;
173                        } else if (preferences.data.noname && this.attr["highway"] && (!this.attr["name"] || this.attr["name"].substr(0,6)=='(type ')) {
174                casingx=2; casingcol=0xFF0000;
175                        } else if (this.attr["bridge"] && this.attr["bridge"]!="no") {
176                                casingx=(scale<17) ? 2 : 1.8; br=true;
177                        }
178
179                        if ((f>-1 || br || casing[this.attr['highway']]) && !this.locked) {
180                                if (!_root.map.areas[this._name]) { _root.map.areas.createEmptyMovieClip(this._name,++areadepth); }
181                                with (_root.map.areas[this._name]) {
182                                        clear();
183                                        enabled=false;
184                                        moveTo(this.path[0].x,this.path[0].y); 
185                                        if (f>-1) { beginFill(f,20); }
186                                                 else { lineStyle(linewidth*casingx,casingcol,100,false,"none"); }
187                                        for (var i=1; i<this.path.length; i+=1) {
188                                                lineTo(this.path[i].x,this.path[i].y);
189                                        }
190                                        if (f>-1) { endFill(); }
191                                };
192                        } else if (_root.map.areas[this._name]) {
193                                removeMovieClip(_root.map.areas[this._name]);
194                        }
195       
196                        // Draw line and tagged nodes
197       
198                        this.line.moveTo(this.path[0].x,this.path[0].y);
199                        for (var i=0; i<this.path.length; i+=1) {
200                                if (i>0) { this.line.lineTo(this.path[i].x,this.path[i].y); }
201                                if (this.path[i].tagged) {
202                                        // **** attach correct icon:
203                                        // if (this.path[i].attr['frog']) {
204                                        // this.taggednodes.attachMovie("poi_22",i,i);
205                                        // ...probably don't have to do _root.map.pois[point].__proto__=undefined;
206                                        //    because clicks are handled by the way movieclip
207                                        this.taggednodes.attachMovie("poiinway",i,i);
208                                        this.taggednodes[i]._x=this.path[i].x;
209                                        this.taggednodes[i]._y=this.path[i].y;
210                                        this.taggednodes[i]._xscale=this.taggednodes[i]._yscale=taggedscale;
211                                }
212                        }
213
214                        // Draw relations
215
216                        var z=_root.wayrels[this._name];
217                        for (var rel in z) { _root.map.relations[rel].redraw(); }
218                       
219                        // Draw direction arrows
220                       
221                        if (this.attr["oneway"] && !skiponeway) {
222                                this.drawArrows(this.attr["oneway"]);
223                        }
224                }
225        };
226
227        OSMWay.prototype.getFill=function() {
228                var f=-1; 
229                if (this.path[this.path.length-1]==this.path[0] && this.path.length>2) {
230                        if (this.attr['area']) { f='0x777777'; }
231                        var z=this.attr;
232                        for (var i in z) { if (areas[i] && this.attr[i]!='' && this.attr[i]!='coastline') { f=areas[i]; } }
233                }
234                return f;
235        };
236
237        OSMWay.prototype.drawArrows=function(dir) {
238                if (dir=='no' || dir=='false') { return; }
239                var dashes=[4,1,1,1,1,10];
240                var widths=[2,7,5,3,1,0];
241                if (dir=="-1") { dashes.reverse(); widths.reverse(); }
242                var arrowcol=0x6C70D5; if (this.attr['highway']=='primary' || this.attr['highway']=='primary_link' || 
243                                                                   this.attr['highway']=='trunk' || this.attr['highway']=='trunk_link' || 
244                                                                   this.attr['highway']=='motorway' || this.attr['highway']=='motorway_link') { arrowcol=0; }
245               
246                var draw=false;
247                var dashleft=0;
248                var segleft=0;
249                var dc=[]; var wc=[];
250                var a,xc,yc,curx,cury,dx,dy;
251                var i=0;
252
253                var g=this.arrows;
254                g.moveTo(this.path[0].x,this.path[0].y);
255                while (i<this.path.length-1 || segleft>0) {
256                        if (dashleft<=0) {
257                                if (dc.length==0) { dc=dashes.slice(0); wc=widths.slice(0); }
258                                dashleft=dc.shift()/bscale*2;
259                                dashwidth=wc.shift();
260                        }
261                        if (segleft<=0) {
262                                curx=this.path[i].x; dx=this.path[i+1].x-curx;
263                                cury=this.path[i].y; dy=this.path[i+1].y-cury;
264                                a=Math.atan2(dy,dx); xc=Math.cos(a); yc=Math.sin(a);
265                                segleft=Math.sqrt(dx*dx+dy*dy);
266                                i++;
267                        }
268
269                        if (segleft<=dashleft) {
270                                // the path segment is shorter than the dash
271                                curx+=dx; cury+=dy;
272                                moveLine(g,curx,cury,dashwidth,arrowcol);
273                                dashleft-=segleft; segleft=0;
274                        } else {
275                                // the path segment is longer than the dash
276                                curx+=dashleft*xc; dx-=dashleft*xc;
277                                cury+=dashleft*yc; dy-=dashleft*yc;
278                                moveLine(g,curx,cury,dashwidth,arrowcol);
279                                segleft-=dashleft; dashleft=0;
280                        }
281                }
282        };
283
284        function moveLine(g,x,y,dashwidth,colour) {
285                if (dashwidth==0) {
286                        g.moveTo(x,y);
287                } else {
288                        g.lineStyle(dashwidth,colour,100,false,"none","none");
289                        g.lineTo(x,y);
290                }
291        };
292
293        // ---- Tidy in line/circle
294       
295        OSMWay.prototype.tidy=function() {
296                var a=this.path[0]; var b=this.path[this.path.length-1];
297                var w=new Object(); w[this._name]=true;
298                if (a.id==b.id) {
299                        // Tidy in circle
300                        if (this.path.length<4) { return; }
301                        this.saveChangeUndo();
302                       
303                        // Find centre-point
304                        var patharea=0;
305                        var cx=0; var lx=b.x; //coord2long(b.x);
306                        var cy=0; var ly=b.y; //coord2y(b.y);
307                        for (var i=0; i<this.path.length; i++) {
308                var latp=this.path[i].y; //coord2y(this.path[i].y);
309                var lon =this.path[i].x; // coord2long(this.path[i].x);
310                                var sc = (lx*latp-lon*ly); //*masterscale;
311                                cx += (lx+lon)*sc;
312                                cy += (ly+latp)*sc;
313                                patharea += sc;
314                                lx=lon; ly=latp;
315                        }
316                        patharea/=2;
317                        cx=cx/patharea/6; //long2coord(cx/patharea/6);
318                        cy=cy/patharea/6; //y2coord(cy/patharea/6);
319
320                        // Average distance to centre
321                        var d=0; var angles=[];
322                        for (var i=0; i<this.path.length; i++) {
323                                d+=Math.sqrt(Math.pow(this.path[i].x-cx,2)+Math.pow(this.path[i].y-cy,2));
324                        }
325                        d=d/this.path.length;
326                       
327                        // Move each node
328                        for (var i=0; i<this.path.length-1; i++) {
329                                var c=Math.sqrt(Math.pow(this.path[i].x-cx,2)+Math.pow(this.path[i].y-cy,2));
330                                this.path[i].x=cx+(this.path[i].x-cx)/c*d;
331                                this.path[i].y=cy+(this.path[i].y-cy)/c*d;
332                                this.path[i].clean=false;
333                                var l=this.path[i].ways; for (var o in l) { w[o]=true; }
334                        }
335
336                        // Insert extra nodes to make circle
337                        // clockwise: angles decrease, wrapping round from -170 to 170
338                        var newpath=[]; var diff,b;
339                        var clockwise=_root.panel.i_clockwise._visible;
340                        for (var i=0; i<this.path.length-1; i++) {
341                                var j=(i+1) % this.path.length;
342                                newpath.push(this.path[i]);
343                                a1=Math.atan2(this.path[i].x-cx,this.path[i].y-cy)*(180/Math.PI);
344                                a2=Math.atan2(this.path[j].x-cx,this.path[j].y-cy)*(180/Math.PI);
345
346                                if (clockwise) {
347                                        if (a2>a1) { a2=a2-360; }
348                                        diff=a1-a2;
349                                        if (diff>20) {
350                                                for (var ang=a1-20; ang>a2+10; ang-=20) {
351                                                        _root.newnodeid--;
352                                                        _root.nodes[newnodeid]=new Node(newnodeid,cx+Math.sin(ang*Math.PI/180)*d,cy+Math.cos(ang*Math.PI/180)*d,new Object(),0);
353                                                        _root.nodes[newnodeid].addWay(this._name);
354                                                        newpath.push(_root.nodes[newnodeid]);
355                                                }
356                                        }
357                                } else {
358                                        if (a1>a2) { a1=a1-360; }
359                                        diff=a2-a1;
360                                        if (diff>20) {
361                                                for (var ang=a1+20; ang<a2-10; ang+=20) {
362                                                        _root.newnodeid--;
363                                                        _root.nodes[newnodeid]=new Node(newnodeid,cx+Math.sin(ang*Math.PI/180)*d,cy+Math.cos(ang*Math.PI/180)*d,new Object(),0);
364                                                        _root.nodes[newnodeid].addWay(this._name);
365                                                        newpath.push(_root.nodes[newnodeid]);
366                                                }
367                                        }
368                                }
369                        }
370                        newpath.push(this.path[this.path.length-1]);
371                        this.path=newpath;
372
373                } else {
374                        // Tidy in line
375                        if (this.path.length<3) { return; }
376
377                        // First, calculate divergence to make sure we're not straightening a really bendy way
378                        var d=0; var t,x1,y1;
379                        var thislat=coord2lat(_root.map._y); var latfactor=Math.cos(thislat/(180/Math.PI));
380                        for (var i=1; i<this.path.length-1; i++) {
381                                u=((this.path[i].x-a.x)*(b.x-a.x)+
382                                   (this.path[i].y-a.y)*(b.y-a.y))/
383                                   (Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2));
384                                x1=a.x+u*(b.x-a.x);
385                                y1=a.y+u*(b.y-a.y);
386                                t=Math.sqrt(Math.pow(x1-this.path[i].x,2)+Math.pow(y1-this.path[i].y,2));
387                                t=Math.floor((111200*latfactor)*t/masterscale);
388                                if (t>d) { d=t; }
389                        }
390                        if (d>50 && !Key.isDown(Key.SHIFT)) { setAdvice(false,iText('advice_bendy')); return; }
391                        this.saveChangeUndo();
392
393                        var el=long2coord(_root.bigedge_l);             // We don't want to delete any off-screen nodes
394                        var er=long2coord(_root.bigedge_r);             // (because they might be used in other ways)
395                        var et= lat2coord(_root.bigedge_t);
396                        var eb= lat2coord(_root.bigedge_b);
397                        var retain=new Array(a);
398                        for (var i=1; i<this.path.length-1; i++) {
399                                if (this.path[i].numberOfWays()>1 || hasTags(this.path[i].attr) ||
400                                        this.path[i].x<el || this.path[i].x>er || 
401                                        this.path[i].y<et || this.path[i].y>eb) {
402                                        u=((this.path[i].x-a.x)*(b.x-a.x)+
403                                           (this.path[i].y-a.y)*(b.y-a.y))/
404                                           (Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2));
405                                        this.path[i].x=a.x+u*(b.x-a.x);
406                                        this.path[i].y=a.y+u*(b.y-a.y);
407                                        this.path[i].clean=false;
408                                        var l=this.path[i].ways; for (var o in l) { w[o]=true; }
409                                        retain.push(this.path[i]);
410                                } else {
411                                        this.markAsDeleted(this.path[i],false);
412                                        memberDeleted('Node', this.path[i].id);
413                                }
414                        }
415                        retain.push(b);
416                        this.path=retain;
417                        this.removeDuplicates();
418                }
419                this.clean=false; markClean(false);
420                for (var i in w) { _root.map.ways[i].redraw(); }
421                this.select();
422        };
423
424
425
426        // ---- Show direction
427
428        OSMWay.prototype.direction=function() {
429                if (this.path.length<2) {
430                        _root.panel.i_clockwise._visible=false;
431                        _root.panel.i_anticlockwise._visible=false;
432                        _root.panel.i_direction._visible=true;
433                        _root.panel.i_direction._alpha=50;
434                } else {
435                        var dx=this.path[this.path.length-1].x-this.path[0].x;
436                        var dy=this.path[this.path.length-1].y-this.path[0].y;
437                        if (dx!=0 || dy!=0) {
438                                // Non-circular
439                                _root.panel.i_direction._rotation=180-Math.atan2(dx,dy)*(180/Math.PI)-45;
440                                _root.panel.i_direction._alpha=100;
441                                _root.panel.i_direction._visible=true;
442                                _root.panel.i_clockwise._visible=false;
443                                _root.panel.i_anticlockwise._visible=false;
444                        } else {
445                                // Circular
446                                _root.panel.i_direction._visible=false;
447                                // Find lowest rightmost point
448                                // cf http://geometryalgorithms.com/Archive/algorithm_0101/
449                                var lowest=0;
450                                var xmax=-999999; var ymin=-999999;
451                                for (var i=0; i<this.path.length; i++) {
452                                        if      (this.path[i].y> ymin) { lowest=i; xmin=this.path[i].x; ymin=this.path[i].y; }
453                                        else if (this.path[i].y==ymin
454                                                  && this.path[i].x> xmax) { lowest=i; xmin=this.path[i].x; ymin=this.path[i].y; }
455                                }
456                                var clockwise=(this.onLeft(lowest)>0);
457                                _root.panel.i_clockwise._visible=clockwise;
458                                _root.panel.i_anticlockwise._visible=!clockwise;
459                        }
460                }
461        };
462
463        OSMWay.prototype.onLeft=function(j) {
464                var left=0;
465                if (this.path.length>=3) {
466                        var i=j-1; if (i==-1) { i=this.path.length-2; }
467                        var k=j+1; if (k==this.path.length) { k=1; }
468                        left=((this.path[j].x-this.path[i].x) * (this.path[k].y-this.path[i].y) -
469                                  (this.path[k].x-this.path[i].x) * (this.path[j].y-this.path[i].y));
470                }
471                return left;
472        };
473       
474
475
476        // ---- Remove from server
477       
478        OSMWay.prototype.remove=function() {
479                clearFloater();
480                memberDeleted('Way', this._name);
481                var z=this.path; for (var i in z) {
482                        if (z[i].numberOfWays()==1) { memberDeleted('Node', z[i].id); }
483                }
484                uploadDirtyRelations();
485
486                this.deleteMergedWays();
487                this.removeNodeIndex();
488
489                if (!this.historic && !this.locked) {
490                        var z=shallowCopy(this.path); this.path=new Array();
491                        for (var i in z) { 
492                                this.markAsDeleted(z[i],false); }
493                        if (_root.sandbox) {
494                                if (this._name>=0) { 
495                                        _root.waystodelete[this._name]=[this.version,deepCopy(this.deletednodes)];
496                                } else {
497                                        var z=this.deletednodes; for (var j in z) { deleteNodeAsPOI(j); }
498                                        var z=this.path;         for (var j in z) { deleteNodeAsPOI(j.id); }
499                                        markClean(false);
500                                }
501                        } else if (this._name>=0) {
502                                if (renewChangeset()) { return; }
503                                deleteresponder = function() { };
504                                deleteresponder.onResult = function(result) { deletewayRespond(result); };
505                                _root.writesrequested++;
506                                remote_write.call('deleteway',deleteresponder,_root.usertoken,_root.changeset,Number(this._name),Number(this.version),this.deletednodes);
507                        }
508                }
509
510                if (this._name==wayselected) { stopDrawing(); deselectAll(); }
511                removeMovieClip(_root.map.areas[this._name]);
512                removeMovieClip(this);
513        };
514
515        function deletewayRespond(result) {
516                _root.writesrequested--;
517                var code=result.shift(); var msg=result.shift(); if (code) { handleError(code,msg,result); return; }
518                var z=result[2]; for (var i in z) { delete _root.nodes[i]; }
519                operationDone(result[0]);
520                freshenChangeset();
521        };
522
523        function deleteNodeAsPOI(nid) {
524                if (nid<0) { return; }
525                if (_root.nodes[nid].numberOfWays()>0) { return; }
526                // if we're not going to send a deleteway for this (because it's negative - probably due to a split),
527                // then delete it as a POI instead
528                var n=_root.nodes[nid];
529                _root.poistodelete[nid]=[n.version,coord2long(n.x),coord2lat(n.y),deepCopy(n.attr)];
530        }
531
532        // ---- Variant with confirmation if any nodes have tags
533       
534        OSMWay.prototype.removeWithConfirm=function() {
535                var c=true;
536                var z=this.path;
537                for (var i in z) {
538                        if (this.path[i].tagged && hashLength(this.path[i].ways)==1) { c=false; }
539                }
540                if (c) {
541                        _root.ws.saveDeleteUndo(iText('deleting'));
542                        this.remove();
543                        markClean(true);
544                } else {
545                        _root.windows.attachMovie("modal","confirm",++windowdepth);
546                        _root.windows.confirm.init(275,80,new Array(iText('cancel'),iText('delete')),
547                                function(choice) {
548                                        if (choice==iText('delete')) { _root.ws.saveDeleteUndo(iText('deleting')); _root.ws.remove(); markClean(true); }
549                                });
550                        _root.windows.confirm.box.createTextField("prompt",2,7,9,250,100);
551                        writeText(_root.windows.confirm.box.prompt,iText('prompt_taggedpoints'));
552                }
553        };
554
555        // ---- Upload to server
556       
557        OSMWay.prototype.upload=function() {
558                putresponder=function() { };
559                putresponder.onResult=function(result) {
560                        _root.writesrequested--;
561                        var code=result.shift(); var msg=result.shift(); if (code) { _root.map.ways[result[0]].notUploading(); handleError(code,msg,result); return; }
562
563                        var i,r,z,nw,ow;
564                        ow=result[0];                   // old way ID
565                        nw=result[1];                   // new way ID
566                        if (ow!=nw) { renumberWay(ow,nw); }
567                        _root.map.ways[nw].clean=true;
568                        _root.map.ways[nw].uploading=false;
569                        _root.map.ways[nw].historic=false;
570                        _root.map.ways[nw].version=result[3];
571                       
572                        // renumber nodes (and any relations/ways they're in)
573                        z=result[2];
574                        for (var oid in z) {
575                                var nid = result[2][oid];
576                                nodes[oid].renumberTo(nid);
577                                nodes[nid].addWay(nw);
578                                renumberMemberOfRelation('Node', oid, nid);
579                        }
580                        for (var oid in z) { delete _root.nodes[oid]; } // delete -ve nodes
581                       
582                        // set versions, clean, not uploading
583                        z=result[4]; for (var nid in z) { nodes[nid].version=result[4][nid]; nodes[nid].uploading=false; nodes[nid].clean=true; }
584
585                        // remove deleted nodes
586                        z=result[5]; for (var nid in z) { c=_root.map.ways[nw]; delete c.deletednodes[nid]; delete _root.nodes[nid]; }
587                       
588                        _root.map.ways[nw].clearPOIs();
589                        uploadDirtyRelations();
590                        _root.map.ways[nw].deleteMergedWays();
591                        uploadDirtyWays();                      // make sure dependencies are uploaded
592                        operationDone(ow);
593                        freshenChangeset();
594                        updateInspector();
595                };
596
597                if (!this.uploading && !this.hasDependentNodes() && !this.locked && (!_root.sandbox || _root.uploading) && this.path.length>1) {
598                        this.deleteMergedWays();
599
600                        // Assemble list of changed nodes, and send
601                        if (renewChangeset()) { return; }
602                        this.uploading=true;
603                        var sendpath =new Array();
604                        var sendnodes=new Array();
605                        for (i=0; i<this.path.length; i++) {
606                                sendpath.push(this.path[i].id);
607                                if (!this.path[i].clean && !this.path[i].uploading) {
608                                        sendnodes.push(new Array(coord2long(this.path[i].x),
609                                                                                         coord2lat (this.path[i].y),
610                                                                                         this.path[i].id,this.path[i].version,
611                                                                                         deepCopy  (this.path[i].attr)));
612                                        this.path[i].uploading=true;
613                                }
614                        }
615                        delete this.attr['created_by'];
616                        _root.writesrequested++;
617                        remote_write.call('putway',putresponder,_root.usertoken,_root.changeset,this.version,Number(this._name),sendpath,this.attr,sendnodes,this.deletednodes);
618                        updateInspector();
619                } else { 
620                        operationDone(this._name);      // next please!
621                }
622        };
623
624        // ---- Delete any ways merged into this one
625
626        OSMWay.prototype.deleteMergedWays=function() {
627                while (this.mergedways.length>0) {
628                        var i=this.mergedways.shift();
629                        _root.map.ways.attachMovie("way",i[0],++waydepth);      // can't remove unless the movieclip exists!
630                        _root.map.ways[i[0]].version=i[1];                                      //  |
631                        _root.map.ways[i[0]].deletednodes=i[2];                         //  |
632                        _root.map.ways[i[0]].remove();
633                }
634        };
635
636        // ---- Revert to copy in database
637       
638        OSMWay.prototype.reload=function() {
639                _root.waysrequested+=1;
640                while (this.mergedways.length>0) {
641                        var i=this.mergedways.shift();
642                        _root.waysrequested+=1;
643                        _root.map.ways.attachMovie("way",i[0],++waydepth);
644                        _root.map.ways[i[0]].load();
645                }
646                this.checkconnections=true;
647                this.load();
648        };
649       
650        // ---- Save for undo
651
652        OSMWay.prototype.saveDeleteUndo=function(str) {
653                _root.undo.append(UndoStack.prototype.undo_deleteway,
654                                                  new Array(this._name,this._x,this._y,
655                                                                        deepCopy(this.attr),deepCopy(this.path),this.version),
656                                                                        iText('a_way',str));
657        };
658       
659        OSMWay.prototype.saveChangeUndo=function(str) {
660                _root.undo.append(UndoStack.prototype.undo_changeway,
661                                                  new Array(this._name,deepCopy(this.path),deepCopy(this.deletednodes),deepCopy(this.attr)),
662                                                  iText('action_changeway'));
663        };
664
665
666        // ---- Click handling 
667
668        OSMWay.prototype.onRollOver=function() {
669                if (this._name!=_root.wayselected && _root.drawpoint>-1 && !_root.map.anchorhints) {
670                        this.highlightPoints(5001,"anchorhint");
671                        setPointer('penplus');
672                } else if (_root.drawpoint>-1) { setPointer('penplus'); }
673                                                                  else { setPointer(''); }
674                if (this._name!=_root.wayselected) { var a=getName(this.attr,waynames); if (a) { setFloater(a); } }
675        };
676       
677        OSMWay.prototype.onRollOut=function() {
678                if (this.hitTest(_root._xmouse,_root._ymouse,true)) { return; } // false rollout
679                if (_root.wayselected) { setPointer(''   ); }
680                                                  else { setPointer('pen'); }
681                _root.map.anchorhints.removeMovieClip();
682                clearFloater();
683        };
684       
685        OSMWay.prototype.onPress=function() {
686                removeWelcome(true);
687                if (Key.isDown(Key.SHIFT) && this._name==_root.wayselected && _root.drawpoint==-1) {
688                        // shift-click current way: insert point
689                        this.insertAnchorPointAtMouse();
690                } else if (Key.isDown(Key.SHIFT) && _root.wayselected && this.name!=_root.wayselected && _root.drawpoint==-1 && _root.ws.hitTest(_root._xmouse,_root._ymouse,true)) {
691                        // shift-click other way (at intersection with current): make intersection
692                        this.insertAnchorPointAtMouse();
693                } else if (Key.isDown(Key.CONTROL) && _root.wayselected && this.name!=_root.wayselected && _root.drawpoint==-1) {
694                        // control/command-click other way: merge two ways
695                        mergeWayKeepingID(this,_root.ws);
696                } else if (_root.drawpoint>-1) {
697                        // click other way while drawing: insert point as junction
698                        if (!this.historic) {
699                                if (this._name==_root.wayselected && _root.drawpoint>0) {
700                                        _root.drawpoint+=1;     // inserting node earlier into the way currently being drawn
701                                }
702                                _root.newnodeid--;
703                                _root.nodes[newnodeid]=new Node(newnodeid,0,0,new Object(),0);
704                                this.insertAnchorPoint(_root.nodes[newnodeid]);
705                                this.highlightPoints(5001,"anchorhint");
706                                addEndPoint(_root.nodes[newnodeid]);
707                        }
708                        _root.junction=true;
709                        restartElastic();
710                } else {
711                        // click way: select
712                        _root.panel.properties.saveAttributes();
713                        this.select();
714                        clearTooltip();
715                        _root.clicktime=new Date();
716                        // was the click on a tagged node? if so, select directly
717                        var n;
718                        for (var i=0; i<this.path.length; i+=1) {
719                                if (this.taggednodes[i].hitTest(_root._xmouse,_root._ymouse,true)) { n=i; }
720                        }
721                        if (n) { _root.map.anchors[n].beginDrag();
722                                         _root.map.anchors[n].select(); }
723                          else { this.beginDrag(); }
724                }
725        };
726
727        OSMWay.prototype.beginDrag=function() {
728                this.onMouseMove=function() { this.trackDrag(); };
729                this.onMouseUp  =function() { this.endDrag();   };
730                this.dragged=false;
731                this.held=true;
732                _root.firstxmouse=_root.map._xmouse;
733                _root.firstymouse=_root.map._ymouse;
734        };
735
736        OSMWay.prototype.trackDrag=function() {
737                var t=new Date();
738                var longclick=(t.getTime()-_root.clicktime)>1000;
739                var xdist=Math.abs(_root.map._xmouse-_root.firstxmouse);
740                var ydist=Math.abs(_root.map._ymouse-_root.firstymouse);
741                // Don't enable drag unless way held for a while after click
742                if ((xdist>=tolerance   || ydist>=tolerance  ) &&
743                        (t.getTime()-_root.clicktime)<300 &&
744                        lastwayselected!=wayselected) { this.held=false; }
745                // Move way if dragged a long way, or dragged a short way after a while
746                if ((xdist>=tolerance*4 || ydist>=tolerance*4) ||
747                   ((xdist>=tolerance/4 || ydist>=tolerance/4) && longclick) &&
748                   this.held) {
749                        this.dragged=true;
750                }
751                if (this.dragged) {
752                        _root.map.anchors._x=_root.map.areas[this._name]._x=_root.map.highlight._x=this._x=_root.map._xmouse-_root.firstxmouse;
753                        _root.map.anchors._y=_root.map.areas[this._name]._y=_root.map.highlight._y=this._y=_root.map._ymouse-_root.firstymouse;
754                }
755        };
756       
757        OSMWay.prototype.endDrag=function() {
758                delete this.onMouseMove;
759                delete this.onMouseUp;
760                _root.map.anchors._x=_root.map.areas[this._name]._x=_root.map.highlight._x=this._x=0;
761                _root.map.anchors._y=_root.map.areas[this._name]._y=_root.map.highlight._y=this._y=0;
762                if (this.dragged) {
763                        this.moveNodes(_root.map._xmouse-_root.firstxmouse,_root.map._ymouse-_root.firstymouse);
764                        setAdvice(false,iText('advice_waydragged'));
765                        this.redraw();
766                        this.select();
767                        _root.undo.append(UndoStack.prototype.undo_movenodes,
768                                                          new Array(this,_root.map._xmouse-_root.firstxmouse,
769                                                                                         _root.map._ymouse-_root.firstymouse),
770                                                          iText('action_moveway'));
771                }
772        };
773       
774        // ---- Select/highlight
775       
776        OSMWay.prototype.select=function() {
777                _root.panel.properties.tidy();
778                if (_root.wayselected!=this._name || _root.poiselected!=0) { uploadSelected(); }
779//              _root.panel.properties.saveAttributes();
780                _root.pointselected=-2;
781                selectWay(this._name);
782                _root.poiselected=0;
783                this.highlightPoints(5000,"anchor");
784                removeMovieClip(_root.map.anchorhints);
785                this.highlight();
786                setTypeText(iText('way'),this._name);
787                removeIconPanel();
788                _root.panel.properties.init('way',getPanelColumns(),4);
789                _root.panel.presets.init(_root.panel.properties);
790                updateButtons();
791                updateScissors(false);
792        };
793       
794        OSMWay.prototype.highlight=function() {
795                _root.map.createEmptyMovieClip("highlight",5);
796                if (_root.pointselected>-2) {
797                        highlightSquare(_root.map.anchors[pointselected]._x,_root.map.anchors[pointselected]._y, _root.anchorsize*0.05);
798                } else {
799                        var linecolour=0xFFFF00; if (this.locked) { var linecolour=0x00FFFF; }
800                        _root.map.highlight.lineStyle(linewidth*1.5+8,linecolour,80,false,"none");
801                        _root.map.highlight.moveTo(this.path[0].x,this.path[0].y);
802                        for (var i=1; i<this.path.length; i+=1) {
803                                _root.map.highlight.lineTo(this.path[i].x,this.path[i].y);
804                        }
805                }
806                this.direction();
807        };
808
809        OSMWay.prototype.highlightPoints=function(d,atype) {
810                var group=atype+"s";
811                _root.map.createEmptyMovieClip(group,d);
812                for (var i=0; i<this.path.length; i+=1) {
813                        var asprite=atype; if (this.path[i].numberOfWays()>1) { asprite+="_junction"; }
814                        _root.map[group].attachMovie(asprite,i,i);
815                        _root.map[group][i]._x=this.path[i].x;
816                        _root.map[group][i]._y=this.path[i].y;
817                        _root.map[group][i]._xscale=_root.anchorsize;
818                        _root.map[group][i]._yscale=_root.anchorsize;
819                        _root.map[group][i].node=this.path[i].id;
820                        _root.map[group][i].way=this;
821                        if (this.path[i].tagged) {
822                                // anchor point should be black if it has tags
823                                _root.map[group][i].blacken=new Color(_root.map[group][i]);
824                                _root.map[group][i].blacken.setTransform(to_black);
825                        }
826                }
827        };
828
829        // ---- Split, merge, reverse
830
831        OSMWay.prototype.splitWay=function(point,newattr) {
832                var i,z;
833                if (point>0 && point<(this.path.length-1) && !this.historic) {
834                        _root.newwayid--;                                                                                       // create new way
835                        _root.map.ways.attachMovie("way",newwayid,++waydepth);          //  |
836                        _root.map.ways[newwayid].path=shallowCopy(this.path);           // deep copy path array
837                        this.removeNodeIndex();
838
839                        if (newattr) { _root.map.ways[newwayid].attr=newattr; }
840                                        else { _root.map.ways[newwayid].attr=deepCopy(this.attr); }
841
842                        z=_root.wayrels[this._name];                                                            // copy relations
843                        for (i in z) {                                                                                          //  |
844                                _root.map.relations[i].setWayRole(newwayid,z[i]);               //  |
845                        }                                                                                                                       //  |
846 
847                        this.path.splice(Math.floor(point)+1);                                          // current way
848                        this.redraw();                                                                                          //  |
849                        this.createNodeIndex();
850
851                        _root.map.ways[newwayid].path.splice(0,point);                          // new way
852                        _root.map.ways[newwayid].locked=this.locked;                            //  |
853                        _root.map.ways[newwayid].redraw();                                                      //  |
854                        _root.map.ways[newwayid].clean=false;                                           //  | - in case it doesn't upload
855                        _root.map.ways[newwayid].upload();                                                      //  |
856                        _root.map.ways[newwayid].createNodeIndex();                                     //  |
857
858                        this.clean=false;                                                                                       // upload current way
859                        this.upload();                                                                                          //  |
860                        this.select();                                                                                          //  |
861                        uploadDirtyRelations();
862                        _root.undo.append(UndoStack.prototype.undo_splitway,
863                                                          new Array(this,_root.map.ways[newwayid]),
864                                                          iText('action_splitway'));
865                };
866        };
867
868        //              Merge (start/end of this way,other way object,start/end of other way)
869
870        OSMWay.prototype.mergeWay=function(topos,otherway,frompos) {
871                var i,z;
872                var conflict=false;
873                if (this.historic || otherway.historic) { return; }
874                if (otherway==this) { return; }
875
876                var mergepoint=this.path.length;
877                if (topos==0) {
878                        _root.undo.append(UndoStack.prototype.undo_mergeways,
879                                                          new Array(this,deepCopy(otherway.attr),deepCopy(this.attr),frompos),
880                                                          iText('action_mergeways'));
881                } else {
882                        _root.undo.append(UndoStack.prototype.undo_mergeways,
883                                                          new Array(this,deepCopy(this.attr),deepCopy(otherway.attr),topos),
884                                                          iText('action_mergeways'));
885                }
886                if (frompos==0) { for (i=0; i<otherway.path.length;    i+=1) { this.addPointFrom(topos,otherway,i); } }
887                                   else { for (i=otherway.path.length-1; i>=0; i-=1) { this.addPointFrom(topos,otherway,i); } }
888
889                // Merge attributes
890                z=otherway.attr;
891                for (i in z) {
892                        if (otherway.attr[i].substr(0,6)=='(type ') { otherway.attr[i]=null; }
893                        if (this.attr[i].substr(0,6)=='(type ') { this.attr[i]=null; }
894                        if (this.attr[i]) {
895                                if (this.attr[i]!=otherway.attr[i] && otherway.attr[i]) { var s=this.attr[i]+'; '+otherway.attr[i]; this.attr[i]=s.substr(0,255); conflict=true; }
896                        } else {
897                                this.attr[i]=otherway.attr[i];
898                        }
899                        if (!this.attr[i]) { delete this.attr[i]; }
900                }
901
902                // Merge relations
903                z=_root.wayrels[otherway._name];                                                // copy relations
904                for (i in z) {                                                                                  //  |
905                        if (!_root.wayrels[this._name][i]) {                            //  |
906                                _root.map.relations[i].setWayRole(this._name,z[i]);     
907                        }                                                                                                       //  |
908                }                                                                                                               //  |
909                memberDeleted('Way', otherway._name);                                   // then remove old way from them
910                z=otherway.deletednodes;                                                                //  | and its deletednodes
911                for (i in z) { memberDeleted('Node',z[i]); }                    //  |
912
913                // Add to list of merged ways (so they can be deleted on next putway)
914        if (_root.sandbox) {
915            otherway.remove();
916        } else {
917                this.mergedways.push(new Array(otherway._name,otherway.version,otherway.deletednodes));
918                this.mergedways.concat(otherway.mergedways);
919        }
920                this.clean=false;
921                markClean(false);
922                if (otherway.locked) { this.locked=true; }
923                otherway.removeNodeIndex();
924                removeMovieClip(_root.map.areas[otherway._name]);
925                removeMovieClip(otherway);
926                if (this._name==_root.wayselected) { 
927                        _root.panel.properties.reinit();
928                }
929                if (conflict) { setAdvice(false,iText('advice_tagconflict')); }
930        };
931
932        OSMWay.prototype.addPointFrom=function(topos,otherway,srcpt) {
933                if (topos==0) { if (this.path[0                                 ]==otherway.path[srcpt]) { return; } }  // don't add duplicate points
934                                 else { if (this.path[this.path.length-1]==otherway.path[srcpt]) { return; } }  //  |
935                if (topos==0) { this.path.unshift(otherway.path[srcpt]); }
936                             else { this.path.push(otherway.path[srcpt]); }
937                otherway.path[srcpt].addWay(this._name);
938        };
939
940        OSMWay.prototype.mergeAtCommonPoint=function(sel) {
941                var selstart =sel.path[0];
942                var sellen   =sel.path.length-1;
943                var selend   =sel.path[sellen];
944                var thisstart=this.path[0];
945                var thislen  =this.path.length-1;
946                var thisend  =this.path[thislen];
947                if      (selstart==thisstart) { sel.mergeWay(0,this,0);                    return true; }
948                else if (selstart==thisend  ) { sel.mergeWay(0,this,thislen);      return true; }
949                else if (selend  ==thisstart) { sel.mergeWay(sellen,this,0);       return true; }
950                else if (selend  ==thisend  ) { sel.mergeWay(sellen,this,thislen); return true; }
951                else                                              { setAdvice(true,iText('advice_nocommonpoint')); return false; }
952        };
953
954        // ---- Reverse order
955       
956        OSMWay.prototype.reverseWay=function() {
957                if (this.path.length<2) { return; }
958                if (_root.drawpoint>-1) { _root.drawpoint=(this.path.length-1)-_root.drawpoint; }
959                this.path.reverse();
960                this.redraw();
961                this.direction();
962                this.select();
963                this.clean=false;
964                markClean(false);
965                _root.undo.append(UndoStack.prototype.undo_reverse,new Array(this),iText('action_reverseway'));
966        };
967
968        // ---- Move all nodes within a way
969       
970        OSMWay.prototype.moveNodes=function(xdiff,ydiff) {
971                var i,n;
972                var movedalready=new Array();
973                this.clean=false;
974                markClean(false);
975                for (i=0; i<this.path.length; i+=1) {
976                        n=this.path[i].id;
977                        if (movedalready[n]) {
978                        } else {
979                                this.path[i].moveTo(this.path[i].x+xdiff,
980                                                                        this.path[i].y+ydiff,
981                                                                        this._name);
982                                movedalready[n]=true;
983                        }
984                }
985        };
986
987        // ---- Add node to deleted list
988        //              (should have been removed from way first)
989       
990        OSMWay.prototype.markAsDeleted=function(rnode,check2) {
991                var d=true;
992                // If we're just removing one point, check it's not used elsewhere in the way before removing from .ways
993                if (check2) {
994                        var z=this.path; 
995                        for (var i in z) { if (this.path[i].id==rnode.id) { d=false; } }
996                }
997                if (d) { rnode.removeWay(this._name); }
998                if (rnode.numberOfWays()==0 && rnode.id>0) { this.deletednodes[rnode.id]=rnode.version; }
999        };
1000
1001
1002        // ---- Check for duplicates (e.g. when C is removed from ABCB)
1003       
1004        OSMWay.prototype.removeDuplicates=function() {
1005                var z=this.path; var ch=false;
1006                for (var i in z) {
1007                        if (i>0) {
1008                                if (this.path[i]==this.path[i-1]) { this.path.splice(i,1); ch=true; }
1009                        }
1010                }
1011                return ch;
1012        };
1013
1014        // ---- Add point into way with SHIFT-clicking
1015        //              cf http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/source.vba
1016        //              for algorithm to find nearest point on a line
1017       
1018        OSMWay.prototype.insertAnchorPoint=function(nodeobj) {
1019                var nx,ny,u,closest,closei,i,a,b,direct,via,newpoint;
1020                nx=_root.map._xmouse;   // where we're inserting it
1021                ny=_root.map._ymouse;   //      |
1022                closest=0.1; closei=0;
1023                for (i=0; i<(this.path.length)-1; i+=1) {
1024                        a=this.path[i  ];
1025                        b=this.path[i+1];
1026                        direct=Math.sqrt((b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y));
1027                        via   =Math.sqrt((nx -a.x)*(nx -a.x)+(ny -a.y)*(ny -a.y));
1028                        via  +=Math.sqrt((nx -b.x)*(nx -b.x)+(ny -b.y)*(ny -b.y));
1029                        if (Math.abs(via/direct-1)<closest) {
1030                                closei=i+1;
1031                                closest=Math.abs(via/direct-1);
1032                                u=((nx-a.x)*(b.x-a.x)+
1033                                   (ny-a.y)*(b.y-a.y))/
1034                                   (Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2));
1035                                nodeobj.x=a.x+u*(b.x-a.x);
1036                                nodeobj.y=a.y+u*(b.y-a.y);
1037                        }
1038                }
1039                // Insert
1040                nodeobj.addWay(this._name);
1041                this.path.splice(closei,0,nodeobj);
1042                this.clean=false;
1043                this.redraw();
1044                markClean(false);
1045//              _root.adjustedxmouse=tx;        // in case we're adding extra points...
1046//              _root.adjustedymouse=ty;        // should probably return an array or an object **
1047                return closei;
1048        };
1049
1050        //              Wrapper around the above to:
1051        //              - insert at mouse position
1052        //              - add to undo stack
1053        //              - add intersection at any crossing ways
1054
1055        OSMWay.prototype.insertAnchorPointAtMouse=function() {
1056                _root.newnodeid--;
1057                _root.nodes[newnodeid]=new Node(newnodeid,0,0,new Object(),0);
1058                _root.pointselected=this.insertAnchorPoint(_root.nodes[newnodeid]);
1059                var waylist=new Array(); waylist.push(this);
1060                var poslist=new Array(); poslist.push(_root.pointselected);
1061                for (qway in _root.map.ways) {
1062                        if (_root.map.ways[qway].hitTest(_root._xmouse,_root._ymouse,true) && qway!=this._name) {
1063                                poslist.push(_root.map.ways[qway].insertAnchorPoint(_root.nodes[newnodeid]));
1064                                waylist.push(_root.map.ways[qway]);
1065                        }
1066                }
1067                _root.undo.append(UndoStack.prototype.undo_addpoint,
1068                                                  new Array(waylist,poslist), iText('action_insertnode'));
1069                _root.ws.highlightPoints(5000,"anchor");
1070                _root.map.anchors[pointselected].beginDrag();
1071                updateInspector();
1072        };
1073
1074        // ---- Remove point from this way (only)
1075       
1076        OSMWay.prototype.removeAnchorPoint=function(point) {
1077                // ** if length<2, then mark as way removal
1078                _root.undo.append(UndoStack.prototype.undo_deletepoint,
1079                                                  new Array(deepCopy(this.path[point]),
1080                                                                        new Array(this._name),
1081                                                                        new Array(point)),
1082                                                  iText('action_deletepoint'));
1083                var rnode=this.path[point];
1084                this.path.splice(point,1);
1085                this.removeDuplicates();
1086                this.markAsDeleted(rnode,true);
1087                if (rnode.numberOfWays()==0) { memberDeleted('Node', rnode.id); }
1088                if (this.path.length<2) { this.remove(); }
1089                                                   else { this.redraw(); this.clean=false; }
1090        };
1091
1092        // ---- Bounding box utility functions
1093
1094        OSMWay.prototype.resetBBox=function() {
1095                this.xmin=this.ymin= 999;
1096                this.xmax=this.ymax=-999;
1097        };
1098       
1099        OSMWay.prototype.updateBBox=function(long,lat) {
1100                this.xmin=Math.min(long,this.xmin);
1101                this.xmax=Math.max(long,this.xmax);
1102                this.ymin=Math.min(lat ,this.ymin);
1103                this.ymax=Math.max(lat ,this.ymax);
1104        };
1105
1106        // ---- Reset 'uploading' flag
1107
1108        OSMWay.prototype.notUploading=function() {
1109                this.uploading=false;
1110                var z=this.path; for (i in z) { this.path[i].uploading=false; }
1111                updateInspector();
1112        };
1113
1114        // ---- Node->way associations
1115       
1116        OSMWay.prototype.createNodeIndex  =function() { var z=this.path; for (var i in z) { this.path[i].addWay(this._name);    } };
1117        OSMWay.prototype.removeNodeIndex  =function() { var z=this.path; for (var i in z) { this.path[i].removeWay(this._name); } };
1118        OSMWay.prototype.renumberNodeIndex=function(n) {
1119                var z=this.path; for (i in z) { 
1120                        this.path[i].removeWay(this._name);
1121                        this.path[i].addWay(n);
1122                }
1123        };
1124        OSMWay.prototype.hasDependentNodes=function() {
1125        if (_root.sandbox && _root.uploading) { return false; }    // not an issue in consecutive uploads
1126                var d=false;
1127                var z=this.path; for (var i in z) {
1128                        if (this.path[i].id<0) {
1129                                var ways=this.path[i].ways; for (var w in ways) {
1130                                        if (_root.map.ways[w].uploading) { d=true; }
1131                                }
1132                        }
1133                }
1134                return d;
1135        };
1136
1137        // =====================================================================================
1138        // Offset path
1139        // ** much of the dialogue box could be refactored to share with relations dialogue
1140
1141        function askOffset() {
1142                if (!_root.wayselected) { return; }
1143                _root.windows.attachMovie("modal","offset",++windowdepth);
1144                _root.windows.offset.init(300, 170, [iText('cancel'), iText('ok')], completeOffset);
1145                var z = 5;
1146                var box = _root.windows.offset.box;
1147               
1148                box.createTextField("title",z++,7,7,300-14,20);
1149                box.title.text = iText('prompt_createparallel');
1150                with (box.title) {
1151                        wordWrap=true;
1152                        setTextFormat(boldText);
1153                        selectable=false; type='dynamic';
1154                }
1155                adjustTextField(box.title);
1156               
1157                box.createTextField("instr",z++,7,30,300-14,40);
1158
1159                // Create radio buttons and menu
1160
1161                box.attachMovie("radio","offsetoption",z++);
1162                box.offsetoption.addButton(10,35 ,iText('offset_dual'));
1163                box.offsetoption.addButton(10,55 ,iText('offset_motorway'));
1164                box.offsetoption.addButton(10,75 ,iText('offset_narrowcanal'));
1165                box.offsetoption.addButton(10,95 ,iText('offset_broadcanal'));
1166                box.offsetoption.addButton(10,115,iText('offset_choose'));
1167                box.offsetoption.select(1);
1168
1169                var w=box.offsetoption[5].prompt._width+25;
1170                box.createTextField("useroffset",z++,w,110,290-w,17);
1171                box.useroffset.setNewTextFormat(plainSmall);
1172                box.useroffset.type='input';
1173                box.useroffset.backgroundColor=0xDDDDDD;
1174                box.useroffset.background=true;
1175                box.useroffset.border=true;
1176                box.useroffset.borderColor=0xFFFFFF;
1177                box.useroffset.onSetFocus=function() { this._parent.offsetoption.select(5); };
1178        }
1179
1180        // typical widths:
1181        // central reservation:
1182        //       4.5m on a rural motorway/dual carriageway
1183        //       3.5m on an urban motorway
1184        //       1.8m on an urban dual carriageway
1185        // lane widths are typically always 3.65m
1186        // hard shoulders are typically 3.30m
1187        // hard strips are typically 1m
1188
1189        function completeOffset(button) {
1190                if (button!=iText('ok')) { return false; }
1191                var radio=_root.windows.offset.box.offsetoption.selected;
1192                var m;
1193                if (radio==5) {
1194                        m=_root.windows.offset.box.useroffset.text;
1195                        if (!button) { return false; }
1196                } else {
1197                        m=new Array(0,4.5+3.65*2,4.5+3.65*3,5.5,11);
1198                        m=m[radio];
1199                }
1200                var thislat=coord2lat(_root.map._y);                    // near as dammit
1201                var latfactor=Math.cos(thislat/(180/Math.PI));  // 111200m in a degree at the equator
1202                m=masterscale/(111200*latfactor)*m;                             // 111200m*cos(lat in radians) elsewhere
1203                _root.ws.offset( m);
1204                _root.ws.offset(-m);
1205                _root.undo.append(UndoStack.prototype.undo_makeways,
1206                                                  new Array(_root.newwayid,_root.newwayid+1),
1207                                                  iText('action_createparallel'));
1208        }
1209
1210        // Create (locked) offset way
1211        // offset is + or - depending on which side
1212
1213        OSMWay.prototype.offset=function(tpoffset) {
1214                var a,b,o,df,x,y;
1215                var offsetx=new Array();
1216                var offsety=new Array();
1217                var wm=1;       // was 10 before
1218
1219                _root.newwayid--;                                                                                       // create new way
1220                _root.map.ways.attachMovie("way",newwayid,++waydepth);          //  |
1221                var nw=_root.map.ways[newwayid];
1222                nw.locked=true;
1223                nw.clean=false;
1224
1225                // Normalise, and calculate offset vectors
1226
1227                for (var i=0; i<this.path.length; i++) {
1228                        a=this.path[i  ].y - this.path[i+1].y;
1229                        b=this.path[i+1].x - this.path[i  ].x;
1230                        h=Math.sqrt(a*a+b*b);
1231                        if (h!=0) { a=a/h; b=b/h; }
1232                                 else { a=0; b=0; }
1233                        offsetx[i]=wm*a;
1234                        offsety[i]=wm*b;
1235                }
1236
1237                _root.newnodeid--;
1238                _root.nodes[newnodeid]=new Node(newnodeid,this.path[0].x+tpoffset*offsetx[0],
1239                                                                                                  this.path[0].y+tpoffset*offsety[0],
1240                                                                                                  new Object(),0);
1241                _root.nodes[newnodeid].addWay(newwayid);
1242                nw.path.push(_root.nodes[newnodeid]);
1243               
1244                for (i=1; i<(this.path.length-1); i++) {
1245       
1246                        a=det(offsetx[i]-offsetx[i-1],
1247                                  offsety[i]-offsety[i-1],
1248                                  this.path[i+1].x - this.path[i  ].x,
1249                                  this.path[i+1].y - this.path[i  ].y);
1250                        b=det(this.path[i  ].x - this.path[i-1].x,
1251                                  this.path[i  ].y - this.path[i-1].y,
1252                                  this.path[i+1].x - this.path[i  ].x,
1253                                  this.path[i+1].y - this.path[i  ].y);
1254                        if (b!=0) { df=a/b; } else { df=0; }
1255                       
1256                        x=this.path[i].x + tpoffset*(offsetx[i-1]+df*(this.path[i].x - this.path[i-1].x));
1257                        y=this.path[i].y + tpoffset*(offsety[i-1]+df*(this.path[i].y - this.path[i-1].y));
1258       
1259                        _root.newnodeid--;
1260                        _root.nodes[newnodeid]=new Node(newnodeid,x,y,new Object(),0);
1261                        _root.nodes[newnodeid].addWay(newwayid);
1262                        nw.path.push(_root.nodes[newnodeid]);
1263                }
1264       
1265                _root.newnodeid--;
1266                _root.nodes[newnodeid]=new Node(newnodeid,this.path[i].x+tpoffset*offsetx[i-1],
1267                                                                                                  this.path[i].y+tpoffset*offsety[i-1],new Object(),0);
1268                _root.nodes[newnodeid].addWay(newwayid);
1269                nw.path.push(_root.nodes[newnodeid]);
1270                nw.redraw();
1271        };
1272
1273    function det(a,b,c,d) { return a*d-b*c; }
1274
1275        // =====================================================================================
1276        // Inspector
1277       
1278        OSMWay.prototype.inspect=function() {
1279                var str='';
1280
1281                // Status
1282                if (this.locked) { str+=iText('inspector_way_locked') + "\n"; }
1283                if (!this.clean) { str+=iText('inspector_unsaved'); }
1284                if (this.uploading) { str+=' ' + iText('inspector_uploading'); }
1285                if (!this.clean || this.uploading) { str+="\n"; }
1286
1287                // Number of nodes
1288                if (this.path[this.path.length-1]==this.path[0]) {
1289            str += iText('inspector_way_nodes_closed', this.path.length);
1290        } else {
1291            str += iText('inspector_way_nodes', this.path.length);
1292        }
1293                str+="\n";
1294
1295                // Connections to other ways of same type
1296                var principal='';
1297                if      (this.attr['highway' ]) { principal='highway';  }
1298                else if (this.attr['waterway']) { principal='waterway'; }
1299                else if (this.attr['railway' ]) { principal='railway';  }
1300                var same=0; var different=0;
1301                var z=this.path; for (i in z) {
1302                        var w=z[i].ways; for (id in w) {
1303                                if (id!=this._name) {
1304                                        if (_root.map.ways[id].attr[principal]==this.attr[principal]) { same++; }
1305                                        else if (_root.map.ways[id].attr[principal]) { different++; }
1306                                }
1307                        }
1308                }
1309
1310                if (this.attr[principal]==undefined) { 
1311            str += iText('inspector_way_connects_to', same);
1312                } else {
1313            str += iText('inspector_way_connects_to_principal', same, this.attr[principal], different, principal);
1314                }
1315
1316                return "<p>"+str+"</p>";
1317        };
1318
1319
1320
1321        Object.registerClass("way",OSMWay);
1322
1323
1324        // =====================================================================================
1325        // Drawing support functions
1326
1327        // removeNodeFromWays - now see Node.removeFromAllWays
1328
1329        // startNewWay            - create new way from first node
1330
1331        function startNewWay(node) {
1332                uploadSelected();
1333                _root.newwayid--;
1334                selectWay(newwayid);
1335                _root.poiselected=0;
1336                _root.map.ways.attachMovie("way",newwayid,++waydepth);
1337                _root.map.ways[newwayid].path[0]=_root.nodes[node];
1338                _root.map.ways[newwayid].redraw();
1339                _root.map.ways[newwayid].select();
1340                _root.map.ways[newwayid].clean=false;
1341                _root.nodes[node].addWay(newwayid);
1342                _root.map.anchors[0].startElastic();
1343                _root.drawpoint=0;
1344                markClean(false);
1345                setTooltip(iText('hint_drawmode'),0);
1346        }
1347
1348        // addEndPoint(node object) - add point to start/end of line
1349
1350        function addEndPoint(node) {
1351                var drawnode=_root.ws.path[_root.drawpoint];
1352                if (_root.drawpoint==_root.ws.path.length-1) {
1353                        _root.ws.path.push(node);
1354                        _root.drawpoint=_root.ws.path.length-1;
1355                } else {
1356                        _root.ws.path.unshift(node);    // drawpoint=0, add to start
1357                }
1358                node.addWay(_root.wayselected);
1359       
1360                // Redraw line (if possible, just extend it to save time)
1361                if (_root.ws.getFill()>-1 || 
1362                        _root.ws.path.length<3 ||
1363                        _root.pointselected>-2 ||
1364                        _root.ws.attr['oneway']) {
1365                        _root.ws.redraw();
1366                        _root.ws.select();
1367                } else {
1368                        _root.ws.line.moveTo(drawnode.x,drawnode.y);
1369                        _root.ws.line.lineTo(node.x,node.y);
1370                        if (casing[_root.ws.attr['highway']]) {
1371                                _root.map.areas[wayselected].moveTo(drawnode.x,drawnode.y);
1372                                _root.map.areas[wayselected].lineTo(node.x,node.y);
1373                        }
1374                        _root.map.highlight.moveTo(drawnode.x,drawnode.y);
1375                        _root.map.highlight.lineTo(node.x,node.y);
1376                        _root.ws.direction();
1377                        _root.ws.highlightPoints(5000,"anchor");
1378                        removeMovieClip(_root.map.anchorhints);
1379                        var z=_root.wayrels[_root.wayselected];
1380                        for (var rel in z) { _root.map.relations[rel].redraw(); }
1381                }
1382                // Mark as unclean
1383                _root.ws.clean=false;
1384                markClean(false);
1385                _root.map.elastic.clear();
1386                var poslist=new Array(); poslist.push(_root.drawpoint);
1387                _root.undo.append(UndoStack.prototype.undo_addpoint,
1388                                                  new Array(new Array(_root.ws),poslist),
1389                                                  iText('action_addpoint'));
1390                updateInspector();
1391        }
1392
1393        function stopDrawing() {
1394                _root.map.anchors[_root.drawpoint].endElastic();
1395                _root.drawpoint=-1;
1396                if (_root.ws.path.length<=1) { 
1397                        // way not long enough, so abort
1398                        _root.ws.removeNodeIndex();
1399                        removeMovieClip(_root.map.areas[wayselected]);
1400                        removeMovieClip(_root.ws);
1401                        removeMovieClip(_root.map.anchors);
1402                        updateInspector();
1403                }
1404                _root.map.elastic.clear();
1405                clearTooltip();
1406        };
1407
1408        // cycleStacked - cycle through ways sharing same point
1409
1410        function cycleStacked() {
1411                if (_root.pointselected>-2) {
1412                        stopDrawing();
1413                        var id=_root.ws.path[_root.pointselected].id;
1414                        var firstfound=0; var nextfound=0;
1415                        for (qway in _root.map.ways) {
1416                                if (qway!=_root.wayselected) {
1417                                        for (qs=0; qs<_root.map.ways[qway].path.length; qs+=1) {
1418                                                if (_root.map.ways[qway].path[qs].id==id) {
1419                                                        var qw=Math.floor(qway);
1420                                                        if (firstfound==0 || qw<firstfound) { firstfound=qw; }
1421                                                        if ((nextfound==0 || qw<nextfound) && qw>wayselected) { nextfound=qw; }
1422                                                }
1423                                        }
1424                                }
1425                        }
1426                        if (firstfound) {
1427                                if (nextfound==0) { var nextfound=firstfound; }
1428                                _root.map.ways[nextfound].swapDepths(_root.ws);
1429                                _root.map.ways[nextfound].select();
1430                        }
1431                }
1432        };
1433
1434        // ================================================================
1435        // Way communication
1436       
1437        // whichWays    - get list of ways from remoting server
1438
1439        function whichWays(force) {
1440                _root.lastwhichways=new Date();
1441                var within=(_root.edge_l>=_root.bigedge_l &&
1442                                        _root.edge_r<=_root.bigedge_r &&
1443                                        _root.edge_b>=_root.bigedge_b &&
1444                                        _root.edge_t<=_root.bigedge_t);
1445                if (_root.waycount>500) { purgeWays(); }
1446                if (_root.poicount>500) { purgePOIs(); }
1447                if (within && !force) {
1448                        // we have already loaded this area, so ignore
1449                } else {
1450                        whichresponder=function() {};
1451                        whichresponder.onResult=function(result) {
1452                                _root.whichreceived+=1;
1453                                var code=result.shift(); var msg=result.shift(); if (code) { handleError(code,msg,result); return; }
1454                                var waylist  =result[0];
1455                                var pointlist=result[1];
1456                                var relationlist=result[2];
1457                                var s;
1458
1459                                for (i in pointlist) {                                                                          // POIs
1460                                        point=pointlist[i][0];                                                                  //  |
1461                                        if ((!_root.map.pois[point] || _root.map.pois[point].version!=pointlist[i][4]) && !_root.poistodelete[point]) {
1462                                                var a=getPOIIcon(pointlist[i][3]);                                      //  |
1463                                                if (a=='poi') { s=poiscale; } else { s=iconscale; }     //  |
1464                                                _root.map.pois.attachMovie(a,point,++poidepth); //  |
1465                                                _root.map.pois[point]._x=long2coord(pointlist[i][1]);// |
1466                                                _root.map.pois[point]._y=lat2coord (pointlist[i][2]);// |
1467                                                _root.map.pois[point]._xscale=_root.map.pois[point]._yscale=s;
1468                                                _root.map.pois[point].version=pointlist[i][4];          //  |
1469                                                _root.map.pois[point].attr=pointlist[i][3];                     //  |
1470                                                _root.map.pois[point].icon=a;                                           //  |
1471                                                _root.poicount+=1;                                                                      //  |
1472                                                if (point==prenode) { deselectAll(); prenode=undefined;
1473                                                                                          _root.map.pois[point].select(); }
1474                                        }
1475                                }
1476
1477                                for (i in waylist) {                                                                            // ways
1478                                        way=waylist[i][0];                                                                              //  |
1479                                        if ((!_root.map.ways[way] || _root.map.ways[way].version!=waylist[i][1]) && !_root.waystodelete[way]) {
1480                                                _root.waystoload.push(way);
1481                                        }
1482                                }
1483                               
1484                                for (i in relationlist) {
1485                                        rel = relationlist[i][0];
1486                    if (!_root.map.relations[rel] || _root.map.relations[rel].version!=relationlist[i][1]) {
1487                                                _root.relstoload.push(rel);
1488                                        }
1489                }
1490
1491                                if (preferences.data.limitways && _root.relstoload.length+_root.waystoload.length>1200) {
1492                                        _root.windows.attachMovie("modal","confirm",++windowdepth);
1493                                        _root.windows.confirm.init(275,100,new Array(iText('no'),iText('yes')),
1494                                                function(choice) {
1495                                                        if (choice==iText('yes')) { 
1496                                                                _root.waystoload=[];
1497                                                                _root.relstoload=[];
1498                                                                zoomTo(Math.min(_root.scale+2,_root.maxscale),_root.map._x*2-xradius,_root.map._y*2-yradius,true,true);
1499                                                        } else {
1500                                                                loadItems();
1501                                                        }
1502                                                });
1503                                        _root.windows.confirm.box.createTextField("prompt",2,7,9,250,120);
1504                                        writeText(_root.windows.confirm.box.prompt,iText('prompt_manyways'));
1505                                } else {
1506                                        loadItems();
1507                                }
1508
1509                        };
1510                        remote_read.call('whichways',whichresponder,_root.edge_l,_root.edge_b,_root.edge_r,_root.edge_t);
1511                        _root.whichrequested+=1;
1512                }
1513        }
1514
1515        // loadWay/loadRelation
1516
1517        function loadItems() {
1518                while (_root.waystoload.length) { loadWay(_root.waystoload.pop()); }
1519                while (_root.relstoload.length) { loadRelation(_root.relstoload.pop()); }
1520                if (_root.edge_l<_root.bigedge_l ||
1521                        _root.edge_r>_root.bigedge_r ||
1522                        _root.edge_t>_root.bigedge_t ||
1523                        _root.edge_b<_root.bigedge_b) { setBigEdges(); }
1524        }
1525
1526        function loadWay(way) {
1527                _root.map.ways.attachMovie("way",way,++waydepth);
1528                _root.map.ways[way].load();
1529                _root.waycount+=1;
1530                _root.waysrequested+=1;
1531        }
1532       
1533        function loadRelation(rel) {
1534                _root.map.relations.attachMovie("relation",rel,++reldepth);
1535                _root.map.relations[rel].load();
1536                _root.relcount+=1;
1537                _root.relsrequested+=1;
1538        }
1539       
1540        function setBigEdges() {
1541                _root.bigedge_l=_root.edge_l; _root.bigedge_r=_root.edge_r;
1542                _root.bigedge_b=_root.edge_b; _root.bigedge_t=_root.edge_t;
1543        }
1544
1545        // purgeWays - remove any clean ways outside current view
1546       
1547        function purgeWays() {
1548                for (qway in _root.map.ways) {
1549                        if (qway==_root.wayselected) {
1550                        } else if (!_root.map.ways[qway].clean) {
1551                                _root.map.ways[qway].upload();
1552                                uploadDirtyRelations();
1553                        } else if (((_root.map.ways[qway].xmin<edge_l && _root.map.ways[qway].xmax<edge_l) ||
1554                                                (_root.map.ways[qway].xmin>edge_r && _root.map.ways[qway].xmax>edge_r) ||
1555                                            (_root.map.ways[qway].ymin<edge_b && _root.map.ways[qway].ymax<edge_b) ||
1556                                                (_root.map.ways[qway].ymin>edge_t && _root.map.ways[qway].ymax>edge_t))) {
1557                                removeMovieClip(_root.map.ways[qway]);
1558                                removeMovieClip(_root.map.areas[qway]);
1559                                _root.waycount-=1;
1560                        }
1561                }
1562                setBigEdges();
1563        }
1564
1565        function selectWay(id) {
1566                _root.lastwayselected=_root.wayselected;
1567                _root.wayselected=Math.floor(id);
1568                _root.ws=_root.map.ways[id];
1569               
1570                if (id==0) {
1571                        _root.panel.advanced.disableOption(0);
1572                        _root.panel.advanced.disableOption(1);
1573                } else {
1574                        _root.panel.advanced.enableOption(0);
1575                        _root.panel.advanced.enableOption(1);
1576                }
1577                updateInspector();
1578        }
1579       
1580        function renumberWay(ow,nw) {
1581                _root.map.ways[ow].renumberNodeIndex(nw);
1582                wayrels[nw]=wayrels[ow]; delete wayrels[ow];
1583                _root.map.ways[ow]._name=nw;
1584                renumberMemberOfRelation('Way', ow, nw);
1585                if (_root.map.areas[ow]) { _root.map.areas[ow]._name=nw; }
1586                if (_root.panel.t_details.text==ow) { _root.panel.t_details.text=nw; _root.panel.t_details.setTextFormat(plainText); }
1587                if (wayselected==ow) { selectWay(nw); }
1588        }
1589       
1590        function mergeWayKeepingID(w1,w2) {
1591                var t=(w1==_root.ws || w2==_root.ws);
1592                var w,s;
1593                if (Number(w1._name)<0 && Number(w2._name)>0) {
1594                        s=w1.mergeAtCommonPoint(w2); w=w2;
1595                } else {
1596                        s=w2.mergeAtCommonPoint(w1); w=w1;
1597                }
1598                if (s) { w.redraw(); if (t) { w.select(); } }
1599        }
1600
1601        function uploadDirtyWays(allow_ws) {
1602                if (_root.sandbox) { return; }
1603                var z=_root.map.ways;
1604                for (i in z) {
1605                        if (!_root.map.ways[i].clean && (i!=wayselected || allow_ws) && !_root.map.ways[i].hasDependentNodes()) { 
1606                                _root.map.ways[i].upload();
1607                        }
1608                }
1609        };
1610
Note: See TracBrowser for help on using the repository browser.