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

Last change on this file since 16526 was 16525, checked in by richard, 10 years ago

Potlatch 1.1a

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