source: trunk/gui/scripts/mapviewer.tcl @ 4275

Last change on this file since 4275 was 4275, checked in by gah, 10 years ago

allow map type to default to projected

File size: 53.4 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2
3# ----------------------------------------------------------------------
4#  COMPONENT: mapviewer - Map object viewer
5#
6#  It connects to the GeoVis server running on a rendering farm,
7#  transmits data, and displays the results.
8# ======================================================================
9#  AUTHOR:  Michael McLennan, Purdue University
10#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
11#
12#  See the file "license.terms" for information on usage and
13#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14# ======================================================================
15package require Itk
16package require BLT
17#package require Img
18
19option add *MapViewer.width 4i widgetDefault
20option add *MapViewer*cursor crosshair widgetDefault
21option add *MapViewer.height 4i widgetDefault
22option add *MapViewer.foreground black widgetDefault
23option add *MapViewer.controlBackground gray widgetDefault
24option add *MapViewer.controlDarkBackground #999999 widgetDefault
25option add *MapViewer.plotBackground black widgetDefault
26option add *MapViewer.plotForeground white widgetDefault
27option add *MapViewer.font \
28    -*-helvetica-medium-r-normal-*-12-* widgetDefault
29
30# must use this name -- plugs into Rappture::resources::load
31proc MapViewer_init_resources {} {
32    Rappture::resources::register \
33        geovis_server Rappture::MapViewer::SetServerList
34}
35
36itcl::class Rappture::MapViewer {
37    inherit Rappture::VisViewer
38
39    itk_option define -plotforeground plotForeground Foreground ""
40    itk_option define -plotbackground plotBackground Background ""
41
42    private variable _layers "";        # Name of layers tab widget
43    private variable _mapsettings;      # Global map settings
44
45    constructor { hostlist args } {
46        Rappture::VisViewer::constructor $hostlist
47    } {
48        # defined below
49    }
50    destructor {
51        # defined below
52    }
53    public proc SetServerList { namelist } {
54        Rappture::VisViewer::SetServerList "geovis" $namelist
55    }
56    public method add {dataobj {settings ""}}
57    public method camera {option args}
58    public method delete {args}
59    public method disconnect {}
60    public method download {option args}
61    public method get {args}
62    public method isconnected {}
63    public method limits { colormap }
64    public method parameters {title args} {
65        # do nothing
66    }
67    public method scale {args}
68
69    protected method Connect {}
70    protected method CurrentDatasets {args}
71    protected method Disconnect {}
72    protected method DoResize {}
73    protected method DoRotate {}
74    protected method AdjustSetting {what {value ""}}
75    protected method FixSettings { args  }
76    protected method KeyPress { key }
77    protected method KeyRelease { key }
78    protected method MouseClick { button x y }
79    protected method MouseDoubleClick { button x y }
80    protected method MouseDrag { button x y }
81    protected method MouseMotion {}
82    protected method MouseRelease { button x y }
83    protected method MouseScroll { direction }
84    protected method Pan {option x y}
85    protected method Pick {x y}
86    protected method Rebuild {}
87    protected method ReceiveDataset { args }
88    protected method ReceiveImage { args }
89    protected method Rotate {option x y}
90    protected method Zoom {option}
91
92    # The following methods are only used by this class.
93    private method UpdateLayerControls {}
94    private method ChangeLayerVisibility { dataobj layer }
95    private method BuildCameraTab {}
96    private method BuildLayerTab {}
97    private method BuildDownloadPopup { widget command }
98    private method BuildTerrainTab {}
99    private method EventuallyResize { w h }
100    private method EventuallyHandleMotionEvent { x y }
101    private method EventuallyRotate { q }
102    private method GetImage { args }
103    private method PanCamera {}
104    private method SetObjectStyle { dataobj layer }
105    private method SetOpacity { dataset }
106    private method SetOrientation { side }
107
108    private variable _arcball ""
109    private variable _dlist "";         # list of data objects
110    private variable _obj2datasets
111    private variable _obj2ovride;       # maps dataobj => style override
112    private variable _datasets;         # contains all the dataobj-component
113                                        # datasets in the server
114    private variable _click;            # info used for rotate operations
115    private variable _limits;           # autoscale min/max for all axes
116    private variable _view;             # view params for 3D view
117    private variable _settings
118    private variable _visibility
119    private variable _style;            # Array of current component styles.
120    private variable _initialStyle;     # Array of initial component styles.
121    private variable _reset 1;          # Indicates that server was reset and
122                                        # needs to be reinitialized.
123    private variable _haveTerrain 0
124
125    private variable _first ""     ;# This is the topmost dataset.
126    private variable _start 0
127    private variable _title ""
128
129    common _downloadPopup          ;# download options from popup
130    private common _hardcopy
131    private variable _width 0
132    private variable _height 0
133    private variable _resizePending 0
134    private variable _rotatePending 0
135    private variable _rotateDelay 150
136    private variable _scaleDelay 100
137    private variable _motion
138}
139
140itk::usual MapViewer {
141    keep -background -foreground -cursor -font
142    keep -plotbackground -plotforeground
143}
144
145# ----------------------------------------------------------------------
146# CONSTRUCTOR
147# ----------------------------------------------------------------------
148itcl::body Rappture::MapViewer::constructor {hostlist args} {
149    set _serverType "geovis"
150
151    if { [catch {
152       
153    # Rebuild event
154    $_dispatcher register !rebuild
155    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
156
157    # Resize event
158    $_dispatcher register !resize
159    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
160
161    # Rotate event
162    $_dispatcher register !rotate
163    $_dispatcher dispatch $this !rotate "[itcl::code $this DoRotate]; list"
164
165    # <Motion> event
166    $_dispatcher register !motion
167    $_dispatcher dispatch $this !motion "[itcl::code $this MouseMotion]; list"
168
169    #
170    # Populate parser with commands handle incoming requests
171    #
172    $_parser alias image    [itcl::code $this ReceiveImage]
173    $_parser alias dataset  [itcl::code $this ReceiveDataset]
174
175    array set _motion {
176        x               0
177        y               0
178        pending         0
179        delay           100
180    }
181    # Initialize the view to some default parameters.
182    array set _view {
183        qw              1.0
184        qx              0.0
185        qy              0.0
186        qz              0.0
187        zoom            1.0
188        xpan            0.0
189        ypan            0.0
190    }
191    set _arcball [blt::arcball create 100 100]
192    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
193    $_arcball quaternion $q
194
195    set _limits(zmin) 0.0
196    set _limits(zmax) 1.0
197
198    array set _settings [subst {
199        legend                 1
200        terrain-edges          0
201        terrain-lighting       0
202        terrain-vertscale      1.0
203        terrain-wireframe      0
204    }]
205    itk_component add view {
206        canvas $itk_component(plotarea).view \
207            -highlightthickness 0 -borderwidth 0
208    } {
209        usual
210        ignore -highlightthickness -borderwidth  -background
211    }
212
213    set c $itk_component(view)
214    bind $c <KeyPress-Left>  [list %W xview scroll 10 units]
215    bind $c <KeyPress-Right> [list %W xview scroll -10 units]
216    bind $c <KeyPress-Up>    [list %W yview scroll 10 units]
217    bind $c <KeyPress-Down>  [list %W yview scroll -10 units]
218    bind $c <Enter> "focus %W"
219    bind $c <Control-F1> [itcl::code $this ToggleConsole]
220
221    # Fix the scrollregion in case we go off screen
222    $c configure -scrollregion [$c bbox all]
223
224    set _map(id) [$c create image 0 0 -anchor nw -image $_image(plot)]
225    set _map(cwidth) -1
226    set _map(cheight) -1
227    set _map(zoom) 1.0
228    set _map(original) ""
229
230    set f [$itk_component(main) component controls]
231    itk_component add reset {
232        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
233            -highlightthickness 0 \
234            -image [Rappture::icon reset-view] \
235            -command [itcl::code $this Zoom reset]
236    } {
237        usual
238        ignore -highlightthickness
239    }
240    pack $itk_component(reset) -side top -padx 2 -pady 2
241    Rappture::Tooltip::for $itk_component(reset) \
242        "Reset the view to the default zoom level"
243
244    itk_component add zoomin {
245        button $f.zin -borderwidth 1 -padx 1 -pady 1 \
246            -highlightthickness 0 \
247            -image [Rappture::icon zoom-in] \
248            -command [itcl::code $this Zoom in]
249    } {
250        usual
251        ignore -highlightthickness
252    }
253    pack $itk_component(zoomin) -side top -padx 2 -pady 2
254    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
255
256    itk_component add zoomout {
257        button $f.zout -borderwidth 1 -padx 1 -pady 1 \
258            -highlightthickness 0 \
259            -image [Rappture::icon zoom-out] \
260            -command [itcl::code $this Zoom out]
261    } {
262        usual
263        ignore -highlightthickness
264    }
265    pack $itk_component(zoomout) -side top -padx 2 -pady 2
266    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
267
268    BuildLayerTab
269    BuildCameraTab
270
271    # Legend
272
273    set _image(legend) [image create photo]
274    itk_component add legend {
275        canvas $itk_component(plotarea).legend -width 50 -highlightthickness 0
276    } {
277        usual
278        ignore -highlightthickness
279        rename -background -plotbackground plotBackground Background
280    }
281
282    # Hack around the Tk panewindow.  The problem is that the requested
283    # size of the 3d view isn't set until an image is retrieved from
284    # the server.  So the panewindow uses the tiny size.
285    set w 10000
286    pack forget $itk_component(view)
287    blt::table $itk_component(plotarea) \
288        0,0 $itk_component(view) -fill both -reqwidth $w
289    blt::table configure $itk_component(plotarea) c1 -resize none
290
291    # Bindings for keyboard events
292    bind $itk_component(view) <KeyPress> \
293        [itcl::code $this KeyPress %N]
294    bind $itk_component(view) <KeyRelease> \
295        [itcl::code $this KeyRelease %N]
296
297    # Bindings for rotation via mouse
298    bind $itk_component(view) <ButtonPress-1> \
299        [itcl::code $this MouseClick 1 %x %y]
300        #[itcl::code $this Rotate click %x %y]
301    bind $itk_component(view) <Double-1> \
302        [itcl::code $this MouseDoubleClick 1 %x %y]
303    bind $itk_component(view) <B1-Motion> \
304        [itcl::code $this MouseDrag 1 %x %y]
305        #[itcl::code $this Rotate drag %x %y]
306    bind $itk_component(view) <ButtonRelease-1> \
307        [itcl::code $this MouseRelease 1 %x %y]
308        #[itcl::code $this Rotate release %x %y]
309    bind $itk_component(view) <Configure> \
310        [itcl::code $this EventuallyResize %w %h]
311
312    # Bindings for panning via mouse
313    bind $itk_component(view) <ButtonPress-2> \
314        [itcl::code $this MouseClick 2 %x %y]
315        #[itcl::code $this Pan click %x %y]
316    bind $itk_component(view) <Double-2> \
317        [itcl::code $this MouseDoubleClick 2 %x %y]
318    bind $itk_component(view) <B2-Motion> \
319        [itcl::code $this MouseDrag 2 %x %y]
320        #[itcl::code $this Pan drag %x %y]
321    bind $itk_component(view) <ButtonRelease-2> \
322        [itcl::code $this MouseRelease 2 %x %y]
323        #[itcl::code $this Pan release %x %y]
324
325    bind $itk_component(view) <ButtonPress-3> \
326        [itcl::code $this MouseClick 3 %x %y]
327    bind $itk_component(view) <Double-3> \
328        [itcl::code $this MouseDoubleClick 3 %x %y]
329    bind $itk_component(view) <B3-Motion> \
330        [itcl::code $this MouseDrag 3 %x %y]
331    bind $itk_component(view) <ButtonRelease-3> \
332        [itcl::code $this MouseRelease 3 %x %y]
333
334    bind $itk_component(view) <Motion> \
335        [itcl::code $this EventuallyHandleMotionEvent %x %y]
336
337    # Bindings for panning via keyboard
338    bind $itk_component(view) <KeyPress-Left> \
339        [itcl::code $this Pan set -10 0]
340    bind $itk_component(view) <KeyPress-Right> \
341        [itcl::code $this Pan set 10 0]
342    bind $itk_component(view) <KeyPress-Up> \
343        [itcl::code $this Pan set 0 -10]
344    bind $itk_component(view) <KeyPress-Down> \
345        [itcl::code $this Pan set 0 10]
346    bind $itk_component(view) <Shift-KeyPress-Left> \
347        [itcl::code $this Pan set -2 0]
348    bind $itk_component(view) <Shift-KeyPress-Right> \
349        [itcl::code $this Pan set 2 0]
350    bind $itk_component(view) <Shift-KeyPress-Up> \
351        [itcl::code $this Pan set 0 -2]
352    bind $itk_component(view) <Shift-KeyPress-Down> \
353        [itcl::code $this Pan set 0 2]
354
355    # Bindings for zoom via keyboard
356    bind $itk_component(view) <KeyPress-Prior> \
357        [itcl::code $this Zoom out]
358    bind $itk_component(view) <KeyPress-Next> \
359        [itcl::code $this Zoom in]
360
361    bind $itk_component(view) <Enter> "focus $itk_component(view)"
362
363    if {[string equal "x11" [tk windowingsystem]]} {
364        # Bindings for zoom via mouse
365        #bind $itk_component(view) <4> [itcl::code $this Zoom out]
366        #bind $itk_component(view) <5> [itcl::code $this Zoom in]
367        bind $itk_component(view) <4> [itcl::code $this MouseScroll up]
368        bind $itk_component(view) <5> [itcl::code $this MouseScroll down]
369    }
370
371    set _image(download) [image create photo]
372
373    eval itk_initialize $args
374    Connect
375} errs] != 0 } {
376        puts stderr errs=$errs
377    }
378}
379
380# ----------------------------------------------------------------------
381# DESTRUCTOR
382# ----------------------------------------------------------------------
383itcl::body Rappture::MapViewer::destructor {} {
384    Disconnect
385    $_dispatcher cancel !rebuild
386    $_dispatcher cancel !resize
387    $_dispatcher cancel !rotate
388    image delete $_image(plot)
389    image delete $_image(download)
390    catch { blt::arcball destroy $_arcball }
391}
392
393itcl::body Rappture::MapViewer::DoResize {} {
394    set sendResize 1
395    if { $_width < 2 } {
396        set _width 500
397        set sendResize 0
398    }
399    if { $_height < 2 } {
400        set _height 500
401        set sendResize 0
402    }
403    set _start [clock clicks -milliseconds]
404    if {$sendResize} {
405        SendCmd "screen size $_width $_height"
406    }
407    set _resizePending 0
408}
409
410itcl::body Rappture::MapViewer::DoRotate {} {
411    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
412    SendCmd "camera orient $q"
413    set _rotatePending 0
414}
415
416itcl::body Rappture::MapViewer::EventuallyResize { w h } {
417    set _width $w
418    set _height $h
419    $_arcball resize $w $h
420    if { !$_resizePending } {
421        set _resizePending 1
422        $_dispatcher event -after 200 !resize
423    }
424}
425
426itcl::body Rappture::MapViewer::EventuallyRotate { q } {
427    foreach { _view(qw) _view(qx) _view(qy) _view(qz) } $q break
428    if { !$_rotatePending } {
429        set _rotatePending 1
430        $_dispatcher event -after $_rotateDelay !rotate
431    }
432}
433
434# ----------------------------------------------------------------------
435# USAGE: add <dataobj> ?<settings>?
436#
437# Clients use this to add a data object to the plot.  The optional
438# <settings> are used to configure the plot.  Allowed settings are
439# -color, -brightness, -width, -linestyle, and -raise.
440# ----------------------------------------------------------------------
441itcl::body Rappture::MapViewer::add {dataobj {settings ""}} {
442    array set params {
443        -color auto
444        -width 1
445        -linestyle solid
446        -brightness 0
447        -raise 0
448        -description ""
449        -param ""
450        -type ""
451    }
452    array set params $settings
453    set params(-description) ""
454    set params(-param) ""
455    array set params $settings
456
457    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
458        # can't handle -autocolors yet
459        set params(-color) black
460    }
461    set pos [lsearch -exact $_dlist $dataobj]
462    if {$pos < 0} {
463        #if {[llength $_dlist] > 0} {
464        #    error "Can't add more than 1 map to mapviewer"
465        #}
466        lappend _dlist $dataobj
467    }
468    set _obj2ovride($dataobj-color) $params(-color)
469    set _obj2ovride($dataobj-width) $params(-width)
470    set _obj2ovride($dataobj-raise) $params(-raise)
471    $_dispatcher event -idle !rebuild
472}
473
474# ----------------------------------------------------------------------
475# USAGE: delete ?<dataobj1> <dataobj2> ...?
476#
477#       Clients use this to delete a dataobj from the plot.  If no dataobjs
478#       are specified, then all dataobjs are deleted.  No data objects are
479#       deleted.  They are only removed from the display list.
480#
481# ----------------------------------------------------------------------
482itcl::body Rappture::MapViewer::delete {args} {
483    if { [llength $args] == 0} {
484        set args $_dlist
485    }
486    # Delete all specified dataobjs
487    set changed 0
488    foreach dataobj $args {
489        set pos [lsearch -exact $_dlist $dataobj]
490        if { $pos < 0 } {
491            continue;                   # Don't know anything about it.
492        }
493        # When a map is marked deleted, we hide its layers.
494        foreach layer [$dataobj layers] {
495            set tag $dataobj-$layer
496            SendCmd "map layer visible 0 $tag"
497            set _visibility($tag) 0
498        }
499        # Remove it from the dataobj list.
500        set _dlist [lreplace $_dlist $pos $pos]
501        array unset _obj2ovride $dataobj-*
502        array unset _settings $dataobj-*
503        set changed 1
504    }
505    # If anything changed, then rebuild the plot
506    if { $changed } {
507        $_dispatcher event -idle !rebuild
508    }
509}
510
511# ----------------------------------------------------------------------
512# USAGE: get ?-objects?
513# USAGE: get ?-visible?
514# USAGE: get ?-image view?
515#
516# Clients use this to query the list of objects being plotted, in
517# order from bottom to top of this result.  The optional "-image"
518# flag can also request the internal images being shown.
519# ----------------------------------------------------------------------
520itcl::body Rappture::MapViewer::get {args} {
521    if {[llength $args] == 0} {
522        set args "-objects"
523    }
524
525    set op [lindex $args 0]
526    switch -- $op {
527        "-objects" {
528            # put the dataobj list in order according to -raise options
529            set dlist {}
530            foreach dataobj $_dlist {
531                if { ![$dataobj isvalid] } {
532                    continue
533                }
534                if {[info exists _obj2ovride($dataobj-raise)] &&
535                    $_obj2ovride($dataobj-raise)} {
536                    set dlist [linsert $dlist 0 $dataobj]
537                } else {
538                    lappend dlist $dataobj
539                }
540            }
541            return $dlist
542        }
543        "-visible" {
544            set dlist {}
545            foreach dataobj $_dlist {
546                if { ![$dataobj isvalid] } {
547                    continue
548                }
549                if { ![info exists _obj2ovride($dataobj-raise)] } {
550                    # No setting indicates that the object isn't visible.
551                    continue
552                }
553                # Otherwise use the -raise parameter to put the object to
554                # the front of the list.
555                if { $_obj2ovride($dataobj-raise) } {
556                    set dlist [linsert $dlist 0 $dataobj]
557                } else {
558                    lappend dlist $dataobj
559                }
560            }
561            return $dlist
562        }           
563        -image {
564            if {[llength $args] != 2} {
565                error "wrong # args: should be \"get -image view\""
566            }
567            switch -- [lindex $args end] {
568                view {
569                    return $_image(plot)
570                }
571                default {
572                    error "bad image name \"[lindex $args end]\": should be view"
573                }
574            }
575        }
576        default {
577            error "bad option \"$op\": should be -objects or -image"
578        }
579    }
580}
581
582# ----------------------------------------------------------------------
583# USAGE: scale ?<data1> <data2> ...?
584#
585# Sets the default limits for the overall plot according to the
586# limits of the data for all of the given <data> objects.  This
587# accounts for all objects--even those not showing on the screen.
588# Because of this, the limits are appropriate for all objects as
589# the user scans through data in the ResultSet viewer.
590# ----------------------------------------------------------------------
591itcl::body Rappture::MapViewer::scale {args} {
592    array unset _mapsettings
593
594    # Verify that all the maps have the same global settings. For example,
595    # you can't have one map type "geocentric" and the other "projected".
596
597    foreach dataobj $args {
598        if { ![$dataobj isvalid] } {
599            continue
600        }
601        array unset hints
602        array set hints [$dataobj hints]
603        if { ![info exists overall(type)] } {
604            set _mapsettings(type) $hints(type)
605        } elseif { $hints(type) != $_mapsettings(type) } {
606            error "maps \"$hints(label)\" have differing types"
607        }
608        if { ![info exists _mapsettings(projection)] } {
609            set _mapsettings(projection) $hints(projection)
610        } elseif { $hints(projection) != $_mapsettings(projection) } {
611            error "maps \"$hints(label)\" have differing projections"
612        }
613        if { ![info exists _mapsettings(extents)] } {
614            set _mapsettings(extents) $hints(extents)
615        } elseif { $hints(extents) != $_mapsettings(extents) } {
616            error "maps \"$hints(label)\" have differing extents"
617        }
618
619        foreach layer [$dataobj layers] {
620            set type [$dataobj layer $layer]
621            switch -- $type {
622                "elevation" {
623                    set _haveTerrain 1
624                }
625            }
626        }
627    }
628    if { $_haveTerrain } {
629        if { ![$itk_component(main) exists "Terrain Settings"] } {
630            if { [catch { BuildTerrainTab } errs ]  != 0 } {
631                puts stderr "errs=$errs"
632            }
633        }
634    }
635}
636
637# ----------------------------------------------------------------------
638# USAGE: download coming
639# USAGE: download controls <downloadCommand>
640# USAGE: download now
641#
642# Clients use this method to create a downloadable representation
643# of the plot.  Returns a list of the form {ext string}, where
644# "ext" is the file extension (indicating the type of data) and
645# "string" is the data itself.
646# ----------------------------------------------------------------------
647itcl::body Rappture::MapViewer::download {option args} {
648    switch $option {
649        coming {
650            if {[catch {
651                blt::winop snap $itk_component(plotarea) $_image(download)
652            }]} {
653                $_image(download) configure -width 1 -height 1
654                $_image(download) put #000000
655            }
656        }
657        controls {
658            set popup .mapviewerdownload
659            if { ![winfo exists .mapviewerdownload] } {
660                set inner [BuildDownloadPopup $popup [lindex $args 0]]
661            } else {
662                set inner [$popup component inner]
663            }
664            set _downloadPopup(image_controls) $inner.image_frame
665            set num [llength [get]]
666            set num [expr {($num == 1) ? "1 result" : "$num results"}]
667            set word [Rappture::filexfer::label downloadWord]
668            $inner.summary configure -text "$word $num in the following format:"
669            update idletasks            ;# Fix initial sizes
670            return $popup
671        }
672        now {
673            set popup .mapviewerdownload
674            if {[winfo exists .mapviewerdownload]} {
675                $popup deactivate
676            }
677            switch -- $_downloadPopup(format) {
678                "image" {
679                    return [$this GetImage [lindex $args 0]]
680                }
681            }
682            return ""
683        }
684        default {
685            error "bad option \"$option\": should be coming, controls, now"
686        }
687    }
688}
689
690# ----------------------------------------------------------------------
691# USAGE: Connect ?<host:port>,<host:port>...?
692#
693# Clients use this method to establish a connection to a new
694# server, or to reestablish a connection to the previous server.
695# Any existing connection is automatically closed.
696# ----------------------------------------------------------------------
697itcl::body Rappture::MapViewer::Connect {} {
698    global readyForNextFrame
699    set readyForNextFrame 1
700    set _hosts [GetServerList "geovis"]
701    if { "" == $_hosts } {
702        return 0
703    }
704    set result [VisViewer::Connect $_hosts]
705    if { $result } {
706        if { $_reportClientInfo }  {
707            # Tell the server the viewer, hub, user and session.
708            # Do this immediately on connect before buffering any commands
709            global env
710
711            set info {}
712            set user "???"
713            if { [info exists env(USER)] } {
714                set user $env(USER)
715            }
716            set session "???"
717            if { [info exists env(SESSION)] } {
718                set session $env(SESSION)
719            }
720            lappend info "hub" [exec hostname]
721            lappend info "client" "mapviewer"
722            lappend info "user" $user
723            lappend info "session" $session
724            SendCmd "clientinfo [list $info]"
725        }
726
727        set w [winfo width $itk_component(view)]
728        set h [winfo height $itk_component(view)]
729        EventuallyResize $w $h
730    }
731    return $result
732}
733
734#
735# isconnected --
736#
737#       Indicates if we are currently connected to the visualization server.
738#
739itcl::body Rappture::MapViewer::isconnected {} {
740    return [VisViewer::IsConnected]
741}
742
743#
744# disconnect --
745#
746itcl::body Rappture::MapViewer::disconnect {} {
747    Disconnect
748    set _reset 1
749}
750
751#
752# Disconnect --
753#
754#       Clients use this method to disconnect from the current rendering
755#       server.
756#
757itcl::body Rappture::MapViewer::Disconnect {} {
758    VisViewer::Disconnect
759
760    # disconnected -- no more data sitting on server
761    array unset _datasets
762    global readyForNextFrame
763    set readyForNextFrame 1
764}
765
766# ----------------------------------------------------------------------
767# USAGE: ReceiveImage -bytes <size> -type <type> -token <token>
768#
769# Invoked automatically whenever the "image" command comes in from
770# the rendering server.  Indicates that binary image data with the
771# specified <size> will follow.
772# ----------------------------------------------------------------------
773itcl::body Rappture::MapViewer::ReceiveImage { args } {
774    global readyForNextFrame
775    set readyForNextFrame 1
776    array set info {
777        -token "???"
778        -bytes 0
779        -type image
780    }
781    array set info $args
782    set bytes [ReceiveBytes $info(-bytes)]
783    if { $info(-type) == "image" } {
784        $_image(plot) configure -data $bytes
785    } elseif { $info(type) == "print" } {
786        set tag $this-print-$info(-token)
787        set _hardcopy($tag) $bytes
788    }
789}
790
791#
792# ReceiveDataset --
793#
794itcl::body Rappture::MapViewer::ReceiveDataset { args } {
795    if { ![isconnected] } {
796        return
797    }
798    set option [lindex $args 0]
799    switch -- $option {
800        "coords" {
801            foreach { x y z } [lrange $args 1 end] break
802            puts stderr "Coords: $x $y $z"
803        }
804        "scalar" {
805            set option [lindex $args 1]
806            switch -- $option {
807                "world" {
808                    foreach { x y z value tag } [lrange $args 2 end] break
809                }
810                "pixel" {
811                    foreach { x y value tag } [lrange $args 2 end] break
812                }
813            }
814        }
815        "vector" {
816            set option [lindex $args 1]
817            switch -- $option {
818                "world" {
819                    foreach { x y z vx vy vz tag } [lrange $args 2 end] break
820                }
821                "pixel" {
822                    foreach { x y vx vy vz tag } [lrange $args 2 end] break
823                }
824            }
825        }
826        "names" {
827            foreach { name } [lindex $args 1] {
828                #puts stderr "Dataset: $name"
829            }
830        }
831        default {
832            error "unknown dataset option \"$option\" from server"
833        }
834    }
835}
836
837# ----------------------------------------------------------------------
838# USAGE: Rebuild
839#
840# Called automatically whenever something changes that affects the
841# data in the widget.  Clears any existing data and rebuilds the
842# widget to display new data.
843# ----------------------------------------------------------------------
844itcl::body Rappture::MapViewer::Rebuild {} {
845
846    set w [winfo width $itk_component(view)]
847    set h [winfo height $itk_component(view)]
848    if { $w < 2 || $h < 2 } {
849        $_dispatcher event -idle !rebuild
850        return
851    }
852
853    # Turn on buffering of commands to the server.  We don't want to be
854    # preempted by a server disconnect/reconnect (which automatically
855    # generates a new call to Rebuild).
856    StartBufferingCommands
857
858    if { $_reset } {
859        set _width $w
860        set _height $h
861        $_arcball resize $w $h
862        DoResize
863
864        if { [info exists _mapsettings(type)] } {
865
866            # The map must be reset once before any layers are added This
867            # should not be done more than once as it is very expensive.
868
869            if { $_mapsettings(type) == "geocentric" } {
870                SendCmd "map reset geocentric"
871            } else {
872                if { $_mapsettings(extents) == ""} {
873                    SendCmd "map reset projected global-mercator"
874                } else {
875                    SendCmd \
876        "map reset projected $_mapsettings(projection) $_mapsettings(extents)"
877                }
878            }
879            if { $_haveTerrain } {
880                FixSettings terrain-edges terrain-lighting terrain-vertscale \
881                    terrain-wireframe
882            }
883            SendCmd "imgflush"
884        }
885    }
886
887    set _limits(zmin) ""
888    set _limits(zmax) ""
889    set _first ""
890    set count 0
891
892    foreach dataobj [get -objects] {
893        set _obj2datasets($dataobj) ""
894        foreach layer [$dataobj layers] {
895            array unset info
896            array set info [$dataobj layer $layer]
897            set tag $dataobj-$layer
898            if { ![info exists _datasets($tag)] } {
899                if { ![info exists info(url)] }  {
900                    continue
901                }
902                # FIXME: wms, tms layers have additional options
903                switch -- $info(type) {
904                    "raster" {
905                        set type "image"
906                    }
907                    default {
908                        set type "$info(type)"
909                    }
910                }
911                if { $_reportClientInfo }  {
912                    set cinfo {}
913                    lappend cinfo "tool_id"       [$dataobj hints toolId]
914                    lappend cinfo "tool_name"     [$dataobj hints toolName]
915                    lappend cinfo "tool_version"  [$dataobj hints toolRevision]
916                    lappend cinfo "tool_title"    [$dataobj hints toolTitle]
917                    lappend cinfo "dataset_label" [$dataobj hints label]
918                    lappend cinfo "dataset_tag"   $tag
919                    SendCmd [list "clientinfo" $cinfo]
920                }
921                SendCmd [list map layer add $type $info(url) $tag]
922                set _datasets($tag) 1
923                SetObjectStyle $dataobj $layer
924            }
925            lappend _obj2datasets($dataobj) $tag
926            if { [info exists _obj2ovride($dataobj-raise)] } {
927                SendCmd "map layer visible 1 $tag"
928                set _visibility($tag) 1
929                #SetLayerOpacity $tag
930            }
931        }
932    }
933    if {"" != $_first} {
934        set location [$_first hints camera]
935        if { $location != "" } {
936            array set view $location
937        }
938    }
939    if { $_reset } {
940        set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
941        $_arcball quaternion $q
942        SendCmd "camera reset"
943        DoRotate
944        PanCamera
945        Zoom reset
946    }
947    UpdateLayerControls
948    set _reset 0
949    global readyForNextFrame
950    set readyForNextFrame 0;            # Don't advance to the next frame
951                                        # until we get an image.
952
953    # Actually write the commands to the server socket.  If it fails, we
954    # don't care.  We're finished here.
955    blt::busy hold $itk_component(hull)
956    StopBufferingCommands
957    blt::busy release $itk_component(hull)
958}
959
960# ----------------------------------------------------------------------
961# USAGE: CurrentDatasets ?-all -visible? ?dataobjs?
962#
963# Returns a list of server IDs for the current datasets being displayed.
964# This is normally a single ID, but it might be a list of IDs if the
965# current data object has multiple components.
966# ----------------------------------------------------------------------
967itcl::body Rappture::MapViewer::CurrentDatasets {args} {
968    set flag [lindex $args 0]
969    switch -- $flag {
970        "-all" {
971            if { [llength $args] > 1 } {
972                error "CurrentDatasets: can't specify dataobj after \"-all\""
973            }
974            set dlist [get -objects]
975        }
976        "-visible" {
977            if { [llength $args] > 1 } {
978                set dlist {}
979                set args [lrange $args 1 end]
980                foreach dataobj $args {
981                    if { [info exists _obj2ovride($dataobj-raise)] } {
982                        lappend dlist $dataobj
983                    }
984                }
985            } else {
986                set dlist [get -visible]
987            }
988        }           
989        default {
990            set dlist $args
991        }
992    }
993    set rlist ""
994    foreach dataobj $dlist {
995        foreach layer [$dataobj layers] {
996            set tag $dataobj-$layer
997            if { [info exists _datasets($tag)] && $_datasets($tag) } {
998                lappend rlist $tag
999            }
1000        }
1001    }
1002    return $rlist
1003}
1004
1005itcl::body Rappture::MapViewer::KeyPress {k} {
1006    SendCmd "key press $k"
1007}
1008
1009itcl::body Rappture::MapViewer::KeyRelease {k} {
1010    SendCmd "key release $k"
1011}
1012
1013itcl::body Rappture::MapViewer::MouseClick {button x y} {
1014    if {0} {
1015    set w [winfo width $itk_component(view)]
1016    set h [winfo height $itk_component(view)]
1017    set x [expr {(2.0 * double($x)/$w) - 1.0}]
1018    set y [expr {(2.0 * double($y)/$h) - 1.0}]
1019    }
1020    SendCmd "mouse click $button $x $y"
1021}
1022
1023itcl::body Rappture::MapViewer::MouseDoubleClick {button x y} {
1024    if {0} {
1025    set w [winfo width $itk_component(view)]
1026    set h [winfo height $itk_component(view)]
1027    set x [expr {(2.0 * double($x)/$w) - 1.0}]
1028    set y [expr {(2.0 * double($y)/$h) - 1.0}]
1029    }
1030    SendCmd "mouse dblclick $button $x $y"
1031}
1032
1033itcl::body Rappture::MapViewer::MouseDrag {button x y} {
1034    if {0} {
1035    set w [winfo width $itk_component(view)]
1036    set h [winfo height $itk_component(view)]
1037    set x [expr {(2.0 * double($x)/$w) - 1.0}]
1038    set y [expr {(2.0 * double($y)/$h) - 1.0}]
1039    }
1040    SendCmd "mouse drag $button $x $y"
1041}
1042
1043itcl::body Rappture::MapViewer::MouseRelease {button x y} {
1044    if {0} {
1045    set w [winfo width $itk_component(view)]
1046    set h [winfo height $itk_component(view)]
1047    set x [expr {(2.0 * double($x)/$w) - 1.0}]
1048    set y [expr {(2.0 * double($y)/$h) - 1.0}]
1049    }
1050    SendCmd "mouse release $button $x $y"
1051}
1052
1053#
1054# EventuallyHandleMotionEvent --
1055#
1056#       This routine compresses (no button press) motion events.  It
1057#       delivers a server mouse command once every 100 milliseconds (if a
1058#       motion event is pending).
1059#
1060itcl::body Rappture::MapViewer::EventuallyHandleMotionEvent {x y} {
1061    set _motion(x) $x
1062    set _motion(y) $y
1063    if { !$_motion(pending) } {
1064        set _motion(pending) 1
1065        $_dispatcher event -after $_motion(delay) !motion
1066    }
1067}
1068
1069itcl::body Rappture::MapViewer::MouseMotion {} {
1070    if {0} {
1071    set w [winfo width $itk_component(view)]
1072    set h [winfo height $itk_component(view)]
1073    set x [expr {(2.0 * double($_motion(x))/$w) - 1.0}]
1074    set y [expr {(2.0 * double($_motion(y))/$h) - 1.0}]
1075    }
1076    SendCmd "mouse motion $_motion(x) $_motion(y)"
1077    set _motion(pending) 0
1078}
1079
1080itcl::body Rappture::MapViewer::MouseScroll {direction} {
1081    switch -- $direction {
1082        "up" {
1083            SendCmd "mouse scroll 1"
1084        }
1085        "down" {
1086            SendCmd "mouse scroll -1"
1087        }
1088    }
1089}
1090
1091# ----------------------------------------------------------------------
1092# USAGE: Zoom in
1093# USAGE: Zoom out
1094# USAGE: Zoom reset
1095#
1096# Called automatically when the user clicks on one of the zoom
1097# controls for this widget.  Changes the zoom for the current view.
1098# ----------------------------------------------------------------------
1099itcl::body Rappture::MapViewer::Zoom {option} {
1100    switch -- $option {
1101        "in" {
1102            set _view(zoom) [expr {$_view(zoom)*1.25}]
1103            #SendCmd "camera zoom $_view(zoom)"
1104            set z -0.25
1105            SendCmd "camera zoom $z"
1106        }
1107        "out" {
1108            set _view(zoom) [expr {$_view(zoom)*0.8}]
1109            #SendCmd "camera zoom $_view(zoom)"
1110            set z 0.25
1111            SendCmd "camera zoom $z"
1112        }
1113        "reset" {
1114            array set _view {
1115                qw      1.0
1116                qx      0.0
1117                qy      0.0
1118                qz      0.0
1119                zoom    1.0
1120                xpan    0.0
1121                ypan    0.0
1122            }
1123            if { $_first != "" } {
1124                set location [$_first hints camera]
1125                if { $location != "" } {
1126                    array set _view $location
1127                }
1128            }
1129            set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
1130            $_arcball quaternion $q
1131            DoRotate
1132            SendCmd "camera reset"
1133        }
1134    }
1135}
1136
1137itcl::body Rappture::MapViewer::PanCamera {} {
1138    set x $_view(xpan)
1139    set y $_view(ypan)
1140    SendCmd "camera pan $x $y"
1141}
1142
1143# ----------------------------------------------------------------------
1144# USAGE: Rotate click <x> <y>
1145# USAGE: Rotate drag <x> <y>
1146# USAGE: Rotate release <x> <y>
1147#
1148# Called automatically when the user clicks/drags/releases in the
1149# plot area.  Moves the plot according to the user's actions.
1150# ----------------------------------------------------------------------
1151itcl::body Rappture::MapViewer::Rotate {option x y} {
1152    switch -- $option {
1153        "click" {
1154            $itk_component(view) configure -cursor fleur
1155            set _click(x) $x
1156            set _click(y) $y
1157        }
1158        "drag" {
1159            if {[array size _click] == 0} {
1160                Rotate click $x $y
1161            } else {
1162                set w [winfo width $itk_component(view)]
1163                set h [winfo height $itk_component(view)]
1164                if {$w <= 0 || $h <= 0} {
1165                    return
1166                }
1167
1168                if {[catch {
1169                    # this fails sometimes for no apparent reason
1170                    set dx [expr {double($x-$_click(x))/$w}]
1171                    set dy [expr {double($y-$_click(y))/$h}]
1172                }]} {
1173                    return
1174                }
1175                if { $dx == 0 && $dy == 0 } {
1176                    return
1177                }
1178                set q [$_arcball rotate $x $y $_click(x) $_click(y)]
1179                EventuallyRotate $q
1180                set _click(x) $x
1181                set _click(y) $y
1182            }
1183        }
1184        "release" {
1185            Rotate drag $x $y
1186            $itk_component(view) configure -cursor ""
1187            catch {unset _click}
1188        }
1189        default {
1190            error "bad option \"$option\": should be click, drag, release"
1191        }
1192    }
1193}
1194
1195itcl::body Rappture::MapViewer::Pick {x y} {
1196    foreach tag [CurrentDatasets -visible] {
1197        SendCmd "dataset getscalar pixel $x $y $tag"
1198    }
1199}
1200
1201# ----------------------------------------------------------------------
1202# USAGE: $this Pan click x y
1203#        $this Pan drag x y
1204#        $this Pan release x y
1205#
1206# Called automatically when the user clicks on one of the zoom
1207# controls for this widget.  Changes the zoom for the current view.
1208# ----------------------------------------------------------------------
1209itcl::body Rappture::MapViewer::Pan {option x y} {
1210    switch -- $option {
1211        "set" {
1212            set w [winfo width $itk_component(view)]
1213            set h [winfo height $itk_component(view)]
1214            set x [expr $x / double($w)]
1215            set y [expr $y / double($h)]
1216            set _view(xpan) [expr $_view(xpan) + $x]
1217            set _view(ypan) [expr $_view(ypan) + $y]
1218            PanCamera
1219            return
1220        }
1221        "click" {
1222            set _click(x) $x
1223            set _click(y) $y
1224            $itk_component(view) configure -cursor hand1
1225        }
1226        "drag" {
1227            if { ![info exists _click(x)] } {
1228                set _click(x) $x
1229            }
1230            if { ![info exists _click(y)] } {
1231                set _click(y) $y
1232            }
1233            set w [winfo width $itk_component(view)]
1234            set h [winfo height $itk_component(view)]
1235            set dx [expr ($_click(x) - $x)/double($w)]
1236            set dy [expr ($_click(y) - $y)/double($h)]
1237            set _click(x) $x
1238            set _click(y) $y
1239            set _view(xpan) [expr $_view(xpan) - $dx]
1240            set _view(ypan) [expr $_view(ypan) - $dy]
1241            PanCamera
1242        }
1243        "release" {
1244            Pan drag $x $y
1245            $itk_component(view) configure -cursor ""
1246        }
1247        default {
1248            error "unknown option \"$option\": should set, click, drag, or release"
1249        }
1250    }
1251}
1252
1253# ----------------------------------------------------------------------
1254# USAGE: FixSettings <what> ?<value>?
1255#
1256# Used internally to update rendering settings whenever parameters
1257# change in the popup settings panel.  Sends the new settings off
1258# to the back end.
1259# ----------------------------------------------------------------------
1260itcl::body Rappture::MapViewer::FixSettings { args } {
1261    foreach setting $args {
1262        AdjustSetting $setting
1263    }
1264}
1265
1266#
1267# AdjustSetting --
1268#
1269#       Changes/updates a specific setting in the widget.  There are
1270#       usually user-setable option.  Commands are sent to the render
1271#       server.
1272#
1273itcl::body Rappture::MapViewer::AdjustSetting {what {value ""}} {
1274    if { ![isconnected] } {
1275        return
1276    }
1277    switch -- $what {
1278        "terrain-edges" {
1279            set bool $_settings(terrain-edges)
1280            SendCmd "map terrain edges $bool"
1281        }
1282        "terrain-lighting" {
1283            set bool $_settings(terrain-lighting)
1284            SendCmd "map terrain lighting $bool"
1285        }
1286        "terrain-palette" {
1287            set cmap [$itk_component(terrainpalette) value]
1288            #SendCmd "map terrain colormap $cmap"
1289        }
1290        "terrain-vertscale" {
1291            set val $_settings(terrain-vertscale)
1292            SendCmd "map terrain vertscale $val"
1293        }
1294        "terrain-wireframe" {
1295            set bool $_settings(terrain-wireframe)
1296            SendCmd "map terrain wireframe $bool"
1297        }
1298        default {
1299            error "don't know how to fix $what"
1300        }
1301    }
1302}
1303
1304# ----------------------------------------------------------------------
1305# CONFIGURATION OPTION: -plotbackground
1306# ----------------------------------------------------------------------
1307itcl::configbody Rappture::MapViewer::plotbackground {
1308    if { [isconnected] } {
1309        foreach {r g b} [Color2RGB $itk_option(-plotbackground)] break
1310        SendCmd "screen bgcolor $r $g $b"
1311    }
1312}
1313
1314# ----------------------------------------------------------------------
1315# CONFIGURATION OPTION: -plotforeground
1316# ----------------------------------------------------------------------
1317itcl::configbody Rappture::MapViewer::plotforeground {
1318    if { [isconnected] } {
1319        foreach {r g b} [Color2RGB $itk_option(-plotforeground)] break
1320        #fix this!
1321        #SendCmd "color background $r $g $b"
1322    }
1323}
1324
1325itcl::body Rappture::MapViewer::limits { dataobj } {
1326    error "no limits"
1327    foreach layer [$dataobj layers] {
1328        set tag $dataobj-$layer
1329
1330        foreach { xMin xMax yMin yMax zMin zMax} $_limits($tag) break
1331        if {![info exists limits(xmin)] || $limits(xmin) > $xMin} {
1332            set limits(xmin) $xMin
1333        }
1334        if {![info exists limits(xmax)] || $limits(xmax) < $xMax} {
1335            set limits(xmax) $xMax
1336        }
1337        if {![info exists limits(ymin)] || $limits(ymin) > $yMin} {
1338            set limits(ymin) $xMin
1339        }
1340        if {![info exists limits(ymax)] || $limits(ymax) < $yMax} {
1341            set limits(ymax) $yMax
1342        }
1343        if {![info exists limits(zmin)] || $limits(zmin) > $zMin} {
1344            set limits(zmin) $zMin
1345        }
1346        if {![info exists limits(zmax)] || $limits(zmax) < $zMax} {
1347            set limits(zmax) $zMax
1348        }
1349    }
1350    return [array get limits]
1351}
1352
1353itcl::body Rappture::MapViewer::BuildTerrainTab {} {
1354
1355    set fg [option get $itk_component(hull) font Font]
1356    #set bfg [option get $itk_component(hull) boldFont Font]
1357
1358    set inner [$itk_component(main) insert end \
1359        -title "Terrain Settings" \
1360        -icon [Rappture::icon mesh]]
1361    $inner configure -borderwidth 4
1362
1363    checkbutton $inner.wireframe \
1364        -text "Show Wireframe" \
1365        -variable [itcl::scope _settings(terrain-wireframe)] \
1366        -command [itcl::code $this AdjustSetting terrain-wireframe] \
1367        -font "Arial 9" -anchor w
1368
1369    checkbutton $inner.lighting \
1370        -text "Enable Lighting" \
1371        -variable [itcl::scope _settings(terrain-lighting)] \
1372        -command [itcl::code $this AdjustSetting terrain-lighting] \
1373        -font "Arial 9" -anchor w
1374
1375    checkbutton $inner.edges \
1376        -text "Show Edges" \
1377        -variable [itcl::scope _settings(terrain-edges)] \
1378        -command [itcl::code $this AdjustSetting terrain-edges] \
1379        -font "Arial 9" -anchor w
1380
1381    label $inner.palette_l -text "Palette" -font "Arial 9" -anchor w
1382    itk_component add terrainpalette {
1383        Rappture::Combobox $inner.palette -width 10 -editable no
1384    }
1385    $inner.palette choices insert end \
1386        "BCGYR"              "BCGYR"            \
1387        "BGYOR"              "BGYOR"            \
1388        "blue"               "blue"             \
1389        "blue-to-brown"      "blue-to-brown"    \
1390        "blue-to-orange"     "blue-to-orange"   \
1391        "blue-to-grey"       "blue-to-grey"     \
1392        "green-to-magenta"   "green-to-magenta" \
1393        "greyscale"          "greyscale"        \
1394        "nanohub"            "nanohub"          \
1395        "rainbow"            "rainbow"          \
1396        "spectral"           "spectral"         \
1397        "ROYGB"              "ROYGB"            \
1398        "RYGCB"              "RYGCB"            \
1399        "brown-to-blue"      "brown-to-blue"    \
1400        "grey-to-blue"       "grey-to-blue"     \
1401        "orange-to-blue"     "orange-to-blue"   
1402
1403    $itk_component(terrainpalette) value "BCGYR"
1404    bind $inner.palette <<Value>> \
1405        [itcl::code $this AdjustSetting terrain-palette]
1406
1407    label $inner.vscale_l -text "Vertical Scale" -font "Arial 9" -anchor w
1408    ::scale $inner.vscale -from 0 -to 100 -orient horizontal \
1409        -variable [itcl::scope _settings(terrain-vertscale)] \
1410        -width 10 \
1411        -showvalue off \
1412        -command [itcl::code $this AdjustSetting terrain-vertscale]
1413    $inner.vscale set $_settings(terrain-vertscale)
1414
1415    blt::table $inner \
1416        0,0 $inner.wireframe -cspan 2  -anchor w -pady 2 \
1417        1,0 $inner.lighting  -cspan 2  -anchor w -pady 2 \
1418        2,0 $inner.edges     -cspan 2  -anchor w -pady 2 \
1419        4,0 $inner.vscale_l  -anchor w -pady 2 \
1420        4,1 $inner.vscale    -fill x   -pady 2 \
1421        5,0 $inner.palette_l -anchor w -pady 2 \
1422        5,1 $inner.palette   -fill x   -pady 2 
1423
1424    blt::table configure $inner r* c* -resize none
1425    blt::table configure $inner r7 c1 -resize expand
1426}
1427
1428itcl::body Rappture::MapViewer::BuildLayerTab {} {
1429
1430    set fg [option get $itk_component(hull) font Font]
1431    #set bfg [option get $itk_component(hull) boldFont Font]
1432
1433    set inner [$itk_component(main) insert end \
1434        -title "Layers" \
1435        -icon [Rappture::icon wrench]]
1436    $inner configure -borderwidth 4
1437    set f [frame $inner.layers]
1438    blt::table $inner \
1439        0,0 $f -fill both
1440    set _layers $inner
1441}
1442
1443itcl::body Rappture::MapViewer::BuildCameraTab {} {
1444    set inner [$itk_component(main) insert end \
1445        -title "Camera Settings" \
1446        -icon [Rappture::icon camera]]
1447    $inner configure -borderwidth 4
1448
1449    label $inner.view_l -text "view" -font "Arial 9"
1450    set f [frame $inner.view]
1451    foreach side { front back left right top bottom } {
1452        button $f.$side  -image [Rappture::icon view$side] \
1453            -command [itcl::code $this SetOrientation $side]
1454        Rappture::Tooltip::for $f.$side "Change the view to $side"
1455        pack $f.$side -side left
1456    }
1457
1458    blt::table $inner \
1459        0,0 $inner.view_l -anchor e -pady 2 \
1460        0,1 $inner.view -anchor w -pady 2
1461
1462    set labels { qx qy qz qw xpan ypan zoom }
1463    set row 1
1464    foreach tag $labels {
1465        label $inner.${tag}label -text $tag -font "Arial 9"
1466        entry $inner.${tag} -font "Arial 9"  -bg white \
1467            -textvariable [itcl::scope _view($tag)]
1468        bind $inner.${tag} <KeyPress-Return> \
1469            [itcl::code $this camera set ${tag}]
1470        blt::table $inner \
1471            $row,0 $inner.${tag}label -anchor e -pady 2 \
1472            $row,1 $inner.${tag} -anchor w -pady 2
1473        blt::table configure $inner r$row -resize none
1474        incr row
1475    }
1476
1477    blt::table configure $inner c* r* -resize none
1478    blt::table configure $inner c2 -resize expand
1479    blt::table configure $inner r$row -resize expand
1480}
1481
1482#
1483#  camera --
1484#
1485itcl::body Rappture::MapViewer::camera {option args} {
1486    switch -- $option {
1487        "show" {
1488            puts [array get _view]
1489        }
1490        "set" {
1491            set who [lindex $args 0]
1492            set x $_view($who)
1493            set code [catch { string is double $x } result]
1494            if { $code != 0 || !$result } {
1495                return
1496            }
1497            switch -- $who {
1498                "xpan" - "ypan" {
1499                    PanCamera
1500                }
1501                "qx" - "qy" - "qz" - "qw" {
1502                    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
1503                    $_arcball quaternion $q
1504                    EventuallyRotate $q
1505                }
1506                "zoom" {
1507                    SendCmd "camera zoom $_view(zoom)"
1508                }
1509            }
1510        }
1511    }
1512}
1513
1514itcl::body Rappture::MapViewer::GetImage { args } {
1515    if { [image width $_image(download)] > 0 &&
1516         [image height $_image(download)] > 0 } {
1517        set bytes [$_image(download) data -format "jpeg -quality 100"]
1518        set bytes [Rappture::encoding::decode -as b64 $bytes]
1519        return [list .jpg $bytes]
1520    }
1521    return ""
1522}
1523
1524itcl::body Rappture::MapViewer::BuildDownloadPopup { popup command } {
1525    Rappture::Balloon $popup \
1526        -title "[Rappture::filexfer::label downloadWord] as..."
1527    set inner [$popup component inner]
1528    label $inner.summary -text "" -anchor w
1529
1530    radiobutton $inner.image_button -text "Image File" \
1531        -variable [itcl::scope _downloadPopup(format)] \
1532        -value image
1533    Rappture::Tooltip::for $inner.image_button \
1534        "Save as digital image."
1535
1536    button $inner.ok -text "Save" \
1537        -highlightthickness 0 -pady 2 -padx 3 \
1538        -command $command \
1539        -compound left \
1540        -image [Rappture::icon download]
1541
1542    button $inner.cancel -text "Cancel" \
1543        -highlightthickness 0 -pady 2 -padx 3 \
1544        -command [list $popup deactivate] \
1545        -compound left \
1546        -image [Rappture::icon cancel]
1547
1548    blt::table $inner \
1549        0,0 $inner.summary -cspan 2  \
1550        2,0 $inner.image_button -anchor w -cspan 2 -padx { 4 0 } \
1551        4,1 $inner.cancel -width .9i -fill y \
1552        4,0 $inner.ok -padx 2 -width .9i -fill y
1553    blt::table configure $inner r3 -height 4
1554    blt::table configure $inner r4 -pady 4
1555    raise $inner.image_button
1556    $inner.image_button invoke
1557    return $inner
1558}
1559
1560itcl::body Rappture::MapViewer::SetObjectStyle { dataobj layer } {
1561    set tag $dataobj-$layer
1562    set _visibility($tag) 1
1563}
1564
1565itcl::body Rappture::MapViewer::SetOrientation { side } {
1566    array set positions {
1567        front "1 0 0 0"
1568        back  "0 0 1 0"
1569        left  "0.707107 0 -0.707107 0"
1570        right "0.707107 0 0.707107 0"
1571        top   "0.707107 -0.707107 0 0"
1572        bottom "0.707107 0.707107 0 0"
1573    }
1574    foreach name { qw qx qy qz } value $positions($side) {
1575        set _view($name) $value
1576    }
1577    set q [list $_view(qw) $_view(qx) $_view(qy) $_view(qz)]
1578    $_arcball quaternion $q
1579    SendCmd "camera orient $q"
1580    #SendCmd "camera reset"
1581    set _view(xpan) 0
1582    set _view(ypan) 0
1583    set _view(zoom) 1.0
1584}
1585
1586itcl::body Rappture::MapViewer::SetOpacity { dataset } {
1587    foreach {dataobj layer} [split $dataset -] break
1588    set type [$dataobj type $layer]
1589    set val $_settings(-opacity)
1590    set sval [expr { 0.01 * double($val) }]
1591    if { !$_obj2ovride($dataobj-raise) } {
1592        # This is wrong.  Need to figure out why raise isn't set with 1
1593        #set sval [expr $sval * .6]
1594    }
1595    SendCmd "$type opacity $sval $dataset"
1596}
1597
1598itcl::body Rappture::MapViewer::ChangeLayerVisibility { dataobj layer } {
1599    set tag $dataobj-$layer
1600    set bool $_visibility($tag)
1601    SendCmd "map layer visible $bool $tag"
1602}
1603
1604itcl::body Rappture::MapViewer::UpdateLayerControls {} {
1605    set row 0
1606    set inner $_layers
1607    if { [winfo exists $inner.layers] } {
1608        foreach w [winfo children $inner.layers] {
1609            destroy $w
1610        }
1611    }
1612    set f $inner.layers
1613    foreach dataobj [get -objects] {
1614        foreach name [$dataobj layers] {
1615            array unset info
1616            array set info [$dataobj layer $name]
1617            set tag $dataobj-$name
1618            set w [string range $dataobj$name 2 end]
1619            checkbutton $f.$w \
1620                -text $info(title) \
1621                -variable [itcl::scope _visibility($tag)] \
1622                -command [itcl::code $this \
1623                              ChangeLayerVisibility $dataobj $name] \
1624                    -font "Arial 9" -anchor w
1625            blt::table $f $row,0 $f.$w -anchor w -pady 2
1626            Rappture::Tooltip::for $f.$w $info(description)
1627            incr row
1628        }
1629    }
1630    if { $row > 0 } {
1631        blt::table configure $f r* c* -resize none
1632        blt::table configure $f r$row c1 -resize expand
1633    }
1634}
Note: See TracBrowser for help on using the repository browser.