def convertToPolygon(self, geom): if QgsWkbTypes.geometryType(geom.wkbType( )) == QgsWkbTypes.PointGeometry and geom.geometry().nCoordinates() < 3: raise QgsProcessingException( self.tr('Cannot convert from Point to Polygon').format( QgsWkbTypes.displayString(geom.wkbType()))) elif QgsWkbTypes.geometryType( geom.wkbType()) == QgsWkbTypes.PointGeometry: # multipoint with at least 3 points # TODO: mega inefficient - needs rework when geometry iterators land # (but at least it doesn't lose Z/M values) points = [] for g in geom.geometry().coordinateSequence(): for r in g: for p in r: points.append(p) linestring = QgsLineString(points) linestring.close() p = QgsPolygonV2() p.setExteriorRing(linestring) return [QgsGeometry(p)] elif QgsWkbTypes.geometryType( geom.wkbType()) == QgsWkbTypes.LineGeometry: if QgsWkbTypes.isMultiType(geom): parts = [] for i in range(geom.geometry().numGeometries()): p = QgsPolygonV2() linestring = geom.geometry().geometryN(i).clone() linestring.close() p.setExteriorRing(linestring) parts.append(QgsGeometry(p)) return QgsGeometry.collectGeometry(parts) else: # linestring to polygon p = QgsPolygonV2() linestring = geom.geometry().clone() linestring.close() p.setExteriorRing(linestring) return [QgsGeometry(p)] else: #polygon if QgsWkbTypes.isMultiType(geom): return geom.asGeometryCollection() else: return [geom]
def getSurfaces(self, geometry): surfaces = [] if isinstance(geometry, QgsGeometryCollection): # collection for i in range(geometry.numGeometries()): surfaces.extend(self.getSurfaces(geometry.geometryN(i))) else: # not collection if geometry.vertexCount() > 2: surface = QgsPolygonV2() surface.setExteriorRing(geometry.clone()) surfaces.append(surface) return surfaces
def _addGridPolygonsToLayer(self, layer, transformer, originX, intervalX, repeatX, originY, intervalY, repeatY, attributes, localFieldX='local_x', localFieldY='local_x', mapFieldX='map_x', mapFieldY='map_y'): if (layer is None or not layer.isValid() or layer.geometryType() != QGis.Polygon): return features = [] for localX in range(originX, originX + intervalX * repeatX, intervalX): for localY in range(originY, originY + intervalY * repeatY, intervalY): localPoint = QgsPointV2(localX, localY) mapPoint = transformer.map(localPoint) points = [] points.append(transformer.map(localPoint)) points.append( transformer.map(QgsPointV2(localX, localY + intervalY))) points.append( transformer.map( QgsPointV2(localX + intervalX, localY + intervalY))) points.append( transformer.map(QgsPointV2(localX + intervalX, localY))) feature = QgsFeature(layer.dataProvider().fields()) lineGeometry = QgsLineStringV2() lineGeometry.setPoints(points) polygonGeometry = QgsPolygonV2() polygonGeometry.setExteriorRing(lineGeometry) feature.setGeometry(QgsGeometry(polygonGeometry)) self._setAttributes(feature, attributes) feature.setAttribute(localFieldX, localX) feature.setAttribute(localFieldY, localY) feature.setAttribute(mapFieldX, mapPoint.x()) feature.setAttribute(mapFieldY, mapPoint.y()) features.append(feature) layers.addFeatures(features, layer)
def canvasMoveEvent(self, event): """ When the mouse is moved :param event: mouse event """ if self.__selecting: self.__rubber.reset() firstV2 = QgsPointV2(self.first) second = QgsPointV2(self.first.x(), event.mapPoint().y()) third = QgsPointV2(event.mapPoint()) fourth = QgsPointV2(event.mapPoint().x(), self.first.y()) lineV2 = QgsLineStringV2() lineV2.setPoints([firstV2, second, third, fourth, firstV2]) polygonV2 = QgsPolygonV2() polygonV2.setExteriorRing(lineV2) self.geom = QgsGeometry(polygonV2) self.__rubber.setToGeometry(self.geom, None)
def run(self): """Run method that performs all the real work""" self.dlg.show() # show the dialog result = self.dlg.exec_() # Run the dialog event loop if result: # The user pressed OK, and this is what happened next! survex3dfile = self.dlg.selectedFile.text() gpkg_file = self.dlg.selectedGPKG.text() include_legs = self.dlg.Legs.isChecked() include_stations = self.dlg.Stations.isChecked() include_polygons = self.dlg.Polygons.isChecked() include_walls = self.dlg.Walls.isChecked() include_xsections = self.dlg.XSections.isChecked() include_traverses = self.dlg.Traverses.isChecked() exclude_surface_legs = not self.dlg.LegsSurface.isChecked() exclude_splay_legs = not self.dlg.LegsSplay.isChecked() exclude_duplicate_legs = not self.dlg.LegsDuplicate.isChecked() exclude_surface_stations = not self.dlg.StationsSurface.isChecked() use_clino_wgt = self.dlg.UseClinoWeights.isChecked() include_up_down = self.dlg.IncludeUpDown.isChecked() discard_features = not self.dlg.KeepFeatures.isChecked() get_crs_from_file = self.dlg.CRSFromFile.isChecked() get_crs_from_project = self.dlg.CRSFromProject.isChecked() if not os.path.exists(survex3dfile): raise Exception("File '%s' doesn't exist" % survex3dfile) if discard_features: self.leg_list = [] self.station_list = [] self.station_xyz = {} self.xsect_list = [] # Read .3d file as binary, parse, and save data structures with open(survex3dfile, 'rb') as fp: line = fp.readline().rstrip() # File ID check if not line.startswith(b'Survex 3D Image File'): raise IOError('Not a survex .3d file: ' + survex3dfile) line = fp.readline().rstrip() # File format version if not line.startswith(b'v'): raise IOError('Unrecognised survex .3d version in ' + survex3dfile) version = int(line[1:]) if version < 8: raise IOError('Survex .3d version >= 8 required in ' + survex3dfile) line = fp.readline().rstrip( ) # Metadata (title and coordinate system) fields = line.split(b'\x00') previous_title = '' if discard_features else self.title if previous_title: self.title = previous_title + ' + ' + fields[0] else: self.title = fields[0] # Try to work out EPSG number from second field if available. # The project_crs should end up as a lowercase string like 'epsg:27700' if get_crs_from_project: project_crs = self.iface.mapCanvas().mapRenderer( ).destinationCrs() self.extract_epsg(project_crs.authid().lower()) elif get_crs_from_file and len(fields) > 1: self.extract_epsg(fields[1]) else: self.epsg = None line = fp.readline().rstrip( ) # Timestamp, unused in present application if not line.startswith(b'@'): raise IOError('Unrecognised timestamp in ' + survex3dfile) # timestamp = int(line[1:]) flag = ord(fp.read(1)) # file-wide flag if flag & 0x80: # abort if extended elevation raise IOError("Can't deal with extended elevation in " + survex3dfile) # All front-end data read in, now read byte-wise # according to .3d spec. Note that all elements must # be processed, in order, otherwise we get out of sync. # We first define some baseline dates date0 = QDate(1900, 1, 1) date1 = QDate(1900, 1, 1) date2 = QDate(1900, 1, 1) label, style = '', 0xff # initialise label and style legs = [] # will be used to capture leg data between MOVEs xsect = [] # will be used to capture XSECT data nlehv = None # .. remains None if there isn't any error data... while True: # start of byte-gobbling while loop char = fp.read(1) if not char: # End of file (reached prematurely?) raise IOError('Premature end of file in ' + survex3dfile) byte = ord(char) if byte <= 0x05: # STYLE if byte == 0x00 and style == 0x00: # this signals end of data if legs: # there may be a pending list of legs to save self.leg_list.append((legs, nlehv)) break # escape from byte-gobbling while loop else: style = byte elif byte <= 0x0e: # Reserved continue elif byte == 0x0f: # MOVE xyz = self.read_xyz(fp) if legs: self.leg_list.append((legs, nlehv)) legs = [] elif byte == 0x10: # DATE (none) date1 = date2 = date0 elif byte == 0x11: # DATE (single date) days = unpack('<H', fp.read(2))[0] date1 = date2 = date0.addDays(days) elif byte == 0x12: # DATE (date range, short format) days, extra = unpack('<HB', fp.read(3)) date1 = date0.addDays(days) date2 = date0.addDays(days + extra + 1) elif byte == 0x13: # DATE (date range, long format) days1, days2 = unpack('<HH', fp.read(4)) date1 = date0.addDays(days1) date2 = date0.addDays(days2) elif byte <= 0x1e: # Reserved continue elif byte == 0x1f: # Error info nlehv = unpack('<iiiii', fp.read(20)) elif byte <= 0x2f: # Reserved continue elif byte <= 0x33: # XSECT label = self.read_label(fp, label) if byte & 0x02: lrud = unpack('<iiii', fp.read(16)) else: lrud = unpack('<hhhh', fp.read(8)) xsect.append((label, lrud)) if byte & 0x01: # XSECT_END self.xsect_list.append(xsect) xsect = [] elif byte <= 0x3f: # Reserved continue elif byte <= 0x7f: # LINE flag = byte & 0x3f if not (flag & 0x20): label = self.read_label(fp, label) xyz_prev = xyz xyz = self.read_xyz(fp) while (True): # code pattern to implement logic if exclude_surface_legs and flag & 0x01: break if exclude_duplicate_legs and flag & 0x02: break if exclude_splay_legs and flag & 0x04: break legs.append(((xyz_prev, xyz), label, style, date1, date2, flag)) break elif byte <= 0xff: # LABEL (or NODE) flag = byte & 0x7f label = self.read_label(fp, label) xyz = self.read_xyz(fp) while (True): # code pattern to implement logic if exclude_surface_stations and flag & 0x01 and not flag & 0x02: break self.station_list.append((xyz, label, flag)) break self.station_xyz[label] = xyz # End of byte-gobbling while loop # file closes automatically, with open(survex3dfile, 'rb') as fp: # Now create the layers in QGIS. Attributes are inserted # like pushing onto a stack, so in reverse order. Layers # are created only if required and data is available. # If nlehv is still None, then no error data has been provided. layers = [] # used to keep a list of the created layers if include_stations and self.station_list: # station layer station_layer = self.add_layer('stations', 'Point') attrs = [ QgsField(self.station_attr[k], QVariant.Int) for k in self.station_flags ] attrs.insert(0, QgsField('ELEVATION', QVariant.Double)) attrs.insert(0, QgsField('NAME', QVariant.String)) station_layer.dataProvider().addAttributes(attrs) station_layer.updateFields() features = [] for (xyz, label, flag) in self.station_list: xyz = [0.01 * v for v in xyz] attrs = [1 if flag & k else 0 for k in self.station_flags] attrs.insert(0, round(xyz[2], 2)) # elevation attrs.insert(0, label) feat = QgsFeature() geom = QgsGeometry(QgsPointV2(QgsWKBTypes.PointZ, *xyz)) feat.setGeometry(geom) feat.setAttributes(attrs) features.append(feat) station_layer.dataProvider().addFeatures(features) layers.append(station_layer) if include_legs and self.leg_list: # leg layer leg_layer = self.add_layer('legs', 'LineString') attrs = [ QgsField(self.leg_attr[k], QVariant.Int) for k in self.leg_flags ] if nlehv: [ attrs.insert(0, QgsField(s, QVariant.Double)) for s in self.error_fields ] attrs.insert(0, QgsField('NLEGS', QVariant.Int)) attrs.insert(0, QgsField('DATE2', QVariant.Date)) attrs.insert(0, QgsField('DATE1', QVariant.Date)) attrs.insert(0, QgsField('STYLE', QVariant.String)) attrs.insert(0, QgsField('ELEVATION', QVariant.Double)) attrs.insert(0, QgsField('NAME', QVariant.String)) leg_layer.dataProvider().addAttributes(attrs) leg_layer.updateFields() features = [] for legs, nlehv in self.leg_list: for (xyz_pair, label, style, from_date, to_date, flag) in legs: elev = 0.5 * sum([0.01 * xyz[2] for xyz in xyz_pair]) points = [] for xyz in xyz_pair: xyz = [0.01 * v for v in xyz] points.append(QgsPointV2(QgsWKBTypes.PointZ, *xyz)) attrs = [1 if flag & k else 0 for k in self.leg_flags] if nlehv: [ attrs.insert(0, 0.01 * v) for v in reversed(nlehv[1:5]) ] attrs.insert(0, nlehv[0]) attrs.insert(0, to_date) attrs.insert(0, from_date) attrs.insert(0, self.style_type[style]) attrs.insert(0, round(elev, 2)) attrs.insert(0, label) linestring = QgsLineStringV2() linestring.setPoints(points) feat = QgsFeature() geom = QgsGeometry(linestring) feat.setGeometry(geom) feat.setAttributes(attrs) features.append(feat) leg_layer.dataProvider().addFeatures(features) layers.append(leg_layer) # Now do wall features if asked if (include_traverses or include_xsections or include_walls or include_polygons) and self.xsect_list: trav_features = [] wall_features = [] xsect_features = [] quad_features = [] for xsect in self.xsect_list: if len(xsect) < 2: # if there's only one station .. continue # .. give up as we don't know which way to face centerline = [ ] # will contain the station position and LRUD data for label, lrud in xsect: xyz = self.station_xyz[ label] # look up coordinates from label lrud_or_zero = tuple([max(0, v) for v in lrud ]) # deal with missing data centerline.append( xyz + lrud_or_zero) # and collect as 7-uple direction = [ ] # will contain the corresponding direction vectors # The calculations below use integers for xyz and lrud, and # conversion to metres is left to the end. Then dh2 is an # integer and the test for a plumb is safely dh2 = 0. # The directions are unit vectors optionally weighted by # cos(inclination) = dh/dl where dh^2 = dx^2 + dy^2 + dz^2 # and dl^2 = dh^2 + dz^2. The normalisation is correspondingly # either 1/dh, or 1/dh * dh/dl = 1/dl. for i, xyzlrud in enumerate(centerline): x, y, z = xyzlrud[0:3] if i > 0: dx, dy, dz = x - xp, y - yp, z - zp dh2 = dx * dx + dy * dy # integer horizontal displacement (mm^2) norm = sqrt(dh2 + dz * dz) if use_clino_wgt else sqrt(dh2) dx, dy = (dx / norm, dy / norm) if dh2 > 0 and norm > 0 else (0, 0) direction.append((dx, dy)) xp, yp, zp = x, y, z left_wall = [] right_wall = [] up_down = [] # We build the walls by walking through the list # of stations and directions, with simple defaults # for the start and end stations for i, (x, y, z, l, r, u, d) in enumerate(centerline): d1x, d1y = direction[i - 1] if i > 0 else (0, 0) d2x, d2y = direction[i] if i + 1 < len( centerline) else (0, 0) dx, dy = d1x + d2x, d1y + d2y # mean (sum of) direction vectors norm = sqrt(dx * dx + dy * dy) # normalise to unit vector ex, ey = (dx / norm, dy / norm) if norm > 0 else (0, 0) # Convert to metres when saving the points left_wall.append((0.01 * (x - l * ey), 0.01 * (y + l * ex), 0.01 * z)) right_wall.append((0.01 * (x + r * ey), 0.01 * (y - r * ex), 0.01 * z)) up_down.append((0.01 * u, 0.01 * d)) # Mean elevation of centerline, used for elevation attribute elev = 0.01 * sum([xyzlrud[2] for xyzlrud in centerline ]) / len(centerline) attrs = [round(elev, 2)] # Now create the feature sets - first the centerline traverse points = [] for xyzlrud in centerline: xyz = [0.01 * v for v in xyzlrud[0:3] ] # These were mm, convert to metres points.append(QgsPointV2(QgsWKBTypes.PointZ, *xyz)) linestring = QgsLineStringV2() linestring.setPoints(points) feat = QgsFeature() geom = QgsGeometry(linestring) feat.setGeometry(geom) feat.setAttributes(attrs) trav_features.append(feat) # The walls as line strings for wall in (left_wall, right_wall): points = [ QgsPointV2(QgsWKBTypes.PointZ, *xyz) for xyz in wall ] linestring = QgsLineStringV2() linestring.setPoints(points) feat = QgsFeature() geom = QgsGeometry(linestring) feat.setGeometry(geom) feat.setAttributes(attrs) wall_features.append(feat) # Slightly more elaborate, pair up points on left # and right walls, and build a cross section as a # 2-point line string, and a quadrilateral polygon # with a closed 5-point line string for the # exterior ring. Note that QGIS polygons are # supposed to have their points ordered clockwise. for i, xyz_pair in enumerate(zip(left_wall, right_wall)): elev = 0.01 * centerline[i][ 2] # elevation of station in centerline attrs = [round(elev, 2)] points = [ QgsPointV2(QgsWKBTypes.PointZ, *xyz) for xyz in xyz_pair ] linestring = QgsLineStringV2() linestring.setPoints(points) feat = QgsFeature() geom = QgsGeometry(linestring) feat.setGeometry(geom) feat.setAttributes(attrs) xsect_features.append(feat) if i > 0: elev = 0.5 * (prev_xyz_pair[0][2] + xyz_pair[0][2] ) # average elevation attrs = [round(elev, 2)] if include_up_down: # average up / down attrs += [ 0.5 * (v1 + v2) for (v1, v2) in zip(up_down[i - 1], up_down[i]) ] points = [ ] # will contain the exterior 5-point ring, as follows... for xyz in tuple( reversed(prev_xyz_pair)) + xyz_pair + ( prev_xyz_pair[1], ): points.append( QgsPointV2(QgsWKBTypes.PointZ, *xyz)) linestring = QgsLineStringV2() linestring.setPoints(points) polygon = QgsPolygonV2() polygon.setExteriorRing(linestring) feat = QgsFeature() geom = QgsGeometry(polygon) feat.setGeometry(geom) feat.setAttributes(attrs) quad_features.append(feat) prev_xyz_pair = xyz_pair # End of processing xsect_list - now add features to requested layers attrs = [QgsField('ELEVATION', QVariant.Double)] # common to all if include_traverses and trav_features: # traverse layer travs_layer = self.add_layer('traverses', 'LineString') travs_layer.dataProvider().addAttributes(attrs) travs_layer.updateFields() travs_layer.dataProvider().addFeatures(trav_features) layers.append(travs_layer) if include_xsections and xsect_features: # xsection layer xsects_layer = self.add_layer('xsections', 'LineString') xsects_layer.dataProvider().addAttributes(attrs) xsects_layer.updateFields() xsects_layer.dataProvider().addFeatures(xsect_features) layers.append(xsects_layer) if include_walls and wall_features: # wall layer walls_layer = self.add_layer('walls', 'LineString') walls_layer.dataProvider().addAttributes(attrs) walls_layer.updateFields() walls_layer.dataProvider().addFeatures(wall_features) layers.append(walls_layer) if include_up_down: # add fields if requested for polygons attrs += [ QgsField(s, QVariant.Double) for s in ('MEAN_UP', 'MEAN_DOWN') ] if include_polygons and quad_features: # polygon layer quads_layer = self.add_layer('polygons', 'Polygon') quads_layer.dataProvider().addAttributes(attrs) quads_layer.updateFields() quads_layer.dataProvider().addFeatures(quad_features) layers.append(quads_layer) # All layers have been created, now update extents and add to QGIS registry if layers: [layer.updateExtents() for layer in layers] QgsMapLayerRegistry.instance().addMapLayers(layers) # Save layers to a GeoPackage if selected. # QgsVectorFileWriter would be ideal but it can only write # single layers (afaik!), so the GeoPackage layers, # fields, and attributes are created using OGR calls, # translating the corresponding QGIS objects on the fly. # It's possible this could be done faster by iterating # numerically over the attributes but I'm not sure the OGR # features would be visited in the right order, so here # use the field names as indices. Meanwhile, the user is # appraised of progress by a progress bar. if gpkg_file: nfeatures = 0 # how many features in total for layer in layers: nfeatures += layer.featureCount() if nfeatures > 100: # create a progress bar progress_bar = QProgressBar() progress_bar.setMaximum(nfeatures) progress_bar.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) msg = 'Saving ' + QFileInfo(gpkg_file).fileName() progressMessageBar = self.iface.messageBar().createMessage( msg) progressMessageBar.layout().addWidget(progress_bar) self.iface.messageBar().pushWidget( progressMessageBar, self.iface.messageBar().INFO) ntrack = int(10**(floor(log10(nfeatures)) - 1)) # update frequency else: progress_bar = None gpkg_driver = ogr.GetDriverByName('GPKG') if os.path.exists(gpkg_file): gpkg_driver.DeleteDataSource(gpkg_file) ogr_dataset = gpkg_driver.CreateDataSource(gpkg_file) if self.epsg: # figure out the spatial reference system in OGR terms ogr_srs = osr.SpatialReference() ogr_srs.ImportFromEPSG(self.epsg) else: ogr_srs = None ncount = 0 # to keep track of number of features processed for layer in layers: # go through all the layers qgis_name = layer.name() match = search(' - ([a-z]*)', qgis_name) ogr_name = str(match.group( 1)) if match else qgis_name # ie, legs, stations, etc ogr_type = self.ogr_vec_type[layer.wkbType()] ogr_layer = ogr_dataset.CreateLayer(ogr_name, srs=ogr_srs, geom_type=ogr_type) qgis_fields = layer.pendingFields() names = [str(field.name()) for field in qgis_fields] types = [ self.ogr_type[field.type()] for field in qgis_fields ] ogr_type_of_ = dict(zip( names, types)) # map field names to OGR field types [ ogr_layer.CreateField( ogr.FieldDefn(name, ogr_type_of_[name])) for name in names ] ogr_schema = ogr_layer.GetLayerDefn( ) # for creating features in the OGR layer for qgis_feat in layer.getFeatures( ): # go through all the features ncount += 1 # update feature count if progress_bar and ncount % ntrack: # update progress bar progress_bar.setValue(ncount) ogr_feat = ogr.Feature(ogr_schema) ogr_wkt = qgis_feat.geometry().exportToWkt().replace( *self.wkt_replace[ogr_type]) ogr_feat.SetGeometry( ogr.CreateGeometryFromWkt(ogr_wkt)) qgis_attrs = qgis_feat.attributes() for name, qgis_attr in zip(names, qgis_attrs): if ogr_type_of_[ name] == ogr.OFTString: # fix for strings ogr_feat.SetField(name, str(qgis_attr)) elif ogr_type_of_[ name] == ogr.OFTDate: # translate dates ogr_feat.SetField( name, str(qgis_attr.toString(Qt.ISODate))) else: # everything else just passes through ogr_feat.SetField(name, qgis_attr) ogr_layer.CreateFeature(ogr_feat) ogr_dataset = None # all done, flush to disk if progress_bar: # clean up and free resources self.iface.messageBar().clearWidgets() progress_bar = None msg = QFileInfo(gpkg_file).fileName() + ' to ' + QFileInfo( gpkg_file).path() QgsMessageLog.logMessage('Saved ' + msg, tag='Import .3d', level=QgsMessageLog.INFO) self.iface.messageBar().pushMessage('Saved', msg, level=QgsMessageBar.INFO, duration=5)
point = QgsPointV2(2, 1) r_point.setGeometry(point) # To show a polyline (Replace QgsRubberBand if you are using QgsAbstractGeometryV2 geometries) r_polyline = QgsGeometryRubberBand(canvas, QGis.Line) points = [QgsPointV2(-1, -1), QgsPointV2(0, 1), QgsPointV2(1, -1)] linestring_polyline = QgsLineStringV2() linestring_polyline.setPoints(points) r_polyline.setGeometry(linestring_polyline) # To show a polygon (Replace QgsRubberBand if you are using QgsAbstractGeometryV2 geometries) r_polygon = QgsGeometryRubberBand(canvas, QGis.Polygon) points = [QgsPointV2(3, 5), QgsPointV2(5, 9), QgsPointV2(8, 5)] linestring_polygon = QgsLineStringV2() linestring_polygon.setPoints(points) polygon = QgsPolygonV2() polygon.setExteriorRing(linestring_polygon) r_polygon.setGeometry(polygon) # Customize rubber band r_polygon.setBrushStyle(Qt.SolidPattern) r_polygon.setFillColor(QColor('blue')) r_polygon.setOutlineWidth(3) r_polygon.setOutlineColor(QColor(255, 0, 255)) canvas.refresh() # Remove the point, polyline and polygon QgsGeometryRubberBand band items # canvas.scene().removeItem(r_point) # canvas.scene().removeItem(r_polyline) # canvas.scene().removeItem(r_polygon)