prochitecture / blosm

GNU General Public License v3.0
11 stars 3 forks source link

[streets] Relations between way-sections and triangles #34

Open polarkernel opened 2 years ago

polarkernel commented 2 years ago

In the current version of _roadpolygons.py there exists a mixture of different approaches, where and how way-sections are stored. These are the result of the development history and certainly need to be refactored, so that finally the triangles can be related to these sections. Within this issue I will try to explain the current state in detail. Based on this knowledge the discussion can be opened about how finally the triangles and the way-sections have to be related to get the desired result for the rendering of the streets. Since we do not yet know how this final result will be derived from the triangles, this topic may possibly also be a starting point for ideas on this topic.

polarkernel commented 2 years ago

Way-Sections

Starting point of the process is the section-network. It contains way-sections, which have been constructed from one or more OSM way-segments or parts of them. Their endpoints are intersections, end-points of dead-end ways or vertices, where the category tag changes. They are stored in instances of the class NetSegment. This class stores not only start- and endpoints of the way-section but also the paths built by all original vertices provided by OSM. Way-sections may have identical start- end endpoints, but different paths. Here, as an example, is an extract from a network:

Graph-Cycles

Graph-cycles are derived from the section-network by following the sections in counter-clockwise order. In most cases they are single simple polygons with edges formed by the way-segments of the paths of the way-sections (see left image below). When they are triangulated, all their vertices belong to a triangle. Large graph-cycles may be dissolved to smaller parts, which I called slices (right image below). In both cases, the class GraphCycle stores them as a list of polygons, containing only one in the former case. After the triangulation of graph-slices, not all triangle vertices belong to a way vertex.

Dead-Ends (Spurs)

Following dead-ends in counter-clockwise order, as depicted in the left image, lead to degenerated polygons which can't be processed any more. These segments are eliminated from the graph-cycle and stored separately in the instance of GraphCycle. I called them spurs. They are not involved in the process of triangulation and are currently unused. The red lines in the right image show examples of such spurs.

Way-Islands

Within graph-cycles, there may be way-islands. Two situations can produce them. The first occurs, when inner cycles within graph-cycles are only connected to the outer cycle by a single way. This single way again produces a degenerated polygon and is eliminated by the spurs removal. In the image below, the white areas were only connected to the outer polygon by the red spurs, which produced islands, the white holes in the polygon. These holes form other graph-cycles, which are processed separately. These holes are way-sections that are stored as holes in the slices of the cycle polygon and, again, the spurs connected to the holes are stored in the instance of GraphCycle

However, in rare cases, there may exist inner cycles that have never been connected to the outer graph-cycle. These are detected as cycles in clockwise order in the section-network. After a search for their smallest enclosing graph-cycle, they are integrated as a hole into this. Here an example, where the graph-cycle has been splitted into slices after that.

Solitaires

Way-sections that are not connected to graph-cycles and do not form a cycle are detected in the section-network as polygons that have zero area. I called them solitaires. Sometimes they are only single straight segments. They are currently detected, but not processed at all. Here two examples (blue lines):

Object Subtraction

Finally, the polyline objects within the graph-cycle are subtracted and produce additional holes in the polygon. The subtraction is done individually for every slice of a graph-cycle, if it has been splitted. Object polygons never touch a way-segment of the graph-cycle, but this is not tested for spurs. After the subtraction, every cycle slice is triangulated separately. All the triangles are stored in the instance of GraphCycle. Here some examples:

Relation of Ways to Triangles

With the exception of solitaires, all way-sections of a graph-cycle are held together in its instance of the class GraphCycle. However, for a given way-section, there are triangles on both sides of the way, in two different graph-cycles.

vvoovv commented 2 years ago

Thank you for the good wrapup.

It would be also good to have a chart for the current data model.

I'll write later about the desired output.

polarkernel commented 2 years ago

I see currently three main issues to solve:

It would be also good to have a chart for the current data model.

Let me see what I can do.

polarkernel commented 2 years ago

I will rename the subdivided polygon parts from slices to tiles, which fits better in my opinion.

What is the name of the region on the sides of the ways, the region that we finally like to find with our triangles? I named it way-environment in the current code, but I do not like this name. Do you have a better proposition?

vvoovv commented 2 years ago

I see currently three main issues to solve

I use the class BldgVector in the branch dev to process building footprints and building parts instead of just dealing with coordinates.

The class BldgVector has references to instances of

I think something similar is needed for way-segments. PyGEOS classes can be extended to deal with those vectors.

vvoovv commented 2 years ago

What is the name of the region on the sides of the ways, the region that we finally like to find with our triangles? I named it way-environment in the current code, but I do not like this name. Do you have a better proposition?

I don't understand what you are referring to. Aren't they graph-cycles or tiles? An image would be helpful to understand the idea.

polarkernel commented 2 years ago

I don't understand what you are referring to.

I mean the whole area near the streets, combined for instance from the neighboring triangles. Something like what you sketched here.

polarkernel commented 2 years ago

I think something similar is needed for way-segments. PyGEOS classes can be extended to deal with those vectors.

There is an important difference between BldgEdge, BldgPolygonand pyGEOS polygons used in the process pipeline: Vertices and edges are not modified for buildings. When polygons that represent cycles are subtracted or tiled, new vertices and edges are created. The new vertices may or may not be on existing way-segments and the new edges may or may not be part of these segments.

All this happens deep inside the pyGEOS library. pyGEOS does not know edges, it uses only lists of vertices (of class Coordinate). New polygons are constructed by adding intersection vertices, removing existing vertices and rearranging the lists. The result may be of class Polygonor MultiPolygon, with or without inner geometries. And all of this based on a theory that I only begin to understand. I would never dare to change anything in this library.

vvoovv commented 2 years ago

The class Coordinate at PyGEOS is quite simple. What if we add some kind of vector attribute to this class. For the coordinates created in the boolean operations the vector attribute should be set to None.

polarkernel commented 2 years ago

What if we add some kind of vector attribute to this class. For the coordinates created in the boolean operations the vector attribute should be set to None.

This would not solve the problem I mean. I will try to visualize it using an example. I once plotted only those triangles, that have at least one vertex on a way-segment, as you proposed here. This is easy to realize, because the way-segments that formed the original graph-cycle are stored in every instance of the class GraphCycle. When I disabled the tiling of the graph-cycles, we get almost what we wanted. The image below shows the graph-cycles in blue with a thick black border and the selected triangles in red.

When tiling takes place, the cycles (or better their tiles) get smaller and new vertices are constructed. But these vertices are no more in the list of way-segements stored in GraphCycle. Therefore, the corresponding triangles are not found:

So a vectorattribute in Coordinatewould only ease to find the existing vertices, but not the new ones, because their attribute would be just None.

vvoovv commented 2 years ago

Is tiling done also using PyGEOS?

polarkernel commented 2 years ago

Is tiling done also using PyGEOS?

Yes. The input graph-cycle is a pyGEOS polygon and the resulting tiles have to be pyGEOS polygons, so its the simplest way. Writing the code by ourselfs would be very complicated, because the splitting line can intersect the input polygon several times and it has also to split the eventual holes in the polygon.

polarkernel commented 2 years ago

It would be also good to have a chart for the current data model.

I don't really know which representation is helpful for this. I am not very skilled with UML diagrams (how to represent a dictionary of dictionaires of lists in UML?). Will it help you if I just point out and briefly describe the most important data structures? I don't think a full description is useful yet, as some refactoring will certainly be required.

vvoovv commented 2 years ago

Will it help you if I just point out and briefly describe the most important data structures?

I hope so.

vvoovv commented 2 years ago

I'd like to write about the desired output.

image

The triangulation should allow to find the borders of carriage ways and the borders of crossings between them, so that

The same applies to the cycle ways and tram rails if they are available.

The sidewalks occupy the rest of the space near the buildings.

vvoovv commented 2 years ago

I see currently three main issues to solve:

I'll describe below how the latter two issues can be solved.

Let's define an edgeKey for an edge with the coordinates of its vertices (x1,y1) and (x2,y2) as follows.

If x1 < x2, the edgeKey is a tuple ( (x1,y1), (x2,y2) ) If x1 > x2, the edgeKey is a tuple ( (x2,y2), (x1,y1) ) If x1 == x2 and y1 < y2, the edgeKey is a tuple ( (x1,y1), (x2,y2) ) If x1==x2 and y1 > y2, the edgeKey is a tuple ( (x2,y2), (x1,y1) )

A python dictionary edges is used to search for an edge entry through its edgeKey:

edge = edges[edgeKey]

edge in the expression above is an instance of some class Edge which keeps the references to some entities using the edge, namely

So for each PyGEOS polygon (including triangles) a companion polygon should be created that has a list of vectors. The Vector class has at least two members:

This way we can easily find a triangle that shares an edge with a particular way-segment.