The Road to Delphi

Delphi – Free Pascal – Oxygene

Using Google maps (Static Maps) without TWebBrowser

6 Comments

Commonly we use the TWebBrowser component to access Google maps, but there is another option, using the Google Static Map service.

The Google Static Map service allows you get an static image from a location without requiring JavaScript or any dynamic page loading. The Google Static Map service creates your map based on URL parameters sent through a standard HTTP request.

So the code to interact with this service is fairly easy. all we need is an TIdHTTP and a TImage component.

First we need to set the TIdHTTP property UserAgent (the UserAgent is what a browser uses to identify itself to the HTTP server
) to an valid Agent, if you use the default value Mozilla/3.0 (compatible; Indy Library) you will get a awful message like this HTTP 1.1/ 403 Forbidden. so we can change this value to Mozilla/3.0 or to another valid agent.

then we need build the url to request the image from an location. a valid URL look like this

http://maps.google.com/maps/api/staticmap?center=40.714728,-73.998672&zoom=12&size=400x400&sensor=false

You can see the full syntax for build a valid URL on this page.

Now using the TIdHTTP component, we send the request and get the image

var
  StreamData :TMemoryStream;
  JPEGImage  : TJPEGImage;
begin
  EditURL.Text:=buildUrl;//build the url with the params
  StreamData := TMemoryStream.Create;
  JPEGImage  := TJPEGImage.Create;
  try
    try
     idhttp1.Get(EditURL.Text, StreamData); //Send the request and get the image
     StreamData.Seek(0,soFromBeginning);
     JPEGImage.LoadFromStream(StreamData);//load the image in a Stream
     ImageMap.Picture.Assign(JPEGImage);//Load the image
    Except On E : Exception Do
     MessageDlg('Exception: '+E.Message,mtError, [mbOK], 0);
    End;
  finally
    StreamData.free;
    JPEGImage.Free;
  end;
end;

finally a very important note from google

Use of the Google Static Maps API is subject to a query limit of 1000 unique (different) image requests per viewer per day. Since this restriction is a quota per viewer, most developers should not need to worry about exceeding their quota. However, note that we enforce an additional request rate limit to prevent abuse of the service. Requests of identical images, in general, do not count towards this limit beyond the original request.

If a user exceeds the limit as proscribed above, the following image will be displayed indicating that the quota has been exceeded:

This limit is enforced to prevent abuse and/or repurposing of the Static Maps API, and this limit may be changed in the future without notice. If you exceed the 24-hour limit or otherwise abuse the service, the Static Maps API may stop working for you temporarily. If you continue to exceed this limit, your access to the Static Maps API may be blocked.

Static Map URLs are restricted to 2048 characters in size. In practice, you will probably not have need for URLs longer than this, unless you produce complicated maps with a high number of markers and paths. Note, however, that certain characters may be URL-encoded by browsers and/or services before sending them off to the Static Map service, resulting in increased character usage.

…Note that static maps may only be displayed within browser content; use of static maps outside of the browser is not allowed. (Google Maps API Premier users are waived of this requirement.)

check the source code for educational use only (do you need a API Premier account to  use the Static maps ouside of an browser), you can download the full project from this location.

unit UnitMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP,
  StdCtrls, ExtCtrls, XPMan, ComCtrls;

type
  TFormMain = class(TForm)
    ImageMap: TImage;
    IdHTTP10: TIdHTTP;
    XPManifest1: TXPManifest;
    ScrollBoxMap: TScrollBox;
    Panel1: TPanel;
    EditURL: TEdit;
    ButtonGet: TButton;
    CheckBoxRealTime: TCheckBox;
    Panel2: TPanel;
    Panel3: TPanel;
    EditWidth: TEdit;
    UpDown3: TUpDown;
    UpDown2: TUpDown;
    UpDown1: TUpDown;
    ComboBoxMapType: TComboBox;
    EditZoom: TEdit;
    ComboBoxFormat: TComboBox;
    EditHeight: TEdit;
    EditLongitude: TEdit;
    EditLatitude: TEdit;
    Label7: TLabel;
    Label6: TLabel;
    Label5: TLabel;
    Label4: TLabel;
    Label3: TLabel;
    Label2: TLabel;
    Label1: TLabel;
    CheckBoxMarker: TCheckBox;
    ProgressBar1: TProgressBar;
    IdHTTP1: TIdHTTP;
    procedure ButtonGetClick(Sender: TObject);
    procedure EditZoomChange(Sender: TObject);
    procedure ComboBoxMapTypeChange(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ImageMapMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure ImageMapMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure IdHTTP10WorkBegin(ASender: TObject; AWorkMode: TWorkMode;
      AWorkCountMax: Integer);
    procedure IdHTTP10Work(ASender: TObject; AWorkMode: TWorkMode;
      AWorkCount: Integer);
    procedure IdHTTP10WorkEnd(ASender: TObject; AWorkMode: TWorkMode);
  private
    { Private declarations }
    SX: Integer;
    SY: Integer;
    LX: Integer;
    LY: Integer;
    function  buildUrl:string;
    procedure GetMapImage;
  public
    { Public declarations }
  end;

var
  FormMain: TFormMain;

implementation

uses
jpeg; //this project only supports jpg, you can add addtional units to support gif and png

{$R *.dfm}

const
UrlPrefix='http://maps.google.com/maps/api/staticmap?';

function TFormMain.buildUrl: string; //build the url based in the user input
begin
  Result:=UrlPrefix+'center='+EditLatitude.Text+','+EditLongitude.Text+'&zoom='+EditZoom.Text+'&size='+EditWidth.Text+'x'+EditHeight.Text+'&maptype='+ComboBoxMapType.Text+'&sensor=false&format='+ComboBoxFormat.Text;
  if CheckBoxMarker.Checked then
  Result:=Result+'&markers=color:blue|'+EditLatitude.Text+','+EditLongitude.Text;
end;

procedure TFormMain.ButtonGetClick(Sender: TObject);
begin
 GetMapImage;
end;

procedure TFormMain.ComboBoxMapTypeChange(Sender: TObject);
begin
 if CheckBoxRealTime.Checked then
 GetMapImage;
end;

procedure TFormMain.EditZoomChange(Sender: TObject);
begin
 if CheckBoxRealTime.Checked then
 GetMapImage;
end;

procedure TFormMain.FormCreate(Sender: TObject);
begin
 ScrollBoxMap.DoubleBuffered := True; //avoid the flicker when the pan effect is activated
end;

procedure TFormMain.GetMapImage; //Get the image from the Google Service
var
  StreamData : TMemoryStream;
  JPEGImage  : TJPEGImage;
begin
  EditURL.Text:=buildUrl;
  StreamData := TMemoryStream.Create;
  JPEGImage  := TJPEGImage.Create;
  try
    try
     idhttp1.Get(EditURL.Text, StreamData);
     StreamData.Seek(0,soFromBeginning);

     ImageMap.Top := 0;
     ImageMap.Left := 0;
     JPEGImage.LoadFromStream(StreamData);
     LX := (ImageMap.Width - ScrollBoxMap.ClientWidth) * -1;
     LY := (ImageMap.Height - ScrollBoxMap.ClientHeight) * -1;

     ImageMap.Picture.Assign(JPEGImage);
    Except On E : Exception Do
     MessageDlg('Exception: '+E.Message,mtError, [mbOK], 0);
    End;
  finally
    StreamData.free;
    JPEGImage.Free;
  end;
end;

procedure TFormMain.IdHTTP10Work(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCount: Integer);
begin
  ProgressBar1.Position := AWorkCount;
end;

procedure TFormMain.IdHTTP10WorkBegin(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCountMax: Integer);
begin
  ProgressBar1.Position := 0;
  ProgressBar1.Max      := IdHTTP1.Response.ContentLength;
end;

procedure TFormMain.IdHTTP10WorkEnd(ASender: TObject; AWorkMode: TWorkMode);
begin
  //ProgressBar1.Position := 0;
end;

procedure TFormMain.ImageMapMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
   SX := X;
   SY := Y;
end;

procedure TFormMain.ImageMapMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); //Allow pannig over the image
var NX: Integer;
    NY: Integer;
begin
    if not (ssLeft in Shift) then   Exit;
    NX := ImageMap.Left + X - SX;
    NY := ImageMap.Top + Y - SY;

    if (NX < 0) and (NX > LX) then  ImageMap.Left := NX;
    if (NY < 0) and (NY > LY) then  ImageMap.Top := NY;
end;

end.

Author: Rodrigo

Just another Delphi guy.

6 thoughts on “Using Google maps (Static Maps) without TWebBrowser

  1. The terms and conditions of the static API states:

    “Note that static maps may only be displayed within browser content; use of static maps outside of the browser is not allowed. (Google Maps API Premier users are waived of this requirement.)”

    You may want to indicate that in order to legally use your example code, you need a Premier account.

  2. Thank you!

  3. can you provide a zip file for this?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s