Sunday 30 December 2018

QGIS plugin: PointByPolarCoord



In this post I will create QGIS plugin that allows you to easily create point which is defined in 'polar coordinates' way, that means: by distance and bearing against reference point with known coordinates and magnetic variation. This is also the first post of post series that are related to 'local coordinates' issue', quite common used in aviation data publication


 Result is stored in memory point layer, example:
with following attributes:


Layer can be convert into any format served by QGIS using save as option from context menu in Layer panel.

Before we start calculation let's create code that cover creating memory layer, checks input data and...

Creating memory layer is done by method:

@staticmethod
def create_mem_lyr(lyr_name):
    """ Create temporary 'memory' layer to store results.
    :param lyr_name: string, layer name
    """
    mlyr = QgsVectorLayer('Point?crs=epsg:4326', lyr_name, 'memory')
    mprov = mlyr.dataProvider()
    mlyr.startEditing()
    mprov.addAttributes([QgsField("R_ID", QVariant.String),  # Reference point ID
                         QgsField("R_LATSRC", QVariant.String),  # Reference point latitude (source)
                         QgsField("R_LONSRC", QVariant.String),  # Reference point longitude (source)
                         QgsField("PP_NAME", QVariant.String),  # 'Polar point' name
                         QgsField("PP_LATDD", QVariant.String),  # 'Polar point' latitude (decimal degrees)
                         QgsField("PP_LONDD", QVariant.String),  # 'Polar point' longitude (decimal degrees)
                         QgsField("PP_DEF", QVariant.String)])  # 'Polar point'in polar coordinates 'definition'
    mlyr.commitChanges()
    QgsProject.instance().addMapLayer(mlyr)

Adding data into this layer will be done by method:

def add_point_to_layer(self, point, attributes):
    out_lyr = self.iface.activeLayer()
    out_lyr.startEditing()
    out_prov = out_lyr.dataProvider()
    feat = QgsFeature()
    feat.setGeometry(QgsGeometry.fromPointXY(point))
    feat.setAttributes(attributes)
    out_prov.addFeatures([feat])
    out_lyr.commitChanges()
    out_lyr.updateExtents()
    self.iface.mapCanvas().setExtent(out_lyr.extent())
    self.iface.mapCanvas().refresh()

where point - is QgsPoint object, attributes - list of attributes. They will be create in method that calculates 'polar point' coordinates

So, we have function that creates layer that will keeps result of calculation and function that adds calculated point.

We need distance unit of measure, it require to check which item is chosen on the combobox:

def get_distance_uom(self):
    """ Returns distance unit of measure from calculated point to reference """
    if self.dlg.comboBoxDistUOM.currentIndex() == 0:  # m
        return lct.UOM_M
    elif self.dlg.comboBoxDistUOM.currentIndex() == 1:  # km
        return lct.UOM_KM
    elif self.dlg.comboBoxDistUOM.currentIndex() == 2:  # NM
        return lct.UOM_NM
    elif self.dlg.comboBoxDistUOM.currentIndex() == 3:  # feet
        return lct.UOM_FEET
    elif self.dlg.comboBoxDistUOM.currentIndex() == 4:  # SM
        return lct.UOM_SM

Distance and bearing are expressed against reference point, so now we create method that checks if data for reference point is valid, and if it is, it create BasePoint object:

def check_ref_point_input(self):
    """ Check if input data for reference point is correct
    :return check_result: bool, True if input data is valid, False otherwise
    :return err_msg: bool, True if input data is valid, False otherwise
    """
    check_result = True
    err_msg = ''
    # Get input data from Qt LineEdit
    ref_point_id = self.dlg.lineEditRefId.text()
    src_ref_lat = self.dlg.lineEditRefLat.text()
    src_ref_lon = self.dlg.lineEditRefLon.text()
    src_ref_mag_var = self.dlg.lineEditRefMagVar.text()

    self.ref_point = lct.BasePoint(src_ref_lat, src_ref_lon, src_ref_mag_var, ref_point_id)

    if self.ref_point.is_valid is False:
        check_result = False
        err_msg += self.ref_point.err_msg

    return check_result, err_msg

Now, its time to check if polar coordinates are correct, and it they are, assign appropriate variables that will be use to calculate latitude and longitude:

def check_polar_point_input(self):
    """ Check if input data for calculated point is correct
    :return check_result: bool, True if input data is valid, False otherwise
    :return err_msg: bool, True if input data is valid, False otherwise
    """
    # Check reference (origin) point input and assign results to variables
    check_result, err_msg = self.check_ref_point_input()
    # Get input dat from Qt LineEdit
    self.ep_name = self.dlg.lineEditEndPointName.text()
    ep_brng = self.dlg.lineEditEndPointBrng.text()
    ep_dist = self.dlg.lineEditEndPointDist.text()

    if self.ep_name == '':
        check_result = False
        err_msg += 'Enter end point name!\n'

    self.ep_brng = lct.Bearing(ep_brng)

    if self.ep_brng.is_valid is False:
        check_result = False
        err_msg += self.ep_brng.err_msg

    if ep_dist == '':
        check_result = False
        err_msg += 'Enter distance!\n'
    else:
        self.ep_dist = lct.check_distance2(ep_dist)
        if self.ep_dist == lct.NOT_VALID:
            check_result = False
            err_msg += 'Distance wrong value!\n'

    if not check_result:
        QMessageBox.critical(w, "Message", err_msg)
    return check_result

Finally we are able to calculate location of point:


def calc_point(self):
    if self.check_polar_point_input_point_input():
        dist_m = lct.to_meters(float(self.ep_dist), self.get_distance_uom())
        polar_point = lct.PolarCoordPoint(self.ref_point, self.ep_brng, dist_m)
        true_mag = 'MAG'
        if self.ref_point.mag_var.src_value == '':
            true_mag = 'TRUE'
        # Calculated point 'definition': expressed in polar coordinates
        pp_def = 'Ref point: {} Bearing {} {} Distance: {} {}'.format(self.ref_point.origin_id,
                                                                      self.ep_brng.src_value, true_mag,
                                                                      self.ep_dist, self.get_distance_uom())

        pp_qgs_point = QgsPointXY(polar_point.ep_lon_dd, polar_point.ep_lat_dd)
        pp_attributes = [self.ref_point.origin_id,
                         self.ref_point.src_lat,
                         self.ref_point.src_lon,
                         self.ep_name,
                         polar_point.ep_lat_dd,
                         polar_point.ep_lon_dd,
                         pp_def]

        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 = lct.get_tmp_name()

        if self.mlyr_name not in layers_list:
            self.create_mem_lyr(self.mlyr_name)
            self.add_point_to_layer(pp_qgs_point, pp_attributes)
        else:
            self.add_point_to_layer(pp_qgs_point, pp_attributes)




No comments:

Post a Comment