Month: September 2010

การเขียนโปรแกรมต่อ GPS ด้วยคอมโพเน็นต์ OpenGPSX (ภาค 5)

ทดสอบ OpenGPSX บน Ubuntu

  • มาลองทดสอบกันบน Ubuntu Lucid ก็เริ่มจากดาวน์โหลดคอมโพเน็นต์ มาก่อน ทำการติดตั้งเหมือนในวินโดส์ ที่ผมกล่าวไปแล้ว เนื่องจากโค้ดของ Lazarus เป็น cross-platform โค้ดที่ใช้ใน Windows ก็สามารถนำมาใช้ได้เลยใน Linux แต่ติดขัดตรงโปรแกรมที่จะใช้ทดสอบ ผมจะแยกเวอร์ชั่นวินโดส์และเวอร์ชั่น Linux เพราะฟอร์มที่สร้างในวินโดส์ ถ้านำมาใช้ใน Linux ตัวหนังสือ object ที่เราแปะลงฟอร์มเช่น Label, Edit จะใหญ่มากเพราะปัญหาเรื่อง Font ที่ไม่ตรงกันของสอง OS

ดาวน์โหลดและติดตั้ง

  • สนใจโปรแกรมทดสอบดาวน์โหลดได้ตามลิงค์นี้ OpenGPSProject.zip
  • เปิด Lazarus ติดตั้งคอมโพเน็นต์ OpenGPSX ก่อน แล้วค่อย unzip ไฟล์ทดสอบ OpenGPSProject.zip ของผมเองเก็บไว้ที่ /home/priabroy/Lazarus/OpenGPSProject
หน้าตาของ Lazarus ที่ติดตั้ง component OpenGPSX แล้ว
  • เปิดโปรแกรมทดสอบ OpenGPSProject เปิด project ไฟล์ opengpstest.lpi แล้วกด F9 เพื่อรันได้เลย สังเกตว่าการ compile & build ใน Linux จะเร็วมากกว่าบนวินโดส์เทียบกันไม่ติด แต่ตอนรันโปรแกรมดูเหมือนในวินโดส์ทำงานได้เร็วกว่า
  • เปิดไฟล์ NMEA ที่อยู่ในโฟลเดอร์ Data ชื่อไฟล์ gpslog-mod.txt แล้วคลิกปุ่ม “Play NMEA >>”
เปิดไฟล์ NMEA คือไฟล์ gpslog-mod.txt
  • ลองทดสอบกันอีกไฟล์คือไฟล์ griffith-park.nmea จะเห็นหน้าตาตอนรันโปรแกรมดังนี้
ทดสอบด้วยไฟล์ griffith-park.nmea

ปัญหาของโค้ด Cross-platform

  • ผมมีเกร็ดจะเล่าคือเรื่องปํญหาโค้ด cross-platform จากสโลแกนของ Lazarus ที่ว่า Write Once Compile Anywhere ผมเห็นว่าไม่ร้อยเปอร์เซ็นต์ อย่างเช่น Class TGPSSkyplot โค้ดที่ใช้ได้ในวินโดส์กลับไม่ work ใน Linux  TGPSSkyplot เป็น class ที่ผมใช้เขียนสัญลักษณ์ GPS บนท้องฟ้า ในวินโดส์ใช้การเขียน Sprite ได้ แต่กลับใช้ไม่ได้ใน Linux ต้องหันมาใช้วิธีอื่น ลองดูโค้ดกันครับ
      </ul>
      </ul>
      &nbsp;
      <ul>
      <ul>procedure TGPSSkyPlot.DrawGPSSkyPlot(ASatInfos : TGSVInfos);</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>var</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>rSat, rFull : TRect;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>r : TRect;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>sk1, img1 : TPoint;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>x, y : integer;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>i : integer;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>dDist : double;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>Sat : TGSVInfo;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>ts : TSize;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>begin</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>if Assigned(ASatInfos) then</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>begin</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>FGSVInfos := ASatInfos;</ul>
      </ul>
      rSat := Rect(0, 0, FSatSize, FSatSize);
      rFull := Rect(0, 0, FBmpBackground.Width, FBmpBackground.Height);
      //First Sat, Copy FBufferBackgroundImg to FBmpBuffer.
      if (TGSVInfo(FGSVInfos.items[0]).FirstSat) then
      FBmpBuffer.Canvas.CopyRect(rFull, FBmpBackground.Canvas, rFull);
      
      for i := 0 to FGSVInfos.Count - 1 do
      begin
      Sat := TGSVInfo(FGSVInfos.Items[i]);
      dDist := (90 - Sat.Elevation) * FSkyHalfW / 90;
      sk1 := Polar2Cartesian(trunc(dDist),Sat.Azimuth);
      img1 := Cartesian2Pixel(sk1, FTransfrm);
      //Keep image coordinate system into TSatInfo.
      Sat.X := img1.X;
      Sat.Y := img1.Y;
      {Draw Sprites works very well in WINDOWS, Not in Linux}
      {$IFDEF WINDOWS}
      BitBlt(FBmpSatDest[i].Canvas.Handle, 0, 0, FBmpSatDest[i].Width, FBmpSatDest[i].Height,
      FBmpBuffer.Canvas.Handle, img1.X - FImgShift, img1.Y - FImgShift, SRCCOPY);
      BitBlt(FBmpSatDest[i].Canvas.Handle, 0, 0, FBmpSatDest[i].Width, FBmpSatDest[i].Height,
      FBmpMaskSat.Canvas.Handle, 0, 0, SRCAND);
      if (Sat.SNR = 0) then
      BitBlt(FBmpSatDest[i].Canvas.Handle, 0, 0, FBmpSatDest[i].Width, FBmpSatDest[i].Height,
      FBmpRedSpriteSat.Canvas.Handle, 0, 0, SRCINVERT)
      else
      BitBlt(FBmpSatDest[i].Canvas.Handle, 0, 0, FBmpSatDest[i].Width, FBmpSatDest[i].Height,
      FBmpGreenSpriteSat.Canvas.Handle, 0, 0, SRCINVERT);
      {$ENDIF}
      {$IFDEF LINUX}
      BitBlt(FBmpSatDest[i].Canvas.Handle, 0, 0, FBmpSatDest[i].Width, FBmpSatDest[i].Height,
      FBmpBuffer.Canvas.Handle, img1.X - FImgShift, img1.Y - FImgShift, SRCCOPY);
      if (Sat.SNR = 0) then
      FBmpSatDest[i].Canvas.Draw(0, 0, FRedSatImg)
      else
      FBmpSatDest[i].Canvas.Draw(0, 0, FGreenSatImg);
      {$ENDIF}
      BitBlt(FBmpBuffer.Canvas.Handle, img1.X - FImgShift, img1.Y - FImgShift,
      FBmpSatDest[i].Width, FBmpSatDest[i].Height, FBmpSatDest[i].Canvas.Handle, 0, 0, SRCCOPY);
      
      //************* Draw PRN ********************
      with FBmpTxtDest[i] do
      begin
      //Assumed image size to valid for Canvas.Font.
      Width := 10;
      Height := 10;
      Canvas.Font.Color := clBlack;
      Canvas.Font.Style := [fsBold];
      Canvas.Font.Name := 'Sans';
      Canvas.Font.Size := trunc(8/20 * FSatSize);
      ts := Canvas.TextExtent( Sat.Prn);
      //The real size.
      Width := ts.cx;
      Height := ts.cy;
      Canvas.Brush.Style := bsClear;
      Canvas.Brush.Color := self.Color;
      x := img1.X - Width div 2 - 2;
      y := img1.Y + FSatSize div 2 - 2;
      r := Rect(0 , 0, Width, Height);
      BitBlt(Canvas.Handle, 0, 0, Width, Height,
      FBmpBuffer.Canvas.Handle, x, y, SRCCOPY);
      Canvas.TextOut(0, 0, Sat.PRN);
      BitBlt(FBmpBuffer.Canvas.Handle, x, y, Width, Height, Canvas.Handle, 0, 0, SRCCOPY);
      //Last Sat, Copy FBmpBuffer to TGPSSkyplot Canvas.
      if (Sat.LastSat) then
      BitBlt(self.Canvas.Handle, 0, 0, FBmpBuffer.Width, FBmpBuffer.Height, FBmpBuffer.Canvas.Handle, 0, 0, SRCCOPY);
      end;
      end; //for
      end;
      end;
      
      
      • จะเห็นว่าผมต้องแยกโค้ดในวินโดส์และ Linux ด้วยการใช้ directive {$IFDEF} ที่ต้องแยกโค้ดแบบนี้ก็ไม่มากมายหรอกครับ บางครั้งก็สถานการณ์ก็บังคับเช่น hardware อย่าง serial port ในวินโดส์จะเริ่มตั้งแต Com1, Com2, Com3,… ส่วนในลินุกส์เริ่มจาก /dev/ttys0, /dev/ttys1, /dev/ttys2,…
      • ก็ขอจบตอนนี้ไว้ก่อน ตอนหน้ามาดูว่าถ้าต่อ GPS ผ่าน serial port ในวินโดส์และลินุกส์ บนวินโดส์ผมทดสอบเรียบร้อยแล้วใช้งานได้ดี แต่ในลินุกส์ยังไม่ได้ทดสอบ ก็มารอดูกันครับ ติดตามตอนหน้าซึ่งจะเป็นตอนสุดท้าย

การเขียนโปรแกรมต่อ GPS ด้วยคอมโพเน็นต์ OpenGPSX (ภาค 4)

OpenGPSX component on SourceForge

  • ผมได้นำ component ตัวนี้ไปใส่ใน SourceForge.net เป็นที่เรียบร้อยแล้ว ถ้าต้องการตัวที่ update ก็ไปดาวน์โหลดกันได้เลยครับ หน้าตาของ OpenGPSX บน sourceforge ก็ประมาณดังรูปด้านล่าง

OpenGPSX component บน SourceForge.net

  • จากตอนที่แล้วเราทำการแปะคอมโพเน็นต์ลงบนฟอร์ม เพื่อทดสอบอย่างง่าย ต่อไปจะเปิด project ของ Lazarus ที่ผมเขียนขึ้นเพื่อนำคอมโพเน็นต์มาใช้งาน สามารถดาวน์โหลดตามลิงค์นี้ opengpsproject.zip ก่อนจะเปิด project นี้ต้องติดตั้งตัวคอมโพเน็นต์ ที่ผมกล่าวไปแล้วในภาคที่ 3 ก่อนนะครับ ไม่งั้นจะ Error เมื่อดาวน์โหลดมาแล้วก็ unzip ไปไว้ตรงไหนก็ได้

เปิดโปรแกรมทดสอบ

  • รัน Lazarus คลิกที่เมนู Project > Open Project…

เปิดไฟล์ opengpstest.lpi

  • เมื่อเปิดไฟล์ opengpstest.lpi แล้ว ที่ source editor ของ Lazarus คลิกไปที่ main กด F12 บนคีย์บอร์ดดูเพื่อดูฟอร์ม

OpenGPSX Test Project

ตั้ง property ให้ object

  • เมื่อเราแปะ object ลงบนฟอร์ม Lazarus จะตั้งชื่อให้ NMEADecode1, GPSSkyplot1, GPSTarget1, GPSPortConnected1 และ GPSSignalplot1 เพื่อให้ object แต่ละตัวรู้จักกันจะต้องเพิ่มโค้ดประมาณ 3 บรรทัดที่ FormCreate ดังที่ผมคอมเม็นต์ไว้ครับ
      </ul>
      </ul>
      &nbsp;
      <ul>
      <ul>procedure TForm1.FormCreate(Sender: TObject);</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>begin</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>FNMEAs := TStringList.Create;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>InitControls;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>FStop := false;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>cmdStopPlay.Enabled := false;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>{Don't forget to assign for the following objects.}</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>GPSTarget1.NMEADecode := NMEADecode1;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>GPSSkyplot1.NMEADecode := NMEADecode1;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>GPSSignalPlot1.NMEADecode := NMEADecode1;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>FSuccess := false;</ul>
      </ul>
      &nbsp;
      <ul>
      <ul>end;</ul>
      </ul>
      &nbsp;
      <ul>

    หลักการทำงานของ object

    • ถ้าต่อกับเครื่อง GPS ตัว GPSPortConnected1 จะทำการเชื่อมกับ serial port เมื่อตั้งค่าต่างๆแล้วผ่านทางเมธอด GPSPortConnected1.SetupPort ทำการ connect เข้ากับ GPS ด้วยเมธอด GPSPortConnected1.OpenPort เครื่อง GPS จะส่งประโยค NMEA ผ่านทาง serial port ด้วยการสร้างอีเวนต์  GPSPortConnected1RxData ผู้ใช้ก็มาดักจับประโยค NMEA ที่นี่ โดยการเอา NMEADecode1 มาจับเอาประโยค NMEA ผ่านทาง property “Sentence”
        </ul>
        </ul>
        &nbsp;
        <ul>
        <ul>procedure TForm1.GPSPortConnected1RxData(Sender: TObject; Instring : string);</ul>
        </ul>
        &nbsp;
        <ul>
        <ul>begin</ul>
        </ul>
        &nbsp;
        <ul>
        <ul>//There are many empty string lines which come from TSdpoSerial.</ul>
        </ul>
        &nbsp;
        <ul>
        <ul>//Check it out.</ul>
        </ul>
        &nbsp;
        <ul>
        <ul>if InString &lt;&gt; '' then</ul>
        </ul>
        &nbsp;
        <ul>
        <ul>begin</ul>
        </ul>
        &nbsp;
        <ul>
        <ul>NMEADecode1.Sentence := Instring;</ul>
        </ul>
        &nbsp;
        <ul>
        <ul>if (NMEADecode1.Success) then</ul>
        </ul>
        &nbsp;
        <ul>
        <ul>memo1.Lines.Add(Instring);</ul>
        </ul>
        &nbsp;
        <ul>
        <ul>end;</ul>
        </ul>
        &nbsp;
        <ul>
        <ul>end;</ul>
        </ul>
        &nbsp;
        <ul>
      • NMEADecode1 เมื่อทำการแปลสตริง NMEA ที่รู้จักแล้วถ้าสำเร็จจะส่งข้อมูลผ่านทางอีเวนต์ OnNMEA (รวมประโยค NMEA ที่แปลแล้วทุกๆประโยค) และกระจายอีเวนต์แยกไปตามรูปประโยค NMEA เช่น OnGGA, OnRMC, OnGSV, OnGLL เป็นต้น และ object ตัวนี้จะสร้างอีเวนต์พิเศษคือ OnGPSTarget, OnGPSSkyplot และ OnGPSSignalplot เพื่อส่งข้อมูลที่แปลแล้วไปให้ class TGPSTarget, TGPSSkyplot และ TGPSSignalplot ตามลำดับ
      • GPSTarget1, GPSSkyplot1 และ GPSSignalplot1 เมื่อได้รับข้อมูล NMEADecode1 ก็จะทำหน้าที่ของตัวเอง

      ทดสอบโดยการเล่นไฟล์ NMEA

      • ในการทดสอบโปรแกรม บางท่านอาจจะไม่มีเครื่อง GPS ให้ลอง ผมได้เตรียมไฟล์ NMEA ให้โดยอยู่ที่โฟลเดอร์ “Data” เมื่อรัน opengpstest.lpi แล้ว ที่ด้านล่างจะมี Button ให้อยู่ 3 ตัว คลิกที่ัตัว “Open NMEA File…” ผมจะลองเปิดไฟล์ gpslog-mod.txt เสร็จแล้วก็คลิกที่ปุ่ม “Play NMEA >>” จะหยุดเล่นเวลาไหนก็คลิกที่ปุ่ม “Stop” ได้ครับ
      ทดสอบด้วยการเล่นไฟล์ NMEA
      • สำหรับ TGPSTarget มี property ที่สามารถตั้งค่าให้เลือกตั้งค่าพิกัดจุดหมายปลายทางได้เองหรือจะให้เลือกเอาจากประโยค $GPWPL ก็ได้ สังเกตว่าไฟล์นี้ขาดประโยค $GPVTG มาลองดูโค้ดที่ implement NMEA ที่ decode มาแล้ว
          </ul>
          </ul>
          &nbsp;
          <ul>
          <ul>{TNMEADecode is full of all decoded NMEA sentences.}</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>procedure TForm1.NMEADecode1NMEA(Sender: TObject; NMEA: TNMEADecode);</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>var</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>dLat, dLong : double;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>case NMEA.MessageType of</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>GGAMsg :</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>with NMEA.GGA do</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGGAGeoidal.Caption := Separation + ' ' + SeparationUnits;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGGAAltitude.Caption := Altitude + ' ' + AltitudeUnits;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGGASatCount.Caption := SatsInUse;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>dLat := abs(strtofloat(LatitudeDegree));</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>dLong := abs(strtofloat(LongitudeDegree));</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGGALat.Caption := format('%9.6f', [dLat]) + ' (' + LatHemis + ')';</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGGALong.Caption := format('%9.6f', [dLong]) + ' (' + LongHemis + ')';</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGGAHDOP.Caption := HDOP;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGGAUTCTime.Caption := UTC;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>if (FixQuality = '0') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGGAQuality.Caption := 'Invalid'</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>else if (FixQuality = '1') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGGAQuality.Caption := 'GPS Fix'</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>else if (FixQuality = '2') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGGAQuality.Caption := 'DGPS Fix';</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>GSAMsg :</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>with NMEA.GSA do</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>if (FixStatus = '1') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGSAFix.Caption := 'Fix not available'</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>else if (FixStatus = '2') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGSAFix.Caption := FixStatus + 'D'</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>else if (FixStatus = '3') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGSAFix.Caption := FixStatus + 'D';</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>if (Mode = 'A') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGSAMode.Caption := 'Automatic 2D/3D'</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>else if (Mode = 'M') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGSAMode.Caption := 'Manual';</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGSAHDOP.Caption := HDOP;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGSAVDOP.Caption := VDOP;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGSAPDOP.Caption := PDOP;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>RMCMsg :</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>with NMEA.RMC do</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>if (Status = 'A') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblRMCValidity.Caption := 'Active'</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>else if (Status = 'V') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblRMCValidity.Caption := 'Invalid';</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>dLat := abs(strtofloat(LatitudeDegree));</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>dLong := abs(strtofloat(LongitudeDegree));</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblRMCLat.Caption := format('%9.6f', [dLat]) + ' (' + LatHemis + ')';</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblRMCLong.Caption := format('%9.6f', [dLong]) + ' (' + LongHemis + ')';</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblRMCSpeed.Caption := format('%5.1f', [strtofloat(Speed) * 1.852]) + ' Km/h';</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblRMCDate.Caption := UTCDate;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblRMCTime.Caption := UTCTime;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblRMCTrue.Caption := TrueCourse;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>if (MagneticVariation = '-9999') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblRMCMagVar.Caption := ''</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>else</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblRMCMagVar.Caption := MagneticVariation + ' ' + MagDeviation;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>WPLMsg :</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>with NMEA.WPL do</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblWPName.Caption := WayPointName;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblWPLatitude.Caption := Latitude;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblWPLongitude.Caption := Longitude;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>GLLMsg :</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>with NMEA.GLL do</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>dLat := abs(strtofloat(LatitudeDegree));</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>dLong := abs(strtofloat(LongitudeDegree));</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGLLLat.Caption := format('%9.6f', [dLat]) + ' (' + LatHemis + ')';</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGLLLong.Caption := format('%9.6f', [dLong]) + ' (' + LongHemis + ')';</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGLLTime.Caption := UTCTime;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>if (Status = 'A') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGLLStatus.Caption := 'Valid'</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>else if (Status = 'V') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGLLStatus.Caption := 'Invalid';</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>PGRMMsg:</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>with NMEA.PGRMM do</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGarminMapDatum.Caption := MapDatum;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>PGRMZMsg:</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>with NMEA.PGRMZ do</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>if (UnitAltitude = 'f') then</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGarminAltitude.Caption := Altitude + ' ' + 'Feet'</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>else</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGarminAltitude.Caption := Altitude + ' ' + UnitAltitude;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGarminPosFix.Caption := PositionFix;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>PGRMEMsg:</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>with NMEA.PGRME do</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>begin</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGarminHPE.Caption := HPE + ' ' + UnitHPE;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGarminVPE.Caption := VPE + ' ' + UnitVPE;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>lblGarminSEPE.Caption := SEPE + ' ' + UnitSEPE;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          &nbsp;
          <ul>
          <ul>end;</ul>
          </ul>
          
          • สังเกตว่าพารามิเตอร์ NMEA : TNMEADecode จะแยกชนิดของประโยคด้วย property “MessageType”

          ทดสอบเล่นไฟล์ NMEA อีกไฟล์

          • ลองเปิดไฟล์ NMEA อีกตัวคือไฟล์ “griffith-park.nmea” ซึ่งผมดาวน์โหลดได้จาก internet ดูค่าพิกัดแล้วน่าจะอยู่แถวๆ รัสเซีย ลองทดสอบดูกัน
          ทดสอบอีกไฟล์
          • สังเกตว่าไฟล์ทดสอบนี้ำจะไม่มีประโยค $GPGLL และ $GPWPL แต่จะมีประโยค $GPVTG มาแทน เอาละครับมาต่อตอนต่อไป ผมอาจจะลองทดสอบดูใน Ubutu Linux หรืออาจจะทดสอบต่อกับเครื่อง GPS ผ่าน serial port

การเขียนโปรแกรมต่อ GPS ด้วยคอมโพเน็นต์ OpenGPSX (ภาค 3)

ทำความเข้าใจเรื่อง Baud rate

  • Blog ที่ผมเขียนเรื่องนี้คงจะมีหลายภาค ตอนนี้ภาค 3 จะมาเริ่มโปรแกรมมิ่งกัน เพื่อนำคอมโพเน็นต์ OpenGPSX มาใช้งาน ก่อนจะไปต่อผมขอพูดเกร็ดเล็กเกร็ดน้อย เกี่ยวกับเรื่อง Baud rate กับ NMEA บางประโยคเช่น $GPGSV การต่อเครื่อง GPS เข้ากับเครื่องคอมพิวเตอร์ผ่านทาง serial port คงจะคุ้นๆเรื่อง Baud rate เดิมทีก่อนหน้านี้ใช้ Baud rate 4800 b/s ก็น่าจะพอ ตัวเลข 4800 หน่วยเป็น bit ถ้าแปลงเป็น byte (เท่ากับ 1 ตัวอักษร) ก็หารด้วย 8 จะได้ 600 ตัวอักษรต่อวินาที
  • สมมติว่าเครื่อง GPS ของเราส่งประโยคของ NMEA มาให้ดังนี้คือ $GPRMC, $GPGSA, $GPGGA, $GPGLL, $GPVTG เท่ากับ 5 ประโยค (ยกเว้นประโยค $GPGSV) จะมีขนาดค่อนข้างคงที่เฉลี่ยแล้วประมาณ 80 ตัวอักษรต่อประโยคซึ่งจะประมาณ 5 x 80 = 400 ตัวอักษร ที่นี้จะเหลือประมาณ 200 ตัวอักษรที่จะเหลือให้ประโยค $GPGSV ซึ่งประโยค GSV จะส่งมาเป็นชุดอาจจะเป็น 2 บรรทัด ถ้าในขณะนั้นเครื่อง GPS รับสัญญาณได้ไม่เกิน 8 ดวง (4 ดวงต่อบรรทัด) แต่ถ้ารับได้มากกว่า 8 ดวงไม่เกิน 12 ดวง จะได้ประโยคนี้ 3 บรรทัด หาขนาดได้คร่าวๆเท่ากับ 3 x 80 = 240 ตัวอักษร
  • ดังนั้นประโยค $GPRMC, $GPGSA, $GPGGA, $GPGLL, $GPVTG ที่รับจากเครื่อง GPS ประมาณ 400 ตัวอักษรรวมกับประโยค $GPGSV 3 บรรทัดเท่ากับ = 400 + 240 = 640 ซึ่งจะเกิน 600 ตัวอักษรต่อวินาที (Baud rate 4800 b/s) ทำให้ข้อมูลจะเกิดการสูญหาย ผมสังเกตว่าประโยค $GPGSV เครื่อง GPS จะส่งมาให้ทุกๆวินาทีไม่เหมือนกับประโยคบางประโยคเช่น $GPBOD, $GPWPL หรือ $GPRTE นานๆจะส่งมาสักครั้ง
  • ดังนั้น Baud rate 4800 b/s ไม่พอเสียแล้วครับ

ปรับ Baud rate เป็น 9800 b/s

  • 9800 b/s หารด้วย 8 จะได้ 1225 ตัวอักษรต่อวินาที
  • เครื่อง GPS มือถือแบบที่เราใช้กันปัจจุบันจะระบุว่ารับได้ 12 channels ก็คือรับดาวเทียม GPS บนท้องฟ้าได้เต็มที่ 12 ดวง แต่ในอนาคตจะมีเครื่องที่สามารถรับได้มากกว่า 12 ดวงให้เห็นกันทั่วไป ผมค้นดูใน internet เห็นยี่ห้อ UBlox ที่รับได้ถึง 16 channels ซึ่งจะมีประโยค GSV เป็นชุดทั้งหมด 4 ประโยคดังนี้

$GPGSV,4,1,14,25,15,175,30,14,80,041,,19,38,259,14,01,52,223,18*76
$GPGSV,4,2,14,18,16,079,,11,19,312,,14,80,041,,21,04,135,25*7D
$GPGSV,4,3,14,15,27,134,18,03,25,222,,22,51,057,16,09,07,036,*79
$GPGSV,4,4,14,07,01,181,,15,25,135,*76

  • ถ้าในอนาคตเป็นเครื่องรับ 24 channels สมมติว่ารับสัญญาณของ GPS บนท้องฟ้าได้เต็มที่ 24 ดวง จะต้องใช้ประโยค $GPGSV ทั้งหมดเท่ากับ 24 = 4 ดวง x 6 บรรทัด  ประมาณขนาดได้ 6 x 80 = 480 ตัวอักษร รวมกับประโยคอื่นๆที่ผมกล่าวไปแล้ว 400 ตัวอักษรเท่ากับ 480 + 400 = 880 ตัวอักษร ดังนั้น baud rate 9800 b/s (1225 ตัวอักษร) ก็น่าจะพอ แต่ถ้าในขณะนั้นประโยคอื่นๆถูกส่งมาด้วยเช่น $GPZDA, $GPBOD, $GPWPL, $GPRTE, $GPGGK อะไรประมาณนี้ขนาด baud rate 9800 b/s ก็ฉิวเฉียดจะเต็ม ดังนั้นในอนาคต baud rate ของ GPS ควรจะอยู่ที่ 19200 b/s

ดาวน์โหลด

  • เฉพาะคอมโพเน็นต์ OpenGPSX ดาวน์โหลดได้ตามลิงค์นี้ OpenGPSX0.3.21.zip
  • โปรแกรมที่จะใช้ทดสอบที่จะเขียนในตอนนี้ ดาวน์โหลดได้ตามลิงค์นี้ OpenGPSProject.zip

ติดตั้งคอมโพเน็นต์ OpenGPSX

  • เมื่อดาวน์โหลด component มาแล้วจำทำการติดตั้งบน Lazarus ถ้าเป็นเครื่องที่ไม่เคยติดตั้ง component “OpenGPSX” มาก่อนหน้าตาจะเป็นดังรูปด้านล่าง
Lazarus ที่ยังไม่ได้ติดตั้ง OpenGPSX
  • unzip โฟลเดอร์คอมโพเน็นต์ที่ดาวน์โหลดมาแล้ว copy ไปไว้ที่โฟลเดอร์ของ Lazarus ตัวอย่างเช่นเครื่องผมอยู่ที่ “c:\lazarus\components\opengpsx”
โฟลเดอร์ของคอมโพเน็นต์ OpenGPSX

Compile & Rebuild

  • คลิกที่เมนู Package > Open package file (.lpk)… เปิดไฟล์ opengpsx.lpk ที่อยู่ในโฟลเดอร์ c:\lazarus\components\opengpsx
เปิดไฟล์ package ของคอมโพเน็นต์ OpenGPSX
  • เปิดไฟล์ opengpsx.lpk
เปิดไฟล์ opengpsx.lpk
  • ทำการ compile และ ติดตั้งคอมโพเน็นต์
ติดตั้งคอมโพเน็นต์
  • เมื่่อติดตั้ง component เสร็จก็พร้อมจะนำมาใช้งาน
Class ต่างๆของ component

ทดสอบคอมโพเน็นต์แบบง่าย

  • จะลอง component อย่างเร็วก่อน ด้วยการคลิกที่ OpenGPSX palette แต่ละ class แล้วมาแปะที่ฟอร์มดังรูปด้านล่าง
ทดสอบอย่างเร็ว
  • จะเห็นหน้าตาของ object แต่ละตัว ซึ่ง TGPSSkyplot, TGPSTarget และ TGPSSignalplot เป็น Visual component ส่วนอีกสองตัวได้แก่ TNMEADecode และ TGPSPortConnected เป็น Non visual component มาลองดูโค้ดที่ Lazarus สร้างขึ้นมาให้จะเห็น unit ที่ถูกเรียกใช้เช่น gpsskyplot, gpstarget, gpssignalplot, nmeadecode และ gpsportconnected
      </ul>
      </ul>
      &nbsp;
      <ul>
      <ul>unit Unit1;</ul>
      </ul>
      {$mode objfpc}{$H+}
      
      interface
      
      uses
      Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, gpsskyplot,
      gpstarget, gpssignalplot, nmeadecode, gpsportconnected;
      
      type
      
      { TForm1 }
      
      TForm1 = class(TForm)
      GPSPortConnected1: TGPSPortConnected;
      GPSSignalPlot1: TGPSSignalPlot;
      GPSSkyPlot1: TGPSSkyPlot;
      GPSTarget1: TGPSTarget;
      NMEADecode1: TNMEADecode;
      private
      { private declarations }
      public
      { public declarations }
      end;
      
      var
      Form1: TForm1;
      
      implementation
      
      {$R *.lfm}
      
      end.
      
      
      • ก็ขอจบตอนนี้ก่อนครับ ตอนหน้ามาดูโปรแกรมที่ผมเขียนขึ้นมาเพื่อทดสอบและเรียกใช้ OpenGPSX component กันเป็นเรื่องเป็นราวครับ

การเขียนโปรแกรมต่อ GPS ด้วยคอมโพเน็นต์ OpenGPSX (ภาค 2)

คอมโพเน็นต์ OpenGPSX

  • Concept ของ component คือผู้พัฒนาโปรแกรมทั่วๆไปสามารถนำ component ของผมไปใช้งานได้ง่าย ใช้เวลาน้อย โดยไม่ต้องมีความรู้เรื่อง GPS มาก ผมเขียนคอมโพเน็นต์ตัวนี้ขึ้นมาเพราะผมใช้ GPS บ่อย ตั้งแต่เครื่องมือถือทั่วๆไปเช่น Garmin หรือบน Pocket PC ก็เคยใช้ จนกระทั่งเครื่อง GPS ระดับ Precise เช่น Trimble, Leica ที่มีสนนราคาเรือนล้านบาท ใช้งานใน mode เช่น
    • Differential GPS (DGPS) ตั้งแต่ตั้ง Reference Station เอง จนถึงเช่าสัญญาณ DGPS จาก Omnistar หรือ Landstar
    • RTK (Real Time Kinematic) ที่ติดตั้ง Reference Station ประกอบกับอุปกรณ์ Radio Modem เพื่่อส่งสัญญาณ Correction ไปยังเครื่อง Rover ทำให้รับสัญญาณได้ละเอียดกว่าระบบ DGPS
    • Static หรือ Fast Static ใช้มากเพื่อขยายหมุดจากหมุด GPS ที่ทราบค่าพิกัดแล้วโดยการตั้งเครื่องรับ GPS ที่หมุด GPS แล้วนำเครื่อง GPS อีกเครื่องไปตั้งที่หมุดใหม่ ทำการรังวัดเก็บข้อมูล แล้วนำข้อมูลทั้งสองเครื่องไปคำนวณผ่านโปรแกรมต่างๆเช่น TGO,  LGO หรือโปรแกรมอื่นๆของผู้ผลิต GPS
  • ก็เลยมีแรงบันดาลใจว่าน่าจะทำ component สำหรับ Lazarus ซักตัวหนึ่งที่สามารถ decode ประโยคต่างๆของ  NMEA ได้ และนำข้อมูลเหล่านี้ไปแสดงผลเช่นวาดสัญลักษณ์ GPS บนท้องฟ้า แสดงความแรงของสัญญาณ GPS ตลอดจนวาดลูกศรแสดงเป้าหมายของ Waypoint ในการ programming ก็ไม่ถือว่าเป็นเรื่องยากหนักหนาสาหัส แต่ติดขัดคือผมมีงานประจำ และงานโปรแกรมมิ่งเป็นงานอดิเรก จึงมีเวลาค่อนข้างน้อย แต่ผมก็พยายามจนสามารถออกรุ่นแรกได้คือรุ่น 0.3 ซึ่งความรู้สึกของผมน่าจะพอใช้งานได้

ความสามารถของ Component “OpenGPSX”

  • สำหรับ Features ของคอมโพเน็นต์ผมจะเรียงไปตาม Class หลักๆ ดังนี้ครับ
  • TGPSPortConnected เอาไว้ตั้งค่าการเชื่อมต่อกับเครื่อง GPS เช่นตั้งค่าการเชื่อมต่อกับ Serial Port เช่นตั้งค่า Com port, Baud rate, Parity, Data bit และ Stop bit เมื่อทำการเชื่อมต่อแล้ว class นี้สามารถดักจับข้อมูล NMEA ที่ผ่านทาง port ก่อนจะกระจายสตริงของ NMEA ผ่านทาง Event
แสดงการตั้งค่า Serial Port
  • TNMEADecode เป็น class ที่อ่านค่า NMEA ที่เครื่อง GPS ส่งมาให้ผ่าน TGPSPortConnected แล้ว decode รหัสของ NMEA ตามชนิดของ sentence เช่น $GPGSA, $GPGSV, $GPRMC, $GPGGA, $GPVTG, $GPGLL, $GPWPL, $GPRTE หรือของ Garmin บางส่วน
  • TGPSSkyPlot เป็น class ที่วาดรูปสัญลักษณ์ของ GPS บนท้องฟ้า เมื่อได้รับการแปล NMEA จากรูปประโยค $GPGSV
GPS Skyplot Viewer
  • TGPSSignalStrength แสดงความแรงของสัญญาณของ GPS เมื่อได้รับการแปล NMEA จากประโยค $GPGSV เช่นเดียวกัน
GpS signal strength (แสดงในแนวราบ)
GPS signal strength (แสดงในแนวตั้ง)
  • TGPSTarget เป็นตัวบอกทิศทางจุดเป้าหมาย (Waypoint) ที่ต้องการจะไป โดยคอมโพเน็นต์จะคำนวณจากค่าพิกัดจุดเป้าหมายและค่าพิกัดปัจจุบันจาก GPS
แสดงทิศทางและระยะทางของจุดเป้าหมาย
  • ส่วนการ Implement ตัว Event จาก TNMEADecode สามารถแยกไปตามชนิดของประโยคของ NMEA ดังตัวอย่างรูปดัานล่างได้ง่ายๆ
แสดงการนำ NMEA ที่แปลแล้วมาใช้แสดงข้อมูลของ GPS

ไฟล์และส่วนประกอบของคอมโพเน็นต์

  • ส่วนใหญ่จะเขียนเองเกือบทั้งหมดยกเว้น การเชื่อมต่อกับ Serial port ผมดึงไฟล์จากคอมโพเน็นต์ “SdpoSerial” ซึ่งในคอมโพเน็นต์นี้พัฒนาต่อจาก Synaser โดยการเขียน class ขึ้นมา wrap “Synaser” ให้ใช้งานได้ง่าย ผมดัดแปลงโค้ดบ้างเล็กน้อยให้เหมาะสมกับการใช้งาน GPS ซึ่งไฟล์ที่นำมาใช้มีสองไฟล์คือ sdposerial.pas กับ synaser.pas
  • อื่นๆก็เขียนเองเกือบหมดรวมทั้ง class ที่ใช้คำนวณหาระยะทางและทิศทางบน Ellipsoid ถ้ายังไม่ลืมก็คือใช้อัลกอริที่มของ Vincenty ซึ่งผมได้แก้ bug ใน unit นี้ด้วย
ไฟล์และส่วนประกอบของคอมโพเน็นต์ OpenGPSX
  • ตอนหน้าผมจะลงลิงค์ให้ดาวน์โหลดคอมโพเน็นต์ OpenGPSX ซึ่งเราจะเริ่มเขียนโปรแกรมทดสอบเพื่อนำคอมโพเน็นต์นี้มาใช้กัน ติดตามตอนต่อไปได้ครับ

มาลองใช้ฐานข้อมูลเชิงพื้นที่ Spatialite ที่ทั้งเล็ก เร็ว และแรง ตอนที่ 2

  • ก่อนจะไปต่อผมอยากจะพูดถึงงาน FOSS4G 2010 มีการ present เรื่อง  “SpatiaLite, the Shapefile of the Future?” ตั้งเครื่องหมายคำถามไว้ด้วยว่า Spatialite จะเป็น shapefile ในอนาคตหรือไม่ ซึ่งที่ผ่านมาก็หลายปีแล้วครับ ที่ฐานข้อมูลแบบ Geodatabase ที่เรียบง่ายแบบ Shapefile ยังไม่มีตัวไหนที่จะได้รับความนิยมเท่ากับ Shapefile แต่ Shapefile ก็มีข้อจำกัดหลายอย่าง ซึ่ง Spatialite ก็เป็นความหวังตรงนี้ แต่จะไปได้สวยหรือปล่าว เพราะในงาน FOSS4G 2010 คุณ Frank Warmerdam (หัวหน้าโครงการ gdal/ogr library อันเลื่องชื่อ) ได้พูดถึงความเสถียร spatialite ว่ายังไม่ถึงจุดนั้นดี และคุณ Alessandro (ผู้พัฒนา Spatialite จาก Italy) ก็ไม่เปิดการพัฒนาเท่าที่มันควรจะเป็น ตรงนี้หรือปล่าวที่ผมดูแล้ว QGIS ก็ยังสนับสนุน Spatialite ในระดับที่จำกัด

RasterLite

  • ตอนนี้เราจะมาลองดู tool ที่จัดการด้านภาพคือ Rasterlite แต่ดู forum ของ QGIS แล้วยังจะไม่ support ในตอนนี้ซึ่งก็น่าเสียดาย แต่ที่ผมลองแล้วเมื่อใช้ Rasterlite ทำการปั๊มรูป GeoTiff เข้าไปในฐานข้อมูล Spatialite เมื่อเปิดดูด้วย Spatialite-gui ข้อมูลเข้าได้ถูกต้อง แต่เมื่อเปิดด้วย Spatialite-gis กลับตาลปัตร เอ้าครับมาลองดูกัน

  • ผมจะเพิ่ม DEM ที่ shade แล้วเป็นรูปภาพธรรมดา(GeoTiff) ครับเข้าไป แต่ที่นี้จะเป็นปัญหาแล้วครับ เพราะ spatialite-gui support เฉพาะ vector ถ้าไปดูโฟลเดอร์ c:\spatialite ที่เราดาวน์โหลดมาจะเห็น tool ของ rasterlite ถ้า จะเพิ่มไฟล์ GeoTiff เข้าไปจะต้องใช้คำสั่ง command line ซึ่งบางคนอาจขยาด แต่ในอนาคตผมเห็นว่าทางผู้พัฒนาต้อง implement ตัว rasterlite เข้าไปใน spatialite-gui เพื่อให้เกิดความสะดวกในการใช้งาน วิธีการคือ กดคีย์ “Win” บนคีย์บอร์ดค้าง แล้วกดคีย์ “R” พิมพ์คำว่า cmd แล้วกด Enter วินโดส์จะเรียกหน้าต่าง command line ออกมา
ใช้ command line เพื่อเรียกใช้ rasterlite

  • ผมจะใช้คำสั่ง raterlite_load ดังรูปด้านล่าง พารามิเตอร์ -d ระบุฐานข้อมูล spatialite (เครื่องหมาย ..\..\ หมายถึงอยู่บนไดเรคทอรีเหนือขึ้นไป 2 ชั้น)  ส่วน -T ระบุไฟล์ Geotiff ที่จะนำไปรวมเข้ากับฐานข้อมูล ส่วน -D ระบุถ้าพบไฟล์ Geotiff อื่นๆที่อยู่ในโฟลเดอร์นี้ให้ process นำไปรวมเข้ากับฐานข้อมูลด้วย
ใช้คำสั่ง rasterlite_load
  • ถ้าคำสั่ง rasterlite_load สำเร็จจะแสดงขนาดของรูปที่ใส่เข้าไปในฐานข้อมูล จากตัวอย่างนี้จะแบ่งรูปเป็นตาราง(คล้ายๆกระเบื้องปูพื้น) ขนาด 3 x 3 หรือรวมทั้งหมดเท่ากับ 9 record ต่อไปจะไปดูข้อมูลด้วย Spatialite-gui คลิกที่เลเยอร์ “DEM_Shaded_rasters” คลิกขวาแล้วเลือก Edit table rows ลองคลิกไปที่ตาราง table ดูที่คอลัมน์ raster ตรง ROWID = 1 คลิกขวาเลือก BLOB Explore ดังรูป

 

 

Spatialite-gis

  • ต่อไปจะลองดูด้วย spatialite-gis ทำการ connect ข้อมูล จะเห็นว่าการแสดงผลไม่ถูกต้องคือจะเห็นภาพ raster เป็น polygon ไป (ผมทำอะไรผิดหรือปล่าว)

 

 

  • ผมจะลองปิด Raster แล้วตั้งค่า Layer ของ point กับ contour ดู มาดูที่เลเยอร์ “GeoPoint” ก่อน คลิกขวาตั้งค่าดังรูป
ตั้งค่าให้เลเยอร์ GeoPoint
ตั้งค่า classify ให้กับ Layer “contours”
  • ลอง identify ดูจะเห็นข้อมูลเป็นตัวอย่างหิน ประเภทของหิน รวมถึงรูปถ่าย
แสดง Identify ข้อมูล
  • เอาละ Spatialite เพิ่งกำเนิดมาเมื่อปี 2008 ที่ผ่านมานี้เอง ยังมีเวลาพอที่จะพิสูจน์ได้ว่าจะเป็น “Shapefile of the future” หรือปล่าว ต่อไปจะลองนำข้อมูล Spatialite ไปเปิดด้วย QGIS ดูกัน

Quantum GIS 1.6.0 (svn trunk)

  • ตอนที่เขียนอยู่นี้เป็นรุ่น 1.6.0 กำลังพัฒนากันอยู่ รุ่นที่เสถียรคือ 1.5.0 QGIS ผมลงด้วย osgeo4w ก็เลยได้ทั้ง 1.5.0 และ 1.6.0 เพราะตอนติดตั้ง (ผ่าน internet) ไม่ได้ตั้งค่าอะไรมาก ก็เลยโหลดมาซะหลายเวอร์ชั่น มาดูกันครับ
  • คลิกที่เมนู Layer > Add Spatialite Layer… เลือกไฟล์ geology.sqlite ที่สร้างไว้ ลองปรับตั้งค่า layer ดู และอย่างที่ทราบกันคือ table ของเลเยอร์ DEM_Shaded มองเห็นเป็น polygon เช่นเดียวกันกับ spatialite-gis
QGIS แสดงข้อมูลของ Spatialite

QGIS มองไม่เห็นข้อมูล BLOB ของ Spatialite

  • จะสังเกตเห็นข้อมูลเลเยอร์ GeoPoint จะมีรูปถ่ายของหินแนบมาในตารางด้วย ตอนขึ้นข้อมูลในตอนที่ 1 จะเป็น BLOB (Binary Large object) ตัว BLOB เป็นอะไรก็ได้ที่สามารถ stream เก็บเป็นไบนารี ตัวอย่างง่ายที่สุดก็คือ รูปภาพ ลองคลิกที่เลเยอร์ Geopoint แล้วคลิกขวาเลือก Open Attribute Table จะเห็น QGIS แสดงดังรูปด้านล่าง
QGIS มองไม่เห็น BLOB
  • หลังจากงาน FOSS4G 2010 ผมว่าผู้พัฒนา Spatialite คงได้รับ feed back พอสมควรและก็น่าปรับกระบวนยุทธ์กันใหม่ ถ้าจะให้ Spatialite เป็นฟอร์แม็ตมาตรฐานในแวดวง GIS ผมคนหนึ่งก็จะเอาใจช่วยครับ

แจ่มจริงๆ Ubuntu Lucid กับสารพันโปรแกรมต่างๆที่ผมใช้งาน

Ubuntu Lucid

  • หลังจากกลับจากต่างประเทศ ผมเพิ่งจะมีโอกาสได้อัปเกรด Ubuntu จาก Karmic เป็น Lucid ด้วย Update Manager ซึ่งก่อนหน้านี้การเปลี่ยนเวอร์ชั่น ต้องอาศัยการ install ใหม่ทั้งหมดทำให้เสียเวลาไปมาก เมื่อใช้ Update Manager ก็เสียเวลาดาวน์โหลดไฟล์ไม่นานนัก เนื่องจาก internet สมัยนี้เร็ว ต้องยอมรับว่าโทนหรือ Theme เน้นไปทาง Dark มากขึ้น เพิ่มสีม่วง สวย นวลเนียนมาก ก็ไม่ผิดหวังเป็น distro ที่ผมชอบมาก
Ubuntu Lucid กับ Compiz

มีอะไรบ้างหลังจากเปลี่ยนเป็น Lucid แล้วต้องติดตั้งใหม่หรือต้องแก้ไข

Compiz Fusion

  • ไม่ต้องทำอะไรเลย ผมเปิด desktop ทั้งหมด 8 desktop แล้วตั้ง effect เป็นทรงกระบอก ปิดการ render ตัว desktop ของ Nautilus ทำให้สามารถใช้ effect “Wallpapers” ของ compiz ได้ เครื่องผมเป็น Notebook ใช้ชิปกราฟฟิคของ intel เมื่อหมุนควง desktop จะ smooth ไม่มีอาการกระตุกให้เห็นครับ

Stellarium

  • นี่เป็นอีกโปรแกรมสามัญประจำเครื่องสำหรับผมเลย เอาไว้จำลองดูดาวบนท้องฟ้าในเวลากลางคืน เวอร์ชั่นก่อนหน้านี้คือ Karmic จะตีกับ compiz ไม่สามารถใช้งานได้แต่ใน lucid นี้ลองเปิดดู OK เลยครับไม่ต้องทำอะไรเพิ่ม ก็ใช้งานได้เลย เปิดการแสดงผลเป็นภาษาไทย ทำได้ยอดเยี่ยมครับ ชื่อดาว เนบิวลา ทำได้ดีมาก
stellarium บน Ubuntu Lucid

Quantum GIS

  • เนื่องจาก repo ตัวเก่าอยู่ที่ launchpad.net ค่อนข้างมีปัญหาบ่อยคือเวลา update จะฟ้องว่า PGP Key Error อะไรประมาณนี้ตอนนี้ย้ายมาที่นี่ เพิ่ม repo สองบรรทัดล่างที่ synaptic ก็ OK ครับ
deb     http://qgis.org/debian-nightly lucid main
deb-src http://qgis.org/debian-nightly lucid main
  • เมื่อเพิ่ม repo แล้วก็ update ต่อจากนั้น upgrade จะได้ Quantum GIS ในเวอร์ชั่น 1.6.0 ซึ่งตอนนี้เป็นรุ่น SVN Trunk หลังจากติดตั้งแล้วเรียก QGIS ใช้ก็ผ่านฉลุยครับ
Quantum GIS 1.6.0

Lazarus

  • เป็นคอมไพเลอร์ cross-platform ที่เดิมทีโคลนนิ่งมาจาก Delphi แต่ตอนนี้มาไกลและ stable มาก ตัว Lazarus เองเป็น opensource และมีกลุ่มนักพัฒนาที่ช่วยกันอยู่ engine ของ Lazarus เป็น free pascal compiler (fpc) ซึ่งก็มีกลุ่มนักพัฒนาอีกกลุ่มที่ดูแลกันอยู่
  • เมื่ออัปเกรดเป็น Lucid ผมก็แทบไม่ต้องทำอะไร ได้แต่ update ผ่าน svn (subversion) แล้วทำการคอมไพล์และ build ใหม่ก็ใช้ได้ทันที
Lazarus ยังเป็นเวอร์ชั่น 0.9.29

OpenOffice

  • ผมใช้ OpenOffice มาร่วมๆ 2 ปีแล้วพบว่าดีกว่าแต่ก่อนมาก แต่ในสำนักงานเกือบทั้งหมดก็ยังใช้ MS Office อยู่ ปัญหาที่พบมาตลอดคือเรื่องขนาดฟอนต์ใน linux กับ windows ใช้ฟอนต์เดียวกันคือนำฟอนต์ วินโดส์ ไปลงใน linux ด้วย แต่ขนาดตัวหนังสือใน openoffice บน linux ตัวจะใหญ่กว่ามากถ้า save ใน linux แล้วมาแก้ไขบนวินโดส์ จะต้องจัดหน้าใหม่ทุกที
  • ถ้าจะใช้ฟอนต์เช่น Waree, Norasi, Loma เวลาแปลงเป็น MS Office เครื่องอื่นที่ส่งไฟล์ไปให้ก็ไม่มีฟอนต์เหล่านี้ ปัญหานี้ไม่ใช่ปัญหาของ Openoffice ทางการน่าจะผลักดันเรื่องฟอนต์ น่าจะมีฟอนต์แห่งชาติ ไม่ต้องใช้ฟอนต์ลิขสิทธิ์ของ MS เหมือนทุกวันนี้
  • ตอนหลังผมเลยใช้ openoffice บนวินโดส์อย่างเดียวเพื่อหลีกเลี่ยงปัญหานี้ ตอนนี้ openoffice ผมยังใช้ 3.2 อยู่เมื่อติดตั้ง Lucid ก็ยังใช้งานได้เหมือนเดิม
OpenOffice 3.2

Spatialite

  • Spatialite-gui และ Spatialite-gis ก็ OK ไม่ต้องทำอะไรเพิ่ม เพราะเป็นโปรแกรมตัวเล็กๆ
Spatialite-gui
Spatialite-gis

โปรแกรมตกแต่งภาพ Gimp

  • ก็ยังเป็นเวอร์ชั่นเดิมคือ 2.6 ผมใช้แทน photoshop มานานแล้วซึ่งฟีเจอร์ที่มีอยู่สำหรับผมพอแล้ว ออกจะมากไปด้วยจนเลือกใช้ไม่ถูก (ฮา) Gimp เป็น cross-platform บนวินโดส์ก็ใช้ดีเหมือนใน linux

โปรแกรมเล่นเพลง Songbird

  • พยายามติดตั้งผ่าน repo แต่ไม่สำเร็จ ก็เลยไปดาวน์โหลด debian package มาติดตั้งเป็นรุ่น 1.4.1 ผมค่อนข้างชอบใจ Songbird ตรงที่เป็น Web Media Player มี Add-On มาก สามารถหารูปหน้าปกอัลบั้มและเนื้อเพลงได้ (ผมฟังเพลงยุค 1970 ยุคทองของดนตรี Rock, Hard Rock บางครั้งก็ฟัง Jazz/Fusion บ้าง บางทีก็พวก New Age เพลงไทยก็ลูกทุ่ง)
Songbird 1.4.1

โปรแกรมดูหนัง VLC Media Player

  • ไม่ต้องบรรยายอะไรมากสำหรับ VLC เป็น cross-platform ฟรี ใช้ได้ดีทุก platform รวมทั้งวินโดส์ตอนนี้รุ่น Goldeneye โปรแกรมนี้อัดมาแน่นด้วย codec ไม่ต้องหาอะไรมาติดตั้งเพิ่ม คือฟอร์แม็ตอะไรๆก็เล่นได้หมด ขนาดไฟล์อิมเมจ .ape ที่ผมหาอะไรมา mount ก็ไม่สำเร็จสักทีมาตายด้วย VLC นี้แหละครับ

BricsCad

  • ผมเพิ่งได้รับการกระจาย email จากบ.ผู้พัฒนา จึงเข้าไปดาวน์โหลดมาลองใช้งานดู BricsCad เป็นโปรแกรมด้าน CAD ที่น่าจะเทียบเท่ากับ Autocad (ซึ่ง Autocad ปัจจุบันมันเน่าเหลือเกิน หลังรุ่น 2007 ก็มีปัญหาความเสถึยรมาก)  ผมดาวน์โหลดมาแบบ debian package จึงติดตั้งไม่ยากแล้วลองเปิดไฟล์ dwg ดู ก็ดูช้าไปนิดแต่การแสดงผลไม่ผิดเพี้ยนจาก Autocad เลย รุ่นทดลองใช้ใช้ได้ประมาณ 30 วันราคาบน linux ประมาณ $385 หรือประมาณหมื่นกว่าบาท ไม่แพงเลย ความจริง BricsCad for Linux นี้เป็นโรคเลื่อนมานานมาก น่าจะมากกว่าหนึ่งปี เนื่องจากติดขัดเรื่อง graphic engine
BricsCad V 10.6.6
  • เมื่อหันไปดูพวก text ที่เป็นภาษาไทยยังแสดงผลไม่ถูกเนื่องจากไม่มี font ผมก็เลยไป copy จากวินโดส์มาให้ ตัวโปรแกรมของ Bricscad จะอยู่ที่ /opt/bricsys/bricscad/v10 และมีไดเรคทอรี Fonts ก็อยู่ในนี้จัดการเป็น root แล้ว copy ไฟล์ฟอนต์ .shx มาใส่จะแสดงภาษาไทยได้ถูกต้อง แต่สิ่งหนึ่งที่ Autocad ไม่มีให้ต้องซื้อเพิ่ม ก็คือ RasterImage ส่วน BricsCad ให้มาเลย สามารถนำไฟล์รูป Geo มา insert เข้าและยังมี function ด้าน Raster อื่นๆให้ใช้
แสดงผลภาษาไทยได้ถูกต้อง

โปรแกรมอื่นๆ

  • FireFox ก็คงคุณภาพเหมือนเดิมไม่ว่าจะอยู่บนวินโดส์หรือ linux สนับสนุนภาษาไทยได้เต็มร้อย มี Add-on ให้ใช้จนเลือกไม่ไหว
  • Google Chrome ถ้าเอามาท่องเน็ตอย่างเดียวดี เร็ว แต่ถ้าใช้มานั่งเขียน blog แบบนี้ภาษาไทยมันรวนไปหมดสระอยู่ไม่ถูกที่ สับสนอลหม่านไปหมด ผมเลยใช้น้อยมาก

สรุป

  • Ubuntu เป็น OS ทางเลือกที่ดี ถ้าใช้งานด้านเอกสาร ดูหนัง ฟังเพลง แค่นี้ Ubuntu สบายครับ ไม่ต้องกังวลเรื่อง Virus ให้ปวดใจ Windows 7 ที่ว่าสวยๆ มาเจอ Ubuntu + Compiz เอาแค่หมุนควง Desktop 3D นี่ต้องรีบหลบข้างทางไปเลย ข้อเสียของ linux ก็คือมีโปรแกรมด้าน CAD/GIS น้อยมาก โดยเฉพาะด้าน CAD โปรแกรมที่เห็นๆ ฟรีๆ ไม่ไหว ตอนนี้ก็มี BricsCad ที่ OK แต่จะไปรอดหรือปล่าว ส่วนด้าน GIS ก็ยังดีที่มี QGIS ที่เป็น application แบบ cross-platform

มาลองใช้ฐานข้อมูลเชิงพื้นที่ Spatialite ที่ทั้งเล็ก เร็ว และแรง ตอนที่ 1

  • ฐานข้อมูลเชิงพื้นที่ที่กำลังดีวันดีคืน แต่ฐานข้อมูลจะดีแค่ไหนก็ตาม แต่ถ้า Application ทั้งหลายไม่สนับสนุนก็ยากที่จะอยู่ได้ หันมาดู Spatialite ตอนนี้ใน Quantum GIS สนับสนุนฐานข้อมูลนี้ในระดับที่พอใช้ได้ ก็คือสนับสนุนในระดับที่จำกัดที่ผมพบมาคือจะมองข้อมูล BLOB (Binary Large Object) ของ spatialite ซึ่งเป็นรูปถ่ายเป็น NULL แต่อย่างไรก็ตามอนาคตก็น่าจะ support ได้เต็มที่มากกว่านี้
ตัวอย่างการใช้ฐานข้อมูล spatialite

ข้อดีของ Spatialite

  • spatialite ก็คือ sqlite ที่มีส่วนขยายรองรับฐานข้อมูลแบบเชิงพื้นที่ (ความหมายที่ตรงกันก็คือ ฐานข้อมูล GIS หรือ Geographic data หรือ Geospatial data อะไรประมาณนี้) ข้อดีของ spatialite
    • ติดตั้งง่ายๆ เนื่องจากมีขนาดเล็กมาก ขนาดของ sqlite + spatialite ประมาณ 2.2 MB
    • ไม่ต้องมี admin ดูแล
    • กินทรัพยากรของระบบต่ำมาก
    • เร็วและแรง
    • ใช้งานง่าย
  • ผมลองใช้แล้วเห็นด้วยกับทางผู้พัฒนาที่เคลมว่าฐานข้อมูล spatialite นั้นเป็น “The world’s smallest and simplest database” ก็คือ เล็กและเรียบง่ายที่สุด

แบบจำลองเรขาคณิต(Geometry Model) ของฐานข้อมูล

  • ก่อนจะไปต่อผมจะอธิบายสั้นๆ คุณลักษณะเชิงพื้นที่ของรูปลักษณทางภูมิศาตร์ ของฐานข้อมูล spatialite ออกแบบตามมาตรฐาน OGC (Open Geospatial Consortium Inc.) ซึ่งทั้งนี้แบบจำลองนี้จะตามมาตรฐาน OpenGIS ด้วย ซึ่ง geometry จะแบ่งเป็นชั้นๆได้ดังนี้
ภาพแสดงไดอะแกรมแบบจำลองเรขาคณิต
  • จากไดอะแกรมด้านบนจะเป็นแบบจำลองเรขาคณิตของรูปลักษณ์ทางภูมิศาสตร์ ซึ่งสีเหลืองจะเรียกว่า Non-Instantiable ซึ่งหาคำแปลในภาษาไทยได้ยากเหลือเกิน สำหรับผมแล้วง่ายๆก็คือ สร้างไม่ได้ ส่วนสีฟ้าเรียกว่า Instantiable (สร้างได้) ซึ่งผมจะกล่าวถึงเฉพาะสีฟ้าที่สามารถ implement ได้เท่านั้น
  • ซึ่งแบบจำลองจะแบ่งเป็น Class ใหญ่ๆได้ 2 ชั้นคือ Geometry และ Geometry Collection ซึ่งจะมีการแยกย่อยไปดังนี้
    • Class Geometry (ชั้นของเรขาคณิต)
      • class point (จุด) จะมีค่าพิกัด (x,y) ไม่มีขนาด
      • class line string (ชุดของเส้นตรง)
      • class polygon (รูปปิดหลายเหลี่ยม)  รูปปิดนี้จะมีรูปปิดด้านในซ้อนอยู่ก็ได้เรียกว่า interior ring
      Point
      ตัวอย่าง Linestring
      ตัวอย่าง Polygon
    • Class Geometry Collection(ชุดของชั้นเรขาคณิต)
      • MultiPoint (ชุดของจุด)
      • MultiLineString (ชุดของเส้นตรงหลายๆชุด)
      • MultiPolygon (ชุดของรูปปิดหลายเหลี่ยม)
    MultiPoint
    MultiLinestrings

    MultiPolygons
  • เหตุที่ต้องมีแบบจำลองเหล่านี้ไว้เพื่อให้สามารถจัดเก็บรูปทรงเหล่านี้ได้ในรูปแบบไฟล์ และที่สำคัญที่สุดต้องสามารถเข้าถึง สืบค้น จัดการ ประมวลผล และแลกเปลี่ยนข้อมูลด้านสารสนเทศทางภูมิศาสตร์ได้สำหรับวัตถุเชิงพื้นที่

ดาวน์โหลดและใช้งานสำหรับผู้ใช้วินโดส์

  • เนื่องจาก Sqlite + Spatialite มีขนาดเล็กจึงไม่ต้องการติดตั้งเพียงดาวน์โหลดไฟล์มาแล้ว unzip ไฟล์ไปวางไว้แล้วทำ shortcut ก็ใช้งานได้เลย ซึ่งก็เหมือนใน linux เพียงแค่ untar ไฟล์ก็เรียกใช้ได้
  • ตามไปที่นี่ได้เลย http://www.gaia-gis.it/spatialite/binaries.html ซึ่งจะมีไฟล์ให้ดาวน์โหลดอยู่มาก อาจจะงงๆ ผมจะลงลิงค์ดาวน์โหลดให้(แต่อนาคตลิงค์อาจจะขาด เพราะไฟล์เปลี่ยนเวอร์ชั่น) คลิกที่ลิงค์เพื่อดาวน์โหลดได้เลย

ติดตั้งเพื่อใช้งาน

  • เนื่องจากไม่มีโปรแกรม install สำหรับผมแล้วจะสร้างโฟลเดอร์ “spatialite” เช่น c:\spatialite จากนั้นก็ unzip ไฟล์ ที่เราดาวน์โหลดไฟล์ทั้งหมดที่ผมแนะนำมาขั้นต้นลงไปในโฟลเดอร์เดียวกันเลย  และอย่าลืมตั้ง path “c:\spatialite” ให้ด้วย
โฟลเดอร์สำหรับ Spatialite

สร้างฐานข้อมูลเพื่่อทดสอบ

  • ที่โฟลเดอร์ผมจะใช้ 2 tools คือ spatialite-gui และ spatialite-gis สำหรับนำมาเตรียมข้อมูลและแสดงผล ผู้ใช้อาจจะสร้าง shortcut เพื่่อเรียกใช้ได้ง่ายๆ  ตอนแรกผมจะเรียก spatialite-gui มาก่อนดังรูป
spatialite-gui เมื่อเปิดครั้งแรก
  • คลิกที่เมนู Files > Create a New (Empty) SQLite DB ตั้งชื่อไฟล์ใหม่จะมี extension เป็น sqlite อัตโนมัติ ผมตั้งชื่อไฟล์ geology.sqlite
สร้างฐานข้อมูล spatialite ใหม่
  • ผมจะลองข้อมูลอย่างง่ายๆ โดยเปิด shape file ที่เป็น point เล็กๆ ไม่กี่จุด เพื่่อ import ไปในฐานข้อมูล spatialite ที่เมนูไฟล์คลิก Files > Advanced > Load Shapefile เปิดไฟล์ geo.shp มาที่นี่สำคัญที่สุดคือต้องป้อนค่า SRID (Spatial Reference System Identifier) ให้ถูกต้อง ค่า SRID จะเป็นหมายเลขแสดงระบบพิกัดที่เราใช้อยู่ ตัวอย่างที่ผมใช้อยู่คือค่า 32647 เป็น UTM Zone 47N บน WGS84
ตั้งค่าต่างๆให้กับ shape file ที่ import เข้า spatialite
  • จะเห็น Table “GeoPoint” ขึ้นมาคลิกขวาเลือก Edit table rows ต่อไปจะเพิ่ม column เข้าไปเพื่อจัดเก็บรูปถ่าย
แสดง Table ที่จะทำการเพิ่ม column
  • เพิ่ม column ใหม่ให้ฐานข้อมูลเพื่อเก็บรูปภาพ ที่ table “GeoPoint” คลิกขวาเลือก Add New Column

 

 

  • ต่อไปจะเริ่ม import รูปถ่ายตามชื่อไฟล์ของรูป
Import Blob to new column
  • ต่อไปผมเพิ่ม table ที่เป็นเส้นชั้นความสูงด้วยวิธีการที่กล่าวมาแล้วคือคลิกที่เมนู Files > Advanced > Load Shapefile เลือกไฟล์ Contours.shp

 

  • ตอนนี้ก็พอแค่นี้ก่อนครับ ติดตามตอนหน้า จะนำฐานข้อมูล spatialite มาเปิดดูด้วย spatialite-gis และไปลองฐานข้อมูลด้วย QGIS ว่าจะ support ได้สักขนาดไหนกันครับ