001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/rtree/RTree.java $ 002 // 003 //RTree implementation. 004 //Copyright (C) 2002-2004 Wolfgang Baer - WBaer@gmx.de 005 // 006 //This library is free software; you can redistribute it and/or 007 //modify it under the terms of the GNU Lesser General Public 008 //License as published by the Free Software Foundation; either 009 //version 2.1 of the License, or (at your option) any later version. 010 // 011 //This library is distributed in the hope that it will be useful, 012 //but WITHOUT ANY WARRANTY; without even the implied warranty of 013 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 //Lesser General Public License for more details. 015 // 016 //You should have received a copy of the GNU Lesser General Public 017 //License along with this library; if not, write to the Free Software 018 //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 019 020 package org.deegree.io.rtree; 021 022 import java.io.Serializable; 023 import java.util.Arrays; 024 import java.util.Enumeration; 025 import java.util.Stack; 026 import java.util.Vector; 027 028 /** 029 * <br> 030 * Implementation of a R-Tree after the algorithms of Antonio Guttman. With nearest neighbour search 031 * after algorithm of Cheung & Fu <br> 032 * 033 * @author Wolfgang Baer - WBaer@gmx.de 034 * @author last edited by: $Author: apoth $ 035 * 036 * @version $Revision: 6813 $, $Date: 2007-05-04 15:41:50 +0200 (Fr, 04 Mai 2007) $ 037 */ 038 public class RTree implements Serializable { 039 040 private PageFile file; 041 042 /** 043 * Creates an empty R-Tree with a memory-mapped pagefile (MemoryPageFile) and an empty root node 044 * 045 * @param dimension - 046 * dimension of the data to store 047 * @param maxLoad - 048 * maximum load of a node 049 * @throws RTreeException 050 */ 051 public RTree( int dimension, int maxLoad ) throws RTreeException { 052 this.file = new MemoryPageFile(); 053 054 try { 055 file.initialize( dimension, maxLoad + 1 ); 056 Node rootNode = new LeafNode( 0, this.file ); 057 file.writeNode( rootNode ); 058 } catch ( PageFileException e ) { 059 e.fillInStackTrace(); 060 throw new RTreeException( "PageFileException in constructor occured" ); 061 } 062 } 063 064 /** 065 * Creates an empty R-Tree with a persistent pagefile (PersistentPageFile) and an empty root 066 * node. 067 * 068 * @param dimension - 069 * dimension of the data to store 070 * @param maxLoad - 071 * maximum load of a node 072 * @param fileName - 073 * name of the rtree file 074 * @throws RTreeException 075 */ 076 public RTree( int dimension, int maxLoad, String fileName ) throws RTreeException { 077 try { 078 this.file = new PersistentPageFile( fileName ); 079 this.file.initialize( dimension, maxLoad + 1 ); 080 081 Node rootNode = new LeafNode( 0, this.file ); 082 file.writeNode( rootNode ); 083 } catch ( PageFileException e ) { 084 e.fillInStackTrace(); 085 throw new RTreeException( "PageFileException in constructor occured" ); 086 } 087 } 088 089 /** 090 * Creates an R-Tree from an EXISTING persistent pagefile (PersistentPageFile). 091 * 092 * @param fileName - 093 * name of the existing rtree file 094 * @throws RTreeException 095 */ 096 public RTree( String fileName ) throws RTreeException { 097 098 this.file = new PersistentPageFile( fileName ); 099 try { 100 this.file.initialize( -999, -999 ); 101 } catch ( PageFileException e ) { 102 e.fillInStackTrace(); 103 throw new RTreeException( "PageFileException in constructor occured" ); 104 } 105 } 106 107 /** 108 * Searches all entries in the R-Tree whose HyperBoundingBoxes intersect with the given. 109 * 110 * @param box - 111 * given test HyperBoundingBox 112 * @return Object[] - Array with retrieved Objects 113 * @throws RTreeException 114 */ 115 public Object[] intersects( HyperBoundingBox box ) 116 throws RTreeException { 117 if ( box.getDimension() != file.getDimension() ) 118 throw new IllegalArgumentException( "HyperBoundingBox has wrong dimension " + box.getDimension() + " != " 119 + file.getDimension() ); 120 121 Vector<Object> v = new Vector<Object>( 100 ); 122 // calls the real search method 123 try { 124 intersectsSearch( file.readNode( 0 ), v, box ); 125 } catch ( PageFileException e ) { 126 e.fillInStackTrace(); 127 throw new RTreeException( "PageFileException RTree.search() - readNode()" ); 128 } 129 130 return v.toArray(); 131 } 132 133 /** 134 * Searches all entries in the R-Tree whose HyperBoundingBoxes contain the given. 135 * 136 * @param box - 137 * given test HyperBoundingBox 138 * @return Object[] - Array with retrieved Objects 139 * @throws RTreeException 140 */ 141 public Object[] contains( HyperBoundingBox box ) 142 throws RTreeException { 143 if ( box.getDimension() != file.getDimension() ) 144 throw new IllegalArgumentException( "HyperBoundingBox has wrong dimension" ); 145 146 Vector<Object> v = new Vector<Object>( 100 ); 147 try { 148 containsSearch( file.readNode( 0 ), v, box ); 149 } catch ( PageFileException e ) { 150 e.fillInStackTrace(); 151 throw new RTreeException( "PageFileException RTree.search() - readNode() " ); 152 } 153 154 return v.toArray(); 155 } 156 157 // private method for contains search 158 private void containsSearch( Node node1, Vector<Object> v, HyperBoundingBox box ) { 159 if ( node1 instanceof LeafNode ) { 160 LeafNode node = (LeafNode) node1; 161 162 for ( int i = 0; i < node.getUsedSpace(); i++ ) { 163 // if box is contained put into Vector 164 if ( node.hyperBBs[i].contains( box ) ) 165 v.addElement( node.getData( i ) ); 166 } 167 return; 168 } 169 NoneLeafNode node = (NoneLeafNode) node1; 170 171 // node is no Leafnode - so search all entries for overlapping 172 for ( int i = 0; i < node.getUsedSpace(); i++ ) { 173 if ( node.hyperBBs[i].contains( box ) ) 174 containsSearch( (Node) node.getData( i ), v, box ); 175 } 176 } 177 178 // private method for intersects search 179 private void intersectsSearch( Node node1, Vector<Object> v, HyperBoundingBox box ) { 180 if ( node1 instanceof LeafNode ) { 181 LeafNode node = (LeafNode) node1; 182 183 for ( int i = 0; i < node.getUsedSpace(); i++ ) { 184 if ( node.hyperBBs[i].overlaps( box ) ) 185 v.addElement( node.getData( i ) ); 186 } 187 return; 188 } 189 190 NoneLeafNode node = (NoneLeafNode) node1; 191 192 for ( int i = 0; i < node.getUsedSpace(); i++ ) { 193 if ( node.hyperBBs[i].overlaps( box ) ) 194 intersectsSearch( (Node) node.getData( i ), v, box ); 195 } 196 } 197 198 /** 199 * Inserts the given Object associated with the given HyperBoundingBox object into the R-Tree. 200 * 201 * @param obj - 202 * Object to insert 203 * @param box - 204 * associated HyperBoundingBox 205 * @return boolean - true if successfull 206 * @throws RTreeException 207 */ 208 public boolean insert( Object obj, HyperBoundingBox box ) 209 throws RTreeException { 210 211 try { 212 Node[] newNodes = new Node[] { null, null }; 213 // Find position for new record 214 LeafNode node; 215 node = chooseLeaf( file.readNode( 0 ), box ); 216 217 // Add record to leaf node 218 219 if ( node.getUsedSpace() < ( file.getCapacity() - 1 ) ) { 220 node.insertData( obj, box ); 221 file.writeNode( node ); 222 } else { 223 // invoke SplitNode 224 node.insertData( obj, box ); 225 file.writeNode( node ); 226 newNodes = splitNode( node ); 227 } 228 229 if ( newNodes[0] != null ) { 230 adjustTree( newNodes[0], newNodes[1] ); 231 } else { 232 adjustTree( node, null ); 233 } 234 } catch ( PageFileException e ) { 235 e.fillInStackTrace(); 236 throw new RTreeException( "PageFileException occured" ); 237 } 238 239 return true; 240 } 241 242 // algorithm to split a full node 243 private Node[] splitNode( Node node ) 244 throws PageFileException { 245 246 // new node 247 Node newNode = null; 248 // temp help node 249 Node helpNode = null; 250 251 // compute the start entries 252 int[] seeds = pickSeeds( node ); 253 254 if ( node instanceof LeafNode ) { 255 newNode = new LeafNode( this.file ); 256 helpNode = new LeafNode( node.getPageNumber(), this.file ); 257 } else { 258 newNode = new NoneLeafNode( -1, this.file ); 259 helpNode = new NoneLeafNode( node.getPageNumber(), this.file ); 260 } 261 262 // write the new node to pagefile 263 file.writeNode( newNode ); 264 265 node.counter = 0; 266 node.unionMinBB = HyperBoundingBox.getNullHyperBoundingBox( file.getDimension() ); 267 268 // insert the start entries 269 helpNode.insertData( node.getData( seeds[0] ), node.getHyperBoundingBox( seeds[0] ) ); 270 newNode.insertData( node.getData( seeds[1] ), node.getHyperBoundingBox( seeds[1] ) ); 271 272 // mark the inserted entries - first build a marker array 273 boolean[] marker = new boolean[file.getCapacity()]; 274 for ( int i = 0; i < file.getCapacity(); i++ ) 275 marker[i] = false; 276 277 // mark them 278 marker[seeds[0]] = true; 279 marker[seeds[1]] = true; 280 281 int doneCounter = file.getCapacity() - 2; 282 283 // do until all entries are put into one of the groups or until 284 // one group has so less entries that the remainder must be given to that group 285 while ( doneCounter > 0 ) { 286 int[] entry; 287 entry = pickNext( node, marker, helpNode, newNode ); 288 doneCounter--; 289 if ( entry[0] == 1 ) 290 helpNode.insertData( node.getData( entry[1] ), node.getHyperBoundingBox( entry[1] ) ); 291 else 292 newNode.insertData( node.getData( entry[1] ), node.getHyperBoundingBox( entry[1] ) ); 293 294 if ( ( file.getMinimum() - helpNode.getUsedSpace() ) == doneCounter ) { 295 296 for ( int i = 0; i < file.getCapacity(); i++ ) 297 if ( marker[i] == false ) 298 helpNode.insertData( node.getData( i ), node.getHyperBoundingBox( i ) ); 299 break; 300 } 301 302 if ( ( file.getMinimum() - newNode.getUsedSpace() ) == doneCounter ) { 303 304 for ( int i = 0; i < file.getCapacity(); i++ ) 305 if ( marker[i] == false ) 306 newNode.insertData( node.getData( i ), node.getHyperBoundingBox( i ) ); 307 break; 308 } 309 } 310 311 // put the entries from the temp node to current node 312 for ( int x = 0; x < helpNode.getUsedSpace(); x++ ) 313 node.insertData( helpNode.getData( x ), helpNode.getHyperBoundingBox( x ) ); 314 315 file.writeNode( node ); 316 file.writeNode( newNode ); 317 318 return new Node[] { node, newNode }; 319 } 320 321 // picks the first to entries for the new nodes - returns the index of the entries 322 private int[] pickSeeds( Node node ) { 323 324 double max = 0.0; 325 int e1 = 0; 326 int e2 = 0; 327 328 // walks through all combinations and takes 329 // the combination with the largest area enlargement 330 for ( int i = 0; i < file.getCapacity(); i++ ) 331 for ( int j = 0; j < file.getCapacity(); j++ ) { 332 if ( i != j ) { 333 double d = ( node.getHyperBoundingBox( i ) ).unionBoundingBox( node.getHyperBoundingBox( j ) ).getArea() 334 - node.getHyperBoundingBox( i ).getArea() - node.getHyperBoundingBox( j ).getArea(); 335 if ( d > max ) { 336 max = d; 337 e1 = i; 338 e2 = j; 339 } 340 } 341 } 342 343 return new int[] { e1, e2 }; 344 } 345 346 // int[0] = group, int[1] = entry 347 private int[] pickNext( Node node, boolean[] marker, Node group1, Node group2 ) { 348 349 double d0 = 0; 350 double d1 = 0; 351 double diff = -1; 352 double max = -1; 353 int entry = 99; 354 int group = 99; 355 356 for ( int i = 0; i < file.getCapacity(); i++ ) { 357 if ( marker[i] == false ) { 358 d0 = group1.getUnionMinBB().unionBoundingBox( node.getHyperBoundingBox( i ) ).getArea() 359 - group1.getUnionMinBB().getArea(); 360 361 d1 = group2.getUnionMinBB().unionBoundingBox( node.getHyperBoundingBox( i ) ).getArea() 362 - group2.getUnionMinBB().getArea(); 363 diff = Math.abs( d0 - d1 ); 364 if ( diff > max ) { 365 if ( d0 < d1 ) 366 group = 1; 367 else 368 group = 2; 369 max = diff; 370 entry = i; 371 } 372 if ( diff == max ) { 373 if ( d0 < d1 ) 374 group = 1; 375 else 376 group = 2; 377 max = diff; 378 entry = i; 379 } 380 } 381 } 382 383 marker[entry] = true; 384 return new int[] { group, entry }; 385 } 386 387 // searches the leafnode with LeastEnlargment criterium for insert 388 private LeafNode chooseLeaf( Node node, HyperBoundingBox box ) { 389 390 if ( node instanceof LeafNode ) { 391 return (LeafNode) node; 392 } 393 NoneLeafNode node1 = (NoneLeafNode) node; 394 int least = node1.getLeastEnlargement( box ); 395 return chooseLeaf( (Node) node1.getData( least ), box ); 396 397 } 398 399 /** 400 * Queries the nearest neighbour to given search HyperPoint 401 * 402 * @param point - 403 * search point 404 * @return double[] - Place 0 = Distance, Place 1 = data number (must be cast to int) 405 * @throws RTreeException 406 */ 407 public double[] nearestNeighbour( HyperPoint point ) 408 throws RTreeException { 409 try { 410 return nearestNeighbour( file.readNode( 0 ), point, new double[] { Double.POSITIVE_INFINITY, -1.0 } ); 411 } catch ( PageFileException e ) { 412 e.fillInStackTrace(); 413 throw new RTreeException( "PageFileException - nearestNeighbour - readNode(0)" ); 414 } 415 } 416 417 // private method for nearest neighbour query 418 private double[] nearestNeighbour( Node node, HyperPoint point, double[] temp ) { 419 420 if ( node instanceof LeafNode ) { 421 // if mindist this < tempDist 422 for ( int i = 0; i < node.getUsedSpace(); i++ ) { 423 double dist = node.getHyperBoundingBox( i ).minDist( point ); 424 if ( dist < temp[0] ) { 425 // then this = nearest Neighbour - update tempDist 426 temp[1] = ( (LeafNode) node ).data[i]; 427 temp[0] = dist; 428 } 429 } 430 431 } else { 432 // inner class ABL 433 class ABL implements Comparable { 434 435 Node node; 436 437 double minDist; 438 439 /** 440 * @param node 441 * @param minDist 442 */ 443 public ABL( Node node, double minDist ) { 444 this.node = node; 445 this.minDist = minDist; 446 } 447 448 public int compareTo( Object obj ) { 449 ABL help = (ABL) obj; 450 if ( this.minDist < help.minDist ) 451 return -1; 452 453 if ( this.minDist > help.minDist ) 454 return 1; 455 return 0; 456 } 457 } 458 459 // generate ActiveBranchList of node 460 ABL[] abl = new ABL[node.getUsedSpace()]; 461 462 for ( int i = 0; i < node.getUsedSpace(); i++ ) { 463 Node help = (Node) node.getData( i ); 464 abl[i] = new ABL( help, help.getUnionMinBB().minDist( point ) ); 465 } 466 467 // sort activebranchlist 468 Arrays.sort( abl ); 469 470 for ( int i = 0; i < abl.length; i++ ) { 471 // apply heuristic 3 472 if ( abl[i].minDist <= temp[0] ) { 473 temp = nearestNeighbour( abl[i].node, point, temp ); 474 } 475 } 476 } 477 478 return temp; 479 } 480 481 /** 482 * Closes the rtree. 483 * 484 * @throws RTreeException - 485 * if an error occures. 486 */ 487 public void close() 488 throws RTreeException { 489 try { 490 file.close(); 491 } catch ( PageFileException e ) { 492 e.fillInStackTrace(); 493 throw new RTreeException( "PageFileException - close()" ); 494 } 495 } 496 497 /** 498 * Deletes an entry from the RTree. 499 * 500 * @param box - 501 * HyperBoundingBox of the entry to deleted 502 * @param objID - 503 * Integer value of Object-ID to be deleted 504 * @return boolean - true if successfull 505 * @throws RTreeException 506 */ 507 public boolean delete( HyperBoundingBox box, int objID ) 508 throws RTreeException { 509 510 Vector<Object> v = new Vector<Object>( 100 ); 511 try { 512 findLeaf( file.readNode( 0 ), box, objID, v ); 513 } catch ( PageFileException e ) { 514 e.fillInStackTrace(); 515 throw new RTreeException( "PageFileException - delete()" ); 516 } 517 518 if ( v.size() < 1 ) 519 return false; 520 521 if ( v.size() == 1 ) { 522 523 LeafNode leaf = (LeafNode) v.elementAt( 0 ); 524 525 for ( int i = 0; i < leaf.getUsedSpace(); i++ ) { 526 if ( leaf.getHyperBoundingBox( i ).equals( box ) && leaf.data[i] == objID ) { 527 leaf.deleteData( i ); 528 529 try { 530 file.writeNode( leaf ); 531 } catch ( PageFileException e ) { 532 e.fillInStackTrace(); 533 throw new RTreeException( "PageFileException - delete()" ); 534 } 535 } 536 } 537 538 Stack<Object> stack = new Stack<Object>(); 539 try { 540 condenseTree( leaf, stack ); 541 } catch ( PageFileException e ) { 542 e.fillInStackTrace(); 543 throw new RTreeException( "PageFileException - condenseTree()" ); 544 } 545 546 while ( !stack.empty() ) { 547 548 Node node = (Node) stack.pop(); 549 550 if ( node instanceof LeafNode ) { 551 for ( int i = 0; i < node.getUsedSpace(); i++ ) 552 this.insert( ( (LeafNode) node ).getData( i ), ( (LeafNode) node ).getHyperBoundingBox( i ) ); 553 } else { 554 for ( int i = 0; i < node.getUsedSpace(); i++ ) 555 stack.push( ( (NoneLeafNode) node ).getData( i ) ); 556 } 557 558 try { 559 file.deleteNode( node.pageNumber ); 560 } catch ( PageFileException e ) { 561 e.fillInStackTrace(); 562 throw new RTreeException( "PageFileException - delete() - deleteNode(0)" ); 563 } 564 } 565 } 566 567 return true; 568 } 569 570 /** 571 * Deletes all entries from the R-Tree with given HyperBoundingBox 572 * 573 * @param box - 574 * HyperBoundingBox 575 * @return boolean - true if successfull 576 * @throws RTreeException 577 */ 578 public boolean delete( HyperBoundingBox box ) 579 throws RTreeException { 580 581 Vector<Object> v = new Vector<Object>( 100 ); 582 try { 583 findLeaf( file.readNode( 0 ), box, v ); 584 } catch ( PageFileException e ) { 585 e.fillInStackTrace(); 586 throw new RTreeException( "PageFileException - delete()" ); 587 } 588 589 if ( v.size() < 1 ) 590 return false; 591 592 LeafNode leaf; 593 594 for ( Enumeration en = v.elements(); en.hasMoreElements(); ) { 595 596 leaf = (LeafNode) en.nextElement(); 597 598 for ( int i = 0; i < leaf.getUsedSpace(); i++ ) { 599 if ( leaf.getHyperBoundingBox( i ).equals( box ) ) { 600 leaf.deleteData( i ); 601 602 try { 603 file.writeNode( leaf ); 604 } catch ( PageFileException e ) { 605 e.fillInStackTrace(); 606 throw new RTreeException( "PageFileException - delete()" ); 607 } 608 } 609 } 610 611 Stack<Object> stack = new Stack<Object>(); 612 try { 613 condenseTree( leaf, stack ); 614 } catch ( PageFileException e ) { 615 e.fillInStackTrace(); 616 throw new RTreeException( "PageFileException - condenseTree()" ); 617 } 618 619 while ( !stack.empty() ) { 620 621 Node node = (Node) stack.pop(); 622 623 if ( node instanceof LeafNode ) { 624 for ( int i = 0; i < node.getUsedSpace(); i++ ) 625 this.insert( ( (LeafNode) node ).getData( i ), ( (LeafNode) node ).getHyperBoundingBox( i ) ); 626 } else { 627 for ( int i = 0; i < node.getUsedSpace(); i++ ) 628 stack.push( ( (NoneLeafNode) node ).getData( i ) ); 629 } 630 631 try { 632 file.deleteNode( node.pageNumber ); 633 } catch ( PageFileException e ) { 634 e.fillInStackTrace(); 635 throw new RTreeException( "PageFileException - delete() - deleteNode(0)" ); 636 } 637 } 638 } 639 640 return true; 641 } 642 643 /** 644 * Retrieves all entries with the given HyperBoundingBox. 645 * 646 * @param box - 647 * HyperBoundingBox 648 * @return Object[] - array with retrieved objects 649 * @throws RTreeException 650 */ 651 public Object[] find( HyperBoundingBox box ) 652 throws RTreeException { 653 if ( box.getDimension() != file.getDimension() ) 654 throw new IllegalArgumentException( "HyperBoundingBox has wrong dimension" ); 655 656 Vector<Object> v = new Vector<Object>( 100 ); 657 // ruft die eigentliche suche auf 658 try { 659 findSearch( file.readNode( 0 ), v, box ); 660 } catch ( PageFileException e ) { 661 e.fillInStackTrace(); 662 throw new RTreeException( "PageFileException RTree.search() - readNode()" ); 663 } 664 665 return v.toArray(); 666 } 667 668 // F�hrt die eigentliche Suche durch - Aufruf von search(HyperBoundingBox box) 669 private void findSearch( Node node1, Vector<Object> v, HyperBoundingBox box ) { 670 if ( node1 instanceof LeafNode ) { 671 LeafNode node = (LeafNode) node1; 672 673 for ( int i = 0; i < node.getUsedSpace(); i++ ) { 674 // wenn eintraege enthalten diese in Vechtor aufnehmen; 675 if ( node.hyperBBs[i].equals( box ) ) 676 v.addElement( node.getData( i ) ); 677 } 678 return; 679 } 680 681 NoneLeafNode node = (NoneLeafNode) node1; 682 683 // node ist kein LeafNode 684 // alle eintrraege auf �berlappung durchsuchen 685 for ( int i = 0; i < node.getUsedSpace(); i++ ) { 686 // wenn enthalten rekursiv search mit diesem node aufrufen 687 if ( node.hyperBBs[i].contains( box ) ) { 688 findSearch( (Node) node.getData( i ), v, box ); 689 } 690 } 691 692 } 693 694 // Retrieves all leaf nodes regardless of the id 695 private void findLeaf( Node node, HyperBoundingBox box, Vector<Object> v ) { 696 if ( node instanceof LeafNode ) { 697 for ( int i = 0; i < node.getUsedSpace(); i++ ) { 698 if ( node.getHyperBoundingBox( i ).equals( box ) ) 699 v.addElement( node ); 700 } 701 } else { 702 for ( int i = 0; i < node.getUsedSpace(); i++ ) { 703 if ( node.getHyperBoundingBox( i ).overlaps( box ) ) 704 findLeaf( (Node) node.getData( i ), box, v ); 705 } 706 } 707 } 708 709 // Retrieves all leaf nodes with correct box and id 710 private void findLeaf( Node node, HyperBoundingBox box, int objID, Vector<Object> v ) { 711 if ( node instanceof LeafNode ) { 712 for ( int i = 0; i < node.getUsedSpace(); i++ ) { 713 if ( ( (LeafNode) node ).data[i] == objID && node.getHyperBoundingBox( i ).equals( box ) ) 714 v.addElement( node ); 715 } 716 } else { 717 for ( int i = 0; i < node.getUsedSpace(); i++ ) { 718 if ( node.getHyperBoundingBox( i ).overlaps( box ) ) 719 findLeaf( (Node) node.getData( i ), box, objID, v ); 720 } 721 } 722 } 723 724 // condenses the tree after remove of some entries 725 private void condenseTree( Node n, Stack<Object> stack ) 726 throws PageFileException { 727 if ( !n.isRoot() ) { 728 729 Node p = n.getParent(); 730 if ( n.getUsedSpace() < file.getMinimum() ) { 731 p.deleteData( n.place ); 732 stack.push( n ); 733 } else { 734 p.hyperBBs[n.place] = n.getUnionMinBB(); 735 p.updateNodeBoundingBox(); 736 } 737 738 file.writeNode( p ); 739 740 condenseTree( p, stack ); 741 } else { 742 if ( n.getUsedSpace() == 1 && ( n instanceof NoneLeafNode ) ) { 743 744 Node kind = (Node) n.getData( 0 ); 745 Node newRoot = null; 746 if ( kind instanceof LeafNode ) { 747 newRoot = new LeafNode( 0, this.file ); 748 for ( int i = 0; i < kind.getUsedSpace(); i++ ) 749 newRoot.insertData( kind.getData( i ), kind.getHyperBoundingBox( i ) ); 750 } else { 751 newRoot = new NoneLeafNode( 0, this.file ); 752 for ( int i = 0; i < kind.getUsedSpace(); i++ ) 753 newRoot.insertData( kind.getData( i ), kind.getHyperBoundingBox( i ) ); 754 } 755 756 file.writeNode( newRoot ); 757 } 758 } 759 } 760 761 // adjustes the Tree with the correct bounding boxes and 762 // propagates needed splits upwards 763 private void adjustTree( Node n1, Node n2 ) 764 throws PageFileException { 765 // if n2 = null - only adjust boundingboxes 766 // if n2 != null a split occured - maybe propagate split 767 768 if ( n1.isRoot() ) { 769 770 // if n2 != null we need a new Root node - Root Split 771 if ( ( n2 != null ) && n1.isRoot() ) { 772 773 // Node must be written from page number 0 (root number) to other 774 n1.setPageNumber( -1 ); 775 int pagenumber; 776 777 pagenumber = file.writeNode( n1 ); 778 779 for ( int x = 0; x < n1.getUsedSpace(); x++ ) { 780 Object obj = n1.getData( x ); 781 782 if ( obj instanceof Node ) { 783 Node node = (Node) obj; 784 node.parentNode = pagenumber; 785 file.writeNode( node ); 786 } 787 788 obj = null; 789 } 790 791 NoneLeafNode newRoot = new NoneLeafNode( 0, this.file ); 792 793 newRoot.insertData( n1, n1.getUnionMinBB() ); 794 newRoot.insertData( n2, n2.getUnionMinBB() ); 795 newRoot.parentNode = 0; 796 797 file.writeNode( newRoot ); 798 } 799 800 return; 801 } 802 803 // adjust the bounding boxes in the parents for Node n1 804 NoneLeafNode p = (NoneLeafNode) n1.getParent(); 805 p.hyperBBs[n1.place] = n1.getUnionMinBB(); 806 p.unionMinBB = ( p.getUnionMinBB() ).unionBoundingBox( n1.getUnionMinBB() ); 807 808 file.writeNode( p ); 809 810 // propagate adjustment upwards 811 if ( n2 == null ) { 812 adjustTree( p, null ); 813 } else { 814 // as there occured a split - the second node has to be inserted 815 Node[] newNodes = new Node[] { null, null }; 816 if ( p.getUsedSpace() < ( file.getCapacity() - 1 ) ) { 817 // new split must happen 818 p.insertData( n2, n2.getUnionMinBB() ); 819 file.writeNode( p ); 820 newNodes[0] = p; 821 } else { 822 p.insertData( n2, n2.getUnionMinBB() ); 823 file.writeNode( p ); 824 newNodes = splitNode( p ); 825 } 826 827 adjustTree( newNodes[0], newNodes[1] ); 828 } 829 } 830 }