Let's assume that you have to create vector data based on textual description of polygons, e. g.:
585200N0312800E-584700N0312700E-583500N0311700E-583200N0312700E- 583200N0313300E-582900N0313800E-582800N0314400E- 582500N0314500E-585200N0312800E.
It is not a big problem, when you have to enter into system 1, 2, 5 or even 10 such polygons that are have not too many vertices.
But what if you have many of them and deadline is coming?
We can write plugin for QGIS which will create polygons.
Note: The solution presented below is working but have some limitation of usability. I have made following assumption:
- coordinates are given in DMSH, degrees, minutes, seconds without fraction of seconds, e.g. 585200N
- textual description of polygon is given as pairs of latitude-longitude or longitude-latitude, that create segments of polygon ring
- the number of latitude and longitude is the same (I mean, that scripts does not check if the pairs of coordinates are given correctly)
- tool does not check if polygon is valid, e. g. if has no overlapping rings, self-intersections, dangling segments
- only polygon with one ring is possible (no outer and inner rings)
- due to the way of extracting coordinates from text the tool is designed for relatively small polygons
- the lines between vertices are 'strait' lines, not geodesic ones - this mean that for large polygons, or cases where distance between vertices is large it can lead to wrong results.
Regular expressions for coordinates in DMSH format - decimal, degree and seconds followed by hemisphere letter:
LAT_DMSH_COMP_REGEX = re.compile(r'\d{6}[NS]') LON_DMSH_COMP_REGEX = re.compile(r'\d{7}[EW]')
Function that converts coordinate from DMS format to DD (decimal degrees) format, which will be used later in QGIS functions to create polygon:
def dmsh_comp2dd(dms): """ Converts coordinate in DMSH format into DD format. :param dms: str, coordinate in DMSH format :return: float, decimal degrees """ h = dms[-1] # Get hemisphere letter dms = dms[:-1] # Trim hemisphere letter if h in ['N', 'S']: # for latitude d = float(dms[:2]) m = float(dms[2:4]) s = float(dms[4:]) elif h in ['E', 'W']: # for longitude d = float(dms[:3]) m = float(dms[3:5]) s = float(dms[5:]) if h in ['S', 'W']: # for southern and western hemisphere coordinates are negative return -(d + m / 60 + s / 3600) else: return d + m / 60 + s / 3600
And function that will return two lists: for latitude and longitude from text with description of polygon:
def get_latlon_list(raw_text, lat_regex, lon_regex): """ Extracts latitude and longitude from given text. :param raw_text: str, text for search latitude and longitude :param lat_regex: regex object, regular expression for latitude :param lon_regex: regex object, regular expression for latitude :return: 2 element tuple of latitude and longitude list """ lat_list = re.findall(lat_regex, raw_text) lon_list = re.findall(lon_regex, raw_text) return lat_list, lon_list
Now, we have to create QGIS plugin - the easy way to do it is to use plugin PluginBuilder.
A good tutorial how to do this can be found here:
https://www.qgistutorials.com/en/docs/building_a_python_plugin.html
Next, we have to create GUI, using for example QT Deisgner. Some of the basic of Qt will be discussed in separate posts.
First we need to extract latitude and longitude from text:
def extract_latlon(self): raw_text = self.dlg.textEditRawText.toPlainText() if self.dlg.comboBoxCoordFormat.currentIndex() == 0: # DMSH - no degree, minute, second symbol lat_list, lon_list = poly_tools.get_latlon_list(raw_text, poly_tools.LAT_DMSH_COMP_REGEX, poly_tools.LON_DMSH_COMP_REGEX) for i in range(len(lat_list)): for j in range(len(lon_list)): if i == j: row_pos = self.dlg.tableWidgetCoordinates.rowCount() self.dlg.tableWidgetCoordinates.insertRow(row_pos) self.dlg.tableWidgetCoordinates.setItem(row_pos, 0, QTableWidgetItem(lat_list[i])) self.dlg.tableWidgetCoordinates.setItem(row_pos, 1, QTableWidgetItem(lon_list[j])) return
NOTE: Writing extracting coordinates into QTableWidget is not necessary. I did it only to check myself is coordinates are extracted correctly and to learn how to use some basic of QTableWidget.
Now, we are ready to create polygon. We are going to do this in the following steps:
- read coordinates values from QTableWidget
- create QgsPointXY (QGIS 3) objects from coordinates pairs
- append QgsPointXY object to list of points
- create temporary memory layer (if it doesn't exist)
- pass list of points to QgsGeometry.fromPolygonXY to create polygon
vertices = [] # List of vertices for polygon feature point_count = self.dlg.tableWidgetCoordinates.rowCount() if self.dlg.comboBoxCoordFormat.currentIndex() == 0: # DMSH - no degree, minute, second symbol for i in range(0, point_count): vertex_lat = float(poly_tools.dmsh_comp2dd(self.dlg.tableWidgetCoordinates.item(i, 0).text())) vertex_lon = float(poly_tools.dmsh_comp2dd(self.dlg.tableWidgetCoordinates.item(i, 1).text())) vertex = QgsPointXY(vertex_lon, vertex_lat) vertices.append(vertex)
Creating polygon from extracted coordinates and adding it to layer that name is keep in self.mlyr_name variable.
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([vertices])) 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()
I am ware that is not comprehensive, so you can find full source code here and download plugin from: https://github.com/strpaw/QGIS3-CreatePolygonFromText
Good afternoon! Is your plugin still available in github? the link provided above is a dead link and do you still share it? Thanks in advance!
ReplyDelete