18
18
import array
19
19
import tempfile
20
20
import itertools
21
+ from datetime import date
21
22
22
23
#
23
24
# Constants for shape types
36
37
MULTIPOINTM = 28
37
38
MULTIPATCH = 31
38
39
40
+ MISSING = [None ,'' ]
41
+
39
42
PYTHON3 = sys .version_info [0 ] == 3
40
43
41
44
if PYTHON3 :
@@ -492,14 +495,11 @@ def __record(self):
492
495
# deleted record
493
496
return None
494
497
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 ):
497
499
if name == 'DeletionFlag' :
498
500
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.
503
503
value = value .replace (b ('\0 ' ), b ('' )).strip ()
504
504
value = value .replace (b ('*' ), b ('' )) # QGIS NULL is all '*' chars
505
505
if value == b ('' ):
@@ -517,18 +517,28 @@ def __record(self):
517
517
#not parseable as int, set to None
518
518
value = None
519
519
elif typ == b ('D' ):
520
+ # date: 8 bytes - date stored as a string in the format YYYYMMDD.
520
521
if value .count (b ('0' )) == len (value ): # QGIS NULL is all '0' chars
521
522
value = None
522
523
else :
523
524
try :
524
525
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 )
526
527
except :
527
528
value = value .strip ()
528
529
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 ('?' )
531
540
else :
541
+ # anything else is forced to string/unicode
532
542
value = u (value )
533
543
value = value .strip ()
534
544
record .append (value )
@@ -920,11 +930,30 @@ def __dbfRecords(self):
920
930
for (fieldName , fieldType , size , dec ), value in zip (self .fields , record ):
921
931
fieldType = fieldType .upper ()
922
932
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 ''." )
925
949
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 ()
927
955
else :
956
+ # anything else is forced to string
928
957
value = str (value )[:size ].ljust (size )
929
958
if len (value ) != size :
930
959
raise ShapefileException (
@@ -982,6 +1011,12 @@ def poly(self, parts=[], shapeType=POLYGON, partTypes=[]):
982
1011
983
1012
def field (self , name , fieldType = "C" , size = "50" , decimal = 0 ):
984
1013
"""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
985
1020
self .fields .append ((name , fieldType , size , decimal ))
986
1021
987
1022
def record (self , * recordList , ** recordDict ):
0 commit comments