1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
|
## -*- tcl -*-
# # ## ### ##### ######## ############# ######################
## (c) 2022 Andreas Kupries
##
## Originally developed within the AKIS project (c) Andreas Kupries
# @@ Meta Begin
# Package map::box::entry 0.1
# Meta author {Andreas Kupries}
# Meta location https://core.tcl.tk/tklib
# Meta platform tcl
# Meta summary Map Action Engine - Box Entry
# Meta description Attachment to map display widgets providing custom behaviour.
# Meta description This attachment enables users to enter a bounding box.
# Meta subject {addon, box entry, map display}
# Meta subject {box entry, map display, addon}
# Meta subject {map display, addon, box entry}
# Meta require {Tcl 8.6-}
# Meta require canvas::edit::rectangle
# Meta require debug
# Meta require debug::caller
# Meta require {map::slippy 0.8}
# Meta require snit
# @@ Meta End
package provide map::box::entry 0.1
# # ## ### ##### ######## ############# ######################
## API
#
## <class> OBJ map-widget
#
## <obj> active -> VOID Is editing on ?
## <obj> box -> VOID Query current box spec
## <obj> clear -> VOID Clear box spec
## <obj> disable -> VOID Stop editing
## <obj> enable -> VOID Start editing
## <obj> fit -> VOID Center and fit current box
## <obj> set GEOBOX -> VOID Set box spec into editor
#
## -on-box-change Report changes to the box definition
#
# # ## ### ##### ######## ############# ######################
## Requirements
package require Tcl 8.6-
# ;# Tcllib
package require debug ;# - Narrative Tracing
package require debug::caller ;#
package require map::slippy 0.8 ;# - Map utilities
package require snit ;# - OO system
# ;# Tklib.
package require canvas::edit::rectangle ;# - Pixel level editor
# # ## ### ##### ######## ############# ######################
## Ensemble setup.
namespace eval map { namespace export box ; namespace ensemble create }
namespace eval map::box { namespace export entry ; namespace ensemble create }
debug level tklib/map/box/entry
debug prefix tklib/map/box/entry {<[pid]> [debug caller] | }
# # ## ### ##### ######## ############# ######################
snit::type ::map::box::entry {
# . . .. ... ..... ........ ............. .....................
## User configuration
option -on-box-change -default {}
# . . .. ... ..... ........ ............. .....................
## State
variable myeditor {} ;# Core rectangle editor
variable mymap {} ;# Map display the behaviour is attached to
variable mycanvas {} ;# Canvas internal to the map display
variable myzoom {} ;# Map zoom level
variable mycanvasdim {} ;# Canvas viewport dimensions
variable mybox {} ;# Box specification
variable myignore no ;# Internal flag to control handling of Points callback
variable myviewchain {} ;# Old view reporting callback
# . . .. ... ..... ........ ............. .....................
## Lifecycle
constructor {map args} {
debug.tklib/map/box/entry {}
$self configurelist $args
set mymap $map
set mycanvas [$map canvas]
set mybox {}
set myignore no
set myeditor \
[canvas::edit rectangle ${selfns}::RECT $mycanvas \
-radius 6 \
-add-remove-point 1 \
-drag-point 2 \
-data-cmd [mymethod BoxChanged]]
set myviewchain [$mymap cget -on-view-change]
$mymap configure -on-view-change [mymethod ViewChanged]
return
}
destructor {
debug.tklib/map/box/entry {}
return
if {![winfo exists $mycanvas]} return
$self disable
# Restore old view port reporting
$mymap configure -on-view-change $myviewchain
return
}
# . . .. ... ..... ........ ............. .....................
## API
delegate method disable to myeditor
delegate method enable to myeditor
delegate method active to myeditor
method box {} {
debug.tklib/map/box/entry {}
return $mybox
}
method clear {} {
debug.tklib/map/box/entry {}
set mybox {}
${selfns}::RECT clear
return
}
method fit {} {
debug.tklib/map/box/entry {}
$self Fit
return
}
method set {geobox} {
debug.tklib/map/box/entry {}
$self Set $geobox
$self Fit
return
}
# . . .. ... ..... ........ ............. .....................
## Internal
method Set {geobox} {
debug.tklib/map/box/entry {}
set mybox $geobox
if {![llength $mybox]} return
# Load base editor with pixel positions of the geobox, from the geo box
$self Ignore yes
set pbox [map slippy geo box 2point $myzoom $geobox]
${selfns}::RECT set {*}$pbox
$self Ignore no
return
}
method Fit {} {
debug.tklib/map/box/entry {}
if {[llength $mybox] < 2} return
set zoom [map slippy geo box fit $mybox $mycanvasdim [expr {[$mymap levels]-1}]]
set center [map slippy geo box center $mybox]
$mymap center $center $zoom
return
}
method ViewChanged {zoom viewbox geobox} {
debug.tklib/map/box/entry {}
# Note that the viewport is reported twice, as both pixel and geo coordinates.
# We are only interested in the pixel coordinates, coming first.
debug.tklib/map/box/entry {}
# Pass view change reporting to old callback, if any
if {[llength $myviewchain]} {
uplevel 1 [list {*}$myviewchain $zoom $viewbox $geobox]
}
# Update the canvas dimensions, needed for fitting.
set mycanvasdim [map slippy point box dimensions $viewbox]
# Ignore panning
if {$zoom == $myzoom} return
# For zoom changes regenerate the pixel positions from the geo locations. We are using the
# core function because here because performing a fitting here is incorrect.
set myzoom $zoom
$self Set $mybox
return
}
method BoxChanged {_ pbox} {
debug.tklib/map/box/entry {}
# Compute geo locations from pixel positions, if not suppressed (See set)
if {$myignore} return
if {![llength $pbox]} {
set mybox {}
} else {
set mybox [map slippy geo box limit [map slippy point box 2geo $myzoom $pbox]]
}
# Report changes further, if requested
if {![llength $options(-on-box-change)]} return
uplevel 1 [list {*}$options(-on-box-change) $mybox]
return
}
method Ignore {x} {
debug.tklib/map/box/entry {}
set myignore $x
return
}
# . . .. ... ..... ........ ............. .....................
}
# # ## ### ##### ######## ############# ######################
return
|