def testAttributeMap(self): # start with a feature with no fields f = QgsFeature() f.setAttributes([1, 'a', NULL]) with self.assertRaises(ValueError): _ = f.attributeMap() # set fields fields = QgsFields() field1 = QgsField('my_field') fields.append(field1) field2 = QgsField('my_field2') fields.append(field2) field3 = QgsField('my_field3') fields.append(field3) f.setFields(fields) f.setAttributes([1, 'a', NULL]) self.assertEqual(f.attributeMap(), {'my_field': 1, 'my_field2': 'a', 'my_field3': NULL}) # unbalanced fields/attributes -- should be handled gracefully # less attributes than fields f.setAttributes([1, 'a']) with self.assertRaises(ValueError): _ = f.attributeMap() f.setAttributes([1, 'a', 2, 3]) # more attributes than fields with self.assertRaises(ValueError): _ = f.attributeMap()
def test_AddAttribute(self): feat = QgsFeature() feat.addAttribute(1, "text") myCount = len(feat.attributeMap()) myExpectedCount = 1 myMessage = '\nExpected: %s\nGot: %s' % (myExpectedCount, myCount) assert myCount == myExpectedCount, myMessage
def gotFeatureForIdentification(self, pos): """Show a dialog with road information """ #pos is a rectangle self.mem_layer_obj.select() ftr = QgsFeature() ftr_ids = [] while self.mem_layer_obj.nextFeature(ftr): if ftr.geometry().intersects(pos): ftr_ids.append(ftr.id()) self.chosenFOIGeoms = [] self.info = QgsMessageViewer() if ftr_ids != []: f = QgsFeature() foi_type = self.foi_type.lower() if foi_type == 'areaofinterestdefiner': ftrData = "You have selected the following feature(s) for use as an Area of Interest:\n\n" if foi_type == 'lineofinterestdefiner': ftrData = "You have selected the following feature(s) for use as a Line of Interest:\n\n" if foi_type == 'pointofinterestdefiner': ftrData = "You have selected the following feature(s) for use as a Point of Interest:\n\n" for fid in ftr_ids: self.mem_layer_obj.dataProvider().featureAtId(fid, f, True) ftrData += f.attributeMap()[0].toString() ftrData += "\n_____________________________\n" self.chosenFOIGeoms.append(f.geometry()) id_fid = self.addGeomToMemoryLayer(f.geometry()) self.info.setMessageAsPlainText(ftrData) else: self.info.setMessageAsPlainText("no data to show") self.info.show() return
def layer_multifields_stats(layer, fields): """ return value distribution for field in given vector layer """ provider = layer.dataProvider() if provider is None: return False # retrieve index for all fields f_indices = [] for field in fields: idx = layer_field_index(layer, field) if idx == -1: return False f_indices.append(idx) f = QgsFeature() stats = {} provider.select(f_indices, provider.extent()) provider.rewind() for _idx in range(provider.featureCount()): provider.nextFeature(f) # create compound key _key = '' for idx in f_indices: if _key != '': _key = _key + '_' _key += str(f.attributeMap()[idx].toString()).upper() # update stats based on compound key if stats.has_key(_key): stats[_key] += 1 else: stats[_key] = 1 return stats
def layer_multifields_stats(layer, fields): """ return value distribution for field in given vector layer """ provider = layer.dataProvider() if provider is None: return False # retrieve index for all fields f_indices = [] for field in fields: idx = layer_field_index(layer, field) if idx == -1: return False f_indices.append(idx) f = QgsFeature() stats = {} provider.select(f_indices, provider.extent()) provider.rewind() for _idx in range(provider.featureCount()): provider.nextFeature(f) # create compound key _key = '' for idx in f_indices: if _key != '': _key = _key + '_' _key += str(f.attributeMap()[idx].toString()).upper() # update stats based on compound key if stats.has_key(_key): stats[_key]+=1 else: stats[_key]=1 return stats
def testAddFeatures(self): layer = QgsVectorLayer("Point", "test", "memory") provider = layer.dataProvider() res = provider.addAttributes([ QgsField( "name", QVariant.String, ), QgsField("age", QVariant.Int), QgsField("size", QVariant.Double) ]) assert res, "Failed to add attributes" myMessage = ('Expected: %s\nGot: %s\n' % (3, provider.fieldCount())) assert provider.fieldCount() == 3, myMessage ft = QgsFeature() ft.setGeometry(QgsGeometry.fromPoint(QgsPoint(10, 10))) ft.setAttributeMap({ 0: QVariant("Johny"), 1: QVariant(20), 2: QVariant(0.3) }) res, t = provider.addFeatures([ft]) assert res, "Failed to add feature" myMessage = ('Expected: %s\nGot: %s\n' % (1, provider.featureCount())) assert provider.featureCount() == 1, myMessage f = QgsFeature() provider.select() while provider.nextFeature(f): attrMap = f.attributeMap() myMessage = ('Expected: %s\nGot: %s\n' % ("Johny", str(attrMap[0].toString()))) assert str(attrMap[0].toString()) == "Johny", myMessage myMessage = ('Expected: %s\nGot: %s\n' % (20, attrMap[1].toInt()[0])) assert attrMap[1].toInt()[0] == 20, myMessage myMessage = ('Expected: %s\nGot: %s\n' % (0.3, attrMap[2].toFloat()[0])) assert (attrMap[0].toFloat()[0] - 0.3) < 0.0000001, myMessage geom = f.geometry() myMessage = ( 'Expected: %s\nGot: %s\n' % ("POINT(10.000000 10.000000)", str(geom.exportToWkt()))) assert str( geom.exportToWkt()) == "POINT(10.000000 10.000000)", myMessage
def test_ChangeAttribute(self): feat = QgsFeature() feat.addAttribute(1, "text") feat.changeAttribute(1, "changed") myChangedAttribute = feat.attributeMap()[1].toString() myExpectedAttribute = "changed" myMessage = '\nExpected: %s\nGot: %s' % (myExpectedAttribute, myChangedAttribute) assert myChangedAttribute == myExpectedAttribute, myMessage
@pyqtSlot(QPoint, QObject) def showInfo(self, point, mouseButton): """ event handler for toolInfo @see QGIS tutorial for detail point-polygon search on currently selected layer """ cur_layer_name = self.ui.cb_layer_selector.currentText() if cur_layer_name.isEmpty(): return try: cur_layer_idx = self.LAYER_NAMES.index(cur_layer_name) cur_layer = self.map_layers[cur_layer_idx] # if layer is not in same projection as map canvas # need to project query point if cur_layer.crs() != self.canvas.mapRenderer().destinationCrs(): transform = QgsCoordinateTransform(self.canvas.mapRenderer().destinationCrs(), cur_layer.crs()) point = transform.transform(point) # do query provider = cur_layer.dataProvider() provider.rewind() feature = QgsFeature() colonIndexes = provider.attributeIndexes() # search using point as center of rectangle polygon search_buffer_x = self.canvas.extent().width() * self.SEARCH_BUFFER / self.canvas.width() search_buffer_y = self.canvas.extent().height() * self.SEARCH_BUFFER / self.canvas.height() provider.select(colonIndexes, QgsRectangle(point.x()-search_buffer_x, point.y()-search_buffer_y, point.x()+search_buffer_x, point.y()+search_buffer_y), True) # get selected and display in result detail dialog box selected = [] while provider.nextFeature(feature): # for polygons, only show geometry containing query point if cur_layer.geometryType() == QGis.Polygon: if feature.geometry() is not None and not feature.geometry().contains (point): continue selected.append(feature.attributeMap()) if len(selected)>0: # display result if exists if cur_layer_idx == self.EXPOSURE: self.dlgResultDetail.showExposureData(provider.fields(), selected) else: self.dlgResultDetail.showInfoData(provider.fields(), selected) self.dlgResultDetail.exec_() else: logUICall.log(get_ui_string("widget.result.info.notfound"), logUICall.WARNING) except Exception as err: # point-in-polygon search is not critical, continue on error
def testAddFeatures(self): layer = QgsVectorLayer("Point", "test", "memory") provider = layer.dataProvider() res = provider.addAttributes([QgsField("name", QVariant.String,), QgsField("age", QVariant.Int), QgsField("size", QVariant.Double)]) assert res, "Failed to add attributes" myMessage = ('Expected: %s\nGot: %s\n' % (3, provider.fieldCount())) assert provider.fieldCount() == 3, myMessage ft = QgsFeature() ft.setGeometry(QgsGeometry.fromPoint(QgsPoint(10,10))) ft.setAttributeMap({0 : QVariant("Johny"), 1 : QVariant(20), 2 : QVariant(0.3)}) res, t = provider.addFeatures([ft]) assert res, "Failed to add feature" myMessage = ('Expected: %s\nGot: %s\n' % (1, provider.featureCount())) assert provider.featureCount() == 1, myMessage f = QgsFeature() provider.select() while provider.nextFeature(f): attrMap = f.attributeMap() myMessage = ('Expected: %s\nGot: %s\n' % ("Johny", str(attrMap[0].toString()))) assert str(attrMap[0].toString()) == "Johny", myMessage myMessage = ('Expected: %s\nGot: %s\n' % (20, attrMap[1].toInt()[0])) assert attrMap[1].toInt()[0] == 20, myMessage myMessage = ('Expected: %s\nGot: %s\n' % (0.3, attrMap[2].toFloat()[0])) assert (attrMap[0].toFloat()[0] - 0.3) < 0.0000001, myMessage geom = f.geometry() myMessage = ('Expected: %s\nGot: %s\n' % ("POINT(10.0 10.0)", str(geom.exportToWkt()))) assert str(geom.exportToWkt()) == "POINT(10.0 10.0)", myMessage
def testLocalCities(self): """Test that we can retrieve the cities local to the event""" myShakeId = '20120726022003' myShakeEvent = ShakeEvent(myShakeId) # Get teh mem layer myCitiesLayer = myShakeEvent.localCitiesMemoryLayer() myProvider = myCitiesLayer.dataProvider() myFeature = QgsFeature() myAttributes = myProvider.attributeIndexes() myProvider.select(myAttributes) myExpectedFeatureCount = 6 self.assertEquals(myProvider.featureCount(), myExpectedFeatureCount) myStrings = [] while myProvider.nextFeature(myFeature): # fetch map of attributes myAttributes = myFeature.attributeMap() for (myKey, myValue) in myAttributes.iteritems(): myStrings.append("%d: %s\n" % (myKey, myValue.toString())) myStrings.append('------------------\n') LOGGER.debug('Mem table:\n %s' % myStrings) myFilePath = unique_filename(prefix='testLocalCities', suffix='.txt', dir=temp_dir('test')) myFile = file(myFilePath, 'wt') myFile.writelines(myStrings) myFile.close() myFixturePath = os.path.join(dataDir(), 'tests', 'testLocalCities.txt') myFile = file(myFixturePath, 'rt') myExpectedString = myFile.readlines() myFile.close() myDiff = difflib.unified_diff(myStrings, myExpectedString) myDiffList = list(myDiff) myDiffString = '' for _, myLine in enumerate(myDiffList): myDiffString += myLine myMessage = ('Diff is not zero length:\n' 'Control file: %s\n' 'Test file: %s\n' 'Diff:\n%s' % (myFixturePath, myFilePath, myDiffString)) self.assertEqual(myDiffString, '', myMessage)
def do_dbf_import(self): """ last step: create table and import data """ QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) tablename = unicode(self.field("tablename").toString()) pkey = unicode(self.field("pkey").toString()) if pkey == "(none)": pkey = None try: # create the table self.db.create_table(tablename, self.dbf_fields, pkey) cursor = self.db.con.cursor() # now let's get the features and import them to database pr = self.vlayer.dataProvider() flds = pr.fields() pr.enableGeometrylessFeatures(True) pr.select(pr.attributeIndexes(), QgsRectangle(), False) # all attrs, no geometry f = QgsFeature() while pr.nextFeature(f): attrs = f.attributeMap() values = [] for (i,val) in attrs.iteritems(): vartype = flds[i].type() if val.isNull(): values.append("NULL") elif vartype == QVariant.Int: values.append(str(val.toInt()[0])) elif vartype == QVariant.Double: values.append(str(val.toDouble()[0])) else: # string or something else values.append("'%s'" % str(val.toString().toUtf8()).replace("'","''").replace("\\", "\\\\")) self.db.insert_table_row(tablename, values, None, cursor) # commit changes to DB self.db.con.commit() except postgis_utils.DbError, e: QApplication.restoreOverrideCursor() DlgDbError.showError(e, self) return False
def _aggregate(self, myImpactLayer, myExpectedResults): myAggregationLayer = QgsVectorLayer( os.path.join(BOUNDDATA, 'kabupaten_jakarta.shp'), 'test aggregation', 'ogr') # create a copy of aggregation layer myGeoExtent = extent_to_geo_array( myAggregationLayer.extent(), myAggregationLayer.crs()) myAggrAttribute = self.keywordIO.read_keywords( myAggregationLayer, self.defaults['AGGR_ATTR_KEY']) # noinspection PyArgumentEqualDefault myAggregationLayer = clip_layer( layer=myAggregationLayer, extent=myGeoExtent, explode_flag=True, explode_attribute=myAggrAttribute) myAggregator = Aggregator(None, myAggregationLayer) # setting up myAggregator.isValid = True myAggregator.layer = myAggregationLayer myAggregator.safeLayer = safe_read_layer( str(myAggregator.layer.source())) myAggregator.aoiMode = False myAggregator.aggregate(myImpactLayer) myProvider = myAggregator.layer.dataProvider() myProvider.select(myProvider.attributeIndexes()) myFeature = QgsFeature() myResults = [] while myProvider.nextFeature(myFeature): myFeatureResults = {} myAtMap = myFeature.attributeMap() for (k, attr) in myAtMap.iteritems(): myFeatureResults[k] = str(attr.toString()) myResults.append(myFeatureResults) self.assertEqual(myExpectedResults, myResults)
def test_AttributeMap(self): myPath = os.path.join(unitTestDataPath(), 'lines.shp') myLayer = QgsVectorLayer(myPath, 'Lines', 'ogr') provider = myLayer.dataProvider() allAttrs = provider.attributeIndexes() provider.select(allAttrs) feat = QgsFeature() provider.nextFeature(feat) myAttributeMap = feat.attributeMap() myExpectedAttributeMap = {0: QVariant("Highway"), 1: QVariant(1)} # Only for printing purposes myAttributeDict = { 0:str(myAttributeMap[0].toString()), 1:int(myAttributeMap[1].toString())} myExpectedAttributeDict = {0: "Highway", 1: 1} myMessage = '\nExpected: %s\nGot: %s' % (myExpectedAttributeDict, myAttributeDict) assert myAttributeMap == myExpectedAttributeMap, myMessage
def test_AttributeMap(self): myPath = os.path.join(unitTestDataPath(), 'lines.shp') myLayer = QgsVectorLayer(myPath, 'Lines', 'ogr') provider = myLayer.dataProvider() allAttrs = provider.attributeIndexes() provider.select(allAttrs) feat = QgsFeature() provider.nextFeature(feat) myAttributeMap = feat.attributeMap() myExpectedAttributeMap = {0: QVariant("Highway"), 1: QVariant(1)} # Only for printing purposes myAttributeDict = { 0: str(myAttributeMap[0].toString()), 1: int(myAttributeMap[1].toString()) } myExpectedAttributeDict = {0: "Highway", 1: 1} myMessage = '\nExpected: %s\nGot: %s' % (myExpectedAttributeDict, myAttributeDict) assert myAttributeMap == myExpectedAttributeMap, myMessage
def addActivitiesFromLayer(self): """ """ def _createNewActivity(id=None, otherActivity=None): # Connect to the protocol signal self.connect(self.activityProtocol, SIGNAL("created( bool, int, QString )"), _createNewActivityFinished) # Create a new Uuid if id is not None: uid = QPlainUuid(id) else: uid = QPlainUuid(QUuid.createUuid()) tagGroups = list(TagGroup() for i in range(len(groups))) # attrs is a dictionary: key = field index, value = QgsFeatureAttribute # show all attributes and their values self.log("Attribute list:") for (k, attr) in attrs.iteritems(): if k is not identifierColumnIndex: self.log("%s: %s" % (fieldIndexMap[k], attr.toString())) # First search the correct taggroup to append attributeName = provider.fields()[k].name() currentTagGroup = 0 for g in groups: if attributeName in g: break else: currentTagGroup += 1 if attr is not None and attr.toString() != '': tag = Tag(key=fieldIndexMap[k], value=attr.toString()) tagGroups[currentTagGroup].addTag(tag) if tagGroups[currentTagGroup].mainTag() is None: tagGroups[currentTagGroup].setMainTag(tag) a = Activity(id=uid) a.setGeometry(feature.geometry()) for tg in tagGroups: if len(tg.tags) > 0: a.addTagGroup(tg) wrapperObj = {} wrapperObj['activities'] = [a.createDiff(otherActivity)] self.activityProtocol.add(json.dumps(wrapperObj, sort_keys=True, indent=4 * ' ')) def _createNewActivityFinished(success, statusCode, response): # Connect to the protocol signal self.disconnect(self.activityProtocol, SIGNAL("created( bool, int, QString )"), _createNewActivityFinished) # The newly returned activity should be parsed and the ID needs to # be written to the QgsFeature self.log("Server returned status code %i and response:\n%s" % (statusCode, response)) def _checkActivityExists(uid): self.connect(self.activityProtocol, SIGNAL("readSignal( bool, int, QString )"), _checkActivityExistsFinished) self.activityProtocol.readById(uid.toString()) self.log("Check if activity exists:") def _checkActivityExistsFinished(success, statusCode, response): self.disconnect(self.activityProtocol, SIGNAL("readSignal( bool, int, QString )"), _checkActivityExistsFinished) self.log("Server returned status code %i and response:\n%s" % (statusCode, response)) activities = self.parseActivitiesResponse(response) if len(activities) == 0: self.log("Activity does not yet exist") _createNewActivity(uid) #else: # _createNewActivity(uid, activities[0]) # Get the dict that maps the attribute names from the landmatrix input Shapefile to the # fields defined in the global definition yaml identifierColumn, transformMap, groups = self.getTagGroupsConfiguration("landmatrix.activity.ini") # Get the active layer and its data provider layer = self.iface.activeLayer() provider = layer.dataProvider() # The current feature feature = QgsFeature() # List of attribute indexes to select attributeIndexes = [] # Dict that maps the field index to the fields defined in the global YAML fieldIndexMap = {} # Find the index of the uuid column identifierColumnIndex = None for (i, field) in provider.fields().iteritems(): if str(field.name()) in transformMap: attributeIndexes.append(i) fieldIndexMap[i] = transformMap[str(field.name())] elif field.name() == str(identifierColumn): identifierColumnIndex = i # Start data retreival: fetch geometry and necessary attributes for each feature provider.select(attributeIndexes + [identifierColumnIndex]) # retreive every feature with its geometry and attributes while provider.nextFeature(feature): # fetch map of attributes attrs = feature.attributeMap() # Get the identifier value identifierValue = attrs[identifierColumnIndex].toString() # If the identifier is empty or None, create a new activity. # This should only be necessary at the initial imports if identifierValue is None or str(identifierValue) == str(''): _createNewActivity() #if str(idenitfierValue).contains(QRegExp('a0c78834-fd98-4d4d-b8b5-0754b50f2510')) else: uid = QPlainUuid(identifierValue) _checkActivityExists(uid)
def run(self): """Run any post processors requested by the impact function. Args: None Returns: None Raises: None """ try: myRequestedPostProcessors = self.functionParams['postprocessors'] myPostProcessors = get_postprocessors(myRequestedPostProcessors) except (TypeError, KeyError): # TypeError is for when functionParams is none # KeyError is for when ['postprocessors'] is unavailable myPostProcessors = {} LOGGER.debug('Running this postprocessors: ' + str(myPostProcessors)) myFeatureNameAttribute = self.aggregator.attributes[ self.aggregator.defaults['AGGR_ATTR_KEY']] if myFeatureNameAttribute is None: self.attributeTitle = self.tr('Aggregation unit') else: self.attributeTitle = myFeatureNameAttribute myNameFieldIndex = self.aggregator.layer.fieldNameIndex( self.attributeTitle) mySumFieldIndex = self.aggregator.layer.fieldNameIndex( self._sumFieldName()) myFemaleRatioIsVariable = False myFemRatioFieldIndex = None myFemaleRatio = None if 'Gender' in myPostProcessors: #look if we need to look for a variable female ratio in a layer try: myFemRatioField = self.aggregator.attributes[ self.aggregator.defaults['FEM_RATIO_ATTR_KEY']] myFemRatioFieldIndex = self.aggregator.layer.fieldNameIndex( myFemRatioField) myFemaleRatioIsVariable = True except KeyError: try: myFemaleRatio = self.keywordIO.read_keywords( self.aggregator.layer, self.aggregator.defaults['FEM_RATIO_KEY']) except KeywordNotFoundError: myFemaleRatio = self.aggregator.defaults['FEM_RATIO'] #iterate zone features myProvider = self.aggregator.layer.dataProvider() myAttributes = myProvider.attributeIndexes() # start data retreival: fetch no geometry and all attributes for each # feature myProvider.select(myAttributes, QgsRectangle(), False) myFeature = QgsFeature() myPolygonIndex = 0 while myProvider.nextFeature(myFeature): #get all attributes of a feature myAttributeMap = myFeature.attributeMap() #if a feature has no field called if myNameFieldIndex == -1: myZoneName = str(myFeature.id()) else: myZoneName = myAttributeMap[myNameFieldIndex].toString() #create dictionary of attributes to pass to postprocessor myGeneralParams = {'target_field': self.aggregator.targetField} if self.aggregator.statisticsType == 'class_count': myGeneralParams['impact_classes'] = ( self.aggregator.statisticsClasses) elif self.aggregator.statisticsType == 'sum': myImpactTotal, _ = myAttributeMap[mySumFieldIndex].toDouble() myGeneralParams['impact_total'] = myImpactTotal try: myGeneralParams['impact_attrs'] = ( self.aggregator.impactLayerAttributes[myPolygonIndex]) except IndexError: #rasters and attributeless vectors have no attributes myGeneralParams['impact_attrs'] = None for myKey, myValue in myPostProcessors.iteritems(): myParameters = myGeneralParams try: #look if params are available for this postprocessor myParameters.update( self.functionParams['postprocessors'][myKey]['params']) except KeyError: pass if myKey == 'Gender': if myFemaleRatioIsVariable: myFemaleRatio, mySuccessFlag = myAttributeMap[ myFemRatioFieldIndex].toDouble() if not mySuccessFlag: myFemaleRatio = self.aggregator.defaults[ 'FEM_RATIO'] LOGGER.debug(mySuccessFlag) myParameters['female_ratio'] = myFemaleRatio myValue.setup(myParameters) myValue.process() myResults = myValue.results() myValue.clear() # LOGGER.debug(myResults) try: self.postProcessingOutput[myKey].append( (myZoneName, myResults)) except KeyError: self.postProcessingOutput[myKey] = [] self.postProcessingOutput[myKey].append( (myZoneName, myResults)) #increment the index myPolygonIndex += 1
def _clip_vector_layer( layer, extent, extra_keywords=None, explode_flag=True, hard_clip_flag=False, explode_attribute=None): """Clip a Hazard or Exposure layer to the extents provided. The layer must be a vector layer or an exception will be thrown. The output layer will always be in WGS84/Geographic. :param layer: A valid QGIS vector or raster layer :type layer: :param extent: Either 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. or: A QgsGeometry of type polygon. **Polygon clipping is currently only supported for vector datasets.** :type extent: list(float, float, float, float) :param extra_keywords: Optional keywords dictionary to be added to output layer. :type extra_keywords: dict :param explode_flag: A bool specifying whether multipart features should be 'exploded' into singleparts. **This parameter is ignored for raster layer clipping.** :type explode_flag: bool :param hard_clip_flag: A bool specifying whether line and polygon features that extend beyond the extents should be clipped such that they are reduced in size to the part of the geometry that intersects the extent only. Default is False. **This parameter is ignored for raster layer clipping.** :type hard_clip_flag: bool :param explode_attribute: A str specifying to which attribute #1, #2 and so on will be added in case of explode_flag being true. The attribute is modified only if there are at least 2 parts. :type explode_attribute: str :returns: Clipped layer (placed in the system temp dir). The output layer will be reprojected to EPSG:4326 if needed. :rtype: QgsVectorLayer """ if not layer or not extent: myMessage = tr('Layer or Extent passed to clip is None.') raise InvalidParameterError(myMessage) if layer.type() != QgsMapLayer.VectorLayer: myMessage = tr('Expected a vector layer but received a %s.' % str(layer.type())) raise InvalidParameterError(myMessage) #myHandle, myFilename = tempfile.mkstemp('.sqlite', 'clip_', # temp_dir()) myHandle, myFilename = tempfile.mkstemp('.shp', 'clip_', temp_dir()) # 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, layer.crs()) myAllowedClipTypes = [QGis.WKBPolygon, QGis.WKBPolygon25D] if type(extent) is list: myRect = QgsRectangle( extent[0], extent[1], extent[2], extent[3]) # noinspection PyCallByClass myClipPolygon = QgsGeometry.fromRect(myRect) elif (type(extent) is QgsGeometry and extent.wkbType in myAllowedClipTypes): myRect = extent.boundingBox().toRectF() myClipPolygon = extent else: raise InvalidClipGeometryError( tr( 'Clip geometry must be an extent or a single part' 'polygon based geometry.')) myProjectedExtent = myXForm.transformBoundingBox(myRect) # Get vector layer myProvider = layer.dataProvider() if myProvider is None: myMessage = tr('Could not obtain data provider from ' 'layer "%s"' % layer.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, layer.wkbType(), myGeoCrs, #'SQLite') # FIXME (Ole): This works but is far too slow '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(layer.crs(), myGeoCrs) # Retrieve every feature with its geometry and attributes myFeature = QgsFeature() myCount = 0 myHasMultipart = False if explode_attribute is not None: theExplodeAttributeIndex = myProvider.fieldNameIndex( explode_attribute) while myProvider.nextFeature(myFeature): myGeometry = myFeature.geometry() if explode_attribute is not None: myAttrs = myFeature.attributeMap() # Loop through the parts adding them to the output file # we write out single part features unless explode_flag is False if explode_flag: myGeometryList = explode_multipart_geometry(myGeometry) else: myGeometryList = [myGeometry] for myPartIndex, myPart in enumerate(myGeometryList): myPart.transform(myXForm) if hard_clip_flag: # Remove any dangling bits so only intersecting area is # kept. myPart = clip_geometry(myClipPolygon, myPart) if myPart is None: continue myFeature.setGeometry(myPart) # There are multiple parts and we want to show it in the # explode_attribute if myPartIndex > 0 and explode_attribute is not None: myHasMultipart = True myPartAttr = QVariant( '%s #%s' % (myAttrs[theExplodeAttributeIndex].toString(), myPartIndex)) myFeature.changeAttribute(theExplodeAttributeIndex, myPartAttr) 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. If hazard and exposure data doesn\'t overlap at all, it ' 'is not possible to do an analysis. Another possibility is that ' 'the layers do overlap but because they may have different ' 'spatial references, they appear to be disjointed. If this is the ' 'case, try to turn on reproject on-the-fly in QGIS.') raise NoFeaturesInExtentError(myMessage) myKeywordIO = KeywordIO() if extra_keywords is None: extra_keywords = {} extra_keywords['HAD_MULTIPART_POLY'] = myHasMultipart myKeywordIO.copy_keywords( layer, myFilename, extra_keywords=extra_keywords) myBaseName = '%s clipped' % layer.name() myLayer = QgsVectorLayer(myFilename, myBaseName, 'ogr') return myLayer
def addStakeholdersFromLayer(self): """ Import all stakeholders from the active layer to the Land Observatory platform. It is not (yet) tested if a stakeholder already exists or not. """ # Connect to the protocol to get noticed as soon as the stakeholder has # been created self.connect(self.stakeholderProtocol, SIGNAL("created( bool, int, QString"), self.addStakeholdersFinished) # Get the dict maps the attribute names from the landmatrix input Shapefile to the # fields defined in the global definition yaml identifierColumn, transformMap, groups = self.getTagGroupsConfiguration("landmatrix.stakeholder.ini") # Get the active layer and its data provider layer = self.iface.activeLayer() provider = layer.dataProvider() # The current feature feature = QgsFeature() # List of attribute indexes to select attributeIndexes = [] # Dict that maps the field index to the fields defined in the global YAML fieldIndexMap = {} for (i, field) in provider.fields().iteritems(): if str(field.name()) in transformMap: attributeIndexes.append(i) fieldIndexMap[i] = transformMap[str(field.name())] # Start data retreival: fetch geometry and necessary attributes for each feature provider.select(attributeIndexes) stakeholders = [] # retreive every feature with its geometry and attributes while provider.nextFeature(feature): tagGroups = list(TagGroup() for i in range(len(groups))) # fetch map of attributes attrs = feature.attributeMap() # attrs is a dictionary: key = field index, value = QgsFeatureAttribute # show all attributes and their values for (k, attr) in attrs.iteritems(): self.log("%s: %s" % (fieldIndexMap[k], attr.toString())) # First search the correct taggroup to append attributeName = provider.fields()[k].name() currentTagGroup = 0 for g in groups: if attributeName in g: break else: currentTagGroup += 1 if attr is not None and attr.toString() != '': tag = Tag(key=fieldIndexMap[k], value=attr.toString()) tagGroups[currentTagGroup].addTag(tag) if tagGroups[currentTagGroup].mainTag() is None: tagGroups[currentTagGroup].setMainTag(tag) s = Stakeholder() for tg in tagGroups: if len(tg.tags) > 0: s.addTagGroup(tg) stakeholders.append(s) msg, rawBody = self.stakeholderProtocol.add(stakeholders) self.log(msg) self.log(rawBody) # Disconnect the signal self.disconnect(self.stakeholderProtocol, SIGNAL("created( bool, int, QString"), self.addStakeholdersFinished)
def processing(self): #check edit mode if not self.active_layer.isEditable(): QMessageBox.warning(self, self.tr("FieldPyculator warning"), self.tr("Layer is not in edit mode! Please start editing the layer!")) return start = datetime.datetime.now() new_ns = {} #run global code if self.ui.grpGlobalExpression.isChecked(): try: code = unicode(self.ui.txtGlobalExp.toPlainText()) bytecode = compile(code, '<string>', 'exec') exec bytecode in new_ns except: QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), (self.tr("Global code block can't be executed!\n%1: %2")) .arg(unicode(sys.exc_info()[0].__name__)) .arg(unicode(sys.exc_info()[1]))) return code = unicode(self.ui.txtFieldExp.toPlainText()) #TODO: check 'result' existing in text of code???!!! #replace all fields tags field_map = self.data_provider.fields() for num, field in field_map.iteritems(): field_name = unicode(field.name()) replval = '__attr[' + str(num) + ']' code = code.replace("<"+field_name+">", replval) #replace all special vars code = code.replace('$id', '__id') code = code.replace('$geom', '__geom') #is it need: $area, $length, $x, $y???? #print code #debug #search needed vars (hmmm... comments?!) need_id = code.find("__id") != -1 need_geom = code.find("__geom") != -1 need_attrs = code.find("__attr") != -1 #compile try: bytecode = compile(code, '<string>', 'exec') except: QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Field code block can't be executed!\n%1: %2") .arg(unicode(sys.exc_info()[0].__name__)) .arg(unicode(sys.exc_info()[1]))) return #get num of updating field field_num = self.data_provider.fieldNameIndex(self.ui.cmbUpdateField.currentText()) #setup progress bar self.ui.prgTotal.setValue(0) #run if not self.ui.chkOnlySelected.isChecked(): #select all features features_for_update = self.data_provider.featureCount() if features_for_update > 0: self.ui.prgTotal.setMaximum(features_for_update) feat = QgsFeature() if need_attrs: attr_ind = self.data_provider.attributeIndexes() else: attr_ind = [] self.data_provider.select(attr_ind, QgsRectangle(), need_geom) while self.data_provider.nextFeature( feat ): feat_id = feat.id() #add needed vars if need_id: new_ns['__id'] = feat_id if need_geom: geom = feat.geometry() new_ns['__geom'] = geom if need_attrs: attr_map = feat.attributeMap() attr = [] for num, a in attr_map.iteritems(): attr.append(self.qvar2py(a)) new_ns['__attr'] = attr #clear old result if new_ns.has_key(self.RESULT_VAR_NAME): del new_ns[self.RESULT_VAR_NAME] #exec try: exec bytecode in new_ns except: QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Field code block can't be executed for feature %3!\n%1: %2") .arg(unicode(sys.exc_info()[0].__name__)) .arg(unicode(sys.exc_info()[1])) .arg(unicode(feat_id))) return #check result if not new_ns.has_key(self.RESULT_VAR_NAME): QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Field code block does not return '%1' variable! Please declare this variable in your code!") .arg(self.RESULT_VAR_NAME)) return #try assign try: self.active_layer.changeAttributeValue(feat_id, field_num, new_ns[self.RESULT_VAR_NAME]) except: QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Result value can't be assigned to the feature %3!\n%1: %2") .arg(unicode(sys.exc_info()[0].__name__)) .arg(unicode(sys.exc_info()[1])) .arg(unicode(feat_id))) return self.ui.prgTotal.setValue(self.ui.prgTotal.value()+1) else: #only selected (TODO: NEED REFACTORING - copy-past!!!) features_for_update = self.active_layer.selectedFeatureCount() if features_for_update > 0: self.ui.prgTotal.setMaximum(features_for_update) for feat in self.active_layer.selectedFeatures(): feat_id = feat.id() #add needed vars if need_id: new_ns['__id'] = feat_id if need_geom: geom = feat.geometry() new_ns['__geom'] = geom if need_attrs: attr_map = feat.attributeMap() attr = [] for num, a in attr_map.iteritems(): attr.append(self.qvar2py(a)) new_ns['__attr'] = attr #clear old result if new_ns.has_key(self.RESULT_VAR_NAME): del new_ns[self.RESULT_VAR_NAME] #exec try: exec bytecode in new_ns except: QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Field code block can't be executed for feature %3!\n%1: %2") .arg(unicode(sys.exc_info()[0].__name__)) .arg(unicode(sys.exc_info()[1])) .arg(unicode(feat_id))) return #check result if not new_ns.has_key(self.RESULT_VAR_NAME): QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Field code block does not return '%1' variable! Please declare this variable in your code!") .arg(self.RESULT_VAR_NAME)) return #try assign try: self.active_layer.changeAttributeValue(feat_id, field_num, new_ns[self.RESULT_VAR_NAME]) except: QMessageBox.critical(self, self.tr("FieldPyculator code execute error"), self.tr("Result value can't be assigned to the feature %3!\n%1: %2") .arg(unicode(sys.exc_info()[0].__name__)) .arg(unicode(sys.exc_info()[1])) .arg(unicode(feat_id))) return self.ui.prgTotal.setValue(self.ui.prgTotal.value()+1) stop = datetime.datetime.now() #workaround for python < 2.7 td = stop - start if sys.version_info[:2] < (2, 7): total_sec = (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 else: total_sec = td.total_seconds() QMessageBox.information(self, self.tr("FieldPyculator code executed successfully"), self.tr("Updated %1 features for %2 seconds") .arg(unicode(features_for_update)) .arg(unicode(total_sec)) )
def showInfo(self, point, mouseButton): """ event handler for toolInfo @see QGIS tutorial for detail point-polygon search on currently selected layer """ cur_layer_name = self.ui.cb_layer_selector.currentText() if cur_layer_name.isEmpty(): return try: cur_layer_idx = self.LAYER_NAMES.index(cur_layer_name) cur_layer = self.map_layers[cur_layer_idx] # if layer is not in same projection as map canvas # need to project query point if cur_layer.crs() != self.canvas.mapRenderer().destinationCrs(): transform = QgsCoordinateTransform( self.canvas.mapRenderer().destinationCrs(), cur_layer.crs()) point = transform.transform(point) # do query provider = cur_layer.dataProvider() provider.rewind() feature = QgsFeature() colonIndexes = provider.attributeIndexes() # search using point as center of rectangle polygon search_buffer_x = self.canvas.extent().width( ) * self.SEARCH_BUFFER / self.canvas.width() search_buffer_y = self.canvas.extent().height( ) * self.SEARCH_BUFFER / self.canvas.height() provider.select( colonIndexes, QgsRectangle(point.x() - search_buffer_x, point.y() - search_buffer_y, point.x() + search_buffer_x, point.y() + search_buffer_y), True) # get selected and display in result detail dialog box selected = [] while provider.nextFeature(feature): # for polygons, only show geometry containing query point if cur_layer.geometryType() == QGis.Polygon: if feature.geometry( ) is not None and not feature.geometry().contains(point): continue selected.append(feature.attributeMap()) if len(selected) > 0: # display result if exists if cur_layer_idx == self.EXPOSURE: self.dlgResultDetail.showExposureData( provider.fields(), selected) else: self.dlgResultDetail.showInfoData(provider.fields(), selected) self.dlgResultDetail.exec_() else: logUICall.log(get_ui_string("widget.result.info.notfound"), logUICall.WARNING) except Exception as err: # point-in-polygon search is not critical, continue on error logUICall.log(str(err), logUICall.WARNING)
def testClipping(self): """Test that we can clip geometries using other geometries.""" myMemoryLayer = QgsVectorLayer( ('LineString?crs=epsg:4326&field=name:string(20)&index=yes'), 'clip-in', 'memory') assert myMemoryLayer is not None, 'Provider not initialised' myProvider = myMemoryLayer.dataProvider() assert myProvider is not None myFeature1 = QgsFeature() myFeature1.setGeometry(QgsGeometry.fromPolyline([ QgsPoint(10,10), QgsPoint(20,10), QgsPoint(30,10), QgsPoint(40,10), ] )) myFeature1.setAttributeMap({0 : QVariant('Johny')}) myFeature2 = QgsFeature() myFeature2.setGeometry(QgsGeometry.fromPolyline([ QgsPoint(10,10), QgsPoint(20,20), QgsPoint(30,30), QgsPoint(40,40), ] )) myFeature2.setAttributeMap({0 : QVariant('Be')}) myFeature3 = QgsFeature() myFeature3.setGeometry(QgsGeometry.fromPolyline([ QgsPoint(10,10), QgsPoint(10,20), QgsPoint(10,30), QgsPoint(10,40), ] )) myFeature3.setAttributeMap({0 : QVariant('Good')}) myResult, myFeatures = myProvider.addFeatures( [myFeature1, myFeature2, myFeature3]) assert myResult == True assert len(myFeatures) == 3 myClipPolygon = QgsGeometry.fromPolygon([[ QgsPoint(20,20), QgsPoint(20,30), QgsPoint(30,30), QgsPoint(30,20), QgsPoint(20,20), ]] ) print 'Clip: %s' % myClipPolygon.exportToWkt() writeShape(myMemoryLayer, 'clipGeometryBefore.shp') myProvider.rewind() myProvider.select(myProvider.attributeIndexes()) myFeatures = [] myFeature = QgsFeature() while myProvider.nextFeature(myFeature): myGeometry = myFeature.geometry() if myGeometry.intersects(myClipPolygon): # Adds nodes where the clip and the line intersec myCombinedGeometry = myGeometry.combine(myClipPolygon) # Gives you the areas inside the clip mySymmetricalGeometry = myGeometry.symDifference( myCombinedGeometry) # Gives you areas outside the clip area # myDifferenceGeometry = myCombinedGeometry.difference( # myClipPolygon) #print 'Original: %s' % myGeometry.exportToWkt() #print 'Combined: %s' % myCombinedGeometry.exportToWkt() #print 'Difference: %s' % myDifferenceGeometry.exportToWkt() print 'Symmetrical: %s' % mySymmetricalGeometry.exportToWkt() myExpectedWkt = 'LINESTRING(20.0 20.0, 30.0 30.0)' # There should only be one feature that intersects this clip # poly so this assertion should work. self.assertEqual(myExpectedWkt, mySymmetricalGeometry.exportToWkt()) myNewFeature = QgsFeature() myNewFeature.setAttributeMap(myFeature.attributeMap()) myNewFeature.setGeometry(mySymmetricalGeometry) myFeatures.append(myNewFeature) myNewMemoryLayer = QgsVectorLayer( ('LineString?crs=epsg:4326&field=name:string(20)&index=yes'), 'clip-out', 'memory') myNewProvider = myNewMemoryLayer.dataProvider() myResult, myFeatures = myNewProvider.addFeatures(myFeatures) self.assertTrue(myResult) self.assertEqual(len(myFeatures), 1) myNewMemoryLayer.commitChanges() writeShape(myNewMemoryLayer, 'clipGeometryAfter.shp')
def _preparePolygonLayer(self, theQgisLayer): """Create a new layer with no intersecting features to self.layer. A helper function to align the polygons to the postprocLayer polygons. If one input polygon is in two or more postprocLayer polygons then it is divided so that each part is within only one of the postprocLayer polygons. this allows to aggregate in postrocessing using centroid in polygon. The function assumes EPSG:4326 but no checks are enforced Args: theQgisLayer of the file to be processed Returns: QgisLayer of the processed file Raises: Any exceptions raised by the InaSAFE library will be propagated. """ # import time # startTime = time.clock() myMessage = m.Message( m.Heading(self.tr('Preclipping input data...')), m.Paragraph(self.tr( 'Modifying %1 to avoid intersections with the aggregation ' 'layer' ).arg(theQgisLayer.name()))) self._sendMessage(myMessage) theLayerFilename = str(theQgisLayer.source()) myPostprocPolygons = self.safeLayer.get_geometry() myPolygonsLayer = safe_read_layer(theLayerFilename) myRemainingPolygons = numpy.array(myPolygonsLayer.get_geometry()) # myRemainingAttributes = numpy.array(myPolygonsLayer.get_data()) myRemainingIndexes = numpy.array(range(len(myRemainingPolygons))) #used for unit tests only self.preprocessedFeatureCount = 0 # FIXME (MB) the intersecting array is used only for debugging and # could be safely removed myIntersectingPolygons = [] myInsidePolygons = [] # FIXME (MB) maybe do raw geos without qgis #select all postproc polygons with no attributes aggregationProvider = self.layer.dataProvider() aggregationProvider.select([]) # copy polygons to a memory layer myQgisMemoryLayer = create_memory_layer(theQgisLayer) polygonsProvider = myQgisMemoryLayer.dataProvider() allPolygonAttrs = polygonsProvider.attributeIndexes() polygonsProvider.select(allPolygonAttrs) myQgisPostprocPoly = QgsFeature() myQgisFeat = QgsFeature() myInsideFeat = QgsFeature() fields = polygonsProvider.fields() myTempdir = temp_dir(sub_dir='preprocess') myOutFilename = unique_filename(suffix='.shp', dir=myTempdir) self.keywordIO.copy_keywords(theQgisLayer, myOutFilename) mySHPWriter = QgsVectorFileWriter(myOutFilename, 'UTF-8', fields, polygonsProvider.geometryType(), polygonsProvider.crs()) if mySHPWriter.hasError(): raise InvalidParameterError(mySHPWriter.errorMessage()) # end FIXME for (myPostprocPolygonIndex, myPostprocPolygon) in enumerate(myPostprocPolygons): LOGGER.debug('PostprocPolygon %s' % myPostprocPolygonIndex) myPolygonsCount = len(myRemainingPolygons) aggregationProvider.featureAtId( myPostprocPolygonIndex, myQgisPostprocPoly, True, []) myQgisPostprocGeom = QgsGeometry(myQgisPostprocPoly.geometry()) # myPostprocPolygon bounding box values A = numpy.array(myPostprocPolygon) minx = miny = sys.maxint maxx = maxy = -minx myPostprocPolygonMinx = min(minx, min(A[:, 0])) myPostprocPolygonMaxx = max(maxx, max(A[:, 0])) myPostprocPolygonMiny = min(miny, min(A[:, 1])) myPostprocPolygonMaxy = max(maxy, max(A[:, 1])) # create an array full of False to store if a BB vertex is inside # or outside the myPostprocPolygon myAreVerticesInside = numpy.zeros(myPolygonsCount * 4, dtype=numpy.bool) # Create Nx2 vector of vertices of bounding boxes myBBVertices = [] # Compute bounding box for each geometry type for myPoly in myRemainingPolygons: minx = miny = sys.maxint maxx = maxy = -minx # Do outer ring only as the BB is outside anyway A = numpy.array(myPoly) minx = min(minx, numpy.min(A[:, 0])) maxx = max(maxx, numpy.max(A[:, 0])) miny = min(miny, numpy.min(A[:, 1])) maxy = max(maxy, numpy.max(A[:, 1])) myBBVertices.extend([(minx, miny), (minx, maxy), (maxx, maxy), (maxx, miny)]) # see if BB vertices are in myPostprocPolygon myBBVertices = numpy.array(myBBVertices) inside, _ = points_in_and_outside_polygon(myBBVertices, myPostprocPolygon) # make True if the vertice was in myPostprocPolygon myAreVerticesInside[inside] = True # myNextIterPolygons has the 0:count indexes # myOutsidePolygons has the mapped to original indexes # and is overwritten at every iteration because we care only of # the outside polygons remaining after the last iteration myNextIterPolygons = [] myOutsidePolygons = [] for i in range(myPolygonsCount): k = i * 4 myMappedIndex = myRemainingIndexes[i] # memory layers counting starts at 1 instead of 0 as in our # indexes myFeatId = myMappedIndex + 1 doIntersection = False # summ the isInside bool for each of the boundingbox vertices # of each poygon. for example True + True + False + True is 3 myPolygonLocation = numpy.sum(myAreVerticesInside[k:k + 4]) if myPolygonLocation == 4: # all vertices are inside -> polygon is inside #ignore this polygon from further analysis myInsidePolygons.append(myMappedIndex) polygonsProvider.featureAtId(myFeatId, myQgisFeat, True, allPolygonAttrs) mySHPWriter.addFeature(myQgisFeat) self.preprocessedFeatureCount += 1 # LOGGER.debug('Polygon %s is fully inside' %myMappedIndex) # tmpWriter.addFeature(myQgisFeat) elif myPolygonLocation == 0: # all vertices are outside # check if the polygon BB is completely outside of the # myPostprocPolygon BB. myPolyMinx = numpy.min(myBBVertices[k:k + 4, 0]) myPolyMaxx = numpy.max(myBBVertices[k:k + 4, 0]) myPolyMiny = numpy.min(myBBVertices[k:k + 4, 1]) myPolyMaxy = numpy.max(myBBVertices[k:k + 4, 1]) # check if myPoly is all E,W,N,S of myPostprocPolygon if ((myPolyMinx > myPostprocPolygonMaxx) or (myPolyMaxx < myPostprocPolygonMinx) or (myPolyMiny > myPostprocPolygonMaxy) or (myPolyMaxy < myPostprocPolygonMiny)): #polygon is surely outside myOutsidePolygons.append(myMappedIndex) # we need this polygon in the next iteration myNextIterPolygons.append(i) else: # polygon might be outside or intersecting. consider # it intersecting so it goes into further analysis doIntersection = True else: # some vertices are outside some inside -> polygon is # intersecting doIntersection = True #intersect using qgis if doIntersection: # LOGGER.debug('Intersecting polygon %s' % myMappedIndex) myIntersectingPolygons.append(myMappedIndex) ok = polygonsProvider.featureAtId(myFeatId, myQgisFeat, True, allPolygonAttrs) if not ok: LOGGER.debug('Couldn\'t fetch feature: %s' % myFeatId) LOGGER.debug([str(error) for error in polygonsProvider.errors()]) myQgisPolyGeom = QgsGeometry(myQgisFeat.geometry()) myAtMap = myQgisFeat.attributeMap() # for (k, attr) in myAtMap.iteritems(): # LOGGER.debug( "%d: %s" % (k, attr.toString())) # make intersection of the myQgisFeat and the postprocPoly # write the inside part to a shp file and the outside part # back to the original QGIS layer try: myIntersec = myQgisPostprocGeom.intersection( myQgisPolyGeom) # if myIntersec is not None: myIntersecGeom = QgsGeometry(myIntersec) #from ftools myUnknownGeomType = 0 if myIntersecGeom.wkbType() == myUnknownGeomType: int_com = myQgisPostprocGeom.combine( myQgisPolyGeom) int_sym = myQgisPostprocGeom.symDifference( myQgisPolyGeom) myIntersecGeom = QgsGeometry( int_com.difference(int_sym)) # LOGGER.debug('wkbType type of intersection: %s' % # myIntersecGeom.wkbType()) polygonTypesList = [QGis.WKBPolygon, QGis.WKBMultiPolygon] if myIntersecGeom.wkbType() in polygonTypesList: myInsideFeat.setGeometry(myIntersecGeom) myInsideFeat.setAttributeMap(myAtMap) mySHPWriter.addFeature(myInsideFeat) self.preprocessedFeatureCount += 1 else: pass # LOGGER.debug('Intersection not a polygon so ' # 'the two polygons either touch ' # 'only or do not intersect. Not ' # 'adding this to the inside list') #Part of the polygon that is outside the postprocpoly myOutside = myQgisPolyGeom.difference(myIntersecGeom) # if myOutside is not None: myOutsideGeom = QgsGeometry(myOutside) if myOutsideGeom.wkbType() in polygonTypesList: # modifiy the original geometry to the part # outside of the postproc polygon polygonsProvider.changeGeometryValues( {myFeatId: myOutsideGeom}) # we need this polygon in the next iteration myOutsidePolygons.append(myMappedIndex) myNextIterPolygons.append(i) except TypeError: LOGGER.debug('ERROR with FID %s', myMappedIndex) # LOGGER.debug('Inside %s' % myInsidePolygons) # LOGGER.debug('Outside %s' % myOutsidePolygons) # LOGGER.debug('Intersec %s' % myIntersectingPolygons) if len(myNextIterPolygons) > 0: #some polygons are still completely outside of the postprocPoly #so go on and reiterate using only these nextIterPolygonsIndex = numpy.array(myNextIterPolygons) myRemainingPolygons = myRemainingPolygons[ nextIterPolygonsIndex] # myRemainingAttributes = myRemainingAttributes[ # nextIterPolygonsIndex] myRemainingIndexes = myRemainingIndexes[nextIterPolygonsIndex] LOGGER.debug('Remaining: %s' % len(myRemainingPolygons)) else: print 'no more polygons to be checked' break # del tmpWriter # here the full polygon set is represented by: # myInsidePolygons + myIntersectingPolygons + myNextIterPolygons # the a polygon intersecting multiple postproc polygons appears # multiple times in the array # noinspection PyUnboundLocalVariable LOGGER.debug('Results:\nInside: %s\nIntersect: %s\nOutside: %s' % ( myInsidePolygons, myIntersectingPolygons, myOutsidePolygons)) #add in- and outside polygons for i in myOutsidePolygons: myFeatId = i + 1 polygonsProvider.featureAtId(myFeatId, myQgisFeat, True, allPolygonAttrs) mySHPWriter.addFeature(myQgisFeat) self.preprocessedFeatureCount += 1 del mySHPWriter # LOGGER.debug('Created: %s' % self.preprocessedFeatureCount) myName = '%s %s' % (theQgisLayer.name(), self.tr('preprocessed')) myOutLayer = QgsVectorLayer(myOutFilename, myName, 'ogr') if not myOutLayer.isValid(): #TODO (MB) use a better exception raise Exception('Invalid qgis Layer') if self.showIntermediateLayers: self.keywordIO.update_keywords(myOutLayer, {'title': myName}) QgsMapLayerRegistry.instance().addMapLayer(myOutLayer) return myOutLayer
def aggregate(self, theSafeImpactLayer): """Do any requested aggregation post processing. Performs Aggregation postprocessing step by * creating a copy of the dataset clipped by the impactlayer bounding box * stripping all attributes beside the aggregation attribute * delegating to the appropriate aggregator for raster and vectors :raises: ReadLayerError """ if not self.isValid: raise InvalidAggregatorError myMessage = m.Message( m.Heading(self.tr('Aggregating results'), **PROGRESS_UPDATE_STYLE), m.Paragraph(self.tr( 'This may take a little while - we are aggregating the impact' ' by %1').arg(self.layer.name()))) self._sendMessage(myMessage) myQGISImpactLayer = safe_to_qgis_layer(theSafeImpactLayer) if not myQGISImpactLayer.isValid(): myMessage = self.tr('Error when reading %1').arg(myQGISImpactLayer) # noinspection PyExceptionInherit raise ReadLayerError(myMessage) myLayerName = str(self.tr('%1 aggregated to %2').arg( myQGISImpactLayer.name()).arg(self.layer.name())) #delete unwanted fields myProvider = self.layer.dataProvider() myFields = myProvider.fields() #mark important attributes as needed self._setPersistantAttributes() myUnneededAttributes = [] for i in myFields: if (myFields[i].name() not in self.attributes.values()): myUnneededAttributes.append(i) LOGGER.debug('Removing this attributes: ' + str(myUnneededAttributes)) # noinspection PyBroadException try: self.layer.startEditing() myProvider.deleteAttributes(myUnneededAttributes) self.layer.commitChanges() # FIXME (Ole): Disable pylint check for the moment # Need to work out what exceptions we will catch here, though. except: # pylint: disable=W0702 myMessage = self.tr('Could not remove the unneeded fields') LOGGER.debug(myMessage) del myUnneededAttributes, myProvider, myFields self.keywordIO.update_keywords( self.layer, {'title': myLayerName}) self.statisticsType, self.statisticsClasses = ( self.keywordIO.get_statistics(myQGISImpactLayer)) #call the correct aggregator if myQGISImpactLayer.type() == QgsMapLayer.VectorLayer: self._aggregateVectorImpact(myQGISImpactLayer, theSafeImpactLayer) elif myQGISImpactLayer.type() == QgsMapLayer.RasterLayer: self._aggregateRasterImpact(myQGISImpactLayer) else: myMessage = self.tr('%1 is %2 but it should be either vector or ' 'raster').\ arg(myQGISImpactLayer.name()).arg(myQGISImpactLayer.type()) # noinspection PyExceptionInherit raise ReadLayerError(myMessage) # show a styled aggregation layer if self.showIntermediateLayers: if self.statisticsType == 'sum': #style layer if we are summing myProvider = self.layer.dataProvider() myAttr = self._sumFieldName() myAttrIndex = myProvider.fieldNameIndex(myAttr) myProvider.select([myAttrIndex], QgsRectangle(), False) myFeature = QgsFeature() myHighestVal = 0 while myProvider.nextFeature(myFeature): myAttrMap = myFeature.attributeMap() myVal, ok = myAttrMap[myAttrIndex].toInt() if ok and myVal > myHighestVal: myHighestVal = myVal myClasses = [] myColors = ['#fecc5c', '#fd8d3c', '#f31a1c'] myStep = int(myHighestVal / len(myColors)) myCounter = 0 for myColor in myColors: myMin = myCounter myCounter += myStep myMax = myCounter myClasses.append( {'min': myMin, 'max': myMax, 'colour': myColor, 'transparency': 30, 'label': '%s - %s' % (myMin, myMax)}) myCounter += 1 myStyle = {'target_field': myAttr, 'style_classes': myClasses} set_vector_graduated_style(self.layer, myStyle) else: #make style of layer pretty much invisible myProps = {'style': 'no', 'color_border': '0,0,0,127', 'width_border': '0.0' } # noinspection PyCallByClass,PyTypeChecker,PyArgumentList mySymbol = QgsFillSymbolV2.createSimple(myProps) myRenderer = QgsSingleSymbolRendererV2(mySymbol) self.layer.setRendererV2(myRenderer) self.layer.saveDefaultStyle()