Skip to content

Adding spatial tables to existing SpatiaLite DBs

5 messages · Roger Bivand, Barry Rowlingson, Lee Hachadoorian

#
I don't think what I'm trying to do is "appending". I'm trying to
write two spatial data tables with different names in the single
spatial database file. The database file is the DSN and the tables are
layers - and rgdal is quite happy to add Shapefile "layers" to a
folder "DSN"...

Have just upgraded to rgdal 1.1-1 and still the same problem. Here's a
reprex without prompts:

require(rgdal)
pts=data.frame(x=runif(10),y=runif(10),z=1:10)
coordinates(pts)=~x+y
file.remove("tmpfile.db")

# write layer pts, this works
writeOGR(pts, "tmpfile.db", "pts", driver="SQLite",
dataset_option="SPATIALITE=YES", layer_option="FORMAT=SPATIALITE")

# write layer pts2
writeOGR(pts, "tmpfile.db", "pts2", driver="SQLite",
dataset_option="SPATIALITE=YES", layer_option="FORMAT=SPATIALITE")

Interestingly when I try and write with the *same* table name, rgdal
helpfully suggests "layer exists, use a new layer name", but when I
obey, I get the error behaviour I've described....

Barry
On Mon, Nov 2, 2015 at 11:21 PM, Roger Bivand <Roger.Bivand at nhh.no> wrote:
#
On Tue, 3 Nov 2015, Barry Rowlingson wrote:

            
There is nothing (much) in rgdal::writeOGR() that knows much about the 
drivers and (see separate recent thread) the implementation is really 
based on the simplest formats that existed 10 years ago. Spatialite is 
fast-changing, and not very stable in my experience. You're asking for OGR 
to handle this, but writeOGR() isn't aware that it needs to ask OGR for 
things that were not there when it was written (it was based on GRASS 
v.out.ogr from even earlier). It would be nice if it worked by chance, 
unspecified, but more needs to be done to check, and it looks as though 
it doesn't work (the single added layer overwrites the existing layer, 
problably because the dsn= isn't opened to append).

Does the same thing happen without specifying Spatialite? Does the same 
thing happen with PostGIS? Other DBs? We know that when dsn= is a 
directory and the driver is ESRI Shapefile, it does what we expect, but 
should we expect it to do that when dsn= is a file?

Please look around line 60 in rgdal/R/ogr_write.R to see how writeOGR() 
handles dsn and layer checking. This may need conditioning on the driver - 
there is already a kludgy "fix" for dsn= deletion for shapefiles for GDAL
When you're checking improvements to writeOGR(), please set a baseline 
using gdalUtils::ogr2ogr() so that we know where we are. We'll need to 
support GDAL < 2 and GDAL >= 2, which use drivers differently.

Best wishes,

Roger

  
    
#
On Tue, 3 Nov 2015, Roger Bivand wrote:

            
tf1 <- tempfile()
tf2 <- tempfile()
require(rgdal)
pts=data.frame(x=runif(10),y=runif(10),z=1:10)
coordinates(pts)=~x+y
writeOGR(pts, tf1, "pts", driver="SQLite",
dataset_option="SPATIALITE=YES", layer_option="FORMAT=SPATIALITE")
writeOGR(pts, tf2, "pts2", driver="SQLite",
dataset_option="SPATIALITE=YES", layer_option="FORMAT=SPATIALITE")
ogrListLayers(tf1)
ogrListLayers(tf2)
library(gdalUtils)
ogr2ogr(tf2, tf1, append=TRUE, nln="pts2a")
ogrListLayers(tf1)
ogrInfo(tf1, "pts")
ogrInfo(tf1, "pts2a")

Roger

  
    
#
After some digging...

The problem appears to be that OGR_write.cpp always tries to create a
new data source. This would appear to be the wrong thing to do when
you have an existing data source that can have multiple layers. Code
should probably only try and create the data source if its not there.
The PostGIS driver in OGR can't create data sources, so I'd expect
writing PostGIS with writeOGR to fail, which I'm not sure is the case
because surely people would scream and I don't have a PostGIS handy so
that's a bit contradictory... There seems to be an OGR "Can You Create
a Data Source?" capability test for drivers...

I think ogr2ogr works because it has a special case for a few drivers
(inlcuding SQLite) where it uses the same driver handle for source and
destination - so it doesn't even try creating the destination data
source.

I'm not sure how much more I want to dig into this, especially since
I'm on a Gdal 1.11 system but everything is going all 2.0 now. Would
there be any point making it work for 1.11 systems? How long until
we're all on 2.0?

Barry
On Tue, Nov 3, 2015 at 7:15 AM, Roger Bivand <Roger.Bivand at nhh.no> wrote:
#
On 11/03/2015 04:24 AM, Barry Rowlingson wrote:
Ogr2ogr can add layers to any driver that supports multiple layers using 
the -update switch:

     ogr2ogr -update destination source [layers]

If layers are not specified for a multi-layer source, *all* of them will 
be transferred.

Although writeOGR does expose the -update switch, it does successfully 
add layers to existing PostGIS database.

As Roger pointed out, gdalUtils exposes full functionality of ogr2ogr. 
So the following takes a couple of extra lines of code but works:

```
library(rgdal)
library(gdalUtils)

pts = data.frame(x=runif(10),y=runif(10),z=1:10)
coordinates(pts)=~x+y
writeOGR(pts, "final.sqlite", "pts", driver="SQLite", dataset_options = 
"SPATIALITE=YES")
writeOGR(pts, "tmp.sqlite", "pts2", driver="SQLite", dataset_options = 
"SPATIALITE=YES")

ogr2ogr("tmp.sqlite", "final.sqlite", "pts2", update=TRUE)
file.remove("tmp.sqlite")

```

Some other notes:

Without the -update switch, ogr2ogr will silently overwrite the 
destination.

Note that what writeOGR calls dataset_options are dataset *creation* 
options, so "SPATIALITE=YES" is not needed when adding layers to 
existing SpatiaLite DB. (For that matter, neither is the layer creation 
option "FORMAT=SPATIALITE".)

Best,
--Lee