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

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

couple of little fixes

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