Wouldn't be convenient to create circle by passing center latitude and longitude and radius as it they are given in source - in terms of coordinate format and unit of measure?
A real-world example:
Lateral limits of airspace:
A CIRCLE OF 10NM RADIUS CENTERED AT 142230N0763430E
Let's start with defining variables that keeps circle center coordinates, raidus of circle, name of temporary layer and polygon name:
self.circ_center = None # Circle center coordinate pair self.radius_m = None # Circle radius in meters self.mlyr_name = '' # Output layer name self.polygon_name = ''
Now it's time to code behaviour of QWidgets that we used in plugin:
When user click button Add, function add_circle will be executed. This function will cover the all work: adding circle to layer.
self.dlg.pushButtonAddCircle.clicked.connect(self.add_circle)
To get circle radius unit of measure we need check which option is chosesn from comboBoxRadiusUOM and assign appropriate constant:
def get_radius_uom(self): """ Returns radius unit of measure """ if self.dlg.comboBoxRadiusUOM.currentIndex() == 0: # m return ct.UOM_M elif self.dlg.comboBoxRadiusUOM.currentIndex() == 1: # km return ct.UOM_KM elif self.dlg.comboBoxRadiusUOM.currentIndex() == 2: # NM return ct.UOM_NM elif self.dlg.comboBoxRadiusUOM.currentIndex() == 3: # feet return ct.UOM_FEET elif self.dlg.comboBoxRadiusUOM.currentIndex() == 4: # SM return ct.UOM_SM
We also need to check if input data is correct - method check_input():
def check_input(self): """ Checks if input data is correct """ err_msg = '' check_result = True
Variables:
- err_msg: keeps message with errors that in case some of required input data (e. g. distance) is incorrect of missing
- check_result: keeps if input is correct (True) or incorrect (False)
Input data validation will be carried out as follows:
- get value of QLineEdit widget that is appropriate for required input (e. g. distance)
- check if the value is valid
- if it is not valid:
- set check_result vale to False
- assign to err_msg variable error message
lat_src = self.dlg.lineEditCenterLat.text() lon_src = self.dlg.lineEditCenterLon.text() radius_src = self.dlg.lineEditRadius.text() self.circ_center = ct.CoordinatesPair(lat_src, lon_src) if self.circ_center.is_valid is False: check_result = False err_msg = self.circ_center.err_msg if radius_src == '': check_result = False err_msg += 'Enter radius!' else: radius_check = ct.check_distance2(radius_src) if radius_check == ct.NOT_VALID: check_result = False err_msg += 'Radius error!' else: self.radius_m = ct.convert_distance(radius_check, self.get_radius_uom(), ct.UOM_M) if check_result is False: QMessageBox.critical(w, "Message", err_msg) return check_result
Now, if we check input data, we are almost ready to create polygons. But before we are going to fill in method add_circle() with code, let's write method that creates memory layer:
@staticmethod def create_mem_lyr(lyr_name): """ Create temporary 'memory' layer to store results. :param lyr_name: string, layer name """ mlyr = QgsVectorLayer('Polygon?crs=epsg:4326', lyr_name, 'memory') prov = mlyr.dataProvider() mlyr.startEditing() prov.addAttributes([QgsField("PNAME", QVariant.String)]) mlyr.commitChanges() QgsProject.instance().addMapLayer(mlyr)
and adds polygon to memory layer:
def add_feature_to_layer(self, boundary): """ Add feature to layer :param: boundary: list, list of QgsPoint objects """ out_lyr = QgsVectorLayer('Polygon?crs=epsg:4326', self.mlyr_name, 'memory') out_lyr = self.iface.activeLayer() out_lyr.startEditing() out_prov = out_lyr.dataProvider() feat = QgsFeature() feat.setGeometry(QgsGeometry.fromPolygonXY([boundary])) feat.setAttributes([self.polygon_name]) out_prov.addFeatures([feat]) out_lyr.commitChanges() out_lyr.updateExtents() self.iface.mapCanvas().setExtent(out_lyr.extent()) self.iface.mapCanvas().refresh()
Finally, we can write method that creates circle and adds it into memory layer:
- call method check_input()
- if method check_input() returns True (input is correct) calculate vertices on circle
- create list of QgsPointXY objects
- add polygon to layer
def add_circle(self): if self.check_input(): # Create circle polygon poly_vertices = [] for i in range(0, 360): vertex_lat, vertex_lon = ct.vincenty_direct_solution(self.circ_center.dd_lat, self.circ_center.dd_lon, i, self.radius_m, ct.WGS84_A, ct.WGS84_B, ct.WGS84_F) poly_vertex = QgsPointXY(vertex_lon, vertex_lat) poly_vertices.append(poly_vertex) layers = QgsProject.instance().layerTreeRoot().children() layers_list = [] # List of layers in current (opened) QGIS project for layer in layers: layers_list.append(layer.name()) if self.mlyr_name == '': self.mlyr_name = ct.get_tmp_name() self.polygon_name = self.dlg.lineEditPolyName.text() if self.mlyr_name not in layers_list: self.create_mem_lyr(self.mlyr_name) self.add_feature_to_layer(poly_vertices) else: self.add_feature_to_layer(poly_vertices)
Source code and plugin can be download from: https://github.com/strpaw/QGIS3-CreateCircle-plugin
No comments:
Post a Comment