4

Using QGIS 2.18.14, I would like to know the node number at lower-right corner of each polygon, shown as red circle in a image below.

My goal is to obtain xy coordinates, by giving such number n to x( point_n ($geometry, n)) or y_at(n).

Tricky one is the polygon "C" for which I want to avoid orange circle, if it is at all possible. Tested functions such as x_max y_max but I can not figure out how to define the LR corner.

enter image description here

Edit The look of my polygons is relatively simple with distinctive corners (if my subjective description is allowed... it is just like the posted picture). They do not have multipart features.

Edit2 I did not notice but due to the change of the title the approach I was thinking is no more valid. (Struk out accordingly).

  • And ... what do u call lower ? what do u call right ? Your example is quite "clear" (maybe because of the "row" of polygons ...) but u want something valid is any situation ? – snaileater Nov 12 '17 at 6:51
  • @snaileater I honestly haven't had given good thought about complicated polygons... my case is pretty simple, as you see each polygon does not have many nodes. I also preclude multi-part polygons (will try not to worry about it for now). – Kazuhito Nov 12 '17 at 6:59
5

I think one approach might be to choose the point which

  • Is on the convex hull (ignore concave vertices like the inside of the ‘L’-shaped geometry)
  • Is a vertex
  • Is closest to the south-east corner of the (unrotated) bounding box.

Using a couple of geometry generators on the screenshot (to show the bounding boxes and the candidate point)

point_n(
    shortest_line( 
        make_point(
            x_max($geometry),
            y_min($geometry)
        ),
        nodes_to_points(convex_hull($geometry))
    ),
2)

The nodes_to_points splits the polygon feature into a set of discrete nodes, so we find the nearest vertex when doing shortest_line, rather than a point on a line segment.

Tried this on some buildings in OpenStreetMap. It seems to do a fairly good job on the simpler polygons; on more complex polygons it finds the most "south-easterly vertex", rather than a corner. You can see that on the cathedral...

enter image description here

There are always going to be edge cases where this won’t work… for example, a square rotated by 45 degrees won’t have a most south-easterly vertex.

Another possibility is to find the shortest line between a vertex and the eastern edge of the bounding box… although i suspect that's just x_max($geometry)

point_n(
    shortest_line( 
        make_line(
            make_point(
                x_max($geometry),
                y_min($geometry)
            ),
            make_point(
                x_max($geometry),
                y_max($geometry)
            )
        ),
        nodes_to_points(convex_hull($geometry))
    ),
2)

I think you may need to consider a more robust approach, for example

  • using minimum oriented bounding box
  • work out the orientation of the polygon using the azimuth of the longest line
  • choosing a different rule depending on orientation

I don't think minimum oriented bounding box is available in the geometry generator, but that might be another thing to try?

2

I created a similar layer as in your example:

enter image description here

and I used following code to get lower right corners of each polygon.

import processing

registry = QgsMapLayerRegistry.instance()

polygons = registry.mapLayersByName('polygons')

pol_feats = [ feat for feat in polygons[0].getFeatures() ]

ring_polygons = [ feat.geometry().asPolygon()[0] for feat in pol_feats ]

path = processing.runalg('qgis:orientedminimumboundingbox',
                         polygons[0],
                         False,
                         None)

bounding_box = QgsVectorLayer(path['OUTPUT'],
                              'bounding_box',
                              'ogr')

points_bounding_box = [ feat.geometry().asPolygon()
                        for feat in bounding_box.getFeatures() ]

line = QgsGeometry.fromPolyline([points_bounding_box[0][0][1], 
                                 points_bounding_box[0][0][2]])

ref_point = points_bounding_box[0][0][1]

right_points = []

for pol in ring_polygons:
    distances_idx = []
    for j, point in enumerate(pol):
        d = line.distance(QgsGeometry.fromPoint(point))
        distances_idx.append([d, pol[j]])
        distances_idx.sort()

    if ref_point[0] - distances_idx[0][1][0] < ref_point[0] - distances_idx[1][1][0]:
        right_points.append(distances_idx[0][1])
    else:
        right_points.append(distances_idx[1][1])

epsg = polygons[0].crs().authid()

uri = "Point?crs=" + epsg + "&field=id:integer""&index=yes"

mem_layer = QgsVectorLayer(uri,
                           'point',
                           'memory')

prov = mem_layer.dataProvider()

feats = [ QgsFeature() for i in range(len(right_points)) ]

for i, feat in enumerate(feats):
    feat.setAttributes([i])
    feat.setGeometry(QgsGeometry.fromPoint(right_points[i]))

prov.addFeatures(feats)

QgsMapLayerRegistry.instance().addMapLayer(mem_layer)

It uses a bounding oriented box to get a reference line and point for creating these lower right corners as memory layer; as it can be observed at following image:

enter image description here

I rotated original features to corroborate that bounding oriented box was always produced with same geometric criteria (for point and line reference); as it can be also observed at following image:

enter image description here

  • Thanks @xunilk but for some reason I obtained points at upper right (NE) corners. Will try to fix but I have no knowledge in python... – Kazuhito Nov 13 '17 at 0:00
  • Sorry, I didn't know it. Issue it could be related to direction of digitalization. It was a situation that I didn't try out. – xunilk Nov 13 '17 at 0:25
  • Thanks so much @xunilk Will try to digitize both clockwise / counter-clockwise and see if it changes. – Kazuhito Nov 13 '17 at 1:34
  • direction of digitization did change the outcome but they were either upper-right or upper-left. Sometimes they went lower when I added feature... but not sure if it was the reason. Could CRS influence? Tried WGS84 LatLong and Pseudo-Mercator (EPSG3857) so far. Will test more. – Kazuhito Nov 13 '17 at 5:01
  • EPSG:32612 gives points on the lowermost (southern) segment. Some at lower-left, mostly lower-right. Will try again after I learn python... – Kazuhito Nov 19 '17 at 10:22
1

One definition would be the point with the largest (X-Y) coordinate. If you rotate polygon C clockwise at some point the yellow vertex will become the "bottom right" once it gets more than 45 over the red point because then its more "Right" than Red even thought its less "Bottom".

This definition has the property that if you rotate the map 90 degrees clockwise and get the "bottom left" you get the same point.

If you want "Bottomness" to be more important that "Rightness" then adjust the formula, so find the one with the largest aX-Y (Or X-aY, can't get my head round this at the moment...) and choose "a".

  • Thanks @Spacedman I slept on this answer, which looks promising. Probably (dx) and (dy) from centroid and scaling factor (a) will define the outermost points, and overlaying them with those out from 45-deg rotated ellipsis. I paused there, but will do continue trying it further. Maybe with R. – Kazuhito Nov 12 '17 at 23:53
  • I give up this route, with apologies. – Kazuhito Nov 15 '17 at 12:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.