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

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

Use SendData? method to send binary data in viewers, make _outbuf private in
superclass.

File size: 91.2 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 _viewpointsFrame ""; # Name of viewpoints frame widget
43    private variable _mapsettings;        # Global map settings
44
45    constructor { hostlist args } {
46        Rappture::VisViewer::constructor $hostlist
47    } {
48        # defined below
49    }
50    destructor {
51        # defined below
52    }
53    public proc SetServerList { namelist } {
54        Rappture::VisViewer::SetServerList "geovis" $namelist
55    }
56    public method add {dataobj {settings ""}}
57    public method camera {option args}
58    public method delete {args}
59    public method disconnect {}
60    public method download {option args}
61    public method get {args}
62    public method isconnected {}
63    public method parameters {title args} {
64        # do nothing
65    }
66    public method scale {args}
67
68    private method KeyPress { key }
69    private method KeyRelease { key }
70    private method MouseClick { button x y }
71    private method MouseDoubleClick { button x y }
72    private method MouseDrag { button x y }
73    private method MouseMotion {}
74    private method MouseRelease { button x y }
75    private method MouseScroll { direction }
76
77    # The following methods are only used by this class.
78    private method AdjustSetting {what {value ""}}
79    private method BuildCameraTab {}
80    private method BuildDownloadPopup { widget command }
81    private method BuildHelpTab {}
82    private method BuildLayerTab {}
83    private method BuildMapTab {}
84    private method BuildTerrainTab {}
85    private method BuildViewpointsTab {}
86    private method Connect {}
87    private method CurrentLayers {args}
88    private method DisablePanningMouseBindings {}
89    private method DisableRotationMouseBindings {}
90    private method DisableZoomMouseBindings {}
91    private method Disconnect {}
92    private method DoPan {}
93    private method DoResize {}
94    private method DoRotate {}
95    private method DoSelect {}
96    private method DrawLegend { colormap min max }
97    private method EarthFile {}
98    private method EnablePanningMouseBindings {}
99    private method EnableRotationMouseBindings {}
100    private method EnableZoomMouseBindings {}
101    private method EventuallyHandleMotionEvent { x y }
102    private method EventuallyPan { dx dy }
103    private method EventuallyResize { w h }
104    private method EventuallyRotate { dx dy }
105    private method EventuallySelect { x y }
106    private method GetImage { args }
107    private method GetNormalizedMouse { x y }
108    private method GoToViewpoint { dataobj viewpoint }
109    private method InitSettings { args  }
110    private method MapIsGeocentric {}
111    private method Pan {option x y}
112    private method Pin {option x y}
113    private method Rebuild {}
114    private method ReceiveImage { args }
115    private method ReceiveLegend { args }
116    private method ReceiveMapInfo { args }
117    private method ReceiveScreenInfo { args }
118    private method RequestLegend { colormap w h }
119    private method Rotate {option x y}
120    private method Select {option x y}
121    private method SetHeading { {value 0} }
122    private method SetLayerOpacity { dataobj layer {value 100} }
123    private method SetLayerStyle { dataobj layer }
124    private method SetLayerVisibility { dataobj layer }
125    private method SetPitch { {value -89.999} }
126    private method SetTerrainStyle { style }
127    private method ToggleGrid {}
128    private method ToggleLighting {}
129    private method ToggleWireframe {}
130    private method UpdateLayerControls {}
131    private method UpdateViewpointControls {}
132    private method Zoom {option {x 0} {y 0}}
133
134    private variable _dlist "";         # list of data objects
135    private variable _obj2datasets
136    private variable _obj2ovride;       # maps dataobj => style override
137    private variable _layers;           # Contains the names of all the
138                                        # layer in the server.
139    private variable _viewpoints;
140    private variable _click;            # info used for rotate operations
141    private variable _view;             # view params for 3D view
142    private variable _pan;
143    private variable _rotate;
144    private variable _select;
145    private variable _motion;
146    private variable _settings
147    private variable _opacity
148    private variable _visibility
149    private variable _style;            # Array of current component styles.
150    private variable _initialStyle;     # Array of initial component styles.
151    private variable _reset 1;          # Indicates that server was reset and
152                                        # needs to be reinitialized.
153    private variable _initCamera 1;
154    private variable _haveTerrain 0;
155
156    private variable _first ""     ;# This is the topmost dataset.
157    private variable _start 0
158    private variable _title ""
159
160    common _downloadPopup          ;# download options from popup
161    private common _hardcopy
162    private variable _width 0
163    private variable _height 0
164    private variable _resizePending 0
165    private variable _sendEarthFile 0
166    private variable _useServerManip 0
167    private variable _labelCount 0
168    private variable _b1mode "pan"
169}
170
171itk::usual MapViewer {
172    keep -background -foreground -cursor -font
173    keep -plotbackground -plotforeground
174}
175
176# ----------------------------------------------------------------------
177# CONSTRUCTOR
178# ----------------------------------------------------------------------
179itcl::body Rappture::MapViewer::constructor {hostlist args} {
180    set _serverType "geovis"
181
182    if { [catch {
183
184    # Rebuild event
185    $_dispatcher register !rebuild
186    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
187
188    # Resize event
189    $_dispatcher register !resize
190    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
191
192    # Pan event
193    $_dispatcher register !pan
194    $_dispatcher dispatch $this !pan "[itcl::code $this DoPan]; list"
195
196    # Rotate event
197    $_dispatcher register !rotate
198    $_dispatcher dispatch $this !rotate "[itcl::code $this DoRotate]; list"
199
200    # Select event
201    $_dispatcher register !select
202    $_dispatcher dispatch $this !select "[itcl::code $this DoSelect]; list"
203
204    # <Motion> event
205    $_dispatcher register !motion
206    $_dispatcher dispatch $this !motion "[itcl::code $this MouseMotion]; list"
207
208    #
209    # Populate parser with commands handle incoming requests
210    #
211    $_parser alias image    [itcl::code $this ReceiveImage]
212    $_parser alias legend   [itcl::code $this ReceiveLegend]
213    $_parser alias map      [itcl::code $this ReceiveMapInfo]
214    $_parser alias camera   [itcl::code $this camera]
215    $_parser alias screen   [itcl::code $this ReceiveScreenInfo]
216
217    # Millisecond delay before animated wait dialog appears
218    set _waitTimeout 900
219
220    # Settings for mouse motion events: these are required
221    # to update the Lat/Long coordinate display
222    array set _motion {
223        compress        1
224        delay           100
225        enable          1
226        pending         0
227        x               0
228        y               0
229    }
230    array set _pan {
231        compress        1
232        delay           100
233        pending         0
234        x               0
235        y               0
236    }
237    array set _rotate {
238        azimuth         0
239        compress        1
240        delay           100
241        elevation       0
242        pending         0
243    }
244    array set _select {
245        compress        1
246        delay           100
247        pending         0
248        x               0
249        y               0
250    }
251    # This array holds the Viewpoint parameters that the
252    # server sends on "camera get".
253    array set _view {
254        distance        1.0
255        heading         0.0
256        pitch           -89.9
257        srs             ""
258        verticalDatum   ""
259        x               0.0
260        y               0.0
261        z               0.0
262    }
263
264    # Note: grid types are "geodetic", "utm" and "mgrs"
265    # Currently only work in geocentric maps
266    array set _settings [subst {
267        camera-throw           0
268        coords-precision       5
269        coords-units           "latlong_decimal_degrees"
270        coords-visible         1
271        grid                   0
272        grid-type              "geodetic"
273        legend                 1
274        terrain-edges          0
275        terrain-lighting       0
276        terrain-vertscale      1.0
277        terrain-wireframe      0
278        time                   12
279    }]
280
281    set _settings(time) [clock format [clock seconds] -format %k -gmt 1]
282
283    itk_component add view {
284        canvas $itk_component(plotarea).view \
285            -highlightthickness 0 -borderwidth 0
286    } {
287        usual
288        ignore -highlightthickness -borderwidth  -background
289    }
290
291    set c $itk_component(view)
292    bind $c <Enter> "focus %W"
293    bind $c <Control-F1> [itcl::code $this ToggleConsole]
294
295    # Fix the scrollregion in case we go off screen
296    $c configure -scrollregion [$c bbox all]
297
298    set _map(id) [$c create image 0 0 -anchor nw -image $_image(plot)]
299    set _map(cwidth) -1
300    set _map(cheight) -1
301    set _map(zoom) 1.0
302    set _map(original) ""
303
304    set f [$itk_component(main) component controls]
305    itk_component add reset {
306        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
307            -highlightthickness 0 \
308            -image [Rappture::icon reset-view] \
309            -command [itcl::code $this camera reset]
310    } {
311        usual
312        ignore -highlightthickness
313    }
314    pack $itk_component(reset) -side top -padx 2 -pady 2
315    Rappture::Tooltip::for $itk_component(reset) \
316        "Reset the view to the default zoom level"
317
318    itk_component add zoomin {
319        button $f.zin -borderwidth 1 -padx 1 -pady 1 \
320            -highlightthickness 0 \
321            -image [Rappture::icon zoom-in] \
322            -command [itcl::code $this Zoom in]
323    } {
324        usual
325        ignore -highlightthickness
326    }
327    pack $itk_component(zoomin) -side top -padx 2 -pady 2
328    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
329
330    itk_component add zoomout {
331        button $f.zout -borderwidth 1 -padx 1 -pady 1 \
332            -highlightthickness 0 \
333            -image [Rappture::icon zoom-out] \
334            -command [itcl::code $this Zoom out]
335    } {
336        usual
337        ignore -highlightthickness
338    }
339    pack $itk_component(zoomout) -side top -padx 2 -pady 2
340    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
341
342    BuildLayerTab
343    BuildViewpointsTab
344    BuildMapTab
345    BuildTerrainTab
346    BuildCameraTab
347    BuildHelpTab
348
349    # Legend
350
351    set _image(legend) [image create photo]
352    itk_component add legend {
353        canvas $itk_component(plotarea).legend -width 50 -highlightthickness 0
354    } {
355        usual
356        ignore -highlightthickness
357        rename -background -plotbackground plotBackground Background
358    }
359
360    # Hack around the Tk panewindow.  The problem is that the requested
361    # size of the 3d view isn't set until an image is retrieved from
362    # the server.  So the panewindow uses the tiny size.
363    pack forget $itk_component(view)
364    blt::table $itk_component(plotarea) \
365        0,0 $itk_component(view) -fill both -reqwidth 10000
366    blt::table configure $itk_component(plotarea) c1 -resize none
367
368    bind $itk_component(view) <Configure> \
369        [itcl::code $this EventuallyResize %w %h]
370
371    EnablePanningMouseBindings
372    EnableRotationMouseBindings
373    EnableZoomMouseBindings
374
375    if {$_useServerManip} {
376        # Bindings for keyboard events
377        bind $itk_component(view) <KeyPress> \
378            [itcl::code $this KeyPress %N]
379        bind $itk_component(view) <KeyRelease> \
380            [itcl::code $this KeyRelease %N]
381
382        # Zoom to point
383        bind $itk_component(view) <Double-1> \
384            [itcl::code $this MouseDoubleClick 1 %x %y]
385        bind $itk_component(view) <Double-3> \
386            [itcl::code $this MouseDoubleClick 3 %x %y]
387
388        # Unused
389        bind $itk_component(view) <Double-2> \
390            [itcl::code $this MouseDoubleClick 2 %x %y]
391
392        # Binding for mouse motion events
393        if {$_motion(enable)} {
394            bind $itk_component(view) <Motion> \
395                [itcl::code $this EventuallyHandleMotionEvent %x %y]
396        }
397    } else {
398        # Zoom to point
399        bind $itk_component(view) <Double-1> \
400            [itcl::code $this camera go %x %y 0.4]
401        # Travel to point (no zoom)
402        bind $itk_component(view) <Shift-Double-1> \
403            [itcl::code $this camera go %x %y 1.0]
404        # Zoom out centered on point
405        bind $itk_component(view) <Double-3> \
406            [itcl::code $this camera go %x %y 2.5]
407
408        # Pin placemark annotations
409        bind $itk_component(view) <Control-ButtonPress-1> \
410            [itcl::code $this Pin add %x %y]
411        bind $itk_component(view) <Control-ButtonPress-3> \
412            [itcl::code $this Pin delete %x %y]
413
414        # Draw selection rectangle
415        bind $itk_component(view) <Shift-ButtonPress-1> \
416            [itcl::code $this Select click %x %y]
417        bind $itk_component(view) <B1-Motion> \
418            +[itcl::code $this Select drag %x %y]
419        bind $itk_component(view) <Shift-ButtonRelease-1> \
420            [itcl::code $this Select release %x %y]
421
422        # Update coordinate readout
423        bind $itk_component(view) <ButtonPress-1> \
424            +[itcl::code $this SendCmd "map setpos %x %y"]
425        bind $itk_component(view) <Double-3> \
426            +[itcl::code $this SendCmd "map setpos %x %y"]
427
428        # Bindings for panning via keyboard
429        bind $itk_component(view) <KeyPress-Left> \
430            [itcl::code $this Pan set 10 0]
431        bind $itk_component(view) <KeyPress-Right> \
432            [itcl::code $this Pan set -10 0]
433        bind $itk_component(view) <KeyPress-Up> \
434            [itcl::code $this Pan set 0 -10]
435        bind $itk_component(view) <KeyPress-Down> \
436            [itcl::code $this Pan set 0 10]
437
438        bind $itk_component(view) <Shift-KeyPress-Left> \
439            [itcl::code $this Pan set 2 0]
440        bind $itk_component(view) <Shift-KeyPress-Right> \
441            [itcl::code $this Pan set -2 0]
442        bind $itk_component(view) <Shift-KeyPress-Up> \
443            [itcl::code $this Pan set 0 -2]
444        bind $itk_component(view) <Shift-KeyPress-Down> \
445            [itcl::code $this Pan set 0 2]
446
447        # Bindings for rotation via keyboard
448        bind $itk_component(view) <Control-Left> \
449            [itcl::code $this Rotate set 10 0]
450        bind $itk_component(view) <Control-Right> \
451            [itcl::code $this Rotate set -10 0]
452        bind $itk_component(view) <Control-Up> \
453            [itcl::code $this Rotate set 0 -10]
454        bind $itk_component(view) <Control-Down> \
455            [itcl::code $this Rotate set 0 10]
456
457        bind $itk_component(view) <Control-Shift-Left> \
458            [itcl::code $this Rotate set 2 0]
459        bind $itk_component(view) <Control-Shift-Right> \
460            [itcl::code $this Rotate set -2 0]
461        bind $itk_component(view) <Control-Shift-Up> \
462            [itcl::code $this Rotate set 0 -2]
463        bind $itk_component(view) <Control-Shift-Down> \
464            [itcl::code $this Rotate set 0 2]
465
466        # Bindings for zoom via keyboard
467        bind $itk_component(view) <KeyPress-Prior> \
468            [itcl::code $this Zoom out]
469        bind $itk_component(view) <KeyPress-Next> \
470            [itcl::code $this Zoom in]
471        bind $itk_component(view) <KeyPress-Home> \
472            [itcl::code $this camera reset]
473
474        # Keyboard shortcuts
475        # Reset heading to North
476        bind $itk_component(view) <n> \
477            [itcl::code $this SetHeading]
478        # Reset pitch to top-down (2D) view
479        bind $itk_component(view) <p> \
480            [itcl::code $this SetPitch]
481        bind $itk_component(view) <g> \
482            [itcl::code $this ToggleGrid]
483        bind $itk_component(view) <l> \
484            [itcl::code $this ToggleLighting]
485        bind $itk_component(view) <w> \
486            [itcl::code $this ToggleWireframe]
487
488        # Binding for mouse motion events
489        set _motion(compress) 1
490        if {$_motion(enable)} {
491            bind $itk_component(view) <Motion> \
492                [itcl::code $this EventuallyHandleMotionEvent %x %y]
493        }
494        #bind $itk_component(view) <Motion> \
495        #    +[itcl::code $this SendCmd "map pin hover %x %y"]
496    }
497
498    bind $itk_component(view) <Enter> "focus $itk_component(view)"
499
500    if {[string equal "x11" [tk windowingsystem]]} {
501        # Bindings for zoom via mouse
502        if {$_useServerManip} {
503            bind $itk_component(view) <4> [itcl::code $this MouseScroll up]
504            bind $itk_component(view) <5> [itcl::code $this MouseScroll down]
505        } else {
506            bind $itk_component(view) <4> [itcl::code $this Zoom out]
507            bind $itk_component(view) <5> [itcl::code $this Zoom in]
508        }
509    }
510
511    set _image(download) [image create photo]
512
513    eval itk_initialize $args
514    Connect
515} errs] != 0 } {
516        puts stderr errs=$errs
517    }
518}
519
520# ----------------------------------------------------------------------
521# DESTRUCTOR
522# ----------------------------------------------------------------------
523itcl::body Rappture::MapViewer::destructor {} {
524    Disconnect
525    $_dispatcher cancel !rebuild
526    $_dispatcher cancel !resize
527    $_dispatcher cancel !rotate
528    image delete $_image(plot)
529    image delete $_image(download)
530}
531
532itcl::body Rappture::MapViewer::DoResize {} {
533    set sendResize 1
534    if { $_width < 2 } {
535        set _width 500
536        set sendResize 0
537    }
538    if { $_height < 2 } {
539        set _height 500
540        set sendResize 0
541    }
542    #set _start [clock clicks -milliseconds]
543    if {$sendResize} {
544        SendCmd "screen size $_width $_height"
545    }
546    set _resizePending 0
547}
548
549itcl::body Rappture::MapViewer::DoRotate {} {
550    SendCmd "camera rotate $_rotate(azimuth) $_rotate(elevation)"
551    set _rotate(azimuth) 0
552    set _rotate(elevation) 0
553    set _rotate(pending) 0
554}
555
556itcl::body Rappture::MapViewer::DoSelect {} {
557    SendCmd "map box update $_select(x) $_select(y)"
558    set _select(x) 0
559    set _select(y) 0
560    set _select(pending) 0
561}
562
563itcl::body Rappture::MapViewer::EventuallyResize { w h } {
564    set _width $w
565    set _height $h
566    if { !$_resizePending } {
567        set _resizePending 1
568        $_dispatcher event -after 200 !resize
569    }
570}
571
572itcl::body Rappture::MapViewer::DoPan {} {
573    SendCmd "camera pan $_pan(x) $_pan(y)"
574    set _pan(x) 0
575    set _pan(y) 0
576    set _pan(pending) 0
577}
578
579itcl::body Rappture::MapViewer::EventuallyPan { dx dy } {
580    set _pan(x) [expr $_pan(x) + $dx]
581    set _pan(y) [expr $_pan(y) + $dy]
582    if { !$_pan(compress) } {
583        DoPan
584        return
585    }
586    if { !$_pan(pending) } {
587        set _pan(pending) 1
588        $_dispatcher event -after $_pan(delay) !pan
589    }
590}
591
592itcl::body Rappture::MapViewer::EventuallyRotate { dx dy } {
593    set _rotate(azimuth) [expr $_rotate(azimuth) + $dx]
594    set _rotate(elevation) [expr $_rotate(elevation) + $dy]
595    if { !$_rotate(compress) } {
596        DoRotate
597        return
598    }
599    if { !$_rotate(pending) } {
600        set _rotate(pending) 1
601        $_dispatcher event -after $_rotate(delay) !rotate
602    }
603}
604
605itcl::body Rappture::MapViewer::EventuallySelect { x y } {
606    set _select(x) $x
607    set _select(y) $y
608    if { !$_select(compress) } {
609        DoSelect
610        return
611    }
612    if { !$_select(pending) } {
613        set _select(pending) 1
614        $_dispatcher event -after $_select(delay) !select
615    }
616}
617
618itcl::body Rappture::MapViewer::DrawLegend { colormap min max } {
619    if { [info exists itk_component(legend-$colormap) ] } {
620        $itk_component(legend-$colormap-min) configure -text $min
621        $itk_component(legend-$colormap-max) configure -text $max
622        $itk_component(legend-$colormap) configure -image $_image(legend-$colormap)
623    }
624}
625
626itcl::body Rappture::MapViewer::RequestLegend { colormap w h } {
627    SendCmd "legend $colormap $w $h 0 [Color2RGB #d9d9d9]"
628}
629
630# ----------------------------------------------------------------------
631# USAGE: add <dataobj> ?<settings>?
632#
633# Clients use this to add a data object to the plot.  The optional
634# <settings> are used to configure the plot.  Allowed settings are
635# -color, -brightness, -width, -linestyle, and -raise.
636# ----------------------------------------------------------------------
637itcl::body Rappture::MapViewer::add {dataobj {settings ""}} {
638    array set params {
639        -color auto
640        -width 1
641        -linestyle solid
642        -brightness 0
643        -raise 0
644        -description ""
645        -param ""
646        -type ""
647    }
648    array set params $settings
649    set params(-description) ""
650    set params(-param) ""
651    array set params $settings
652
653    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
654        # can't handle -autocolors yet
655        set params(-color) black
656    }
657    set pos [lsearch -exact $_dlist $dataobj]
658    if {$pos < 0} {
659        #if {[llength $_dlist] > 0} {
660        #    error "Can't add more than 1 map to mapviewer"
661        #}
662        lappend _dlist $dataobj
663    }
664    set _obj2ovride($dataobj-color) $params(-color)
665    set _obj2ovride($dataobj-width) $params(-width)
666    set _obj2ovride($dataobj-raise) $params(-raise)
667    $_dispatcher event -idle !rebuild
668}
669
670# ----------------------------------------------------------------------
671# USAGE: delete ?<dataobj1> <dataobj2> ...?
672#
673# Clients use this to delete a dataobj from the plot.  If no dataobjs
674# are specified, then all dataobjs are deleted.  No data objects are
675# deleted.  They are only removed from the display list.
676# ----------------------------------------------------------------------
677itcl::body Rappture::MapViewer::delete {args} {
678    if { [llength $args] == 0} {
679        set args $_dlist
680    }
681    # Delete all specified dataobjs
682    set changed 0
683    foreach dataobj $args {
684        set pos [lsearch -exact $_dlist $dataobj]
685        if { $pos < 0 } {
686            continue;                   # Don't know anything about it.
687        }
688        # When a map is marked deleted, we hide its layers.
689        foreach layer [$dataobj layers] {
690            SendCmd "map layer visible 0 $layer"
691            set _visibility($layer) 0
692        }
693        # Remove it from the dataobj list.
694        set _dlist [lreplace $_dlist $pos $pos]
695        array unset _obj2ovride $dataobj-*
696        array unset _settings $dataobj-*
697        set changed 1
698    }
699    # If anything changed, then rebuild the plot
700    if { $changed } {
701        $_dispatcher event -idle !rebuild
702    }
703}
704
705# ----------------------------------------------------------------------
706# USAGE: get ?-objects?
707# USAGE: get ?-visible?
708# USAGE: get ?-image view?
709#
710# Clients use this to query the list of objects being plotted, in
711# order from bottom to top of this result.  The optional "-image"
712# flag can also request the internal images being shown.
713# ----------------------------------------------------------------------
714itcl::body Rappture::MapViewer::get {args} {
715    if {[llength $args] == 0} {
716        set args "-objects"
717    }
718
719    set op [lindex $args 0]
720    switch -- $op {
721        "-objects" {
722            # put the dataobj list in order according to -raise options
723            set dlist {}
724            foreach dataobj $_dlist {
725                if { ![$dataobj isvalid] } {
726                    continue
727                }
728                if {[info exists _obj2ovride($dataobj-raise)] &&
729                    $_obj2ovride($dataobj-raise)} {
730                    set dlist [linsert $dlist 0 $dataobj]
731                } else {
732                    lappend dlist $dataobj
733                }
734            }
735            return $dlist
736        }
737        "-visible" {
738            set dlist {}
739            foreach dataobj $_dlist {
740                if { ![$dataobj isvalid] } {
741                    continue
742                }
743                if { ![info exists _obj2ovride($dataobj-raise)] } {
744                    # No setting indicates that the object isn't visible.
745                    continue
746                }
747                # Otherwise use the -raise parameter to put the object to
748                # the front of the list.
749                if { $_obj2ovride($dataobj-raise) } {
750                    set dlist [linsert $dlist 0 $dataobj]
751                } else {
752                    lappend dlist $dataobj
753                }
754            }
755            return $dlist
756        }
757        -image {
758            if {[llength $args] != 2} {
759                error "wrong # args: should be \"get -image view\""
760            }
761            switch -- [lindex $args end] {
762                view {
763                    return $_image(plot)
764                }
765                default {
766                    error "bad image name \"[lindex $args end]\": should be view"
767                }
768            }
769        }
770        default {
771            error "bad option \"$op\": should be -objects or -image"
772        }
773    }
774}
775
776itcl::body Rappture::MapViewer::MapIsGeocentric {} {
777    if { [info exists _mapsettings(type)] } {
778        return [expr {$_mapsettings(type) eq "geocentric"}]
779    } else {
780        return 0
781    }
782}
783
784# ----------------------------------------------------------------------
785# USAGE: scale ?<data1> <data2> ...?
786#
787# Sets the default limits for the overall plot according to the
788# limits of the data for all of the given <data> objects.  This
789# accounts for all objects--even those not showing on the screen.
790# Because of this, the limits are appropriate for all objects as
791# the user scans through data in the ResultSet viewer.
792# ----------------------------------------------------------------------
793itcl::body Rappture::MapViewer::scale {args} {
794    array unset _mapsettings
795    set _haveTerrain 0
796
797    # Verify that all the maps have the same global settings. For example,
798    # you can't have one map type "geocentric" and the other "projected".
799
800    foreach dataobj $args {
801        if { ![$dataobj isvalid] } {
802            continue
803        }
804        array unset hints
805        array set hints [$dataobj hints]
806        if { ![info exists _mapsettings(label)] } {
807            set _mapsettings(label) $hints(label)
808        }
809        if { ![info exists _mapsettings(style)] } {
810            set _mapsettings(style) $hints(style)
811        }
812        if { ![info exists _mapsettings(type)] } {
813            set _mapsettings(type) $hints(type)
814        } elseif { $hints(type) != $_mapsettings(type) } {
815            error "maps \"$hints(label)\" have differing types"
816        }
817        if { ![info exists _mapsettings(projection)] } {
818            set _mapsettings(projection) $hints(projection)
819        } elseif { $hints(projection) != $_mapsettings(projection) } {
820            error "maps \"$hints(label)\" have differing projections"
821        }
822        if { $hints(extents) != "" } {
823            if { ![info exists _mapsettings(extents)] } {
824                set _mapsettings(extents) $hints(extents)
825            }
826            foreach {x1 y1 x2 y2} $hints(extents) break
827            if { ![info exists _mapsettings(x1)] || $x1 < $_mapsettings(x1) } {
828                set _mapsettings(x1) $x1
829            }
830            if { ![info exists _mapsettings(y1)] || $y1 < $_mapsettings(y1) } {
831                set _mapsettings(y1) $y1
832            }
833            if { ![info exists _mapsettings(x2)] || $x2 > $_mapsettings(x2) } {
834                set _mapsettings(x2) $x2
835            }
836            if { ![info exists _mapsettings(y2)] || $y2 > $_mapsettings(y2) } {
837                set _mapsettings(y2) $y2
838            }
839        }
840        if { [info exists hints(camera)] } {
841            if { ![info exists _mapsettings(camera)] } {
842                set _mapsettings(camera) $hints(camera)
843            }
844        }
845        foreach layer [$dataobj layers] {
846            if { [$dataobj type $layer] == "elevation" } {
847                set _haveTerrain 1
848                break
849            }
850        }
851        foreach viewpoint [$dataobj viewpoints] {
852            set _viewpoints($viewpoint) [$dataobj viewpoint $viewpoint]
853            array set vp $_viewpoints($viewpoint)
854            foreach key { label description x y z distance heading pitch srs verticalDatum } {
855                if { [info exists vp($key)] } {
856                    puts stderr "$viewpoint $key $vp($key)"
857                }
858            }
859        }
860    }
861    if { $_haveTerrain } {
862        if { [$itk_component(main) exists "Terrain Settings"] } {
863            # TODO: Enable controls like vertical scale that only have
864            # an effect when terrain is present
865        }
866    }
867}
868
869# ----------------------------------------------------------------------
870# USAGE: download coming
871# USAGE: download controls <downloadCommand>
872# USAGE: download now
873#
874# Clients use this method to create a downloadable representation
875# of the plot.  Returns a list of the form {ext string}, where
876# "ext" is the file extension (indicating the type of data) and
877# "string" is the data itself.
878# ----------------------------------------------------------------------
879itcl::body Rappture::MapViewer::download {option args} {
880    switch $option {
881        coming {
882            if {[catch {
883                blt::winop snap $itk_component(plotarea) $_image(download)
884            }]} {
885                $_image(download) configure -width 1 -height 1
886                $_image(download) put #000000
887            }
888        }
889        controls {
890            set popup .mapviewerdownload
891            if { ![winfo exists .mapviewerdownload] } {
892                set inner [BuildDownloadPopup $popup [lindex $args 0]]
893            } else {
894                set inner [$popup component inner]
895            }
896            set _downloadPopup(image_controls) $inner.image_frame
897            set num [llength [get]]
898            set num [expr {($num == 1) ? "1 result" : "$num results"}]
899            set word [Rappture::filexfer::label downloadWord]
900            $inner.summary configure -text "$word $num in the following format:"
901            update idletasks            ;# Fix initial sizes
902            return $popup
903        }
904        now {
905            set popup .mapviewerdownload
906            if {[winfo exists .mapviewerdownload]} {
907                $popup deactivate
908            }
909            switch -- $_downloadPopup(format) {
910                "image" {
911                    return [$this GetImage [lindex $args 0]]
912                }
913            }
914            return ""
915        }
916        default {
917            error "bad option \"$option\": should be coming, controls, now"
918        }
919    }
920}
921
922# ----------------------------------------------------------------------
923# USAGE: Connect ?<host:port>,<host:port>...?
924#
925# Clients use this method to establish a connection to a new
926# server, or to reestablish a connection to the previous server.
927# Any existing connection is automatically closed.
928# ----------------------------------------------------------------------
929itcl::body Rappture::MapViewer::Connect {} {
930    global readyForNextFrame
931    set readyForNextFrame 1
932    set _reset 1
933    set _hosts [GetServerList "geovis"]
934    if { "" == $_hosts } {
935        return 0
936    }
937    set result [VisViewer::Connect $_hosts]
938    if { $result } {
939        if { $_reportClientInfo }  {
940            # Tell the server the viewer, hub, user and session.
941            # Do this immediately on connect before buffering any commands
942            global env
943
944            set info {}
945            set user "???"
946            if { [info exists env(USER)] } {
947                set user $env(USER)
948            }
949            set session "???"
950            if { [info exists env(SESSION)] } {
951                set session $env(SESSION)
952            }
953            lappend info "version" "$Rappture::version"
954            lappend info "build" "$Rappture::build"
955            lappend info "svnurl" "$Rappture::svnurl"
956            lappend info "installdir" "$Rappture::installdir"
957            lappend info "hub" [exec hostname]
958            lappend info "client" "mapviewer"
959            lappend info "user" $user
960            lappend info "session" $session
961            SendCmd "clientinfo [list $info]"
962        }
963
964        set w [winfo width $itk_component(view)]
965        set h [winfo height $itk_component(view)]
966        EventuallyResize $w $h
967    }
968    return $result
969}
970
971#
972# isconnected --
973#
974#   Indicates if we are currently connected to the visualization server.
975#
976itcl::body Rappture::MapViewer::isconnected {} {
977    return [VisViewer::IsConnected]
978}
979
980#
981# disconnect --
982#
983itcl::body Rappture::MapViewer::disconnect {} {
984    Disconnect
985    set _reset 1
986}
987
988#
989# Disconnect --
990#
991#   Clients use this method to disconnect from the current rendering
992#   server.
993#
994itcl::body Rappture::MapViewer::Disconnect {} {
995    VisViewer::Disconnect
996
997    $_dispatcher cancel !pan
998    $_dispatcher cancel !motion
999    $_dispatcher cancel !rebuild
1000    $_dispatcher cancel !resize
1001    $_dispatcher cancel !rotate
1002    # disconnected -- no more data sitting on server
1003    array unset _layers
1004    array unset _layersFrame
1005    global readyForNextFrame
1006    set readyForNextFrame 1
1007}
1008
1009# ----------------------------------------------------------------------
1010# USAGE: ReceiveImage -type <type> -token <token> -bytes <size>
1011#
1012# Invoked automatically whenever the "image" command comes in from
1013# the rendering server.  Indicates that binary image data with the
1014# specified <size> will follow.
1015# ----------------------------------------------------------------------
1016itcl::body Rappture::MapViewer::ReceiveImage { args } {
1017    global readyForNextFrame
1018    set readyForNextFrame 1
1019    array set info {
1020        -bytes 0
1021        -token "???"
1022        -type image
1023    }
1024    array set info $args
1025    set bytes [ReceiveBytes $info(-bytes)]
1026    if { $info(-type) == "image" } {
1027        $_image(plot) configure -data $bytes
1028    } elseif { $info(type) == "print" } {
1029        set tag $this-print-$info(-token)
1030        set _hardcopy($tag) $bytes
1031    }
1032    set _waitTimeout 0
1033}
1034
1035#
1036# ReceiveLegend
1037#
1038# Invoked automatically whenever the "legend" command comes in from
1039# the rendering server.  Indicates that binary image data with the
1040# specified <size> will follow.
1041#
1042itcl::body Rappture::MapViewer::ReceiveLegend { colormap min max size } {
1043puts stderr "ReceiveLegend colormap=$colormap range=$min,$max size=$size"
1044    if { [IsConnected] } {
1045        set bytes [ReceiveBytes $size]
1046        if { ![info exists _image(legend)] } {
1047            set _image(legend-$colormap) [image create photo]
1048        }
1049        if 0 {
1050            set f [open "/tmp/legend-${colormap}.ppm" "w"]
1051            fconfigure $f -translation binary -encoding binary
1052            puts $f $bytes
1053            close $f
1054        }
1055        $_image(legend-$colormap) configure -data $bytes
1056puts stderr "read $size bytes for [image width $_image(legend-$colormap)]x[image height $_image(legend-$colormap)] legend>"
1057        if { [catch {DrawLegend $colormap $min $max} errs] != 0 } {
1058            global errorInfo
1059            puts stderr "errs=$errs errorInfo=$errorInfo"
1060        }
1061    }
1062}
1063
1064#
1065# ReceiveMapInfo --
1066#
1067itcl::body Rappture::MapViewer::ReceiveMapInfo { args } {
1068    if { ![isconnected] } {
1069        return
1070    }
1071    set timeReceived [clock clicks -milliseconds]
1072    set elapsed [expr $timeReceived - $_start]
1073    set option [lindex $args 0]
1074    switch -- $option {
1075        "coords" {
1076            set len [llength $args]
1077            if {$len < 3} {
1078                error "Bad map coords response"
1079            } else {
1080                set token [lindex $args 1]
1081            }
1082            foreach { x y z } [lindex $args 2] {
1083                puts stderr "\[$token\] Map coords: $x $y $z"
1084            }
1085            if {$len > 3} {
1086                set srs [lindex $args 3]
1087                set vert [lindex $args 4]
1088                puts stderr "\[$token\] {$srs} {$vert}"
1089            }
1090        }
1091        "names" {
1092            foreach { name } [lindex $args 1] {
1093                puts stderr "layer: $name"
1094            }
1095        }
1096        default {
1097            error "unknown map option \"$option\" from server"
1098        }
1099    }
1100}
1101
1102#
1103# ReceiveScreenInfo --
1104#
1105itcl::body Rappture::MapViewer::ReceiveScreenInfo { args } {
1106    if { ![isconnected] } {
1107        return
1108    }
1109    set option [lindex $args 0]
1110    switch -- $option {
1111        "coords" {
1112            set len [llength $args]
1113            if {$len < 3} {
1114                error "Bad screen coords response"
1115            } else {
1116                set token [lindex $args 1]
1117            }
1118            foreach { x y z } [lindex $args 2] {
1119                puts stderr "\[$token\] Screen coords: $x $y $z"
1120            }
1121        }
1122        default {
1123            error "unknown screen option \"$option\" from server"
1124        }
1125    }
1126}
1127
1128# ----------------------------------------------------------------------
1129# USAGE: Rebuild
1130#
1131# Called automatically whenever something changes that affects the
1132# data in the widget.  Clears any existing data and rebuilds the
1133# widget to display new data.
1134# ----------------------------------------------------------------------
1135itcl::body Rappture::MapViewer::Rebuild {} {
1136    set w [winfo width $itk_component(view)]
1137    set h [winfo height $itk_component(view)]
1138    if { $w < 2 || $h < 2 } {
1139        update idletasks
1140        $_dispatcher event -idle !rebuild
1141        return
1142    }
1143
1144    # Turn on buffering of commands to the server.  We don't want to be
1145    # preempted by a server disconnect/reconnect (which automatically
1146    # generates a new call to Rebuild).
1147    StartBufferingCommands
1148
1149    if { $_reset } {
1150        set _width $w
1151        set _height $h
1152        DoResize
1153
1154        if { [info exists _mapsettings(type)] } {
1155            # The map must be reset once before any layers are added This
1156            # should not be done more than once as it is very expensive.
1157            if {$_sendEarthFile} {
1158                set bytes [EarthFile]
1159                if {0} {
1160                    set f [open "/tmp/map.earth" "w"]
1161                    puts $f $bytes
1162                    close $f
1163                }
1164                set length [string length $bytes]
1165                SendCmd "map load data follows $length"
1166                SendData $bytes
1167            } else {
1168                if { [info exists _mapsettings(style)] } {
1169                    array set settings {
1170                        -color white
1171                    }
1172                    array set settings $_mapsettings(style)
1173                }
1174                set bgcolor [Color2RGB $settings(-color)]
1175                if { $_mapsettings(type) == "geocentric" } {
1176                    $itk_component(grid) configure -state normal
1177                    $itk_component(time_l) configure -state normal
1178                    $itk_component(time) configure -state normal
1179                    $itk_component(pitch_slider_l) configure -state normal
1180                    $itk_component(pitch_slider) configure -state normal
1181                    EnableRotationMouseBindings
1182                    SendCmd "map reset geocentric $bgcolor"
1183                }  else {
1184                    $itk_component(grid) configure -state disabled
1185                    $itk_component(time_l) configure -state disabled
1186                    $itk_component(time) configure -state disabled
1187                    $itk_component(pitch_slider_l) configure -state disabled
1188                    $itk_component(pitch_slider) configure -state disabled
1189                    DisableRotationMouseBindings
1190                    set proj $_mapsettings(projection)
1191                    SendCmd "screen bgcolor $bgcolor"
1192                    if { $proj == "" } {
1193                        SendCmd "map reset projected $bgcolor global-mercator"
1194                    } elseif { ![info exists _mapsettings(extents)] || $_mapsettings(extents) == "" } {
1195                        SendCmd "map reset projected $bgcolor [list $proj]"
1196                    } else {
1197                        #foreach {x1 y1 x2 y2} $_mapsettings(extents) break
1198                        foreach key "x1 y1 x2 y2" {
1199                            set $key $_mapsettings($key)
1200                        }
1201                        SendCmd "map reset projected $bgcolor [list $proj] $x1 $y1 $x2 $y2"
1202                    }
1203                }
1204                # XXX: Remove these after implementing batch load of layers with reset
1205                SendCmd "map layer delete base"
1206            }
1207
1208            # Most terrain settings are global to the map and apply even
1209            # if there is no elevation layer.  The exception is the
1210            # vertical scale, which only applies if there is an elevation
1211            # layer
1212            if { [info exists _mapsettings(style)] } {
1213                SetTerrainStyle $_mapsettings(style)
1214            } else {
1215                InitSettings terrain-edges terrain-lighting \
1216                    terrain-vertscale terrain-wireframe
1217            }
1218            InitSettings coords-visible
1219        } else {
1220            error "No map settings on reset"
1221        }
1222    }
1223
1224    set _first ""
1225    set count 0
1226
1227    set haveTerrain 0
1228    foreach dataobj [get -objects] {
1229        set _obj2datasets($dataobj) ""
1230        foreach layer [$dataobj layers] {
1231            array unset info
1232            array set info [$dataobj layer $layer]
1233            if { ![info exists _layers($layer)] } {
1234                if { $_reportClientInfo }  {
1235                    set cinfo {}
1236                    lappend cinfo "tool_id"       [$dataobj hints toolid]
1237                    lappend cinfo "tool_name"     [$dataobj hints toolname]
1238                    lappend cinfo "tool_title"    [$dataobj hints tooltitle]
1239                    lappend cinfo "tool_command"  [$dataobj hints toolcommand]
1240                    lappend cinfo "tool_revision" [$dataobj hints toolrevision]
1241                    lappend cinfo "dataset_label" [$dataobj hints label]
1242                    lappend cinfo "dataset_tag"   $layer
1243                    SendCmd "clientinfo [list $cinfo]"
1244                }
1245                set _layers($layer) 1
1246                SetLayerStyle $dataobj $layer
1247            }
1248            if {$info(type) == "elevation"} {
1249                set haveTerrain 1
1250            }
1251            lappend _obj2datasets($dataobj) $layer
1252            # FIXME: This is overriding all layers' initial visibility setting
1253            if { [info exists _obj2ovride($dataobj-raise)] } {
1254                SendCmd "map layer visible 1 $layer"
1255                set _visibility($layer) 1
1256                #SetLayerOpacity $dataobj $layer
1257            }
1258        }
1259    }
1260
1261    if ($haveTerrain) {
1262        $itk_component(vscale_l) configure -state normal
1263        $itk_component(vscale) configure -state normal
1264    } else {
1265        $itk_component(vscale_l) configure -state disabled
1266        $itk_component(vscale) configure -state disabled
1267    }
1268
1269    if {$_reset} {
1270        if {$_initCamera} {
1271            # If this is the first Rebuild, we need to
1272            # set up the initial view settings if there
1273            # are any
1274            if { [info exists _mapsettings(camera)] } {
1275                set location $_mapsettings(camera)
1276                if { $location != "" } {
1277                    array set _view $location
1278                    camera set all
1279                }
1280            }
1281            set _initCamera 0
1282        } else {
1283            # Restore view from before reconnect
1284            camera set all
1285        }
1286    }
1287
1288    UpdateLayerControls
1289    UpdateViewpointControls
1290    set _reset 0
1291    global readyForNextFrame
1292    set readyForNextFrame 0;            # Don't advance to the next frame
1293                                        # until we get an image.
1294
1295    # Actually write the commands to the server socket.  If it fails, we
1296    # don't care.  We're finished here.
1297    blt::busy hold $itk_component(hull)
1298    StopBufferingCommands
1299    blt::busy release $itk_component(hull)
1300}
1301
1302itcl::body Rappture::MapViewer::EnablePanningMouseBindings {} {
1303    if {$_useServerManip} {
1304        bind $itk_component(view) <ButtonPress-1> \
1305            [itcl::code $this MouseClick 1 %x %y]
1306        bind $itk_component(view) <B1-Motion> \
1307            [itcl::code $this MouseDrag 1 %x %y]
1308        bind $itk_component(view) <ButtonRelease-1> \
1309            [itcl::code $this MouseRelease 1 %x %y]
1310    } else {
1311        bind $itk_component(view) <ButtonPress-1> \
1312            [itcl::code $this Pan click %x %y]
1313        bind $itk_component(view) <B1-Motion> \
1314            [itcl::code $this Pan drag %x %y]
1315        bind $itk_component(view) <ButtonRelease-1> \
1316            [itcl::code $this Pan release %x %y]
1317    }
1318}
1319
1320itcl::body Rappture::MapViewer::DisablePanningMouseBindings {} {
1321    bind $itk_component(view) <ButtonPress-1> {}
1322    bind $itk_component(view) <B1-Motion> {}
1323    bind $itk_component(view) <ButtonRelease-1> {}
1324}
1325
1326itcl::body Rappture::MapViewer::EnableRotationMouseBindings {} {
1327    if {$_useServerManip} {
1328        bind $itk_component(view) <ButtonPress-2> \
1329            [itcl::code $this Rotate click %x %y]
1330        bind $itk_component(view) <B2-Motion> \
1331            [itcl::code $this Rotate drag %x %y]
1332        bind $itk_component(view) <ButtonRelease-2> \
1333            [itcl::code $this Rotate release %x %y]
1334    } else {
1335        # Bindings for rotation via mouse
1336        bind $itk_component(view) <ButtonPress-2> \
1337            [itcl::code $this MouseClick 2 %x %y]
1338        bind $itk_component(view) <B2-Motion> \
1339            [itcl::code $this MouseDrag 2 %x %y]
1340        bind $itk_component(view) <ButtonRelease-2> \
1341            [itcl::code $this MouseRelease 2 %x %y]
1342    }
1343}
1344
1345itcl::body Rappture::MapViewer::DisableRotationMouseBindings {} {
1346    bind $itk_component(view) <ButtonPress-2> {}
1347    bind $itk_component(view) <B2-Motion> {}
1348    bind $itk_component(view) <ButtonRelease-2> {}
1349}
1350
1351itcl::body Rappture::MapViewer::EnableZoomMouseBindings {} {
1352    if {$_useServerManip} {
1353        bind $itk_component(view) <ButtonPress-3> \
1354            [itcl::code $this MouseClick 3 %x %y]
1355        bind $itk_component(view) <B3-Motion> \
1356            [itcl::code $this MouseDrag 3 %x %y]
1357        bind $itk_component(view) <ButtonRelease-3> \
1358            [itcl::code $this MouseRelease 3 %x %y]
1359    } else {
1360        bind $itk_component(view) <ButtonPress-3> \
1361            [itcl::code $this Zoom click %x %y]
1362        bind $itk_component(view) <B3-Motion> \
1363            [itcl::code $this Zoom drag %x %y]
1364        bind $itk_component(view) <ButtonRelease-3> \
1365            [itcl::code $this Zoom release %x %y]
1366    }
1367}
1368
1369itcl::body Rappture::MapViewer::DisableZoomMouseBindings {} {
1370    bind $itk_component(view) <ButtonPress-3> {}
1371    bind $itk_component(view) <B3-Motion> {}
1372    bind $itk_component(view) <ButtonRelease-3> {}
1373}
1374# ----------------------------------------------------------------------
1375# USAGE: CurrentLayers ?-all -visible? ?dataobjs?
1376#
1377# Returns a list of server IDs for the current datasets being displayed.
1378# This is normally a single ID, but it might be a list of IDs if the
1379# current data object has multiple components.
1380# ----------------------------------------------------------------------
1381itcl::body Rappture::MapViewer::CurrentLayers {args} {
1382    set flag [lindex $args 0]
1383    switch -- $flag {
1384        "-all" {
1385            if { [llength $args] > 1 } {
1386                error "CurrentLayers: can't specify dataobj after \"-all\""
1387            }
1388            set dlist [get -objects]
1389        }
1390        "-visible" {
1391            if { [llength $args] > 1 } {
1392                set dlist {}
1393                set args [lrange $args 1 end]
1394                foreach dataobj $args {
1395                    if { [info exists _obj2ovride($dataobj-raise)] } {
1396                        lappend dlist $dataobj
1397                    }
1398                }
1399            } else {
1400                set dlist [get -visible]
1401            }
1402        }
1403        default {
1404            set dlist $args
1405        }
1406    }
1407    set rlist ""
1408    foreach dataobj $dlist {
1409        foreach layer [$dataobj layers] {
1410            if { [info exists _layers($layer)] && $_layers($layer) } {
1411                lappend rlist $layer
1412            }
1413        }
1414    }
1415    return $rlist
1416}
1417
1418itcl::body Rappture::MapViewer::KeyPress {k} {
1419    SendCmd "key press $k"
1420}
1421
1422itcl::body Rappture::MapViewer::KeyRelease {k} {
1423    SendCmd "key release $k"
1424}
1425
1426itcl::body Rappture::MapViewer::GetNormalizedMouse {x y} {
1427    set w [winfo width $itk_component(view)]
1428    set h [winfo height $itk_component(view)]
1429    set x [expr {(2.0 * double($x)/$w) - 1.0}]
1430    set y [expr {(2.0 * double($y)/$h) - 1.0}]
1431    return [list $x $y]
1432}
1433
1434itcl::body Rappture::MapViewer::MouseClick {button x y} {
1435    SendCmd "mouse click $button $x $y"
1436}
1437
1438itcl::body Rappture::MapViewer::MouseDoubleClick {button x y} {
1439    SendCmd "mouse dblclick $button $x $y"
1440}
1441
1442itcl::body Rappture::MapViewer::MouseDrag {button x y} {
1443    SendCmd "mouse drag $button $x $y"
1444}
1445
1446itcl::body Rappture::MapViewer::MouseRelease {button x y} {
1447    SendCmd "mouse release $button $x $y"
1448}
1449
1450itcl::body Rappture::MapViewer::MouseMotion {} {
1451    #SendCmd "mouse motion $_motion(x) $_motion(y)"
1452    SendCmd "map pin hover $_motion(x) $_motion(y)"
1453    set _motion(pending) 0
1454}
1455
1456itcl::body Rappture::MapViewer::MouseScroll {direction} {
1457    switch -- $direction {
1458        "up" {
1459            SendCmd "mouse scroll 1"
1460        }
1461        "down" {
1462            SendCmd "mouse scroll -1"
1463        }
1464    }
1465}
1466
1467#
1468# EventuallyHandleMotionEvent --
1469#
1470#   This routine compresses (no button press) motion events.  It
1471#   delivers a server mouse command once every 100 milliseconds (if a
1472#   motion event is pending).
1473#
1474itcl::body Rappture::MapViewer::EventuallyHandleMotionEvent {x y} {
1475    set _motion(x) $x
1476    set _motion(y) $y
1477    if { !$_motion(compress) } {
1478        MouseMotion
1479        return
1480    }
1481    if { !$_motion(pending) } {
1482        set _motion(pending) 1
1483        $_dispatcher event -after $_motion(delay) !motion
1484    }
1485}
1486
1487# ----------------------------------------------------------------------
1488# USAGE: Zoom in
1489# USAGE: Zoom out
1490# USAGE: Zoom reset
1491#        $this Zoom click x y
1492#        $this Zoom drag x y
1493#        $this Zoom release x y
1494#
1495# Called automatically when the user clicks on one of the zoom
1496# controls for this widget.  Changes the zoom for the current view.
1497# Also implements mouse zoom.
1498# ----------------------------------------------------------------------
1499itcl::body Rappture::MapViewer::Zoom {option {x 0} {y 0}} {
1500    switch -- $option {
1501        "in" {
1502            # z here is normalized mouse Y delta
1503            set z -0.25
1504            SendCmd "camera zoom $z"
1505        }
1506        "out" {
1507            # z here is normalized mouse Y delta
1508            set z 0.25
1509            SendCmd "camera zoom $z"
1510        }
1511        "reset" {
1512            SendCmd "camera dist $_view(distance)"
1513        }
1514        "click" {
1515            set _click(x) $x
1516            set _click(y) $y
1517            $itk_component(view) configure -cursor hand1
1518        }
1519        "drag" {
1520            if { ![info exists _click(x)] } {
1521                set _click(x) $x
1522            }
1523            if { ![info exists _click(y)] } {
1524                set _click(y) $y
1525            }
1526            set h [winfo height $itk_component(view)]
1527            set dy [expr ($_click(y) - $y)/double($h)]
1528            set _click(x) $x
1529            set _click(y) $y
1530            if {[expr (abs($dy) > 0.0)]} {
1531                SendCmd "camera zoom $dy"
1532            }
1533        }
1534        "release" {
1535            Zoom drag $x $y
1536            $itk_component(view) configure -cursor ""
1537        }
1538    }
1539}
1540
1541# ----------------------------------------------------------------------
1542# USAGE: Rotate click <x> <y>
1543# USAGE: Rotate drag <x> <y>
1544# USAGE: Rotate release <x> <y>
1545#
1546# Called automatically when the user clicks/drags/releases in the
1547# plot area.  Moves the plot according to the user's actions.
1548# ----------------------------------------------------------------------
1549itcl::body Rappture::MapViewer::Rotate {option x y} {
1550    switch -- $option {
1551        "click" {
1552            $itk_component(view) configure -cursor fleur
1553            set _click(x) $x
1554            set _click(y) $y
1555            set _rotate(azimuth) 0
1556            set _rotate(elevation) 0
1557        }
1558        "drag" {
1559            if {[array size _click] == 0} {
1560                Rotate click $x $y
1561            } else {
1562                set w [winfo width $itk_component(view)]
1563                set h [winfo height $itk_component(view)]
1564                if {$w <= 0 || $h <= 0} {
1565                    return
1566                }
1567                set dx [expr ($x - $_click(x))/double($w)]
1568                set dy [expr ($_click(y) - $y)/double($h)]
1569                set _click(x) $x
1570                set _click(y) $y
1571                if {[expr (abs($dx) > 0.0 || abs($dy) > 0.0)]} {
1572                    #SendCmd "camera rotate $dx $dy"
1573                    EventuallyRotate $dx $dy
1574                }
1575            }
1576        }
1577        "release" {
1578            Rotate drag $x $y
1579            $itk_component(view) configure -cursor ""
1580            catch {unset _click}
1581        }
1582        "set" {
1583            set w [winfo width $itk_component(view)]
1584            set h [winfo height $itk_component(view)]
1585            set dx [expr $x / double($w)]
1586            set dy [expr $y / double($h)]
1587            if {[expr (abs($dx) > 0.0 || abs($dy) > 0.0)]} {
1588                EventuallyRotate $dx $dy
1589            }
1590        }
1591        default {
1592            error "bad option \"$option\": should be click, drag, release"
1593        }
1594    }
1595}
1596
1597itcl::body Rappture::MapViewer::Select {option x y} {
1598    switch -- $option {
1599        "click" {
1600            set _click(x) $x
1601            set _click(y) $y
1602            set _b1mode "select"
1603            SendCmd "map box init $x $y"
1604        }
1605        "drag" {
1606            if {$_b1mode == "select"} {
1607                EventuallySelect $x $y
1608            }
1609        }
1610        "release" {
1611            set _b1mode ""
1612            if {$_click(x) == $x &&
1613                $_click(y) == $y} {
1614                SendCmd "map box clear"
1615            }
1616        }
1617    }
1618}
1619
1620itcl::body Rappture::MapViewer::Pin {option x y} {
1621    set _click(x) $x
1622    set _click(y) $y
1623    switch -- $option {
1624        "add" {
1625            incr _labelCount
1626            set label "Label $_labelCount"
1627            SendCmd [list "map" "pin" "add" $x $y $label]
1628        }
1629        "delete" {
1630            SendCmd "map pin delete $x $y"
1631        }
1632    }
1633}
1634
1635# ----------------------------------------------------------------------
1636# USAGE: $this Pan set x y
1637#        $this Pan click x y
1638#        $this Pan drag x y
1639#        $this Pan release x y
1640#
1641# Called automatically when the user clicks on one of the zoom
1642# controls for this widget.  Changes the zoom for the current view.
1643# ----------------------------------------------------------------------
1644itcl::body Rappture::MapViewer::Pan {option x y} {
1645    switch -- $option {
1646        "set" {
1647            set w [winfo width $itk_component(view)]
1648            set h [winfo height $itk_component(view)]
1649            set x [expr $x / double($w)]
1650            set y [expr $y / double($h)]
1651            if {[expr (abs($x) > 0.0 || abs($y) > 0.0)]} {
1652                SendCmd "camera pan $x $y"
1653            }
1654            return
1655        }
1656        "click" {
1657            set _click(x) $x
1658            set _click(y) $y
1659            set _pan(x) 0
1660            set _pan(y) 0
1661            $itk_component(view) configure -cursor hand1
1662            set _b1mode "pan"
1663        }
1664        "drag" {
1665            if {$_b1mode != "pan"} {
1666                return
1667            }
1668            if { ![info exists _click(x)] } {
1669                set _click(x) $x
1670            }
1671            if { ![info exists _click(y)] } {
1672                set _click(y) $y
1673            }
1674            set w [winfo width $itk_component(view)]
1675            set h [winfo height $itk_component(view)]
1676            set dx [expr ($x - $_click(x))/double($w)]
1677            set dy [expr ($_click(y) - $y)/double($h)]
1678            set _click(x) $x
1679            set _click(y) $y
1680            if {[expr (abs($dx) > 0.0 || abs($dy) > 0.0)]} {
1681                EventuallyPan $dx $dy
1682                #SendCmd "camera pan $dx $dy"
1683            }
1684        }
1685        "release" {
1686            Pan drag $x $y
1687            $itk_component(view) configure -cursor ""
1688            set _b1mode ""
1689        }
1690        default {
1691            error "unknown option \"$option\": should set, click, drag, or release"
1692        }
1693    }
1694}
1695
1696itcl::body Rappture::MapViewer::SetHeading { {value 0} } {
1697    set _view(heading) $value
1698    camera set heading
1699}
1700
1701itcl::body Rappture::MapViewer::SetPitch { {value -89.999} } {
1702    set _view(pitch) $value
1703    camera set pitch
1704}
1705
1706# ----------------------------------------------------------------------
1707# USAGE: InitSettings <what> ?<value>?
1708#
1709# Used internally to update rendering settings whenever parameters
1710# change in the popup settings panel.  Sends the new settings off
1711# to the back end.
1712# ----------------------------------------------------------------------
1713itcl::body Rappture::MapViewer::InitSettings { args } {
1714    foreach setting $args {
1715        AdjustSetting $setting
1716    }
1717}
1718
1719#
1720# AdjustSetting --
1721#
1722#   Changes/updates a specific setting in the widget.  There are
1723#   usually user-setable option.  Commands are sent to the render
1724#   server.
1725#
1726itcl::body Rappture::MapViewer::AdjustSetting {what {value ""}} {
1727    if { ![isconnected] } {
1728        return
1729    }
1730    switch -- $what {
1731        "coords-visible" - "coords-precision" - "coords-units" {
1732            set bool $_settings(coords-visible)
1733            set units $_settings(coords-units)
1734            set precision $_settings(coords-precision)
1735            SendCmd "map posdisp $bool $units $precision"
1736        }
1737        "grid" - "grid-type" {
1738            set bool $_settings(grid)
1739            set gridType $_settings(grid-type)
1740            SendCmd "map grid $bool $gridType"
1741        }
1742        "camera-throw" {
1743            set bool $_settings($what)
1744            SendCmd "camera throw $bool"
1745        }
1746        "terrain-edges" {
1747            set bool $_settings($what)
1748            SendCmd "map terrain edges $bool"
1749        }
1750        "terrain-lighting" {
1751            set bool $_settings($what)
1752            SendCmd "map terrain lighting $bool"
1753        }
1754        "terrain-palette" {
1755            set cmap [$itk_component(terrainpalette) value]
1756            #SendCmd "map terrain colormap $cmap"
1757        }
1758        "terrain-vertscale" {
1759            set val $_settings($what)
1760            SendCmd "map terrain vertscale $val"
1761        }
1762        "terrain-wireframe" {
1763            set bool $_settings($what)
1764            SendCmd "map terrain wireframe $bool"
1765        }
1766        "time" {
1767            set val $_settings($what)
1768            SendCmd "map time $val"
1769        }
1770        default {
1771            error "don't know how to fix $what"
1772        }
1773    }
1774}
1775
1776# ----------------------------------------------------------------------
1777# CONFIGURATION OPTION: -plotbackground
1778# ----------------------------------------------------------------------
1779itcl::configbody Rappture::MapViewer::plotbackground {
1780    if { [isconnected] } {
1781        set rgb [Color2RGB $itk_option(-plotbackground)]
1782        SendCmd "screen bgcolor $rgb"
1783    }
1784}
1785
1786# ----------------------------------------------------------------------
1787# CONFIGURATION OPTION: -plotforeground
1788# ----------------------------------------------------------------------
1789itcl::configbody Rappture::MapViewer::plotforeground {
1790    if { [isconnected] } {
1791        set rgb [Color2RGB $itk_option(-plotforeground)]
1792        # FIXME: Set font foreground colors
1793    }
1794}
1795
1796itcl::body Rappture::MapViewer::BuildMapTab {} {
1797    set fg [option get $itk_component(hull) font Font]
1798    #set bfg [option get $itk_component(hull) boldFont Font]
1799
1800    set inner [$itk_component(main) insert end \
1801        -title "Map Settings" \
1802        -icon [Rappture::icon wrench]]
1803    $inner configure -borderwidth 4
1804
1805    checkbutton $inner.posdisp \
1806        -text "Show Coordinate Readout" \
1807        -variable [itcl::scope _settings(coords-visible)] \
1808        -command [itcl::code $this AdjustSetting coords-visible] \
1809        -font "Arial 9" -anchor w
1810
1811    itk_component add grid {
1812        checkbutton $inner.grid \
1813        -text "Show Graticule" \
1814        -variable [itcl::scope _settings(grid)] \
1815        -command [itcl::code $this AdjustSetting grid] \
1816        -font "Arial 9" -anchor w
1817    } {
1818        ignore -font
1819    }
1820    Rappture::Tooltip::for $inner.grid "Toggle graticule (grid) display <g>"
1821
1822    checkbutton $inner.wireframe \
1823        -text "Show Wireframe" \
1824        -variable [itcl::scope _settings(terrain-wireframe)] \
1825        -command [itcl::code $this AdjustSetting terrain-wireframe] \
1826        -font "Arial 9" -anchor w
1827    Rappture::Tooltip::for $inner.wireframe "Toggle wireframe rendering of terrain geometry <w>"
1828
1829    checkbutton $inner.lighting \
1830        -text "Enable Lighting" \
1831        -variable [itcl::scope _settings(terrain-lighting)] \
1832        -command [itcl::code $this AdjustSetting terrain-lighting] \
1833        -font "Arial 9" -anchor w
1834    Rappture::Tooltip::for $inner.lighting "Toggle sky lighting of terrain <l>"
1835
1836    checkbutton $inner.edges \
1837        -text "Show Edges" \
1838        -variable [itcl::scope _settings(terrain-edges)] \
1839        -command [itcl::code $this AdjustSetting terrain-edges] \
1840        -font "Arial 9" -anchor w
1841
1842    itk_component add time_l {
1843        label $inner.time_l -text "Time (UTC)" -font "Arial 9"
1844    } {
1845        ignore -font
1846    }
1847    itk_component add time {
1848        ::scale $inner.time -from 0 -to 23.9 -orient horizontal \
1849            -resolution 0.1 \
1850            -variable [itcl::scope _settings(time)] \
1851            -width 10 \
1852            -showvalue on \
1853            -command [itcl::code $this AdjustSetting time]
1854    }
1855
1856    blt::table $inner \
1857        0,0 $inner.posdisp   -cspan 2 -anchor w -pady 2 \
1858        1,0 $inner.grid      -cspan 2 -anchor w -pady 2 \
1859        2,0 $inner.wireframe -cspan 2 -anchor w -pady 2 \
1860        3,0 $inner.lighting  -cspan 2 -anchor w -pady 2 \
1861        4,0 $inner.time_l    -cspan 2 -anchor w -pady 2 \
1862        4,1 $inner.time      -cspan 2 -fill x   -pady 2
1863#        4,0 $inner.edges     -cspan 2  -anchor w -pady 2
1864
1865    blt::table configure $inner r* c* -resize none
1866    blt::table configure $inner r5 c1 -resize expand
1867}
1868
1869itcl::body Rappture::MapViewer::BuildTerrainTab {} {
1870    set fg [option get $itk_component(hull) font Font]
1871    #set bfg [option get $itk_component(hull) boldFont Font]
1872
1873    set inner [$itk_component(main) insert end \
1874        -title "Terrain Settings" \
1875        -icon [Rappture::icon terrain]]
1876    $inner configure -borderwidth 4
1877
1878    label $inner.palette_l -text "Palette" -font "Arial 9" -anchor w
1879    itk_component add terrainpalette {
1880        Rappture::Combobox $inner.palette -width 10 -editable no
1881    }
1882    $inner.palette choices insert end [GetColormapList]
1883
1884    $itk_component(terrainpalette) value "BCGYR"
1885    bind $inner.palette <<Value>> \
1886        [itcl::code $this AdjustSetting terrain-palette]
1887
1888    itk_component add vscale_l {
1889        label $inner.vscale_l -text "Vertical Scale" -font "Arial 9" -anchor w
1890    }
1891    itk_component add vscale {
1892        ::scale $inner.vscale -from 0 -to 10 -orient horizontal \
1893            -variable [itcl::scope _settings(terrain-vertscale)] \
1894            -width 10 \
1895            -resolution 0.1 \
1896            -showvalue on \
1897            -command [itcl::code $this AdjustSetting terrain-vertscale]
1898    }
1899    $inner.vscale set $_settings(terrain-vertscale)
1900
1901    blt::table $inner \
1902        0,0 $inner.vscale_l  -anchor w -pady 2 \
1903        0,1 $inner.vscale    -fill x   -pady 2
1904#        1,0 $inner.palette_l -anchor w -pady 2 \
1905#        1,1 $inner.palette   -fill x   -pady 2
1906
1907    blt::table configure $inner r* c* -resize none
1908    blt::table configure $inner r3 c1 -resize expand
1909}
1910
1911itcl::body Rappture::MapViewer::BuildLayerTab {} {
1912    set fg [option get $itk_component(hull) font Font]
1913    #set bfg [option get $itk_component(hull) boldFont Font]
1914
1915    set inner [$itk_component(main) insert end \
1916        -title "Layers" \
1917        -icon [Rappture::icon layers]]
1918    $inner configure -borderwidth 4
1919    set f [frame $inner.layers]
1920    blt::table $inner \
1921        0,0 $f -fill both
1922    set _layersFrame $inner
1923}
1924
1925itcl::body Rappture::MapViewer::BuildViewpointsTab {} {
1926    set fg [option get $itk_component(hull) font Font]
1927    #set bfg [option get $itk_component(hull) boldFont Font]
1928
1929    set inner [$itk_component(main) insert end \
1930        -title "Places" \
1931        -icon [Rappture::icon placemark16]]
1932    $inner configure -borderwidth 4
1933    set f [frame $inner.viewpoints]
1934    blt::table $inner \
1935        0,0 $f -fill both
1936    set _viewpointsFrame $inner
1937}
1938
1939itcl::body Rappture::MapViewer::BuildCameraTab {} {
1940    set inner [$itk_component(main) insert end \
1941        -title "Camera Settings" \
1942        -icon [Rappture::icon camera]]
1943    $inner configure -borderwidth 4
1944
1945    set row 0
1946
1947    set labels { x y z heading pitch distance }
1948    foreach tag $labels {
1949        label $inner.${tag}label -text $tag -font "Arial 9"
1950        entry $inner.${tag} -font "Arial 9"  -bg white \
1951            -textvariable [itcl::scope _view($tag)]
1952        bind $inner.${tag} <KeyPress-Return> \
1953            [itcl::code $this camera set ${tag}]
1954        bind $inner.${tag} <KP_Enter> \
1955            [itcl::code $this camera set ${tag}]
1956        blt::table $inner \
1957            $row,0 $inner.${tag}label -anchor e -pady 2 \
1958            $row,1 $inner.${tag} -anchor w -pady 2
1959        blt::table configure $inner r$row -resize none
1960        incr row
1961    }
1962    set labels { srs verticalDatum }
1963    foreach tag $labels {
1964        label $inner.${tag}label -text $tag -font "Arial 9"
1965        entry $inner.${tag} -font "Arial 9"  -bg white \
1966            -textvariable [itcl::scope _view($tag)]
1967        bind $inner.${tag} <KeyPress-Return> \
1968            [itcl::code $this camera set ${tag}]
1969        bind $inner.${tag} <KP_Enter> \
1970            [itcl::code $this camera set ${tag}]
1971        blt::table $inner \
1972            $row,0 $inner.${tag}label -anchor e -pady 2 \
1973            $row,1 $inner.${tag} -anchor w -pady 2
1974        blt::table configure $inner r$row -resize none
1975        incr row
1976    }
1977
1978    if {0} {
1979    button $inner.get \
1980        -text "Get Camera Settings" \
1981        -font "Arial 9" \
1982        -command [itcl::code $this SendCmd "camera get"]
1983    blt::table $inner \
1984        $row,0 $inner.get -anchor w -pady 2 -cspan 2
1985    blt::table configure $inner r$row -resize none
1986    incr row
1987
1988    button $inner.set \
1989        -text "Apply Camera Settings" \
1990        -font "Arial 9" \
1991        -command [itcl::code $this camera set all]
1992    blt::table $inner \
1993        $row,0 $inner.set -anchor w -pady 2 -cspan 2
1994    blt::table configure $inner r$row -resize none
1995    incr row
1996    }
1997
1998    if {$_useServerManip} {
1999        checkbutton $inner.throw \
2000            -text "Enable Throw" \
2001            -font "Arial 9" \
2002            -variable [itcl::scope _settings(camera-throw)] \
2003            -command [itcl::code $this AdjustSetting camera-throw]
2004        blt::table $inner \
2005            $row,0 $inner.throw -anchor w -pady 2 -cspan 2
2006        blt::table configure $inner r$row -resize none
2007        incr row
2008    }
2009
2010    label $inner.heading_slider_l -text "Heading" -font "Arial 9"
2011    ::scale $inner.heading_slider -font "Arial 9" \
2012        -from -180 -to 180 -orient horizontal \
2013        -variable [itcl::scope _view(heading)] \
2014        -width 10 \
2015        -showvalue on \
2016        -command [itcl::code $this camera set heading]
2017
2018    blt::table $inner \
2019            $row,0 $inner.heading_slider_l -anchor w -pady 2
2020    blt::table $inner \
2021            $row,1 $inner.heading_slider -fill x -anchor w -pady 2
2022    blt::table configure $inner r$row -resize none
2023    incr row
2024
2025    itk_component add pitch_slider_l {
2026        label $inner.pitch_slider_l -text "Pitch" -font "Arial 9"
2027    }
2028    itk_component add pitch_slider {
2029        ::scale $inner.pitch_slider -font "Arial 9" \
2030            -from -10 -to -90 -orient horizontal \
2031            -variable [itcl::scope _view(pitch)] \
2032            -width 10 \
2033            -showvalue on \
2034            -command [itcl::code $this camera set pitch]
2035    }
2036
2037    blt::table $inner \
2038            $row,0 $inner.pitch_slider_l -anchor w -pady 2
2039    blt::table $inner \
2040            $row,1 $inner.pitch_slider -fill x -anchor w -pady 2
2041    blt::table configure $inner r$row -resize none
2042    incr row
2043
2044    blt::table configure $inner c* r* -resize none
2045    blt::table configure $inner c2 -resize expand
2046    blt::table configure $inner r$row -resize expand
2047}
2048
2049itcl::body Rappture::MapViewer::BuildHelpTab {} {
2050    set fg [option get $itk_component(hull) font Font]
2051    #set bfg [option get $itk_component(hull) boldFont Font]
2052
2053    set inner [$itk_component(main) insert end \
2054        -title "Help" \
2055        -icon [Rappture::icon question_mark12]]
2056    $inner configure -borderwidth 4
2057
2058    set helptext {*************************
2059Mouse bindings:
2060*************************
2061  Left - Panning
2062  Middle - Rotation
2063  Right - Zoom
2064
2065Zoom/travel:
2066  Left double-click:
2067    Zoom to point
2068  Left shift-double:
2069    Travel to point
2070  Right double-click:
2071    Zoom out from point
2072
2073Pins:
2074  Ctl-Left: Drop pin
2075  Ctl-Right: Delete pin
2076
2077Select:
2078  Shift-Left click-drag
2079
2080*************************
2081Keyboard bindings:
2082*************************
2083  g - Toggle graticule
2084  l - Toggle lighting
2085  n - Set North up
2086  p - Reset pitch
2087  w - Toggle wireframe
2088  arrows - panning
2089  Shift-arrows - fine pan
2090  Ctl-arrows - rotation
2091  Ctl-Shift-arrows:
2092    fine rotation
2093  PgUp/PgDown - zoom
2094  Home - Reset camera
2095*************************}
2096
2097    text $inner.info -width 25 -bg white
2098    $inner.info insert end $helptext
2099    $inner.info configure -state disabled
2100    blt::table $inner \
2101        0,0 $inner.info -fill both
2102}
2103
2104#
2105#  camera --
2106#
2107# USAGE: camera get
2108#        This is called by the server to transfer the
2109#        current Viewpoint settings
2110# USAGE: camera reset
2111#        Reset the camera to the default view
2112#
2113itcl::body Rappture::MapViewer::camera {option args} {
2114    switch -- $option {
2115        "get" {
2116            # We got the camera settings from the server
2117            foreach name {x y z heading pitch distance srs verticalDatum} value $args {
2118                set _view($name) $value
2119            }
2120#            puts stderr "view: $_view(x), $_view(y), $_view(z), $_view(heading), $_view(pitch), $_view(distance), {$_view(srs)}, {$_view(verticalDatum)}"
2121        }
2122        "go" {
2123            SendCmd "camera go $args"
2124        }
2125        "reset" {
2126            array set _view {
2127                x               0.0
2128                y               0.0
2129                z               0.0
2130                heading         0.0
2131                pitch           -89.9
2132                distance        1.0
2133                srs             ""
2134                verticalDatum   ""
2135            }
2136            if { [info exists _mapsettings(camera)] } {
2137                # Check if the tool specified a default
2138                set location $_mapsettings(camera)
2139                if { $location != "" } {
2140                    array set _view $location
2141                    set duration 0.0
2142                    SendCmd [list camera set $_view(x) $_view(y) $_view(z) $_view(heading) $_view(pitch) $_view(distance) $duration $_view(srs) $_view(verticalDatum)]
2143                } else {
2144                    SendCmd "camera reset"
2145                    # Retrieve the settings
2146                    #SendCmd "camera get"
2147                }
2148            } else {
2149                SendCmd "camera reset"
2150                # Retrieve the settings
2151               # SendCmd "camera get"
2152            }
2153        }
2154        "set" {
2155            set who [lindex $args 0]
2156            if {$who != "all" && $who != "srs" && $who != "verticalDatum"} {
2157                set val $_view($who)
2158                set code [catch { string is double $val } result]
2159                if { $code != 0 || !$result } {
2160                    return
2161                }
2162            }
2163            switch -- $who {
2164                "distance" {
2165                    SendCmd [list camera dist $_view(distance)]
2166                }
2167                "all" - "x" - "y" - "z" - "heading" - "pitch" - "srs" - "verticalDatum" {
2168                    set duration 0.0
2169                    SendCmd [list camera set $_view(x) $_view(y) $_view(z) $_view(heading) $_view(pitch) $_view(distance) $duration $_view(srs) $_view(verticalDatum)]
2170                }
2171            }
2172        }
2173    }
2174}
2175
2176itcl::body Rappture::MapViewer::GoToViewpoint { dataobj viewpoint } {
2177    if 0 {
2178    array set view {
2179        x 0
2180        y 0
2181        z 0
2182        heading 0
2183        pitch -89.999
2184        distance 0
2185        srs ""
2186        verticalDatum ""
2187    }
2188    }
2189    array set view [$dataobj viewpoint $viewpoint]
2190    foreach key {x y z heading pitch distance srs verticalDatum} {
2191        if { [info exists view($key)] } {
2192            set _view($key) $view($key)
2193        }
2194    }
2195    set duration 2.0
2196    SendCmd [list camera set $_view(x) $_view(y) $_view(z) $_view(heading) $_view(pitch) $_view(distance) $duration $_view(srs) $_view(verticalDatum)]
2197}
2198
2199itcl::body Rappture::MapViewer::GetImage { args } {
2200    if { [image width $_image(download)] > 0 &&
2201         [image height $_image(download)] > 0 } {
2202        set bytes [$_image(download) data -format "jpeg -quality 100"]
2203        set bytes [Rappture::encoding::decode -as b64 $bytes]
2204        return [list .jpg $bytes]
2205    }
2206    return ""
2207}
2208
2209itcl::body Rappture::MapViewer::BuildDownloadPopup { popup command } {
2210    Rappture::Balloon $popup \
2211        -title "[Rappture::filexfer::label downloadWord] as..."
2212    set inner [$popup component inner]
2213    label $inner.summary -text "" -anchor w
2214
2215    radiobutton $inner.image_button -text "Image File" \
2216        -variable [itcl::scope _downloadPopup(format)] \
2217        -value image
2218    Rappture::Tooltip::for $inner.image_button \
2219        "Save as digital image."
2220
2221    button $inner.ok -text "Save" \
2222        -highlightthickness 0 -pady 2 -padx 3 \
2223        -command $command \
2224        -compound left \
2225        -image [Rappture::icon download]
2226
2227    button $inner.cancel -text "Cancel" \
2228        -highlightthickness 0 -pady 2 -padx 3 \
2229        -command [list $popup deactivate] \
2230        -compound left \
2231        -image [Rappture::icon cancel]
2232
2233    blt::table $inner \
2234        0,0 $inner.summary -cspan 2  \
2235        2,0 $inner.image_button -anchor w -cspan 2 -padx { 4 0 } \
2236        4,1 $inner.cancel -width .9i -fill y \
2237        4,0 $inner.ok -padx 2 -width .9i -fill y
2238    blt::table configure $inner r3 -height 4
2239    blt::table configure $inner r4 -pady 4
2240    raise $inner.image_button
2241    $inner.image_button invoke
2242    return $inner
2243}
2244
2245itcl::body Rappture::MapViewer::ToggleGrid {} {
2246    set _settings(grid) [expr !$_settings(grid)]
2247    AdjustSetting grid
2248}
2249
2250itcl::body Rappture::MapViewer::ToggleLighting {} {
2251    set _settings(terrain-lighting) [expr !$_settings(terrain-lighting)]
2252    AdjustSetting terrain-lighting
2253}
2254
2255itcl::body Rappture::MapViewer::ToggleWireframe {} {
2256    set _settings(terrain-wireframe) [expr !$_settings(terrain-wireframe)]
2257    AdjustSetting terrain-wireframe
2258}
2259
2260itcl::body Rappture::MapViewer::SetTerrainStyle { style } {
2261    array set settings {
2262        -color white
2263        -edgecolor black
2264        -edges 0
2265        -lighting 0
2266        -linewidth 1.0
2267        -vertscale 1.0
2268        -wireframe 0
2269    }
2270    array set settings $style
2271
2272    SendCmd "map terrain edges $settings(-edges)"
2273    set _settings(terrain-edges) $settings(-edges)
2274    SendCmd "map terrain color [Color2RGB $settings(-color)]"
2275    #SendCmd "map terrain colormode constant"
2276    SendCmd "map terrain lighting $settings(-lighting)"
2277    set _settings(terrain-lighting) $settings(-lighting)
2278    SendCmd "map terrain linecolor [Color2RGB $settings(-edgecolor)]"
2279    #SendCmd "map terrain linewidth $settings(-linewidth)"
2280    SendCmd "map terrain vertscale $settings(-vertscale)"
2281    set _settings(terrain-vertscale) $settings(-vertscale)
2282    SendCmd "map terrain wireframe $settings(-wireframe)"
2283    set _settings(terrain-wireframe) $settings(-wireframe)
2284}
2285
2286itcl::body Rappture::MapViewer::SetLayerStyle { dataobj layer } {
2287    array set info [$dataobj layer $layer]
2288    set _visibility($layer) 1
2289
2290    switch -- $info(type) {
2291        "image" {
2292            array set settings {
2293                -minlevel 0
2294                -maxlevel 23
2295                -opacity 1.0
2296            }
2297            if { [info exists info(style)] } {
2298                array set settings $info(style)
2299            }
2300            if { [info exists info(opacity)] } {
2301                set settings(-opacity) $info(opacity)
2302                set _opacity($layer) $info(opacity)
2303            }
2304            set _opacity($layer) [expr $settings(-opacity) * 100]
2305            if {!$_sendEarthFile} {
2306                switch -- $info(driver)  {
2307                    "colorramp" {
2308                        set cmapName $layer
2309                        SendCmd [list colormap define $cmapName $info(colorramp.colormap)]
2310                        SendCmd [list map layer add $layer image colorramp \
2311                                     $info(colorramp.url) $info(cache) $info(colorramp.elevdriver) $info(profile)  \
2312                                     $cmapName]
2313                    }
2314                    "debug" {
2315                        SendCmd [list map layer add $layer image debug]
2316                    }
2317                    "gdal" {
2318                        SendCmd [list map layer add $layer image gdal \
2319                                     $info(gdal.url) $info(cache)]
2320                    }
2321                    "tms" {
2322                        SendCmd [list map layer add $layer image tms \
2323                                     $info(tms.url) $info(cache)]
2324                    }
2325                    "wms" {
2326                        SendCmd [list map layer add $layer image wms \
2327                                     $info(wms.url) $info(cache) \
2328                                     $info(wms.layers) \
2329                                     $info(wms.format) \
2330                                     $info(wms.transparent)]
2331                    }
2332                    "xyz" {
2333                        SendCmd [list map layer add $layer image xyz \
2334                                     $info(xyz.url) $info(cache)]
2335                    }
2336                }
2337            }
2338            SendCmd "map layer opacity $settings(-opacity) $layer"
2339        }
2340        "elevation" {
2341            array set settings {
2342                -minlevel 0
2343                -maxlevel 23
2344            }
2345            if { [info exists info(style)] } {
2346                array set settings $info(style)
2347            }
2348            if {!$_sendEarthFile} {
2349                switch -- $info(driver)  {
2350                    "gdal" {
2351                        SendCmd [list map layer add $layer elevation gdal \
2352                                     $info(gdal.url)]
2353                    }
2354                    "tms" {
2355                        SendCmd [list map layer add $layer elevation tms \
2356                                     $info(tms.url)]
2357                    }
2358                }
2359            }
2360        }
2361        "line" {
2362            array set settings {
2363                -color black
2364                -minbias 1000
2365                -opacity 1.0
2366                -width 1
2367            }
2368            if { [info exists info(style)] } {
2369                array set settings $info(style)
2370            }
2371            if { [info exists info(opacity)] } {
2372                set settings(-opacity) $info(opacity)
2373            }
2374            set _opacity($layer) [expr $settings(-opacity) * 100]
2375            foreach {r g b} [Color2RGB $settings(-color)] {}
2376            if {[info exists settings(-minrange)] && [info exists settings(-maxrange)]} {
2377                SendCmd [list map layer add $layer line $info(ogr.url) $r $g $b $settings(-width) $settings(-minrange) $settings(-maxrange)]
2378            } else {
2379                SendCmd [list map layer add $layer line $info(ogr.url) $r $g $b $settings(-width)]
2380            }
2381            SendCmd "map layer opacity $settings(-opacity) $layer"
2382        }
2383        "point" {
2384            array set settings {
2385                -color black
2386                -minbias 1000
2387                -opacity 1.0
2388                -size 1
2389            }
2390            if { [info exists info(style)] } {
2391                array set settings $info(style)
2392            }
2393            if { [info exists info(opacity)] } {
2394                set settings(-opacity) $info(opacity)
2395            }
2396            set _opacity($layer) [expr $settings(-opacity) * 100]
2397            foreach {r g b} [Color2RGB $settings(-color)] {}
2398            SendCmd [list map layer add $layer point $info(ogr.url) $r $g $b $settings(-size)]
2399            SendCmd "map layer opacity $settings(-opacity) $layer"
2400        }
2401        "polygon" {
2402            array set settings {
2403                -color white
2404                -minbias 1000
2405                -opacity 1.0
2406            }
2407            if { [info exists info(style)] } {
2408                array set settings $info(style)
2409            }
2410            if { [info exists info(opacity)] } {
2411                set settings(-opacity) $info(opacity)
2412            }
2413            set _opacity($layer) [expr $settings(-opacity) * 100]
2414            foreach {r g b} [Color2RGB $settings(-color)] {}
2415            SendCmd [list map layer add $layer polygon $info(ogr.url) $r $g $b]
2416            SendCmd "map layer opacity $settings(-opacity) $layer"
2417        }
2418        "label" {
2419            array set settings {
2420                -align "center-center"
2421                -color black
2422                -declutter 1
2423                -font Arial
2424                -fontsize 16.0
2425                -halocolor white
2426                -halowidth 2.0
2427                -layout "left-to-right"
2428                -minbias 1000
2429                -opacity 1.0
2430                -removedupes 1
2431            }
2432            if { [info exists info(style)] } {
2433                array set settings $info(style)
2434            }
2435            if { [info exists info(opacity)] } {
2436                set settings(-opacity) $info(opacity)
2437            }
2438            set _opacity($layer) [expr $settings(-opacity) * 100]
2439            set contentExpr $info(content)
2440            if {[info exists info(priority)]} {
2441                set priorityExpr $info(priority)
2442            } else {
2443                set priorityExpr ""
2444            }
2445            foreach {fgR fgG fgB} [Color2RGB $settings(-color)] {}
2446            foreach {bgR bgG bgB} [Color2RGB $settings(-halocolor)] {}
2447            if {[info exists settings(-minrange)] && [info exists settings(-maxrange)]} {
2448                SendCmd [list map layer add $layer text $info(ogr.url) $contentExpr $priorityExpr $fgR $fgG $fgB $bgR $bgG $bgB $settings(-halowidth) $settings(-fontsize) $settings(-removedupes) $settings(-declutter) $settings(-minrange) $settings(-maxrange)]
2449            } else {
2450                SendCmd [list map layer add $layer text $info(ogr.url) $contentExpr $priorityExpr $fgR $fgG $fgB $bgR $bgG $bgB $settings(-halowidth) $settings(-fontsize) $settings(-removedupes) $settings(-declutter)]
2451            }
2452            SendCmd "map layer opacity $settings(-opacity) $layer"
2453        }
2454    }
2455
2456    if { [info exists info(visible)] } {
2457        if { !$info(visible) } {
2458            set _visibility($layer) 0
2459            SendCmd "map layer visible 0 $layer"
2460        }
2461    }
2462}
2463
2464itcl::body Rappture::MapViewer::SetLayerOpacity { dataobj layer {value 100}} {
2465    set val $_opacity($layer)
2466    set sval [expr { 0.01 * double($val) }]
2467    SendCmd "map layer opacity $sval $layer"
2468}
2469
2470itcl::body Rappture::MapViewer::SetLayerVisibility { dataobj layer } {
2471    set bool $_visibility($layer)
2472    SendCmd "map layer visible $bool $layer"
2473}
2474
2475itcl::body Rappture::MapViewer::UpdateLayerControls {} {
2476    set row 0
2477    set inner $_layersFrame
2478    if { [winfo exists $inner.layers] } {
2479        foreach w [winfo children $inner.layers] {
2480            destroy $w
2481        }
2482    }
2483    set f $inner.layers
2484    set attrib [list]
2485    set imgIdx 0
2486    foreach dataobj [get -objects] {
2487        foreach layer [$dataobj layers] {
2488            array unset info
2489            array set info [$dataobj layer $layer]
2490            checkbutton $f.${layer}_visible \
2491                -text $info(label) \
2492                -font "Arial 9" -anchor w \
2493                -variable [itcl::scope _visibility($layer)] \
2494                -command [itcl::code $this \
2495                              SetLayerVisibility $dataobj $layer]
2496            blt::table $f $row,0 $f.${layer}_visible -anchor w -pady 2 -cspan 2
2497            incr row
2498            if { $info(type) == "image" } {
2499                incr imgIdx
2500                if { $info(driver) == "colorramp" } {
2501                    set colormap $layer
2502                    if { ![info exists _image(legend-$colormap)] } {
2503                        set _image(legend-$colormap) [image create photo]
2504                    }
2505                    itk_component add legend-$colormap-min {
2506                        label $f.${layer}_legend-$colormap-min -text 0
2507                    }
2508                    itk_component add legend-$colormap-max {
2509                        label $f.${layer}_legend-$colormap-max -text 1
2510                    }
2511                    itk_component add legend-$colormap {
2512                        label $f.${layer}_legend-$colormap -image $_image(legend-$colormap)
2513                    }
2514                    blt::table $f $row,0 $f.${layer}_legend-$colormap-min -anchor w -pady 0
2515                    blt::table $f $row,1 $f.${layer}_legend-$colormap-max -anchor e -pady 0
2516                    incr row
2517                    blt::table $f $row,0 $f.${layer}_legend-$colormap -anchor w -pady 2 -cspan 2
2518                    incr row
2519                    RequestLegend $colormap 256 16
2520                }
2521            }
2522            if { $info(type) != "elevation" && ($info(type) != "image" || $imgIdx > 1) } {
2523                label $f.${layer}_opacity_l -text "Opacity" -font "Arial 9"
2524                ::scale $f.${layer}_opacity -from 0 -to 100 \
2525                    -orient horizontal -showvalue off \
2526                    -variable [itcl::scope _opacity($layer)] \
2527                    -width 10 \
2528                    -command [itcl::code $this \
2529                                  SetLayerOpacity $dataobj $layer]
2530                Rappture::Tooltip::for $f.${layer}_opacity "Set opacity of $info(label) layer"
2531                blt::table $f $row,0 $f.${layer}_opacity_l -anchor w -pady 2
2532                blt::table $f $row,1 $f.${layer}_opacity -anchor w -pady 2
2533                incr row
2534            }
2535            set tooltip [list $info(description)]
2536            if { [info exists info(attribution)] &&
2537                 $info(attribution) != ""} {
2538                lappend tooltip $info(attribution)
2539            }
2540            Rappture::Tooltip::for $f.${layer}_visible [join $tooltip \n]
2541        }
2542        set mapAttrib [$dataobj hints "attribution"]
2543        if { $mapAttrib != "" } {
2544            lappend attrib $mapAttrib
2545        }
2546    }
2547    SendCmd "[list map attrib [join $attrib ,]]"
2548    label $f.map_attrib -text [join $attrib \n] -font "Arial 9"
2549    blt::table $f $row,0 $f.map_attrib -anchor sw -pady 2 -cspan 2
2550    #incr row
2551    if { $row > 0 } {
2552        blt::table configure $f r* c* -resize none
2553        blt::table configure $f r$row c1 -resize expand
2554    }
2555}
2556
2557itcl::body Rappture::MapViewer::UpdateViewpointControls {} {
2558    set row 0
2559    set inner $_viewpointsFrame
2560    if { [winfo exists $inner.viewpoints] } {
2561        foreach w [winfo children $inner.viewpoints] {
2562            destroy $w
2563        }
2564    }
2565    set f $inner.viewpoints
2566    foreach dataobj [get -objects] {
2567        foreach viewpoint [$dataobj viewpoints] {
2568            array unset info
2569            array set info [$dataobj viewpoint $viewpoint]
2570            button $f.${viewpoint}_go \
2571                -relief flat -compound left \
2572                -image [Rappture::icon placemark16] \
2573                -text $info(label) \
2574                -font "Arial 9" -anchor w \
2575                -command [itcl::code $this \
2576                              GoToViewpoint $dataobj $viewpoint]
2577            label $f.${viewpoint}_label \
2578                -text $info(label) \
2579                -font "Arial 9" -anchor w
2580            blt::table $f $row,0 $f.${viewpoint}_go -anchor w -pady 2 -cspan 2
2581            #blt::table $f $row,1 $f.${viewpoint}_label -anchor w -pady 2
2582            Rappture::Tooltip::for $f.${viewpoint}_go $info(description)
2583            incr row
2584        }
2585    }
2586    if { $row > 0 } {
2587        blt::table configure $f r* c* -resize none
2588        blt::table configure $f r$row c1 -resize expand
2589    }
2590}
2591
2592#
2593# Generate an OSG Earth file to send to server.  This is inteneded
2594# as a stopgap and testing tool until the protocol is fleshed out.
2595#
2596# Note that the lighting settings are required to be "hard-coded"
2597# as below for the runtime control to work.  Don't make those user
2598# configurable.
2599#
2600# Also note: Use "true"/"false" for boolean settings.  Not sure if
2601# the parser in OSG Earth accepts all of Tcl's forms of boolean vals.
2602#
2603itcl::body Rappture::MapViewer::EarthFile {} {
2604    append out "<map"
2605    append out " name=\"$_mapsettings(label)\""
2606    append out " type=\"$_mapsettings(type)\""
2607    append out " version=\"2\""
2608    append out ">\n"
2609    append out " <options lighting=\"true\">\n"
2610    # FIXME: convert color setting to hex
2611    # array set style $_mapsettings(style)
2612    # if {[info exists style(-color)]} {
2613    #     set color "?"
2614    # }
2615    set color "#ffffffff"
2616    append out "  <terrain lighting=\"false\" color=\"$color\"/>\n"
2617    if { [info exists _mapsettings(projection)] } {
2618        append out "  <profile"
2619        append out " srs=\"$_mapsettings(projection)\""
2620        if { [info exists _mapsettings(extents)] } {
2621            append out " xmin=\"$_mapsettings(x1)\""
2622            append out " ymin=\"$_mapsettings(y1)\""
2623            append out " xmax=\"$_mapsettings(x2)\""
2624            append out " ymax=\"$_mapsettings(y2)\""
2625        }
2626        append out "/>\n"
2627    }
2628    append out " </options>\n"
2629
2630    foreach dataobj [get -objects] {
2631        foreach layer [$dataobj layers] {
2632            set _layers($layer) 1
2633            array unset info
2634            array set info [$dataobj layer $layer]
2635            switch -- $info(type) {
2636                "image" {
2637                    append out " <image"
2638                    append out " name=\"$layer\""
2639                    append out " driver=\"gdal\""
2640                    if { [info exists info(opacity)] } {
2641                        append out " opacity=\"$info(opacity)\""
2642                    }
2643                    if { $info(visible) } {
2644                        append out " visible=\"true\""
2645                    } else {
2646                        append out " visible=\"false\""
2647                    }
2648                    append out ">\n"
2649                    append out "  <url>$info(url)</url>\n"
2650                    append out " </image>\n"
2651                }
2652                "elevation" {
2653                    append out " <elevation"
2654                    append out " name=\"$layer\""
2655                    append out " driver=\"gdal\""
2656                    if { $info(visible) } {
2657                        append out " visible=\"true\""
2658                    } else {
2659                        append out " visible=\"false\""
2660                    }
2661                    append out ">\n"
2662                    append out "  <url>$info(url)</url>\n"
2663                    append out " </elevation>\n"
2664                }
2665                default {
2666                    puts stderr "Type $info(type) not implemented in MapViewer::EarthFile"
2667                }
2668            }
2669        }
2670    }
2671    append out "</map>\n"
2672    return $out
2673}
Note: See TracBrowser for help on using the repository browser.