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

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

Fix for extents (should be 4 args, not a list)

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