source: subversion/applications/editors/potlatch/relation.as @ 17889

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

Potlatch 1.2c. Not checking in SWF until I've spoken to avar and am reasonably confident about the i18n stuff

File size: 15.7 KB
Line 
1
2        // =====================================================================================
3        // relations.as
4        // Potlatch relation-handling code
5        // =====================================================================================
6
7        // ** highlight isn't yellow if it's part of a relation (should be)
8        // ** default tags: type, name
9        // ** verboseText isn't very good
10        // ** some circumstances (while saving?) in which if you pan and a new version
11        //    is loaded, they're added to the relation all over again
12
13        // ** need to move relations hash out of way/nodes (so we can maintain it for unloaded ways)
14
15        // =====================================================================================
16        // Classes - OSMRelation
17
18        function OSMRelation() {
19                this.members = new Array();
20                this.attr=new Object();
21                this.isHighlighting = false;
22                this.clean=true;                                        // altered since last upload?
23                this.uploading=false;                           // currently uploading?
24                this.locked=false;                                      // locked against upload?
25                this.version=0;
26        };
27        OSMRelation.prototype=new MovieClip();
28
29
30        // OSMRelation.verboseText - create long description of relation
31
32        OSMRelation.prototype.verboseText = function() {
33                var text = this._name+": ";
34                var type = undefined;
35                if ( this.attr['type'] ) {
36                        type = this.attr['type'];
37                        text += type + " ";
38                }
39
40                if ( type == 'route' ) {
41                        if ( this.attr['route'] )       text += this.attr['route'] + " ";
42                        if ( this.attr['network'] )     text += this.attr['network'] + " ";
43                        if ( this.attr['ref'] )         text += this.attr['ref']+" ";
44                        if ( this.attr['name'] )        text += this.attr['name']+" ";
45                        if ( this.attr['state'] )       text += "("+this.attr['state']+") ";
46                } else if ( this.attr['name'] ) text += this.attr['name'];
47
48                return text;
49        };
50
51        // OSMRelation.getType/getName - summary info used in property window
52
53        OSMRelation.prototype.getType=function() {
54                if (!this.attr['type']) { return "relation"; }
55                if (this.attr['type']=='route') {
56                        if (this.attr['network']) { return this.attr['network']; }
57                        if (this.attr['route']) { return this.attr['route']; }
58                }
59                return this.attr['type'];
60        };
61       
62        OSMRelation.prototype.getName=function() {
63                if (this.attr['ref' ]) { return this.attr['ref' ]; }
64                if (this.attr['name']) { return this.attr['name']; }
65                return '';
66        };
67
68        // OSMRelation.load - load from remote server
69
70        OSMRelation.prototype.load=function() {
71                responder = function() { };
72                responder.onResult = function(result) {
73                        _root.relsreceived+=1;
74                        var code=result.shift(); var msg=result.shift(); if (code) { handleError(code,msg,result); return; }
75                        defineRelationFromAMF(result);
76                };
77                remote_read.call('getrelation',responder,Math.floor(this._name));
78        };
79
80        function defineRelationFromAMF(result) {
81                var w=result[0];
82                var i,id;
83                _root.map.relations[w].clean=true;
84                _root.map.relations[w].locked=false;
85                _root.map.relations[w].attr=result[1];
86                _root.map.relations[w].members=result[2];
87                _root.map.relations[w].version=result[3];
88                _root.map.relations[w].redraw();
89                var mems=result[2];
90                for (m in mems) {
91                        findLinkedHash(mems[m][0],mems[m][1])[w]=mems[m][2];
92                }
93        };
94
95        OSMRelation.prototype.reload=function() {
96                if ( this._name < 0 )
97                        this.removeMovieClip();
98                else {
99                        _root.relsrequested++;
100                        this.load();
101                }
102        };
103
104        // OSMRelation.upload - save to remote server
105
106        OSMRelation.prototype.upload=function() {
107                putresponder=function() { };
108                putresponder.onResult=function(result) {
109                        _root.writesrequested--;
110                        var code=result.shift(); var msg=result.shift(); if (code) { handleError(code,msg,result); return; }
111                        var nw=result[1];       // new relation ID
112                        if (result[0]!=nw) {
113                                _root.map.relations[result[0]]._name=nw;                                // rename relation object
114                                var mems=_root.map.relations[nw].members;                               // make sure wayrels/noderels entries are up-to-date
115                                for (var i in mems) {                                                                   //  |
116                                        var r=findLinkedHash(mems[i][0],mems[i][1]);            //  |
117                                        r[nw]=mems[i][2];                                                                       //  |
118                                        delete r[result[0]];                                                            //  |
119                                }
120                        }
121                        _root.map.relations[nw].uploading=false;
122                        _root.map.relations[nw].clean=true;
123                        _root.map.relations[nw].version=result[2];
124                        operationDone(result[0]);
125                };
126
127                // ways/nodes for negative IDs should have been previously put
128                // so the server should know about them
129                if (!this.uploading && !this.locked && (!_root.sandbox || _root.uploading) ) {
130                        renewChangeset();
131                        this.uploading=true;
132                        _root.writesrequested++;
133                        remote_write.call('putrelation', putresponder,
134                                _root.usertoken, _root.changeset, this.version,
135                                Math.floor(this._name),
136                                this.attr, this.members, 1);
137                } else { 
138                        operationDone(this._name);      // next please!
139                }
140        };
141
142
143        // OSMRelation.redraw           - show on map
144        // OSMRelation.drawPoint
145        // OSMRelation.setHighlight
146
147        OSMRelation.prototype.redraw=function() {
148                this.createEmptyMovieClip("line",1);                                    // clear line
149                var linewidth=Math.max(_root.linewidth*3,10); if (scale>17) { linewidth*=0.8; }
150                var linealpha= this.isHighlighting ? 75 : 50;
151                var c = this.isHighlighting ? 0xff8800 : 0x8888ff;
152
153                var type = this.getType();
154                if ( !this.isHighlighting ) {
155                        if ( relcolours[type] != undefined ) {
156                                c = relcolours[type];   // linewidth = relwidths[type];
157                                linealpha = relalphas[type];
158                        }
159                }
160                if (linealpha==0 && !isHighlighting) { return; }        // don't draw relations where alpha=0
161                if (linealpha==0) { linealpha=25; }                                     //  | unless they're highlighted
162                this.line.lineStyle(linewidth,c,linealpha,false,"none");
163
164                var ms = this.members;
165                for ( var m = 0; m < ms.length; m++ ) {
166                        if ( ms[m][0] == 'Way' && _root.map.ways[ms[m][1]] ) {
167                                var way = _root.map.ways[ms[m][1]];
168               
169                                this.line.moveTo(way.path[0].x,way.path[0].y);
170                                for (var i=1; i<way.path.length; i+=1) {
171                                        this.line.lineTo(way.path[i].x,way.path[i].y);
172                                }
173                        } else if ( ms[m][0] == 'Node' && _root.map.pois[ms[m][1]] ) {
174                                var poi = _root.map.pois[ms[m][1]];
175                                this.drawPoint(poi._x, poi._y);
176                        } else if ( ms[m][0] == 'Node' ) {
177                                this.drawPoint(nodes[ms[m][1]].x,nodes[ms[m][1]].y);
178                        }
179                }
180        };
181
182        OSMRelation.prototype.drawPoint = function(x, y) {
183                var z = Math.max(100/Math.pow(2,_root.scale-13),6.25)/30;
184                this.line.moveTo(x-z,y-z);
185                this.line.lineTo(x-z,y+z);
186                this.line.lineTo(x+z,y+z);
187                this.line.lineTo(x+z,y-z);
188                this.line.lineTo(x-z,y-z);
189        };
190
191        OSMRelation.prototype.setHighlight = function(highlight) {
192                if ( this.isHighlighting == highlight )
193                        return;
194
195                this.isHighlighting = highlight;
196                this.redraw();
197        };
198
199        // ---- Editing and information functions
200
201        OSMRelation.prototype.getWayRole=function(way_id) {
202                return this.getRole('Way', way_id);
203        };
204
205        OSMRelation.prototype.getRole=function(type, id) {
206                return findLinkedHash(type,id)[this._name];
207        };
208
209        OSMRelation.prototype.renumberMember=function(type, id, new_id) {
210                var mems = this.members;
211                var set = false;
212                var r;
213                for ( var m = 0; m < mems.length && !set; m++ ) {
214                        if ( mems[m][0] == type && mems[m][1] == id ) {
215                                mems[m][1] = new_id;
216                                set = true;
217                                r=findLinkedHash(type,id); delete r[this._name];
218                                findLinkedHash(type,new_id)[this._name]=mems[m][2];
219                                this.clean = false;
220                        }
221                }
222        };
223
224        OSMRelation.prototype.setRole=function(type, id, role) {
225                var mems = this.members;
226                var set = false;
227                var diff = true;
228                for ( var m = 0; m < mems.length && !set; m++ ) {
229                        if ( mems[m][0] == type && mems[m][1] == id ) {
230                                diff = (mems[m][2] != role);
231                                mems[m][2] = role;
232                                set = true;
233                        }
234                }
235                findLinkedHash(type,id)[this._name]=role;
236                if ( !set )
237                        this.members.push([type, id, role]);
238                if ( diff ) {
239                        this.clean = false;
240                        this.redraw();
241                }
242        };
243
244        OSMRelation.prototype.setWayRole=function(way_id, role) {
245                this.setRole('Way', way_id, role);
246        };
247
248        OSMRelation.prototype.hasWay=function(way_id) {
249                var role = this.getWayRole(way_id);
250                return role == undefined ? false : true;
251        };
252
253        OSMRelation.prototype.getNodeRole=function(node_id) {
254                return this.getRole('Node', node_id);
255        };
256
257        OSMRelation.prototype.setNodeRole=function(node_id, role) {
258                this.setRole('Node', node_id, role);
259        };
260
261        OSMRelation.prototype.hasNode=function(node_id) {
262                var role = this.getNodeRole(node_id);
263                return role == undefined ? false : true;
264        };
265
266        OSMRelation.prototype.removeMember=function(type, id) {
267                this.removeMemberDirty(type, id, true);
268        };
269
270        OSMRelation.prototype.removeMemberDirty=function(type, id, markDirty) {
271                var mems = this.members;
272                for (var m in mems) {
273                        if ( mems[m][0] == type && mems[m][1] == id ) {
274                                mems.splice(m, 1);
275                                if ( markDirty ) { this.clean = false; }
276                                this.redraw();
277                        }
278                }
279                var r=findLinkedHash(type,id);
280                delete r[this._name];
281        };
282
283        OSMRelation.prototype.removeWay=function(way_id) {
284                this.removeMember('Way', way_id);
285        };
286
287        OSMRelation.prototype.removeNode=function(node_id) {
288                this.removeMember('Node', node_id);
289        };
290
291        // ----- UI
292
293        OSMRelation.prototype.editRelation = function(relexists) {
294                _root.relexists=relexists;
295                var rel = this;
296                var completeEdit = function(button) {
297                        if (button==iText('ok') ) {
298                                // save changes to relation tags
299                                _root.windows.relation.box.properties.tidy();
300                        } else if (!relexists) {
301                                // cancel in dialogue after "create new relation"
302                                removeMovieClip(_root.editingrelation); rel=null;
303                        } else {
304                                // cancel for existing relation
305                                _root.editingrelation.attr=_root.editingrelationattr;
306                        }
307                        rel.setHighlight(false);
308                        _root.panel.properties.reinit();
309                };
310
311                rel.setHighlight(true);
312                _root.panel.properties.enableTabs(false);
313               
314                _root.windows.attachMovie("modal","relation",++windowdepth);
315                _root.windows.relation.init(402, 255, [iText('cancel'), iText('ok')], completeEdit);
316                var z=5;
317                var box=_root.windows.relation.box;
318               
319                box.createTextField("title",z++,7,7,400-14,20);
320                with (box.title) {
321                        type='dynamic';
322                        text=this._name; setTextFormat(plainText);
323                        setNewTextFormat(boldText); replaceSel("Relation ");
324                }
325
326                // Light grey background
327                box.createEmptyMovieClip('lightgrey',z++);
328                with (box.lightgrey) {
329                        beginFill(0xF3F3F3,100);
330                        moveTo(10,30); lineTo(392,30);
331                        lineTo(392,213); lineTo(10,213);
332                        lineTo(10,30); endFill();
333                };
334
335                box.attachMovie("propwindow","properties",z++);
336                with (box.properties) { _x=14; _y=34; };
337                _root.editingrelation = this;
338                _root.editingrelationattr = deepCopy(this.attr);
339                box.properties.ynumber = 9;
340                box.properties.init("relation",2,9);
341
342                box.attachMovie("newattr", "newattr", z++);
343                with ( box.newattr ) {
344                        _x = 400-16; _y = 18;
345                }
346                box.newattr.onRelease =function() { box.properties.enterNewAttribute(); };
347                box.newattr.onRollOver=function() { setFloater(iText('tip_addtag')); };
348                box.newattr.onRollOut =function() { clearFloater(); };
349        };
350
351        Object.registerClass("relation",OSMRelation);
352
353
354        // ===============================
355        // Support functions for relations
356
357        function findLinkedHash(type,id) {
358                // returns hash of relations in way/POI/node object
359                var r;
360                if (type=='Way') {
361                        if (!_root.wayrels[id]) { _root.wayrels[id]=new Object(); }
362                        r=_root.wayrels[id]; 
363                } else {
364                        if (!_root.noderels[id]) { _root.noderels[id]=new Object(); }
365                        r=_root.noderels[id];
366                }
367                return r;
368        }
369
370        function getRelations(type, id) {
371                // this is very expensive and shouldn't be called unless really necessary
372                var rels = new Object();
373                var z = _root.map.relations;
374                for (var i in z) {
375                        var mems=z[i].members;
376                        for (var m in mems) {
377                                if (mems[m][0]==type && mems[m][1]==id) { rels[i]=mems[m][2]; }
378                        }
379                }
380                return rels;
381        }
382
383        function memberDeleted(type, id) {
384                var rels = _root.map.relations;
385                for ( r in rels )
386                        rels[r].removeMemberDirty(type, id, true);
387        }
388
389        function renumberMemberOfRelation(type, id, new_id) {
390                var rels = _root.map.relations;
391                for ( r in rels )
392                        rels[r].renumberMember(type, id, new_id);
393        }
394
395        function markWayRelationsDirty(way) {
396                var z=_root.wayrels[way];
397                for (var i in z) { _root.map.relations[i].clean=false; }
398
399                var p=_root.map.ways[way].path;
400                for (var i in p) { markNodeRelationsDirty(_root.map.ways[way].path[i].id); }
401        }
402       
403        function markNodeRelationsDirty(node) {
404                var z=_root.noderels[node];
405                for (var i in z) { _root.map.relations[i].clean=false; }
406        }
407
408        function uploadDirtyRelations() {
409                if (_root.sandbox) { return; }
410                var rs = _root.map.relations;
411                for ( var i in rs ) {
412                        if ( !rs[i].clean ) {
413                                rs[i].upload();
414                        }
415                }
416        }
417
418        // addToRelation - add a way/point to a relation
419        //                                 called when user clicks '+' relation button
420
421        function addToRelation() {
422                var proptype = _root.panel.properties.proptype;
423                var type, id;
424                switch (proptype) {
425                        case 'way':             type='Way' ; id=wayselected; break;
426                        case 'point':   type='Node'; id=_root.ws.path[_root.pointselected].id; break;
427                        case 'POI':             type='Node'; id=poiselected; break;
428                }
429                if ( type == undefined || id == undefined ) return;
430
431                var completeAdd = function(button) {
432
433                        if ( button != iText('ok') ) return false;
434
435                        var box=_root.windows.relation.box;
436                        var radio=box.reloption.selected;
437                        var keepDialog = false;
438
439                        switch (radio) {
440                                case 1: // Add to an existing relation
441                                                var selected=box.addroute_menu.selected;
442                                                var rs=_root.map.relations;
443                                                var i=0;
444                                                for (var r in rs) {
445                                                        if (selected==i) { 
446                                                        rs[r].setRole(type, id, ''); }
447                                                        i++;
448                                                }
449                                                break;
450                                case 2: // Create a new relation
451                                                var nid = newrelid--;
452                                                _root.map.relations.attachMovie("relation",nid,++reldepth);
453                                                _root.map.relations[nid].setRole(type, id, '');
454                                                _root.map.relations[nid].attr['type'] = undefined;
455                                                _root.windows.relation.remove(); keepDialog = true;
456                                                _root.map.relations[nid].editRelation(false);
457                                                break;
458                                case 3: // Find a relation
459                                                keepDialog=true;
460                                                if (box.search.text!='') {
461                                                        findresponder=function() {};
462                                                        findresponder.onResult=function(rellist) {
463                                                                for (r in rellist) {
464                                                                        if (!_root.map.relations[rellist[r][0]]) {
465                                                                                _root.map.relations.attachMovie("relation",rellist[r][0],++reldepth);
466                                                                                defineRelationFromAMF(rellist[r]);
467                                                                        }
468                                                                }
469                                                                createRelationMenu(_root.windows.relation.box,20);
470                                                                _root.windows.relation.box.search.text='';
471                                                        };
472                                                        remote_read.call('findrelations',findresponder,box.search.text);
473                                                }
474                                                break;
475                        }
476                        _root.panel.properties.reinit();
477                        if (keepDialog) { _root.panel.properties.enableTabs(false); }
478                        return keepDialog;
479                };
480
481                // Create dialogue
482
483                _root.panel.properties.enableTabs(false);
484                _root.windows.attachMovie("modal","relation",++windowdepth);
485                _root.windows.relation.init(300, 150, [iText('cancel'), iText('ok')], completeAdd);
486                var z = 5;
487                var box = _root.windows.relation.box;
488               
489                box.createTextField("title",z++,7,7,300-14,20);
490                box.title.text = iText('prompt_addtorelation',proptype);
491                with (box.title) {
492                        wordWrap=true;
493                        setTextFormat(boldText);
494                        selectable=false; type='dynamic';
495                }
496                adjustTextField(box.title);
497               
498                box.createTextField("instr",z++,7,30,300-14,40);
499
500                // Create radio buttons and menu
501
502                box.attachMovie("radio","reloption",z++);
503                box.reloption.addButton(10,35,iText('existingrelation'));
504                box.reloption.addButton(10,75,iText('createrelation'));
505                box.reloption.addButton(10,95,iText('findrelation'));
506
507                createRelationMenu(box,20);
508
509                var w=box.reloption[3].prompt._width+25;
510                box.createTextField("search",z++,w,90,290-w,17);
511                box.search.setNewTextFormat(plainSmall);
512                box.search.type='input';
513                box.search.backgroundColor=0xDDDDDD;
514                box.search.background=true;
515                box.search.border=true;
516                box.search.borderColor=0xFFFFFF;
517                box.search.onSetFocus=function() { this._parent.reloption.select(3); };
518        }
519
520        function createRelationMenu(box,z) {
521                var relations=new Array();
522                var rs = _root.map.relations;
523                for ( var r in rs ) {
524                        relations.push(rs[r].verboseText());
525                }
526
527                if (relations.length==0) {
528                        relations.push(iText('norelations'));
529                        box.reloption.disable(1);
530                        box.reloption.select(2);
531                } else {
532                        box.reloption.enable(1);
533                        box.reloption.select(1);
534                }
535                box.attachMovie("menu", "addroute_menu", z);
536                box.addroute_menu.init(25, 50, 0, relations,
537                                        iText('tip_selectrelation'),null, null, 268);
538        }
Note: See TracBrowser for help on using the repository browser.