lazarus-ccr/components/lazmapviewer/examples/elevations/main.pas

308 lines
7.6 KiB
ObjectPascal

unit Main;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, LazFileUtils,
LCLIntf, Forms, Controls, Graphics, Dialogs, StdCtrls,
ExtCtrls, Grids, Buttons, Types,
mvMapViewer, mvTypes, mvGeoMath, mvEngine, mvGpsObj, mvDrawingEngine;
type
{ TMainForm }
TMainForm = class(TForm)
ApplicationProperties: TApplicationProperties;
cbElevDatasets: TComboBox;
DataGrid: TDrawGrid;
ImageList1: TImageList;
Label1: TLabel;
lblDatasets: TLabel;
lblURL: TLabel;
MapView: TMapView;
Log: TMemo;
Panel1: TPanel;
btnDelete: TSpeedButton;
Splitter1: TSplitter;
Splitter2: TSplitter;
procedure ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean);
procedure DataGridDrawCell(Sender: TObject; aCol, aRow: Integer;
aRect: TRect; aState: TGridDrawState);
procedure DataGridPrepareCanvas(Sender: TObject; aCol, aRow: Integer;
aState: TGridDrawState);
procedure FormCreate(Sender: TObject);
procedure lblURLClick(Sender: TObject);
procedure lblURLMouseEnter(Sender: TObject);
procedure lblURLMouseLeave(Sender: TObject);
procedure MapViewDrawGpsPoint(Sender: TObject;
ADrawer: TMvCustomDrawingEngine; APoint: TGpsPoint);
procedure MapViewMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure btnDeleteClick(Sender: TObject);
private
FCounter: Integer;
function GetElevation(APoint: TRealPoint): Double;
function SelectedDataset: String;
public
end;
var
MainForm: TMainForm;
implementation
{$R *.lfm}
uses
Math, fpjson, jsonparser;
const
_CLICKED_POINTS_ = 10;
{ TMainForm }
procedure TMainForm.FormCreate(Sender: TObject);
begin
MapView.Center := RealPoint(47, 11);
MapView.Zoom := 8;
MapView.MapProvider := 'Google Satellite';
MapView.Active := true;
DataGrid.ColWidths[0] := 40;
DataGrid.ColWidths[1] := 100;
DataGrid.ColWidths[2] := 100;
DataGrid.ColWidths[3] := 90;
end;
// Sends a request to opentopodata.org and extracts the elevation from the
// returned json stream which is structures as follows:
(*
{
"results": [{
"elevation": 815.0,
"location": {
"lat": 56.0,
"lng": 123.0
},
"dataset": "test-dataset"
}],
"status": "OK"
}
*)
function TMainForm.GetElevation(APoint: TRealPoint): Double;
var
url: String;
fs: TFormatSettings;
stream: TStream;
json: TJsonData;
jsonData: TJsonData;
begin
fs := DefaultFormatSettings;
fs.DecimalSeparator := '.';
url := Format('https://api.opentopodata.org/v1/%s?locations=%.9f,%.9f', [
SelectedDataset, APoint.Lat, APoint.Lon
], fs);
stream := TMemoryStream.Create;
try
MapView.Engine.DownloadEngine.DownloadFile(url, stream);
json := GetJSON(stream);
try
Log.Text := json.FormatJSON;
jsonData := json.FindPath('results[0]').FindPath('elevation');
if jsonData.IsNull then
Result := NaN
else
Result := jsonData.AsFloat;
finally
json.Free;
end;
finally
stream.free;
end;
end;
// Calculates the selected dataset parameter for the opentopodata URL.
function TMainForm.SelectedDataset: String;
var
s: String;
p: Integer;
begin
Result := '';
if cbElevDatasets.ItemIndex <> -1 then
begin
s := cbElevDatasets.Items[cbElevDatasets.ItemIndex];
p := pos(' ', s);
if p > 0 then
Result := Copy(s, 1, p-1);
end;
end;
// Draws the stored GPS points
procedure TMainForm.MapViewDrawGpsPoint(Sender: TObject;
ADrawer: TMvCustomDrawingEngine; APoint: TGpsPoint);
const
MARGIN = 8;
var
P: TPoint;
d: Integer;
extent: TSize;
R: TRect;
begin
// Screen coordinates of the GPS point
P := TMapView(Sender).LatLonToScreen(APoint.RealPoint);
// Draw the point symbol
ADrawer.PenColor := clRed;
ADrawer.PenWidth := 3;
d := 5;
ADrawer.Line(P.x - d, P.Y - d, P.X + d, P.Y + d);
ADrawer.Line(P.x - d, P.Y + d, P.X + d, P.Y - d);
// Prepare text output: background color...
inc(P.Y, d + 4);
extent := ADrawer.TextExtent(APoint.Name);
R := Rect(0, 0, extent.CX, extent.CY);
OffsetRect(R, P.X - extent.CX div 2, P.Y);
InflateRect(R, MARGIN, 0);
ADrawer.BrushStyle := bsSolid;
ADrawer.BrushColor := clRed;
ADrawer.FillRect(R.Left, R.Top, R.Right, R.Bottom);
// ... and font
ADrawer.FontColor := clWhite; //MapView.Font.Color;
ADrawer.FontName := MapView.Font.Name;
ADrawer.FontSize := MapView.Font.Size;
ADrawer.FontStyle := MapView.Font.Style;
// Write the POI text
ADrawer.TextOut(P.X - extent.CX div 2, P.Y, APoint.Name);
end;
{ Gets the GPS coordinates of the clicked point in the map view and query the
elevation. Store as a data point in the GPSItems of the MapView. }
procedure TMainForm.MapViewMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
p: TRealPoint;
gpsPt: TGpsPoint;
elev: Double;
begin
if (ssCtrl in Shift) then
begin
p := MapView.ScreenToLatLon(Point(X, Y));
elev := GetElevation(p);
gpsPt := TGpsPoint.CreateFrom(p, elev);
inc(FCounter);
gpsPt.Name := IntToStr(FCounter);
MapView.GpsItems.Add(gpsPt, _CLICKED_POINTS_);
DataGrid.RowCount := MapView.GPSItems.Count + DataGrid.FixedRows;
end;
end;
{ Draws a cell in the DataGrid which gets is data from the GPS points stored in
the MapView component. }
procedure TMainForm.DataGridDrawCell(Sender: TObject; aCol, aRow: Integer;
aRect: TRect; aState: TGridDrawState);
var
cellText: String;
idx: Integer;
pt: TGPSPoint;
begin
cellText := '';
if ARow = 0 then
case ACol of
0: cellText := 'No.';
1: cellText := 'Longitude';
2: cellText := 'Latitude';
3: cellText := 'Elevation (m)';
end
else
begin
idx := ARow - DataGrid.FixedRows;
pt := MapView.GPSItems[idx] as TGPSPoint;
case ACol of
0: cellText := pt.Name;
1: cellText := LonToStr(pt.Lon, true);
2: cellText := LatToStr(pt.Lat, true);
3: if IsNaN(pt.Elevation) then
cellText := '(unknown)'
else
cellText := FormatFloat('0', pt.Elevation);
end;
end;
InflateRect(ARect, -varCellPadding, -varCellPadding);
DataGrid.Canvas.TextRect(ARect, ARect.Left, ARect.Top, cellText);
end;
procedure TMainForm.ApplicationPropertiesIdle(Sender: TObject; var Done: Boolean);
begin
btnDelete.Enabled := (DataGrid.RowCount > 1) and (DataGrid.Row > 0);
end;
// Some formatting for the DataGrid: Headers will be bold and centered,
// data cells will be right-aligned.
procedure TMainForm.DataGridPrepareCanvas(Sender: TObject; aCol, aRow: Integer;
aState: TGridDrawState);
var
ts: TTextStyle;
begin
ts := DataGrid.Canvas.TextStyle;
if aRow = 0 then
begin
DataGrid.Canvas.Font.Style := [fsBold];
ts.Alignment := taCenter;
end else
begin
ts.Alignment := taRightJustify;
end;
DataGrid.Canvas.TextStyle := ts;
end;
// Deletes the selected datapoint in the DataGrid.
procedure TMainForm.btnDeleteClick(Sender: TObject);
var
idx: Integer;
item: TGPSObj;
i: Integer;
begin
idx := DataGrid.Row - DataGrid.FixedRows;
if idx < 0 then
exit;
// Delete from map's GPS items
item := MapView.GPSItems[idx];
MapView.GPSItems.Delete(item);
MapView.Invalidate;
// Update grid
DataGrid.RowCount := DataGrid.RowCount-1;
DataGrid.Invalidate;
end;
// Open the browser with the opentopodata.org web-site.
procedure TMainForm.lblURLClick(Sender: TObject);
begin
OpenURL('https://www.opentopodata.org/');
end;
// Underline the opentopodata.org URL when the mouse enters the label
procedure TMainForm.lblURLMouseEnter(Sender: TObject);
begin
lblURL.Font.Style := [fsUnderline];
end;
procedure TMainForm.lblURLMouseLeave(Sender: TObject);
begin
lblURL.Font.Style := [];
end;
end.