[GIS] Upload shapefile using GeoServer REST API and C#

cgeoserverrestshapefile

I'd like to upload shapefiles using GeoServer REST API and C#. So far, I can create workspaces using the REST API and C#. From GeoServer documentation (specific to cURL), the difference between creating a workspace and uploading a shapefile is in the HTTP Method (workspace creation uses POST, shapefile uploading uses PUT), ContentType (workspace creation uses application/xml, shapefile uploading uses application/zip), and finally there is the --data-binary parameter in shapefile uploading.

The code I use for creating workspace is shown below. Can anyone kindly help me refactor this code so that I can use it to upload a shapefile.

        String gUrl = "http://localhost:8081/geoserver/rest/workspaces";
        WebRequest request = WebRequest.Create(gUrl);

        request.ContentType = "text/xml";
        request.Method = "POST";
        request.Credentials = new NetworkCredential("geoServer-username", "password");

        byte[] buffer = Encoding.GetEncoding("UTF-8").GetBytes("<workspace><name>new_workspace_csharp_2</name></workspace>");
        Stream requestStream = request.GetRequestStream();
        requestStream.Write(buffer, 0, buffer.Length);
        requestStream.Close();

        WebResponse response = request.GetResponse();
        Console.Write("Response from GeoServer: " + response);

EDITED:
I've managed to make some headway, and I can now upload a shapefile using C# and the GeoServer REST API. My refactoring effort, which now works, is shown below:

    public bool UploadShapeFile(string workspace, string dsName, Uri zipUri)
    {
        String fileUri = zipUri.AbsolutePath;
        Console.Write(fileUri);

        byte[] localShapeFile = readLocalShapeFile(fileUri);

        String sUrl = "http://localhost:8081/geoserver/rest/workspaces/cs_ws1/datastores/cs_ds1/file.shp";
        WebRequest request = WebRequest.Create(sUrl);

        request.ContentType = "application/zip";
        request.Method = "PUT";
        request.Credentials = new NetworkCredential("gs-username", "password");

        // byte[] buffer = Encoding.GetEncoding("UTF-8").GetBytes(@fileUri);
        Stream requestStream = request.GetRequestStream();
        requestStream.Write(localShapeFile, 0, localShapeFile.Length);
        requestStream.Close();

        WebResponse response = request.GetResponse();
        Console.Write("Response from GeoServer: " + response);


        return false;
    }

    private byte[] readLocalShapeFile(string filePath)
    {
        byte[] buffer;
        FileStream fStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        try {
            int length = (int)fStream.Length;
            buffer = new byte[length]; 
            int count; 
            int sum = 0; 

            // Read until Read method returns 0 - End of stream reached
            while ((count = fStream.Read(buffer, sum, length - sum)) > 0)
                sum += count;
        }
        finally {
            fStream.Close();
        }

        return buffer;
    }

However, I still believe there can be a more elegant way of doing this. Any suggestions will be highly appreciated.

Best Answer

I've been able to achieve this using the code segments below. But it'll be great if someone can come up with a better solution.

Part I: Uploading a shape file to GeoServer without PostGIS

    public bool UploadShapeFile(string workspace, string dsName, Uri zipUri)
    {
        String fileUri = zipUri.AbsolutePath;
        Console.Write(fileUri);

        byte[] localShapeFile = readLocalShapeFile(fileUri);

        String sUrl = "http://localhost:8081/geoserver/rest/workspaces/" + 
                        workspace + "/datastores/" + 
                        dsName + "/file.shp";

        WebRequest request = WebRequest.Create(sUrl);

        request.ContentType = "application/zip";
        request.Method = "PUT";
        request.Credentials = new NetworkCredential("geoserver-username", "passwd");

        Stream requestStream = request.GetRequestStream();
        requestStream.Write(localShapeFile, 0, localShapeFile.Length);
        requestStream.Close();

        WebResponse response = request.GetResponse();
        Console.Write("Response from GeoServer: " + response);


        return false;
    }

    private byte[] readLocalShapeFile(string filePath)
    {
        byte[] buffer;
        FileStream fStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        try {
            int length = (int)fStream.Length; 
            buffer = new byte[length]; 
            int count;
            int sum = 0;

            while ((count = fStream.Read(buffer, sum, length - sum)) > 0)
                sum += count;
        }
        finally {
            fStream.Close();
        }

        return buffer;
    }

Part II: Upload Shape File to GeoServer's PostGIS store First, create a DB Data Store as shown:

    public string CreateDbDataStore(string ws, string dsName)
    {
        String gUrl = "http://localhost:8081/geoserver/rest/workspaces/" + ws + "/datastores.xml";
        WebRequest request = WebRequest.Create(gUrl);

        request.ContentType = "application/xml";
        request.Method = "POST";
        request.Credentials = new NetworkCredential("geoserver-username", "passwd");
        string dbXml = getDbXml(dsName);

        byte[] buffer = Encoding.GetEncoding("UTF-8").GetBytes(dbXml);
        Stream requestStream = request.GetRequestStream();
        requestStream.Write(buffer, 0, buffer.Length);
        requestStream.Close();

        WebResponse response = request.GetResponse();
        Console.Write("Response from GeoServer: " + response);

        return dsName;
    }

Then, create a DB Table and FeatureType as shown:

    public bool CreatePostGISTableAndFeatureType(string ws, string ds, string title, string projection)
    {
        string featXml = GetFeatureXml(ds, title, projection);
        string fUrl = "http://localhost:8081/geoserver/rest/workspaces/" + ws +
                        "/datastores/" + ds + "/featuretypes";

        WebRequest request = WebRequest.Create(fUrl);
        request.ContentType = "application/xml";
        request.Method = "POST";
        request.Credentials = new NetworkCredential("geoserver-username", "passwd");

        byte[] buffer = Encoding.GetEncoding("UTF-8").GetBytes(featXml);

        Stream requestStream = request.GetRequestStream();
        requestStream.Write(buffer, 0, buffer.Length);
        requestStream.Close();

        WebResponse response = request.GetResponse();

        return false;
    }

    private string GetFeatureXml(string dsName, string title, string projection)
    {
        string fXml = "<featureType>" +
                            "<name>" + dsName + "</name>" +
                            "<nativeName>" + dsName + "</nativeName>" +
                            "<title>" + title + "</title>" +
                            "<srs>" + projection + "</srs>" +
                            "<attributes>" +
                                "<attribute>" +
                                    "<name>the_geom</name>" +
                                    "<binding>com.vividsolutions.jts.geom.Point</binding>" +
                                "</attribute>" +
                                "<attribute>" +
                                    "<name>description</name>" +
                                    "<binding>java.lang.String</binding>" +
                                "</attribute>" +
                                "<attribute>" +
                                    "<name>timestamp</name>" +
                                    "<binding>java.util.Date</binding>" +
                                "</attribute>" +
                            "</attributes>" +
                        "</featureType>";
        return fXml;
    }

Finally, upload the shape file using the UploadShapeFile(..) method above, specifying the previously created (postgis) data store as the dsName.

Related Question