PBMap - Cartes OSM, Here, Geoserver dans un Canvas

Programmation d'applications complexes
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 »

Pour le proxy on va toujours avoir le problème, je pense qu'il faut utiliser curl. Je pense qu'il y a un souci avec le code et les index, car ceux-ci peuvent changer en fonction de l'ajout/retrait d'éléments. Il vaut mieux passer par des pointeurs. Je suis dessus...
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: OpenStreetMap dans un Canvas

Message par Thyphoon »

djes a écrit :Pour le proxy on va toujours avoir le problème, je pense qu'il faut utiliser curl. Je pense qu'il y a un souci avec le code et les index, car ceux-ci peuvent changer en fonction de l'ajout/retrait d'éléments. Il vaut mieux passer par des pointeurs. Je suis dessus...
Pour le proxy je comprends pas pourquoi Fred ne l'a pas intégré ça :( si il y en a qui veulent appuyer ma demande : http://www.purebasic.fr/english/viewtop ... =3&t=66080

Ok ça marche 8) ... si ça peut t'aider voici un teste que j'ai fait (c'est juste un teste car pas super satisfait) . On perd de la fluidité .... mais la carte s'affiche et il y a moins de souci avec les index
j'utilise un PostEvent (le windowstimer ça n'a pas marché) pour rafraichir la map

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, 
;************************************************************** 

InitNetwork()

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

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

Module OSM
  
  
  #USEPROXY = #True
  UsePNGImageDecoder()
  UsePNGImageEncoder()
  
  Enumeration #PB_Event_FirstCustomValue
    #EvenementStartRefresh
    #EvenementStopRefresh
  EndEnumeration
  
  
  
  CompilerIf #USEPROXY = #True
    IncludeFile("C:\Users\lebrun_y_413\Documents\Developpement\Purebasic\includes Share\http.pbi")
  CompilerEndIf
  
  Structure Location
    Longitude.d
    Latitude.d
  EndStructure
  
  Structure Tile
    X.d
    Y.d
  EndStructure
  
  Structure Pixel
    X.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
  
  Structure OSM
    Windows.i                               ;
    Gadget.i                                ; Canvas Gadget Id 
    TargetLocation.Location                 ; Latitude and Longitude from focus point
    TargetTile.Tile                         ; Focus Tile coord
    
    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
    
    HDDCachePath.S                          ; path where to load an 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
    MapImageSemaphore.i                       ; Semaphore to control Thread
    
    
    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
    
    
  EndStructure
  
  Global OSM.OSM
  
  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
    ; 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
  
  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)
      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  
      UnlockMutex(OSM\MemCache\Mutex)
      ProcedureReturn Index
    Else
      Debug "NO ADD TILE TO MEM CACHE BECAUSE BAD IMAGE"
    EndIf 
  EndProcedure
  
  Procedure.i GetTileFromMem(Zoom.i, XTile.i, YTile.i)
    Protected nImage.i
    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
        nImage=OSM\MemCache\Image()\nImage
        Debug "Load From MEM Tile X : " + Str(XTile) + " ; Tile Y : " + Str(YTile)+" IsImage:"+Str(IsImage(nImage))
        
        Break;
             ;TODO Find a better way to clean Image in memory  
             ; Clean Image in memory  
      ElseIf Zoom<>OSM\MemCache\Image()\Zoom 
        FreeImage(OSM\MemCache\Image()\nImage)
        DeleteElement(OSM\MemCache\Image())
      ElseIf ( OSM\MemCache\Image()\XTile<OSM\TargetTile\X-5 And OSM\MemCache\Image()\XTile>OSM\TargetTile\X+5 And OSM\MemCache\Image()\YTile<OSM\TargetTile\Y-5 And OSM\MemCache\Image()\YTile>OSM\TargetTile\Y+5 )
        FreeImage(OSM\MemCache\Image()\nImage)
        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"
    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
  
  Procedure.i GetTileFromWeb(Zoom.i, XTile.i, YTile.i)
    Protected *Buffer
    Protected nImage.i
    Protected CacheFile.s = "OSM_" + Str(Zoom) + "_" + Str(XTile) + "_" + Str(YTile) + ".png"
    Protected TileURL.s = OSM\ServerURL + Str(Zoom) + "/" + Str(XTile) + "/" + Str(YTile) + ".png"
    ;Debug "DOWNLOAD : " + psURL
    CompilerIf #USEPROXY = #True
      Protected http.HTTP_Query
      HTTP_proxy(@http, "spxy.bpi.fr", 3128)
      HTTP_DownloadToMem(@http, TileURL)
      nImage=CatchImage(#PB_Any, http\data, MemorySize(http\data))
      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)
      EndIf
    CompilerElse
      *Buffer = ReceiveHTTPMemory(TileURL)
      If *Buffer
        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
        EndIf
      Else
        Debug "Problem loading :" + TileURL
      EndIf
    CompilerEndIf
    
    ProcedureReturn nImage
  EndProcedure
  
  Procedure GetTile(*Index.ImgMemCach)
    Protected Zoom.i, XTile.i, YTile.i
    
    Zoom=*Index\Zoom
    XTile=*Index\XTile
    YTile=*Index\YTile
    
    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 
    
    *Index\nImage=nImage
    
    SignalSemaphore(OSM\MapImageSemaphore)
  EndProcedure
  
  Procedure ThreadConstructMap(z.i)
    PostEvent(#EvenementStartRefresh)
    Protected x.i, y.i
    Protected nx.i = (GadgetWidth(OSM\Gadget)/OSM\TileSize)*2 ;How many tiles
    Protected ny.i = (GadgetHeight(OSM\Gadget)/OSM\TileSize)*2
    
    Protected tx.i = Int(OSM\TargetTile\X)
    Protected ty.i = Int(OSM\TargetTile\Y)
    Protected mx.i=Int(nx/2)
    Protected my.i=Int(ny/2)
    LockMutex(OSM\MapImageMutex)
    For y = 0 To nx
      For x = 0 To ny
        WaitSemaphore(OSM\MapImageSemaphore)
        
        OSM\MapImage(x,y)\XTile=tx+x-mx
        OSM\MapImage(x,y)\YTile=ty+y-my
        OSM\MapImage(x,y)\Zoom=OSM\Zoom
        Protected th.i
        th.i=CreateThread(@GetTile(),@OSM\MapImage(x,y))
      Next
    Next
    UnlockMutex(OSM\MapImageMutex)
    WaitThread(th)
    PostEvent(#EvenementStopRefresh)
  EndProcedure
  
  Procedure ConstructMap()
    
    CreateThread(@ThreadConstructMap(),0)
  EndProcedure
  
  Procedure DrawMap()
    Static myTimer.i
    If myTimer=0 Or ElapsedMilliseconds()-myTimer>250 ;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
      LockMutex(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 = (GadgetWidth(OSM\Gadget)/OSM\TileSize)*2 ;How many tiles
      ny = (GadgetWidth(OSM\Gadget)/OSM\TileSize)*2
      
      
      StartDrawing(CanvasOutput(OSM\Gadget))
      Box(0, 0, GadgetWidth(OSM\Gadget), GadgetHeight(OSM\Gadget), RGB(255, 255, 255))
      
      For y = 0 To nx
        For x = 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=0
            DrawText( x2, y2+20, "Loading")
            
          ElseIf IsImage(nImage) And (x2 + 256) > 0 And (y2 + 256) > 0 And x2 < GadgetWidth(OSM\Gadget) And y2 < GadgetHeight(OSM\Gadget)
            
            DrawImage(ImageID(nImage), x2,y2)
            
          Else
            DrawText( x2, y2+20, "Error to Display Image")
          EndIf       
          
        Next
      Next
      UnlockMutex(OSM\MapImageMutex)
      Circle(GadgetWidth(OSM\Gadget)/2, GadgetHeight(OSM\Gadget)/2, 5, RGB(Random(255),Random(255),Random(255)))
      ;DrawText(0, 0, "DeltaCursorX : " + Str(OSM\DeltaCursor\X) + " deltaX : " + Str(deltaX) + " Tile X : " + StrD(OSM\TargetTile\X))
      DrawText(0, 16, "Image loaded:"+Str(ListSize(OSM\MemCache\Image())))
      
      ; Draw Track
      Protected Pixel.Pixel
      Protected Location.Location
      
      ForEach OSM\track()
        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()
      myTimer=ElapsedMilliseconds()
    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 
  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
  
  Procedure InitOSM()
    Debug GetTemporaryDirectory()
    OSM\HDDCachePath = GetTemporaryDirectory()
    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\Windows=0
    AddWindowTimer(OSM\Windows,1, 500)
  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 #EvenementStartRefresh 
          ;Debug "Start"
          AddWindowTimer(OSM\Windows,1, 500)
        Case #EvenementStopRefresh 
          ;Debug "Stop"
          DrawMap()
          
          ;RemoveWindowTimer(OSM\Windows, 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_LeftButtonDown
                  ;Mem cursor Coord
                  OSM\StartCursor\X = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                  OSM\StartCursor\Y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) 
                Case #PB_EventType_MouseMove 
                  If OSM\StartCursor\X<>-1
                    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 
                  DrawMap()
                Case #PB_EventType_LeftButtonUp
                  DrawMap()
                  tx=(OSM\DeltaCursor\X/256)
                  ty=(OSM\DeltaCursor\Y/256)
                  OSM\DeltaCursor\X=0
                  OSM\DeltaCursor\Y=0
                  OSM\StartCursor\X=-1
                  TileTranslate(@OSM\TargetTile,tx,ty)
                  XY2LatLon(@OSM\TargetTile,@OSM\TargetLocation)
                  ConstructMap()
                  
              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.0346374511718750,2.0787782669067383,17)
  OSM::ConstructMap()
  OSM::DrawMap()
  
  OSM::LoadGpxFile("Roller.gpx")
  
  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
MetalOS
Messages : 1498
Inscription : mar. 20/juin/2006 22:17
Localisation : Lorraine
Contact :

Re: OpenStreetMap dans un Canvas

Message par MetalOS »

Non, je vais tester quand je rentre du travail.
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 »

Je pense qu'il ne l'a pas implémenté parce que c'est pour des développements avancés, et que normalement le système devrait le prendre en compte. Et puis, maintenant, on peut intégrer directement les fonctions de curl dans le code sans dll, alors on aurait tort de s'en priver, d'autant que ça va beaucoup plus loin...
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 »

Je n'ai pas pu bosser encore comme je le voulais, voici un WIP de débogage, où j'ai mélangé nos deux codes. Le threading est plus ou moins désactivé, j'étais en train d'essayer de comprendre pourquoi j'ai un affichage avant le chargement et pourquoi l'affichage n'affiche rien.

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

Module OSM
  #USEPROXY = #False
  
  CompilerIf #USEPROXY = #True
    ;  IncludeFile("C : \Users\lebrun_y_413\Documents\Developpement\Purebasic\includes Share\http.pbi")
    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:", "")
    IncludeFile("ReceiveHTTPToMemory.pbi")
  CompilerEndIf
  
  Structure Location
    Longitude.d
    Latitude.d
  EndStructure
  
  Structure Tile
    X.d
    Y.d
  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
  
  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
    StartCursor.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
    MapImageMutex.i                         ; Mutex to lock
    MapImageSemaphore.i                     ; Semaphore to control Thread
    
    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\StartCursor\X = - 1
    OSM\TileSize = 256
    OSM\MemCache\Mutex = CreateMutex()
    ;OSM\MemCache\Semaphore = CreateSemaphore(1)
    OSM\MapImageMutex = CreateMutex()
    OSM\MapImageSemaphore = CreateSemaphore(1)
  EndProcedure
  
  Procedure OSMGadget(Gadget.i, X.i, Y.i, Width.i, Height.i)
    If Gadget = #PB_Any
      OSM\Gadget = CanvasGadget(OSM\Gadget, X, Y, Width, Height)
    Else
      OSM\Gadget = Gadget
      CanvasGadget(OSM\Gadget, X, Y, Width, Height)
    EndIf 
  EndProcedure
  
  Procedure LatLon2XY(*Location.Location, *Tile.Tile)
    Protected n.d = Pow(2.0, OSM\Zoom)
    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)
      ;Add to the list
      ;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  
      UnlockMutex(OSM\MemCache\Mutex)
      ;ProcedureReturn Index
    Else
      Debug "Tile not added to mem cache"
    EndIf 
  EndProcedure
  
  Procedure.i GetTileFromMem(Zoom.i, XTile.i, YTile.i)
    Protected nImage.i
    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 And OSM\MemCache\Image()\nImage > 0
        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"
    
    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) + " nImage:" + Str(nImage)
        AddTileToMemCache(Zoom, XTile, YTile, nImage)
        ProcedureReturn nImage
      EndIf 
      
    EndIf 
    ProcedureReturn #False
  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"
    
    CompilerIf #USEPROXY = #True
      *Buffer = ReceiveHTTPToMemory(TileURL, ProxyURL$, ProxyPort$, ProxyUser$, ProxyPassword$)
    CompilerElse
      *Buffer = ReceiveHTTPMemory(TileURL)  ;TODO to thread by using #PB_HTTP_Asynchronous
    CompilerEndIf
    
    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))
        ;CallDebugger    
      EndIf
    Else
      Debug "Problem loading from web " + TileURL  
    EndIf      
    
    ProcedureReturn nImage
    
  EndProcedure
  
  Procedure GetTile(*ptr)
    Protected Zoom.i, XTile.i, YTile.i
    Protected nImage.i
    
    LockMutex(OSM\MapImageMutex)
    ChangeCurrentElement(OSM\MapImageIndex(), *ptr)
    Zoom  = OSM\MapImageIndex()\Zoom
    XTile = OSM\MapImageIndex()\XTile
    YTile = OSM\MapImageIndex()\YTile
    ;UnlockMutex(OSM\MapImageMutex)
    
    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" + Str(XTile) + " Y " + Str(YTile) + " Zoom "+Str(Zoom)
          ProcedureReturn #False
        EndIf
      EndIf
    EndIf
    
    ;LockMutex(OSM\MapImageMutex)
    If ChangeCurrentElement(OSM\MapImageIndex(), *ptr)
      OSM\MapImageIndex()\nImage = nImage
    Else
      Debug "Error GetTile procedure : list doesn't have current element" ; TODO Check Why !!!
    EndIf 
    UnlockMutex(OSM\MapImageMutex)
    ;SignalSemaphore(OSM\MapImageSemaphore)
    
  EndProcedure
  
  Procedure ThreadConstructMap(z.i)
    Protected x.i, y.i
    Protected Index.i
    
    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
    
    Protected tx = Int(OSM\TargetTile\X)  ;Don't forget the Int() !
    Protected ty = Int(OSM\TargetTile\Y)
    
    LockMutex(OSM\MapImageMutex)
    ClearList(OSM\MapImageIndex())
    
    Debug " Constructing"
    For y = ty - ny To ty + ny
      For x = tx - nx To tx + nx
        ;WaitSemaphore(OSM\MapImageSemaphore)
        ;LockMutex(OSM\MapImageMutex)
        LastElement(OSM\MapImageIndex())
        AddElement(OSM\MapImageIndex())
        OSM\MapImageIndex()\XTile = x
        OSM\MapImageIndex()\YTile = y
        OSM\MapImageIndex()\Zoom = OSM\Zoom
        ;UnlockMutex(OSM\MapImageMutex)
        CreateThread(@GetTile(), @OSM\MapImageIndex())
      Next
    Next
    UnlockMutex(OSM\MapImageMutex)
    
  EndProcedure
  
  Procedure ConstructMap()
  ;  CreateThread(@ThreadConstructMap(), 0)
    ThreadConstructMap(0) 
  EndProcedure
  
  Procedure DrawMap()
    Protected x.i, y.i, nx.i, ny.i, CenterX.i, CenterY.i, n.i = 0
    Protected deltaX.i, deltaY.i
    Protected x2.i, y2.i
    
    deltaX = OSM\Position\X - Int(OSM\TargetTile\X) * OSM\TileSize  ;Get the position into the tile
    deltaY = OSM\Position\Y - Int(OSM\TargetTile\Y) * OSM\TileSize
    
    CenterX = GadgetWidth(OSM\Gadget) / 2
    CenterY = GadgetHeight(OSM\Gadget) / 2
    nx = CenterX / OSM\TileSize ;How many tiles around the point
    ny = CenterY / OSM\TileSize
    
    LockMutex(OSM\MapImageMutex)
    ResetList(OSM\MapImageIndex())
    
    StartDrawing(CanvasOutput(OSM\Gadget))
    Box(0, 0, GadgetWidth(OSM\Gadget), GadgetHeight(OSM\Gadget), RGB(255, 255, 255))
    
    Debug " Drawing"
    
    For y = - ny To ny
      For x = - nx To nx
        If NextElement(OSM\MapImageIndex())
                    
          x2 = CenterX - deltaX + x * OSM\TileSize
          y2 = CenterY - deltaY + y * OSM\TileSize
          nImage = OSM\MapImageIndex()\nImage
          
          If IsImage(nImage)            
            DrawImage(ImageID(nImage), x2, y2)
            DrawText( x2, y2, Str(x) + ", " + Str(y))
          Else
            ;No image
            Box(x2, y2, OSM\TileSize, OSM\TileSize, RGB($EE, $EE, $EE))
            Line(x2, y2, OSM\TileSize, OSM\TileSize, RGB($AA, $AA, $AA))
            Line(x2, y2 + OSM\TileSize, OSM\TileSize, -OSM\TileSize, RGB($AA, $AA, $AA))
            DrawText(x2 + OSM\TileSize / 2 - TextWidth("Loading")/2, y2 + OSM\TileSize / 2 - TextHeight("Loading") / 2, "Loading", RGB($AA, $AA, $AA), RGB($EE, $EE, $EE))
          EndIf       
          
          ;n = n + 1
        Else
          Debug" Error, no next element"
          Break 2
        EndIf 
      Next
    Next
    Circle(CenterX, CenterY, 5, #Red)
    UnlockMutex(OSM\MapImageMutex)
    
    ; Draw Track
    Protected Pixel.Pixel
    Protected Location.Location
    n=0;
    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 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 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
  
  Procedure Event(Event.l)
    Protected Gadget.i
    Protected tx.i, ty.i
    Protected OldX.i, OldY.i
    If IsGadget(OSM\Gadget) And GadgetType(OSM\Gadget) = #PB_GadgetType_Canvas 
      Select Event
        Case #PB_Event_Gadget ;{
          Gadget = EventGadget()
          Select Gadget
            Case OSM\Gadget
              Select EventType()
                Case #PB_EventType_LeftButtonDown
                  ;Mem cursor Coord
                  OSM\StartCursor\X = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                  OSM\StartCursor\Y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) 
                Case #PB_EventType_MouseMove 
                  If OSM\StartCursor\X <> - 1
                    tx = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) - OSM\StartCursor\X
                    ty = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) - OSM\StartCursor\Y
                    OldX = OSM\Position\X : OldY = OSM\Position\Y
                    OSM\Position\X - tx
                    OSM\Position\Y - ty
                    ;Moved to a new tile ?
                    If (OSM\Position\X / OSM\TileSize) <> (OldX / OSM\TileSize) Or (OSM\Position\Y / OSM\TileSize) <> (OldY / OSM\TileSize) 
                      Debug "--- New tile"
                      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)
                      Debug "OSM\TargetTile\X " + StrD(OSM\TargetTile\X) + " ; OSM\TargetTile\Y "  + StrD(OSM\TargetTile\Y) 
                      ConstructMap()
                    EndIf
                    DrawMap()
                    OSM\StartCursor\X = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                    OSM\StartCursor\Y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY)                   
                  EndIf 
                Case #PB_EventType_LeftButtonUp
                  OSM\StartCursor\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)
                  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::ConstructMap()
  Delay(1000)
  OSM::DrawMap()
  ;OSM::SetLocation(49.0361165, 2.0456982)
  
  Repeat
    Event = WaitWindowEvent()
    
    OSM::Event(Event)
    Select Event
      Case #PB_Event_CloseWindow : Quit = 1
      Case #PB_Event_Gadget ;{
        Gadget = EventGadget()
        Select Gadget
          Case #Button_4
            OSM::SetZoom(1)
          Case #Button_5
            OSM::SetZoom( - 1)
        EndSelect
    EndSelect
  Until Quit = #True
EndIf
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: OpenStreetMap dans un Canvas

Message par Thyphoon »

djes a écrit :Je n'ai pas pu bosser encore comme je le voulais, voici un WIP de débogage, où j'ai mélangé nos deux codes. Le threading est plus ou moins désactivé, j'étais en train d'essayer de comprendre pourquoi j'ai un affichage avant le chargement et pourquoi l'affichage n'affiche rien.
C'est prometteur ^_^ Je pars demain matin pour 3 jours je sais pas si j'aurais beaucoup de temps mais j'essayerai d'avancer sur des petits trucs en attendant. et j'éspère bien dimanche avoir du temps. On a quand bien bien avancé on est plus très loin :mrgreen:
Avatar de l’utilisateur
MetalOS
Messages : 1498
Inscription : mar. 20/juin/2006 22:17
Localisation : Lorraine
Contact :

Re: OpenStreetMap dans un Canvas

Message par MetalOS »

Ouais c'est kler que vous avez bien bossé. Dommage qu'on ne trouve pas en natif un MapGadget() en natif a PB qui utilise OpenStreetMap.
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: OpenStreetMap dans un Canvas

Message par Thyphoon »

MetalOS a écrit :Ouais c'est kler que vous avez bien bossé. Dommage qu'on ne trouve pas en natif un MapGadget() en natif a PB qui utilise OpenStreetMap.
le but ici est d'essayer de donné un MapGadget le plus simple possible a utiliser. Et on est en bonne voit !! ^_^
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 »

Je ne sais pas si j'aurai le temps de coder ces jours-ci, en tous cas dans la tête oui. Prochaine étape, virer le drawmap (ou plutôt, le fusionner avec le constructmap), créer une liste des choses à dessiner (liste effacée en cas de déplacement), et faire le dessin en threadé (avec la gestion du chargement en threadé idem).
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: OpenStreetMap dans un Canvas

Message par Thyphoon »

djes a écrit :Je ne sais pas si j'aurai le temps de coder ces jours-ci, en tous cas dans la tête oui. Prochaine étape, virer le drawmap (ou plutôt, le fusionner avec le constructmap), créer une liste des choses à dessiner (liste effacée en cas de déplacement), et faire le dessin en threadé (avec la gestion du chargement en threadé idem).
Pas beaucoup de temps ce week-end moi aussi :? . Tu crois que c'est bon de fusionner le construcmap et le drawmap ? je crois que si je les avais séparer c'était pour éviter lorsqu'on fait glisser la carte de passer par le construcmap qui peut être gourmand(surtout si on doit télécharger les images du web)... mais tu as carte blanche, tu as peut être une idée en tête :D

Mais j'ai quand même eu le temps de faire cette fonction qui permet de centrer la carte sur un ensemble de point. Il y a encore une erreur sur le calcul du zoom mais je suis pas loin... :roll:
Edit: C'est bon fonction mis à jour ça marche super ^_^

Code : Tout sélectionner

    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
  
   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);
    
   resolution.d = Max(resolutionHorizontal, resolutionVertical)* paddingFactor;
   zoom.d = Log(360 / (resolution * OSM\TileSize))/Log(2)

   lon.d = centerX;
   lat.d = centerY;

    Debug "lat:"+StrD(lat)
    Debug "lon:"+StrD(lon)
    Debug "zoom:"+StrD(zoom)
    SetLocation(lat,lon, Round(zoom,#PB_Round_Up))
EndProcedure
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 »

Je vais regarder après. Sinon pour la fusion, oui, je pense que c'est la voie à suivre pour mieux séparer le chargement de l'affichage. En gros, le maître est l'utilisateur (thread principal)->calcul des tuiles à afficher->les tuiles sont indépendantes autant en chargement qu'en affichage (un thread par tuile qui peut s'arrêter si l'utilisateur bouge). Je m'en occupe dès que j'ai un peu de temps :)
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: OpenStreetMap dans un Canvas

Message par Thyphoon »

djes a écrit :Je vais regarder après. Sinon pour la fusion, oui, je pense que c'est la voie à suivre pour mieux séparer le chargement de l'affichage. En gros, le maître est l'utilisateur (thread principal)->calcul des tuiles à afficher->les tuiles sont indépendantes autant en chargement qu'en affichage (un thread par tuile qui peut s'arrêter si l'utilisateur bouge). Je m'en occupe dès que j'ai un peu de temps :)
ça marche :mrgreen: Tient j'ai testé de passer l'affichage en utilisant le VectorDrawing ça permet de faire quelques choses de plus propre pour tracer un chemin :)

si ça t'amuse voilà mon dernier code. Je n'ai pas retouché a la partie affichage je te laisse cette partie.

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 for the first OSM implementation
;                    Fred, Freak and all people who made purebasic what is it !
;************************************************************** 

InitNetwork()

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

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 SetZoom(Zoom.i, mode.i = #PB_Relative)

  Declare LoadGpxFile(file.s);
  Declare ZoomToArea()
  Declare ConstructMap()
  Declare DrawMap()
EndDeclareModule

Module OSM
  
  
  #USEPROXY = #False
  UsePNGImageDecoder()
  UsePNGImageEncoder()
  
  Enumeration #PB_Event_FirstCustomValue
    #EvenementStartRefresh
    #EvenementStopRefresh
  EndEnumeration
  
  
  
  CompilerIf #USEPROXY = #True
    IncludeFile("C:\Users\lebrun_y_413\Documents\Developpement\Purebasic\includes Share\http.pbi")
  CompilerEndIf
  
  Structure Location
    Longitude.d
    Latitude.d
  EndStructure
  
  Structure Tile
    X.d
    Y.d
  EndStructure
  
  Structure Pixel
    X.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
  
  Structure OSM
    Windows.i                               ;
    Gadget.i                                ; Canvas Gadget Id 
    TargetLocation.Location                 ; Latitude and Longitude from focus point
    TargetTile.Tile                         ; Focus Tile coord
    
    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
    
    HDDCachePath.S                          ; path where to load an 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
    MapImageSemaphore.i                       ; Semaphore to control Thread
    
    
    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
    
    
  EndStructure
  
  Global OSM.OSM
  
  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
    ; 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
  
  Procedure distance(*posA.Location,*posB.Location)
  Protected R.l = 6371; // km
Protected dLat.d = Radian(*posB\Latitude-*posA\Latitude);
Protected dLon.d = Radian(*posB\Longitude-*posA\Longitude)
Protected lat1.d = Radian(*posA\Latitude);
Protected lat2.d = Radian(*posB\Latitude);

Protected a.d = Sin(dLat/2) * Sin(dLat/2) + Sin(dLon/2) * Sin(dLon/2) * Cos(lat1) * Cos(lat2); 
Protected c.d = 2 * ATan2(Sqr(a), Sqr(1-a)); 
Protected distance.d = R * c                      ;
ProcedureReturn distance
EndProcedure

; HaversineAlgorithm 

    Procedure.d HaversineInKM(*posA.Location,*posB.Location)
      Static eQuatorialEarthRadius.d = 6378.1370;
      Protected dlong.d = Radian(*posB\Longitude - *posA\Longitude);
      Protected dlat.d = Radian(*posB\Latitude - *posA\Latitude);
      Protected a.d = Pow(Sin(dlat / 2), 2) + Cos(Radian(*posA\Latitude)) * Cos(Radian(*posB\Latitude))* Pow(Sin(dlong / 2), 2);
      Protected c.d = 2 * ATan2(Sqr(a), Sqr(1 - a));
      Protected didstance.d = eQuatorialEarthRadius * c;
      ProcedureReturn d;
    EndProcedure
    
        Procedure.d HaversineInM(*posA.Location,*posB.Location)
        ProcedureReturn (1000 * HaversineInKM(@*posA,@*posB));
    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)
      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  
      UnlockMutex(OSM\MemCache\Mutex)
      ProcedureReturn Index
    Else
      Debug "NO ADD TILE TO MEM CACHE BECAUSE BAD IMAGE"
    EndIf 
  EndProcedure
  
  Procedure.i GetTileFromMem(Zoom.i, XTile.i, YTile.i)
    Protected nImage.i
    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
        nImage=OSM\MemCache\Image()\nImage
        Debug "Load From MEM Tile X : " + Str(XTile) + " ; Tile Y : " + Str(YTile)+" IsImage:"+Str(IsImage(nImage))
        
        Break;
             ;TODO Find a better way to clean Image in memory  
             ; Clean Image in memory  
      ElseIf Zoom<>OSM\MemCache\Image()\Zoom 
        FreeImage(OSM\MemCache\Image()\nImage)
        DeleteElement(OSM\MemCache\Image())
      ElseIf ( OSM\MemCache\Image()\XTile<OSM\TargetTile\X-5 And OSM\MemCache\Image()\XTile>OSM\TargetTile\X+5 And OSM\MemCache\Image()\YTile<OSM\TargetTile\Y-5 And OSM\MemCache\Image()\YTile>OSM\TargetTile\Y+5 )
        FreeImage(OSM\MemCache\Image()\nImage)
        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"
    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
  
  Procedure.i GetTileFromWeb(Zoom.i, XTile.i, YTile.i)
    Protected *Buffer
    Protected nImage.i
    Protected CacheFile.s = "OSM_" + Str(Zoom) + "_" + Str(XTile) + "_" + Str(YTile) + ".png"
    Protected TileURL.s = OSM\ServerURL + Str(Zoom) + "/" + Str(XTile) + "/" + Str(YTile) + ".png"
    ;Debug "DOWNLOAD : " + psURL
    CompilerIf #USEPROXY = #True
      Protected http.HTTP_Query
      HTTP_proxy(@http, "spxy.bpi.fr", 3128)
      HTTP_DownloadToMem(@http, TileURL)
      nImage=CatchImage(#PB_Any, http\data, MemorySize(http\data))
      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)
      EndIf
    CompilerElse
      *Buffer = ReceiveHTTPMemory(TileURL)
      If *Buffer
        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
        EndIf
      Else
        Debug "Problem loading :" + TileURL
      EndIf
    CompilerEndIf
    
    ProcedureReturn nImage
  EndProcedure
  
  Procedure GetTile(*Index.ImgMemCach)
    Protected Zoom.i, XTile.i, YTile.i
    
    Zoom=*Index\Zoom
    XTile=*Index\XTile
    YTile=*Index\YTile
    
    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 
    
    *Index\nImage=nImage
    
    SignalSemaphore(OSM\MapImageSemaphore)
  EndProcedure
  
  Procedure ThreadConstructMap(z.i)
    PostEvent(#EvenementStartRefresh)
    Protected x.i, y.i
    Protected nx.i = (GadgetWidth(OSM\Gadget)/OSM\TileSize)*2 ;How many tiles
    Protected ny.i = (GadgetHeight(OSM\Gadget)/OSM\TileSize)*2
    
    Protected tx.i = Int(OSM\TargetTile\X)
    Protected ty.i = Int(OSM\TargetTile\Y)
    Protected mx.i=Int(nx/2)
    Protected my.i=Int(ny/2)
    LockMutex(OSM\MapImageMutex)
    For y = 0 To nx
      For x = 0 To ny
        WaitSemaphore(OSM\MapImageSemaphore)
        
        OSM\MapImage(x,y)\XTile=tx+x-mx
        OSM\MapImage(x,y)\YTile=ty+y-my
        OSM\MapImage(x,y)\Zoom=OSM\Zoom
        Protected th.i
        th.i=CreateThread(@GetTile(),@OSM\MapImage(x,y))
      Next
    Next
    UnlockMutex(OSM\MapImageMutex)
    WaitThread(th)
    PostEvent(#EvenementStopRefresh)
  EndProcedure
  
  Procedure ConstructMap()
    
    CreateThread(@ThreadConstructMap(),0)
  EndProcedure
  
  Procedure DrawMap()
    Static myTimer.i
    If myTimer=0 Or ElapsedMilliseconds()-myTimer>250 ;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
      LockMutex(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 = (GadgetWidth(OSM\Gadget)/OSM\TileSize)*2 ;How many tiles
      ny = (GadgetWidth(OSM\Gadget)/OSM\TileSize)*2
      
      
      StartVectorDrawing(CanvasVectorOutput(OSM\Gadget))
      AddPathBox(0, 0, GadgetWidth(OSM\Gadget), GadgetHeight(OSM\Gadget))
      VectorSourceColor(RGBA(255, 255, 255, 255))
      StrokePath(10)

      
      For y = 0 To nx
        For x = 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=0
            MovePathCursor(x2, y2+20)
            DrawVectorText(  "Loading")
            
          ElseIf IsImage(nImage) And (x2 + 256) > 0 And (y2 + 256) > 0 And x2 < GadgetWidth(OSM\Gadget) And y2 < GadgetHeight(OSM\Gadget)
            MovePathCursor(x2,y2)

            DrawVectorImage(ImageID(nImage))
            
          Else
            MovePathCursor(x2, y2+20)
            DrawVectorText("Error Loading")
          EndIf       
          
        Next
      Next
      UnlockMutex(OSM\MapImageMutex)
      ;Circle(GadgetWidth(OSM\Gadget)/2, GadgetHeight(OSM\Gadget)/2, 5, RGB(Random(255),Random(255),Random(255)))
      ;DrawText(0, 0, "DeltaCursorX : " + Str(OSM\DeltaCursor\X) + " deltaX : " + Str(deltaX) + " Tile X : " + StrD(OSM\TargetTile\X))
      ;DrawText(0, 16, "Image loaded:"+Str(ListSize(OSM\MemCache\Image())))
      
      ; Draw Track
      Protected Pixel.Pixel
      Protected Location.Location
      
      ForEach OSM\track()
        
        If ListIndex(OSM\track())=0
          Location\Latitude=OSM\track()\Latitude
          Location\Longitude=OSM\track()\Longitude 
         
        EndIf 
         If ListIndex(OSM\track())=50
          ; Debug HaversineInKM(@Location,@OSM\track())
          Location\Latitude=OSM\track()\Latitude
          Location\Longitude=OSM\track()\Longitude 
        EndIf 
        
        
        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)
          If ListIndex(OSM\track())=0
            MovePathCursor(x,y)
          Else
            AddPathLine(x,y)
          EndIf 
          ;EndIf
        EndIf 
        
      Next
       VectorSourceColor(RGBA(0, 255, 0, 150))
      StrokePath(10, #PB_Path_RoundEnd|#PB_Path_RoundCorner)

      StopVectorDrawing()
      myTimer=ElapsedMilliseconds()
    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 
  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
  
  
  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);
    
   resolution.d = Max(resolutionHorizontal, resolutionVertical)* paddingFactor;
   zoom.d = Log(360 / (resolution * OSM\TileSize))/Log(2)

   lon.d = centerX;
   lat.d = centerY;

    Debug "lat:"+StrD(lat)
    Debug "lon:"+StrD(lon)
    Debug "zoom:"+StrD(zoom)
    SetLocation(lat,lon, Round(zoom,#PB_Round_Down))
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
  
  Procedure InitOSM()
    Debug GetTemporaryDirectory()
    OSM\HDDCachePath = GetTemporaryDirectory()
    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\Windows=0
    AddWindowTimer(OSM\Windows,1, 500)
  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 #EvenementStartRefresh 
          ;Debug "Start"
          AddWindowTimer(OSM\Windows,1, 500)
        Case #EvenementStopRefresh 
          ;Debug "Stop"
          DrawMap()
          
          ;RemoveWindowTimer(OSM\Windows, 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_LeftButtonDown
                  ;Mem cursor Coord
                  OSM\StartCursor\X = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseX) 
                  OSM\StartCursor\Y = GetGadgetAttribute(OSM\Gadget, #PB_Canvas_MouseY) 
                Case #PB_EventType_MouseMove 
                  If OSM\StartCursor\X<>-1
                    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 
                  DrawMap()
                Case #PB_EventType_LeftButtonUp
                  DrawMap()
                  tx=(OSM\DeltaCursor\X/256)
                  ty=(OSM\DeltaCursor\Y/256)
                  OSM\DeltaCursor\X=0
                  OSM\DeltaCursor\Y=0
                  OSM\StartCursor\X=-1
                  TileTranslate(@OSM\TargetTile,tx,ty)
                  XY2LatLon(@OSM\TargetTile,@OSM\TargetLocation)
                  ConstructMap()
                  
              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
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.0346374511718750,2.0787782669067383,17)

  
  OSM::LoadGpxFile("Rallye.gpx")
  OSM::ZoomToArea()
  
  OSM::ConstructMap()
  OSM::DrawMap()
  
  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
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 »

Ah oui, ça rend bien, effectivement :)
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, j'ai codé le bazar, mais comme tout est threadé, y'a encore des bugs de synchro. Je devrais poster un code demain.
Avatar de l’utilisateur
Thyphoon
Messages : 2706
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: OpenStreetMap dans un Canvas

Message par Thyphoon »

djes a écrit :Bon, j'ai codé le bazar, mais comme tout est threadé, y'a encore des bugs de synchro. Je devrais poster un code demain.
pas de problème j'ai pas eu beaucoup de temps aujourd'hui. :P
Répondre