- ตอนที่แล้วได้แนะนำสูตรที่จะใช้ในการคำนวณและแสดงยูนิต GeodesicCompute พร้อมทั้งยูนิต GeoEllipsoids ที่เคยแสดงไปแล้วเรื่องการแปลงค่าพิกัดระหว่าง UTM และ Geographic
- เปิด Lazarus คลิกที่เมนเมนู Project > New Project… คลิกเลือก Application คลิก OK
ตั้งค่า Compiler Options ก่อน
- ผมขอย้ำอีกครั้งว่าเมื่อสร้าง Project ใหม่แล้วสิ่งแรกที่ต้องทำคือตั้ง Compiler Options ให้ตรงกับ Platform และ Widget ที่เราใช้อยู่ ที่เมนู Project > Compiler options… ที่ path เลือก LCL Widget เป็น win32/win64 ดังรูปด้านล่าง

- ที่ code คลิกเลือก Target OS เป็น win32/win64 และ Target CPU Family เป็น i386 ส่วน Target processor เลือกเป็น Default ดังรูปด้านล่าง
เพิ่มยูนิตเข้าไปใน Project
- จากที่ได้กล่าวไปข้างต้นเรามี 2 ยูนิตที่เตรียมไว้แล้วคือ GeodesicCompute (geodesiccompute.pas) และ GeoEllipsoids (geoellipsoids.pas) ที่ Project Inspector คลิกที่เครื่องหมายบวกเพื่อเพิ่มยูนิต คลิกที่แท็ป Add files จากนั้นคลิกที่ Browse เพื่อเลือกไฟล์ที่เราเตรียมไว้
- เมื่อเลือกไฟล์ได้แล้วจะเห็นหน้าตาของ Project Inspector ดังรูปด้านล่าง
- คลิกที่เมนู Project > Save Project As… ตั้งชื่อเป็น Geodesics จะเห็นหน้าตาของ Lazarus และ project Geodesics ดังรูปด้านล่าง

- ต่อไปจะแปะพวก object ทั้งหลายเฃ่น Label, Edit, ComboBox, Button ลงไปและ ตั้งฃื่อ (Name) ดังรูปด้านล่าง
- ส่วน Label และ GroupBox ใ้ส่ชื่อดังรูปด้านล่าง
โค๊ดของฟอร์ม Unit1
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
StdCtrls, ExtCtrls, GeoEllipsoids, GeodesicCompute;
type
TForm1 = class(TForm)
cmdEx1: TButton;
cmdCompute: TButton;
cmdEx2: TButton;
cmdClose: TButton;
cmbEllipsoids: TComboBox;
grbIn: TGroupBox;
grbOut: TGroupBox;
labin2: TLabel;
labin1: TLabel;
labout3: TLabel;
labout1: TLabel;
Label8: TLabel;
labout2: TLabel;
radCT: TRadioGroup;
txtIn1: TEdit;
txtOut1: TEdit;
txtLong1: TEdit;
txtLat1: TEdit;
GroupBox1: TGroupBox;
Label1: TLabel;
Label2: TLabel;
txtIn2: TEdit;
txtOut3: TEdit;
txtOut2: TEdit;
procedure cmdCloseClick(Sender: TObject);
procedure cmdComputeClick(Sender: TObject);
procedure cmdEx1Click(Sender: TObject);
procedure cmdEx2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure radCTClick(Sender: TObject);
private
FEllipses : TEllipsoidList;
public
end;
var
Form1: TForm1;
implementation
function Coordinate(const X, Y : double) : TCoordinate;
begin
result.X := X;
result.Y := Y;
end;
procedure TForm1.cmdCloseClick(Sender: TObject);
begin
Close;
end;
procedure TForm1.cmdComputeClick(Sender: TObject);
var
inverse : TGeodesicInverse;
direct : TGeodesicDirect;
ell : TEllipsoid;
x1, y1, x2, y2, az, ds : double;
geocoor : TCoordinate;
begin
ell := FEllipses.FindEx(cmbEllipsoids.Text);
if (radCT.ItemIndex = 0) then
begin
x1 := strtofloat(txtlong1.Text);
y1 := strtofloat(txtlat1.Text);
x2 := strtofloat(txtIn2.Text);
y2 := strtofloat(txtIn1.Text);
try
inverse := TGeodesicInverse.Create;
inverse.Ellipsoid := ell;
inverse.GeoCoordinate1 := Coordinate(x1, y1);
inverse.GeoCoordinate2 := Coordinate(x2, y2);
inverse.Compute;
txtOut1.Text := format('%11.8f', [inverse.InitialAzimuth]);
txtOut2.Text := format('%11.8f', [inverse.FinalAzimuth]);
txtOut3.Text := format('%10.3f m.', [inverse.Distance]);
finally
inverse.Free;
end;
end
else if (radCT.ItemIndex = 1) then
begin
x1 := strtofloat(txtlong1.Text);
y1 := strtofloat(txtlat1.Text);
ds := strtofloat(txtIn2.Text);
az := strtofloat(txtIn1.Text);
try
direct := TGeodesicDirect.Create;
direct.Ellipsoid := ell;
direct.GeoCoordinate1 := Coordinate(x1, y1);
direct.Azimuth := az;
direct.Distance := ds;
direct.Compute;
x2 := direct.GeoCoordinate2.X;
y2 := direct.GeoCoordinate2.Y;
txtOut1.Text := format('%11.8f', [y2]);
txtOut2.Text := format('%11.8f', [x2]);
finally
direct.Free;
end;
end;
end;
procedure TForm1.cmdEx1Click(Sender: TObject);
begin
radCT.ItemIndex := 0;
txtLat1.Text := '53.150555556';
txtLong1.Text :='-1.844444444';
txtIn1.Text := '52.205277778';
txtIn2.Text :='-0.142500000';
txtOut1.Text := '';
txtOut2.Text := '';
txtOut3.Text := '';
end;
procedure TForm1.cmdEx2Click(Sender: TObject);
begin
radCT.ItemIndex := 1;
txtLat1.Text := '-37.9510334';
txtLong1.Text := '144.424868';
txtIn1.Text :='306.8681583';
txtIn2.Text :='54972.271';
txtOut1.Text := '';
txtOut2.Text := '';
txtOut3.Text := '';
end;
procedure TForm1.FormCreate(Sender: TObject);
var
i : integer;
begin
FEllipses := TEllipsoidList.Create;
for i := 0 to FEllipses.Count - 1 do
begin
cmbEllipsoids.Items.Add(TEllipsoid(FEllipses.Objects[i]).EllipsoidName);
end;
cmbEllipsoids.ItemIndex := FEllipses.Count - 1;
end;
procedure TForm1.radCTClick(Sender: TObject);
begin
if (radCT.ItemIndex = 0) then
begin
grbin.Caption := 'Point 2';
labin1.Caption := 'Latitude : ';
labin2.Caption := 'Longitude : ';
grbout.Caption := 'Azimuth && Distance';
labout1.Caption := 'Initial Azimuth : ';
labout2.Caption := 'Final Azimuth : ';
labout3.Show;
txtout3.Show;
labout3.Caption := 'Distance : ';
end
else if (radCT.ItemIndex = 1) then
begin
grbin.Caption := 'Azimuth && Distance';
labin1.Caption := 'Azimuth : ';
labin2.Caption := 'Distance : ';
grbout.Caption := 'Point 2';
labout1.Caption := 'Latitude : ';
labout2.Caption := 'Longitude : ';
labout3.Hide;
txtout3.Hide;
end;
end;
initialization
{$I unit1.lrs}
end.
อธิบายโค๊ด
- ที่นี้มาลองไล่ดูโค๊ดเฉพาะบางส่วนที่สำคัญ เริ่มแรกตรงเมธอด FormCreate
procedure TForm1.FormCreate(Sender: TObject);
var
i : integer;
begin
FEllipses := TEllipsoidList.Create;
for i := 0 to FEllipses.Count - 1 do
begin
cmbEllipsoids.Items.Add(TEllipsoid(FEllipses.Objects[i]).EllipsoidName);
end;
cmbEllipsoids.ItemIndex := FEllipses.Count - 1;
end;
- เ่ริ่มจากการสร้าง object สำหรับเก็บทรงรี TEllipsoidList นั้นออกแบบมาคล้ายๆกับ Collection เมื่อสร้าง Object เรียบร้อยแล้วก็นำมาใ่ส่ให้ ComboBox ชื่อ cmbEllipsoids เ้สร็จแล้วให้สถานะของทรงรีเป็น WGS84 (ทรงรี WGS84 จะอยู่ท้ายสุดใน list)
- ต่อไปผมสร้าง Event เมื่อคลิกด้วยเมาส์เพื่อโยงให้ radio group box ชื่อ radCT (property Items ผมใส่ string ให้ 2 string คือ Geodesic Inverse และ Geodesic Direct ตามลำดับ) ตัว radCT จะทำหน้าที่ให้ผู้ใช้โปรแกรมเลือกว่าจะคำนวณแบบไหน เมื่อเลือกแบบใดแบบหนึ่งโปรแกรมจะเปลี่ยน label ให้สอดคล้อง input และ output
procedure TForm1.radCTClick(Sender: TObject);
begin
if (radCT.ItemIndex = 0) then
begin
grbin.Caption := 'Point 2';
labin1.Caption := 'Latitude : ';
labin2.Caption := 'Longitude : ';
grbout.Caption := 'Azimuth && Distance';
labout1.Caption := 'Initial Azimuth : ';
labout2.Caption := 'Final Azimuth : ';
labout3.Show;
txtout3.Show;
labout3.Caption := 'Distance : ';
end
else if (radCT.ItemIndex = 1) then
begin
grbin.Caption := 'Azimuth && Distance';
labin1.Caption := 'Azimuth : ';
labin2.Caption := 'Distance : ';
grbout.Caption := 'Point 2';
labout1.Caption := 'Latitude : ';
labout2.Caption := 'Longitude : ';
labout3.Hide;
txtout3.Hide;
end;
end;
- เพื่อให้การใช้โปรแกรมนั้นง่าย ผมจะใส่ตัวอย่างข้อมูสำหรับป้อนเข้า ไว้สองตัวอย่างคือคำนวณแบบ Inverse (Example 1) และคำนวณแบบ Direct (Example 2) เมื่อผู่ใช้ทำการคลิกที่ปุ่ม Example 1 จะเรียก Event ที่สร้างไว้คือ cmdEx1Click ส่วนปุ่ม Example 2 จะเรียก event cmdEx2Click
procedure TForm1.cmdEx1Click(Sender: TObject);
begin
radCT.ItemIndex := 0;
txtLat1.Text := '53.150555556';
txtLong1.Text :='-1.844444444';
txtIn1.Text := '52.205277778';
txtIn2.Text :='-0.142500000';
txtOut1.Text := '';
txtOut2.Text := '';
txtOut3.Text := '';
end;
procedure TForm1.cmdEx2Click(Sender: TObject);
begin
radCT.ItemIndex := 1;
txtLat1.Text := '-37.9510334';
txtLong1.Text := '144.424868';
txtIn1.Text :='306.8681583';
txtIn2.Text :='54972.271';
txtOut1.Text := '';
txtOut2.Text := '';
txtOut3.Text := '';
end;
- สุดท้ายก็มาดูที่เมธอดสำคัญคือ cmdComputeClick สร้างไว้เพื่อดัก event เมื่อผู้ใช้คลิกที่ปุ่ม Compute
procedure TForm1.cmdComputeClick(Sender: TObject);
var
inverse : TGeodesicInverse;
direct : TGeodesicDirect;
ell : TEllipsoid;
x1, y1, x2, y2, az, ds : double;
geocoor : TCoordinate;
begin
ell := FEllipses.FindEx(cmbEllipsoids.Text);
if (radCT.ItemIndex = 0) then
begin
x1 := strtofloat(txtlong1.Text);
y1 := strtofloat(txtlat1.Text);
x2 := strtofloat(txtIn2.Text);
y2 := strtofloat(txtIn1.Text);
try
inverse := TGeodesicInverse.Create;
inverse.Ellipsoid := ell;
inverse.GeoCoordinate1 := Coordinate(x1, y1);
inverse.GeoCoordinate2 := Coordinate(x2, y2);
inverse.Compute;
txtOut1.Text := format('%11.8f', [inverse.InitialAzimuth]);
txtOut2.Text := format('%11.8f', [inverse.FinalAzimuth]);
txtOut3.Text := format('%10.3f m.', [inverse.Distance]);
finally
inverse.Free;
end;
end
else if (radCT.ItemIndex = 1) then
begin
x1 := strtofloat(txtlong1.Text);
y1 := strtofloat(txtlat1.Text);
ds := strtofloat(txtIn2.Text);
az := strtofloat(txtIn1.Text);
try
direct := TGeodesicDirect.Create;
direct.Ellipsoid := ell;
direct.GeoCoordinate1 := Coordinate(x1, y1);
direct.Azimuth := az;
direct.Distance := ds;
direct.Compute;
x2 := direct.GeoCoordinate2.X;
y2 := direct.GeoCoordinate2.Y;
txtOut1.Text := format('%11.8f', [y2]);
txtOut2.Text := format('%11.8f', [x2]);
finally
direct.Free;
end;
end;
end;
- จากโค๊ดด้านบน เมื่อผู้ใช้เลือกแบบการคำนวณโดยการคลิกที่ radio group โปรแกรมเราจะเลือกการคำนวณให้ที่เหมาะสม โดยจะมี 2 ตัวแปรสำคํญคือ direct เรา assign จาก class ชื่อ TGeodesicDirect และตัวแปรอีกตัวคือ inverse โดย derive มาจาก class ชื่อ TGeodesicInverse ลองดู source code ก็ไม่มีอะไรยากเลย เริ่มต้นจากการสร้าง object => inverse หรือ direct จากนั้นก็เอาทรงรีมา assign ให้จากผู้ใช้คลิกเลือกทรงรีที่ ComboBox ต่อไปก็ input ค่าพิกัด Geographic ให้ 2 จุด (ถ้าคำนวณ inverse) หรือป้อนค่าพิกัดให้ 1 จุด แล้วป้อน ระยะทาง และ อะซิมัท จากนั้นจะเรียกเมธอด Compute และส่งค่าผลลัพธ์มาทาง property
การคำนวณแบบ Inverse
- เมื่อทุกสิ่งทุกอย่างพร้อมกดปุ่มคีย์บอร์ด F9 เพื่อให้ Lazarus ทำการ compile และ build เราตั้งชื่อ project เราว่า Geodesics เมื่อ build แล้วจะได้ไฟล์ Geodesics.exe ส่วนใน Linux จะไม่มี extension ให้ เวลารันถ้าใช้ command ใน linux จะเป็นดังนี้ $./geodesics (พิมพ์จุดและเครื่องหมาย fore slash แล้วตามด้วยชื่อโปรแกรม) ตัวอย่างเมื่อรันโปรแกรม คลิกที่ปุ่ม Example 1 แล้วคลิก Compute เพื่อทำการคำนวณ จะได้ผลลัพธ์ดังรูปด้านล่าง
- สังเกตว่าที่เราทำการคำนวณคือระยะทางและอะซิมัทไปตามเส้น Geodesic ซึ่งเป็นเส้นโค้งอะซิมัทจะค่อยๆเปลี่ยนไปตามเส้นโค้ง ดังน้น Initial Azimuth จึงเป็นค่าอะซิมัทเริ่มต้น และ Final Azimuth คือค่าอะซิมัทสุดท้ายที่เส้น geodesic ไปแตะจุดที่สองพอดี
การคำนวณแบบ Direct
- รันโปรแกรม คลิกที่ปุ่ม Example 2 แล้วคลิก Compute เพื่อทำการคำนวณ จะได้ผลลัพธ์ดังรูปด้านล่าง
ข้อจำกัดของโปรแกรม
- เนื่องจากเป็นโปรแกรมตัวอย่างที่ให้ดูกันง่ายๆ จึงไม่มีการดักข้อมูล error จากการพิมพ์ใส่ข้อมูล ที่ EditBox เช่นถ้าผู้ใช้ป้อนแทนที่จะเป็นตัวเลขแต่ไปใส่อักขระอื่นแทนก็จะ error และไม่มีการดักจับค่าพิกัด 2 จุดจะต้องไม่เท่ากัน ถ้าเท่ากันก็จะ error เช่นเดียวกันขณะรันโปรแกรม
- ก็ขอจบกันไว้เพียงเท่านี้ ตอนหน้าจะมาว่าเรื่องฐานข้อมูลฉบับกระเป๋าเล้กพริกขี้หนู SQLite ส่วนในตอนต่อๆไปอาจจะเป็นฐานข้อมูลเจ้านกไฟ FireBird
Download sourcecode
Related