Page 1 of 1

Get latitude and longitude

Posted: Fri Jan 11, 2019 2:54 am
by betoncu
How can I get the latitude and longitude of a place clicked by a user on the map.
Thanks in advance

Re: Get latitude and longitude

Posted: Fri Jan 11, 2019 6:54 am
by anserkk
Please try the following code.

Remember Google has changed its policies last July. Now, to use maps, Google requires you to register with them and then obtain an API key from them. You should use your Google Maps Javascript API key in your code to view the Map.

Once you have the API key with you, the following code will work. I tested it here with my API key and is working fine.

The code will show the lat and lon of the place when you move your mouse, if you click on a point on the map, it will place a marker where you have clicked. It will also show the lat and lon of the last clicked place above the Map window

Image

Code: Select all

#Include "FiveWin.ch"

//-----------------------------
Function Main()
    ViewGoogleMap()
Return NIL

//-----------------------------------------------------------------------/
Function ViewGoogleMap()
   Local cMapFile:="D:\GMaps.htm", cHtmlContent 

TEXT INTO cHtmlContent
    
<html>
    <head>
        <title>Google Map</title>
        <meta name="viewport" content="initial-scale=1.0">
        <meta charset="utf-8">
        <style>          
          #map { 
            height: 500px;    
            width: 900px;            
          }          
        </style>        
    </head>    
    <body>
        <div id="latclicked"></div>
        <div id="longclicked"></div>
        
        <div id="latmoved"></div>
        <div id="longmoved"></div>
        
        <div style="padding:10px">
            <div id="map"></div>
        </div>
        
        <script type="text/javascript">
        var map;
        
        function initMap() {                            
            var latitude = 27.7172453; // YOUR LATITUDE VALUE
            var longitude = 85.3239605; // YOUR LONGITUDE VALUE
            
            var myLatLng = {lat: latitude, lng: longitude};
            
            map = new google.maps.Map(document.getElementById('map'), {
              center: myLatLng,
              zoom: 14,
              disableDoubleClickZoom: true, // disable the default map zoom on double click
            });
            
            // Update lat/long value of div when anywhere in the map is clicked    
            google.maps.event.addListener(map,'click',function(event) {                
                document.getElementById('latclicked').innerHTML = event.latLng.lat();
                document.getElementById('longclicked').innerHTML =  event.latLng.lng();
            });
            
            // Update lat/long value of div when you move the mouse over the map
            google.maps.event.addListener(map,'mousemove',function(event) {
                document.getElementById('latmoved').innerHTML = event.latLng.lat();
                document.getElementById('longmoved').innerHTML = event.latLng.lng();
            });
                    
            var marker = new google.maps.Marker({
              position: myLatLng,
              map: map,
              //title: 'Hello World'
              
              // setting latitude & longitude as title of the marker
              // title is shown when you hover over the marker
              title: latitude + ', ' + longitude 
            });    
            
            // Update lat/long value of div when the marker is clicked
            marker.addListener('click', function(event) {              
              document.getElementById('latclicked').innerHTML = event.latLng.lat();
              document.getElementById('longclicked').innerHTML =  event.latLng.lng();
            });
            
            // Create new marker on double click event on the map
            google.maps.event.addListener(map,'dblclick',function(event) {
                var marker = new google.maps.Marker({
                  position: event.latLng, 
                  map: map, 
                  title: event.latLng.lat()+', '+event.latLng.lng()
                });
                
                // Update lat/long value of div when the marker is clicked
                marker.addListener('click', function() {
                  document.getElementById('latclicked').innerHTML = event.latLng.lat();
                  document.getElementById('longclicked').innerHTML =  event.latLng.lng();
                });            
            });
            
            // Create new marker on single click event on the map
            google.maps.event.addListener(map,'click',function(event) {
                var marker = new google.maps.Marker({
                  position: event.latLng, 
                  map: map, 
                  title: event.latLng.lat()+', '+event.latLng.lng()
                });                
            });
        }
        </script>
        <script src="https://maps.googleapis.com/maps/api/js?key=[YOUR API KEY HERE and remove the square brackets]&callback=initMap"
        async defer></script>
    </body>    
</html>
    
ENDTEXT
    
     MEMOWRIT( cMapFile, cHtmlContent )

    // Opens the Map using your default internet browser. A more generic solution
     Shellexecute( NIL, "open", cMapFile )  
   
Return NIL
You may modify the code to suit your requirements

Re: Get latitude and longitude

Posted: Sun Jan 13, 2019 10:59 am
by betoncu
Perfect. Many thanks.
Is it possible to pass the coordinates to the calling program?

Re: Get latitude and longitude

Posted: Mon Jan 14, 2019 4:50 am
by anserkk
betoncu wrote:Perfect. Many thanks.
Is it possible to pass the coordinates to the calling program?
Instead of

Code: Select all

Shellexecute( NIL, "open", cMapFile )  
Use Internet Explorer and read the DIV values of latclicked and longclicked
For eg something like

Code: Select all

oIE:=CreateObject("InternetExplorer.Application")
oIE:Document:getElementsByName("latclicked"):Item(0):Value

Re: Get latitude and longitude

Posted: Wed Jan 16, 2019 4:02 am
by betoncu
Sorry but I couldn't. Anyhow, thanks for your help.

Re: Get latitude and longitude

Posted: Wed Jan 16, 2019 2:29 pm
by AntoninoP
Some time ago I did a FiveWin control that uses OpenStreetMap: http://forums.fivetechsupport.com/viewt ... =3&t=35535

here an updated code:

Code: Select all

#include <fivewin.ch>

********** TEST CODE **********
proc main
    LOCAL oWnd, oMap
    DEFINE WINDOW oWnd TITLE "3D objects"
    SetWndDefault(oWnd)
    oMap := TMapControl():New()
    oMap:SetCenter(-4.806640,36.505522)
    oMap:AddMarker(-4.806640,36.505522)
    oWnd:oClient := oMap    
    ACTIVATE WINDOW oWnd
*******************************

/// https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
class TMapControl FROM TControl
    CLASSDATA lRegistered AS LOGICAL
    CLASSDATA aServers AS ARRAY INIT {'https://a.tile.openstreetmap.org/{zoom}/{x}/{y}.png', ;
                                                 'https://b.tile.openstreetmap.org/{zoom}/{x}/{y}.png', ;
                                                 'https://c.tile.openstreetmap.org/{zoom}/{x}/{y}.png'}
    CLASSDATA nMaxZoom AS NUMERIC INIT 19
    DATA oTimer
    DATA aHttps             // Queue of downloading images, array of { "Msxml2.XMLHTTP.6.0", zoom, x, y, Seconds(), cUrl }
    DATA nZoom, nLat, nLon  // current zoom and center of the screen
    DATA nServer            // Last used server Id (from aServers)
    DATA aMarkers AS ARRAY INIT {}   // Couples of lat+lon to mark on map
    DATA aImages AS ARRAY INIT {}    // Array of loaded images
    DATA aTopLeftTileInfo            // Information about tile at top left corner { xpos, ypos, x-tile, y-tile }
    DATA lastMousePos, lastRenderTime   // mouse down position and seconds of last rendering

    METHOD New( nRow, nCol, oWnd, nWidth, nHeight ) CONSTRUCTOR
    METHOD End()
    METHOD Display() INLINE ::BeginPaint(),::Paint(),::EndPaint(),0
    METHOD EraseBkGnd( ) INLINE 1
    METHOD Paint()
    
    Method SetCenter(nLon,nLat)
    Method AddMarker(nLon,nLat)

    METHOD RButtonDown(nRow, nCol, nKeyFlags, lTouch)
    METHOD LButtonDown(nRow, nCol, nKeyFlags, lTouch)
    METHOD MouseMove( nRow, nCol, nKeyFlags )
    METHOD MouseWheel( nKey, nDelta, nXPos, nYPos )

    METHOD TimerEvent()
    
    METHOD GetImage(x,y,zoom,lQueue) HIDDEN
    METHOD GetTileNumber(lon,lat,zoom)
    METHOD GetCoordsFromTile(x,y,zoom)  
    METHOD GetCoordsFromPixel(x,y)
   
   METHOD PaintTile(hDCMem,hDCBmp,l,t,tx,ty,zoom)
       
endclass


METHOD New( nRow, nCol, oWnd, nWidth, nHeight ) CLASS TMapControl
    DEFAULT nRow := 10, nCol := 10, oWnd := GetWndDefault()
    DEFAULT nWidth := 500
    DEFAULT nHeight := 300
    ::nZoom := 15
    ::nLon := 0
    ::nLat := 0
    ::nServer := 1
    ::aMarkers := {}

    ::oWnd      := oWnd
    ::nId           := ::GetNewId()
    ::nStyle        := nOR( WS_CHILD, WS_VISIBLE, WS_TABSTOP, WS_BORDER )
    ::nTop      := nRow
    ::nLeft     := nCol
    ::nBottom   := ::nTop + nHeight - 1
    ::nRight        := ::nLeft + nWidth
    
    ::aHttps := {}
    
    ::Register( nOR( CS_VREDRAW, CS_HREDRAW ) )

    if ! Empty( oWnd:hWnd )
        ::Create( )
        oWnd:AddControl( Self )
    else
        oWnd:DefControl( Self )
    endif
    
    DEFINE TIMER ::oTimer OF SELF INTERVAL 0.1 ACTION ::TimerEvent()
    ::oTimer:Activate()
    
return Self

METHOD End() class TMapControl
    if .not. empty(::oTimer)
        ::oTimer:Deactivate()
        ::oTimer:End()
    endif
return ::Super:End()

Method SetCenter(nLon,nLat) class TMapControl
    local r := {::nLon,::nLat}
    ::nLon := nLon
    ::nLat := nLat
return r

Method AddMarker(nLon,nLat) class TMapControl
return aAdd(::aMarkers, {nLon,nLat})

#define SRCCOPY 13369376

METHOD Paint() class TMapControl
    LOCAL x,y, img, ix,iy, top, left, sx,sy,  hBmpMem
    LOCAL w := ::nWidth,  h := ::nHeight, hDCMem, hDCBmp
   hDCMem  = CreateCompatibleDC( ::hDC )
   hBmpMem = CreateCompatibleBitmap( ::hDC, w, h )
   SelectObject( hDCMem, hBmpMem )
   FillRect( hDCMem, {0,0,h,w}, ::oBrush:hBrush )
    // get tile of center
    x := ::GetTileNumber(::nLon,::nLat)
    y := x[2]
    x := x[1]
    // move the desired pixel in the centre of canvas
    sx := floor(x)
    sy := floor(y)
    top  := h/2 - int((y-sy)*256)
    left := w/2 - int((x-sx)*256)
    // check for fill all area
    do while top>0
        sy-=1
        top-=256
    enddo
    do while left>0
        sx-=1
        left-=256
    enddo
    // draw the map
   ::lastRenderTime := Seconds()
   hDCBmp := CreateCompatibleDC( ::hDC )
    for iy:=0 to ceiling((h-top)/256)
        for ix:=0 to ceiling((w-left)/256)
         ::PaintTile(hDCMem,hDCBmp,left+ix*256,top+iy*256 ,sx+ix,sy+iy,::nZoom)
        next
    next
   DeleteDC( hDCBmp )
    // draw the markers
    for ix:=1 to len(::aMarkers) 
        x := ::GetTileNumber(::aMarkers[ix,1],::aMarkers[ix,2])
        y := (x[2] - sy) * 256 + top
        x := (x[1] - sx) * 256 + left
        MoveTo(hDCMem,x-5,y-5)
        LineTo(hDCMem,x+5,y+5)
        MoveTo(hDCMem,x-5,y+5)
        LineTo(hDCMem,x+5,y-5)
    next
    // save these infos
    ::aTopLeftTileInfo := {top,left,sx,sy}
   
   //img := "Queue len: " + alltrim(str(len(::aHttps)))
   //TextOut( hDCMem, 4, 4, img, Len( img ) )
   BitBlt( ::hDC, 0,0, w, h, hDCMem, 0,0, SRCCOPY )
   DeleteDC(hDCMem)
return nil

METHOD PaintTile(hDCMem,hDCBmp,l,t,tx,ty,zoom)
   LOCAL img, sx,sy, ix, iy, n
   img := ::GetImage(tx,ty,zoom)
   if .not. empty(img)
         SelectObject(hDCBmp, img)
         BitBlt( hDCMem, l,t, 256, 256, hDCBmp, 0,0, SRCCOPY )
            return nil
   endif
   
   // try less zoomed images (if they are not in cache it are not downloaded)
   img := ::GetImage(hb_bitShift(tx,-1),hb_bitShift(ty,-1),zoom-1,.F.)
   if .not. empty(img)
      SelectObject(hDCBmp, img)
      StretchBlt( hDCMem, l,t, 256, 256, hDCBmp, hb_bitAnd(tx,1)*128,hb_bitAnd(ty,1)*128,128,128, SRCCOPY )
      return nil
   endif
   
   
   sx := hb_bitShift(tx,1)
   sy := hb_bitShift(ty,1)
   for iy:=0 to 1 
      for ix:=0 to 1 
         img := ::GetImage(sx+ix,sy+iy,zoom+1,.F.)
         if .not. empty(img)
            SelectObject(hDCBmp, img)
            StretchBlt( hDCMem, l+ix*128,t+iy*128, 128, 128, hDCBmp, 0,0,255,255, SRCCOPY )
         endif            
      next
   next
return nil

METHOD TimerEvent() class TMapControl
    LOCAL lRedraw := .F., oHttp, img, idx
    
    for idx:=1 to len(::aHttps)
        oHttp := ::aHttps[idx]
        if oHttp[1]:readyState = 4
         // downloaded a missing image!
            img := GDIP_ImageFromStr(oHttp[1]:ResponseBody(), .t., .f.)
            if .not. empty(img) // correctly created
                lRedraw := .T.
                aAdd(::aImages, { img, oHttp[2], oHttp[3], oHttp[4] })
            endif
            hb_ADel(::aHttps,idx,.t.)
      else 
         // stop the unfinisched download that are not relative of current view
         if oHttp[5]<::lastRenderTime
            oHttp[1]:abort()
            hb_ADel(::aHttps,idx,.t.)
         endif
        endif
    next
    
    if lRedraw
        ::Refresh()
    endif
return nil

// directly from OpenStreetMap wiki
METHOD GetTileNumber(lon,lat,zoom) class TMapControl
    LOCAL x,y,n, latRad
    DEFAULT zoom := ::nZoom
    n := hb_bitShift(1, zoom)
    latRad := lat * PI() / 180
    x := n * (lon + 180) / 360
    y := n * (1-(log(tan(latRad) + 1/cos(latRad)) / PI())) / 2
    do while(x<0) 
        x+=n
    enddo
    do while(x>=n) 
        x-=n
    enddo
    if y<0
        y:=0
    endif
    if y>=n
        y:=n-1
    endif
return {x,y}

// directly from OpenStreetMap wiki
METHOD GetCoordsFromTile(x,y,zoom) class TMapControl
    LOCAL lon, lat, n, lat_rad
    DEFAULT zoom := ::nZoom
    n := hb_bitShift(1, zoom)
    lon = x / n * 360.0 - 180.0
    lat_rad = atan(sinh(PI() * (1 - 2 * y / n)))
    lat = lat_rad * 180.0 / PI()
return {lon,lat}

// screen to tile, with decimal, using aTopLeftTileInfo
METHOD GetCoordsFromPixel(x,y)  class TMapControl
    LOCAL top  := ::aTopLeftTileInfo[1]
    LOCAL left := ::aTopLeftTileInfo[2]
    LOCAL sx    := ::aTopLeftTileInfo[3]
    LOCAL sy    := ::aTopLeftTileInfo[4]
return ::GetCoordsFromTile(sx+(x-left)/256,sy+(y-top)/256)

METHOD LButtonDown(nRow, nCol, nKeyFlags, lTouch) class TMapControl
    ::lastMousePos := {nRow,nCol}
return ::Super:LButtonDown( nRow, nCol, nKeyFlags, lTouch )

METHOD RButtonDown(nRow, nCol, nKeyFlags, lTouch) class TMapControl
    ::lastMousePos := {nRow,nCol}
return ::Super:RButtonDown( nRow, nCol, nKeyFlags, lTouch )

METHOD MouseMove( nRow, nCol, nKeyFlags ) class TMapControl
    LOCAL oldMouseCoords,newMouseCoords
    if (nKeyFlags<>1 .and. nKeyFlags<>2) .or. empty(::lastMousePos)
        return 0
    endif
    oldMouseCoords := ::GetCoordsFromPixel(::lastMousePos[2],::lastMousePos[1])
    newMouseCoords := ::GetCoordsFromPixel(nCol,nRow)
    ::nLon += oldMouseCoords[1] - newMouseCoords[1]
    ::nLat += oldMouseCoords[2] - newMouseCoords[2]
    ::lastMousePos := {nRow,nCol}
    ::Refresh()
return ::Super:MouseMove( nRow, nCol, nKeyFlags )

METHOD MouseWheel( nKey, nDelta, nXPos, nYPos )  class TMapControl
    if nDelta>0 .and. ::nZoom<::nMaxZoom 
        ::nZoom+=1
    endif
    if nDelta<0 .and. ::nZoom>0
        ::nZoom-=1
    endif
    ::Refresh()
return ::Super:MouseWheel( nKey, nDelta, nXPos, nYPos )


METHOD GetImage(x,y,zoom,lQueue) class TMapControl
    local n, cUrl, img
    LOCAL oHttp
    DEFAULT zoom := ::nZoom
   DEFAULT lQueue := .T.
    
    x:=int(x)
    y:=int(y)
    // looking for the image in the "cache"
    n:= aScan(::aImages, {|v| v[2]=zoom .and. v[3]=x .and. v[4]=y })
    if n>0
        // move the last returned image on top of cache
      img := ::aImages[n][1]
      //aDel(::aImages,n)
      //::aImages[len(::aImages)] := img
        return img
    endif
   if .not. lQueue
      return nil
   endif
    // TODO: Limit cache size
    cUrl := ::aServers[::nServer]
    ::nServer++
    if ::nServer>len(::aServers)
        ::nServer:=1
    endif
    cUrl := StrTran(cUrl,"{zoom}", allTrim(str(zoom)))
    cUrl := StrTran(cUrl,"{x}", allTrim(str(x)))
    cUrl := StrTran(cUrl,"{y}", allTrim(str(y)))
    if (n:=aScan(::aHttps, {|v| v[2] = zoom .and. v[3] = x .and. v[4] = y})) > 0
      // already in download, update last query time
      ::aHttps[n,5]:=Seconds()
        return nil
    endif
    begin sequence
        oHttp := win_oleCreateObject( "Msxml2.XMLHTTP.6.0" )
        oHttp:Open("GET", cUrl, .T. )
        oHttp:Send()
        if oHttp:readyState <> 4
            if oHttp:readyState=1 .or. oHttp:readyState=3
                aAdd(::aHttps,{ oHttp, zoom, x, y, Seconds(), cUrl })
            endif
        else
            img := GDIP_ImageFromStr(oHttp:ResponseBody(), .t., .f.)
            if .not. empty(img)
                aAdd(::aImages, { img, zoom, x, y })
            endif
        endif
    end sequence
return img
In this control is present the method GetCoordsFromPixel that returns {lon,lan}
And you can add put the markers with the method AddMarker.

What is necessary is replace the rendering of the markers :)

The only thing I ask is if you use it and improve it, share the improvement with the forum ;)