Re: OpenStreetMap dans un Canvas
Publié : jeu. 07/juil./2016 11:45
				
				Il va falloir revoir le mode de chargement pour qu'il se fasse dès un déplacement. C'est plus compliqué que prévu 
			Forums PureBasic - Français
https://www.purebasic.fr/french/
J'avais prévu de charger les Tiles avec un Thread je commence a bien maîtriser les Mutexs et les sémaphoresdjes a écrit :Il va falloir revoir le mode de chargement pour qu'il se fasse dès un déplacement. C'est plus compliqué que prévu
pourtant dans mon code je lorsqu'on bouge la map effectivement tant que tu es appuyer ça ne fait qu'un déclage en pixel de l'image... mais des que tu relaches le bouton de la souris je recalcule les coordonées du Tiledjes a écrit :Le problème vient du calcul de déplacement inverse, c'est à dire retrouver les coordonnées de la tuile "actuelle", et lui rajouter le décalage précédent (ce qui n'est pas fait), plus le déplacement en cours. Bref, il faudrait vraiment que ce soit dynamique pour être toujours centré sur la bonne tuile.
Code : Tout sélectionner
tx=(OSM\DeltaCursor\X/512)
ty=(OSM\DeltaCursor\Y/512)
 TileTranslate(@OSM\TargetTile,tx.d,ty.d)Code : Tout sélectionner
 XY2LatLon(@OSM\TargetTile,@OSM\TargetLocation)
Code : Tout sélectionner
Case OSM\Gadget
              Select EventType()
                Case #PB_EventType_LeftButtonDown ; Un clic gauche je mémorise la position
                  ;Mem cursor Coord
                  OSM\StartCursor\X=GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX)
                  OSM\StartCursor\Y=GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY)
                Case #PB_EventType_MouseMove ; la souris se déplace je calcule le décalage par rapport a la position d'origine.
                  If OSM\StartCursor\X<>0 Or OSM\StartCursor\Y<>0
                    OSM\DeltaCursor\X=GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX)-OSM\StartCursor\X
                    OSM\DeltaCursor\Y=GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY)-OSM\StartCursor\Y
                  EndIf 
                Case #PB_EventType_LeftButtonUp ; je relache je calcul les nouvelles coordonées
                  Define tx.d,ty.d
                  tx=(OSM\DeltaCursor\X/512)
                  ty=(OSM\DeltaCursor\Y/512)
                  OSM\DeltaCursor\X=0
                  OSM\DeltaCursor\Y=0
                  OSM\StartCursor\X=0
                  OSM\StartCursor\Y=0
                  TileTranslate(@OSM\TargetTile,tx.d,ty.d)
                  XY2LatLon(@OSM\TargetTile,@OSM\TargetLocation)
                  GetSquareTile()
                  ;move(tx,ty)
                  ;SetGadgetText(#String_1, StrD(osm\gfLongitude))
                  ;SetGadgetText(#String_0, StrD(osm\gfLatitude))
              EndSelect
heu ....djes a écrit :Oui, je suis dessus. D'abord, quand le déplacement est très grand, il n'y a plus de nouvelles tuiles sur les bords. Ensuite, le calcul se base sur la tuile de départ, jusqu'au relâchement. Or, la nouvelle tuile peut être très loin de la première, du coup le calcul n'est pas bon. Enfin, on arrive à une position qui n'est pas "juste", donc il faut une nouvelle position de départ "décalée".
Moi aussi je m'y perdsThyphoon a écrit :heu ....djes a écrit :Oui, je suis dessus. D'abord, quand le déplacement est très grand, il n'y a plus de nouvelles tuiles sur les bords. Ensuite, le calcul se base sur la tuile de départ, jusqu'au relâchement. Or, la nouvelle tuile peut être très loin de la première, du coup le calcul n'est pas bon. Enfin, on arrive à une position qui n'est pas "juste", donc il faut une nouvelle position de départ "décalée".tu me dis si je me trompe
Un tile déplacé a l'écran de 256 pixels correspond bien a 0.5 dans les coordonnées du Tile sur la Map .. donc si on deplace de 512 pixel ça devrait décalé de 1 tile complet. c'est bine ça.. ou bien il faut prendre en référence le quadrant ? j'avoue m'y perdre... lollll en tout cas merci de te pencher dessus
![]()
je vais essayer de vérifier ça
Code : Tout sélectionner
InitNetwork()
DeclareModule OSM
  Declare InitOSM()
  Declare OSMGadget(Gadget.i, X.l, Y.l, Width.l, Height.l)
  Declare Event(Event.l)
  Declare SetLocation(latitude.d, longitude.d, zoom = 15)
  Declare DrawMap()
  Declare SetZoom(Zoom.l, mode.l = #PB_Relative)
EndDeclareModule
Module OSM
  #USEPROXY = #False
  UsePNGImageDecoder()
  UsePNGImageEncoder()
  
  CompilerIf #USEPROXY = #True
    IncludeFile("C : \Users\lebrun_y_413\Documents\Developpement\Purebasic\includes Share\http.pbi")
  CompilerEndIf
  
  Structure Location
    Longitude.d
    Latitude.d
  EndStructure
  
  Structure Tile
    X.d
    Y.d
  EndStructure
  
  Structure Pixel
    X.l
    Y.l
  EndStructure
  
  Structure OSM
    Gadget.i
    TargetLocation.Location
    TargetTile.tile
    
    ServerURL.s
    ZoomMin.l
    ZoomMax.l
    Zoom.l
    
    CachePath.S
    
    StartCursor.Pixel
    DeltaCursor.Pixel
    
    Shift.Pixel
  EndStructure
  
  Global OSM.OSM
  
  Procedure InitOSM()
    OSM\CachePath = GetTemporaryDirectory()
    OSM\ServerURL = "http : //tile.openstreetmap.org/"
    OSM\ZoomMin = 0
    OSM\ZoomMax = 18
    OSM\StartCursor\X = - 1
  EndProcedure
  
  Procedure OSMGadget(Gadget.i, X.l, Y.l, Width.l, Height.l)
    If Gadget = #PB_Any
      OSM\Gadget = CanvasGadget(OSM\Gadget, X, Y, Width, Height)
    Else
      OSM\Gadget = Gadget
      CanvasGadget(OSM\Gadget, X, Y, Width, Height)
    EndIf 
  EndProcedure
  
  Procedure LatLon2XY(*Location.Location, *Tile.Tile)
    Protected n.d = Pow(2.0, OSM\Zoom), LatRad.d = Radian(*Location\Latitude)
    *Tile\X = n * ( (*Location\Longitude + 180.0) / 360.0) 
    *Tile\Y = n * ( 1.0 - Log(Tan(LatRad) + 1.0/Cos(LatRad)) / #PI ) / 2.0
  EndProcedure
  
  Procedure XY2LatLon(*Tile.Tile, *Location.Location)
    Protected n.d = Pow(2.0, OSM\Zoom)
    Protected LatitudeRad.d
    *Location\Longitude  = *Tile\X / n * 360.0 - 180.0
    LatitudeRad = ATan(SinH(#PI * (1.0 - 2.0 * *Tile\Y / n)))
    *Location\Latitude = Degree(LatitudeRad)
  EndProcedure
  
  Procedure LoadMapTile(Image.l, Zoom.l, XTile.l, YTile.l)
    Protected *Buffer
    Protected TileURL.s = OSM\ServerURL + Str(Zoom) + "/" + Str(XTile) + "/" + Str(YTile) + ".png"
    ; Test if in cache else download it
    Protected CacheFile.s = "OSM_" + Str(Zoom) + "_" + Str(XTile) + "_" + Str(YTile) + ".png"
    If FileSize(osm\cachePath + cacheFile) > 0
      ;Debug "Use Cache : " + cacheFile
      LoadImage(Image, OSM\CachePath + CacheFile)
    Else 
      ;Debug "DOWNLOAD : " + psURL
      CompilerIf #USEPROXY = #True
        Protected http.HTTP_Query
        HTTP_proxy(@http, "spxy.bpi.fr", 3128)
        HTTP_DownloadToMem(@http, TileURL)
        If IsImage(Image)
          FreeImage(Image)
        EndIf
        If CatchImage(Image, http\data, MemorySize(http\data))
          SaveImage(Image, OSM\CachePath + CacheFile, #PB_ImagePlugin_PNG)
        EndIf
      CompilerElse
        *Buffer = ReceiveHTTPMemory(TileURL)  ;TODO to thread by using #PB_HTTP_Asynchronous
        If IsImage(Image)
          FreeImage(Image)
        EndIf
        If *Buffer
          If CatchImage(Image, *Buffer, MemorySize(*Buffer))
            SaveImage(image, OSM\CachePath + CacheFile, #PB_ImagePlugin_PNG)
            FreeMemory(*Buffer)
          EndIf
        EndIf
      CompilerEndIf
      
    EndIf
    
  EndProcedure
  
  Procedure GetSquareTile()
    Protected x.l, y.l, nx.l, ny.l, n.l = 0, tx.l, ty.l
    nx = Int(GadgetWidth(OSM\Gadget)/256) 
    ny = Int(GadgetHeight(OSM\Gadget)/256) 
    tx = 2*OSM\TargetTile\X
    ty = 2*OSM\TargetTile\Y
    For y = ty - ny To ty + ny + ny - 1    
      For x = tx - nx To tx + nx + nx - 1
        Debug Str(x) + " " + Str(y)
        LoadMapTile(n, OSM\Zoom + 1, x, y)
        n = n + 1
      Next
    Next
    Debug Str(n) + " Images Chargées";
  EndProcedure
  
  Procedure DrawMap()
    Protected x.l, y.l, nx.l, ny.l, mx.l, my.l, n.l = 0
    Protected deltaX.l, deltaY.l
    ;Protected tx.l, ty.l
    Protected x2.l, y2.l
    ;deltaX = 512*(OSM\TargetTile\X - Int(OSM\TargetTile\X)) - 256 ;TODO Why - 256 ?????
    ;deltaY = 512*(OSM\TargetTile\Y - Int(OSM\TargetTile\Y)) - 512 ;TODO Why - 512 ?????
    ;tx = 2 * OSM\TargetTile\X
    ;ty = 2 * OSM\TargetTile\Y
    nx = Int(GadgetWidth(OSM\Gadget)/256)
    ny = Int(GadgetHeight(OSM\Gadget)/256)
    
    StartDrawing(CanvasOutput(OSM\Gadget))
    Box(0, 0, GadgetWidth(OSM\Gadget), GadgetHeight(OSM\Gadget), RGB(255, 255, 255))
    
    For y = - ny To ny + ny - 1    
      For x = - nx To nx + nx - 1
        x2 = x*256 + OSM\DeltaCursor\X ;+ OSM\Shift\X
        y2 = y*256 + OSM\DeltaCursor\Y ;+ OSM\Shift\Y
        If IsImage(n) And (x2 + 256) > 0 And (y2 + 256) > 0 And x2 < GadgetWidth(OSM\Gadget) And y2 < GadgetHeight(OSM\Gadget)
          DrawImage(ImageID(n), x2, y2, 254, 254)
          DrawText( x2, y2, Str(x - Int(nx/2)) + ", " + Str(y - Int(ny/2)))
        EndIf        
        n = n + 1
      Next
    Next
    
    Circle(GadgetWidth(OSM\Gadget)/2, GadgetHeight(OSM\Gadget)/2, 5, #Red)
    DrawText(0, 0, "DeltaCursorX : " + Str(OSM\DeltaCursor\X) + " deltaX : " + Str(deltaX) + " Tile X : " + StrD(OSM\TargetTile\X))
    StopDrawing()
  EndProcedure
  
  Procedure SetLocation(latitude.d, longitude.d, zoom = 15)
    If zoom > OSM\ZoomMax : zoom = OSM\ZoomMax : EndIf
    If zoom < OSM\ZoomMin : zoom = OSM\ZoomMin : EndIf
    OSM\Zoom = zoom
    OSM\TargetLocation\Latitude = latitude
    OSM\TargetLocation\Longitude = longitude
    LatLon2XY(@OSM\TargetLocation, @OSM\TargetTile)
    GetSquareTile()
    DrawMap()
  EndProcedure
  
  Procedure TileTranslate(*Tile.Tile, tx.d, ty.d)
    Debug " - move - "
    
    Protected pfValue.d
    If tx <> 0
      pfValue = *Tile\X - tx
      If pfValue > Pow(2, OSM\Zoom) - 1
        *Tile\X = Pow(2, OSM\Zoom) - 2
      ElseIf pfValue < 0 
        *Tile\X = Pow(2, OSM\Zoom) - 2
      Else
        *Tile\X = pfValue
        
      EndIf
    EndIf
    
    If ty <> 0
      pfValue = *Tile\Y - ty
      If pfValue > Pow(2, OSM\Zoom) - 1
        *Tile\Y = Pow(2, OSM\Zoom) - 2
      ElseIf pfValue < 0 
        *Tile\Y = Pow(2, OSM\Zoom) - 2
      Else
        *Tile\Y = pfValue
        
      EndIf
    EndIf
  EndProcedure
  
  Procedure SetZoom(Zoom.l, mode.l = #PB_Relative)
    Select mode
      Case #PB_Relative
        OSM\Zoom = OSM\Zoom + zoom
      Case #PB_Absolute
        OSM\Zoom = zoom
    EndSelect
    If OSM\Zoom > OSM\ZoomMax : OSM\Zoom = OSM\ZoomMax : EndIf
    If OSM\Zoom < OSM\ZoomMin : OSM\Zoom = OSM\ZoomMin : EndIf
    LatLon2XY(@OSM\TargetLocation, @OSM\TargetTile)
    GetSquareTile()
    DrawMap()
  EndProcedure
  
  Procedure Event(Event.l)
    Protected Gadget.i
    Protected tx.i, ty.i
    If IsGadget(OSM\Gadget) And GadgetType(OSM\Gadget) = #PB_GadgetType_Canvas 
      Select Event
        Case #PB_Event_Gadget ;{
          Gadget = EventGadget()
          Select Gadget
            Case OSM\Gadget
              Select EventType()
                Case #PB_EventType_LeftButtonDown
                  ;Mem cursor Coord
                  OSM\StartCursor\X = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                  OSM\StartCursor\Y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) 
                Case #PB_EventType_MouseMove 
                  If OSM\StartCursor\X <> - 1
                    tx = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) - OSM\StartCursor\X + OSM\Shift\X
                    ty = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) - OSM\StartCursor\Y + OSM\Shift\Y
                    TileTranslate(@OSM\TargetTile, tx/256, ty/256)
                    OSM\DeltaCursor\X = tx%256
                    OSM\DeltaCursor\Y = ty%256
                    SetLocation(OSM\TargetLocation\Latitude, OSM\TargetLocation\Longitude, OSM\Zoom)
                  EndIf 
                Case #PB_EventType_LeftButtonUp
                  OSM\Shift\X = OSM\DeltaCursor\X
                  OSM\Shift\Y = OSM\DeltaCursor\Y
                  OSM\DeltaCursor\X = 0
                  OSM\DeltaCursor\Y = 0
                  OSM\StartCursor\X = - 1
                  ;move(tx, ty)
                  ;SetGadgetText(#String_1, StrD(osm\gfLongitude))
                  ;SetGadgetText(#String_0, StrD(osm\gfLatitude))
              EndSelect
          EndSelect
          
          
          
      EndSelect
    Else
      MessageRequester("Module OSM", "You must use OSMGadget before", #PB_MessageRequester_Ok )
      End
    EndIf  
  EndProcedure
  
  
  
  
EndModule
Enumeration
  #Window_0
  #Map
  #Button_0
  #Button_1
  #Button_2
  #Button_3
  #Button_4
  #Button_5
  #Combo_0
  #Text_0
  #Text_1
  #Text_2
  #Text_3
  #Text_4
  #String_0
  #String_1
EndEnumeration
;- Main
If OpenWindow(#Window_0, 260, 225, 700, 571, "OpenStreetMap",  #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_TitleBar | #PB_Window_ScreenCentered )
  
  OSM::InitOSM()
  LoadFont(0, "Wingdings", 12)
  LoadFont(1, "Arial", 12, #PB_Font_Bold)
  
  OSM::OSMGadget(#Map, 10, 10, 512, 512)
  
  TextGadget(#Text_1, 530, 50, 60, 15, "Movements : ")
  ButtonGadget(#Button_0, 550, 100, 30, 30, Chr($E7))  : SetGadgetFont(#Button_0, FontID(0)) 
  ButtonGadget(#Button_1, 610, 100, 30, 30, Chr($E8))  : SetGadgetFont(#Button_1, FontID(0)) 
  ButtonGadget(#Button_2, 580, 070, 30, 30, Chr($E9))  : SetGadgetFont(#Button_2, FontID(0)) 
  ButtonGadget(#Button_3, 580, 130, 30, 30, Chr($EA))  : SetGadgetFont(#Button_3, FontID(0)) 
  TextGadget(#Text_2, 530, 160, 60, 15, "Zoom : ")
  ButtonGadget(#Button_4, 550, 180, 50, 30, " + ")      : SetGadgetFont(#Button_4, FontID(1)) 
  ButtonGadget(#Button_5, 600, 180, 50, 30, " - ")      : SetGadgetFont(#Button_5, FontID(1)) 
  TextGadget(#Text_3, 530, 230, 60, 15, "Latitude : ")
  StringGadget(#String_0, 600, 230, 90, 20, "")
  TextGadget(#Text_4, 530, 250, 60, 15, "Longitude : ")
  StringGadget(#String_1, 600, 250, 90, 20, "")
  
  Define Event.l, Gadget.l, Quit.b = #False
  Define pfValue.d
  OSM::SetLocation(49.04599, 2.03347)
  ;OSM::SetLocation(49.0361165, 2.0456982)
  
  Repeat
    Event = WaitWindowEvent()
    
    OSM::Event(Event)
    Select Event
      Case #PB_Event_CloseWindow : Quit = 1
      Case #PB_Event_Gadget ;{
        Gadget = EventGadget()
        Select Gadget
          Case #Button_4
            OSM::SetZoom(1)
          Case #Button_5
            OSM::SetZoom( - 1)
        EndSelect
    EndSelect
  Until Quit = #True
EndIfHa oui Bravo ça prend formedjes a écrit : Moi aussi je m'y perds
Voilà où j'en suis, pas loin je crois![]()
Oui, il y a un problème au niveau du chargement. Là, pareil, va falloir transformer ça en dynamique pour éviter des rechargements inutiles alors qu'il y a un cache et qu'on pourrait en garder en mémoire.Thyphoon a écrit :Ha oui Bravo ça prend formedjes a écrit : Moi aussi je m'y perds
Voilà où j'en suis, pas loin je crois![]()
... faut que je regarde en détail ce que tu as fait.
![]()
On a toujours un saut currieux des qu'on se déplace de plus d'un quadrant.
Franchement c'est quand même pas super simple leur système ... et pas super bien documenté. Ou alors c'est moi.djes a écrit : Oui, il y a un problème au niveau du chargement. Là, pareil, va falloir transformer ça en dynamique pour éviter des rechargements inutiles alors qu'il y a un cache.
Code : Tout sélectionner
    deltaX = 512*(OSM\TargetTile\X - Int(OSM\TargetTile\X))
    deltaY = 512*(OSM\TargetTile\Y - Int(OSM\TargetTile\Y))
lolll désolé ça fait pas mal de temps que je tourne en rond sur ce problème j'avais d'abord essayé de me débrouiller tout seul.. et comme j'y arrivais pas, en cherchant je suis tombé sur le code de Progi1984 qui m'a pas mal inspiré. Mais voilà je tourne en rond depuis pas mal de temps et je comprends pas bien le pourquoi du comment ...djes a écrit :Oui, c'est pas si simple. La difficulté, c'est de jongler avec les "ardoises" et les coordonnées des bords ; d'habitude je me fais un petit papier pour savoir où je vais mais là je perds beaucoup de temps à savoir comment fonctionne ce qui a été fait et dans quel but ! En plus je n'ai pas codé depuis TRES longtemps.
Code : Tout sélectionner
InitNetwork()
DeclareModule OSM
  Declare InitOSM()
  Declare OSMGadget(Gadget.i, X.i, Y.i, Width.i, Height.i)
  Declare Event(Event.l)
  Declare SetLocation(latitude.d, longitude.d, zoom = 15)
  Declare GetSquareTile()
  Declare DrawMap()
  Declare SetZoom(Zoom.i, mode.i = #PB_Relative)
EndDeclareModule
Module OSM
  #USEPROXY = #False
  UsePNGImageDecoder()
  UsePNGImageEncoder()
  
  CompilerIf #USEPROXY = #True
    IncludeFile("C : \Users\lebrun_y_413\Documents\Developpement\Purebasic\includes Share\http.pbi")
  CompilerEndIf
  
  Structure Location
    Longitude.d
    Latitude.d
  EndStructure
  
  Structure Tile
    X.i
    Y.i
  EndStructure
  
  Structure Pixel
    X.i
    Y.i
  EndStructure
  
  Structure OSM
    Gadget.i
    TargetLocation.Location
    TargetTile.tile
    
    ServerURL.s
    ZoomMin.i
    ZoomMax.i
    Zoom.i
    
    CachePath.S
    
    StartCursor.Pixel
    DeltaCursor.Pixel
    
    Shift.Pixel
  EndStructure
  
  Global OSM.OSM
  
  Procedure InitOSM()
    OSM\CachePath = GetTemporaryDirectory()
    OSM\ServerURL = "http : //tile.openstreetmap.org/"
    OSM\ZoomMin = 0
    OSM\ZoomMax = 18
    OSM\StartCursor\X = - 1
  EndProcedure
  
  Procedure OSMGadget(Gadget.i, X.i, Y.i, Width.i, Height.i)
    If Gadget = #PB_Any
      OSM\Gadget = CanvasGadget(OSM\Gadget, X, Y, Width, Height)
    Else
      OSM\Gadget = Gadget
      CanvasGadget(OSM\Gadget, X, Y, Width, Height)
    EndIf 
  EndProcedure
  
  Procedure LatLon2XY(*Location.Location, *Tile.Tile)
    Protected n.d = Pow(2.0, OSM\Zoom), LatRad.d = Radian(*Location\Latitude)
    *Tile\X = n * ( (*Location\Longitude + 180.0) / 360.0) 
    *Tile\Y = n * ( 1.0 - Log(Tan(LatRad) + 1.0/Cos(LatRad)) / #PI ) / 2.0
  EndProcedure
  
  Procedure XY2LatLon(*Tile.Tile, *Location.Location)
    Protected n.d = Pow(2.0, OSM\Zoom)
    Protected LatitudeRad.d
    *Location\Longitude  = *Tile\X / n * 360.0 - 180.0
    LatitudeRad = ATan(SinH(#PI * (1.0 - 2.0 * *Tile\Y / n)))
    *Location\Latitude = Degree(LatitudeRad)
  EndProcedure
  
  Procedure LoadMapTile(Image.i, Zoom.i, XTile.i, YTile.i)
    Protected *Buffer
    Protected TileURL.s = OSM\ServerURL + Str(Zoom) + "/" + Str(XTile) + "/" + Str(YTile) + ".png"
    ; Test if in cache else download it
    Protected CacheFile.s = "OSM_" + Str(Zoom) + "_" + Str(XTile) + "_" + Str(YTile) + ".png"
    If FileSize(osm\cachePath + cacheFile) > 0
      ;Debug "Use Cache : " + cacheFile
      LoadImage(Image, OSM\CachePath + CacheFile)
    Else 
      ;Debug "DOWNLOAD : " + psURL
      CompilerIf #USEPROXY = #True
        Protected http.HTTP_Query
        HTTP_proxy(@http, "spxy.bpi.fr", 3128)
        HTTP_DownloadToMem(@http, TileURL)
        If IsImage(Image)
          FreeImage(Image)
        EndIf
        If CatchImage(Image, http\data, MemorySize(http\data))
          SaveImage(Image, OSM\CachePath + CacheFile, #PB_ImagePlugin_PNG)
        EndIf
      CompilerElse
        *Buffer = ReceiveHTTPMemory(TileURL)  ;TODO to thread by using #PB_HTTP_Asynchronous
        If IsImage(Image)
          FreeImage(Image)
        EndIf
        If *Buffer
          If CatchImage(Image, *Buffer, MemorySize(*Buffer))
            SaveImage(image, OSM\CachePath + CacheFile, #PB_ImagePlugin_PNG)
            FreeMemory(*Buffer)
          EndIf
        EndIf
      CompilerEndIf
      
    EndIf
    
  EndProcedure
  
  Procedure GetSquareTile()
    Protected x.i, y.i, nx.i, ny.i, n.i = 0, tx.i, ty.i
    nx = Int(GadgetWidth(OSM\Gadget)/256) 
    ny = Int(GadgetHeight(OSM\Gadget)/256) 
    tx = 2*OSM\TargetTile\X
    ty = 2*OSM\TargetTile\Y
    For y = ty - ny To ty + ny + ny - 1    
      For x = tx - nx To tx + nx + nx - 1
        Debug Str(x) + " " + Str(y)
        LoadMapTile(n, OSM\Zoom + 1, x, y)
        n = n + 1
      Next
    Next
    Debug Str(n) + " Images Chargées";
  EndProcedure
  
  Procedure DrawMap()
    Protected x.i, y.i, nx.i, ny.i, mx.i, my.i, n.i = 0
    Protected deltaX.i, deltaY.i
    ;Protected tx.i, ty.i
    Protected x2.i, y2.i
    ;deltaX = 512*(OSM\TargetTile\X - Int(OSM\TargetTile\X)) - 256 ;TODO Why - 256 ?????
    ;deltaY = 512*(OSM\TargetTile\Y - Int(OSM\TargetTile\Y)) - 512 ;TODO Why - 512 ?????
    ;tx = 2 * OSM\TargetTile\X
    ;ty = 2 * OSM\TargetTile\Y
        
    nx = Int(GadgetWidth(OSM\Gadget)/256)
    ny = Int(GadgetHeight(OSM\Gadget)/256)
    
    StartDrawing(CanvasOutput(OSM\Gadget))
    Box(0, 0, GadgetWidth(OSM\Gadget), GadgetHeight(OSM\Gadget), RGB(255, 255, 255))
    
    For y = - ny To ny + ny - 1    
      For x = - nx To nx + nx - 1
        x2 = x*256 + OSM\DeltaCursor\X ;+ OSM\Shift\X
        y2 = y*256 + OSM\DeltaCursor\Y ;+ OSM\Shift\Y
        If IsImage(n) And (x2 + 256) > 0 And (y2 + 256) > 0 And x2 < GadgetWidth(OSM\Gadget) And y2 < GadgetHeight(OSM\Gadget)
          DrawImage(ImageID(n), x2, y2, 254, 254)
          DrawText( x2, y2, Str(x - Int(nx/2)) + ", " + Str(y - Int(ny/2)))
        EndIf        
        n = n + 1
      Next
    Next
    
    Circle(GadgetWidth(OSM\Gadget)/2, GadgetHeight(OSM\Gadget)/2, 5, #Red)
    DrawText(0, 0, "DeltaCursorX : " + Str(OSM\DeltaCursor\X) + " deltaX : " + Str(deltaX) + " Tile X : " + StrD(OSM\TargetTile\X))
    StopDrawing()
  EndProcedure
  
  Procedure SetLocation(latitude.d, longitude.d, zoom = 15)
    If zoom > OSM\ZoomMax : zoom = OSM\ZoomMax : EndIf
    If zoom < OSM\ZoomMin : zoom = OSM\ZoomMin : EndIf
    OSM\Zoom = zoom
    OSM\TargetLocation\Latitude = latitude
    OSM\TargetLocation\Longitude = longitude
    LatLon2XY(@OSM\TargetLocation, @OSM\TargetTile)
  EndProcedure
  
  Procedure TileTranslate(*Tile.Tile, tx.d, ty.d)
    Debug " - move - "
    
    Protected pfValue.d
    If tx <> 0
      pfValue = *Tile\X - tx
      If pfValue > Pow(2, OSM\Zoom) - 1
        *Tile\X = Pow(2, OSM\Zoom) - 2
      ElseIf pfValue < 0 
        *Tile\X = Pow(2, OSM\Zoom) - 2
      Else
        *Tile\X = pfValue
        
      EndIf
    EndIf
    
    If ty <> 0
      pfValue = *Tile\Y - ty
      If pfValue > Pow(2, OSM\Zoom) - 1
        *Tile\Y = Pow(2, OSM\Zoom) - 2
      ElseIf pfValue < 0 
        *Tile\Y = Pow(2, OSM\Zoom) - 2
      Else
        *Tile\Y = pfValue
        
      EndIf
    EndIf
  EndProcedure
  
  Procedure SetZoom(Zoom.i, mode.i = #PB_Relative)
    Select mode
      Case #PB_Relative
        OSM\Zoom = OSM\Zoom + zoom
      Case #PB_Absolute
        OSM\Zoom = zoom
    EndSelect
    If OSM\Zoom > OSM\ZoomMax : OSM\Zoom = OSM\ZoomMax : EndIf
    If OSM\Zoom < OSM\ZoomMin : OSM\Zoom = OSM\ZoomMin : EndIf
    LatLon2XY(@OSM\TargetLocation, @OSM\TargetTile)
    GetSquareTile()
    DrawMap()
  EndProcedure
  
  Procedure Event(Event.l)
    Protected Gadget.i
    Protected tx.i, ty.i
    If IsGadget(OSM\Gadget) And GadgetType(OSM\Gadget) = #PB_GadgetType_Canvas 
      Select Event
        Case #PB_Event_Gadget ;{
          Gadget = EventGadget()
          Select Gadget
            Case OSM\Gadget
              Select EventType()
                Case #PB_EventType_LeftButtonDown
                  ;Mem cursor Coord
                  OSM\StartCursor\X = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                  OSM\StartCursor\Y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) 
                Case #PB_EventType_MouseMove 
                  If OSM\StartCursor\X <> - 1
                    tx = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) - OSM\StartCursor\X + OSM\Shift\X
                    ty = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) - OSM\StartCursor\Y + OSM\Shift\Y
                    ;TileTranslate(@OSM\TargetTile, tx/256, ty/256)
                    OSM\DeltaCursor\X = tx%512
                    OSM\DeltaCursor\Y = ty%512
                    If tx/512 <> 0 Or ty/512 <> 0
                      Debug tx/512
                      Debug ty/512
                      OSM\TargetTile\X - tx/512
                      OSM\TargetTile\Y - ty/512
                      OSM\Shift\X = OSM\DeltaCursor\X                     
                      OSM\Shift\Y = OSM\DeltaCursor\Y
                      OSM\StartCursor\X = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                      OSM\StartCursor\Y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) 
                      XY2LatLon(@OSM\TargetTile, @OSM\TargetLocation)
                      ;SetLocation(OSM\TargetLocation\Latitude, OSM\TargetLocation\Longitude, OSM\Zoom)
                      Debug OSM\TargetLocation\Latitude
                      GetSquareTile()
                    EndIf
                    DrawMap()
                  EndIf 
                Case #PB_EventType_LeftButtonUp
                  OSM\Shift\X = OSM\DeltaCursor\X
                  OSM\Shift\Y = OSM\DeltaCursor\Y
                  OSM\DeltaCursor\X = 0
                  OSM\DeltaCursor\Y = 0
                  OSM\StartCursor\X = - 1
                  ;move(tx, ty)
                  ;SetGadgetText(#String_1, StrD(osm\gfLongitude))
                  ;SetGadgetText(#String_0, StrD(osm\gfLatitude))
              EndSelect
          EndSelect
          
          
          
      EndSelect
    Else
      MessageRequester("Module OSM", "You must use OSMGadget before", #PB_MessageRequester_Ok )
      End
    EndIf  
  EndProcedure
  
  
  
  
EndModule
Enumeration
  #Window_0
  #Map
  #Button_0
  #Button_1
  #Button_2
  #Button_3
  #Button_4
  #Button_5
  #Combo_0
  #Text_0
  #Text_1
  #Text_2
  #Text_3
  #Text_4
  #String_0
  #String_1
EndEnumeration
;- Main
If OpenWindow(#Window_0, 260, 225, 700, 571, "OpenStreetMap",  #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_TitleBar | #PB_Window_ScreenCentered )
  
  OSM::InitOSM()
  LoadFont(0, "Wingdings", 12)
  LoadFont(1, "Arial", 12, #PB_Font_Bold)
  
  OSM::OSMGadget(#Map, 10, 10, 512, 512)
  
  TextGadget(#Text_1, 530, 50, 60, 15, "Movements : ")
  ButtonGadget(#Button_0, 550, 100, 30, 30, Chr($E7))  : SetGadgetFont(#Button_0, FontID(0)) 
  ButtonGadget(#Button_1, 610, 100, 30, 30, Chr($E8))  : SetGadgetFont(#Button_1, FontID(0)) 
  ButtonGadget(#Button_2, 580, 070, 30, 30, Chr($E9))  : SetGadgetFont(#Button_2, FontID(0)) 
  ButtonGadget(#Button_3, 580, 130, 30, 30, Chr($EA))  : SetGadgetFont(#Button_3, FontID(0)) 
  TextGadget(#Text_2, 530, 160, 60, 15, "Zoom : ")
  ButtonGadget(#Button_4, 550, 180, 50, 30, " + ")      : SetGadgetFont(#Button_4, FontID(1)) 
  ButtonGadget(#Button_5, 600, 180, 50, 30, " - ")      : SetGadgetFont(#Button_5, FontID(1)) 
  TextGadget(#Text_3, 530, 230, 60, 15, "Latitude : ")
  StringGadget(#String_0, 600, 230, 90, 20, "")
  TextGadget(#Text_4, 530, 250, 60, 15, "Longitude : ")
  StringGadget(#String_1, 600, 250, 90, 20, "")
  
  Define Event.i, Gadget.i, Quit.b = #False
  Define pfValue.d
  OSM::SetLocation(49.04599, 2.03347)
  OSM::GetSquareTile()
  OSM::DrawMap()
  ;OSM::SetLocation(49.0361165, 2.0456982)
  
  Repeat
    Event = WaitWindowEvent()
    
    OSM::Event(Event)
    Select Event
      Case #PB_Event_CloseWindow : Quit = 1
      Case #PB_Event_Gadget ;{
        Gadget = EventGadget()
        Select Gadget
          Case #Button_4
            OSM::SetZoom(1)
          Case #Button_5
            OSM::SetZoom( - 1)
        EndSelect
    EndSelect
  Until Quit = #True
EndIf