1818import array
1919import tempfile
2020import itertools
21+ from datetime import date
2122
2223#
2324# Constants for shape types
3637MULTIPOINTM = 28
3738MULTIPATCH = 31
3839
40+ MISSING = [None ,'' ]
41+
3942PYTHON3 = sys .version_info [0 ] == 3
4043
4144if PYTHON3 :
@@ -492,14 +495,11 @@ def __record(self):
492495 # deleted record
493496 return None
494497 record = []
495- for (name , typ , size , deci ), value in zip (self .fields ,
496- recordContents ):
498+ for (name , typ , size , deci ), value in zip (self .fields , recordContents ):
497499 if name == 'DeletionFlag' :
498500 continue
499- elif not value .strip ():
500- record .append (value )
501- continue
502- elif typ == "N" :
501+ elif typ in ("N" ,"F" ):
502+ # numeric or float: number stored as a string, right justified, and padded with blanks to the width of the field.
503503 value = value .replace (b ('\0 ' ), b ('' )).strip ()
504504 value = value .replace (b ('*' ), b ('' )) # QGIS NULL is all '*' chars
505505 if value == b ('' ):
@@ -517,18 +517,28 @@ def __record(self):
517517 #not parseable as int, set to None
518518 value = None
519519 elif typ == b ('D' ):
520+ # date: 8 bytes - date stored as a string in the format YYYYMMDD.
520521 if value .count (b ('0' )) == len (value ): # QGIS NULL is all '0' chars
521522 value = None
522523 else :
523524 try :
524525 y , m , d = int (value [:4 ]), int (value [4 :6 ]), int (value [6 :8 ])
525- value = [ y , m , d ]
526+ value = date ( y , m , d )
526527 except :
527528 value = value .strip ()
528529 elif typ == b ('L' ):
529- value = (value in b ('YyTt' ) and b ('T' )) or \
530- (value in b ('NnFf' ) and b ('F' )) or b ('?' )
530+ # logical: 1 byte - initialized to 0x20 (space) otherwise T or F.
531+ if value == " " :
532+ value = None # space means missing or not yet set
533+ else :
534+ if value in b ('YyTt1' ):
535+ value = True
536+ elif value in b ('NnFf0' ):
537+ value = False
538+ else :
539+ value = b ('?' )
531540 else :
541+ # anything else is forced to string/unicode
532542 value = u (value )
533543 value = value .strip ()
534544 record .append (value )
@@ -920,11 +930,30 @@ def __dbfRecords(self):
920930 for (fieldName , fieldType , size , dec ), value in zip (self .fields , record ):
921931 fieldType = fieldType .upper ()
922932 size = int (size )
923- if fieldType .upper () == "N" :
924- value = str (value ).rjust (size )
933+ if fieldType in ("N" ,"F" ):
934+ # numeric or float: number stored as a string, right justified, and padded with blanks to the width of the field.
935+ if value in MISSING :
936+ value = str ("*" * size ) # QGIS NULL
937+ else :
938+ value = str (value ).rjust (size )
939+ elif fieldType == "D" :
940+ # date: 8 bytes - date stored as a string in the format YYYYMMDD.
941+ if isinstance (value , date ):
942+ value = value .strftime ("%Y%m%d" )
943+ elif isinstance (value , list ) and len (value ) == 3 :
944+ value = date (* value ).strftime ("%Y%m%d" )
945+ elif value in MISSING :
946+ value = b ('0' ) * 8 # QGIS NULL for date type
947+ else :
948+ raise ShapefileException ("Date values must be either a datetime.date object, a list, or a missing value of None or ''." )
925949 elif fieldType == 'L' :
926- value = str (value )[0 ].upper ()
950+ # logical: 1 byte - initialized to 0x20 (space) otherwise T or F.
951+ if value in MISSING :
952+ value = str (' ' ) # missing is set to space
953+ else :
954+ value = str (value )[0 ].upper ()
927955 else :
956+ # anything else is forced to string
928957 value = str (value )[:size ].ljust (size )
929958 if len (value ) != size :
930959 raise ShapefileException (
@@ -982,6 +1011,12 @@ def poly(self, parts=[], shapeType=POLYGON, partTypes=[]):
9821011
9831012 def field (self , name , fieldType = "C" , size = "50" , decimal = 0 ):
9841013 """Adds a dbf field descriptor to the shapefile."""
1014+ if fieldType == "D" :
1015+ size = "8"
1016+ decimal = 0
1017+ elif fieldType == "L" :
1018+ size = "1"
1019+ decimal = 0
9851020 self .fields .append ((name , fieldType , size , decimal ))
9861021
9871022 def record (self , * recordList , ** recordDict ):
0 commit comments