PBMap - Cartes OSM, Here, Geoserver dans un Canvas

Programmation d'applications complexes
Avatar de l’utilisateur
MetalOS
Messages : 1492
Inscription : mar. 20/juin/2006 22:17
Localisation : Lorraine
Contact :

Re: OpenStreetMap dans un Canvas

Message par MetalOS »

Alors moi je suis complètement largué. Désolé le gars j'aimerais vraiment vous aider.
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: OpenStreetMap dans un Canvas

Message par Thyphoon »

MetalOS a écrit :Alors moi je suis complètement largué. Désolé le gars j'aimerais vraiment vous aider.
pas de souci. Attend qu'on est quelques choses de stable. On te demandera un coup de main pour tester le tout apres :wink:
Avatar de l’utilisateur
MetalOS
Messages : 1492
Inscription : mar. 20/juin/2006 22:17
Localisation : Lorraine
Contact :

Re: OpenStreetMap dans un Canvas

Message par MetalOS »

Ok en tout cas merci pour votre travail car je cherche ce genre de chose pour mon logiciel depuis plusieurs années.
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: OpenStreetMap dans un Canvas

Message par djes »

Bon voilà un premier jet, vous verrez que les nouvelles tuiles suivent le déplacement. C'est parce que les threads sont "lancés" et que je ne les arrête pas (encore). Le fait est que je me tâte pour dessiner dans une image et ensuite cette image sur le gadget, afin d'éviter le flickering, et peut-être faire du "scrolling" afin de fluidifier et éviter des chargements...

Choses importantes : voici le fichier curl.pbi, qui remplace le fichier ReceiveHTTPToMemory.pbi. C'est maintenant un module. Je pensais que ça permettrait de le threader, mais il semble que non (?)

curl.pbi

Code : Tout sélectionner

DeclareModule curl
  Declare ReceiveHTTPToMemory(URL$, ProxyURL$="", ProxyPort$="", ProxyUser$="", ProxyPassword$="")
EndDeclareModule

Module curl
  
  IncludeFile "libcurl.pbi" ; https://github.com/deseven/pbsamples/tree/master/crossplatform/libcurl
  
  EnableExplicit
  
  CompilerIf #PB_Compiler_IsMainFile
    EnableExplicit
  CompilerEndIf
  
  Global *ReceiveHTTPToMemoryBuffer, ReceiveHTTPToMemoryBufferPtr.i
  
  ProcedureC ReceiveHTTPWriteToMemoryFunction(*ptr, Size.i, NMemB.i, *Stream)
    
    Protected SizeProper.i  = Size & 255
    Protected NMemBProper.i = NMemB
    
    If *ReceiveHTTPToMemoryBuffer = 0
      *ReceiveHTTPToMemoryBuffer = AllocateMemory(SizeProper * NMemBProper)
      If *ReceiveHTTPToMemoryBuffer = 0
        Debug "Problem allocating memory"
        End
      EndIf
    Else
      *ReceiveHTTPToMemoryBuffer = ReAllocateMemory(*ReceiveHTTPToMemoryBuffer, MemorySize(*ReceiveHTTPToMemoryBuffer) + SizeProper * NMemBProper)
      If *ReceiveHTTPToMemoryBuffer = 0
        Debug "Problem reallocating memory"
        End
      EndIf  
    EndIf
    
    CopyMemory(*ptr, *ReceiveHTTPToMemoryBuffer + ReceiveHTTPToMemoryBufferPtr, SizeProper * NMemBProper)
    ReceiveHTTPToMemoryBufferPtr + SizeProper * NMemBProper
    
    ProcedureReturn SizeProper * NMemBProper
    
  EndProcedure
  
  Procedure.i ReceiveHTTPToMemory(URL$, ProxyURL$="", ProxyPort$="", ProxyUser$="", ProxyPassword$="")
    
    Protected *Buffer, curl.i, Timeout.i, res.i
    
    ;Debug "ReceiveHTTPToMemory" + URL$ + ProxyURL$ + ProxyPort$ + ProxyUser$ + ProxyPassword$
    
    If Len(URL$)
      
      curl  = curl_easy_init()
      If curl
        
        Timeout = 3
        
        curl_easy_setopt(curl, #CURLOPT_URL, str2curl(URL$))
        curl_easy_setopt(curl, #CURLOPT_SSL_VERIFYPEER, 0)
        curl_easy_setopt(curl, #CURLOPT_SSL_VERIFYHOST, 0)
        curl_easy_setopt(curl, #CURLOPT_HEADER, 0)      
        curl_easy_setopt(curl, #CURLOPT_TIMEOUT, Timeout)
        
        If Len(ProxyURL$)
          ;curl_easy_setopt(curl, #CURLOPT_HTTPPROXYTUNNEL, #True)
          If Len(ProxyPort$)
            ProxyURL$ + ":" + ProxyPort$
          EndIf
          Debug ProxyURL$
          curl_easy_setopt(curl, #CURLOPT_PROXY, str2curl(ProxyURL$))
          If Len(ProxyUser$)
            If Len(ProxyPassword$)
              ProxyUser$ + ":" + ProxyPassword$
            EndIf
            ;Debug ProxyUser$
            curl_easy_setopt(curl, #CURLOPT_PROXYUSERPWD, str2curl(ProxyUser$))
          EndIf
        EndIf
        
        curl_easy_setopt(curl, #CURLOPT_WRITEFUNCTION, @ReceiveHTTPWriteToMemoryFunction())
        res = curl_easy_perform(curl)
        
        If res = #CURLE_OK
          *Buffer = AllocateMemory(ReceiveHTTPToMemoryBufferPtr)
          If *Buffer
            CopyMemory(*ReceiveHTTPToMemoryBuffer, *Buffer, ReceiveHTTPToMemoryBufferPtr)
            FreeMemory(*ReceiveHTTPToMemoryBuffer)
            *ReceiveHTTPToMemoryBuffer = #Null
            ReceiveHTTPToMemoryBufferPtr = 0
          Else
            Debug "Problem allocating buffer"         
          EndIf        
          ;curl_easy_cleanup(curl) ;Was its original place but moved below as it seems more logical to me.
        Else
          Debug "CURL NOT OK"
        EndIf
        
        curl_easy_cleanup(curl)
        
      Else
        Debug "Can't Init CURL"
      EndIf
      
    EndIf
    
    Debug "Curl Buffer : " + Str(*Buffer)
    
    ProcedureReturn *Buffer
    
  EndProcedure
  
EndModule

CompilerIf #PB_Compiler_IsMainFile
  
  Define *Buffer, ProxyURL$, ProxyPort$, ProxyUser$, ProxyPass$
  
  InitNetwork()
  
  ProxyURL$  = InputRequester("ProxyServer", "Do you use a Proxy Server? Then enter the full url:", "")
  ProxyPort$ = InputRequester("ProxyPort"  , "Do you use a specific port? Then enter it", "")
  ProxyUser$ = InputRequester("ProxyUser"  , "Do you use a user name? Then enter it", "")
  ProxyPass$ = InputRequester("ProxyPass"  , "Do you use a password? Then enter it:", "")
  
  *Buffer = curl::ReceiveHTTPToMemory("http://www.purebasic.fr/english/index.php", ProxyURL$, ProxyPort$, ProxyUser$, ProxyPass$)
  If *Buffer
    ShowMemoryViewer(*Buffer, MemorySize(*Buffer))
    FreeMemory(*Buffer)
  Else
    MessageRequester("Erreur", "Problem while loading", #PB_MessageRequester_Ok )
  EndIf
  
CompilerEndIf
Ne pas oublier le fichier libcurl.pbi d'infratec qui contient les constantes, à prendre sur https://github.com/deseven/pbsamples/tr ... rm/libcurl

Voici le code

Code : Tout sélectionner

;************************************************************** 
; Program:           OSM (OpenStreetMap Module) 
; Author:            Thyphoon And Djes
; Date:              Mai 17, 2016
; License:           Free, unrestricted, credit appreciated 
;                    but not required.
; Note:              Please share improvement !
; Thanks:            Progi1984, 
;************************************************************** 

CompilerIf #PB_Compiler_Thread = #False
  MessageRequester("Warning !!","You must enable ThreadSafe support in compiler options",#PB_MessageRequester_Ok )
  End
CompilerEndIf 

EnableExplicit

InitNetwork()
UsePNGImageDecoder()
UsePNGImageEncoder()

IncludeFile("curl.pbi")

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 Draw()
  Declare SetZoom(Zoom.i, mode.i = #PB_Relative)
  Declare LoadGpxFile(file.s);  
EndDeclareModule

Module OSM 
  
  EnableExplicit
  
  Structure Location
    Longitude.d
    Latitude.d
  EndStructure
  
  ;- Tile Structure
  Structure Tile
    x.d
    y.d
    OSMTileX.i
    OSMTileY.i
    OSMZoom.i
    nImage.i
    LoadingSemaphore.i
    GetImageThread.i
    DrawTileThread.i
    Quit.i
  EndStructure
  
  Structure TileThread
    LoadingSemaphore.i
    GetImageThread.i
    DrawTileThread.i
    *Tile.Tile
  EndStructure
  
  Structure Pixel
    x.i
    y.i
  EndStructure
  
  Structure ImgMemCach
    nImage.i
    Zoom.i
    XTile.i
    YTile.i
  EndStructure
  
  Structure TileMemCach
    List Image.ImgMemCach()
    Mutex.i
    Semaphore.i
  EndStructure
  
  ;-OSM Structure
  Structure OSM
    Gadget.i                                ; Canvas Gadget Id 
    
    TargetLocation.Location                 ; Latitude and Longitude from focus point
    TargetTile.Tile                         ; Focus Tile coord
    
    Position.Pixel                          ; Actual focus Point coords in pixels
    DeltaX.i
    DeltaY.i
    MoveStartingPoint.Pixel                       ; Start mouse position coords when dragging the map
    
    ServerURL.s                             ; Web URL ex: http://tile.openstreetmap.org/
    ZoomMin.i                               ; Min Zoom supported by server
    ZoomMax.i                               ; Max Zoom supported by server
    Zoom.i                                  ; Current zoom
    TileSize.i                              ; Tile size downloaded on the server ex : 256
    
    HDDCachePath.S                          ; Path where to load and save tiles downloaded from server
    MemCache.TileMemCach                    ; Image in memory cache
    List MapImageIndex.ImgMemCach()         ; Index from MemCache\Image() to construct map
    
    DrawingThread.i
    DrawingMutex.i
    CurlMutex.i                             ;seems that I can't thread curl ! :(((((
    List TilesThreads.TileThread()
    
    MapImageMutex.i                         ; Mutex to lock
    
    List track.Location()                   ;to display a GPX track
    
  EndStructure
  
  Global OSM.OSM
  
  Procedure InitOSM()
    OSM\HDDCachePath = GetTemporaryDirectory()
    OSM\ServerURL = "http://tile.openstreetmap.org/"
    OSM\ZoomMin = 0
    OSM\ZoomMax = 18
    OSM\MoveStartingPoint\x = - 1
    OSM\TileSize = 256
    OSM\MemCache\Mutex = CreateMutex()
    OSM\DrawingMutex = CreateMutex()
    OSM\CurlMutex = CreateMutex()
    
    Global Proxy = #True
    
    If proxy
      Global ProxyURL$  = InputRequester("ProxyServer", "Do you use a Proxy Server? Then enter the full url:", "")
      Global ProxyPort$ = InputRequester("ProxyPort"  , "Do you use a specific port? Then enter it", "")
      Global ProxyUser$ = InputRequester("ProxyUser"  , "Do you use a user name? Then enter it", "")
      Global ProxyPassword$ = InputRequester("ProxyPass"  , "Do you use a password? Then enter it:", "")
    EndIf
    
  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)
    Protected 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
    Debug "Latitude : " + StrD(*Location\Latitude) + " ; Longitude : " + StrD(*Location\Longitude)
    Debug "Tile X : " + Str(*Tile\x) + " ; Tile Y : " + Str(*Tile\y)
  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 getPixelCoorfromLocation(*Location.Location, *Pixel.Pixel) ; TODO to Optimize 
    Protected mapWidth.l    = Pow(2,OSM\Zoom+8)
    Protected mapHeight.l   = Pow(2,OSM\Zoom+8)
    Protected x1.l,y1.l
    
    Protected deltaX = OSM\Position\x - Int(OSM\TargetTile\x) * OSM\TileSize  ;Get the position into the tile
    Protected deltaY = OSM\Position\y - Int(OSM\TargetTile\y) * OSM\TileSize
    
    ; get x value
    x1 = (*Location\Longitude+180)*(mapWidth/360)
    ; convert from degrees To radians
    Protected latRad.d = *Location\Latitude*#PI/180;
    
    Protected mercN.d = Log(Tan((#PI/4)+(latRad/2)));
    y1     = (mapHeight/2)-(mapWidth*mercN/(2*#PI)) ;
    
    Protected x2.l, y2.l
    ; get x value
    x2 = (OSM\TargetLocation\Longitude+180)*(mapWidth/360)
    ; convert from degrees To radians
    latRad = OSM\TargetLocation\Latitude*#PI/180;
                                                ; get y value
    mercN = Log(Tan((#PI/4)+(latRad/2)))        ;
    y2     = (mapHeight/2)-(mapWidth*mercN/(2*#PI));
    
    *Pixel\x=GadgetWidth(OSM\Gadget)/2  - (x2-x1) + deltaX
    *Pixel\y=GadgetHeight(OSM\Gadget)/2 - (y2-y1) + deltaY
  EndProcedure
  
  Procedure LoadGpxFile(file.s)
    If LoadXML(0, file.s)
      Protected Message.s
      If XMLStatus(0) <> #PB_XML_Success
        Message = "Error in the XML file:" + Chr(13)
        Message + "Message: " + XMLError(0) + Chr(13)
        Message + "Line: " + Str(XMLErrorLine(0)) + "   Character: " + Str(XMLErrorPosition(0))
        MessageRequester("Error", Message)
      EndIf
      Protected *MainNode,*subNode,*child,child.l
      *MainNode=MainXMLNode(0)
      *MainNode=XMLNodeFromPath(*MainNode,"/gpx/trk/trkseg")
      ClearList(OSM\track())
      For child = 1 To XMLChildCount(*MainNode)
        *child = ChildXMLNode(*MainNode, child)
        AddElement(OSM\track())
        If ExamineXMLAttributes(*child)
          While NextXMLAttribute(*child)
            Select XMLAttributeName(*child)
              Case "lat"
                OSM\track()\Latitude=ValD(XMLAttributeValue(*child))
              Case "lon"
                OSM\track()\Longitude=ValD(XMLAttributeValue(*child))
            EndSelect
          Wend
        EndIf
      Next 
    EndIf
  EndProcedure
  
  Procedure AddTileToMemCache(Zoom.i, XTile.i, YTile.i, nImage.i)
    
    Protected Index.i
    
    If IsImage(nImage)
      Debug "Adding tile " + Str(nImage) + " to mem cache"
      LockMutex(OSM\MemCache\Mutex)
      AddElement(OSM\MemCache\Image())
      OSM\MemCache\Image()\xTile = XTile
      OSM\MemCache\Image()\yTile = YTile
      OSM\MemCache\Image()\Zoom = Zoom
      OSM\MemCache\Image()\nImage = nImage  
      UnlockMutex(OSM\MemCache\Mutex)
    Else
      Debug "Tile not added to mem cache"
    EndIf 
    
  EndProcedure
  
  Procedure.i GetTileFromMem(Zoom.i, XTile.i, YTile.i)
    
    Protected nImage.i
    
    Debug "Check if we have this image in memory"
    
    LockMutex(OSM\MemCache\Mutex)    
    ForEach OSM\MemCache\Image()
      If Zoom = OSM\MemCache\Image()\Zoom And OSM\MemCache\Image()\xTile = XTile And OSM\MemCache\Image()\yTile = YTile
        nImage = OSM\MemCache\Image()\nImage
        Debug "Load from MEM Tile X : " + Str(XTile) + " ; Tile Y : " + Str(YTile) + " nImage:" + Str(nImage)
        Break;
      ElseIf Zoom<>OSM\MemCache\Image()\Zoom
        ;        DeleteElement(OSM\MemCache\Image())
      EndIf 
    Next 
    UnlockMutex(OSM\MemCache\Mutex)
    
    ProcedureReturn nImage
    
  EndProcedure
  
  Procedure.i GetTileFromHDD(Zoom.i, XTile.i, YTile.i)
    
    Protected nImage.i
    Protected CacheFile.s = "OSM_" + Str(Zoom) + "_" + Str(XTile) + "_" + Str(YTile) + ".png"
    
    Debug "Check if we have this image on HDD"
    
    If FileSize(OSM\HDDCachePath + cacheFile) > 0
      nImage = LoadImage(#PB_Any, OSM\HDDCachePath + CacheFile)
      
      If IsImage(nImage)
        Debug "Load from HDD Tile " + CacheFile
        AddTileToMemCache(Zoom, XTile, YTile, nImage)
        ProcedureReturn nImage
      EndIf 
      
    EndIf

    ProcedureReturn 0
    
  EndProcedure
  
  Procedure.i GetTileFromWeb(Zoom.i, XTile.i, YTile.i)
    
    Protected *Buffer
    Protected nImage.i
    
    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"
    
    Debug "Check if we have this image on Web"
    
    If Proxy
      LockMutex(OSM\CurlMutex)
      *Buffer = curl::ReceiveHTTPToMemory(TileURL, ProxyURL$, ProxyPort$, ProxyUser$, ProxyPassword$)
      UnlockMutex(OSM\CurlMutex)
    Else
      *Buffer = ReceiveHTTPMemory(TileURL)  ;TODO to thread by using #PB_HTTP_Asynchronous
    EndIf
    Debug "Image buffer " + Str(*Buffer)
    
    If *Buffer
      nImage = CatchImage(#PB_Any, *Buffer, MemorySize(*Buffer))
      
      If nImage > 0
        Debug "Loaded from web " + TileURL + " as Tile nb " + nImage
        AddTileToMemCache(Zoom, XTile, YTile, nImage)
        SaveImage(nImage, OSM\HDDCachePath + CacheFile, #PB_ImagePlugin_PNG)
        FreeMemory(*Buffer)
      Else
        Debug "Can't catch image " + TileURL
        ;ShowMemoryViewer(*Buffer, MemorySize(*Buffer))
      EndIf
    Else
      Debug "Problem loading from web " + TileURL  
    EndIf      
    
    ProcedureReturn nImage
    
  EndProcedure
  
  Procedure.i ThreadGetImage(*Tile.Tile)
    
    LockMutex(OSM\LoadingMutex)
    Protected nImage = GetTileFromMem(*Tile\OSMZoom, *Tile\OSMTileX, *Tile\OSMTileY)
    If nImage = 0 And *Tile\Quit = 0
      nImage = GetTileFromHDD(*Tile\OSMZoom, *Tile\OSMTileX, *Tile\OSMTileY)
      If nImage = 0 And *Tile\Quit = 0
        nImage = GetTileFromWeb(*Tile\OSMZoom, *Tile\OSMTileX, *Tile\OSMTileY)
        If nImage = 0
          Debug "Error ThreadGetImage procedure : can't load this tile - Zoom:" + Str(*Tile\OSMZoom) + " X:" + Str(*Tile\OSMTileX) + " Y:" + Str(*Tile\OSMTileY)
          nImage = -1
        EndIf
      EndIf
    EndIf
    UnlockMutex(OSM\LoadingMutex)
    
    *Tile\nImage = nImage
    
    ;The loading is ended, draw can occur
    SignalSemaphore(*Tile\LoadingSemaphore)  
    Debug " Signal semaphore " + Str(*Tile\LoadingSemaphore) + " loading image " + Str(nImage) + " ended" 
    
  EndProcedure
  
  Procedure ThreadDrawTile(*Tile.Tile)
    
    Protected x = *Tile\x - OSM\DeltaX
    Protected y = *Tile\y - OSM\DeltaY
    
    Debug "  Thread drawing tile nb " + Str(*Tile\DrawTileThread) + " X : " + Str(x) + " Y : " + Str(y)
    
;         If *Tile\nImage = 0 
;           LockMutex(OSM\DrawingMutex)
;           ;Waiting image
;           StartDrawing(CanvasOutput(OSM\Gadget))  
;           Box(x, y, OSM\TileSize, OSM\TileSize, RGB($EE, $EE, $EE))
;           Line(x, y, OSM\TileSize, OSM\TileSize, RGB($AA, $AA, $AA))
;           Line(x, y + OSM\TileSize, OSM\TileSize, -OSM\TileSize, RGB($AA, $AA, $AA))
;           DrawText(x + OSM\TileSize / 2 - TextWidth("Loading")/2, y + OSM\TileSize / 2 - TextHeight("Loading") / 2, "Loading", RGB($AA, $AA, $AA), RGB($EE, $EE, $EE))
;           UnlockMutex(OSM\DrawingMutex)
;           StopDrawing()
;         EndIf
    
    ;Wait for the loading thread
    Debug " Waiting loading semaphore " + Str(*Tile\LoadingSemaphore)
    WaitSemaphore(*Tile\LoadingSemaphore)
    Debug " ---> Waiting nImage " + Str(*Tile\nImage) + " semaphore ended" 
    
    LockMutex(OSM\DrawingMutex)
    If *Tile\Quit = 0 ;Quit the thread before drawing
      StartDrawing(CanvasOutput(OSM\Gadget))  
      If IsImage(*Tile\nImage)          
        DrawImage(ImageID(*Tile\nImage), x, y)
        DrawText( x, y, Str(x) + ", " + Str(y))
      Else
        Box(x, y, OSM\TileSize, OSM\TileSize, RGB($EE, $EE, $EE))
        Line(x, y, OSM\TileSize, OSM\TileSize, RGB($AA, $AA, $AA))
        Line(x, y + OSM\TileSize, OSM\TileSize, -OSM\TileSize, RGB($AA, $AA, $AA))
        DrawText(x + OSM\TileSize / 2 - TextWidth("Error")/2, y + OSM\TileSize / 2 - TextHeight("Error") / 2, "Error", RGB($FF, $00, $00), RGB($EE, $EE, $EE))
      EndIf
      StopDrawing()
    EndIf
    UnlockMutex(OSM\DrawingMutex)
    
    FreeSemaphore(*Tile\LoadingSemaphore)
    FreeMemory(*Tile)
    
  EndProcedure
  
  Procedure StopDrawingRunningThreads()
    
    ;Stop all the drawing running threads
    ForEach OSM\TilesThreads()
      If IsThread(OSM\TilesThreads()\DrawTileThread)
        Debug " Stopping drawing thread " + Str(OSM\TilesThreads()\DrawTileThread)
        OSM\TilesThreads()\Tile\Quit = #True
      EndIf         
    Next
    ClearList(OSM\TilesThreads())
    
  EndProcedure
  
  Procedure DrawTiles()
    
    Protected x.i, y.i
    
    Protected tx = Int(OSM\TargetTile\x)  ;Don't forget the Int() !
    Protected ty = Int(OSM\TargetTile\y)
    
    Protected CenterX = GadgetWidth(OSM\Gadget) / 2
    Protected CenterY = GadgetHeight(OSM\Gadget) / 2
    
    Protected nx = CenterX / OSM\TileSize ;How many tiles around the point
    Protected ny = CenterY / OSM\TileSize
    
    Debug "Drawing tiles"
    
    For y = - ny To ny
      For x = - nx To nx
        
        Protected *NewTile.Tile = AllocateMemory(SizeOf(Tile))
        If *NewTile
          With *NewTile
            
            \x = CenterX + x * OSM\TileSize
            \y = CenterY + y * OSM\TileSize
            \OSMTileX = tx + x
            \OSMTileY = ty + y
            \OSMZoom  = OSM\Zoom
            \nImage = 0
            \Quit = 0
            ;This semaphore permits to the drawing thread to wait for an image
            \LoadingSemaphore = CreateSemaphore()
            If \LoadingSemaphore = 0
              Debug "Can't create loading semaphore"
            EndIf
            ;Check if the image exists, if not, load it in the background
            \GetImageThread = CreateThread(@ThreadGetImage(), *NewTile)
            Debug " Creating get image thread nb " + Str(\GetImageThread)
            \DrawTileThread = CreateThread(@ThreadDrawTile(), *NewTile)
            Debug " Creating tile drawing thread " + Str(\DrawTileThread)
            
            AddElement(OSM\TilesThreads())
            OSM\TilesThreads()\DrawTileThread = \DrawTileThread
            OSM\TilesThreads()\LoadingSemaphore = \LoadingSemaphore
            OSM\TilesThreads()\GetImageThread = \GetImageThread
            OSM\TilesThreads()\Tile = *NewTile
          EndWith  
          
        Else
          Debug" Error, can't create a new tile."
          Break 2
        EndIf 
      Next
    Next
    
    
  EndProcedure
  
  Procedure DrawTrack()
    Protected Pixel.Pixel
    Protected Location.Location
    Protected n.i = 0, x.i, y.i
    
    StartDrawing(CanvasOutput(OSM\Gadget))
    ForEach OSM\track()
      n=n+1
      If @OSM\TargetLocation\Latitude<>0 And  @OSM\TargetLocation\Longitude<>0
        getPixelCoorfromLocation(@OSM\track(),@Pixel)
        x=Pixel\x
        y=Pixel\y
        If x>0 And y>0 And x<GadgetWidth(OSM\Gadget) And y<GadgetHeight(OSM\Gadget)
          Circle(x,y,2,#Green)
        EndIf
      EndIf 
    Next
    StopDrawing()
    
  EndProcedure  
  
  Procedure Draw()
    
    Protected CenterX = GadgetWidth(OSM\Gadget) / 2
    Protected CenterY = GadgetHeight(OSM\Gadget) / 2
    
    LockMutex(OSM\DrawingMutex)
    StartDrawing(CanvasOutput(OSM\Gadget))
    ;Box(0, 0, GadgetWidth(OSM\Gadget), GadgetHeight(OSM\Gadget), RGB(255, 255, 255))
    StopDrawing()
    UnlockMutex(OSM\DrawingMutex)
    DrawTiles()
    LockMutex(OSM\DrawingMutex)
    StartDrawing(CanvasOutput(OSM\Gadget))
    ;DrawTrack()
    Circle(CenterX, CenterY, 5, #Red)
    StopDrawing()
    UnlockMutex(OSM\DrawingMutex)
    
  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)
    OSM\Position\x = OSM\TargetTile\x * OSM\TileSize ;Convert X, Y in tile.decimal into real pixels
    OSM\Position\y = OSM\TargetTile\y * OSM\TileSize
    OSM\DeltaX = OSM\Position\x - Int(OSM\Position\x / OSM\TileSize) * OSM\TileSize
    OSM\DeltaY = OSM\Position\y - Int(OSM\Position\y / OSM\TileSize) * OSM\TileSize  
  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)
    OSM\Position\x = OSM\TargetTile\x * OSM\TileSize ;Convert X, Y in tile.decimal into real pixels
    OSM\Position\y = OSM\TargetTile\y * OSM\TileSize
    OSM\DeltaX = OSM\Position\x - Int(OSM\Position\x / OSM\TileSize) * OSM\TileSize
    OSM\DeltaY = OSM\Position\y - Int(OSM\Position\y / OSM\TileSize) * OSM\TileSize
    Draw()
  EndProcedure
  
  Procedure Event(Event.l)
    Protected Gadget.i
    Protected MouseX.i, MouseY.i
    Protected OldX.i, OldY.i
    Protected TileX.d, TileY.d
    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\MoveStartingPoint\x = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                  OSM\MoveStartingPoint\y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) 
                Case #PB_EventType_MouseMove
                  If OSM\MoveStartingPoint\x <> - 1
                    ;Need a refresh
                    MouseX = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) - OSM\MoveStartingPoint\x
                    MouseY = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) - OSM\MoveStartingPoint\y
                    ;Old move values 
                    OldX = OSM\Position\x 
                    OldY = OSM\Position\y
                    ;New move values
                    OSM\Position\x - MouseX
                    OSM\Position\y - MouseY
                    ;OSM tile position in tile.decimal
                    TileX = OSM\Position\x / OSM\TileSize
                    TileY = OSM\Position\y / OSM\TileSize
                    ;Pixel shift
                    OSM\DeltaX = OSM\Position\x - Int(TileX) * OSM\TileSize
                    OSM\DeltaY = OSM\Position\y - Int(TileY) * OSM\TileSize
                    ;Moved to a new tile ?
                    If (Int(OSM\Position\x / OSM\TileSize)) <> (Int(OldX / OSM\TileSize)) Or (Int(OSM\Position\y / OSM\TileSize)) <> (Int(OldY / OSM\TileSize)) 
                      Debug "--- New tile"
                      OSM\TargetTile\x = TileX
                      OSM\TargetTile\y = TileY
                      Debug "OSM\Position\x " + Str(OSM\Position\x) + " ; OSM\Position\y " + Str(OSM\Position\y) 
                      XY2LatLon(@OSM\TargetTile, @OSM\TargetLocation)
                      Debug "OSM\TargetTile\x " + StrD(OSM\TargetTile\x) + " ; OSM\TargetTile\y "  + StrD(OSM\TargetTile\y) 
                    EndIf
                    StopDrawingRunningThreads()
                    Draw()
                    OSM\MoveStartingPoint\x = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                    OSM\MoveStartingPoint\y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY)                   
                  EndIf
                Case #PB_EventType_LeftButtonUp
                  OSM\MoveStartingPoint\x = - 1
                  OSM\TargetTile\x = OSM\Position\x / OSM\TileSize
                  OSM\TargetTile\y = OSM\Position\y / OSM\TileSize
                  Debug "OSM\Position\x " + Str(OSM\Position\x) + " ; OSM\Position\y " + Str(OSM\Position\y) 
                  XY2LatLon(@OSM\TargetTile, @OSM\TargetLocation)
                  ;Draw()
                  Debug "OSM\TargetTile\x " + StrD(OSM\TargetTile\x) + " ; OSM\TargetTile\y "  + StrD(OSM\TargetTile\y) 
                  ;SetGadgetText(#String_1, StrD(OSM\TargetLocation\Latitude))
                  ;SetGadgetText(#String_0, StrD(OSM\TargetLocation\Longitude))
              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, 17)
  OSM::Draw()
  ;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
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: OpenStreetMap dans un Canvas

Message par Thyphoon »

super bon boulot ! :mrgreen:

Effectivement peut être que dessiner sur une image a appliquer ensuite dans le canvas serait une bonne chose ! :D

Par-contre je me pose la question ... si tu utilises la lib curl dans un module .. si tu veux l'utiliser dans un autre module il faut l'inclure aussi dans l'autre module. 8O
J'ai eu aucune reponse a propos de la gestion du proxy pour les commandes natives ... très chiant . :(
J'avais trouvé un Hook mais ça ne marche pas dans un module pour remplacer la fonction d'origine ...
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: OpenStreetMap dans un Canvas

Message par djes »

Thyphoon a écrit :super bon boulot ! :mrgreen:

Effectivement peut être que dessiner sur une image a appliquer ensuite dans le canvas serait une bonne chose ! :D

Par-contre je me pose la question ... si tu utilises la lib curl dans un module .. si tu veux l'utiliser dans un autre module il faut l'inclure aussi dans l'autre module. 8O
J'ai eu aucune reponse a propos de la gestion du proxy pour les commandes natives ... très chiant . :(
J'avais trouvé un Hook mais ça ne marche pas dans un module pour remplacer la fonction d'origine ...
Merci !
J'ai modifié le code ci-dessus, histoire d'éliminer un vilain bug. Oui, pour le module curl, je ne sais pas trop. Je l'avais mis dans un module pour qu'il soit réentrant (threadable), mais ça n'a pas l'air de fonctionner.
Il faudra aussi penser à la gestion du cache, mémoire et disque, peut-être en fonction de l'heure du dernier usage et de l'espace consommé...? Un thread à part, ça serait peut-être bien.
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: OpenStreetMap dans un Canvas

Message par djes »

Yop, voici la dernière version, à tester (j'ai commenté les lignes pour l'utilisation du proxy)
J'ai vu un petit bug de calcul entre deux tuiles ou lors d'un saut rapide, mais je ne l'ai pas encore repéré.

Edit: intégration de curl, et création d'un fichier de préférences pour faciliter les échanges.

Code : Tout sélectionner

;************************************************************** 
; Program:           OSM (OpenStreetMap Module) 
; Author:            Thyphoon And Djes
; Date:              Mai 17, 2016
; License:           Free, unrestricted, credit appreciated 
;                    but not required.
; Note:              Please share improvement !
; Thanks:            Progi1984, 
;************************************************************** 

CompilerIf #PB_Compiler_Thread = #False
  MessageRequester("Warning !!","You must enable ThreadSafe support in compiler options",#PB_MessageRequester_Ok )
  End
CompilerEndIf 

EnableExplicit

InitNetwork()
UsePNGImageDecoder()
UsePNGImageEncoder()

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 DrawingThread(Null)
  Declare SetZoom(Zoom.i, mode.i = #PB_Relative)
  Declare LoadGpxFile(file.s);  
EndDeclareModule

Module OSM 
  
  EnableExplicit
  
  Structure Location
    Longitude.d
    Latitude.d
  EndStructure
  
  ;- Tile Structure
  Structure Tile
    x.d
    y.d
    OSMTileX.i
    OSMTileY.i
    OSMZoom.i
    nImage.i
    GetImageThread.i
  EndStructure
  
  Structure TileThread
    GetImageThread.i
    *Tile.Tile
  EndStructure
  
  Structure Pixel
    x.i
    y.i
  EndStructure
  
  Structure ImgMemCach
    nImage.i
    Zoom.i
    XTile.i
    YTile.i
    Usage.i
  EndStructure
  
  Structure TileMemCach
    List Image.ImgMemCach()
    Mutex.i
    Semaphore.i
  EndStructure
  
  ;-OSM Structure
  Structure OSM
    Gadget.i                                ; Canvas Gadget Id 
    
    TargetLocation.Location                 ; Latitude and Longitude from focus point
    TargetTile.Tile                         ; Focus Tile coord
    
    Position.Pixel                          ; Actual focus Point coords in pixels
    DeltaX.i
    DeltaY.i
    MoveStartingPoint.Pixel                       ; Start mouse position coords when dragging the map
    
    ServerURL.s                             ; Web URL ex: http://tile.openstreetmap.org/
    ZoomMin.i                               ; Min Zoom supported by server
    ZoomMax.i                               ; Max Zoom supported by server
    Zoom.i                                  ; Current zoom
    TileSize.i                              ; Tile size downloaded on the server ex : 256
    
    HDDCachePath.S                          ; Path where to load and save tiles downloaded from server
    MemCache.TileMemCach                    ; Image in memory cache
    List MapImageIndex.ImgMemCach()         ; Index from MemCache\Image() to construct map
    
    DrawingThreadMutex.i                    ;Only one main drawing thread
    EmergencyQuit.i
    Dirty.i                                 ;To signal that drawing need a refresh
    LoadingMutex.i
    DrawingMutex.i
    CurlMutex.i                             ;seems that I can't thread curl ! :(((((
    List TilesThreads.TileThread()
    
    MapImageMutex.i                         ; Mutex to lock
    
    List track.Location()                   ;to display a GPX track
    
  EndStructure
  
  Global OSM.OSM, Null.i
  
  Procedure InitOSM()
    
    Protected Result.i
    
    OSM\HDDCachePath = GetTemporaryDirectory()
    OSM\ServerURL = "http://tile.openstreetmap.org/"
    OSM\ZoomMin = 0
    OSM\ZoomMax = 18
    OSM\MoveStartingPoint\x = - 1
    OSM\TileSize = 256
    OSM\MemCache\Mutex = CreateMutex()
    OSM\LoadingMutex = CreateMutex()
    OSM\DrawingMutex = CreateMutex()
    OSM\CurlMutex = CreateMutex()
    OSM\DrawingThreadMutex = CreateMutex()
    OSM\EmergencyQuit = #False
    OSM\Dirty = #False
    
    ;-*** PROXY
    
    Global Proxy = #True
    
;- => Use this to customise your preferences    
;     Result = CreatePreferences(GetCurrentDirectory() + "OSM.prefs")
;     If Proxy
;       PreferenceGroup("PROXY")
;       WritePreferenceString("ProxyURL", "myproxy.fr")
;       WritePreferenceString("ProxyPort", "myproxyport")
;       WritePreferenceString("ProxyUser", "myproxyname")     
;     EndIf
;     If Result 
;       ClosePreferences()    
;     EndIf
    
    Result = OpenPreferences(GetCurrentDirectory() + "OSM.prefs")
    If Proxy
      PreferenceGroup("PROXY")       
      Global ProxyURL$  = ReadPreferenceString("ProxyURL", "")  ;InputRequester("ProxyServer", "Do you use a Proxy Server? Then enter the full url:", "")
      Global ProxyPort$ = ReadPreferenceString("ProxyPort", "") ;InputRequester("ProxyPort"  , "Do you use a specific port? Then enter it", "")
      Global ProxyUser$ = ReadPreferenceString("ProxyUser", "") ;InputRequester("ProxyUser"  , "Do you use a user name? Then enter it", "")
      Global ProxyPassword$ = InputRequester("ProxyPass"  , "Do you use a password ? Then enter it", "")
    EndIf  
    If Result
      ClosePreferences()
    EndIf   
    
  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
  
  ;- *** CURL specific ***
  
  Global *ReceiveHTTPToMemoryBuffer, ReceiveHTTPToMemoryBufferPtr.i, ReceivedData.s
  IncludeFile "libcurl.pbi" ; https://github.com/deseven/pbsamples/tree/master/crossplatform/libcurl
  
  ProcedureC ReceiveHTTPWriteToMemoryFunction(*ptr, Size.i, NMemB.i, *Stream)
    
    Protected SizeProper.i  = Size & 255
    Protected NMemBProper.i = NMemB
    
    If *ReceiveHTTPToMemoryBuffer = 0
      *ReceiveHTTPToMemoryBuffer = AllocateMemory(SizeProper * NMemBProper)
      If *ReceiveHTTPToMemoryBuffer = 0
        Debug "Problem allocating memory"
        End
      EndIf
    Else
      *ReceiveHTTPToMemoryBuffer = ReAllocateMemory(*ReceiveHTTPToMemoryBuffer, MemorySize(*ReceiveHTTPToMemoryBuffer) + SizeProper * NMemBProper)
      If *ReceiveHTTPToMemoryBuffer = 0
        Debug "Problem reallocating memory"
        End
      EndIf  
    EndIf
    
    CopyMemory(*ptr, *ReceiveHTTPToMemoryBuffer + ReceiveHTTPToMemoryBufferPtr, SizeProper * NMemBProper)
    ReceiveHTTPToMemoryBufferPtr + SizeProper * NMemBProper
    
    ProcedureReturn SizeProper * NMemBProper
    
  EndProcedure
  
  Procedure.i CurlReceiveHTTPToMemory(URL$, ProxyURL$="", ProxyPort$="", ProxyUser$="", ProxyPassword$="")
    
    Protected *Buffer, curl.i, Timeout.i, res.i
    
    ;Debug "ReceiveHTTPToMemory" + URL$ + ProxyURL$ + ProxyPort$ + ProxyUser$ + ProxyPassword$
    
    If Len(URL$)
      
      curl  = curl_easy_init()
      If curl
        
        Timeout = 3
        
        curl_easy_setopt(curl, #CURLOPT_URL, str2curl(URL$))
        curl_easy_setopt(curl, #CURLOPT_SSL_VERIFYPEER, 0)
        curl_easy_setopt(curl, #CURLOPT_SSL_VERIFYHOST, 0)
        curl_easy_setopt(curl, #CURLOPT_HEADER, 0)      
        curl_easy_setopt(curl, #CURLOPT_TIMEOUT, Timeout)
        
        If Len(ProxyURL$)
          ;curl_easy_setopt(curl, #CURLOPT_HTTPPROXYTUNNEL, #True)
          If Len(ProxyPort$)
            ProxyURL$ + ":" + ProxyPort$
          EndIf
          Debug ProxyURL$
          curl_easy_setopt(curl, #CURLOPT_PROXY, str2curl(ProxyURL$))
          If Len(ProxyUser$)
            If Len(ProxyPassword$)
              ProxyUser$ + ":" + ProxyPassword$
            EndIf
            ;Debug ProxyUser$
            curl_easy_setopt(curl, #CURLOPT_PROXYUSERPWD, str2curl(ProxyUser$))
          EndIf
        EndIf
        
        curl_easy_setopt(curl, #CURLOPT_WRITEFUNCTION, @ReceiveHTTPWriteToMemoryFunction())
        res = curl_easy_perform(curl)
        
        If res = #CURLE_OK
          *Buffer = AllocateMemory(ReceiveHTTPToMemoryBufferPtr)
          If *Buffer
            CopyMemory(*ReceiveHTTPToMemoryBuffer, *Buffer, ReceiveHTTPToMemoryBufferPtr)
            FreeMemory(*ReceiveHTTPToMemoryBuffer)
            *ReceiveHTTPToMemoryBuffer = #Null
            ReceiveHTTPToMemoryBufferPtr = 0
          Else
            Debug "Problem allocating buffer"         
          EndIf        
          ;curl_easy_cleanup(curl) ;Was its original place but moved below as it seems more logical to me.
        Else
          Debug "CURL NOT OK"
        EndIf
        
        curl_easy_cleanup(curl)
        
      Else
        Debug "Can't Init CURL"
      EndIf
      
    EndIf
    
    Debug "Curl Buffer : " + Str(*Buffer)
    
    ProcedureReturn *Buffer
    
  EndProcedure
  ;- ***
  
  Procedure LatLon2XY(*Location.Location, *Tile.Tile)
    Protected n.d = Pow(2.0, OSM\Zoom)
    Protected 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
    Debug "Latitude : " + StrD(*Location\Latitude) + " ; Longitude : " + StrD(*Location\Longitude)
    Debug "Tile X : " + Str(*Tile\x) + " ; Tile Y : " + Str(*Tile\y)
  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 getPixelCoorfromLocation(*Location.Location, *Pixel.Pixel) ; TODO to Optimize 
    Protected mapWidth.l    = Pow(2,OSM\Zoom+8)
    Protected mapHeight.l   = Pow(2,OSM\Zoom+8)
    Protected x1.l,y1.l
    
    Protected deltaX = OSM\Position\x - Int(OSM\TargetTile\x) * OSM\TileSize  ;Get the position into the tile
    Protected deltaY = OSM\Position\y - Int(OSM\TargetTile\y) * OSM\TileSize
    
    ; get x value
    x1 = (*Location\Longitude+180)*(mapWidth/360)
    ; convert from degrees To radians
    Protected latRad.d = *Location\Latitude*#PI/180;
    
    Protected mercN.d = Log(Tan((#PI/4)+(latRad/2)));
    y1     = (mapHeight/2)-(mapWidth*mercN/(2*#PI)) ;
    
    Protected x2.l, y2.l
    ; get x value
    x2 = (OSM\TargetLocation\Longitude+180)*(mapWidth/360)
    ; convert from degrees To radians
    latRad = OSM\TargetLocation\Latitude*#PI/180;
                                                ; get y value
    mercN = Log(Tan((#PI/4)+(latRad/2)))        ;
    y2     = (mapHeight/2)-(mapWidth*mercN/(2*#PI));
    
    *Pixel\x=GadgetWidth(OSM\Gadget)/2  - (x2-x1) + deltaX
    *Pixel\y=GadgetHeight(OSM\Gadget)/2 - (y2-y1) + deltaY
  EndProcedure
  
  Procedure LoadGpxFile(file.s)
    If LoadXML(0, file.s)
      Protected Message.s
      If XMLStatus(0) <> #PB_XML_Success
        Message = "Error in the XML file:" + Chr(13)
        Message + "Message: " + XMLError(0) + Chr(13)
        Message + "Line: " + Str(XMLErrorLine(0)) + "   Character: " + Str(XMLErrorPosition(0))
        MessageRequester("Error", Message)
      EndIf
      Protected *MainNode,*subNode,*child,child.l
      *MainNode=MainXMLNode(0)
      *MainNode=XMLNodeFromPath(*MainNode,"/gpx/trk/trkseg")
      ClearList(OSM\track())
      For child = 1 To XMLChildCount(*MainNode)
        *child = ChildXMLNode(*MainNode, child)
        AddElement(OSM\track())
        If ExamineXMLAttributes(*child)
          While NextXMLAttribute(*child)
            Select XMLAttributeName(*child)
              Case "lat"
                OSM\track()\Latitude=ValD(XMLAttributeValue(*child))
              Case "lon"
                OSM\track()\Longitude=ValD(XMLAttributeValue(*child))
            EndSelect
          Wend
        EndIf
      Next 
    EndIf
  EndProcedure
  
  Procedure.i GetTileFromMem(Zoom.i, XTile.i, YTile.i)
    
    Protected nImage.i = -1
    
    Debug "Check if we have this image in memory"
    
    LockMutex(OSM\LoadingMutex)   
    LockMutex(OSM\MemCache\Mutex)    
    ForEach OSM\MemCache\Image()
      If Zoom = OSM\MemCache\Image()\Zoom And OSM\MemCache\Image()\xTile = XTile And OSM\MemCache\Image()\yTile = YTile
        nImage = OSM\MemCache\Image()\nImage
        Debug "Load from MEM Tile X : " + Str(XTile) + " ; Tile Y : " + Str(YTile) + " nImage:" + Str(nImage)
        Break;
             ;ElseIf Zoom<>OSM\MemCache\Image()\Zoom
             ;        DeleteElement(OSM\MemCache\Image())
      EndIf 
    Next 
    UnlockMutex(OSM\MemCache\Mutex)
    UnlockMutex(OSM\LoadingMutex)   
    
    ProcedureReturn nImage
    
  EndProcedure
  
  Procedure.i GetTileFromHDD(Zoom.i, XTile.i, YTile.i)
    
    Protected nImage.i
    Protected CacheFile.s = "OSM_" + Str(Zoom) + "_" + Str(XTile) + "_" + Str(YTile) + ".png"
    
    Debug "Check if we have this image on HDD"
    
    If FileSize(OSM\HDDCachePath + cacheFile) > 0
      nImage = LoadImage(#PB_Any, OSM\HDDCachePath + CacheFile)
      
      If IsImage(nImage)
        Debug "Load from HDD Tile " + CacheFile
        ProcedureReturn nImage
      EndIf 
      
    EndIf
    
    ProcedureReturn -1
    
  EndProcedure
  
  Procedure.i GetTileFromWeb(Zoom.i, XTile.i, YTile.i)
    
    Protected *Buffer
    Protected nImage.i = -1
    
    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"
    
    Debug "Check if we have this image on Web"
    
    If Proxy
      LockMutex(OSM\CurlMutex)
      *Buffer = CurlReceiveHTTPToMemory(TileURL, ProxyURL$, ProxyPort$, ProxyUser$, ProxyPassword$)
      UnlockMutex(OSM\CurlMutex)
    Else
      *Buffer = ReceiveHTTPMemory(TileURL)  ;TODO to thread by using #PB_HTTP_Asynchronous
    EndIf
    Debug "Image buffer " + Str(*Buffer)
    
    If *Buffer
      LockMutex(OSM\LoadingMutex)
      nImage = CatchImage(#PB_Any, *Buffer, MemorySize(*Buffer))
      UnlockMutex(OSM\LoadingMutex)
      
      If IsImage(nImage)
        Debug "Load from web " + TileURL + " as Tile nb " + nImage
        LockMutex(OSM\LoadingMutex)
        SaveImage(nImage, OSM\HDDCachePath + CacheFile, #PB_ImagePlugin_PNG)
        UnlockMutex(OSM\LoadingMutex)
        FreeMemory(*Buffer)
      Else
        Debug "Can't catch image " + TileURL
        nImage = -1
        ;ShowMemoryViewer(*Buffer, MemorySize(*Buffer))
      EndIf
    Else
      Debug "Problem loading from web " + TileURL  
    EndIf      
    
    ProcedureReturn nImage
    
  EndProcedure
  
  Procedure GetImageThread(*Tile.Tile)
    
    Protected *CacheImagePtr
    Protected nImage.i = -1
    
    LockMutex(OSM\LoadingMutex)
    
    If OSM\EmergencyQuit = 0
      LockMutex(OSM\MemCache\Mutex) 
      *CacheImagePtr = AddElement(OSM\MemCache\Image())
      Debug " CacheImagePtr : " + Str(*CacheImagePtr)
      OSM\MemCache\Image()\xTile = *Tile\OSMTileX
      OSM\MemCache\Image()\yTile = *Tile\OSMTileY
      OSM\MemCache\Image()\Zoom = *Tile\OSMZoom
      OSM\MemCache\Image()\nImage = -1  ;By now, this tile is in "loading" state, for thread synchro
      UnlockMutex(OSM\MemCache\Mutex)
      nImage = GetTileFromHDD(*Tile\OSMZoom, *Tile\OSMTileX, *Tile\OSMTileY)
      If nImage = -1 And OSM\EmergencyQuit = 0
        nImage = GetTileFromWeb(*Tile\OSMZoom, *Tile\OSMTileX, *Tile\OSMTileY)
      EndIf
      LockMutex(OSM\MemCache\Mutex)
      If nImage <> -1 And OSM\EmergencyQuit = 0
        Debug "Adding tile " + Str(nImage) + " to mem cache"
        ;AddTileToMemCache(Zoom, XTile, YTile, nImage)
        OSM\MemCache\Image()\nImage = nImage
        Debug "Image nb " + Str(nImage) + " successfully added to mem cache"   
      Else
        Debug "Error GetImageThread procedure, tile not loaded - Zoom:" + Str(*Tile\OSMZoom) + " X:" + Str(*Tile\OSMTileX) + " Y:" + Str(*Tile\OSMTileY)
        DeleteElement(OSM\MemCache\Image())
        nImage = -1
      EndIf
      UnlockMutex(OSM\MemCache\Mutex)
    EndIf
    *Tile\nImage = nImage
    UnlockMutex(OSM\LoadingMutex)
    
  EndProcedure
  
  Procedure DrawTile(*Tile.Tile)
    
    Protected x = *Tile\x - OSM\DeltaX
    Protected y = *Tile\y - OSM\DeltaY
    
    Debug "  Drawing tile nb " + " X : " + Str(x) + " Y : " + Str(y)
    
    LockMutex(OSM\DrawingMutex)
    If OSM\EmergencyQuit = 0 ;Quit before drawing
      StartDrawing(CanvasOutput(OSM\Gadget))  
      If IsImage(*Tile\nImage)          
        DrawImage(ImageID(*Tile\nImage), x, y)
        DrawText( x, y, Str(x) + ", " + Str(y))
      Else
        Debug "Image missing"
        OSM\Dirty = #True ;Signal that this image is missing so we should have to redraw
      EndIf
      StopDrawing()
    EndIf
    UnlockMutex(OSM\DrawingMutex)
    
  EndProcedure
  
  Procedure DrawTiles()
    
    Protected x.i, y.i
    
    Protected tx = Int(OSM\TargetTile\x)  ;Don't forget the Int() !
    Protected ty = Int(OSM\TargetTile\y)
    
    Protected CenterX = GadgetWidth(OSM\Gadget) / 2
    Protected CenterY = GadgetHeight(OSM\Gadget) / 2
    
    Protected nx = CenterX / OSM\TileSize ;How many tiles around the point
    Protected ny = CenterY / OSM\TileSize
    
    Debug "Drawing tiles"
    
    For y = - ny To ny
      For x = - nx To nx
        
        If OSM\EmergencyQuit
          Break
        EndIf
        
        Protected *NewTile.Tile = AllocateMemory(SizeOf(Tile))
        If *NewTile
          With *NewTile
            
            \x = CenterX + x * OSM\TileSize
            \y = CenterY + y * OSM\TileSize
            \OSMTileX = tx + x
            \OSMTileY = ty + y
            \OSMZoom  = OSM\Zoom
            
            ;Check if the image exists, if not, load it in the background
            \nImage = GetTileFromMem(\OSMZoom, \OSMTileX, \OSMTileY)
            If \nImage = -1 
              \GetImageThread = CreateThread(@GetImageThread(), *NewTile)
              AddElement(OSM\TilesThreads())
              OSM\TilesThreads()\Tile = *NewTile
              OSM\TilesThreads()\GetImageThread = \GetImageThread
            EndIf
            Debug " Creating get image thread nb " + Str(\GetImageThread)
            DrawTile(*NewTile)
            
          EndWith  
          
        Else
          Debug" Error, can't create a new tile."
          Break 2
        EndIf 
      Next
    Next
    
    ForEach OSM\TilesThreads()
      If IsThread(OSM\TilesThreads()\GetImageThread) = 0
        FreeMemory(OSM\TilesThreads()\Tile)
        DeleteElement(OSM\TilesThreads())
      EndIf         
    Next
    
  EndProcedure
  
  Procedure DrawTrack()
    
    Protected Pixel.Pixel
    Protected Location.Location
    Protected n.i = 0, x.i, y.i
    
    StartDrawing(CanvasOutput(OSM\Gadget))
    ForEach OSM\track()
      n=n+1
      If @OSM\TargetLocation\Latitude<>0 And  @OSM\TargetLocation\Longitude<>0
        getPixelCoorfromLocation(@OSM\track(),@Pixel)
        x=Pixel\x
        y=Pixel\y
        If x>0 And y>0 And x<GadgetWidth(OSM\Gadget) And y<GadgetHeight(OSM\Gadget)
          Circle(x,y,2,#Green)
        EndIf
      EndIf 
    Next
    StopDrawing()
    
  EndProcedure  
  
  Procedure DrawingThread(Null)
    
    Debug "--------- Main drawing thread ------------"
    OSM\Dirty = #False
    
    LockMutex(OSM\DrawingThreadMutex) ; Only one main drawing thread at once
    
    Protected CenterX = GadgetWidth(OSM\Gadget) / 2
    Protected CenterY = GadgetHeight(OSM\Gadget) / 2
    
    DrawTiles()
    
    LockMutex(OSM\DrawingMutex)
    StartDrawing(CanvasOutput(OSM\Gadget))
    ;DrawTrack()
    Circle(CenterX, CenterY, 5, #Red)
    StopDrawing()
    UnlockMutex(OSM\DrawingMutex)
    
    UnlockMutex(OSM\DrawingThreadMutex)
    
    ;- Redraw
    ;If something was not correctly drawn, redraw after a while
    If OSM\Dirty
      Debug "Something is dirty !"
      ;Delay(250)
      CreateThread(@DrawingThread(), Null)
    EndIf
    
  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)
    OSM\Position\x = OSM\TargetTile\x * OSM\TileSize ;Convert X, Y in tile.decimal into real pixels
    OSM\Position\y = OSM\TargetTile\y * OSM\TileSize
    OSM\DeltaX = OSM\Position\x - Int(OSM\Position\x / OSM\TileSize) * OSM\TileSize
    OSM\DeltaY = OSM\Position\y - Int(OSM\Position\y / OSM\TileSize) * OSM\TileSize
    CreateThread(@DrawingThread(), Null)
    
  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)
    OSM\Position\x = OSM\TargetTile\x * OSM\TileSize ;Convert X, Y in tile.decimal into real pixels
    OSM\Position\y = OSM\TargetTile\y * OSM\TileSize
    OSM\DeltaX = OSM\Position\x - Int(OSM\Position\x / OSM\TileSize) * OSM\TileSize
    OSM\DeltaY = OSM\Position\y - Int(OSM\Position\y / OSM\TileSize) * OSM\TileSize
    CreateThread(@DrawingThread(), Null)
    
  EndProcedure
  
  Procedure Event(Event.l)
    
    Protected Gadget.i
    Protected MouseX.i, MouseY.i
    Protected OldX.i, OldY.i
    Protected TileX.d, TileY.d
    
    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\MoveStartingPoint\x = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                  OSM\MoveStartingPoint\y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) 
                Case #PB_EventType_MouseMove
                  If OSM\MoveStartingPoint\x <> - 1
                    ;Need a refresh
                    ;OSM\EmergencyQuit = #True
                    MouseX = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) - OSM\MoveStartingPoint\x
                    MouseY = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) - OSM\MoveStartingPoint\y
                    ;Old move values 
                    OldX = OSM\Position\x 
                    OldY = OSM\Position\y
                    ;New move values
                    OSM\Position\x - MouseX
                    OSM\Position\y - MouseY
                    ;OSM tile position in tile.decimal
                    TileX = OSM\Position\x / OSM\TileSize
                    TileY = OSM\Position\y / OSM\TileSize
                    ;Pixel shift
                    OSM\DeltaX = OSM\Position\x - Int(TileX) * OSM\TileSize
                    OSM\DeltaY = OSM\Position\y - Int(TileY) * OSM\TileSize
                    ;Moved to a new tile ?
                    If (Int(OSM\Position\x / OSM\TileSize)) <> (Int(OldX / OSM\TileSize)) Or (Int(OSM\Position\y / OSM\TileSize)) <> (Int(OldY / OSM\TileSize)) 
                      Debug "--- New tile"
                      OSM\TargetTile\x = TileX
                      OSM\TargetTile\y = TileY
                      Debug "OSM\Position\x " + Str(OSM\Position\x) + " ; OSM\Position\y " + Str(OSM\Position\y) 
                      XY2LatLon(@OSM\TargetTile, @OSM\TargetLocation)
                      Debug "OSM\TargetTile\x " + StrD(OSM\TargetTile\x) + " ; OSM\TargetTile\y "  + StrD(OSM\TargetTile\y) 
                    EndIf
                    OSM\EmergencyQuit = #False
                    CreateThread(@DrawingThread(), Null)
                    OSM\MoveStartingPoint\x = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                    OSM\MoveStartingPoint\y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY)
                  EndIf
                Case #PB_EventType_LeftButtonUp
                  OSM\MoveStartingPoint\x = - 1
                  OSM\TargetTile\x = OSM\Position\x / OSM\TileSize
                  OSM\TargetTile\y = OSM\Position\y / OSM\TileSize
                  Debug "OSM\Position\x " + Str(OSM\Position\x) + " ; OSM\Position\y " + Str(OSM\Position\y) 
                  XY2LatLon(@OSM\TargetTile, @OSM\TargetLocation)
                  ;Draw()
                  Debug "OSM\TargetTile\x " + StrD(OSM\TargetTile\x) + " ; OSM\TargetTile\y "  + StrD(OSM\TargetTile\y) 
                  ;SetGadgetText(#String_1, StrD(OSM\TargetLocation\Latitude))
                  ;SetGadgetText(#String_0, StrD(OSM\TargetLocation\Longitude))
              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, 17)
  ;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

Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: OpenStreetMap dans un Canvas

Message par djes »

On bosse en parallèle, pour info. Voici mon dernier code.

Code : Tout sélectionner

;************************************************************** 
; Program:           OSM (OpenStreetMap Module) 
; Author:            Thyphoon and djes
; Date:              Mai 17, 2016
; License:           Free, unrestricted, credit appreciated 
;                    but not required.
; Note:              Please share improvement !
; Thanks:            Progi1984, 
;************************************************************** 

CompilerIf #PB_Compiler_Thread = #False
  MessageRequester("Warning !!","You must enable ThreadSafe support in compiler options",#PB_MessageRequester_Ok )
  End
CompilerEndIf 

EnableExplicit

InitNetwork()
UsePNGImageDecoder()
UsePNGImageEncoder()

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 DrawingThread(Null)
  Declare SetZoom(Zoom.i, mode.i = #PB_Relative)
  Declare SetCallBackLocation(*CallBackLocation)
  Declare LoadGpxFile(file.s);  
EndDeclareModule

Module OSM 
  
  EnableExplicit
  
  Structure Location
    Longitude.d
    Latitude.d
  EndStructure
  
  ;- Tile Structure
  Structure Tile
    x.d
    y.d
    OSMTileX.i
    OSMTileY.i
    OSMZoom.i
    nImage.i
    GetImageThread.i
  EndStructure
  
  Structure DrawingParameters
    x.d
    y.d
    OSMTileX.i
    OSMTileY.i
    OSMZoom.i
    Mutex.i
    Semaphore.i
    Dirty.i
    PassNB.i
    End.i
  EndStructure  
  
  Structure TileThread
    GetImageThread.i
    *Tile.Tile
  EndStructure
  
  Structure Pixel
    x.i
    y.i
  EndStructure
  
  Structure ImgMemCach
    nImage.i
    Zoom.i
    XTile.i
    YTile.i
    Usage.i
  EndStructure
  
  Structure TileMemCach
    List Image.ImgMemCach()
    Mutex.i
    Semaphore.i
  EndStructure
  
  ;-OSM Structure
  Structure OSM
    Gadget.i                                ; Canvas Gadget Id 
    
    TargetLocation.Location                 ; Latitude and Longitude from focus point
    Drawing.DrawingParameters               ; Drawing parameters based on focus point
    
    CallBackLocation.i                      ; @Procedure(latitude.d,lontitude.d)
    
    Position.Pixel                          ; Actual focus Point coords in pixels
    MoveStartingPoint.Pixel                 ; Start mouse position coords when dragging the map
    
    ServerURL.s                             ; Web URL ex: http://tile.openstreetmap.org/
    ZoomMin.i                               ; Min Zoom supported by server
    ZoomMax.i                               ; Max Zoom supported by server
    Zoom.i                                  ; Current zoom
    TileSize.i                              ; Tile size downloaded on the server ex : 256
    
    HDDCachePath.S                          ; Path where to load and save tiles downloaded from server
    MemCache.TileMemCach                    ; Image in memory cache
    List MapImageIndex.ImgMemCach()         ; Index from MemCache\Image() to construct map
    
    Moving.i
    Dirty.i                                 ;To signal that drawing need a refresh
    LoadingMutex.i
    DrawingMutex.i
    ;CurlMutex.i                            ;seems that I can't thread curl ! :(((((
    List TilesThreads.TileThread()
    
    MapImageMutex.i                         ; Mutex to lock
    
    List track.Location()                   ;to display a GPX track
    
  EndStructure
  
  Global OSM.OSM, Null.i
  
  ;- *** CURL specific ***
  
  Global *ReceiveHTTPToMemoryBuffer, ReceiveHTTPToMemoryBufferPtr.i, ReceivedData.s
  IncludeFile "libcurl.pbi" ; https://github.com/deseven/pbsamples/tree/master/crossplatform/libcurl
  
  ProcedureC ReceiveHTTPWriteToMemoryFunction(*ptr, Size.i, NMemB.i, *Stream)
    
    Protected SizeProper.i  = Size & 255
    Protected NMemBProper.i = NMemB
    
    If *ReceiveHTTPToMemoryBuffer = 0
      *ReceiveHTTPToMemoryBuffer = AllocateMemory(SizeProper * NMemBProper)
      If *ReceiveHTTPToMemoryBuffer = 0
        Debug "Problem allocating memory"
        End
      EndIf
    Else
      *ReceiveHTTPToMemoryBuffer = ReAllocateMemory(*ReceiveHTTPToMemoryBuffer, MemorySize(*ReceiveHTTPToMemoryBuffer) + SizeProper * NMemBProper)
      If *ReceiveHTTPToMemoryBuffer = 0
        Debug "Problem reallocating memory"
        End
      EndIf  
    EndIf
    
    CopyMemory(*ptr, *ReceiveHTTPToMemoryBuffer + ReceiveHTTPToMemoryBufferPtr, SizeProper * NMemBProper)
    ReceiveHTTPToMemoryBufferPtr + SizeProper * NMemBProper
    
    ProcedureReturn SizeProper * NMemBProper
    
  EndProcedure
  
  Procedure.i CurlReceiveHTTPToMemory(URL$, ProxyURL$="", ProxyPort$="", ProxyUser$="", ProxyPassword$="")
    
    Protected *Buffer, curl.i, Timeout.i, res.i
    
    ;Debug "ReceiveHTTPToMemory" + URL$ + ProxyURL$ + ProxyPort$ + ProxyUser$ + ProxyPassword$
    
    If Len(URL$)
      
      curl  = curl_easy_init()
      
      If curl
        
        Timeout = 3
        
        curl_easy_setopt(curl, #CURLOPT_URL, str2curl(URL$))
        curl_easy_setopt(curl, #CURLOPT_SSL_VERIFYPEER, 0)
        curl_easy_setopt(curl, #CURLOPT_SSL_VERIFYHOST, 0)
        curl_easy_setopt(curl, #CURLOPT_HEADER, 0)      
        curl_easy_setopt(curl, #CURLOPT_TIMEOUT, Timeout)
        
        If Len(ProxyURL$)
          ;curl_easy_setopt(curl, #CURLOPT_HTTPPROXYTUNNEL, #True)
          If Len(ProxyPort$)
            ProxyURL$ + ":" + ProxyPort$
          EndIf
          Debug ProxyURL$
          curl_easy_setopt(curl, #CURLOPT_PROXY, str2curl(ProxyURL$))
          If Len(ProxyUser$)
            If Len(ProxyPassword$)
              ProxyUser$ + ":" + ProxyPassword$
            EndIf
            ;Debug ProxyUser$
            curl_easy_setopt(curl, #CURLOPT_PROXYUSERPWD, str2curl(ProxyUser$))
          EndIf
        EndIf
        
        curl_easy_setopt(curl, #CURLOPT_WRITEFUNCTION, @ReceiveHTTPWriteToMemoryFunction())
        res = curl_easy_perform(curl)
        
        If res = #CURLE_OK
          *Buffer = AllocateMemory(ReceiveHTTPToMemoryBufferPtr)
          If *Buffer
            CopyMemory(*ReceiveHTTPToMemoryBuffer, *Buffer, ReceiveHTTPToMemoryBufferPtr)
            FreeMemory(*ReceiveHTTPToMemoryBuffer)
            *ReceiveHTTPToMemoryBuffer = #Null
            ReceiveHTTPToMemoryBufferPtr = 0
          Else
            Debug "Problem allocating buffer"         
          EndIf        
          ;curl_easy_cleanup(curl) ;Was its original place but moved below as it seems more logical to me.
        Else
          Debug "CURL NOT OK"
        EndIf
        
        curl_easy_cleanup(curl)
        
      Else
        Debug "Can't Init CURL"
      EndIf
      
    EndIf
    
    Debug "Curl Buffer : " + Str(*Buffer)
    
    ProcedureReturn *Buffer
    
  EndProcedure
  ;- ***
  
  Procedure InitOSM()
    
    Protected Result.i
    
    OSM\HDDCachePath = GetTemporaryDirectory()
    OSM\ServerURL = "http://tile.openstreetmap.org/"
    OSM\ZoomMin = 0
    OSM\ZoomMax = 18
    OSM\MoveStartingPoint\x = - 1
    OSM\TileSize = 256
    OSM\MemCache\Mutex = CreateMutex()
    OSM\LoadingMutex = CreateMutex()
    OSM\DrawingMutex = CreateMutex()
    ;OSM\CurlMutex = CreateMutex()
    OSM\Dirty = #False
    OSM\Drawing\Semaphore = CreateSemaphore()
    OSM\Drawing\Mutex = CreateMutex()
    
    ;-*** PROXY
    
    Global Proxy = #False
    
    ;- => Use this to customise your preferences    
    ;     Result = CreatePreferences(GetHomeDirectory() + "OSM.prefs")
    ;     If Proxy
    ;       PreferenceGroup("PROXY")
    ;       WritePreferenceString("ProxyURL", "myproxy.fr")
    ;       WritePreferenceString("ProxyPort", "myproxyport")
    ;       WritePreferenceString("ProxyUser", "myproxyname")     
    ;     EndIf
    ;     If Result 
    ;       ClosePreferences()    
    ;     EndIf
    
    Result = OpenPreferences(GetHomeDirectory() + "OSM.prefs")
    If Proxy
      PreferenceGroup("PROXY")       
      Global ProxyURL$  = ReadPreferenceString("ProxyURL", "")  ;InputRequester("ProxyServer", "Do you use a Proxy Server? Then enter the full url:", "")
      Global ProxyPort$ = ReadPreferenceString("ProxyPort", "") ;InputRequester("ProxyPort"  , "Do you use a specific port? Then enter it", "")
      Global ProxyUser$ = ReadPreferenceString("ProxyUser", "") ;InputRequester("ProxyUser"  , "Do you use a user name? Then enter it", "")
      Global ProxyPassword$ = InputRequester("ProxyPass"  , "Do you use a password ? Then enter it", "")
    EndIf
    If Result
      ClosePreferences()
    EndIf
    
    curl_global_init(#CURL_GLOBAL_ALL);
    CreateThread(@DrawingThread(), @OSM\Drawing)
    
  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
  
  ;*** Converts coords to tile.decimal
  Procedure LatLon2XY(*Location.Location, *Tile.Tile)
    Protected n.d = Pow(2.0, OSM\Zoom)
    Protected 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
    Debug "Latitude : " + StrD(*Location\Latitude) + " ; Longitude : " + StrD(*Location\Longitude)
    Debug "Tile X : " + Str(*Tile\x) + " ; Tile Y : " + Str(*Tile\y)
  EndProcedure
  
  ;*** Converts tile.decimal to coords
  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 getPixelCoorfromLocation(*Location.Location, *Pixel.Pixel) ; TODO to Optimize 
    Protected mapWidth.l    = Pow(2,OSM\Zoom+8)
    Protected mapHeight.l   = Pow(2,OSM\Zoom+8)
    Protected x1.l,y1.l
    
    Protected deltaX = OSM\Position\x - Int(OSM\Drawing\x) * OSM\TileSize  ;Get the position into the tile
    Protected deltaY = OSM\Position\y - Int(OSM\Drawing\y) * OSM\TileSize
    
    ; get x value
    x1 = (*Location\Longitude+180)*(mapWidth/360)
    ; convert from degrees To radians
    Protected latRad.d = *Location\Latitude*#PI/180;
    
    Protected mercN.d = Log(Tan((#PI/4)+(latRad/2)));
    y1     = (mapHeight/2)-(mapWidth*mercN/(2*#PI)) ;
    
    Protected x2.l, y2.l
    ; get x value
    x2 = (OSM\TargetLocation\Longitude+180)*(mapWidth/360)
    ; convert from degrees To radians
    latRad = OSM\TargetLocation\Latitude*#PI/180;
                                                ; get y value
    mercN = Log(Tan((#PI/4)+(latRad/2)))        ;
    y2     = (mapHeight/2)-(mapWidth*mercN/(2*#PI));
    
    *Pixel\x=GadgetWidth(OSM\Gadget)/2  - (x2-x1) - deltaX
    *Pixel\y=GadgetHeight(OSM\Gadget)/2 - (y2-y1) - deltaY
  EndProcedure
  
  Procedure LoadGpxFile(file.s)
    If LoadXML(0, file.s)
      Protected Message.s
      If XMLStatus(0) <> #PB_XML_Success
        Message = "Error in the XML file:" + Chr(13)
        Message + "Message: " + XMLError(0) + Chr(13)
        Message + "Line: " + Str(XMLErrorLine(0)) + "   Character: " + Str(XMLErrorPosition(0))
        MessageRequester("Error", Message)
      EndIf
      Protected *MainNode,*subNode,*child,child.l
      *MainNode=MainXMLNode(0)
      *MainNode=XMLNodeFromPath(*MainNode,"/gpx/trk/trkseg")
      ClearList(OSM\track())
      For child = 1 To XMLChildCount(*MainNode)
        *child = ChildXMLNode(*MainNode, child)
        AddElement(OSM\track())
        If ExamineXMLAttributes(*child)
          While NextXMLAttribute(*child)
            Select XMLAttributeName(*child)
              Case "lat"
                OSM\track()\Latitude=ValD(XMLAttributeValue(*child))
              Case "lon"
                OSM\track()\Longitude=ValD(XMLAttributeValue(*child))
            EndSelect
          Wend
        EndIf
      Next 
    EndIf
  EndProcedure
  
  Procedure.i GetTileFromMem(Zoom.i, XTile.i, YTile.i)
    
    Protected nImage.i = -1
    
    Debug "Check if we have this image in memory"
    
    LockMutex(OSM\LoadingMutex)   
    LockMutex(OSM\MemCache\Mutex)    
    ForEach OSM\MemCache\Image()
      If Zoom = OSM\MemCache\Image()\Zoom And OSM\MemCache\Image()\xTile = XTile And OSM\MemCache\Image()\yTile = YTile
        nImage = OSM\MemCache\Image()\nImage
        Debug "Load from MEM Tile X : " + Str(XTile) + " ; Tile Y : " + Str(YTile) + " nImage:" + Str(nImage)
        Break;
             ;ElseIf Zoom<>OSM\MemCache\Image()\Zoom
             ;        DeleteElement(OSM\MemCache\Image())
      EndIf 
    Next 
    UnlockMutex(OSM\MemCache\Mutex)
    UnlockMutex(OSM\LoadingMutex)   
    
    ProcedureReturn nImage
    
  EndProcedure
  
  Procedure.i GetTileFromHDD(Zoom.i, XTile.i, YTile.i)
    
    Protected nImage.i
    Protected CacheFile.s = "OSM_" + Str(Zoom) + "_" + Str(XTile) + "_" + Str(YTile) + ".png"
    
    Debug "Check if we have this image on HDD"
    
    If FileSize(OSM\HDDCachePath + cacheFile) > 0
      nImage = LoadImage(#PB_Any, OSM\HDDCachePath + CacheFile)
      
      If IsImage(nImage)
        Debug "Load from HDD Tile " + CacheFile
        ProcedureReturn nImage
      EndIf 
      
    EndIf
    
    ProcedureReturn -1
    
  EndProcedure
  
  Procedure.i GetTileFromWeb(Zoom.i, XTile.i, YTile.i)
    
    Protected *Buffer
    Protected nImage.i = -1
    
    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"
    
    Debug "Check if we have this image on Web"
    
    If Proxy
      ;LockMutex(OSM\CurlMutex)             ;Seems no more necessary
      *Buffer = CurlReceiveHTTPToMemory(TileURL, ProxyURL$, ProxyPort$, ProxyUser$, ProxyPassword$)
      ;UnlockMutex(OSM\CurlMutex)
    Else
      *Buffer = ReceiveHTTPMemory(TileURL)  ;TODO to thread by using #PB_HTTP_Asynchronous
    EndIf
    Debug "Image buffer " + Str(*Buffer)
    
    If *Buffer
      LockMutex(OSM\LoadingMutex)
      nImage = CatchImage(#PB_Any, *Buffer, MemorySize(*Buffer))
      UnlockMutex(OSM\LoadingMutex)
      
      If IsImage(nImage)
        Debug "Load from web " + TileURL + " as Tile nb " + nImage
        LockMutex(OSM\LoadingMutex)
        SaveImage(nImage, OSM\HDDCachePath + CacheFile, #PB_ImagePlugin_PNG)
        UnlockMutex(OSM\LoadingMutex)
        FreeMemory(*Buffer)
      Else
        Debug "Can't catch image " + TileURL
        nImage = -1
        ;ShowMemoryViewer(*Buffer, MemorySize(*Buffer))
      EndIf
    Else
      Debug "Problem loading from web " + TileURL  
    EndIf      
    
    ProcedureReturn nImage
    
  EndProcedure
  
  Procedure GetImageThread(*Tile.Tile)
    
    Protected *CacheImagePtr
    Protected nImage.i = -1
    
    LockMutex(OSM\LoadingMutex)
    
    LockMutex(OSM\MemCache\Mutex) 
    *CacheImagePtr = AddElement(OSM\MemCache\Image())
    Debug " CacheImagePtr : " + Str(*CacheImagePtr)
    OSM\MemCache\Image()\xTile = *Tile\OSMTileX
    OSM\MemCache\Image()\yTile = *Tile\OSMTileY
    OSM\MemCache\Image()\Zoom = *Tile\OSMZoom
    OSM\MemCache\Image()\nImage = -1  ;By now, this tile is in "loading" state, for thread synchro
    UnlockMutex(OSM\MemCache\Mutex)
    nImage = GetTileFromHDD(*Tile\OSMZoom, *Tile\OSMTileX, *Tile\OSMTileY)
    If nImage = -1
      nImage = GetTileFromWeb(*Tile\OSMZoom, *Tile\OSMTileX, *Tile\OSMTileY)
    EndIf
    LockMutex(OSM\MemCache\Mutex)
    If nImage <> -1
      Debug "Adding tile " + Str(nImage) + " to mem cache"
      ;AddTileToMemCache(Zoom, XTile, YTile, nImage)
      OSM\MemCache\Image()\nImage = nImage
      Debug "Image nb " + Str(nImage) + " successfully added to mem cache"   
    Else
      Debug "Error GetImageThread procedure, tile not loaded - Zoom:" + Str(*Tile\OSMZoom) + " X:" + Str(*Tile\OSMTileX) + " Y:" + Str(*Tile\OSMTileY)
      DeleteElement(OSM\MemCache\Image())
      nImage = -1
    EndIf
    UnlockMutex(OSM\MemCache\Mutex)
    *Tile\nImage = nImage
    UnlockMutex(OSM\LoadingMutex)
    
  EndProcedure
  
  Procedure DrawTile(*Tile.Tile)
    
    Protected x = *Tile\x 
    Protected y = *Tile\y 
    
    Debug "  Drawing tile nb " + " X : " + Str(*Tile\OSMTileX) + " Y : " + Str(*Tile\OSMTileX)
    Debug "  at coords " + Str(x) + "," + Str(y)
    
    LockMutex(OSM\DrawingMutex)
    StartVectorDrawing(CanvasVectorOutput(OSM\Gadget)) 
    If IsImage(*Tile\nImage)    
      MovePathCursor(x, y)
      DrawVectorImage(ImageID(*Tile\nImage))
      MovePathCursor(x, y)
      DrawVectorText(Str(x) + ", " + Str(y))
    Else
      Debug "Image missing"
      OSM\Drawing\Dirty = #True ;Signal that this image is missing so we should have to redraw
    EndIf
    StopVectorDrawing()
    UnlockMutex(OSM\DrawingMutex)
    
  EndProcedure
  
  Procedure DrawTiles(*Drawing.DrawingParameters)
    
    Protected x.i, y.i
    
    Protected tx = Int(*Drawing\x)  ;Don't forget the Int() !
    Protected ty = Int(*Drawing\y)
    
    Protected CenterX = GadgetWidth(OSM\Gadget) / 2
    Protected CenterY = GadgetHeight(OSM\Gadget) / 2
    
    Protected nx = CenterX / OSM\TileSize ;How many tiles around the point
    Protected ny = CenterY / OSM\TileSize
    
    ;Pixel shift, aka position in the tile
    Protected DeltaX = *Drawing\x * OSM\TileSize - (Int(*Drawing\x) * OSM\TileSize)
    Protected DeltaY = *Drawing\y * OSM\TileSize - (Int(*Drawing\y) * OSM\TileSize)
    
    Debug "Drawing tiles"
    
    For y = - ny To ny
      For x = - nx To nx
        
        ;Was quiting the loop if a move occured, giving maybe smoother movement
        ;If OSM\Moving
        ;  Break 2
        ;EndIf
        
        Protected *NewTile.Tile = AllocateMemory(SizeOf(Tile))
        If *NewTile
          With *NewTile
            
            ;Keep a track of tiles (especially to free memory)
            AddElement(OSM\TilesThreads())
            OSM\TilesThreads()\Tile = *NewTile
            
            ;New tile parameters
            \x = CenterX + x * OSM\TileSize - DeltaX
            \y = CenterY + y * OSM\TileSize - DeltaY
            \OSMTileX = tx + x
            \OSMTileY = ty + y
            \OSMZoom  = OSM\Zoom
            
            ;Check if the image exists
            \nImage = GetTileFromMem(\OSMZoom, \OSMTileX, \OSMTileY)
            If \nImage = -1 
              ;If not, load it in the background
              \GetImageThread = CreateThread(@GetImageThread(), *NewTile)
              OSM\TilesThreads()\GetImageThread = \GetImageThread
              Debug " Creating get image thread nb " + Str(\GetImageThread)
            EndIf
            DrawTile(*NewTile)
            
          EndWith  
          
        Else
          Debug" Error, can't create a new tile."
          Break 2
        EndIf 
      Next
    Next
    
    ;Free tile memory when the loading thread has finished
    ;TODO : exit this proc from drawtiles in a special "free ressources" task
    ForEach OSM\TilesThreads()
      If IsThread(OSM\TilesThreads()\GetImageThread) = 0
        FreeMemory(OSM\TilesThreads()\Tile)
        DeleteElement(OSM\TilesThreads())
      EndIf         
    Next
    
  EndProcedure
  
  Procedure  DrawTrack(*Drawing.DrawingParameters)
    
    Protected Pixel.Pixel
    Protected Location.Location
    Protected DeltaX = *Drawing\x * OSM\TileSize - (Int(*Drawing\x) * OSM\TileSize)
    Protected DeltaY = *Drawing\y * OSM\TileSize - (Int(*Drawing\y) * OSM\TileSize)

    If ListSize(OSM\track())>0
      
      ForEach OSM\track()
        If @OSM\TargetLocation\Latitude<>0 And  @OSM\TargetLocation\Longitude<>0
          getPixelCoorfromLocation(@OSM\track(), @Pixel)
          If ListIndex(OSM\track())=0
            MovePathCursor(Pixel\X + DeltaX, Pixel\Y + DeltaY)
          Else
            AddPathLine(Pixel\X + DeltaX, Pixel\Y + DeltaY)
          EndIf 
          
        EndIf 
        
      Next
      VectorSourceColor(RGBA(0, 255, 0, 150))
      StrokePath(10, #PB_Path_RoundEnd|#PB_Path_RoundCorner)
      
    EndIf
    
  EndProcedure
  
  Procedure Pointer(x.i, y.i, color.l = #Red)
    
    color=RGBA(255, 0, 0, 255)
    VectorSourceColor(color)
    MovePathCursor(x, y)
    AddPathLine(-8,-16,#PB_Path_Relative)
    AddPathCircle(8,0,8,180,0,#PB_Path_Relative)
    AddPathLine(-8,16,#PB_Path_Relative)
    ;FillPath(#PB_Path_Preserve) 
    ;ClipPath(#PB_Path_Preserve)
    AddPathCircle(0,-16,5,0,360,#PB_Path_Relative)
    VectorSourceColor(color)
    FillPath(#PB_Path_Preserve):VectorSourceColor(RGBA(0, 0, 0, 255)):StrokePath(1)
    
  EndProcedure
  
  Procedure DrawingThread(*Drawing.DrawingParameters)
    
    Repeat
      
      WaitSemaphore(*Drawing\Semaphore)
      
      Debug "--------- Main drawing thread ------------"
      
      LockMutex(*Drawing\Mutex) ; Only one main drawing thread at once
      
      *Drawing\Dirty = #False
      Protected CenterX = GadgetWidth(OSM\Gadget) / 2
      Protected CenterY = GadgetHeight(OSM\Gadget) / 2
      
      DrawTiles(*Drawing)
      LockMutex(OSM\DrawingMutex)
      StartVectorDrawing(CanvasVectorOutput(OSM\Gadget))
      DrawTrack(*Drawing)
      Pointer(CenterX, CenterY, #Red)
      StopVectorDrawing()
      UnlockMutex(OSM\DrawingMutex)
      
      ;- Redraw
      ;If something was not correctly drawn, redraw after a while
      If *Drawing\Dirty
        Debug "Something was dirty ! We try again to redraw"
        ;Delay(250)
        *Drawing\PassNb + 1
        SignalSemaphore(*Drawing\Semaphore)
      EndIf
      
      UnlockMutex(*Drawing\Mutex)
      
    Until *Drawing\End
    
  EndProcedure
  
  Procedure SetLocation(latitude.d, longitude.d, zoom = 15)
    
    OSM\TargetLocation\Latitude = latitude
    OSM\TargetLocation\Longitude = longitude
    
    OSM\Zoom = zoom
    
    If OSM\Zoom > OSM\ZoomMax : OSM\Zoom = OSM\ZoomMax : EndIf
    If OSM\Zoom < OSM\ZoomMin : OSM\Zoom = OSM\ZoomMin : EndIf
    
    LockMutex(OSM\Drawing\Mutex)
    LatLon2XY(@OSM\TargetLocation, @OSM\Drawing)
    ;Convert X, Y in tile.decimal into real pixels
    OSM\Position\X = OSM\Drawing\x * OSM\TileSize
    OSM\Position\Y = OSM\Drawing\y * OSM\TileSize 
    OSM\Drawing\PassNb = 1
    UnlockMutex(OSM\Drawing\Mutex)
    ;Start drawing
    SignalSemaphore(OSM\Drawing\Semaphore)
    ;***
    
  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
    
    LockMutex(OSM\Drawing\Mutex)
    LatLon2XY(@OSM\TargetLocation, @OSM\Drawing)
    ;Convert X, Y in tile.decimal into real pixels
    OSM\Position\X = OSM\Drawing\x * OSM\TileSize
    OSM\Position\Y = OSM\Drawing\y * OSM\TileSize 
    ;*** Creates a drawing thread and fill parameters
    OSM\Drawing\PassNb = 1
    UnlockMutex(OSM\Drawing\Mutex)
    ;Start drawing
    SignalSemaphore(OSM\Drawing\Semaphore)
    ;***
    
  EndProcedure
  
  
  Procedure SetCallBackLocation(CallBackLocation.i)
    OSM\CallBackLocation=CallBackLocation
  EndProcedure
  Procedure Event(Event.l)
    
    Protected Gadget.i
    Protected MouseX.i, MouseY.i
    Protected OldX.i, OldY.i
    Protected *Drawing.DrawingParameters
    
    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\MoveStartingPoint\x = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                  OSM\MoveStartingPoint\y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) 
                Case #PB_EventType_MouseMove
                  If OSM\MoveStartingPoint\x <> - 1
                    MouseX = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) - OSM\MoveStartingPoint\x
                    MouseY = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) - OSM\MoveStartingPoint\y
                    OSM\Moving = #True
                    ;Old move values 
                    OldX = OSM\Position\x 
                    OldY = OSM\Position\y
                    ;New move values
                    OSM\Position\x - MouseX
                    OSM\Position\y - MouseY
                    ;-*** Fill parameters and signal the drawing thread
                    LockMutex(OSM\Drawing\Mutex)
                    ;OSM tile position in tile.decimal
                    OSM\Drawing\x = OSM\Position\x / OSM\TileSize
                    OSM\Drawing\y = OSM\Position\y / OSM\TileSize
                    OSM\Drawing\PassNb = 1
                    ;Moved to a new tile ?
                    ;If (Int(OSM\Position\x / OSM\TileSize)) <> (Int(OldX / OSM\TileSize)) Or (Int(OSM\Position\y / OSM\TileSize)) <> (Int(OldY / OSM\TileSize)) 
                    ;Debug "--- New tile"
                    Debug "OSM\Position\x " + Str(OSM\Position\x) + " ; OSM\Position\y " + Str(OSM\Position\y) 
                    XY2LatLon(@OSM\Drawing, @OSM\TargetLocation)
                    Debug "OSM\Drawing\x " + StrD(OSM\Drawing\x) + " ; OSM\Drawing\y "  + StrD(OSM\Drawing\y) 
                    ;EndIf
                    UnlockMutex(OSM\Drawing\Mutex)
                    ;Start drawing
                    SignalSemaphore(OSM\Drawing\Semaphore)
                    ;- ***                   
                    OSM\MoveStartingPoint\x = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                    OSM\MoveStartingPoint\y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY)
                    ;If CallBackLocation send Location to function
                    If OSM\CallBackLocation>0
                      CallFunctionFast(OSM\CallBackLocation, @OSM\TargetLocation)
                    EndIf 
                  EndIf
                Case #PB_EventType_LeftButtonUp
                  OSM\Moving = #False
                  OSM\MoveStartingPoint\x = - 1
                  OSM\Drawing\x = OSM\Position\x / OSM\TileSize
                  OSM\Drawing\y = OSM\Position\y / OSM\TileSize
                  Debug "OSM\Position\x " + Str(OSM\Position\x) + " ; OSM\Position\y " + Str(OSM\Position\y) 
                  XY2LatLon(@OSM\Drawing, @OSM\TargetLocation)
                  ;Draw()
                  Debug "OSM\Drawing\x " + StrD(OSM\Drawing\x) + " ; OSM\Drawing\y "  + StrD(OSM\Drawing\y) 
                  ;SetGadgetText(#String_1, StrD(OSM\TargetLocation\Latitude))
                  ;SetGadgetText(#String_0, StrD(OSM\TargetLocation\Longitude))
              EndSelect
          EndSelect
      EndSelect
    Else
      MessageRequester("Module OSM", "You must use OSMGadget before", #PB_MessageRequester_Ok )
      End
    EndIf  
    
  EndProcedure
EndModule


;Demonstration
CompilerIf #PB_Compiler_IsMainFile 
  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
    #Gdt_LoadGpx
  EndEnumeration
  
  Structure Location
    Longitude.d
    Latitude.d
  EndStructure
  
  Procedure UpdateLocation(*Location.Location)
    SetGadgetText(#String_0, StrD(*Location\Latitude))
    SetGadgetText(#String_1, StrD(*Location\Longitude))
    ProcedureReturn 0
  EndProcedure
  
  ;- 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, "")
    ButtonGadget(#Gdt_LoadGpx, 530, 280, 150, 30, "Load GPX")
    
    Define Event.i, Gadget.i, Quit.b = #False
    Define pfValue.d
    OSM::SetLocation(49.04599, 2.03347, 17)
    OSM::SetCallBackLocation(@UpdateLocation())
    
    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)
            Case #Gdt_LoadGpx
              OSM::LoadGpxFile(OpenFileRequester("Choisissez un fichier à charger", "", "*.gpx", 0))
          EndSelect
      EndSelect
    Until Quit = #True
  EndIf
CompilerEndIf
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: OpenStreetMap dans un Canvas

Message par Thyphoon »

très beau Boulot Djes ^_^
La différence entre les 2 codes est surtout sur la façon d'afficher la carte.

Et voici mon code. Si vous n'avez pas de proxy vous pouvez tester le code tel qu'il est.
Avec ce code vous pouvez rajouter des marques et les déplacer et afficher un fichier GPX ou il vous affichera chaque km (pas très jolie)
Reste beaucoup a faire... mais ça commence a vraiment bien fonctionner

Code : Tout sélectionner

;************************************************************** 
; Name:              Module OpenStreetMap
; Version:           0.8
; Author:            Thyphoon And Djes
; Date:              July 26, 2016
; License:           Free, unrestricted, credit appreciated 
;                    but not required.
; Note:              Please share improvement !
; Thanks:            Progi1984 for the first OSM implementation
;                    Fred, Freak and all people who made purebasic what is it !
;************************************************************** 

;Only if you use a Proxy
;XIncludeFile("..\includes Share\http.pbi") ;<- You must yo use it if your network have a proxy
CompilerIf Defined(http,#PB_Module)
  http::SetProxy("yourproxy","8080") ;<- Setup you proxy
CompilerEndIf

CompilerIf #PB_Compiler_Thread=0
  CompilerError "Warning !!","You must to Enable 'create ThreadSafe' in compiler option"
  End
CompilerEndIf 

DeclareModule OSM
  Declare InitOSM(window.i=0)
  Declare MapGadget(Gadget.i, X.i, Y.i, Width.i, Height.i)
  Declare Event(Event.l)
  Declare SetLocation(latitude.d, longitude.d, zoom = 15)
  Declare SetCallBackLocation(*CallBackLocation)
  Declare SetZoom(Zoom.i, mode.i = #PB_Relative)
  Declare LoadGpxFile(file.s);
  Declare AddMarker(Latitude.d,Longitude.d,color.l=-1)
  Declare ZoomToArea()
  Declare RefreshMap()
EndDeclareModule

Module OSM
  
  ;Just a Hook to replace original ReceiveHTTPMemory() by http::ReceiveHTTPToMemory(URL) if you want to use my http lib
  CompilerIf Defined(http,#PB_Module)
    Macro ReceiveHTTPMemory(URL)
      http::ReceiveHTTPToMemory(URL)
    EndMacro
  CompilerEndIf
  
  UsePNGImageDecoder()
  UsePNGImageEncoder()
  
  Enumeration #PB_Event_FirstCustomValue
    #EvenementStartRefresh
    #EvenementStopRefresh
  EndEnumeration
  
  Structure Location
    Longitude.d                           ; ex: 49.04599 
    Latitude.d                            ; ex 2.03347
  EndStructure
  
  Structure Tile
    X.d                                   ; ex: 66276.367739138892
    Y.d                                   ; ex: 44987.
  EndStructure
  
  Structure Pixel
    X.i
    Y.i
  EndStructure
  
  Structure ImgMemCach
    nImage.i                                
    Zoom.i
    XTile.i
    YTile.i
    LastUseTime.i                           ; ElapsedMilliseconds value from last Tile Use see GetTileFromMem()
  EndStructure
  
  Structure TileMemCach
    List Image.ImgMemCach()
    Mutex.i
  EndStructure
  
  Structure Marker
    Location.Location
    color.l
  EndStructure
  
  Structure OSM
    Window.i                                ; Window used to put the MapGadget
    Gadget.i                                ; Canvas Gadget Id
    Font.i
    TargetLocation.Location                 ; Latitude and Longitude from focus point
    TargetTile.Tile                         ; Focus Tile coord
    
    CallBackLocation.i                      ; pointer to procedure to sen location
    
    Position.Pixel                          ; Focus Point coord in Pixel
    
    ServerURL.s                             ; Web Url ex: http://tile.openstreetmap.org/
    ZoomMin.i                               ; Min Zoom supported by Server
    ZoomMax.i                               ; Max Zoom supported by Server
    Zoom.i                                  ; Current Zoom
    TileSize.i                              ; Tile Size downloaded on the server ex : 256
    
    ConstructMapMutex.i                     ; To Lock When a Thread ConstructMap run
    
    HDDCachePath.S                          ; Path to Load and Save Tile downloaded from server
    MemCache.TileMemCach                    ; to know image always in memory
                                            ; List MapImageIndex.ImgMemCach()      ; List of Index from MemCache\Image() to construct map
    Array MapImage.ImgMemCach(16,16)
    MapImageMutex.i                         ; Mutex to lock when use MapImage() Array
    MapImageSemaphore.i                     ; Semaphore to control Thread
    List ThreadGetTile.i()                  ; Simple List with all thread GetTile run. used to kill them when No Web Connection 
    ErrorLoadingTile.b                      ; #True when the answer from Web Connection is too slow
    
    StartCursor.Pixel                       ; Coord from start drag the map
    DeltaCursor.Pixel                       ; Delta from curent position and the start position
    
    List track.Location()                   ; To display a track GPX on card
    List Marker.Marker()                    ; To diplay marker
    EditMarkerIndex.i                       ; Index from Marker List about Marker edited
  EndStructure
  
  Global OSM.OSM
  
  ; Open a CanvasGadget to display the map
  Procedure MapGadget(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
  
   Macro Min(a,b)
    (Bool((a) <= (b)) * (a) + Bool((b) < (a)) * (b))
  EndMacro
  
  Macro Max(a,b)
    (Bool((a) >= (b)) * (a) + Bool((b) > (a)) * (b))
  EndMacro
  
  ; Convert Latitude/Longitude Location to Tile Coord
  Procedure LatLon2XY(*Location.Location, *Tile.Tile)
    Protected n.d = Pow(2.0, OSM\Zoom)
    Protected 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
    ;Debug "Latitude : " + StrD(*Location\Latitude) + " ; Longitude : " + StrD(*Location\Longitude)
    ;Debug "Tile X : " + Str(*Tile\X) + " ; Tile Y : " + Str(*Tile\Y)
  EndProcedure
  
  ; Convert Tile Coord to Latitude/Longitude
  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
  
  ; Convert Latitude/Longitude to Pixel Coord on MapGadget
  Procedure GetPixelCoordFromLocation(*Location.Location,*Pixel.Pixel) ; TODO to Optimize 
    Protected mapWidth.l    = Pow(2,OSM\Zoom+8)
    Protected mapHeight.l   = Pow(2,OSM\Zoom+8)
    Protected x1.l,y1.l
    ; get x value
    x1 = (*Location\Longitude+180)*(mapWidth/360)
    ; convert from degrees To radians
    Protected latRad.d = *Location\Latitude*#PI/180;
    
    Protected mercN.d = Log(Tan((#PI/4)+(latRad/2)));
    y1     = (mapHeight/2)-(mapWidth*mercN/(2*#PI)) ;
    
    Protected x2.l,y2.l
    ; get x value
    x2 = (OSM\TargetLocation\Longitude+180)*(mapWidth/360)
    ; convert from degrees To radians
    latRad = OSM\TargetLocation\Latitude*#PI/180;
                                                ; get y value
    mercN = Log(Tan((#PI/4)+(latRad/2)))        ;
    y2     = (mapHeight/2)-(mapWidth*mercN/(2*#PI));
    
    *Pixel\X=GadgetWidth(OSM\Gadget)/2-(x2-x1)+ OSM\DeltaCursor\X
    *Pixel\Y=GadgetHeight(OSM\Gadget)/2-(y2-y1)+ OSM\DeltaCursor\Y
  EndProcedure
  
  
  ; HaversineAlgorithm 
  ; http://andrew.hedges.name/experiments/haversine/
  Procedure.d HaversineInKM(*posA.Location,*posB.Location)
    Protected eQuatorialEarthRadius.d = 6378.1370;6372.795477598;
    Protected dlong.d = (*posB\Longitude - *posA\Longitude);
    Protected dlat.d = (*posB\Latitude - *posA\Latitude)   ;
    Protected alpha.d=dlat/2
    Protected beta.d=dlong/2
    Protected a.d = Sin(Radian(alpha)) * Sin(Radian(alpha)) + Cos(Radian(*posA\Latitude)) * Cos(Radian(*posB\Latitude)) * Sin(Radian(beta)) * Sin(Radian(beta)) 
    Protected c.d = ASin(Min(1,Sqr(a)));
    Protected distance.d = 2*eQuatorialEarthRadius * c     
    ProcedureReturn distance                                                                                                        ;
  EndProcedure
  
  Procedure.d HaversineInM(*posA.Location,*posB.Location)
    ProcedureReturn (1000 * HaversineInKM(@*posA,@*posB));
  EndProcedure
  
  ; Load a GPX file and convert in Path in OSM\Track()
  Procedure LoadGpxFile(file.s)
    If LoadXML(0, file.s)
      Protected Message.s
      If XMLStatus(0) <> #PB_XML_Success
        Message = "Error in the XML file:" + Chr(13)
        Message + "Message: " + XMLError(0) + Chr(13)
        Message + "Line: " + Str(XMLErrorLine(0)) + "   Character: " + Str(XMLErrorPosition(0))
        MessageRequester("Error", Message)
      EndIf
      Protected *MainNode,*subNode,*child,child.l
      *MainNode=MainXMLNode(0)
      *MainNode=XMLNodeFromPath(*MainNode,"/gpx/trk/trkseg")
      ClearList(OSM\track())
      For child = 1 To XMLChildCount(*MainNode)
        *child = ChildXMLNode(*MainNode, child)
        AddElement(OSM\track())
        If ExamineXMLAttributes(*child)
          While NextXMLAttribute(*child)
            Select XMLAttributeName(*child)
              Case "lat"
                OSM\track()\Latitude=ValD(XMLAttributeValue(*child))
              Case "lon"
                OSM\track()\Longitude=ValD(XMLAttributeValue(*child))
            EndSelect
          Wend
        EndIf
      Next 
    EndIf
  EndProcedure
  
  ; Add to memory the image from last Tile Loaded
  Procedure AddTileToMemCache(Zoom.i, XTile.i, YTile.i,nImage.i)
    Protected Index.i
    ;If IsImage(nImage)
    LockMutex(OSM\MemCache\Mutex)
    ;We add To the List And load it
    FirstElement(OSM\MemCache\Image())
    AddElement(OSM\MemCache\Image())
    Index=ListIndex(OSM\MemCache\Image())
    OSM\MemCache\Image()\XTile=XTile
    OSM\MemCache\Image()\YTile=YTile
    OSM\MemCache\Image()\Zoom=Zoom
    OSM\MemCache\Image()\nImage=nImage  
    OSM\MemCache\Image()\LastUseTime=ElapsedMilliseconds()
    UnlockMutex(OSM\MemCache\Mutex)
    ProcedureReturn Index
    ;Else
    ;Debug "NO ADD TILE TO MEM CACHE BECAUSE BAD IMAGE"
    ;EndIf 
  EndProcedure
  
  ; Check if this Tile is already loaded in Memory
  Procedure.i GetTileFromMem(Zoom.i, XTile.i, YTile.i)
    Protected nImage.i=#False
    LockMutex(OSM\MemCache\Mutex)
    ;Check if we have this Image in Memory
    ForEach OSM\MemCache\Image()
      If Zoom=OSM\MemCache\Image()\Zoom And OSM\MemCache\Image()\XTile=XTile And OSM\MemCache\Image()\YTile=YTile
        OSM\MemCache\Image()\LastUseTime=ElapsedMilliseconds()
        nImage=OSM\MemCache\Image()\nImage
        Debug "Load From MEM Tile X : " + Str(XTile) + " ; Tile Y : " + Str(YTile)+" IsImage:"+Str(IsImage(nImage))
        Break; stop Search 
        
        ; Clean Image in memory  
        ;TODO Find a better way to clean Image in memory  
      ElseIf ElapsedMilliseconds()>OSM\MemCache\Image()\LastUseTime+30000 ;If Tile no use after 5 minutes ...
        If OSM\MemCache\Image()\nImage>0 And IsImage(OSM\MemCache\Image()\nImage)
          ;FreeImage(OSM\MemCache\Image()\nImage)
        EndIf 
        ;DeleteElement(OSM\MemCache\Image())
      EndIf 
    Next 
    UnlockMutex(OSM\MemCache\Mutex)
    ProcedureReturn nImage
  EndProcedure
  
  ; Check if this Tile is already on the HDD cache
  Procedure.i GetTileFromHDD(Zoom.i, XTile.i, YTile.i)
    Protected nImage.i
    Protected CacheFile.s = "OSM_" + Str(Zoom) + "_" + Str(XTile) + "_" + Str(YTile) + ".png"
    If FileSize(OSM\HDDCachePath + cacheFile) > 0
      nImage=LoadImage(#PB_Any, OSM\HDDCachePath + CacheFile)
      If IsImage(nImage)
        Debug "Load From HDD Tile X : " + Str(XTile) + " ; Tile Y : " + Str(YTile)+ "IsImage:"+Str(IsImage(nImage))
        AddTileToMemCache(Zoom, XTile, YTile,nImage)
        ProcedureReturn nImage
      EndIf 
    EndIf 
    ProcedureReturn #False
  EndProcedure
  
  ; Download Tile From the Web
  Procedure.i GetTileFromWeb(Zoom.i, XTile.i, YTile.i)
    Protected *Buffer
    Protected nImage.i=0
    Protected CacheFile.s = "OSM_" + Str(Zoom) + "_" + Str(XTile) + "_" + Str(YTile) + ".png"
    Protected TileURL.s = OSM\ServerURL + Str(Zoom) + "/" + Str(XTile) + "/" + Str(YTile) + ".png"
    *Buffer = ReceiveHTTPMemory(TileURL)
    If *Buffer>0
      nImage=CatchImage(#PB_Any, *Buffer, MemorySize(*Buffer))
      If IsImage(nImage)
        AddTileToMemCache(Zoom, XTile, YTile,nImage)
        SaveImage(nImage, OSM\HDDCachePath + CacheFile, #PB_ImagePlugin_PNG)
        Debug "Load From WEB Tile X : " + Str(XTile) + " ; Tile Y : " + Str(YTile)
        FreeMemory(*Buffer)
      Else
        
        Debug "Can't catch image :" + TileURL
        AddTileToMemCache(Zoom, XTile, YTile,-1) ;<-To limit overload Download and display Error Loading On map
      EndIf
    Else
      Debug "Problem loading :" + TileURL
      AddTileToMemCache(Zoom, XTile, YTile,-1) ;<-To limit overload Download and display Error Loading On map
    EndIf
    ProcedureReturn nImage
  EndProcedure
  
  ; Fin Image to Display a Tile
  Procedure GetTile(*Index.ImgMemCach)
    Protected Zoom.i, XTile.i, YTile.i
    LockMutex(OSM\MapImageMutex)
    Zoom=*Index\Zoom
    XTile=*Index\XTile
    YTile=*Index\YTile
    *index\nImage=0
    UnlockMutex(OSM\MapImageMutex)
    Protected nImage.i
    nImage=GetTileFromMem(Zoom, XTile, YTile)
    If nImage=0
      nImage=GetTileFromHDD(Zoom, XTile, YTile)
      If nImage=0
        nImage=GetTileFromWeb(Zoom, XTile, YTile)
        If nImage=0
          Debug "Error GetTile Procedure : can't Load this Tile" ; TODO Check Why !!!
          ProcedureReturn #False
        EndIf
      EndIf
    EndIf 
    LockMutex(OSM\MapImageMutex)
    *Index\nImage=nImage
    UnlockMutex(OSM\MapImageMutex)
    SignalSemaphore(OSM\MapImageSemaphore)
    PostEvent(#EvenementStopRefresh)
    Debug"EndGetTile"
  EndProcedure
  
  ; Prepare OSM\MapImage(x,y) with Image to display
   Procedure ThreadConstructMap(z.i)
    
     
;               ForEach OSM\ThreadGetTile()
;                 If IsThread(OSM\ThreadGetTile())
;                   KillThread(OSM\ThreadGetTile())
;                   SignalSemaphore(OSM\MapImageSemaphore) ; To restore the good levet to Semaphore
;                   Debug "Kill Over"
;                 EndIf 
;               Next
    ; Only 1 Thread about ConstrucMap at the same Time  
    If TryLockMutex(OSM\ConstructMapMutex)
    
    ;PostEvent(#EvenementStartRefresh)
    Protected x.i, y.i
    Protected nx.i = Round(GadgetWidth(OSM\Gadget)/OSM\TileSize,#PB_Round_Up)*2     ; How many tiles horizontally
    Protected ny.i = Round(GadgetHeight(OSM\Gadget)/OSM\TileSize,#PB_Round_Up)*2    ; How many tiles Vertically
    Protected tx.i = Int(OSM\TargetTile\X)                                          ; Get Tile number
    Protected ty.i = Int(OSM\TargetTile\Y)
    Protected mx.i = Int(nx/2)                                                      ;Find the middle Tile
    Protected my.i = Int(ny/2)
    Protected NoCnxTime.i = -1                                                      ;Get elapsedMilliseconds() when Semaphore is overload
    Protected GetTile.b
    ClearList(OSM\ThreadGetTile()); 

    For x = 0 To nx
      For y = 0 To ny
        Repeat 
          GetTile=#True
          If TrySemaphore(OSM\MapImageSemaphore)
            NoCnxTime=-1
            OSM\ErrorLoadingTile=#False
            LockMutex(OSM\MapImageMutex)
            OSM\MapImage(x,y)\XTile=tx+x-mx
            OSM\MapImage(x,y)\YTile=ty+y-my
            OSM\MapImage(x,y)\Zoom=OSM\Zoom
            OSM\MapImage(x,y)\nImage=0
            UnlockMutex(OSM\MapImageMutex)
            LastElement(OSM\ThreadGetTile())
            AddElement(OSM\ThreadGetTile())
            ; Find the Tile
            OSM\ThreadGetTile()=CreateThread(@GetTile(),@OSM\MapImage(x,y))
          ; Semaphore is Overload we wait  
          Else
            Delay(1)
            GetTile=#False
            ; Take the Time from Overload start
            If NoCnxTime=-1
              NoCnxTime=ElapsedMilliseconds()
              OSM\ErrorLoadingTile=#True
            EndIf 
            ; After 4000 Milliseconds Semaphore overload, it's not normal. We kill all Thread to stop Semaphore overload
            If OSM\ErrorLoadingTile=#True And ElapsedMilliseconds()>NoCnxTime+5000
              Break 2
            EndIf
          EndIf
        Until GetTile=#True
      Next
    Next
       PostEvent(#EvenementStopRefresh) ; in Event() ask a refresh Map
    Debug "Unlock"
      UnlockMutex(OSM\ConstructMapMutex)
    Else
      Debug "ConstructMap Overload"
    EndIf 
  EndProcedure
  
  
  Procedure ConstructMap()
      CreateThread(@ThreadConstructMap(),0)
  EndProcedure
  
  ; Draw a pointer to the screen 
  Procedure Pointer(x.l,y.l,color=-1)
    If color=-1:color=RGBA(255, 0, 0, 255):EndIf
    VectorSourceColor(color)
    MovePathCursor(x, y)
    AddPathLine(-8,-16,#PB_Path_Relative)
    AddPathCircle(8,0,8,180,0,#PB_Path_Relative)
    AddPathLine(-8,16,#PB_Path_Relative)
    AddPathCircle(0,-16,5,0,360,#PB_Path_Relative)
    VectorSourceColor(color)
    FillPath(#PB_Path_Preserve):VectorSourceColor(RGBA(0, 0, 0, 255)):StrokePath(1)
  EndProcedure
  
  ; Draw The Track if we have one !
  Procedure  DrawTrack()
    Protected Pixel.Pixel
    Protected Location.Location
    Protected km.d=0
    Protected memKm.l
    If ListSize(OSM\track())>0
      ForEach OSM\track()
        
        ;-Test Distance
        If ListIndex(OSM\track())=0
          Location\Latitude=OSM\track()\Latitude
          Location\Longitude=OSM\track()\Longitude 
        Else 
          km=km+HaversineInKM(@Location,@OSM\track()) ;<- display Distance 
          Location\Latitude=OSM\track()\Latitude
          Location\Longitude=OSM\track()\Longitude 
        EndIf 
        If @OSM\TargetLocation\Latitude<>0 And  @OSM\TargetLocation\Longitude<>0
          GetPixelCoordFromLocation(@OSM\track(),@Pixel)
          If ListIndex(OSM\track())=0
            MovePathCursor(Pixel\X,Pixel\Y)
          Else
            AddPathLine(Pixel\X,Pixel\Y)
            If Int(km)<>memKm
              memKm=Int(km)
              If OSM\Zoom>10
              BeginVectorLayer()
              VectorFont(FontID(OSM\Font), OSM\Zoom)
              VectorSourceColor(RGBA(50, 50, 50, 255))
              DrawVectorText(Str(Int(km)))
              EndVectorLayer()
              EndIf 
            EndIf
            
          EndIf 
        EndIf 
        
      Next
       VectorSourceColor(RGBA(0, 255, 0, 150))
      StrokePath(10, #PB_Path_RoundEnd|#PB_Path_RoundCorner)
    EndIf
  EndProcedure
  
  ; Add a Marker To the Map
  Procedure AddMarker(Latitude.d,Longitude.d,color.l=-1)
    AddElement(OSM\Marker())
    OSM\Marker()\Location\Latitude=Latitude
    OSM\Marker()\Location\Longitude=Longitude
    OSM\Marker()\color=color
  EndProcedure
  
  ; Draw All marker on the screen !
  Procedure  DrawMarker()
    Protected Pixel.Pixel
    ForEach OSM\Marker()
      If OSM\Marker()\Location\Latitude<>0 And  OSM\Marker()\Location\Longitude<>0
        GetPixelCoordFromLocation(OSM\Marker()\Location,@Pixel)
        If Pixel\X>0 And Pixel\Y>0 And Pixel\X<GadgetWidth(OSM\Gadget) And Pixel\Y<GadgetHeight(OSM\Gadget) ; Only if visible ^_^
          Pointer(Pixel\X,Pixel\Y,OSM\Marker()\color)
        EndIf 
      EndIf 
    Next
  EndProcedure
  
  ; Draw The Map one the MapGadget
  Procedure DrawMap()
    Static myTimer.i
    If myTimer=0 Or ElapsedMilliseconds()-myTimer>25 ;To limit refreash
      Protected x.i, y.i, nx.i, ny.i, mx.i, my.i, n.i = 0
      Protected deltaX.i, deltaY.i
      
      Protected nImage.i
      Protected x2.i, y2.i
      
      Static Fade.f=0
      StartVectorDrawing(CanvasVectorOutput(OSM\Gadget))
      VectorFont(FontID(OSM\Font), 25)
      
      AddPathBox(0,0,GadgetWidth(OSM\Gadget),GadgetHeight(OSM\Gadget))
      VectorSourceColor(RGBA(255,255, 255, 255)) 
      FillPath()
      
      If TryLockMutex(OSM\MapImageMutex)
        deltaX = OSM\TileSize*(OSM\TargetTile\X - Int(OSM\TargetTile\X))
        deltaY = OSM\TileSize*(OSM\TargetTile\Y - Int(OSM\TargetTile\Y))
        
        mx = GadgetWidth(OSM\Gadget)/2
        my = GadgetHeight(OSM\Gadget)/2
        
        nx = Round(GadgetWidth(OSM\Gadget)/OSM\TileSize,#PB_Round_Up)*2 ;How many tiles
        ny = Round(GadgetHeight(OSM\Gadget)/OSM\TileSize,#PB_Round_Up)*2

        For x = 0 To nx
          For y = 0 To ny
            x2 = x*256 + OSM\DeltaCursor\X   + mx -deltaX-(nx/2)*OSM\TileSize
            y2 = y*256 + OSM\DeltaCursor\Y   + my - deltaY-(ny/2)*OSM\TileSize
            nImage=OSM\MapImage(x,y)\nImage
            If nImage<1
              VectorSourceColor(RGBA(255, 255, 255, 255))
              AddPathBox(x2, y2, OSM\TileSize, OSM\TileSize)
              FillPath()
              VectorSourceColor(RGBA(0, 0, 0, 255))  
              MovePathCursor(x2, y2+20)
              DrawVectorText("Loading")
            ElseIf  (x2 + 256) > 0 And (y2 + 256) > 0 And x2 < GadgetWidth(OSM\Gadget) And y2 < GadgetHeight(OSM\Gadget)
              If IsImage(nImage)
               MovePathCursor(x2,y2)
              DrawVectorImage(ImageID(nImage))
            Else
                VectorSourceColor(RGBA(0, 0, 0, 255))
                MovePathCursor(x2, y2+20)
                DrawVectorText("Error Loading")
              EndIf   
            EndIf
            
          Next
        Next
        UnlockMutex(OSM\MapImageMutex)
      EndIf 
      
      ; Draw Track and Markers
      DrawTrack()
      DrawMarker()
      
      ;Draw Central Cursor
      BeginVectorLayer()
        Pointer(GadgetWidth(OSM\Gadget)/2,GadgetHeight(OSM\Gadget)/2,RGBA(0,0,0,255))
      EndVectorLayer()
      
      ;Display Red Alert If problem with Error Loading
      
      If OSM\ErrorLoadingTile=#True
        Debug "fade"
      EndIf 
      If OSM\ErrorLoadingTile=#True And Fade<100
        Fade=Fade+5
      ElseIf Fade>0
        Fade=Fade-5
      EndIf
      If Fade>0
        AddPathBox(0,0,GadgetWidth(OSM\Gadget),GadgetHeight(OSM\Gadget))
        VectorSourceColor(RGBA(255, 0, 0, Int(Fade))) 
        FillPath()
      EndIf 
      
      StopVectorDrawing()
      myTimer=ElapsedMilliseconds()
    EndIf 
  EndProcedure
  
  Procedure RefreshMap()
    ConstructMap()
    DrawMap()
  EndProcedure
  
  ;CallBack to return Location after movement 
  Procedure SetCallBackLocation(CallBackLocation.i)
    OSM\CallBackLocation=CallBackLocation
  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)
    OSM\Position\X = OSM\TargetTile\X * OSM\TileSize ;Convert X, Y in tile.decimal into real pixels
    OSM\Position\Y = OSM\TargetTile\Y * OSM\TileSize 
  EndProcedure
  

  
  Procedure  ZoomToArea()
    ;Source => http://gis.stackexchange.com/questions/19632/how-to-calculate-the-optimal-zoom-level-to-display-two-or-more-points-on-a-map
    ;bounding box in long/lat coords (x=long, y=lat)
    Protected MinY.d,MaxY.d,MinX.d,MaxX.d
    ForEach OSM\track()
      If ListIndex(OSM\track())=0 Or OSM\track()\Longitude<MinX
        MinX=OSM\track()\Longitude
      EndIf
      If ListIndex(OSM\track())=0 Or OSM\track()\Longitude>MaxX
        MaxX=OSM\track()\Longitude
      EndIf
      If ListIndex(OSM\track())=0 Or OSM\track()\Latitude<MinY
        MinY=OSM\track()\Latitude
      EndIf
      If ListIndex(OSM\track())=0 Or OSM\track()\Latitude>MaxY
        MaxY=OSM\track()\Latitude
      EndIf
    Next 
    Protected DeltaX.d=MaxX-MinX                            ;assumption ! In original code DeltaX have no source
    Protected centerX.d=MinX+DeltaX/2                       ; assumption ! In original code CenterX have no source
    Protected paddingFactor.f= 1.2                          ;paddingFactor: this can be used to get the "120%" effect ThomM refers to. Value of 1.2 would get you the 120%.
    
    ry1.d = Log((Sin(Radian(MinY)) + 1) / Cos(Radian(MinY)))
    ry2.d = Log((Sin(Radian(MaxY)) + 1) / Cos(Radian(MaxY)))
    ryc.d = (ry1 + ry2) / 2                                 
    centerY.d = Degree(ATan(SinH(ryc)))                     
    
    resolutionHorizontal.d = DeltaX / GadgetWidth(OSM\Gadget)
    
    vy0.d = Log(Tan(#PI*(0.25 + centerY/360)));
    vy1.d = Log(Tan(#PI*(0.25 + MaxY/360)))   ;
    viewHeightHalf.d = GadgetHeight(OSM\Gadget)/2;
    zoomFactorPowered.d = viewHeightHalf / (40.7436654315252*(vy1 - vy0))
    resolutionVertical.d = 360.0 / (zoomFactorPowered * OSM\TileSize)    
    If resolutionHorizontal<>0 And resolutionVertical<>0
      resolution.d = Max(resolutionHorizontal, resolutionVertical)* paddingFactor
      zoom.d = Log(360 / (resolution * OSM\TileSize))/Log(2)
      
      lon.d = centerX;
      lat.d = centerY;
      
      SetLocation(lat,lon, Round(zoom,#PB_Round_Down))
    Else
      SetLocation(OSM\TargetLocation\Latitude,OSM\TargetLocation\Longitude, 15)
    EndIf
    
  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)
    OSM\Position\X = OSM\TargetTile\X * OSM\TileSize ;Convert X, Y in tile.decimal into real pixels
    OSM\Position\Y = OSM\TargetTile\Y * OSM\TileSize 
    ConstructMap()
    DrawMap()
  EndProcedure
  
  ;TODO A Better way to init and configure 
  Procedure InitOSM(window.i=0)
    Debug GetTemporaryDirectory()
    OSM\HDDCachePath = GetTemporaryDirectory()
    Debug OSM\HDDCachePath
    OSM\ServerURL = "http://tile.openstreetmap.org/" ;"https://tile.thunderforest.com/cycle/";
    OSM\ZoomMin = 0
    OSM\ZoomMax = 18
    OSM\StartCursor\X = - 1
    OSM\TileSize = 256
    OSM\MemCache\Mutex=CreateMutex()
    ;OSM\MemCache\Semaphore=CreateSemaphore(1)
    OSM\MapImageMutex=CreateMutex()
    OSM\MapImageSemaphore=CreateSemaphore(8)
    OSM\Window=window
    OSM\EditMarkerIndex=-1
    OSM\Font=LoadFont(#PB_Any, "Comic Sans MS", 20, #PB_Font_Bold)
    OSM\ConstructMapMutex=CreateMutex()
  EndProcedure
  
  Procedure Event(Event.l)
    Protected Gadget.i
    Protected tx.d, ty.d
    Protected OldX.i, OldY.i
    If IsGadget(OSM\Gadget) And GadgetType(OSM\Gadget) = #PB_GadgetType_Canvas 
      Select Event
        Case PB_Event_Timer
          DrawMap()
        Case #EvenementStartRefresh 
          ;Debug "Start"
          AddWindowTimer(OSM\Window,1, 500)
        Case #EvenementStopRefresh 
          ;Debug "Stop"
          Debug "draw Stop Refresh"
          DrawMap()
          RemoveWindowTimer(OSM\Window,1)
        Case #PB_Event_Timer
          If EventTimer()=1;=#Timer_Draw
            DrawMap()
          EndIf 
        Case #PB_Event_Gadget ;{
          Gadget = EventGadget()
          Select Gadget
            Case OSM\Gadget
              Select EventType()
                Case #PB_EventType_RightButtonDown
                  SetLocation(49.0346374511718750,2.0787782669067383,17)
                  RefreshMap()
                  OSM\StartCursor\X=-1
                  OSM\DeltaCursor\X=0
                  OSM\DeltaCursor\Y=0
                Case #PB_EventType_LeftButtonDown
                  ;check if we select a marker
                  Protected Pixel.Pixel
                  ForEach OSM\Marker()
                    GetPixelCoordFromLocation(@OSM\Marker()\Location,@Pixel)
                    If Pixel\X>GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX)-4 And Pixel\X<GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX)+4 And Pixel\Y>GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY)-4 And Pixel\Y<GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY)+4
                      OSM\EditMarkerIndex=ListIndex(OSM\Marker())  
                      Break
                    EndIf  
                  Next
                  ;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\EditMarkerIndex>-1 ;move Marker
                    OSM\DeltaCursor\X=GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX)-OSM\StartCursor\X
                    OSM\DeltaCursor\Y=GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY)-OSM\StartCursor\Y
                    tx=(OSM\DeltaCursor\X/256)
                    ty=(OSM\DeltaCursor\Y/256)
                    OSM\DeltaCursor\X=0
                    OSM\DeltaCursor\Y=0
                    OSM\StartCursor\X = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                    OSM\StartCursor\Y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) 
                    SelectElement(OSM\Marker(),OSM\EditMarkerIndex)
                    Protected Tile.Tile
                    LatLon2XY(@OSM\Marker()\Location,@Tile)
                    TileTranslate(@Tile,-tx,-ty)
                    XY2LatLon(@Tile,@OSM\Marker()\Location)
                    
                  Else ;Move Map
                    If OSM\StartCursor\X<>-1
                      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
                  EndIf 
                  DrawMap()
                  
                Case #PB_EventType_LeftButtonUp
                  DrawMap()
                  If OSM\EditMarkerIndex>-1
                    OSM\EditMarkerIndex=-1
                    OSM\StartCursor\X=-1
                  Else ;Move Map
                    tx=(OSM\DeltaCursor\X/OSM\TileSize)
                    ty=(OSM\DeltaCursor\Y/OSM\TileSize)
                    OSM\DeltaCursor\X=0
                    OSM\DeltaCursor\Y=0
                    OSM\StartCursor\X=-1
                    TileTranslate(@OSM\TargetTile,tx,ty)
                    XY2LatLon(@OSM\TargetTile,@OSM\TargetLocation)
                    ConstructMap()
                    If OSM\CallBackLocation>0
                      CallFunctionFast(OSM\CallBackLocation,@OSM\TargetLocation)
                    EndIf 
                  EndIf 
              EndSelect
          EndSelect
          
      EndSelect
    Else
      MessageRequester("Module OSM", "You must use MapGadget before", #PB_MessageRequester_Ok )
      End
    EndIf  
  EndProcedure
EndModule

;-Exemple
CompilerIf #PB_Compiler_IsMainFile 
  InitNetwork()
  
  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
    #Gdt_LoadGpx
    #Gdt_AddMarker
  EndEnumeration
  
  Structure Location
    Longitude.d
    Latitude.d
  EndStructure
  
  Procedure UpdateLocation(*Location.Location)
    SetGadgetText(#String_0,StrD(*Location\Latitude))
    SetGadgetText(#String_1,StrD(*Location\Longitude))
    ProcedureReturn 0
  EndProcedure
  
  Procedure ResizeAll()
    ResizeGadget(#Map,10,10,WindowWidth(#Window_0)-198,WindowHeight(#Window_0)-59):OSM::RefreshMap()
    ResizeGadget(#Text_1,WindowWidth(#Window_0)-170,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#Button_0,WindowWidth(#Window_0)-150,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#Button_1,WindowWidth(#Window_0)-90,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#Button_2,WindowWidth(#Window_0)-110,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#Button_3,WindowWidth(#Window_0)-110,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#Text_2,WindowWidth(#Window_0)-170,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#Button_4,WindowWidth(#Window_0)-150,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#Button_5,WindowWidth(#Window_0)-100,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#Text_3,WindowWidth(#Window_0)-170,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#String_0,WindowWidth(#Window_0)-100,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#String_1,WindowWidth(#Window_0)-100,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#Text_4,WindowWidth(#Window_0)-170,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#Gdt_AddMarker,WindowWidth(#Window_0)-170,#PB_Ignore,#PB_Ignore,#PB_Ignore)
    ResizeGadget(#Gdt_LoadGpx,WindowWidth(#Window_0)-170,#PB_Ignore,#PB_Ignore,#PB_Ignore)
  EndProcedure
  
  If OpenWindow(#Window_0, 260, 225, 700, 571, "OpenStreetMap",  #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_TitleBar | #PB_Window_ScreenCentered | #PB_Window_SizeGadget)
    OSM::InitOSM()
    LoadFont(0, "Wingdings", 12)
    LoadFont(1, "Arial", 12, #PB_Font_Bold)
    
    OSM::MapGadget(#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, "")
    ButtonGadget(#Gdt_AddMarker, 530, 280, 150, 30, "Add Marker")
    ButtonGadget(#Gdt_LoadGpx, 530, 310, 150, 30, "Load GPX")
    
    Define Event.i, Gadget.i, Quit.b = #False
    Define pfValue.d
    
    OSM::SetLocation(49.04599, 2.03347, 17)
    OSM::SetCallBackLocation(@UpdateLocation())
    OSM::RefreshMap()
    
    
    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)
            Case #Gdt_LoadGpx
              OSM::LoadGpxFile(OpenFileRequester("Choisissez un fichier à charger", "", "*.gpx", 0))
              OSM::ZoomToArea() ; <-To center the view, and to viex all the track
            Case #Gdt_AddMarker
              OSM:: AddMarker(ValD(GetGadgetText(#String_0)),ValD(GetGadgetText(#String_1)),RGBA(Random(255),Random(255),Random(255),255))
          EndSelect
        Case #PB_Event_SizeWindow
          ResizeAll()
      EndSelect
    Until Quit = #True
  EndIf
CompilerEndIf
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: OpenStreetMap dans un Canvas

Message par djes »

Excellent ! C'est bien plus fluide que le mien ! Va falloir que je regarde comment tu as fait :)

Edit : Punaise, je viens de voir que depuis le début je fais plusieurs StartVectorDrawing()/StopVectorDrawing() ... :/ Je m'étais tellement concentré sur le chargement et les synchros que j'ai complètement zappé ça ! J'ai corrigé sur le GIT, on verra plus tard pour le forum :)
Avatar de l’utilisateur
Ar-S
Messages : 9478
Inscription : dim. 09/oct./2005 16:51
Contact :

Re: OpenStreetMap dans un Canvas

Message par Ar-S »

Vous gérez ^^
rendu et zoom nickel
déplacement avec les flèches nickel.
~~~~Règles du forum ~~~~
⋅.˳˳.⋅ॱ˙˙ॱ⋅.˳Ar-S ˳.⋅ॱ˙˙ॱ⋅.˳˳.⋅
W11x64 PB 6.x
Section HORS SUJET : ICI
LDV MULTIMEDIA : Dépannage informatique & mes Logiciels PB
UPLOAD D'IMAGES : Uploader des images de vos logiciels
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: OpenStreetMap dans un Canvas

Message par Thyphoon »

djes a écrit :Excellent ! C'est bien plus fluide que le mien ! Va falloir que je regarde comment tu as fait :)

Edit : Punaise, je viens de voir que depuis le début je fais plusieurs StartVectorDrawing()/StopVectorDrawing() ... :/ Je m'étais tellement concentré sur le chargement et les synchros que j'ai complètement zappé ça ! J'ai corrigé sur le GIT, on verra plus tard pour le forum :)
Non mais avec ta dernière mise à jour c'est toi qui est le plus fluide ^_^ C'est vraiment très propre ce que tu as fait
Ar-S a écrit :Vous gérez ^^
rendu et zoom nickel
déplacement avec les flèches nickel.
Merci ! Encore un peu de boulot mais franchement on est plus très loin du but :P
Avatar de l’utilisateur
MetalOS
Messages : 1492
Inscription : mar. 20/juin/2006 22:17
Localisation : Lorraine
Contact :

Re: OpenStreetMap dans un Canvas

Message par MetalOS »

Ar-S a écrit :Vous gérez ^^
rendu et zoom nickel
déplacement avec les flèches nickel.
Totalement d'accord c'est nickel.
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: OpenStreetMap dans un Canvas

Message par djes »

Merci les gars, ça fait du bien d'avoir des encouragements :)
Avatar de l’utilisateur
MetalOS
Messages : 1492
Inscription : mar. 20/juin/2006 22:17
Localisation : Lorraine
Contact :

Re: OpenStreetMap dans un Canvas

Message par MetalOS »

Il est possible de projeter des points en chargent un fichier json qui contient des coordonnées géographiques ?
Répondre