Loan arranger

8 views
Skip to first unread message

David Winslow

unread,
May 15, 2012, 11:27:42 PM5/15/12
to geos...@googlegroups.com
Hey all, I've been thinking about the workspace and layer API.  Aside from aliasing a few commonly used properties to avoid redundant "get" prefixes, I think the big thing to deal with here is ensuring that resources get properly released when we're done with them.  Some languages (Python with its context managers comes to mind) provide native facilities to deal with this.  In Python the basic idea is to enter a "context" with the "with" statement; some cleanup action will happen when the block is exited, whether an exception is raised or not.

import geoscript.workspace.Directory

with Directory("/tmp/") as tmp:
    work_with(tmp)

# Directory store disposed at this point

As usual, the Scala convention is to rely on conventions and lightweight closure syntax rather than a purpose-specific language construct.  It's typically referred to as the "loan" pattern.  The general structure is that instead of acquiring a resource and storing it in a variable, we pass a closure into a function that promises to ensure any necessary cleanup happens.  Currently my thought is that we should have one generic loan function that takes in the connection parameters, rather than many individual loans for different types of store.

  def withWorkspace[A]
    (params: (String, java.io.Serializable)*)
    (f: Workspace => A)
    : A

The type parameter [A] here is used to let values "escape" from the loaned block - so you can get the result of whatever computation you do while using the Workspace. There's also an overload for passing the parameters as a Map, and an implicit that adds similar methods to DataStoreFactorySpi instances in case you want to ensure that your store is of a particular type.  A helper object named Params provides constructors for parameter maps of common stores; I wrote up Shapefile and database helpers for now but additional parameter maps could be of use.

With this in place, the above Python example would translate to something like this:
  import org.geoscript._, workspace._
  withWorkspace(Params.directory("/tmp/")) { tmp =>
    workWith(tmp)
  }

Once you have a handle to a Workspace, you can use the layerNamed(s: String) method to operate on individual layers.  Access to feature iterators is also mediated by a loan method.  So fleshing out the example even more we have:
  import org.geoscript._, workspace._
  withWorkspace(Params.directory("/tmp/")) { tmp =>
    val foo: layer.Layer = tmp.layerNamed("foo")
    foo.withAll { fs => fs.map(f.get[Int]("n")).sum }
  }

(This produces the sum of the integer field "n" on the layer "foo" from the workspace.)

For reference, here are the type aliases I'm using in geoscript.workspace and geoscript.layer for now:

  package object workspace {
    type Connector = org.geotools.data.DataStoreFactorySpi
    type Workspace = org.geotools.data.DataStore
  }

  package object layer {
    type Layer = org.geotools.data.simple.SimpleFeatureStore
    type Query = org.geotools.data.Query
  }

There are also a number of "enriched" wrapper classes which mostly add simpler names for operations already provided by GeoTools.  For the current  API see my Github repo: https://github.com/dwins/geoscript.scala/tree/tutorial/geoscript/src/main/scala/workspace https://github.com/dwins/geoscript.scala/tree/tutorial/geoscript/src/main/scala/layer

Note I am still working on the "tutorial" branch, not master.

--
David Winslow
Reply all
Reply to author
Forward
0 new messages