Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/contrib/gis/utils/ogrinspect.py: 4%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

93 statements  

1""" 

2This module is for inspecting OGR data sources and generating either 

3models for GeoDjango and/or mapping dictionaries for use with the 

4`LayerMapping` utility. 

5""" 

6 

7from django.contrib.gis.gdal import DataSource 

8from django.contrib.gis.gdal.field import ( 

9 OFTDate, 

10 OFTDateTime, 

11 OFTInteger, 

12 OFTInteger64, 

13 OFTReal, 

14 OFTString, 

15 OFTTime, 

16) 

17 

18 

19def mapping(data_source, geom_name="geom", layer_key=0, multi_geom=False): 

20 """ 

21 Given a DataSource, generate a dictionary that may be used 

22 for invoking the LayerMapping utility. 

23 

24 Keyword Arguments: 

25 `geom_name` => The name of the geometry field to use for the model. 

26 

27 `layer_key` => The key for specifying which layer in the DataSource to use; 

28 defaults to 0 (the first layer). May be an integer index or a string 

29 identifier for the layer. 

30 

31 `multi_geom` => Boolean (default: False) - specify as multigeometry. 

32 """ 

33 if isinstance(data_source, str): 

34 # Instantiating the DataSource from the string. 

35 data_source = DataSource(data_source) 

36 elif isinstance(data_source, DataSource): 

37 pass 

38 else: 

39 raise TypeError( 

40 "Data source parameter must be a string or a DataSource object." 

41 ) 

42 

43 # Creating the dictionary. 

44 _mapping = {} 

45 

46 # Generating the field name for each field in the layer. 

47 for field in data_source[layer_key].fields: 

48 mfield = field.lower() 

49 if mfield[-1:] == "_": 

50 mfield += "field" 

51 _mapping[mfield] = field 

52 gtype = data_source[layer_key].geom_type 

53 if multi_geom: 

54 gtype.to_multi() 

55 _mapping[geom_name] = str(gtype).upper() 

56 return _mapping 

57 

58 

59def ogrinspect(*args, **kwargs): 

60 """ 

61 Given a data source (either a string or a DataSource object) and a string 

62 model name this function will generate a GeoDjango model. 

63 

64 Usage: 

65 

66 >>> from django.contrib.gis.utils import ogrinspect 

67 >>> ogrinspect('/path/to/shapefile.shp','NewModel') 

68 

69 ...will print model definition to stout 

70 

71 or put this in a Python script and use to redirect the output to a new 

72 model like: 

73 

74 $ python generate_model.py > myapp/models.py 

75 

76 # generate_model.py 

77 from django.contrib.gis.utils import ogrinspect 

78 shp_file = 'data/mapping_hacks/world_borders.shp' 

79 model_name = 'WorldBorders' 

80 

81 print(ogrinspect(shp_file, model_name, multi_geom=True, srid=4326, 

82 geom_name='shapes', blank=True)) 

83 

84 Required Arguments 

85 `datasource` => string or DataSource object to file pointer 

86 

87 `model name` => string of name of new model class to create 

88 

89 Optional Keyword Arguments 

90 `geom_name` => For specifying the model name for the Geometry Field. 

91 Otherwise will default to `geom` 

92 

93 `layer_key` => The key for specifying which layer in the DataSource to use; 

94 defaults to 0 (the first layer). May be an integer index or a string 

95 identifier for the layer. 

96 

97 `srid` => The SRID to use for the Geometry Field. If it can be determined, 

98 the SRID of the datasource is used. 

99 

100 `multi_geom` => Boolean (default: False) - specify as multigeometry. 

101 

102 `name_field` => String - specifies a field name to return for the 

103 __str__() method (which will be generated if specified). 

104 

105 `imports` => Boolean (default: True) - set to False to omit the 

106 `from django.contrib.gis.db import models` code from the 

107 autogenerated models thus avoiding duplicated imports when building 

108 more than one model by batching ogrinspect() 

109 

110 `decimal` => Boolean or sequence (default: False). When set to True 

111 all generated model fields corresponding to the `OFTReal` type will 

112 be `DecimalField` instead of `FloatField`. A sequence of specific 

113 field names to generate as `DecimalField` may also be used. 

114 

115 `blank` => Boolean or sequence (default: False). When set to True all 

116 generated model fields will have `blank=True`. If the user wants to 

117 give specific fields to have blank, then a list/tuple of OGR field 

118 names may be used. 

119 

120 `null` => Boolean (default: False) - When set to True all generated 

121 model fields will have `null=True`. If the user wants to specify 

122 give specific fields to have null, then a list/tuple of OGR field 

123 names may be used. 

124 

125 Note: Call the _ogrinspect() helper to do the heavy lifting. 

126 """ 

127 return "\n".join(_ogrinspect(*args, **kwargs)) 

128 

129 

130def _ogrinspect( 

131 data_source, 

132 model_name, 

133 geom_name="geom", 

134 layer_key=0, 

135 srid=None, 

136 multi_geom=False, 

137 name_field=None, 

138 imports=True, 

139 decimal=False, 

140 blank=False, 

141 null=False, 

142): 

143 """ 

144 Helper routine for `ogrinspect` that generates GeoDjango models corresponding 

145 to the given data source. See the `ogrinspect` docstring for more details. 

146 """ 

147 # Getting the DataSource 

148 if isinstance(data_source, str): 

149 data_source = DataSource(data_source) 

150 elif isinstance(data_source, DataSource): 

151 pass 

152 else: 

153 raise TypeError( 

154 "Data source parameter must be a string or a DataSource object." 

155 ) 

156 

157 # Getting the layer corresponding to the layer key and getting 

158 # a string listing of all OGR fields in the Layer. 

159 layer = data_source[layer_key] 

160 ogr_fields = layer.fields 

161 

162 # Creating lists from the `null`, `blank`, and `decimal` 

163 # keyword arguments. 

164 def process_kwarg(kwarg): 

165 if isinstance(kwarg, (list, tuple)): 

166 return [s.lower() for s in kwarg] 

167 elif kwarg: 

168 return [s.lower() for s in ogr_fields] 

169 else: 

170 return [] 

171 

172 null_fields = process_kwarg(null) 

173 blank_fields = process_kwarg(blank) 

174 decimal_fields = process_kwarg(decimal) 

175 

176 # Gets the `null` and `blank` keywords for the given field name. 

177 def get_kwargs_str(field_name): 

178 kwlist = [] 

179 if field_name.lower() in null_fields: 

180 kwlist.append("null=True") 

181 if field_name.lower() in blank_fields: 

182 kwlist.append("blank=True") 

183 if kwlist: 

184 return ", " + ", ".join(kwlist) 

185 else: 

186 return "" 

187 

188 # For those wishing to disable the imports. 

189 if imports: 

190 yield "# This is an auto-generated Django model module created by ogrinspect." 

191 yield "from django.contrib.gis.db import models" 

192 yield "" 

193 yield "" 

194 

195 yield "class %s(models.Model):" % model_name 

196 

197 for field_name, width, precision, field_type in zip( 

198 ogr_fields, layer.field_widths, layer.field_precisions, layer.field_types 

199 ): 

200 # The model field name. 

201 mfield = field_name.lower() 

202 if mfield[-1:] == "_": 

203 mfield += "field" 

204 

205 # Getting the keyword args string. 

206 kwargs_str = get_kwargs_str(field_name) 

207 

208 if field_type is OFTReal: 

209 # By default OFTReals are mapped to `FloatField`, however, they 

210 # may also be mapped to `DecimalField` if specified in the 

211 # `decimal` keyword. 

212 if field_name.lower() in decimal_fields: 

213 yield ( 

214 " %s = models.DecimalField(max_digits=%d, decimal_places=%d%s)" 

215 ) % ( 

216 mfield, 

217 width, 

218 precision, 

219 kwargs_str, 

220 ) 

221 else: 

222 yield " %s = models.FloatField(%s)" % (mfield, kwargs_str[2:]) 

223 elif field_type is OFTInteger: 

224 yield " %s = models.IntegerField(%s)" % (mfield, kwargs_str[2:]) 

225 elif field_type is OFTInteger64: 

226 yield " %s = models.BigIntegerField(%s)" % (mfield, kwargs_str[2:]) 

227 elif field_type is OFTString: 

228 yield " %s = models.CharField(max_length=%s%s)" % ( 

229 mfield, 

230 width, 

231 kwargs_str, 

232 ) 

233 elif field_type is OFTDate: 

234 yield " %s = models.DateField(%s)" % (mfield, kwargs_str[2:]) 

235 elif field_type is OFTDateTime: 

236 yield " %s = models.DateTimeField(%s)" % (mfield, kwargs_str[2:]) 

237 elif field_type is OFTTime: 

238 yield " %s = models.TimeField(%s)" % (mfield, kwargs_str[2:]) 

239 else: 

240 raise TypeError("Unknown field type %s in %s" % (field_type, mfield)) 

241 

242 # TODO: Autodetection of multigeometry types (see #7218). 

243 gtype = layer.geom_type 

244 if multi_geom: 

245 gtype.to_multi() 

246 geom_field = gtype.django 

247 

248 # Setting up the SRID keyword string. 

249 if srid is None: 

250 if layer.srs is None: 

251 srid_str = "srid=-1" 

252 else: 

253 srid = layer.srs.srid 

254 if srid is None: 

255 srid_str = "srid=-1" 

256 elif srid == 4326: 

257 # WGS84 is already the default. 

258 srid_str = "" 

259 else: 

260 srid_str = "srid=%s" % srid 

261 else: 

262 srid_str = "srid=%s" % srid 

263 

264 yield " %s = models.%s(%s)" % (geom_name, geom_field, srid_str) 

265 

266 if name_field: 

267 yield "" 

268 yield " def __str__(self): return self.%s" % name_field