Hailey is heading to FOSS4G in a few weeks so we wanted to take a shot at using GeoServer before hand, so that she had an introduction before speaking with the GeoGurus at the conference. It turns out that GeoServer, though not particuarly lightweight, is extremely useful, especially for an organization like PCIC as nearly all of our data is spatial. After getting some examples up and running, it became clear that we will be able to utilize this heavily in the future. However, here are some notes for the two days that we spent on it.

The good

  • GeoServer can serve up data from a variety of sources and output a variety of formats. Input types include ESRI Shapefiles, PostGIS data stores of vector data, and GeoTIFF's. That covers nearly all of our geographic data with NetCDF grids being the notable exception. I'm pretty sure that I saw that there was a GDAL plugin or something, which would cover our NetCDF use case. Output specs include WFS, WMS, WCS, GoogleEarth, GML, OpenLayers, and tonnes of others. It appears to be very, very functional.
  • GeoServer allows you to add and use your own styles for styling features. This is pretty cool... it took Hailey all of about 10 minutes to find a good elevation style, and serve up a styled DEM to GoogleEarth which looked really good. Impressive.

The bad

  • Java applications can be a real pain in the butt. It's supposedly easy to just modify the properties of a server that you're trying to start, but I can never find the correct place to change them
  • Tomcat refused to even start from the default install on Debian etch. That pretty much precluded starting GeoServer from the .war file that they distribute. The Tomcat error looked something like this:
    8-Aug-2011 1:39:53 PM org.apache.catalina.core.StandardServer await
    SEVERE: StandardServer.await: create[8010]: 
    java.net.SocketException: Invalid argument
            at java.net.PlainSocketImpl.socketBind(Native Method)
            at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:383)
            at java.net.ServerSocket.bind(ServerSocket.java:328)
            at java.net.ServerSocket.(ServerSocket.java:194)
            at org.apache.catalina.core.StandardServer.await(StandardServer.java:373)
            at org.apache.catalina.startup.Catalina.await(Catalina.java:662)
            at org.apache.catalina.startup.Catalina.start(Catalina.java:614)
            at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
            at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
            at java.lang.reflect.Method.invoke(Method.java:597)
            at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
            at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
     which wasn't very helpful for me, in determining what the problem was.
    
  • The distributed shell script which seems to run it from a Jetty server won't bind the server to the global address. I was forced to run it on localhost (on windy), which doesn't make it very easy to test it. That means that I either have to run a web browser over X11 forwarding on windy (as well as any helper application which open the layers that I'm testing. Boooo), or tunnel my http connection over ssh, which is what I ended up doing.
    ssh hiebert@windy.pcic.uvic.ca -L 8080:localhost:8080
  • Getting GeoServer to connect to PostGIS turned out to be non-trivial. Every time I tried, I kept getting a network error.
    9 Aug 09:38:48 WARN [data.store] - Error obtaining new data store
    java.io.IOException
            at org.geoserver.catalog.ResourcePool.getDataStore(ResourcePool.java:376)
            at org.geoserver.catalog.impl.DataStoreInfoImpl.getDataStore(DataStoreInfoImpl.java:37)
            at org.geoserver.web.data.store.DataAccessNewPage.onSaveDataStore(DataAccessNewPage.java:87)
            at org.geoserver.web.data.store.AbstractDataAccessPage$1.onSubmit(AbstractDataAccessPage.java:159)
            at org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink$1.onSubmit(AjaxSubmitLink.java:68)
            at org.apache.wicket.ajax.form.AjaxFormSubmitBehavior.onEvent(AjaxFormSubmitBehavior.java:143)
            at org.apache.wicket.ajax.AjaxEventBehavior.respond(AjaxEventBehavior.java:177)
            at org.apache.wicket.ajax.AbstractDefaultAjaxBehavior.onRequest(AbstractDefaultAjaxBehavior.java:300)
            at org.apache.wicket.request.target.component.listener.BehaviorRequestTarget.processEvents(BehaviorRequestTarget.java:119)
            at org.apache.wicket.request.AbstractRequestCycleProcessor.processEvents(AbstractRequestCycleProcessor.java:92)
            at org.apache.wicket.RequestCycle.processEventsAndRespond(RequestCycle.java:1250)
            at org.apache.wicket.RequestCycle.step(RequestCycle.java:1329)
            at org.apache.wicket.RequestCycle.steps(RequestCycle.java:1436)
            at org.apache.wicket.RequestCycle.request(RequestCycle.java:545)
            at org.apache.wicket.protocol.http.WicketFilter.doGet(WicketFilter.java:484)
            at org.apache.wicket.protocol.http.WicketServlet.doPost(WicketServlet.java:160)
            at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
            at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
            at org.springframework.web.servlet.mvc.ServletWrappingController.handleRequestInternal(ServletWrappingController.java:158)
            at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:153)
            at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:48)
            at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:875)
            at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:809)
            at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
            at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:511)
            at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
            at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
            at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
            at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1093)
            at org.geoserver.filters.ThreadLocalsCleanupFilter.doFilter(ThreadLocalsCleanupFilter.java:23)
            at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:68)
            at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.springframework.security.ui.basicauth.BasicProcessingFilter.doFilterHttp(BasicProcessingFilter.java:174)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.springframework.security.providers.anonymous.AnonymousProcessingFilter.doFilterHttp(AnonymousProcessingFilter.java:105)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.springframework.security.ui.rememberme.RememberMeProcessingFilter.doFilterHttp(RememberMeProcessingFilter.java:116)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:278)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:192)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:192)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:99)
            at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:99)
            at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:394)
            at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:99)
            at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.providers.anonymous.AnonymousProcessingFilter.doFilterHttp(AnonymousProcessingFilter.java:105)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.ui.rememberme.RememberMeProcessingFilter.doFilterHttp(RememberMeProcessingFilter.java:116)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:278)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:192)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:185)
            at org.geoserver.filters.SpringDelegatingFilter$Chain.doFilter(SpringDelegatingFilter.java:64)
            at org.geoserver.filters.SpringDelegatingFilter.doFilter(SpringDelegatingFilter.java:39)
            at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
            at org.geoserver.platform.AdvancedDispatchFilter.doFilter(AdvancedDispatchFilter.java:49)
            at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
            at org.vfny.geoserver.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:109)
            at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:394)
            at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
            at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.providers.anonymous.AnonymousProcessingFilter.doFilterHttp(AnonymousProcessingFilter.java:105)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.ui.rememberme.RememberMeProcessingFilter.doFilterHttp(RememberMeProcessingFilter.java:116)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:278)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
            at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
            at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:406)
            at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:185)
            at org.springframework.security.util.FilterToBeanProxy.doFilter(FilterToBeanProxy.java:99)
            at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
            at org.geoserver.filters.LoggingFilter.doFilter(LoggingFilter.java:71)
            at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
            at org.geoserver.filters.ReverseProxyFilter.doFilter(ReverseProxyFilter.java:183)
            at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
            at org.geoserver.filters.GZIPFilter.doFilter(GZIPFilter.java:41)
            at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
            at org.geoserver.filters.SessionDebugFilter.doFilter(SessionDebugFilter.java:46)
            at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
            at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
            at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
            at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
            at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:726)
            at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
            at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:206)
            at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
            at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
            at org.mortbay.jetty.Server.handle(Server.java:324)
            at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
            at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:842)
            at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:648)
            at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
            at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
            at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
            at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:450)
    Caused by: java.lang.RuntimeException: Unable to obtain connection: Cannot create PoolableConnectionFactory (The connection attempt failed.)
            at org.geotools.jdbc.JDBCDataStore.createConnection(JDBCDataStore.java:1609)
            at org.geotools.jdbc.JDBCDataStore.createTypeNames(JDBCDataStore.java:804)
            at org.geotools.data.store.ContentDataStore.getTypeNames(ContentDataStore.java:298)
            at org.vfny.geoserver.util.DataStoreUtils.getDataAccess(DataStoreUtils.java:114)
            at org.geoserver.catalog.ResourcePool.getDataStore(ResourcePool.java:343)
            ... 152 more
    Caused by: org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (The connection attempt failed.)
            at org.apache.commons.dbcp.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:1549)
            at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1388)
            at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
            at org.geotools.data.jdbc.datasource.AbstractManageableDataSource.getConnection(AbstractManageableDataSource.java:44)
            at org.geotools.jdbc.JDBCDataStore.createConnection(JDBCDataStore.java:1601)
            ... 156 more
    Caused by: org.postgresql.util.PSQLException: The connection attempt failed.
            at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:150)
            at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:66)
            at org.postgresql.jdbc2.AbstractJdbc2Connection.(AbstractJdbc2Connection.java:125)
            at org.postgresql.jdbc3.AbstractJdbc3Connection.(AbstractJdbc3Connection.java:30)
            at org.postgresql.jdbc3.Jdbc3Connection.(Jdbc3Connection.java:24)
            at org.postgresql.Driver.makeConnection(Driver.java:393)
            at org.postgresql.Driver.connect(Driver.java:267)
            at org.apache.commons.dbcp.DriverConnectionFactory.createConnection(DriverConnectionFactory.java:38)
            at org.apache.commons.dbcp.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:582)
            at org.apache.commons.dbcp.BasicDataSource.validateConnectionFactory(BasicDataSource.java:1556)
            at org.apache.commons.dbcp.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:1545)
            ... 160 more
    Caused by: java.net.SocketException: Network is unreachable
            at java.net.PlainSocketImpl.socketConnect(Native Method)
            at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
            at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
            at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
            at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
            at java.net.Socket.connect(Socket.java:529)
            at java.net.Socket.connect(Socket.java:478)
            at java.net.Socket.(Socket.java:375)
            at java.net.Socket.(Socket.java:189)
            at org.postgresql.core.PGStream.(PGStream.java:62)
            at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:76)
            ... 170 more
    
    It took a lot of searching before I found this post explaining that it's an IPV6 bug in Sun's Java 6 and that doing this 
    $ sudo /sbin/sysctl net.ipv6.bindv6only=0
    is the solution. Or running java with
    -Djava.net.preferIPv4Stack=true
    . Of course.
  • When trying to add Shapefiles as data sources, we kept getting an error that was "Could not acquire data access". Turns out that the file browse button fills in the web input box with the file path e.g. /path/to/my/file.shp. Unfortunately the application wants a url e.g. file:/path/to/my/file.shp. That's an annoying bug and should be fixed.
  • More thoughts tomorrow, if I get a chance. -----


blog comments powered by Disqus

Published

09 August 2011

Category

work

Tags