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

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

Initial test of selection box

File size: 71.0 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: mapviewer - Map object viewer
4#
5#  It connects to the GeoVis server running on a rendering farm,
6#  transmits data, and displays the results.
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
9#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
10#
11#  See the file "license.terms" for information on usage and
12#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13# ======================================================================
14package require Itk
15package require BLT
16#package require Img
17
18option add *MapViewer.width 4i widgetDefault
19option add *MapViewer*cursor crosshair widgetDefault
20option add *MapViewer.height 4i widgetDefault
21option add *MapViewer.foreground black widgetDefault
22option add *MapViewer.controlBackground gray widgetDefault
23option add *MapViewer.controlDarkBackground #999999 widgetDefault
24option add *MapViewer.plotBackground black widgetDefault
25option add *MapViewer.plotForeground white widgetDefault
26option add *MapViewer.font \
27    -*-helvetica-medium-r-normal-*-12-* widgetDefault
28
29# must use this name -- plugs into Rappture::resources::load
30proc MapViewer_init_resources {} {
31    Rappture::resources::register \
32        geovis_server Rappture::MapViewer::SetServerList
33}
34
35itcl::class Rappture::MapViewer {
36    inherit Rappture::VisViewer
37
38    itk_option define -plotforeground plotForeground Foreground ""
39    itk_option define -plotbackground plotBackground Background ""
40
41    private variable _layersFrame "";   # Name of layers frame widget
42    private variable _mapsettings;      # Global map settings
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 AdjustSetting {what {value ""}}
69    protected method Connect {}
70    protected method CurrentLayers {args}
71    protected method Disconnect {}
72    protected method DoPan {}
73    protected method DoResize {}
74    protected method DoRotate {}
75    protected method InitSettings { 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 Pin {option x y}
86    protected method Rebuild {}
87    protected method ReceiveMapInfo { args }
88    protected method ReceiveScreenInfo { args }
89    protected method ReceiveImage { args }
90    protected method Rotate {option x y}
91    protected method Select {option x y}
92    protected method Zoom {option {x 0} {y 0}}
93
94    # The following methods are only used by this class.
95    private method BuildCameraTab {}
96    private method BuildDownloadPopup { widget command }
97    private method BuildLayerTab {}
98    private method BuildTerrainTab {}
99    private method ChangeLayerVisibility { dataobj layer }
100    private method EventuallyHandleMotionEvent { x y }
101    private method EventuallyPan { dx dy }
102    private method EventuallyResize { w h }
103    private method EventuallyRotate { dx dy }
104    private method GetImage { args }
105    private method GetNormalizedMouse { x y }
106    private method MapIsGeocentric {}
107    private method SetLayerStyle { dataobj layer }
108    private method SetTerrainStyle { style }
109    private method SetOpacity { dataset }
110    private method UpdateLayerControls {}
111    private method EarthFile {}
112
113    private variable _dlist "";         # list of data objects
114    private variable _obj2datasets
115    private variable _obj2ovride;       # maps dataobj => style override
116    private variable _layers;           # Contains the names of all the
117                                        # layer in the server.
118    private variable _click;            # info used for rotate operations
119    private variable _view;             # view params for 3D view
120    private variable _pan;
121    private variable _rotate;
122    private variable _motion;
123    private variable _settings
124    private variable _visibility
125    private variable _style;            # Array of current component styles.
126    private variable _initialStyle;     # Array of initial component styles.
127    private variable _reset 1;          # Indicates that server was reset and
128                                        # needs to be reinitialized.
129    private variable _initCamera 1;
130    private variable _haveTerrain 0;
131
132    private variable _first ""     ;# This is the topmost dataset.
133    private variable _start 0
134    private variable _title ""
135
136    common _downloadPopup          ;# download options from popup
137    private common _hardcopy
138    private variable _width 0
139    private variable _height 0
140    private variable _resizePending 0
141    private variable _sendEarthFile 0
142    private variable _useServerManip 0
143    private variable _labelCount 0
144    private variable _b1mode "pan"
145}
146
147itk::usual MapViewer {
148    keep -background -foreground -cursor -font
149    keep -plotbackground -plotforeground
150}
151
152# ----------------------------------------------------------------------
153# CONSTRUCTOR
154# ----------------------------------------------------------------------
155itcl::body Rappture::MapViewer::constructor {hostlist args} {
156    set _serverType "geovis"
157
158    if { [catch {
159       
160    # Rebuild event
161    $_dispatcher register !rebuild
162    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
163
164    # Resize event
165    $_dispatcher register !resize
166    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
167
168    # Pan event
169    $_dispatcher register !pan
170    $_dispatcher dispatch $this !pan "[itcl::code $this DoPan]; list"
171
172    # Rotate event
173    $_dispatcher register !rotate
174    $_dispatcher dispatch $this !rotate "[itcl::code $this DoRotate]; list"
175
176    # <Motion> event
177    $_dispatcher register !motion
178    $_dispatcher dispatch $this !motion "[itcl::code $this MouseMotion]; list"
179
180    #
181    # Populate parser with commands handle incoming requests
182    #
183    $_parser alias image    [itcl::code $this ReceiveImage]
184    $_parser alias map      [itcl::code $this ReceiveMapInfo]
185    $_parser alias camera   [itcl::code $this camera]
186    $_parser alias screen   [itcl::code $this ReceiveScreenInfo]
187
188    # Settings for mouse motion events: these are required
189    # to update the Lat/Long coordinate display
190    array set _motion {
191        compress        1
192        delay           100
193        enable          1
194        pending         0
195        x               0
196        y               0
197    }
198    array set _pan {
199        compress        1
200        delay           100
201        pending         0
202        x               0
203        y               0
204    }
205    array set _rotate {
206        azimuth         0
207        compress        1
208        delay           100
209        elevation       0
210        pending         0
211    }
212    # This array holds the Viewpoint parameters that the
213    # server sends on "camera get".
214    array set _view {
215        distance        1.0
216        heading         0.0
217        pitch           -89.9
218        srs             ""
219        verticalDatum   ""
220        x               0.0
221        y               0.0
222        z               0.0
223    }
224
225    # Note: grid types are "geodetic", "utm" and "mgrs"
226    # Currently only work in geocentric maps
227    array set _settings [subst {
228        camera-throw           0
229        coords-precision       5
230        coords-units           "latlong_decimal_degrees"
231        coords-visible         1
232        grid                   0
233        grid-type              "geodetic"
234        legend                 1
235        terrain-edges          0
236        terrain-lighting       0
237        terrain-vertscale      1.0
238        terrain-wireframe      0
239    }]
240    itk_component add view {
241        canvas $itk_component(plotarea).view \
242            -highlightthickness 0 -borderwidth 0
243    } {
244        usual
245        ignore -highlightthickness -borderwidth  -background
246    }
247
248    set c $itk_component(view)
249    #bind $c <KeyPress-Left>  [list %W xview scroll 10 units]
250    #bind $c <KeyPress-Right> [list %W xview scroll -10 units]
251    #bind $c <KeyPress-Up>    [list %W yview scroll 10 units]
252    #bind $c <KeyPress-Down>  [list %W yview scroll -10 units]
253    bind $c <Enter> "focus %W"
254    bind $c <Control-F1> [itcl::code $this ToggleConsole]
255
256    # Fix the scrollregion in case we go off screen
257    $c configure -scrollregion [$c bbox all]
258
259    set _map(id) [$c create image 0 0 -anchor nw -image $_image(plot)]
260    set _map(cwidth) -1
261    set _map(cheight) -1
262    set _map(zoom) 1.0
263    set _map(original) ""
264
265    set f [$itk_component(main) component controls]
266    itk_component add reset {
267        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
268            -highlightthickness 0 \
269            -image [Rappture::icon reset-view] \
270            -command [itcl::code $this camera reset]
271    } {
272        usual
273        ignore -highlightthickness
274    }
275    pack $itk_component(reset) -side top -padx 2 -pady 2
276    Rappture::Tooltip::for $itk_component(reset) \
277        "Reset the view to the default zoom level"
278
279    itk_component add zoomin {
280        button $f.zin -borderwidth 1 -padx 1 -pady 1 \
281            -highlightthickness 0 \
282            -image [Rappture::icon zoom-in] \
283            -command [itcl::code $this Zoom in]
284    } {
285        usual
286        ignore -highlightthickness
287    }
288    pack $itk_component(zoomin) -side top -padx 2 -pady 2
289    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
290
291    itk_component add zoomout {
292        button $f.zout -borderwidth 1 -padx 1 -pady 1 \
293            -highlightthickness 0 \
294            -image [Rappture::icon zoom-out] \
295            -command [itcl::code $this Zoom out]
296    } {
297        usual
298        ignore -highlightthickness
299    }
300    pack $itk_component(zoomout) -side top -padx 2 -pady 2
301    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
302
303    BuildLayerTab
304    BuildTerrainTab
305    BuildCameraTab
306
307    # Legend
308
309    set _image(legend) [image create photo]
310    itk_component add legend {
311        canvas $itk_component(plotarea).legend -width 50 -highlightthickness 0
312    } {
313        usual
314        ignore -highlightthickness
315        rename -background -plotbackground plotBackground Background
316    }
317
318    # Hack around the Tk panewindow.  The problem is that the requested
319    # size of the 3d view isn't set until an image is retrieved from
320    # the server.  So the panewindow uses the tiny size.
321    set w 10000
322    pack forget $itk_component(view)
323    blt::table $itk_component(plotarea) \
324        0,0 $itk_component(view) -fill both -reqwidth $w
325    blt::table configure $itk_component(plotarea) c1 -resize none
326
327    bind $itk_component(view) <Configure> \
328        [itcl::code $this EventuallyResize %w %h]
329
330    if {$_useServerManip} {
331        # Bindings for keyboard events
332        bind $itk_component(view) <KeyPress> \
333            [itcl::code $this KeyPress %N]
334        bind $itk_component(view) <KeyRelease> \
335            [itcl::code $this KeyRelease %N]
336
337        # Bindings for rotation via mouse
338        bind $itk_component(view) <ButtonPress-1> \
339            [itcl::code $this MouseClick 1 %x %y]
340        bind $itk_component(view) <Double-1> \
341            [itcl::code $this MouseDoubleClick 1 %x %y]
342        bind $itk_component(view) <B1-Motion> \
343            [itcl::code $this MouseDrag 1 %x %y]
344        bind $itk_component(view) <ButtonRelease-1> \
345            [itcl::code $this MouseRelease 1 %x %y]
346
347        # Bindings for panning via mouse
348        bind $itk_component(view) <ButtonPress-2> \
349            [itcl::code $this MouseClick 2 %x %y]
350        bind $itk_component(view) <Double-2> \
351            [itcl::code $this MouseDoubleClick 2 %x %y]
352        bind $itk_component(view) <B2-Motion> \
353            [itcl::code $this MouseDrag 2 %x %y]
354        bind $itk_component(view) <ButtonRelease-2> \
355            [itcl::code $this MouseRelease 2 %x %y]
356
357        bind $itk_component(view) <ButtonPress-3> \
358            [itcl::code $this MouseClick 3 %x %y]
359        bind $itk_component(view) <Double-3> \
360            [itcl::code $this MouseDoubleClick 3 %x %y]
361        bind $itk_component(view) <B3-Motion> \
362            [itcl::code $this MouseDrag 3 %x %y]
363        bind $itk_component(view) <ButtonRelease-3> \
364            [itcl::code $this MouseRelease 3 %x %y]
365
366        # Binding for mouse motion events
367        if {$_motion(enable)} {
368            bind $itk_component(view) <Motion> \
369                [itcl::code $this EventuallyHandleMotionEvent %x %y]
370        }
371    } else {
372        # Bindings for panning via mouse
373        bind $itk_component(view) <ButtonPress-1> \
374            [itcl::code $this Pan click %x %y]
375        bind $itk_component(view) <B1-Motion> \
376            [itcl::code $this Pan drag %x %y]
377        bind $itk_component(view) <ButtonRelease-1> \
378            [itcl::code $this Pan release %x %y]
379        bind $itk_component(view) <Button-1> \
380            +[itcl::code $this SendCmd "map setpos %x %y"]
381        bind $itk_component(view) <Double-1> \
382            [itcl::code $this camera go %x %y 0.4]
383
384        # Pin placemark annotations
385        bind $itk_component(view) <Control-ButtonPress-1> \
386            +[itcl::code $this Pin add %x %y]
387        bind $itk_component(view) <Control-ButtonPress-3> \
388            +[itcl::code $this Pin delete %x %y]
389
390        bind $itk_component(view) <Shift-ButtonPress-1> \
391            [itcl::code $this Select click %x %y]
392        bind $itk_component(view) <B1-Motion> \
393            +[itcl::code $this Select drag %x %y]
394        bind $itk_component(view) <Shift-ButtonRelease-1> \
395            +[itcl::code $this Select release %x %y]
396
397        if {1} {
398        # Bindings for rotation via mouse
399        bind $itk_component(view) <ButtonPress-2> \
400            [itcl::code $this Rotate click %x %y]
401        bind $itk_component(view) <B2-Motion> \
402            [itcl::code $this Rotate drag %x %y]
403        bind $itk_component(view) <ButtonRelease-2> \
404            [itcl::code $this Rotate release %x %y]
405        }
406
407        # Bindings for zoom via mouse
408        bind $itk_component(view) <ButtonPress-3> \
409            [itcl::code $this Zoom click %x %y]
410        bind $itk_component(view) <B3-Motion> \
411            [itcl::code $this Zoom drag %x %y]
412        bind $itk_component(view) <ButtonRelease-3> \
413            [itcl::code $this Zoom release %x %y]
414        bind $itk_component(view) <Double-3> \
415            [itcl::code $this camera go %x %y 2.5]
416        bind $itk_component(view) <Double-3> \
417            +[itcl::code $this SendCmd "map setpos %x %y"]
418
419        # Bindings for panning via keyboard
420        bind $itk_component(view) <KeyPress-Left> \
421            [itcl::code $this Pan set 10 0]
422        bind $itk_component(view) <KeyPress-Right> \
423            [itcl::code $this Pan set -10 0]
424        bind $itk_component(view) <KeyPress-Up> \
425            [itcl::code $this Pan set 0 -10]
426        bind $itk_component(view) <KeyPress-Down> \
427            [itcl::code $this Pan set 0 10]
428
429        # Binding for mouse motion events
430        set _motion(compress) 1
431        if {$_motion(enable)} {
432            bind $itk_component(view) <Motion> \
433                [itcl::code $this EventuallyHandleMotionEvent %x %y]
434        }
435        #bind $itk_component(view) <Motion> \
436        #    +[itcl::code $this SendCmd "map pin hover %x %y"]
437    }
438
439    bind $itk_component(view) <Shift-KeyPress-Left> \
440        [itcl::code $this Pan set 2 0]
441    bind $itk_component(view) <Shift-KeyPress-Right> \
442        [itcl::code $this Pan set -2 0]
443    bind $itk_component(view) <Shift-KeyPress-Up> \
444        [itcl::code $this Pan set 0 -2]
445    bind $itk_component(view) <Shift-KeyPress-Down> \
446        [itcl::code $this Pan set 0 2]
447
448    # Bindings for zoom via keyboard
449    bind $itk_component(view) <KeyPress-Prior> \
450        [itcl::code $this Zoom out]
451    bind $itk_component(view) <KeyPress-Next> \
452        [itcl::code $this Zoom in]
453
454    bind $itk_component(view) <Enter> "focus $itk_component(view)"
455
456    if {[string equal "x11" [tk windowingsystem]]} {
457        # Bindings for zoom via mouse
458        if {$_useServerManip} {
459            bind $itk_component(view) <4> [itcl::code $this MouseScroll up]
460            bind $itk_component(view) <5> [itcl::code $this MouseScroll down]
461        } else {
462            bind $itk_component(view) <4> [itcl::code $this Zoom out]
463            bind $itk_component(view) <5> [itcl::code $this Zoom in]
464        }
465    }
466
467    set _image(download) [image create photo]
468
469    eval itk_initialize $args
470    Connect
471} errs] != 0 } {
472        puts stderr errs=$errs
473    }
474}
475
476# ----------------------------------------------------------------------
477# DESTRUCTOR
478# ----------------------------------------------------------------------
479itcl::body Rappture::MapViewer::destructor {} {
480    Disconnect
481    $_dispatcher cancel !rebuild
482    $_dispatcher cancel !resize
483    $_dispatcher cancel !rotate
484    image delete $_image(plot)
485    image delete $_image(download)
486}
487
488itcl::body Rappture::MapViewer::DoResize {} {
489    set sendResize 1
490    if { $_width < 2 } {
491        set _width 500
492        set sendResize 0
493    }
494    if { $_height < 2 } {
495        set _height 500
496        set sendResize 0
497    }
498    #set _start [clock clicks -milliseconds]
499    if {$sendResize} {
500        SendCmd "screen size $_width $_height"
501    }
502    set _resizePending 0
503}
504
505itcl::body Rappture::MapViewer::DoRotate {} {
506    SendCmd "camera rotate $_rotate(azimuth) $_rotate(elevation)"
507    set _rotate(azimuth) 0
508    set _rotate(elevation) 0
509    set _rotate(pending) 0
510}
511
512itcl::body Rappture::MapViewer::EventuallyResize { w h } {
513    set _width $w
514    set _height $h
515    if { !$_resizePending } {
516        set _resizePending 1
517        $_dispatcher event -after 200 !resize
518    }
519}
520
521itcl::body Rappture::MapViewer::DoPan {} {
522    SendCmd "camera pan $_pan(x) $_pan(y)"
523    set _pan(x) 0
524    set _pan(y) 0
525    set _pan(pending) 0
526}
527
528itcl::body Rappture::MapViewer::EventuallyPan { dx dy } {
529    set _pan(x) [expr $_pan(x) + $dx]
530    set _pan(y) [expr $_pan(y) + $dy]
531    if { !$_pan(compress) } {
532        DoPan
533        return
534    }
535    if { !$_pan(pending) } {
536        set _pan(pending) 1
537        $_dispatcher event -after $_pan(delay) !pan
538    }
539}
540
541itcl::body Rappture::MapViewer::EventuallyRotate { dx dy } {
542    set _rotate(azimuth) [expr $_rotate(azimuth) + $dx]
543    set _rotate(elevation) [expr $_rotate(elevation) + $dy]
544    if { !$_rotate(compress) } {
545        DoRotate
546        return
547    }
548    if { !$_rotate(pending) } {
549        set _rotate(pending) 1
550        $_dispatcher event -after $_rotate(delay) !rotate
551    }
552}
553
554# ----------------------------------------------------------------------
555# USAGE: add <dataobj> ?<settings>?
556#
557# Clients use this to add a data object to the plot.  The optional
558# <settings> are used to configure the plot.  Allowed settings are
559# -color, -brightness, -width, -linestyle, and -raise.
560# ----------------------------------------------------------------------
561itcl::body Rappture::MapViewer::add {dataobj {settings ""}} {
562    array set params {
563        -color auto
564        -width 1
565        -linestyle solid
566        -brightness 0
567        -raise 0
568        -description ""
569        -param ""
570        -type ""
571    }
572    array set params $settings
573    set params(-description) ""
574    set params(-param) ""
575    array set params $settings
576
577    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
578        # can't handle -autocolors yet
579        set params(-color) black
580    }
581    set pos [lsearch -exact $_dlist $dataobj]
582    if {$pos < 0} {
583        #if {[llength $_dlist] > 0} {
584        #    error "Can't add more than 1 map to mapviewer"
585        #}
586        lappend _dlist $dataobj
587    }
588    set _obj2ovride($dataobj-color) $params(-color)
589    set _obj2ovride($dataobj-width) $params(-width)
590    set _obj2ovride($dataobj-raise) $params(-raise)
591    $_dispatcher event -idle !rebuild
592}
593
594# ----------------------------------------------------------------------
595# USAGE: delete ?<dataobj1> <dataobj2> ...?
596#
597#       Clients use this to delete a dataobj from the plot.  If no dataobjs
598#       are specified, then all dataobjs are deleted.  No data objects are
599#       deleted.  They are only removed from the display list.
600#
601# ----------------------------------------------------------------------
602itcl::body Rappture::MapViewer::delete {args} {
603    if { [llength $args] == 0} {
604        set args $_dlist
605    }
606    # Delete all specified dataobjs
607    set changed 0
608    foreach dataobj $args {
609        set pos [lsearch -exact $_dlist $dataobj]
610        if { $pos < 0 } {
611            continue;                   # Don't know anything about it.
612        }
613        # When a map is marked deleted, we hide its layers.
614        foreach layer [$dataobj layers] {
615            SendCmd "map layer visible 0 $layer"
616            set _visibility($layer) 0
617        }
618        # Remove it from the dataobj list.
619        set _dlist [lreplace $_dlist $pos $pos]
620        array unset _obj2ovride $dataobj-*
621        array unset _settings $dataobj-*
622        set changed 1
623    }
624    # If anything changed, then rebuild the plot
625    if { $changed } {
626        $_dispatcher event -idle !rebuild
627    }
628}
629
630# ----------------------------------------------------------------------
631# USAGE: get ?-objects?
632# USAGE: get ?-visible?
633# USAGE: get ?-image view?
634#
635# Clients use this to query the list of objects being plotted, in
636# order from bottom to top of this result.  The optional "-image"
637# flag can also request the internal images being shown.
638# ----------------------------------------------------------------------
639itcl::body Rappture::MapViewer::get {args} {
640    if {[llength $args] == 0} {
641        set args "-objects"
642    }
643
644    set op [lindex $args 0]
645    switch -- $op {
646        "-objects" {
647            # put the dataobj list in order according to -raise options
648            set dlist {}
649            foreach dataobj $_dlist {
650                if { ![$dataobj isvalid] } {
651                    continue
652                }
653                if {[info exists _obj2ovride($dataobj-raise)] &&
654                    $_obj2ovride($dataobj-raise)} {
655                    set dlist [linsert $dlist 0 $dataobj]
656                } else {
657                    lappend dlist $dataobj
658                }
659            }
660            return $dlist
661        }
662        "-visible" {
663            set dlist {}
664            foreach dataobj $_dlist {
665                if { ![$dataobj isvalid] } {
666                    continue
667                }
668                if { ![info exists _obj2ovride($dataobj-raise)] } {
669                    # No setting indicates that the object isn't visible.
670                    continue
671                }
672                # Otherwise use the -raise parameter to put the object to
673                # the front of the list.
674                if { $_obj2ovride($dataobj-raise) } {
675                    set dlist [linsert $dlist 0 $dataobj]
676                } else {
677                    lappend dlist $dataobj
678                }
679            }
680            return $dlist
681        }           
682        -image {
683            if {[llength $args] != 2} {
684                error "wrong # args: should be \"get -image view\""
685            }
686            switch -- [lindex $args end] {
687                view {
688                    return $_image(plot)
689                }
690                default {
691                    error "bad image name \"[lindex $args end]\": should be view"
692                }
693            }
694        }
695        default {
696            error "bad option \"$op\": should be -objects or -image"
697        }
698    }
699}
700
701itcl::body Rappture::MapViewer::MapIsGeocentric {} {
702    if { [info exists _mapsettings(type)] } {
703        return [expr {$_mapsettings(type) eq "geocentric"}]
704    } else {
705        return 0
706    }
707}
708
709# ----------------------------------------------------------------------
710# USAGE: scale ?<data1> <data2> ...?
711#
712# Sets the default limits for the overall plot according to the
713# limits of the data for all of the given <data> objects.  This
714# accounts for all objects--even those not showing on the screen.
715# Because of this, the limits are appropriate for all objects as
716# the user scans through data in the ResultSet viewer.
717# ----------------------------------------------------------------------
718itcl::body Rappture::MapViewer::scale {args} {
719    array unset _mapsettings
720    set _haveTerrain 0
721
722    # Verify that all the maps have the same global settings. For example,
723    # you can't have one map type "geocentric" and the other "projected".
724
725    foreach dataobj $args {
726        if { ![$dataobj isvalid] } {
727            continue
728        }
729        array unset hints
730        array set hints [$dataobj hints]
731        if { ![info exists _mapsettings(label)] } {
732            set _mapsettings(label) $hints(label)
733        }
734        if { ![info exists _mapsettings(style)] } {
735            set _mapsettings(style) $hints(style)
736        }
737        if { ![info exists _mapsettings(type)] } {
738            set _mapsettings(type) $hints(type)
739        } elseif { $hints(type) != $_mapsettings(type) } {
740            error "maps \"$hints(label)\" have differing types"
741        }
742        if { ![info exists _mapsettings(projection)] } {
743            set _mapsettings(projection) $hints(projection)
744        } elseif { $hints(projection) != $_mapsettings(projection) } {
745            error "maps \"$hints(label)\" have differing projections"
746        }
747        if { $hints(extents) != "" } {
748            if { ![info exists _mapsettings(extents)] } {
749                set _mapsettings(extents) $hints(extents)
750            }
751            foreach {x1 y1 x2 y2} $hints(extents) break
752            if { ![info exists _mapsettings(x1)] || $x1 < $_mapsettings(x1) } {
753                set _mapsettings(x1) $x1
754            }
755            if { ![info exists _mapsettings(y1)] || $y1 < $_mapsettings(y1) } {
756                set _mapsettings(y1) $y1
757            }
758            if { ![info exists _mapsettings(x2)] || $x2 > $_mapsettings(x2) } {
759                set _mapsettings(x2) $x2
760            }
761            if { ![info exists _mapsettings(y2)] || $y2 > $_mapsettings(y2) } {
762                set _mapsettings(y2) $y2
763            }
764        }
765        foreach layer [$dataobj layers] {
766            if { [$dataobj type $layer] == "elevation" } {
767                set _haveTerrain 1
768                break
769            }
770        }
771    }
772    if { $_haveTerrain } {
773        if { [$itk_component(main) exists "Terrain Settings"] } {
774            # TODO: Enable controls like vertical scale that only have
775            # an effect when terrain is present
776        }
777    }
778}
779
780# ----------------------------------------------------------------------
781# USAGE: download coming
782# USAGE: download controls <downloadCommand>
783# USAGE: download now
784#
785# Clients use this method to create a downloadable representation
786# of the plot.  Returns a list of the form {ext string}, where
787# "ext" is the file extension (indicating the type of data) and
788# "string" is the data itself.
789# ----------------------------------------------------------------------
790itcl::body Rappture::MapViewer::download {option args} {
791    switch $option {
792        coming {
793            if {[catch {
794                blt::winop snap $itk_component(plotarea) $_image(download)
795            }]} {
796                $_image(download) configure -width 1 -height 1
797                $_image(download) put #000000
798            }
799        }
800        controls {
801            set popup .mapviewerdownload
802            if { ![winfo exists .mapviewerdownload] } {
803                set inner [BuildDownloadPopup $popup [lindex $args 0]]
804            } else {
805                set inner [$popup component inner]
806            }
807            set _downloadPopup(image_controls) $inner.image_frame
808            set num [llength [get]]
809            set num [expr {($num == 1) ? "1 result" : "$num results"}]
810            set word [Rappture::filexfer::label downloadWord]
811            $inner.summary configure -text "$word $num in the following format:"
812            update idletasks            ;# Fix initial sizes
813            return $popup
814        }
815        now {
816            set popup .mapviewerdownload
817            if {[winfo exists .mapviewerdownload]} {
818                $popup deactivate
819            }
820            switch -- $_downloadPopup(format) {
821                "image" {
822                    return [$this GetImage [lindex $args 0]]
823                }
824            }
825            return ""
826        }
827        default {
828            error "bad option \"$option\": should be coming, controls, now"
829        }
830    }
831}
832
833# ----------------------------------------------------------------------
834# USAGE: Connect ?<host:port>,<host:port>...?
835#
836# Clients use this method to establish a connection to a new
837# server, or to reestablish a connection to the previous server.
838# Any existing connection is automatically closed.
839# ----------------------------------------------------------------------
840itcl::body Rappture::MapViewer::Connect {} {
841    global readyForNextFrame
842    set readyForNextFrame 1
843    set _reset 1
844    set _hosts [GetServerList "geovis"]
845    if { "" == $_hosts } {
846        return 0
847    }
848    set result [VisViewer::Connect $_hosts]
849    if { $result } {
850        if { $_reportClientInfo }  {
851            # Tell the server the viewer, hub, user and session.
852            # Do this immediately on connect before buffering any commands
853            global env
854
855            set info {}
856            set user "???"
857            if { [info exists env(USER)] } {
858                set user $env(USER)
859            }
860            set session "???"
861            if { [info exists env(SESSION)] } {
862                set session $env(SESSION)
863            }
864            lappend info "hub" [exec hostname]
865            lappend info "client" "mapviewer"
866            lappend info "user" $user
867            lappend info "session" $session
868            SendCmd "clientinfo [list $info]"
869        }
870
871        set w [winfo width $itk_component(view)]
872        set h [winfo height $itk_component(view)]
873        EventuallyResize $w $h
874    }
875    return $result
876}
877
878#
879# isconnected --
880#
881#       Indicates if we are currently connected to the visualization server.
882#
883itcl::body Rappture::MapViewer::isconnected {} {
884    return [VisViewer::IsConnected]
885}
886
887#
888# disconnect --
889#
890itcl::body Rappture::MapViewer::disconnect {} {
891    Disconnect
892    set _reset 1
893}
894
895#
896# Disconnect --
897#
898#       Clients use this method to disconnect from the current rendering
899#       server.
900#
901itcl::body Rappture::MapViewer::Disconnect {} {
902    VisViewer::Disconnect
903
904    $_dispatcher cancel !pan
905    $_dispatcher cancel !motion
906    $_dispatcher cancel !rebuild
907    $_dispatcher cancel !resize
908    $_dispatcher cancel !rotate
909    # disconnected -- no more data sitting on server
910    array unset _layers
911    array unset _layersFrame
912    global readyForNextFrame
913    set readyForNextFrame 1
914}
915
916# ----------------------------------------------------------------------
917# USAGE: ReceiveImage -type <type> -token <token> -bytes <size>
918#
919# Invoked automatically whenever the "image" command comes in from
920# the rendering server.  Indicates that binary image data with the
921# specified <size> will follow.
922# ----------------------------------------------------------------------
923itcl::body Rappture::MapViewer::ReceiveImage { args } {
924    global readyForNextFrame
925    set readyForNextFrame 1
926    array set info {
927        -bytes 0
928        -token "???"
929        -type image
930    }
931    array set info $args
932    set bytes [ReceiveBytes $info(-bytes)]
933    if { $info(-type) == "image" } {
934        $_image(plot) configure -data $bytes
935    } elseif { $info(type) == "print" } {
936        set tag $this-print-$info(-token)
937        set _hardcopy($tag) $bytes
938    }
939}
940
941#
942# ReceiveMapInfo --
943#
944itcl::body Rappture::MapViewer::ReceiveMapInfo { args } {
945    if { ![isconnected] } {
946        return
947    }
948    set timeReceived [clock clicks -milliseconds]
949    set elapsed [expr $timeReceived - $_start]
950    set option [lindex $args 0]
951    switch -- $option {
952        "coords" {
953            set len [llength $args]
954            if {$len < 3} {
955                error "Bad map coords response"
956            } else {
957                set token [lindex $args 1]
958            }
959            foreach { x y z } [lindex $args 2] {
960                puts stderr "\[$token\] Map coords: $x $y $z"
961            }
962            if {$len > 3} {
963                set srs [lindex $args 3]
964                set vert [lindex $args 4]
965                puts stderr "\[$token\] {$srs} {$vert}"
966            }
967        }
968        "names" {
969            foreach { name } [lindex $args 1] {
970                puts stderr "layer: $name"
971            }
972        }
973        default {
974            error "unknown map option \"$option\" from server"
975        }
976    }
977}
978
979#
980# ReceiveScreenInfo --
981#
982itcl::body Rappture::MapViewer::ReceiveScreenInfo { args } {
983    if { ![isconnected] } {
984        return
985    }
986    set option [lindex $args 0]
987    switch -- $option {
988        "coords" {
989            set len [llength $args]
990            if {$len < 3} {
991                error "Bad screen coords response"
992            } else {
993                set token [lindex $args 1]
994            }
995            foreach { x y z } [lindex $args 2] {
996                puts stderr "\[$token\] Screen coords: $x $y $z"
997            }
998        }
999        default {
1000            error "unknown screen option \"$option\" from server"
1001        }
1002    }
1003}
1004
1005# ----------------------------------------------------------------------
1006# USAGE: Rebuild
1007#
1008# Called automatically whenever something changes that affects the
1009# data in the widget.  Clears any existing data and rebuilds the
1010# widget to display new data.
1011# ----------------------------------------------------------------------
1012itcl::body Rappture::MapViewer::Rebuild {} {
1013    set w [winfo width $itk_component(view)]
1014    set h [winfo height $itk_component(view)]
1015    if { $w < 2 || $h < 2 } {
1016        $_dispatcher event -idle !rebuild
1017        return
1018    }
1019
1020    # Turn on buffering of commands to the server.  We don't want to be
1021    # preempted by a server disconnect/reconnect (which automatically
1022    # generates a new call to Rebuild).
1023    StartBufferingCommands
1024
1025    if { $_reset } {
1026        set _width $w
1027        set _height $h
1028        DoResize
1029
1030        if { [info exists _mapsettings(type)] } {
1031            # The map must be reset once before any layers are added This
1032            # should not be done more than once as it is very expensive.
1033            if {$_sendEarthFile} {
1034                set bytes [EarthFile]
1035                if {0} {
1036                    set f [open "/tmp/map.earth" "w"]
1037                    puts $f $bytes
1038                    close $f
1039                }
1040                set length [string length $bytes]
1041                SendCmd "map load data follows $length"
1042                append _outbuf $bytes
1043            } else {
1044                if { $_mapsettings(type) == "geocentric" } {
1045                    SendCmd "map reset geocentric"
1046                }  else {
1047                    set proj $_mapsettings(projection)
1048                    if { $proj == "" } {
1049                        SendCmd "map reset projected global-mercator"
1050                    } elseif { ![info exists _mapsettings(extents)] || $_mapsettings(extents) == "" } {
1051                        SendCmd [list map reset "projected" $proj]
1052                    } else {
1053                        #foreach {x1 y1 x2 y2} $_mapsettings(extents) break
1054                        foreach key "x1 y1 x2 y2" {
1055                            set $key $_mapsettings($key)
1056                        }
1057                        SendCmd [list map reset "projected" $proj $x1 $y1 $x2 $y2]
1058                    }
1059                }
1060                # XXX: Remove these after implementing batch load of layers with reset
1061                SendCmd "map layer delete base"
1062            }
1063
1064            # Most terrain settings are global to the map and apply even
1065            # if there is no elevation layer.  The exception is the
1066            # vertical scale, which only applies if there is an elevation
1067            # layer
1068            if { [info exists _mapsettings(style)] } {
1069                SetTerrainStyle $_mapsettings(style)
1070            } else {
1071                InitSettings terrain-edges terrain-lighting \
1072                    terrain-vertscale terrain-wireframe
1073            }
1074            InitSettings coords-visible
1075        } else {
1076            error "No map settings on reset"
1077        }
1078    }
1079
1080    set _first ""
1081    set count 0
1082
1083    foreach dataobj [get -objects] {
1084        set _obj2datasets($dataobj) ""
1085        foreach layer [$dataobj layers] {
1086            array unset info
1087            array set info [$dataobj layer $layer]
1088            if { ![info exists _layers($layer)] } {
1089                if { $_reportClientInfo }  {
1090                    set cinfo {}
1091                    lappend cinfo "tool_id"       [$dataobj hints toolId]
1092                    lappend cinfo "tool_name"     [$dataobj hints toolName]
1093                    lappend cinfo "tool_version"  [$dataobj hints toolRevision]
1094                    lappend cinfo "tool_title"    [$dataobj hints toolTitle]
1095                    lappend cinfo "dataset_label" [$dataobj hints label]
1096                    lappend cinfo "dataset_tag"   $layer
1097                    SendCmd [list "clientinfo" $cinfo]
1098                }
1099                set _layers($layer) 1
1100                SetLayerStyle $dataobj $layer
1101            }
1102            lappend _obj2datasets($dataobj) $layer
1103            # FIXME: This is overriding all layers' initial visibility setting
1104            if { [info exists _obj2ovride($dataobj-raise)] } {
1105                SendCmd "map layer visible 1 $layer"
1106                set _visibility($layer) 1
1107                #SetLayerOpacity $layer
1108            }
1109        }
1110    }
1111
1112    if {$_reset} {
1113        if {$_initCamera} {
1114            # If this is the first Rebuild, we need to
1115            # set up the initial view settings if there
1116            # are any
1117            if { [info exists _mapsettings(camera)] } {
1118                set location $_mapsettings(camera)
1119                if { $location != "" } {
1120                    array set _view $location
1121                    camera set all
1122                }
1123            }
1124            set _initCamera 0
1125        } else {
1126            # Restore view from before reconnect
1127            camera set all
1128        }
1129    }
1130
1131    UpdateLayerControls
1132    set _reset 0
1133    global readyForNextFrame
1134    set readyForNextFrame 0;            # Don't advance to the next frame
1135                                        # until we get an image.
1136
1137    # Actually write the commands to the server socket.  If it fails, we
1138    # don't care.  We're finished here.
1139    blt::busy hold $itk_component(hull)
1140    StopBufferingCommands
1141    blt::busy release $itk_component(hull)
1142}
1143
1144# ----------------------------------------------------------------------
1145# USAGE: CurrentLayers ?-all -visible? ?dataobjs?
1146#
1147# Returns a list of server IDs for the current datasets being displayed.
1148# This is normally a single ID, but it might be a list of IDs if the
1149# current data object has multiple components.
1150# ----------------------------------------------------------------------
1151itcl::body Rappture::MapViewer::CurrentLayers {args} {
1152    set flag [lindex $args 0]
1153    switch -- $flag {
1154        "-all" {
1155            if { [llength $args] > 1 } {
1156                error "CurrentLayers: can't specify dataobj after \"-all\""
1157            }
1158            set dlist [get -objects]
1159        }
1160        "-visible" {
1161            if { [llength $args] > 1 } {
1162                set dlist {}
1163                set args [lrange $args 1 end]
1164                foreach dataobj $args {
1165                    if { [info exists _obj2ovride($dataobj-raise)] } {
1166                        lappend dlist $dataobj
1167                    }
1168                }
1169            } else {
1170                set dlist [get -visible]
1171            }
1172        }           
1173        default {
1174            set dlist $args
1175        }
1176    }
1177    set rlist ""
1178    foreach dataobj $dlist {
1179        foreach layer [$dataobj layers] {
1180            if { [info exists _layers($layer)] && $_layers($layer) } {
1181                lappend rlist $layer
1182            }
1183        }
1184    }
1185    return $rlist
1186}
1187
1188itcl::body Rappture::MapViewer::KeyPress {k} {
1189    SendCmd "key press $k"
1190}
1191
1192itcl::body Rappture::MapViewer::KeyRelease {k} {
1193    SendCmd "key release $k"
1194}
1195
1196itcl::body Rappture::MapViewer::GetNormalizedMouse {x y} {
1197    set w [winfo width $itk_component(view)]
1198    set h [winfo height $itk_component(view)]
1199    set x [expr {(2.0 * double($x)/$w) - 1.0}]
1200    set y [expr {(2.0 * double($y)/$h) - 1.0}]
1201    return [list $x $y]
1202}
1203
1204itcl::body Rappture::MapViewer::MouseClick {button x y} {
1205    SendCmd "mouse click $button $x $y"
1206}
1207
1208itcl::body Rappture::MapViewer::MouseDoubleClick {button x y} {
1209    SendCmd "mouse dblclick $button $x $y"
1210}
1211
1212itcl::body Rappture::MapViewer::MouseDrag {button x y} {
1213    SendCmd "mouse drag $button $x $y"
1214}
1215
1216itcl::body Rappture::MapViewer::MouseRelease {button x y} {
1217    SendCmd "mouse release $button $x $y"
1218}
1219
1220itcl::body Rappture::MapViewer::MouseMotion {} {
1221    #SendCmd "mouse motion $_motion(x) $_motion(y)"
1222    SendCmd "map pin hover $_motion(x) $_motion(y)"
1223    set _motion(pending) 0
1224}
1225
1226itcl::body Rappture::MapViewer::MouseScroll {direction} {
1227    switch -- $direction {
1228        "up" {
1229            SendCmd "mouse scroll 1"
1230        }
1231        "down" {
1232            SendCmd "mouse scroll -1"
1233        }
1234    }
1235}
1236
1237#
1238# EventuallyHandleMotionEvent --
1239#
1240#       This routine compresses (no button press) motion events.  It
1241#       delivers a server mouse command once every 100 milliseconds (if a
1242#       motion event is pending).
1243#
1244itcl::body Rappture::MapViewer::EventuallyHandleMotionEvent {x y} {
1245    set _motion(x) $x
1246    set _motion(y) $y
1247    if { !$_motion(compress) } {
1248        MouseMotion
1249        return
1250    }
1251    if { !$_motion(pending) } {
1252        set _motion(pending) 1
1253        $_dispatcher event -after $_motion(delay) !motion
1254    }
1255}
1256
1257# ----------------------------------------------------------------------
1258# USAGE: Zoom in
1259# USAGE: Zoom out
1260# USAGE: Zoom reset
1261#        $this Zoom click x y
1262#        $this Zoom drag x y
1263#        $this Zoom release x y
1264#
1265# Called automatically when the user clicks on one of the zoom
1266# controls for this widget.  Changes the zoom for the current view.
1267# Also implements mouse zoom.
1268# ----------------------------------------------------------------------
1269itcl::body Rappture::MapViewer::Zoom {option {x 0} {y 0}} {
1270    switch -- $option {
1271        "in" {
1272            # z here is normalized mouse Y delta
1273            set z -0.25
1274            SendCmd "camera zoom $z"
1275        }
1276        "out" {
1277            # z here is normalized mouse Y delta
1278            set z 0.25
1279            SendCmd "camera zoom $z"
1280        }
1281        "reset" {
1282            SendCmd "camera dist $_view(distance)"
1283        }
1284        "click" {
1285            set _click(x) $x
1286            set _click(y) $y
1287            $itk_component(view) configure -cursor hand1
1288        }
1289        "drag" {
1290            if { ![info exists _click(x)] } {
1291                set _click(x) $x
1292            }
1293            if { ![info exists _click(y)] } {
1294                set _click(y) $y
1295            }
1296            set h [winfo height $itk_component(view)]
1297            set dy [expr ($_click(y) - $y)/double($h)]
1298            set _click(x) $x
1299            set _click(y) $y
1300            if {[expr (abs($dy) > 0.0)]} {
1301                SendCmd "camera zoom $dy"
1302            }
1303        }
1304        "release" {
1305            Zoom drag $x $y
1306            $itk_component(view) configure -cursor ""
1307        }
1308    }
1309}
1310
1311# ----------------------------------------------------------------------
1312# USAGE: Rotate click <x> <y>
1313# USAGE: Rotate drag <x> <y>
1314# USAGE: Rotate release <x> <y>
1315#
1316# Called automatically when the user clicks/drags/releases in the
1317# plot area.  Moves the plot according to the user's actions.
1318# ----------------------------------------------------------------------
1319itcl::body Rappture::MapViewer::Rotate {option x y} {
1320    switch -- $option {
1321        "click" {
1322            $itk_component(view) configure -cursor fleur
1323            set _click(x) $x
1324            set _click(y) $y
1325            set _rotate(azimuth) 0
1326            set _rotate(elevation) 0
1327        }
1328        "drag" {
1329            if {[array size _click] == 0} {
1330                Rotate click $x $y
1331            } else {
1332                set w [winfo width $itk_component(view)]
1333                set h [winfo height $itk_component(view)]
1334                if {$w <= 0 || $h <= 0} {
1335                    return
1336                }
1337                set dx [expr ($x - $_click(x))/double($w)]
1338                set dy [expr ($_click(y) - $y)/double($h)]
1339                set _click(x) $x
1340                set _click(y) $y
1341                if {[expr (abs($dx) > 0.0 || abs($dy) > 0.0)]} {
1342                    #SendCmd "camera rotate $dx $dy"
1343                    EventuallyRotate $dx $dy
1344                }
1345            }
1346        }
1347        "release" {
1348            Rotate drag $x $y
1349            $itk_component(view) configure -cursor ""
1350            catch {unset _click}
1351        }
1352        default {
1353            error "bad option \"$option\": should be click, drag, release"
1354        }
1355    }
1356}
1357
1358itcl::body Rappture::MapViewer::Select {option x y} {
1359    switch -- $option {
1360        "click" {
1361            set _click(x) $x
1362            set _click(y) $y
1363            set _b1mode "select"
1364            SendCmd "map box init $x $y"
1365        }
1366        "drag" {
1367            if {$_b1mode == "select"} {
1368                SendCmd "map box update $x $y"
1369            }
1370        }
1371        "release" {
1372            set _b1mode ""
1373            if {$_click(x) == $x &&
1374                $_click(y) == $y} {
1375                SendCmd "map box clear"
1376            }
1377        }
1378    }
1379}
1380
1381itcl::body Rappture::MapViewer::Pin {option x y} {
1382    set _click(x) $x
1383    set _click(y) $y
1384    switch -- $option {
1385        "add" {
1386            incr _labelCount
1387            set label "Label $_labelCount"
1388            SendCmd [list "map" "pin" "add" $x $y $label]
1389        }
1390        "delete" {
1391            SendCmd "map pin delete $x $y"
1392        }
1393    }
1394}
1395
1396# ----------------------------------------------------------------------
1397# USAGE: $this Pan set x y
1398#        $this Pan click x y
1399#        $this Pan drag x y
1400#        $this Pan release x y
1401#
1402# Called automatically when the user clicks on one of the zoom
1403# controls for this widget.  Changes the zoom for the current view.
1404# ----------------------------------------------------------------------
1405itcl::body Rappture::MapViewer::Pan {option x y} {
1406    switch -- $option {
1407        "set" {
1408            set w [winfo width $itk_component(view)]
1409            set h [winfo height $itk_component(view)]
1410            set x [expr $x / double($w)]
1411            set y [expr $y / double($h)]
1412            if {[expr (abs($x) > 0.0 || abs($y) > 0.0)]} {
1413                SendCmd "camera pan $x $y"
1414            }
1415            return
1416        }
1417        "click" {
1418            set _click(x) $x
1419            set _click(y) $y
1420            set _pan(x) 0
1421            set _pan(y) 0
1422            $itk_component(view) configure -cursor hand1
1423            set _b1mode "pan"
1424        }
1425        "drag" {
1426            if {$_b1mode != "pan"} {
1427                return
1428            }
1429            if { ![info exists _click(x)] } {
1430                set _click(x) $x
1431            }
1432            if { ![info exists _click(y)] } {
1433                set _click(y) $y
1434            }
1435            set w [winfo width $itk_component(view)]
1436            set h [winfo height $itk_component(view)]
1437            set dx [expr ($x - $_click(x))/double($w)]
1438            set dy [expr ($_click(y) - $y)/double($h)]
1439            set _click(x) $x
1440            set _click(y) $y
1441            if {[expr (abs($dx) > 0.0 || abs($dy) > 0.0)]} {
1442                EventuallyPan $dx $dy
1443                #SendCmd "camera pan $dx $dy"
1444            }
1445        }
1446        "release" {
1447            Pan drag $x $y
1448            $itk_component(view) configure -cursor ""
1449            set _b1mode ""
1450        }
1451        default {
1452            error "unknown option \"$option\": should set, click, drag, or release"
1453        }
1454    }
1455}
1456
1457# ----------------------------------------------------------------------
1458# USAGE: InitSettings <what> ?<value>?
1459#
1460# Used internally to update rendering settings whenever parameters
1461# change in the popup settings panel.  Sends the new settings off
1462# to the back end.
1463# ----------------------------------------------------------------------
1464itcl::body Rappture::MapViewer::InitSettings { args } {
1465    foreach setting $args {
1466        AdjustSetting $setting
1467    }
1468}
1469
1470#
1471# AdjustSetting --
1472#
1473#       Changes/updates a specific setting in the widget.  There are
1474#       usually user-setable option.  Commands are sent to the render
1475#       server.
1476#
1477itcl::body Rappture::MapViewer::AdjustSetting {what {value ""}} {
1478    if { ![isconnected] } {
1479        return
1480    }
1481    switch -- $what {
1482        "coords-visible" - "coords-precision" - "coords-units" {
1483            set bool $_settings(coords-visible)
1484            set units $_settings(coords-units)
1485            set precision $_settings(coords-precision)
1486            SendCmd "map posdisp $bool $units $precision"
1487        }
1488        "grid" - "grid-type" {
1489            set bool $_settings(grid)
1490            set gridType $_settings(grid-type)
1491            SendCmd "map grid $bool $gridType"
1492        }
1493        "camera-throw" {
1494            set bool $_settings($what)
1495            SendCmd "camera throw $bool"
1496        }
1497        "terrain-edges" {
1498            set bool $_settings($what)
1499            SendCmd "map terrain edges $bool"
1500        }
1501        "terrain-lighting" {
1502            set bool $_settings($what)
1503            SendCmd "map terrain lighting $bool"
1504        }
1505        "terrain-palette" {
1506            set cmap [$itk_component(terrainpalette) value]
1507            #SendCmd "map terrain colormap $cmap"
1508        }
1509        "terrain-vertscale" {
1510            set val $_settings($what)
1511            SendCmd "map terrain vertscale $val"
1512        }
1513        "terrain-wireframe" {
1514            set bool $_settings($what)
1515            SendCmd "map terrain wireframe $bool"
1516        }
1517        default {
1518            error "don't know how to fix $what"
1519        }
1520    }
1521}
1522
1523# ----------------------------------------------------------------------
1524# CONFIGURATION OPTION: -plotbackground
1525# ----------------------------------------------------------------------
1526itcl::configbody Rappture::MapViewer::plotbackground {
1527    if { [isconnected] } {
1528        foreach {r g b} [Color2RGB $itk_option(-plotbackground)] break
1529        SendCmd "screen bgcolor $r $g $b"
1530    }
1531}
1532
1533# ----------------------------------------------------------------------
1534# CONFIGURATION OPTION: -plotforeground
1535# ----------------------------------------------------------------------
1536itcl::configbody Rappture::MapViewer::plotforeground {
1537    if { [isconnected] } {
1538        foreach {r g b} [Color2RGB $itk_option(-plotforeground)] break
1539        #fix this!
1540        #SendCmd "color background $r $g $b"
1541    }
1542}
1543
1544itcl::body Rappture::MapViewer::limits { dataobj } {
1545    error "no limits"
1546}
1547
1548itcl::body Rappture::MapViewer::BuildTerrainTab {} {
1549
1550    set fg [option get $itk_component(hull) font Font]
1551    #set bfg [option get $itk_component(hull) boldFont Font]
1552
1553    set inner [$itk_component(main) insert end \
1554        -title "Terrain Settings" \
1555        -icon [Rappture::icon surface]]
1556    $inner configure -borderwidth 4
1557
1558    checkbutton $inner.posdisp \
1559        -text "Show Coordinate Readout" \
1560        -variable [itcl::scope _settings(coords-visible)] \
1561        -command [itcl::code $this AdjustSetting coords-visible] \
1562        -font "Arial 9" -anchor w
1563
1564    checkbutton $inner.grid \
1565        -text "Show Graticule" \
1566        -variable [itcl::scope _settings(grid)] \
1567        -command [itcl::code $this AdjustSetting grid] \
1568        -font "Arial 9" -anchor w
1569
1570    checkbutton $inner.wireframe \
1571        -text "Show Wireframe" \
1572        -variable [itcl::scope _settings(terrain-wireframe)] \
1573        -command [itcl::code $this AdjustSetting terrain-wireframe] \
1574        -font "Arial 9" -anchor w
1575
1576    checkbutton $inner.lighting \
1577        -text "Enable Lighting" \
1578        -variable [itcl::scope _settings(terrain-lighting)] \
1579        -command [itcl::code $this AdjustSetting terrain-lighting] \
1580        -font "Arial 9" -anchor w
1581
1582    checkbutton $inner.edges \
1583        -text "Show Edges" \
1584        -variable [itcl::scope _settings(terrain-edges)] \
1585        -command [itcl::code $this AdjustSetting terrain-edges] \
1586        -font "Arial 9" -anchor w
1587
1588    label $inner.palette_l -text "Palette" -font "Arial 9" -anchor w
1589    itk_component add terrainpalette {
1590        Rappture::Combobox $inner.palette -width 10 -editable no
1591    }
1592    $inner.palette choices insert end [GetColormapList]
1593
1594    $itk_component(terrainpalette) value "BCGYR"
1595    bind $inner.palette <<Value>> \
1596        [itcl::code $this AdjustSetting terrain-palette]
1597
1598    label $inner.vscale_l -text "Vertical Scale" -font "Arial 9" -anchor w
1599    ::scale $inner.vscale -from 0 -to 10 -orient horizontal \
1600        -variable [itcl::scope _settings(terrain-vertscale)] \
1601        -width 10 \
1602        -resolution 0.1 \
1603        -showvalue on \
1604        -command [itcl::code $this AdjustSetting terrain-vertscale]
1605    $inner.vscale set $_settings(terrain-vertscale)
1606
1607    blt::table $inner \
1608        0,0 $inner.posdisp   -cspan 2  -anchor w -pady 2 \
1609        1,0 $inner.grid      -cspan 2  -anchor w -pady 2 \
1610        2,0 $inner.wireframe -cspan 2  -anchor w -pady 2 \
1611        3,0 $inner.lighting  -cspan 2  -anchor w -pady 2 \
1612        4,0 $inner.edges     -cspan 2  -anchor w -pady 2 \
1613        5,0 $inner.vscale_l  -anchor w -pady 2 \
1614        5,1 $inner.vscale    -fill x   -pady 2 \
1615        6,0 $inner.palette_l -anchor w -pady 2 \
1616        6,1 $inner.palette   -fill x   -pady 2 
1617
1618    blt::table configure $inner r* c* -resize none
1619    blt::table configure $inner r8 c1 -resize expand
1620}
1621
1622itcl::body Rappture::MapViewer::BuildLayerTab {} {
1623
1624    set fg [option get $itk_component(hull) font Font]
1625    #set bfg [option get $itk_component(hull) boldFont Font]
1626
1627    set inner [$itk_component(main) insert end \
1628        -title "Layers" \
1629        -icon [Rappture::icon layers]]
1630    $inner configure -borderwidth 4
1631    set f [frame $inner.layers]
1632    blt::table $inner \
1633        0,0 $f -fill both
1634    set _layersFrame $inner
1635}
1636
1637itcl::body Rappture::MapViewer::BuildCameraTab {} {
1638    set inner [$itk_component(main) insert end \
1639        -title "Camera Settings" \
1640        -icon [Rappture::icon camera]]
1641    $inner configure -borderwidth 4
1642
1643    set row 0
1644
1645    set labels { x y z heading pitch distance }
1646    foreach tag $labels {
1647        label $inner.${tag}label -text $tag -font "Arial 9"
1648        entry $inner.${tag} -font "Arial 9"  -bg white \
1649            -textvariable [itcl::scope _view($tag)]
1650        bind $inner.${tag} <KeyPress-Return> \
1651            [itcl::code $this camera set ${tag}]
1652        bind $inner.${tag} <KP_Enter> \
1653            [itcl::code $this camera set ${tag}]
1654        blt::table $inner \
1655            $row,0 $inner.${tag}label -anchor e -pady 2 \
1656            $row,1 $inner.${tag} -anchor w -pady 2
1657        blt::table configure $inner r$row -resize none
1658        incr row
1659    }
1660    set labels { srs verticalDatum }
1661    foreach tag $labels {
1662        label $inner.${tag}label -text $tag -font "Arial 9"
1663        entry $inner.${tag} -font "Arial 9"  -bg white \
1664            -textvariable [itcl::scope _view($tag)]
1665        bind $inner.${tag} <KeyPress-Return> \
1666            [itcl::code $this camera set ${tag}]
1667        bind $inner.${tag} <KP_Enter> \
1668            [itcl::code $this camera set ${tag}]
1669        blt::table $inner \
1670            $row,0 $inner.${tag}label -anchor e -pady 2 \
1671            $row,1 $inner.${tag} -anchor w -pady 2
1672        blt::table configure $inner r$row -resize none
1673        incr row
1674    }
1675
1676    if {0} {
1677    button $inner.get \
1678        -text "Get Camera Settings" \
1679        -font "Arial 9" \
1680        -command [itcl::code $this SendCmd "camera get"]
1681    blt::table $inner \
1682        $row,0 $inner.get -anchor w -pady 2 -cspan 2
1683    blt::table configure $inner r$row -resize none
1684    incr row
1685
1686    button $inner.set \
1687        -text "Apply Camera Settings" \
1688        -font "Arial 9" \
1689        -command [itcl::code $this camera set all]
1690    blt::table $inner \
1691        $row,0 $inner.set -anchor w -pady 2 -cspan 2
1692    blt::table configure $inner r$row -resize none
1693    incr row
1694    }
1695
1696    if {$_useServerManip} {
1697        checkbutton $inner.throw \
1698            -text "Enable Throw" \
1699            -font "Arial 9" \
1700            -variable [itcl::scope _settings(camera-throw)] \
1701            -command [itcl::code $this AdjustSetting camera-throw]
1702        blt::table $inner \
1703            $row,0 $inner.throw -anchor w -pady 2 -cspan 2
1704        blt::table configure $inner r$row -resize none
1705        incr row
1706    }
1707
1708    blt::table configure $inner c* r* -resize none
1709    blt::table configure $inner c2 -resize expand
1710    blt::table configure $inner r$row -resize expand
1711}
1712
1713#
1714#  camera --
1715#
1716# USAGE: camera get
1717#        This is called by the server to transfer the
1718#        current Viewpoint settings
1719# USAGE: camera reset
1720#        Reset the camera to the default view
1721#
1722itcl::body Rappture::MapViewer::camera {option args} {
1723    switch -- $option {
1724        "get" {
1725            # We got the camera settings from the server
1726            foreach name {x y z heading pitch distance srs verticalDatum} value $args {
1727                set _view($name) $value
1728            }
1729            puts stderr "view: $_view(x), $_view(y), $_view(z), $_view(heading), $_view(pitch), $_view(distance), {$_view(srs)}, {$_view(verticalDatum)}"
1730        }
1731        "go" {
1732            SendCmd "camera go $args"
1733        }
1734        "reset" {
1735            array set _view {
1736                x               0.0
1737                y               0.0
1738                z               0.0
1739                heading         0.0
1740                pitch           -89.9
1741                distance        1.0
1742                srs             ""
1743                verticalDatum   ""
1744            }
1745            if { [info exists _mapsettings(camera)] } {
1746                # Check if the tool specified a default
1747                set location $_mapsettings(camera)
1748                if { $location != "" } {
1749                    array set _view $location
1750                    set duration 0.0
1751                    SendCmd [list camera set $_view(x) $_view(y) $_view(z) $_view(heading) $_view(pitch) $_view(distance) $duration $_view(srs) $_view(verticalDatum)]
1752                } else {
1753                    SendCmd "camera reset"
1754                    # Retrieve the settings
1755                    #SendCmd "camera get"
1756                }
1757            } else {
1758                SendCmd "camera reset"
1759                # Retrieve the settings
1760               # SendCmd "camera get"
1761            }
1762        }
1763        "set" {
1764            set who [lindex $args 0]
1765            if {$who != "all" && $who != "srs" && $who != "verticalDatum"} {
1766                set val $_view($who)
1767                set code [catch { string is double $val } result]
1768                if { $code != 0 || !$result } {
1769                    return
1770                }
1771            }
1772            switch -- $who {
1773                "distance" {
1774                    SendCmd [list camera dist $_view(distance)]
1775                }
1776                "all" - "x" - "y" - "z" - "heading" - "pitch" - "srs" - "verticalDatum" {
1777                    set duration 0.0
1778                    SendCmd [list camera set $_view(x) $_view(y) $_view(z) $_view(heading) $_view(pitch) $_view(distance) $duration $_view(srs) $_view(verticalDatum)]
1779                }
1780            }
1781        }
1782    }
1783}
1784
1785itcl::body Rappture::MapViewer::GetImage { args } {
1786    if { [image width $_image(download)] > 0 &&
1787         [image height $_image(download)] > 0 } {
1788        set bytes [$_image(download) data -format "jpeg -quality 100"]
1789        set bytes [Rappture::encoding::decode -as b64 $bytes]
1790        return [list .jpg $bytes]
1791    }
1792    return ""
1793}
1794
1795itcl::body Rappture::MapViewer::BuildDownloadPopup { popup command } {
1796    Rappture::Balloon $popup \
1797        -title "[Rappture::filexfer::label downloadWord] as..."
1798    set inner [$popup component inner]
1799    label $inner.summary -text "" -anchor w
1800
1801    radiobutton $inner.image_button -text "Image File" \
1802        -variable [itcl::scope _downloadPopup(format)] \
1803        -value image
1804    Rappture::Tooltip::for $inner.image_button \
1805        "Save as digital image."
1806
1807    button $inner.ok -text "Save" \
1808        -highlightthickness 0 -pady 2 -padx 3 \
1809        -command $command \
1810        -compound left \
1811        -image [Rappture::icon download]
1812
1813    button $inner.cancel -text "Cancel" \
1814        -highlightthickness 0 -pady 2 -padx 3 \
1815        -command [list $popup deactivate] \
1816        -compound left \
1817        -image [Rappture::icon cancel]
1818
1819    blt::table $inner \
1820        0,0 $inner.summary -cspan 2  \
1821        2,0 $inner.image_button -anchor w -cspan 2 -padx { 4 0 } \
1822        4,1 $inner.cancel -width .9i -fill y \
1823        4,0 $inner.ok -padx 2 -width .9i -fill y
1824    blt::table configure $inner r3 -height 4
1825    blt::table configure $inner r4 -pady 4
1826    raise $inner.image_button
1827    $inner.image_button invoke
1828    return $inner
1829}
1830
1831itcl::body Rappture::MapViewer::SetTerrainStyle { style } {
1832    array set settings {
1833        -color white
1834        -edgecolor black
1835        -edges 0
1836        -lighting 0
1837        -linewidth 1.0
1838        -vertscale 1.0
1839        -wireframe 0
1840    }
1841    array set settings $style
1842
1843    SendCmd "map terrain edges $settings(-edges)"
1844    set _settings(terrain-edges) $settings(-edges)
1845    #SendCmd "map terrain color [Color2RGB $settings(-color)]"
1846    #SendCmd "map terrain colormode constant"
1847    SendCmd "map terrain lighting $settings(-lighting)"
1848    set _settings(terrain-lighting) $settings(-lighting)
1849    SendCmd "map terrain linecolor [Color2RGB $settings(-edgecolor)]"
1850    #SendCmd "map terrain linewidth $settings(-linewidth)"
1851    SendCmd "map terrain vertscale $settings(-vertscale)"
1852    set _settings(terrain-vertscale) $settings(-vertscale)
1853    SendCmd "map terrain wireframe $settings(-wireframe)"
1854    set _settings(terrain-wireframe) $settings(-wireframe)
1855}
1856
1857itcl::body Rappture::MapViewer::SetLayerStyle { dataobj layer } {
1858    array set info [$dataobj layer $layer]
1859    set _visibility($layer) 1
1860
1861    switch -- $info(type) {
1862        "image" {
1863            array set settings {
1864                -min_level 0
1865                -max_level 23
1866                -opacity 1.0
1867            }
1868            if { [info exists info(style)] } {
1869                array set settings $info(style)
1870            }
1871            if { [info exists info(opacity)] } {
1872                set settings(-opacity) $info(opacity)
1873            }
1874            if {!$_sendEarthFile} {
1875                switch -- $info(driver)  {
1876                    "debug" {
1877                        SendCmd [list map layer add image debug $layer]
1878                    }
1879                    "gdal" {
1880                        SendCmd [list map layer add image gdal \
1881                                     $info(gdal.url) $layer]
1882                    }
1883                    "tms" {
1884                        SendCmd [list map layer add image tms \
1885                                     $info(tms.url) $layer]
1886                    }
1887                    "wms" {
1888                        SendCmd [list map layer add image wms \
1889                                     $info(wms.url) \
1890                                     $info(wms.layers) \
1891                                     $info(wms.format) \
1892                                     $info(wms.transparent) \
1893                                     $layer]
1894                    }
1895                    "xyz" {
1896                        SendCmd [list map layer add image xyz \
1897                                     $info(xyz.url) \
1898                                     $layer]
1899                    }
1900                }                       
1901            }
1902            SendCmd "map layer opacity $settings(-opacity) $layer"
1903        }
1904        "elevation" {
1905            array set settings {
1906                -min_level 0
1907                -max_level 23
1908            }
1909            if { [info exists info(style)] } {
1910                array set settings $info(style)
1911            }
1912            if {!$_sendEarthFile} {
1913                switch -- $info(driver)  {
1914                    "gdal" {
1915                        SendCmd [list map layer add elevation gdal \
1916                                     $info(gdal.url) $layer]
1917                    }
1918                    "tms" {
1919                        SendCmd [list map layer add elevation tms \
1920                                     $info(tms.url) $layer]
1921                    }
1922                }
1923            }
1924        }
1925        "line" {
1926            array set settings {
1927                -color black
1928                -minbias 1000
1929                -opacity 1.0
1930                -width 1
1931            }
1932            if { [info exists info(style)] } {
1933                array set settings $info(style)
1934            }
1935            if { [info exists info(opacity)] } {
1936                set settings(-opacity) $info(opacity)
1937            }
1938            SendCmd [list map layer add line $info(ogr.url) $layer]
1939            SendCmd "map layer opacity $settings(-opacity) $layer"
1940        }
1941        "polygon" {
1942            array set settings {
1943                -color white
1944                -minbias 1000
1945                -opacity 1.0
1946            }
1947            if { [info exists info(style)] } {
1948                array set settings $info(style)
1949            }
1950            if { [info exists info(opacity)] } {
1951                set settings(-opacity) $info(opacity)
1952            }
1953            SendCmd [list map layer add polygon $info(ogr.url) $layer]
1954            SendCmd "map layer opacity $settings(-opacity) $layer"
1955        }
1956        "label" {
1957            array set settings {
1958                -align "center-center"
1959                -color black
1960                -declutter 1
1961                -font Arial
1962                -fontsize 16.0
1963                -halocolor white
1964                -halowidth 2.0
1965                -layout "ltr"
1966                -minbias 1000
1967                -opacity 1.0
1968                -removedupe 1
1969            }
1970            if { [info exists info(style)] } {
1971                array set settings $info(style)
1972            }
1973            if { [info exists info(opacity)] } {
1974                set settings(-opacity) $info(opacity)
1975            }
1976            set contentExpr $info(content)
1977            if {[info exists info(priority)]} {
1978                set priorityExpr $info(priority)
1979            } else {
1980                set priorityExpr ""
1981            }
1982            SendCmd [list map layer add text $info(ogr.url) $contentExpr $priorityExpr $layer]
1983            SendCmd "map layer opacity $settings(-opacity) $layer"
1984        }
1985    }
1986
1987    if { [info exists info(visible)] } {
1988        if { !$info(visible) } {
1989            set _visibility($layer) 0
1990            SendCmd "map layer visible 0 $layer"
1991        }
1992    }
1993}
1994
1995itcl::body Rappture::MapViewer::SetOpacity { dataset } {
1996    foreach {dataobj layer} [split $dataset -] break
1997    set type [$dataobj type $layer]
1998    set val $_settings(-opacity)
1999    set sval [expr { 0.01 * double($val) }]
2000    if { !$_obj2ovride($dataobj-raise) } {
2001        # This is wrong.  Need to figure out why raise isn't set with 1
2002        #set sval [expr $sval * .6]
2003    }
2004    SendCmd "$type opacity $sval $dataset"
2005}
2006
2007itcl::body Rappture::MapViewer::ChangeLayerVisibility { dataobj layer } {
2008    set bool $_visibility($layer)
2009    SendCmd "map layer visible $bool $layer"
2010}
2011
2012itcl::body Rappture::MapViewer::UpdateLayerControls {} {
2013    set row 0
2014    set inner $_layersFrame
2015    if { [winfo exists $inner.layers] } {
2016        foreach w [winfo children $inner.layers] {
2017            destroy $w
2018        }
2019    }
2020    set f $inner.layers
2021    foreach dataobj [get -objects] {
2022        foreach layer [$dataobj layers] {
2023            array unset info
2024            array set info [$dataobj layer $layer]
2025            checkbutton $f.$layer \
2026                -text $info(label) \
2027                -variable [itcl::scope _visibility($layer)] \
2028                -command [itcl::code $this \
2029                              ChangeLayerVisibility $dataobj $layer] \
2030                    -font "Arial 9" -anchor w
2031            blt::table $f $row,0 $f.$layer -anchor w -pady 2
2032            Rappture::Tooltip::for $f.$layer $info(description)
2033            incr row
2034        }
2035    }
2036    if { $row > 0 } {
2037        blt::table configure $f r* c* -resize none
2038        blt::table configure $f r$row c1 -resize expand
2039    }
2040}
2041
2042#
2043# Generate an OSG Earth file to send to server.  This is inteneded
2044# as a stopgap and testing tool until the protocol is fleshed out.
2045#
2046# Note that the lighting settings are required to be "hard-coded"
2047# as below for the runtime control to work.  Don't make those user
2048# configurable.
2049#
2050# Also note: Use "true"/"false" for boolean settings.  Not sure if
2051# the parser in OSG Earth accepts all of Tcl's forms of boolean vals.
2052#
2053itcl::body Rappture::MapViewer::EarthFile {} {
2054    append out "<map"
2055    append out " name=\"$_mapsettings(label)\""
2056    append out " type=\"$_mapsettings(type)\""
2057    append out " version=\"2\""
2058    append out ">\n"
2059    append out " <options lighting=\"true\">\n"
2060    # FIXME: convert color setting to hex
2061    # array set style $_mapsettings(style)
2062    # if {[info exists style(-color)]} {
2063    #     set color "?"
2064    # }
2065    set color "#ffffffff"
2066    append out "  <terrain lighting=\"false\" color=\"$color\"/>\n"
2067    if { [info exists _mapsettings(projection)] } {
2068        append out "  <profile"
2069        append out " srs=\"$_mapsettings(projection)\""
2070        if { [info exists _mapsettings(extents)] } {
2071            append out " xmin=\"$_mapsettings(x1)\""
2072            append out " ymin=\"$_mapsettings(y1)\""
2073            append out " xmax=\"$_mapsettings(x2)\""
2074            append out " ymax=\"$_mapsettings(y2)\""
2075        }
2076        append out "/>\n"
2077    }
2078    append out " </options>\n"
2079
2080    foreach dataobj [get -objects] {
2081        foreach layer [$dataobj layers] {
2082            set _layers($layer) 1
2083            array unset info
2084            array set info [$dataobj layer $layer]
2085            switch -- $info(type) {
2086                "image" {
2087                    append out " <image"
2088                    append out " name=\"$layer\""
2089                    append out " driver=\"gdal\""
2090                    if { [info exists info(opacity)] } {
2091                        append out " opacity=\"$info(opacity)\""
2092                    }
2093                    if { $info(visible) } {
2094                        append out " visible=\"true\""
2095                    } else {
2096                        append out " visible=\"false\""
2097                    }
2098                    append out ">\n"
2099                    append out "  <url>$info(url)</url>\n"
2100                    append out " </image>\n"
2101                }
2102                "elevation" {
2103                    append out " <elevation"
2104                    append out " name=\"$layer\""
2105                    append out " driver=\"gdal\""
2106                    if { $info(visible) } {
2107                        append out " visible=\"true\""
2108                    } else {
2109                        append out " visible=\"false\""
2110                    }
2111                    append out ">\n"
2112                    append out "  <url>$info(url)</url>\n"
2113                    append out " </elevation>\n"
2114                }
2115                default {
2116                    puts stderr "Type $info(type) not implemented in MapViewer::EarthFile"
2117                }
2118            }
2119        }
2120    }
2121    append out "</map>\n"
2122    return $out
2123}
Note: See TracBrowser for help on using the repository browser.