prochitecture / blosm

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

Splitting a section #94

Closed vvoovv closed 5 months ago

vvoovv commented 7 months ago

I'd like to split a section with a side lane transition. I think the split should happen in the renderer code. The split is needed for a more straightforward rendering.

Have you implemented a method to split a section?

polarkernel commented 7 months ago

Have you implemented a method to split a section?

No, not yet, but I can do it quickly. Once, an interface has been proposed:

    def split(self, 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.
        pass    # to be implemented

Is this still valid? What was the idea behind itemLength? Should the section be shortened by this value? In a straight line or along the centerline? This could lead to exceptions if this value is too large. How should these be handled? Please define your requirements and I will implement it as soon as possible.

vvoovv commented 6 months ago

I suggest implementing two methods of the class Section.

length: the length of the newly created polyline after the split. item: the newly created polyline after the split will be assigned to the item.

A possible optimization could be keeping the original centerline and providing an index of the point of the original centerline after which the split occurred and the coordinates of the split. If the split occured right at an existing vertex of the original polyline, the coordinates are set to None. An additional attribute centerlineData can be used to store the index and the coordinates as a tuple.

I am not sure though if that optimization is worth at all.

polarkernel commented 6 months ago

The class PolyLine, which is used for the centerline, already implements two types of parameters, that could be used to describe split positions. The line parameter t uses the number of vertices and the percentage of the interval between them:

# The line parameter <t> describes a position onto the line. It is
# constructed ba a compound of the last vertex number and the percentage
# of the following segment. <t> measures the distance from the start
# vertex, given by the <view> of the polyline. The vertex at the position
# can be found by the method t2v().
#
#          t=1.4 (fwd)                 t= 2.8 (rev)
#        ------------>         <--------------------------
#       |             |       |                           |
#       |             V       V                           |
#       o---------o---x-----o-x-------o---------o---------o
#       0         1         2         3         4         5

Just to be clear, Polyline implements two views, forward (fwd) and reverse (rev). But Sections handle that automatically, so we don't need to worry about it. .

The other line parameter d describes the length of the line (in meters) from the start vertex to the split point:

# The line parameter <d> describes a position onto the line. It measures
# the cumulated length of the polyline from the start vertex, given by
# the <view> of the polyline. to the position. The vertex at the position
# can be found by the method d2v().
#
#            d (fwd)                      d (rev)
#        ---------------->         <----------------------
#       |                 |       |                       |
#       |                 V       V                       |
#       o-------------o---x-----o-x-------o-----o---------o
#       0             1         2         3     4         5

Some methods allow interchangeability between them and the computation of the splitting vertex:

    def t2v(self,t):
        # Computes the vertex given by the line parameter <t>.

    def d2v(self,d):
        # Computes the vertex given by the line parameter <d>.

    def d2t(self,d):

    def t2d(self,d):

In the class Section, the parameter t is already used to trim the ends of a section at intersections:

        self.trimS = 0.                      # trim factor for start
        self.trimT = len(self.polyline)-1    # trim factor for target

The trimming itself is done at the very end of the module generate_streets in the method finalizeOutput. PolyLine provides a method trimmed, which returns a trimmed instance of PolyLine:

section.centerline = section.polyline.trimmed(section.trimS,section.trimT)

Each of these parameters (t or d) could be used to describe a split point. A new method split of the class PolyLine could return both parts of the split centerline. In any case, we should define what to do with the trim parameters. They may conflict with the splitting for short sections.

vvoovv commented 6 months ago

Suppose there is a section, and the method section.splitFromStart(length, item) or section.splitFromEnd(length, item) is called.

If the parameter length supplied in splitFromStart or splitFromEnd is equal to or larger than the length of the section, then the section will be completely replaced by the supplied parameter item. A reference to the replaced section will be kept: item.replacedItem = section. For now I suggest considering only an adjacent section, even if the parameter lengh is much longer than the length of that section.

vvoovv commented 6 months ago

What else would be required to implement the methods splitFromStart(length, item) and splitFromEnd(length, item)?

polarkernel commented 6 months ago

What else would be required to implement the methods splitFromStart(length, item) and splitFromEnd(length, item)?

Well, I am not sure yet, what the result of these operations shall be. I assume that the sections are embedded in a Street. Let's take first splitFromStart(length, item). If I understand correctly, the result would be a piece of the section with the length length from its start, linked by succ to item (and item by prev to the trimmed section) while the original reference succ of the section becomes succ of item.

What irritates me is the term split. When I split something, I expect to get two parts, and not just the left or right end.

vvoovv commented 6 months ago

splitFromStart(length, item)


Before a split: section


After the split: item followed by section, i.e. section was shortened by length

item.succ == section
section.prev == item

The signature of the method can be changed to better fit the term split in the following way.

splitFromStart(length, itemForSection1, itemForSection2)

where

itemForSection1.succ == itemForSection2
itemForSection2.prev == itemForSection1
polarkernel commented 6 months ago

What irritates me is the term split.

I decided to change the term. I have implemented and committed methods of Section with the following signatures:

    def insertAfter(self, length, item):
        # Shortens the section to the length <length> and appends <item> after it. <length> is
        # the physical length in meters. After the split the section is followed by <item>. The
        # method returns True, if item is successfully inserted and False else.
        # For now, the method returns False, when <length> is larger than the length of the section.
        # <item> is inserted into the linked list by:
        # section.succ.pred = item    # if pred exists
        # item.succ = section.succ
        # section.succ = item
        # item. pred = section

    def insertBefore(self, length, item):
        # Shortens the section so that it starts after the length <length> and remains until its end.
        # <length> is the physical length in meters. After the split the item is followed by the
        # remaining section. The method returns True, if <item> is successfully inserted and False else.
        # For now, the method returns False, when <length> is larger than the length of the section.
        # <item> is inserted into the linked list by:
        # section.pred.succ = item  # if succ exists
        # item.pred = section.pred
        # item.succ = section
        # section.pred = item

To use them, import way/item/section.py. For now, the methods returns False, when length is larger than the length of the section. We may adapt this behavior later, after first experiences.

vvoovv commented 6 months ago

To use them, import way/item/section.py.

Why is it necessary to import the module way.item.section? The methods insertAfter(..) and insertBefore(..) aren't static.

polarkernel commented 6 months ago

Why is it necessary to import the module way.item.section?

Uuups. This was an effect of decades of projects with C++. There, an appropriate include statement was always required. Python is different, I will learn it once.

vvoovv commented 6 months ago
    def insertAfter(self, length, item):

    def insertBefore(self, length, item):

Would it be possible to add an attribute centerline to the item after the modification of the section?

polarkernel commented 6 months ago

Would it be possible to add an attribute centerline to the item after the modification of the section?

Sure. Should this attribute centerline be the remaining part of the section's centerline? What about the order of the vertices? Use the same direction as the section had at that part?

vvoovv commented 6 months ago

Both affected sections (self and item) should have the attribute centerline. The direction is the same as the original section had.

polarkernel commented 6 months ago

Both affected sections (self anditem) should have the attribute centerline. The direction is the same as the original section had.

Implemented and committed. There is no assumption made, that item is a Section. It just gets the centerline and no additional adjustments are made, that would be required for a Section (for example, for trim values).

vvoovv commented 6 months ago

I called the methodinsertBefore(..) for the section.id == 115 with the parameter length == 10 and got the result below:

image

The length of the gap is 10. The white point on the screenshot must be on the right end of the current gap.

polarkernel commented 6 months ago

The length of the gap is 10. The white point on the screenshot must be on the right end of the current gap.

Bug fixed and committed.

vvoovv commented 6 months ago

Bug fixed and committed.

Thank. It works now.

vvoovv commented 6 months ago

I added the parameter updateNeighbors for the methods insertAfter and insertBefore. All affected items already have the appropriate values for attributes pred and succ. Setting them in those methods leads to incorrect values for the attributes pred and succ.