mirror of https://github.com/CGAL/cgal
561 lines
19 KiB
TeX
561 lines
19 KiB
TeX
% +------------------------------------------------------------------------+
|
|
% | CGAL User Manual: main.tex
|
|
% +------------------------------------------------------------------------+
|
|
% | R-tree User Manual
|
|
% |
|
|
% | 31.03.2000 Gabriele Neyer
|
|
% | Start writing the user manual
|
|
% |
|
|
\RCSdef{\hdsRev}{$Id$}
|
|
\RCSdefDate{\hdsDate}{$Date$}
|
|
% +------------------------------------------------------------------------+
|
|
% 257 11:49 latex wrapper
|
|
% 258 11:50 makeindex wrapper
|
|
% 259 11:50 index_fix wrapper.ind
|
|
% 260 11:50 ls /pub/blabla/CGAL/Tools/3.5/scripts/index_fix wrapper.ind
|
|
% 261 11:50 latex wrapper
|
|
% 262 11:50 xdvi wrapper.dvi
|
|
% setenv TEXINPUTS .:/home/neyer/tex:/pub/blabla/tex:
|
|
% setenv LATEX_CONV_CONFIG /pub/blabla/CGAL/Tools/latex_converter_config
|
|
% setenv LATEX_CONV_HEADER /pub/blabla/CGAL/CGAL/include
|
|
% setenv LATEX_CONV_BIN /pub/blabla/sun4OS5/bin
|
|
|
|
|
|
\chapter{External Memory Data Structures}
|
|
\label{chapterExternalMemory}
|
|
|
|
\section{R-Tree and R$^\star $-Tree}
|
|
\subsection{What we provide}
|
|
CGAL provides the R-Tree and the R$^\star$-Tree data structures
|
|
as dynamic index structure for spatial
|
|
searching. The design goal was to create an algorithm component
|
|
that is easily, safely, and efficiently adaptable to all common
|
|
R-trees and R$^\star$-Trees to be used in
|
|
new unforseen contexts.
|
|
|
|
We designed the tree to be independent of the spatial objects the
|
|
tree manages, the underlying database, and to some extend
|
|
independent of the structure of the index structure. We provide
|
|
some databases and the most common components that define the
|
|
index structure of an R-tree. Note, often we simply write R-Tree
|
|
instead of R-Tree and R$^\star$-Tree.
|
|
|
|
\subsection{What is an R-tree?}
|
|
The R-Tree \cite{g-rtdis-84} is a hierarchical data structure that
|
|
can be viewed as a multidimensional generalization of a
|
|
B$^+$-Tree \cite{come-79}.
|
|
It supports exact search, window queries, insertion, and deletion
|
|
of spatial objects in any dimension $d$. Usually, the smallest
|
|
$d$-dimensional aligned rectangle that encloses the spatial object (the {\em orthogonal bounding box}) is
|
|
associated with the object. The index of the tree is build
|
|
according to these orthogonal bounding boxes, which are therefore
|
|
called \ccc{ Key}s.
|
|
Each node in the tree corresponds to the smallest $d$-dimensional
|
|
aligned rectangle that encloses its child nodes. The leaf nodes store the
|
|
actual geometric objects. The data structure of a \ccc{Key} is
|
|
not restricted to have the form of a $d$-dimensional bounding
|
|
box. One can also define another data strucure as the \ccc{Key}
|
|
data structure, e.g. the smallest enclosing circle.
|
|
%Each
|
|
%$d$-dimensional aligned rectangle may be a container (bounding
|
|
%box) for a
|
|
%{\em geometric object} with a more complex shape.
|
|
|
|
|
|
R-Trees are balanced in the sense that all leaf nodes appear on
|
|
the same level.
|
|
Often, each node of the tree represents a disk block and therefore has a fixed maximum
|
|
storage capacity. Therefore, the R-Tree has variable parameters
|
|
that define the minimum and maximum size of a node or of a leaf
|
|
of the tree. \ccc{IO_max_cap_nodes} is the
|
|
maximum number of \ccc{Keys} a node can hold; \ccc{IO_min_cap_nodes}$\le\frac{\ccc{IO_max_cap_nodes}}{2}$ is the minimum number of
|
|
\ccc{Keys} a node should hold.
|
|
Just as in $B$-trees, $R$-trees guarantee for each
|
|
node except the root to have between \ccc{IO_min_cap_nodes} and \ccc{IO_max_cap_nodes}
|
|
entries; the root has between $2$ and \ccc{IO_max_cap_nodes} entries.
|
|
Since the data elements are allowed to have different sizes, we
|
|
can not define the maximum size of a leaf in terms of the number
|
|
of \ccc{Data} elements. We therefore have a parameter
|
|
\ccc{IO_page_size} which defines the maximum size of a
|
|
leaf. \ccc{IO_min_cap_leaves} defines the minmum number of
|
|
\ccc{Data} elements in a leaf.
|
|
We say that
|
|
underflow occurs if a node uses less then \ccc{IO_min_cap_nodes}
|
|
(\ccc{IO_min_cap_leaves}, resp.) entries.
|
|
Overflow occurs when we try to insert the $\ccc{IO_max_cap_nodes}+1^{st}$
|
|
entry into a node or a Data element in a leaf which exceeds the
|
|
leaf capacity \ccc{IO_page_size}.
|
|
%If an underflow occurs, nodes are merged or reinserted;
|
|
%if overflow occurs, the node has to be
|
|
%split. Many merging and splitting strategies have been
|
|
%proposed. Some important ones are those from
|
|
%Guttman~\cite{g-rtdis-84} and from Beckmann et al.~\cite{bkss-rtera-90}.
|
|
|
|
Note that the R-tree is not unique. Its structure depends heavily
|
|
on the order in which the individual bounding boxes were inserted
|
|
into (and possibly deleted from) the tree.
|
|
|
|
A new object is inserted into a leaf node. The appropriate leaf
|
|
node is determined by traversing the R-tree starting at its root
|
|
and at each step {\bf choosing the subtree} whose corresponding
|
|
\ccc{Key} causes the minimum {\em costs} when enlarging it with
|
|
the \ccc{Key} of the new object. Various objective functions have
|
|
been proposed for the calculation of the minimum cost. The most
|
|
common functions are: the minimum area enlargement, the minimum margin
|
|
enlargement and the minimum overlap enlargement between the \ccc{Key}s of the
|
|
subtrees.
|
|
Once the leaf node is determined, a check is made to see if the
|
|
insertion of the \ccc{Key} will cause the node to overflow. If
|
|
yes, then the node must be split, and the node entries are
|
|
distributed into two nodes. Splits are propargated
|
|
up the tree.
|
|
|
|
Many {\bf split strategies} have been proposed. Some important ones are those from
|
|
Guttman~\cite{g-rtdis-84} and from Beckmann et
|
|
al.~\cite{bkss-rtera-90}.
|
|
The goals of these strategies are to
|
|
partition the data entries in two
|
|
nodes such that the overlap of these nodes is minimized, the
|
|
area is minimized, the margin is minimized or that a combination of
|
|
these criteria is optimized.
|
|
|
|
{\bf Deletion} of an object from an R-tree proceeds by locating the
|
|
leaf node that contains the object and removing the object from the node. Then, the
|
|
covering \ccc{Key}s on the path from the root to the leaf node
|
|
are adjusted. Underflow occurs if a
|
|
node uses less than $\ccc{IO_min_cap_nodes}$
|
|
(\ccc{IO_min_cap_leaves}, resp.) entries. In this case the
|
|
underfilled node is either removed from the tree and the children
|
|
are reinserted or the elements of the underfilled node are merged
|
|
(in respect to the cost function)
|
|
into its siblings which are the other nodes in the same level of the
|
|
tree as the underfilled node is.
|
|
|
|
{\bf Searching} all objects that have the same \ccc{Key} as the
|
|
query \ccc{Key}, have
|
|
non empty intersection with the query \ccc{Key}, or enclose the
|
|
query \ccc{Key}, is straight forward. The only problem is that
|
|
a large number of nodes may have to be returned since a \ccc{Key}
|
|
may be contained in the covering \ccc{Key} of many nodes while
|
|
its corresponding object is contained only in one of the leaf
|
|
nodes.
|
|
Furthermore, the output of queries has to be performed
|
|
output sensitive. That is, the objects
|
|
are returned one by one in order not to exceed the
|
|
main memory capacity.
|
|
|
|
\subsection{Data Structure Design}
|
|
|
|
\begin{ccTexOnly}
|
|
\begin{figure}
|
|
%\psfrag{A}{\footnotesize\ccc{Data}}
|
|
%\psfrag{B}{\footnotesize\ccc{Key}}
|
|
%\psfrag{C}{\footnotesize\ccc{R_tree_traits}}
|
|
%\psfrag{D}{\footnotesize\ccc{R_tree}}
|
|
%\psfrag{E}{\footnotesize\ccc{IO_tree_traits}}
|
|
%\psfrag{F}{\footnotesize\ccc{IO_tree_traits}}
|
|
%\psfrag{G}{\footnotesize\hspace*{-.1cm}\parbox{1.9cm}{Database of tree nodes}}
|
|
%\psfrag{H}{\footnotesize\hspace*{-.1cm}\parbox{1.9cm}{Database of leaf nodes}}
|
|
%\psfrag{I}{\footnotesize\ccc{choose_subtree}}
|
|
%\psfrag{J}{\footnotesize\ccc{split_node}}
|
|
%\psfrag{K}{\footnotesize\ccc{split_leaf}}
|
|
%\psfrag{L}{\footnotesize\ccc{reinsertion_node}}
|
|
%\psfrag{M}{\footnotesize\ccc{reinsertion_leaf}}
|
|
%\psfrag{O}{\footnotesize\ccc{R_tree_index}}
|
|
%\psfrag{P}{\footnotesize\ccc{R_tree_storage}}
|
|
\begin{center}
|
|
\includegraphics[width=14cm,clip]{ExternalMemoryStructures/rtree-classes4.eps}
|
|
\end{center}
|
|
\caption{\label{User:r-tree-design} R-Tree components}
|
|
\end{figure}
|
|
\end{ccTexOnly}
|
|
\begin{ccHtmlOnly}
|
|
<!2><TABLE BORDER=0 CELLSPACING=2 CELLPADDING=0 WIDTH=650>
|
|
<TR><TD ALIGN=LEFT VALIGN=TOP WIDTH=100% NOWRAP COLSPAN=2>
|
|
<img border=0 src="./rtree-classes4.gif" alt="R-Tree components">
|
|
</TD>
|
|
<TD ALIGN=LEFT VALIGN=TOP WIDTH=50%><img border=0 src="./rtree-classes4.gif"
|
|
alt="R-Tree components">
|
|
</TD></TR></TABLE>
|
|
\end{ccHtmlOnly}
|
|
|
|
\begin{ccTexOnly}
|
|
Figure~\ref{User:r-tree-design} shows the different components of the
|
|
R-Tree (R$^\star $-Tree) that can be plugged into the tree.
|
|
\end{ccTexOnly}
|
|
\begin{ccHtmlOnly}
|
|
The figure above shows the different components of the
|
|
R-Tree (R*-Tree) that can be plugged into the tree.
|
|
\end{ccHtmlOnly}
|
|
The R-Tree has three template arguments:
|
|
\begin{description}
|
|
\item[\ccc{R-tree_traits}] is the traits class which defines the
|
|
access on the \ccc{Data} and their \ccc{Key}s. E.g. as
|
|
\ccc{Data} you could define a 2-dimensional polygon and as
|
|
\ccc{Key} you could define a bounding box of a
|
|
2-dimensional polygon.
|
|
\item[\ccc{R_tree_index}] defines the indexing strategies of the
|
|
tree. E.g. a strategy that defines in which subtree a new item
|
|
is inserted, or how to
|
|
split an overfull node. \cgal\/ provides predefined index
|
|
functions for the most commonly used strategies.
|
|
\item[\ccc{R_tree_storage}] defines the databases for the inner
|
|
nodes of the tree and the leave nodes in which the \ccc{Data}
|
|
items are stored. The functionality of the databases is
|
|
accessed through the traits class \ccc{IO_tree_traits}. \cgal\/
|
|
provides a database in external memory whith variable cash size
|
|
and a database in internal memory.
|
|
\end{description}
|
|
Instead of using the predefined classes you can always
|
|
design your own classes. For this, please refer the reference manual.
|
|
|
|
|
|
\section{Example program}
|
|
\label{User:r-star-tree-example}
|
|
The following piece of code defines an R$^\star$-Tree.
|
|
The \ccc{Data} type simply defines an \ccc{R_tree_key_2} type as
|
|
\ccc{Key} data type. The \ccc{R_tree_traits} class implementation
|
|
is instanciated with this \ccc{Data} type.
|
|
As \ccc{R_tree_index} class we chose the predefined
|
|
\ccc{R_star_tree_index} class. As \ccc{R_tree_storage} class we
|
|
chose the \ccc{R_tree_external_storage} class.
|
|
|
|
An R$^\star$-Tree is created and 16 2-dimensional squares are
|
|
inserted. Then, it is iterated through all elements of the tree
|
|
and various queries are performed. Furthermore, some elements are
|
|
deleted.
|
|
|
|
|
|
|
|
\ccExample
|
|
\begin{cprog}
|
|
#include <CGAL/R_tree.h>
|
|
#include <CGAL/R_tree_key.h>
|
|
#include <CGAL/R_star_tree_index.h>
|
|
#include <CGAL/R_tree_traits_example.h>
|
|
#include <CGAL/R_tree_external_storage.h>
|
|
#define NUMBER 16
|
|
|
|
/* Definition of the data type */
|
|
struct Data{
|
|
public:
|
|
typedef CGAL::R_tree_key_2 Key;
|
|
Key key;
|
|
size_t size(void) const {
|
|
return sizeof(*this);
|
|
}
|
|
void read(char ** s) {
|
|
key.read(s);
|
|
}
|
|
void write(char * s) {
|
|
key.write(s);
|
|
}
|
|
void dump(int level =0){
|
|
key.dump();
|
|
}
|
|
};
|
|
|
|
/* definition of the R_tree_traits - depending on the Data type */
|
|
typedef CGAL::R_tree_traits<Data> TTraits;
|
|
typedef Data::Key Key;
|
|
|
|
/* definition of the R_Tree that contains Data elements, uses
|
|
Star Tree index structure and stores the elements in
|
|
external memory */
|
|
typedef CGAL::R_tree<TTraits, CGAL::R_star_tree_index<TTraits>,
|
|
CGAL::R_tree_external_storage> R_Tree_Inst;
|
|
|
|
int main() {
|
|
TTraits traits;
|
|
Data elem;
|
|
int k;
|
|
Key key= Key(0,2,0,2);
|
|
std::vector<Data > source;
|
|
/* creation of R_tree associated to the files: */
|
|
R_Tree_Inst r_star_tree("__star_tree.head","__star_tree.dat",
|
|
"__star_leaf_data.dat");
|
|
|
|
/* create 2-dimensional squares as input data */
|
|
for (k=0;k<NUMBER;++k) {
|
|
elem.key=key;
|
|
source.push_back(elem);
|
|
key.xmin++; key.ymin++; key.xmax++; key.ymax++;
|
|
}
|
|
|
|
/* Insertion of the data elements */
|
|
for (k=0;k<NUMBER;++k) {
|
|
r_star_tree.insert(source[k]);
|
|
}
|
|
r_star_tree.dump();
|
|
|
|
std::cerr<< "Iteration through all elements of the tree\n";
|
|
R_Tree_Inst::iterator it_begin=r_star_tree.begin();
|
|
R_Tree_Inst::iterator it_end=r_star_tree.end();
|
|
while(it_begin != it_end){
|
|
std::cerr << "------------";
|
|
(*it_begin).dump();
|
|
++it_begin;
|
|
}
|
|
std::cerr<< "End of iteration through all elements of the tree\n";
|
|
|
|
std::cerr<< "Iteration through all elements of the tree\n";
|
|
std::cerr<< "that have non empty intersection with source[0].key=(0,2,0,2)\n";
|
|
it_begin=r_star_tree.begin(source[0].key);
|
|
it_end=r_star_tree.end(source[0].key);
|
|
while(it_begin != it_end){
|
|
std::cerr << std::endl;
|
|
(*it_begin).dump();
|
|
++it_begin;
|
|
}
|
|
std::cerr<< "End of iteration through the query elements of the tree\n";
|
|
|
|
std::cerr<< "Iteration through all elements of the tree\n";
|
|
std::cerr<< "that enclose source[2].key=(2,4,2,4)\n";
|
|
it_begin=r_star_tree.begin_enclose(source[2].key);
|
|
it_end=r_star_tree.end_enclose(source[2].key);
|
|
while(it_begin != it_end){
|
|
std::cerr << std::endl;
|
|
(*it_begin).dump();
|
|
++it_begin;
|
|
}
|
|
std::cerr<< "End of iteration through the query elements of the tree\n";
|
|
|
|
std::cerr<< "Iteration through all elements of the tree\n";
|
|
std::cerr<< "that compare source[4].key=(4,8,4,8)\n";
|
|
it_begin=r_star_tree.begin_compare(source[4].key);
|
|
it_end=r_star_tree.end_compare(source[4].key);
|
|
while(it_begin != it_end){
|
|
std::cerr << std::endl;
|
|
(*it_begin).dump();
|
|
++it_begin;
|
|
}
|
|
std::cerr<< "End of iteration through the query elements of the tree\n";
|
|
|
|
std::cerr << "Check for elements that intersects source[1].key=(1,2,1,2)\n";
|
|
if(!r_star_tree.find_key_intersect(source[1].key))
|
|
{
|
|
std::cerr << "no key intersection of ";
|
|
traits.dump(traits.build(source[1]));
|
|
}
|
|
else
|
|
std::cerr << "key intersection = true";
|
|
|
|
std::cerr << "Check for elements that intersects source[1].key=(1,2,1,2)\n";
|
|
if(!r_star_tree.find_key_include(source[1].key))
|
|
{
|
|
std::cerr << "no key include of";
|
|
traits.dump(traits.build(source[1]));
|
|
}
|
|
else
|
|
std::cerr << "key include = true";
|
|
|
|
Data data_del;
|
|
std::cerr << "Deletion of all data with key source[0].key=(0,2,0,2)\n";
|
|
while(r_star_tree.delete_key(source[0].key,data_del))
|
|
traits.dump(data_del.key);
|
|
std::cerr << "Deletion of all data with key source[1].key=(1,3,1,3)\n";
|
|
while(r_star_tree.delete_key(source[1].key,data_del))
|
|
traits.dump(data_del.key);
|
|
std::cerr << "Deletion of all data with key source[2].key=(2,4,2,4)\n";
|
|
while(r_star_tree.delete_key(source[2].key,data_del))
|
|
traits.dump(data_del.key);
|
|
|
|
std::cerr << "Check for elements that intersect source[1].key=(1,2,1,2)\n";
|
|
if(!r_star_tree.find_key_intersect(source[1].key))
|
|
{
|
|
std::cerr << "no key intersect of";
|
|
traits.dump(traits.build(source[1]));
|
|
}
|
|
else
|
|
std::cerr << "key intersect = true";
|
|
}
|
|
\end{cprog}
|
|
|
|
Class \ccc{R_tree_key_2} looks like this:
|
|
|
|
\begin{cprog}
|
|
|
|
/* R_tree_key_2 is a 2 dimensional rectangle. */
|
|
class R_tree_key_2
|
|
{
|
|
int min(int a, int b) const { return (a<b) ? a : b; }
|
|
int max(int a, int b) const { return (a<b) ? b : a; }
|
|
public:
|
|
int xmin, xmax, ymin, ymax;
|
|
size_t size(void) const {
|
|
return 4*sizeof(int)+4;
|
|
}
|
|
|
|
R_tree_key_2(){xmin=xmax=ymin=ymax=0;}
|
|
R_tree_key_2(int x1, int x2, int y1, int y2){
|
|
xmin=x1; xmax=x2; ymin=y1; ymax=y2;
|
|
}
|
|
|
|
R_tree_key_2(const R_tree_key_2 &t){
|
|
xmin=t.xmin; xmax=t.xmax; ymin=t.ymin; ymax=t.ymax;
|
|
}
|
|
|
|
bool operator==(const R_tree_key_2 &p) const {
|
|
return ((xmin==p.xmin) && (xmax == p.xmax)
|
|
&& (ymin == p.ymin) &&(ymax == p.ymax));
|
|
}
|
|
|
|
R_tree_key_2 & operator=(const R_tree_key_2 &t) {
|
|
xmin=t.xmin; xmax=t.xmax; ymin=t.ymin; ymax=t.ymax;
|
|
return *this;
|
|
}
|
|
|
|
class C_Compare_1{
|
|
public:
|
|
bool operator()(const R_tree_key_2 &p, const R_tree_key_2 &q)
|
|
{
|
|
if(p.xmin < q.xmin)
|
|
return true;
|
|
else
|
|
if(p.xmin==q.xmin)
|
|
if(p.xmax < q.xmax)
|
|
return true;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
class C_Compare_2{
|
|
public:
|
|
bool operator()(const R_tree_key_2 &p, const R_tree_key_2 &q)
|
|
{
|
|
if(p.ymin < q.ymin)
|
|
return true;
|
|
else
|
|
if(p.ymin==q.ymin)
|
|
if(p.ymax < q.ymax)
|
|
return true;
|
|
return false;
|
|
}
|
|
};
|
|
typedef C_Compare_1 compare_1;
|
|
typedef C_Compare_2 compare_2;
|
|
|
|
void unify( const R_tree_key_2 & p, const R_tree_key_2 & q){
|
|
xmin = min(p.xmin, q.xmin); xmax = max(p.xmax, q.xmax);
|
|
ymin = min(p.ymin, q.ymin); ymax = max(p.ymax, q.ymax);
|
|
}
|
|
|
|
/* returns true if *this includes y */
|
|
bool include(const R_tree_key_2& y) const {
|
|
if ((xmin > y.xmin) || (ymin > y.ymin))
|
|
return false;
|
|
if ((xmax < y.xmax) || (ymax < y.ymax))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool compare(const R_tree_key_2 & y) const{
|
|
return intersect(y);
|
|
}
|
|
|
|
bool intersect( const R_tree_key_2& y) const {
|
|
if ((xmax <= y.xmin) || (y.xmax <= xmin)) {
|
|
return false;
|
|
}
|
|
if ((ymax <= y.ymin) || (y.ymax <= ymin)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
double cost() const {
|
|
return (xmax - xmin) * (ymax - ymin);
|
|
}
|
|
|
|
void intersection( const R_tree_key_2 & p, const R_tree_key_2 & q){
|
|
if(p.intersect(q))
|
|
{
|
|
xmin = max(p.xmin, q.xmin); xmax = min(p.xmax, q.xmax);
|
|
ymin = max(p.ymin, q.ymin); ymax = min(p.ymax, q.ymax);
|
|
}
|
|
}
|
|
|
|
/* compute the distance between the centers of the keys */
|
|
double center_dist( const R_tree_key_2& q) const {
|
|
double x=xmin + 0.5*(xmax-xmin);
|
|
double y=ymin + 0.5*(ymax-ymin);
|
|
double qx=q.xmin + 0.5*(q.xmax-q.xmin);
|
|
double qy=q.ymin + 0.5*(q.ymax-q.ymin);
|
|
double dist = (x-qx)*(x-qx) + (y-qy)*(y-qy);
|
|
return dist;
|
|
}
|
|
|
|
void read(char **s) {
|
|
int sint=(int)sizeof(int);
|
|
char *from_int=new char[sint];
|
|
|
|
int i,r=0;
|
|
for (i=0; i<sint; i++)
|
|
from_int[i] = (*s)[i];
|
|
r += sint;
|
|
xmin=*((int *)from_int);
|
|
|
|
for (i=0; i<sint; i++)
|
|
from_int[i] = (*s)[i+r];
|
|
r += sint;
|
|
xmax=*((int *)from_int);
|
|
|
|
for (i=0; i<sint; i++)
|
|
from_int[i] = (*s)[i+r];
|
|
r += sint;
|
|
ymin=*((int *)from_int);
|
|
|
|
for (i=0; i<sint; i++)
|
|
from_int[i] = (*s)[i+r];
|
|
r += sint;
|
|
ymax=*((int *)from_int);
|
|
|
|
delete[] from_int;
|
|
}
|
|
|
|
void write(char **s)
|
|
{
|
|
int sint=(int)sizeof(int);
|
|
char *from_int=new char[sint];
|
|
int i,r=0;
|
|
memcpy(from_int,(char *)(&xmin),sint);
|
|
for (i=0; i<sint; i++)
|
|
(*s)[i] = from_int[i];
|
|
r += sint;
|
|
|
|
memcpy(from_int,(char *)(&xmax),sint);
|
|
for (i=0; i<sint; i++)
|
|
(*s)[i+r] = from_int[i];
|
|
r += sint;
|
|
|
|
memcpy(from_int, (char *)(&ymin),sint);
|
|
for (i=0; i<sint; i++)
|
|
(*s)[i+r] = from_int[i];
|
|
r += sint;
|
|
|
|
memcpy(from_int, (char *)(&ymax),sint);
|
|
for (i=0; i<sint; i++)
|
|
(*s)[i+r] = from_int[i];
|
|
|
|
delete[] from_int;
|
|
}
|
|
|
|
void dump(int depth=0) const {
|
|
std::cerr << "(" << xmin << "/" << ymin
|
|
<< "),(" << xmax << "/" << ymax << ")";
|
|
}
|
|
|
|
};
|
|
|
|
std::ostream& operator << (std::ostream& s, R_tree_key_2 m) {
|
|
s << "(" << m.xmin << "/" << m.ymin << ")("
|
|
<< m.xmax << "/" << m.ymax << ")" << std::endl;
|
|
return s;
|
|
}
|
|
\end{cprog}
|
|
|
|
\newpage
|
|
|
|
% As long as there is only one manual we include this here:
|
|
%\input{ExternalMemoryStructures/reference.tex}
|