[Qgis-developer] calling createEmptyDataSource from python

Stefanie Tellex stefie10 at media.mit.edu
Thu Jun 5 16:46:35 EDT 2008


Hi,

I wrote a patch that makes it possible to call createEmptyDataSource 
from python, against revision 8592. It's kind of big.  There are three 
parts:

1.  Changes to qgisapp.cpp, qgsvectordataprovider.cpp, 
qgsvectordataprovider.h
   * qgisapp.cpp was looking up createEmptyDataSource dynamically in the 
ogr library.  I refactored this into a function in 
qgsvectordataprovider.  This function looks up the real 
createEmptyDataSource in exactly the same way, but takes an additional 
parameter that is the name of the provider.  qgisapp.cpp now calls this 
function.

2.  Changes to the python interface.
   * Updated conversions.sip to convert an std::list<std::pair<TYPE1, 
TYPE2>> to and from a list of tuples.  I didn't test the "to" code. 
(This is required to pass the attributes of the new data source.)
   * qgsvectordataprovider.sip - declare the new method, and added an 
extra include.  (The extra include is needed because it's a function, 
not a class method.)

3.  Error messages in qgsogrprovider.cpp.  I added a bunch of warnings 
to createEmptyDataSource to help debug the new code I wrote, when 
calling it from python.


Stefanie


(PS:  Here's my python test case:
     def testNewLayer(self):
         try:
             print createEmptyDataSource
             fname = "newlayertest"
             self.assertEqual(os.path.exists(fname), False)
             out = createEmptyDataSource("ogr", fname, "ESRI Shapefile", 
"System", QGis.WKBLineString,
                                         [("id", "Integer")])

             print "out", out
             self.assertEqual(out, True)
             self.assertEqual(os.path.exists(fname), True)

             layer = QgsVectorLayer("%s/%s.shp" % (fname, fname), "test 
layer", "ogr")
             self.assertEqual(layer.featureCount(), 0)
 
self.assertEqual(layer.getDataProvider().indexFromFieldName("id"), 0)
         finally:
             os.system("rm -rf %s" % (fname))
             pass
)


Stefanie Tellex wrote:
> Hi,
> 
> Is there a way to call the ogr provider's createEmptyDataSource function 
> from python?  I could try using ctypes, but I wasn't sure if it would 
> work with the sip interface.  Combining two ways of talking to C/C++ 
> from python seems hairy.
> 
> I played around with adding it via SIP, but it also got hairy. SIP seems 
> to like separate modules for C and C++ interfaces, and there aren't 
> (yet) provider specific python modules.
> 
> I can do the work to add it to the python interface, but advice on the 
> best way to do it would be great.
> 
> Thanks!
> 
> Stefanie
> _______________________________________________
> Qgis-developer mailing list
> Qgis-developer at lists.osgeo.org
> http://lists.osgeo.org/mailman/listinfo/qgis-developer

-------------- next part --------------
Index: qgis/qgis_unstable/src/core/qgsvectordataprovider.cpp
===================================================================
--- qgis.orig/qgis_unstable/src/core/qgsvectordataprovider.cpp	2008-06-05 10:16:54.000000000 -0400
+++ qgis/qgis_unstable/src/core/qgsvectordataprovider.cpp	2008-06-05 16:05:00.000000000 -0400
@@ -18,7 +18,8 @@
 
 #include <cfloat> // for DBL_MAX
 #include <climits>
-
+#include <QLibrary>
+#include "qgsproviderregistry.h"
 #include "qgsvectordataprovider.h"
 #include "qgsfeature.h"
 #include "qgsfield.h"
@@ -356,3 +357,42 @@
 
   mCacheMinMaxDirty = FALSE;
 }
+
+bool createEmptyDataSource(const QString& provider,
+			   const QString& filename,
+			   const QString& fileformat,
+			   const QString& enc,
+			   QGis::WKBTYPE geometrytype,
+			   const std::list<std::pair<QString, QString> >& attributes) {
+
+  QgsProviderRegistry * pReg = QgsProviderRegistry::instance();
+  QString lib = pReg->library(provider);
+  // load the data provider
+  QLibrary* myLib = new QLibrary(lib);
+  bool loaded = myLib->load();
+  typedef bool (*createEmptyDataSourceProc)(const QString&, const QString&, const QString&, QGis::WKBTYPE, const std::list<std::pair<QString, QString> >&);
+
+  createEmptyDataSourceProc createEmptyDataSource=(createEmptyDataSourceProc)myLib->resolve("createEmptyDataSource");
+
+  if(createEmptyDataSource)
+    {
+      if(geometrytype != QGis::WKBUnknown)
+      {
+	bool retval = createEmptyDataSource(filename,fileformat, enc, geometrytype, attributes);
+	if (! retval) {
+	  QgsLogger::warning("Could not greate data source.  See previous messages for details.");
+	}
+        return retval;
+      }
+      else
+      {
+	QgsLogger::warning("geometry type not recognised");
+        return FALSE;
+      }
+    }
+    else
+      {
+	QgsLogger::warning("Resolving newEmptyDataSource(...) failed");
+	return FALSE;
+    }
+}
Index: qgis/qgis_unstable/src/core/qgsvectordataprovider.h
===================================================================
--- qgis.orig/qgis_unstable/src/core/qgsvectordataprovider.h	2008-06-05 12:17:20.000000000 -0400
+++ qgis/qgis_unstable/src/core/qgsvectordataprovider.h	2008-06-05 12:37:57.000000000 -0400
@@ -295,4 +295,11 @@
       QSet<QString> mSupportedNativeTypes;
 };
 
+bool createEmptyDataSource(const QString& provider,
+			   const QString& filename,
+			   const QString& fileformat,
+			   const QString& enc,
+			   QGis::WKBTYPE geometrytype,
+			   const std::list<std::pair<QString, QString> >& attributes);
+
 #endif
Index: qgis/qgis_unstable/src/app/qgisapp.cpp
===================================================================
--- qgis.orig/qgis_unstable/src/app/qgisapp.cpp	2008-06-05 12:33:57.000000000 -0400
+++ qgis/qgis_unstable/src/app/qgisapp.cpp	2008-06-05 16:12:36.000000000 -0400
@@ -2683,51 +2683,13 @@
   settings.writeEntry("/UI/encoding", openFileDialog->encoding());
 
   delete openFileDialog;
-
-  //try to create the new layer with OGRProvider instead of QgsVectorFileWriter
-  QgsProviderRegistry * pReg = QgsProviderRegistry::instance();
-  QString ogrlib = pReg->library("ogr");
-  // load the data provider
-  QLibrary* myLib = new QLibrary(ogrlib);
-  bool loaded = myLib->load();
-  if (loaded)
-  {
-    QgsDebugMsg("ogr provider loaded");
-
-    typedef bool (*createEmptyDataSourceProc)(const QString&, const QString&, const QString&, QGis::WKBTYPE, \
-      const std::list<std::pair<QString, QString> >&);
-    createEmptyDataSourceProc createEmptyDataSource=(createEmptyDataSourceProc)myLib->resolve("createEmptyDataSource");
-    if(createEmptyDataSource)
-    {
-#if 0
-      if(geometrytype == QGis::WKBPoint)
-      {
-        createEmptyDataSource(filename,fileformat, enc, QGis::WKBPoint, attributes);
-      }
-      else if (geometrytype == QGis::WKBLineString)
-      {
-        createEmptyDataSource(filename,fileformat, enc, QGis::WKBLineString, attributes);
-      }
-      else if(geometrytype == QGis::WKBPolygon)
-      {
-        createEmptyDataSource(filename,fileformat, enc, QGis::WKBPolygon, attributes);
-      }
-#endif
-      if(geometrytype != QGis::WKBUnknown)
-      {
-        createEmptyDataSource(filename,fileformat, enc, geometrytype, attributes);
-      }
-      else
-      {
-        QgsDebugMsg("geometry type not recognised");
-        return;
-      }
-    }
-    else
-    {
-      QgsDebugMsg("Resolving newEmptyDataSource(...) failed");
+  QgsLogger::warning("format");
+  QgsLogger::warning(fileformat);
+  if (!createEmptyDataSource("ogr", filename, fileformat, enc,
+			     geometrytype, attributes)) {
+      QgsDebugMsg("Data source not created");
+      return;
     }
-  }
 
   //then add the layer to the view
   QStringList filenames;
Index: qgis/qgis_unstable/python/core/qgsvectordataprovider.sip
===================================================================
--- qgis.orig/qgis_unstable/python/core/qgsvectordataprovider.sip	2008-06-05 12:46:07.000000000 -0400
+++ qgis/qgis_unstable/python/core/qgsvectordataprovider.sip	2008-06-05 15:17:36.000000000 -0400
@@ -1,4 +1,9 @@
 
+%ModuleHeaderCode
+#include <qgsvectordataprovider.h>
+%End
+
+
 class QgsVectorDataProvider : QgsDataProvider
 {
 %TypeHeaderCode
@@ -239,3 +244,9 @@
       void setFetchFeaturesWithoutGeom(bool fetch);
 
 };
+bool createEmptyDataSource(const QString& provider,
+			   const QString& filename,
+			   const QString& fileformat,
+			   const QString& enc,
+			   QGis::WKBTYPE geometrytype,
+			   const std::list<std::pair<QString, QString> >& attributes);
Index: qgis/qgis_unstable/python/core/conversions.sip
===================================================================
--- qgis.orig/qgis_unstable/python/core/conversions.sip	2008-06-05 14:47:51.000000000 -0400
+++ qgis/qgis_unstable/python/core/conversions.sip	2008-06-05 16:32:35.000000000 -0400
@@ -8,6 +8,7 @@
 - QMap<int, QMap<int, TYPE> >
 - QMap<TYPE1, TYPE2*>
 - QMultiMap<double, TYPE2>
+- std::list<std::pair<TYPE1, TYPE2>>
 */
 
 %ModuleHeaderCode
@@ -631,3 +632,115 @@
     return sipGetState(sipTransferObj);
 %End
    };
+
+
+
+
+
+template<TYPE1, TYPE2>
+%MappedType std::list<std::pair<TYPE1, TYPE2>>
+{
+%TypeHeaderCode
+#include <list>
+#include <utility>
+%End
+
+%ConvertFromTypeCode
+    // Create the dictionary.
+    PyObject *list = PyList_New(0);
+
+    if (!list)
+        return NULL;
+
+    // Set the dictionary elements.
+    std::list<std::pair<TYPE1, TYPE2> >::const_iterator i = sipCpp->begin();
+
+    while (i != sipCpp->end())
+    {
+      TYPE1 t1 = i->first;
+      TYPE2 t2 = i->second;
+
+      PyObject *t1obj = sipConvertFromNewInstance(&t1, sipClass_TYPE1, sipTransferObj);
+      PyObject *t2obj = sipConvertFromInstance(&t2, sipClass_TYPE2, sipTransferObj);
+
+      PyObject *tuple = PyTuple_New(2);
+      if (PyTuple_SetItem(tuple, 0, t1obj) ||
+	  PyTuple_SetItem(tuple, 1, t2obj) ||
+	  PyList_Append(list, tuple)) {
+	Py_DECREF(list);
+	if (t1obj) {
+	  Py_DECREF(t1obj);
+	}
+	if (t2obj) {
+	  Py_DECREF(t2obj);
+	}
+	if (tuple) {
+	  Py_DECREF(tuple);
+	}
+	return NULL;
+      }
+
+      Py_DECREF(t1obj);
+      Py_DECREF(t2obj);
+
+        ++i;
+    }
+
+    return list;
+%End
+
+%ConvertToTypeCode
+    PyObject *t1obj, *t2obj;
+#if PY_VERSION_HEX >= 0x02050000
+    Py_ssize_t i = 0;
+#else
+    int i = 0;
+#endif
+
+    // Check the type if that is all that is required.
+    if (sipIsErr == NULL)
+    {
+        if (!PyList_Check(sipPy))
+            return 0;
+
+	for (i = 0; i < PyList_Size(sipPy); i++) {
+	  PyObject * tuple = PyList_GetItem(sipPy, i);
+	  t1obj = PyTuple_GetItem(tuple, 0);
+	  t2obj = PyTuple_GetItem(tuple, 1);
+	  if (!sipCanConvertToInstance(t1obj, sipClass_TYPE1, SIP_NOT_NONE))
+	    return 0;
+
+	  if (!sipCanConvertToInstance(t2obj, sipClass_TYPE2, SIP_NOT_NONE))
+	    return 0;
+        }
+        return 1;
+    }
+
+    std::list<std::pair<TYPE1, TYPE2> > *list = new std::list<std::pair<TYPE1, TYPE2> >;
+    for (i = 0; i < PyList_Size(sipPy); i++) {
+      int state1, state2;
+      PyObject * tuple = PyList_GetItem(sipPy, i);
+      PyObject * sip1 = PyTuple_GetItem(tuple, 0);
+      PyObject * sip2 = PyTuple_GetItem(tuple, 1);
+
+      TYPE1 * t1 = reinterpret_cast<TYPE1 *>(sipConvertToInstance(sip1, sipClass_TYPE1, sipTransferObj, SIP_NOT_NONE, &state1, sipIsErr));
+      TYPE2 * t2 = reinterpret_cast<TYPE2 *>(sipConvertToInstance(sip2, sipClass_TYPE2, sipTransferObj, SIP_NOT_NONE, &state2, sipIsErr));
+      if (*sipIsErr)
+        {
+	  sipReleaseInstance(t1, sipClass_TYPE1, state1);
+	  sipReleaseInstance(t2, sipClass_TYPE2, state2);
+	  delete list;
+	  return 0;
+        }
+
+      list->push_back(std::pair<TYPE1, TYPE2>(*t1, *t2));
+      sipReleaseInstance(t1, sipClass_TYPE1, state1);
+      sipReleaseInstance(t2, sipClass_TYPE2, state2);
+    }
+
+    *sipCppPtr = list;
+
+    return sipGetState(sipTransferObj);
+%End
+};
+
Index: qgis/qgis_unstable/src/providers/ogr/qgsogrprovider.cpp
===================================================================
--- qgis.orig/qgis_unstable/src/providers/ogr/qgsogrprovider.cpp	2008-06-05 16:03:12.000000000 -0400
+++ qgis/qgis_unstable/src/providers/ogr/qgsogrprovider.cpp	2008-06-05 16:32:32.000000000 -0400
@@ -1132,6 +1132,10 @@
   driver = OGRGetDriverByName(format.toAscii());
   if(driver == NULL)
   {
+    QString msg = "Failed to get the driver.";
+    msg.append(" Name: ");
+    msg.append(format);
+    QgsLogger::warning(msg);
     return false;
   }
 
@@ -1139,6 +1143,7 @@
   dataSource = OGR_Dr_CreateDataSource(driver,QFile::encodeName(uri).constData(), NULL);
   if(dataSource == NULL)
   {
+    QgsLogger::warning("Failed to create the data source.");
     return false;
   }
 
@@ -1177,8 +1182,7 @@
     break;
   default:
     {
-      QgsLogger::debug("Unknown vector type of: ", (int)(vectortype), 1, 
-        __FILE__, __FUNCTION__, __LINE__);
+      QgsLogger::warning("Unknown vector type of");
       return false;
       break;
     }
@@ -1188,6 +1192,7 @@
   layer = OGR_DS_CreateLayer(dataSource,QFile::encodeName(QFileInfo(uri).baseName()).constData(), reference, OGRvectortype, NULL);
   if(layer == NULL)
   {
+    QgsLogger::warning("Could not create layer");
     return false;
   }
 
@@ -1224,6 +1229,12 @@
         QgsLogger::warning("creation of OFTString field failed");
       }
     }
+    else {
+      QString msg = "Unknown attribute type.  Ignoring attribute. ";
+      msg.append(" Type: ");
+      msg.append(it->second);
+      QgsLogger::warning(msg);
+    }
   }
 
   OGR_DS_Destroy(dataSource);


More information about the Qgis-developer mailing list