Richard Suchenwirth - Here's version 0.2 of the map viewer tkWorld. Still preliminary, but contains better color scheme (light blue for lakes, shades of green for land, white for polar regions) and, best of all, an improved zooming that reacts on left/right mouse click as well.
The name has now changed to Tclworld, the projection of the map is now by default a quasi-Winkel, and a database browse/zoom-in mechanism has been added.
namespace eval geo {variable db ;# array that holds our database} proc geo::Set args { variable db if {[llength $args]==1} {set args [lindex $args 0]} foreach line [split $args \n] { set name [lindex $line 0] if {[llength $line]>2 && $name !="#"} { set db($name) [lrange $line 1 end] } } } proc geo::Get arg { variable db if {[info exists db($arg)]} { set value $db($arg) } elseif {![catch {expr [join $arg +]}]} { set value [concat point $arg] } else {return ""} set rest [lrange $value 1 end] if {[llength $rest]==1} {set res [lindex $rest 0]} switch -glob -- [lindex $value 0] { area - lake {set res [Make polygon $rest]} bound* {set res [Make line $rest]} city {set res [city $arg]} point { if {[llength $rest] != 2} return foreach {x y} $rest break set res [list point [expr {-$x}] [expr {-$y}]] } road {set res [Make line $rest]} default {return -code error "cannot get $arg"} } set res } proc geo::Make {item argl} { foreach arg $argl { eval lappend item [lrange [Get $arg] 1 end] } Join $item } proc geo::city name { variable db foreach {x y} [lrange $db($name) 1 2] break concat oval [expr {-$x-.01}] [expr {-$y-.01}] \ [expr {-$x+.01}] [expr {-$y+.01}] } proc geo::Join list { set res [lrange $list 0 2] set lastx [lindex $res 1] set lasty [lindex $res 2] foreach {x y} [lrange $list 3 end] { if {$x!=$lastx || $y!=$lasty} { lappend res $x $y } set lastx $x set lasty $y } join $res }
# Rendering is done with an overloaded canvas:
proc geo::swapxy L { variable zoom set res [lindex $L 0] foreach {lat lon} [lrange $L 1 end] { lappend res [expr {$lon*$zoom}] [expr {$lat*$zoom}]} set res } proc geo::Canvas {} { variable canvas set canvas } proc geo::Map {w args} { variable zoom 2. canvas eval canvas $w $args ;# create the base widget set canvas $w rename ::$w ::_$w proc ::$w {cmd args} [string map [list @w@ _$w] { variable db set w [lindex [info level 1] 0] set name [lindex $args 0] set rest [lrange $args 1 end] switch -- $cmd { show { set data [geo::swapxy [geo::Get $name]] #if {[llength $data]<4} return set item [eval _$w create $data -tag [list $name]] eval _$w itemconfig $item $rest set type [lindex $geo::db($name) 0] if {$type=="city"} { _$w itemconfig $item -fill red } elseif {$type=="lake"} { _$w addtag lake withtag $item _$w itemconfig $item -fill lightblue } set item } default {eval @w@ $cmd $args} } }] set w } proc geo::Names {{pat *}} { variable db array names db $pat } #---------------------------------------------------------------------- proc scrolled {type w args} { frame $w set res [eval $type $w.0 \ [list -xscrollc "$w.x set" -yscrollc "$w.y set"] $args] scrollbar $w.x -ori hori -command "$w.0 xview" scrollbar $w.y -ori vert -command "$w.0 yview" grid $w.0 $w.y -sticky news grid $w.x -sticky news grid columnconfigure $w 0 -weight 1 grid rowconfigure $w 0 -weight 1 set res } proc lpick L {lindex $L [expr {int(rand()*[llength $L])}]} proc scale {w factor} { if {$factor==0} {set factor [expr {2./$::scale}]} $w scale all 0 0 $factor $factor $w config -scrollregion [$w bbox all] set ::scale [expr {$::scale*$factor}] } proc scale2 {w x y factor} { if {$x=="."} { set x [expr [$w cget -width]/2] set y [expr [$w cget -height]/2] } foreach {x0 y0 x1 y1} [$w bbox all] break set width [expr double($x1-$x0)] set height [expr double($y1-$y0)] set cx [$w canvasx $x] set cy [$w canvasy $y] set relw [expr {($cx-$x0-$x/$factor)/$width}] set relh [expr {($cy-$y0-$y/$factor)/$height}] #scale $w $factor if {$factor==0} {set factor [expr {2./$::scale}]} $w scale all 0 0 $factor $factor $w config -scrollregion [$w bbox all] set ::scale [expr {$::scale*$factor}] $w xview moveto $relw $w yview moveto $relh } proc where {w x y} { global scale set here [$w find withtag current] if {[llength $here]} { set ::where [string map { current {} lake {} } [$w itemcget $here -tags]] } else {set ::where ""} set lat [format %.2f [expr {-[$w canvasy $y]/$scale}]] if {$lat>0} {append lat N} else {set lat [expr {-$lat}]S} set lon [format %.2f [expr {-[$w canvasx $x]/$scale}]] if {$lon>0} {append lon W} else {set lon [expr {-$lon}]E} set ::position "$lat $lon" } #---------------------------------------------------------------------- set c [scrolled geo::Map .m -width 720 -height 360 -bg lightblue] frame .top label .top.scale -textvar scale -width 10 button .top.plus -text + -width 3 -pady 0 -command [list scale2 $c . . 2.0] button .top.minus -text - -width 3 -pady 0 -command [list scale2 $c . . 0.5] button .top.all -text {[]} -width 3 -pady 0 \ -command [list scale $c 0] label .top.pos -textvar position -width 20 -bg white label .top.where -textvar where eval pack [winfo children .top] -side left pack .top .m -fill x pack .m -fill both -expand 1 wm title . Wait... $c create line -360 0 360 0 -fill white $c create line 0 -180 0 180 -fill white $c config -scrollregion [$c bbox all] $c create text 0 0 -text "TkWorld..." -font "Helvetica 96 italic" \ -fill blue -tag wait . config -cursor watch update set scale 2.0 set colors { green2 green3 green4 olivedrab2 olivedrab3 olivedrab4 palegreen3 palegreen4 } cd [file dirname [info script]] ;# requires all files in one dir source world.tg foreach name [geo::Names] { set id [$c show $name -fill [lpick $colors]] set bbox [$c bbox $id] if {[lindex $bbox 3]>120 || [lindex $bbox 3]<-117} { $c itemconfig $id -fill white } update } $c raise lake cities $c $c delete wait . config -cursor {} wm title . "Welcome to TkWorld 0.2!" bind $c <Motion> [list where $c %x %y] focus $c bind $c + [list scale2 $c . . 2.0] bind $c - [list scale2 $c . . 0.5] bind $c <1> [list scale2 $c %x %y 2.0] bind $c <3> [list scale2 $c %x %y 0.5] update bind . ? {console show} if [catch {source bodensee.tg}] {puts $errorInfo}
world.tg is too big to put here (110kB). mailto:[email protected] for a complimentary copy, or, if you have tkWorld 0.1, modify the end after the data to read like this:
proc cities w { foreach {city lat lon} { Beijing 40 -117 Berlin 52.5 -13.5 "Buenos Aires" -35 58 London 51.8 0.1 Madrid 40.5 3.5 Melbourne -38 -144.5 "Mexico City" 20 99 Moskva 55.9 -37.5 "New Delhi" 28 -77 "New York" 41 73.5 Paris 48.8 -2.4 Roma 41.9 -12.4 "Sao Paulo" -23 46 Tokyo 36 -138 Varna 43.5 -27.9 } { set lon2 [expr {-$lon-$lon-.5}] set lat2 [expr {-$lat-$lat-.5}] $w create oval $lon2 $lat2 [expr {$lon2+.5}] [expr {$lat2+.5}]\ -fill red -tag $city update } } foreach {id coords} $data { set t [lrange $coords 6 end] if {[llength $t]>=4} { set type [expr {[string match {[~A-Z]*} $id]? "area": "bound"}] if {[string index $id 0]=="~"} { set type lake set id [string range $id 1 end] } set cmd [list geo::Set $id $type] foreach {lat lon} $t {lappend cmd [list $lat [expr {-$lon}]]} eval $cmd } }
bodensee.tg is short enough to go here completely: # sample extension for Tkworld, displaying Lake Constance (Bodensee)
geo::Set { Bodensee lake Bo-DE Bo-CH AT-Bo Bo-DE bound AT.Bo.DE Lindau Nonnenhorn \ Langenargen Friedrichshafen \ Immenstaad Meersburg Bodman-Ludwigshafen Bodman \ KN-Dingelsdorf KN-Staad KN-Hörnle Konstanz Allensbach \ {47.75 -8.95} {47.72 -9.01} Radolfzell Moos Gaienhofen-Horn \ Gaienhofen Öhningen Bo-CH bound Eschenz Steckborn Ermatingen Kreuzlingen Romanshorn \ Rorschach AT.Bo.CH AT-Bo bound AT.Bo.CH Bregenz AT.Bo.DE AT.Bo.DE point 47.6 -9.72 AT.Bo.CH point 47.51 -9.6 Bodman point 47.8 -9.03 Bodman-Ludwigshafen point 47.81 -9.05 Bregenz city 47.55 -9.69 Eschenz point 47.64 -8.73 Friedrichshafen city 47.7 -9.48 Gaienhofen point 47.68 -8.98 Gaienhofen-Horn point 47.7 -9.0 Immenstaad city 47.7 -9.37 Konstanz city 47.7 -9.13 KN-Dingelsdorf point 47.74 -9.14 KN-Hörnle point 47.7 -9.15 Kreuzlingen city 47.69 -9.13 Langenargen point 47.65 -9.55 Lindau city 47.63 -9.7 Nonnenhorn point 47.651 -9.65 Öhningen point 47.66 -8.75 Radolfzell city 47.75 -8.92 Romanshorn city 47.6 -9.37 Rorschach city 47.49 -9.5 } foreach i { Bodensee Lindau Bregenz Radolfzell Konstanz Kreuzlingen Friedrichshafen Rorschach Romanshorn } { [geo::Canvas] show $i }