You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
475 lines
20 KiB
Python
475 lines
20 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
/***************************************************************************
|
|
GemeindeMessdatenImport
|
|
A QGIS plugin
|
|
Importiert Messdaten im TXT-Format in die Gemeindeeigene Messdaten-Datenbank.
|
|
|
|
|
|
-------------------
|
|
begin : 2024-02-01
|
|
git sha : $Format:%H$
|
|
copyright : (C) 2024 by Sebastian Pertl
|
|
email : sebastian.pertl@fridolfing.bayern.de
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
"""
|
|
from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
|
|
from qgis.PyQt.QtGui import QIcon
|
|
from qgis.PyQt.QtWidgets import QAction, QComboBox
|
|
|
|
from qgis.PyQt import uic
|
|
from qgis.PyQt import QtWidgets
|
|
from qgis.PyQt.QtWidgets import QFileDialog, QMessageBox, QInputDialog, QLineEdit
|
|
|
|
#from qgis.core import QgsVectorLayer, QgsVectorFileWriter, QgsProviderRegistry
|
|
#from qgis.core import QgsProject, QgsFeature
|
|
from qgis.core import *
|
|
from qgis.utils import iface
|
|
import qgis.core
|
|
|
|
import toml
|
|
|
|
# Initialize Qt resources from file resources.py
|
|
# from .resources import *
|
|
# Import the code for the dialog
|
|
#from .gdeMessdatenImport_dialog import GemeindeMessdatenImportDialog
|
|
|
|
import os
|
|
import os.path
|
|
|
|
import ezdxf
|
|
import pandas as pd
|
|
import numpy as np
|
|
import networkx as nx
|
|
import math
|
|
import scipy
|
|
import sqlite3
|
|
import subprocess
|
|
import win32com.client
|
|
|
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
import time
|
|
|
|
hostName = "localhost"
|
|
serverPort = 8080
|
|
|
|
|
|
file_path = "D:/Daten/nc/Ingenieurbüro/98-CAD-Daten/Tools-QGIS/ibpTools/"
|
|
with open(file_path + 'config.toml', 'r') as f:
|
|
CFG = toml.load(f)
|
|
|
|
|
|
class MyServer(BaseHTTPRequestHandler):
|
|
def do_GET(self):
|
|
self.send_response(200)
|
|
self.send_header("Content-type", "text/html")
|
|
self.end_headers()
|
|
self.wfile.write(bytes("<html><head><title>https://pythonbasics.org</title></head>", "utf-8"))
|
|
self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
|
|
self.wfile.write(bytes("<body>", "utf-8"))
|
|
self.wfile.write(bytes("<p>This is an example web server.</p>", "utf-8"))
|
|
self.wfile.write(bytes("</body></html>", "utf-8"))
|
|
|
|
basepath = os.path.dirname(os.path.realpath(__file__))
|
|
TEIGHA_PATH = basepath.replace("\\","/") + "/Teigha File Converter 4.3.2/TeighaFileConverter.exe"
|
|
|
|
|
|
tmp_folder = r"C:/tmp"
|
|
|
|
|
|
class ibpTools:
|
|
"""QGIS Plugin Implementation."""
|
|
|
|
def __init__(self, iface):
|
|
"""Constructor.
|
|
|
|
:param iface: An interface instance that will be passed to this class
|
|
which provides the hook by which you can manipulate the QGIS
|
|
application at run time.
|
|
:type iface: QgsInterface
|
|
"""
|
|
# Save reference to the QGIS interface
|
|
self.iface = iface
|
|
# initialize plugin directory
|
|
self.plugin_dir = os.path.dirname(__file__)
|
|
|
|
# Declare instance attributes
|
|
# Eintrag Menüband Oben
|
|
self.actions = []
|
|
self.menu = u'&IBP: Kanalplanung-Tools'
|
|
|
|
# Check if plugin was started the first time in current QGIS session
|
|
# Must be set in initGui() to survive plugin reloads
|
|
self.first_start = None
|
|
|
|
inp_file = 'D:/Daten/nc/Ingenieurbüro/20 Projekte/Kanalplanung/kanalplanung.gpkg'
|
|
self.md = QgsProviderRegistry.instance().providerMetadata("ogr")
|
|
self.gpkg_con = self.md.createConnection( inp_file, {})
|
|
|
|
self.doc = False
|
|
|
|
def initGui(self):
|
|
"""Create the menu entries and toolbar icons inside the QGIS GUI."""
|
|
|
|
#
|
|
self.toolBar = self.iface.addToolBar("IBP: Kanalplanung-Tools")
|
|
|
|
self.actList = []
|
|
|
|
icon_path = os.path.join(self.plugin_dir, 'icon_filter.png')
|
|
act = QAction(QIcon(icon_path), u'Setze Projektfilter', self.iface.mainWindow())
|
|
act.triggered.connect(self.run_projectFilter)
|
|
self.actList.append(act)
|
|
|
|
icon_path = os.path.join(self.plugin_dir, 'icon_setup.png')
|
|
act = QAction(QIcon(icon_path), u'Initialisiere BricsCAD', self.iface.mainWindow())
|
|
act.triggered.connect(self.run_setup)
|
|
self.actList.append(act)
|
|
|
|
icon_path = os.path.join(self.plugin_dir, 'icon_import.png')
|
|
act = QAction(QIcon(icon_path), u'Importiere von BricsCAD', self.iface.mainWindow())
|
|
act.triggered.connect(self.run_import_from_bricscad)
|
|
self.actList.append(act)
|
|
|
|
icon_path = os.path.join(self.plugin_dir, 'icon_export.png')
|
|
act = QAction(QIcon(icon_path), u'Übertrag in BricsCAD', self.iface.mainWindow())
|
|
act.triggered.connect(self.run_export_to_bricscad)
|
|
self.actList.append(act)
|
|
|
|
|
|
for i in self.actList:
|
|
self.toolBar.addAction(i)
|
|
self.iface.addPluginToMenu(self.menu, i)
|
|
|
|
|
|
|
|
self.first_start = True
|
|
|
|
|
|
def unload(self):
|
|
"""Removes the plugin menu item and icon from QGIS GUI."""
|
|
for action in self.actions:
|
|
self.iface.removePluginMenu(u'&IBP: Kanalplanung-Tools', action)
|
|
self.iface.removeToolBarIcon(action)
|
|
|
|
def run_projectFilter(self):
|
|
qid = QInputDialog()
|
|
filter, ok = QInputDialog.getText( qid, "Projektfilter", "Projektnummmer", QLineEdit.Normal, "")
|
|
|
|
self.gpkg_con.executeSql("UPDATE projekt SET aktiv = 0;")
|
|
|
|
if filter != "":
|
|
self.gpkg_con.executeSql("UPDATE projekt SET aktiv = 1 WHERE nr='{}';".format(filter))
|
|
|
|
#for i in self.iface.mapCanvas().layers():
|
|
for i in QgsProject.instance().mapLayers().values():
|
|
if type(i) == type(QgsVectorLayer()):
|
|
idx = i.fields().indexFromName('ibpprj')
|
|
if idx >= 0:
|
|
i.setSubsetString("ibpprj LIKE '%"+filter+"%'")
|
|
|
|
else:
|
|
row.setSubsetString("")
|
|
|
|
def run_setup(self):
|
|
self.acad = win32com.client.Dispatch("BricscadApp.AcadApplication")
|
|
self.doc = self.acad.ActiveDocument
|
|
self.model = self.doc.ModelSpace
|
|
|
|
QMessageBox.information(None, "Information", "BricsCAD gestartet und Datei gewählt: "+self.doc.name)
|
|
|
|
def run_import_from_bricscad(self):
|
|
"""
|
|
Lädt alle Layer von BricsCAD in QGIS die mit "_IBP_QIN_" beginnen.
|
|
"""
|
|
|
|
if self.doc == False:
|
|
QMessageBox.warning(None, "Warnung", "Verbindung noch nicht initialisiert!")
|
|
else:
|
|
self.doc.save()
|
|
|
|
ibp_dwg_to_dxf(self.doc.name, self.doc.path.replace("\\","/"))
|
|
|
|
file_path = self.doc.path.replace("\\","/")+self.doc.name.replace(".dwg",".dxf")
|
|
print(file_path)
|
|
s_doc = ezdxf.readfile(file_path)
|
|
s_msp = s_doc.modelspace()
|
|
|
|
# QGIS-Daten
|
|
laylist = {}
|
|
laylist['cad_punkt'] = {"qgis_handle":[], "add_new":[], "qgis_lay": None}
|
|
laylist['cad_linie'] = {"qgis_handle":[], "add_new":[], "qgis_lay": None}
|
|
|
|
#@todo: Wenn Layer nicht existiert soll er neu angelegt werden
|
|
for key, val in laylist.items():
|
|
val['qgis_lay'] = QgsProject.instance().mapLayersByName(key)[0]
|
|
val['qgis_lay'].startEditing()
|
|
for i in val['qgis_lay'].getFeatures():
|
|
if i['doc'] == self.doc.name:
|
|
val['qgis_handle'].append(i['handle'])
|
|
|
|
# Übertrage alle Elemente des Bricscad Layer "_IBP_QIN_..." in QGIS
|
|
cad_qgis_obj = []
|
|
for i in s_msp.query('*[layer?"{}.*"]'.format(CFG['qgs_cad']['import_layer_prefix'])):
|
|
cad_qgis_obj.append(i)
|
|
|
|
#print(cad_qgis_obj)
|
|
|
|
for i in cad_qgis_obj:
|
|
layer = ""
|
|
if i.dxftype() in ["INSERT","POINT","CIRCLE"]:
|
|
layer = "cad_punkt"
|
|
elif i.dxftype() in ["LINE","LWPOLYLINE"]:
|
|
layer = "cad_linie"
|
|
else:
|
|
continue
|
|
new_feat = False
|
|
|
|
if i.dxf.handle not in laylist[layer]['qgis_handle']:
|
|
new_feat = True
|
|
feature = QgsFeature(laylist[layer]['qgis_lay'].fields())
|
|
feature['layer'] = i.dxf.layer
|
|
feature['handle'] = i.dxf.handle
|
|
feature['doc'] = self.doc.name
|
|
feature['type'] = i.dxftype()
|
|
else:
|
|
for j in laylist[layer]['qgis_lay'].getFeatures():
|
|
if j['handle'] == i.dxf.handle and j['doc'] == self.doc.name:
|
|
feature = j
|
|
|
|
if i.dxftype() == "LINE":
|
|
geom = QgsGeometry.fromPolylineXY([QgsPointXY(i.dxf.start[0],i.dxf.start[1]),
|
|
QgsPointXY(i.dxf.end[0],i.dxf.end[1])])
|
|
elif i.dxftype() == "INSERT":
|
|
geom = QgsGeometry.fromPointXY(QgsPointXY(i.dxf.insert[0],i.dxf.insert[1]))
|
|
feature['block_name'] = i.dxf.name
|
|
elif i.dxftype() == "LWPOLYLINE":
|
|
pts = []
|
|
for j in i.vertices():
|
|
pts.append(QgsPointXY(j[0],j[1]))
|
|
##Kontrolle ob geschlossen
|
|
if i.dxf.flags == 1:
|
|
geom = QgsGeometry.fromPolygonXY([pts])
|
|
else:
|
|
geom = QgsGeometry.fromPolylineXY(pts)
|
|
elif i.dxftype() == "POINT":
|
|
geom = QgsGeometry.fromPointXY(QgsPointXY(i.dxf.location[0],i.dxf.location[1]))
|
|
elif i.dxftype() == "CIRCLE":
|
|
geom = QgsGeometry.fromPointXY(QgsPointXY(i.dxf.center[0],i.dxf.center[1]))
|
|
|
|
if new_feat:
|
|
feature.setGeometry(geom)
|
|
laylist[layer]['add_new'].append(feature)
|
|
else:
|
|
laylist[layer]['qgis_lay'].updateFeature(feature)
|
|
laylist[layer]['qgis_lay'].changeGeometry(feature.id(), geom)
|
|
|
|
for key, val in laylist.items():
|
|
val['qgis_lay'].commitChanges()
|
|
val['qgis_lay'].dataProvider().addFeatures(val['add_new'])
|
|
|
|
self.iface.mapCanvas().refreshAllLayers()
|
|
|
|
def run_export_to_bricscad(self):
|
|
|
|
if self.doc == False:
|
|
QMessageBox.warning(None, "Warnung", "Verbindung noch nicht initialisiert!")
|
|
else:
|
|
lgp_file = self.doc.path.replace("\\","/")+self.doc.name.replace(".dwg","_Lageplan.dxf")
|
|
|
|
try:
|
|
ibp_dwg_to_dxf(lgp_file.split("/")[-1].replace(".dxf",".dwg"),lgp_file.replace(lgp_file.split("/")[-1],""))
|
|
lgp_doc = ezdxf.readfile(lgp_file)
|
|
except:
|
|
lgp_doc = ezdxf.readfile('C:/Daten/nc/Ingenieurbüro/99-Vorlagen/ezdxf_vorl.dxf')
|
|
|
|
lgp_msp = lgp_doc.modelspace()
|
|
|
|
lay = QgsProject.instance().mapLayersByName('schacht')[0]
|
|
lay.startEditing()
|
|
|
|
for i in lay.getFeatures():
|
|
if i['schacht_dn'] > 0:
|
|
rad = i['schacht_dn']/1000/2
|
|
else:
|
|
if i['fallrohr'] > 0:
|
|
rad = i['fallrohr']/1000/2
|
|
else:
|
|
rad = 0.05
|
|
|
|
if len(lgp_msp.query().filter(lambda e: "QGIS:schacht:symb:{}".format(i['fid']) in e.get_hyperlink())) == 1:
|
|
# Element vorhanden
|
|
#ent_symb = lgp_msp.query('*[handle=="'+i['cad_out_handle_symb']+'"]')[0]
|
|
ent_symb = lgp_msp.query().filter(lambda e: "QGIS:schacht:symb:{}".format(i['fid']) in e.get_hyperlink())[0]
|
|
else:
|
|
# Element anlegen
|
|
ent_symb = lgp_msp.add_circle((0,0), 1)
|
|
ent_symb.dxf.layer = '_IBP_QO_EW_{}_SCH'.format(i['art'])
|
|
|
|
ent_symb.dxf.center = (i.geometry().asPoint().x(), i.geometry().asPoint().y())
|
|
ent_symb.dxf.radius = rad
|
|
|
|
if len(lgp_msp.query().filter(lambda e: "QGIS:schacht:txt1:{}".format(i['fid']) in e.get_hyperlink())) == 1:
|
|
# Element vorhanden
|
|
# ent_txt = lgp_msp.query('*[handle=="{}"]'.format(i['cad_out_handle_txt']))[0]
|
|
ent_txt = lgp_msp.query().filter(lambda e: "QGIS:schacht:txt1:{}".format(i['fid']) in e.get_hyperlink())[0]
|
|
else:
|
|
# Element anlegen
|
|
ent_txt = lgp_msp.add_mtext("abc")
|
|
ent_txt.dxf.insert = ent_symb.dxf.center
|
|
ent_txt.dxf.layer = '_IBP_QO_EW_{}_SCH_Txt'.format(i['art'])
|
|
ent_txt.dxf.char_height = 0.25
|
|
ent_txt.dxf.style = 'IBP_Norm'
|
|
|
|
ent_txt.text = "{}".format(i['name'])
|
|
if i['schacht_dn'] != NULL:
|
|
ent_txt.text+="\nDN{:.0f}".format(i['schacht_dn'])
|
|
if i['dh'] != NULL:
|
|
ent_txt.text+="\nDH: {:.2f}".format(i['dh'])
|
|
if i['sh'] != NULL:
|
|
ent_txt.text+="\nSH: {:.2f}".format(i['sh'])
|
|
|
|
#i['cad_out_handle_symb'] = ent_symb.dxf.handle
|
|
#i['cad_out_handle_txt'] = ent_txt.dxf.handle
|
|
|
|
ent_symb.set_hyperlink("QGIS:schacht:symb:{}".format(i['fid']))
|
|
ent_txt.set_hyperlink("QGIS:schacht:txt1:{}".format(i['fid']))
|
|
|
|
|
|
#lay.commitChanges()
|
|
|
|
lay = QgsProject.instance().mapLayersByName('haltung')[0]
|
|
lay.startEditing()
|
|
|
|
for i in lay.getFeatures():
|
|
points = []
|
|
for j in i.geometry().vertices():
|
|
points.append((j.x(),j.y()))
|
|
|
|
qry = lgp_msp.query().filter(lambda e: "QGIS:haltung:symb:{}".format(i['fid']) in e.get_hyperlink())
|
|
if len(qry) == 1:
|
|
# Element vorhanden
|
|
#ent_symb = lgp_msp.query('*[handle=="{}"]'.format(i['cad_out_handle_symb']))[0]
|
|
ent_symb = qry[0]
|
|
else:
|
|
# Element anlegen
|
|
ent_symb = lgp_msp.add_lwpolyline(points)
|
|
ent_symb.dxf.layer = '_IBP_QO_EW_{}_Lei'.format(i['art'])
|
|
|
|
#i['cad_out_handle_symb'] = ent_symb.dxf.handle
|
|
ent_symb.set_hyperlink("QGIS:haltung:symb:{}".format(i['fid']))
|
|
|
|
mid_point = i.geometry().interpolate(i.geometry().length()/2)
|
|
mid_point_c = [mid_point.asPoint().x(), mid_point.asPoint().y()]
|
|
mid_angle = i.geometry().interpolateAngle(i.geometry().length()/2)
|
|
mid_angle_deg = 90-mid_angle/math.pi*180
|
|
if mid_angle_deg < 0:
|
|
mid_angle_deg+= 360
|
|
if mid_angle_deg > 90 and mid_angle_deg < 270:
|
|
mid_angle_deg-= 180
|
|
|
|
qry = lgp_msp.query().filter(lambda e: "QGIS:haltung:txt1:{}".format(i['fid']) in e.get_hyperlink())
|
|
if len(qry) == 1:
|
|
# Element vorhanden
|
|
#ent_txt = lgp_msp.query('*[handle=="{}"]'.format(i['cad_out_handle_txt']))[0]
|
|
ent_txt = qry[0]
|
|
else:
|
|
# Element anlegen
|
|
ent_txt = lgp_msp.add_mtext("abc")
|
|
ent_txt.dxf.insert = [mid_point_c[0]+math.cos((mid_angle_deg+90)/180*math.pi)*.1,
|
|
mid_point_c[1]+math.sin((mid_angle_deg+90)/180*math.pi)*.1]
|
|
ent_txt.dxf.layer = '_IBP_QO_EW_{}_Lei_Txt'.format(i['art'])
|
|
ent_txt.dxf.rotation = mid_angle_deg
|
|
ent_txt.dxf.attachment_point = ezdxf.lldxf.const.MTEXT_BOTTOM_CENTER
|
|
ent_txt.dxf.char_height = 0.25
|
|
ent_txt.dxf.style = 'IBP_Norm'
|
|
ent_txt.text = "{}".format(i['mat'])
|
|
#i['cad_out_handle_txt'] = ent_txt.dxf.handle
|
|
ent_txt.set_hyperlink("QGIS:haltung:txt1:{}".format(i['fid']))
|
|
|
|
qry = lgp_msp.query().filter(lambda e: "QGIS:haltung:txt2:{}".format(i['fid']) in e.get_hyperlink())
|
|
if len(qry) == 1:
|
|
# Element vorhanden
|
|
#ent_txt = lgp_msp.query('*[handle=="{}"]'.format(i['cad_out_handle_txt2']))[0]
|
|
ent_txt = qry[0]
|
|
else:
|
|
# Element anlegen
|
|
ent_txt = lgp_msp.add_mtext("abc")
|
|
ent_txt.dxf.insert = [mid_point_c[0]-math.cos((mid_angle_deg+90)/180*math.pi)*.1,
|
|
mid_point_c[1]-math.sin((mid_angle_deg+90)/180*math.pi)*.1]
|
|
ent_txt.dxf.layer = '_IBP_QO_EW_{}_Lei_Txt'.format(i['art'])
|
|
ent_txt.dxf.rotation = mid_angle_deg
|
|
ent_txt.dxf.attachment_point = ezdxf.lldxf.const.MTEXT_TOP_CENTER
|
|
ent_txt.dxf.char_height = 0.25
|
|
ent_txt.dxf.style = 'IBP_Norm'
|
|
if i['gefaelle'] == qgis.core.NULL:
|
|
ent_txt.text = '{:.2f}m'.format(i['c_laenge'])
|
|
else:
|
|
ent_txt.text = '{:.2f}‰; {:.2f}m'.format(i['gefaelle'], i['c_laenge'])
|
|
#i['cad_out_handle_txt2'] = ent_txt.dxf.handle
|
|
ent_txt.set_hyperlink("QGIS:haltung:txt2:{}".format(i['fid']))
|
|
|
|
|
|
#lay.updateFeature(i) #Update-QGIS-Layer
|
|
|
|
|
|
|
|
lay.commitChanges()
|
|
|
|
lgp_doc.saveas(lgp_file)
|
|
ibp_dxf_to_dwg(lgp_file.split("/")[-1],lgp_file.replace(lgp_file.split("/")[-1],""))
|
|
|
|
def run_init(self):
|
|
# Create the dialog with elements (after translation) and keep reference
|
|
# Only create GUI ONCE in callback, so that it will only load when the plugin is started
|
|
if self.first_start == True:
|
|
self.first_start = False
|
|
self.dlg = ibpToolsDialog()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# PARAMS:
|
|
# Input folder
|
|
# Output folder
|
|
# Output version: ACAD9, ACAD10, ACAD12, ACAD14, ACAD2000, ACAD2004, ACAD2007, ACAD20010, ACAD2013, ACAD2018
|
|
# Output file type: DWG, DXF, DXB
|
|
# Recurse Input Folder: 0, 1
|
|
# Audit each file: 0, 1
|
|
# (Optional) Input files filter: *.DWG, *.DXF
|
|
|
|
def ibp_dwg_to_dxf(file, INPUT_FOLDER = tmp_folder, OUTPUT_FOLDER = tmp_folder, RECURSIVE = "0", AUDIT = "1"):
|
|
if INPUT_FOLDER != tmp_folder and OUTPUT_FOLDER == tmp_folder:
|
|
OUTPUT_FOLDER = INPUT_FOLDER
|
|
OUTVER = "ACAD2018"
|
|
OUTFORMAT = "DXF"
|
|
# Command to run
|
|
cmd = [TEIGHA_PATH, INPUT_FOLDER, OUTPUT_FOLDER, OUTVER, OUTFORMAT, RECURSIVE, AUDIT, file]
|
|
|
|
# Run
|
|
subprocess.run(cmd, shell=True)
|
|
|
|
|
|
def ibp_dxf_to_dwg(file, INPUT_FOLDER = tmp_folder, OUTPUT_FOLDER = tmp_folder, RECURSIVE = "0", AUDIT = "1"):
|
|
if INPUT_FOLDER != tmp_folder and OUTPUT_FOLDER == tmp_folder:
|
|
OUTPUT_FOLDER = INPUT_FOLDER
|
|
OUTVER = "ACAD2018"
|
|
OUTFORMAT = "DWG"
|
|
# Command to run
|
|
cmd = [TEIGHA_PATH, INPUT_FOLDER, OUTPUT_FOLDER, OUTVER, OUTFORMAT, RECURSIVE, AUDIT, file]
|
|
|
|
# Run
|
|
subprocess.run(cmd, shell=True)
|
|
|