คอมไพล์ Python Script เป็นไฟล์ Executable ด้วย PyInstaller

PyInstaller

คือเครื่องมือที่ช่วยการแปลงโปรแกรมที่เขียนด้วยไพทอนเป็น execute binary file  ที่สามารถนำไปรันได้โดยที่เครื่องคอมพิวเตอร์ปลายทางไม่ต้องติดตั้งไพทอน สำหรับ PyInstaller เป็น cross-platform สามารถใช้งานได้บนวินโดส์ แมค และลีนุกซ์ สนับสนุนไพทอนรุ่น 2.7 และ ไพทอน รุ่น 3.3 ถึง 3.6 จุดมุ่งหมายของ PyInstaller คือต้องการช่วยผู้ใช้ในการแปลงโปรแกรมไพทอน ที่ใช้โมดูลไลบรารีภายนอกเช่น Matplotlib, DJango, wxPython, PyQt เป็นต้น ให้สามารถทำได้ง่ายสะดวก

ติดตั้ง PyInstaller

ติดตั้งง่ายๆด้วยคำสั่ง pip ใน command prompt

pip install pyinstaller

ใช้งาน PyInstaller

การใช้งานสามารถใช้งานผ่าน command line ได้ แต่สำหรับโปรแกรมที่เรียกใช้โมดูลไลบรารีข้างนอกและต้องขนข้อมูล (data) ที่โมดูลไลบรารีนั้นๆต้องการใช้  ผมแนะนำให้ใช้ไฟล์สคริปท์ (Spec file) มาช่วยจะดีกว่า ปรับแต่งได้มากกว่า ตัว spec file จริงๆก็คือไฟล์สคริปท์ของไพทอนนั่นเอง กรณีที่ต้องใช้ Spec file อีกกรณีหนึ่งคือต้องการขนรันไทม์ไลบรารีเช่น .dll หรือ .so ไปแบบแมนวล กรณีที่ผมเจอคือผมใช้ PySide2 ที่รุ่นทางการจริงๆยังไม่ออกมา แต่ hook file ก็มีมาให้แล้วพร้อมกับ PyInstaller รุ่นใหม่ 3.3 แต่ผมใช้งานแล้วยังไม่สำเร็จ ดังนั้นจึงต้องใช้ Spec file นี้เป็นตัวช่วยในการขนรันไทม์ไลบรารีไป ส่วนเรื่อง hook file คืออะไรค่อยว่าอีกที

กรณีศึกษาด้วย Surveyor Pocket Tools บนวินโดส์

โปรแกรม Surveyor Pocket Tools พัฒนาด้วยไพทอน ปัจจุบันใช้ไพทอน รุ่น 3.6 ใช้โมดูลไลบรารีข้างนอกคือ openpyxl, pyproj, geographiclib, gmplot, simplekml, pyshp และที่ขาดไม่ได้คือ PySide2 ซึ่งสำหรับ openpyxl และ pyproj จะมีการขนข้อมูลไปด้วย ส่วน PySide2 ผมจะขนไฟล์ dll  ที่ต้องการด้วยมือล้วนๆ

Spec file ของ Surveyor Pocket Tools

มาดูไฟล์สคริปนี้ ผมตั้งชื่อว่า “setup.spec”

# -*- mode: python -*-
# -*- mode: python -*-
import sys
import PySide2
import os
block_cipher = None
 
dirname = os.path.dirname(PySide2.__file__)
plugins_path = os.path.join(dirname, 'plugins', '')
 
pyside2_plugins = [(plugins_path + 'iconengines/*', 'plugins/iconengines/'),
                  (plugins_path + 'imageformats/*', 'plugins/imageformats/'),
		  (plugins_path + 'platforms/*', 'plugins/platforms/'),
		  (plugins_path + 'printsupport/*', 'plugins/printsupport/'),
		  (plugins_path + 'sqldrivers/*', 'plugins/sqldrivers/')]
 
added_files = [('markers/*', 'markers/'),
               ('geoids/*', 'geoids/'),
	       ('database/*', 'database/'),
	       ('example data/*', 'example data/'),
	       ('qt.conf', ''), 
	       ('*.xml', '')]
 
a = Analysis(['main.py'],
             pathex=['D:\\sourcecodes\\python\\surveyor pocket tools'],
             binaries=None,
             datas=added_files + pyside2_plugins,
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          exclude_binaries=True,
          name='surveyor pocket tools',
		  icon='Land Surveying-64.ico',
          debug=False,
          strip=False,
          upx=False,
          console=False )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='setup')

ลองมาดูโค้ดกัน เริ่มจาก import PySide2 เข้ามาเพื่อจะตรวจสอบว่า PySide2 ที่เราใช้งานเป็น 32 บิตหรือ 64 บิต เพื่อจะได้ขน .dll ไปถูกรุ่น จากนั้นเก็บไดเรคทอรีของ PySide2 เข้าเก็บใน dirname ผ่านฟังก์ชัน os.path.dirname() ที่นี้เราทราบว่าในไดเรคทอรีของ PySide2 จะมีไดเรคทอรีย่อยชื่อ “plugins” อยู่ ทำการเก็บไดเรคทอรีนี้ด้วยฟังก์ชั่น os.path.join() ไปเก็บไว้ในตัวแปร plugins_path

# -*- mode: python -*-
import sys
import PySide2
import os
block_cipher = None
 
dirname = os.path.dirname(PySide2.__file__)
plugins_path = os.path.join(dirname, 'plugins', '')

ต่อไปคือตัวแปร pyside2_plugins จะเป็นลิสต์เก็บ tuple โดยสมาชิกตัวแรกจะเก็บชื่อไฟล์ไดเรคทอรีต้นทาง ใช้เครื่องหมาย * เพราะต้องการทุกๆไฟล์ในไดเรคทอรีนี้ สมาชิกตัวที่สอง จะเก็บชื่อไดเรคทอรีปลายทางที่ต้องการไฟล์เหล่านี้ไปอยู่

 
pyside2_plugins = [(plugins_path + 'iconengines/*', 'plugins/iconengines/'),
                   (plugins_path + 'imageformats/*', 'plugins/imageformats/'),
		   (plugins_path + 'platforms/*', 'plugins/platforms/'),
		   (plugins_path + 'printsupport/*', 'plugins/printsupport/'),
		   (plugins_path + 'sqldrivers/*', 'plugins/sqldrivers/')]

ลองมาดูว่าไดเรคทอรี “plugins” ผมไฮไลท์ไว้เฉพาะไดเรคทอรีที่โปรแกรม Surveyor Pocket Tools ต้องการ

ต่อไปจะขนไฟล์ที่โปรแกรม Surveyor Pocket Tools ต้องการใช้ ให้ใส่ไว้ที่ตัวแปร added_files โครงสร้างเป็น tuple เหมือนกัน และขนไฟล์ชื่อ qt.conf ที่ PySide2 ต้องการไปด้วย

added_files = [('markers/*', 'markers/'),
               ('geoids/*', 'geoids/'),
	       ('database/*', 'database/'),
	       ('example data/*', 'example data/'),
	       ('qt.conf', ''), 
	       ('*.xml', '')]

มาดูไดเรคทอรีที่โปรแกรมต้องการดังนี้

ต่อไปมาดูโค้ดส่วนที่สำคัญมาก ‘main.py’ คือไฟล์สคริปท์หลักของโปรแกรม Surveyor Pocket Tools ต่อไปคือ pathex เป็นไดเรคทอรีของไฟล์ไพทอนสคริปท์ และ datas ที่ผมจัดการรวม added_files และ pyside2_plugins เข้าด้วยกัน สุดท้าย hookspath คือไดเรคทอรีที่เก็บไฟล์ hook ไว้ สำหรับไฟล์ hook นี้ PyInstaller จะอ่านสคริปท์นี้ทีละไฟล์มาตัดสินใจว่าจะขนข้อมูลไดเรคทอรีไหนไป ผมเลือกใช้ดีฟอลท์ครับคือปล่อยว่าง

a = Analysis(['main.py'],
             pathex=['D:\\sourcecodes\\python\\surveyor pocket tools'],
             binaries=None,
             datas=added_files + pyside2_plugins,
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)

สำหรับไดเรอทอรี hooks ที่เป็นดีฟอลท์มากับ PyInstaller ผมใช้ไฟล์เพียงสองไฟล์เท่านั้น ตามที่ไฮไลท์ไว้

ใช้ PyInstaller คอมไพล์ไฟล์ setup.spec

ผมใช้ Minoconda เมื่อจะคอมไพล์ก็เรียก command prompt มาดังนี้ ใช้คำสั่ง cd เข้ามาที่พาทของสคริปท์ของไพทอน ใช้คำสั่ง dir ดูไฟล์ setup.spec

ต่อไปทำการคอมไพล์ ด้วยคำสั่ง

pyinstaller setup.spec

ผลลัพธ์ของ PyInstaller

เมื่อคอมไพล์เสร็จแล้ว ไม่มี error จะได้ไดเรคทอรีมาสองคือ “build” และ “dist” เมื่อเข้าไปดูใน “dist” จะเห็นไดเรคทอรีย่อยช “setup” ชื่อไดเรคทอรีนี้ PyInstaller จะสร้างตามชื่อหน้าของไฟล์ setup.spec เมื่อเข้าดูที่ไดเรคทอรี “setup” จะเห็นไฟล์ต่างๆที่โปรแกรมต้องการ

ผมลองดับเบิ้ลคลิกไฟล์ “surveyor pocket tools.exe” ก็สามารถเปิดมาและทำงานได้ตามปกติ ลองดูชื่อไดเรคทอรีจะเห็นสองไดเรคทอรี ที่ได้จากไฟล์ hooks คือ openpyxl และ pyproj ลองเข้าไปดูในไดเรคทอรี จะเห็นข้อมูลที่ pyproj ขนไปใช้ หมายเหตุว่าข้อมูลนี้ pyproj จะนำไปเป็นฐานข้อมูลในการแปลงพิกัดตาม datum และ projection

ทำไฟล์ Setup ด้วย Inno Setup

จากนั้นผมจะ copy ไดเรคทอรีที่อยู่ใน “setup” ไปไว้อีกที่หนึ่ง พื้นที่นี้สำหรับใช้ Inno Setup มาทำไฟล์ติดตั้ง ลองดูไดเรคทอรี

ในไดเรคทอรีนี้ผมจะมีไฟล์ “surveyorpockettools64.iss” เป็นไฟล์สคริปท์ของ Inno Setup เพื่อสร้างไฟล์ติดตั้ง setup สำหรับวินโดส์ 64 บิต

#define MyAppName "Surveyor Pocket Tools"
#define MyAppEXE "Surveyor Pocket Tools.exe"
#define MyShortAppName "SurveyorPocketTools"
#define MyMainRoot "Survey Suite"
#define Developer "Prajuab Riabroy"
#define Version "0.98"
#define Build "573"
 
[Setup]
AppName={#MyAppName}
AppVerName={#MyAppName} V{#Version}
DefaultDirName={pf}\{#MyMainRoot}\{#MyAppName}
DefaultGroupName={#MyMainRoot}\{#MyAppName}
UseSetupLdr=yes
UninstallDisplayIcon={app}\{#MyAppEXE}
VersionInfoProductName={#MyAppName}
VersionInfoCompany=priabroy
VersionInfoCopyright=Copyright 2000-2017 by {#Developer}
VersionInfoDescription={#MyAppName}
VersionInfoProductVersion={#Version}
VersionInfoVersion={#Version}
OutputDir=Setup
OutputBaseFilename={#MyShortAppName}V{#Version}Build{#Build}Setup64
;OutputDir=TraverseProV250Setup64
; "ArchitecturesAllowed=x64" specifies that Setup cannot run on
; anything but x64.
ArchitecturesAllowed=x64
; "ArchitecturesInstallIn64BitMode=x64" requests that the install be
; done in "64-bit mode" on x64, meaning it should use the native
; 64-bit Program Files directory and the 64-bit view of the registry.
;ArchitecturesInstallIn64BitMode=x64
ArchitecturesInstallIn64BitMode=x64
AppPublisher={#Developer}
AppPublisherURL=https://www.surveyorpockettools.org
AppVersion={#Version}.{#Build}
LicenseFile = eula.txt
ChangesEnvironment=yes
SolidCompression=yes
Compression=lzma2/ultra64
LZMAUseSeparateProcess=yes
LZMADictionarySize=1048576
LZMANumFastBytes=273
 
[Files]
Source: "{#MyAppName}.exe"; DestDir: "{app}"
Source: "base_library.zip"; DestDir: "{app}" ;
Source: "plugins\*"; DestDir: "{app}\plugins\"; Flags: ignoreversion recursesubdirs
Source: "database\*"; DestDir: "{userappdata}\{#MyAppName}\database\";
Source: "geoids\*"; DestDir: "{userappdata}\{#MyAppName}\geoids\";
Source: "markers\*"; DestDir: "{userappdata}\{#MyAppName}\markers\";
Source: "example data\*"; DestDir:"{userappdata}\{#MyAppName}\example data\";
Source: "pyproj\data\*"; DestDir: "{app}\pyproj\data\";
Source: "openpyxl\*"; DestDir: "{app}\openpyxl\";
;Source: "requests\*"; DestDir: "{app}\requests\";
Source: "*.html"; DestDir: "{userappdata}\{#MyAppName}\";
Source: "*.dll"; DestDir: "{app}"
Source: "*.pyd"; DestDir: "{app}"
Source: "*.xml"; DestDir: "{userappdata}\{#MyAppName}\";
Source: "qt.conf"; DestDir: "{app}"
 
[Icons]
;create icon at start menu group
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExe}"
;create icon at desktop
Name: "{commondesktop}\{#MyAppName}"; FileName:"{app}\{#MyAppExe}"
 
[Registry]
; Start "Software\My Company\My Program" keys under HKEY_CURRENT_USER
; and HKEY_LOCAL_MACHINE. The flags tell it to always delete the
; "My Program" keys upon uninstall, and delete the "My Company" keys
; if there is nothing left in them.
Root: HKCU; Subkey: "Software\{#MyMainRoot}"; Flags: uninsdeletekeyifempty
Root: HKCU; Subkey: "Software\{#MyMainRoot}\{#MyAppName}"; Flags: uninsdeletekey
Root: HKLM; Subkey: "Software\{#MyMainRoot}"; Flags: uninsdeletekeyifempty
Root: HKLM; Subkey: "Software\{#MyMainRoot}\{#MyAppName}"; Flags: uninsdeletekey
Root: HKLM; Subkey: "Software\{#MyMainRoot}\{#MyAppName}\Settings"; ValueType: string; ValueName: "InstalledPath"; ValueData: "{app}"
Root: HKLM; Subkey: "Software\{#MyMainRoot}\{#MyAppName}\Settings"; ValueType: string; ValueName: "DevelopedBy"; ValueData: "{#Developer}"
Root: HKLM; Subkey: "Software\{#MyMainRoot}\{#MyAppName}\Settings"; ValueType: string; ValueName: "ApplicationName"; ValueData: "{#MyAppName}"
;Root: HKCU; Subkey: "Environment"; ValueType:string; ValueName:"PROJ_LIB"; ValueData:"{userappdata}\{#MyAppName}\geoidgrids\" ; Flags: preservestringtype ;

ถ้าเป็นไฟล์สำหรับ Surveyor Pocket Tools รุ่น 32 บิตเพียงใส่คอมเมนต์หน้า ;ArchitecturesInstallIn64BitMode=x64 ก็พอ สำหรับรายละเอียดสคริปท์ของ Inno Setup ผมจะไม่กล่าวถึงรายละเอียดในที่นี้ผู้อ่านที่สนใจสามารถศึกษาได้ครับ จากนั้นก็ใช้ Inno Setup ทำการ build ก็จะได้ไฟล์ Exe เดี่ยวๆ ที่สามารถ zip ไปให้ผู้ใช้ได้ download ต่อไป พบกันตอนหน้าครับ

ก้าวไปอีกหนึ่งก้าวกับ XSection Plot

สวมวิญญานใหม่ด้วย PySide2

หลังจากผมคอมไพล์ XSection Plot ใหม่ด้วยสภาวะแวดล้อมพัฒนาของ Qt5 platform ด้วย PySide2 ผมเปลี่ยนลิขสิทธิ์ของโปรแกรมเดิมที่กำกวมออกมาฟรีสมบูรณ์แบบเหมือนกันกับ Surveyor Pocket Tools สามารถนำไปทำซ้ำแจกจ่ายได้ตามอัธยาศัย แต่ห้ามดัดแปลง ห้ามนำไปจำหน่ายหรือให้เช่า

XSection Plot
Copyright (C) Prajuab Riabroy. All Rights Reserved.

XSection Plot is free for use in any environment, including but not necessarily limited to: personal, academic, commercial, government, business, non-profit, and for-profit. "Free" in the preceding sentence means that there is no cost or charge associated with the installation and use of XSection Plot. 
Permission is hereby granted, free of charge, to any person obtaining a copy of this software (the "Software"), to use the Software without restriction, including the rights to use, copy, publish, and distribute the Software, and to permit persons to whom the Software is furnished to do so.

You may not modify, adapt, rent, lease, loan, sell, or create derivative works based upon the Software or any part thereof. 

The above copyright notice and this permission notice shall be included in all copies of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

แก้ไข bugs

นอกจากย้ายโค้ดมาใช้ PySide2 แล้ว ผมเลยถือโอกาสแก้บั๊กเล็กน้อยไปหลายอย่าง เช่น

  • เวลาคลิกขวาเพื่อเรียกเมนูในช่องป้อนข้อมูล โปรแกรมจะ terminate ทันที
  • ใน Section Layout ตรง Horizontal Grid เมื่อปรับ Distance from CL to left ไปแล้ว โปรแกรมไม่จำค่าใหม่
  • อื่นๆอีกเล็กน้อยประมาณสิบกว่าอย่าง

คอมไพล์และสร้างไฟล์ execute binary ด้วย PyInstaller

ตอนนี้โปรแกรมสนับสนุนทั้ง 32 บิตและ 64 บิต ผมใช้ PyCharm เป็นทูลส์ในการพัฒนา และเลือกได้ว่าจะใช้ 32 บิตหรือ 64 บิต เมื่อโปรแกรม stable แล้ว ก็จะสร้าง execute binary file ด้วย PyInstaller ตามสภาวะแวดล้อม ต้องทำสองครั้ง ครั้งแรก 32 บิตและครั้งที่สอง 64 บิต โดยแต่ละครั้งจะได้ไฟล์ exe, pyd, dll รวมถึงไลบรารีของไพทอนที่เราเรียกใช้ และที่สำคัญคือไลบรารีของ PySide2

ทำไฟล์ Setup ด้วย Inno Setup

ไฟล์ที่ได้จาก PyInstaller ทั้งหมด ผมจะนำมาสร้างไฟล์ Setup ด้วย Inno Setup เพื่อนเก่าที่ใช้กันมานมนาน มีดีเพียงพอที่จะสร้างไฟล์ Setup ได้ง่ายๆ มี options ให้เลือกพอสมควร สุดท้ายจะได้ไฟล์ Setup ที่เป็น Execute file ไฟล์เดียวพร้อมจะนำไปอัพโหลดให้ผู้ใช้นำไปใช้งานได้

ทดสอบโปรแกรมด้วยแบบรูปตัดตามยาว

ผมจะลองทดสอบโปรแกรมจากข้อมูล ความจริง XSection Plot คือโปรแกรมสร้างหรือช่วยเขียนรูปตัดตามขวาง แต่ยังพอเอามาประยุกต์ใช้กับ Long Profile ได้ แต่ไฟล์ข้อมูลส่วนใหญ่จะมีข้อมูลเพียงหนึ่ง Section เท่านั้นจะเริ่มจากไฟล์ข้อมูลของ Existing Ground Section ก่อนครับ ข้อมูลดังในกรอบข้างล่าง สามารถ copy ไป paste ในโปรแกรม text file editor เช่น Notepad ได้จากนั้น save ตั้ง extension เป็น gxml (ตัวอย่างผมตั้งชื่อว่า lake-road.gxml)

<root version="1.0">
  <!--Generated by XSection Plot-->
  <!--This data file is Existing ground section-->
  <Header>
    <AppName>XSection Plot</AppName>
    <Developer>Prajuab Riabroy</Developer>
    <Version>4.1.512</Version>
    <SectionType>Ground</SectionType>
    <DateCreated>2017-11-12 19:24:28.634707</DateCreated>
  </Header>
  <ProjectInfo>
    <ProjectName>Cantonment Lake Road</ProjectName>
    <ClientName></ClientName>
    <ContractorName></ContractorName>
    <DrawingTitle></DrawingTitle>
    <DrawnName></DrawnName>
    <ApprovedName></ApprovedName>
    <ClientApprovedName></ClientApprovedName>
    <CheckedName></CheckedName>
    <SurveyorName></SurveyorName>
    <SurveyedDate></SurveyedDate>
    <DrawingNo></DrawingNo>
    <SheetNo></SheetNo>
    <DrawingDate></DrawingDate>
    <Revision></Revision>
    <UseLocaleLanguage>False</UseLocaleLanguage>
    <MapTexts>
      <MapText Label="Client" Locale="เจ้าของโครงการ"/>
      <MapText Label="Contractor" Locale="ผู้รับจ้าง"/>
      <MapText Label="Drawn" Locale="เขียน"/>
      <MapText Label="Design" Locale="ออกแบบ"/>
      <MapText Label="Surveyor" Locale="ผู้สำรวจ"/>
      <MapText Label="Surveyed Date" Locale="วันที่สำรวจ"/>
      <MapText Label="Checked" Locale="ตรวจสอบ"/>
      <MapText Label="Approved" Locale="อนุมัติ"/>
      <MapText Label="Client Approved" Locale="ผู้คุมงานอนุมัติ"/>
      <MapText Label="Drawing No." Locale="แบบเลขที่"/>
      <MapText Label="Plotted Date" Locale="แบบวันที่"/>
      <MapText Label="Project" Locale="โครงการ"/>
      <MapText Label="Drawing Title" Locale="แผนที่แสดง"/>
      <MapText Label="Sheet No." Locale="แบบเลขที่"/>
      <MapText Label="Scale" Locale="มาตราส่วน"/>
      <MapText Label="Vertical" Locale="ทางดิ่ง"/>
      <MapText Label="Horizontal" Locale="ทางราบ"/>
      <MapText Label="Vertical Scale" Locale="มาตราส่วนทางดิ่ง"/>
      <MapText Label="Horizontal Scale" Locale="มาตราส่วนทางราบ"/>
      <MapText Label="Legend" Locale="สัญลักษณ์"/>
      <MapText Label="Note" Locale="หมายเหตุ"/>
      <MapText Label="Geodetic Information" Locale="ข้อมูลระบบพิกัด"/>
      <MapText Label="No." Locale="ครั้งที่"/>
      <MapText Label="Amendments" Locale="ความเห็น"/>
      <MapText Label="By" Locale="โดย"/>
      <MapText Label="Date" Locale="วันที่"/>
      <MapText Label="Revision" Locale="ครั้งที่แก้ไข"/>
    </MapTexts>
  </ProjectInfo>
  <SectionOptions>
    <VerticalScale>500.0</VerticalScale>
    <HorizontalScale>1000.0</HorizontalScale>
    <HozGridSpace>10.0</HozGridSpace>
    <VertGridSpace>2.0</VertGridSpace>
    <GridLineType>0</GridLineType>
    <CalcIntersection>True</CalcIntersection>
    <CalcArea>True</CalcArea>
    <TrimTypical>False</TrimTypical>
    <NumDecimalElev>3</NumDecimalElev>
    <NumDecimalDist>3</NumDecimalDist>
    <UseIntervalText>True</UseIntervalText>
    <IntervalDist>10.0</IntervalDist>
    <PrefixText>MSL</PrefixText>
    <PostfixText>MSL</PostfixText>
    <UsePostPrefix>1</UsePostPrefix>
    <UseOffsetElevFormat>2</UseOffsetElevFormat>
    <CalcPlotAreaCut>True</CalcPlotAreaCut>
    <CalcPlotAreaFill>True</CalcPlotAreaFill>
    <NumVertCLLeft>1</NumVertCLLeft>
    <NumVertCLRight>90</NumVertCLRight>
    <NumHozTopBottom>10</NumHozTopBottom>
    <LeftSideText>LT.</LeftSideText>
    <RightSideText>RT.</RightSideText>
    <StationText>Km.</StationText>
    <SelectedTBlock>2</SelectedTBlock>
    <NumSectionRows>1</NumSectionRows>
    <NumSectionColumns>1</NumSectionColumns>
    <SurveyType>0</SurveyType>
    <PlotTBlock>True</PlotTBlock>
    <SwapLeftAndRight>False</SwapLeftAndRight>
  </SectionOptions>
  <PageSetup>
    <Size>Custom</Size>
    <Width>940.0</Width>
    <Height>200.0</Height>
  </PageSetup>
  <Sections>
    <NumSections>1</NumSections>
    <Section Name="0+000">
      <XPositionOnPaper>47.7</XPositionOnPaper>
      <YPositionOnPaper>11.7</YPositionOnPaper>
      <TopGridElev>14.0</TopGridElev>
      <NumPoints>29</NumPoints>
      <Points>
        <Point Elevation="5.741" Offset="0.0"/>
        <Point Elevation="5.802" Offset="29.5"/>
        <Point Elevation="4.186" Offset="44.5"/>
        <Point Elevation="1.955" Offset="66.778"/>
        <Point Elevation="1.04" Offset="84.5"/>
        <Point Elevation="1.017" Offset="114.5"/>
        <Point Elevation="0.895" Offset="144.5"/>
        <Point Elevation="1.162" Offset="174.5"/>
        <Point Elevation="1.012" Offset="234.5"/>
        <Point Elevation="1.145" Offset="264.5"/>
        <Point Elevation="1.16" Offset="316.642"/>
        <Point Elevation="1.317" Offset="339.5"/>
        <Point Elevation="1.619" Offset="386.947"/>
        <Point Elevation="1.518" Offset="409.5"/>
        <Point Elevation="1.311" Offset="454.5"/>
        <Point Elevation="1.261" Offset="484.5"/>
        <Point Elevation="1.065" Offset="544.5"/>
        <Point Elevation="1.113" Offset="574.5"/>
        <Point Elevation="2.799" Offset="634.5"/>
        <Point Elevation="1.664" Offset="664.5"/>
        <Point Elevation="1.442" Offset="694.5"/>
        <Point Elevation="1.108" Offset="724.5"/>
        <Point Elevation="1.099" Offset="754.5"/>
        <Point Elevation="1.854" Offset="784.5"/>
        <Point Elevation="1.549" Offset="814.5"/>
        <Point Elevation="8.402" Offset="844.5"/>
        <Point Elevation="8.555" Offset="866.792"/>
        <Point Elevation="8.68" Offset="889.5"/>
        <Point Elevation="8.58" Offset="916.9"/>
      </Points>
    </Section>
  </Sections>
</root>

ต่อไปเป็น Typical Section ขนาดเล็กกว่า ผมตั้งชื่อว่า lake-typical.txml

<root version="1.0">
  <!--Generated by XSection Plot-->
  <!--This data file is Typical section-->
  <Header>
    <AppName>XSection Plot</AppName>
    <Developer>Prajuab Riabroy</Developer>
    <Version>4.1.512</Version>
    <SectionType>Typical</SectionType>
    <DateCreated>2017-11-12 18:42:28.929847</DateCreated>
  </Header>
  <Sections>
    <NumSections>1</NumSections>
    <Section Name="">
      <XPositionOnPaper>0.0</XPositionOnPaper>
      <YPositionOnPaper>0.0</YPositionOnPaper>
      <TopGridElev>0.0</TopGridElev>
      <NumPoints>4</NumPoints>
      <Points>
        <Point Elevation="5.0" Offset="36.944"/>
        <Point Elevation="5.0" Offset="695.0"/>
        <Point Elevation="8.4" Offset="845.0"/>
        <Point Elevation="8.44" Offset="900.0"/>
      </Points>
    </Section>
  </Sections>
</root>

เปิดไฟล์ข้อมูลทดสอบบน XSection Plot

จากนั้นนำสองไฟล์มาเปิดด้วย XSection Plot เวลาเปิดไฟล์ให้เลือกรูปแบบของไฟล์ด้วยจะได้เปิดง่าย  มีชื่อ extension ตามลำดับดังนี้ gxml, txml

จะได้ข้อมูลปรากฎขึ้นบนโปรแกรมดังนี้

ตั้งหน้ากระดาษ (Page Setup)

ผมลองเลือกใช้หน้ากระดาษที่ไม่มาตรฐานเพื่อให้ฟิตกับขนาดรูปตัดตามยาว ยาว 940 มม. และกว้าง 200 มม.

ตั้งค่า (Settings)

ผมตั้งสเกลทางราบเป็น 1:1000 และสเกลทางดิ่ง 1:250 อย่างอื่นดูรูปด้านล่าง

จัดวางรูปตัดบนกระดาษ (Section Layout)

จะเห็นกระดาษขนาด 200 มม. x 940 มม. แล้วเลือกพารามิเตอร์ดังรูปด้านล่าง

ดูรูปตัด (Section Viewer)

จะเห็นรูปตัดตามยาวที่ประกอบไปด้วย Existing Ground และ Typical

ลองซูมดู ก็ได้แบบ drawing มาพอถูๆไถๆ ที่สามารถนำไปเขียนเพิ่มเติมได้ในโปรแกรมด้านเขียนแบบทั้งหลายเช่น Autocad, Microstation, Draftsight

Save to DXF

จัดเก็บไฟล์ในรูป Autocad DXF เพื่อสามารถนำไปเปิดในโปรแกรมอื่นได้

เปิดไฟล์แบบรูปตัดตามยาว

ผมใช้ Microstation เปิดแบบรูปตัดตามยาวได้ผลลัพธ์ดังนี้และพร้อมจะนำแบบไปเขียนเพิ่มเติมตามความต้องการ

ครับคงอีกไม่นานก็จะคงจะปล่อยเวอร์ชั่นเสถียรให้สามารถดาวน์โหลดได้ พบกันใหม่ครับ

แนะนำการย้ายโค้ดจาก PyQt5 เป็น PySide2

ย้ายโค้ด XSection Plot

ในขณะนี้ทำงานอยู่ที่บังคลาเทศ โครงการก่อสร้างรถไฟฟ้าที่กรุงธากา มีโอกาสกลับมาพัก ก็พอมีเวลาว่างพยายามย้ายโค้ดของโปรแกรม XSection Plot จากของเดิมที่พัฒนาด้วย PyQt5 ที่ยังติดเรื่องลิขสิทธิ์บางส่วน โดยย้ายมาใช้ PySide2 ที่เปิดกว้างกว่า ความจริงทั้งคู่ใช้เครื่องยนต์ (Engine) เดียวกันคือ Qt5 platform ดังนั้นเมื่อย้ายโค้ดสำเร็จแล้วเวลารันก็หน้าตาเหมือนกันเป๊ะดังรูปด้านล่างที่คอมไพล์ด้วย PySide2

จัดการปลั๊กอิน PySide2

การย้ายโค๊ดใช้เวลาไม่นานนัก ใช้เวลาประมาณ 2 ชั่วโมง เนื่องจากผมเคยย้ายโค้ด Surveyor Pocket Tools ทำให้รู้แนวทางลัดพอสมควร อันดับแรกขอย้อนกลับหน่อย เนื่องจาก PySide2 จะมองหาโฟลเดอร์ลิ๊งค์ไลบรารีของตัวเองชื่อ “plugins” ถ้าไม่เจอจะ error แล้วหยุดทันที ดังนั้นก่อนอื่นควรจะแทรกโค๊ดนี้เข้าไปก่อน เริ่มตั้งแต่ import PySide2 ตามด้วยตรวจสอบว่า PySide2 อยู่ที่โฟลเดอร์ไหนจัดเก็บเข้าตัวแปร dirname จากนั้นค้นหาพาทของ “plugins” ที่อยู่ใต้โฟลเดอร์ dirname ด้วยคำสั่ง os.path.join()

import os
import sys
import PySide2
dirname = os.path.dirname(PySide2.__file__)
plugin_path = os.path.join(dirname, 'plugins', '')
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path
print('plugin_path = ', plugin_path)

ที่เครื่องคอมพิวเตอร์ผม จะปริ๊นท์พาทของ “plugins” ดังนี้

plugin_path =  C:\Miniconda3\envs\py36_64\lib\site-packages\PySide2\plugins\

เพราะว่าผมใช้ Miniconda เป็นตัวจัดการระบบ environment ของ python ผมสร้าง envs ชื่อ “py36_64” เป็น python รุ่น 3.6 แบบ 64 บิต และ PySide2 ก็จะถูกติดตั้งมาอยู่ภายใต้โฟลเดอร์นี้ อีก envs หนึ่งที่สร้างไว้ชื่อ “py36_32” เป็น python รุ่น 3.6 แบบ 32 บิต เมื่อรันโปรแกรมแล้วจะปริ๊นท์พาทมาดังนี้

plugin_path =  C:\Miniconda3\envs\py36_32\lib\site-packages\PySide2\plugins\

วิธีการสร้าง environment สำหรับไพทอนก็กลับไปดูโพสต์เก่าของผมได้ครับ

เปลี่ยนคำ PyQt5 เป็น PySide2

ยังอยู่ในส่วน import ที่โค๊ดเดิมของโปรแกรมผมเรียกใช้ไลบรารีของ PyQt5 ดังนี้

from PyQt5.QtGui import QPixmap, QIcon, QKeySequence, QFont, QIntValidator, QCursor
from PyQt5.QtCore import Qt, pyqtSlot, QSettings, QFileInfo, QSize, QFile
from PyQt5.QtWidgets import QUndoStack, QSplashScreen, QApplication, QMainWindow, QTabWidget, QAction, QStatusBar,\
     QMenu, QWidget, QSizePolicy, QLineEdit, QFileDialog, QMessageBox, QDesktopWidget

เปลี่ยนเป็น

from PySide2.QtGui import QPixmap, QIcon, QKeySequence, QFont, QIntValidator, QCursor
from PySide2.QtCore import Qt, Slot, QSettings, QFileInfo, QSize, QFile
from PySide2.QtWidgets import QUndoStack, QSplashScreen, QApplication, QMainWindow, QTabWidget, QAction, QStatusBar,\
     QMenu, QWidget, QSizePolicy, QLineEdit, QFileDialog, QMessageBox, QDesktopWidget

ส่วนใหญ่เกือบ 99.99% ที่เหมือนกัน ยกเว้น Signal & Slot

Signal and Slot

มีข้อแตกต่างกันเล็กน้อย เช่นเดิมใน PyQt5 เรียกใช้ pyqtSlot, pyqtSignal ให้เปลี่ยนเป็น Slot, Signal ใน PySide2 ครับ
นอกจากส่วน import แล้ว ในโค๊ดเดิมที่ประกาศคลาส โค้ดเดิมผมเรียกใช้ Signal and Slot ดังนี้

class OverlapSection(QObject):
    '''Horizontal &amp; Vertical overlapped.'''
    overlapped = pyqtSignal(str)
 
    def __init__(self):
            QObject.__init__(self)
 
    def emitOverlapSignal(self, message):
        self.overlapped.emit(message)

เปลี่ยนใหม่เป็น

class OverlapSection(QObject):
    '''Horizontal &amp; Vertical overlapped.'''
    overlapped = Signal(str)
 
    def __init__(self):
            QObject.__init__(self)
 
    def emitOverlapSignal(self, message):
        self.overlapped.emit(message)

ติดตั้ง PySide2 จากไฟล์ wheel

ก็ขอแนะอีกนิดว่า PySide2 เวลาติดตั้งให้ใช้แพ๊คเกจแบบ wheel ที่ทางทีมงานได้ทำไว้ดีกว่าครับ ติดตั้งง่ายไม่งอแง ดาวน์โหลดได้ตามลิ๊งค์นี้ จะสังเกตเห็นชื่อไฟล์ประมาณนี้

PySide2-5.6-cp35-cp35m-win32.whl 	 
PySide2-5.6-cp35-cp35m-win_amd64.whl 	 
PySide2-5.6-cp36-cp36m-win32.whl 
PySide2-5.6-cp36-cp36m-win_amd64.whl

จะเห็นว่า PySide2 สนับสนุนทั้งไพทอน 3.5 และ 3.6 และในตอนนี้ Qt5 รุ่น  5.6 สำหรับคำสั่งที่ติดตั้งก็ง่ายๆใช้ pip ตามด้วยชื่อไฟล์ wheel

pip install PySide2-5.6-cp36-cp36m-win_amd64.whl

สรุปแล้วการย้ายโค้ดง่ายๆไม่ลำบากกินแรง แต่ไปกินแรงเข็นครกอีกทีคือตอนสร้างไบนารีไฟล์ด้วย Pyinstaller ความจริง Pyinstaller ถ้าเข้าใจแล้วปรับใช้ได้ไม่ยาก แต่สำหรับมือใหม่บอกตรงๆว่า ถ้าโปรแกรมที่พัฒนาเรียกใช้ไลบรารีมากหลายอันแล้ว เป็นนรกลูกย่อมๆครับ ถ้าไลบรารีตัวไหนมีคนเขียนไฟล์ hook ให้ก็ง่ายหน่อย แต่ถ้าไม่มีต้องออกแรงกันพอสมควร สำหรับ PySide2 ผมจัดการแบบ manual ครับ รู้ว่าตอนโปรแกรมรันมันต้องการอะไร ตอนใช้ Pyinstaller ผมก็จัดการ copy ไฟล์ไปตามต้องการ ถ้ามีโอกาสจะมาเขียนเรื่องการใช้ Pyinstaller อีกสักตอน พบกันตอนหน้าครับ

การตั้งค่า (Settings) ของ Surveyor Pocket Tools

Surveyor Pocket Tools ออกมาตั้งนานแล้วเพิ่งจะเปิดโอกาสให้ผู้ใช้ได้ตั้งค่าต่างๆเช่นจำนวนทศนิยมของค่าพิกัด จำนวนทศนิยมของระยะทาง ความสูง หรือแม้แต่ของมุม เมื่อเปิดโปรแกรม Surveyor Pocket Tools จะเห็นมีไอคอน Settings รูปเกียร์เพิ่มดังรูป

python_2017-07-15_09-19-59

เมื่อดับเบิ้ลคลิกเข้าไปจะเห็นไดอะล็อก

python_2017-07-15_09-24-21

จะมีแท็บ Unit, Linear Precision, Angular Precision, Google Maps และ Google Earth เรียงรายกันตามลำดับ เริ่มต้นที่ Unit ออกแบบเพื่ออนาคตสำหรับหน่วยอื่นที่ไม่ใช่หน่วย metric แต่ตอนนี้สนับสนุนหน่วยเมตริกอย่างเดียวครับ

Linear Precision

มาดูที่ Linear Precision คือตั้งความละเอียดหรือจำนวนทศนิยมให้กับหน่วยที่เป็นเชิงเส้นทั้งหลายเช่นระยะทาง ความสูง พื้นที่หรือแม้กระทั่งจำนวนทศนิยมค่าพิกัดของระบบพิกัดฉาก และจำนวนทศนิยมของสเกลแฟคเตอร์

python_2017-07-15_10-47-21

ลองคำนวณการแปลงพิกัดด้วยทูลส์ Transform Coordinates ตรวจสอบจำนวนทศนิยม

python_2017-07-15_10-45-24

Angular Precision

สำหรับ Angular Precision ตั้งความละเอียดหรือจำนวนทศนิยมของมุมทั้งหลายเช่นค่าพิกัดในระบบภูมิศาสตร์หรือมุม convergence ดังรูปด้านล่าง

python_2017-07-15_11-19-19

การใช้งานลองดู UTM-Geo Converter 

python_2017-07-15_11-22-39

python_2017-07-15_11-25-01

ตั้งค่าสำหรับ Google Maps

ตั้งค่าสำหรับปักหมุดบน Google Maps ได้แก่รูปแบบของหมุด สี ดังรูปด้านล่าง

python_2017-07-15_11-27-06

ตัวอย่างการใช้งานโดยใช้ทูลส์ Geodesic Distance 

python_2017-07-15_11-36-42

ลองปักหมุดจะเห็นรูปแบบหมุด สี และสีของเส้นที่เราตั้งค่าไว้ดังรูปด้านล่าง

firefox_2017-07-15_11-36-59

ตัวอย่างการใช้งานคำนวณหาพื้นที่ Compute Area 

python_2017-07-15_14-26-57

ปักหมุดพื้นที่ลงไป

firefox_2017-07-15_14-32-18

บางสถานการณ์ไม่ต้องการรูปหมุด ต้องการแค่วงรอบพื้นที่ ตั้งคาใหม่ด้วยการไม่ติ๊กที่ Draw Pin ดังรูปด้านลาง

python_2017-07-15_14-33-21

จะได้ผลลัพธ์บน Google Maps ดังนี้

firefox_2017-07-15_14-36-03

การตั้งค่าสำหรับ Google Earth

คล้ายๆ ตั้งค่าให้ Google Maps ที่ผ่านมา ดูรูปด้านล่าง

python_2017-07-15_14-40-42

ตัวอย่างการใช้งานขอใช้ทูลส์ Line Scale Factor 

python_2017-07-15_14-43-03

ปักหมุดลง Google Earth ป้อนชื่อไฟล์ก่อนแล้วจะสวิชท์เข้า Google Earth

googleearth_2017-07-15_14-47-59

การตั้งค่า (settings) จะเก็บไว้ที่ไฟล์ settings.xml ไฟล์นี้อยู่ที่โฟลเดอร์ “Appdata” ถ้าเป็นเครื่องผมเมื่อเปิดด้วยไอคอน “Example Data”

explorer_2017-07-15_14-52-19

ความจริงในงานสำรวจต้องการค่าพิกัดฉากที่ทศนิยมหลักที่ 3 ซึ่งเป็นหลักมิลลิเมตรและเพียงพอ ส่วนค่าพิกัดภูมิศาสตร์เช่นแล็ตติจูดและลองจิจูดจะละเอียดเทียบเท่าระดับมิลลิเมตร ต้องการทศนิยมมากกว่าตำแหน่งที่ 5 ขึ้นไปเช่น 23°48’23.78768″N อย่างไรก็ตาม Surveyor Pocket Tools เปิดโอกาสให้ตั้งได้ตามความต้องการของผู้ใช้ พบใหม่ในตอนต่อไปครับ

สนุกกับโปรแกรมเครื่องคิดเลขสำหรับงานสำรวจ ตอนที่ 1 โปรแกรมแปลงพิกัด “Geo2UTM” บนเครื่องคิดเลข Casio FX 5800P

  • สวัสดีครับท่านผู้อ่านทุกท่าน ในโอกาสที่บ.เคเอ็นเอส เอ็นจิเนียริ่ง คอร์ปอเรชั่น จำกัด มีอายุใกล้จะขวบปีแล้ว ผมในฐานะวัยแล้วเกือบจะรุ่นพ่อของน้องๆชุดนี้แล้ว เห็นความตั้งใจของน้องๆ และก็ยินดีเป็นอย่างยิ่ง ในโอกาสนี้ด้วยความที่สนิทสนมกันก็ถูกลากมาให้เขียนบทความให้เพื่อฉลองครบรอบหนึ่งปี และลงที่นี่ ไม่ใช่ที่บล็อก priabroy.com ที่ประจำ ตอนแรกคิดๆอยู่ว่าจะเขียนเรื่องอะไรดี แต่สุดท้ายก็จะเขียนเรื่องโปรแกรมมิ่งเครื่องคิดเลข เพราะว่าเครื่องคิดเลขเป็นเครื่องมือพื้นฐานที่สุดสำหรับช่างสำรวจ ช่างโยธาของเรา
  • บทความนี้คงจะมีหลายตอนก็มาติดตามกัน

ย้อนอดึตแห่งความทรงจำ

  • เครื่องคิดเลข Casio นับว่าเป็นขวัญใจของเราช่างสำรวจ ช่างโยธา ตอนเรียนอยู่วิทยาลัยหรือมหาวิทยาลัย บางคนอาจจะมีประสบการณ์ในการพาเครื่องไปฝากไว้ที่โรงจำนำเพราะกลัวทำหาย ^-^ ตอนสมัยเรียนช่วงปี 30-33 ผมใช้ FX3800 ปัจจุบันเลิกผลิตไปนานแล้ว เหลือไว้แต่ความทรงจำ สมัยแต่ก่อนเขียนโปรแกรมลงเครื่องพวกนี้คงได้แค่โปรแกรมเล็กๆ เพราะเครื่องมื memory ที่จำกัดจำเขี่ยมากๆ
Casio Fx3800
Casio Fx3800
  • ต่อมาทำงานได้สักสองปีก็ไปถอยเอาเครื่องในตำนาน  FX-880P ได้มาเครื่องหนึ่ง รุ่นนี้ปัจจุบันขึ้นหิ้งเป็นตำนานไปแล้ว ทั้งที่ผ่านระยะเวลามายี่สิบกว่าปี ยังมีคนตามล่าหากัน รุ่นนี้เวลาพกพาก็เสียบไว้ที่กระเป๋าหลังของกางเกงยีนส์ ดูมันเท่ห์ แต่พกแบบนี้ บางคนเผลอนั่งทับจนเครื่องคิดเลขหักเป็นท่อน น้ำตาตกกันมาแล้วก็มี โปรแกรมมิ่งรุ่นนี้ใช้โปรแกรมภาษาเบสิคแบบมีหมายเลขบรรทัดกำกับ เปิดโลกโปรแกรมมิ่งไปอีกหลายระดับ เขียนโปรแกรมยากๆได้พอสมควร การตั้งตัวแปรใช้ตัวอักษรหลายตัวได้ เครื่องเดิมๆ มี memory 32 กิโลไบต์ มันเยอะพอสมควร
Casio FX - 880P
Casio FX – 880P
  • ด้านหลังเครื่องรุ่นนี้ยังมีช่องให้ใส่แรมเพิ่มได้อีก 32 กิโลไบต์ ผมอุตส่าห์ไปเดินแถวสะพานเหล็ก คลองถมจนได้มาหนึ่งอัน เมื่อใส่แล้วก็รวมกันได้ 64 กิโลไบต์ คิดเป็น 65536 ไบต์ เหลือเฟือ ขนาดเขียนวงรอบเล่นๆ ยังไม่เต็มเลย
s-l300
Memory Pack 32KB for FX-880P
  • มาดูโปรแกรมสำหรับเครื่องคิดเลข FX-880P เพื่อรำลึกความหลัง ผมเขียนโปรแกรมนี้หาจุดตัดระหว่างเส้นตรงกับเส้นตรง เส้นตรงกับวงกลม วงกลมกับวงกลม ผมลงมาให้เต็มๆแบบไม่มีตัดทอน ตอนนี้ผมไม่มีเครื่องคิดเลขรุ่นนี้ให้ลองแล้ว

5 ‘Intersection
10 CLS:BEEP:BEEP1:ANGLE 0:SET F5:PRINT CHR$(9);:CLS
20 PRINT ” ***Find Intersection point***”
30 CLS:PRINT “<<1:AZI#AZI>> <<2:AZI#DIST>>”;:PRINT
40 PRINT”<<3:DIST#DIST>> <>”;
50 T$=INKEY$
60 IF (T$=”1″) THEN 150
70 IF (T$=”2″) THEN 600
80 IF (T$=”3″) THEN 800
90 IF (T$=”4″) THEN 400
100 IF (T$=”Q”) THEN PRINT TAB(10);”<<>>”;
:SET F9:END
110 GOTO 50
150 CLS:PRINT “N1=”;N1;:INPUT N1
160 PRINT “E1=”;E1;:INPUT E1
170 PRINT “AZIMUTH1=”;AZI1;:INPUT AZI1
180 FANG=AZI1:GOSUB 3000:DAZI1=DANG
190 PRINT “N2=”;N2;:INPUT N2
200 PRINT “E2=”;E2;:INPUT E2
210 PRINT “AZIMUTH2=”;AZI2;:INPUT AZI2
220 FANG=AZI2:GOSUB 3000:DAZI2=DANG
230 DELTY=N2-N1:DELTX=E2-E1
240 GOSUB 3500:AZI12=Y:DIST12=X
250 NI=(TAN(DAZI2)*N2-TAN(DAZI1)*N1+E1-E2)/(TAN(DAZI2)-
TAN(DAZI1))
260 EI=(NI-N1)*TAN(DAZI1)+E1
290 PRINT “NI= “;NI;:PRINT
300 PRINT “EI= “;EI
310 GOTO 30
400 ‘Four points
410 CLS:PRINT “N1= “;N1;:INPUT N1
420 PRINT “E1= “;E1;:INPUT E1
430 PRINT “N2= “;N2;:INPUT N2
440 PRINT “E2= “;E2;:INPUT E2
450 PRINT “N3= “;N3;:INPUT N3
460 PRINT “E3= “;E3;:INPUT E3
470 PRINT “N4= “;N4;:INPUT N4
480 PRINT “E4= “;E4;:INPUT E4
490 DELTY=N2-N1:DELTX=E2-E1:GOSUB 3500:AZI12=Y:DIST12=X
500 DELTY=N4-N3:DELTX=E4-E3:GOSUB 3500:AZI34=Y:DIST34=X
510 DELTY=N3-N1:DELTX=E3-E1:GOSUB 3500:AZI=Y
512 IF (AZI12=90 OR AZI12=270) THEN NI=N1 ELSE 515
513 EI=(NI-N3)*TAN(AZI34)+E3
515 IF (AZI34=90 OR AZI34=270) THEN NI=N3 ELSE 520
520 IF NOT((AZI12=90 OR AZI12=270) OR (AZI34=90 OR
AZI34=270)) THEN NI=(TAN(AZI34)*N3-TAN(AZI12)*N1+E1-
E3)/(TAN(AZI34)-TAN(AZI12)) ELSE 540
530 EI=(NI-N1)*TAN(AZI12)+E1
540 PRINT “NI= “;NI;:PRINT
550 PRINT “EI= “;EI
560 GOTO 30
600 ‘AZI # DIST
610 CLS:PRINT “N1= “;N1;:INPUT N1
620 PRINT “E1= “;E1;:INPUT E1
630 PRINT “AZIMUTH= “;INAZI;:INPUT INAZI:FANG=INAZI:GOSUB
3000:DAZI1=DANG
635 CANG=0:PAZI=DAZI1:GOSUB 4500:DAZI2=NAZI
640 PRINT “N2= “;N2;:INPUT N2
645 PRINT “E2= “;E2;:INPUT E2
650 PRINT “DIST= “;DIST;:INPUT DIST
660 DELTY=N2-N1:DELTX=E2-E1:GOSUB 3500:DIST12=X:AZI12=Y
670 PHI1=AZI12-DAZI1:IF PHI1 675 PHI2=AZI12-DAZI2:IF PHI2 Int No.1
900 CANG=ANG2:PAZI=AZI12:GOSUB 4500:AZI2I2=NAZI
910 PNI=N2+DIST2*COS(AZI2I1)
920 PEI=E2+DIST2*SIN(AZI2I1)
930 MNI=N2+DIST2*COS(AZI2I2)
940 MEI=E2+DIST2*SIN(AZI2I2)
950 CLS:BEEP:PRINT “NI(1)= “;PNI;:PRINT
960 PRINT “EI(1)= “;PEI
970 CLS:PRINT “NI(2)= “;MNI;:PRINT
980 PRINT “EI(2)= “;MEI
990 GOTO 30
3000 ‘Convert input angle to degree
3010 DD=FIX(FANG)
3020 TEMP=FRAC(FANG)*100
3030 MM=FIX(TEMP)
3040 SS=FRAC(TEMP)*100
3050 DANG=DEG(DD,MM,SS)
3060 RETURN
3500 ‘Find Azimuth
3510 X=POL(DELTY,DELTX)
3520 IF Y180 THEN 4550 ELSE 4530
4530 TEMP=TEMP+180
4540 GOTO 4590
4550 IF TEMP>540 THEN 4580
4560 TEMP=TEMP-180
4570 GOTO 4590
4580 TEMP=TEMP-540
4590 NAZI=TEMP
4600 RETURN

ถึงยามต้องพรากจากกัน

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

สิบปีที่เครื่องคิดเลขห่างหาย

  • ตั้งแต่นั้นเป็นต้นมาผมก็ไม่เคยซื้อเครื่องคิดเลขอีกเลยประมาณสิบปี เพราะว่าเป็นยุคเวลาของโน๊ตบุ๊ค เอะอะจะคำนวณอะไรๆก็ต้องที่โน๊ตบุ๊ค แต่ชีวิตเหมือนขาดอะไรไป จนมาถึงยุคมือถือจอสัมผัสยิ่งแล้วไปกันใหญ่ เพราะมีโปรแกรมจำลองเครื่องคิดเลขมาให้ใช้งาน วันนั้นไปเดินห้างเห็นเครื่องคิดเลข FX 5800P วางอยู่ที่ชั้นขายของ ป้ายบอกลดราคาเหลือ 1990 บาท จากราคาเดิม 2890 บาทคิดอยู่ในใจว่ามันลดราคากระหน่ำแท้ๆ ราคานี้ไม่รวมสายลิ๊งค์ ดูสเป็คบอกว่าเขียนโปรแกรมได้ มีเม็มโมรีมาให้ 28500 ไบต์ ผมนึกถึง Fx-880P ทันที ตัดสินใจซื้อมาลอง ที่ไหนได้มาถึงบ้านเปิดดูในเน็ตเห็นราคาขายออนไลน์ราคา 1790 บาทได้สายลิ๊งค์ด้วย มันถูกว่ากว่าที่ผมซื้อหลายร้อยบาท เอาละวะ ภูมิใจที่ได้ใช้ของแพงกว่า
Casio FX5800P
Casio FX5800P

FX 5800P กับอารมณ์กระชากใจเหมือนกลับไปเรียน (Back to old school) อีกครั้ง

  • เอาละครับ ได้ใช้เครื่องคิดเลขจริงๆสักที คือหลายสิบปีที่ผ่านมาเหมือนรออะไรบางอย่าง แต่ไม่รู้ว่ามันคืออะไร เจออีกทีใช่เลย เวลากดคีย์เครื่องคิดเลขมันมีการตอบสนองได้อารมณ์เหมือนได้กลับไปใช้และเรียนมหาวิทยาลัยอีกครั้ง ผมเริ่มอ่านคู่มือเพื่อจะเขียนโปรแกรม ใช้เวลาไม่มากนักเพราะคุ้นๆอยู่ คู่มืออะไรก็หาง่ายในยุคนี้ ดาวน์โหลดมาอ่านได้สบายๆ
  • ช่วงเริ่มต้นกับเครื่องคิดเลข ผมเริ่มโปรแกรมง่ายๆก่อนเช่นจำพวก Cogo เช่นหามุม ระยะทางเมื่อกำหนดค่าพิกัดของจุดสามจุด ความรู้สึกแรกคือชอบเครื่องคิดเลขรุ่นนี้พอสมควร
  • เคยคิดเล่นๆว่าเครื่องคิดเลขพวกนี้ ยกเว้น FX-880P ที่เทพไปแล้ว จะสามารถเขียนโปรแกรมระดับ Advance ได้ไหมเช่นแปลงพิกัดไปมาระหว่าง UTM และ ค่าพิกัดภุมิศาตร์ (Geographic) หาระยะทางระหว่างสองจุดบน Ellipsoid หรือหาระยะทาง Geodesic distance

ข้อจำกัดด้านโปรแกรมมิ่งแต่ฟ้าปิดกั้นดินไม่ได้

  • จั่วหัวให้เว่อร์ซะยังงั้น ปัญหาจริงๆที่คนจะเขียนโปรแกรมพวกนี้คืออย่างแรกคือสูตร ไม่รู้จะใช้เวอร์ชั่นไหนดี อย่างที่สองคือเครื่องคิดเลขรุ่นพวกนี้มีตัวแปรจำกัด จาก A ถึง Z นับได้ 26 ตัว น้อยซะจริงๆ ถ้าสูตรมีการใช้ตัวแปรมากกว่านี้จะทำอย่างไร ปัญหานี้ยังมีทางออก เครื่องคิดเลขรุ่นนี้เตรียมตัวแปรอนุกรมให้คือ Z เราสามารถใช้งาน Z[1],Z[2],Z[3],…. ได้มากเท่าที่เมมโมรีเครื่องคิดเลขยังเหลือพอ
  • ข้อจำกัดอีกอย่างคือไม่สามารถนำไฟล์ข้อมูลให้โปรแกรมได้ ดังนั้นโปรแกรมเครื่องคิดเลขจึงจะต้องไม่ซับซ้อนมาก ถ้ามากกว่านี้ต้องใช้เครื่องคอมพิวเตอร์จะดีที่สุด

โปรแกรมแปลงพิกัดจากค่าพิกัดภูมิศาสตร์ ไปยัง ค่าพิกัดระบบพิกัดฉาก UTM

  • ก่อนจะไปต่อเรื่องโปรแกรมมิ่ง มาเรียกน้ำย่อยกันก่อน มาดูรูปการคำนวนกันก่อน ต้องการแปลงค่าพิกัด lat=14°27’44.71″ long=100°58’27.02″ ไปยังค่าพิกัด UTM

input_geo

  • แปลงพิกัดเป็น UTM ได้ค่า N=1599784.382 E=712796.211

output_utm1

  • และคำนวน zone  ของ UTM มาให้ด้วยจุดพิกัดนี้อยู่ในโซน 47

output_utm2

แปลงพิกัดบน WGS84

  • สำหรับโปรแกรมจะแปลงพิกัดบนพื้นฐาน WGS84 เท่านั้น ไม่มีการแปลงข้ามพื้นหลักฐานเพราะมันจะซับซ้อนยุ่งยากเกินกว่าเครื่องคิดเลข เมมโมรีน้อยๆจะทำได้

โปรแกรมคำนวณ

  • ข้อจำกัดอีกอย่างของเครื่องคิดเลขรุ่นนี้คือสามาถใช้สายลิ๊งค์โอนโปรแกรมจากเครื่องไปหาเครื่องอื่นเท่านั้น แต่ถ้าโอนโปรแกรมเข้าเครื่องคอมพิวเตอร์ผมพยายามหาในเน็ตตามฟอรั่ม มีคนพยายามจะแกะโปรโตคอลแต่ไม่น่าจะสำเร็จ ที่นี้จะเอาโปรแกรมมาแสดงบนคอมพิวเตอร์ได้ยังไง ก็ต้องนั่งแกะโปรแกรมทีละเม็ด เขียนด้วยเวิร์ดเพราะต้องการสัญลักษณ์ให้ตรงกับที่แสดงในเครื่องคิดเลขมากที่สุด สัญลักษณ์ส่วนใหญ่หาได้ในฟอนต์ Symbol, Wingdings
  • เมื่อแกะเสร็จแล้วก็ดูทวนอีกทีว่าพิมพ์ได้ตรงกับในเครื่องคิดเลขไหม ไม่มีอะไรตกหล่นก็ export มาเป็นภาพเอามาแปะ ผมพยายามแยกสีให้ดูง่ายตรงไหนเป็นฟังก์ชันใช้สีแดงเข้ม ตัวแปรสีน้ำเงิน เงื่อนไขโปรแกรมใช้สีเขียว ลองดูครับ
Geo2UTM
Geo2UTM
Geo2UTM(continued)
Geo2UTM(continued)
  • สำหรับเครื่องคิดเลขแล้ว ก็ไม่ถือว่าโปรแกรมใหญ่มากนัก แต่สังเกตดูตัวแปร ตั้งแต่ตัว A ถึงตัว Z ใช้แทบหมด ผมคงไม่อธิบายตัวโปรแกรมนะครบ จะยืดเยื้อ สำหรับคนที่เคยเขียนโปรแกรมเครื่องคิดเลขคาสิโอ้รุ่นเหล่านี้ มองแป๊ปเดียวก็โอเคแล้ว

วิธีใช้งาน

  • วิธีใช้งานก็ง่ายครับตามสไตล์เครื่องคิดเลข กดเรียกโปรแกรมก่อน Shift+Prog ที่เครื่องคิดเลขผมเลือก “GEO2UTM

20170108_122613

ตัวอย่างที่ 1

  • โปรแกรมจะถามค่าพิกัดแลตติจูดและค่าลองจิจูด ตัวอย่างการใช้นี้กำหนดให้ latitude = 26°12’3.6128″N longitude = 50°36’25.1928″E ที่เครื่องคิดเลขกดคีย์ “EXE” โปรแกรมจะถามค่าแลตติจูด ป้อนไปให้ทศนิยมครบ ตามรูปแรก  (เครื่องคิดเลขเวลาแสดงค่าพิกัดที่เราป้อนไปแล้ว จะแสดงแค่ทศนิยมสองตำแหน่ง) และป้อนค่าลองจิจูดให้ตามรูปถัดไป

20170108_123456

20170108_123609

ผลลัพธ์การคำนวณ

  • โปรแกรมจะคำนวณค่าพิกัดฉาก UTM ให้พร้อมบอกหมายเลขโซนของยูทีเอ็มมาด้วยและบอกว่าเป็นโซนด้านเหนือหรือด้านใต้ของเส้นศูนย์สูตร ในที่นี้จุดค่าพิกัดนี้แถวประเทศบาเรนห์ โซน 39N (เหนือ)

20170108_130154 20170108_130205

  • เทียบกับค่าที่คำนวณด้วยโปรแกรม Surveyor Pocket Tools ตรงกันครับ หมายเหตุนิดหนึ่งว่าการคำนวณการแปลงพิกัดในโปรแกรม  Surveyor Pocket Tools ใช้ไลบรารีของ Proj4 ผ่านทาง pyproj
Surveyor Pocket Tools
Surveyor Pocket Tools

ตัวอย่างที่ 2

  • มาลองดูกัน ถ้าผู้อ่านเกิดจับพลัดจับผลูไปทำงานต่างประเทศที่อยู่ใต้เส้นศูนย์สูตร ก็ยังสามารถใช้ได้ กำหนด แลตติจูด = 16d9’7.048″S ลองจิจูด = 33d33’49.779″E ค่าพิกัดอยู่ที่ประเทศโมซัมบิค ทวีปอาฟริกา
  • ป้อนค่าพิกัดเข้าดังนี้ ค่าแลตติจูดอยู่ใต้เส้นศูนย์สูตรให้ติดเครื่องหมายลบข้างหน้า ส่วนค่าพิกัดลองจิจูดก็ป้อนปกติ

20170108_132745
20170108_132903

  • ผลลัพธ์การแปลงพิกัดได้ดังนี้ จุดอยู่ที่โซน 36S ใต้เส้นศูนย์สูตร

20170108_132916
20170108_132923

  • เปรียบเทียบผลการคำนวณกับ Surveyor Pocket Tools ตรงกัน

surveyor-pocket-tools_2017-01-08_13-31-09

  • ก็พอหอมปากหอมคอครับ ตอนหน้ามาดูโปรแกรมแปลงค่าพิกัดฉากยูทีเอ็ม (UTM) ไปยังค่าพิกัดภูมิศาสตร์บ้าง ติดตามกันตอนต่อไปครับ

สนุกกับโปรแกรมเครื่องคิดเลขสำหรับงานสำรวจ ตอนที่ 2 โปรแกรมแปลงพิกัด “UTM2Geo” บนเครื่องคิดเลข Casio FX 5800P

โปรแกรม “UTM2Geo” สำหรับเครื่องคิดเลข Casio FX 5800P

  • สวัสดีครับผู้อ่านทุกท่าน พบกันตอนนี้เป็นตอนที่ 2 แล้วครับ ตอนแรกนำเสนอโปรแกรม “Geo2UTM” แปลงพิกัดจากคาพิกัดภูมิศาสตร์ (แลตติจูด/ลองจิจูด) ไปเป็นค่าพิกัดบนระบบพิกัดฉาก UTM มาตอนนี้กลับกันครับ เราจะมาเขียนโปรแกรมที่แปลงพิกัดจากระบบพิกัดฉากยูทีเอ็มไปเป็นค่าพิกัดภูมิศาสตร์
  • มาเขียนบทความที่นี่ให้กับ kns-engineering เป็นการเฉพาะกิจ สำหรับเพื่อนพี่น้องชาวสำรวจแวะไปเยี่ยมเยียนผมได้ที่บล็อกประจำ priabroy.name

เกร็ดความรู้เล็กน้อยสำหรับช่างสำรวจ

  • ก่อนจะเข้าไปว่าเรื่องโปรแกรมมิ่งบนเครื่องคิดเลข ขอซักซ้อมความรู้เซอร์เวย์สักเล็กน้อย มีสองอย่างคือมุม อะซิมัท (Azimuth) และฟังก์ชัน Atan2 สองอย่างนี้เกี่ยวพันกับเรื่องโปรแกรมในด้านการสำรวจเสียส่วนใหญ่ (แต่งานคำนวณแปลงพิกัดนี้ไม่ได้ใช้)
  • มุมอะซิมัทเป็นมุมที่แสดงทิศทางในด้านงานสำรวจของเรา เป็นมุมที่กวาดจากทิศเหนือตามเข็มนาฬิกา สมมติว่ามีจุด A มีค่าพิกัด (x,y,) = (500,500) และจุด B มีค่าพิกัด (x,y) = (586.603,550) คำถามพื้นฐานก็คือ
    • อะซิมัทจากจุด A ไปจุด B เท่าไหร่
    • อะซิมัทจากจุด B ไปจุด A เท่าไหร
    • ระยะทางหรือระยะราบเท่าไหร่ระหว่างจุดทั้งสอง

  • ระยะราบหาได้จากสูตรตรีโกณมิติสามัญพื้นฐาน ระยะราบ = √((500-586.603)² + (500-550)²) = 100 เมตร
  • แล้วอะซิมัทละคำนวณอย่างไร การคำนวณจะมาเกี่ยวพันกับเครื่องคิดเลขของเราอย่างแนบแน่น ฟังก์ชัน Atan2 เรียกกันบนเครื่องคอมพิวเตอร์ (ใครเขียนโปรแกรมบนเครื่องคอมพิวเตอร์จะรู้จักฟังก์ชั่นนี้ดี มีทุกภาษา) เที่ยบเท่าบนเครื่องคิดเลขก็คือ Pol() คือฟังก์ชั่นการที่จะมาช่วยย่นการคำนวณนี้
  • Atan2 ดั้งเดิมจะคำนวณหามุมที่กวาดจากแกน X ทวนเข็มนาฬิกา ดังนั้นในคู่มือคาสิโอ จะเขียนฟังก์ชั่น Pol() แบบนี้ครับ ในวงเล็บ x มาก่อน  y สังเกตว่ามุม θ กวาดจากแกน X

  • แต่สำหรับมุมที่ต้องการสำหรับงานสำรวจคือมุมอะซิมัท(Azimuth) คือมุมคือกวาดจากแกน Y ลงมาตามเข็มนาฬิกา จะทำอย่างไร
  • เทคนิคเวลาใช้งานสลับเอาไว้ค่า Y มาก่อนและ X ตามหลังครับ >> Pol(y,x) จะได้มุมอะซิมัท ผมเขียนรูปใหม่ดังนี้

  • ถ้ามีเครื่องคิดเลขก็ลองกดดูเลย Pol((550-500),(586.603-500))  ผลการคำนวณเครื่องคิดเลขเอาค่าระยะทางไปเก็บไว้ในตัว “I” และมุมอะซิมัทไว้ในตัว “J” ลอง RCL (recall) มาดูจะได้ I = 100.000 และ J = 60.000 มุม 60 ก็คืออะซิมัทจาก A ไป B นั่นเอง
  • จากโจทย์ข้างบนก็ตอบได้นะครับ อะซิมัทย้อน (Backward) ก็ให้เอาอะซิมัทไป (Forward) ± 180 ถ้ามากกว่า 180 ให้เอา 180 ไปลบ ถ้าน้อยกว่า 180 ให้เอา 180 ไปบวก ดังนั้นอะซิมัทจาก B มา A จะได้ = 60 + 180 = 240 
  • มุมอะซิมัทเป็นมุมมหัศจรรย์สำหรับช่างสำรวจ มหัศจรรย์ยังไงตอนหน้ามาว่ากันต่อ ตอนนี้ไปต่อเรื่องโปรแกรมกัน

โค๊ดโปรแกรม UTM2Geo

  • เหมือนเดิมนั่งแกะโปรแกรมทีละเม็ดแล้วเขียนลงในเวิร์ด แล้วตรวจทานสอง สามเที่ยว น้ำตาไหลพราก  ผมก็คิดเหมือนกันว่าผู้อ่านไปคีย์โปรแกรมตามที่ผมลิสต์ออกมาคงไม่ใช่เรื่องสนุก เอาอย่างนี้ ถ้าไม่อยากคีย์โปรแกรมเอง ติดต่อน้องๆที่ท้ายบล็อกผมจะลงชื่อไว้เพื่อขอดูดโปรแกรม มีข้อแม้เล็กๆน้อยๆว่าเครื่องต้องเป็นเครื่อง Casio Fx-5800P รุ่นเดียวกันและพกสายลิ๊งค์มาด้วยตัวเอง

ต้วอย่างที่ 1

  • สำหรับท่านที่ต้องการป้อนโปรแกรมลงในเครื่องคิดเลข วิธีป้อนโปรแกรมเลงเครื่องคิดเลขคงไม่ต้องสาธยายนะครับเพราะคงคุ้นเคยกันอยู่ มาดูการใช้งาน สมมติว่ามีหมุดที่มีค่าพิกัดฉาก UTM ดังนี้
    • Northing = 1,615,517.540 Easting = 395,698.272 Zone 47N อยู่บนพื้นหลักฐาน WGS84
  • เรียกโปรแกรม ด้วยการกด “Prog” แล้วเลือกโปรแกรม “UTM2Geo

  • ป้อนค่าพิกัด Norhting, Easting

  • ต่อไปโปรแกรมจะถาม Zone No ลำดับโซนหมายเลขที่เท่าไหร่ ถ้าโซนอยู่ด้านเหนือเส้นศูนย์สูตรให้ป้อนค่าบวกเช่น 47N ก็ป้อนตัวเลขธรรมดาไปครับ แต่ถ้าอยู่ใต้เส้นศูนย์สูตรป้อนเป็นตัวเลขลบ ในที่นี้ป้อนตัวเลข 47 เข้าไปดังรูป

ผลลัพธ์ของโปรแกรม

  • มาดูผลลัพธ์กันครับ

  • ผมเอาค่าพิกัดฉากนี้ไปป้อนในโปรแกรมแปลงพิกัด UTM-Geo Converter ที่อยู่ใน Surveyor Pocket Tools ก็ได้ตรงกันครับ แต่ทศนิยมในเครื่องคิดเลขที่ฟิลิปดาได้เต็มที่สองตำแหน่ง (น่าเสียดาย ทศนิยมของฟิลิปดาตำแหน่งที่สอง เทียบเป็นหน่วยเมตริกแล้วได้แค่ระดับหลักสิบเซนติเมตร) ถ้าต้องการหลักมิลลิเมตรก็ต้องบนคอมพิวเตอร์แล้วครับ แต่สำหรับเครื่องคิดเลขคิดมาได้ขนาดนี้ผมก็โอเคแล้วครับ

 ตัวอย่างที่ 2

  • สมมติว่าผู้อ่านมีโอกาสไปทำงานต่างประเทศไกลๆ มาลองค่าพิกัดที่อยู่โซนตะวันตกและอยู่ใต้เส้นศุนย์สูตรกันดูครับ สมมติว่าค่าพิกัดนี้อยู่ในบราซิลครับ ชีวิตจริงไม่เคยไปถึงทวีปอเมริกาครับ ไกลสุดแค่ยุโรปกับทวีปอาฟริกา
    •  Northing = 7,721,526.876 Easting = 505,464.207 Zone 24S บนพื้นหลักฐาน WGS84
  • พร้อมแล้วเรียกโปรแกรมป้อนตัวเลขกันเลย

 

  •  ต่อไปหมายเลขโซนให้ป้อนตัวเลขเป็นลบ -24 เพราะอยู่ใต้เส้นศูนย์สูตร

  • มาดูผลลัพธ์กัน จะได้ค่า Latitude = -20°36’19.28″ ค่าเป็นลบแสดงว่าอยู่ใต้เส้นศูนย์สูตร ได้ค่า Longitude = -38°56’51.220″ ได้ค่าเป็นลบแสดงว่าอยู่ทางด้านตะวันตกของตำบลกรีนนิช ของอังกฤษ (ตำบลกรีนนิชค่าลองจิจูด = 0)

  • เปรียบเทียบกับ Surveyor Pocket Tools เท่ากัน

  • ทรรศนะส่วนตัวผมมีโปรแกรมแปลงพิกัดติดเครื่องคิดเลขรุ่นนี้ FX – 5800P ทำให้เครื่องคิดเลขดูเทพขึ้นมาทันตาเห็น 🙂 สองโปรแกรมนี้กินเม็มไปจิ๊บๆครับ
  • ตอนหน้ามาว่าเรื่องยากขึ้นไปอีกนิด การคำนวณระยะทางที่สั้นที่สุดบนทรงรี (Geodesic distance) สูตรลากกันยาวเฟื้อย ตัวแปรบนเครื่องคิดเลขใช้หมดเกลี้ยงต้องไปดึงตัวอนุกรมมาช่วยด้วย ถ้าพิมพ์โปรแกรมตามผมต้องร้องว่า เจ็บกว่านี้มีอีกไหม
  • ตอนหน้ามาว่ากัน แต่ผมบอกก่อนว่าในฐานะช่างสำรวจ เรื่อง  geodesic distance บางครั้งเราใช้มันอย่างไม่รู้ตัว และไม่ใช่เรื่องไกลตัว ขอฝากน้องๆไว้ครับ มีความรู้ก็ใส่ตัวก็ใช่ว่าจะต้องไปเหนื่อยแบกหามตามโบราณที่ว่าไว้

ติดต่อขอลิ๊งค์โปรแกรม

  • ติดต่อคุณนันทวุฒิ อึ้งตระกูลได้ครับสำหรับลิ๊งค์ดูดโปรแกรม ผมจะฝากโปรแกรมไว้ที่นั่น แต่ถ้าใจไม่ร้อน รอบทความอีกสักสอง สามตอนจบก่อนก็ดี (เพราะบางโปรแกรมที่จะนำเสนอยังไม่ได้เขียนลงเครื่องคิดเลข)
  • สังคมจะน่าอยู่ ถ้าแบ่งปันกันและเอื้อเฟื้อเผื่อแผ่สำหรับคนที่มีโอกาสน้อยกว่า พบกันตอนใหม่ครับ

สนุกกับโปรแกรมเครื่องคิดเลขสำหรับงานสำรวจ ตอนที่ 3 โปรแกรมคำนวณระยะทางบนทรงรี สำหรับเครื่องคิดเลข Casio FX 5800P

โปรแกรมคำนวณระยะทางที่สั้นที่สุดบนทรงรี (Geodesic Distance)

  •  สวัสดีครับผู้อ่านกลับมาพบกันอีกครั้ง ครั้งนี้หอบเอาสูตรยาวๆมาฝากกัน เรื่องระยะทางบนทรงรีความจริงจัดอยู่ในหมวด  Geodesy ที่ถือว่าเป็นเรื่องยากสำหรับนักศึกษาสมัยก่อน เพราะคำนวณทีต้องเปิดตารางล็อก สมัยนี้ถ้าทำความเข้าใจก็ไม่ได้ยากแล้วครับ มีตัวช่วยมากมายเช่นเครื่องคอมพิวเตอร์
  • ระยะทางที่สั้นที่สุดบนทรงรีเรียกว่า Geodesic Distance ถือว่าเป็นะระยะทางที่สั้นที่สุดบนทรงรี จัดเป็นสูตรที่มีมานานนมแล้ว นักคณิตศาสตร์สมัยก่อนคิดค้นขึ้นมาเพื่อช่วยในการคำนวณระยะทางที่สั้นที่สุดเช่นสำหรับการเดินเรือเดินข้ามมหาสมุทร เพราะแผนที่ที่เราใช้กัน ถ้าเมืองท่าที่ต้องการเดินทางนั้นอยู่กันไกลหลายพันไมล์ทะเล จะเอาดินสอมาขีดตรงๆเชื่อมกันบนแผนที่เดินเรือ จะได้ระยะทางที่ไม่ใช่ใกล้ที่สุด จะกลายอ้อมไป
  • เคยสังเกตเวลานั่งเครื่องบินไปต่างประเทศไกลๆไหมครับ จะมีรูปเส้นทางบินบนจอมอนิเตอร์ให้ดู จะเห็นเส้นทางบินเป็นเส้นโค้งๆ ไม่ใช่ตรงๆ นั่นเป็นเพราะเส้นโครงแผนที่ ความจริงแนวเครื่องบินก็ไม่ได้เฉไปไหน แต่เมื่อเอาแนวบินมาวาดบนแผนที่ที่ใช้เส้นโครงแผนที่แบบรักษารูปร่าง(Conformal) ที่เราใช้กันในปัจจุบันเช่น UTM เส้นทางบินจะกลายเป็นเส้นโค้งไป

  • ก่อนจะไปต่อ ผมอยากจะพูดถึงระยะทางอีกระยะทางหนึ่งบนทรงกลม เรียกว่า Great Circle Distance คือระยะทางที่สั้นที่สุดบนทรงกลม คนโบราณนำระยะทางนี้มาคำนวณเส้นทางการเดินเรือ เส้นทางการบิน แต่เนื่องจากโลกเราไมได้กลมแต่ทรงคล้ายออกมาทางทรงรีมากกว่า Geodesic distance เลยแม่นกว่า Great circle distance แต่คนก็ยังนิยมใช้ Greate circle distance มากกว่าอยู่ดีถึงจะคลาดเคลื่อนจาก Geodesice distance ไปประมาณ 0.1% แต่ก็คำนวณง่ายกว่า ในสูตรไม่มีการวนลูป เรียกสูตรการคำนวณบนทรงกลมนี้ว่า “Haversine
  • ถ้าสนใจเรื่อง Geodesic distance และ Greate circle distance ในรายละเอียดและโปรแกรมคำนวณบนคอมพิวเตอร์ติดตามได้ในโปรแกรม Survey Pocket Tools ของผมได้ที่บล็อก priabroy.name

การประยุกต์ใช้งาน

  • บางคนอาจจะว่าไกลตัว แต่เวลาเราค้นหาเส้นทางสำหรับโปรแกรมนำทาง (Navigator) บนโทรศัพท์มือถือทั้งหลาย ทราบไหมว่าเขาคำนวณระยะทางได้อย่างไร อย่างของ google maps ไม่มีเอกสารเปิดเผย แต่หลักการคือเส้นทางต่างๆของกูเกิ้ลจะต่อกันเป็นจุดๆ เรียกว่า node แต่ละโหนดจะมีค่าพิกัดแลตติจูด/ลองจิจูด กำกับอยู่ โปรแกรมจะคำนวณหาระยะทางระหว่างแต่ละโหนดด้วยการนำค่าพิกัดดังกล่าวมาเข้าสูตรคำนวณระยะทางที่สั้นที่สุดบนทรงรี (geodesic distance) หรือไม่ก็ระยะทางบนทรงกลม (great circle distance) เมื่อนำระยะทางแต่ละเส้นมารวมๆกันก็จะได้เป็นระยะทางแสดงให้เราดูบนหน้าจอโทรศัพท์มือถือ
  • ส่วนการเลือกเส้นทางจากต้นทางไปจุดหมายปลายทางแต่ละสถานที่ ว่าไปตามถนนเส้นไหนจะสั้นที่สุดอยูในเรื่อง Travelling Salesman Problem (TSP) อันนี้ลึกซึ้งมากครับ ไม่ขอกล่าวถึง
  • ในฐานะช่างสำรวจ เราใช้กันมันเกือบทุกวันโดยที่ไม่รู้ตัว ก็ลองมาศึกษากันหน่อยว่ามันทำงาน คำนวณมาให้ได้อย่างไร แต่ก่อนจะไปกันต่อ มาลองซักซ้อมเรื่องความรู้เซอร์เวย์กันเล็กน้อยก่อน

เกร็ดความรู้เล็กน้อยสำหรับช่างสำรวจ

  • วันนี้มาว่ากันต่อเรื่องมุมอะซิมัทและฟังก์ชั่น Rec() บนเครื่องคิดเลข Casio FX-5800P ฟังก์ชัน Rec() เอาไว้คำนวณหาค่าพิกัดปลายทาง เมื่อทราบค่าพิกัดต้นทาง, ระยะทางและอะซิมัท
  • Rec(r,θ) เขียนใหม่เป็น Rec(ระยะทาง, อะซิมัท) ก่อนจะเข้าวิธีการใช้งาน มาเท้าความกันหน่อย

  • ลองมาดูรูปด้านบน กำหนดมุมอะซิมัท (θ) และระยะทาง (R) ต้องการหาระยะทางไปตามแกน X จากจุด A ไปหาจุด B หา ได้เท่ากับ  ระยะทาง x Sine (มุมอะซิมัท)
    • ΔX = R x Sin(θ)  เรียกว่า “Departure” คุ้นๆไหม
  • ระยะทางไปตามแกน Y จากจุด A ไปจุด B
    • ΔY= R x Cos(θ)  เรียกว่า “Latitude”
  • ดังนั้นค่าพิกัดจุด A หาได้จาก
    • XB  = XA + ΔX = XA + R x Sin(θ)
    • YB  = YA + ΔY = YA + R x Cos(θ) 
  • ถ้าจำสูตรยากหรือขี้เกียจจำ ฟังก์ชั่น Rec() ช่วยได้ มาดูตัวอย่าง

  • ต้องการทราบค่าพิกัด A เมื่อทราบค่า (X,Y) ของจุด B ดังรูปด้านบน ระยะทาง 100 เมตร อะซิมัทจาก B ไป A = 240º
    • แทนค่าลงไปในสูตร Rec(ระยะทาง, อะซิมัท) = > กดเครื่องคิดเลขเลย Rec(100,240) ผลการคำนวณได้ผลลัพธ์มาสองอย่างคือ ΔX เก็บไว้ในตัวแปร “J” ส่วนค่า ΔY เก็บไว้ในตัวแปร “I”
    •  ผู้อ่านจะสังเกตว่าทำไม  ΔX ไม่เก็บไว้ในตัวแปร “I” และ ΔY ไม่เเก็บไว้ในตัวแปร “J” อย่างที่มันควรจะเป็น ต้องไม่ลืมว่ามุมที่เขาเขียนฟังก์ชันตัวนี้มาคือมุมกวาดจากแกน X แต่เราใช้มุมกวาดจากแกน Y มันเลยสลับร่างสร้างรัก ด้วยประการฉะนี้
  • ลอง Recall (RCL) ค่ามาดูจะได้ I = -50 และ J = -86.603
    • XA  = XB + ΔX = 586.603 – 86.603 = 500
    • YA  = YB + ΔY =  550 – 50 = 500
    • คำตอบคือค่าพิกัดจุด A (500,500)
  • มุมอะซิมัทเป็นมุมที่มหัศจรรย์ครับ เวลาคูณ Cos กับ Sin มันจะไปตกควอดแรนท์ที่ถูกต้องให้ โดยที่หัวสมองเราไม่ต้องไปนึกตาม ลองคิดถึงุมุม Bearing สิครับ สยดสยอง แต่โชคดีบ้านเราไม่ได้ใช้
  • ยังมีเกร็ดเล็กเกร็ดน้อยเกี่ยวกับอะซิมัทอีกนิด มาว่ากันต่อตอนต่อไป

โปรแกรมคำนวณระยะทางที่ส้ั้นที่สุดบนทรงรี (Geodesic Distance)

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

  • การคำนวณจะมีการวนลูปด้วยคำสั่ง For … Next ทุกคร้้งจะมีการเปรียบเทียบค่าใหม่กับค่าเริ่มต้นถ้าค่าต่างกันน้อยกว่า สิบยกกำลังลบสิบสอง (10-12) การคำนวณจะหยุดทันทีแสดงว่าค่าใช้ได้ ผมลองจับจำนวนครั้งที่วนลูปดูประมาณ 3-4 ครั้งแค่นั้น ในตัวโปรแกรมตัวแปรไปดึงตัวแปรอนุกรม Z มาใช้ทั้งหมด 10 ค่า Z[1], Z[2], Z[3], …. Z[10] ตอนประกาศใช้ใช้คำสั่ง 10→DimZ ตอนเลิกใช้ตอนท้ายโปรแกรมประกาศ 0→DimZ เพื่อคืนเมมโมรีให้กับเครื่องคิดเลข
  • เวลาคีย์ชื่อโปรแกรมเนื่องจำกเครื่องคิดเลขจำกัดความยาวตั้งชื่อสั้นว่า “GEODESIC

วิธีการใช้งานโปรแกรม

  • ต้องการระยะทาง Geodesic distance
    • จากจุดที่ 1 ค่า latitude = 14°27’27.0″N longitude = 100°54’12.57″E
    • ไปหาจุดที่ 2 ค่า latitude = 14°36’49.53″N longitude = 98°1’39.63″E
  • เรียกโปรแกรมก่อน กดที่ “File” เลื่อนไปที่ “Geodesic

  • ป้อนค่าพิกัดจุดที่ 1 ค่า latitude = 14°27’27.0″N longitude = 100°54’12.57″E

  • ป้อนค่าพิกัดจุดที่ 2 ค่า latitude = 14°36’49.53″N longitude = 98°1’39.63″E

ผลลัพธ์ของการคำนวณ

  • ผู้อ่านสังเกตไหมเวลากด “EXE” เพื่อคำนวณขั้นสุดท้าย เครื่องจะวูบไปหลายวินาที แต่ถ้าโปรแกรมนี้ถ้าย้ายไปเครื่อง Casio FX-9860G II คงใช้เวลาน้อยกว่าเพราะตามสเป็คแล้วแรงกว่า FX-5800P ประมาณสองเท่า มาดูผลลัพธ์กัน ได้ระยะทาง = 310432.516 เมตร หรือ 310.433 กม.

  • เทียบกับโปรแกรม Geodesic ใน Surveyor Pocket Tools ได้ค่าเท่ากัน ซึ่งหมายเหตุอีกนิดว่าอัลกอริทึ่มที่ใช้คำนวณในโปรแกรม Surveyor Pocket Tools ผมใช้ไลบรารีจาก GeographicLib ซึ่งผลลัพธ์การคำนวณตรงกับสูตรที่เราป้อนในเครื่องคิดเลข ก็ทำให้เรามั่นใจครับว่ามาถูกทาง สูตรถูกต้องแล้ว

  • ตอนหน้าคงเป็นตอนสุดท้ายแล้ว โปรแกรมที่ผมเขียนไว้คำนวณด้านพื้นฐานมีเยอะพอสมควร แต่เนื่องจากผมค้นดูในอินเทอร์เน็ตมีคนเขียนโปรแกรมขายพร้อมเครื่องอยู่หลายๆท่าน เกรงว่าจะไปปิดทางทำมาหากินกัน (พูดอย่างนี้ไม่ใช่โปรแกรมที่ผมเขียนขึ้นมาจะดีกว่านะครับ) ซึ่งผมเคารพในทรัพย์สินทางปัญญาของทุกๆท่าน ผมจึงพยายามเขียนโปรแกรมสำหรับเครื่องคิดเลขฉีกไปในด้าน advance ในประเด็นที่สูตรยากๆที่โปรแกรมคอมพิวเตอร์สามารถทำได้ เครื่องคิดเลขก็สามารถทำได้เช่นกัน ถึงแม้จะช้ากว่า แต่มีดีที่แบตเตอรีอึด ใช้ได้เป็นปี เครื่องก็เบาคุ้มที่จะพกพาไปไหนมาไหน
  • ถึงแม้จะแอพเครื่องคิดเลขบนโทรศัพท์มือถือ จะเอาบวกลบคูนหาร แต่ผมไม่เปิดแอพเครื่องคิดเลขอีกเลย เพราะหยิบโทรศัพท์มือถือมา มันทำให้เสียสมาธิพอสมควร เนื่องจากเปิดหน้าจอมาจะเห็น โปรแกรมต่างๆ เช่นด้านโซเชียลคอยดึงดูดให้กดตามมันไป จนบางครั้งลืมว่าหยิบโทรศัพท์มาทำอะไร
  • พบกันตอนหน้าตอนสุดท้ายครับ อย่าลืมแวะไปเยี่ยมเยียนผมได้ที่เดิม www.priabroy.name ขอบคุณครับ

เริ่มต้น Python ด้วย PySide2 + Miniconda + PyCharm

ขอนำเสนอวิธีการติดตั้งในเบื้องต้นเพื่อใช้ PySide2 สำหรับผู้ที่เริ่มต้นศึกษาเขียนไพทอน บางครั้งไม่รู้จะเริ่มต้นตรงไหน ผมจะขอแนะนำสามสหายที่จะมาช่วยทำให้ชีวิตง่ายขึ้น

  • PySide2 อยู่ระหว่างการพัฒนาจากเจ้าของ Qt framework เองจึงไม่ต้องห่วงว่าโครงการจะล้มลาเลิกร้างกันก่อน ฟรีและมีสัญญาอนุญาตแบบ LPGL v2 สามารถพัฒนาโปรแกรมเพื่อการค้าได้ รุ่นเสถียรอีกไม่นานนักน่าจะออกมาแล้ว
  • Miniconda3 เป็นส่วนหนึ่งของ Anaconda ซึ่งเป็นเฟรมเวิร์คสำหรับการทำ data science สำหรับภาษาไพทอน ส่วน Miniconda เป็นตัวจัดสภาพแวดล้อมให้ไพทอน เช่นถ้าต้องการพัฒนาโปรแกรมให้สามารถรันได้ทั้ง 32 บิตและ 64  บิต ก็ต้องตัวนี้เลย สามารถเลือกรุ่นของ Python ได้ด้วย อย่างเช่น PySide2 ต้องการไพทอน 3.5 สามารถกำหนดรุ่นของไพทอนได้ทีหลังด้วยการ config ที่ง่ายไม่กี่ขั้นตอน ฟรีแบบมีสัญญาอนุญาตแบบ BSD 3-clause
  • PyCharm สภาพแวดล้อมในการพัฒนาทั้ง editor, debugger ครบครัน ใช้งานสะดวก ง่าย ฟรีสำหรับรุ่น community ครั้งหนึ่งผมเคยชมว่า Eric ดีแต่ตัวนี้ดีกว่ามาก รุ่น community ฟรีมีสัญญาอนุญาตแบบ Apache license

ต่อไปจะลำดับความเรียงว่าต้องติดตั้งตัวไหนก่อนตัวไหนหลัง

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

ขณะที่เขียนอยู่นี้เวอร์ชั่น 3 ต้องการดาวน์โหลดก็ตามลิ๊งนี้ download miniconda ส่วนตัวผมเลือกรุ่น 64 บิตขนาดประมาณ 60 MB ติดตั้งก็ง่ายๆ เมื่อได้ไฟล์มาแล้วตอนติดตั้ง ช่วงที่ถามว่าจะเอาโปรแกรมไปเก็บไว้ที่ไหนผมขอแนะนำให้ไว้ตรงรากของไดรว์ C จะหาง่ายและสะดวกที่สุดตอนคลิกเข้าไปดูไฟล์

Miniconda3-latest-Windows-x86_64 (1)_2017-04-23_16-57-39

เมื่อติดตั้งเสร็จ เวลาใช้โปรแกรมนี้ใน windows 10 ใช้ปุ่มวินโดส์ (Win) บนคีย์บอร์ดแล้วพิมพ์คำว่า “anaconda” บางทีพิมพ์แค่คำว่า “ana” ก็จะเห็น shortcut “Anaconda Prompt” ขึ้นมาก็ enter เข้าได้เลย จะแสดงพาทที่ติดตั้งไว้ด้วยเครื่องผมคือ “C:\Miniconda3” ลองเรียก python ดูจะเห็นว่าเป็นรุ่น 3.6.0

cmd_2017-04-24_04-54-34

สมมติว่าผมต้องการติดตั้ง python รุ่น 3.5 เพราะว่า Pyside2 ต้องการรุ่นนี้ และผมต้องการพัฒนาโปรแกรมด้วย PySide2 ทั้ง 32 bit และ 64 bit ที่คอมมานด์ พร็อมป์จัดการพิมพ์ตามนี้

set CONDA_FORCE_32BIT=1
conda create -n py35_32 python=3.5

คำสั่งแรกบังคับให้ miniconda อยู่ในโหมด 32 บิต คำสั่งถัดไปสร้างสภาพแวดล้อมชื่อ py35_32 แล้ว Miniconda จะดาวน์โหลดไลบรารีที่เกี่ยวข้องมาติดตั้งให้ทันที โดยในที่นี้ก็คือ Python รุ่น 3.5  เวลาต้องการใช้งานสภาพแวดล้อมนี้ใช้คำสั่งนี้

activate py35_32

จะได้ผลลัพธิ์ดังนี้ จะเห็นตัวหนังสือด้านหน้าจะเปลี่ยนเป็น “py35_32”

cmd_2017-04-23_18-47-30

ต่อไปสร้างสภาวะแวดล้อมเป็นไพทอน 64 บิต คำสั่งแรกปลดล็อคการใช้โหมด 32 บิต และคำสั่งที่สองสร้างสภาวะแวดล้อมชื่อ “py35_64” ระบุไปว่าเราต้องการไพทอนรุ่น 3.5

set CONDA_FORCE_32BIT=
conda create -n py35_64 python=3.5

เวลาใข้งานต้องการจะใช้ในโหมดนี้ ใช้คำสั่ง

activate py35_64

ถ้าดูโฟลเดอร์ของ miniconda จะเห็นว่ามีการสร้างโฟลเดอร์ให้สองโฟลเดอร์ ตามที่เราสร้างด้วยคำสั่ง create

explorer_2017-04-24_04-58-04

ติดตั้งไลบรารีบนสภาพแวดล้อม 32 บิต

ต่อไปจะติดตั้งไลบรารีต่างๆ ซึ่งก็แล้วแต่ละคนที่จำเป็นต้องใช้ ตัวอย่างที่ผมต้องการใช้งานได้แก่ openpyxl, pyproj, geographiclib, pyshp, gmplot, simplekml และที่สำคัญที่สุดคือ pyside2 เนื่องจากไลบรารี openpyxl บางครั้งผมติดตั้งจากคำสั่ง pip และไลบรารีตัวนี้ต้องการ lxml บางครั้งติดตั้งแล้วใช้งานไม่ได้ ต้องไปดาวน์โหลดไฟล์ wheel มาติดตั้งเอง หมายเหตุว่าไฟล์ wheel คุณสมบัติพิเศษของมันคือสามารถแนบไฟล์ที่ต้องการใช้มาได้ด้วยเช่นไฟล์ dll

ต่อไปจะติดตั้งไลบรารีในโหมด 32 บิตก่อน อย่าลืมใช้คำสั่ง

activate py35_32

ติดตั้ง lxml ด้วยการไปดาวน์โหลดไฟล์ wheel ที่เว็บนี้มีเกือบทุกอย่างที่ต้องการ download python extension packages ไปถึงแล้วก็ค้นหาในเว็บหา lxml เจอแล้วก็ดาวน์โหลดมาทั้ง 32 บิตและ 64 บิต และไฟล์ต้องมีคำว่า cp35 ด้วยเป็นการระบุว่าใช้กับ python 3.5

firefox_2017-04-24_14-12-55

pip install lxml‑3.7.3‑cp35‑cp35m‑win32.whl

ต่อไปติดตั้งไลบรารีของไพทอนตัวที่เหลือ

pip install openpyxl

pip install geographiclib

pip install pyshp

pip install pyproj

pip install gmplot

pip install simplekml

ติดตั้ง PySide2 ด้วย wheel

เนื่องจาก PySide2 เวอร์ชั่นจริงยังไม่ได้ออกมา ดังนั้นต้องอาศัยไฟล์ wheel หรืออีกทางหนึ่งก็คือไปดึงไฟล์โค๊ดจาก github มาคอมไพล์เอง แต่ผมทำไม่สำเร็จ เลยต้องอาศัยวิธีนี้ ดาวน์โหลดไฟล์ wheel ได้จากลิ๊งค์นี้ download pyside2

ในตอนนี้จะเห็นไฟล์อยู่สองไฟล์ดังรูปด้านล่าง จะเห็นว่าชื่อไฟล์มีคำว่า cp35 คือระบุว่าต้องการไพทอนรุ่น 3.5 ก็เหมือนที่ผมบอกไปแล้วว่าทำไมเราต้องการไพทอนรุ่น 3.5 ให้ดาวน์โหลดไปทั้งสองไฟล์ครับ

firefox_2017-04-24_07-35-19

ใช้ command prompt ของ miniconda ทำการ cd เข้าไปหาไฟล์ที่เก็บไว้แล้วติดตั้งด้วย pip

pip install  PySide2-2.0.0.dev0-cp35-cp35m-win32.whl

สำหรับเครื่องคอมพิวเตอร์ผม ตอนติดตั้ง PySide2 ดูจากรูปด้านล่าง

cmd_2017-04-24_07-41-40

ติดตั้งไลบรารีบนสภาพแวดล้อม 64 บิต

เหมือนกับติดตั้งให้ 32 บิต เพียงแต่ตอนติดตั้ง PySide2 ใช้ไฟล์ wheel คนละตัว เริ่มด้วยคำสั่งนี้ก่อน

activate py35_64

จากนั้นก็ใส่ยาวเลย

pip install lxml‑3.7.3‑cp35‑cp35m‑win_amd64.whl

pip install openpyxl

pip install geographiclib

pip install pyshp

conda install pyproj

pip install gmplot

pip install simplekml

pip install PySide2-2.0.0.dev0-cp35-cp35m-win_amd64.whl

ลองทดสอบเรียกไพทอน แล้ว import ไลบรารีเหล่านี้ดูกัน

cmd_2017-04-24_07-52-59

ผ่านฉลุยครับไม่มี error เลย

ติดตั้ง PyCharm

ต่อไปจะดาวน์โหลด PyCharm รุ่น Community ได้ตามลิ๊งค์นี้ download pycharm เลือกเอารุ่น community ซึ่งจะฟรี ได้ไฟล์มาแล้วก็ทำการติดตั้งจากไฟล์ installer ที่ดาวน์โหลดมา เมื่อติดตั้งเสร็จแล้ว ที่สำคัญคือจะ config ให้ pycharm รู้จัก python ว่าต้องใช้สภาพแวดล้อมไหนอย่างไร รัน Pycharm ครั้งแรกถ้ายังไม่มีไฟล์ project เลยเรียกเมนู File > New Project… แล้วใส่ชื่อโครงการ และที่สำคัญคือสามารถเลือกสภาวะแวดล้อมได้ ผมเลือก “py35_64”

pycharm64_2017-04-24_08-05-09

เมื่อเข้าไปแล้วโครงการยังว่าง ผมสร้างไฟล์ใหม่ ด้วยคำสั่ง File > New… เลือกเป็น python file จะเห็นหน้าตาโล่งๆ ว่างปล่าว

pycharm64_2017-04-24_08-11-26

แต่ถ้าต้องการเปลี่ยนเป็นสภาวะแวดล้อม “py35_32” ในโหมด 32 บิต จะต้องไปที่เมนู File > Settings… จะเห็นไดอะล็อกที่สามารถเปลี่ยนได้ที่ Project Intepreter

pycharm64_2017-04-24_08-12-59

โปรแกรมตัวอย่างทดสอบ PySide2

มาลอง PySide2 กันครับ ตรงหน้าว่างๆของ test.py ลองพิมพ์ตัวอย่างนี้เข้าไป หรือ copy & paste ไปก็ได้ แต่มือใหม่ควรจะหัดพิมพ์ สำหรับโปรแกรมมิ่งจะไม่อธิบายนะครับ แต่ผมแนะนำว่าให้หาอ่านจาก PyQt5 ดีกว่าเพราะ tutorial เยอะมาก

class=”code”># -*- coding: utf-8 -*-

import sys
from PySide2.QtWidgets import QApplication, QWidget

if __name__ == ‘__main__’:

app = QApplication(sys.argv)

w = QWidget()
w.resize(250, 150)
w.move(300, 300)
w.setWindowTitle(‘Simple’)
w.show()

sys.exit(app.exec_())

จะทำการรันดู ที่เมนู Run > Run… เลือกไฟล์ test.py  จะได้ผลลัพธ์คือ error ไม่มีอะไรครับ PyCharm  หาไลบรารีของ PySide2 ไม่เจอ

pycharm64_2017-04-24_08-23-45

มีวิธีหลายวิธีตั้งแต่ environment variables  ให้วินโดส์ วิธีแรกที่จะแนะนำนั้นง่ายเพิ่มโค๊ดประมาณ 5 บรรทัด

# -*- coding: utf-8 -*-
import sys
import os
import PySide2

dirname = os.path.dirname(PySide2.__file__)
plugin_path = os.path.join(dirname, 'plugins', '')
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path

from PySide2.QtWidgets import QApplication, QWidget

if __name__ == '__main__':
    app = QApplication(sys.argv)

    w = QWidget()
    w.resize(250, 150)
    w.move(300, 300)
    w.setWindowTitle('Hello world')
    w.show()

    sys.exit(app.exec_())

โดยเฉพาะบรรทัดที่ dirname = os.path.dirname(PySide2.__file__) จะเป็นการดึงเอาที่อยู่ของไดเรคทอรีหรือโฟลเดอร์ของ PySide2 ที่่เราใช้งานอยู่ ไม่ว่าเรากำลังใช้ 32 บิตหรือ 64 บิต ก็ไม่มีปัญหา ลองรันดูที่นี้ผ่านครับ

pycharm64_2017-04-24_14-39-17

วิธีที่สองใช้วิธีเขียน Environment Variables ที่คีย์บอร์ดกดคีย์ win  ค้างไว้กด x (Win + x) จะมีเมนูของวินโดส์เลือก System แล้วเลือก Advance system settings จากนั้นจะมีไดอะล็อกอีกตัวขึ้นมา คลิกเลือก Environment Variables

SystemPropertiesAdvanced_2017-04-24_17-19-09

คลิก New… ที่ System Variables ถ้าเครื่องผมป้อนดังรูปด้านล่าง สำหรับสภาพแวดล้อม 64 บิต ถ้า 32 บิต แก้ตัวหนังสือ py35_64 เป็น py35_32

SystemPropertiesAdvanced_2017-04-24_17-21-36

วิธีนี้อานุภาพรุนแรงครับ เพราะว่าโปรแกรมบางโปรแกรมที่เขียนด้วย PyQt5 หรือ Qt5 ที่ติดตั้งไว้บนเครื่องคอมพิวเตอร์จะพร้อมใจกัน error เพราะหาไลบรารีของ  Qt5 ไม่เจอ เพราะ system variable ตัวนี้ QT_QPA_PLATFORM_PLUGIN_PATH สิทธิ์จะมากที่สุด ปกติโปรแกรมที่พัฒนาด้วย Qt จะเขียนไฟล์ qt.conf ไว้เพื่อระบุว่าไฟล์ไลบรารีของ Qt อยู่ที่ไหน โดยที่ไฟล์ qt.conf ก็วางที่เดียวกับ execute file แต่ถ้าเจอ system variable ตัวนี้จะไม่อ่านไฟล์ qt.conf ก่อน แต่จะวิ่งมาที่พาธที่เราตั้งนี้

เวลาจะเขียนโปรแกรมด้วย PyCharm ผมจะมาตั้งก่อน เสียเวลานิดหน่อย เวลาไม่ได้เขียนก็ลบออก โปรแกรมอื่นๆจะทำงานได้ตามปกติ แต่ตอนที่ผมยังใช้ PyQt5 ไม่มีปัญหาครับ ไม่ต้องมานั่งใส่ใหม่และลบออก ชีวิตยุ่งขึ้นมานิด แต่แลกเอาสัญญาอนุญาตของ PySide2 ที่เปิดกว้างกว่าครับ พบกันตอนหน้าครับ

ปลดพันธนาการ PyQt5 ด้วย PySide2

ตอนนี้ถือว่ามาเล่าสู่กันฟังจากประสบการณ์ เป็นเรื่องโปรแกรมมิ่ง ถ้าไม่สนใจเรื่องโปรแกรมมิ่งก็ผ่านไปได้ครับ

PyQt5 กับลิขสิทธิ์แบบ GPL v3

ผมเขียนไพทอนด้วยการใช้ PyQt5 มาได้สักระยะเวลาหนึ่ง น่าจะสองปีกว่าได้ ยอมรับว่าชอบมากๆ ก็ไม่ได้ระแวดระวังเรื่องกฎหมายลิขสิทธิ์เท่าไหร่นัก ลิขสิทธิ์ของ PyQt5 เป็นแบบ GPL v3 ซึ่งสาระโดยรวมๆสามารถเอาไปใช้ได้สองกรณีคือ พัฒนาโปรแกรมแบบเปิดโค๊ด (open source) และแจกจ่ายฟรีพร้อมโค๊ด กรณีนี้ไม่จำเป็นต้องเสียเงินค่าลิขสิทธิ์ แต่ถ้าเป็นกรณีที่สองคือการพัฒนาโปรแกรมเพื่อการค้าและปิดโค๊ด จะต้องเสียค่าลิขสิทธิ์ประมาณ 500US$ ต่อปี ซึ่งไม่น่าจะมากมายนัก ผมเองเอา PyQt5 มาใช้และแจกจ่ายโปรแกรมของผมให้ฟรีก็จริงแต่ปิดโค๊ด ยังไม่พร้อมที่จะเปิดโค๊ดและจ่ายค่าลิขสิทธิ์เนื่องจากไม่ได้หารายได้จากโปรแกรมที่แจกจ่ายไป

ทางสองแพร่ง

ผมลองมองหาเครื่องมือพัฒนาตัวอื่นๆที่มีลิขสิทธิ์เอื้อแบบปิดโค๊ดแต่ยังสามารถใช้ได้ฟรี ที่อ่านๆมาแล้วอยู่ในใจคือ Kivy Platform

  • Kivy framework Platform รองรับภาษาไพทอน ข้อดีหลายอย่างคือ  cross platform  เขียนโปรแกรมครั้งเดียวสามารถนำไปคอมไพล์ บิวด์ได้บน Linux, Mac OS หรือสามารถพอร์ตลงไปหามือถือ Android, IOS ก็ได้ ที่สำคัญคือเป็นโครงการเปิดโค๊ด มีลิขสิทธิ์แบบ MIT License ซึ่งนอกจากจะเปิดโค๊ดแล้ว ยังอิสระเสรีมาก ซอฟแวร์ที่ใช้ลิขสิทธิ์แบบนี้ ผู้ใช้สามารถเอาไปทำอะไรก็ได้ตั้งแต่ก๊อปปี้ แก้ไข รวม แจกจ่ายหรือกระทั่งนำไปขายก็ได้ สุดท้ายถ้าใช้ Kivy พัฒนาก็ยังสามารถนำโปรแกรมไปทำการค้าได้
  • แต่ปัญหาของผมคือ โปรแกรมที่เขียนมีขนาดค่อนข้างใหญ่แล้ว การพอร์ตจากโค๊ดของ PyQt5 ไปยัง Kivy ไม่ใช่่จะทำได้ง่ายๆ เนื่องจาก Kivy มีรูปแบบ GUI ของตัวเอง ไม่ง่ายครับต้องใช้เวลามาก

PySide2 ผู้มาช่วยชีวิต

ในขณะที่กำลังจะจมน้ำอยู่นั้น นึกถึง PySide รุ่นแรกซึ่งรองรับและใช้ได้แค่ Qt4 ไม่ใช่ Qt5 ที่ผมตกร่องปล่องชิ้นไปแล้ว การจะ downgrade กลับหลังหันไปใช้ Qt4 ผ่าน PySide รุ่นหนึ่งไม่ได้ง่ายต้องรื้อโค๊ดพอสมควร เผลอๆอาจจะยากกว่าการกลับไปขอคืนดีกับแฟนเก่า 🙂 ส่วนเรื่องลิขสิทธิ์เท่าที่ทราบมาคือ PySide ใช้ลิขสิทธิ์แบบ LGPL v2.1 คือสามารถนำไปใช้พัฒนาโปรแกรมสำหรับปิดหรือเปิดโค๊ดได้ ทั้งแจกจ่ายฟรีและขายได้ เพียงแต่ไปเอาไลบรารีตัวไหนที่เป็น LPGL มาใช้จะต้องคงความเป็น LGPL ไว้คือต้องเปิดโค๊ตไลบรารีตัวนั้นไปให้ผู้ใช้ด้วย  ข่าวร้ายของ PySide รุ่นหนึ่งคือโครงการตาย ไม่ขยับมาแล้วสามปีกว่า ผมลองค้นเข้าไปลึกๆ ปรากฎว่าโครงการนี้ตอนแรกๆ ได้มีนักพัฒนาอิสระ fork โครงการมาบน Github กลุ่มเล็กๆต่อมาได้ย้ายและไปพัฒนาต่อเป็นทางการจาก Qt Company เจ้าของ Qt framework ตัวจริงเสียงจริง เหมือนฟ้ามาโปรด แต่ยังไม่แน่ใจว่าโค๊ดเดิม PyQt5 ของผมจะ compatible กับ PySide2 แค่ไหน

ในขณะที่ไม่รู้จะทำอะไรก็เลยไปดาวน์โหลดหยิบเอา PySide ตอนนี้เป็นรุ่น PySide2 ที่กำลังพัฒนาอยู่ เป็นไฟล์ wheel ครับที่ทีมงานได้คอมไพล์และบิวท์มาให้ลองใช้ก่อน รุ่นที่ผมหยิบมาใช้ในขณะนี้ บิวท์นานมาแล้วประมาณหกเดือนกว่า รุ่นล่ากว่านี้ยังไม่มี

PySide2 ใช้ลิขสิทธิ์แบบ LGPL v2 เหมือนกันกับ PySide รุ่นหนึ่ง เมื่อดาวนโหลดมาแล้วก็ติดตั้ง มาลองดูว่าพอไปได้ไหม ผมใช้เวลาว่างๆตอนเลิกงานลองไปหก เจ็ดวัน สำเร็จครับ ส่วนใหญ่ใช้ได้กับโค๊ดเดิม แค่ตอน import ไลบรารีเปลี่ยนแค่หัวจาก PyQt5 มาเป็น PySide2 มีส่วนนิดเดียวผมแก้ไขโค๊ดใหม่ให้เข้ากับ PySide2 แต่น้อยมาก และที่เจออีกหนักอีกหน่อยคือ ระบบรายงานผลการแสดงภาพบนจอมอนิเตอร์คือ Screen ยังไม่เสร็จ ทำให้ผมไม่สามารถตรวจได้ว่าผู้ใช้ใช้จอที่ resolution เท่าไหร่ มี dot pixel  ratio เท่าไหร่ อันนี้สำคัญเพราะว่าจอ HiDPI เช่นจอ 4K ทั้งหลาย ตอนนี้ใช้กันมากแล้ว เมื่อ PySide2 ไม่มีให้ ต้องไปหาโค๊ดมาช่วย ใช้ Windows API ไปพลางๆก่อน พอเสร็จเมื่อไหร่ ค่อยกลับมาใช้โค๊ดของ PySide2 ที่เสร็จแล้วต่อ

ผมพูดได้ว่าตอนนี้โปรแกรมของผม เกือบจะ 99.9% ใช้ของเดิม มาปรับแต่งเองเพียง 0.1% มันง่ายหรือเพราะโปรแกรมผมไม่ได้ใช้เขียนอะไรพิศดารหรือปล่าวเช่นระบบกราฟฟิคที่เลิศหรู แต่เอาละต้องขอบคุณทีมงาน PySide2 มา ณ ที่นี้ด้วยครับ

ข้อจำกัด PySide2 รุ่นพัฒนา

ข้อจำกัดของ PySide2 รุ่นพัฒนายังมีอยู่มากครับ บางครั้งผมรันโปรแกรมผ่านบรรทัดที่ผิด ไม่มีการนับแปดครับ โดนน๊อคกลางอากาศค้างไปดื้อๆ ก็น่าจะอีกสักพักครับ ที่ทีมงานของ Qt จะบิวท์มาให้ใช้กันใหม่ เพราะของเก่าผ่านมาหกเดือนกว่าแล้ว ตัวใหม่น่าจะเสถียรกว่า ดีกว่า

แปลงเป็นไฟล์ Execute ให้รันได้ นรกของโปรแกรมเมอร์ไพทอน

ปกติผมใช้ PyInstaller  สำหรับแปลงโค๊ดไพทอนเป็น execute file (exe) ที่สามารถนำไปรันได้ ทั้งที่ข้อดีของไพทอนคือเขียนง่าย อ่านง่าย ทรงพลัง และไลบรารีที่มีให้เลือกให้ใช้มากมายมหาศาล แต่เป็นที่รู้กันว่างานแปลงไฟล์จากโค๊ดไปเป็นไฟล์ exe ที่รันใช้งานได้ มันเป็นงานสุดหินสุดโหด ตอนผมเริ่มต้นใหม่ๆ ผมก็ไม่รู้ว่าไลบรารีตัวไหน ต้องการขนเอาไฟล์อะไรไปบ้างเพื่อให้สามารถรันได้ บางตัวขนกันไปเป็นโฟลเดอร์ คือแบบเรือพ่วงลากกันไปเป็นพวงๆ ตอนหลังมารู้ว่า มีไฟล์ hooks ที่ PyInstaller  อ่านมาเพื่อตัดสินใจว่าจะขนอะไรไปให้ โฟลเดอร์ไหน ก็ง่ายขึ้น ไฟล์ hooks ส่วนใหญ่มากับ PyInstaller  ถ้าไม่มีก็ไปค้นหาดาวน์โหลดมาได้

ตอนนี้ PySide2 ยังมีนักพัฒนาเอาไปใช้ในวงจำกัดอยู่ จึงไม่มีไฟล์ hooks ปล่อยมา นรกกลับมาเริ่มต้นกับผมอีกครั้ง ต้องลองทดสอบว่า PySide2 เอาอะไรไปใช้บ้าง ตอนนี้ขณะเขียนบทความนี้ ยังไม่สำเร็จครับ กำลังลองผิดลองถูก ไม่เป็นไรชีวิตย่อมมีหนทางเสมอ ผมใช้เวลาในวันอาทิตย์เกือบค่อนวันก็สำเร็จ สามารถนำไฟล์ exe ไปรันได้ตามปกติ สาเหตุที่ไม่ผ่านในตอนแรก ตัว PyInstaller มีปัญหากับโมดูลระบบชื่อ requests ต้องเอา requests รุ่นที่ไม่มีปัญหากันมาใส่แทน

เมื่อไฟล์ exe รันแล้วไม่ติดขัด ก็จะเป็นขั้นตอนต่อไปคือใช้โปรแกรมจำพวก installer มาใช้เช่น Inno Setup ขวัญใจมหาชนเจ้าประจำผมใช้อยู่ ขั้นตอนนี้ง่ายครับ เตรียมไฟล์ exe ให้พร้อม ไฟล์ไลบรารีทั้งหลาย โฟลเดอร์ที่จำเป็นต้องใช้ จากนั้นทำการ build ก็ได้โปรแกรมติดตั้ง ที่สามารถนำไปติดตั้งใช้งานได้

ดาวน์โหลด (Download)

ไปดาวน์โหลดโปรแกรมรุ่นที่บิวท์ด้วย PySide2  เป็นรุ่น V0.70 build 513 ได้แล้วที่หน้า Download ใครที่ใช้รุ่นก่อนหน้านี้ขอความกรุณาช่วย uninstall และมาดาวน์โหลดรุ่นใหม่ไปใช้ด้วย

python_2017-04-22_18-37-32
Surveyor Pocket Tools ในฉบับของ PySide2 เหมือนเดิมเพราะ engine คือตัว Qt framework เดียวกัน
python_2017-04-22_18-49-45

ตัวอย่างหน้าตาของ Transform Coordinates

ความเป็นมาของ Qt framework

เล่าเรื่อง PyQt5 vs. PySide2 ผู้อ่านบางท่านอาจจะงง ถีงที่มาที่ไป PyQt และ PySide คือเครื่องมือสำหรับพัฒนาโปรแกรมประยุกต์ ที่มีพื้นฐานมาจาก Qt framework ที่พัฒนาด้วย C++ ผู้สร้าง Qt คือบริษัท Trolltech บริษัทเล็กๆนอรเวย์ จากนั้นถูกซื้อไปในปี 2008 โดยโนเกีย (Nokia) ยักษ์ใหญ่ในตอนนั้นจากฟินแลนด์ (ย้อนหลังไป 15-25 ปีที่แล้ว คงไม่มีใครไม่รู้จักโนเกียเพราะเป็นเจ้าพ่อแห่งวงการมือถือ ยุคก่อน IPhone OS และ Android) ทำให้ลิขสิทธิ์ของ Qt ตกมาอยู่กับโนเกีย และโนเกียพยายามจะพัฒนาให้ Qt สามารถใช้กับมือถือได้ (แต่ก็ไม่ทัน ไอโอเอสและแอนดรอยด์ จนแพ้สงครามนี้ในที่สุด) ช่วงที่ Qt อยู่กับโนเกีย ในขณะนั้น Riverbank Computing บริษัทจากอังกฤษได้พัฒนา PyQt  แต่ตอนหลังมีปัญหากัน เพราะ Riverbank ไม่ยอมเปลี่่ยนลิขสิทธิ์ของ PyQt ที่ใช้แบบ GPL ส่วน Qt ใช้ลิขสิทธิ์แบบ LGPL ซึ่งยืดหยุ่นกว่า โนเกียก็ได้พัฒนา PySide ขึ้นมาเพื่อให้มีลิขสิทธิ์แบบเดียวกันกับ Qt แต่สถาณการณ์ของโนเกียตอนนั้นกำลังย่ำแย่มากๆ เพราะมือถือจากไอโอเอสของแอปเปิ้ลกับแอนด์ดรอยของกูเกิ้ลได้ครองตลาดเบ็ดเสร็จแล้ว

ในปี 2011 โนเกียขาย Qt ให้บริษัท Digia จากฟินแลนด์ หลังจากนั้นฉากสุดท้ายแล้วก็เป็นที่ทราบกันดีว่าโนเกียถูกซื้อเสนอซื้อโดยไมโครซอฟท์ในปี 2013 เฉพาะส่วนที่เกี่ยวกับมือถือสมาร์ทโฟนจบการดีลการซื้อขายในปี 2014 และจบตำนานโนเกียในที่สุด สะท้อนของสัจธรรมที่ว่าในโลกนี้ไม่มีอะไรเที่ยงแท้แน่นอน หลังจาก Digia ได้ Qt ไปในปี 2014 ได้ก่อตั้งบริษัท Qt Company และเป็นผู้พัฒนา Qt จนถึงปัจจุบัน และตามที่ผมกล่าวมาแล้ว Qt Company ได้ดึงโครงการ PySide มาสานต่อเป็นโครงการ PySide2 ซึ่งถ้าเปิดใช้เป็นทางการเมื่อไหร่ ผมก็เป็นคนหนึ่งที่ตั้งหน้าตั้งตารอคอย

โครงการในอนาคต

ก็เป็นโครงการที่วาดฝันครับ คือพอร์ตโปรแกรม Surveyor Pocket Tools ลงบนมือถือด้วย Kivy framework คงแยกแต่ละ tool ไปเป็นแต่ละ app ซึ่งก็มีฟรีบ้างขายบ้าง ก็เป็นเรื่องอนาคตไม่ได้ตั้งความหวังอะไรมากมาย ปัจจุบันคือพัฒนาและปรับปรุงโปรแกรมทั้งหลายบน Desktop ให้ใช้งานกันต่อไป และยังยืนยันว่าฟรีเหมือนเดิม และความคิดก็ยังเหมือนเดิมครับ “โลกนี้จะน่าอยู่ ถ้าทุกคนแบ่งปัน ถ้อยทีถ้อยอาศัยกัน

ทิ้งท้ายกันนิดหนึ่งจากหัวข้อ “ปลดพันธนาการ” ก็ดูจะโหดร้ายไป ตั้งให้น่าสนใจแค่นั้นครับ PyQt5 นั้นเป็นเครื่องมือพัฒนาโปรแกรมที่ดีมาก มี document ให้อ่านเยอะแยะ ติดขัดตรงไหน Stackoverflow ช่วยได้ สำหรับคนใช้เครื่องมือเพื่อการค้า ก็อุดหนุนซื้อกันไป แต่ถ้าพัฒนาโปรแกรมแบบเปิดโค๊ดก็ตัวนี้เลย ไม่ผิดหวัง สำหรับผมขอเลือก PySide2 ด้วยเหตุผลที่กล่าวมาข้างต้นทั้งหมด พบกันตอนต่อไปครับ

Surveyor Pocket Tools – Update เพิ่มโปรแกรมคำนวณสเกลแฟคเตอร์ (Point Scale Factor) – ตอนที่ 2 (ตอนจบ)

ทดสอบตัวอย่างที่ 2 บนพื้นหลักฐาน Indian 1975

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

psf_display_db

  • จะได้ตารางข้อมูลที่เก็บค่าพิกัดและค่าระดับ(ถ้ามี) พร้อมทั้งระบบพิกัด เมื่อเปิดมาแล้วผมลากเปลียนขนาดให้ดูใหญ่ว่าแต่ละคอลัมน์มีอะไรบ้าง และเลื่อนตารางไปท้ายสุด ดูบรรทัดที่ไฮไลท์เป็นสีน้ำเงินไว้ เราจะทดสอบโดยใช้ข้อมูลนี้ ระบบพิกัดของจุดนี้อยู่บนพื้นหลักฐาน “Indian 1975” บน UTM zone 48N ดูคอลัมน์ “Point Group” จะเห็นว่าจุดนี้เป็น “Projected Coordinate System” คือเป็นค่าพิกัดในระบบพิกัดฉากนั่นเอง

Surveyor Pocket Tools_2017-02-22_14-37-18

  • จากนั้นให้คลิกเมาส์กดแล้วลากจุดค่าพิกัดนี้ไปทิ้งที่ช่องป้อนข้อมูล ผมทำสัญลักษณ์ตอนลากให้ดูง่ายๆ ว่ากำลังลากจุดที่มีค่าพิกัด

Surveyor Pocket Tools_2017-02-22_14-43-24.png

  • เมื่อวางแล้วจะได้ค่าพิกัดและระบบพิกัดจะเปลี่ยนแปลงไปให้ตามจุดข้อมูล สังเกตว่าโซนยูทีเอ็มเดิม 47N จะเปลี่ยนไปให้ตามหมุดกลายเป็น 48N

Surveyor Pocket Tools_2017-02-22_14-46-39.png

  • คลิกที่ปุ่มลูกศรเพื่อทำคำนวณสเกลแฟคเตอร์ จะได้ผลลัพธ์

Surveyor Pocket Tools_2017-02-22_14-48-49

  • จบแล้วง่ายไหมครับ ก่อนหน้านี้ผมคำนวณ Elevation Scale Factor คำนวณด้วยมือ ส่วน Grid scale factor ใช้โปรแกรมอื่น ไม่ค่อยสะดวกเท่าไหร่ สุดท้ายก็มาเขียนโปรแกรมใช้เอง ได้ตรงกับใจที่ต้องการ

เบื้องหลังการคำนวณ

  • เบื้องหลังการคำนวณจะเริ่มจากแปลงพิกัดของ “Indian 1975” ไปเป็นค่าพิกัดภูมิศาสตร์บนพื้นฐาน “WGS84” เพื่อเอาค่าพิกัด latitude/longitude ไปดึงเอาค่าความสูงจีออยด์ (N)
  • มาลองย้อนรอยดูครับ ผมจะแปลงพิกัดโดยใช้โปรแกรม “Transform Coordinate” แล้วจากตารางฐานข้อมูลตัวเดิมผมจะลากจุดตัวนี้เข้าโปรแกรม

Surveyor Pocket Tools_2017-02-22_15-12-11.png

  • โปรแกรมจะใส่ค่าพิกัดและจัดระบบพิกัดให้ตรงกับข้อมูล แล้วด้านซ้ายมือตั้งให้เป็นพื้นหลักฐาน WGS84 / UTM zone 48N แล้วคลิกลูกศรชี้ไปด้านซ้ายเพื่อทำการคำนวณจากขวามาซ้าย

Surveyor Pocket Tools_2017-02-22_15-10-19.png

  • จะได้ค่าพิกัด “WGS84” ออกมา latitude = 14.1353282778, longitude = 102.8941568333 และจะเห็นค่าแลตติจูดบน “Indian 1975” latitude = 14.1336620802, longitude = 102.8977353234

 

  • เปิดโปรแกรม “EGM” ทำการคำนวณค่าความสูงจีออยด์ ได้ค่า = -24.3452 m แทนค่าในสูตร h = H + N = 92.274 – 24.345 = 67.929 m  อย่าลืม ความสูงนี้เทียบกับทรงรี WGS84

  • ขั้นตอนต่อไปหาความสูงทรงรีของ “Everest 1830” ของพื้นหลักฐาน “Indian 1975” ด้วยไลบรารี Proj4 สูตรในโปรแกรมคอมพิวเตอร์ก็ประมาณดังที่แสดงไว้ด้านล่าง

x2, y2, z2 = transform(proj1, proj2, x1, y1, z1)
x2, y2, z2 = transform(proj1, proj2,  102.8941568333,  14.1353282778,  67.929)

  • เราต้องการค่าพิกัด x2, y2, z2 จาก Proj1 ไปยัง Proj2 โดยที่ Proj1 = “WGS84 / Geographic” และ Proj2 = “Indian 1975 / Geographic” คำนวณแล้วได้ค่า z2 = 97.891 m ตัวนี้คือความสูงเมื่อเทียบกับทรงรี “Everest 1830”
  • คำนวณหารัศมีทรงรี R – Mean Radius of Curvature จากสูตร เตรียมค่าสำหรับทรงรี “Everest 1830a = 6377276.345, f = 1/300.8017, e² = 2f – f² = 0.00663784663, e’² = e²/(1-e²) = 0.00668220206 แลตติจูด (θ) = 14.1336620802

  • R = 6377276.345 x √(1 – 0.00663784663) / (1 – 0.00663784663 x sin² (14.1336620802)) = 6358592.078
  • Elevation Scale Factor(ESF) = R / (R + h) = 6358592.078 / (6358592.078 + 97.891) = 0.9999846052 ตรงกับที่คำนวณด้วยโปรแกรม “Point Scale Factor” ข้างต้น
  •  คำนวณ GSF ด้วยสูตร ɸ = 14.1336620802, ƛ = 102.8977353234, ƛ0 = 105

  • การคำนวณด้วยมือ ผมใช้เครื่องคิดเลข

T = tan²(14.1336620802) = 0.06340692275
C = 0.00668220206 x cos²(14.1336620802) = 0.00628376769
A = (102.8977353234 – 105) x 3.141592654/180 x cos(14.1336620802) = -0.03558074351
e’² = 0.00668220206
แทนค่า T,C,A,e’2 ในสูตร จะได้ค่า k =  = 1.00023704

  • ดังนั้น Grid Scale Factor (GSF) = 1.00023704 ซึ่งตรงกับที่โปรแกรม “Point Scale Factor” คำนวณมาได้ข้างต้น

คำนวณค่า Combined Scale Factor (CSF)

  • Combined Scale Factor = ESF x GSF = 0.9999846052 x  1.0002370396 = 1.0002216411
  • ลองมาแปลงเป็น ppm (part per million) เพื่อดูว่าระยะทางหนึ่งกม.จะเพี้ยน (distortion) เท่าไหร่ นำตัวเลขมาลบด้วย 1 จะได้ 1.0002216411 – 1 = 0.0002216411 ทำให้เป็นตัวเลขหารด้วยหนึ่งล้าน(คือสิบยกกำลังหก) = 221.64 / 10 = 221.64 ppm
  • แสดงว่าระยะทาง 1 กม. ระยะบนพิกัดฉากจะต่างกับระยะทางจริงๆบนพื้นโลก  221.6 mm.  = 22.1 cm. ซึ่งไม่ถือว่าน้อย ถ้าวัดบนพื้นโลกได้ 1000 m จะวัดระยะทางบนระบบพิกัดฉากได้ 1000.222 m

soffice.bin_2017-02-23_14-55-36

  • ก็ขอจบตอนแค่นี้ ตอนหน้ามาว่าเรื่อง “Line Scale Factor” คำนวณหาค่า CSF แบบเฉลี่ย ที่จะนำไปใช้งานกันจริงๆ