Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bundles, descriptions and issues #80

Open
polarkernel opened this issue Jan 5, 2024 · 94 comments
Open

Bundles, descriptions and issues #80

polarkernel opened this issue Jan 5, 2024 · 94 comments

Comments

@polarkernel
Copy link
Collaborator

The first task in the search for objects of the class Bundle is the search for equidistant path segments. For a better understanding of the concept and its problems, I will first explain how the algorithm works.

First, all way-sections that exceed a minimal length and are not too curved are entered into a static spatial index. This index uses bounding boxes around the sections to find neighbor sections. Way-sections, whose bounding boxes overlap, are candidates as neighbors. For every section (template) in this index, its neighbors are searched and tested, whether they are equidistant to the template section. This is done as follows:

The black line is the centerline of the template section. A buffering polygon is constructed around this line, spaced according to the category of the section (red dotted polygon). Assume that the cyan line is the centerline of the neighbor candidate. A clipping algorithm first determines the length inLineLength of the candidate within the buffer polygon. In this example, this is the length between p1 and p2. This length must at least be 10 % of the total length of the candidate.

However, a short candidate line could be perpendicular to the template, but almost completely within the buffer polygon. So we need an additional condition: The distances d1 and d2 between the entry and exit points p1 and p2 and the template are computed. Then, some kind of slope relative to the template is determined from the difference between these distances and the length inLineLength of the candidate within the buffer polygon to as slope = abs(d1-d2)/inLineLength. Demanding a small value for slope rules such cases out.

This algorithm has the advantage, that overlapping way-sections continue the equidistant parts, as long as only one side is interrupted. For instance these three way-sections (black lines with red dotted surrounding buffer polygon) would be considered to belong to the same group (Bundle):

However, there is a disadvantage, when the intersections are almost perpendicular, so that this effect does not work anymore. In the image below on the left (middle part in rotterdam_01.osm) this effect is clearly visible. Overlapping sections result in parallel ways through the intersection (all the same color). In the image on the right (intersection between Karl-Marx-Allee and Straße der Pariser Kommune in berlin_karl_marx_allee.osm), the ways are almost perpendicular, and the groups always contain only the short parallel parts (same color is same group).

@vvoovv
Copy link
Member

vvoovv commented Jan 6, 2024

However, a short candidate line could be perpendicular to the template, but almost completely within the buffer polygon.

I don't understand this case. If the short candidate is perpendicular to the template and completely or always completely within the buffer polygon, then there is no intersection or only one intersection of the short candidate with the buffer polygon. So both d1 and d2 can not be calculated or only either of d1 or d2 can be calculated.

@vvoovv
Copy link
Member

vvoovv commented Jan 6, 2024

A buffering polygon is constructed around this line.

On a separate note. To construct the buffering polygon, the original curve must be offsetted. Do you check for self-intersections when offsetting the curve? Do you process the self-intersections? I need this feature to render roadways and sidewalks correctly.

@polarkernel
Copy link
Collaborator Author

I don't understand this case.

The case with the intersection points p1 and p2, which I have drawn above, is only a general illustration. Often, the equidistant candidates are completely inside the buffer polygon, like candidateA in the figure below. Then, the endpoints of the candidate line are used as the entry and exit points of the "inside" part. Because the difference between the distances of these points to the template line is very small compared to the inside length of the candidate, the value of slope gets small.

In this example, you can also see why I called this value slope. If we straightened the template line to a straight horizontal line and drew a straight line using the distances d1 and d2 for the points p1 and p2 of the candidate line, the value of slope would be exactly the slope of the candidate line.

For the other case with candidateB, the difference of the distances from the entry and exit points on the candidate line to the template line is large compared to the inside length of the candidate, the value of slope becomes large. Such a line can't be accepted as parallel (equidistant).

@polarkernel
Copy link
Collaborator Author

Do you check for self-intersections when offsetting the curve? Do you process the self-intersections?

No, not completely. Self-intersections are only processed between adjacent line segments. For the equidistant line detection application, this is not important because lines that are too curved are already excluded from the static spatial index. In addition, I successfully replaced corners with a Corner class, as discussed at the end of #72 (comment).

Some time ago, I made an attempt to use a different algorithm that gave better results, as far as I can remember. I will try to find it and let you know if I do.

@vvoovv
Copy link
Member

vvoovv commented Jan 6, 2024

I actually meant this kind of self-intersections:

image

@polarkernel
Copy link
Collaborator Author

I actually meant this kind of self-intersections:

I think these are computed correctly, as expected for an offset line:

@vvoovv
Copy link
Member

vvoovv commented Jan 6, 2024

I think these are computed correctly, as expected for an offset line

Could you please paste the code to generate the image above. I'd like to know the API calls to generate an offset for a curve with processing of self-intersections.

@polarkernel
Copy link
Collaborator Author

The code was:

from mathutils import Vector
import matplotlib.pyplot as plt
from lib.CompGeom.PolyLine import PolyLine

line = [
    Vector((1.8,3)),
    Vector((1,1.8)),
    Vector((0.2,1)),
    Vector((0,0)),
    Vector((0.2,-1)),
    Vector((0.5,-2)),
    Vector((0.8,-3))
]

polyline = PolyLine(line)
polyline.plot('k',2)

for d in range(20):
    dist = 0.2*(d+1)
    offsetPoly = polyline.buffer(dist,dist)

    x = [n[0] for n in offsetPoly] + [offsetPoly[0][0]]
    y = [n[1] for n in offsetPoly] + [offsetPoly[0][1]]
    plt.plot(x[:-1],y[:-1],'r',linewidth=0.5)
    plt.plot(x[:-1],y[:-1],'b.',markersize=2)

plt.gca().axis('equal')
plt.show()

@vvoovv
Copy link
Member

vvoovv commented Jan 6, 2024

image

I noticed that extra points are created for convex corners. That rounds the resulting corners. Is it possible to turn the rounding mode off?

@polarkernel
Copy link
Collaborator Author

I noticed that extra points are created for convex corners. That rounds the resulting corners. Is it possible to turn the rounding mode off?

Well, for now just a patch: In module lib/CompGeom/OffsetGenerator.py, replace line 91

fillet = self.constructFillet(p2,seg[1],p3,abs(self.distance))

by

fillet = None #self.constructFillet(p2,seg[1],p3,abs(self.distance))

Should you like to use this feature permanently, we will have to introduce some function argument.

@polarkernel
Copy link
Collaborator Author

Some time ago, I made an attempt to use a different algorithm that gave better results, as far as I can remember. I will try to find it and let you know if I do.

I found this old code. It implements a totally different algorithm. It has a function for lines and a function for polygons. As far as I remember, there were some minor floating-point accuracy issues (see red arrow), but it actually works on a much larger parameter range than the one that is in the addon. It is also capable of creating holes, as shown in the examples below, which is not the case with the current one. I am convinced that I would be able to finish it and make it robust and reliable if needed.

@vvoovv
Copy link
Member

vvoovv commented Jan 7, 2024

However, there is a disadvantage, when the intersections are almost perpendicular, so that this effect does not work anymore.

image

Did I understand the problem correctly that it's not possible to join the adjacent bundles for perpendicular way-sections since the buffering polygons do not intersect in that case?

Adjacent way-sections of the bundle can be checked if they are collinear with the way-sections of the bundle and that they also form a bundle.

@polarkernel
Copy link
Collaborator Author

Did I understand the problem correctly that it's not possible to join the adjacent bundles for perpendicular way-sections since the buffering polygons do not intersect in that case?

I should have written difference instead of disadvantage. For now, this is just an observation that may or may not play a role later. I will try to build a more complete set of observations within this thread. For the moment it does not make sense to build a theory, an intersection may also look like this (on the left of osm_extracts/streets/milano_01.osm):

@polarkernel
Copy link
Collaborator Author

I made a first attempt to find clustered intersections. It is quite simple and works as follows:

  • Collect all the endpoints of sections that have been identified as equidistant into a set so that each vertex appears only once.
  • Apply the DBSCAN algorithm to this set of endpoints. This algorithm groups closely-spaced points (points with neighbors within a given maximum distance) into point clusters, which can then be used as nuclei for clustered intersections.

The result looks already quite promising:

Looking at the images of the intersection between Karl-Marx-Allee and Straße der Pariser Kommune in berlin_karl_marx_allee.osm (left) and of the intersection in the middle part of rotterdam_01.osm (right), you can notice a problem, that will have to be discussed (the dotted polygon is the convex hull of all point detected as one group):

In both cases, all intersections belonging to this cluster have been detected, including those between pedestrian and bicycle ways. The question now is, where do the bundles (Bundle class) and the intersections (Intersection class) start? What about crosswalks? Do they belong to the bundles, and what about their intersections?

Filtering out all these details is a major task that now awaits us. Not all crossings are as classically shaped as these two examples above:

@vvoovv
Copy link
Member

vvoovv commented Jan 8, 2024

Not all crossings are as classically shaped

What if intersections with footways and cycleways are ignored? Will the intersections have a more classical shape?

@vvoovv
Copy link
Member

vvoovv commented Jan 8, 2024

What about crosswalks? Do they belong to the bundles, and what about their intersections?

Originally I thought that a crosswalk is a part of a Roadway (see also #72 (comment)). Filets on intersections make the things more complicated. The question can be also raised if a filet should be a part of an intersection.

The crosswalks are located on the filets on the intersection below:

image

I'll think what to do with the filets in that context.

@polarkernel
Copy link
Collaborator Author

What if intersections with footways and cycleways are ignored? Will the intersections have a more classical shape?

Yes. It should also be simple to detect them. I have more concerns about their assignment, as you also noted:

The question can be also raised if a filet should be a part of an intersection.

Locating fillets on bundles can be ambiguous. For example, the one at the top right might belong to the upward or to the right-hand bundle.

@polarkernel
Copy link
Collaborator Author

What if intersections with footways and cycleways are ignored? Will the intersections have a more classical shape?

I committed a version in which intersections between categories of minor roads such as "pedestrian", "track", "footpath", "path", "cycleway" or "bridleway" are distinguished from intersections with major roads. The areas of the two are drawn as convex hull in different saturations, and the intersections of the former are drawn as rectangles and the latter as circles:

@vvoovv
Copy link
Member

vvoovv commented Jan 11, 2024

I also suggest drawing a convex hull for "roadway" intersections (primary, secondary, tertiary, residential, etc).

@polarkernel
Copy link
Collaborator Author

I also suggest drawing a convex hull for "roadway" intersections (primary, secondary, tertiary, residential, etc).

This is already done. The major road intersections have a round dot and their convex hull is darker (or more saturated, large alpha) than the minor road intersections, which have a smaller square dot and a brighter (less saturated, small alpha) convex hull. Major road intersections are defined as those, that have at least one way with a category, that does not belong to the categories of minor roads (pedestrian, track, footpath, path, cycleway, bridleway).

I made this version for visualization purposes only, so we can discuss issues using real-world examples.

@vvoovv
Copy link
Member

vvoovv commented Jan 11, 2024

I meant crossing nodes where ALL ways are not minor roads (pedestrian, track, footpath, path, cycleway, bridleway).

@polarkernel
Copy link
Collaborator Author

I meant crossing nodes where ALL ways are not minor roads (pedestrian, track, footpath, path, cycleway, bridleway).

I see. I made now a version, that produces an additional category of intersections, which have only major roads. These are plotted as circle dots and their convex hull color is fully saturated with alpha=1.

For many clusters, there are only two such major intersections, so it is not possible to draw a convex hull. These are then connected by a thick line:

The version with this feature is committed.

@vvoovv
Copy link
Member

vvoovv commented Jan 11, 2024

I made now a version, that produces an additional category of intersections, which have only major roads.

To be more precise, a crossing node can be shared by minor ways, but it must be shared by at least 3 major ways.

@polarkernel
Copy link
Collaborator Author

To be more precise, a crossing node can be shared by minor ways, but it must be shared by at least 3 major ways.

OK, committed version adapted.

@vvoovv
Copy link
Member

vvoovv commented Jan 11, 2024

To be more precise, a crossing node can be shared by minor ways, but it must be shared by at least 3 major ways.

Sorry. That sentence was not formulated precisely again. I meant at least 3 major way-segments instead of at least 3 major ways.

@polarkernel
Copy link
Collaborator Author

Sorry. That sentence was not formulated precisely again. I meant at least 3 major way-segments instead of at least 3 major ways.

To be precise, here are the rules of the current implementation. For each node that is an intersection, the sum wayCount of all incoming and outgoing ways is determined. The number of ways belonging to the categories pedestrian, track, footpath, path, cycleway or bridleway, is determined as nrOfLow and the rest as nrOfMajor.

The following intersection types are assigned according to the number of different categories:

  • If all wayCount ways belong to the categories pedestrian, track, footpath, path, cycleway or bridleway (wayCount==nrOfLow), the intersection node is classified as low intersection (plotted as small rectangle).
  • If at least three of these ways are major roads (nrOfMajor> 2), the intersection node is classified as major intersection (plotted as large ring).
  • If there are major roads, but less than three (nrOfMajor< 3), the intersection node is classified as main intersection (plotted as large dot).

@vvoovv
Copy link
Member

vvoovv commented Jan 11, 2024

For each node that is an intersection, the sum wayCount of all incoming and outgoing ways is determined.

Did you mean way-segments or ways?

If a node is located at the middle of a way and there are no other ways that share the node, then wayCount=1 and waySegmentCount=2

@polarkernel
Copy link
Collaborator Author

Did you mean way-segments or ways?

I mean way-sections. I have never used the concept of ways that go through an intersection, because it can be ambiguous. If there are multiple way-sections at an intersection, it is not always clear, which ones belong to the same way.

@vvoovv
Copy link
Member

vvoovv commented Jan 12, 2024

I'll think what to do with the filets in that context.

Created #83

@vvoovv
Copy link
Member

vvoovv commented Jan 17, 2024

The question now is, where do the bundles (Bundle class) and the intersections (Intersection class) start? What about crosswalks? Do they belong to the bundles, and what about their intersections?

An intersection is created by finding the intersection points of the roadway borders. So a crosswalk is a part of the roadway.

But there is a notable exception: a diagonal crossing or a pedestrian scramble :

image

It's a part of a intersection and painted right on it. I suggest ignoring the minor ways (footways, crossways) when generating the intersections. Then the footways located within the intersection polygon can be filtered out.

I added the new OSM file streets/tokyo_shibuya.osm which contains the famous Shibuya Crossing.

@vvoovv
Copy link
Member

vvoovv commented Feb 6, 2024

Another reason of having Bundles is the generation of sidewalks.

Without a Bundle the sidewalks are generated along both sides of a Section or another item.

If a Bundle is present, the sidewalks are generated along both sides of the Bundle (or the top Bundle if there is a hierarchy of Bundles), rather than the sides of each member of the Bundle.

@vvoovv
Copy link
Member

vvoovv commented Feb 12, 2024

for src, dst, key, street in manager.waymap.iterSections():
    ...

I started a rewrite of the street renderer. For now I managed to render instances of the class Section using the above method.

image

Let's discuss what data can be delivered to the renderer in the next step of the development.

@polarkernel
Copy link
Collaborator Author

polarkernel commented Feb 12, 2024

Let's discuss what data can be delivered to the renderer in the next step of the development.

I see three different approaches:

  • Implement first items within a Street: SideLane and SymLane. This would be interesting, because we could discuss and find out how to extend the doubly linked list in a Street.
  • Implement PtStop. Requires detecting them, finding a way to hold them in a Street, and finally rendering them. This would be a very demanding task, but is a fairly open point of the structures.
  • After the edges, implement the nodes in the waymap: Intersections. Could be based on the old implementation, using their connectors and the trimming of the way-sections. Nothing really new, but would "beautify" the rendered result. No clustered intersections for now.

@vvoovv
Copy link
Member

vvoovv commented Feb 12, 2024

I'd prefer to start from the intersections.

What are the obstacles now to get clustered intersections?

@polarkernel
Copy link
Collaborator Author

What are the obstacles now to get clustered intersections?

Clustered intersections require not only the definition and construction of clusters, but also those of bundles. This will be a huge and demanding development step. See also the discussion starting here. To get illustrations, you can run the code in script mode, using --highways --generateStreets.

@polarkernel
Copy link
Collaborator Author

I'd prefer to start from the intersections.

In the old implementation, the neighbors of intersections were way-sections. Their boundaries were intersected, as shown below, and these intersection points (red dots) were connected to a polygon (red) that was completed so that the connectors were perpendicular. The way-sections were then trimmed along their centerlines so that the connectors fit the intersection.

With the new structures, the neighbors of an instance of Intersection are instances of Street, connected to their start or end. Also, an intersection cluster at the tails of Bundles will be an instance of Intersection, and similar rules apply. Therefore, instances of Street and of Bundle must end in an instance of Section, otherwise, this algorithm can't be applied. We will need a solution for Crosswalks.

@vvoovv
Copy link
Member

vvoovv commented Feb 12, 2024

Therefore, instances of Street and of Bundle must end in an instance of Section, otherwise, this algorithm can't be applied. We will need a solution for Crosswalks.

What if Crosswalks, PtStops and other similar items are inserted into a Street after the algorithm finished its job? In this manner the algorithm will deal with Sections only, as the neighbors of Intersections.

@polarkernel
Copy link
Collaborator Author

What if Crosswalks, PtStops and other similar items are inserted into a Street after the algorithm finished its job? In this manner the algorithm will deal with Sections only, as the neighbors of Intersections.

This is a viable option. The only condition is, that the perpendicular connector line of the intersection still fits to whatever has been inserted.

Another point is the connection between Intersection and Street. I think, my first idea will not work:

The Intersection's connector should reference the instance of Street and, additionally, an information on which end of this instance. Instead of pred and succ of the Sections, new attributes pred and succ of the instance of Street should reference the instance of the Intersection, and additionally, their connector. When Crosswalks, PtStops and other similar items are inserted into a Street, only the attributes start and end have to be adapted, while the instance of Intersection remains unchanged.

@vvoovv
Copy link
Member

vvoovv commented Feb 12, 2024

I think, my first idea will not work

Then s1.pred = s2.succ = None to indicate that s1 and s2 are the first and the last items in the Street?

@polarkernel
Copy link
Collaborator Author

Then s1.pred = s2.succ = None to indicate that s1 and s2 are the first and the last items in the Street?

Yes. I also propose to rename start and end to head and tail, as it is usually done for doubly linked lists. succ and pred of Street would then reference the Intersection before and after the Street.

Next issue: How can one find the connector to an Intersection, when the street is given, and how can one reference the street that belongs to a connector, when the intersection is given? I would like to keep the idea to use direct references to objects and not to use any index or similar. Let me illustrate that using the image below.

Given the intersection is1, one of its connectors, for instance c2, could contain a tuple c2=(st1, i), where st1 is the reference to the street st1 and i the index of a point on the connector line of the intersection's polygon, as we had it already before. Similarly, the sign of i could indicate the start or the end of st1.

The inverse direction is more complicated. The reference pred of st1 lets us find the instance is1 of the intersection. But how can we find the corresponding connector c2? One could implement connectors as a Python list and add the index 2 to pred of st1. But the net may be modified, streets be removed or be combined to bundles, so that this index must be considered as mutable.

I had an idea, that may look strange in the first moment. I am also not really sure that it would work under all circumstances. It is possible to use an object as key of a dictionary in Python, given it has a __hash__() and a __eq__() method. These could be implemented for our objects, using for instance hash(location) for intersections or hash( (src,dst,key) ) for elongated objects. Using this feature, connectors would be implemented as a dictionary, using connectors[st1]=i to reference st1, while i=st1.pred.connectors[st1] would reference the connector to st1.

Do you have any ideas? Or do you maybe have less or additional requirements, seen from the viewpoint of the renderer?

@vvoovv
Copy link
Member

vvoovv commented Feb 13, 2024

Yes. I also propose to rename start and end to head and tail, as it is usually done for doubly linked lists.

Ok.

Next issue: How can one find the connector to an Intersection, when the street is given, and how can one reference the street that belongs to a connector, when the intersection is given?

Another possible solution is to use a separate class for the connectors and to get rid of the connector indices all together.

class IntConnector:

    def __init__(self, intersection):

        # the intersection, to which the connector belongs to
        self.intersection = intersection

        # the item, to which the connector is connected to
        self.item = None

        # the preceding connector in the intersection (for, example, when considered counterclockwise)
        self.pred = None

        # the succeeding connector in the intersection (for, example, when considered counterclockwise)
        self.succ = None

street.tail and street.head are then set to a instance of the class IntConnector.

@polarkernel
Copy link
Collaborator Author

Another possible solution is to use a separate class for the connectors and to get rid of the connector indices all together.

This is a great idea, I like it. A circular doubly-linked list of instances of IntConnector. The class Intersection would need an anchor to hold a start of this list, for example connectors. Similar to what we already did in the old implementation, within IntConnector, the attribute self.index is the index of a vertex in the intersection polygon, so that the item connects between self.index and self.index+1. Either the sign of this index or the additional attribute self.fwd, which is True, if the start of the street is connected, and False otherwise, define the direction of the street. I would prefer the latter. The order of the polygon in Intersection is counter-clockwise, so it makes sense to order this circular list in the same direction. The class definition becomes then

class IntConnector:

    def __init__(self, intersection):

        # the intersection, to which the connector belongs to
        self.intersection = intersection

        # the first index of the connector in the area polygon of Intersection
        self.index = None

        # the item, to which the connector is connected to
        self.item = None

        # the direction of the item, to which the connector is connected to
        self.fwd = None

        # the preceding connector in the intersection (in clockwise direction)
        self.pred = None

        # the succeeding connector in the intersection (in counter-clockwise direction)
        self.succ = None

OK for you?

street.tail and street.head are then set to a instance of the class IntConnector.

You mean street.pred and street.succ in my image.

Once all the items are set up by the StreetGenerator, do you already have an idea of how you want to access them? It is not possible to just provide a starting item and then to follow the links, because the network may contain islands.

@vvoovv
Copy link
Member

vvoovv commented Feb 13, 2024

The class Intersection would need an anchor to hold a start of this list, for example connectors.

Is a list of connectors? I suggest having an attribute with the name startConnector or firstConnector that holds a reference to an initial connector.

Either the sign of this index or the additional attribute self.fwd, which is True, if the start of the street is connected, and False otherwise, define the direction of the street. I would prefer the latter.

self.index can be also equal to zero, so it can't be used to define the direction of a street. I think the term "incoming" is used throughout the code. I suggest naming it self.incoming.

OK for you?

Yes, with the notes above and below.

You mean street.pred and street.succ in my image.

Yes.

Once all the items are set up by the StreetGenerator, do you already have an idea of how you want to access them?

for src, dst, key, street in manager.waymap.iterSections() seems to be ok for accessing the streets. There is a special case of a circular street. In that case street.tail is equal to street.head. The attributes street.pred and street.succ aren't used.

It is not possible to just provide a starting item and then to follow the links, because the network may contain islands.

Doesn't manager.waymap.iterSections() return all Streets if there are islands?

@polarkernel
Copy link
Collaborator Author

Is a list of connectors? I suggest having an attribute with the name startConnector or firstConnector that holds a reference to an initial connector.

It is a circular doubly-linked list. The attribute startConnector of IntConnector holds a reference to the first connector in the list:

self.index can be also equal to zero, so it can't be used to define the direction of a street. I think the term "incoming" is used throughout the code. I suggest naming it self.incoming.

You are right. To be consistent with the existing code, I named it self.leaving. If this is True, this means that the start of the street is at the intersection. The sequence of the items in Street start at head.

for src, dst, key, street in manager.waymap.iterSections() seems to be ok for accessing the streets. There is a special case of a circular street. In that case street.tail is equal to street.head.

Yes.

The attributes street.pred and street.succ aren't used.

Yes, if it is a circular street with no intersection. For a loop, which is also kind of circular, these must reference their intersections

Doesn't manager.waymap.iterSections() return all Streets if there are islands?

Yes, it returns all Streets in the network.

@vvoovv
Copy link
Member

vvoovv commented Feb 14, 2024

I think everything is agreed now. Looking forward to the intersections in the street renderer.

@polarkernel
Copy link
Collaborator Author

I think everything is agreed now. Looking forward to the intersections in the street renderer.

All this is already implemented in the current version. Most of the intersections already look as desired, when executed in script mode:

1

but there are overlaps, because we do not yet have any clustering, which can produce some "salad" like this

2

There also seem to be some minor bugs in the construction of the intersections. However, the structures should already be correct (though not yet thoroughly tested), so you might want to do some initial checks.

@vvoovv
Copy link
Member

vvoovv commented Feb 14, 2024

Would it be possible to enable trimming of centerlines due to the intersections?

@polarkernel
Copy link
Collaborator Author

Would it be possible to enable trimming of centerlines due to the intersections?

Committed. It is a very temporary solution, implemented only for your checks. It will be solved elsewhere, once I finalize the code.

Note: Section has become an attribute valid. Use only valid Sections.

@vvoovv
Copy link
Member

vvoovv commented Feb 14, 2024

Note: Section has become an attribute valid. Use only valid Sections.

If a Section in a Street is invalid, does it mean that the whole Street becomes invalid?

@vvoovv
Copy link
Member

vvoovv commented Feb 14, 2024

Also, I'd like to have all Intersections in a Python list (e.g. manager.intersections). I think it's much easier to create it in the street generator, than trace the intersection in the renderer down, since the Intersections are created consequently in the street generator.

@polarkernel
Copy link
Collaborator Author

If a Section in a Street is invalid, does it mean that the whole Street becomes invalid?

For now, yes. This is only a temporary solution, because I cannot handle all problems with short sections at the moment. Later, invalid sections will be removed, and the links will be adjusted somehow.

Also, I'd like to have all Intersections in a Python list (e.g. manager.intersections).

I assumed that when I asked for "how you want to access them". I will provide that as soon as possible. Currently, only intersections with three or more leaving sections are processed. Later, the others will become SymLanes, SideLanes, Corners(?), ..., they are not yet included in this list.

@polarkernel
Copy link
Collaborator Author

I will provide that as soon as possible.

Committed.

@vvoovv
Copy link
Member

vvoovv commented Feb 15, 2024

I'll take some time to develop the code.

I'd suggest including SymLanes and SideLanes in the next step. I suggest not including them to manager.intersections, since there is a 1-to-1 mapping between a transition and its Street.

@polarkernel
Copy link
Collaborator Author

I'd suggest including SymLanes and SideLanes in the next step. I suggest not including them to manager.intersections, since there is a 1-to-1 mapping between a transition and its Street.

OK, I intend to implement them as a linked instance between two Sections in a Street, as shown in the image here.

@vvoovv
Copy link
Member

vvoovv commented Feb 15, 2024

OK, I intend to implement them as a linked instance between two Sections in a Street, as shown in the image here.

I meant exactly that.

@polarkernel
Copy link
Collaborator Author

I'd suggest including SymLanes and SideLanes in the next step. I suggest not including them to manager.intersections, since there is a 1-to-1 mapping between a transition and its Street.

A first version has been committed. I think the new classes SymLane and SideLane are almost self-explanatory with my comments. They are embedded in their Street in a double-linked list, as shown here.

For SideLane, the incoming and outgoing sections are not trimmed at the transition vertex. The transition must be created in the addon. The widths of the incoming and outgoing sections are still corrected according to this post.

A SymLane contains an attribute area, which is the polygon of its transition area. The incoming and outgoing sections are trimmed at the transition vertex to fit the transition area. I didn't add connectors as we did for Intersections. The incoming (smaller) section always connects to the vertex area[0] and the outgoing section to area[2] (as the first vertex in counter-clockwise direction).

In the current version, sections are not yet split by Corners, as discussed here, because we do not yet use parallel sections.

@polarkernel
Copy link
Collaborator Author

I didn't add connectors as we did for Intersections.

Sorry, I forgot to remove some code that uses connectors in SymLane. A fixed version without this code is committed.

@polarkernel
Copy link
Collaborator Author

I committed a version with code for experimental intersection clustering. This is only for backup reasons, the code itself is not called.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants