1 /*
2  * $Id: ogr_python.i 83343857e45420d7b50dfd65e85ee6dcb7c3e7e9 2021-10-24 01:28:35 +0200 Even Rouault $
3  *
4  * python specific code for ogr bindings.
5  */
6 
7 %feature("autodoc");
8 
9 #ifndef FROM_GDAL_I
10 %init %{
11 
12   if ( OGRGetDriverCount() == 0 ) {
13     OGRRegisterAll();
14   }
15 
16 %}
17 #endif
18 
19 /*%{
20 
21 #if PY_MINOR_VERSION >= 4
22 #include "datetime.h"
23 #define USE_PYTHONDATETIME 1
24 #endif
25 %}
26 */
27 
28 %include "ogr_layer_docs.i"
29 #ifndef FROM_GDAL_I
30 %include "ogr_datasource_docs.i"
31 %include "ogr_driver_docs.i"
32 #endif
33 %include "ogr_feature_docs.i"
34 %include "ogr_featuredef_docs.i"
35 %include "ogr_fielddef_docs.i"
36 %include "ogr_geometry_docs.i"
37 
38 %rename (GetDriverCount) OGRGetDriverCount;
39 %rename (GetOpenDSCount) OGRGetOpenDSCount;
40 %rename (SetGenerate_DB2_V72_BYTE_ORDER) OGRSetGenerate_DB2_V72_BYTE_ORDER;
41 %rename (RegisterAll) OGRRegisterAll();
42 
43 #ifndef FROM_GDAL_I
44 %{
45 #define MODULE_NAME           "ogr"
46 %}
47 
48 %include "python_exceptions.i"
49 %include "python_strings.i"
50 
51 %extend OGRDataSourceShadow {
52   %pythoncode {
53     def Destroy(self):
54       "Once called, self has effectively been destroyed.  Do not access. For backwards compatibility only"
55       _ogr.delete_DataSource(self)
56       self.thisown = 0
57 
58     def Release(self):
59       "Once called, self has effectively been destroyed.  Do not access. For backwards compatibility only"
60       _ogr.delete_DataSource(self)
61       self.thisown = 0
62 
63     def Reference(self):
64       "For backwards compatibility only."
65       return self.Reference()
66 
67     def Dereference(self):
68       "For backwards compatibility only."
69       self.Dereference()
70 
71     def __len__(self):
72         """Returns the number of layers on the datasource"""
73         return self.GetLayerCount()
74 
75     def __getitem__(self, value):
76         """Support dictionary, list, and slice -like access to the datasource.
77         ds[0] would return the first layer on the datasource.
78         ds['aname'] would return the layer named "aname".
79         ds[0:4] would return a list of the first four layers."""
80         if isinstance(value, slice):
81             output = []
82             step = value.step if value.step else 1
83             for i in range(value.start, value.stop, step):
84                 lyr = self.GetLayer(i)
85                 if lyr is None:
86                     return output
87                 output.append(lyr)
88             return output
89         if isinstance(value, int):
90             if value > len(self) - 1:
91                 raise IndexError
92             return self.GetLayer(value)
93         elif isinstance(value, str):
94             return self.GetLayer(value)
95         else:
96             raise TypeError('Input %s is not of String or Int type' % type(value))
97 
98     def GetLayer(self, iLayer=0):
99         """Return the layer given an index or a name"""
100         if isinstance(iLayer, str):
101             return self.GetLayerByName(str(iLayer))
102         elif isinstance(iLayer, int):
103             return self.GetLayerByIndex(iLayer)
104         else:
105             raise TypeError("Input %s is not of String or Int type" % type(iLayer))
106 
107     def DeleteLayer(self, value):
108         """Deletes the layer given an index or layer name"""
109         if isinstance(value, str):
110             for i in range(self.GetLayerCount()):
111                 name = self.GetLayer(i).GetName()
112                 if name == value:
113                     return _ogr.DataSource_DeleteLayer(self, i)
114             raise ValueError("Layer %s not found to delete" % value)
115         elif isinstance(value, int):
116             return _ogr.DataSource_DeleteLayer(self, value)
117         else:
118             raise TypeError("Input %s is not of String or Int type" % type(value))
119   }
120 }
121 
122 #endif
123 
124 
125 %extend OGRLayerShadow {
126   %pythoncode %{
127     def Reference(self):
128       "For backwards compatibility only."
129       pass
130 
131     def Dereference(self):
132       "For backwards compatibility only."
133       pass
134 
135     def __len__(self):
136         """Returns the number of features in the layer"""
137         return self.GetFeatureCount()
138 
139     # To avoid __len__ being called when testing boolean value
140     # which can have side effects (#4758)
141     def __nonzero__(self):
142         return True
143 
144     # For Python 3 compat
145     __bool__ = __nonzero__
146 
147     def __getitem__(self, value):
148         """Support list and slice -like access to the layer.
149         layer[0] would return the first feature on the layer.
150         layer[0:4] would return a list of the first four features."""
151         if isinstance(value, slice):
152             import sys
153             output = []
154             if value.stop == sys.maxsize:
155                 #for an unending slice, sys.maxsize is used
156                 #We need to stop before that or GDAL will write an
157                 ##error to stdout
158                 stop = len(self) - 1
159             else:
160                 stop = value.stop
161             for i in range(value.start, stop, value.step):
162                 feature = self.GetFeature(i)
163                 if feature:
164                     output.append(feature)
165                 else:
166                     return output
167             return output
168         if isinstance(value, int):
169             if value > len(self) - 1:
170                 raise IndexError
171             return self.GetFeature(value)
172         else:
173             raise TypeError("Input %s is not of IntType or SliceType" % type(value))
174 
175     def CreateFields(self, fields):
176         """Create a list of fields on the Layer"""
177         for i in fields:
178             self.CreateField(i)
179 
180     def __iter__(self):
181         self.ResetReading()
182         while True:
183             feature = self.GetNextFeature()
184             if not feature:
185                 break
186             yield feature
187 
188     def schema(self):
189         output = []
190         defn = self.GetLayerDefn()
191         for n in range(defn.GetFieldCount()):
192             output.append(defn.GetFieldDefn(n))
193         return output
194     schema = property(schema)
195 
196   %}
197 
198 }
199 
200 %extend OGRFeatureShadow {
201 
202   %apply ( const char *utf8_path ) { (const char* value) };
203   void SetFieldString(int id, const char* value) {
204     OGR_F_SetFieldString(self, id, value);
205   }
206   %clear (const char* value );
207 
208   %pythoncode %{
209     def Reference(self):
210       pass
211 
212     def Dereference(self):
213       pass
214 
215     def Destroy(self):
216       "Once called, self has effectively been destroyed.  Do not access. For backwards compatibility only"
217       _ogr.delete_Feature(self)
218       self.thisown = 0
219 
220     def __cmp__(self, other):
221         """Compares a feature to another for equality"""
222         return self.Equal(other)
223 
224     def __copy__(self):
225         return self.Clone()
226 
227     def _getfieldindex(self, fieldname):
228         case_insensitive_idx = -1
229         fdefn = _ogr.Feature_GetDefnRef(self)
230         for i in range(fdefn.GetFieldCount()):
231             name = fdefn.GetFieldDefn(i).GetName()
232             if name == fieldname:
233                 return i
234             elif case_insensitive_idx < 0 and name.lower() == fieldname.lower():
235                 case_insensitive_idx = i
236         return case_insensitive_idx
237 
238     # This makes it possible to fetch fields in the form "feature.area".
239     # This has some risk of name collisions.
240     def __getattr__(self, key):
241         """Returns the values of fields by the given name"""
242         if key == 'this':
243             return self.__dict__[key]
244 
245         idx = self._getfieldindex(key)
246         if idx < 0:
247             idx = self.GetGeomFieldIndex(key)
248             if idx < 0:
249                 raise AttributeError(key)
250             else:
251                 return self.GetGeomFieldRef(idx)
252         else:
253             return self.GetField(idx)
254 
255     # This makes it possible to set fields in the form "feature.area".
256     # This has some risk of name collisions.
257     def __setattr__(self, key, value):
258         """Set the values of fields by the given name"""
259         if key == 'this' or key == 'thisown':
260             self.__dict__[key] = value
261         else:
262             idx = self._getfieldindex(key)
263             if idx != -1:
264                 self.SetField2(idx, value)
265             else:
266                 idx = self.GetGeomFieldIndex(key)
267                 if idx != -1:
268                     self.SetGeomField(idx, value)
269                 else:
270                     self.__dict__[key] = value
271 
272     # This makes it possible to fetch fields in the form "feature['area']".
273     def __getitem__(self, key):
274         """Returns the values of fields by the given name / field_index"""
275         if isinstance(key, str):
276             fld_index = self._getfieldindex(key)
277         else:
278             fld_index = key
279             if key == self.GetFieldCount():
280                 raise IndexError
281         if fld_index < 0:
282             if isinstance(key, str):
283                 fld_index = self.GetGeomFieldIndex(key)
284             if fld_index < 0:
285                 raise KeyError("Illegal field requested in GetField()")
286             else:
287                 return self.GetGeomFieldRef(fld_index)
288         else:
289             return self.GetField(fld_index)
290 
291     # This makes it possible to set fields in the form "feature['area'] = 123".
292     def __setitem__(self, key, value):
293         """Returns the value of a field by field name / index"""
294         if isinstance(key, str):
295             fld_index = self._getfieldindex(key)
296         else:
297             fld_index = key
298             if key == self.GetFieldCount():
299                 raise IndexError
300         if fld_index < 0:
301             if isinstance(key, str):
302                 fld_index = self.GetGeomFieldIndex(key)
303             if fld_index < 0:
304                 raise KeyError("Illegal field requested in SetField()")
305             else:
306                 return self.SetGeomField(fld_index, value)
307         else:
308             return self.SetField2(fld_index, value)
309 
310     def GetField(self, fld_index):
311         if isinstance(fld_index, str):
312             fld_index = self._getfieldindex(fld_index)
313         if (fld_index < 0) or (fld_index > self.GetFieldCount()):
314             raise KeyError("Illegal field requested in GetField()")
315         if not (self.IsFieldSet(fld_index)) or self.IsFieldNull(fld_index):
316             return None
317         fld_type = self.GetFieldType(fld_index)
318         if fld_type == OFTInteger:
319             return self.GetFieldAsInteger(fld_index)
320         if fld_type == OFTInteger64:
321             return self.GetFieldAsInteger64(fld_index)
322         if fld_type == OFTReal:
323             return self.GetFieldAsDouble(fld_index)
324         if fld_type == OFTStringList:
325             return self.GetFieldAsStringList(fld_index)
326         if fld_type == OFTIntegerList:
327             return self.GetFieldAsIntegerList(fld_index)
328         if fld_type == OFTInteger64List:
329             return self.GetFieldAsInteger64List(fld_index)
330         if fld_type == OFTRealList:
331             return self.GetFieldAsDoubleList(fld_index)
332         ## if fld_type == OFTDateTime or fld_type == OFTDate or fld_type == OFTTime:
333         #     return self.GetFieldAsDate(fld_index)
334         # default to returning as a string.  Should we add more types?
335         try:
336             return self.GetFieldAsString(fld_index)
337         except:
338             # For Python3 on non-UTF8 strings
339             return self.GetFieldAsBinary(fld_index)
340 
341     # With several override, SWIG cannot dispatch automatically unicode strings
342     # to the right implementation, so we have to do it at hand
343     def SetField(self, *args):
344         """
345         SetField(self, int id, char value)
346         SetField(self, char name, char value)
347         SetField(self, int id, int value)
348         SetField(self, char name, int value)
349         SetField(self, int id, double value)
350         SetField(self, char name, double value)
351         SetField(self, int id, int year, int month, int day, int hour, int minute,
352             int second, int tzflag)
353         SetField(self, char name, int year, int month, int day, int hour,
354             int minute, int second, int tzflag)
355         """
356 
357         if len(args) == 2 and args[1] is None:
358             return _ogr.Feature_SetFieldNull(self, args[0])
359 
360         if len(args) == 2 and (type(args[1]) == type(1) or type(args[1]) == type(12345678901234)):
361             fld_index = args[0]
362             if isinstance(fld_index, str):
363                 fld_index = self._getfieldindex(fld_index)
364             return _ogr.Feature_SetFieldInteger64(self, fld_index, args[1])
365 
366 
367         if len(args) == 2 and isinstance(args[1], str):
368             fld_index = args[0]
369             if isinstance(fld_index, str):
370                 fld_index = self._getfieldindex(fld_index)
371             return _ogr.Feature_SetFieldString(self, fld_index, args[1])
372 
373         return _ogr.Feature_SetField(self, *args)
374 
375     def SetField2(self, fld_index, value):
376         if isinstance(fld_index, str):
377             fld_index = self._getfieldindex(fld_index)
378         if (fld_index < 0) or (fld_index > self.GetFieldCount()):
379             raise KeyError("Illegal field requested in SetField2()")
380 
381         if value is None:
382             self.SetFieldNull(fld_index)
383             return
384 
385         if isinstance(value, list):
386             if not value:
387                 self.SetFieldNull(fld_index)
388                 return
389             if isinstance(value[0], type(1)) or isinstance(value[0], type(12345678901234)):
390                 self.SetFieldInteger64List(fld_index, value)
391                 return
392             elif isinstance(value[0], float):
393                 self.SetFieldDoubleList(fld_index, value)
394                 return
395             elif isinstance(value[0], str):
396                 self.SetFieldStringList(fld_index, value)
397                 return
398             else:
399                 raise TypeError('Unsupported type of list in SetField2(). Type of element is %s' % str(type(value[0])))
400 
401         try:
402             self.SetField(fld_index, value)
403         except:
404             self.SetField(fld_index, str(value))
405         return
406 
407     def keys(self):
408         names = []
409         for i in range(self.GetFieldCount()):
410             fieldname = self.GetFieldDefnRef(i).GetName()
411             names.append(fieldname)
412         return names
413 
414     def items(self):
415         keys = self.keys()
416         output = {}
417         for key in keys:
418             output[key] = self.GetField(key)
419         return output
420     def geometry(self):
421         return self.GetGeometryRef()
422 
423     def ExportToJson(self, as_object=False, options=None):
424         """Exports a GeoJSON object which represents the Feature. The
425            as_object parameter determines whether the returned value
426            should be a Python object instead of a string. Defaults to False.
427            The options parameter is passed to Geometry.ExportToJson()"""
428 
429         try:
430             import simplejson
431         except ImportError:
432             try:
433                 import json as simplejson
434             except ImportError:
435                 raise ImportError("Unable to import simplejson or json, needed for ExportToJson.")
436 
437         geom = self.GetGeometryRef()
438         if geom is not None:
439             if options is None:
440                 options = []
441             geom_json_string = geom.ExportToJson(options=options)
442             geom_json_object = simplejson.loads(geom_json_string)
443         else:
444             geom_json_object = None
445 
446         output = {'type':'Feature',
447                    'geometry': geom_json_object,
448                    'properties': {}
449                   }
450 
451         fid = self.GetFID()
452         if fid != NullFID:
453             output['id'] = fid
454 
455         for key in self.keys():
456             fld_defn = self.GetFieldDefnRef(self.GetFieldIndex(key))
457             if fld_defn.GetType() == _ogr.OFTInteger and fld_defn.GetSubType() == _ogr.OFSTBoolean:
458                 output['properties'][key] = bool(self.GetField(key))
459             else:
460                 output['properties'][key] = self.GetField(key)
461 
462         if not as_object:
463             output = simplejson.dumps(output)
464 
465         return output
466 
467 
468 %}
469 
470 }
471 
472 %extend OGRGeometryShadow {
473 %pythoncode %{
474   def Destroy(self):
475     self.__swig_destroy__(self)
476     self.thisown = 0
477 
478   def __str__(self):
479     return self.ExportToWkt()
480 
481   def __copy__(self):
482     return self.Clone()
483 
484   def __deepcopy__(self, memo):
485     g = self.Clone()
486     srs = self.GetSpatialReference()
487     if srs:
488         g.AssignSpatialReference(srs.Clone())
489     return g
490 
491   def __reduce__(self):
492     return (self.__class__, (), self.ExportToWkb())
493 
494   def __setstate__(self, state):
495       result = CreateGeometryFromWkb(state)
496       self.this = result.this
497 
498   def __iter__(self):
499       for i in range(self.GetGeometryCount()):
500           yield self.GetGeometryRef(i)
501 
502 %}
503 }
504 
505 
506 %extend OGRFieldDefnShadow {
507 %pythoncode {
508     width = property(GetWidth, SetWidth)
509     type = property(GetType, SetType)
510     precision = property(GetPrecision, SetPrecision)
511     name = property(GetName, SetName)
512     justify = property(GetJustify, SetJustify)
513 }
514 }
515 
516 %extend OGRGeomFieldDefnShadow {
517 %pythoncode {
518     type = property(GetType, SetType)
519     name = property(GetName, SetName)
520     srs = property(GetSpatialRef, SetSpatialRef)
521 }
522 }
523 
524 %extend OGRFeatureDefnShadow {
525 %pythoncode {
526   def Destroy(self):
527     "Once called, self has effectively been destroyed.  Do not access. For backwards compatibility only"
528     _ogr.delete_FeatureDefn(self)
529     self.thisown = 0
530 
531 }
532 }
533 
534 %extend OGRFieldDefnShadow {
535 %pythoncode %{
536   def Destroy(self):
537     "Once called, self has effectively been destroyed.  Do not access. For backwards compatibility only"
538     _ogr.delete_FieldDefn(self)
539     self.thisown = 0
540 %}
541 }
542 
543 %import typemaps_python.i
544 
545 #ifndef FROM_GDAL_I
546 %include "callback.i"
547 
548 
549 %extend GDALMajorObjectShadow {
550 %pythoncode %{
551   def GetMetadata(self, domain=''):
552     if domain[:4] == 'xml:':
553       return self.GetMetadata_List(domain)
554     return self.GetMetadata_Dict(domain)
555 %}
556 }
557 #endif
558 
559 #ifdef no_longer_defined_since_it_breaks_py2exe_pyinstaller_ticket_6364
560 %pythoncode %{
561 
562 # Backup original dictionary before doing anything else
563 _initial_dict = globals().copy()
564 
565 @property
566 def wkb25Bit(module):
567     import warnings
568     warnings.warn("ogr.wkb25DBit deprecated: use ogr.GT_Flatten(), ogr.GT_HasZ() or ogr.GT_SetZ() instead", DeprecationWarning)
569     return module._initial_dict['wkb25DBit']
570 
571 @property
572 def wkb25DBit(module):
573     import warnings
574     warnings.warn("ogr.wkb25DBit deprecated: use ogr.GT_Flatten(), ogr.GT_HasZ() or ogr.GT_SetZ() instead", DeprecationWarning)
575     return module._initial_dict['wkb25DBit']
576 
577 # Inspired from http://www.dr-josiah.com/2013/12/properties-on-python-modules.html
578 class _Module(object):
579     def __init__(self):
580         self.__dict__ = globals()
581         self._initial_dict = _initial_dict
582 
583         # Transfer properties from the object to the Class
584         for k, v in list(self.__dict__.items()):
585             if isinstance(v, property):
586                 setattr(self.__class__, k, v)
587                 #del self.__dict__[k]
588 
589         # Replace original module by our object
590         import sys
591         self._original_module = sys.modules[self.__name__]
592         sys.modules[self.__name__] = self
593 
594 # Custom help() replacement to display the help of the original module
595 # instead of the one of our instance object
596 class _MyHelper(object):
597 
598     def __init__(self, module):
599         self.module = module
600         self.original_help = help
601 
602         # Replace builtin help by ours
603         import builtins
604         builtins.help = self
605 
606     def __repr__(self):
607         return self.original_help.__repr__()
608 
609     def __call__(self, *args, **kwds):
610 
611         if args == (self.module,):
612             import sys
613 
614             # Restore original module before calling help() otherwise
615             # we don't get methods or classes mentioned
616             sys.modules[self.module.__name__] = self.module._original_module
617 
618             ret = self.original_help(self.module._original_module, **kwds)
619 
620             # Reinstall our module
621             sys.modules[self.module.__name__] = self.module
622 
623             return ret
624         elif args == (self,):
625             return self.original_help(self.original_help, **kwds)
626         else:
627             return self.original_help(*args, **kwds)
628 
629 _MyHelper(_Module())
630 del _MyHelper
631 del _Module
632 
633 %}
634 #endif
635