lazarus-ccr/components/lazmapviewer/example/main.pas
2019-03-27 18:56:52 +00:00

504 lines
13 KiB
ObjectPascal

unit Main;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Types, Forms, Controls, Graphics, Dialogs,
ExtCtrls, StdCtrls, ComCtrls, Buttons,
mvGeoNames, mvMapViewer, mvTypes, mvGpsObj;
type
{ TMainForm }
TMainForm = class(TForm)
BtnSearch: TButton;
BtnGoTo: TButton;
BtnGPSPoints: TButton;
CbDoubleBuffer: TCheckBox;
CbFoundLocations: TComboBox;
CbLocations: TComboBox;
CbProviders: TComboBox;
CbUseThreads: TCheckBox;
CbMouseCoords: TGroupBox;
GbCenterCoords: TGroupBox;
InfoCenterLatitude: TLabel;
InfoCenterLongitude: TLabel;
InfoBtnGPSPoints: TLabel;
GPSPointInfo: TLabel;
Label8: TLabel;
LblCenterLatitude: TLabel;
LblPositionLongitude: TLabel;
LblPositionLatitude: TLabel;
InfoPositionLongitude: TLabel;
InfoPositionLatitude: TLabel;
LblCenterLongitude: TLabel;
LblProviders: TLabel;
LblZoom: TLabel;
MapView: TMapView;
GeoNames: TMVGeoNames;
ControlPanel: TPanel;
BtnLoadMapProviders: TSpeedButton;
BtnSaveMapProviders: TSpeedButton;
ZoomTrackBar: TTrackBar;
procedure BtnGoToClick(Sender: TObject);
procedure BtnSearchClick(Sender: TObject);
procedure BtnGPSPointsClick(Sender: TObject);
procedure CbDoubleBufferChange(Sender: TObject);
procedure CbFoundLocationsDrawItem(Control: TWinControl; Index: Integer;
ARect: TRect; State: TOwnerDrawState);
procedure CbProvidersChange(Sender: TObject);
procedure CbUseThreadsChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure GeoNamesNameFound(const AName: string; const ADescr: String;
const ALoc: TRealPoint);
procedure MapViewDrawGpsPoint(Sender, ACanvas: TObject; APoint: TGpsPoint);
procedure MapViewMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure MapViewMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure MapViewZoomChange(Sender: TObject);
procedure BtnLoadMapProvidersClick(Sender: TObject);
procedure BtnSaveMapProvidersClick(Sender: TObject);
procedure ZoomTrackBarChange(Sender: TObject);
private
procedure ClearFoundLocations;
procedure UpdateDropdownWidth(ACombobox: TCombobox);
procedure UpdateLocationHistory(ALocation: String);
public
procedure ReadFromIni;
procedure WriteToIni;
end;
var
MainForm: TMainForm;
implementation
{$R *.lfm}
uses
LCLType, IniFiles, Math, FPCanvas, FPImage, IntfGraphics,
mvExtraData,
gpslistform;
type
TLocationParam = class
Descr: String;
Loc: TRealPoint;
end;
const
MAX_LOCATIONS_HISTORY = 50;
HOMEDIR = '';
MAP_PROVIDER_FILENAME = 'map-providers.xml';
var
PointFormatSettings: TFormatsettings;
function CalcIniName: String;
begin
Result := ChangeFileExt(Application.ExeName, '.ini');
end;
{ TMainForm }
procedure TMainForm.BtnLoadMapProvidersClick(Sender: TObject);
var
fn: String;
msg: String;
begin
fn := Application.Location + MAP_PROVIDER_FILENAME;
if FileExists(fn) then begin
if MapView.Engine.ReadProvidersFromXML(fn, msg) then begin
MapView.GetMapProviders(CbProviders.Items);
CbProviders.ItemIndex := 0;
MapView.MapProvider := CbProviders.Text;
end else
ShowMessage(msg);
end;
end;
procedure TMainForm.BtnSaveMapProvidersClick(Sender: TObject);
begin
MapView.Engine.WriteProvidersToXML(Application.Location + MAP_PROVIDER_FILENAME);
end;
procedure TMainForm.BtnSearchClick(Sender: TObject);
begin
ClearFoundLocations;
GeoNames.Search(CbLocations.Text, MapView.DownloadEngine);
UpdateDropdownWidth(CbFoundLocations);
UpdateLocationHistory(CbLocations.Text);
if CbFoundLocations.Items.Count > 0 then CbFoundLocations.ItemIndex := 0;
end;
procedure TMainForm.BtnGPSPointsClick(Sender: TObject);
var
F: TGpsListViewer;
begin
F := TGpsListViewer.Create(nil);
try
F.MapViewer := MapView;
F.ShowModal;
finally
F.Free;
end;
end;
procedure TMainForm.BtnGoToClick(Sender: TObject);
var
s: String;
P: TLocationParam;
begin
if CbFoundLocations.ItemIndex = -1 then
exit;
// Extract parameters of found locations. We need that to get the coordinates.
s := CbFoundLocations.Items.Strings[CbFoundLocations.ItemIndex];
P := TLocationParam(CbFoundLocations.Items.Objects[CbFoundLocations.ItemIndex]);
if P = nil then
exit;
CbFoundLocations.Text := s;
// Show location in center of mapview
MapView.Zoom := 12;
MapView.Center := P.Loc;
MapView.Invalidate;
end;
procedure TMainForm.CbDoubleBufferChange(Sender: TObject);
begin
MapView.DoubleBuffered := CbDoubleBuffer.Checked;
end;
procedure TMainForm.CbFoundLocationsDrawItem(Control: TWinControl;
Index: Integer; ARect: TRect; State: TOwnerDrawState);
var
s: String;
P: TLocationParam;
combo: TCombobox;
x, y: Integer;
begin
combo := TCombobox(Control);
if (State * [odSelected, odFocused] <> []) then begin
combo.Canvas.Brush.Color := clHighlight;
combo.Canvas.Font.Color := clHighlightText;
end else begin
combo.Canvas.Brush.Color := clWindow;
combo.Canvas.Font.Color := clWindowText;
end;
combo.Canvas.FillRect(ARect);
combo.Canvas.Brush.Style := bsClear;
s := combo.Items.Strings[Index];
P := TLocationParam(combo.Items.Objects[Index]);
x := ARect.Left + 2;
y := ARect.Top + 2;
combo.Canvas.Font.Style := [fsBold];
combo.Canvas.TextOut(x, y, s);
inc(y, combo.Canvas.TextHeight('Tg'));
combo.Canvas.Font.Style := [];
combo.Canvas.TextOut(x, y, P.Descr);
end;
procedure TMainForm.CbProvidersChange(Sender: TObject);
begin
MapView.MapProvider := CbProviders.Text;
end;
procedure TMainForm.CbUseThreadsChange(Sender: TObject);
begin
MapView.UseThreads := CbUseThreads.Checked;
end;
procedure TMainForm.ClearFoundLocations;
var
i: Integer;
P: TLocationParam;
begin
for i:=0 to CbFoundLocations.Items.Count-1 do begin
P := TLocationParam(CbFoundLocations.Items.Objects[i]);
P.Free;
end;
CbFoundLocations.Items.Clear;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
ForceDirectories(HOMEDIR + 'cache/');
MapView.CachePath := HOMEDIR + 'cache/';
MapView.GetMapProviders(CbProviders.Items);
CbProviders.ItemIndex := CbProviders.Items.Indexof(MapView.MapProvider);
MapView.DoubleBuffered := true;
MapView.Zoom := 1;
ControlPanel.Caption := '';
CbUseThreads.Checked := MapView.UseThreads;
CbDoubleBuffer.Checked := MapView.DoubleBuffered;
ReadFromIni;
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
WriteToIni;
ClearFoundLocations;
end;
procedure TMainForm.FormShow(Sender: TObject);
begin
MapView.Active := true;
end;
procedure TMainForm.GeoNamesNameFound(const AName: string;
const ADescr: String; const ALoc: TRealPoint);
var
P: TLocationParam;
begin
P := TLocationParam.Create;
P.Descr := ADescr;
P.Loc := ALoc;
CbFoundLocations.Items.AddObject(AName, P);
end;
procedure TMainForm.MapViewDrawGpsPoint(Sender, ACanvas: TObject;
APoint: TGpsPoint);
const
R = 5;
var
P: TPoint;
cnv: TFPCustomCanvas;
txt: String;
w, h: Integer;
bmp: TBitmap;
img: TLazIntfImage;
begin
if not (ACanvas is TFPCustomCanvas) then
exit;
// Screen coordinates of the GPS point
P := TMapView(Sender).LonLatToScreen(APoint.RealPoint);
// Draw the GPS point as a circle
cnv := TFPCustomCanvas(ACanvas);
cnv.Brush.FPColor := colRed;
cnv.Ellipse(P.X-R, P.Y-R, P.X+R, P.Y+R);
// Draw the "name" of the GPS point. Note: FPCustomCanvas, by default,
// does not support text output. Therefore we paint to a bitmap first and
// render this on the FPCustomCanvas.
txt := APoint.Name;
bmp := TBitmap.Create;
try
// bmp.PixelFormat := pf32Bit; // crashes Linux!
w := bmp.Canvas.TextWidth(txt);
h := bmp.Canvas.TextHeight(txt);
bmp.SetSize(w, h);
bmp.Canvas.Brush.Color := clWhite;
bmp.Canvas.FillRect(0, 0, w, h);
bmp.Canvas.TextOut(0, 0, txt);
img := bmp.CreateIntfImage;
cnv.Draw(P.X - w div 2, P.Y - h - 2*R, img);
img.Free;
finally
bmp.Free;
end;
end;
procedure TMainForm.MapViewMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
const
DELTA = 3;
var
rPt: TRealPoint;
rArea: TRealArea;
gpsList: TGpsObjList;
L: TStrings;
i: Integer;
begin
rPt := MapView.Center;
InfoCenterLongitude.Caption := Format('%.6f°', [rPt.Lon]);
InfoCenterLatitude.Caption := Format('%.6f°', [rPt.Lat]);
rPt := MapView.ScreenToLonLat(Point(X, Y));
InfoPositionLongitude.Caption := Format('%.6f°', [rPt.Lon]);
InfoPositionLatitude.Caption := Format('%.6f°', [rPt.Lat]);
rArea.TopLeft := MapView.ScreenToLonLat(Point(X-DELTA, Y-DELTA));
rArea.BottomRight := MapView.ScreenToLonLat(Point(X+DELTA, Y+DELTA));
gpsList := MapView.GpsItems.GetObjectsInArea(rArea);
try
if gpsList.Count > 0 then begin
L := TStringList.Create;
try
for i:=0 to gpsList.Count-1 do
if gpsList[i] is TGpsPoint then
with TGpsPoint(gpsList[i]) do
L.Add(Format('%s' + Lineending + ' (lat=%.6f°, lon=%.6f°)', [Name, Lat, Lon]));
GPSPointInfo.Caption := L.Text;
finally
L.Free;
end;
end else
GPSPointInfo.Caption := '';
finally
gpsList.Free;
end;
end;
procedure TMainForm.MapViewMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
rPt: TRealPoint;
gpsPt: TGpsPoint;
gpsName: String;
begin
if (Button = mbRight) then begin
if not InputQuery('Name of GPS location', 'Please enter name', gpsName) then
exit;
rPt := MapView.ScreenToLonLat(Point(X, Y));
gpsPt := TGpsPoint.CreateFrom(rPt);
gpsPt.Name := gpsName;
MapView.GpsItems.Add(gpsPt, _CLICKED_POINTS_);
end;
end;
procedure TMainForm.MapViewZoomChange(Sender: TObject);
begin
ZoomTrackbar.Position := MapView.Zoom;
end;
procedure TMainForm.ReadFromIni;
var
ini: TCustomIniFile;
List: TStringList;
L, T, W, H: Integer;
R: TRect;
i: Integer;
s: String;
pt: TRealPoint;
begin
ini := TMemIniFile.Create(CalcIniName);
try
R := Screen.DesktopRect;
L := ini.ReadInteger('MainForm', 'Left', Left);
T := ini.ReadInteger('MainForm', 'Top', Top);
W := ini.ReadInteger('MainForm', 'Width', Width);
H := ini.ReadInteger('MainForm', 'Height', Height);
if L + W > R.Right then L := R.Right - W;
if L < R.Left then L := R.Left;
if T + H > R.Bottom then T := R.Bottom - H;
if T < R.Top then T := R.Top;
SetBounds(L, T, W, H);
MapView.MapProvider := ini.ReadString('MapView', 'Provider', MapView.MapProvider);
CbProviders.Text := MapView.MapProvider;
MapView.Zoom := ini.ReadInteger('MapView', 'Zoom', MapView.Zoom);
pt.Lon := StrToFloatDef(ini.ReadString('MapView', 'Center.Longitude', ''), 0.0, PointFormatSettings);
pt.Lat := StrToFloatDef(ini.ReadString('MapView', 'Center.Latitude', ''), 0.0, PointFormatSettings);
MapView.Center := pt;
List := TStringList.Create;
try
ini.ReadSection('Locations', List);
for i:=0 to List.Count-1 do begin
s := ini.ReadString('Locations', List[i], '');
if s <> '' then
CbLocations.Items.Add(s);
end;
finally
List.Free;
end;
finally
ini.Free;
end;
end;
procedure TMainForm.UpdateDropdownWidth(ACombobox: TCombobox);
var
cnv: TControlCanvas;
i, w: Integer;
s: String;
P: TLocationParam;
begin
w := 0;
cnv := TControlCanvas.Create;
try
cnv.Control := ACombobox;
cnv.Font.Assign(ACombobox.Font);
for i:=0 to ACombobox.Items.Count-1 do begin
cnv.Font.Style := [fsBold];
s := ACombobox.Items.Strings[i];
w := Max(w, cnv.TextWidth(s));
P := TLocationParam(ACombobox.Items.Objects[i]);
cnv.Font.Style := [];
w := Max(w, cnv.TextWidth(P.Descr));
end;
ACombobox.ItemWidth := w + 16;
ACombobox.ItemHeight := 2 * cnv.TextHeight('Tg') + 6;
finally
cnv.Free;
end;
end;
procedure TMainForm.UpdateLocationHistory(ALocation: String);
var
idx: Integer;
begin
idx := CbLocations.Items.IndexOf(ALocation);
if idx <> -1 then
CbLocations.Items.Delete(idx);
CbLocations.Items.Insert(0, ALocation);
while CbLocations.Items.Count > MAX_LOCATIONS_HISTORY do
CbLocations.Items.Delete(Cblocations.items.Count-1);
CbLocations.Text := ALocation;
end;
procedure TMainForm.WriteToIni;
var
ini: TCustomIniFile;
L: TStringList;
i: Integer;
begin
ini := TMemIniFile.Create(CalcIniName);
try
ini.WriteInteger('MainForm', 'Left', Left);
ini.WriteInteger('MainForm', 'Top', Top);
ini.WriteInteger('MainForm', 'Width', Width);
ini.WriteInteger('MainForm', 'Height', Height);
ini.WriteString('MapView', 'Provider', MapView.MapProvider);
ini.WriteInteger('MapView', 'Zoom', MapView.Zoom);
ini.WriteString('MapView', 'Center.Longitude', FloatToStr(MapView.Center.Lon, PointFormatSettings));
ini.WriteString('MapView', 'Center.Latitude', FloatToStr(MapView.Center.Lat, PointFormatSettings));
ini.EraseSection('Locations');
for i := 0 to CbLocations.Items.Count-1 do
ini.WriteString('Locations', 'Item'+IntToStr(i), CbLocations.Items[i]);
finally
ini.Free;
end;
end;
procedure TMainForm.ZoomTrackBarChange(Sender: TObject);
begin
MapView.Zoom := ZoomTrackBar.Position;
LblZoom.Caption := Format('Zoom (%d):', [ZoomTrackbar.Position]);
end;
initialization
PointFormatSettings.DecimalSeparator := '.';
end.