Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failed to create a Shapefile following the GDAL Vector API tutorial #147

Open
guillemborrell opened this issue Oct 21, 2020 · 5 comments
Open
Milestone

Comments

@guillemborrell
Copy link

I used an example I found in the tests following the GDAL Vector API tutorial to create a very simple shapefile. The MEMORY driver works fine, but when I try to write the same data down to a file:

ArchGDAL.create("test.shp", driver=ArchGDAL.getdriver("ESRI Shapefile")) do dataset
    layer = ArchGDAL.createlayer(
        name = "point_out",
        dataset = dataset,
        geom = GDAL.wkbPoint
    )
    ArchGDAL.addfielddefn!(layer, "Name", GDAL.OFTString, nwidth = 32)
    featuredefn = ArchGDAL.layerdefn(layer)
    ArchGDAL.createfeature(layer) do feature
        ArchGDAL.setfield!(feature, ArchGDAL.findfieldindex(feature, "Name"), "myname")
        ArchGDAL.setgeom!(feature, ArchGDAL.createpoint(100.123, 0.123))
    end
end

I get the following stack trace (in a jupyter notebook).

Failed to set feature.

Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] macro expansion at /home/guillem/.julia/packages/ArchGDAL/j9NPL/src/utils.jl:14 [inlined]
 [3] setfeature! at /home/guillem/.julia/packages/ArchGDAL/j9NPL/src/ogr/featurelayer.jl:443 [inlined]
 [4] createfeature(::var"#70#72", ::ArchGDAL.IFeatureLayer) at /home/guillem/.julia/packages/ArchGDAL/j9NPL/src/ogr/featurelayer.jl:490
 [5] (::var"#69#71")(::ArchGDAL.Dataset) at ./In[48]:9
 [6] create(::var"#69#71", ::String; kwargs::Base.Iterators.Pairs{Symbol,ArchGDAL.Driver,Tuple{Symbol},NamedTuple{(:driver,),Tuple{ArchGDAL.Driver}}}) at /home/guillem/.julia/packages/ArchGDAL/j9NPL/src/context.jl:216
 [7] top-level scope at In[48]:1
 [8] include_string(::Function, ::Module, ::String, ::String) at ./loading.jl:1091
 [9] execute_code(::String, ::String) at /home/guillem/.julia/packages/IJulia/rWZ9e/src/execute_request.jl:27
 [10] execute_request(::ZMQ.Socket, ::IJulia.Msg) at /home/guillem/.julia/packages/IJulia/rWZ9e/src/execute_request.jl:86
 [11] #invokelatest#1 at ./essentials.jl:710 [inlined]
 [12] invokelatest at ./essentials.jl:709 [inlined]
 [13] eventloop(::ZMQ.Socket) at /home/guillem/.julia/packages/IJulia/rWZ9e/src/eventloop.jl:8
 [14] (::IJulia.var"#15#18")() at ./task.jl:356

Is this a bug or is it anything I am missing?

Thanks a lot for your work!

@yeesian
Copy link
Owner

yeesian commented Oct 22, 2020

Thanks for giving it a spin! Yeah I can reproduce your example -- unfortunately it looks like a limitation of the shapefile driver in GDAL:

julia> ArchGDAL.create("test.shp", driver=ArchGDAL.getdriver("ESRI Shapefile")) do dataset
          layer = ArchGDAL.createlayer(
              name = "point_out",
              dataset = dataset,
              geom = GDAL.wkbPoint
          )
          ArchGDAL.listcapability(layer)
       end
Dict{String,Bool} with 18 entries:
  "SequentialWrite"    => true
  "DeleteField"        => true
  "IgnoreFields"       => true
  "FastSpatialFilter"  => false
  "DeleteFeature"      => true
  "FastFeatureCount"   => true
  "StringsAsUTF8"      => true
  "CreateGeomField"    => false
  "ReorderFields"      => true
  "MeasuredGeometries" => true
  "FastSetNextByIndex" => true
  "CreateField"        => true
  "RandomWrite"        => true
  "RandomRead"         => true
  "CurveGeometries"    => false
  "FastGetExtent"      => true
  "Transactions"       => false
  "AlterFieldDefn"     => true

One possibility is to create the dataset in memory first, before writing it out as a shapefile, e.g.:

julia> ArchGDAL.create(ArchGDAL.getdriver("MEMORY")) do dataset
           layer = ArchGDAL.createlayer(
               name = "point_out",
               dataset = dataset,
               geom = GDAL.wkbPoint
           )
           ArchGDAL.addfielddefn!(layer, "Name", GDAL.OFTString, nwidth = 32)
           ArchGDAL.createfeature(layer) do feature
               ArchGDAL.setfield!(feature, ArchGDAL.findfieldindex(feature, "Name"), "myname")
               ArchGDAL.setgeom!(feature, ArchGDAL.createpoint(100.123, 0.123))
           end
           ArchGDAL.write(dataset, "test.shp", driver=ArchGDAL.getdriver("ESRI Shapefile"))
       end

@guillemborrell
Copy link
Author

Thanks! That's exactly what I needed.

@visr
Copy link
Collaborator

visr commented Oct 22, 2020

This trick of using a MEMORY dataset seems to be needed quite often. I guess in this case it was the "CreateGeomField" capability? Perhaps it's good to reopen this issue as a reminder to document it. On https://yeesian.com/ArchGDAL.jl/dev/features/ there is nothing about feature dataset creation at the moment.

@visr visr reopened this Oct 22, 2020
@yeesian
Copy link
Owner

yeesian commented Oct 23, 2020

Yeah agreed, while the design of ArchGDAL is supposed to be permissive with what's possible, it's important to have well-trodden paths (without having to tailor to the particularities of individual drivers) for users who are just looking to get common tasks done.

@ErickChacon
Copy link

I just tested the code shared above and I noticed that the geometry is created without name. Is there a way to set up the geometry name? Should the driver do this automatically?

ArchGDAL.create(ArchGDAL.getdriver("MEMORY")) do dataset
   layer = ArchGDAL.createlayer(
       name = "point_out",
       dataset = dataset,
       geom = ArchGDAL.wkbPoint
   )
   ArchGDAL.addfielddefn!(layer, "Name", ArchGDAL.OFTString, nwidth = 32)
   ArchGDAL.createfeature(layer) do feature
       ArchGDAL.setfield!(feature, ArchGDAL.findfieldindex(feature, "Name"), "myname")
       ArchGDAL.setgeom!(feature, ArchGDAL.createpoint(100.123, 0.123))
   end
   ArchGDAL.write(dataset, "test.shp", driver=ArchGDAL.getdriver("ESRI Shapefile"))
end

prueba = AG.read(joinpath("test.shp"))
AG.getlayer(prueba, 0)

# Layer: test
#     Geometry 0 (): [wkbPoint], POINT (100.123 0.123)
#         Field 0 (Name): [OFTString], myname

I also got an error when trying to use GPKG driver. It seems like it is trying to write raster data. Any suggestion?

ArchGDAL.create(ArchGDAL.getdriver("MEMORY")) do dataset
   layer = ArchGDAL.createlayer(
       name = "point_out",
       dataset = dataset,
       geom = ArchGDAL.wkbPoint
   )
   ArchGDAL.addfielddefn!(layer, "Name", ArchGDAL.OFTString, nwidth = 32)
   ArchGDAL.createfeature(layer) do feature
       ArchGDAL.setfield!(feature, ArchGDAL.findfieldindex(feature, "Name"), "myname")
       ArchGDAL.setgeom!(feature, ArchGDAL.createpoint(100.123, 0.123))
   end
   ArchGDAL.write(dataset, "test.gpkg", driver=ArchGDAL.getdriver("GPKG"))
end

# ERROR: GDALError (CE_Failure, code 6):
#     Only 1 (Grey/ColorTable), 2 (Grey+Alpha), 3 (RGB) or 4 (RGBA) band dataset supported

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants