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

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

Potlatch 1.3

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