Skip to content

Prajuab Riabroy's Blog

Survey Application – GNSS – UAV – Programing – Python – Dart&Flutter – Rust

Menu
  • Home
  • About Us
  • Survey Application
    • Surveyor Pocket Tools
    • Traverse Pro
    • XSection Plot
    • Geoid Height Pro
    • Spot Fire
    • โปรแกรมงานสำรวจสำหรับเครื่องคิดเลข Casio fx-5800P
    • โปรแกรมงานสำรวจสำหรับเครื่องคิดเลข Casio fx-9860G II SD
  • Download
  • Privacy Policy
Menu

การเขียนโปรแกรมเพื่อคำนวณระยะทางและอะซิมัท (Distance/Azimuth) บน Ellipsoid ด้วย Lazarus (ตอนที่ 2)

Posted on 25/10/2009
  • ตอนที่แล้วได้แนะนำสูตรที่จะใช้ในการคำนวณและแสดงยูนิต 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 ดังรูปด้านล่าง
    geodesicsb1
  • ที่ code คลิกเลือก Target OS เป็น win32/win64 และ Target CPU Family เป็น i386 ส่วน Target processor เลือกเป็น Default ดังรูปด้านล่าง
geodesicsb2

เพิ่มยูนิตเข้าไปใน Project

  • จากที่ได้กล่าวไปข้างต้นเรามี 2 ยูนิตที่เตรียมไว้แล้วคือ GeodesicCompute (geodesiccompute.pas) และ GeoEllipsoids (geoellipsoids.pas) ที่ Project Inspector คลิกที่เครื่องหมายบวกเพื่อเพิ่มยูนิต คลิกที่แท็ป Add files จากนั้นคลิกที่ Browse เพื่อเลือกไฟล์ที่เราเตรียมไว้
projectInspector
  • เมื่อเลือกไฟล์ได้แล้วจะเห็นหน้าตาของ Project Inspector ดังรูปด้านล่าง
projectInspector2
  • คลิกที่เมนู Project > Save Project As… ตั้งชื่อเป็น Geodesics จะเห็นหน้าตาของ Lazarus และ project Geodesics ดังรูปด้านล่าง
    geodesicsb4
  • ต่อไปจะแปะพวก object ทั้งหลายเฃ่น Label, Edit, ComboBox, Button ลงไปและ ตั้งฃื่อ (Name) ดังรูปด้านล่าง
geodesicsb5
  • ส่วน Label และ GroupBox ใ้ส่ชื่อดังรูปด้านล่าง
geodesicsb7

โค๊ดของฟอร์ม Unit1

unit Unit1; 

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  StdCtrls, ExtCtrls, GeoEllipsoids, GeodesicCompute;

type

  { TForm1 }

  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
    { private declarations }
    FEllipses : TEllipsoidList;
  public
    { public declarations }
  end; 

var
  Form1: TForm1; 

implementation

{ TForm1 }

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 //Direct
  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; //Geodesic Inverse
  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; //Geodesic Direct
  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; //Default with WGS84
end;

procedure TForm1.radCTClick(Sender: TObject);
begin
  if (radCT.ItemIndex = 0) then //Inverse.
  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; //Default with WGS84
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 //Inverse.
  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; //Geodesic Inverse
  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; //Geodesic Direct
  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 //Direct
  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 เพื่อทำการคำนวณ จะได้ผลลัพธ์ดังรูปด้านล่าง
geodesicsb8
  • สังเกตว่าที่เราทำการคำนวณคือระยะทางและอะซิมัทไปตามเส้น Geodesic ซึ่งเป็นเส้นโค้งอะซิมัทจะค่อยๆเปลี่ยนไปตามเส้นโค้ง ดังน้น Initial Azimuth จึงเป็นค่าอะซิมัทเริ่มต้น และ Final Azimuth คือค่าอะซิมัทสุดท้ายที่เส้น geodesic ไปแตะจุดที่สองพอดี

การคำนวณแบบ Direct

  • รันโปรแกรม คลิกที่ปุ่ม Example 2 แล้วคลิก Compute เพื่อทำการคำนวณ จะได้ผลลัพธ์ดังรูปด้านล่าง
geodesicsb9

ข้อจำกัดของโปรแกรม

  • เนื่องจากเป็นโปรแกรมตัวอย่างที่ให้ดูกันง่ายๆ จึงไม่มีการดักข้อมูล error จากการพิมพ์ใส่ข้อมูล ที่ EditBox เช่นถ้าผู้ใช้ป้อนแทนที่จะเป็นตัวเลขแต่ไปใส่อักขระอื่นแทนก็จะ error และไม่มีการดักจับค่าพิกัด 2 จุดจะต้องไม่เท่ากัน ถ้าเท่ากันก็จะ error เช่นเดียวกันขณะรันโปรแกรม
  • ก็ขอจบกันไว้เพียงเท่านี้ ตอนหน้าจะมาว่าเรื่องฐานข้อมูลฉบับกระเป๋าเล้กพริกขี้หนู SQLite ส่วนในตอนต่อๆไปอาจจะเป็นฐานข้อมูลเจ้านกไฟ FireBird

Download sourcecode

  • sourcecode ของโปรแกรมตอนนี้สามารถ download ได้ที่นี่ GeodesicProj.zip

Related

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

October 2026
M T W T F S S
 1234
567891011
12131415161718
19202122232425
262728293031  
« Sep    

Blog Stats

There was an issue retrieving stats. Please try again later.

Recent Posts

  • เร็วแรงทะลุนรกกับ POCUT (POint Cloud UTils)
  • อัพเดทแอพ “SurveyStar LDP Thailand” เพื่อรับกับยกร่างมาตรฐาน “TH-LDP”
  • ออกแอพใหม่ SurveyStar Traverse Pro สืบสานงานคำนวณวงรอบ ตอนที่ 2
  • ออกแอพใหม่ SurveyStar Traverse Pro สืบสานงานคำนวณวงรอบ ตอนที่ 1
  • โปรแกรมอรรถประโยชน์ Pocut (POint Cloud Utils)
  • โปรแกรมแปลงไฟล์แคดข้ามระบบพิกัด (Drawing Transformer)
  • มาตรฐานระบบพิกัดแผนที่แบบแอลดีพีของประเทศไทยสำหรับงานวิศวกรรมและการก่อสร้าง (Thailand Low Distortion Map Coordinate System for Engineering and Construction)
  • ประสบการณ์ใช้งาน Kimi K3 Code เขมือบแบบสยอง
  • สร้างแพลตฟอร์มดู Point Cloud บนเว็บด้วย Potree + CesiumJS — เรื่องเล่าจากสนามบินอู่ตะเภา
  • เมื่อวิศวกรสำรวจคำนวณเรื่องน้ำ – วิเคราะห์ระบบระบายน้ำบริเวณ RESA
  • แนะนำหนังสือ “จีออเดซี” ที่ยอดเยี่ยมของดร.พุทธิพล
  • ESPrtk ทำให้ ESP32 ใช้ไม่ได้? วิธีกู้คืน
  • กรณีศึกษา – คืนชีพ SDL30 ปี 2000 ด้วย ESP32 และแอพทำเอง SDL30 Collector
  • DIY RTK Base Station (survey-grade) รุ่น V3.21 ด้วย Raspberry Pi 2B + UM980 เมื่อคุณปู่กลับมาเป็นฮีโร่ ตอนที่ 4
  • รีวิวกูปรี (CoPre2) โปรแกรมประมวลผลไลดาร์จากค่าย CHCNav
  • DIY RTK Base Station (survey-grade) รุ่น V3.2 ด้วย Orange Pi 3 Zero + UM980 ตอนที่ 3
  • DIY RTK Base Station (survey-grade) รุ่น V3 ด้วย Orange Pi 3 Zero + UM980 ตอนที่ 2
  • เส้นทางและไทม์ไลน์ของใบรับรองความรู้ความชำนาญในการประกอบอาชีพวิศวกรรม(สำรวจ)ระดับวิศวกรวิชาชีพ
  • มัดแหนมรวมสามโปรแกรมไปใช้บนสโตร์ Snap ฝั่งลีนุกซ์
  • ปัดฝุ่น Traverse Pro เพิ่มรายละเอียดรายการคำนวณใหม่แบบแนวนอน (Landscape) และธีมมืด
  • DIY RTK Base Station (Survey-Grade) ด้วยชิป UM980 และบอร์ด ESP32 ตอนที่ 1
  • สร้าง NTRIP Caster ด้วย BNC บนคลาวด์แบบ DIY สำหรับ RTK Base Station ใช้ CHCNav P5
  • จับ AI มาไตรกรีฬา สร้างตัวติดตั้ง Snap สำหรับ Surveyor Pocket Tools กับประเด็นแฝงเร้นความเป็นส่วนตัวของผู้ใช้
  • ทดสอบรีด QGIS แบบโหดสัสด้วยชุดข้อมูล Open Building V3 โพลีกอน 74 ล้านหลัง
  • เมื่อ AI เป็นพิษชักจูงผู้ใช้ด้วยข้อมูลผิดๆ

Subscribe to Blog via Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 122 other subscribers

Archives

  • October 2026
  • September 2026
  • August 2026
  • July 2026
  • May 2026
  • April 2026
  • March 2026
  • January 2026
  • December 2025
  • November 2025
  • September 2025
  • August 2025
  • July 2025
  • June 2025
  • May 2025
  • January 2025
  • December 2024
  • November 2024
  • October 2024
  • September 2024
  • July 2024
  • May 2024
  • April 2024
  • January 2024
  • November 2023
  • October 2023
  • September 2023
  • July 2023
  • June 2023
  • May 2023
  • March 2023
  • February 2023
  • January 2023
  • November 2022
  • October 2022
  • September 2022
  • July 2022
  • May 2022
  • December 2021
  • October 2021
  • September 2021
  • August 2021
  • May 2021
  • February 2021
  • January 2021
  • December 2020
  • November 2020
  • October 2020
  • September 2020
  • July 2020
  • June 2020
  • May 2020
  • April 2020
  • March 2020
  • February 2020
  • January 2020
  • December 2019
  • November 2019
  • September 2019
  • August 2019
  • July 2019
  • May 2019
  • April 2019
  • March 2019
  • February 2019
  • January 2019
  • November 2018
  • October 2018
  • September 2018
  • August 2018
  • July 2018
  • June 2018
  • May 2018
  • April 2018
  • March 2018
  • January 2018
  • November 2017
  • July 2017
  • June 2017
  • May 2017
  • April 2017
  • March 2017
  • February 2017
  • January 2017
  • December 2016
  • November 2016
  • October 2016
  • August 2016
  • November 2015
  • March 2015
  • August 2014
  • March 2014
  • January 2014
  • December 2013
  • February 2012
  • January 2012
  • November 2011
  • October 2011
  • August 2011
  • April 2011
  • February 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • June 2010
  • April 2010
  • March 2010
  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • September 2009
  • August 2009

Recent Comments

  • priabroy on ออกแอพใหม่ SurveyStar Traverse Pro สืบสานงานคำนวณวงรอบ ตอนที่ 1
  • Konexana Yomleuangsa on ออกแอพใหม่ SurveyStar Traverse Pro สืบสานงานคำนวณวงรอบ ตอนที่ 1
  • เฟือง เถา on DIY RTK Base Station (survey-grade) รุ่น V3.21 ด้วย Raspberry Pi 2B + UM980 เมื่อคุณปู่กลับมาเป็นฮีโร่ ตอนที่ 4
  • priabroy on DIY RTK Base Station (survey-grade) รุ่น V3.21 ด้วย Raspberry Pi 2B + UM980 เมื่อคุณปู่กลับมาเป็นฮีโร่ ตอนที่ 4
  • ตรันฟองนาม on DIY RTK Base Station (survey-grade) รุ่น V3.21 ด้วย Raspberry Pi 2B + UM980 เมื่อคุณปู่กลับมาเป็นฮีโร่ ตอนที่ 4
©2026 Prajuab Riabroy's Blog | Design: Newspaperly WordPress Theme