Геодезія: по полю на «Пітон»

image
 
Доброго часу, Хабр!
Трохи історії: в ході навчання програмуванню, шукав я собі реальну задачу, та таку щоб з користю. Знайшов. Побачив як знайомий геодезист, на роботі, вважає обсяг земельної ділянки. Дуже довго і нудно…
 
Геодезичний розрахунок обсягів:
При зведенні житлових споруд, високотехнологічних приміщень, автомобільних і залізниць, а так — же в цілях визначення обсягів будівельних матеріалів і підрахунку об'єму земляних робіт, потрібна допомога геодезистів. Вони "відстрілюють" територію, розбиваючи всю площу на так звану геосітка , далі получениє точки з приладу вивантажуються в autoCAD і вираховують обсяг всій території. Нижче приклад геосетки :
 
 image
 
Кожна точка має свої координати (x, y, z-висота) щодо балтійського моря. По порядку беруться 4 точки як показано на попередньому зображенні і обчислюється виходячи з координат цих точок обсяг ділянки землі, так обчислюються всі обсяги одержані на сітці фігур, плюсуються і в результаті ми маємо обсяг всій території.
 
Робота дуже копітка, а так як я вже писав, я хотів взятися за реальний проект, взамін на досвід. Тому я навіть не став розглядати той, варіант, що давно вже все є до мене.
 
Кілька днів я був заморочені на алгоритмі пошуку сусідів, в результаті вийшло не зовсім те чого я чекав, алгоритм працював як хотів у зв'язку з тим, що сітка не рівна як в теорії, а крива як ходив помічник геодезиста по полю і ставив відмітки, потім я зрозумів що і AutoCAD дуже просто сортує за часом створення точки. Далі трохи включившись в AutoCAD я помітив цікавий порядок вивантаження точок в XML файл, і знайшов можливість призначення кожної точки ID, тепер ми призначили кожній точці ім'я з лівого кута до правого краю і так-же розуміючи на ряд вище про іменували всі крапки (це куди швидше, ніж два дні вважати обсяг території площею 500х700 метрів). Нижче приклад такого готового файлу з іменованими по порядку точками:
 
 image
 
По ідеї обсяги вважаються за Піфагором, але фігури абсолютно різні і як показує практика дуже рідко зустрічаються квадрати і прямокутники. Тому я вдався до формули Герона у своїх обчисленнях. Тобто принцип софтина, що я написав такий: Читаю XML файл, збираю точки, далі масив з масивами чотирьох кутників, отримую всі фігури яким потрібно порахувати обсяг, тепер перед розрахунком обсягу я перевіряю по сторонам фігур (вектора) і кутах на прямокутник і квадрат (велика рідкість, у разі конкретно нашого геодезиста, що такі будуть, і якщо є то застосовую пифагора) а фігури типу трапеції, паралелепіпеда та інші я просто приймаю за невідомий чотирьох кутник і вважаю їх за формулою Герона, ділю по діагоналі фігуру на два трикутника вважаю їх площі в просторі (враховую висоту), висота, береться найвища з 4-х точок (так мені пояснив, мій колега-геодезист) і плюсуючи площі двох трикутників далі я вже виходячи із загальної площі фігури отримую її обсяг.
 
Плюсую всі ці обсяги і отримую за 0.2 секунди результат до якого він йде пару днів, перевіряли на трьох проектах поки дані сходяться, програмою навіть більш точніше виходить обсяг. Продовжуємо її тестувати. Тепер днями я вирішив прикрутити цей код до юзабельно інтерфейсу, з PyQt4, мій перший досвід роботи з написанням графіки, нижче я опублікую код який відноситься тільки до GUI.
 
 
from PyQt4 import QtGui
import sys
import cvgLeicaXmlReader
import cvgMath

class myWindow(QtGui.QMainWindow):

    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.setWindowTitle('CVG2014')
        self.setFixedSize(350, 350)
        self.setWindowIcon(QtGui.QIcon('static/Icon.png'))
        self.setStyleSheet("QMainWindow {background-image: url(static/background.png);}")
        self.directory = ''
        self.file = ''

        self.labelFilename = QtGui.QLabel('Select .XML file with points', self)
        self.labelFilename.setFixedWidth(300)
        self.labelFilename.setFixedHeight(25)
        self.labelFilename.move(10, 5)
        self.labelFilename.setStyleSheet("QLabel { background-color: white; \
                                           border: 1px solid grey; \
                                           color: grey;}")

        self.SB_WidthOfXAxis = QtGui.QSpinBox(self)
        self.SB_WidthOfXAxis.move(10, 35)
        self.SB_WidthOfXAxis.setFixedWidth(50)
        self.SB_WidthOfXAxis.setMaximum(9999)

        self.labelPointsWidth = QtGui.QLabel('Length of points on X axis', self)
        self.labelPointsWidth.setFixedWidth(250)
        self.labelPointsWidth.setFixedHeight(25)
        self.labelPointsWidth.move(65, 47.5)

        self.SB_HeightAboveSeaLevel = QtGui.QDoubleSpinBox(self)
        self.SB_HeightAboveSeaLevel.move(10, 75)
        self.SB_HeightAboveSeaLevel.setFixedWidth(50)
        self.SB_HeightAboveSeaLevel.setRange(-9999.99, 9999.99)

        self.labelPointsSeaLevel = QtGui.QLabel('Height above sea level', self)
        self.labelPointsSeaLevel.setFixedWidth(250)
        self.labelPointsSeaLevel.setFixedHeight(25)
        self.labelPointsSeaLevel.move(65, 87)

        self.buttonOpenFile = QtGui.QPushButton('...', self)
        self.buttonOpenFile.setFixedWidth(30)
        self.buttonOpenFile.setFixedHeight(27)
        self.buttonOpenFile.move(311, 4)
        self.buttonOpenFile.clicked.connect(self.getXmlFile)

        self.buttonGetVolume = QtGui.QPushButton('RUN', self)
        self.buttonGetVolume.setFixedWidth(52)
        self.buttonGetVolume.setFixedHeight(35)
        self.buttonGetVolume.move(9, 115)
        self.buttonGetVolume.clicked.connect(self.getVolume)

        self.showVolume = QtGui.QLabel('Get Vol', self)
        self.showVolume.setFixedWidth(140)
        self.showVolume.setFixedHeight(32)
        self.showVolume.move(72, 117)
        self.showVolume.setStyleSheet("QLabel { background-color: white; \
                                           border: 1px solid grey; \
                                           color: grey;}")


    def getVolume(self):
        xml_file = win.getFileName()
        points = cvgLeicaXmlReader.getPointsFromXmlFile(xml_file)
        if(xml_file and points):


            # length_of_points = len(points)
            QUANTITY_POINTS_AT_X_AXIS = self.SB_WidthOfXAxis.value()
            STATIC_HEIGHT = self.SB_HeightAboveSeaLevel.value()

            rows = cvgLeicaXmlReader.getRowsFromPoints(points, QUANTITY_POINTS_AT_X_AXIS)
            quads = cvgLeicaXmlReader.getAllQuads(rows)

            volumes = []
            if (STATIC_HEIGHT or QUANTITY_POINTS_AT_X_AXIS) != 0:
                for quadrangle in quads:
                    Quadrangle_type = cvgMath.getTypeQuadrangle(quadrangle)
                    v = cvgMath.getVolumeQuadrangle(quadrangle, Quadrangle_type, STATIC_HEIGHT)
                    volumes.append(v)
            else:
                volumes = 0

            volumes = 0 if STATIC_HEIGHT == 0 else (round(sum(volumes), 3))
            result = '-'+str(volumes) if STATIC_HEIGHT < 0 else str(volumes)
            result = '0' if result == '-0' else result
            self.showVolume.setStyleSheet("QLabel { background-color: white; \
                                           border: 1px solid grey; \
                                           color: grey;}")
            self.showVolume.setText(result)
        else:
            self.showVolume.setText('Select the correct file!')
            self.showVolume.setStyleSheet("QLabel { background-color: white; \
                                           border: 1px solid grey; \
                                           color: red; \
                                           font-weight: bold}")


    def getXmlFile(self):
        sender = self.sender()
        path = QtGui.QFileDialog.getOpenFileName(sender, 'Open Xml file with points', self.directory, 'XML *.xml')
        fileName = path[path.rfind('/')+1:]
        self.directory = path[:path.rfind('/')]
        if(len(path) > 54):
            start = len(path)-54
            pathSlice = path[start:]
            pathSlice = pathSlice[pathSlice.find('/'):]
            pathSlice = '..'+pathSlice
        else:
            pathSlice = path
        self.labelFilename.setText(pathSlice)
        print(len(path))
        self.file = path


if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    win = myWindow()
    win.show()
    app.exec_()

 
З іншими модулями кому цікаво, можна ознайомитися на гітхабе
Так само я завантажив . Rar архів зі скомпільований кодом в exe для windows.
 
 image
 
Що потрібно від нас: відкриваємо XML файл з точками (два файли для прикладу лежать так-же в на гіті (2 коректних і один битий, для тіста)), вказуємо кількість точок по ширині поля, нижче вказуємо висоту щодо рівня моря, і чекаємо кнопку RUN, отримуємо обсяг у поле з право від кнопки запуску.
 
Якщо чесно я не знаю, чи можна застосовувати цей інструмент на виробництві. Але своє бажання я задовольнив. Всім дякую за увагу.
 
 Файл з 65 точками (ширина 13 точок)
 Файл з 78 точками (ширина 13 точок)
 Модуль читання XML файлу
 Модуль з математичними обчисленнями
 Модуль візуального оформлення
 Архів зі скомпільований кодом в Exe

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.