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

Last change on this file since 4287 was 4287, checked in by ldelgass, 10 years ago

Add option to toggle camera throw

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