Edit: The below approach works, but ignores the critical feature of R-trees -- that The splitting behavior of R-tree nodes is well defined, and maintains a balanced tree (through B-tree-like properties). So in fact, all you have to do is:
- Pick the maximum number of rectangles per page
- Create seed rectangles (use points furthest away from each other, centroids, whatever).
- For each point, choose a rectangle to put it into
- If it already falls into a single rectangle, put it in there
- If it does not, extend the rectangle that needs to be extended least (different ways to measure "least" exits -- area works)
- If multiple rectangles apply -- choose one based on how full it is, or some other heuristic
- If overflow occurs -- use the quadratic split to move things around...
- And so on, using R-tree algorithms straight out of a text book.
I think the method below is ok for finding your initial seed rectangles; but you don't want to populate your whole R-tree that way. Doing the splits and rebalancing all the time can be a bit expensive, so you will probably want to do a decent chunk of the work with the KD approach below; just not all of the work.
The KD approach: enclose everything in a rectangle.
If the number of points in the rectangle is > threshold, sweep in direction D until you cover half the points.
Divide into rectangles left and right (or above and below) the splitting point).
Call the same procedure recursively on the new rectangles, with the next direction (if you were going left to right, you will now go top to bottom, and vice versa).
The advantage this has over the divide-into-squares approach offered by another poster is that it accommodates skewed point distributions better.