Пример #1
0
def get_gc_positions(start, end):
    """
    Get positions along a Great Circle to enable plotting.
    I couldn't do this with Basemap as we have to instantiate
    first and we don't know the map boundaries until we have the GC
    positions.

    :param start: The start position
    :type start: tuple
    :param end: The end position
    :type end: tuple
    :return: list of positions
    :rtype: list of dict
    """
    positions = []
    spacing = 100000  # Positions 100km apart
    geoid = Geodesic(Constants.WGS84_a, Constants.WGS84_f)
    gc = geoid.InverseLine(
            start[0], start[1],
            end[0], end[1]
    )

    n = math.ceil(gc.s13 / spacing)

    for i in range(n + 1):
        s = min(spacing * i, gc.s13)
        result = gc.Position(s, Geodesic.STANDARD | Geodesic.LONG_UNROLL)
        position = {
            'Lat': result['lat2'],
            'Lon': result['lon2']
        }
        positions.append(position)

    return positions
Пример #2
0
class GeodesicDensifier:
    """QGIS Plugin Implementation."""
    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)

        # Create the dialog (after translation) and keep reference
        self.dlg = GeodesicDensifierDialog()
        self.dlg.mMapLayerComboBox.setFilters(
            QgsMapLayerProxyModel.LineLayer
            | QgsMapLayerProxyModel.PolygonLayer
            | QgsMapLayerProxyModel.PointLayer)
        # Declare instance attributes
        self.actions = []
        self.menu = u'&Geodesic Densifier'
        self.toolbar = self.iface.addToolBar(u'GeodesicDensifier')
        self.toolbar.setObjectName(u'GeodesicDensifier')

    def add_action(self,
                   icon_path,
                   text,
                   callback,
                   enabled_flag=True,
                   add_to_menu=True,
                   add_to_toolbar=True,
                   status_tip=None,
                   whats_this=None,
                   parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        # Create the dialog (after translation) and keep reference
        self.dlg = GeodesicDensifierDialog()

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/GeodesicDensifier/icon.png'
        self.add_action(icon_path,
                        text=u'Geodesic Densifier',
                        callback=self.run,
                        parent=self.iface.mainWindow())

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(u'&Geodesic Densifier', action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    def run(self):
        """Run method that performs all the real work"""

        # show the dialog
        self.dlg.show()

        # set default values
        self.inLayer = self.dlg.mMapLayerComboBox.currentLayer()

        # set segmenting method
        self.segmentMethod = ''
        if self.dlg.spacingRadioButton.isChecked():
            self.segmentMethod = 'spacing'
        else:
            self.segmentMethod = 'count'

        def set_in_layer():
            """ function to set the input layer from the GUI """
            self.inLayer = self.dlg.mMapLayerComboBox.currentLayer()
            if self.inLayer:
                if self.inLayer.crs():
                    self.dlg.messageBox.setText("Input Layer Set: " +
                                                str(self.inLayer.name()))
                else:
                    self.dlg.messageBox.setText(
                        "Error: Input must have projection defined")

        # listener to set input layer when combo box changes
        self.dlg.mMapLayerComboBox.layerChanged.connect(set_in_layer)

        # clear the ellipsoid combobox
        self.dlg.EllipsoidcomboBox.clear()

        # this is a dictionary of common ellipsoid parameters
        # http://www.ga.gov.au/__data/assets/file/0019/11377/Vincentys-formulae-to-calculate-distance-and-bearing-from-latitude-and-longitude.xls
        ellipsoid_dict = {
            '165': [6378165.000, 298.3],
            'ANS': [6378160, 298.25],
            'CLARKE 1858': [6378293.645, 294.26],
            'GRS80': [6378137, 298.2572221],
            'WGS72': [6378135, 298.26],
            'International 1924': [6378388, 297],
            'WGS84': [6378137, 298.2572236]
        }

        # add items to ellipsoid combobox
        for k in list(ellipsoid_dict.keys()):
            self.dlg.EllipsoidcomboBox.addItem(str(k))

        # default ellipsoid is WGS84
        self.ellipsoid_a = 6378137.0
        self.ellipsoid_f = 298.2572236
        self.ellipsoid_name = 'WGS84'
        self.dlg.EllipsoidcomboBox.setCurrentText(self.ellipsoid_name)

        def set_in_ellipsoid():
            """ This function gets the ellipsoid name from the GUI and sets the parameters """
            in_ellipsoid_name = self.dlg.EllipsoidcomboBox.currentText()
            for k in list(ellipsoid_dict.keys()):
                if k == in_ellipsoid_name:
                    self.ellipsoid_a = ellipsoid_dict[k][0]
                    self.ellipsoid_f = ellipsoid_dict[k][1]
                    self.ellipsoid_name = k
                    self.dlg.messageBox.setText("Ellipsoid set to " + str(k))

        # listener to set input ellipsoid when combo box changes
        self.dlg.EllipsoidcomboBox.currentIndexChanged.connect(
            set_in_ellipsoid)

        # default is point spacing with 900m
        self.spacing = 900
        self.dlg.spacingSpinBox.setValue(self.spacing)
        self.dlg.spacingRadioButton.setChecked(True)

        # choose segment length
        def set_in_spacing():
            self.spacing = int(self.dlg.spacingSpinBox.value())
            self.dlg.messageBox.setText("Point spacing set to " +
                                        str(self.spacing) + "m")

        # listener to set input point spacing when spin box changes
        self.dlg.spacingSpinBox.valueChanged.connect(set_in_spacing)

        # default segment number is 10
        self.segmentCount = 10
        self.dlg.segmentsSpinBox.setValue(self.segmentCount)
        self.dlg.segmentsRadioButton.setChecked(False)

        # choose number of segments
        def set_in_segments():
            self.segmentCount = int(self.dlg.segmentsSpinBox.value())
            self.dlg.messageBox.setText("Segment count set to " +
                                        str(self.segmentCount))

        # listener to set input point spacing when spin box changes
        self.dlg.segmentsSpinBox.valueChanged.connect(set_in_segments)

        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:

            # set the input layer
            self.inLayer = self.dlg.mMapLayerComboBox.currentLayer()

            # set segmenting method
            self.segmentMethod = ''
            if self.dlg.spacingRadioButton.isChecked():
                self.segmentMethod = 'spacing'
            else:
                self.segmentMethod = 'count'

            # get the field list
            fields = self.inLayer.fields()

            # handle layers that aren't WGS84 (EPSG:4326)
            wgs84crs = QgsCoordinateReferenceSystem("EPSG:4326")
            if self.inLayer.crs() != wgs84crs:
                transtowgs84 = QgsCoordinateTransform(self.inLayer.crs(),
                                                      wgs84crs,
                                                      QgsProject.instance())
                transfromwgs84 = QgsCoordinateTransform(
                    wgs84crs, self.inLayer.crs(), QgsProject.instance())

            # get input geometry type
            self.inType = 'Unknown'
            if self.inLayer.geometryType() == QgsWkbTypes.PointGeometry:
                self.inType = 'Point'

            elif self.inLayer.geometryType() == QgsWkbTypes.LineGeometry:
                self.inType = 'LineString'

            elif self.inLayer.geometryType() == QgsWkbTypes.PolygonGeometry:
                self.inType = 'Polygon'

            else:
                self.iface.messageBar().pushWarning(
                    "Error", "geometry type not recognized")

            # setup output layers
            if self.inType == 'Point':
                self.create_point = True
                # create and add to map canvas a point memory layer
                layer_name = "Densified Point " + str(
                    self.ellipsoid_name) + " " + str(self.spacing) + "m"
                out_point_layer = self.iface.addVectorLayer(
                    "Point?crs={}".format(self.inLayer.crs().authid()),
                    layer_name, "memory")
                # set data provider
                provider = out_point_layer.dataProvider()
                # add attribute fields
                provider.addAttributes(fields)
                self.pointTypeField = ''
                for fieldName in ["pointType", "pntType", "pntTyp"]:
                    if fieldName not in [field.name() for field in fields]:
                        self.pointTypeField = fieldName
                provider.addAttributes(
                    [QgsField(self.pointTypeField, QVariant.String)])
                out_point_layer.updateFields()
            else:
                self.create_point = False

            if self.inType == 'LineString':
                self.create_polyline = True
                # create and add to map canvas a polyline memory layer
                layer_name = "Densified Line " + str(
                    self.ellipsoid_name) + " " + str(self.spacing) + "m"
                out_line_layer = self.iface.addVectorLayer(
                    "LineString?crs={}".format(self.inLayer.crs().authid()),
                    layer_name, "memory")
                # set data provider
                provider = out_line_layer.dataProvider()
                # add attribute fields
                provider.addAttributes(fields)
                out_line_layer.updateFields()
            else:
                self.create_polyline = False

            if self.inType == 'Polygon':
                self.create_polygon = True
                # create and add to map canvas a polyline memory layer
                layer_name = "Densified Polygon " + str(
                    self.ellipsoid_name) + " " + str(self.spacing) + "m"
                out_poly_layer = self.iface.addVectorLayer(
                    "Polygon?crs={}".format(self.inLayer.crs().authid()),
                    layer_name, "memory")
                # set data provider
                provider = out_poly_layer.dataProvider()
                # add attribute fields
                provider.addAttributes(fields)
                out_poly_layer.updateFields()
            else:
                self.create_polygon = False

            # Create a geographiclib Geodesic object
            self.geod = Geodesic(self.ellipsoid_a, 1 / self.ellipsoid_f)

            def densify_point(in_layer, pr):
                """ This function densifies the input point layer and writes it to the output provider"""
                # iterator to read input layer
                iterator = in_layer.getFeatures()
                # counter to mark first point as "original"
                counter = 0
                # empty feature used to store temporary data
                current_feature = QgsFeature()
                # counter to report features that don't work
                bad_geom = 0
                for feature in iterator:
                    if not feature.geometry().isMultipart():
                        try:
                            if counter == 0:
                                # this is only for the first point
                                pointxy = feature.geometry().asPoint()
                                current_feature.setGeometry(
                                    QgsGeometry.fromPointXY(pointxy))
                                attr = feature.attributes()
                                attr.append("Original")
                                current_feature.setAttributes(attr)
                                pr.addFeatures([current_feature])
                            else:
                                start_pt = current_feature.geometry().asPoint()
                                end_pt = feature.geometry().asPoint()
                                if self.inLayer.crs() != wgs84crs:
                                    start_pt = transtowgs84.transform(start_pt)
                                    end_pt = transtowgs84.transform(end_pt)
                                # create a geographiclib line object
                                line_object = self.geod.InverseLine(
                                    start_pt.y(), start_pt.x(), end_pt.y(),
                                    end_pt.x())
                                # determine how many densified segments there will be
                                if self.segmentMethod == 'count':
                                    n = self.segmentCount
                                else:
                                    n = int(
                                        math.ceil(line_object.s13 /
                                                  self.spacing))
                                # adjust the spacing distance
                                seglen = line_object.s13 / n
                                # create densified points along the line object
                                for i in range(1, n):
                                    if i > 0:
                                        s = seglen * i
                                        g = line_object.Position(
                                            s, Geodesic.LATITUDE
                                            | Geodesic.LONGITUDE
                                            | Geodesic.LONG_UNROLL)
                                        geom = QgsPointXY(g['lon2'], g['lat2'])
                                        attr = feature.attributes()
                                        attr.append("Densified")
                                        current_feature.setAttributes(attr)
                                        if self.inLayer.crs(
                                        ) != wgs84crs:  # Convert each point back to the output CRS
                                            geom = transfromwgs84.transform(
                                                geom)
                                        current_feature.setGeometry(
                                            QgsGeometry.fromPointXY(geom))
                                        # write the point
                                        pr.addFeatures([current_feature])
                                # write the last point
                                geom = feature.geometry().asPoint()
                                current_feature.setGeometry(
                                    QgsGeometry.fromPointXY(geom))
                                attr = feature.attributes()
                                attr.append("Original")
                                current_feature.setAttributes(attr)
                                pr.addFeatures([current_feature])
                            counter += 1
                        except:
                            bad_geom += 1
                            counter += 1
                    else:
                        bad_geom += 1
                        self.iface.messageBar().pushWarning(
                            "error",
                            "multipoint geometries will not be densified")
                if bad_geom > 0:
                    # report number of features that didn't work
                    self.iface.messageBar().pushWarning(
                        "Error", "{} features failed".format(bad_geom))

            def densify_poly(in_layer, pr):
                bad_geom = 0
                iterator = in_layer.getFeatures()
                # create empty feature to write to
                for feature in iterator:
                    try:
                        if feature.geometry().wkbType(
                        ) == QgsWkbTypes.LineString:
                            line_geom = feature.geometry().asPolyline()
                            geom_type = "LineString"
                        elif feature.geometry().wkbType(
                        ) == QgsWkbTypes.MultiLineString:
                            multiline_geom = feature.geometry(
                            ).asMultiPolyline()
                            geom_type = "MultiLineString"
                        elif feature.geometry().wkbType(
                        ) == QgsWkbTypes.Polygon:
                            poly_geom = feature.geometry().asPolygon()
                            geom_type = "Polygon"
                        elif feature.geometry().wkbType(
                        ) == QgsWkbTypes.MultiPolygon:
                            multipoly_geom = feature.geometry().asMultiPolygon(
                            )
                            geom_type = "MultiPolygon"
                        else:
                            bad_geom += 1
                    except:
                        bad_geom += 1

                    if geom_type == "LineString":
                        dense_points = []
                        point_count = len(line_geom)
                        start_pt = QgsPointXY(line_geom[0][0], line_geom[0][1])
                        dense_points.append(start_pt)
                        if self.inLayer.crs() != wgs84crs:
                            start_pt = transtowgs84.transform(start_pt)
                        for j in range(1, point_count):
                            end_pt = QgsPointXY(line_geom[j][0],
                                                line_geom[j][1])
                            if self.inLayer.crs() != wgs84crs:
                                end_pt = transtowgs84.transform(end_pt)
                            # create a geographiclib line object
                            line_object = self.geod.InverseLine(
                                start_pt.y(), start_pt.x(), end_pt.y(),
                                end_pt.x())
                            # determine how many densified segments there will be
                            if self.segmentMethod == 'count':
                                n = self.segmentCount
                            else:
                                n = int(
                                    math.ceil(line_object.s13 / self.spacing))
                            if line_object.s13 > self.spacing:
                                seglen = line_object.s13 / n
                                for k in range(1, n):
                                    s = seglen * k
                                    g = line_object.Position(
                                        s,
                                        Geodesic.LATITUDE | Geodesic.LONGITUDE
                                        | Geodesic.LONG_UNROLL)
                                    waypoint = QgsPointXY(g['lon2'], g['lat2'])
                                    if self.inLayer.crs() != wgs84crs:
                                        waypoint = transfromwgs84.transform(
                                            waypoint)
                                    dense_points.append(waypoint)
                                if self.inLayer.crs() != wgs84crs:
                                    end_pt = transfromwgs84.transform(end_pt)
                                dense_points.append(end_pt)
                            start_pt = end_pt

                    elif geom_type == "MultiLineString":
                        dense_features = []
                        for i in range(len(multiline_geom)):
                            dense_points = []
                            line = multiline_geom[i]
                            point_count = len(line)
                            start_pt = QgsPointXY(line[0][0], line[0][1])
                            dense_points.append(start_pt)
                            for j in range(1, point_count):
                                end_pt = QgsPointXY(line[j][0], line[j][1])
                                if self.inLayer.crs() != wgs84crs:
                                    start_pt = transtowgs84.transform(start_pt)
                                    end_pt = transtowgs84.transform(end_pt)
                                # create a geographiclib line object
                                line_object = self.geod.InverseLine(
                                    start_pt.y(), start_pt.x(), end_pt.y(),
                                    end_pt.x())
                                # determine how many densified segments there will be
                                if self.segmentMethod == 'count':
                                    n = self.segmentCount
                                else:
                                    n = int(
                                        math.ceil(line_object.s13 /
                                                  self.spacing))
                                if line_object.s13 > self.spacing:
                                    seglen = line_object.s13 / n
                                    for k in range(1, n):
                                        s = seglen * k
                                        g = line_object.Position(
                                            s, Geodesic.LATITUDE
                                            | Geodesic.LONGITUDE
                                            | Geodesic.LONG_UNROLL)
                                        waypoint = QgsPointXY(
                                            g['lon2'], g['lat2'])
                                        if self.inLayer.crs() != wgs84crs:
                                            waypoint = transfromwgs84.transform(
                                                waypoint)
                                        dense_points.append(waypoint)
                                    if self.inLayer.crs() != wgs84crs:
                                        end_pt = transfromwgs84.transform(
                                            end_pt)
                                    dense_points.append(end_pt)
                                start_pt = end_pt
                            dense_features.append(dense_points)

                    elif geom_type == "Polygon":
                        for poly in poly_geom:
                            dense_points = []
                            point_count = len(poly)
                            start_pt = QgsPointXY(poly[0][0], poly[0][1])
                            dense_points.append(start_pt)
                            for j in range(1, point_count):
                                end_pt = QgsPointXY(poly[j][0], poly[j][1])
                                if self.inLayer.crs() != wgs84crs:
                                    end_pt = transtowgs84.transform(end_pt)
                                    start_pt = transtowgs84.transform(start_pt)
                                # create a geographiclib line object
                                line_object = self.geod.InverseLine(
                                    start_pt.y(), start_pt.x(), end_pt.y(),
                                    end_pt.x())
                                # determine how many densified segments there will be
                                if self.segmentMethod == 'count':
                                    n = self.segmentCount
                                else:
                                    n = int(
                                        math.ceil(line_object.s13 /
                                                  self.spacing))
                                if line_object.s13 > self.spacing:
                                    seglen = line_object.s13 / n
                                    for k in range(1, n):
                                        s = seglen * k
                                        g = line_object.Position(
                                            s, Geodesic.LATITUDE
                                            | Geodesic.LONGITUDE
                                            | Geodesic.LONG_UNROLL)
                                        waypoint = QgsPointXY(
                                            g['lon2'], g['lat2'])
                                        if self.inLayer.crs() != wgs84crs:
                                            waypoint = transfromwgs84.transform(
                                                waypoint)
                                        dense_points.append(waypoint)
                                    if self.inLayer.crs() != wgs84crs:
                                        end_pt = transfromwgs84.transform(
                                            end_pt)
                                    dense_points.append(end_pt)
                                start_pt = end_pt

                    if geom_type == "MultiPolygon":
                        dense_features = []
                        for i in range(len(multipoly_geom)):
                            dense_points = []
                            poly = multipoly_geom[i][0]
                            point_count = len(poly)
                            start_pt = QgsPointXY(poly[0][0], poly[0][1])
                            dense_points.append(start_pt)
                            for j in range(1, point_count):
                                end_pt = QgsPointXY(poly[j][0], poly[j][1])
                                if self.inLayer.crs() != wgs84crs:
                                    start_pt = transtowgs84.transform(start_pt)
                                    end_pt = transtowgs84.transform(end_pt)
                                # create a geographiclib line object
                                line_object = self.geod.InverseLine(
                                    start_pt.y(), start_pt.x(), end_pt.y(),
                                    end_pt.x())
                                # determine how many densified segments there will be
                                if self.segmentMethod == 'count':
                                    n = self.segmentCount
                                else:
                                    n = int(
                                        math.ceil(line_object.s13 /
                                                  self.spacing))
                                if line_object.s13 > self.spacing:
                                    seglen = line_object.s13 / n
                                    for k in range(1, n):
                                        s = seglen * k
                                        g = line_object.Position(
                                            s, Geodesic.LATITUDE
                                            | Geodesic.LONGITUDE
                                            | Geodesic.LONG_UNROLL)
                                        waypoint = QgsPointXY(
                                            g['lon2'], g['lat2'])
                                        if self.inLayer.crs() != wgs84crs:
                                            waypoint = transfromwgs84.transform(
                                                waypoint)
                                        dense_points.append(waypoint)
                                    if self.inLayer.crs() != wgs84crs:
                                        end_pt = transfromwgs84.transform(
                                            end_pt)
                                    dense_points.append(end_pt)
                                start_pt = end_pt
                            dense_features.append(dense_points)

                    new_poly = QgsFeature()
                    if geom_type == "LineString":
                        new_poly.setGeometry(
                            QgsGeometry.fromPolylineXY(dense_points))
                    elif geom_type == "MultiLineString":
                        new_poly.setGeometry(
                            QgsGeometry.fromMultiPolylineXY(dense_features))
                    elif geom_type == "Polygon":
                        new_poly.setGeometry(
                            QgsGeometry.fromPolygonXY([dense_points]))
                    elif geom_type == "MultiPolygon":
                        new_poly.setGeometry(
                            QgsGeometry.fromMultiPolygonXY([dense_features]))
                    new_poly.setAttributes(feature.attributes())
                    pr.addFeatures([new_poly])
                if bad_geom > 0:
                    self.iface.messageBar().pushWarning(
                        "", "{} features failed".format(bad_geom))

            if self.create_point:
                densify_point(self.inLayer, provider)
                out_point_layer.reload()

            if self.create_polyline:
                densify_poly(self.inLayer, provider)
                out_line_layer.reload()

            if self.create_polygon:
                densify_poly(self.inLayer, provider)
                out_poly_layer.reload()
Пример #3
0
def coordat(f, c1, c2):
    geod = Geodesic(Constants.WGS84_a, Constants.WGS84_f)
    l = geod.InverseLine(c1[1], c1[0], c2[1], c2[0])
    x = (l.s13 * f)
    p = l.Position(x, Geodesic.STANDARD)
    return [p['lon2'], p['lat2']]
Пример #4
0
class GeodesicDensifier:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)

        # Create the dialog (after translation) and keep reference
        self.dlg = GeodesicDensifierDialog()

        # Declare instance attributes
        self.actions = []
        self.menu = u'&Geodesic Densifier'
        self.toolbar = self.iface.addToolBar(u'GeodesicDensifier')
        self.toolbar.setObjectName(u'GeodesicDensifier')

    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/GeodesicDensifier3/icon.png'
        self.add_action(
            icon_path,
            text=u'Geodesic Densifier',
            callback=self.run,
            parent=self.iface.mainWindow())


    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(u'&Geodesic Densifier', action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar


    def run(self):
        """Run method that performs all the real work"""

        # show the dialog
        self.dlg.show()

        # set default values
        self.inLayer = self.dlg.mMapLayerComboBox.currentLayer()

        def set_in_layer():
            self.inLayer = self.dlg.mMapLayerComboBox.currentLayer()
            if self.inLayer:
                if self.inLayer.crs():
                    self.dlg.messageBox.setText("Input Layer Set: " + str(self.inLayer.name()))
                else:
                    self.dlg.messageBox.setText("Error: Input must have projection defined")

        # listener to set input layer when combo box changes
        self.dlg.mMapLayerComboBox.layerChanged.connect(set_in_layer)

        # clear the ellipsoid combobox
        self.dlg.EllipsoidcomboBox.clear()

        ellipsoid_dict = {'WGS84': [6378137, 298.2572236],
                          '165': [6378165.000, 298.3],
                          'ANS': [6378160, 298.25],
                          'CLARKE 1858': [6378293.645, 294.26],
                          'GRS80': [6378137, 298.2572221],
                          'WGS72': [6378135, 298.26],
                          'International 1924': [6378388, 297]}

        # add items to ellipsoid combobox
        for k in list(ellipsoid_dict.keys()):
            self.dlg.EllipsoidcomboBox.addItem(str(k))

        # default ellipsoid is WGS84
        self.ellipsoid_a = 6378137.0
        self.ellipsoid_f = 298.2572236
        self.ellipsoid_name = 'WGS84'

        def set_in_ellipsoid():
            in_ellipsoid_name = self.dlg.EllipsoidcomboBox.currentText()
            for k in list(ellipsoid_dict.keys()):
                if k == in_ellipsoid_name:
                    self.ellipsoid_a = ellipsoid_dict[k][0]
                    self.ellipsoid_f = ellipsoid_dict[k][1]
                    self.ellipsoid_name = k
                    self.dlg.messageBox.setText("Ellipsoid set to " + str(k))

        # listener to set input ellipsoid when combo box changes
        self.dlg.EllipsoidcomboBox.currentIndexChanged.connect(set_in_ellipsoid)

        # default point spacing is 900
        self.spacing = 900

        def set_in_spacing():
            self.spacing = int(self.dlg.spacingSpinBox.value())
            self.dlg.messageBox.setText("Point spacing set to " + str(self.spacing) + "m")

        # listener to set input point spacing when spin box changes
        self.dlg.spacingSpinBox.valueChanged.connect(set_in_spacing)

        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:

            # set the input layer
            self.inLayer = self.dlg.mMapLayerComboBox.currentLayer()

            # get the field list
            fields = self.inLayer.fields()

            # handle layers that aren't WGS84 (EPSG:4326)
            wgs84crs = QgsCoordinateReferenceSystem("EPSG:4326")
            if self.inLayer.crs() != wgs84crs:
                transtowgs84 = QgsCoordinateTransform(self.inLayer.crs(), wgs84crs, QgsProject.instance())
                transfromwgs84 = QgsCoordinateTransform(wgs84crs, self.inLayer.crs(), QgsProject.instance())

            # get input geometry type
            if self.inLayer.geometryType() == QgsWkbTypes.PointGeometry:
                self.inType = 'Point'           # works

            if self.inLayer.geometryType() == QgsWkbTypes.LineGeometry:
                self.inType = 'LineString'      # works

            if self.inLayer.geometryType() == QgsWkbTypes.PolygonGeometry:
                self.inType = 'Polygon'         # works

            # setup output layers
            if self.inType == 'Point':
                self.create_point = True
                # create and add to map canvas a point memory layer
                layer_name = "Densified Point " + str(self.ellipsoid_name) + " " + str(self.spacing) + "m"
                out_point_layer = self.iface.addVectorLayer("Point?crs={}".format(self.inLayer.crs().authid()),
                                                            layer_name,
                                                            "memory")
                # set data provider
                pointPr = out_point_layer.dataProvider()
                # add attribute fields
                pointPr.addAttributes(fields)
                pointTypeField = ''
                if "pointType" not in [field.name() for field in fields]:
                    pointTypeField = "pointType"
                elif "pntType" not in [field.name() for field in fields]:
                    pointTypeField = "pntType"
                elif "pntTyp" not in [field.name() for field in fields]:
                    pointTypeField = "pntTyp"
                pointPr.addAttributes([QgsField(pointTypeField, QVariant.String)])
                out_point_layer.updateFields()
            else:
                self.create_point = False

            if self.inType == 'LineString':
                self.create_polyline = True
                # create and add to map canvas a polyline memory layer
                layer_name = "Densified Line " + str(self.ellipsoid_name) + " " + str(self.spacing) + "m"
                out_line_layer = self.iface.addVectorLayer("LineString?crs={}".format(self.inLayer.crs().authid()),
                                                           layer_name,
                                                           "memory")
                # set data provider
                linePr = out_line_layer.dataProvider()
                # add attribute fields
                linePr.addAttributes(fields)
                out_line_layer.updateFields()
            else:
                self.create_polyline = False

            if self.inType == 'Polygon':
                self.create_polygon = True
                # create and add to map canvas a polyline memory layer
                layer_name = "Densified Polygon " + str(self.ellipsoid_name) + " " + str(self.spacing) + "m"
                out_poly_layer = self.iface.addVectorLayer("Polygon?crs={}".format(self.inLayer.crs().authid()),
                                                           layer_name,
                                                           "memory")
                # set data provider
                polyPr = out_poly_layer.dataProvider()
                # add attribute fields
                polyPr.addAttributes(fields)
                out_poly_layer.updateFields()
            else:
                self.create_polygon = False

            # Create a geographiclib Geodesic object
            self.geod = Geodesic(self.ellipsoid_a, 1 / self.ellipsoid_f)

            def densifyPoint(inLayer, pr):
                pointTypeFieldIdx = pr.fieldNameIndex(pointTypeField)
                iterator = inLayer.getFeatures()
                featureCount = pr.featureCount()
                counter = 0
                currentFeature = QgsFeature()
                badGeom = 0
                for feature in iterator:
                    if feature.geometry().wkbType() == QgsWkbTypes.Point:
                        try:
                            if counter == 0:
                                pointxy = feature.geometry().asPoint()
                                currentFeature.setGeometry(QgsGeometry.fromPointXY(pointxy))
                                attr = feature.attributes()
                                attr.append("Original")
                                currentFeature.setAttributes(attr)
                                pr.addFeatures([currentFeature])
                            else:
                                startPt = currentFeature.geometry().asPoint()
                                endPt = feature.geometry().asPoint()
                                if self.inLayer.crs() != wgs84crs:
                                    startPt = transtowgs84.transform(startPt)
                                    endPt = transtowgs84.transform(endPt)
                                # create a geographiclib line object
                                lineObject = self.geod.InverseLine(startPt.y(), startPt.x(), endPt.y(), endPt.x())
                                # determine how many densified segments there will be
                                n = int(math.ceil(lineObject.s13 / self.spacing))
                                # adjust the spacing distance
                                seglen = lineObject.s13 / n
                                for i in range(1, n):
                                    if i > 0:
                                        s = seglen * i
                                        g = lineObject.Position(s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL)
                                        geom = QgsPointXY(g['lon2'], g['lat2'])
                                        attr = feature.attributes()
                                        attr.append("Densified")
                                        currentFeature.setAttributes(attr)
                                        if self.inLayer.crs() != wgs84crs:  # Convert each point back to the output CRS
                                            geom = transfromwgs84.transform(geom)
                                        currentFeature.setGeometry(QgsGeometry.fromPointXY(geom))
                                        pr.addFeatures([currentFeature])
                                geom = feature.geometry().asPoint()
                                currentFeature.setGeometry(QgsGeometry.fromPointXY(geom))
                                attr = feature.attributes()
                                attr.append("Original")
                                currentFeature.setAttributes(attr)
                                pr.addFeatures([currentFeature])
                            counter += 1
                        except:
                            badGeom += 1
                            counter += 1
                    else:
                        badGeom += 1
                        self.iface.messageBar().pushWarning("multipoint geometries will not be densified")
                if badGeom > 0:
                    self.iface.messageBar().pushWarning("", "{} features failed".format(badGeom))

            def densifyLine(inLayer, pr):
                badGeom = 0
                iterator = inLayer.getFeatures()
                # create empty feature to write to
                newLine = QgsFeature()
                segments = []
                for feature in iterator:
                    try:
                        if feature.geometry().wkbType() ==  QgsWkbTypes.LineString:
                            segments = [feature.geometry().asPolyline()]
                        elif feature.geometry().wkbType() ==  QgsWkbTypes.MultiLineString:
                            segments = feature.geometry().asMultiPolyline()
                        else:
                            badGeom += 1
                        segmentCount = len(segments)
                    except:
                        badGeom += 1
                    if feature.geometry().wkbType() ==  QgsWkbTypes.LineString:
                        line = segments[0]
                        pointCount = len(line)
                        startPt = QgsPointXY(line[0][0], line[0][1])
                        if self.inLayer.crs() != wgs84crs:
                            startPt = transtowgs84.transform(startPt)
                        pointList = [startPt]
                        for i in range(1,pointCount):
                            endPt = QgsPointXY(line[i][0], line[i][1])
                            if self.inLayer.crs() != wgs84crs:
                                endPt = transtowgs84.transform(endPt)
                            # create a geographiclib line object
                            lineObject = self.geod.InverseLine(startPt.y(), startPt.x(), endPt.y(), endPt.x())
                            # determine how many densified segments there will be
                            n = int(math.ceil(lineObject.s13 / self.spacing))
                            if lineObject.s13 > self.spacing:
                                seglen = lineObject.s13 / n
                                for i in range(1, n):
                                    s = seglen * i
                                    g = lineObject.Position(s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL)
                                    pointList.append(QgsPointXY(g['lon2'], g['lat2']))
                            pointList.append(endPt)
                            startPt = endPt
                        if self.inLayer.crs() != wgs84crs:  # Convert each point back to the output CRS
                            for x, pt in enumerate(pointList):
                                pointList[x] = transfromwgs84.transform(pt)
                        newLine.setGeometry(QgsGeometry.fromPolylineXY(pointList))
                    elif feature.geometry().wkbType() ==  QgsWkbTypes.MultiLineString:
                        outsegment = []
                        for line in segments:
                            pointCount = len(line)
                            startPt = QgsPointXY(line[0][0], line[0][1])
                            if self.inLayer.crs() != wgs84crs:  # Convert to 4326
                                startPt = transtowgs84.transform(startPt)
                            pts = [startPt]
                            for x in range(1, pointCount):
                                endPt = QgsPointXY(line[x][0], line[x][1])
                                if self.inLayer.crs() != wgs84crs:  # Convert to 4326
                                    endPt = transtowgs84.transform(endPt)
                                lineObject = self.geod.InverseLine(startPt.y(), startPt.x(), endPt.y(), endPt.x())
                                n = int(math.ceil(lineObject.s13 / self.spacing))
                                if lineObject.s13 > self.spacing:
                                    seglen = lineObject.s13 / n
                                    for i in range(1, n):
                                        s = seglen * i
                                        g = lineObject.Position(s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL)
                                        pts.append(QgsPointXY(g['lon2'], g['lat2']))
                                pts.append(endPt)
                                startPt = endPt

                            if self.inLayer.crs() != wgs84crs:  # Convert each point back to the output CRS
                                for x, pt in enumerate(pts):
                                    pts[x] = transfromwgs84.transform(pt)
                            outsegment.append(pts)

                        newLine.setGeometry(QgsGeometry.fromMultiPolylineXY(outsegment))

                    else:
                        badGeom += 1

                    newLine.setAttributes(feature.attributes())
                    pr.addFeatures([newLine])
                if badGeom > 0:
                    self.iface.messageBar().pushWarning("", "{} features failed".format(badGeom))

            def densifyPolygon(inLayer, pr):
                badGeom = 0
                iterator = inLayer.getFeatures()
                # create empty feature to write to
                newPoly = QgsFeature()
                for feature in iterator:
                    try:
                        if feature.geometry().wkbType() ==  QgsWkbTypes.Polygon:
                            polygon = feature.geometry().asPolygon()
                            polyCount = len(polygon)
                            pointList = []
                            for points in polygon:
                                pointCount = len(points)
                                startPt = QgsPointXY(points[0][0], points[0][1])
                                if self.inLayer.crs() != wgs84crs:
                                    startPt = transtowgs84.transform(startPt)
                                polyPointList = [startPt]
                                for i in range(1, pointCount):
                                    endPt = QgsPointXY(points[i][0], points[i][1])
                                    if self.inLayer.crs() != wgs84crs:  # Convert to 4326
                                        endPt = transtowgs84.transform(endPt)
                                    lineObject = self.geod.InverseLine(startPt.y(), startPt.x(), endPt.y(), endPt.x())
                                    n = int(math.ceil(lineObject.s13 / self.spacing))
                                    seglen = lineObject.s13 / n
                                    for i in range(1, n):
                                        s = seglen * i
                                        g = lineObject.Position(s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL)
                                        polyPointList.append(QgsPointXY(g['lon2'], g['lat2']))
                                    polyPointList.append(endPt)
                                    startPt = endPt

                                if self.inLayer.crs() != wgs84crs:
                                    for x, pt in enumerate(polyPointList):
                                        polyPointList[x] = transfromwgs84.transform(pt)

                            outPolygon = QgsFeature()
                            outPolygon.setGeometry(QgsGeometry.fromPolygonXY([polyPointList]))
                            outPolygon.setAttributes(feature.attributes())
                            pr.addFeatures([outPolygon])

                        elif feature.geometry().wkbType() ==  QgsWkbTypes.MultiPolygon:
                            print("multipoly")
                            multipolygon = feature.geometry().asMultiPolygon()
                            multiPointList = []
                            for polygon in multipolygon:
                                polyPointList = []
                                for points in polygon:
                                    print('points:', points)
                                    pointCount = len(points)
                                    startPt = QgsPointXY(points[0][0], points[0][1])
                                    print('startPt:', startPt)
                                    if self.inLayer.crs() != wgs84crs:
                                        startPt = transtowgs84.transform(startPt)
                                    polyPointList = [startPt]
                                    for i in range(1, pointCount):
                                        endPt = QgsPointXY(points[i][0], points[i][1])
                                        print('endPt:', endPt)
                                        if self.inLayer.crs() != wgs84crs:  # Convert to 4326
                                            endPt = transtowgs84.transform(endPt)
                                        lineObject = self.geod.InverseLine(startPt.y(), startPt.x(), endPt.y(), endPt.x())
                                        n = int(math.ceil(lineObject.s13 / self.spacing))
                                        seglen = lineObject.s13 / n
                                        for i in range(1, n):
                                            s = seglen * i
                                            g = lineObject.Position(s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL)
                                            polyPointList.append(QgsPointXY(g['lon2'], g['lat2']))
                                        polyPointList.append(endPt)
                                        startPt = endPt

                                    if self.inLayer.crs() != wgs84crs:
                                        for x, pt in enumerate(polyPointList):
                                            polyPointList[x] = transfromwgs84.transform(pt)
                                multiPointList.append(polyPointList)
                            print('multiPointList:', multiPointList)
                            outMultiPolygon = QgsFeature()
                            print('setGeometry')
                            outMultiPolygon.setGeometry(QgsGeometry.fromMultiPolygonXY([multiPointList]))
                            print('setAttributes')
                            outMultiPolygon.setAttributes(feature.attributes())
                            print('addFeatures')
                            pr.addFeatures([outMultiPolygon])
                            print('finished')
                        else:
                            badGeom += 1
                    except:
                        badGeom += 1
                if badGeom > 0:
                    self.iface.messageBar().pushWarning("", "{} features failed".format(badGeom))

            if self.create_point:
                densifyPoint(self.inLayer, pointPr)
                out_point_layer.reload()

            if self.create_polyline:
                densifyLine(self.inLayer, linePr)
                out_line_layer.reload()

            if self.create_polygon:
                densifyPolygon(self.inLayer, polyPr)
                out_poly_layer.reload()