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

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

Potlatch 1.2e

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