def _clipVectorLayer(theLayer, theExtent, theExtraKeywords=None): """Clip a Hazard or Exposure layer to the extents of the current view frame. The layer must be a vector layer or an exception will be thrown. The output layer will always be in WGS84/Geographic. Args: * theLayer - a valid QGIS vector layer in EPSG:4326 * theExtent - an array representing the exposure layer extents in the form [xmin, ymin, xmax, ymax]. It is assumed that the coordinates are in EPSG:4326 although currently no checks are made to enforce this. * theExtraKeywords - any additional keywords over and above the original keywords that should be associated with the cliplayer. Returns: Path to the output clipped layer (placed in the system temp dir). Raises: None """ if not theLayer or not theExtent: myMessage = tr('Layer or Extent passed to clip is None.') raise InvalidParameterException(myMessage) if theLayer.type() != QgsMapLayer.VectorLayer: myMessage = tr('Expected a vector layer but received a %s.' % str(theLayer.type())) raise InvalidParameterException(myMessage) myHandle, myFilename = tempfile.mkstemp('.shp', 'clip_', getTempDir()) # Ensure the file is deleted before we try to write to it # fixes windows specific issue where you get a message like this # ERROR 1: c:\temp\inasafe\clip_jpxjnt.shp is not a directory. # This is because mkstemp creates the file handle and leaves # the file open. os.close(myHandle) os.remove(myFilename) # Get the clip extents in the layer's native CRS myGeoCrs = QgsCoordinateReferenceSystem() myGeoCrs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId) myXForm = QgsCoordinateTransform(myGeoCrs, theLayer.crs()) myRect = QgsRectangle(theExtent[0], theExtent[1], theExtent[2], theExtent[3]) myProjectedExtent = myXForm.transformBoundingBox(myRect) # Get vector layer myProvider = theLayer.dataProvider() if myProvider is None: myMessage = tr('Could not obtain data provider from ' 'layer "%s"' % theLayer.source()) raise Exception(myMessage) # get the layer field list, select by our extent then write to disk # .. todo:: FIXME - for different geometry types we should implement # different clipping behaviour e.g. reject polygons that # intersect the edge of the bbox. Tim myAttributes = myProvider.attributeIndexes() myFetchGeometryFlag = True myUseIntersectFlag = True myProvider.select(myAttributes, myProjectedExtent, myFetchGeometryFlag, myUseIntersectFlag) myFieldList = myProvider.fields() myWriter = QgsVectorFileWriter(myFilename, 'UTF-8', myFieldList, theLayer.wkbType(), myGeoCrs, 'ESRI Shapefile') if myWriter.hasError() != QgsVectorFileWriter.NoError: myMessage = tr('Error when creating shapefile: <br>Filename:' '%s<br>Error: %s' % (myFilename, myWriter.hasError())) raise Exception(myMessage) # Reverse the coordinate xform now so that we can convert # geometries from layer crs to geocrs. myXForm = QgsCoordinateTransform(theLayer.crs(), myGeoCrs) # Retrieve every feature with its geometry and attributes myFeature = QgsFeature() myCount = 0 while myProvider.nextFeature(myFeature): myGeometry = myFeature.geometry() # Loop through the parts adding them to the output file # we ALWAYS write out single part features myGeometryList = explodeMultiPartGeometry(myGeometry) for myPart in myGeometryList: myPart.transform(myXForm) myFeature.setGeometry(myPart) myWriter.addFeature(myFeature) myCount += 1 del myWriter # Flush to disk if myCount < 1: myMessage = tr('No features fall within the clip extents. ' 'Try panning / zooming to an area containing data ' 'and then try to run your analysis again.') raise NoFeaturesInExtentException(myMessage) myKeywordIO = ISKeywordIO() myKeywordIO.copyKeywords(theLayer, myFilename, theExtraKeywords=theExtraKeywords) return myFilename # Filename of created file
myMessage = tr('<p>Error while executing the following shell command:' '</p><pre>%s</pre><p>Error message: %s' % (myCommand, str(e))) # shameless hack - see https://github.com/AIFDR/inasafe/issues/141 if sys.platform == 'darwin': # Mac OS X if 'Errno 4' in str(e): # continue as the error seems to be non critical pass else: raise Exception(myMessage) else: raise Exception(myMessage) # .. todo:: Check the result of the shell call is ok myKeywordIO = ISKeywordIO() myKeywordIO.copyKeywords(theLayer, myFilename, theExtraKeywords=theExtraKeywords) return myFilename # Filename of created file def extentToKml(theExtent): """A helper to get a little kml doc for an extent so that we can use it with gdal warp for clipping.""" myBottomLeftCorner = '%f,%f' % (theExtent[0], theExtent[1]) myTopLeftCorner = '%f,%f' % (theExtent[0], theExtent[3]) myTopRightCorner = '%f,%f' % (theExtent[2], theExtent[3]) myBottomRightCorner = '%f,%f' % (theExtent[2], theExtent[1]) myKml = ("""<?xml version="1.0" encoding="utf-8" ?> <kml xmlns="http://www.opengis.net/kml/2.2"> <Document> <Folder>