File: box-entry.tcl

package info (click to toggle)
tklib 0.9-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 23,156 kB
  • sloc: tcl: 105,088; sh: 2,573; ansic: 792; pascal: 359; makefile: 69; sed: 53; exp: 21
file content (242 lines) | stat: -rw-r--r-- 6,489 bytes parent folder | download
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