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

Workflow and data structure for streets #72

Open
vvoovv opened this issue Dec 4, 2023 · 63 comments
Open

Workflow and data structure for streets #72

vvoovv opened this issue Dec 4, 2023 · 63 comments

Comments

@vvoovv
Copy link
Member

vvoovv commented Dec 4, 2023

No description provided.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 4, 2023

(1)
Create instances of the class Section (currently StreetSection). The attributes start and end are set to either a neighbor Section or an intersection or None (in the case of a dead-end).

Street sections are grouped into roadways. A street may have one, two or more roadways. If a street contains two or more roadways, then they are grouped into a cluster.

The class Street should have an attribute roadways. It's a Python tuple with a single element in the case of a single roadway and a Python list in the case of two or more roadways.

The class Roadway should have an attribute start. It is the reference to the first street section (an instance of the class Section) in the roadway. The other street sections in the roadway can be accessed with the related linked list (the attributes start and end of the class Section).

@vvoovv
Copy link
Member Author

vvoovv commented Dec 4, 2023

(2)
A PML style is set for the instance of the class Street using the function getStyle supplied to the class StreetGenerator. The categories of street sections and the number of roadways will be used to assign a PML style to an instance of the class Street.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 4, 2023

(3)
A PML style should contain a style block street with the @setup directive where the default attributes are set:

@setup
street {
    numLanes: random_weighted( (2, 70), (4, 30) );
    numLanesOneway: random_weighted( (1, 75), (2, 25) );
}

Those style blocks with the @setup directive are evaluated immediately after assigning the PML style. The evaluated attributes numLanes and numLanesOneway and other, if provided, will be used if the number of lanes is not given in the OSM data for a street section.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 4, 2023

(4)
Perform the initialization like in the current method createWaySections(): setting the number of lanes, calculating widths, etc,

@vvoovv
Copy link
Member Author

vvoovv commented Dec 4, 2023

(5)
Crosswalks should be extracted from the OSM data.

An OSM node representing a crosswalk is tagged with crossing=*. I suggest using the attribute crosswalkNodeIndices in the class Section to store a Python list of node indices of the cropped centerline where a sidewalk is located.

An OSM node with a crosswalk may be in the cropped part. In that case case the index of the node with the crosswalk will be 0 or N-1, where N is the number of nodes in the cropped centerline.

@polarkernel
Copy link
Collaborator

(1)
Create instances of the class Section (currently StreetSection). The attributes start and end are set to either a neighbor Section or an intersection or None (in the case of a dead-end).

Intersection areas do not yet exist at this stage. Perhaps, the positions of the intersection nodes (currently of mathutils Vector class) could be used as keys to a container, that will later be filled by instances of theIntersectionArea class that belong to these nodes. Later in the process, the start and end attributes will need to be adjusted, when intersections are merged or clustered.

Besides neighbor sections, intersections and dead-ends, instances of a TransitionSymLane class or a TransitionSideLane class should also be considered as contents of the start and end attributes. But there we have the same problem as above, they do not exist yet at this time.

Street sections are grouped into roadways. A street may have one, two or more roadways. If a street contains two or more roadways, then they are grouped into a cluster.

I am not sure if I understand this correctly. Does the Roadway class group also include streets that are separated by instances of a TransitionSymLane class or a TransitionSideLane class, as shown here, or is Roadway somehow a replacement for what we had with the WayCluster class? Note that clusters do not yet exist at this stage.

The class Roadway should have an attribute start. It is the reference to the first street section (an instance of the class Section) in the roadway. The other street sections in the roadway can be accessed with the related linked list (the attributes start and end of the class Section).

This will work fine for linear cases, like those with the TransitionSymLane class or a TransitionSideLane class, as described above. But it will not work for clusters, because multiple way-sections may start from the same (clustered) intersection. Then, the start attribute will have to be a list.

@polarkernel
Copy link
Collaborator

polarkernel commented Dec 5, 2023

The evaluated attributes numLanes and numLanesOneway and other, if provided, will be used if the number of lanes is not given in the OSM data for a street section.

I still do not understand this idea. The only case I know of where the number of lanes is not specified in OSM is when there is no lanes tag. Then, as far as I know, OSM assumes one lane for one-way streets and two lanes for the others. Would it make sense to change this using random numbers from the PML attributes?

EDITED:
It seems that I am wrong. We even defined a default number of lanes in our default way properties wayCategoryProps in way_properties.py. I don't know to what extent this was justified. However, maybe the question is whether this table should be moved to PML eventually, as these numbers may have some locale aspect.

@polarkernel
Copy link
Collaborator

(5)
Crosswalks should be extracted from the OSM data.
An OSM node representing a crosswalk is tagged with crossing=*. I suggest using the attribute crosswalkNodeIndices in the class Section to store a Python list of node indices of the cropped centerline where a sidewalk is located.

This is now new to me. I searched for OSM objects tagged as crossing=* using overpass turbo and got something like this:

The result seem to be footways. What do you mean by "node indices of the cropped centerline where a sidewalk is located"? Are these the nodes of the trimmed centerline, between them a crossing footway intersects the centerline? Or are you talking about objects that are marked as sidewalk?

@vvoovv
Copy link
Member Author

vvoovv commented Dec 5, 2023

Intersection areas do not yet exist at this stage.

How about using an empty Intersection class? It can be later filled with values.

Besides neighbor sections, intersections and dead-ends, instances of a TransitionSymLane class or a TransitionSideLane class should also be considered as contents of the start and end attributes. But there we have the same problem as above, they do not exist yet at this time.

Linked lists allow inserting and removing items easily. So a TransitionSymLane or a TransitionSideLane can be inserted later once the numbers of lanes are set and the calculations are performed. Similarly, a future Footprint class or a PtStop class (for a public transport stop) can be inserted into a linked list that forms a Roadway class. I will write about those classes later.

I am not sure if I understand this correctly. Does the Roadway class group also include streets that are separated by instances of a TransitionSymLane class or a TransitionSideLane class, as shown here, or is Roadway somehow a replacement for what we had with the WayCluster class? Note that clusters do not yet exist at this stage.

A class Roadway holds a reference to a linked list that is made of instances of the classes Section, TransitionSideLane, TransitionSymLane, Footway, PtStop, etc. In the majority of cases it contains just a single instance of Section.

The class Street can be considered as a replacement of the class WayCluster. However in the majority of cases it contains just a single instance of the class Roadway.

It would be highly desirable to do clustering before assigning a PML style to an instance of the class Street, since a double-roadway street looks differently than a single roadway street.

This will work fine for linear cases, like those with the TransitionSymLane class or a TransitionSideLane class, as described above. But it will not work for clusters, because multiple way-sections may start from the same (clustered) intersection. Then, the start attribute will have to be a list.

Do you mean something like on the image below? The intersections at the start and at the end of an instance of the class Street are shown with the blue color.

image

@vvoovv
Copy link
Member Author

vvoovv commented Dec 5, 2023

However, maybe the question is whether this table should be moved to PML eventually, as these numbers may have some locale aspect.

Yes, that table should be moved to PML.

Using the function random_weighted to set the number of lanes and other attributes does make sense. All street categories have the most probable value of the number of lanes. But there are always exceptions. Other values of the number of lanes can also happen with a smaller probability.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 6, 2023

This is now new to me. I searched for OSM objects tagged as crossing=* using overpass turbo and got something like this:

The tag crossing=* is applied only to nodes.

The result seem to be footways. What do you mean by "node indices of the cropped centerline where a sidewalk is located"? Are these the nodes of the trimmed centerline, between them a crossing footway intersects the centerline? Or are you talking about objects that are marked as sidewalk?

Let's consider the following intersection:

image

Suppose that the OSM way on the right starts in the center of the intersection. A node with the sidewalk marked with the red color has the index 2 in the OSM way. After generating the intersection, nodes with the indices 0 and 1 will be cropped. A new node with the index 0 will placed at the border with the the intersection. The node with the crosswalk will have the index 1 after cropping.

@vvoovv vvoovv changed the title Workflow for streets Workflow and data structure for streets Dec 7, 2023
@vvoovv
Copy link
Member Author

vvoovv commented Dec 7, 2023

I suggest introducing a new data element Bundle for complex cases like on the image below:

image

An instance of the class Street is always composed of a chain of items represented by a chained list. For the above example:
Section | SplitMerge | Bundle

An instance of the class Bundle defines parallel chains or threads or items. I don't know yet what is the proper name for that.
In the example above a bundle would have: Section in the lower roadway and another Section in the upper roadway.

Consider a more complex example:
image

Abbreviations used in the image:
(S) - Section
(C) - Crosswalk
(PT) - PtStop
(SM) - SplitMerge

The chained list for this case:
Section | Crosswalk | Section | PtStop | Section | SplitMerge | Bundle

The chained list for the lower roadway of the Bundle:
Section | Crosswalk | Section

The chained list for the upper roadway of the Bundle:
Section | Crosswalk | Section | PtStop | Section

A Bundle can have another Bundle in its roadway.

Note that the class Roadway is not used anywhere in the examples. At the moment I am not sure if it is needed at all.

@polarkernel
Copy link
Collaborator

The tag crossing=* is applied only to nodes.

You are right, I did a mismatch in my overpass turbo query. We will need a new method in the WayManager class to query for these crossing nodes, similar to getAllVehicleWays().

I suggest using the attribute crosswalkNodeIndices in the class Section to store a Python list of node indices of the cropped centerline where a sidewalk is located.
An OSM node with a crosswalk may be in the cropped part. In that case the index of the node with the crosswalk will be 0 or N-1, where N is the number of nodes in the cropped centerline.

It will be easy to derive this number from the trim parameter of the way-section's polyline.

@polarkernel
Copy link
Collaborator

How about using an empty Intersection class? It can be later filled with values.

Yes, this will be the solution.

Linked lists allow inserting and removing items easily. So a TransitionSymLane or a TransitionSideLane can be inserted later once the numbers of lanes are set and the calculations are performed.
A class Roadway holds a reference to a linked list that is made of instances of the classes Section, TransitionSideLane, TransitionSymLane, Footway, PtStop, etc.

I assume you mean a linear, doubly linked list. But that requires that the position of all these objects (TransitionSymLane, TransitionSideLane, Footprint or a PtStop) has to be detected before this list is constructed. Otherwise, we risk, that a Section has to be split by one of these objects later:

| -------- Section --------|        =>        | ---- Section ----| --PtStop--| ---- Section ----|

It would be highly desirable to do clustering before assigning a PML style to an instance of the class Street, since a double-roadway street looks differently than a single roadway street.

In principle, the complete 2D topology has to be known before PML styles get assigned. This is possible, I think, but it is a major redesign.

Do you mean something like on the image below? The intersections at the start and at the end of an instance of the class Street are shown with the blue color.

Yes. You wrote:

The class Roadway should have an attribute start. It is the reference to the first street section (an instance of the class Section) in the roadway. The other street sections in the roadway can be accessed with the related linked list (the attributes start and end of the class Section).

On the right side of the drawing, there are two starts.

@polarkernel
Copy link
Collaborator

I suggest introducing a new data element Bundle for complex cases like on the image below:

I like this idea, and I also think the term Bundle fits the underlying structure well.

What I still don't really like is the special importance of an intersection. If I put an additional one to one of the arms of the bundle, a situation that occurs frequently, the structure gets split somehow, it is no more linear. While the Crosswalk in your illustration appears on both parallel ways of the bundle, an intersection can't do that:

However, I am afraid I do not have a reasonable suggestion as to how this could be improved.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 8, 2023

I assume you mean a linear, doubly linked list.

Yes.

Otherwise, we risk, that a Section has to be split by one of these objects later:
| -------- Section --------|        =>        | ---- Section ----| --PtStop--| ---- Section ----|

I don't consider splitting of sections as a risk. I consider splitting as an integral part of the workflow, since it's required to split the related Section to insert an instance of Crosswalk or PtStop. The calculations can be delegated to the Geometry Nodes, but then the trim lengths are needed as the input parameters for the GN-setup.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 8, 2023

I forgot to describe the case of the lower arm of the bundle. Here we would have:
Section | PartialIntersection | Section

@polarkernel
Copy link
Collaborator

I don't consider splitting of sections as a risk. I consider splitting as an integral part of the workflow ...

I am somewhat reluctant to use this seemingly simple solution of mapping all routes with linked lists. Let me try to explain my reasons.

The simple splitting of sections (list nodes) within a doubly-linked list is quite simple and well known. A problem arises when it is necessary to find all these sections at once. This is the case when they have to be inserted into a spatial index (for performance reasons). Then you have to search all of them along many linked lists, insert them into the index, and somehow provide a reference to find them backwards, when some of them are given as a result of an index operation (for example, find all neighbors of one of them). If any changes have to be made due to such an operation, the change is not only in the linked list, but also in the reference index. Of course this is possible, but it is quite complicated and prone to errors.

There are several tasks in the whole process, for instance building clusters or merging intersecting areas, where such situations occur. As I said, I had some concerns and therefore did not choose such approaches in my previous solution. But let's give it a try.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 9, 2023

As I said, I had some concerns and therefore did not choose such approaches in my previous solution. But let's give it a try.

What are the alternatives? Perhaps there is a way to combine two approaches.

@polarkernel
Copy link
Collaborator

What are the alternatives? Perhaps there is a way to combine two approaches.

You have studied what it takes to use PML in a meaningful way. This way, I get very concrete requirements for the processes in StreetGenerator, and we establish a more detailed cooperation. Even though it means resetting and redesigning much of the development that has already been done, I like this situation. So let us try to find a solution.

Many of the previous ideas can be reused, perhaps in a slightly different form. I am wondering: Should we start a new branch so that the previous code is preserved even if it is no longer used? I already stored a lot of functions in the code that do not currently have any use.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 10, 2023

I created the tag dev_before_street_pml pointing to the current version of the branch streets_for_gn. That's another way to preserve the code. Legacy functions can be kept in a dedicated Python module.

@polarkernel
Copy link
Collaborator

I like this idea, and I also think the term Bundle fits the underlying structure well.

I would like to take a closer look at the "bundle" concept. When we used clusters, these were constructed more or less together as cluster-way and cluster-intersection. Here are two examples of such cluster intersections, on the left the simple one (East of Karl-Marx-Allee scene) and on the right a more complicated one with four way section ends, two of which go underground (Moscow scene).

    

Intersection areas can only be calculated if the width of all way-sections is known, which is not yet the case when getStyle is called for PML processing. At this point, only for the first, simpler example (left image), it is known that every two paths with their endpoints belong to a Bundle.

    

My suggestion now is that I try to find the intersections that belong to these endpoints and define their center of gravity as the provisional node of a still incomplete intersection (red in the right image). Each of its connectors could then be a list of instances of the Section class, holding the way-section, that start at this (bundle) intersection.

But what to do with the sections marked by green lines? When we were working with clusters, in most cases they had to be deleted at the time when the area was constructed. But I'm not sure whether it would be safe to remove them before the getStyle call. On the other hand, it would be complicated to hold them using the single endpoint intersections in addition to the temporary clustered intersection. I'll have to think about that.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 10, 2023

What if the largest possible roadway width is used to detect a cluster of roadways?

@polarkernel
Copy link
Collaborator

I am currently investigating structures that can be used locally in StreetGenerator, with which I can then create the desired class instances ready for a call to getStyle. In the meanwhile, a few new questions have arisen.

The first ones arise from the question we have already begun to discuss in issue #66. We need to define directions for the linear structures proposed in this thread, they should have a source (src) vertex and a destination (dst) vertex. Instances of the class Section should use the definition given by OSM: Forward is in the direction of the drawing (the order of the nodes) and backward is in the opposite direction. By the way: We should probably not forget the driving_side tag.

The question is, how shall we define the directions of the higher-level structures (Roadway, Bundle)? For Roadway, we could use the direction of the internal Section instances. But the same would not work always for a Bundle (draw direction in red):

If we were to use the x- and y-coordinate rules, the start coordinates of the Roadway instances could sometimes be on opposite sides of the bundle.

@polarkernel
Copy link
Collaborator

At the call to getStyle, do the lane attributes of a Section instance already need to be computed?

        self.isOneWay = False       # True if one-way road.
        self.totalLanes = None      # Total number of lanes, includes bothLanes
        self.forwardLanes = None    # Number of forward lanes
        self.backwardLanes = None   # Number of backward lanes
        self.bothLanes = None       # Number of lanes that allow a turn available in both directions (tag lanes:both_ways)

@polarkernel
Copy link
Collaborator

I already often flirted with using the library networkx for various network tasks in the blosm addon. But I didn't know how to install the library inside Blender, since pip install is not possible. But now, I found an article here, showing how easy it can be. In principle, it is sufficient to import a specific folder with pure Python code, similar as we did with ANTLR4 for PML, for example. I tested it and it works fine.

So I decided to base the refactoring of the StreetGenerator on this library. Perhaps it would make sense to rewrite some older code later, if it is based on graphs and networks.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 13, 2023

At the call to getStyle, do the lane attributes of a Section instance already need to be computed?

It's not required.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 13, 2023

I already often flirted with using the library networkx

I see the packages scipy and pandas in the dependency list. Do the modules of networkx which you used in your tests, import those packages? Can networkx be just copied to blosm folder and used as-is?

Installing external packages for Blender's Python can be very tricky taking into account a broad user base of the addon.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 13, 2023

The question is, how shall we define the directions of the higher-level structures (Roadway, Bundle)?

At the moment I can't propose for bundles anything else besides the XY-coordinates rule. The details of the calculation can be hidden in a method getDirection or similar. The practice will show which method to determine the direction will be the best.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 18, 2023

Finally, I was able to create a version of NetworkX that excludes all unwanted dependencies and works fine for my purposes.

It would be good to describe it briefly how to get this version.

a way-map

I suggesting adding the definition of a way-map to our section Definitions.

@polarkernel
Copy link
Collaborator

It would be good to describe it briefly how to get this version.

The version of blosm_networkx in the lib folder of the blosm addon is based on the latest release 3.2.1 of NetworkX downloaded from its GitHub repository on 12/16/2023. Only the code in its folder networkx is used.

Blender does not support the scipy and pandas packages, which are part of the NetworkX requirements. Therefore, we have reduced our version (blosm_networkx) by removing all files in it, that use functions from these packages or derivatives of methods that use them.

In detail, we did:

  • Remove all folders named test. They are not needed within the addon.
  • In the file convert.py, of the root folder networkx/, comment out the parts that start with Pandas DataFrame and scipy sparse array - any format.
  • Remove the specified files in the folders listed below and adapt the files named __init__.py in these folders:
    • networkx/: conftest.py, convert_matrix.py, lazy_imports.py.
    • networkx/alorithms/approximations: traveling_salesman.py.
    • networkx/alorithms/assortativity: correlation.py.
    • networkx/alorithms/bipartite: matching.py, matrix.py, spectral.py, covering.py.
    • networkx/alorithms/centrality: eigenvector.py, flow_matrix.py, laplacian.py, subgraph_alg.py, current_flow_betweenness.py, current_flow_betweenness_subset.py, current_flow_closeness.py.
    • networkx/generators: geometric.py, random_graphs.py, spectral_graph_forge.py.
  • Remove the following folders completely:
    • networkx/alorithms/link_analysis
    • networkx/linalg
  • Rename the folder networkx to blosm_networkx.

@polarkernel
Copy link
Collaborator

I suggesting adding the definition of a way-map to our section Definitions.

The way-map, as I intend to use it, does not fit into these (by the way very old) definitions. It is just a tool of mine to keep all objects in a single structure. It has two main purposes. First, it should be a factory for any linked lists you want to have. Second, it should allow me to perform operations, such as splitting ways, without needing to find the same object in multiple structures. It should be a solution to the problem mentioned here. At present, its functionality is still rather unclear and experimental.

What about the new definitions Intersection, PartialIntersection, SideLane, SymLane, Crosswalk, PtStop, DeadEnd, Section, Roadway, Bundle?

@vvoovv
Copy link
Member Author

vvoovv commented Dec 18, 2023

I suggest moving modules from way/waymap/nodes and way/waymap/section to way/items like it's done for buildings.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 18, 2023

What about the new definitions Intersection, PartialIntersection, SideLane, SymLane, Crosswalk, PtStop, DeadEnd, Section, Roadway, Bundle?

I like them. At the moment I am not sure if the item Roadway will be needed at all.

@polarkernel
Copy link
Collaborator

I suggest moving modules from way/waymap/nodes and way/waymap/section to way/items like it's done for buildings.

Done.

@polarkernel
Copy link
Collaborator

If you ever get some time: I could use a manager to find the nodes for crosswalks and public transport stops from the OSM data. I think this can't be done using the WayManager, or am I wrong?

@vvoovv
Copy link
Member Author

vvoovv commented Dec 18, 2023

Some of these nodes may have multiple connections, so we need to define something similar to the connectors, that we had before. Then we need three line objects, each with a source attribute src and a destination attribute dst, which are the locations of nodes and together are used as keys in the way-map: Section, Roadway, Bundle

The existing class Intersection can be used for now. I don't understand what is meant under three line objects.

Note that these are not the same as the location src and dst attributes.

How will the attributes src and dst be used?

I would prefer to call these attributes pred, for predecessor, and succ, for successor.

How about calling them prev and next?

I do not know yet if PtStop will split its section.

A PtStop can split the adjacent sections if it has a special marking on the roadway and/or a bus bay as shown at #76 (comment)

It is still unclear to me where a Roadway sequence ends when more than one section is used. Is it only at Intersection and PartialIntersection

A Roadway can end at Intersection. It can't end at PartialIntersection.

I have replaced your SplitMerge object in the middle with an Intersection object, as it was done before for clusters.

I thought SplitMerge and Intersection are different. A Street can't end at a SplitMerge. A Roadway can be split into a number of items at a SplitMerge or several items of a Bundle can be merged into a single Roadway at the SplitMerge.

Should the crosswalk or the bus-stop or both be included in a Roadway object?

Yes.

On the right, can a Bundle be constructed somehow (in the old implementation, without crosswalk and bus-stops, this was a cluster). Where does the Crosswalk belong to?

A Bundle can be considered as an upgraded version of a Cluster. Without crosswalks and pt-stops we would have:

  • for the upper Roadway: Section
  • for the lower Roadway: Section | SplitMerge | Section

The Crosswalk belongs to both roadways.

@polarkernel
Copy link
Collaborator

The existing class Intersection can be used for now. I don't understand what is meant under three line objects.

I had some trouble with the language. Unlike nodes, which are local objects, Section, Roadway, and Bundle have a length and are similar to a line. So I called them line objects. Linear did not seem correct to me, as it somehow means straight to my understanding. I couldn't find any better term.

How will the attributes src and dst be used?

They will be instances of mathutils.Vector and will only be used as keys in the way-map structure, that inherits from MultiDiGraph, a class of the blosm_networkx.

How about calling them prev and next?

I wanted to avoid that, because next is a Python keyword. Note that in contrast to src and dst, these are references to objects.

A PtStop can split the adjacent sections if it has a special marking on the roadway and/or a bus bay as shown at #76 (comment)

You say "can split", and, in #76 (comment), "The related Section may be split ...", both ambiguous, therefore my question. What construction with Section should it be, if not split? What are the rules for when to split and when not to split?

A Roadway can end at Intersection. It can't end at PartialIntersection.

I will need a recipe on how a Roadway (and a Bundle) is constructed.

I thought SplitMerge and Intersection are different. A Street can't end at a SplitMerge. A Roadway can be split into a number of items at a SplitMerge or several items of a Bundle can be merged into a single Roadway at the SplitMerge.

We never had a SpliMerge until now, this term is new. I can't imagine a situation, where a Bundle gets merged into a single Roadway. What we had until now was a SymLane, which connected two way-sections with different width, using an area (-> split), or an IntersectionCluster, which was the intersection at the end of a cluster. Something like this:

@vvoovv
Copy link
Member Author

vvoovv commented Dec 19, 2023

So I called them line objects.

A pt-stop is also a line object, it may have a noticeable length. It's another reason why I proposed to move all of them to the directory items.

I would prefer to call these attributes pred, for predecessor, and succ, for successor.

Ok, let's use the names pred and succ.

What construction with Section should it be, if not split? What are the rules for when to split and when not to split?

A PtStop splits its section if there is a special marking on the roadway and/or a bus bay. There is no need to split the section if a pt-stop is only marked with a pole on the sidewalk. That will be defined in the PML style.

I will need a recipe on how a Roadway (and a Bundle) is constructed.

The construction of Roadways can skipped for now. I suggest considering ambiguous cases for the constructions of Bundles.

We never had a SpliMerge until now, this term is new.

An example of a possible class SplitMerge can be found at berlin_karl_marx_allee.osm on Strasse der Pariser Kommune. The image below was rotated.

image

@vvoovv
Copy link
Member Author

vvoovv commented Dec 19, 2023

If you ever get some time: I could use a manager to find the nodes for crosswalks and public transport stops from the OSM data. I think this can't be done using the WayManager, or am I wrong?

The code to detect and process crosswalks and pt-stops must be developed. I suggesting discussing that in #73 and #76.

@polarkernel
Copy link
Collaborator

An example of a possible class SpliMerge can be found at berlin_karl_marx_allee.osm on Strasse der Pariser Kommune. The image below was rotated.

OK, we can introduce the additional class SplitMerge. The problem for the example at the Strasse der Pariser Kommune is the distance between the end of the Bundle and the position of the intersection of the OSM-ways, here shown as a result of the old implementation using clusters:

The cluster (green area) ends where the ways are no more parallel (red dots). From there, the intersection of the OSM-ways (yellow dot) is more than 30 m away. It is now a matter of heuristics whether such a long distance is accepted as a single area connected to the cluster or not.

The SplitMerge class will have an area polygon and three connector-like records as attributes, just like an Intersection. It could be defined as a special intersection, that merges two one-way Sections of opposite directions within a Bundle into a two-way Section. Some kind of maximum size for such a structure will have to be defined.

@vvoovv
Copy link
Member Author

vvoovv commented Dec 20, 2023

Below is the example of a pure N-to-1 merge at Tijuana border crossing. There is also a pure 1-to-N split next to this spot.

image

@vvoovv
Copy link
Member Author

vvoovv commented Dec 20, 2023

Nevertheless, the class still can be named as SplitMerge.

@polarkernel
Copy link
Collaborator

I made an approach to tune the algorithm, that finds groups of parallel ways, but now for the complete set of ways, that is the way-sections returned by the getAllWays() function of the way-manager, instead of the getAllVehicleWays() function. The results are not filtered yet, but already look encouraging (osm_extracts/streets/milano_01.osm):

Maybe I should change the term parallel to equidistant, because the way-sections can also be curved (same scene and osm_extracts/facade_visibility/bratislava_old_town.osm):

Although equidistant is only true for any two ways in the group, while other ways may have a different, but still constant, distance. For example, I like the result on osm_extracts/facade_visibility/moscow_leningradski_prospekt.osm, where sometimes up to 9 objects were correctly grouped along the Leningradski Prospekt:

But I also found a problem that needs to be solved somehow. Paths that form a corner must normally be excluded from the equidistant group. However, if we split them at the corner and introduce e.g. a Corner class, more equidistant groups could be found. Here are some examples (corners are marked with an arrow):

Such effects are usually to be expected for cycleways and footways. The example on the left is from osm_extracts/streets/milano_01.osm, the footways around the Via Carlo Gomes, while the example at the right is from osm_extracts/streets/piestany_north.osm, also footways along the Díčova.

@vvoovv
Copy link
Member Author

vvoovv commented Jan 2, 2024

I made an approach to tune the algorithm, that finds groups of parallel ways, but now for the complete set of ways

A great result!

How to reproduce it?

@polarkernel
Copy link
Collaborator

How to reproduce it?

I have committed a version that plots the result of this algorithm, when executed in script mode. The plotting code is at the end of the generate_streets.py module, and the output to the figure starts at line 651 of this module.

If (at the same line) the variable inPieces == False, the whole result is plotted. If inPieces == True, one equidistant group at a time will be plotted. The latter may be helpful in thinking about filtering and processing these groups.

BTW: Is there any way to tell if the code is running in script mode or as an addon in Blender? This would be useful when using pyplot statements to avoid runtime errors.

@vvoovv
Copy link
Member Author

vvoovv commented Jan 3, 2024

What are the parameters in the command line to reproduce the results?

@polarkernel
Copy link
Collaborator

What are the parameters in the command line to reproduce the results?

--highways --generateStreets --setupScript path_to/mpl_blosm.py --osmFilepath path_to/milano_01.osm

@vvoovv
Copy link
Member Author

vvoovv commented Jan 3, 2024

BTW: Is there any way to tell if the code is running in script mode or as an addon in Blender? This would be useful when using pyplot statements to avoid runtime errors.

I've just added the attribute type to the classes derived from app.BaseApp.

The following expressions can be used the methods of the class StreetGenerator:

if self.app.type == AppType.commandLine:
    ...

@polarkernel
Copy link
Collaborator

The following expressions can be used the methods of the class StreetGenerator

Thanks!

@vvoovv
Copy link
Member Author

vvoovv commented Jan 3, 2024

Some visible issues:

image

@polarkernel
Copy link
Collaborator

Some visible issues:

In the old version, almost all of them became intersection clusters. But this is just a matter of filtering. For example, if I reduced the minimum lengths minTemplateLength and minNeighborLength (in the first two lines in defs/way_cluster_params.py) from 20 m to 5 m, most of these parts are also detected. However, for further processing, these will become delicate parts anyway:

@vvoovv
Copy link
Member Author

vvoovv commented Jan 3, 2024

In the old version, almost all of them became intersection clusters.

Ok. Then the next step is to combine it with the generation of intersection clusters.

@vvoovv
Copy link
Member Author

vvoovv commented Jan 23, 2024

Class Section

Attributes for rendering:

  • category: the category of the related OSM-way
  • centerline: a Python list of coordinates
  • offset: an offset from the centerline
  • width: a section width
  • forwardLanes
  • backwardLanes
  • bothLanes
  • totalLanes
  • pred
  • succ

A value of the attributes pred and succ can be None in the case of a dead-end or an instance of Intersection, PartialIntersection, Crosswalk, TransitionSideLane, TransitionSymLane, SplitMerge, PtStop.

In addition the following methods are needed:

  • split(nodeIndex, item, itemLength). The method splits the section at the nodeIndex and inserts the item which has the length itemLength. The attributes centerline, pred, succ of the items in question are changed or set accordingly.

  • trimStart(item, itemLength). The method trims the section at the start and inserts the item at the start of the section. The item has the length itemLength. The attributes centerline, pred, succ of the items in question are changed or set accordingly.

  • trimEnd(item, itemLength). The method trims the section at the end and inserts the item at the end of the section. The item has the length itemLength. The attributes centerline, pred, succ of the items in question are changed or set accordingly.

However those methods will be called in new actions to be created rather than in the renderer.

@vvoovv
Copy link
Member Author

vvoovv commented Jan 25, 2024

Also the attribute category is needed. As I can see, it's already available in the class Section.

@vvoovv
Copy link
Member Author

vvoovv commented Jan 31, 2024

The boolean attribute oneway is also needed.

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