Package madrona :: Package loadshp :: Module forms
[hide private]

Source Code for Module madrona.loadshp.forms

  1  import os 
  2  import zipfile 
  3  import tempfile 
  4  import datetime 
  5  from django import forms 
  6  from django.forms.util import ValidationError 
  7  from django.utils.translation import ugettext_lazy as _ 
  8  from django.conf import settings 
  9  from django.contrib.gis import gdal 
 10   
 11  #http://docs.djangoproject.com/en/dev/topics/http/file-uploads/ 
 12  #http://www.neverfriday.com/sweetfriday/2008/09/-a-long-time-ago.html  
 13   
14 -class UploadForm(forms.Form):
15 16 file_obj = forms.FileField(label='Upload a Zipped Shapefile') 17 multi_feature = True 18 supported_geomtypes = ['Polygon','Point','Line'] 19 enforce_4326 = True 20
21 - def clean_file_obj(self):
22 f = self.cleaned_data['file_obj'] 23 valid_shp, error = self.validate(f) 24 if not valid_shp: 25 raise ValidationError("A problem occured: %s" % error)
26
27 - def handle(self,filefield_data,user):
28 """ Upload the file data, in chunks, to the media/upload/loadshp. 29 Then unpack it, read the features and return them 30 """ 31 # ensure the upload directory exists 32 upload_dir = os.path.join(settings.MEDIA_ROOT,'upload','loadshp',user.username) 33 if not os.path.exists(upload_dir): 34 os.makedirs(upload_dir) 35 36 # contruct the full filepath and filename 37 downloaded_file = os.path.normpath(os.path.join(upload_dir, filefield_data.name)) 38 39 # if we've already got an upload with the same name, append the daymonthyear_minute 40 if os.path.exists(downloaded_file): 41 name, ext = os.path.splitext(downloaded_file) 42 append = datetime.datetime.now().strftime('%Y%m%d_%H%M%S') 43 downloaded_file = '%s_%s%s' % (name,append,ext) 44 45 # write the zip archive to final location 46 self.write_file(downloaded_file,filefield_data) 47 48 #unpack the files 49 zfile = zipfile.ZipFile(downloaded_file) 50 tmp_dir = tempfile.gettempdir() 51 for info in zfile.infolist(): 52 data = zfile.read(info.filename) 53 shp_part = '%s%s%s' % (tmp_dir,os.path.sep,info.filename) 54 fout = open(shp_part, "wb") 55 fout.write(data) 56 fout.close() 57 58 # get the datasource name without extension 59 ds_name = os.path.splitext(zfile.namelist()[0])[0] 60 ds = gdal.DataSource('%s%s%s.shp' % (tmp_dir,os.path.sep,ds_name)) 61 return ds[0]
62
63 - def write_file(self,filename,filefield_data):
64 destination = open(filename, 'wb+') 65 for chunk in filefield_data.chunks(): 66 destination.write(chunk) 67 destination.close()
68
69 - def check_zip_contents(self, ext, zip_file):
70 if not True in [info.filename.endswith(ext) for info in zip_file.infolist()]: 71 return False 72 return True
73
74 - def validate(self,filefield_data):
75 """ Validate the uploaded, zipped shapefile by unpacking to a temporary sandbox. 76 """ 77 # create a temporary file to write the zip archive to 78 tmp = tempfile.NamedTemporaryFile(suffix='.zip', mode='w', delete=False) 79 80 # write zip to tmp sandbox 81 self.write_file(tmp.name,filefield_data) 82 83 if not zipfile.is_zipfile(tmp.name): 84 return False, 'That file is not a valid Zip Archive' 85 86 # create zip object 87 zfile = zipfile.ZipFile(tmp.name) 88 89 # ensure proper file contents by extensions inside 90 if not self.check_zip_contents('shp', zfile): 91 return False, 'Found Zip Archive but no file with a .shp extension found inside.' 92 elif not self.check_zip_contents('dbf', zfile): 93 return False, 'You must supply a .dbf file with the Shapefile to supply attribute data.' 94 elif not self.check_zip_contents('shx', zfile): 95 return False, 'You must supply a .shx file for the Shapefile to have a valid index.' 96 97 # unpack contents into tmp directory 98 tmp_dir = tempfile.gettempdir() 99 for info in zfile.infolist(): 100 data = zfile.read(info.filename) 101 shp_part = '%s%s%s' % (tmp_dir,os.path.sep,info.filename) 102 fout = open(shp_part, "wb") 103 fout.write(data) 104 fout.close() 105 106 # get the datasource name without extension 107 ds_name = os.path.splitext(zfile.namelist()[0])[0] 108 109 # ogr needs the full path to the unpacked 'file.shp' 110 ds = gdal.DataSource('%s%s%s.shp' % (tmp_dir,os.path.sep,ds_name)) 111 112 # shapefiles have just one layer, so grab the first... 113 layer = ds[0] 114 115 # one way of testing a sane shapefile... 116 # further tests should be able to be plugged in here... 117 if layer.test_capability('RandomRead'): 118 if str(ds.driver) != 'ESRI Shapefile': 119 return False, "Sorry, we've experienced a problem on our server. Please try again later." 120 else: 121 return False, 'Cannot read the shapefile, data is corrupted inside the zip, please try to upload again' 122 123 # Must have a prj or have data in the -180,-90,180,90 window (assumed to be latlong) 124 if not self.check_zip_contents('prj', zfile) and (layer.extent.min_x < -180.0 or layer.extent.max_x > 180.0 \ 125 or layer.extent.min_y < -90.0 or layer.extent.max_y > 90.0): 126 return False, 'You must supply a .prj file with the Shapefile to indicate the projection.' 127 else: 128 g = layer[0].geom 129 if g.srs: 130 g.transform_to(4326) 131 ext = g.envelope 132 if ext.min_x < -180.0 or ext.max_x > 180.0 or ext.min_y < -90.0 or ext.max_y > 90.0: 133 return False, 'There was an error reprojecting your geometry. You must supply a .prj file or reproject to WGS84.' 134 135 if layer.geom_type.name not in self.supported_geomtypes: 136 return False, "Sorry, %s geometries are not supported. Try uploading a zipped shapefile with %s geometries" % \ 137 (layer.geom_type.name, ', '.join(self.supported_geomtypes)) 138 139 if not self.multi_feature and layer.num_feat != 1: 140 return False, "We can only support shapefiles with a single feature" 141 142 return True, "Shapefile is good to go"
143