blob: 55482e4514dd83d0352df4ff5045d3ad628a6449 [file] [log] [blame]
danielk1977ebaecc12008-05-26 18:41:54 +00001# 2008 Feb 19
2#
3# The author disclaims copyright to this source code. In place of
4# a legal notice, here is a blessing:
5#
6# May you do good and not evil.
7# May you find forgiveness for yourself and forgive others.
8# May you share freely, never taking more than you give.
9#
10#***********************************************************************
11#
12# This file contains Tcl code that may be useful for testing or
13# analyzing r-tree structures created with this module. It is
14# used by both test procedures and the r-tree viewer application.
15#
16# $Id: rtree_util.tcl,v 1.1 2008/05/26 18:41:54 danielk1977 Exp $
17#
18
19
20#--------------------------------------------------------------------------
21# PUBLIC API:
22#
23# rtree_depth
24# rtree_ndim
25# rtree_node
26# rtree_mincells
27# rtree_check
28# rtree_dump
29# rtree_treedump
30#
31
32proc rtree_depth {db zTab} {
33 $db one "SELECT rtreedepth(data) FROM ${zTab}_node WHERE nodeno=1"
34}
35
36proc rtree_nodedepth {db zTab iNode} {
37 set iDepth [rtree_depth $db $zTab]
38
39 set ii $iNode
40 while {$ii != 1} {
41 set sql "SELECT parentnode FROM ${zTab}_parent WHERE nodeno = $ii"
42 set ii [db one $sql]
43 incr iDepth -1
44 }
45
46 return $iDepth
47}
48
49# Return the number of dimensions of the rtree.
50#
51proc rtree_ndim {db zTab} {
52 set nDim [expr {(([llength [$db eval "pragma table_info($zTab)"]]/6)-1)/2}]
53}
54
55# Return the contents of rtree node $iNode.
56#
57proc rtree_node {db zTab iNode {iPrec 6}} {
58 set nDim [rtree_ndim $db $zTab]
59 set sql "
60 SELECT rtreenode($nDim, data) FROM ${zTab}_node WHERE nodeno = $iNode
61 "
62 set node [db one $sql]
63
64 set nCell [llength $node]
65 set nCoord [expr $nDim*2]
66 for {set ii 0} {$ii < $nCell} {incr ii} {
67 for {set jj 1} {$jj <= $nCoord} {incr jj} {
68 set newval [format "%.${iPrec}f" [lindex $node $ii $jj]]
69 lset node $ii $jj $newval
70 }
71 }
72 set node
73}
74
75proc rtree_mincells {db zTab} {
76 set n [$db one "select length(data) FROM ${zTab}_node LIMIT 1"]
77 set nMax [expr {int(($n-4)/(8+[rtree_ndim $db $zTab]*2*4))}]
78 return [expr {int($nMax/3)}]
79}
80
81# An integrity check for the rtree $zTab accessible via database
82# connection $db.
83#
84proc rtree_check {db zTab} {
85 array unset ::checked
86
87 # Check each r-tree node.
88 set rc [catch {
89 rtree_node_check $db $zTab 1 [rtree_depth $db $zTab]
90 } msg]
91 if {$rc && $msg ne ""} { error $msg }
92
93 # Check that the _rowid and _parent tables have the right
94 # number of entries.
95 set nNode [$db one "SELECT count(*) FROM ${zTab}_node"]
96 set nRow [$db one "SELECT count(*) FROM ${zTab}"]
97 set nRowid [$db one "SELECT count(*) FROM ${zTab}_rowid"]
98 set nParent [$db one "SELECT count(*) FROM ${zTab}_parent"]
99
100 if {$nNode != ($nParent+1)} {
101 error "Wrong number of entries in ${zTab}_parent"
102 }
103 if {$nRow != $nRowid} {
104 error "Wrong number of entries in ${zTab}_rowid"
105 }
106
107 return $rc
108}
109
110proc rtree_node_check {db zTab iNode iDepth} {
111 if {[info exists ::checked($iNode)]} { error "Second ref to $iNode" }
112 set ::checked($iNode) 1
113
114 set node [rtree_node $db $zTab $iNode]
115 if {$iNode!=1 && [llength $node]==0} { error "No such node: $iNode" }
116
117 if {$iNode != 1 && [llength $node]<[rtree_mincells $db $zTab]} {
118 puts "Node $iNode: Has only [llength $node] cells"
119 error ""
120 }
121 if {$iNode == 1 && [llength $node]==1 && [rtree_depth $db $zTab]>0} {
122 set depth [rtree_depth $db $zTab]
123 puts "Node $iNode: Has only 1 child (tree depth is $depth)"
124 error ""
125 }
126
127 set nDim [expr {([llength [lindex $node 0]]-1)/2}]
128
129 if {$iDepth > 0} {
130 set d [expr $iDepth-1]
131 foreach cell $node {
132 set shouldbe [rtree_node_check $db $zTab [lindex $cell 0] $d]
133 if {$cell ne $shouldbe} {
134 puts "Node $iNode: Cell is: {$cell}, should be {$shouldbe}"
135 error ""
136 }
137 }
138 }
139
140 set mapping_table "${zTab}_parent"
141 set mapping_sql "SELECT parentnode FROM $mapping_table WHERE rowid = \$rowid"
142 if {$iDepth==0} {
143 set mapping_table "${zTab}_rowid"
144 set mapping_sql "SELECT nodeno FROM $mapping_table WHERE rowid = \$rowid"
145 }
146 foreach cell $node {
147 set rowid [lindex $cell 0]
148 set mapping [db one $mapping_sql]
149 if {$mapping != $iNode} {
150 puts "Node $iNode: $mapping_table entry for cell $rowid is $mapping"
151 error ""
152 }
153 }
154
155 set ret [list $iNode]
156 for {set ii 1} {$ii <= $nDim*2} {incr ii} {
157 set f [lindex $node 0 $ii]
158 foreach cell $node {
159 set f2 [lindex $cell $ii]
160 if {($ii%2)==1 && $f2<$f} {set f $f2}
161 if {($ii%2)==0 && $f2>$f} {set f $f2}
162 }
163 lappend ret $f
164 }
165 return $ret
166}
167
168proc rtree_dump {db zTab} {
169 set zRet ""
170 set nDim [expr {(([llength [$db eval "pragma table_info($zTab)"]]/6)-1)/2}]
171 set sql "SELECT nodeno, rtreenode($nDim, data) AS node FROM ${zTab}_node"
172 $db eval $sql {
173 append zRet [format "% -10s %s\n" $nodeno $node]
174 }
175 set zRet
176}
177
178proc rtree_nodetreedump {db zTab zIndent iDepth iNode} {
179 set ret ""
180 set node [rtree_node $db $zTab $iNode 1]
181 append ret [format "%-3d %s%s\n" $iNode $zIndent $node]
182 if {$iDepth>0} {
183 foreach cell $node {
184 set i [lindex $cell 0]
185 append ret [rtree_nodetreedump $db $zTab "$zIndent " [expr $iDepth-1] $i]
186 }
187 }
188 set ret
189}
190
191proc rtree_treedump {db zTab} {
192 set d [rtree_depth $db $zTab]
193 rtree_nodetreedump $db $zTab "" $d 1
194}
195