Android Question Converting map lat/lng to screen point when close to 180 degrees meridian (Solved)

RB Smissaert

Well-Known Member
Licensed User
Longtime User
Using OSM viewer as posted here:

I noticed problems (lines not being drawn as the clicks on the map) when drawing on the map when close to the 180 degrees meridian (date line) and especially when crossing that line, so for example when a line starts at Lng 178 and moves to Lng -178.

The problem seems to be with this Sub in the above mentioned project. I made an alteration and this has definitely helped, in that now I can draw a polygon across the date line and it shows on the map as intended. There are still problem though for example when moving up the zoom level or just moving the map.

B4X:
'return  TMapPoint from TMapLatLng
Public Sub LatLngToPoint(aLatLng As TMapLatLng, strCallingSub As String) As TMapPoint
    
    Dim d As Double
    Dim dTileSize As Double = MapUtilities.cTileSize
    Dim bLog As Boolean = (strCallingSub = "clsMapShapePolygon, Draw")
    
    'https://www.netzwolf.info/osm/tilebrowser.html?marker.x=12&marker.y=189&tx=1&ty=0&tz=1&ts=256#tile
    Dim v As B4XView = PointToTile(0, 0)
    Dim txy As TMapTileXY = v.tag
    Dim gpx As Double = (txy.fX + (-v.Left / dTileSize)) / fTilesCount
    Dim gpy As Double = (txy.fy + (-v.top / dTileSize)) / fTilesCount
    Dim tx As TMapTileNumberOffset = LngToTileXPlusOffset(aLatLng.fLng, fMap.fZoomLevel)
    Dim ty As TMapTileNumberOffset = LatToTileYPlusOffset(aLatLng.fLat, fMap.fZoomLevel)
    Dim ppx As Double = (tx.fTile + (tx.fOffset / dTileSize)) / fTilesCount
    Dim ppy As Double = (ty.fTile + (ty.fOffset / dTileSize)) / fTilesCount
    
    d = ppx - gpx
    '------------------------------------------------------------------------------------------------------------------
    'this has been added/altered to avoid wrong values (much too high) for p.fX when we have a large longitude eg > 175
    'note that there still can be problems eg when moving up zoom levels or when moving the map
    '------------------------------------------------------------------------------------------------------------------
    If d > 1 Then
        d = d - 1
    End If
    'this was the old code
    '------------------------------
'    Dim p As TMapPoint = MapUtilities.initPoint(fTilesCount * MapUtilities.cTileSize * (ppx - gpx), _
'                                                 fTilesCount * MapUtilities.cTileSize * (ppy - gpy))
    Dim p As TMapPoint = MapUtilities.initPoint(fTilesCount * dTileSize * d, _
                                                fTilesCount * dTileSize * (ppy - gpy))
    
    If bLog Then
        Log("==== LatLngToPoint, Calling sub: " & strCallingSub)
        Log("==== aLatLng.fLng: " & aLatLng.fLng & ", aLatLng.fLat: " & aLatLng.fLat)
        Log("==== fMap.fZoomLevel: " & fMap.fZoomLevel)
        Log("==== v.Left: " & v.Left)
        Log("==== v.Width: " & v.Width)
        Log("==== dTileSize: " & dTileSize)
        Log("==== txy.fX: " & txy.fX & ", txy.fY: " & txy.fY)
        Log("==== gpx: " & gpx)
        Log("==== gpy: " & gpy)
        Log("==== tx.fTile : " & tx.fTile & ", tx.fOffset : " & tx.fOffset)
        Log("==== ty.fTile : " & ty.fTile & ", ty.fOffset : " & ty.fOffset)
        Log("==== ppx: " & ppx)
        Log("==== ppy: " & ppy)
        Log("==== ppx - gpx: " & (ppx - gpx))
        Log("==== ppy - gpy: " & (ppy - gpy))
        Log("==== d: " & d)
        Log("==== fTilesCount: " & fTilesCount)
        Log("==== p.fX: " & p.fX & ", p.fY: " & p.fY)
        Log("=====================================================")
        
    End If
    
    Return p
    
End Sub

There also was a problem with the below Sub (used in Sub LatLngToPoint) in that it produced wrong tile numbers if the Lng was again close to the 180 degrees meridian.
This was fixed with a simple function, CorrectTileNumber, posted below as well.

B4X:
Public Sub LngToTileXPlusOffset(aLng As Double, aZoom As Int) As TMapTileNumberOffset
    
    Dim v As Double = (aLng + 180) / 360 * Power(2, aZoom)
    'Log("cvMap, LngToTileX, v: " & v)
    Dim t As TMapTileNumberOffset
    t.Initialize
    Log("LngToTileXPlusOffset, aLng: " & aLng & ", Floor(" & v & "): " & Floor(v))
    t.fTile = MapUtilities.CorrectTileNumber(Floor(v), aZoom)
    Log("LngToTileXPlusOffset (after correcting), t.fTile: " & t.fTile)
    t.fOffset = (v - Floor(v)) * MapUtilities.cTileSize
    
    Return t
    
End Sub

Sub CorrectTileNumber(iX As Int, iZoom As Int) As Int
    
    Dim iLimit As Int = Power(2, iZoom)
    
    If iX > iLimit - 1 Then
        Return iLimit - iX
    End If
    
    If iX < 0 Then
        Return iLimit + iX
    End If
    
    Return iX
    
End Sub

Altogether it seems dealing with map locations close to the 180 degrees meridian is quite tricky and I would be very interested if somebody
has dealt with this successfully or has some general idea how to deal with this.
Note that I am not using Google maps, but mentioned OSM viewer instead.

RBS
 

RB Smissaert

Well-Known Member
Licensed User
Longtime User
An overnight epiphany (maybe!)

How about creating and drawing the bounding polygon using the screen pixel (x, y) values rather than the (longitude, latitude) values?

The pixel x axis would be minus-half to plus-half rather than 0 to full.

Eg at zoom 5 with 768-wide tiles, the full "width" (circle around) of earth is 2**5 * 768 = 24576 pixels, and you'd use x values -12288 to +12287 rather than 0 to 24575
I had the same thought as now the lat/lng is calculated from the screen/view position and then we are calculating the screen/view position from the lat/lng to draw on the views.
Seems convoluted.
Off to some other work now.

RBS
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
I had the same thought as now the lat/lng is calculated from the screen/view position and then we are calculating the screen/view position from the lat/lng to draw on the views.
Seems convoluted.
Off to some other work now.

RBS
Not worked on this idea yet, but solved 2 problems demonstrated in the posted image.

One was that adding random map points inside a polygon that was drawn round a point on the 180 degree meridian didn't work OK.
This was caused by a Sub that checked if a lat/lng point was inside that polygon didn't work in this situation.
The simple solution to this was to shift the lat/lng points to the left so they were all treated as positive.

B4X:
Sub WindingPointInLatLngPolygon(dLat As Double, dLng As Double, lstLatLng As List) As Boolean
    
    'http://geomalgorithms.com/a03-_inclusion.html
    '2012 Dan Sunday
    '---------------------------------------------

    Dim i As Int
    Dim tLL1 As TMapLatLng
    Dim tLL2 As TMapLatLng
    Dim wn As Int
    Dim iUBound As Int
    
    iUBound = lstLatLng.Size - 1

    For i = 0 To iUBound - 1 '- 1 as we do i + 1
        tLL1 = lstLatLng.Get(i)
        tLL2 = lstLatLng.Get(i + 1)
        If tLL1.fLng <= dLng Then
            If tLL2.fLng > dLng Then
                If IsLeftLatLng(tLL1, tLL2, dLat, dLng) Then
                    wn = wn + 1
                End If
            End If
        Else
            If tLL2.fLng <= dLng Then
                If IsLeftLatLng(tLL1, tLL2, dLat, dLng) = False Then
                    wn = wn - 1
                End If
            End If
        End If
    Next
    
    Return wn <> 0

End Sub

Sub AlterDouble(dDouble As Double) As Double
    
    If dDouble < 0 Then
        Return 100 - (180 + dDouble)
    Else
        Return 100 + (180 - dDouble)
    End If
    
End Sub

Sub WindingPointInLatLngPolygonPassingDateLine(dLat As Double, dLng As Double, lstLatLng As List) As Boolean
    
    'http://geomalgorithms.com/a03-_inclusion.html
    '2012 Dan Sunday
    '---------------------------------------------

    Dim i As Int
    Dim tLL1 As TMapLatLng
    Dim tLL2 As TMapLatLng
    Dim wn As Int
    Dim iUBound As Int
    
    dLat = AlterDouble(dLat)
    dLng = AlterDouble(dLng)
    
    iUBound = lstLatLng.Size - 1

    For i = 0 To iUBound - 1 '- 1 as we do i + 1
        tLL1 = lstLatLng.Get(i)
        tLL2 = lstLatLng.Get(i + 1)
        If AlterDouble(tLL1.fLng) <= dLng Then
            If AlterDouble(tLL2.fLng) > dLng Then
                If IsLeftLatLngPassingDateLine(tLL1, tLL2, dLat, dLng) Then
                    wn = wn + 1
                End If
            End If
        Else
            If AlterDouble(tLL2.fLng) <= dLng Then
                If IsLeftLatLngPassingDateLine(tLL1, tLL2, dLat, dLng) = False Then
                    wn = wn - 1
                End If
            End If
        End If
    Next
    
    Return wn <> 0

End Sub

Sub IsLeftLatLng(tLL1 As TMapLatLng, tLL2 As TMapLatLng, dLat As Double, dLng As Double) As Boolean
    
    Return (tLL2.fLat - tLL1.fLat) * (dLng - tLL1.fLng) - _
           (dLat - tLL1.fLat) * (tLL2.fLng - tLL1.fLng) > 0
              
End Sub

Sub IsLeftLatLngPassingDateLine(tLL1 As TMapLatLng, tLL2 As TMapLatLng, dLat As Double, dLng As Double) As Boolean
    
    Return (AlterDouble(tLL2.fLat) - AlterDouble(tLL1.fLat)) * (dLng - AlterDouble(tLL1.fLng)) - _
           (dLat - AlterDouble(tLL1.fLat)) * (AlterDouble(tLL2.fLng) - AlterDouble(tLL1.fLng)) > 0
              
End Sub

The second problem demonstrated in the image was that the convexhull wasn't drawn properly at all in this situation.
The same idea (shifting the lat/lng values to the left) was used to fix this problem.

B4X:
Sub Orientation(tLL1 As TMapLatLng, tLL2 As TMapLatLng, tLL3 As TMapLatLng) As Int
    
    Dim dVal As Double = (tLL2.fLat - tLL1.fLat) * (tLL3.fLng - tLL2.fLng) - (tLL2.fLng - tLL1.fLng) * (tLL3.fLat - tLL2.fLat)
        
    If dVal = 0 Then Return 0 ' collinear
        
    Return IIf(dVal > 0, 1, 2) ' clock or counterclock wise
        
End Sub

Sub OrientationPassingDateLine(tLL1 As TMapLatLng, tLL2 As TMapLatLng, tLL3 As TMapLatLng) As Int
    
    Dim dVal As Double = (AlterDouble(tLL2.fLat) - AlterDouble(tLL1.fLat)) * _
                         (AlterDouble(tLL3.fLng) - AlterDouble(tLL2.fLng)) - (AlterDouble(tLL2.fLng) - AlterDouble(tLL1.fLng)) * _
                         (AlterDouble(tLL3.fLat) - AlterDouble(tLL2.fLat))
    
    If dVal = 0 Then Return 0 ' collinear
        
    Return IIf(dVal > 0, 1, 2) ' clock or counterclock wise
        
End Sub
    
Sub ConvexHull(lst_tLL As List, iPoints As Int) As List
    
    Dim i As Int
    Dim iLeft As Int
    Dim tLL As TMapLatLng
    Dim tLL_Left As TMapLatLng
    Dim iP As Int
    Dim iQ As Int
    Dim lst_tLLBorder As List
    
    'There must be at least 3 points
    If iPoints < 3 Then Return lst_tLLBorder 'return un-initialized list

    lst_tLLBorder.Initialize

    'Find the leftmost point
    For i = 1 To iPoints - 1
        tLL = lst_tLL.Get(i)
        tLL_Left = lst_tLL.Get(iLeft)
        If tLL.fLng < tLL_Left.fLng Then
            iLeft = i
        End If
    Next
    
    ' Start from leftmost point, keep moving counterclockwise
    ' until reach the start point again.
    
    iP = iLeft    'hidden by obscurity within original c code: int p = l, q;
    
    Do While True
        'Add current point to result
        lst_tLLBorder.Add(lst_tLL.Get(iP))

        ' Search for a point 'q' such that orientation(p, q, x) is counterclockwise for all points 'x'.
        iQ = (iP + 1) Mod iPoints
        
        For i = 0 To iPoints - 1
            'If i is more counterclockwise than current q, then update q
            If Orientation(lst_tLL.Get(iP), lst_tLL.Get(i), lst_tLL.Get(iQ)) = 2 Then
                iQ = i
            End If
        Next

        ' Now q is the most counterclockwise with respect to p
        ' Set p as q for next iteration, so that q is added to result 'hull'
        iP = iQ
        
        'Log("iP: " & iP)

        If iP = iLeft Then
            lst_tLLBorder.Add(lst_tLL.Get(iP))    'close hull polygon
            Exit
        End If
    Loop
    
    Return lst_tLLBorder

End Sub

Sub ConvexHull_PassingDateLine(lst_tLL As List, iPoints As Int) As List
    
    Dim i As Int
    Dim iLeft As Int
    Dim tLL As TMapLatLng
    Dim tLL_Left As TMapLatLng
    Dim tLL_Test As TMapLatLng
    Dim iP As Int
    Dim iQ As Int
    Dim lst_tLLBorder As List
    
    'There must be at least 3 points
    If iPoints < 3 Then Return lst_tLLBorder 'return un-initialized list

    lst_tLLBorder.Initialize

    'Find the leftmost point
    For i = 1 To iPoints - 1
        tLL = lst_tLL.Get(i)
        tLL_Left = lst_tLL.Get(iLeft)
        If AlterDouble(tLL.fLng) < AlterDouble(tLL_Left.fLng) Then
            iLeft = i
        End If
    Next
    
    ' Start from leftmost point, keep moving counterclockwise
    ' until reach the start point again.
    
    iP = iLeft    'hidden by obscurity within original c code: int p = l, q;
    tLL_Test = lst_tLL.Get(iLeft)
    Log("lst_tLL.Get(" & iLeft & "): " & tLL_Test.fLng)
    
    Do While True
        'Add current point to result
        lst_tLLBorder.Add(lst_tLL.Get(iP))

        ' Search for a point 'q' such that orientation(p, q, x) is counterclockwise for all points 'x'.
        iQ = (iP + 1) Mod iPoints
        
        For i = 0 To iPoints - 1
            'If i is more counterclockwise than current q, then update q
            If OrientationPassingDateLine(lst_tLL.Get(iP), lst_tLL.Get(i), lst_tLL.Get(iQ)) = 2 Then
                iQ = i
            End If
        Next

        ' Now q is the most counterclockwise with respect to p
        ' Set p as q for next iteration, so that q is added to result 'hull'
        iP = iQ
        
        'Log("iP: " & iP)

        If iP = iLeft Then
            lst_tLLBorder.Add(lst_tLL.Get(iP))    'close hull polygon
            Exit
        End If
    Loop
    
    Return lst_tLLBorder

End Sub

Now this works all fine as shown in the attached image.

RBS
 

Attachments

  • DrawingAroundDateLinePoint.zip
    216.5 KB · Views: 18
Upvote 0

emexes

Expert
Licensed User
The simple solution to this was to shift the lat/lng points to the left so they were all treated as positive.

I think that might shift the problem to the new wraparound meridian at ..358..359..0..1..2,,

Given that your solution seems to work except for at the wraparound meridian, maybe the simplest solution is to re-express the longitudes as either 0..359 or -180..179 ie whichever numbering range keeps the point cloud from encompassing the wraparound meridian (at either 180 or 0 respectively).
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
I think that might shift the problem to the new wraparound meridian at ..358..359..0..1..2,,

Given that your solution seems to work except for at the wraparound meridian, maybe the simplest solution is to re-express the longitudes as either 0..359 or -180..179 ie whichever numbering range keeps the point cloud from encompassing the wraparound meridian (at either 180 or 0 respectively).
> I think that might shift the problem to the new wraparound meridian at ..358..359..0..1..2,,

I tidied things up a bit and I don't think this is a problem.
Actually, practically there never could be a problem as more than likely users of this app will never get close to the 180 degrees meridian.

We start with getting a boundingbox of the map polygon.
We have to make sure that this boundingbox as is shown on the map, that is the lats and lngs are as shown on the map:

B4X:
    Type TMapLatLng(fLat As Double, fLng As Double)
    Type TMapBoxLatLng(fLeftTop As TMapLatLng, fRightBottom As TMapLatLng)


Sub GetBBFromPolygon(lst_tLL As List) As TMapBoxLatLng
    
    Dim dMinLat As Double  = cMaxLat
    Dim dMaxLat As Double = cMinLat
    Dim dMinLng As Double = cMaxLng
    Dim dMaxLng As Double = cMinLng
    Dim dMinPosLng As Double = cMaxLng 'needed for if we are passing the 180 meridian (date line)
    Dim dMaxNegLng As Double = cMinLng 'needed for if we are passing the 180 meridian (date line)
    
    For Each tLL As TMapLatLng In lst_tLL
        If tLL.fLat < dMinLat Then
            dMinLat = tLL.fLat
        Else
            If tLL.fLat > dMaxLat Then
                dMaxLat = tLL.flat
            End If
        End If
        If tLL.fLng < dMinLng Then
            dMinLng = tLL.fLng
        Else
            If tLL.fLng > dMaxLng Then
                dMaxLng = tLL.fLng
            End If
        End If
        If tLL.fLng > 0 Then
            If tLL.fLng < dMinPosLng Then
                dMinPosLng = tLL.fLng
            End If
        Else
            If tLL.fLng > dMaxNegLng Then
                dMaxNegLng = tLL.flng
            End If
        End If
    Next
    
    'Log("GetBBFromPolygon, dMinLng: " & dMinLng & ", dMaxLng: " & dMaxLng)
    
    If dMaxLng > 100 And dMinLng < -100 Then
        'polygon surrounding a point on the 180 degree meridian (date line)
        Dim BB As TMapBoxLatLng = initBoxLatLng(dMaxLat, dMinPosLng, dMinLat, dMaxNegLng)
    Else
        Dim BB As TMapBoxLatLng = initBoxLatLng(dMaxLat, dMinLng, dMinLat, dMaxLng)
    End If
    
    Return BB
    
End Sub

Then from this boundingbox we can see if the polygon has a point on the 180 degree meridian on it:

'producing a value showing how much to the left the polygon points should be shifted
'note the actual polygon points won't change
B4X:
Sub Get180MeridianLeftShiftValue(BB As TMapBoxLatLng) As Int
    
    If Not (BB.fLeftTop.fLng > 90 And BB.fRightBottom.fLng < -90) Then
        Return 0
    End If
        
    Return Floor(180 + BB.fRightBottom.fLng) + 2
    
End Sub

Then there are 2 situations where I need to adjust according to if the polygon has a point on the date line on it or not:
1. Putting random points in the polygon.
2. Drawing a convexhull around those points.

Random points:

B4X:
Sub GetRandomPointsInPolygon(lstPolygon As List, iCount As Int, BB As TMapBoxLatLng, iLeftShift As Int) As List
    
    Dim i As Int
    Dim iRandom As Int
    Dim dLatPart As Double
    Dim dLngPart As Double
    Dim lstRandom_tLL As List
    Dim fLat As Float
    Dim fLng As Float
    Dim tLL As TMapLatLng
    
    lstRandom_tLL.Initialize
    
    If iLeftShift > 0 Then
        'shape passing the 180 meridian (date line)
        dLngPart = ((180 - BB.fLeftTop.fLng) + (180 + BB.fRightBottom.fLng)) / iCount
    Else
        dLngPart = (BB.fRightBottom.fLng - BB.fLeftTop.fLng) / iCount
    End If
    
    dLatPart = (BB.fLeftTop.flat - BB.fRightBottom.flat) / iCount
    
    Do While True
        iRandom = Rnd(0, iCount)
        fLat = BB.fRightBottom.fLat + dLatPart * iRandom
        iRandom = Rnd(0, iCount)
        fLng = BB.fLeftTop.fLng + dLngPart * iRandom
        
        If iRandom < 6 Then
            Log("random < 6, fLng: " & fLng)
        End If
            
        If iLeftShift > 0 Then
            If fLng > 180.0 Then
                fLng = -180 + (fLng - 180)
            End If
        End If
        
            If WindingPointInLatLngPolygon(fLat, fLng, lstPolygon, iLeftShift) Then
                Dim tLL As TMapLatLng = initLatLng(fLat, fLng)
                lstRandom_tLL.Add(tLL)
                If lstRandom_tLL.Size = iCount Then
                    Exit
                End If
            End If
    Loop
    
    Return lstRandom_tLL
    
End Sub

Sub WindingPointInLatLngPolygon(dLat As Double, dLng As Double, lstLatLng As List, iLeftShift As Int) As Boolean
    
    'http://geomalgorithms.com/a03-_inclusion.html
    '2012 Dan Sunday
    '---------------------------------------------

    Dim i As Int
    Dim tLL1 As TMapLatLng
    Dim tLL2 As TMapLatLng
    Dim wn As Int
    Dim iUBound As Int
    
    iUBound = lstLatLng.Size - 1
    
    If iLeftShift = 0 Then
        For i = 0 To iUBound - 1 '- 1 as we do i + 1
            tLL1 = lstLatLng.Get(i)
            tLL2 = lstLatLng.Get(i + 1)
            If tLL1.fLng <= dLng Then
                If tLL2.fLng > dLng Then
                    If IsLeftLatLng(tLL1, tLL2, dLat, dLng, 0) Then
                        wn = wn + 1
                    End If
                End If
            Else
                If tLL2.fLng <= dLng Then
                    If IsLeftLatLng(tLL1, tLL2, dLat, dLng, 0) = False Then
                        wn = wn - 1
                    End If
                End If
            End If
        Next
    Else
        
        dLng = AlterDouble(dLng, iLeftShift)
        
        For i = 0 To iUBound - 1 '- 1 as we do i + 1
            tLL1 = lstLatLng.Get(i)
            tLL2 = lstLatLng.Get(i + 1)
            If AlterDouble(tLL1.fLng, iLeftShift) <= dLng Then
                If AlterDouble(tLL2.fLng, iLeftShift) > dLng Then
                    If IsLeftLatLng(tLL1, tLL2, dLat, dLng, iLeftShift) Then
                        wn = wn + 1
                    End If
                End If
            Else
                If AlterDouble(tLL2.fLng, iLeftShift) <= dLng Then
                    If IsLeftLatLng(tLL1, tLL2, dLat, dLng, iLeftShift) = False Then
                        wn = wn - 1
                    End If
                End If
            End If
        Next
        
    End If
    
    Return wn <> 0

End Sub

Sub IsLeftLatLng(tLL1 As TMapLatLng, tLL2 As TMapLatLng, dLat As Double, dLng As Double, iLeftShift As Int) As Boolean
    
    'note we don't change latitude!
    If iLeftShift = 0 Then
        Return (tLL2.fLat - tLL1.fLat) * (dLng - tLL1.fLng) - _
                  (dLat - tLL1.fLat) * (tLL2.fLng - tLL1.fLng) > 0
    Else
        Return (tLL2.fLat - tLL1.fLat) * (dLng - AlterDouble(tLL1.fLng, iLeftShift)) - _
               (dLat - tLL1.fLat) * (AlterDouble(tLL2.fLng, iLeftShift) - AlterDouble(tLL1.fLng, iLeftShift)) > 0
    End If
              
End Sub

Sub AlterDouble(dDouble As Double, iLeftShift As Int) As Double
    
    If dDouble < 0 Then
        Return iLeftShift - (180 + dDouble)
    Else
        Return iLeftShift + (180 - dDouble)
    End If
    
End Sub

Then finally, the main thing drawing a convexhull around the random points:

B4X:
Sub ConvexHull(lst_tLL As List, iPoints As Int, iLeftShift As Int) As List
    
    Dim i As Int
    Dim iLeft As Int
    Dim tLL As TMapLatLng
    Dim tLL_Left As TMapLatLng
    Dim tLL_Test As TMapLatLng
    Dim iP As Int
    Dim iQ As Int
    Dim lst_tLLBorder As List
    
    'There must be at least 3 points
    If iPoints < 3 Then Return lst_tLLBorder 'return un-initialized list

    lst_tLLBorder.Initialize

    'Find the leftmost point
    If iLeftShift = 0 Then
        For i = 1 To iPoints - 1
            tLL = lst_tLL.Get(i)
            tLL_Left = lst_tLL.Get(iLeft)
            If tLL.fLng < tLL_Left.fLng Then
                iLeft = i
            End If
        Next
    Else
        For i = 1 To iPoints - 1
            tLL = lst_tLL.Get(i)
            tLL_Left = lst_tLL.Get(iLeft)
            If AlterDouble(tLL.fLng, iLeftShift) < AlterDouble(tLL_Left.fLng, iLeftShift) Then
                iLeft = i
            End If
        Next
    End If
    
    Log("iLeft: " & iLeft)
    
    ' Start from leftmost point, keep moving counterclockwise
    ' until reach the start point again.
    
    iP = iLeft    'hidden by obscurity within original c code: int p = l, q;
    tLL_Test = lst_tLL.Get(iLeft)
    
    Do While True
        'Add current point to result
        lst_tLLBorder.Add(lst_tLL.Get(iP))

        ' Search for a point 'q' such that orientation(p, q, x) is counterclockwise for all points 'x'.
        iQ = (iP + 1) Mod iPoints
        
        For i = 0 To iPoints - 1
            'If i is more counterclockwise than current q, then update q
            If Orientation(lst_tLL.Get(iP), lst_tLL.Get(i), lst_tLL.Get(iQ), iLeftShift) = 2 Then
                iQ = i
            End If
        Next

        ' Now q is the most counterclockwise with respect to p
        ' Set p as q for next iteration, so that q is added to result 'hull'
        iP = iQ
        
        If iP = iLeft Then
            lst_tLLBorder.Add(lst_tLL.Get(iP))    'close hull polygon
            Exit
        End If
    Loop
    
    Return lst_tLLBorder

End Sub

'note now we need to shift both lngs and the lats
Sub Orientation(tLL1 As TMapLatLng, tLL2 As TMapLatLng, tLL3 As TMapLatLng, iLeftShift As Int) As Int
    
    If iLeftShift = 0 Then
        Dim dVal As Double = (tLL2.fLat - tLL1.fLat) * (tLL3.fLng - tLL2.fLng) - _
                             (tLL2.fLng - tLL1.fLng) * (tLL3.fLat - tLL2.fLat)
    Else
        Dim dVal As Double = (AlterDouble(tLL2.fLat, iLeftShift) - AlterDouble(tLL1.fLat, iLeftShift)) * _
                              (AlterDouble(tLL3.fLng, iLeftShift) - AlterDouble(tLL2.fLng, iLeftShift)) - _
                             (AlterDouble(tLL2.fLng, iLeftShift) - AlterDouble(tLL1.fLng, iLeftShift)) * _
                              (AlterDouble(tLL3.fLat, iLeftShift) - AlterDouble(tLL2.fLat, iLeftShift))
    End If
        
    If dVal = 0 Then Return 0 ' collinear
        
    Return IIf(dVal > 0, 1, 2) ' clock or counterclock wise
        
End Sub

Tested now at various longitudes and not seen any problems yet.
Not tested in funny places like the poles, but I think we can forget about those.

RBS
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
> I think that might shift the problem to the new wraparound meridian at ..358..359..0..1..2,,

I tidied things up a bit and I don't think this is a problem.
Actually, practically there never could be a problem as more than likely users of this app will never get close to the 180 degrees meridian.

We start with getting a boundingbox of the map polygon.
We have to make sure that this boundingbox as is shown on the map, that is the lats and lngs are as shown on the map:

B4X:
    Type TMapLatLng(fLat As Double, fLng As Double)
    Type TMapBoxLatLng(fLeftTop As TMapLatLng, fRightBottom As TMapLatLng)


Sub GetBBFromPolygon(lst_tLL As List) As TMapBoxLatLng
   
    Dim dMinLat As Double  = cMaxLat
    Dim dMaxLat As Double = cMinLat
    Dim dMinLng As Double = cMaxLng
    Dim dMaxLng As Double = cMinLng
    Dim dMinPosLng As Double = cMaxLng 'needed for if we are passing the 180 meridian (date line)
    Dim dMaxNegLng As Double = cMinLng 'needed for if we are passing the 180 meridian (date line)
   
    For Each tLL As TMapLatLng In lst_tLL
        If tLL.fLat < dMinLat Then
            dMinLat = tLL.fLat
        Else
            If tLL.fLat > dMaxLat Then
                dMaxLat = tLL.flat
            End If
        End If
        If tLL.fLng < dMinLng Then
            dMinLng = tLL.fLng
        Else
            If tLL.fLng > dMaxLng Then
                dMaxLng = tLL.fLng
            End If
        End If
        If tLL.fLng > 0 Then
            If tLL.fLng < dMinPosLng Then
                dMinPosLng = tLL.fLng
            End If
        Else
            If tLL.fLng > dMaxNegLng Then
                dMaxNegLng = tLL.flng
            End If
        End If
    Next
   
    'Log("GetBBFromPolygon, dMinLng: " & dMinLng & ", dMaxLng: " & dMaxLng)
   
    If dMaxLng > 100 And dMinLng < -100 Then
        'polygon surrounding a point on the 180 degree meridian (date line)
        Dim BB As TMapBoxLatLng = initBoxLatLng(dMaxLat, dMinPosLng, dMinLat, dMaxNegLng)
    Else
        Dim BB As TMapBoxLatLng = initBoxLatLng(dMaxLat, dMinLng, dMinLat, dMaxLng)
    End If
   
    Return BB
   
End Sub

Then from this boundingbox we can see if the polygon has a point on the 180 degree meridian on it:

'producing a value showing how much to the left the polygon points should be shifted
'note the actual polygon points won't change
B4X:
Sub Get180MeridianLeftShiftValue(BB As TMapBoxLatLng) As Int
   
    If Not (BB.fLeftTop.fLng > 90 And BB.fRightBottom.fLng < -90) Then
        Return 0
    End If
       
    Return Floor(180 + BB.fRightBottom.fLng) + 2
   
End Sub

Then there are 2 situations where I need to adjust according to if the polygon has a point on the date line on it or not:
1. Putting random points in the polygon.
2. Drawing a convexhull around those points.

Random points:

B4X:
Sub GetRandomPointsInPolygon(lstPolygon As List, iCount As Int, BB As TMapBoxLatLng, iLeftShift As Int) As List
   
    Dim i As Int
    Dim iRandom As Int
    Dim dLatPart As Double
    Dim dLngPart As Double
    Dim lstRandom_tLL As List
    Dim fLat As Float
    Dim fLng As Float
    Dim tLL As TMapLatLng
   
    lstRandom_tLL.Initialize
   
    If iLeftShift > 0 Then
        'shape passing the 180 meridian (date line)
        dLngPart = ((180 - BB.fLeftTop.fLng) + (180 + BB.fRightBottom.fLng)) / iCount
    Else
        dLngPart = (BB.fRightBottom.fLng - BB.fLeftTop.fLng) / iCount
    End If
   
    dLatPart = (BB.fLeftTop.flat - BB.fRightBottom.flat) / iCount
   
    Do While True
        iRandom = Rnd(0, iCount)
        fLat = BB.fRightBottom.fLat + dLatPart * iRandom
        iRandom = Rnd(0, iCount)
        fLng = BB.fLeftTop.fLng + dLngPart * iRandom
       
        If iRandom < 6 Then
            Log("random < 6, fLng: " & fLng)
        End If
           
        If iLeftShift > 0 Then
            If fLng > 180.0 Then
                fLng = -180 + (fLng - 180)
            End If
        End If
       
            If WindingPointInLatLngPolygon(fLat, fLng, lstPolygon, iLeftShift) Then
                Dim tLL As TMapLatLng = initLatLng(fLat, fLng)
                lstRandom_tLL.Add(tLL)
                If lstRandom_tLL.Size = iCount Then
                    Exit
                End If
            End If
    Loop
   
    Return lstRandom_tLL
   
End Sub

Sub WindingPointInLatLngPolygon(dLat As Double, dLng As Double, lstLatLng As List, iLeftShift As Int) As Boolean
   
    'http://geomalgorithms.com/a03-_inclusion.html
    '2012 Dan Sunday
    '---------------------------------------------

    Dim i As Int
    Dim tLL1 As TMapLatLng
    Dim tLL2 As TMapLatLng
    Dim wn As Int
    Dim iUBound As Int
   
    iUBound = lstLatLng.Size - 1
   
    If iLeftShift = 0 Then
        For i = 0 To iUBound - 1 '- 1 as we do i + 1
            tLL1 = lstLatLng.Get(i)
            tLL2 = lstLatLng.Get(i + 1)
            If tLL1.fLng <= dLng Then
                If tLL2.fLng > dLng Then
                    If IsLeftLatLng(tLL1, tLL2, dLat, dLng, 0) Then
                        wn = wn + 1
                    End If
                End If
            Else
                If tLL2.fLng <= dLng Then
                    If IsLeftLatLng(tLL1, tLL2, dLat, dLng, 0) = False Then
                        wn = wn - 1
                    End If
                End If
            End If
        Next
    Else
       
        dLng = AlterDouble(dLng, iLeftShift)
       
        For i = 0 To iUBound - 1 '- 1 as we do i + 1
            tLL1 = lstLatLng.Get(i)
            tLL2 = lstLatLng.Get(i + 1)
            If AlterDouble(tLL1.fLng, iLeftShift) <= dLng Then
                If AlterDouble(tLL2.fLng, iLeftShift) > dLng Then
                    If IsLeftLatLng(tLL1, tLL2, dLat, dLng, iLeftShift) Then
                        wn = wn + 1
                    End If
                End If
            Else
                If AlterDouble(tLL2.fLng, iLeftShift) <= dLng Then
                    If IsLeftLatLng(tLL1, tLL2, dLat, dLng, iLeftShift) = False Then
                        wn = wn - 1
                    End If
                End If
            End If
        Next
       
    End If
   
    Return wn <> 0

End Sub

Sub IsLeftLatLng(tLL1 As TMapLatLng, tLL2 As TMapLatLng, dLat As Double, dLng As Double, iLeftShift As Int) As Boolean
   
    'note we don't change latitude!
    If iLeftShift = 0 Then
        Return (tLL2.fLat - tLL1.fLat) * (dLng - tLL1.fLng) - _
                  (dLat - tLL1.fLat) * (tLL2.fLng - tLL1.fLng) > 0
    Else
        Return (tLL2.fLat - tLL1.fLat) * (dLng - AlterDouble(tLL1.fLng, iLeftShift)) - _
               (dLat - tLL1.fLat) * (AlterDouble(tLL2.fLng, iLeftShift) - AlterDouble(tLL1.fLng, iLeftShift)) > 0
    End If
             
End Sub

Sub AlterDouble(dDouble As Double, iLeftShift As Int) As Double
   
    If dDouble < 0 Then
        Return iLeftShift - (180 + dDouble)
    Else
        Return iLeftShift + (180 - dDouble)
    End If
   
End Sub

Then finally, the main thing drawing a convexhull around the random points:

B4X:
Sub ConvexHull(lst_tLL As List, iPoints As Int, iLeftShift As Int) As List
   
    Dim i As Int
    Dim iLeft As Int
    Dim tLL As TMapLatLng
    Dim tLL_Left As TMapLatLng
    Dim tLL_Test As TMapLatLng
    Dim iP As Int
    Dim iQ As Int
    Dim lst_tLLBorder As List
   
    'There must be at least 3 points
    If iPoints < 3 Then Return lst_tLLBorder 'return un-initialized list

    lst_tLLBorder.Initialize

    'Find the leftmost point
    If iLeftShift = 0 Then
        For i = 1 To iPoints - 1
            tLL = lst_tLL.Get(i)
            tLL_Left = lst_tLL.Get(iLeft)
            If tLL.fLng < tLL_Left.fLng Then
                iLeft = i
            End If
        Next
    Else
        For i = 1 To iPoints - 1
            tLL = lst_tLL.Get(i)
            tLL_Left = lst_tLL.Get(iLeft)
            If AlterDouble(tLL.fLng, iLeftShift) < AlterDouble(tLL_Left.fLng, iLeftShift) Then
                iLeft = i
            End If
        Next
    End If
   
    Log("iLeft: " & iLeft)
   
    ' Start from leftmost point, keep moving counterclockwise
    ' until reach the start point again.
   
    iP = iLeft    'hidden by obscurity within original c code: int p = l, q;
    tLL_Test = lst_tLL.Get(iLeft)
   
    Do While True
        'Add current point to result
        lst_tLLBorder.Add(lst_tLL.Get(iP))

        ' Search for a point 'q' such that orientation(p, q, x) is counterclockwise for all points 'x'.
        iQ = (iP + 1) Mod iPoints
       
        For i = 0 To iPoints - 1
            'If i is more counterclockwise than current q, then update q
            If Orientation(lst_tLL.Get(iP), lst_tLL.Get(i), lst_tLL.Get(iQ), iLeftShift) = 2 Then
                iQ = i
            End If
        Next

        ' Now q is the most counterclockwise with respect to p
        ' Set p as q for next iteration, so that q is added to result 'hull'
        iP = iQ
       
        If iP = iLeft Then
            lst_tLLBorder.Add(lst_tLL.Get(iP))    'close hull polygon
            Exit
        End If
    Loop
   
    Return lst_tLLBorder

End Sub

'note now we need to shift both lngs and the lats
Sub Orientation(tLL1 As TMapLatLng, tLL2 As TMapLatLng, tLL3 As TMapLatLng, iLeftShift As Int) As Int
   
    If iLeftShift = 0 Then
        Dim dVal As Double = (tLL2.fLat - tLL1.fLat) * (tLL3.fLng - tLL2.fLng) - _
                             (tLL2.fLng - tLL1.fLng) * (tLL3.fLat - tLL2.fLat)
    Else
        Dim dVal As Double = (AlterDouble(tLL2.fLat, iLeftShift) - AlterDouble(tLL1.fLat, iLeftShift)) * _
                              (AlterDouble(tLL3.fLng, iLeftShift) - AlterDouble(tLL2.fLng, iLeftShift)) - _
                             (AlterDouble(tLL2.fLng, iLeftShift) - AlterDouble(tLL1.fLng, iLeftShift)) * _
                              (AlterDouble(tLL3.fLat, iLeftShift) - AlterDouble(tLL2.fLat, iLeftShift))
    End If
       
    If dVal = 0 Then Return 0 ' collinear
       
    Return IIf(dVal > 0, 1, 2) ' clock or counterclock wise
       
End Sub

Tested now at various longitudes and not seen any problems yet.
Not tested in funny places like the poles, but I think we can forget about those.

RBS
As there was still a problem when a shape was drawn around a point on the 180 degree meridian in that when the shape was moved horizontally there could appear lines
going around the globe and I added some code to correct this.
As this problem only occurred when the drawn shape was out of side this code will make sure the shape is not drawn when the shape is off the app screen.

In class clsMapShapePolygon:

B4X:
Sub Class_Globals

    Type TMapShapePolygon(fLatLng As List, fColor As Int, fFilled As Boolean, fStrokeWidth As Double, fData As Object)
    Private fcvMap As cvMap
    Private fShape As TMapShapePolygon
    Private mLayer As Int 'added RBS 02/05/2022
    Private BB_Shape As TMapBoxLatLng
    Private bShapePassesDateLine As Boolean

End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(aCVMap As cvMap, aShape As TMapShapePolygon)
    
    fcvMap = aCVMap
    fShape = aShape
    mLayer = MapUtilities.iPolygonLayer 'added RBS 02/05/2022
    BB_Shape = MapUtilities.GetBBFromPolygon(aShape.fLatLng) 'added RBS 06/12/2024
    bShapePassesDateLine = MapUtilities.Get180MeridianLeftShiftValue(BB_Shape) > 0 'added RBS 06/12/2024, not sure we need this
    
End Sub

Sub ShapeIsVisible As Boolean
    
    'check if R bottom of shape BB is visible
    If BB_Shape.fRightBottom.fLng < fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng Then
        If BB_Shape.fRightBottom.fLng > fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng Then
            If BB_Shape.fRightBottom.fLat < fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat Then
                If BB_Shape.fRightBottom.fLat > fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then
                    Return True
                    'Log("bShapeVisible: " & bShapeVisible)
                End If
            End If
        End If
    End If
    
    'check if R top of shape BB is visible
    If BB_Shape.fRightBottom.fLng < fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng Then
        If BB_Shape.fRightBottom.fLng > fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng Then
            If BB_Shape.fLeftTop.fLat < fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat Then
                If BB_Shape.fLeftTop.fLat > fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then
                    Return True
                End If
            End If
        End If
    End If
    
    'check if L top of shape BB is visible
    If BB_Shape.fLeftTop.fLng > fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng Then
        If BB_Shape.fLeftTop.fLng < fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng Then
            If BB_Shape.fLeftTop.fLat > fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then
                If BB_Shape.fLeftTop.fLat < fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat Then
                    Return True
                End If
            End If
        End If
    End If
    
    'check if L bottom of shape BB is visible
    If BB_Shape.fLeftTop.fLng > fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng Then
        If BB_Shape.fLeftTop.fLng < fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng Then
            If BB_Shape.fRightBottom.fLat > fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then
                If BB_Shape.fRightBottom.fLat < fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat Then
                    Return True
                End If
            End If
        End If
    End If
    
    'check if overlapping laterally on both sides
    If BB_Shape.fLeftTop.fLng < fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng Then
        If BB_Shape.fRightBottom.fLng > fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng Then
                    
            If BB_Shape.fRightBottom.fLat < fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat And _
                       BB_Shape.fRightBottom.fLat > fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Or _
                       BB_Shape.fLeftTop.fLat < fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat And _
                       BB_Shape.fLeftTop.fLat > fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then
                Return True
            End If
                    
        End If
    End If
    
    'check if overlapping vertically on both sides
    If BB_Shape.fLeftTop.fLat > fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat Then
        If BB_Shape.fRightBottom.fLat < fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then
                    
            If BB_Shape.fRightBottom.fLng < fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng And _
                       BB_Shape.fRightBottom.fLng > fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng Or _
                       BB_Shape.fLeftTop.fLng > fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng And _
                       BB_Shape.fLeftTop.fLng < fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng Then
                Return True
            End If
                    
        End If
    End If
    
    Return False

End Sub

'draw the shape
'---------------------------------------------------------
Public Sub Draw
    
    Dim tLL1 As TMapLatLng
    Dim tLL2 As TMapLatLng
    Dim p As B4XPath

   
    If fShape.fLatLng.IsInitialized = False Then Return

    If fShape.fLatLng.Size = 0 Then Return

    If Enums.bCheckIfPolygonVisible Then
        If ShapeIsVisible = False Then
            Return
        End If
    End If 'If Enums.bCheckIfPolygonVisible Then

    tLL1 = fShape.fLatLng.Get(0)
    
    'very unlikely first point will be at the 180 meridian, but may have to accomodate that
    '--------------------------------------------------------------------------------------
    Dim ptStart As TMapPoint = fcvMap.LatLngToPoint(tLL1, "clsMapShapePolygon, Draw") 'first polygon point
    
    p.Initialize(ptStart.fX, ptStart.fY)
    
    For i = 1 To fShape.fLatLng.Size - 1
        tLL2 = fShape.fLatLng.Get(i)
        Dim ptNext As TMapPoint = fcvMap.LatLngToPoint(tLL2, "clsMapShapePolygon, Draw")
        p.LineTo(ptNext.fX, ptNext.fY)
        tLL1 = tLL2
    Next
    
    fcvMap.ShapeCanvas.DrawPath(p, fShape.fColor, fShape.fFilled, fShape.fStrokeWidth)

End Sub

In class cvMap:

B4X:
Public Sub setZoomLevel(avalue As Int)
    
    fMap.fZoomLevel = MapUtilities.validZoomLevel(avalue)
    fViewTiles.RemoveAllViews
    fTilesCount = Power(2, fMap.fZoomLevel)
    
    '19 is the maximum zoom factor in this app
    dCenterToEdgeLat = Power(2, 19 - fMap.fZoomLevel) * Enums.dFindZoomFactorLat  'default found for this app/device: 0.00057
    dCenterToEdgeLng = Power(2, 19 - fMap.fZoomLevel) * Enums.dFindZoomFactorLng  'default found for this app/device: 0.0005
    
    fViewZoomLevel.Text = $"${fMap.fZoomLevel}/${MapUtilities.iMaxZoomLevel}"$
    
    If SubExists(mCallBack,mEventName & "_zoomLevelChanged") Then
        CallSub2(mCallBack,mEventName & "_zoomLevelChanged", fMap.fZoomLevel)
    End If
    
End Sub

This nearly sorts out now all problem I saw related to shapes near the 180 degree meridian.
Still funny things can happen when drawing a shape, then zooming up and then moving the map.
May look at this next.

RBS
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
As there was still a problem when a shape was drawn around a point on the 180 degree meridian in that when the shape was moved horizontally there could appear lines
going around the globe and I added some code to correct this.
As this problem only occurred when the drawn shape was out of side this code will make sure the shape is not drawn when the shape is off the app screen.

In class clsMapShapePolygon:

B4X:
Sub Class_Globals

    Type TMapShapePolygon(fLatLng As List, fColor As Int, fFilled As Boolean, fStrokeWidth As Double, fData As Object)
    Private fcvMap As cvMap
    Private fShape As TMapShapePolygon
    Private mLayer As Int 'added RBS 02/05/2022
    Private BB_Shape As TMapBoxLatLng
    Private bShapePassesDateLine As Boolean

End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(aCVMap As cvMap, aShape As TMapShapePolygon)
   
    fcvMap = aCVMap
    fShape = aShape
    mLayer = MapUtilities.iPolygonLayer 'added RBS 02/05/2022
    BB_Shape = MapUtilities.GetBBFromPolygon(aShape.fLatLng) 'added RBS 06/12/2024
    bShapePassesDateLine = MapUtilities.Get180MeridianLeftShiftValue(BB_Shape) > 0 'added RBS 06/12/2024, not sure we need this
   
End Sub

Sub ShapeIsVisible As Boolean
   
    'check if R bottom of shape BB is visible
    If BB_Shape.fRightBottom.fLng < fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng Then
        If BB_Shape.fRightBottom.fLng > fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng Then
            If BB_Shape.fRightBottom.fLat < fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat Then
                If BB_Shape.fRightBottom.fLat > fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then
                    Return True
                    'Log("bShapeVisible: " & bShapeVisible)
                End If
            End If
        End If
    End If
   
    'check if R top of shape BB is visible
    If BB_Shape.fRightBottom.fLng < fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng Then
        If BB_Shape.fRightBottom.fLng > fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng Then
            If BB_Shape.fLeftTop.fLat < fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat Then
                If BB_Shape.fLeftTop.fLat > fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then
                    Return True
                End If
            End If
        End If
    End If
   
    'check if L top of shape BB is visible
    If BB_Shape.fLeftTop.fLng > fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng Then
        If BB_Shape.fLeftTop.fLng < fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng Then
            If BB_Shape.fLeftTop.fLat > fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then
                If BB_Shape.fLeftTop.fLat < fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat Then
                    Return True
                End If
            End If
        End If
    End If
   
    'check if L bottom of shape BB is visible
    If BB_Shape.fLeftTop.fLng > fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng Then
        If BB_Shape.fLeftTop.fLng < fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng Then
            If BB_Shape.fRightBottom.fLat > fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then
                If BB_Shape.fRightBottom.fLat < fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat Then
                    Return True
                End If
            End If
        End If
    End If
   
    'check if overlapping laterally on both sides
    If BB_Shape.fLeftTop.fLng < fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng Then
        If BB_Shape.fRightBottom.fLng > fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng Then
                   
            If BB_Shape.fRightBottom.fLat < fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat And _
                       BB_Shape.fRightBottom.fLat > fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Or _
                       BB_Shape.fLeftTop.fLat < fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat And _
                       BB_Shape.fLeftTop.fLat > fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then
                Return True
            End If
                   
        End If
    End If
   
    'check if overlapping vertically on both sides
    If BB_Shape.fLeftTop.fLat > fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat Then
        If BB_Shape.fRightBottom.fLat < fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then
                   
            If BB_Shape.fRightBottom.fLng < fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng And _
                       BB_Shape.fRightBottom.fLng > fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng Or _
                       BB_Shape.fLeftTop.fLng > fcvMap.CenterLatLng.fLng - fcvMap.dCenterToEdgeLng And _
                       BB_Shape.fLeftTop.fLng < fcvMap.CenterLatLng.fLng + fcvMap.dCenterToEdgeLng Then
                Return True
            End If
                   
        End If
    End If
   
    Return False

End Sub

'draw the shape
'---------------------------------------------------------
Public Sub Draw
   
    Dim tLL1 As TMapLatLng
    Dim tLL2 As TMapLatLng
    Dim p As B4XPath

  
    If fShape.fLatLng.IsInitialized = False Then Return

    If fShape.fLatLng.Size = 0 Then Return

    If Enums.bCheckIfPolygonVisible Then
        If ShapeIsVisible = False Then
            Return
        End If
    End If 'If Enums.bCheckIfPolygonVisible Then

    tLL1 = fShape.fLatLng.Get(0)
   
    'very unlikely first point will be at the 180 meridian, but may have to accomodate that
    '--------------------------------------------------------------------------------------
    Dim ptStart As TMapPoint = fcvMap.LatLngToPoint(tLL1, "clsMapShapePolygon, Draw") 'first polygon point
   
    p.Initialize(ptStart.fX, ptStart.fY)
   
    For i = 1 To fShape.fLatLng.Size - 1
        tLL2 = fShape.fLatLng.Get(i)
        Dim ptNext As TMapPoint = fcvMap.LatLngToPoint(tLL2, "clsMapShapePolygon, Draw")
        p.LineTo(ptNext.fX, ptNext.fY)
        tLL1 = tLL2
    Next
   
    fcvMap.ShapeCanvas.DrawPath(p, fShape.fColor, fShape.fFilled, fShape.fStrokeWidth)

End Sub

In class cvMap:

B4X:
Public Sub setZoomLevel(avalue As Int)
   
    fMap.fZoomLevel = MapUtilities.validZoomLevel(avalue)
    fViewTiles.RemoveAllViews
    fTilesCount = Power(2, fMap.fZoomLevel)
   
    '19 is the maximum zoom factor in this app
    dCenterToEdgeLat = Power(2, 19 - fMap.fZoomLevel) * Enums.dFindZoomFactorLat  'default found for this app/device: 0.00057
    dCenterToEdgeLng = Power(2, 19 - fMap.fZoomLevel) * Enums.dFindZoomFactorLng  'default found for this app/device: 0.0005
   
    fViewZoomLevel.Text = $"${fMap.fZoomLevel}/${MapUtilities.iMaxZoomLevel}"$
   
    If SubExists(mCallBack,mEventName & "_zoomLevelChanged") Then
        CallSub2(mCallBack,mEventName & "_zoomLevelChanged", fMap.fZoomLevel)
    End If
   
End Sub

This nearly sorts out now all problem I saw related to shapes near the 180 degree meridian.
Still funny things can happen when drawing a shape, then zooming up and then moving the map.
May look at this next.

RBS
Just noticed that there problems with wrong polygon lines not just when near to the 180 degrees meridian, but also when we are well away from that line.
Attached 2 images that show this.
The polygon shows fine at zoom 15, then we move to zoom 16 and move the map slightly to the right 2 wrong polygon lines will show.
At least now with not drawing the polygon when it is off the map those wrong lines will disappear quick when moving further to the right.
I think this is a bug in the OSM viewer project and will see if I can pinpoint this and fix it.

RBS
 

Attachments

  • RightPolygonLines.zip
    468.9 KB · Views: 15
  • WrongPolygonLines.zip
    354.6 KB · Views: 16
Upvote 0

emexes

Expert
Licensed User
Might be something to do with longitudinal scale changing as you zoom and pan "vertically".

Eg even without changing zoom, the longitudinal pixels-per-degree decreases as you move towards the poles whilst the latitudinal pixels-per-degree remains constant.

Lol which sounds like it'd screw up the idea of caching results of bounding polygon using screen coordinates. Damned if you do, damned if you don't.

I guess you'd have to do the process from scratch every time anything changes. Surely that'd work. What could possibly go rwong?
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
Might be something to do with longitudinal scale changing as you zoom and pan "vertically".

Eg even without changing zoom, the longitudinal pixels-per-degree decreases as you move towards the poles whilst the latitudinal pixels-per-degree remains constant.

Lol which sounds like it'd screw up the idea of caching results of bounding polygon using screen coordinates. Damned if you do, damned if you don't.

I guess you'd have to do the process from scratch every time anything changes. Surely that'd work. What could possibly go rwong?
I fixed this now for map spots well away from that date line, which would be practically always.
The change in the relevant Sub, Sub LatLngToPoint has been marked line this: '<<<< And bShapePassesDateLine was added here

B4X:
'return  TMapPoint from TMapLatLng
Public Sub LatLngToPoint(aLatLng As TMapLatLng, strCallingSub As String, bShapePassesDateLine As Boolean) As TMapPoint
    
    Dim iZoom As Int = fMap.fZoomLevel
    Dim lTilePosOfLatLng As Long
    Dim iTileSize As Int = MapUtilities.cTileSize
    Dim iX As Int
    Dim iY As Int
    Dim bTilePosOfLatLngCorrected As Boolean
    Dim bLog As Boolean = (strCallingSub = "clsMapShapePolygon, Draw")
    
    'https://www.netzwolf.info/osm/tilebrowser.html?marker.x=12&marker.y=189&tx=1&ty=0&tz=1&ts=256#tile
    
    'top-left tile
    '-------------
    Dim v As B4XView = PointToTile(0, 0) 
    Dim txy As TMapTileXY = v.tag
    Dim gpx As Double = (txy.fX + (-v.Left / iTileSize)) / fTilesCount
    Dim gpy As Double = (txy.fy + (-v.top / iTileSize)) / fTilesCount
    
    'tile holding the lat/lng of the provided TMapLatLng
    '---------------------------------------------------
    Dim tx As TMapTileNumberOffset = LngToTileXPlusOffset(aLatLng.fLng, iZoom)
    Dim ty As TMapTileNumberOffset = LatToTileYPlusOffset(aLatLng.fLat, iZoom)
    
    'correct for if the tile number is lower than the tile number of the top-left tile
    'only do this when the shape is passing the 180 degrees meridian (date line)!
    '---------------------------------------------------------------------------------
    If tx.fTile < txy.fX And bShapePassesDateLine Then '<<<< And bShapePassesDateLine was added here
        lTilePosOfLatLng = Power(2, fMap.fZoomLevel) + tx.fTile
        bTilePosOfLatLngCorrected = True
    Else
        lTilePosOfLatLng = tx.fTile
    End If

    Dim ppx As Double = (lTilePosOfLatLng + (tx.fOffset / iTileSize)) / fTilesCount
    Dim ppy As Double = (ty.fTile + (ty.fOffset / iTileSize)) / fTilesCount
    
    iX = fTilesCount * iTileSize * (ppx - gpx)
    iY = fTilesCount * iTileSize * (ppy - gpy)
    
    Dim p As TMapPoint = MapUtilities.initPoint(iX, iY)

    If bLog Then
        Log("-----------------------------------------------------")
        Log("==== LatLngToPoint, Calling sub: " & strCallingSub)
        Log("==== aLatLng.fLng: " & aLatLng.fLng & ", aLatLng.fLat: " & aLatLng.fLat)
        Log("==== iZoom: " & iZoom)
        Log("==== v.Left: " & v.Left)
        Log("==== v.Width: " & v.Width)
        Log("==== iTileSize: " & iTileSize)
        Log("==== txy.fX: " & txy.fX & ", txy.fY: " & txy.fY)
        Log("==== gpx: " & gpx)
        Log("==== gpy: " & gpy)
        Log("==== tx.fTile : " & tx.fTile & ", tx.fOffset : " & tx.fOffset)
        Log("==== ty.fTile : " & ty.fTile & ", ty.fOffset : " & ty.fOffset)
        Log("==== lTilePosOfLatLng: " & lTilePosOfLatLng)
        Log("==== bTilePosOfLatLngCorrected: " & bTilePosOfLatLngCorrected)
        Log("==== ppx: " & ppx)
        Log("==== ppy: " & ppy)
        Log("==== ppx - gpx: " & NumberFormat2(ppx - gpx, 1, 10, 2, False))
        Log("==== ppy - gpy: " & NumberFormat2(ppy - gpy, 1, 10, 2, False))
        Log("==== fTilesCount: " & fTilesCount)
        Log("==== iX: " & NumberFormat2(iX, 1, 1, 0, False))
        Log("==== iY: " & NumberFormat2(iY, 1, 1, 0, False))
        Log("-----------------------------------------------------")
    End If
    
    Return p
    
End Sub

Shame the author of this project (Open Street Map Viewer), SPSP doesn't seem to work on this anymore.
I guess he/she would fix all this easily.

RBS
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
I fixed this now for map spots well away from that date line, which would be practically always.
The change in the relevant Sub, Sub LatLngToPoint has been marked line this: '<<<< And bShapePassesDateLine was added here

B4X:
'return  TMapPoint from TMapLatLng
Public Sub LatLngToPoint(aLatLng As TMapLatLng, strCallingSub As String, bShapePassesDateLine As Boolean) As TMapPoint
   
    Dim iZoom As Int = fMap.fZoomLevel
    Dim lTilePosOfLatLng As Long
    Dim iTileSize As Int = MapUtilities.cTileSize
    Dim iX As Int
    Dim iY As Int
    Dim bTilePosOfLatLngCorrected As Boolean
    Dim bLog As Boolean = (strCallingSub = "clsMapShapePolygon, Draw")
   
    'https://www.netzwolf.info/osm/tilebrowser.html?marker.x=12&marker.y=189&tx=1&ty=0&tz=1&ts=256#tile
   
    'top-left tile
    '-------------
    Dim v As B4XView = PointToTile(0, 0)
    Dim txy As TMapTileXY = v.tag
    Dim gpx As Double = (txy.fX + (-v.Left / iTileSize)) / fTilesCount
    Dim gpy As Double = (txy.fy + (-v.top / iTileSize)) / fTilesCount
   
    'tile holding the lat/lng of the provided TMapLatLng
    '---------------------------------------------------
    Dim tx As TMapTileNumberOffset = LngToTileXPlusOffset(aLatLng.fLng, iZoom)
    Dim ty As TMapTileNumberOffset = LatToTileYPlusOffset(aLatLng.fLat, iZoom)
   
    'correct for if the tile number is lower than the tile number of the top-left tile
    'only do this when the shape is passing the 180 degrees meridian (date line)!
    '---------------------------------------------------------------------------------
    If tx.fTile < txy.fX And bShapePassesDateLine Then '<<<< And bShapePassesDateLine was added here
        lTilePosOfLatLng = Power(2, fMap.fZoomLevel) + tx.fTile
        bTilePosOfLatLngCorrected = True
    Else
        lTilePosOfLatLng = tx.fTile
    End If

    Dim ppx As Double = (lTilePosOfLatLng + (tx.fOffset / iTileSize)) / fTilesCount
    Dim ppy As Double = (ty.fTile + (ty.fOffset / iTileSize)) / fTilesCount
   
    iX = fTilesCount * iTileSize * (ppx - gpx)
    iY = fTilesCount * iTileSize * (ppy - gpy)
   
    Dim p As TMapPoint = MapUtilities.initPoint(iX, iY)

    If bLog Then
        Log("-----------------------------------------------------")
        Log("==== LatLngToPoint, Calling sub: " & strCallingSub)
        Log("==== aLatLng.fLng: " & aLatLng.fLng & ", aLatLng.fLat: " & aLatLng.fLat)
        Log("==== iZoom: " & iZoom)
        Log("==== v.Left: " & v.Left)
        Log("==== v.Width: " & v.Width)
        Log("==== iTileSize: " & iTileSize)
        Log("==== txy.fX: " & txy.fX & ", txy.fY: " & txy.fY)
        Log("==== gpx: " & gpx)
        Log("==== gpy: " & gpy)
        Log("==== tx.fTile : " & tx.fTile & ", tx.fOffset : " & tx.fOffset)
        Log("==== ty.fTile : " & ty.fTile & ", ty.fOffset : " & ty.fOffset)
        Log("==== lTilePosOfLatLng: " & lTilePosOfLatLng)
        Log("==== bTilePosOfLatLngCorrected: " & bTilePosOfLatLngCorrected)
        Log("==== ppx: " & ppx)
        Log("==== ppy: " & ppy)
        Log("==== ppx - gpx: " & NumberFormat2(ppx - gpx, 1, 10, 2, False))
        Log("==== ppy - gpy: " & NumberFormat2(ppy - gpy, 1, 10, 2, False))
        Log("==== fTilesCount: " & fTilesCount)
        Log("==== iX: " & NumberFormat2(iX, 1, 1, 0, False))
        Log("==== iY: " & NumberFormat2(iY, 1, 1, 0, False))
        Log("-----------------------------------------------------")
    End If
   
    Return p
   
End Sub

Shame the author of this project (Open Street Map Viewer), SPSP doesn't seem to work on this anymore.
I guess he/she would fix all this easily.

RBS
Had another look at this as there were still some problems.
Changed the code to see if a polygon was off the screen and changed the Sub LatLngToPoint.
Things have improved and only problem I see now is that shortly there will be extra lines to the right
when a polygon is drawn at low longitude values (say < -160) and then the map is moved east (scrolling left).
Note that for simplicity and maybe speed the code to check if the polygon is off the screen works with a bounding
box around the polygon and not the polygon itself.

B4X:
Sub ShapeIsVisible As Boolean
        
    If Enums.bLogPolygonOffScreen Then
        Log("-----------------------------------------------------")
    End If
    
    '-----------------------------------------------------------------
    'check for right edge of the shape to be to the left of the screen
    '-----------------------------------------------------------------
    If Enums.bLogPolygonOffScreen Then
        Log("ShapeIsVisible1, BB_Shape.fRightBottom.fLng: " & BB_Shape.fRightBottom.fLng & _
        ", fcvMap.CenterLatLng.fLng: " & fcvMap.CenterLatLng.fLng & _
        ", fcvMap.dCenterToEdgeLng: " & NumberFormat2(fcvMap.dCenterToEdgeLng,1,6,2,False))
    End If
    
    MapUtilities.dLeftScreenEdgeLng = MapUtilities.GetScreenEdgeLng(fcvMap.CenterLatLng.fLng, fcvMap.dCenterToEdgeLng, True)
    
    If Enums.bLogPolygonOffScreen Then
        Log("MapUtilities.dLeftScreenEdgeLng: " & MapUtilities.dLeftScreenEdgeLng)
    End If
        
    If (BB_Shape.fRightBottom.fLng > 0 And MapUtilities.dLeftScreenEdgeLng > 0) Or _
       (BB_Shape.fRightBottom.fLng < 0 And MapUtilities.dLeftScreenEdgeLng < 0) Then
        If BB_Shape.fRightBottom.fLng < MapUtilities.dLeftScreenEdgeLng Then
            If Enums.bLogPolygonOffScreen Then
                Log(">>>>ShapeIsVisible1_1: False")
                Log("-----------------------------------------------------")
            End If
            Return False
        End If
    Else
        If BB_Shape.fRightBottom.fLng > 0 And MapUtilities.dLeftScreenEdgeLng < 0 Then
            If BB_Shape.fRightBottom.fLng < MapUtilities.dLeftScreenEdgeLng Then
                If Enums.bLogPolygonOffScreen Then
                    Log(">>>>ShapeIsVisible1_2: False")
                    Log("-----------------------------------------------------")
                End If
                Return False
            End If
        Else
            If BB_Shape.fRightBottom.fLng > MapUtilities.dLeftScreenEdgeLng Then
                If Enums.bLogPolygonOffScreen Then
                    Log(">>>>ShapeIsVisible1_3: False")
                    Log("-----------------------------------------------------")
                End If
                Return False
            End If
        End If
    End If

    '-----------------------------------------------------------------
    'check for left edge of the shape to be to the right of the screen
    '-----------------------------------------------------------------
    If Enums.bLogPolygonOffScreen Then
        Log("ShapeIsVisible2, BB_Shape.fLeftTop.fLng: " & BB_Shape.fLeftTop.fLng & _
        ", fcvMap.CenterLatLng.fLng: " & fcvMap.CenterLatLng.fLng & _
        ", fcvMap.dCenterToEdgeLng: " & NumberFormat2(fcvMap.dCenterToEdgeLng,1,6,2,False))
    End If
        
    MapUtilities.dRightScreenEdgeLng = MapUtilities.GetScreenEdgeLng(fcvMap.CenterLatLng.fLng, fcvMap.dCenterToEdgeLng, False)
    
    If Enums.bLogPolygonOffScreen Then
        Log("MapUtilities.dRightScreenEdgeLng: " & MapUtilities.dRightScreenEdgeLng)
    End If
    
    If (BB_Shape.fLeftTop.fLng > 0 And MapUtilities.dRightScreenEdgeLng > 0) Or _
       (BB_Shape.fLeftTop.fLng < 0 And MapUtilities.dRightScreenEdgeLng < 0) Then
        If BB_Shape.fLeftTop.fLng > MapUtilities.dRightScreenEdgeLng Then
            If Enums.bLogPolygonOffScreen Then
                Log(">>>>ShapeIsVisible2_1: False")
                Log("-----------------------------------------------------")
            End If
            Return False
        End If
    Else
        If BB_Shape.fLeftTop.fLng > 0 And MapUtilities.dRightScreenEdgeLng < 0 Then
            If BB_Shape.fLeftTop.fLng < MapUtilities.dRightScreenEdgeLng Then
                If Enums.bLogPolygonOffScreen Then
                    Log(">>>>ShapeIsVisible2_2: False")
                    Log("-----------------------------------------------------")
                End If
                Return False
            End If
        Else
            If BB_Shape.fLeftTop.fLng > MapUtilities.dRightScreenEdgeLng Then
                If Enums.bLogPolygonOffScreen Then
                    Log(">>>>ShapeIsVisible2_3: False")
                    Log("-----------------------------------------------------")
                End If
                Return False
            End If
        End If
    End If
    
    '---------------------------------------------------------
    'check for bottom edge of the shape to be above the screen
    '---------------------------------------------------------
    'Log(BB_Shape.fRightBottom.fLat & " > " & (fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat))
    If BB_Shape.fRightBottom.fLat > fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat Then Return False

    '------------------------------------------------------
    'check for top edge of the shape to be below the screen
    '------------------------------------------------------
    'Log(BB_Shape.fLeftTop.fLat & " < " & (fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat))
    If BB_Shape.fLeftTop.fLat < fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then Return False
    
    Return True
    
End Sub

Sub GetScreenEdgeLng(dLngCenter As Double, dLngDistance As Double, bLeftEdge As Boolean) As Double
    
    If bLeftEdge Then
        If dLngCenter - dLngDistance > -180 Then
            Return dLngCenter - dLngDistance
        Else
            Return 360 + (dLngCenter - dLngDistance)
        End If
    Else
        If dLngCenter + dLngDistance < 180 Then
            Return dLngCenter + dLngDistance
        Else
            Return (dLngCenter + dLngDistance) - 360
        End If
    End If
    
End Sub

'return  TMapPoint from TMapLatLng
Public Sub LatLngToPoint(aLatLng As TMapLatLng, strCallingSub As String) As TMapPoint
    
    Dim iZoom As Int = fMap.fZoomLevel
    Dim iTileSize As Int = MapUtilities.cTileSize
    Dim strPositionDiffChange As String
    Dim bLog As Boolean
    
    If Enums.bLogLatLngToPoint Then
        bLog  = (strCallingSub = "clsMapShapePolygon, Draw")
    End If
    
    'https://www.netzwolf.info/osm/tilebrowser.html?marker.x=12&marker.y=189&tx=1&ty=0&tz=1&ts=256#tile
    
    'top-left tile
    '-------------
    Dim v As B4XView = PointToTile(0, 0)
    Dim txy As TMapTileXY = v.tag
    
    Dim lTileTopLeftX As Long = txy.fX
    Dim lTileTopLeftY As Long = txy.fY
    
    'tile holding the lat/lng of the provided TMapLatLng
    '---------------------------------------------------
    Dim tx As TMapTileNumberOffset = LngToTileXPlusOffset(aLatLng.fLng, iZoom)
    Dim ty As TMapTileNumberOffset = LatToTileYPlusOffset(aLatLng.fLat, iZoom)
    
    Dim lTileLatLngX As Long = tx.fTile
    Dim lTileLatLngY As Long = ty.fTile
    
    Dim gpx As Double = (lTileTopLeftX + (-v.Left / iTileSize)) / fTilesCount
    Dim gpy As Double = (lTileTopLeftY + (-v.top / iTileSize)) / fTilesCount
    
    'This can't work as lTileLatLngX < lTileTopLeftX can be true due to:
    'the provided lat/lng point being off the screen to the left
    '-------------------------------------------------------------------
    'If lTileLatLngX < lTileTopLeftX Then
    'lTileLatLngX = Power(2, fMap.fZoomLevel) + lTileLatLngX
    'End If

    Dim ppx As Double = (lTileLatLngX + (tx.fOffset / iTileSize)) / fTilesCount
    Dim ppy As Double = (lTileLatLngY + (ty.fOffset / iTileSize)) / fTilesCount
    
    Dim dxPositionDiff As Double = ppx - gpx
    
    If dxPositionDiff < 0 Then
        If aLatLng.fLng < Enums.dLongitudeAlterPoint Then 'Enums.dLongitudeAlterPoint currently at -160, may need changing
            'this allows drawing around the 180 degrees meridian, but faulty lines showing to the right when moving east
            '-----------------------------------------------------------------------------------------------------------
            dxPositionDiff = dxPositionDiff + 1
            strPositionDiffChange = "add one"
        Else
            strPositionDiffChange = "not changed"
        End If
    Else
        strPositionDiffChange = "not changed"
    End If
    
    'max for iX at zoom 5: 24569 with dxPosDiff being 1
    '--------------------------------------------------
    Dim iX As Int = fTilesCount * iTileSize * (dxPositionDiff)
    Dim iY As Int = fTilesCount * iTileSize * (ppy - gpy)
    
    Dim p As TMapPoint = MapUtilities.initPoint(iX, iY)

    If bLog Then
        Log("-----------------------------------------------------")
        Log("==== LatLngToPoint, Calling sub: " & strCallingSub)
        Log("==== aLatLng.fLng: " & aLatLng.fLng & ", aLatLng.fLat: " & aLatLng.fLat)
        Log("==== iZoom: " & iZoom)
        Log("==== v.Left: " & v.Left)
        Log("==== v.Width: " & v.Width)
        Log("==== iTileSize: " & iTileSize)
        Log("==== lTileTopLeftX: " & lTileTopLeftX)
        Log("==== lTileTopLeftY: " & lTileTopLeftY)
        Log("==== lTileLatLngX: " & lTileLatLngX & ", tx.fOffset : " & tx.fOffset)
        Log("==== lTileLatLngY: " & lTileLatLngY & ", ty.fOffset : " & ty.fOffset)
        Log("==== gpx: " & gpx)
        Log("==== gpy: " & gpy)
        Log("==== ppx: " & ppx)
        Log("==== ppy: " & ppy)
        Log("==== ppx - gpx: " & NumberFormat2(ppx - gpx, 1, 10, 2, False))
        Log("==== dxPositionDiff: " & NumberFormat2(dxPositionDiff, 1, 10, 2, False))
        Log("==== strPositionDiffChange: " & strPositionDiffChange)
        Log("==== ppy - gpy: " & NumberFormat2(ppy - gpy, 1, 10, 2, False))
        Log("==== fTilesCount: " & fTilesCount)
        Log("==== iX: " & NumberFormat2(iX, 1, 1, 0, False))
        Log("==== iY: " & NumberFormat2(iY, 1, 1, 0, False))
        Log("-----------------------------------------------------")
    End If
    
    Return p
    
End Sub

This is the log when those wrong lines show as described above:


-----------------------------------------------------
OK
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -178.4619140625, aLatLng.fLat: 44.09810572925697
==== iZoom: 5
==== v.Left: -95
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 0, tx.fOffset : 105
==== lTileLatLngY: 11, ty.fOffset : 479.0000000000009
==== gpx: 0.0038655598958333335
==== gpy: 0.3172607421875
==== ppx: 0.0042724609375
==== ppy: 0.36324055989583337
==== ppx - gpx: 0.000406901
==== dxPositionDiff: 0.000406901
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.0459798177
==== fTilesCount: 32
==== iX: 9
==== iY: 1130
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -177.978515625, aLatLng.fLat: 47.98011710368253
==== iZoom: 5
==== v.Left: -95
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 0, tx.fOffset : 138
==== lTileLatLngY: 11, ty.fOffset : 97.00000000000182
==== gpx: 0.0038655598958333335
==== gpy: 0.3172607421875
==== ppx: 0.005615234375
==== ppy: 0.34769694010416674
==== ppx - gpx: 0.0017496745
==== dxPositionDiff: 0.0017496745
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.0304361979
==== fTilesCount: 32
==== iX: 43
==== iY: 748
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -177.978515625, aLatLng.fLat: 47.98011710368253
==== iZoom: 5
==== v.Left: -108
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 0, tx.fOffset : 138
==== lTileLatLngY: 11, ty.fOffset : 97.00000000000182
==== gpx: 0.00439453125
==== gpy: 0.3173421223958333
==== ppx: 0.005615234375
==== ppy: 0.34769694010416674
==== ppx - gpx: 0.0012207031
==== dxPositionDiff: 0.0012207031
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.0303548177
==== fTilesCount: 32
==== iX: 30
==== iY: 746
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -171.2255859375, aLatLng.fLat: 51.55430524349756
==== iZoom: 5
==== v.Left: -108
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 0, tx.fOffset : 599
==== lTileLatLngY: 10, ty.fOffset : 486.9999999999991
==== gpx: 0.00439453125
==== gpy: 0.3173421223958333
==== ppx: 0.024373372395833332
==== ppy: 0.33231608072916663
==== ppx - gpx: 0.0199788411
==== dxPositionDiff: 0.0199788411
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.0149739583
==== fTilesCount: 32
==== iX: 491
==== iY: 367
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -167.607421875, aLatLng.fLat: 48.33190860951609
==== iZoom: 5
==== v.Left: -108
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 1, tx.fOffset : 78
==== lTileLatLngY: 11, ty.fOffset : 60.99999999999909
==== gpx: 0.00439453125
==== gpy: 0.3173421223958333
==== ppx: 0.034423828125
==== ppy: 0.34623209635416663
==== ppx - gpx: 0.0300292969
==== dxPositionDiff: 0.0300292969
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.028889974
==== fTilesCount: 32
==== iX: 738
==== iY: 709
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -168.046875, aLatLng.fLat: 41.37680856570235
==== iZoom: 5
==== v.Left: -108
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 1, tx.fOffset : 48
==== lTileLatLngY: 11, ty.fOffset : 731.9999999999986
==== gpx: 0.00439453125
==== gpy: 0.3173421223958333
==== ppx: 0.033203125
==== ppy: 0.37353515624999994
==== ppx - gpx: 0.0288085938
==== dxPositionDiff: 0.0288085938
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.0561930339
==== fTilesCount: 32
==== iX: 708
==== iY: 1380
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -173.935546875, aLatLng.fLat: 40.68063802521458
==== iZoom: 5
==== v.Left: -108
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 0, tx.fOffset : 414
==== lTileLatLngY: 12, ty.fOffset : 27
==== gpx: 0.00439453125
==== gpy: 0.3173421223958333
==== ppx: 0.016845703125
==== ppy: 0.3760986328125
==== ppx - gpx: 0.0124511719
==== dxPositionDiff: 0.0124511719
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.0587565104
==== fTilesCount: 32
==== iX: 306
==== iY: 1444
-----------------------------------------------------
Faulty lines
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -178.4619140625, aLatLng.fLat: 44.09810572925697
==== iZoom: 5
==== v.Left: -108
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 0, tx.fOffset : 105
==== lTileLatLngY: 11, ty.fOffset : 479.0000000000009
==== gpx: 0.00439453125
==== gpy: 0.3173421223958333
==== ppx: 0.0042724609375
==== ppy: 0.36324055989583337
==== ppx - gpx: -0.0001220703 <<<<<<<<<<<<<<<<<<<
==== dxPositionDiff: 0.9998779297
==== strPositionDiffChange: add one
==== ppy - gpy: 0.0458984375
==== fTilesCount: 32
==== iX: 24573 <<<<<<<<<<<<<<<<<<<<<
==== iY: 1128

RBS
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
Had another look at this as there were still some problems.
Changed the code to see if a polygon was off the screen and changed the Sub LatLngToPoint.
Things have improved and only problem I see now is that shortly there will be extra lines to the right
when a polygon is drawn at low longitude values (say < -160) and then the map is moved east (scrolling left).
Note that for simplicity and maybe speed the code to check if the polygon is off the screen works with a bounding
box around the polygon and not the polygon itself.

B4X:
Sub ShapeIsVisible As Boolean
       
    If Enums.bLogPolygonOffScreen Then
        Log("-----------------------------------------------------")
    End If
   
    '-----------------------------------------------------------------
    'check for right edge of the shape to be to the left of the screen
    '-----------------------------------------------------------------
    If Enums.bLogPolygonOffScreen Then
        Log("ShapeIsVisible1, BB_Shape.fRightBottom.fLng: " & BB_Shape.fRightBottom.fLng & _
        ", fcvMap.CenterLatLng.fLng: " & fcvMap.CenterLatLng.fLng & _
        ", fcvMap.dCenterToEdgeLng: " & NumberFormat2(fcvMap.dCenterToEdgeLng,1,6,2,False))
    End If
   
    MapUtilities.dLeftScreenEdgeLng = MapUtilities.GetScreenEdgeLng(fcvMap.CenterLatLng.fLng, fcvMap.dCenterToEdgeLng, True)
   
    If Enums.bLogPolygonOffScreen Then
        Log("MapUtilities.dLeftScreenEdgeLng: " & MapUtilities.dLeftScreenEdgeLng)
    End If
       
    If (BB_Shape.fRightBottom.fLng > 0 And MapUtilities.dLeftScreenEdgeLng > 0) Or _
       (BB_Shape.fRightBottom.fLng < 0 And MapUtilities.dLeftScreenEdgeLng < 0) Then
        If BB_Shape.fRightBottom.fLng < MapUtilities.dLeftScreenEdgeLng Then
            If Enums.bLogPolygonOffScreen Then
                Log(">>>>ShapeIsVisible1_1: False")
                Log("-----------------------------------------------------")
            End If
            Return False
        End If
    Else
        If BB_Shape.fRightBottom.fLng > 0 And MapUtilities.dLeftScreenEdgeLng < 0 Then
            If BB_Shape.fRightBottom.fLng < MapUtilities.dLeftScreenEdgeLng Then
                If Enums.bLogPolygonOffScreen Then
                    Log(">>>>ShapeIsVisible1_2: False")
                    Log("-----------------------------------------------------")
                End If
                Return False
            End If
        Else
            If BB_Shape.fRightBottom.fLng > MapUtilities.dLeftScreenEdgeLng Then
                If Enums.bLogPolygonOffScreen Then
                    Log(">>>>ShapeIsVisible1_3: False")
                    Log("-----------------------------------------------------")
                End If
                Return False
            End If
        End If
    End If

    '-----------------------------------------------------------------
    'check for left edge of the shape to be to the right of the screen
    '-----------------------------------------------------------------
    If Enums.bLogPolygonOffScreen Then
        Log("ShapeIsVisible2, BB_Shape.fLeftTop.fLng: " & BB_Shape.fLeftTop.fLng & _
        ", fcvMap.CenterLatLng.fLng: " & fcvMap.CenterLatLng.fLng & _
        ", fcvMap.dCenterToEdgeLng: " & NumberFormat2(fcvMap.dCenterToEdgeLng,1,6,2,False))
    End If
       
    MapUtilities.dRightScreenEdgeLng = MapUtilities.GetScreenEdgeLng(fcvMap.CenterLatLng.fLng, fcvMap.dCenterToEdgeLng, False)
   
    If Enums.bLogPolygonOffScreen Then
        Log("MapUtilities.dRightScreenEdgeLng: " & MapUtilities.dRightScreenEdgeLng)
    End If
   
    If (BB_Shape.fLeftTop.fLng > 0 And MapUtilities.dRightScreenEdgeLng > 0) Or _
       (BB_Shape.fLeftTop.fLng < 0 And MapUtilities.dRightScreenEdgeLng < 0) Then
        If BB_Shape.fLeftTop.fLng > MapUtilities.dRightScreenEdgeLng Then
            If Enums.bLogPolygonOffScreen Then
                Log(">>>>ShapeIsVisible2_1: False")
                Log("-----------------------------------------------------")
            End If
            Return False
        End If
    Else
        If BB_Shape.fLeftTop.fLng > 0 And MapUtilities.dRightScreenEdgeLng < 0 Then
            If BB_Shape.fLeftTop.fLng < MapUtilities.dRightScreenEdgeLng Then
                If Enums.bLogPolygonOffScreen Then
                    Log(">>>>ShapeIsVisible2_2: False")
                    Log("-----------------------------------------------------")
                End If
                Return False
            End If
        Else
            If BB_Shape.fLeftTop.fLng > MapUtilities.dRightScreenEdgeLng Then
                If Enums.bLogPolygonOffScreen Then
                    Log(">>>>ShapeIsVisible2_3: False")
                    Log("-----------------------------------------------------")
                End If
                Return False
            End If
        End If
    End If
   
    '---------------------------------------------------------
    'check for bottom edge of the shape to be above the screen
    '---------------------------------------------------------
    'Log(BB_Shape.fRightBottom.fLat & " > " & (fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat))
    If BB_Shape.fRightBottom.fLat > fcvMap.CenterLatLng.fLat + fcvMap.dCenterToEdgeLat Then Return False

    '------------------------------------------------------
    'check for top edge of the shape to be below the screen
    '------------------------------------------------------
    'Log(BB_Shape.fLeftTop.fLat & " < " & (fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat))
    If BB_Shape.fLeftTop.fLat < fcvMap.CenterLatLng.fLat - fcvMap.dCenterToEdgeLat Then Return False
   
    Return True
   
End Sub

Sub GetScreenEdgeLng(dLngCenter As Double, dLngDistance As Double, bLeftEdge As Boolean) As Double
   
    If bLeftEdge Then
        If dLngCenter - dLngDistance > -180 Then
            Return dLngCenter - dLngDistance
        Else
            Return 360 + (dLngCenter - dLngDistance)
        End If
    Else
        If dLngCenter + dLngDistance < 180 Then
            Return dLngCenter + dLngDistance
        Else
            Return (dLngCenter + dLngDistance) - 360
        End If
    End If
   
End Sub

'return  TMapPoint from TMapLatLng
Public Sub LatLngToPoint(aLatLng As TMapLatLng, strCallingSub As String) As TMapPoint
   
    Dim iZoom As Int = fMap.fZoomLevel
    Dim iTileSize As Int = MapUtilities.cTileSize
    Dim strPositionDiffChange As String
    Dim bLog As Boolean
   
    If Enums.bLogLatLngToPoint Then
        bLog  = (strCallingSub = "clsMapShapePolygon, Draw")
    End If
   
    'https://www.netzwolf.info/osm/tilebrowser.html?marker.x=12&marker.y=189&tx=1&ty=0&tz=1&ts=256#tile
   
    'top-left tile
    '-------------
    Dim v As B4XView = PointToTile(0, 0)
    Dim txy As TMapTileXY = v.tag
   
    Dim lTileTopLeftX As Long = txy.fX
    Dim lTileTopLeftY As Long = txy.fY
   
    'tile holding the lat/lng of the provided TMapLatLng
    '---------------------------------------------------
    Dim tx As TMapTileNumberOffset = LngToTileXPlusOffset(aLatLng.fLng, iZoom)
    Dim ty As TMapTileNumberOffset = LatToTileYPlusOffset(aLatLng.fLat, iZoom)
   
    Dim lTileLatLngX As Long = tx.fTile
    Dim lTileLatLngY As Long = ty.fTile
   
    Dim gpx As Double = (lTileTopLeftX + (-v.Left / iTileSize)) / fTilesCount
    Dim gpy As Double = (lTileTopLeftY + (-v.top / iTileSize)) / fTilesCount
   
    'This can't work as lTileLatLngX < lTileTopLeftX can be true due to:
    'the provided lat/lng point being off the screen to the left
    '-------------------------------------------------------------------
    'If lTileLatLngX < lTileTopLeftX Then
    'lTileLatLngX = Power(2, fMap.fZoomLevel) + lTileLatLngX
    'End If

    Dim ppx As Double = (lTileLatLngX + (tx.fOffset / iTileSize)) / fTilesCount
    Dim ppy As Double = (lTileLatLngY + (ty.fOffset / iTileSize)) / fTilesCount
   
    Dim dxPositionDiff As Double = ppx - gpx
   
    If dxPositionDiff < 0 Then
        If aLatLng.fLng < Enums.dLongitudeAlterPoint Then 'Enums.dLongitudeAlterPoint currently at -160, may need changing
            'this allows drawing around the 180 degrees meridian, but faulty lines showing to the right when moving east
            '-----------------------------------------------------------------------------------------------------------
            dxPositionDiff = dxPositionDiff + 1
            strPositionDiffChange = "add one"
        Else
            strPositionDiffChange = "not changed"
        End If
    Else
        strPositionDiffChange = "not changed"
    End If
   
    'max for iX at zoom 5: 24569 with dxPosDiff being 1
    '--------------------------------------------------
    Dim iX As Int = fTilesCount * iTileSize * (dxPositionDiff)
    Dim iY As Int = fTilesCount * iTileSize * (ppy - gpy)
   
    Dim p As TMapPoint = MapUtilities.initPoint(iX, iY)

    If bLog Then
        Log("-----------------------------------------------------")
        Log("==== LatLngToPoint, Calling sub: " & strCallingSub)
        Log("==== aLatLng.fLng: " & aLatLng.fLng & ", aLatLng.fLat: " & aLatLng.fLat)
        Log("==== iZoom: " & iZoom)
        Log("==== v.Left: " & v.Left)
        Log("==== v.Width: " & v.Width)
        Log("==== iTileSize: " & iTileSize)
        Log("==== lTileTopLeftX: " & lTileTopLeftX)
        Log("==== lTileTopLeftY: " & lTileTopLeftY)
        Log("==== lTileLatLngX: " & lTileLatLngX & ", tx.fOffset : " & tx.fOffset)
        Log("==== lTileLatLngY: " & lTileLatLngY & ", ty.fOffset : " & ty.fOffset)
        Log("==== gpx: " & gpx)
        Log("==== gpy: " & gpy)
        Log("==== ppx: " & ppx)
        Log("==== ppy: " & ppy)
        Log("==== ppx - gpx: " & NumberFormat2(ppx - gpx, 1, 10, 2, False))
        Log("==== dxPositionDiff: " & NumberFormat2(dxPositionDiff, 1, 10, 2, False))
        Log("==== strPositionDiffChange: " & strPositionDiffChange)
        Log("==== ppy - gpy: " & NumberFormat2(ppy - gpy, 1, 10, 2, False))
        Log("==== fTilesCount: " & fTilesCount)
        Log("==== iX: " & NumberFormat2(iX, 1, 1, 0, False))
        Log("==== iY: " & NumberFormat2(iY, 1, 1, 0, False))
        Log("-----------------------------------------------------")
    End If
   
    Return p
   
End Sub

This is the log when those wrong lines show as described above:


-----------------------------------------------------
OK
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -178.4619140625, aLatLng.fLat: 44.09810572925697
==== iZoom: 5
==== v.Left: -95
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 0, tx.fOffset : 105
==== lTileLatLngY: 11, ty.fOffset : 479.0000000000009
==== gpx: 0.0038655598958333335
==== gpy: 0.3172607421875
==== ppx: 0.0042724609375
==== ppy: 0.36324055989583337
==== ppx - gpx: 0.000406901
==== dxPositionDiff: 0.000406901
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.0459798177
==== fTilesCount: 32
==== iX: 9
==== iY: 1130
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -177.978515625, aLatLng.fLat: 47.98011710368253
==== iZoom: 5
==== v.Left: -95
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 0, tx.fOffset : 138
==== lTileLatLngY: 11, ty.fOffset : 97.00000000000182
==== gpx: 0.0038655598958333335
==== gpy: 0.3172607421875
==== ppx: 0.005615234375
==== ppy: 0.34769694010416674
==== ppx - gpx: 0.0017496745
==== dxPositionDiff: 0.0017496745
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.0304361979
==== fTilesCount: 32
==== iX: 43
==== iY: 748
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -177.978515625, aLatLng.fLat: 47.98011710368253
==== iZoom: 5
==== v.Left: -108
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 0, tx.fOffset : 138
==== lTileLatLngY: 11, ty.fOffset : 97.00000000000182
==== gpx: 0.00439453125
==== gpy: 0.3173421223958333
==== ppx: 0.005615234375
==== ppy: 0.34769694010416674
==== ppx - gpx: 0.0012207031
==== dxPositionDiff: 0.0012207031
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.0303548177
==== fTilesCount: 32
==== iX: 30
==== iY: 746
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -171.2255859375, aLatLng.fLat: 51.55430524349756
==== iZoom: 5
==== v.Left: -108
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 0, tx.fOffset : 599
==== lTileLatLngY: 10, ty.fOffset : 486.9999999999991
==== gpx: 0.00439453125
==== gpy: 0.3173421223958333
==== ppx: 0.024373372395833332
==== ppy: 0.33231608072916663
==== ppx - gpx: 0.0199788411
==== dxPositionDiff: 0.0199788411
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.0149739583
==== fTilesCount: 32
==== iX: 491
==== iY: 367
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -167.607421875, aLatLng.fLat: 48.33190860951609
==== iZoom: 5
==== v.Left: -108
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 1, tx.fOffset : 78
==== lTileLatLngY: 11, ty.fOffset : 60.99999999999909
==== gpx: 0.00439453125
==== gpy: 0.3173421223958333
==== ppx: 0.034423828125
==== ppy: 0.34623209635416663
==== ppx - gpx: 0.0300292969
==== dxPositionDiff: 0.0300292969
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.028889974
==== fTilesCount: 32
==== iX: 738
==== iY: 709
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -168.046875, aLatLng.fLat: 41.37680856570235
==== iZoom: 5
==== v.Left: -108
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 1, tx.fOffset : 48
==== lTileLatLngY: 11, ty.fOffset : 731.9999999999986
==== gpx: 0.00439453125
==== gpy: 0.3173421223958333
==== ppx: 0.033203125
==== ppy: 0.37353515624999994
==== ppx - gpx: 0.0288085938
==== dxPositionDiff: 0.0288085938
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.0561930339
==== fTilesCount: 32
==== iX: 708
==== iY: 1380
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -173.935546875, aLatLng.fLat: 40.68063802521458
==== iZoom: 5
==== v.Left: -108
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 0, tx.fOffset : 414
==== lTileLatLngY: 12, ty.fOffset : 27
==== gpx: 0.00439453125
==== gpy: 0.3173421223958333
==== ppx: 0.016845703125
==== ppy: 0.3760986328125
==== ppx - gpx: 0.0124511719
==== dxPositionDiff: 0.0124511719
==== strPositionDiffChange: not changed
==== ppy - gpy: 0.0587565104
==== fTilesCount: 32
==== iX: 306
==== iY: 1444
-----------------------------------------------------
Faulty lines
-----------------------------------------------------
==== LatLngToPoint, Calling sub: clsMapShapePolygon, Draw
==== aLatLng.fLng: -178.4619140625, aLatLng.fLat: 44.09810572925697
==== iZoom: 5
==== v.Left: -108
==== v.Width: 768
==== iTileSize: 768
==== lTileTopLeftX: 0
==== lTileTopLeftY: 10
==== lTileLatLngX: 0, tx.fOffset : 105
==== lTileLatLngY: 11, ty.fOffset : 479.0000000000009
==== gpx: 0.00439453125
==== gpy: 0.3173421223958333
==== ppx: 0.0042724609375
==== ppy: 0.36324055989583337
==== ppx - gpx: -0.0001220703 <<<<<<<<<<<<<<<<<<<
==== dxPositionDiff: 0.9998779297
==== strPositionDiffChange: add one
==== ppy - gpy: 0.0458984375
==== fTilesCount: 32
==== iX: 24573 <<<<<<<<<<<<<<<<<<<<<
==== iY: 1128

RBS
Had another go at this and thought I could simply get the iX and iY once and then when the map is moved changed these according to recorded movements of the map.
Map movements can be picked up from this Sub in cvMap:

B4X:
'move tiles
Private Sub MoveTiles(aduration As Int, aMoveX As Int, aMoveY As Int)
    
    'Log("MoveTiles")
    
   ' Enums.lMovedX = Enums.lMovedX + aMoveX
    'Enums.lMovedY = Enums.lMovedY + aMoveY

Then the altered iX and iY values can be adjusted by adding Enums.lMovedX and Enums.lMovedY when moving the map.
This works indeed fine if you add the polygon points in one screen (so no map moves between adding the points) and leave the zoom unaltered.
So, no more funny lines from the polygon to the right.
However if you don't stick to the above 2 conditions then funny things happen and it gets a lot more complicated, so I abandoned that idea.
I can see now why it makes sense to base the polygon points on lat/lng points as these lat/lng points are fixed points on the map that won't change
with moving the map or altering the zoom etc.
I like fixing bugs but this seems the most difficult one I have come across.

RBS
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Longtime User
Had another go at this and thought I could simply get the iX and iY once and then when the map is moved changed these according to recorded movements of the map.
Map movements can be picked up from this Sub in cvMap:

B4X:
'move tiles
Private Sub MoveTiles(aduration As Int, aMoveX As Int, aMoveY As Int)
   
    'Log("MoveTiles")
   
   ' Enums.lMovedX = Enums.lMovedX + aMoveX
    'Enums.lMovedY = Enums.lMovedY + aMoveY

Then the altered iX and iY values can be adjusted by adding Enums.lMovedX and Enums.lMovedY when moving the map.
This works indeed fine if you add the polygon points in one screen (so no map moves between adding the points) and leave the zoom unaltered.
So, no more funny lines from the polygon to the right.
However if you don't stick to the above 2 conditions then funny things happen and it gets a lot more complicated, so I abandoned that idea.
I can see now why it makes sense to base the polygon points on lat/lng points as these lat/lng points are fixed points on the map that won't change
with moving the map or altering the zoom etc.
I like fixing bugs but this seems the most difficult one I have come across.

RBS
I think I got this fixed now.
This is the relevant Sub that has been changed in the class cvMap:

B4X:
'return  TMapPoint from TMapLatLng
Public Sub LatLngToPoint(aLatLng As TMapLatLng, strCallingSub As String) As TMapPoint
    
    Dim iZoom As Int = fMap.fZoomLevel
    Dim iTileSize As Int = MapUtilities.cTileSize
    Dim strPositionDiffChange As String
    Dim bNewMapXY As Boolean
    Dim bPositionDiffChanged As Boolean
        
    bNewMapXY = (strCallingSub = "clsMapShapePolygon, Draw")
    
    'https://www.netzwolf.info/osm/tilebrowser.html?marker.x=12&marker.y=189&tx=1&ty=0&tz=1&ts=256#tile
    
    'top-left tile, these values will change if moving the map as it is the the top-left of the current screen
    '---------------------------------------------------------------------------------------------------------
    Dim v As B4XView = PointToTile(0, 0)
    Dim txy As TMapTileXY = v.tag
    
    Dim lTileTopLeftX As Long = txy.fX
    Dim lTileTopLeftY As Long = txy.fY
    
    'tile holding the lat/lng of the provided TMapLatLng
    '---------------------------------------------------
    Dim tx As TMapTileNumberOffset = LngToTileXPlusOffset(aLatLng.fLng, iZoom)
    Dim ty As TMapTileNumberOffset = LatToTileYPlusOffset(aLatLng.fLat, iZoom)
    
    Dim lTileLatLngX As Long = tx.fTile
    Dim lTileLatLngY As Long = ty.fTile
    
    Dim gpx As Double = (lTileTopLeftX + (-v.Left / iTileSize)) / fTilesCount
    Dim gpy As Double = (lTileTopLeftY + (-v.top / iTileSize)) / fTilesCount
    
    Dim ppx As Double = (lTileLatLngX + (tx.fOffset / iTileSize)) / fTilesCount
    Dim ppy As Double = (lTileLatLngY + (ty.fOffset / iTileSize)) / fTilesCount
    
    Dim dxPositionDiff As Double = ppx - gpx
    
    If dxPositionDiff < -0.5 Then
        dxPositionDiff = dxPositionDiff + 1
        strPositionDiffChange = "add one"
        bPositionDiffChanged = True
    Else
        If dxPositionDiff > 0.5 Then
            dxPositionDiff = dxPositionDiff - 1
            strPositionDiffChange = "minus one"
            bPositionDiffChanged = True
        Else
            strPositionDiffChange = "not changed"
        End If
    End If
    
    'max for iX at zoom 5: 24576 (32 * 768) with dxPosDiff being 1
    'max for iX at zoom 6: 49152 (64 * 768) with dxPosDiff being 1
    'max for iX at zoom 7: 98304 (128 * 768) with dxPosDiff being 1
    '--------------------------------------------------------------
    Dim lX As Long = fTilesCount * iTileSize * (dxPositionDiff)
    
    If bPositionDiffChanged Then
        Dim lXUnaltered As Long = fTilesCount * iTileSize * (ppx - gpx)
    End If
    
    Dim lY As Long = fTilesCount * iTileSize * (ppy - gpy)
    
    Dim p As TMapPoint = MapUtilities.initPoint(lX, lY)
    
    If Enums.bLogLatLngToPoint And bNewMapXY Then
        Log("-----------------------------------------------------")
        Log("==== LatLngToPoint, Calling sub: " & strCallingSub)
        Log("==== Enums.iPolygonIndex: " & Enums.iPolygonIndex)
        Log("==== aLatLng.fLng: " & aLatLng.fLng & ", aLatLng.fLat: " & aLatLng.fLat)
        Log("==== Enums.bPolygonLineCrossesDateLine: " & Enums.bPolygonLineCrossesDateLine)
        Log("==== Enums.bAddingNewPolygonPoint: " & Enums.bAddingNewPolygonPoint)
        Log("==== iZoom: " & iZoom)
        Log("==== v.Left: " & v.Left)
        Log("==== v.Width: " & v.Width)
        Log("==== iTileSize: " & iTileSize)
        Log("==== lTileTopLeftX: " & lTileTopLeftX)
        Log("==== lTileTopLeftY: " & lTileTopLeftY)
        Log("==== lTileLatLngX: " & lTileLatLngX & ", tx.fOffset : " & tx.fOffset)
        Log("==== lTileLatLngY: " & lTileLatLngY & ", ty.fOffset : " & ty.fOffset)
        Log("==== gpx: " & NumberFormat2(gpx, 1, 16, 2, False))
        Log("==== gpy: " & NumberFormat2(gpy, 1, 16, 2, False))
        Log("==== ppx: " & NumberFormat2(ppx, 1, 16, 2, False))
        Log("==== ppy: " & NumberFormat2(ppy, 1, 16, 2, False))
        Log("==== ppx - gpx: " & NumberFormat2(ppx - gpx, 1, 10, 2, False))
        Log("==== dxPositionDiff: " & NumberFormat2(dxPositionDiff, 1, 10, 2, False))
        Log("==== strPositionDiffChange: " & strPositionDiffChange)
        Log("==== ppy - gpy: " & NumberFormat2(ppy - gpy, 1, 10, 2, False))
        Log("==== fTilesCount: " & fTilesCount)
        If bPositionDiffChanged Then
            Log("==== lXUnaltered (unaltered dxPositionDiff): " & NumberFormat2(lXUnaltered, 1, 1, 0, False))
        End If
        Log("==== lX: " & NumberFormat2(lX, 1, 1, 0, False))
        Log("==== lY: " & NumberFormat2(lY, 1, 1, 0, False))
        Log("-----------------------------------------------------")
    End If
    
    Return p
    
End Sub

The fixed values 0.5 and -0.5 can be changed, but if a line involves more that half of the east-west surround of the globe then it seems reasonable
to presume that this is a faulty line. Will have a look a bit further at this.

RBS
 
Upvote 0
Top