Page 1 of 2

Use Google Maps in PureBasic (Easy Procedure)

Posted: Sun May 22, 2011 4:10 am
by JustinJack

Code: Select all

InitNetwork()
UsePNGImageDecoder()

Enumeration
  #ROADMAP
  #SATELLITE
  #TERRAIN
  #HYBRID
EndEnumeration

  


Procedure.l getMapImage( MapAddress.s, Zoom=15, MapType = #ROADMAP, Markers.s = "" )
  RetVal = 0
  If Markers <> ""
    myRequestTwo.s = "/maps/api/staticmap?center="+ URLEncoder(MapAddress) + "&" + URLEncoder(Markers) + "&zoom=" + Str(Zoom) + "&size=500x500"
  Else
    myRequestTwo.s = "/maps/api/staticmap?center="+ URLEncoder(MapAddress) + "&zoom=" + Str(Zoom) + "&size=500x500"
  EndIf
  Select MapType
    Case #SATELLITE
      myRequestTwo.s + "&maptype=satellite"  
    Case #TERRAIN
      myRequestTwo.s + "&maptype=terrain"
    Case #HYBRID
      myRequestTwo.s + "&maptype=hybrid" 
    Default
      myRequestTwo.s + "&maptype=roadmap"  
  EndSelect
  
  myRequestTwo.s + "&sensor=false"
  
  myConnection = OpenNetworkConnection("maps.google.com", 80, #PB_Network_TCP)
  If myConnection = 0
    ProcedureReturn 0
  EndIf
  myRequest.s = "GET "+ myRequestTwo + " HTTP/1.0" + #CRLF$
  myRequest.s + "Content-Type: application/x-www-form-urlencoded; charset=iso-8859-1" + #CRLF$
  myRequest.s + "Host: maps.google.com" + #CRLF$ + #CRLF$
  *buffer = AllocateMemory(65535)
  *bufferTwo = AllocateMemory(65535)
  bfTwoPtr = 0
  mSize = 65535
  SendNetworkString(myConnection, myRequest)
  dwBytes = 1
  While dwBytes > 0
    dwBytes = ReceiveNetworkData(myConnection, *buffer, 65535)
    If dwBytes > 0
      If (*bufferTwo + dwBytes) >= mSize
        mSize + 65535
        *bufferTwo = ReAllocateMemory(*bufferTwo, mSize)
      EndIf
      CopyMemory(*buffer, (*bufferTwo+bfTwoPtr), dwBytes)
      bfTwoPtr + dwBytes
    EndIf
  Wend
  CloseNetworkConnection(myConnection)
  FreeMemory(*buffer)
  dblCrlf.s = #CRLF$+#CRLF$
  found = 0
  dataLength = 0
  For k = 0 To bfTwoPtr
    If k > bfTwoPtr
      Break
    EndIf
    If PeekS(*bufferTwo+k, 4) = dblCrlf
      found = k
      headerInfo.s = PeekS(*bufferTwo, k)
      dataBeginSpot = (k + 4)
      *dataBegin = (*bufferTwo + dataBeginSpot)
      dataLength    = (bfTwoPtr - dataBeginSpot)
      Break
    EndIf
  Next
  If ((dataLength > 0) And (found > 0))
    RetVal = CatchImage(#PB_Any, *dataBegin, dataLength)
  EndIf
  FreeMemory(*bufferTwo)
  ProcedureReturn RetVal
EndProcedure



; MapAddress.s   Can be a physical address or a set of Lat / Lon Coordinates like : "32.15648, -97.54235"
; Zoom: Googles Map Zoom Level from:  1 Less Zoom    to  I dunno...20?  More Zoom....
; MapTypes:
  ;#ROADMAP
  ;#SATELLITE
  ;#TERRAIN
  ;#HYBRID
; Markers.  Google draws your map with markers....Check Out Markers at: http://code.google.com/apis/maps/documentation/staticmaps/#MarkerLocations


myImage = getMapImage("Dallas, TX", 15, #HYBRID, "markers=color:blue|label:A|Dallas, TX&markers=color:red|label:B|5706 Lindell Ave, 75206")

If IsImage(myImage)
  OpenWindow(0, 0, 0, 500, 500, "Test", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  ImageGadget(1, 0, 0, 500, 500, ImageID(myImage))
  Repeat : Until WaitWindowEvent() = 16
  FreeImage(myImage)
Else
  MessageBox_(0, "Didn't get a map image!", "Hmm, Strange...", #MB_ICONERROR|#MB_OK)
EndIf

This is really cool. I play with googles API's all the time through PureBasic!

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Sun May 22, 2011 4:15 am
by MachineCode
Awesome! Just awesome! :shock: :D Thanks for sharing! How can we make it show satellite images, though?

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Sun May 22, 2011 4:33 am
by JustinJack
Good Thinking! There you go, I changed it to be able to switch up map types.

I actually spent a lot of time figuring out how to decipher the lat / lon of the bounding rect of the map returned so I could, on some of my apps, draw my own markers and perform hit test to pop up data on the google maps, (all of which is do-able with java script in a web app) but it's cool to have that in a desktop app. I wish they'd give you the view lat / lon of the map returned in the HTTP Response. I did it by placing markers on maps at lat / lons I knew, then setting the map center to a specific lat / lon, and checking it with different coordinates at different zoom levels. But man that stinks!

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Sun May 22, 2011 7:00 am
by Michael Vogel
Fine stuff :wink:

Some of these older snippets for cropping a map to exact coordinates and to get geo coordinates from named spaces here may help using Google Maps, too.

Because the thread mentioned above haven't been updated for a while, the included links to the compiled programs may not work -- here's the correct place for checking my GPS tools written in PureBasic, of course :mrgreen:

Michael

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Sun May 22, 2011 5:19 pm
by JustinJack
Very cool. Your forerunner program is very nice! I was messing around with yahoo's maps also, but that hasn't solved my issue either. I can get the maps and divide the lat/lon per pixel and spit out in an edit box the lat / lons as I drag my mouse around no prob, I would just love to be able to get my own maps and accurately have the geo-locations of the corners of the map or at least opposite / diagonal corners... In google I played with their zoom levels and figured out the coords DOUBLE or are cut in half, depending on how you look at it, with each zoom level, so all you need is to manipulate the map to be positioned how you want the first time, then calculate each zoom levels change..

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Mon May 23, 2011 2:32 am
by IdeasVacuum
Could be very handy :)

On compile, I get an error @ line 75

Code: Select all

RetVal = CatchImage(#PB_Any, *dataBegin, dataLength)
[ERROR] The specified pointer is null.

It's because myConnection fails but the code is allowed to continue without a connection. Fail-safe:

Code: Select all

InitNetwork()
UsePNGImageDecoder()

Enumeration
  #ROADMAP
  #SATELLITE
  #TERRAIN
  #HYBRID
EndEnumeration


Procedure.l getMapImage( MapAddress.s, Zoom=15, MapType = #ROADMAP, Markers.s = "")
;----------------------------------------------------------------------------------
  RetVal = 0

  If Markers <> ""
    myRequestTwo.s = "/maps/api/staticmap?center="+ URLEncoder(MapAddress) + "&" + URLEncoder(Markers) + "&zoom=" + Str(Zoom) + "&size=500x500"
  Else
    myRequestTwo.s = "/maps/api/staticmap?center="+ URLEncoder(MapAddress) + "&zoom=" + Str(Zoom) + "&size=500x500"
  EndIf

  Select MapType
    Case #SATELLITE
      myRequestTwo.s + "&maptype=satellite" 
    Case #TERRAIN
      myRequestTwo.s + "&maptype=terrain"
    Case #HYBRID
      myRequestTwo.s + "&maptype=hybrid"
    Default
      myRequestTwo.s + "&maptype=roadmap" 
  EndSelect
 
  myRequestTwo.s + "&sensor=false"
 
  myConnection = OpenNetworkConnection("maps.google.com", 80, #PB_Network_TCP)

  If myConnection > 0

          myRequest.s = "GET "+ myRequestTwo + " HTTP/1.0" + #CRLF$
          myRequest.s + "Content-Type: application/x-www-form-urlencoded; charset=iso-8859-1" + #CRLF$
          myRequest.s + "Host: maps.google.com" + #CRLF$ + #CRLF$
          *buffer = AllocateMemory(65535)
          *bufferTwo = AllocateMemory(65535)
          bfTwoPtr = 0
          mSize = 65535
          SendNetworkString(myConnection, myRequest)

                dwBytes = 1
          While dwBytes > 0

            dwBytes = ReceiveNetworkData(myConnection, *buffer, 65535)

            If dwBytes > 0

                     If (*bufferTwo + dwBytes) >= mSize
                       mSize + 65535
                       *bufferTwo = ReAllocateMemory(*bufferTwo, mSize)
                     EndIf
                     
                     CopyMemory(*buffer, (*bufferTwo+bfTwoPtr), dwBytes)
                     bfTwoPtr + dwBytes
            EndIf

          Wend

          CloseNetworkConnection(myConnection)
          FreeMemory(*buffer)
          dblCrlf.s = #CRLF$+#CRLF$

          found = 0
          For k = 0 To bfTwoPtr

                  If k > bfTwoPtr
                    Break
                  EndIf
                  
                  If PeekS(*bufferTwo+k, 4) = dblCrlf
                    found = k
                    headerInfo.s = PeekS(*bufferTwo, k)
                    dataBeginSpot = (k + 4)
                    *dataBegin = (*bufferTwo + dataBeginSpot)
                    dataLength    = (bfTwoPtr - dataBeginSpot)
                    Break
                  EndIf

          Next

          RetVal = CatchImage(#PB_Any, *dataBegin, dataLength)
          FreeMemory(*bufferTwo)
  Else
          RetVal = -1
  EndIf

  ProcedureReturn RetVal

EndProcedure

; MapAddress.s   Can be a physical address or a set of Lat / Lon Coordinates like : "32.15648, -97.54235"
; Zoom: Googles Map Zoom Level from:  1 Less Zoom    to  I dunno...20?  More Zoom....
; MapTypes:
  ;#ROADMAP
  ;#SATELLITE
  ;#TERRAIN
  ;#HYBRID
; Markers.  Google draws your map with markers....Check Out Markers at: http://code.google.com/apis/maps/documentation/staticmaps/#MarkerLocations


myImage = getMapImage("Dallas, TX", 15, #HYBRID, "markers=color:blue|label:A|Dallas, TX&markers=color:red|label:B|5706 Lindell Ave, 75206")

If IsImage(myImage)

         OpenWindow(0, 0, 0, 500, 500, "Test", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
         ImageGadget(1, 0, 0, 500, 500, ImageID(myImage))
         Repeat : Until WaitWindowEvent() = 16
         FreeImage(myImage)
Else
         MessageBox_(0, "Didn't get a map image!", "Hmm, Strange...", #MB_ICONERROR|#MB_OK)
EndIf

End

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Mon May 23, 2011 7:29 am
by JustinJack
Hmm...thats strange thats exactly why i have the bit in there about:

Code: Select all

if myConnection =0
    ProcedureReturn 0
Endif
I tested it and when i didnt get a connection or it failed...myConnection equals 0

I think more likely what caused your error is that you DID get a connection... but i didnt spend a lot of time error_proofing the processing of the http response. I just assumed google would send a map image because ut DOES for me since i use geo coords...all we need to do it process the Content-Length: http header, then look for after the dblcrlf, allocate mem for that size,or just check that that ptr is a valid address in our block b4 trying to catch the image...any wayz glad u found that little tid-bit.

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Mon May 23, 2011 8:32 am
by JustinJack
There, I fixed the original code [post]. I just made it to where if it didn't find the end of the HTTP header i.e. #CRLF$+CRLF$, found = 0, so we don't try to access the memory, and if the calculated dataLength is zero we dont either. That way even if HTTP/1.1 is returned and we don't have a Content-Length, (like yahoo maps does) we wont try to read from a null pointer.

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Mon May 23, 2011 4:52 pm
by RichAlgeni
I found a site that is indispensable for me, I've used it to write real-time Ambulance tracking on Google maps.

http://econym.org.uk/gmap/

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Sun Jun 12, 2011 6:55 pm
by jesperbrannmark
I did a followup on this code to use with Google Charts Static Image.
But I posted it in Mac OS Forum instead, because.... mac rules...
http://www.purebasic.fr/english/viewtop ... 05#p354586


Jesper

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Mon Jul 04, 2011 5:04 pm
by ultralazor
I was randomly browsing this section and seen this thread, and sometimes use maps.

Things to 'proper' this:
  1. HTTP 1.1 and chunking(simple header value gives chunk size and last chunk notifier)
  2. gzip handling on data(drastically faster network exchange(deflate content with algo or lib))

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Tue Jul 12, 2011 9:41 pm
by zefiro_flashparty
wow!

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Fri Jul 22, 2011 2:44 pm
by jamirokwai
Hi,

nice finding!
Doesn't work in UNICODE-mode, though...

I changed your code using the URLEncoderX()-function to this...
It's just a fast hack, and uses the ReceiveHTTPFile, but it works in UNICODE-mode :-)
It also adds optional Width and Heigth of the resulting image, maximum 640x640 px

Code: Select all

; add the URL-functions from here: http://forums.purebasic.com/german/viewtopic.php?f=8&t=22286 - made by helpy
; this code is based on http://www.purebasic.fr/english/viewtopic.php?f=12&t=46428#p353302 - made by JustinJack
InitNetwork()
UsePNGImageDecoder()

Enumeration
  #ROADMAP
  #SATELLITE
  #TERRAIN
  #HYBRID
EndEnumeration

Procedure.l getMapImage( MapAddress.s, Zoom=15, Width = 500, Height = 500, MapType = #ROADMAP, Markers.s = "" )
  RetVal = 0
  
  myRequestTwo.s = "http://maps.google.com"
  
  If Markers <> ""
    myRequestTwo + "/maps/api/staticmap?center="+ URLEncoderX(MapAddress) + "&" + URLEncoderX(Markers) + "&zoom=" + Str(Zoom) + "&size=" + Str(Width) + "x" + Str(Height)
  Else
    myRequestTwo + "/maps/api/staticmap?center="+ URLEncoderX(MapAddress) + "&zoom=" + Str(Zoom) + "&size=" + Str(Width) + "x" + Str(Height)
  EndIf
  
  Select MapType
    Case #SATELLITE : myRequestTwo.s + "&maptype=satellite"  
    Case #TERRAIN   : myRequestTwo.s + "&maptype=terrain"
    Case #HYBRID    : myRequestTwo.s + "&maptype=hybrid" 
    Default         : myRequestTwo.s + "&maptype=roadmap"  
  EndSelect
  
  myRequestTwo.s + "&sensor=false"
  
  tempFile.s = GetTemporaryDirectory() + "map.png"
  ReceiveHTTPFile(myRequestTwo, tempFile)
  RetVal = LoadImage(#PB_Any, tempFile)
  DeleteFile(tempFile)
  ProcedureReturn RetVal
EndProcedure

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Fri Jul 22, 2011 6:33 pm
by Andre
I'm sure this is a great piece of code, even if I couldn't test it now...

But anyone looked at the Google "terms of use"?

Maybe it's only allowed to use such stuff for private use, but not for (business) software you want to release to the public... or you have to register and pay royalty fees to Google, or......?

Re: Use Google Maps in PureBasic (Easy Procedure)

Posted: Fri Jul 22, 2011 9:08 pm
by jamirokwai
Andre wrote:I'm sure this is a great piece of code, even if I couldn't test it now...

But anyone looked at the Google "terms of use"?

Maybe it's only allowed to use such stuff for private use, but not for (business) software you want to release to the public... or you have to register and pay royalty fees to Google, or......?
Hi Andre,

thanks for the comment on this. With Google API, you can do both, as it seems :-)
The terms of use say, that if your service is free of charge, you don't need to pay a fee.
If you like to make money with a software or service using the Maps API, you have to pay $10.000 per year...

See http://code.google.com/intl/en-US/apis/maps/index.html
The Maps API is a free service, available for any web site that is free to consumers. Please see the terms of service for more information.
Businesses that charge fees for access, track assets or build internal applications must use Google Maps API Premier, which provides enhanced features, technical support and a service-level agreement.
I assume for free software, you may use it for free. You have to buy a license, if you earn 1 Cent of more with your commercial software...