{-# LANGUAGE ScopedTypeVariables #-}
{-|
Module      : Data.Geometry.Geos.STRTree
Maintainer  : pfrance@gmail.com

A query-only R-tree created using the Sort-Tile-Recursive (STR) algorithm. For two-dimensional spatial data.

The STR packed R-tree is simple to implement and maximizes space utilization; that is, as many leaves as possible are filled to capacity. Overlap between nodes is far less than in a basic R-tree. However, once the tree has been built (explicitly or on the first call to query), items may not be added or removed.

Described in: P. Rigaux, Michel Scholl and Agnes Voisard. Spatial Databases With Application To GIS. Morgan Kaufmann, San Francisco, 2002.

-}


module Data.Geometry.Geos.STRTree (
  foldr,
  toList,
  toVector,
  fromList,
  empty,
  build,
  insert,
  fromFoldable,
  fromFoldable_,
  lookup,
  RT.STRTree,
  RT.STRTreeBuilder
) where

import Prelude hiding (foldr, lookup)
import qualified Data.Geometry.Geos.Raw.STRTree as RT
import qualified Data.Geometry.Geos.Raw.Geometry as RG
import Data.Geometry.Geos.Geometry
import Data.Geometry.Geos.Raw.Base
import Foreign
import qualified Data.Vector as V

-- can't make instance of Foldable because of Storable constraint
foldr :: (Storable a) => (a -> b -> b) -> b -> RT.STRTree a -> b
foldr f a = runGeos . RT.foldr f a


-- unfortunately, the api exposed by geos does not allow retrieval of original geometries
toList :: Storable a => RT.STRTree a -> [a]
toList = foldr (:) []

toVector :: Storable a => RT.STRTree a -> V.Vector a
toVector = foldr V.cons V.empty

fromList :: Storable b => [(Geometry a, b)] -> RT.STRTree b
fromList = fromFoldable

empty :: RT.STRTreeBuilder a
empty = runGeos $ RT.createSTRTreeBuilder 10

build :: RT.STRTreeBuilder a -> RT.STRTree a
build = runGeos . RT.build

insert :: Storable a => Geometry b -> a -> RT.STRTreeBuilder a -> ()
insert geom item tree = runGeos $ do
    rg :: RG.GeomConst <- convertGeometryToRaw geom
    RT.insert tree rg item
    return ()

{-|
`fromFoldable` creates an STRTree with a default node capacity of 10. For finer-grained control over the node capacity, `fromFoldable_` accepts a node-capacity argument.
-}
fromFoldable :: (Foldable f, Storable b) => f (Geometry a, b) -> RT.STRTree b
fromFoldable  = fromFoldable_ 10

fromFoldable_ :: (Foldable f, Storable b) => Int -> f (Geometry a, b) -> RT.STRTree b
fromFoldable_ capacity things = runGeos $ do
  tree <- RT.createSTRTreeBuilder capacity
  mapM_ (ins tree) things
  RT.build tree
  where ins tree' (g,b) = do
          rg :: RG.GeomConst <- convertGeometryToRaw g
          RT.insert tree' rg b


{-| 
Queries the index for all items whose extents intersect the given search Envelope.
Note that some kinds of indexes may also return objects which do not in fact intersect the query envelope.
-}
lookup :: Storable b => Geometry a -> RT.STRTree b -> V.Vector b
lookup g tree = runGeos $ do
  rg :: RG.GeomConst <- convertGeometryToRaw g
  RT.query tree rg