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

Last change on this file since 6658 was 6658, checked in by ldelgass, 8 years ago

fix fallback for invalid (geodetic) map projection

File size: 127.5 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-2016  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 -map map Map ""
39    itk_option define -plotforeground plotForeground Foreground ""
40    itk_option define -plotbackground plotBackground Background ""
41
42    constructor { args } {
43        Rappture::VisViewer::constructor
44    } {
45        # defined below
46    }
47    destructor {
48        # defined below
49    }
50    public proc SetServerList { namelist } {
51        Rappture::VisViewer::SetServerList "geovis" $namelist
52    }
53    public method add {dataobj {settings ""}}
54    public method camera {option args}
55    public method clear {args}
56    public method delete {args} { eval hide $args }
57    public method disconnect {}
58    public method download {option args}
59    public method get {args}
60    public method hide {args}
61    public method isconnected {}
62    public method parameters {title args} { # do nothing }
63    public method placard {option args}
64    public method refresh {} {
65        Rebuild
66        # Temporary hack to fix one frame delay in update
67        SendCmd "imgflush"
68        SendCmd "renderer render"
69    }
70    public method scale {args}
71    public method select {option {args ""}}
72    public method setSelectCallback {cmd}
73
74    private method KeyPress { key }
75    private method KeyRelease { key }
76    private method MouseClick { button x y }
77    private method MouseDoubleClick { button x y }
78    private method MouseDrag { button x y }
79    private method MouseMotion {}
80    private method MouseRelease { button x y }
81    private method MouseScroll { direction }
82
83    # The following methods are only used by this class.
84    private method AdjustSetting {what {value ""}}
85    private method BuildCameraTab {}
86    private method BuildDownloadPopup { widget command }
87    private method BuildHelpTab {}
88    private method BuildLayerTab {}
89    private method BuildMapTab {}
90    private method BuildTerrainTab {}
91    private method BuildViewpointsTab {}
92    private method Camera {option args}
93    private method Connect {}
94    private method ConvertTime { timeval }
95    private method CurrentLayers {args}
96    private method DisablePanningMouseBindings {}
97    private method DisableRotationMouseBindings {}
98    private method DisableZoomMouseBindings {}
99    private method Disconnect {}
100    private method DoPan {}
101    private method DoResize {}
102    private method DoRotate {}
103    private method DoSelect {}
104    private method DoSelectCallback {option {args ""}}
105    private method DrawLegend { colormap min max }
106    private method EnablePanningMouseBindings {}
107    private method EnableRotationMouseBindings {}
108    private method EnableZoomMouseBindings {}
109    private method EventuallyHandleMotionEvent { x y }
110    private method EventuallyPan { dx dy }
111    private method EventuallyResize { w h }
112    private method EventuallyRotate { dx dy }
113    private method EventuallySelect { x y }
114    private method GetImage { args }
115    private method GetNormalizedMouse { x y }
116    private method GoToViewpoint { dataobj viewpoint {duration 2.0} }
117    private method InitSettings { args }
118    private method MapIsGeocentric {}
119    private method Pan {option x y}
120    private method Pin {option x y}
121    private method Rebuild {}
122    private method ReceiveImage { args }
123    private method ReceiveLegend { args }
124    private method ReceiveMapInfo { args }
125    private method ReceiveScreenInfo { args }
126    private method ReceiveSelect { option {args ""} }
127    private method RequestLegend { colormap w h }
128    private method Rotate {option x y}
129    private method Select {option x y}
130    private method SendFiles { path }
131    private method SendStylesheetFiles { stylesheet }
132    private method SetHeading { {value 0} }
133    private method SetLayerOpacity { dataobj layer {value 100} }
134    private method SetLayerStyle { dataobj layer }
135    private method SetLayerVisibility { dataobj layer }
136    private method SetPitch { {value -89.999} }
137    private method SetTerrainStyle { style }
138    private method ToggleGrid {}
139    private method ToggleLighting {}
140    private method ToggleWireframe {}
141    private method UpdateLayerControls {}
142    private method UpdateViewpointControls {}
143    private method Zoom {option {x 0} {y 0}}
144
145    private variable _layersFrame "";     # Name of layers frame widget
146    private variable _viewpointsFrame ""; # Name of viewpoints frame widget
147    private variable _mapsettings;        # Global map settings
148
149    private variable _dlist "";         # list of data objects
150    private variable _hidden "";        # list of hidden data objects
151    private variable _obj2ovride;       # maps dataobj => style override
152    private variable _layers;           # Contains the names of all the
153                                        # layers in the server.
154    private variable _viewpoints;
155    private variable _selectCallback "";
156    private variable _click;            # info used for rotate operations
157    private variable _view;             # view params for 3D view
158    private variable _pan;
159    private variable _rotate;
160    private variable _select;
161    private variable _motion;
162    private variable _settings
163    private variable _opacity
164    private variable _visibility
165    private variable _reset 1;          # Indicates that server was reset and
166                                        # needs to be reinitialized.
167    private variable _initCamera 1;
168
169    private variable _first "";         # This is the topmost dataset.
170    private variable _start 0
171    private variable _title ""
172
173    private variable _width 0
174    private variable _height 0
175    private variable _resizePending 0
176    private variable _useSidebar 1
177    private variable _useServerManip 0
178    private variable _labelCount 0
179    private variable _b1mode "pan"
180
181    private common _downloadPopup;      # download options from popup
182    private common _hardcopy
183}
184
185itk::usual MapViewer {
186    keep -background -foreground -cursor -font
187    keep -plotbackground -plotforeground
188}
189
190# ----------------------------------------------------------------------
191# CONSTRUCTOR
192# ----------------------------------------------------------------------
193itcl::body Rappture::MapViewer::constructor {args} {
194    set _serverType "geovis"
195
196    #DebugOn
197    EnableWaitDialog 900
198
199    if { [catch {
200
201    # Rebuild event
202    $_dispatcher register !rebuild
203    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
204
205    # Resize event
206    $_dispatcher register !resize
207    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
208
209    # Pan event
210    $_dispatcher register !pan
211    $_dispatcher dispatch $this !pan "[itcl::code $this DoPan]; list"
212
213    # Rotate event
214    $_dispatcher register !rotate
215    $_dispatcher dispatch $this !rotate "[itcl::code $this DoRotate]; list"
216
217    # Select event
218    $_dispatcher register !select
219    $_dispatcher dispatch $this !select "[itcl::code $this DoSelect]; list"
220
221    # <Motion> event
222    $_dispatcher register !motion
223    $_dispatcher dispatch $this !motion "[itcl::code $this MouseMotion]; list"
224
225    #
226    # Populate parser with commands handle incoming requests
227    #
228    $_parser alias image    [itcl::code $this ReceiveImage]
229    $_parser alias legend   [itcl::code $this ReceiveLegend]
230    $_parser alias map      [itcl::code $this ReceiveMapInfo]
231    $_parser alias camera   [itcl::code $this Camera]
232    $_parser alias screen   [itcl::code $this ReceiveScreenInfo]
233    $_parser alias select   [itcl::code $this ReceiveSelect]
234
235    # Settings for mouse motion events: these are required
236    # to update the Lat/Long coordinate display
237    array set _motion {
238        compress        1
239        delay           100
240        enable          1
241        pending         0
242        x               0
243        y               0
244    }
245    array set _pan {
246        compress        1
247        delay           100
248        pending         0
249        x               0
250        y               0
251    }
252    array set _rotate {
253        azimuth         0
254        compress        1
255        delay           100
256        elevation       0
257        pending         0
258    }
259    array set _select {
260        compress        1
261        delay           100
262        pending         0
263        x               0
264        y               0
265    }
266    # This array holds the Viewpoint parameters that the
267    # server sends on "camera get".
268    array set _view {
269        distance        1.0
270        heading         0.0
271        pitch           -89.9
272        srs             ""
273        verticalDatum   ""
274        x               0.0
275        y               0.0
276        z               0.0
277    }
278
279    # Note: grid types are "shader", "geodetic", "utm" and "mgrs"
280    # Currently only work in geocentric maps
281    array set _settings [subst {
282        camera-throw           0
283        coords-precision       5
284        coords-units           "latlong_decimal_degrees"
285        coords-visible         1
286        grid                   0
287        grid-type              "shader"
288        terrain-ambient        0.03
289        terrain-edges          0
290        terrain-lighting       0
291        terrain-vertscale      1.0
292        terrain-wireframe      0
293        time                   12
294    }]
295
296    set _settings(time) [clock format [clock seconds] -format %k -gmt 1]
297
298    if {!$_useSidebar} {
299        destroy $itk_component(main)
300        itk_component add main {
301            frame $itk_interior.main
302        }
303        pack $itk_component(main) -expand yes -fill both
304        itk_component add plotarea {
305            frame $itk_component(main).plotarea -highlightthickness 0 -background black
306        } {
307            ignore -background
308        }
309        pack $itk_component(plotarea) -expand yes -fill both
310    }
311
312    DebugTrace "main: [winfo class $itk_component(main)]"
313    itk_component add view {
314        canvas $itk_component(plotarea).view \
315            -highlightthickness 0 -borderwidth 0
316    } {
317        usual
318        ignore -highlightthickness -borderwidth  -background
319    }
320
321    set c $itk_component(view)
322    bind $c <Enter> "focus %W"
323    bind $c <Control-F1> [itcl::code $this ToggleConsole]
324
325    # Fix the scrollregion in case we go off screen
326    $c configure -scrollregion [$c bbox all]
327
328    set _map(id) [$c create image 0 0 -anchor nw -image $_image(plot)]
329    set _map(cwidth) -1
330    set _map(cheight) -1
331    set _map(zoom) 1.0
332    set _map(original) ""
333
334    if {$_useSidebar} {
335        set f [$itk_component(main) component controls]
336        itk_component add reset {
337            button $f.reset -borderwidth 1 -padx 1 -pady 1 \
338                -highlightthickness 0 \
339                -image [Rappture::icon reset-view] \
340                -command [itcl::code $this Camera reset]
341        } {
342            usual
343            ignore -highlightthickness
344        }
345        pack $itk_component(reset) -side top -padx 2 -pady 2
346        Rappture::Tooltip::for $itk_component(reset) \
347            "Reset the view to the default zoom level"
348
349        itk_component add zoomin {
350            button $f.zin -borderwidth 1 -padx 1 -pady 1 \
351                -highlightthickness 0 \
352                -image [Rappture::icon zoom-in] \
353                -command [itcl::code $this Zoom in]
354        } {
355            usual
356            ignore -highlightthickness
357        }
358        pack $itk_component(zoomin) -side top -padx 2 -pady 2
359        Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
360
361        itk_component add zoomout {
362            button $f.zout -borderwidth 1 -padx 1 -pady 1 \
363                -highlightthickness 0 \
364                -image [Rappture::icon zoom-out] \
365                -command [itcl::code $this Zoom out]
366        } {
367            usual
368            ignore -highlightthickness
369        }
370        pack $itk_component(zoomout) -side top -padx 2 -pady 2
371        Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
372    }
373
374    if {$_useSidebar} {
375        BuildLayerTab
376        BuildViewpointsTab
377        BuildMapTab
378        BuildTerrainTab
379        BuildCameraTab
380        BuildHelpTab
381    }
382
383    # Hack around the Tk panewindow.  The problem is that the requested
384    # size of the 3d view isn't set until an image is retrieved from
385    # the server.  So the panewindow uses the tiny size.
386    pack forget $itk_component(view)
387    blt::table $itk_component(plotarea) \
388        0,0 $itk_component(view) -fill both -reqwidth 10000
389    blt::table configure $itk_component(plotarea) c1 -resize none
390
391    bind $itk_component(view) <Configure> \
392        [itcl::code $this EventuallyResize %w %h]
393
394    set _useServerManip 1
395    EnablePanningMouseBindings
396    EnableRotationMouseBindings
397    EnableZoomMouseBindings
398    set _useServerManip 0
399
400    if {$_useServerManip} {
401        # Bindings for keyboard events
402        bind $itk_component(view) <KeyPress> \
403            [itcl::code $this KeyPress %N]
404        bind $itk_component(view) <KeyRelease> \
405            [itcl::code $this KeyRelease %N]
406
407        # Zoom to point
408        bind $itk_component(view) <Double-1> \
409            [itcl::code $this MouseDoubleClick 1 %x %y]
410        bind $itk_component(view) <Double-3> \
411            [itcl::code $this MouseDoubleClick 3 %x %y]
412
413        # Unused
414        bind $itk_component(view) <Double-2> \
415            [itcl::code $this MouseDoubleClick 2 %x %y]
416
417        # Binding for mouse motion events
418        if {$_motion(enable)} {
419            bind $itk_component(view) <Motion> \
420                [itcl::code $this EventuallyHandleMotionEvent %x %y]
421        }
422    } else {
423        # Zoom to point
424        bind $itk_component(view) <Double-1> \
425            [itcl::code $this Camera go %x %y 0.4]
426        # Travel to point (no zoom)
427        bind $itk_component(view) <Shift-Double-1> \
428            [itcl::code $this Camera go %x %y 1.0]
429        # Zoom out centered on point
430        bind $itk_component(view) <Double-3> \
431            [itcl::code $this Camera go %x %y 2.5]
432
433        # Pin placemark annotations
434        bind $itk_component(view) <Control-ButtonPress-1> \
435            [itcl::code $this Pin add %x %y]
436        bind $itk_component(view) <Control-ButtonPress-3> \
437            [itcl::code $this Pin delete %x %y]
438
439        # Draw selection rectangle
440        bind $itk_component(view) <Shift-ButtonPress-1> \
441            [itcl::code $this Select click %x %y]
442        bind $itk_component(view) <B1-Motion> \
443            +[itcl::code $this Select drag %x %y]
444        bind $itk_component(view) <Shift-ButtonRelease-1> \
445            [itcl::code $this Select release %x %y]
446
447        # Update coordinate readout
448        bind $itk_component(view) <ButtonPress-1> \
449            +[itcl::code $this SendCmd "map setpos %x %y"]
450        bind $itk_component(view) <Double-3> \
451            +[itcl::code $this SendCmd "map setpos %x %y"]
452
453        # Bindings for panning via keyboard
454        bind $itk_component(view) <KeyPress-Left> \
455            [itcl::code $this Pan set 10 0]
456        bind $itk_component(view) <KeyPress-Right> \
457            [itcl::code $this Pan set -10 0]
458        bind $itk_component(view) <KeyPress-Up> \
459            [itcl::code $this Pan set 0 -10]
460        bind $itk_component(view) <KeyPress-Down> \
461            [itcl::code $this Pan set 0 10]
462
463        bind $itk_component(view) <Shift-KeyPress-Left> \
464            [itcl::code $this Pan set 2 0]
465        bind $itk_component(view) <Shift-KeyPress-Right> \
466            [itcl::code $this Pan set -2 0]
467        bind $itk_component(view) <Shift-KeyPress-Up> \
468            [itcl::code $this Pan set 0 -2]
469        bind $itk_component(view) <Shift-KeyPress-Down> \
470            [itcl::code $this Pan set 0 2]
471
472        # Bindings for rotation via keyboard
473        bind $itk_component(view) <Control-Left> \
474            [itcl::code $this Rotate set 10 0]
475        bind $itk_component(view) <Control-Right> \
476            [itcl::code $this Rotate set -10 0]
477        bind $itk_component(view) <Control-Up> \
478            [itcl::code $this Rotate set 0 -10]
479        bind $itk_component(view) <Control-Down> \
480            [itcl::code $this Rotate set 0 10]
481
482        bind $itk_component(view) <Control-Shift-Left> \
483            [itcl::code $this Rotate set 2 0]
484        bind $itk_component(view) <Control-Shift-Right> \
485            [itcl::code $this Rotate set -2 0]
486        bind $itk_component(view) <Control-Shift-Up> \
487            [itcl::code $this Rotate set 0 -2]
488        bind $itk_component(view) <Control-Shift-Down> \
489            [itcl::code $this Rotate set 0 2]
490
491        # Bindings for zoom via keyboard
492        bind $itk_component(view) <KeyPress-Prior> \
493            [itcl::code $this Zoom out]
494        bind $itk_component(view) <KeyPress-Next> \
495            [itcl::code $this Zoom in]
496        bind $itk_component(view) <KeyPress-Home> \
497            [itcl::code $this Camera reset]
498
499        # Keyboard shortcuts
500        # Reset heading to North
501        bind $itk_component(view) <n> \
502            [itcl::code $this SetHeading]
503        # Reset pitch to top-down (2D) view
504        bind $itk_component(view) <p> \
505            [itcl::code $this SetPitch]
506        bind $itk_component(view) <g> \
507            [itcl::code $this ToggleGrid]
508        bind $itk_component(view) <l> \
509            [itcl::code $this ToggleLighting]
510        bind $itk_component(view) <w> \
511            [itcl::code $this ToggleWireframe]
512
513        # Binding for mouse motion events
514        set _motion(compress) 1
515        if {$_motion(enable)} {
516            bind $itk_component(view) <Motion> \
517                [itcl::code $this EventuallyHandleMotionEvent %x %y]
518        }
519        #bind $itk_component(view) <Motion> \
520        #    +[itcl::code $this SendCmd "map pin hover %x %y"]
521    }
522
523    bind $itk_component(view) <Enter> "focus $itk_component(view)"
524
525    if {[string equal "x11" [tk windowingsystem]]} {
526        # Bindings for zoom via mouse
527        if {$_useServerManip} {
528            bind $itk_component(view) <4> [itcl::code $this MouseScroll up]
529            bind $itk_component(view) <5> [itcl::code $this MouseScroll down]
530        } else {
531            bind $itk_component(view) <4> [itcl::code $this Zoom out]
532            bind $itk_component(view) <5> [itcl::code $this Zoom in]
533        }
534    }
535
536    set _image(download) [image create photo]
537
538    eval itk_initialize $args
539    Connect
540} errs] != 0 } {
541        puts stderr errs=$errs
542    }
543}
544
545# ----------------------------------------------------------------------
546# DESTRUCTOR
547# ----------------------------------------------------------------------
548itcl::body Rappture::MapViewer::destructor {} {
549    Disconnect
550    $_dispatcher cancel !rebuild
551    $_dispatcher cancel !resize
552    $_dispatcher cancel !rotate
553    image delete $_image(plot)
554    image delete $_image(download)
555}
556
557itcl::body Rappture::MapViewer::DoResize {} {
558    set sendResize 1
559    if { $_width < 2 } {
560        set _width 500
561        set sendResize 0
562    }
563    if { $_height < 2 } {
564        set _height 500
565        set sendResize 0
566    }
567    #set _start [clock clicks -milliseconds]
568    if {$sendResize} {
569        SendCmd "screen size $_width $_height"
570    }
571    set _resizePending 0
572}
573
574itcl::body Rappture::MapViewer::DoRotate {} {
575    SendCmd "camera rotate $_rotate(azimuth) $_rotate(elevation)"
576    set _rotate(azimuth) 0
577    set _rotate(elevation) 0
578    set _rotate(pending) 0
579}
580
581itcl::body Rappture::MapViewer::DoSelect {} {
582    SendCmd "map box update $_select(x) $_select(y)"
583    set _select(x) 0
584    set _select(y) 0
585    set _select(pending) 0
586}
587
588itcl::body Rappture::MapViewer::EventuallyResize { w h } {
589    set _width $w
590    set _height $h
591    if { !$_resizePending } {
592        set _resizePending 1
593        $_dispatcher event -after 200 !resize
594    }
595}
596
597itcl::body Rappture::MapViewer::DoPan {} {
598    SendCmd "camera pan $_pan(x) $_pan(y)"
599    set _pan(x) 0
600    set _pan(y) 0
601    set _pan(pending) 0
602}
603
604itcl::body Rappture::MapViewer::EventuallyPan { dx dy } {
605    set _pan(x) [expr $_pan(x) + $dx]
606    set _pan(y) [expr $_pan(y) + $dy]
607    if { !$_pan(compress) } {
608        DoPan
609        return
610    }
611    if { !$_pan(pending) } {
612        set _pan(pending) 1
613        $_dispatcher event -after $_pan(delay) !pan
614    }
615}
616
617itcl::body Rappture::MapViewer::EventuallyRotate { dx dy } {
618    set _rotate(azimuth) [expr $_rotate(azimuth) + $dx]
619    set _rotate(elevation) [expr $_rotate(elevation) + $dy]
620    if { !$_rotate(compress) } {
621        DoRotate
622        return
623    }
624    if { !$_rotate(pending) } {
625        set _rotate(pending) 1
626        $_dispatcher event -after $_rotate(delay) !rotate
627    }
628}
629
630itcl::body Rappture::MapViewer::EventuallySelect { x y } {
631    set _select(x) $x
632    set _select(y) $y
633    if { !$_select(compress) } {
634        DoSelect
635        return
636    }
637    if { !$_select(pending) } {
638        set _select(pending) 1
639        $_dispatcher event -after $_select(delay) !select
640    }
641}
642
643itcl::body Rappture::MapViewer::DrawLegend { colormap min max } {
644    if { [info exists itk_component(legend-$colormap) ] } {
645        $itk_component(legend-$colormap-min) configure -text $min
646        $itk_component(legend-$colormap-max) configure -text $max
647        $itk_component(legend-$colormap) configure -image $_image(legend-$colormap)
648    }
649}
650
651itcl::body Rappture::MapViewer::RequestLegend { colormap w h } {
652    SendCmd "legend $colormap $w $h 0 [Color2RGB #d9d9d9]"
653}
654
655# ----------------------------------------------------------------------
656# USAGE: add <dataobj> ?<settings>?
657#
658# Clients use this to add a data object to the plot.  The optional
659# <settings> are used to configure the plot.  Allowed settings are
660# -color, -brightness, -width, -linestyle, and -raise.
661# ----------------------------------------------------------------------
662itcl::body Rappture::MapViewer::add {dataobj {settings ""}} {
663    DebugTrace "Enter"
664    array set params {
665        -brightness 0
666        -color auto
667        -description ""
668        -linestyle solid
669        -param ""
670        -raise 0
671        -simulation 0
672        -type ""
673        -width 1
674    }
675    array set params $settings
676    set params(-description) ""
677    set params(-param) ""
678    array set params $settings
679
680    if {$params(-color) == "auto" || $params(-color) == "autoreset"} {
681        # can't handle -autocolors yet
682        set params(-color) black
683    }
684    # Add to display list
685    set pos [lsearch -exact $_dlist $dataobj]
686    if {$pos < 0} {
687        #if {[llength $_dlist] > 0} {
688        #    error "Can't add more than 1 map to mapviewer"
689        #}
690        lappend _dlist $dataobj
691    }
692    # Remove from hidden list
693    set pos [lsearch -exact $_hidden $dataobj]
694    if {$pos >= 0} {
695        set _hidden [lreplace $_hidden $pos $pos]
696    }
697    set _obj2ovride($dataobj-raise) $params(-raise)
698    $_dispatcher event -idle !rebuild
699}
700
701# ----------------------------------------------------------------------
702# USAGE: hide ?<dataobj1> <dataobj2> ...?
703#
704# Clients use this to hide a dataobj from the plot.  If no dataobjs are
705# specified, then all dataobjs are hidden.
706# ----------------------------------------------------------------------
707itcl::body Rappture::MapViewer::hide {args} {
708    DebugTrace "Enter"
709    if { [llength $args] == 0} {
710        set args $_dlist
711    }
712    # Mark all specified dataobjs as hidden
713    set changed 0
714    foreach dataobj $args {
715        set pos [lsearch -exact $_dlist $dataobj]
716        if { $pos < 0 } {
717            continue;                   # Don't know anything about it.
718        }
719        # Remove it from the dataobj list.
720        set _dlist [lreplace $_dlist $pos $pos]
721        # Add to hidden list
722        set pos [lsearch -exact $_hidden $dataobj]
723        if {$pos < 0} {
724            lappend _hidden $dataobj
725        }
726        array unset _obj2ovride $dataobj-*
727        set changed 1
728    }
729    # If anything changed, then rebuild the plot
730    if { $changed } {
731        $_dispatcher event -idle !rebuild
732    }
733}
734
735# ----------------------------------------------------------------------
736# USAGE: clear ?<dataobj1> <dataobj2> ...?
737#
738# Clients use this to permanently clear a dataobj from the plot and the
739# render server.  If no dataobjs are specified, then all dataobjs are
740# cleared.
741# ----------------------------------------------------------------------
742itcl::body Rappture::MapViewer::clear {args} {
743    DebugTrace "Enter"
744    if { [llength $args] == 0} {
745        set args [concat $_dlist $_hidden]
746    }
747    # Delete all specified dataobjs
748    set changed 0
749    foreach dataobj $args {
750        set found 0
751        set dpos [lsearch -exact $_dlist $dataobj]
752        if { $dpos >= 0 } {
753            set found 1
754        }
755        set hpos [lsearch -exact $_hidden $dataobj]
756        if { $hpos >= 0 } {
757            set found 1
758        }
759        if { !$found } {
760            continue
761        }
762        foreach tag [array names _layers -glob $dataobj-*] {
763            SendCmd "map layer delete $tag"
764        }
765        if { [info commands $dataobj] == $dataobj } {
766            # Remove the layers from the client and server.
767            foreach layer [$dataobj layers] {
768                if { ![$dataobj layer $layer shared] } {
769                    # Remove shared layer if no other objects have it?
770                }
771            }
772        }
773        if { $dpos >= 0 } {
774            # Remove it from the dataobj list.
775            set _dlist [lreplace $_dlist $dpos $dpos]
776        }
777        if { $hpos >= 0 } {
778            # Remove it from the hidden list.
779            set _hidden [lreplace $_hidden $hpos $hpos]
780        }
781        array unset _obj2ovride $dataobj-*
782        array unset _layers $dataobj-*
783        array unset _opacity $dataobj-*
784        array unset _visibility $dataobj-*
785        set changed 1
786    }
787    # If anything changed, then rebuild the plot
788    if { $changed } {
789        $_dispatcher event -idle !rebuild
790    }
791}
792
793# ----------------------------------------------------------------------
794# USAGE: get ?-objects?
795# USAGE: get ?-visible?
796# USAGE: get ?-image view?
797# USAGE: get ?-image legend <dataobj> <layer>?
798#
799# Clients use this to query the list of objects being plotted, in
800# order from bottom to top of this result.  The optional "-image"
801# flag can also request the internal images being shown.
802# ----------------------------------------------------------------------
803itcl::body Rappture::MapViewer::get {args} {
804    if {[llength $args] == 0} {
805        set args "-objects"
806    }
807
808    set op [lindex $args 0]
809    switch -- $op {
810        "-objects" {
811            # put the dataobj list in order according to -raise options
812            set dlist {}
813            foreach dataobj $_dlist {
814                if { ![$dataobj isvalid] } {
815                    continue
816                }
817                if {[info exists _obj2ovride($dataobj-raise)] &&
818                    $_obj2ovride($dataobj-raise)} {
819                    set dlist [linsert $dlist 0 $dataobj]
820                } else {
821                    lappend dlist $dataobj
822                }
823            }
824            return $dlist
825        }
826        "-hidden" {
827            set dlist {}
828            foreach dataobj $_hidden {
829                if { [info commands $dataobj] != $dataobj } {
830                    # dataobj was deleted, remove from list
831                    set pos [lsearch -exact $_hidden $dataobj]
832                    if {$pos >= 0} {
833                        set _hidden [lreplace $_hidden $pos $pos]
834                    }
835                    continue
836                }
837                if { ![$dataobj isvalid] } {
838                    continue
839                }
840                if { [info exists _obj2ovride($dataobj-raise)] } {
841                    puts stderr "ERROR: object on hidden list is visible"
842                }
843                lappend dlist $dataobj
844            }
845            return $dlist
846        }
847        "-visible" {
848            set dlist {}
849            foreach dataobj $_dlist {
850                if { ![$dataobj isvalid] } {
851                    continue
852                }
853                if { ![info exists _obj2ovride($dataobj-raise)] } {
854                    # No setting indicates that the object isn't visible.
855                    continue
856                }
857                # Otherwise use the -raise parameter to put the object to
858                # the front of the list.
859                if { $_obj2ovride($dataobj-raise) } {
860                    set dlist [linsert $dlist 0 $dataobj]
861                } else {
862                    lappend dlist $dataobj
863                }
864            }
865            return $dlist
866        }
867        "-image" {
868            if {[llength $args] < 2} {
869                error "wrong # args: should be \"get -image view|legend\""
870            }
871            switch -- [lindex $args 1] {
872                legend {
873                    if {[llength $args] < 4} {
874                        error "wrong # args: should be \"get -image legend <dataobj> <layer>\""
875                    }
876                    set dataobj [lindex $args 2]
877                    set layer [lindex $args 3]
878                    set colormap $layer
879                    if {![$dataobj layer $layer shared]} {
880                        set colormap $dataobj-$layer
881                        set colormap "[regsub -all {::} ${colormap} {}]"
882                        set colormap "[regsub -all {\-} ${colormap} {_}]"
883                    }
884                    return $_image(legend-$colormap)
885                }
886                view {
887                    return $_image(plot)
888                }
889                default {
890                    error "bad image name \"[lindex $args 1]\": should be view|legend"
891                }
892            }
893        }
894        default {
895            error "bad option \"$op\": should be -objects, -hidden, -visible or -image"
896        }
897    }
898}
899
900itcl::body Rappture::MapViewer::MapIsGeocentric {} {
901    if { [info exists _mapsettings(type)] } {
902        return [expr {$_mapsettings(type) eq "geocentric"}]
903    } else {
904        return 0
905    }
906}
907
908# ----------------------------------------------------------------------
909# USAGE: scale ?<data1> <data2> ...?
910#
911# Sets the default limits for the overall plot according to the
912# limits of the data for all of the given <data> objects.  This
913# accounts for all objects--even those not showing on the screen.
914# Because of this, the limits are appropriate for all objects as
915# the user scans through data in the ResultSet viewer.
916# ----------------------------------------------------------------------
917itcl::body Rappture::MapViewer::scale {args} {
918    DebugTrace "Enter"
919    array unset _mapsettings
920
921    foreach dataobj $args {
922        if { ![$dataobj isvalid] } {
923            continue
924        }
925        # Global map view settings are taken from the first dataobj
926        array unset settings
927        array set settings [$dataobj hints]
928        if { [array size _mapsettings] == 0 } {
929            set _mapsettings(label) $settings(label)
930            set _mapsettings(style) $settings(style)
931            set _mapsettings(type) $settings(type)
932            set _mapsettings(projection) $settings(projection)
933            set _mapsettings(extents) $settings(extents)
934            set _mapsettings(camera) $settings(camera)
935        }
936        # If dataobj has the same type and projection as view, expand extents
937        if { $settings(extents) != "" &&
938             $settings(type) == $_mapsettings(type) &&
939             $settings(projection) == $_mapsettings(projection)} {
940            foreach {xmin ymin xmax ymax} $settings(extents) break
941            if { $_mapsettings(extents) == $settings(extents) } {
942                set _mapsettings(xmin) $xmin
943                set _mapsettings(ymin) $ymin
944                set _mapsettings(xmax) $xmax
945                set _mapsettings(ymax) $ymax
946            } else {
947                if { $xmin < $_mapsettings(xmin) } {
948                    set _mapsettings(xmin) $xmin
949                    #set _reset 1
950                }
951                if { $ymin < $_mapsettings(ymin) } {
952                    set _mapsettings(ymin) $ymin
953                    #set _reset 1
954                }
955                if { $xmax > $_mapsettings(xmax) } {
956                    set _mapsettings(xmax) $xmax
957                    #set _reset 1
958                }
959                if { $ymax > $_mapsettings(ymax) } {
960                    set _mapsettings(ymax) $ymax
961                    #set _reset 1
962                }
963            }
964        }
965        foreach viewpoint [$dataobj viewpoints] {
966            set _viewpoints($viewpoint) [$dataobj viewpoint $viewpoint]
967            if {$_debug} {
968                array set vp $_viewpoints($viewpoint)
969                foreach key { label description x y z distance heading pitch srs verticalDatum } {
970                    if { [info exists vp($key)] } {
971                        DebugTrace "vp: $viewpoint $key $vp($key)"
972                    }
973                }
974            }
975        }
976    }
977    #if { $_reset } {
978    #    $_dispatcher event -idle !rebuild
979    #}
980}
981
982itcl::body Rappture::MapViewer::setSelectCallback {cmd} {
983    set _selectCallback $cmd
984}
985
986itcl::body Rappture::MapViewer::DoSelectCallback {option {args ""}} {
987    if { $_selectCallback != "" } {
988        set cmd [concat $_selectCallback $option $args]
989        uplevel #0 $cmd
990    }
991}
992
993# ----------------------------------------------------------------------
994# USAGE: ReceiveSelect clear
995# USAGE: ReceiveSelect feature <args...>
996# USAGE: ReceiveSelect annotation <args...>
997# ----------------------------------------------------------------------
998itcl::body Rappture::MapViewer::ReceiveSelect {option {args ""}} {
999    DebugTrace "Enter"
1000    eval DoSelectCallback $option $args
1001}
1002
1003# ----------------------------------------------------------------------
1004# USAGE: select clear
1005# USAGE: select feature add|delete|set <featureIDList> <layerName>
1006# USAGE: select mode <boolean>
1007#
1008# Clients use this method to notify the map widget of a selection event
1009# originating from outside the map
1010# ----------------------------------------------------------------------
1011itcl::body Rappture::MapViewer::select {option {args ""}} {
1012    switch $option {
1013        "clear" {
1014            if {[llength $args] > 0} {
1015                error "wrong # args: should be \"select clear\""
1016            }
1017            SendCmd "select clear"
1018        }
1019        "feature" {
1020            if {[llength $args] != 3} {
1021                error "wrong #args: should be \"select feature <op> <featureIDList> <layerName>\""
1022            }
1023            set op [lindex $args 0]
1024            set layer [lindex $args end]
1025            if {![info exists _layers($layer)]} {
1026                puts stderr "Unknown layer \"$layer\""
1027                return
1028            }
1029            switch $op {
1030                "add" {
1031                    SendCmd "select fadd [lrange $args 1 end]"
1032                }
1033                "delete" {
1034                    SendCmd "select fdelete [lrange $args 1 end]"
1035                }
1036                "set" {
1037                    SendCmd "select feature [lrange $args 1 end]"
1038                }
1039                default {
1040                    puts stderr "Unknown select feature op \"$op\""
1041                }
1042            }
1043        }
1044        "mode" {
1045            if {[llength $args] != 1} {
1046                error "wrong # args: should be \"select mode <boolean>\""
1047            }
1048            if {![string is boolean $args]} {
1049                error "argument to \"select mode\" must be boolean"
1050            }
1051            SendCmd "select mode $args"
1052        }
1053        default {
1054            puts stderr "Unknown select option \"$option\""
1055        }
1056    }
1057}
1058
1059# ----------------------------------------------------------------------
1060# USAGE: placard enable <state> <layerName>
1061#
1062# Clients use this method to enable/disable display of a feature placard
1063# when a feature is clicked/selected.
1064# ----------------------------------------------------------------------
1065itcl::body Rappture::MapViewer::placard {option args} {
1066    switch $option {
1067        enable {
1068            if {[llength $args] != 2} {
1069                error "wrong $ args: should be \"enable <boolean> <layerName>\""
1070            }
1071            set state [lindex $args 0]
1072            set layerName [lindex $args 1]
1073            if {![info exists _layers($layerName)]} {
1074                error "Unknown layer \"$layerName\""
1075            }
1076            if {![string is boolean $state]} {
1077                error "argument to placard enable must be boolean"
1078            }
1079            SendCmd "placard enable $state $layerName"
1080        }
1081        default {
1082            error "bad option \"$option\": should be enable"
1083        }
1084    }
1085}
1086
1087# ----------------------------------------------------------------------
1088# USAGE: download coming
1089# USAGE: download controls <downloadCommand>
1090# USAGE: download now
1091#
1092# Clients use this method to create a downloadable representation
1093# of the plot.  Returns a list of the form {ext string}, where
1094# "ext" is the file extension (indicating the type of data) and
1095# "string" is the data itself.
1096# ----------------------------------------------------------------------
1097itcl::body Rappture::MapViewer::download {option args} {
1098    switch $option {
1099        coming {
1100            if {[catch {
1101                blt::winop snap $itk_component(plotarea) $_image(download)
1102            }]} {
1103                $_image(download) configure -width 1 -height 1
1104                $_image(download) put #000000
1105            }
1106        }
1107        controls {
1108            set popup .mapviewerdownload
1109            if { ![winfo exists .mapviewerdownload] } {
1110                set inner [BuildDownloadPopup $popup [lindex $args 0]]
1111            } else {
1112                set inner [$popup component inner]
1113            }
1114            set _downloadPopup(image_controls) $inner.image_frame
1115            set num [llength [get]]
1116            set num [expr {($num == 1) ? "1 result" : "$num results"}]
1117            set word [Rappture::filexfer::label downloadWord]
1118            $inner.summary configure -text "$word $num in the following format:"
1119            update idletasks            ;# Fix initial sizes
1120            return $popup
1121        }
1122        now {
1123            set popup .mapviewerdownload
1124            if {[winfo exists .mapviewerdownload]} {
1125                $popup deactivate
1126            }
1127            switch -- $_downloadPopup(format) {
1128                "image" {
1129                    return [$this GetImage [lindex $args 0]]
1130                }
1131            }
1132            return ""
1133        }
1134        default {
1135            error "bad option \"$option\": should be coming, controls, now"
1136        }
1137    }
1138}
1139
1140# ----------------------------------------------------------------------
1141# USAGE: Connect ?<host:port>,<host:port>...?
1142#
1143# Clients use this method to establish a connection to a new
1144# server, or to reestablish a connection to the previous server.
1145# Any existing connection is automatically closed.
1146# ----------------------------------------------------------------------
1147itcl::body Rappture::MapViewer::Connect {} {
1148    global readyForNextFrame
1149    set readyForNextFrame 1
1150    set _reset 1
1151    set _hosts [GetServerList "geovis"]
1152    if { "" == $_hosts } {
1153        return 0
1154    }
1155    set result [VisViewer::Connect $_hosts]
1156    if { $result } {
1157        if { $_reportClientInfo }  {
1158            # Tell the server the viewer, hub, user and session.
1159            # Do this immediately on connect before buffering any commands
1160            global env
1161
1162            set info {}
1163            set user "???"
1164            if { [info exists env(USER)] } {
1165                set user $env(USER)
1166            }
1167            set session "???"
1168            if { [info exists env(SESSION)] } {
1169                set session $env(SESSION)
1170            }
1171            lappend info "version" "$Rappture::version"
1172            lappend info "build" "$Rappture::build"
1173            lappend info "svnurl" "$Rappture::svnurl"
1174            lappend info "installdir" "$Rappture::installdir"
1175            lappend info "hub" [exec hostname]
1176            lappend info "hub_url" [Rappture::Tool::resources -huburl]
1177            lappend info "client" "mapviewer"
1178            lappend info "user" $user
1179            lappend info "session" $session
1180            SendCmd "clientinfo [list $info]"
1181        }
1182
1183        set w [winfo width $itk_component(view)]
1184        set h [winfo height $itk_component(view)]
1185        EventuallyResize $w $h
1186    }
1187    return $result
1188}
1189
1190#
1191# isconnected --
1192#
1193#   Indicates if we are currently connected to the visualization server.
1194#
1195itcl::body Rappture::MapViewer::isconnected {} {
1196    return [VisViewer::IsConnected]
1197}
1198
1199#
1200# disconnect --
1201#
1202itcl::body Rappture::MapViewer::disconnect {} {
1203    Disconnect
1204    set _reset 1
1205}
1206
1207#
1208# Disconnect --
1209#
1210#   Clients use this method to disconnect from the current rendering
1211#   server.
1212#
1213itcl::body Rappture::MapViewer::Disconnect {} {
1214    VisViewer::Disconnect
1215
1216    $_dispatcher cancel !pan
1217    $_dispatcher cancel !motion
1218    $_dispatcher cancel !rebuild
1219    $_dispatcher cancel !resize
1220    $_dispatcher cancel !rotate
1221    # disconnected -- no more data sitting on server
1222    array unset _layers
1223    array unset _layersFrame
1224    global readyForNextFrame
1225    set readyForNextFrame 1
1226}
1227
1228# ----------------------------------------------------------------------
1229# USAGE: ReceiveImage -type <type> -token <token> -bytes <size>
1230#
1231# Invoked automatically whenever the "image" command comes in from
1232# the rendering server.  Indicates that binary image data with the
1233# specified <size> will follow.
1234# ----------------------------------------------------------------------
1235itcl::body Rappture::MapViewer::ReceiveImage { args } {
1236    global readyForNextFrame
1237    set readyForNextFrame 1
1238    array set info {
1239        -bytes 0
1240        -token "???"
1241        -type image
1242    }
1243    array set info $args
1244    set bytes [ReceiveBytes $info(-bytes)]
1245    if { $info(-type) == "image" } {
1246        $_image(plot) configure -data $bytes
1247    } elseif { $info(type) == "print" } {
1248        set tag $this-print-$info(-token)
1249        set _hardcopy($tag) $bytes
1250    }
1251    set _waitTimeout 0
1252}
1253
1254#
1255# ReceiveLegend
1256#
1257# Invoked automatically whenever the "legend" command comes in from
1258# the rendering server.  Indicates that binary image data with the
1259# specified <size> will follow.
1260#
1261itcl::body Rappture::MapViewer::ReceiveLegend { colormap min max size } {
1262    DebugTrace "ReceiveLegend colormap=$colormap range=$min,$max size=$size"
1263    if { [IsConnected] } {
1264        set bytes [ReceiveBytes $size]
1265        if { ![info exists _image(legend-$colormap)] } {
1266            set _image(legend-$colormap) [image create photo]
1267        }
1268        if 0 {
1269            set f [open "/tmp/legend-${colormap}.ppm" "w"]
1270            fconfigure $f -translation binary -encoding binary
1271            puts $f $bytes
1272            close $f
1273        }
1274        $_image(legend-$colormap) configure -data $bytes
1275        #puts stderr "read $size bytes for [image width $_image(legend-$colormap)]x[image height $_image(legend-$colormap)] legend>"
1276        if { [catch {DrawLegend $colormap $min $max} errs] != 0 } {
1277            global errorInfo
1278            puts stderr "errs=$errs errorInfo=$errorInfo"
1279        }
1280    }
1281}
1282
1283#
1284# ReceiveMapInfo --
1285#
1286itcl::body Rappture::MapViewer::ReceiveMapInfo { args } {
1287    if { ![isconnected] } {
1288        return
1289    }
1290    set timeReceived [clock clicks -milliseconds]
1291    set elapsed [expr $timeReceived - $_start]
1292    set option [lindex $args 0]
1293    switch -- $option {
1294        "coords" {
1295            set len [llength $args]
1296            if {$len < 3} {
1297                error "Bad map coords response"
1298            } else {
1299                set token [lindex $args 1]
1300            }
1301            foreach { x y z } [lindex $args 2] {
1302                puts stderr "\[$token\] Map coords: $x $y $z"
1303            }
1304            if {$len > 3} {
1305                set srs [lindex $args 3]
1306                set vert [lindex $args 4]
1307                puts stderr "\[$token\] {$srs} {$vert}"
1308            }
1309        }
1310        "names" {
1311            foreach { name } [lindex $args 1] {
1312                puts stderr "layer: $name"
1313            }
1314        }
1315        default {
1316            error "unknown map option \"$option\" from server"
1317        }
1318    }
1319}
1320
1321#
1322# ReceiveScreenInfo --
1323#
1324itcl::body Rappture::MapViewer::ReceiveScreenInfo { args } {
1325    if { ![isconnected] } {
1326        return
1327    }
1328    set option [lindex $args 0]
1329    switch -- $option {
1330        "coords" {
1331            set len [llength $args]
1332            if {$len < 3} {
1333                error "Bad screen coords response"
1334            } else {
1335                set token [lindex $args 1]
1336            }
1337            foreach { x y z } [lindex $args 2] {
1338                puts stderr "\[$token\] Screen coords: $x $y $z"
1339            }
1340        }
1341        default {
1342            error "unknown screen option \"$option\" from server"
1343        }
1344    }
1345}
1346
1347# ----------------------------------------------------------------------
1348# USAGE: Rebuild
1349#
1350# Called automatically whenever something changes that affects the
1351# data in the widget.  Clears any existing data and rebuilds the
1352# widget to display new data.
1353# ----------------------------------------------------------------------
1354itcl::body Rappture::MapViewer::Rebuild {} {
1355    set w [winfo width $itk_component(view)]
1356    set h [winfo height $itk_component(view)]
1357    if { $w < 2 || $h < 2 } {
1358        update idletasks
1359        $_dispatcher event -idle !rebuild
1360        return
1361    }
1362
1363    # Turn on buffering of commands to the server.  We don't want to be
1364    # preempted by a server disconnect/reconnect (which automatically
1365    # generates a new call to Rebuild).
1366    StartBufferingCommands
1367
1368    if { $_reset } {
1369        set _width $w
1370        set _height $h
1371        DoResize
1372
1373        if { [info exists _mapsettings(type)] } {
1374            # The map must be reset once before any layers are added.
1375            # This should not be done more than once as it is very expensive.
1376            if { [info exists _mapsettings(style)] } {
1377                array set settings {
1378                    -color white
1379                }
1380                array set settings $_mapsettings(style)
1381            }
1382            set bgcolor [Color2RGB $settings(-color)]
1383            if { $_mapsettings(type) == "geocentric" } {
1384                if { [info exists itk_component(grid)] } {
1385                    $itk_component(grid) configure -state normal
1386                }
1387                if { [info exists itk_component(time)] } {
1388                    $itk_component(time_l) configure -state normal
1389                    $itk_component(time) configure -state normal
1390                }
1391                if { [info exists itk_component(pitch_slider)] } {
1392                    $itk_component(pitch_slider_l) configure -state normal
1393                    $itk_component(pitch_slider) configure -state normal
1394                }
1395                EnableRotationMouseBindings
1396                SendCmd "map reset geocentric $bgcolor"
1397            }  else {
1398                if { [info exists itk_component(grid)] } {
1399                    $itk_component(grid) configure -state disabled
1400                }
1401                if { [info exists itk_component(time)] } {
1402                    $itk_component(time_l) configure -state disabled
1403                    $itk_component(time) configure -state disabled
1404                }
1405                if { [info exists itk_component(pitch_slider)] } {
1406                    $itk_component(pitch_slider_l) configure -state disabled
1407                    $itk_component(pitch_slider) configure -state disabled
1408                }
1409                DisableRotationMouseBindings
1410                set proj $_mapsettings(projection)
1411                SendCmd "screen bgcolor $bgcolor"
1412                if { $proj == "" } {
1413                    SendCmd "map reset projected $bgcolor global-mercator"
1414                } elseif { ![info exists _mapsettings(extents)] ||
1415                           $_mapsettings(extents) == "" } {
1416                    SendCmd "map reset projected $bgcolor [list $proj]"
1417                } else {
1418                    foreach key "xmin ymin xmax ymax" {
1419                        set $key $_mapsettings($key)
1420                    }
1421                    SendCmd "map reset projected $bgcolor [list $proj] $xmin $ymin $xmax $ymax"
1422                }
1423            }
1424            # XXX: Remove after implementing batch load of layers on reset
1425            SendCmd "map layer delete base"
1426
1427            # Most terrain settings are global to the map and apply even
1428            # if there is no elevation layer.  The exception is the
1429            # vertical scale, which only applies if there is an elevation
1430            # layer
1431            if { [info exists _mapsettings(style)] } {
1432                SetTerrainStyle $_mapsettings(style)
1433            } else {
1434                InitSettings terrain-ambient terrain-edges terrain-lighting \
1435                    terrain-vertscale terrain-wireframe
1436            }
1437            InitSettings coords-visible
1438        } else {
1439            error "No map settings on reset"
1440        }
1441    }
1442
1443    set _first ""
1444    set haveTerrain 0
1445    foreach dataobj [get -hidden] {
1446        foreach layer [$dataobj layers] {
1447            if { ![$dataobj layer $layer shared] } {
1448                set tag $dataobj-$layer
1449                SendCmd "map layer visible 0 $tag"
1450            }
1451        }
1452    }
1453    foreach dataobj [get -objects] {
1454        if { [info exists _obj2ovride($dataobj-raise)] &&  $_first == "" } {
1455            set _first $dataobj
1456        }
1457        foreach layer [$dataobj layers] {
1458            array unset info
1459            array set info [$dataobj layer $layer]
1460            set tag $layer
1461            if { !$info(shared) } {
1462                set tag $dataobj-$layer
1463            }
1464            if { ![info exists _layers($tag)] } {
1465                if { $_reportClientInfo }  {
1466                    set cinfo {}
1467                    lappend cinfo "tool_id"       [$dataobj hints toolid]
1468                    lappend cinfo "tool_name"     [$dataobj hints toolname]
1469                    lappend cinfo "tool_title"    [$dataobj hints tooltitle]
1470                    lappend cinfo "tool_command"  [$dataobj hints toolcommand]
1471                    lappend cinfo "tool_revision" [$dataobj hints toolrevision]
1472                    lappend cinfo "dataset_label" [encoding convertto utf-8 $info(label)]
1473                    lappend cinfo "dataset_tag"   $tag
1474                    SendCmd "clientinfo [list $cinfo]"
1475                }
1476                set _layers($tag) 1
1477                SetLayerStyle $dataobj $layer
1478            } elseif { [$dataobj dirty $layer] } {
1479                # Recreate layer
1480                # FIXME: retain layer order
1481                SendCmd "map layer delete $tag"
1482                SetLayerStyle $dataobj $layer
1483            }
1484            $dataobj dirty $layer 0
1485            # Don't change visibility of shared/base layers
1486            if { !$info(shared) } {
1487                # FIXME: This is overriding data layers' initial visibility
1488                if { [info exists _obj2ovride($dataobj-raise)] } {
1489                    SendCmd "map layer visible 1 $tag"
1490                    set _visibility($tag) 1
1491                } else {
1492                    SendCmd "map layer visible 0 $tag"
1493                    set _visibility($tag) 0
1494                }
1495            }
1496            if {$info(type) == "elevation"} {
1497                set haveTerrain 1
1498            }
1499        }
1500        # Search our layer list for data layers removed from map object
1501        foreach tag [array names _layers -glob $dataobj-*] {
1502            set layer [string range $tag [string length "$dataobj-"] end]
1503            if {![$dataobj hasLayer $layer]} {
1504                DebugTrace "Delete layer: tag: $tag layer: $layer"
1505                SendCmd "map layer delete $tag"
1506                array unset _layers $tag
1507                array unset _opacity $tag
1508                array unset _visibility $tag
1509            }
1510        }
1511    }
1512
1513    if {$_reset} {
1514        if {$_initCamera} {
1515            # If this is the first Rebuild, we need to
1516            # set up the initial view settings if there
1517            # are any
1518            Camera reset
1519            set _initCamera 0
1520        } else {
1521            # Restore view from before reconnect
1522            Camera set all
1523        }
1524    }
1525
1526    if {$_useSidebar} {
1527        if ($haveTerrain) {
1528            if { [info exists itk_component(vscale)] } {
1529                $itk_component(vscale_l) configure -state normal
1530                $itk_component(vscale) configure -state normal
1531            }
1532        } else {
1533            if { [info exists itk_component(vscale)] } {
1534                $itk_component(vscale_l) configure -state disabled
1535                $itk_component(vscale) configure -state disabled
1536            }
1537        }
1538        UpdateLayerControls
1539        UpdateViewpointControls
1540    }
1541
1542    set _reset 0
1543    global readyForNextFrame
1544    set readyForNextFrame 0;            # Don't advance to the next frame
1545                                        # until we get an image.
1546
1547    # Actually write the commands to the server socket.  If it fails, we
1548    # don't care.  We're finished here.
1549    blt::busy hold $itk_component(hull)
1550    StopBufferingCommands
1551    blt::busy release $itk_component(hull)
1552}
1553
1554itcl::body Rappture::MapViewer::EnablePanningMouseBindings {} {
1555    if {1 || $_useServerManip} {
1556        bind $itk_component(view) <ButtonPress-1> \
1557            [itcl::code $this MouseClick 1 %x %y]
1558        bind $itk_component(view) <B1-Motion> \
1559            [itcl::code $this MouseDrag 1 %x %y]
1560        bind $itk_component(view) <ButtonRelease-1> \
1561            [itcl::code $this MouseRelease 1 %x %y]
1562    } else {
1563        bind $itk_component(view) <ButtonPress-1> \
1564            [itcl::code $this Pan click %x %y]
1565        bind $itk_component(view) <B1-Motion> \
1566            [itcl::code $this Pan drag %x %y]
1567        bind $itk_component(view) <ButtonRelease-1> \
1568            [itcl::code $this Pan release %x %y]
1569    }
1570}
1571
1572itcl::body Rappture::MapViewer::DisablePanningMouseBindings {} {
1573    bind $itk_component(view) <ButtonPress-1> {}
1574    bind $itk_component(view) <B1-Motion> {}
1575    bind $itk_component(view) <ButtonRelease-1> {}
1576}
1577
1578itcl::body Rappture::MapViewer::EnableRotationMouseBindings {} {
1579    if {1 || $_useServerManip} {
1580        # Bindings for rotation via mouse
1581        bind $itk_component(view) <ButtonPress-2> \
1582            [itcl::code $this MouseClick 2 %x %y]
1583        bind $itk_component(view) <B2-Motion> \
1584            [itcl::code $this MouseDrag 2 %x %y]
1585        bind $itk_component(view) <ButtonRelease-2> \
1586            [itcl::code $this MouseRelease 2 %x %y]
1587    } else {
1588        bind $itk_component(view) <ButtonPress-2> \
1589            [itcl::code $this Rotate click %x %y]
1590        bind $itk_component(view) <B2-Motion> \
1591            [itcl::code $this Rotate drag %x %y]
1592        bind $itk_component(view) <ButtonRelease-2> \
1593            [itcl::code $this Rotate release %x %y]
1594    }
1595}
1596
1597itcl::body Rappture::MapViewer::DisableRotationMouseBindings {} {
1598    bind $itk_component(view) <ButtonPress-2> {}
1599    bind $itk_component(view) <B2-Motion> {}
1600    bind $itk_component(view) <ButtonRelease-2> {}
1601}
1602
1603itcl::body Rappture::MapViewer::EnableZoomMouseBindings {} {
1604    if {1 || $_useServerManip} {
1605        bind $itk_component(view) <ButtonPress-3> \
1606            [itcl::code $this MouseClick 3 %x %y]
1607        bind $itk_component(view) <B3-Motion> \
1608            [itcl::code $this MouseDrag 3 %x %y]
1609        bind $itk_component(view) <ButtonRelease-3> \
1610            [itcl::code $this MouseRelease 3 %x %y]
1611    } else {
1612        bind $itk_component(view) <ButtonPress-3> \
1613            [itcl::code $this Zoom click %x %y]
1614        bind $itk_component(view) <B3-Motion> \
1615            [itcl::code $this Zoom drag %x %y]
1616        bind $itk_component(view) <ButtonRelease-3> \
1617            [itcl::code $this Zoom release %x %y]
1618    }
1619}
1620
1621itcl::body Rappture::MapViewer::DisableZoomMouseBindings {} {
1622    bind $itk_component(view) <ButtonPress-3> {}
1623    bind $itk_component(view) <B3-Motion> {}
1624    bind $itk_component(view) <ButtonRelease-3> {}
1625}
1626# ----------------------------------------------------------------------
1627# USAGE: CurrentLayers ?-all -visible? ?dataobjs?
1628#
1629# Returns a list of server IDs for the current datasets being displayed.
1630# This is normally a single ID, but it might be a list of IDs if the
1631# current data object has multiple components.
1632# ----------------------------------------------------------------------
1633itcl::body Rappture::MapViewer::CurrentLayers {args} {
1634    set flag [lindex $args 0]
1635    switch -- $flag {
1636        "-all" {
1637            if { [llength $args] > 1 } {
1638                error "CurrentLayers: can't specify dataobj after \"-all\""
1639            }
1640            set dlist [get -objects]
1641        }
1642        "-visible" {
1643            if { [llength $args] > 1 } {
1644                set dlist {}
1645                set args [lrange $args 1 end]
1646                foreach dataobj $args {
1647                    if { [info exists _obj2ovride($dataobj-raise)] } {
1648                        lappend dlist $dataobj
1649                    }
1650                }
1651            } else {
1652                set dlist [get -visible]
1653            }
1654        }
1655        default {
1656            set dlist $args
1657        }
1658    }
1659    set rlist ""
1660    foreach dataobj $dlist {
1661        foreach layer [$dataobj layers] {
1662            if { [info exists _layers($layer)] && $_layers($layer) } {
1663                lappend rlist $layer
1664            }
1665        }
1666    }
1667    return $rlist
1668}
1669
1670itcl::body Rappture::MapViewer::KeyPress {k} {
1671    SendCmd "key press $k"
1672}
1673
1674itcl::body Rappture::MapViewer::KeyRelease {k} {
1675    SendCmd "key release $k"
1676}
1677
1678itcl::body Rappture::MapViewer::GetNormalizedMouse {x y} {
1679    set w [winfo width $itk_component(view)]
1680    set h [winfo height $itk_component(view)]
1681    set x [expr {(2.0 * double($x)/$w) - 1.0}]
1682    set y [expr {(2.0 * double($y)/$h) - 1.0}]
1683    return [list $x $y]
1684}
1685
1686itcl::body Rappture::MapViewer::MouseClick {button x y} {
1687    SendCmd "mouse click $button $x $y"
1688}
1689
1690itcl::body Rappture::MapViewer::MouseDoubleClick {button x y} {
1691    SendCmd "mouse dblclick $button $x $y"
1692}
1693
1694itcl::body Rappture::MapViewer::MouseDrag {button x y} {
1695    SendCmd "mouse drag $button $x $y"
1696}
1697
1698itcl::body Rappture::MapViewer::MouseRelease {button x y} {
1699    SendCmd "mouse release $button $x $y"
1700}
1701
1702itcl::body Rappture::MapViewer::MouseMotion {} {
1703    SendCmd "mouse motion $_motion(x) $_motion(y)"
1704    #SendCmd "map pin hover $_motion(x) $_motion(y)"
1705    set _motion(pending) 0
1706}
1707
1708itcl::body Rappture::MapViewer::MouseScroll {direction} {
1709    switch -- $direction {
1710        "up" {
1711            SendCmd "mouse scroll 1"
1712        }
1713        "down" {
1714            SendCmd "mouse scroll -1"
1715        }
1716    }
1717}
1718
1719#
1720# EventuallyHandleMotionEvent --
1721#
1722#   This routine compresses (no button press) motion events.  It
1723#   delivers a server mouse command once every 100 milliseconds (if a
1724#   motion event is pending).
1725#
1726itcl::body Rappture::MapViewer::EventuallyHandleMotionEvent {x y} {
1727    set _motion(x) $x
1728    set _motion(y) $y
1729    if { !$_motion(compress) } {
1730        MouseMotion
1731        return
1732    }
1733    if { !$_motion(pending) } {
1734        set _motion(pending) 1
1735        $_dispatcher event -after $_motion(delay) !motion
1736    }
1737}
1738
1739# ----------------------------------------------------------------------
1740# USAGE: Zoom in
1741# USAGE: Zoom out
1742# USAGE: Zoom reset
1743#        $this Zoom click x y
1744#        $this Zoom drag x y
1745#        $this Zoom release x y
1746#
1747# Called automatically when the user clicks on one of the zoom
1748# controls for this widget.  Changes the zoom for the current view.
1749# Also implements mouse zoom.
1750# ----------------------------------------------------------------------
1751itcl::body Rappture::MapViewer::Zoom {option {x 0} {y 0}} {
1752    switch -- $option {
1753        "in" {
1754            # z here is normalized mouse Y delta
1755            set z -0.25
1756            SendCmd "camera zoom $z"
1757        }
1758        "out" {
1759            # z here is normalized mouse Y delta
1760            set z 0.25
1761            SendCmd "camera zoom $z"
1762        }
1763        "reset" {
1764            SendCmd "camera dist $_view(distance)"
1765        }
1766        "click" {
1767            set _click(x) $x
1768            set _click(y) $y
1769            $itk_component(view) configure -cursor hand1
1770        }
1771        "drag" {
1772            if { ![info exists _click(x)] } {
1773                set _click(x) $x
1774            }
1775            if { ![info exists _click(y)] } {
1776                set _click(y) $y
1777            }
1778            set h [winfo height $itk_component(view)]
1779            set dy [expr ($_click(y) - $y)/double($h)]
1780            set _click(x) $x
1781            set _click(y) $y
1782            if {[expr (abs($dy) > 0.0)]} {
1783                SendCmd "camera zoom $dy"
1784            }
1785        }
1786        "release" {
1787            Zoom drag $x $y
1788            $itk_component(view) configure -cursor ""
1789        }
1790    }
1791}
1792
1793# ----------------------------------------------------------------------
1794# USAGE: Rotate click <x> <y>
1795# USAGE: Rotate drag <x> <y>
1796# USAGE: Rotate release <x> <y>
1797#
1798# Called automatically when the user clicks/drags/releases in the
1799# plot area.  Moves the plot according to the user's actions.
1800# ----------------------------------------------------------------------
1801itcl::body Rappture::MapViewer::Rotate {option x y} {
1802    switch -- $option {
1803        "click" {
1804            $itk_component(view) configure -cursor fleur
1805            set _click(x) $x
1806            set _click(y) $y
1807            set _rotate(azimuth) 0
1808            set _rotate(elevation) 0
1809        }
1810        "drag" {
1811            if {[array size _click] == 0} {
1812                Rotate click $x $y
1813            } else {
1814                set w [winfo width $itk_component(view)]
1815                set h [winfo height $itk_component(view)]
1816                if {$w <= 0 || $h <= 0} {
1817                    return
1818                }
1819                set dx [expr ($x - $_click(x))/double($w)]
1820                set dy [expr ($_click(y) - $y)/double($h)]
1821                set _click(x) $x
1822                set _click(y) $y
1823                if {[expr (abs($dx) > 0.0 || abs($dy) > 0.0)]} {
1824                    #SendCmd "camera rotate $dx $dy"
1825                    EventuallyRotate $dx $dy
1826                }
1827            }
1828        }
1829        "release" {
1830            Rotate drag $x $y
1831            $itk_component(view) configure -cursor ""
1832            catch {unset _click}
1833        }
1834        "set" {
1835            set w [winfo width $itk_component(view)]
1836            set h [winfo height $itk_component(view)]
1837            set dx [expr $x / double($w)]
1838            set dy [expr $y / double($h)]
1839            if {[expr (abs($dx) > 0.0 || abs($dy) > 0.0)]} {
1840                EventuallyRotate $dx $dy
1841            }
1842        }
1843        default {
1844            error "bad option \"$option\": should be click, drag, release"
1845        }
1846    }
1847}
1848
1849itcl::body Rappture::MapViewer::Select {option x y} {
1850    switch -- $option {
1851        "click" {
1852            set _click(x) $x
1853            set _click(y) $y
1854            set _b1mode "select"
1855            SendCmd "map box init $x $y"
1856        }
1857        "drag" {
1858            if {$_b1mode != "select"} {
1859                return
1860            }
1861            if { ![info exists _click(x)] } {
1862                set _click(x) $x
1863            }
1864            if { ![info exists _click(y)] } {
1865                set _click(y) $y
1866            }
1867            EventuallySelect $x $y
1868        }
1869        "release" {
1870            set _b1mode ""
1871            if {![info exists _click(x)] ||
1872                ![info exists _click(y)] ||
1873                ($_click(x) == $x &&
1874                 $_click(y) == $y)} {
1875                SendCmd "map box clear"
1876            } else {
1877                SendCmd "map box end $x $y"
1878            }
1879        }
1880    }
1881}
1882
1883itcl::body Rappture::MapViewer::Pin {option x y} {
1884    set _click(x) $x
1885    set _click(y) $y
1886    switch -- $option {
1887        "add" {
1888            incr _labelCount
1889            set label "Label $_labelCount"
1890            SendCmd [list "map" "pin" "add" $x $y [encoding convertto utf-8 $label]]
1891        }
1892        "delete" {
1893            SendCmd "map pin delete $x $y"
1894        }
1895    }
1896}
1897
1898# ----------------------------------------------------------------------
1899# USAGE: $this Pan set x y
1900#        $this Pan click x y
1901#        $this Pan drag x y
1902#        $this Pan release x y
1903#
1904# Called automatically when the user clicks on one of the zoom
1905# controls for this widget.  Changes the zoom for the current view.
1906# ----------------------------------------------------------------------
1907itcl::body Rappture::MapViewer::Pan {option x y} {
1908    switch -- $option {
1909        "click" {
1910            set _click(x) $x
1911            set _click(y) $y
1912            set _pan(x) 0
1913            set _pan(y) 0
1914            $itk_component(view) configure -cursor hand1
1915            set _b1mode "pan"
1916        }
1917        "drag" {
1918            if {$_b1mode != "pan"} {
1919                return
1920            }
1921            if { ![info exists _click(x)] } {
1922                set _click(x) $x
1923            }
1924            if { ![info exists _click(y)] } {
1925                set _click(y) $y
1926            }
1927            set w [winfo width $itk_component(view)]
1928            set h [winfo height $itk_component(view)]
1929            set dx [expr ($x - $_click(x))/double($w)]
1930            set dy [expr ($_click(y) - $y)/double($h)]
1931            set _click(x) $x
1932            set _click(y) $y
1933            if {[expr (abs($dx) > 0.0 || abs($dy) > 0.0)]} {
1934                EventuallyPan $dx $dy
1935                #SendCmd "camera pan $dx $dy"
1936            }
1937        }
1938        "release" {
1939            Pan drag $x $y
1940            $itk_component(view) configure -cursor ""
1941            set _b1mode ""
1942        }
1943        "set" {
1944            set w [winfo width $itk_component(view)]
1945            set h [winfo height $itk_component(view)]
1946            set x [expr $x / double($w)]
1947            set y [expr $y / double($h)]
1948            if {[expr (abs($x) > 0.0 || abs($y) > 0.0)]} {
1949                SendCmd "camera pan $x $y"
1950            }
1951            return
1952        }
1953        default {
1954            error "unknown option \"$option\": should set, click, drag, or release"
1955        }
1956    }
1957}
1958
1959itcl::body Rappture::MapViewer::SetHeading { {value 0} } {
1960    set _view(heading) $value
1961    Camera set heading
1962}
1963
1964itcl::body Rappture::MapViewer::SetPitch { {value -89.999} } {
1965    set _view(pitch) $value
1966    Camera set pitch
1967}
1968
1969# ----------------------------------------------------------------------
1970# USAGE: InitSettings <what> ?<value>?
1971#
1972# Used internally to update rendering settings whenever parameters
1973# change in the popup settings panel.  Sends the new settings off
1974# to the back end.
1975# ----------------------------------------------------------------------
1976itcl::body Rappture::MapViewer::InitSettings { args } {
1977    foreach setting $args {
1978        AdjustSetting $setting
1979    }
1980}
1981
1982#
1983# AdjustSetting --
1984#
1985#   Changes/updates a specific setting in the widget.  There are
1986#   usually user-setable option.  Commands are sent to the render
1987#   server.
1988#
1989itcl::body Rappture::MapViewer::AdjustSetting {what {value ""}} {
1990    if { ![isconnected] } {
1991        return
1992    }
1993    switch -- $what {
1994        "coords-visible" - "coords-precision" - "coords-units" {
1995            set bool $_settings(coords-visible)
1996            set units $_settings(coords-units)
1997            set precision $_settings(coords-precision)
1998            SendCmd "map posdisp $bool $units $precision"
1999        }
2000        "grid" - "grid-type" {
2001            set bool $_settings(grid)
2002            set gridType $_settings(grid-type)
2003            SendCmd "map grid $bool $gridType"
2004        }
2005        "camera-throw" {
2006            set bool $_settings($what)
2007            SendCmd "camera throw $bool"
2008        }
2009        "terrain-ambient" {
2010            set val $_settings($what)
2011            SendCmd "map terrain ambient $val"
2012        }
2013        "terrain-edges" {
2014            set bool $_settings($what)
2015            SendCmd "map terrain edges $bool"
2016        }
2017        "terrain-lighting" {
2018            set bool $_settings($what)
2019            SendCmd "map terrain lighting $bool"
2020        }
2021        "terrain-palette" {
2022            set cmap [$itk_component(terrainpalette) value]
2023            #SendCmd "map terrain colormap $cmap"
2024        }
2025        "terrain-vertscale" {
2026            set val $_settings($what)
2027            SendCmd "map terrain vertscale $val"
2028        }
2029        "terrain-wireframe" {
2030            set bool $_settings($what)
2031            SendCmd "map terrain wireframe $bool"
2032        }
2033        "time" {
2034            set val $_settings($what)
2035            SendCmd "map time $val"
2036        }
2037        default {
2038            error "don't know how to fix $what"
2039        }
2040    }
2041}
2042
2043# ----------------------------------------------------------------------
2044# CONFIGURATION OPTION: -plotbackground
2045# ----------------------------------------------------------------------
2046itcl::configbody Rappture::MapViewer::plotbackground {
2047    if { [isconnected] } {
2048        set rgb [Color2RGB $itk_option(-plotbackground)]
2049        SendCmd "screen bgcolor $rgb"
2050    }
2051}
2052
2053# ----------------------------------------------------------------------
2054# CONFIGURATION OPTION: -plotforeground
2055# ----------------------------------------------------------------------
2056itcl::configbody Rappture::MapViewer::plotforeground {
2057    if { [isconnected] } {
2058        set rgb [Color2RGB $itk_option(-plotforeground)]
2059        # FIXME: Set font foreground colors
2060    }
2061}
2062
2063itcl::body Rappture::MapViewer::BuildMapTab {} {
2064    set fg [option get $itk_component(hull) font Font]
2065    #set bfg [option get $itk_component(hull) boldFont Font]
2066
2067    set inner [$itk_component(main) insert end \
2068        -title "Map Settings" \
2069        -icon [Rappture::icon wrench]]
2070    $inner configure -borderwidth 4
2071
2072    checkbutton $inner.posdisp \
2073        -text "Show Coordinate Readout" \
2074        -variable [itcl::scope _settings(coords-visible)] \
2075        -command [itcl::code $this AdjustSetting coords-visible] \
2076        -font "Arial 9" -anchor w
2077
2078    itk_component add grid {
2079        checkbutton $inner.grid \
2080        -text "Show Graticule" \
2081        -variable [itcl::scope _settings(grid)] \
2082        -command [itcl::code $this AdjustSetting grid] \
2083        -font "Arial 9" -anchor w
2084    } {
2085        ignore -font
2086    }
2087    Rappture::Tooltip::for $inner.grid "Toggle graticule (grid) display <g>"
2088
2089    checkbutton $inner.wireframe \
2090        -text "Show Wireframe" \
2091        -variable [itcl::scope _settings(terrain-wireframe)] \
2092        -command [itcl::code $this AdjustSetting terrain-wireframe] \
2093        -font "Arial 9" -anchor w
2094    Rappture::Tooltip::for $inner.wireframe "Toggle wireframe rendering of terrain geometry <w>"
2095
2096    checkbutton $inner.lighting \
2097        -text "Enable Lighting" \
2098        -variable [itcl::scope _settings(terrain-lighting)] \
2099        -command [itcl::code $this AdjustSetting terrain-lighting] \
2100        -font "Arial 9" -anchor w
2101    Rappture::Tooltip::for $inner.lighting "Toggle sky lighting of terrain <l>"
2102
2103    checkbutton $inner.edges \
2104        -text "Show Edges" \
2105        -variable [itcl::scope _settings(terrain-edges)] \
2106        -command [itcl::code $this AdjustSetting terrain-edges] \
2107        -font "Arial 9" -anchor w
2108
2109    itk_component add time_l {
2110        label $inner.time_l -text "Time (UTC)" -font "Arial 9"
2111    } {
2112        ignore -font
2113    }
2114    itk_component add time {
2115        ::scale $inner.time -from 0 -to 23.9 -orient horizontal \
2116            -resolution 0.1 \
2117            -variable [itcl::scope _settings(time)] \
2118            -width 10 \
2119            -showvalue on \
2120            -command [itcl::code $this AdjustSetting time]
2121    }
2122    Rappture::Tooltip::for $inner.time \
2123        "Set ephemeris time for sky lighting"
2124
2125    itk_component add ambient_l {
2126        label $inner.ambient_l -text "Ambient min." -font "Arial 9"
2127    } {
2128        ignore -font
2129    }
2130    itk_component add ambient {
2131        ::scale $inner.ambient -from 0 -to 1.0 -orient horizontal \
2132            -resolution 0.01 \
2133            -variable [itcl::scope _settings(terrain-ambient)] \
2134            -width 10 \
2135            -showvalue on \
2136            -command [itcl::code $this AdjustSetting terrain-ambient]
2137    }
2138    Rappture::Tooltip::for $inner.ambient \
2139        "Set minimum global ambient light level"
2140
2141    blt::table $inner \
2142        0,0 $inner.posdisp   -cspan 2 -anchor w -pady 2 \
2143        1,0 $inner.grid      -cspan 2 -anchor w -pady 2 \
2144        2,0 $inner.wireframe -cspan 2 -anchor w -pady 2 \
2145        3,0 $inner.lighting  -cspan 2 -anchor w -pady 2 \
2146        4,0 $inner.time_l             -anchor w -pady 2 \
2147        4,1 $inner.time               -fill x   -pady 2 \
2148        5,0 $inner.ambient_l          -anchor w -pady 2 \
2149        5,1 $inner.ambient            -fill x   -pady 2
2150#        4,0 $inner.edges     -cspan 2  -anchor w -pady 2
2151
2152    blt::table configure $inner r* c* -resize none
2153    blt::table configure $inner r6 c1 -resize expand
2154}
2155
2156itcl::body Rappture::MapViewer::BuildTerrainTab {} {
2157    set fg [option get $itk_component(hull) font Font]
2158    #set bfg [option get $itk_component(hull) boldFont Font]
2159
2160    set inner [$itk_component(main) insert end \
2161        -title "Terrain Settings" \
2162        -icon [Rappture::icon terrain]]
2163    $inner configure -borderwidth 4
2164
2165    label $inner.palette_l -text "Palette" -font "Arial 9" -anchor w
2166    itk_component add terrainpalette {
2167        Rappture::Combobox $inner.palette -width 10 -editable no
2168    }
2169    $inner.palette choices insert end [GetColormapList]
2170
2171    $itk_component(terrainpalette) value "BCGYR"
2172    bind $inner.palette <<Value>> \
2173        [itcl::code $this AdjustSetting terrain-palette]
2174
2175    itk_component add vscale_l {
2176        label $inner.vscale_l -text "Vertical Scale" -font "Arial 9" -anchor w
2177    }
2178    itk_component add vscale {
2179        ::scale $inner.vscale -from 0 -to 10 -orient horizontal \
2180            -variable [itcl::scope _settings(terrain-vertscale)] \
2181            -width 10 \
2182            -resolution 0.1 \
2183            -showvalue on \
2184            -command [itcl::code $this AdjustSetting terrain-vertscale]
2185    }
2186    $inner.vscale set $_settings(terrain-vertscale)
2187    Rappture::Tooltip::for $inner.vscale \
2188        "Set terrain vertical scale factor"
2189
2190    blt::table $inner \
2191        0,0 $inner.vscale_l  -anchor w -pady 2 \
2192        0,1 $inner.vscale    -fill x   -pady 2
2193#        1,0 $inner.palette_l -anchor w -pady 2 \
2194#        1,1 $inner.palette   -fill x   -pady 2
2195
2196    blt::table configure $inner r* c* -resize none
2197    blt::table configure $inner r3 c1 -resize expand
2198}
2199
2200itcl::body Rappture::MapViewer::BuildLayerTab {} {
2201    set fg [option get $itk_component(hull) font Font]
2202    #set bfg [option get $itk_component(hull) boldFont Font]
2203
2204    set inner [$itk_component(main) insert end \
2205        -title "Layers" \
2206        -icon [Rappture::icon layers]]
2207    $inner configure -borderwidth 4
2208    set f [frame $inner.layers]
2209    blt::table $inner \
2210        0,0 $f -fill both
2211    set _layersFrame $inner
2212}
2213
2214itcl::body Rappture::MapViewer::BuildViewpointsTab {} {
2215    set fg [option get $itk_component(hull) font Font]
2216    #set bfg [option get $itk_component(hull) boldFont Font]
2217
2218    set inner [$itk_component(main) insert end \
2219        -title "Places" \
2220        -icon [Rappture::icon placemark16]]
2221    $inner configure -borderwidth 4
2222    set f [frame $inner.viewpoints]
2223    blt::table $inner \
2224        0,0 $f -fill both
2225    set _viewpointsFrame $inner
2226}
2227
2228itcl::body Rappture::MapViewer::BuildCameraTab {} {
2229    set inner [$itk_component(main) insert end \
2230        -title "Camera Settings" \
2231        -icon [Rappture::icon camera]]
2232    $inner configure -borderwidth 4
2233
2234    set row 0
2235
2236    set labels { x y z heading pitch distance }
2237    foreach tag $labels {
2238        label $inner.${tag}label -text $tag -font "Arial 9"
2239        entry $inner.${tag} -font "Arial 9"  -bg white \
2240            -textvariable [itcl::scope _view($tag)]
2241        bind $inner.${tag} <KeyPress-Return> \
2242            [itcl::code $this Camera set ${tag}]
2243        bind $inner.${tag} <KP_Enter> \
2244            [itcl::code $this Camera set ${tag}]
2245        blt::table $inner \
2246            $row,0 $inner.${tag}label -anchor e -pady 2 \
2247            $row,1 $inner.${tag} -anchor w -pady 2
2248        blt::table configure $inner r$row -resize none
2249        incr row
2250    }
2251    set labels { srs verticalDatum }
2252    foreach tag $labels {
2253        label $inner.${tag}label -text $tag -font "Arial 9"
2254        entry $inner.${tag} -font "Arial 9"  -bg white \
2255            -textvariable [itcl::scope _view($tag)]
2256        bind $inner.${tag} <KeyPress-Return> \
2257            [itcl::code $this Camera set ${tag}]
2258        bind $inner.${tag} <KP_Enter> \
2259            [itcl::code $this Camera set ${tag}]
2260        blt::table $inner \
2261            $row,0 $inner.${tag}label -anchor e -pady 2 \
2262            $row,1 $inner.${tag} -anchor w -pady 2
2263        blt::table configure $inner r$row -resize none
2264        incr row
2265    }
2266
2267    if {0} {
2268    button $inner.get \
2269        -text "Get Camera Settings" \
2270        -font "Arial 9" \
2271        -command [itcl::code $this SendCmd "camera get"]
2272    blt::table $inner \
2273        $row,0 $inner.get -anchor w -pady 2 -cspan 2
2274    blt::table configure $inner r$row -resize none
2275    incr row
2276
2277    button $inner.set \
2278        -text "Apply Camera Settings" \
2279        -font "Arial 9" \
2280        -command [itcl::code $this Camera set all]
2281    blt::table $inner \
2282        $row,0 $inner.set -anchor w -pady 2 -cspan 2
2283    blt::table configure $inner r$row -resize none
2284    incr row
2285    }
2286
2287    if {$_useServerManip} {
2288        checkbutton $inner.throw \
2289            -text "Enable Throw" \
2290            -font "Arial 9" \
2291            -variable [itcl::scope _settings(camera-throw)] \
2292            -command [itcl::code $this AdjustSetting camera-throw]
2293        blt::table $inner \
2294            $row,0 $inner.throw -anchor w -pady 2 -cspan 2
2295        blt::table configure $inner r$row -resize none
2296        incr row
2297    }
2298
2299    label $inner.heading_slider_l -text "Heading" -font "Arial 9"
2300    ::scale $inner.heading_slider -font "Arial 9" \
2301        -from -180 -to 180 -orient horizontal \
2302        -variable [itcl::scope _view(heading)] \
2303        -width 10 \
2304        -showvalue on \
2305        -command [itcl::code $this Camera set heading]
2306
2307    blt::table $inner \
2308            $row,0 $inner.heading_slider_l -anchor w -pady 2
2309    blt::table $inner \
2310            $row,1 $inner.heading_slider -fill x -anchor w -pady 2
2311    blt::table configure $inner r$row -resize none
2312    incr row
2313
2314    itk_component add pitch_slider_l {
2315        label $inner.pitch_slider_l -text "Pitch" -font "Arial 9"
2316    }
2317    itk_component add pitch_slider {
2318        ::scale $inner.pitch_slider -font "Arial 9" \
2319            -from -10 -to -90 -orient horizontal \
2320            -variable [itcl::scope _view(pitch)] \
2321            -width 10 \
2322            -showvalue on \
2323            -command [itcl::code $this Camera set pitch]
2324    }
2325
2326    blt::table $inner \
2327            $row,0 $inner.pitch_slider_l -anchor w -pady 2
2328    blt::table $inner \
2329            $row,1 $inner.pitch_slider -fill x -anchor w -pady 2
2330    blt::table configure $inner r$row -resize none
2331    incr row
2332
2333    blt::table configure $inner c* r* -resize none
2334    blt::table configure $inner c2 -resize expand
2335    blt::table configure $inner r$row -resize expand
2336}
2337
2338itcl::body Rappture::MapViewer::BuildHelpTab {} {
2339    set fg [option get $itk_component(hull) font Font]
2340    #set bfg [option get $itk_component(hull) boldFont Font]
2341
2342    set inner [$itk_component(main) insert end \
2343        -title "Help" \
2344        -icon [Rappture::icon question_mark12]]
2345    $inner configure -borderwidth 4
2346
2347    set helptext {*************************
2348Mouse bindings:
2349*************************
2350  Left - Panning
2351  Middle - Rotation
2352  Right - Zoom
2353
2354Zoom/travel:
2355  Left double-click:
2356    Zoom to point
2357  Left shift-double:
2358    Travel to point
2359  Right double-click:
2360    Zoom out from point
2361
2362Pins:
2363  Ctl-Left: Drop pin
2364  Ctl-Right: Delete pin
2365
2366Select:
2367  Shift-Left click-drag
2368
2369*************************
2370Keyboard bindings:
2371*************************
2372  g - Toggle graticule
2373  l - Toggle lighting
2374  n - Set North up
2375  p - Reset pitch
2376  w - Toggle wireframe
2377  arrows - panning
2378  Shift-arrows - fine pan
2379  Ctl-arrows - rotation
2380  Ctl-Shift-arrows:
2381    fine rotation
2382  PgUp/PgDown - zoom
2383  Home - Reset camera
2384*************************}
2385
2386    text $inner.info -width 25 -bg white
2387    $inner.info insert end $helptext
2388    $inner.info configure -state disabled
2389    blt::table $inner \
2390        0,0 $inner.info -fill both
2391}
2392
2393#
2394# camera
2395#
2396# This is the public camera API
2397#
2398itcl::body Rappture::MapViewer::camera {option args} {
2399    switch -- $option {
2400        "reset" {
2401            Camera reset
2402        }
2403        "viewpoint" {
2404            if {[llength $args] < 1} {
2405                error "wrong # args to camera viewpoint"
2406            }
2407            set vpopt [lindex $args 0]
2408            switch -- $vpopt {
2409                "go" {
2410                    if {[llength $args] < 3} {
2411                        error "wrong # of args to camera viewpoint go"
2412                    }
2413                    foreach {dataobj viewpoint} [lrange $args 1 end] break
2414                    GoToViewpoint $dataobj $viewpoint
2415                }
2416                default {
2417                    error "Unknown camera viewpoint option \"$vpopt\""
2418                }
2419            }
2420        }
2421        "zoom" {
2422            if {[llength $args] < 1} {
2423                error "wrong # of args to camera zoom"
2424            }
2425            set zoomopt [lindex $args 0]
2426            switch -- $zoomopt {
2427                "extent" {
2428                    if {[llength $args] < 5} {
2429                        error "wrong # of args to camera zoom extent"
2430                    }
2431                    foreach {xmin ymin xmax ymax duration srs} [lrange $args 1 end] break
2432                    foreach key {xmin ymin xmax ymax} {
2433                        if {![string is double -strict [set $key]]} {
2434                            error "Invalid extent: $key=[set $key]"
2435                        }
2436                    }
2437                    if {$duration == ""} {
2438                        set duration 0.0
2439                    } elseif {![string is double $duration]} {
2440                        error "Invalid duration \"$duration\", should be a double"
2441                    }
2442                    SendCmd "camera extent $xmin $ymin $xmax $ymax $duration $srs"
2443                }
2444                "layer" {
2445                    if {[llength $args] < 3} {
2446                        error "wrong # of args to camera zoom layer"
2447                    }
2448                    foreach {dataobj layer duration} [lrange $args 1 end] break
2449                    set tag $layer
2450                    if {![$dataobj layer $layer shared]} {
2451                        set tag $dataobj-$layer
2452                    }
2453                    if {![info exists _layers($tag)]} {
2454                        error "Unknown layer $layer"
2455                    }
2456                    if {$duration == ""} {
2457                        set duration 0.0
2458                    } elseif {![string is double $duration]} {
2459                        error "Invalid duration \"$duration\", should be a double"
2460                    }
2461                    SendCmd "camera lextent $tag $duration"
2462                }
2463                default {
2464                    error "Unknown camera zoom option \"$zoomopt\""
2465                }
2466            }
2467        }
2468        default {
2469            error "Unknown camera option \"$option\""
2470        }
2471    }
2472}
2473
2474#
2475#  Camera --
2476#
2477# USAGE: Camera get
2478#        This is called by the server to transfer the
2479#        current Viewpoint settings
2480# USAGE: Camera reset
2481#        Reset the camera to the default view
2482#
2483itcl::body Rappture::MapViewer::Camera {option args} {
2484    switch -- $option {
2485        "get" {
2486            # We got the camera settings from the server
2487            foreach name {x y z heading pitch distance srs verticalDatum} value $args {
2488                set _view($name) $value
2489            }
2490            #DebugTrace "view: $_view(x), $_view(y), $_view(z), $_view(heading), $_view(pitch), $_view(distance), {$_view(srs)}, {$_view(verticalDatum)}"
2491        }
2492        "go" {
2493            SendCmd "camera go $args"
2494        }
2495        "reset" {
2496            array set _view {
2497                x               0.0
2498                y               0.0
2499                z               0.0
2500                heading         0.0
2501                pitch           -89.9
2502                distance        1.0
2503                srs             ""
2504                verticalDatum   ""
2505            }
2506            if { [info exists _mapsettings(camera)] } {
2507                # Check if the tool specified a default
2508                set view $_mapsettings(camera)
2509                if { $view != "" } {
2510                    array set cam $view
2511                    set duration 0.0
2512                    if {[info exists cam(layer)]} {
2513                        set tag $cam(layer)
2514                        if {$_first != "" &&
2515                            ![$_first layer $cam(layer) shared]} {
2516                            set tag $_first-$cam(layer)
2517                        }
2518                        SendCmd [list camera lextent $tag]
2519                    } elseif {[info exists cam(xmin)] &&
2520                              [info exists cam(ymin)] &&
2521                              [info exists cam(xmax)] &&
2522                              [info exists cam(ymax)]} {
2523                        set srs ""
2524                        if {[info exists cam(srs)]} {
2525                            set srs $cam(srs)
2526                        }
2527                        SendCmd [list camera extent $cam(xmin) $cam(ymin) $cam(xmax) $cam(ymax) $duration $srs]
2528                    } elseif {[info exists cam(latitude)] &&
2529                              [info exists cam(longitude)]} {
2530                        array set _view $view
2531                        set _view(x) $cam(longitude)
2532                        set _view(y) $cam(latitude)
2533                        set _view(srs) wgs84
2534                        SendCmd [list camera set $_view(x) $_view(y) $_view(z) $_view(heading) $_view(pitch) $_view(distance) $duration $_view(srs) $_view(verticalDatum)]
2535                    } else {
2536                        array set _view $view
2537                        SendCmd [list camera set $_view(x) $_view(y) $_view(z) $_view(heading) $_view(pitch) $_view(distance) $duration $_view(srs) $_view(verticalDatum)]
2538                    }
2539                } else {
2540                    SendCmd "camera reset"
2541                    # Retrieve the settings
2542                    #SendCmd "camera get"
2543                }
2544            } else {
2545                SendCmd "camera reset"
2546                # Retrieve the settings
2547                #SendCmd "camera get"
2548            }
2549        }
2550        "set" {
2551            set who [lindex $args 0]
2552            if {$who != "all" && $who != "srs" && $who != "verticalDatum"} {
2553                set val $_view($who)
2554                set code [catch { string is double $val } result]
2555                if { $code != 0 || !$result } {
2556                    return
2557                }
2558            }
2559            switch -- $who {
2560                "distance" {
2561                    SendCmd [list camera dist $_view(distance)]
2562                }
2563                "all" - "x" - "y" - "z" - "heading" - "pitch" - "srs" - "verticalDatum" {
2564                    set duration 0.0
2565                    SendCmd [list camera set $_view(x) $_view(y) $_view(z) $_view(heading) $_view(pitch) $_view(distance) $duration $_view(srs) $_view(verticalDatum)]
2566                }
2567            }
2568        }
2569    }
2570}
2571
2572itcl::body Rappture::MapViewer::GoToViewpoint { dataobj viewpoint {duration 2.0}} {
2573    array set view [subst {
2574        x 0
2575        y 0
2576        z 0
2577        heading 0
2578        pitch -90
2579        distance $_view(distance)
2580        srs "$_view(srs)"
2581        verticalDatum "$_view(verticalDatum)"
2582    }]
2583    array set view [$dataobj viewpoint $viewpoint]
2584    foreach key {x y z heading pitch distance srs verticalDatum} {
2585        if { [info exists view($key)] } {
2586            set _view($key) $view($key)
2587        }
2588    }
2589    # If map is projected, ignore pitch
2590    if {![MapIsGeocentric]} {
2591        set _view(pitch) -90
2592    }
2593    SendCmd [list camera set $_view(x) $_view(y) $_view(z) $_view(heading) $_view(pitch) $_view(distance) $duration $_view(srs) $_view(verticalDatum)]
2594}
2595
2596itcl::body Rappture::MapViewer::GetImage { args } {
2597    if { [image width $_image(download)] > 0 &&
2598         [image height $_image(download)] > 0 } {
2599        set bytes [$_image(download) data -format "jpeg -quality 100"]
2600        set bytes [Rappture::encoding::decode -as b64 $bytes]
2601        return [list .jpg $bytes]
2602    }
2603    return ""
2604}
2605
2606itcl::body Rappture::MapViewer::BuildDownloadPopup { popup command } {
2607    Rappture::Balloon $popup \
2608        -title "[Rappture::filexfer::label downloadWord] as..."
2609    set inner [$popup component inner]
2610    label $inner.summary -text "" -anchor w
2611
2612    radiobutton $inner.image_button -text "Image File" \
2613        -variable [itcl::scope _downloadPopup(format)] \
2614        -value image
2615    Rappture::Tooltip::for $inner.image_button \
2616        "Save as digital image."
2617
2618    button $inner.ok -text "Save" \
2619        -highlightthickness 0 -pady 2 -padx 3 \
2620        -command $command \
2621        -compound left \
2622        -image [Rappture::icon download]
2623
2624    button $inner.cancel -text "Cancel" \
2625        -highlightthickness 0 -pady 2 -padx 3 \
2626        -command [list $popup deactivate] \
2627        -compound left \
2628        -image [Rappture::icon cancel]
2629
2630    blt::table $inner \
2631        0,0 $inner.summary -cspan 2  \
2632        2,0 $inner.image_button -anchor w -cspan 2 -padx { 4 0 } \
2633        4,1 $inner.cancel -width .9i -fill y \
2634        4,0 $inner.ok -padx 2 -width .9i -fill y
2635    blt::table configure $inner r3 -height 4
2636    blt::table configure $inner r4 -pady 4
2637    raise $inner.image_button
2638    $inner.image_button invoke
2639    return $inner
2640}
2641
2642itcl::body Rappture::MapViewer::ToggleGrid {} {
2643    set _settings(grid) [expr !$_settings(grid)]
2644    AdjustSetting grid
2645}
2646
2647itcl::body Rappture::MapViewer::ToggleLighting {} {
2648    set _settings(terrain-lighting) [expr !$_settings(terrain-lighting)]
2649    AdjustSetting terrain-lighting
2650}
2651
2652itcl::body Rappture::MapViewer::ToggleWireframe {} {
2653    set _settings(terrain-wireframe) [expr !$_settings(terrain-wireframe)]
2654    AdjustSetting terrain-wireframe
2655}
2656
2657# Convert from Tcl integer epoch time value to hours as a double
2658itcl::body Rappture::MapViewer::ConvertTime { timeval } {
2659    set hrs [clock format $timeval -format %k -gmt 1]
2660    set min [clock format $timeval -format %M -gmt 1]
2661    set sec [clock format $timeval -format %S -gmt 1]
2662    set val [expr {$hrs + $min/60.0 + $sec/3600.0}]
2663}
2664
2665itcl::body Rappture::MapViewer::SetTerrainStyle { style } {
2666    array set settings {
2667        -ambient 0.03
2668        -color white
2669        -edgecolor black
2670        -edges 0
2671        -lighting 1
2672        -linewidth 1.0
2673        -time now
2674        -vertscale 1.0
2675        -wireframe 0
2676    }
2677    array set settings $style
2678
2679    SendCmd "map terrain ambient $settings(-ambient)"
2680    set _settings(terrain-ambient) $settings(-ambient)
2681    SendCmd "map terrain edges $settings(-edges)"
2682    set _settings(terrain-edges) $settings(-edges)
2683    SendCmd "map terrain color [Color2RGB $settings(-color)]"
2684    #SendCmd "map terrain colormode constant"
2685    if {$settings(-time) eq "now"} {
2686        set settings(-time) [ConvertTime [clock seconds]]
2687    } else {
2688        set settings(-time) [ConvertTime [clock scan $settings(-time) -gmt 1]]
2689    }
2690    SendCmd "map time $settings(-time)"
2691    set _settings(time) $settings(-time)
2692    SendCmd "map terrain lighting $settings(-lighting)"
2693    set _settings(terrain-lighting) $settings(-lighting)
2694    SendCmd "map terrain linecolor [Color2RGB $settings(-edgecolor)]"
2695    #SendCmd "map terrain linewidth $settings(-linewidth)"
2696    SendCmd "map terrain vertscale $settings(-vertscale)"
2697    set _settings(terrain-vertscale) $settings(-vertscale)
2698    SendCmd "map terrain wireframe $settings(-wireframe)"
2699    set _settings(terrain-wireframe) $settings(-wireframe)
2700}
2701
2702itcl::body Rappture::MapViewer::SendStylesheetFiles { stylesheet } {
2703    set files [Rappture::Map::getFilesFromStylesheet $stylesheet]
2704    foreach file $files {
2705        SendFiles $file
2706    }
2707}
2708
2709itcl::body Rappture::MapViewer::SendFiles { path } {
2710    set isRelative [expr {[string first "://" $path] < 0 &&
2711                          [string index $path 0] != "/"}]
2712    if {[string range $path 0 7] != "local://" &&
2713        !$isRelative} {
2714        return
2715    }
2716    DebugTrace "Local path: $path"
2717    if {!$isRelative} {
2718        set path [string range $path 8 end]
2719    }
2720    set basename [file rootname $path]
2721    if {[catch {set files [glob -path $basename .*]} err] != 0} {
2722        puts stderr "File not found: $path"
2723        return
2724    }
2725    foreach file $files {
2726        set name $file
2727        set type [file type $file]
2728        set size [file size $file]
2729        set f [open $file "r"]
2730        fconfigure $f -translation binary -encoding binary
2731        set data [read $f]
2732        close $f
2733        SendCmd [list file put $name $type $size]
2734        SendData $data
2735    }
2736}
2737
2738itcl::body Rappture::MapViewer::SetLayerStyle { dataobj layer } {
2739    array set info [$dataobj layer $layer]
2740    set tag $layer
2741    if { !$info(shared) } {
2742        set tag $dataobj-$layer
2743    }
2744    if { [info exists info(visible)] &&
2745         !$info(visible) } {
2746        set _visibility($tag) 0
2747    } else {
2748        set _visibility($tag) 1
2749    }
2750
2751    switch -- $info(type) {
2752        "image" {
2753            array set style {
2754                -minlevel 0
2755                -maxlevel 23
2756                -opacity 1.0
2757            }
2758            if { [info exists info(style)] } {
2759                DebugTrace "layer style: $info(style)"
2760                array set style $info(style)
2761            }
2762            if { [info exists info(opacity)] } {
2763                set style(-opacity) $info(opacity)
2764                set _opacity($tag) $info(opacity)
2765            }
2766            set _opacity($tag) [expr $style(-opacity) * 100]
2767            set coverage 0
2768            if { [info exists info(coverage)] } {
2769                set coverage $info(coverage)
2770            }
2771            switch -- $info(driver) {
2772                "arcgis" {
2773                    SendCmd [list map layer add $tag image arcgis \
2774                                 $info(arcgis.url) $info(cache) $coverage $info(arcgis.token)]
2775                }
2776                "colorramp" {
2777                    set cmapName "[regsub -all {::} ${tag} {}]"
2778                    set cmapName "[regsub -all {\-} ${cmapName} {_}]"
2779                    SendFiles $info(colorramp.url)
2780                    SendCmd [list colormap define $cmapName $info(colorramp.colormap)]
2781                    SendCmd [list map layer add $tag image colorramp \
2782                                 $info(colorramp.url) $info(cache) $coverage $info(colorramp.elevdriver) $info(profile)  \
2783                                 $cmapName]
2784                }
2785                "debug" {
2786                    SendCmd [list map layer add $tag image debug]
2787                }
2788                "gdal" {
2789                    SendFiles $info(gdal.url)
2790                    SendCmd [list map layer add $tag image gdal \
2791                                 $info(gdal.url) $info(cache) $coverage]
2792                }
2793                "tms" {
2794                    SendCmd [list map layer add $tag image tms \
2795                                 $info(tms.url) $info(cache) $coverage]
2796                }
2797                "wms" {
2798                    if {[info exists info(wms.times)]} {
2799                        SendCmd [list map layer add $tag image wms \
2800                                     $info(wms.url) $info(cache) $coverage \
2801                                     $info(wms.layers) \
2802                                     $info(wms.format) \
2803                                     $info(wms.transparent) \
2804                                     $info(wms.times) \
2805                                     $info(wms.frameSeconds)]
2806                    } else {
2807                        SendCmd [list map layer add $tag image wms \
2808                                     $info(wms.url) $info(cache) $coverage \
2809                                     $info(wms.layers) \
2810                                     $info(wms.format) \
2811                                     $info(wms.transparent)]
2812                    }
2813                }
2814                "xyz" {
2815                    SendCmd [list map layer add $tag image xyz \
2816                                 $info(xyz.url) $info(cache) $coverage]
2817                }
2818            }
2819            SendCmd "map layer opacity $style(-opacity) $tag"
2820        }
2821        "elevation" {
2822            array set style {
2823                -minlevel 0
2824                -maxlevel 23
2825            }
2826            if { [info exists info(style)] } {
2827                array set style $info(style)
2828            }
2829            set verticalDatum ""
2830            if {[info exists info(verticalDatum)]} {
2831                set verticalDatum $info(verticalDatum)
2832            }
2833            switch -- $info(driver) {
2834                "gdal" {
2835                    SendFiles $info(gdal.url)
2836                    SendCmd [list map layer add $tag elevation gdal \
2837                                 $info(gdal.url) $info(cache) $verticalDatum]
2838                }
2839                "tms" {
2840                    SendCmd [list map layer add $tag elevation tms \
2841                                 $info(tms.url) $info(cache) $verticalDatum]
2842                }
2843                "wcs" {
2844                    SendCmd [list map layer add $tag elevation wcs \
2845                                 $info(wcs.url) $info(cache) $verticalDatum $info(wcs.identifier)]
2846                }
2847            }
2848        }
2849        "feature" {
2850            array set style {
2851                -opacity 1.0
2852            }
2853            if { [info exists info(style)] } {
2854                DebugTrace "layer style: $info(style)"
2855                array set style $info(style)
2856            }
2857            if { [info exists info(opacity)] } {
2858                set style(-opacity) $info(opacity)
2859            }
2860            set _opacity($tag) [expr $style(-opacity) * 100]
2861            DebugTrace "stylesheet: $info(stylesheet)"
2862            set script ""
2863            if { [info exists info(script)] } {
2864                set script $info(script)
2865                DebugTrace "script: $script"
2866            }
2867            set selectors [list]
2868            foreach selector [$dataobj selectors $layer] {
2869                array set sinfo [$dataobj selector $layer $selector]
2870                DebugTrace "$selector: [array get sinfo]"
2871                lappend selectors [array get sinfo]
2872                if {[info exists sinfo(styleExpression)]} {
2873                    DebugTrace "$selector: $sinfo(styleExpression)"
2874                } elseif {[info exists sinfo(query)]} {
2875                    if {[info exists sinfo(queryBounds)]} {
2876                        foreach {x1 y1 x2 y2} $sinfo(queryBounds) break
2877                        DebugTrace "queryBounds: xmin $x1 ymin $y1 xmax $x2 ymax $y2"
2878                    }
2879                }
2880            }
2881            SendStylesheetFiles $info(stylesheet)
2882            set ssSize [string length $info(stylesheet)]
2883            set scriptSize [string length $script]
2884            set selectorsSize [string length $selectors]
2885            set terrainPatch 0
2886            if {[info exists info(terrainPatch)] && $info(terrainPatch)} {
2887                set terrainPatch 1
2888            }
2889            switch -- $info(driver) {
2890                "ogr" {
2891                    if {[info exists info(ogr.connection)]} {
2892                        set cmd [list map layer add $tag feature db {} $info(ogr.layer) $info(ogr.connection) $info(cache) $ssSize $scriptSize $selectorsSize $terrainPatch]
2893                        if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
2894                            lappend cmd $style(-minrange) $style(-maxrange)
2895                        }
2896                    } elseif {[info exists info(ogr.geometryUrl)]} {
2897                        set cmd [list map layer add $tag feature wkt_file {} {} $info(ogr.geometryUrl) $info(cache) $ssSize $scriptSize $selectorsSize $terrainPatch]
2898                        if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
2899                            lappend cmd $style(-minrange) $style(-maxrange)
2900                        }
2901                        SendFiles $info(ogr.geometryUrl)
2902                    } else {
2903                        set cmd [list map layer add $tag feature $info(driver) {} {} $info(ogr.url) $info(cache) $ssSize $scriptSize $selectorsSize $terrainPatch]
2904                        if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
2905                            lappend cmd $style(-minrange) $style(-maxrange)
2906                        }
2907                        SendFiles $info(ogr.url)
2908                    }
2909                }
2910                "tfs" {
2911                    set format "json"
2912                    if {[info exists info(tfs.format)]} {
2913                        set format $info(tfs.format)
2914                    }
2915                    set cmd [list map layer add $tag feature $info(driver) $format {} $info(tfs.url) $info(cache) $ssSize $scriptSize $selectorsSize $terrainPatch]
2916                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
2917                        lappend cmd $style(-minrange) $style(-maxrange)
2918                    }
2919                }
2920                "wfs" {
2921                    set format "json"
2922                    if {[info exists info(wfs.format)]} {
2923                        set format $info(wfs.format)
2924                    }
2925                    set wfsType ""
2926                    if {[info exists info(wfs.typename)]} {
2927                        set wfsType $info(wfs.typename)
2928                    }
2929                    set cmd [list map layer add $tag feature $info(driver) $format $wfsType $info(wfs.url) $info(cache) $ssSize $scriptSize $selectorsSize $terrainPatch]
2930                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
2931                        lappend cmd $style(-minrange) $style(-maxrange)
2932                    }
2933                }
2934            }
2935            SendCmd $cmd
2936            if { $ssSize > 0 }        { SendData $info(stylesheet) }
2937            if { $scriptSize > 0 }    { SendData $script }
2938            if { $selectorsSize > 0 } { SendData $selectors }
2939            SendCmd "map layer opacity $style(-opacity) $tag"
2940        }
2941        "mask"  {
2942            set minLOD 0
2943            switch -- $info(driver) {
2944                "ogr" {
2945                    if {[info exists info(ogr.connection)]} {
2946                        SendCmd [list map layer add $tag mask db {} $info(ogr.layer) $info(ogr.connection) $minLOD]
2947                    } elseif {[info exists info(ogr.geometryUrl)]} {
2948                        SendFiles $info(ogr.geometryUrl)
2949                        SendCmd [list map layer add $tag mask wkt_file {} {} $info(ogr.geometryUrl) $minLOD]
2950                    } elseif {[info exists info(ogr.geometry)]} {
2951                        set len [string length $info(ogr.geometry)]
2952                        SendCmd [list map layer add $tag mask wkt {} {} $len $minLOD]
2953                        if {$len > 0} { SendData $info(ogr.geometry) }
2954                    } else {
2955                        SendFiles $info(ogr.url)
2956                        SendCmd [list map layer add $tag mask $info(driver) {} {} $info(ogr.url) $minLOD]
2957                    }
2958                }
2959                "tfs" {
2960                    set format "json"
2961                    if {[info exists info(tfs.format)]} {
2962                        set format $info(tfs.format)
2963                    }
2964                    SendCmd [list map layer add $tag mask $info(driver) $format {} $info(tfs.url) $minLOD]
2965                }
2966                "wfs" {
2967                    set format "json"
2968                    if {[info exists info(wfs.format)]} {
2969                        set format $info(wfs.format)
2970                    }
2971                    set wfsType ""
2972                    if {[info exists info(wfs.typename)]} {
2973                        set wfsType $info(wfs.typename)
2974                    }
2975                    SendCmd [list map layer add $tag mask $info(driver) $format $wfsType $info(wfs.url) $minLOD]
2976                }
2977            }
2978        }
2979        "model" {
2980            set terrainPatch 0
2981            if {[info exists info(terrainPatch)] && $info(terrainPatch)} {
2982                set terrainPatch 1
2983            }
2984            switch -- $info(driver) {
2985                "osg" {
2986                    SendFiles $info(osg.url)
2987                    SendCmd [list map layer add $tag model simple $info(osg.url) $info(osg.x) $info(osg.y) $info(osg.z) $info(osg.rotx) $info(osg.roty) $info(osg.rotz) $terrainPatch]
2988                }
2989            }
2990        }
2991        "line" {
2992            array set style {
2993                -cap "flat"
2994                -clamping terrain
2995                -clamptechnique gpu
2996                -color black
2997                -join "mitre"
2998                -minbias 1000
2999                -opacity 1.0
3000                -stipplepattern 0
3001                -stipplefactor 1
3002                -width 1
3003            }
3004            if { [info exists info(style)] } {
3005                array set style $info(style)
3006            }
3007            if { [info exists info(opacity)] } {
3008                set style(-opacity) $info(opacity)
3009            }
3010            set _opacity($tag) [expr $style(-opacity) * 100]
3011            foreach {r g b} [Color2RGB $style(-color)] {}
3012            switch -- $info(driver) {
3013                "ogr" {
3014                    SendFiles $info(ogr.url)
3015                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3016                        SendCmd [list map layer add $tag line ogr {} {} $info(ogr.url) $info(cache) $r $g $b $style(-width) $style(-cap) $style(-join) $style(-stipplepattern) $style(-stipplefactor) $style(-clamping) $style(-clamptechnique) $style(-minrange) $style(-maxrange)]
3017                    } else {
3018                        SendCmd [list map layer add $tag line ogr {} {} $info(ogr.url) $info(cache) $r $g $b $style(-width) $style(-cap) $style(-join) $style(-stipplepattern) $style(-stipplefactor) $style(-clamping) $style(-clamptechnique)]
3019                    }
3020                }
3021                "tfs" {
3022                    set format "json"
3023                    if {[info exists info(tfs.format)]} {
3024                        set format $info(tfs.format)
3025                    }
3026                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3027                        SendCmd [list map layer add $tag line tfs $format {} $info(tfs.url) $info(cache) $r $g $b $style(-width) $style(-cap) $style(-join) $style(-stipplepattern) $style(-stipplefactor) $style(-clamping) $style(-clamptechnique) $style(-minrange) $style(-maxrange)]
3028                    } else {
3029                        SendCmd [list map layer add $tag line tfs $format {} $info(tfs.url) $info(cache) $r $g $b $style(-width) $style(-cap) $style(-join) $style(-stipplepattern) $style(-stipplefactor) $style(-clamping) $style(-clamptechnique)]
3030                    }
3031                }
3032                "wfs" {
3033                    set format "json"
3034                    if {[info exists info(wfs.format)]} {
3035                        set format $info(wfs.format)
3036                    }
3037                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3038                        SendCmd [list map layer add $tag line wfs $format $info(wfs.typename) $info(wfs.url) $info(cache) $r $g $b $style(-width) $style(-cap) $style(-join) $style(-stipplepattern) $style(-stipplefactor) $style(-clamping) $style(-clamptechnique) $style(-minrange) $style(-maxrange)]
3039                    } else {
3040                        SendCmd [list map layer add $tag line wfs $format $info(wfs.typename) $info(wfs.url) $info(cache) $r $g $b $style(-width) $style(-cap) $style(-join) $style(-stipplepattern) $style(-stipplefactor) $style(-clamping) $style(-clamptechnique)]
3041                    }
3042                }
3043            }
3044            SendCmd "map layer opacity $style(-opacity) $tag"
3045        }
3046        "point" {
3047            array set style {
3048                -color black
3049                -minbias 1000
3050                -opacity 1.0
3051                -size 1
3052            }
3053            if { [info exists info(style)] } {
3054                array set style $info(style)
3055            }
3056            if { [info exists info(opacity)] } {
3057                set style(-opacity) $info(opacity)
3058            }
3059            set _opacity($tag) [expr $style(-opacity) * 100]
3060            foreach {r g b} [Color2RGB $style(-color)] {}
3061            switch -- $info(driver) {
3062                "ogr" {
3063                    SendFiles $info(ogr.url)
3064                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3065                        SendCmd [list map layer add $tag point ogr {} {} $info(ogr.url) $info(cache) $r $g $b $style(-size) $style(-minrange) $style(-maxrange)]
3066                    } else {
3067                        SendCmd [list map layer add $tag point ogr {} {} $info(ogr.url) $info(cache) $r $g $b $style(-size)]
3068                    }
3069                }
3070                "tfs" {
3071                    set format "json"
3072                    if {[info exists info(tfs.format)]} {
3073                        set format $info(tfs.format)
3074                    }
3075                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3076                        SendCmd [list map layer add $tag point tfs $format {} $info(ogr.url) $info(cache) $r $g $b $style(-size) $style(-minrange) $style(-maxrange)]
3077                    } else {
3078                        SendCmd [list map layer add $tag point tfs $format {} $info(ogr.url) $info(cache) $r $g $b $style(-size)]
3079                    }
3080                }
3081                "wfs" {
3082                    set format "json"
3083                    if {[info exists info(wfs.format)]} {
3084                        set format $info(wfs.format)
3085                    }
3086                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3087                        SendCmd [list map layer add $tag point wfs $format $info(wfs.typename) $info(ogr.url) $info(cache) $r $g $b $style(-size) $style(-minrange) $style(-maxrange)]
3088                    } else {
3089                        SendCmd [list map layer add $tag point wfs $format $info(wfs.typename) $info(ogr.url) $info(cache) $r $g $b $style(-size)]
3090                    }
3091                }
3092            }
3093            SendCmd "map layer opacity $style(-opacity) $tag"
3094        }
3095        "icon" {
3096            array set style {
3097                -align "center_bottom"
3098                -declutter 1
3099                -heading {}
3100                -icon pin
3101                -minbias 1000
3102                -opacity 1.0
3103                -placement "vertex"
3104                -scale {}
3105            }
3106            if { [info exists info(style)] } {
3107                array set style $info(style)
3108            }
3109            if { [info exists info(opacity)] } {
3110                set style(-opacity) $info(opacity)
3111            }
3112            set _opacity($tag) [expr $style(-opacity) * 100]
3113            switch -- $info(driver) {
3114                "ogr" {
3115                    SendFiles $info(ogr.url)
3116                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3117                        SendCmd [list map layer add $tag icon ogr {} {} $info(ogr.url) $info(cache) $style(-icon) $style(-scale) $style(-heading) $style(-declutter) $style(-placement) $style(-align) $style(-minrange) $style(-maxrange)]
3118                    } else {
3119                        SendCmd [list map layer add $tag icon ogr {} {} $info(ogr.url) $info(cache) $style(-icon) $style(-scale) $style(-heading) $style(-declutter) $style(-placement) $style(-align)]
3120                    }
3121                }
3122                "tfs" {
3123                    set format "json"
3124                    if {[info exists info(tfs.format)]} {
3125                        set format $info(tfs.format)
3126                    }
3127                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3128                        SendCmd [list map layer add $tag icon tfs $format {} $info(tfs.url) $info(cache) $style(-icon) $style(-scale) $style(-heading) $style(-declutter) $style(-placement) $style(-align) $style(-minrange) $style(-maxrange)]
3129                    } else {
3130                        SendCmd [list map layer add $tag icon tfs $format {} $info(tfs.url) $info(cache) $style(-icon) $style(-scale) $style(-heading) $style(-declutter) $style(-placement) $style(-align)]
3131                    }
3132                }
3133                "wfs" {
3134                    set format "json"
3135                    if {[info exists info(wfs.format)]} {
3136                        set format $info(wfs.format)
3137                    }
3138                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3139                        SendCmd [list map layer add $tag icon wfs $format $info(wfs.typename) $info(wfs.url) $info(cache) $style(-icon) $style(-scale) $style(-heading) $style(-declutter) $style(-placement) $style(-align) $style(-minrange) $style(-maxrange)]
3140                    } else {
3141                        SendCmd [list map layer add $tag icon wfs $format $info(wfs.typename) $info(wfs.url) $info(cache) $style(-icon) $style(-scale) $style(-heading) $style(-declutter) $style(-placement) $style(-align)]
3142                    }
3143                }
3144            }
3145            SendCmd "map layer opacity $style(-opacity) $tag"
3146        }
3147        "polygon" {
3148            array set style {
3149                -clamping terrain
3150                -clamptechnique drape
3151                -color white
3152                -minbias 1000
3153                -opacity 1.0
3154                -strokecolor black
3155                -strokewidth 0.0
3156            }
3157            if { [info exists info(style)] } {
3158                array set style $info(style)
3159            }
3160            if { [info exists info(opacity)] } {
3161                set style(-opacity) $info(opacity)
3162            }
3163            set _opacity($tag) [expr $style(-opacity) * 100]
3164            foreach {r g b} [Color2RGB $style(-color)] {}
3165            foreach {strokeR strokeG strokeB} [Color2RGB $style(-strokecolor)] {}
3166            set terrainPatch 0
3167            if {[info exists info(terrainPatch)] && $info(terrainPatch)} {
3168                set terrainPatch 1
3169            }
3170            switch -- $info(driver) {
3171                "ogr" {
3172                    SendFiles $info(ogr.url)
3173                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3174                        SendCmd [list map layer add $tag polygon ogr {} {} $info(ogr.url) $info(cache) $r $g $b $style(-strokewidth) $strokeR $strokeG $strokeB $style(-clamping) $style(-clamptechnique) $terrainPatch $style(-minrange) $style(-maxrange)]
3175                    } else {
3176                        SendCmd [list map layer add $tag polygon ogr {} {} $info(ogr.url) $info(cache) $r $g $b $style(-strokewidth) $strokeR $strokeG $strokeB $style(-clamping) $style(-clamptechnique) $terrainPatch]
3177                    }
3178                }
3179                "tfs" {
3180                    set format "json"
3181                    if {[info exists info(tfs.format)]} {
3182                        set format $info(tfs.format)
3183                    }
3184                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3185                        SendCmd [list map layer add $tag polygon tfs $format {} $info(tfs.url) $info(cache) $r $g $b $style(-strokewidth) $strokeR $strokeG $strokeB $style(-clamping) $style(-clamptechnique) $terrainPatch $style(-minrange) $style(-maxrange)]
3186                    } else {
3187                        SendCmd [list map layer add $tag polygon tfs $format {} $info(tfs.url) $info(cache) $r $g $b $style(-strokewidth) $strokeR $strokeG $strokeB $style(-clamping) $style(-clamptechnique) $terrainPatch]
3188                    }
3189                }
3190                "wfs" {
3191                    set format "json"
3192                    if {[info exists info(wfs.format)]} {
3193                        set format $info(wfs.format)
3194                    }
3195                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3196                        SendCmd [list map layer add $tag polygon wfs $format $info(wfs.typename) $info(wfs.url) $info(cache) $r $g $b $style(-strokewidth) $strokeR $strokeG $strokeB $style(-clamping) $style(-clamptechnique) $terrainPatch $style(-minrange) $style(-maxrange)]
3197                    } else {
3198                        SendCmd [list map layer add $tag polygon wfs $format $info(wfs.typename) $info(wfs.url) $info(cache) $r $g $b $style(-strokewidth) $strokeR $strokeG $strokeB $style(-clamping) $style(-clamptechnique) $terrainPatch]
3199                    }
3200                }
3201            }
3202            SendCmd "map layer opacity $style(-opacity) $tag"
3203        }
3204        "label" {
3205            array set style {
3206                -align "left_baseline"
3207                -color black
3208                -declutter 1
3209                -font Arial
3210                -fontsize 16.0
3211                -halocolor white
3212                -halowidth 2.0
3213                -layout "left_to_right"
3214                -minbias 1000
3215                -opacity 1.0
3216                -xoffset 0
3217                -yoffset 0
3218            }
3219            if { [info exists info(style)] } {
3220                array set style $info(style)
3221            }
3222            if { [info exists info(opacity)] } {
3223                set style(-opacity) $info(opacity)
3224            }
3225            set _opacity($tag) [expr $style(-opacity) * 100]
3226            set contentExpr $info(content)
3227            if {[info exists info(priority)]} {
3228                set priorityExpr $info(priority)
3229            } else {
3230                set priorityExpr ""
3231            }
3232            foreach {fgR fgG fgB} [Color2RGB $style(-color)] {}
3233            foreach {bgR bgG bgB} [Color2RGB $style(-halocolor)] {}
3234            switch -- $info(driver) {
3235                "ogr" {
3236                    SendFiles $info(ogr.url)
3237                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3238                        SendCmd [list map layer add $tag text ogr {} {} $info(ogr.url) $info(cache) $contentExpr $priorityExpr $fgR $fgG $fgB $bgR $bgG $bgB $style(-halowidth) $style(-fontsize) $style(-declutter) $style(-align) $style(-xoffset) $style(-yoffset) $style(-minrange) $style(-maxrange)]
3239                    } else {
3240                        SendCmd [list map layer add $tag text ogr {} {} $info(ogr.url) $info(cache) $contentExpr $priorityExpr $fgR $fgG $fgB $bgR $bgG $bgB $style(-halowidth) $style(-fontsize) $style(-declutter) $style(-align) $style(-xoffset) $style(-yoffset)]
3241                    }
3242                }
3243                "tfs" {
3244                    set format "json"
3245                    if {[info exists info(tfs.format)]} {
3246                        set format $info(tfs.format)
3247                    }
3248                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3249                        SendCmd [list map layer add $tag text tfs $format {} $info(tfs.url) $info(cache) $contentExpr $priorityExpr $fgR $fgG $fgB $bgR $bgG $bgB $style(-halowidth) $style(-fontsize) $style(-declutter) $style(-align) $style(-xoffset) $style(-yoffset) $style(-minrange) $style(-maxrange)]
3250                    } else {
3251                        SendCmd [list map layer add $tag text tfs $format {} $info(tfs.url) $info(cache) $contentExpr $priorityExpr $fgR $fgG $fgB $bgR $bgG $bgB $style(-halowidth) $style(-fontsize) $style(-declutter) $style(-align) $style(-xoffset) $style(-yoffset)]
3252                    }
3253                }
3254                "wfs" {
3255                    set format "json"
3256                    if {[info exists info(wfs.format)]} {
3257                        set format $info(wfs.format)
3258                    }
3259                    if {[info exists style(-minrange)] && [info exists style(-maxrange)]} {
3260                        SendCmd [list map layer add $tag text wfs $format $info(wfs.typename) $info(wfs.url) $contentExpr $priorityExpr $fgR $fgG $fgB $bgR $bgG $bgB $style(-halowidth) $style(-fontsize) $style(-declutter) $style(-align) $style(-xoffset) $style(-yoffset) $style(-minrange) $style(-maxrange)]
3261                    } else {
3262                        SendCmd [list map layer add $tag text wfs $format $info(wfs.typename) $info(wfs.url) $contentExpr $priorityExpr $fgR $fgG $fgB $bgR $bgG $bgB $style(-halowidth) $style(-fontsize) $style(-declutter) $style(-align) $style(-xoffset) $style(-yoffset)]
3263                    }
3264                }
3265            }
3266            SendCmd "map layer opacity $style(-opacity) $tag"
3267        }
3268    }
3269
3270    if {[info exists info(placard)]} {
3271        if {$info(type) == "image" || $info(type) == "elevation"} {
3272            error "Placard not supported on image or elevation layers"
3273        }
3274        array set placard [$dataobj getPlacardConfig $layer]
3275        SendCmd [list placard config $placard(attrlist) $placard(style) $placard(padding) $tag]
3276    }
3277
3278    SendCmd "map layer visible $_visibility($tag) $tag"
3279}
3280
3281itcl::body Rappture::MapViewer::SetLayerOpacity { dataobj layer {value 100}} {
3282    set tag $layer
3283    if {![$dataobj layer $layer shared]} {
3284        set tag $dataobj-$layer
3285    }
3286    set val $_opacity($tag)
3287    set sval [expr { 0.01 * double($val) }]
3288    SendCmd "map layer opacity $sval $tag"
3289}
3290
3291itcl::body Rappture::MapViewer::SetLayerVisibility { dataobj layer } {
3292    set tag $layer
3293    if {![$dataobj layer $layer shared]} {
3294        set tag $dataobj-$layer
3295    }
3296    set bool $_visibility($tag)
3297    SendCmd "map layer visible $bool $tag"
3298}
3299
3300itcl::body Rappture::MapViewer::UpdateLayerControls {} {
3301    set row 0
3302    set inner $_layersFrame
3303    if { [winfo exists $inner.layers] } {
3304        foreach w [winfo children $inner.layers] {
3305            destroy $w
3306        }
3307    }
3308    set f $inner.layers
3309    set attrib [list]
3310    set imgIdx 0
3311    # FIXME: This order may not match stacking order in server
3312    foreach dataobj [get -objects] {
3313        foreach layer [$dataobj layers] {
3314            array unset info
3315            array set info [$dataobj layer $layer]
3316            set tag $layer
3317            set ctlname $layer
3318            if {!$info(shared)} {
3319                set tag $dataobj-$layer
3320                set ctlname "[regsub -all {::} ${tag} {}]"
3321                set ctlname "[regsub -all {\-} ${ctlname} {_}]"
3322            }
3323
3324            set tooltip [list $info(description)]
3325            if { [info exists info(attribution)] &&
3326                 $info(attribution) != ""} {
3327                lappend tooltip $info(attribution)
3328            }
3329
3330            button $f.${ctlname}_zoom \
3331                -borderwidth 1 -padx 1 -pady 1 \
3332                -highlightthickness 0 \
3333                -image [Rappture::icon zoom-extent] \
3334                -command [itcl::code $this camera zoom layer $dataobj $layer 1.0]
3335            blt::table $f $row,0 $f.${ctlname}_zoom -anchor w -pady 2
3336            Rappture::Tooltip::for $f.${ctlname}_zoom \
3337                "Zoom to extent for $info(label)"
3338
3339            if { $info(type) == "mask" } {
3340                label $f.${ctlname}_lbl \
3341                    -text $info(label) \
3342                    -font "Arial 9" -anchor w
3343                blt::table $f $row,1 $f.${ctlname}_lbl -anchor w -pady 2
3344                Rappture::Tooltip::for $f.${ctlname}_lbl [join $tooltip \n]
3345            } else {
3346                checkbutton $f.${ctlname}_visible \
3347                    -text $info(label) \
3348                    -font "Arial 9" -anchor w \
3349                    -variable [itcl::scope _visibility($tag)] \
3350                    -command [itcl::code $this \
3351                                  SetLayerVisibility $dataobj $layer]
3352                blt::table $f $row,1 $f.${ctlname}_visible -anchor w -pady 2
3353                Rappture::Tooltip::for $f.${ctlname}_visible [join $tooltip \n]
3354            }
3355            incr row
3356            if { $info(type) == "image" } {
3357                incr imgIdx
3358                if { $info(driver) == "colorramp" } {
3359                    set colormap $ctlname
3360                    set f2 $f.cmap_$ctlname
3361                    frame $f2
3362                    if { ![info exists _image(legend-$colormap)] } {
3363                        set _image(legend-$colormap) [image create photo]
3364                    }
3365                    itk_component add legend-$colormap-min {
3366                        label $f2.legend-$colormap-min -text 0
3367                    }
3368                    itk_component add legend-$colormap-max {
3369                        label $f2.legend-$colormap-max -text 1
3370                    }
3371                    itk_component add legend-$colormap {
3372                        label $f2.legend-$colormap -image $_image(legend-$colormap)
3373                    }
3374                    blt::table $f2 0,0 $f2.legend-$colormap-min -anchor w -pady 0
3375                    blt::table $f2 0,1 $f2.legend-$colormap-max -anchor e -pady 0
3376                    blt::table $f2 1,0 $f2.legend-$colormap -anchor w -pady 2 -cspan 2
3377
3378                    blt::table $f $row,0 $f2 -anchor w -pady 0 -cspan 2
3379                    incr row
3380                    RequestLegend $colormap 256 16
3381                }
3382            }
3383            if { $info(type) != "elevation" &&
3384                 $info(type) != "mask" &&
3385                ($info(type) != "image" || $imgIdx > 1) } {
3386                set f2 $f.op_$ctlname
3387                frame $f2
3388                label $f2.${ctlname}_opacity_l -text "Opacity" -font "Arial 9"
3389                ::scale $f2.${ctlname}_opacity -from 0 -to 100 \
3390                    -orient horizontal -showvalue off \
3391                    -variable [itcl::scope _opacity($tag)] \
3392                    -width 10 \
3393                    -command [itcl::code $this \
3394                                  SetLayerOpacity $dataobj $layer]
3395                Rappture::Tooltip::for $f2.${ctlname}_opacity "Set opacity of $info(label) layer"
3396                blt::table $f2 0,0 $f2.${ctlname}_opacity_l -anchor w -pady 2
3397                blt::table $f2 0,1 $f2.${ctlname}_opacity -anchor w -pady 2
3398
3399                blt::table $f $row,0 $f2 -anchor w -pady 0 -cspan 2
3400                incr row
3401            }
3402        }
3403        set mapAttrib [$dataobj hints "attribution"]
3404        if { $mapAttrib != "" } {
3405            lappend attrib $mapAttrib
3406        }
3407    }
3408    SendCmd "[list map attrib [encoding convertto utf-8 [join $attrib ,]]]"
3409    label $f.map_attrib -text [join $attrib \n] -font "Arial 9"
3410    blt::table $f $row,0 $f.map_attrib -anchor sw -pady 2 -cspan 2
3411    #incr row
3412    if { $row > 0 } {
3413        blt::table configure $f r* c* -resize none
3414        blt::table configure $f r$row c1 -resize expand
3415    }
3416}
3417
3418itcl::body Rappture::MapViewer::UpdateViewpointControls {} {
3419    set row 0
3420    set inner $_viewpointsFrame
3421    if { [winfo exists $inner.viewpoints] } {
3422        foreach w [winfo children $inner.viewpoints] {
3423            destroy $w
3424        }
3425    }
3426    set f $inner.viewpoints
3427    foreach dataobj [get -objects] {
3428        foreach viewpoint [$dataobj viewpoints] {
3429            array unset info
3430            array set info [$dataobj viewpoint $viewpoint]
3431            button $f.${viewpoint}_go \
3432                -relief flat -compound left \
3433                -image [Rappture::icon placemark16] \
3434                -text $info(label) \
3435                -font "Arial 9" -anchor w \
3436                -command [itcl::code $this \
3437                              GoToViewpoint $dataobj $viewpoint]
3438            label $f.${viewpoint}_label \
3439                -text $info(label) \
3440                -font "Arial 9" -anchor w
3441            blt::table $f $row,0 $f.${viewpoint}_go -anchor w -pady 2 -cspan 2
3442            #blt::table $f $row,1 $f.${viewpoint}_label -anchor w -pady 2
3443            if {[info exists info(description)]} {
3444                Rappture::Tooltip::for $f.${viewpoint}_go $info(description)
3445            }
3446            incr row
3447        }
3448    }
3449    if { $row > 0 } {
3450        blt::table configure $f r* c* -resize none
3451        blt::table configure $f r$row c1 -resize expand
3452    }
3453}
3454
3455# ----------------------------------------------------------------------
3456# OPTION: -map
3457# ----------------------------------------------------------------------
3458itcl::configbody Rappture::MapViewer::map {
3459    if {$itk_option(-map) != "" } {
3460        if {[catch {$itk_option(-map) isa Rappture::Map} valid] != 0 || !$valid} {
3461            error "bad value \"$itk_option(-map)\": should be Rappture::Map object"
3462        }
3463        $this clear
3464        $this add $itk_option(-map)
3465        $this scale $itk_option(-map)
3466    } else {
3467        $this clear
3468    }
3469
3470    if { [isconnected] } {
3471        $_dispatcher event -idle !rebuild
3472    }
3473}
Note: See TracBrowser for help on using the repository browser.