splitting qgis-using functions, testing others
This commit is contained in:
Родитель
4530aab115
Коммит
968231680f
|
@ -25,8 +25,6 @@ try:
|
|||
iface.pluginToolBar().setVisible(True)
|
||||
|
||||
# Ensure dependencies are installed in the machine
|
||||
# from speckle.utils import enable_remote_debugging
|
||||
# enable_remote_debugging()
|
||||
startDebugger()
|
||||
ensure_dependencies("QGIS")
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
$Env:Pythonpath = ";" + $Env:OSGEO4W_ROOT + "\apps\qgis\python"
|
||||
setx PYTHONPATH "$Env:Pythonpath"
|
||||
|
||||
$Env:OSGEO4W_ROOT = "C:\OSGeo4W64"
|
||||
$Env:Path = $Env:OSGEO4W_ROOT + "\apps\qgis\bin;" + $Env:Path
|
||||
setx PATH "$Env:Path"
|
|
@ -13,7 +13,7 @@ import shutil
|
|||
from speckle.utils.utils import get_qgis_python_path
|
||||
|
||||
_user_data_env_var = "SPECKLE_USERDATA_PATH"
|
||||
_debug = False
|
||||
_debug = True
|
||||
_vs_code_directory = os.path.expanduser(
|
||||
"~\.vscode\extensions\ms-python.python-2023.20.0\pythonFiles\lib\python"
|
||||
)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -27,3 +27,6 @@ devtools = "^0.12.2"
|
|||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
pythonpath = "speckle"
|
||||
|
|
|
@ -1,351 +1 @@
|
|||
""" This module contains all geometry conversion functionality To and From Speckle."""
|
||||
|
||||
from numpy import isin
|
||||
from specklepy.objects.GIS.geometry import GisLineElement, GisPointElement, GisPolygonElement
|
||||
#from speckle.utils.panel_logging import logger
|
||||
from typing import List, Sequence, Union
|
||||
import inspect
|
||||
|
||||
from qgis.core import (QgsGeometry, QgsWkbTypes, QgsMultiPoint,
|
||||
QgsAbstractGeometry, QgsMultiLineString, QgsMultiPolygon,
|
||||
QgsCircularString, QgsLineString, QgsRasterLayer,QgsVectorLayer, QgsFeature,
|
||||
QgsUnitTypes)
|
||||
|
||||
from speckle.converter.geometry.utils import getPolygonFeatureHeight
|
||||
from speckle.converter.geometry.mesh import meshToNative
|
||||
from speckle.converter.geometry.point import pointToNative, pointToSpeckle
|
||||
from speckle.converter.geometry.polygon import (polygonToSpeckleMesh, getZaxisTranslation,
|
||||
isFlat, polygonToSpeckle,
|
||||
polygonToNative, getPolyBoundaryVoids)
|
||||
from speckle.converter.geometry.polyline import (polylineFromVerticesToSpeckle, unknownLineToSpeckle,
|
||||
compoudCurveToSpeckle, anyLineToSpeckle, polylineToSpeckle,
|
||||
arcToSpeckle, getArcCenter, lineToNative, polylineToNative,
|
||||
ellipseToNative, curveToNative, arcToNative, circleToNative,
|
||||
polycurveToNative, speckleEllipseToPoints)
|
||||
from speckle.converter.geometry.utils import addCorrectUnits
|
||||
|
||||
from specklepy.objects import Base
|
||||
from specklepy.objects.geometry import Line, Mesh, Point, Polyline, Curve, Arc, Circle, Ellipse, Polycurve
|
||||
from specklepy.objects.GIS.geometry import GisPolygonGeometry
|
||||
|
||||
from speckle.utils.panel_logging import logToUser
|
||||
from speckle.converter.layers.utils import getElevationLayer, isAppliedLayerTransformByKeywords
|
||||
|
||||
def convertToSpeckle(feature: QgsFeature, layer: QgsVectorLayer or QgsRasterLayer, dataStorage) -> Union[Base, Sequence[Base], None]:
|
||||
"""Converts the provided layer feature to Speckle objects"""
|
||||
try:
|
||||
#print("convertToSpeckle")
|
||||
#print(dataStorage)
|
||||
iterations = 0
|
||||
try:
|
||||
geom: QgsGeometry = feature.geometry()
|
||||
except:
|
||||
geom: QgsGeometry = feature
|
||||
geomSingleType = QgsWkbTypes.isSingleType(geom.wkbType())
|
||||
geomType = geom.type()
|
||||
type = geom.wkbType()
|
||||
units = dataStorage.currentUnits #QgsUnitTypes.encodeUnit(dataStorage.project.crs().mapUnits())
|
||||
|
||||
if geomType == QgsWkbTypes.PointGeometry:
|
||||
# the geometry type can be of single or multi type
|
||||
if geomSingleType:
|
||||
result = pointToSpeckle(geom.constGet(), feature, layer, dataStorage)
|
||||
result.units = units
|
||||
result = [result]
|
||||
#return result
|
||||
else:
|
||||
result = [pointToSpeckle(pt, feature, layer, dataStorage) for pt in geom.parts()]
|
||||
for r in result: r.units = units
|
||||
#return result
|
||||
|
||||
element = GisPointElement(units = units, geometry = result)
|
||||
return element, iterations
|
||||
|
||||
elif geomType == QgsWkbTypes.LineGeometry: # 1
|
||||
if geomSingleType:
|
||||
result = anyLineToSpeckle(geom, feature, layer, dataStorage)
|
||||
result = addCorrectUnits(result, dataStorage)
|
||||
result = [result]
|
||||
#return result
|
||||
else:
|
||||
result = [anyLineToSpeckle(poly, feature, layer, dataStorage) for poly in geom.parts()]
|
||||
for r in result: r = addCorrectUnits(r, dataStorage)
|
||||
#if len(result) == 1: result = result[0]
|
||||
#return result
|
||||
|
||||
element = GisLineElement(units = units, geometry = result)
|
||||
return element, iterations
|
||||
|
||||
if type == QgsWkbTypes.CircularString or type == QgsWkbTypes.CircularStringZ or type == QgsWkbTypes.CircularStringM or type == QgsWkbTypes.CircularStringZM: #Type (not GeometryType)
|
||||
if geomSingleType:
|
||||
result = arcToSpeckle(geom, feature, layer, dataStorage)
|
||||
result.units = units
|
||||
return result
|
||||
else:
|
||||
result = [arcToSpeckle(poly, feature, layer, dataStorage) for poly in geom.parts()]
|
||||
for r in result: r.units = units
|
||||
return result
|
||||
elif type == QgsWkbTypes.CompoundCurve or type == QgsWkbTypes.CompoundCurveZ or type == QgsWkbTypes.CompoundCurveM or type == QgsWkbTypes.CompoundCurveZM: # 9, 1009, 2009, 3009
|
||||
if "CircularString" in str(geom):
|
||||
all_pts = [pt for pt in geom.vertices()]
|
||||
if len(all_pts) == 3:
|
||||
result = arcToSpeckle(geom, feature, layer, dataStorage)
|
||||
result.units = units
|
||||
try: result.plane.origin.units = units
|
||||
except: pass
|
||||
return result
|
||||
else:
|
||||
result = compoudCurveToSpeckle(geom, feature, layer, dataStorage)
|
||||
result.units = units
|
||||
return result
|
||||
else: return None
|
||||
elif geomSingleType: # type = 2
|
||||
result = polylineToSpeckle(geom, feature, layer, dataStorage)
|
||||
result.units = units
|
||||
return result
|
||||
else:
|
||||
result = [polylineToSpeckle(poly, feature, layer, dataStorage) for poly in geom.parts()]
|
||||
for r in result: r.units = units
|
||||
return result
|
||||
|
||||
# check if the layer was received from Mesh originally
|
||||
elif geomType == QgsWkbTypes.PolygonGeometry and not geomSingleType and layer.name().endswith("_as_Mesh") and "Speckle_ID" in layer.fields().names():
|
||||
result = polygonToSpeckleMesh(geom, feature, layer, dataStorage)
|
||||
if result is None: return None, None
|
||||
result.units = units
|
||||
for v in result.displayValue:
|
||||
if v is not None:
|
||||
v.units = units
|
||||
|
||||
if not isinstance(result, List):
|
||||
result = [result]
|
||||
element = GisPolygonElement(units = units, geometry = result)
|
||||
return element, iterations
|
||||
elif geomType == QgsWkbTypes.PolygonGeometry: # 2
|
||||
|
||||
height = getPolygonFeatureHeight(feature, layer, dataStorage)
|
||||
elevationLayer = getElevationLayer(dataStorage)
|
||||
translationZaxis = None
|
||||
|
||||
if geomSingleType:
|
||||
|
||||
try: boundaryPts = [ v[1] for v in enumerate(geom.exteriorRing().vertices())]
|
||||
except: boundaryPts = [ v[1] for v in enumerate(geom.constGet().exteriorRing().vertices())]
|
||||
if height is not None:
|
||||
if isFlat(boundaryPts) is False:
|
||||
logToUser("Extrusion can only be applied to flat polygons", level = 1, func = inspect.stack()[0][3])
|
||||
height = None
|
||||
if elevationLayer is not None and isAppliedLayerTransformByKeywords(layer, ["extrude", "polygon", "project", "elevation"], [], dataStorage) is True:
|
||||
if isFlat(boundaryPts) is False:
|
||||
logToUser("Geometry projections can only be applied to flat polygons", level = 1, func = inspect.stack()[0][3])
|
||||
else:
|
||||
translationZaxis = getZaxisTranslation(layer, boundaryPts, dataStorage)
|
||||
if translationZaxis is None:
|
||||
logToUser("Some polygons are outside the elevation layer extent or extrusion value is Null", level = 1, func = inspect.stack()[0][3])
|
||||
return None, None
|
||||
|
||||
result, iterations = polygonToSpeckle(geom, feature, layer, height, translationZaxis, dataStorage)
|
||||
if result is None: return None, None
|
||||
result.units = units
|
||||
if result.boundary is not None:
|
||||
result.boundary.units = units
|
||||
for v in result.voids:
|
||||
if v is not None:
|
||||
v.units = units
|
||||
try: # if mesh creation failed, displayValue stays None
|
||||
for v in result.displayValue:
|
||||
if v is not None:
|
||||
v.units = units
|
||||
except: pass
|
||||
|
||||
if not isinstance(result, List):
|
||||
result = [result]
|
||||
element = GisPolygonElement(units = units, geometry = result)
|
||||
|
||||
|
||||
else:
|
||||
result = []
|
||||
for poly in geom.parts():
|
||||
|
||||
try: boundaryPts = [ v[1] for v in enumerate(poly.exteriorRing().vertices())]
|
||||
except: boundaryPts = [ v[1] for v in enumerate(poly.constGet().exteriorRing().vertices())]
|
||||
if height is not None:
|
||||
if isFlat(boundaryPts) is False:
|
||||
logToUser("Extrusion can only be applied to flat polygons", level = 1, func = inspect.stack()[0][3])
|
||||
height = None
|
||||
if elevationLayer is not None and isAppliedLayerTransformByKeywords(layer, ["extrude", "polygon", "project", "elevation"], [], dataStorage) is True:
|
||||
if isFlat(boundaryPts) is False:
|
||||
logToUser("Geometry projections can only be applied to flat polygons", level = 1, func = inspect.stack()[0][3])
|
||||
else:
|
||||
translationZaxis = getZaxisTranslation(layer, boundaryPts, dataStorage)
|
||||
if translationZaxis is None:
|
||||
logToUser("Some polygons are outside the elevation layer extent or extrusion value is Null", level = 1, func = inspect.stack()[0][3])
|
||||
continue
|
||||
|
||||
polygon, iterations = polygonToSpeckle(poly, feature, layer, height, translationZaxis, dataStorage)
|
||||
result.append( polygon )
|
||||
for r in result:
|
||||
if r is None: continue
|
||||
r.units = units
|
||||
r.boundary.units = units
|
||||
for v in r.voids:
|
||||
if v is not None:
|
||||
v.units = units
|
||||
for v in r.displayValue:
|
||||
if v is not None:
|
||||
v.units = units
|
||||
|
||||
element = GisPolygonElement(units = units, geometry = result)
|
||||
|
||||
return element, iterations
|
||||
else:
|
||||
logToUser("Unsupported or invalid geometry", level = 1, func = inspect.stack()[0][3])
|
||||
return None, None
|
||||
except Exception as e:
|
||||
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
||||
return None, None
|
||||
|
||||
|
||||
def convertToNative(base: Base, dataStorage) -> Union[QgsGeometry, None]:
|
||||
"""Converts any given base object to QgsGeometry."""
|
||||
try:
|
||||
#print("convertToNative")
|
||||
converted = None
|
||||
conversions = [
|
||||
(Point, pointToNative),
|
||||
(Line, lineToNative),
|
||||
(Polyline, polylineToNative),
|
||||
(Curve, curveToNative),
|
||||
(Arc, arcToNative),
|
||||
(Ellipse, ellipseToNative),
|
||||
(Circle, circleToNative),
|
||||
(Mesh, meshToNative),
|
||||
(Polycurve, polycurveToNative),
|
||||
(GisPolygonGeometry, polygonToNative),
|
||||
(Base, polygonToNative), # temporary solution for polygons (Speckle has no type Polygon yet)
|
||||
]
|
||||
|
||||
for conversion in conversions:
|
||||
# distinguish normal QGIS polygons and the ones sent as Mesh only
|
||||
try:
|
||||
if isinstance(base, GisPolygonGeometry):
|
||||
if base.boundary is None:
|
||||
try:
|
||||
converted: QgsMultiPolygon = meshToNative(base.displayValue, dataStorage )
|
||||
except:
|
||||
converted: QgsMultiPolygon = meshToNative(base['@displayValue'], dataStorage )
|
||||
break
|
||||
elif isinstance(base, conversion[0]):
|
||||
converted = conversion[1](base, dataStorage)
|
||||
break
|
||||
else:
|
||||
# for older commits
|
||||
boundary = base.boundary # will throw exception if not polygon
|
||||
if boundary is None:
|
||||
try:
|
||||
converted: QgsMultiPolygon = meshToNative(base.displayValue, dataStorage )
|
||||
except:
|
||||
converted: QgsMultiPolygon = meshToNative(base['@displayValue'], dataStorage )
|
||||
break
|
||||
elif boundary is not None and isinstance(base, conversion[0]):
|
||||
converted = conversion[1](base, dataStorage)
|
||||
break
|
||||
|
||||
except: # if no "boundary" found (either old Mesh from QGIS or other object)
|
||||
|
||||
try: # check for a QGIS Mesh
|
||||
try:
|
||||
# if sent as Mesh
|
||||
colors = base.displayValue[0].colors # will throw exception
|
||||
if isinstance(base.displayValue[0], Mesh):
|
||||
converted: QgsMultiPolygon = meshToNative(base.displayValue, dataStorage ) # only called for Meshes created in QGIS before
|
||||
except:
|
||||
# if sent as Mesh
|
||||
colors = base['@displayValue'][0].colors # will throw exception
|
||||
if isinstance(base['@displayValue'][0], Mesh):
|
||||
converted: QgsMultiPolygon = meshToNative(base['@displayValue'], dataStorage ) # only called for Meshes created in QGIS before
|
||||
|
||||
except: # any other object
|
||||
if isinstance(base, conversion[0]):
|
||||
converted = conversion[1](base, dataStorage)
|
||||
break
|
||||
|
||||
return converted
|
||||
except Exception as e:
|
||||
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def multiPointToNative(items: List[Point], dataStorage) -> QgsMultiPoint:
|
||||
try:
|
||||
pts = QgsMultiPoint()
|
||||
for item in items:
|
||||
g = pointToNative(item, dataStorage)
|
||||
if g is not None:
|
||||
pts.addGeometry(g)
|
||||
return pts
|
||||
except Exception as e:
|
||||
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def multiPolylineToNative(items: List[Polyline], dataStorage) -> QgsMultiLineString:
|
||||
try:
|
||||
polys = QgsMultiLineString()
|
||||
for item in items:
|
||||
g = polylineToNative(item, dataStorage)
|
||||
if g is not None:
|
||||
polys.addGeometry(g)
|
||||
return polys
|
||||
except Exception as e:
|
||||
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def multiPolygonToNative(items: List[Base], dataStorage) -> QgsMultiPolygon:
|
||||
try:
|
||||
polygons = QgsMultiPolygon()
|
||||
for item in items:
|
||||
g = polygonToNative(item, dataStorage)
|
||||
if g is not None:
|
||||
polygons.addGeometry(g)
|
||||
return polygons
|
||||
except Exception as e:
|
||||
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def convertToNativeMulti(items: List[Base], dataStorage):
|
||||
try:
|
||||
first = items[0]
|
||||
if isinstance(first, Point):
|
||||
return multiPointToNative(items, dataStorage)
|
||||
elif isinstance(first, Line) or isinstance(first, Polyline):
|
||||
return multiPolylineToNative(items, dataStorage)
|
||||
#elif isinstance(first, Arc) or isinstance(first, Polycurve) or isinstance(first, Ellipse) or isinstance(first, Circle) or isinstance(first, Curve):
|
||||
# return [convertToNative(it, dataStorage) for it in items]
|
||||
elif isinstance(first, Base):
|
||||
try:
|
||||
displayVals = []
|
||||
for it in items:
|
||||
try: displayVals.extend(it.displayValue)
|
||||
except: displayVals.extend(it['@displayValue'])
|
||||
if isinstance(first, GisPolygonGeometry):
|
||||
if first.boundary is None:
|
||||
converted: QgsMultiPolygon = meshToNative(displayVals, dataStorage )
|
||||
return converted
|
||||
elif first["boundary"] is not None and first["voids"] is not None:
|
||||
return multiPolygonToNative(items, dataStorage)
|
||||
else:
|
||||
# for older commits
|
||||
boundary = first.boundary # will throw exception if not polygon
|
||||
if boundary is None:
|
||||
converted: QgsMultiPolygon = meshToNative(displayVals, dataStorage )
|
||||
return converted
|
||||
elif boundary is not None:
|
||||
return multiPolygonToNative(items, dataStorage)
|
||||
|
||||
except: # if no "boundary" found (either old Mesh from QGIS or other object)
|
||||
try:
|
||||
if first["boundary"] is not None and first["voids"] is not None:
|
||||
return multiPolygonToNative(items, dataStorage)
|
||||
except: return None
|
||||
except Exception as e:
|
||||
logToUser(e, level = 2, func = inspect.stack()[0][3])
|
||||
return None
|
|
@ -0,0 +1,528 @@
|
|||
from numpy import isin
|
||||
from specklepy.objects.GIS.geometry import (
|
||||
GisLineElement,
|
||||
GisPointElement,
|
||||
GisPolygonElement,
|
||||
)
|
||||
|
||||
# from speckle.utils.panel_logging import logger
|
||||
from typing import List, Sequence, Union
|
||||
import inspect
|
||||
|
||||
from qgis.core import (
|
||||
QgsGeometry,
|
||||
QgsWkbTypes,
|
||||
QgsMultiPoint,
|
||||
QgsAbstractGeometry,
|
||||
QgsMultiLineString,
|
||||
QgsMultiPolygon,
|
||||
QgsCircularString,
|
||||
QgsLineString,
|
||||
QgsRasterLayer,
|
||||
QgsVectorLayer,
|
||||
QgsFeature,
|
||||
QgsUnitTypes,
|
||||
)
|
||||
|
||||
from speckle.converter.geometry.utils import getPolygonFeatureHeight
|
||||
from speckle.converter.geometry.mesh import meshToNative
|
||||
from speckle.converter.geometry.point import pointToNative, pointToSpeckle
|
||||
from speckle.converter.geometry.polygon import (
|
||||
polygonToSpeckleMesh,
|
||||
getZaxisTranslation,
|
||||
isFlat,
|
||||
polygonToSpeckle,
|
||||
polygonToNative,
|
||||
getPolyBoundaryVoids,
|
||||
)
|
||||
from speckle.converter.geometry.polyline import (
|
||||
polylineFromVerticesToSpeckle,
|
||||
unknownLineToSpeckle,
|
||||
compoudCurveToSpeckle,
|
||||
anyLineToSpeckle,
|
||||
polylineToSpeckle,
|
||||
arcToSpeckle,
|
||||
getArcCenter,
|
||||
lineToNative,
|
||||
polylineToNative,
|
||||
ellipseToNative,
|
||||
curveToNative,
|
||||
arcToNative,
|
||||
circleToNative,
|
||||
polycurveToNative,
|
||||
speckleEllipseToPoints,
|
||||
)
|
||||
from speckle.converter.geometry.utils import addCorrectUnits
|
||||
|
||||
from specklepy.objects import Base
|
||||
from specklepy.objects.geometry import (
|
||||
Line,
|
||||
Mesh,
|
||||
Point,
|
||||
Polyline,
|
||||
Curve,
|
||||
Arc,
|
||||
Circle,
|
||||
Ellipse,
|
||||
Polycurve,
|
||||
)
|
||||
from specklepy.objects.GIS.geometry import GisPolygonGeometry
|
||||
|
||||
from speckle.utils.panel_logging import logToUser
|
||||
from speckle.converter.layers.utils import (
|
||||
getElevationLayer,
|
||||
isAppliedLayerTransformByKeywords,
|
||||
)
|
||||
|
||||
|
||||
def convertToSpeckle(
|
||||
feature: QgsFeature, layer: QgsVectorLayer or QgsRasterLayer, dataStorage
|
||||
) -> Union[Base, Sequence[Base], None]:
|
||||
"""Converts the provided layer feature to Speckle objects"""
|
||||
try:
|
||||
# print("convertToSpeckle")
|
||||
# print(dataStorage)
|
||||
iterations = 0
|
||||
try:
|
||||
geom: QgsGeometry = feature.geometry()
|
||||
except:
|
||||
geom: QgsGeometry = feature
|
||||
geomSingleType = QgsWkbTypes.isSingleType(geom.wkbType())
|
||||
geomType = geom.type()
|
||||
type = geom.wkbType()
|
||||
units = (
|
||||
dataStorage.currentUnits
|
||||
) # QgsUnitTypes.encodeUnit(dataStorage.project.crs().mapUnits())
|
||||
|
||||
if geomType == QgsWkbTypes.PointGeometry:
|
||||
# the geometry type can be of single or multi type
|
||||
if geomSingleType:
|
||||
result = pointToSpeckle(geom.constGet(), feature, layer, dataStorage)
|
||||
result.units = units
|
||||
result = [result]
|
||||
# return result
|
||||
else:
|
||||
result = [
|
||||
pointToSpeckle(pt, feature, layer, dataStorage)
|
||||
for pt in geom.parts()
|
||||
]
|
||||
for r in result:
|
||||
r.units = units
|
||||
# return result
|
||||
|
||||
element = GisPointElement(units=units, geometry=result)
|
||||
return element, iterations
|
||||
|
||||
elif geomType == QgsWkbTypes.LineGeometry: # 1
|
||||
if geomSingleType:
|
||||
result = anyLineToSpeckle(geom, feature, layer, dataStorage)
|
||||
result = addCorrectUnits(result, dataStorage)
|
||||
result = [result]
|
||||
# return result
|
||||
else:
|
||||
result = [
|
||||
anyLineToSpeckle(poly, feature, layer, dataStorage)
|
||||
for poly in geom.parts()
|
||||
]
|
||||
for r in result:
|
||||
r = addCorrectUnits(r, dataStorage)
|
||||
# if len(result) == 1: result = result[0]
|
||||
# return result
|
||||
|
||||
element = GisLineElement(units=units, geometry=result)
|
||||
return element, iterations
|
||||
|
||||
if (
|
||||
type == QgsWkbTypes.CircularString
|
||||
or type == QgsWkbTypes.CircularStringZ
|
||||
or type == QgsWkbTypes.CircularStringM
|
||||
or type == QgsWkbTypes.CircularStringZM
|
||||
): # Type (not GeometryType)
|
||||
if geomSingleType:
|
||||
result = arcToSpeckle(geom, feature, layer, dataStorage)
|
||||
result.units = units
|
||||
return result
|
||||
else:
|
||||
result = [
|
||||
arcToSpeckle(poly, feature, layer, dataStorage)
|
||||
for poly in geom.parts()
|
||||
]
|
||||
for r in result:
|
||||
r.units = units
|
||||
return result
|
||||
elif (
|
||||
type == QgsWkbTypes.CompoundCurve
|
||||
or type == QgsWkbTypes.CompoundCurveZ
|
||||
or type == QgsWkbTypes.CompoundCurveM
|
||||
or type == QgsWkbTypes.CompoundCurveZM
|
||||
): # 9, 1009, 2009, 3009
|
||||
if "CircularString" in str(geom):
|
||||
all_pts = [pt for pt in geom.vertices()]
|
||||
if len(all_pts) == 3:
|
||||
result = arcToSpeckle(geom, feature, layer, dataStorage)
|
||||
result.units = units
|
||||
try:
|
||||
result.plane.origin.units = units
|
||||
except:
|
||||
pass
|
||||
return result
|
||||
else:
|
||||
result = compoudCurveToSpeckle(
|
||||
geom, feature, layer, dataStorage
|
||||
)
|
||||
result.units = units
|
||||
return result
|
||||
else:
|
||||
return None
|
||||
elif geomSingleType: # type = 2
|
||||
result = polylineToSpeckle(geom, feature, layer, dataStorage)
|
||||
result.units = units
|
||||
return result
|
||||
else:
|
||||
result = [
|
||||
polylineToSpeckle(poly, feature, layer, dataStorage)
|
||||
for poly in geom.parts()
|
||||
]
|
||||
for r in result:
|
||||
r.units = units
|
||||
return result
|
||||
|
||||
# check if the layer was received from Mesh originally
|
||||
elif (
|
||||
geomType == QgsWkbTypes.PolygonGeometry
|
||||
and not geomSingleType
|
||||
and layer.name().endswith("_as_Mesh")
|
||||
and "Speckle_ID" in layer.fields().names()
|
||||
):
|
||||
result = polygonToSpeckleMesh(geom, feature, layer, dataStorage)
|
||||
if result is None:
|
||||
return None, None
|
||||
result.units = units
|
||||
for v in result.displayValue:
|
||||
if v is not None:
|
||||
v.units = units
|
||||
|
||||
if not isinstance(result, List):
|
||||
result = [result]
|
||||
element = GisPolygonElement(units=units, geometry=result)
|
||||
return element, iterations
|
||||
elif geomType == QgsWkbTypes.PolygonGeometry: # 2
|
||||
height = getPolygonFeatureHeight(feature, layer, dataStorage)
|
||||
elevationLayer = getElevationLayer(dataStorage)
|
||||
translationZaxis = None
|
||||
|
||||
if geomSingleType:
|
||||
try:
|
||||
boundaryPts = [
|
||||
v[1] for v in enumerate(geom.exteriorRing().vertices())
|
||||
]
|
||||
except:
|
||||
boundaryPts = [
|
||||
v[1]
|
||||
for v in enumerate(geom.constGet().exteriorRing().vertices())
|
||||
]
|
||||
if height is not None:
|
||||
if isFlat(boundaryPts) is False:
|
||||
logToUser(
|
||||
"Extrusion can only be applied to flat polygons",
|
||||
level=1,
|
||||
func=inspect.stack()[0][3],
|
||||
)
|
||||
height = None
|
||||
if (
|
||||
elevationLayer is not None
|
||||
and isAppliedLayerTransformByKeywords(
|
||||
layer,
|
||||
["extrude", "polygon", "project", "elevation"],
|
||||
[],
|
||||
dataStorage,
|
||||
)
|
||||
is True
|
||||
):
|
||||
if isFlat(boundaryPts) is False:
|
||||
logToUser(
|
||||
"Geometry projections can only be applied to flat polygons",
|
||||
level=1,
|
||||
func=inspect.stack()[0][3],
|
||||
)
|
||||
else:
|
||||
translationZaxis = getZaxisTranslation(
|
||||
layer, boundaryPts, dataStorage
|
||||
)
|
||||
if translationZaxis is None:
|
||||
logToUser(
|
||||
"Some polygons are outside the elevation layer extent or extrusion value is Null",
|
||||
level=1,
|
||||
func=inspect.stack()[0][3],
|
||||
)
|
||||
return None, None
|
||||
|
||||
result, iterations = polygonToSpeckle(
|
||||
geom, feature, layer, height, translationZaxis, dataStorage
|
||||
)
|
||||
if result is None:
|
||||
return None, None
|
||||
result.units = units
|
||||
if result.boundary is not None:
|
||||
result.boundary.units = units
|
||||
for v in result.voids:
|
||||
if v is not None:
|
||||
v.units = units
|
||||
try: # if mesh creation failed, displayValue stays None
|
||||
for v in result.displayValue:
|
||||
if v is not None:
|
||||
v.units = units
|
||||
except:
|
||||
pass
|
||||
|
||||
if not isinstance(result, List):
|
||||
result = [result]
|
||||
element = GisPolygonElement(units=units, geometry=result)
|
||||
|
||||
else:
|
||||
result = []
|
||||
for poly in geom.parts():
|
||||
try:
|
||||
boundaryPts = [
|
||||
v[1] for v in enumerate(poly.exteriorRing().vertices())
|
||||
]
|
||||
except:
|
||||
boundaryPts = [
|
||||
v[1]
|
||||
for v in enumerate(
|
||||
poly.constGet().exteriorRing().vertices()
|
||||
)
|
||||
]
|
||||
if height is not None:
|
||||
if isFlat(boundaryPts) is False:
|
||||
logToUser(
|
||||
"Extrusion can only be applied to flat polygons",
|
||||
level=1,
|
||||
func=inspect.stack()[0][3],
|
||||
)
|
||||
height = None
|
||||
if (
|
||||
elevationLayer is not None
|
||||
and isAppliedLayerTransformByKeywords(
|
||||
layer,
|
||||
["extrude", "polygon", "project", "elevation"],
|
||||
[],
|
||||
dataStorage,
|
||||
)
|
||||
is True
|
||||
):
|
||||
if isFlat(boundaryPts) is False:
|
||||
logToUser(
|
||||
"Geometry projections can only be applied to flat polygons",
|
||||
level=1,
|
||||
func=inspect.stack()[0][3],
|
||||
)
|
||||
else:
|
||||
translationZaxis = getZaxisTranslation(
|
||||
layer, boundaryPts, dataStorage
|
||||
)
|
||||
if translationZaxis is None:
|
||||
logToUser(
|
||||
"Some polygons are outside the elevation layer extent or extrusion value is Null",
|
||||
level=1,
|
||||
func=inspect.stack()[0][3],
|
||||
)
|
||||
continue
|
||||
|
||||
polygon, iterations = polygonToSpeckle(
|
||||
poly, feature, layer, height, translationZaxis, dataStorage
|
||||
)
|
||||
result.append(polygon)
|
||||
for r in result:
|
||||
if r is None:
|
||||
continue
|
||||
r.units = units
|
||||
r.boundary.units = units
|
||||
for v in r.voids:
|
||||
if v is not None:
|
||||
v.units = units
|
||||
for v in r.displayValue:
|
||||
if v is not None:
|
||||
v.units = units
|
||||
|
||||
element = GisPolygonElement(units=units, geometry=result)
|
||||
|
||||
return element, iterations
|
||||
else:
|
||||
logToUser(
|
||||
"Unsupported or invalid geometry", level=1, func=inspect.stack()[0][3]
|
||||
)
|
||||
return None, None
|
||||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return None, None
|
||||
|
||||
|
||||
def convertToNative(base: Base, dataStorage) -> Union[QgsGeometry, None]:
|
||||
"""Converts any given base object to QgsGeometry."""
|
||||
try:
|
||||
# print("convertToNative")
|
||||
converted = None
|
||||
conversions = [
|
||||
(Point, pointToNative),
|
||||
(Line, lineToNative),
|
||||
(Polyline, polylineToNative),
|
||||
(Curve, curveToNative),
|
||||
(Arc, arcToNative),
|
||||
(Ellipse, ellipseToNative),
|
||||
(Circle, circleToNative),
|
||||
(Mesh, meshToNative),
|
||||
(Polycurve, polycurveToNative),
|
||||
(GisPolygonGeometry, polygonToNative),
|
||||
(
|
||||
Base,
|
||||
polygonToNative,
|
||||
), # temporary solution for polygons (Speckle has no type Polygon yet)
|
||||
]
|
||||
|
||||
for conversion in conversions:
|
||||
# distinguish normal QGIS polygons and the ones sent as Mesh only
|
||||
try:
|
||||
if isinstance(base, GisPolygonGeometry):
|
||||
if base.boundary is None:
|
||||
try:
|
||||
converted: QgsMultiPolygon = meshToNative(
|
||||
base.displayValue, dataStorage
|
||||
)
|
||||
except:
|
||||
converted: QgsMultiPolygon = meshToNative(
|
||||
base["@displayValue"], dataStorage
|
||||
)
|
||||
break
|
||||
elif isinstance(base, conversion[0]):
|
||||
converted = conversion[1](base, dataStorage)
|
||||
break
|
||||
else:
|
||||
# for older commits
|
||||
boundary = base.boundary # will throw exception if not polygon
|
||||
if boundary is None:
|
||||
try:
|
||||
converted: QgsMultiPolygon = meshToNative(
|
||||
base.displayValue, dataStorage
|
||||
)
|
||||
except:
|
||||
converted: QgsMultiPolygon = meshToNative(
|
||||
base["@displayValue"], dataStorage
|
||||
)
|
||||
break
|
||||
elif boundary is not None and isinstance(base, conversion[0]):
|
||||
converted = conversion[1](base, dataStorage)
|
||||
break
|
||||
|
||||
except: # if no "boundary" found (either old Mesh from QGIS or other object)
|
||||
try: # check for a QGIS Mesh
|
||||
try:
|
||||
# if sent as Mesh
|
||||
colors = base.displayValue[0].colors # will throw exception
|
||||
if isinstance(base.displayValue[0], Mesh):
|
||||
converted: QgsMultiPolygon = meshToNative(
|
||||
base.displayValue, dataStorage
|
||||
) # only called for Meshes created in QGIS before
|
||||
except:
|
||||
# if sent as Mesh
|
||||
colors = base["@displayValue"][0].colors # will throw exception
|
||||
if isinstance(base["@displayValue"][0], Mesh):
|
||||
converted: QgsMultiPolygon = meshToNative(
|
||||
base["@displayValue"], dataStorage
|
||||
) # only called for Meshes created in QGIS before
|
||||
|
||||
except: # any other object
|
||||
if isinstance(base, conversion[0]):
|
||||
converted = conversion[1](base, dataStorage)
|
||||
break
|
||||
|
||||
return converted
|
||||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
|
||||
def multiPointToNative(items: List[Point], dataStorage) -> QgsMultiPoint:
|
||||
try:
|
||||
pts = QgsMultiPoint()
|
||||
for item in items:
|
||||
g = pointToNative(item, dataStorage)
|
||||
if g is not None:
|
||||
pts.addGeometry(g)
|
||||
return pts
|
||||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
|
||||
def multiPolylineToNative(items: List[Polyline], dataStorage) -> QgsMultiLineString:
|
||||
try:
|
||||
polys = QgsMultiLineString()
|
||||
for item in items:
|
||||
g = polylineToNative(item, dataStorage)
|
||||
if g is not None:
|
||||
polys.addGeometry(g)
|
||||
return polys
|
||||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
|
||||
def multiPolygonToNative(items: List[Base], dataStorage) -> QgsMultiPolygon:
|
||||
try:
|
||||
polygons = QgsMultiPolygon()
|
||||
for item in items:
|
||||
g = polygonToNative(item, dataStorage)
|
||||
if g is not None:
|
||||
polygons.addGeometry(g)
|
||||
return polygons
|
||||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
|
||||
def convertToNativeMulti(items: List[Base], dataStorage):
|
||||
try:
|
||||
first = items[0]
|
||||
if isinstance(first, Point):
|
||||
return multiPointToNative(items, dataStorage)
|
||||
elif isinstance(first, Line) or isinstance(first, Polyline):
|
||||
return multiPolylineToNative(items, dataStorage)
|
||||
# elif isinstance(first, Arc) or isinstance(first, Polycurve) or isinstance(first, Ellipse) or isinstance(first, Circle) or isinstance(first, Curve):
|
||||
# return [convertToNative(it, dataStorage) for it in items]
|
||||
elif isinstance(first, Base):
|
||||
try:
|
||||
displayVals = []
|
||||
for it in items:
|
||||
try:
|
||||
displayVals.extend(it.displayValue)
|
||||
except:
|
||||
displayVals.extend(it["@displayValue"])
|
||||
if isinstance(first, GisPolygonGeometry):
|
||||
if first.boundary is None:
|
||||
converted: QgsMultiPolygon = meshToNative(
|
||||
displayVals, dataStorage
|
||||
)
|
||||
return converted
|
||||
elif first["boundary"] is not None and first["voids"] is not None:
|
||||
return multiPolygonToNative(items, dataStorage)
|
||||
else:
|
||||
# for older commits
|
||||
boundary = first.boundary # will throw exception if not polygon
|
||||
if boundary is None:
|
||||
converted: QgsMultiPolygon = meshToNative(
|
||||
displayVals, dataStorage
|
||||
)
|
||||
return converted
|
||||
elif boundary is not None:
|
||||
return multiPolygonToNative(items, dataStorage)
|
||||
|
||||
except: # if no "boundary" found (either old Mesh from QGIS or other object)
|
||||
try:
|
||||
if first["boundary"] is not None and first["voids"] is not None:
|
||||
return multiPolygonToNative(items, dataStorage)
|
||||
except:
|
||||
return None
|
||||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return None
|
|
@ -15,7 +15,7 @@ from speckle.converter.layers.utils import get_scale_factor, get_scale_factor_to
|
|||
from speckle.utils.panel_logging import logToUser
|
||||
|
||||
from qgis.core import (
|
||||
Qgis, QgsPoint, QgsPointXY, QgsMultiPolygon, QgsPolygon, QgsLineString, QgsFeature, QgsVectorLayer
|
||||
QgsMultiPolygon, QgsPolygon, QgsLineString, QgsFeature, QgsVectorLayer
|
||||
)
|
||||
#from panda3d.core import Triangulator
|
||||
|
||||
|
|
|
@ -10,31 +10,6 @@ from speckle.converter.layers.symbology import featureColorfromNativeRenderer
|
|||
from speckle.utils.panel_logging import logToUser
|
||||
|
||||
|
||||
def applyOffsetsRotation(x: float, y: float, dataStorage): # on Send
|
||||
try:
|
||||
offset_x = dataStorage.crs_offset_x
|
||||
offset_y = dataStorage.crs_offset_y
|
||||
rotation = dataStorage.crs_rotation
|
||||
if offset_x is not None and isinstance(offset_x, float):
|
||||
x -= offset_x
|
||||
if offset_y is not None and isinstance(offset_y, float):
|
||||
y -= offset_y
|
||||
if (
|
||||
rotation is not None
|
||||
and isinstance(rotation, float)
|
||||
and -360 < rotation < 360
|
||||
):
|
||||
a = rotation * math.pi / 180
|
||||
x2 = x * math.cos(a) + y * math.sin(a)
|
||||
y2 = -x * math.sin(a) + y * math.cos(a)
|
||||
x = x2
|
||||
y = y2
|
||||
return x, y
|
||||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return None, None
|
||||
|
||||
|
||||
def pointToSpeckle(
|
||||
pt: QgsPoint or QgsPointXY, feature: QgsFeature, layer: QgsVectorLayer, dataStorage
|
||||
):
|
||||
|
|
|
@ -423,8 +423,6 @@ def polycurveToNative(poly: Polycurve, dataStorage) -> QgsLineString:
|
|||
curve = QgsLineString(points)
|
||||
return curve
|
||||
except: curve = None
|
||||
|
||||
#new_curve = QgsLineString(points)
|
||||
return curve
|
||||
|
||||
except Exception as e:
|
||||
|
@ -432,42 +430,6 @@ def polycurveToNative(poly: Polycurve, dataStorage) -> QgsLineString:
|
|||
return
|
||||
|
||||
|
||||
r'''
|
||||
def arcToQgisPoints(poly: Arc):
|
||||
points = []
|
||||
angle1 = atan( abs ((poly.startPoint.y - poly.plane.origin.y) / (poly.startPoint.x - poly.plane.origin.x) )) # between 0 and pi/2
|
||||
if poly.plane.origin.x < poly.startPoint.x and poly.plane.origin.y > poly.startPoint.y: angle1 = 2*math.pi - angle1
|
||||
if poly.plane.origin.x > poly.startPoint.x and poly.plane.origin.y > poly.startPoint.y: angle1 = math.pi + angle1
|
||||
if poly.plane.origin.x > poly.startPoint.x and poly.plane.origin.y < poly.startPoint.y: angle1 = math.pi - angle1
|
||||
|
||||
angle2 = atan( abs ((poly.endPoint.y - poly.plane.origin.y) / (poly.endPoint.x - poly.plane.origin.x) )) # between 0 and pi/2
|
||||
if poly.plane.origin.x < poly.endPoint.x and poly.plane.origin.y > poly.endPoint.y: angle2 = 2*math.pi - angle2
|
||||
if poly.plane.origin.x > poly.endPoint.x and poly.plane.origin.y > poly.endPoint.y: angle2 = math.pi + angle2
|
||||
if poly.plane.origin.x > poly.endPoint.x and poly.plane.origin.y < poly.endPoint.y: angle2 = math.pi - angle2
|
||||
|
||||
try: interval = (poly.endAngle - poly.startAngle)
|
||||
except: interval = (angle2-angle1)
|
||||
try:
|
||||
pointsNum = math.floor( abs(interval)) * 12
|
||||
if pointsNum <4: pointsNum = 4
|
||||
points.append(pointToNative(poly.startPoint))
|
||||
|
||||
for i in range(1, pointsNum + 1):
|
||||
k = i/pointsNum # to reset values from 1/10 to 1
|
||||
if poly.plane.normal.z == 0: normal = 1
|
||||
else: normal = poly.plane.normal.z
|
||||
angle = angle1 + k * interval * normal
|
||||
pt = Point( x = poly.plane.origin.x + poly.radius * cos(angle), y = poly.plane.origin.y + poly.radius * sin(angle), z = 0)
|
||||
pt.units = poly.startPoint.units
|
||||
points.append(pointToNative(pt))
|
||||
points.append(pointToNative(poly.endPoint))
|
||||
|
||||
curve = QgsLineString(points)
|
||||
return curve
|
||||
except: return None
|
||||
'''
|
||||
|
||||
|
||||
def speckleEllipseToPoints(poly: Ellipse, dataStorage) -> List[Point]:
|
||||
try:
|
||||
qgsLineStr = ellipseToNative(poly, dataStorage)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import inspect
|
||||
from math import asin, cos, sin, atan
|
||||
from math import cos, sin, atan
|
||||
import math
|
||||
import random
|
||||
from specklepy.objects.geometry import (
|
||||
|
@ -8,39 +8,28 @@ from specklepy.objects.geometry import (
|
|||
Polyline,
|
||||
Circle,
|
||||
Arc,
|
||||
Mesh,
|
||||
Polycurve,
|
||||
Vector,
|
||||
Vector
|
||||
)
|
||||
from specklepy.objects import Base
|
||||
from typing import List, Tuple, Union, Dict
|
||||
from speckle.converter.geometry.point import applyOffsetsRotation
|
||||
from specklepy.objects.geometry import Mesh
|
||||
from typing import Any, List, Tuple, Union, Dict
|
||||
|
||||
from shapely.geometry import Polygon
|
||||
from shapely.ops import triangulate
|
||||
import shapely.wkt
|
||||
import geopandas as gpd
|
||||
from geovoronoi import voronoi_regions_from_coords
|
||||
|
||||
# from speckle.converter.geometry.polyline import speckleArcCircleToPoints, specklePolycurveToPoints
|
||||
from speckle.utils.panel_logging import logToUser
|
||||
|
||||
# import time
|
||||
|
||||
import numpy as np
|
||||
|
||||
from qgis.core import (
|
||||
Qgis,
|
||||
QgsProject,
|
||||
QgsLayerTreeLayer,
|
||||
QgsFeature,
|
||||
QgsRasterLayer,
|
||||
QgsVectorLayer,
|
||||
QgsPoint,
|
||||
)
|
||||
|
||||
|
||||
def cross_product(pt1, pt2):
|
||||
def cross_product(
|
||||
pt1: Union[List[float], Tuple[float]], pt2: Union[List[float], Tuple[float]]
|
||||
) -> List[float]:
|
||||
if len(pt1) < 3 or len(pt2) < 3:
|
||||
raise ValueError(f"Not enough arguments for 3-dimentional point {pt1} or {pt2}")
|
||||
return [
|
||||
(pt1[1] * pt2[2]) - (pt1[2] * pt2[1]),
|
||||
(pt1[2] * pt2[0]) - (pt1[0] * pt2[2]),
|
||||
|
@ -48,11 +37,15 @@ def cross_product(pt1, pt2):
|
|||
]
|
||||
|
||||
|
||||
def dot(pt1: List, pt2: List):
|
||||
def dot(
|
||||
pt1: Union[List[float], Tuple[float]], pt2: Union[list[float], Tuple[float]]
|
||||
) -> float:
|
||||
if len(pt1) < 3 or len(pt2) < 3:
|
||||
raise ValueError(f"Not enough arguments for 3-dimentional point {pt1} or {pt2}")
|
||||
return (pt1[0] * pt2[0]) + (pt1[1] * pt2[1]) + (pt1[2] * pt2[2])
|
||||
|
||||
|
||||
def normalize(pt: List, tolerance=1e-10):
|
||||
def normalize(pt: Union[List[float], Tuple[float]], tolerance=1e-10) -> List[float]:
|
||||
magnitude = dot(pt, pt) ** 0.5
|
||||
if abs(magnitude - 1) < tolerance:
|
||||
return pt
|
||||
|
@ -65,7 +58,15 @@ def normalize(pt: List, tolerance=1e-10):
|
|||
return normalized_vector
|
||||
|
||||
|
||||
def createPlane(pt1: List, pt2: List, pt3: List):
|
||||
def createPlane(
|
||||
pt1: Union[List[float], Tuple[float]],
|
||||
pt2: Union[List[float], Tuple[float]],
|
||||
pt3: Union[List[float], Tuple[float]],
|
||||
) -> dict:
|
||||
if len(pt1) < 3 or len(pt2) < 3 or len(pt3) < 3:
|
||||
raise ValueError(
|
||||
f"Not enough arguments for 3-dimentional point {pt1}, {pt2} or {pt3}"
|
||||
)
|
||||
vector1to2 = [pt2[0] - pt1[0], pt2[1] - pt1[1], pt2[2] - pt1[2]]
|
||||
vector1to3 = [pt3[0] - pt1[0], pt3[1] - pt1[1], pt3[2] - pt1[2]]
|
||||
|
||||
|
@ -74,7 +75,14 @@ def createPlane(pt1: List, pt2: List, pt3: List):
|
|||
return {"origin": pt1, "normal": normal}
|
||||
|
||||
|
||||
def project_to_plane_on_z(point: List, plane: Dict):
|
||||
def project_to_plane_on_z(
|
||||
point: Union[List[float], Tuple[float]], plane: Dict
|
||||
) -> float:
|
||||
if len(point) < 2 or "normal" not in plane.keys() or "origin" not in plane.keys():
|
||||
raise ValueError(f"Invalid arguments for a point {point} or a plane {plane}")
|
||||
if plane["normal"][2] == 0:
|
||||
raise ValueError(f"Invalid arguments for a point {point} or a plane {plane}")
|
||||
|
||||
d = dot(plane["normal"], plane["origin"])
|
||||
z_value_on_plane = (
|
||||
d - (plane["normal"][0] * point[0]) - (plane["normal"][1] * point[1])
|
||||
|
@ -82,7 +90,12 @@ def project_to_plane_on_z(point: List, plane: Dict):
|
|||
return z_value_on_plane
|
||||
|
||||
|
||||
def projectToPolygon(point: List, polygonPts: List):
|
||||
def projectToPolygon(
|
||||
point: Union[List[float], Tuple[float]],
|
||||
polygonPts: List[List[float]],
|
||||
) -> float:
|
||||
if len(point) < 2:
|
||||
raise ValueError(f"Not enough arguments for a point {point}")
|
||||
if len(polygonPts) < 3:
|
||||
return 0
|
||||
pt1 = polygonPts[0]
|
||||
|
@ -96,36 +109,95 @@ def projectToPolygon(point: List, polygonPts: List):
|
|||
return z
|
||||
|
||||
|
||||
def triangulatePolygon(geom, dataStorage):
|
||||
def getPolyPtsSegments(geom: Any, dataStorage: "DataStorage"):
|
||||
vertices = []
|
||||
vertices3d = []
|
||||
segmList = []
|
||||
holes = []
|
||||
try:
|
||||
# import triangle as tr
|
||||
vertices = [] # only outer
|
||||
segments = [] # including holes
|
||||
holes = []
|
||||
pack = getPolyPtsSegments(geom, dataStorage)
|
||||
|
||||
vertices, vertices3d, segments, holes = pack
|
||||
|
||||
if len(vertices) > 0:
|
||||
vertices.append(vertices[0])
|
||||
for i, h in enumerate(holes):
|
||||
if len(holes[i]) > 0:
|
||||
holes[i].append(holes[i][0])
|
||||
dict_shape = {"vertices": vertices, "holes": holes}
|
||||
|
||||
extRing = geom.exteriorRing()
|
||||
pt_iterator = extRing.vertices()
|
||||
except:
|
||||
try:
|
||||
t, iterations = to_triangles(dict_shape, 0)
|
||||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return None, None, None
|
||||
return t, vertices3d, iterations
|
||||
extRing = geom.constGet().exteriorRing()
|
||||
pt_iterator = extRing.vertices()
|
||||
except:
|
||||
pt_iterator = geom.vertices()
|
||||
|
||||
# get boundary points and segments
|
||||
pointListLocal = []
|
||||
startLen = len(vertices)
|
||||
for i, pt in enumerate(pt_iterator):
|
||||
if (
|
||||
len(pointListLocal) > 0
|
||||
and pt.x() == pointListLocal[0].x()
|
||||
and pt.y() == pointListLocal[0].y()
|
||||
): # don't repeat 1st point
|
||||
pass
|
||||
else:
|
||||
pointListLocal.append(pt)
|
||||
for i, pt in enumerate(pointListLocal):
|
||||
x, y = applyOffsetsRotation(pt.x(), pt.y(), dataStorage)
|
||||
vertices.append([x, y])
|
||||
try:
|
||||
vertices3d.append([x, y, pt.z()])
|
||||
except:
|
||||
vertices3d.append([x, y, 0])
|
||||
|
||||
if i > 0:
|
||||
segmList.append([startLen + i - 1, startLen + i])
|
||||
|
||||
# get voids points and segments
|
||||
try:
|
||||
geom = geom.constGet()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
intRingsNum = geom.numInteriorRings()
|
||||
|
||||
for k in range(intRingsNum):
|
||||
intRing = geom.interiorRing(k)
|
||||
pt_iterator = intRing.vertices()
|
||||
|
||||
pt_list = list(pt_iterator)
|
||||
pointListLocal = []
|
||||
startLen = len(vertices)
|
||||
for i, pt in enumerate(pt_list):
|
||||
if (
|
||||
len(pointListLocal) > 0
|
||||
and pt.x() == pointListLocal[0].x()
|
||||
and pt.y() == pointListLocal[0].y()
|
||||
): # don't repeat 1st point
|
||||
continue
|
||||
elif [
|
||||
pt.x(),
|
||||
pt.y(),
|
||||
] not in vertices: # in case it's not the inner part of geometry
|
||||
pointListLocal.append(pt)
|
||||
|
||||
if len(pointListLocal) > 2:
|
||||
holes.append(
|
||||
[
|
||||
applyOffsetsRotation(p.x(), p.y(), dataStorage)
|
||||
for p in pointListLocal
|
||||
]
|
||||
)
|
||||
for i, pt in enumerate(pointListLocal):
|
||||
x, y = applyOffsetsRotation(pt.x(), pt.y(), dataStorage)
|
||||
try:
|
||||
vertices3d.append([x, y, pt.z()])
|
||||
except:
|
||||
vertices3d.append([x, y, None])
|
||||
|
||||
if i > 0:
|
||||
segmList.append([startLen + i - 1, startLen + i])
|
||||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return None, None, None
|
||||
logToUser(e, level=1, func=inspect.stack()[0][3])
|
||||
raise e
|
||||
return vertices, vertices3d, segmList, holes
|
||||
|
||||
|
||||
def to_triangles(data, attempt=0):
|
||||
def to_triangles(data: dict, attempt: int = 0) -> Tuple[dict | None, int]:
|
||||
# https://gis.stackexchange.com/questions/316697/delaunay-triangulation-algorithm-in-shapely-producing-erratic-result
|
||||
try:
|
||||
vert_old = data["vertices"]
|
||||
|
@ -164,8 +236,6 @@ def to_triangles(data, attempt=0):
|
|||
polygon = Polygon([(v[0], v[1]) for v in vert])
|
||||
else:
|
||||
polygon = Polygon([(v[0], v[1]) for v in vert], holes)
|
||||
# polygon = Polygon([ ( v[0], v[1] ) for v in vert ] )
|
||||
# polygon = Polygon([(3.0, 0.0), (2.0, 0.0), (2.0, 0.75), (2.5, 0.75), (2.5, 0.6), (2.25, 0.6), (2.25, 0.2), (3.0, 0.2), (3.0, 0.0)])
|
||||
|
||||
poly_points = []
|
||||
exterior_linearring = polygon.exterior
|
||||
|
@ -226,10 +296,41 @@ def to_triangles(data, attempt=0):
|
|||
if attempt <= 3:
|
||||
return to_triangles(data, attempt)
|
||||
else:
|
||||
return None, None
|
||||
return None, attempt
|
||||
|
||||
|
||||
def trianglateQuadMesh(mesh: Mesh) -> Mesh:
|
||||
def triangulatePolygon(
|
||||
geom: Any, dataStorage: "DataStorage"
|
||||
) -> Tuple[dict, Union[List[List[float]], None], int]:
|
||||
try:
|
||||
# import triangle as tr
|
||||
vertices = [] # only outer
|
||||
segments = [] # including holes
|
||||
holes = []
|
||||
pack = getPolyPtsSegments(geom, dataStorage)
|
||||
|
||||
vertices, vertices3d, segments, holes = pack
|
||||
|
||||
if len(vertices) > 0:
|
||||
vertices.append(vertices[0])
|
||||
for i, h in enumerate(holes):
|
||||
if len(holes[i]) > 0:
|
||||
holes[i].append(holes[i][0])
|
||||
dict_shape = {"vertices": vertices, "holes": holes}
|
||||
|
||||
try:
|
||||
t, iterations = to_triangles(dict_shape, 0)
|
||||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return None, None, None
|
||||
return t, vertices3d, iterations
|
||||
|
||||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return None, None, None
|
||||
|
||||
|
||||
def trianglateQuadMesh(mesh: Mesh) -> Mesh | None:
|
||||
new_mesh = None
|
||||
try:
|
||||
new_v: List[float] = []
|
||||
|
@ -237,30 +338,29 @@ def trianglateQuadMesh(mesh: Mesh) -> Mesh:
|
|||
new_c: List[int] = []
|
||||
|
||||
# fill new color and vertices lists
|
||||
used_ind = []
|
||||
for i, c in enumerate(mesh.colors):
|
||||
try:
|
||||
# new_c.append(c)
|
||||
# continue
|
||||
if i not in used_ind:
|
||||
new_c.extend(
|
||||
[
|
||||
mesh.colors[i],
|
||||
mesh.colors[i + 1],
|
||||
mesh.colors[i + 2],
|
||||
mesh.colors[i + 2],
|
||||
mesh.colors[i + 3],
|
||||
mesh.colors[i],
|
||||
]
|
||||
)
|
||||
used_ind.extend([i, i + 1, i + 2, i + 3])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
if mesh.colors is not None:
|
||||
used_ind_colors = []
|
||||
for i, _ in enumerate(mesh.colors):
|
||||
try:
|
||||
if i not in used_ind_colors:
|
||||
new_c.extend(
|
||||
[
|
||||
mesh.colors[i],
|
||||
mesh.colors[i + 1],
|
||||
mesh.colors[i + 2],
|
||||
mesh.colors[i + 2],
|
||||
mesh.colors[i + 3],
|
||||
mesh.colors[i],
|
||||
]
|
||||
)
|
||||
used_ind_colors.extend([i, i + 1, i + 2, i + 3])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
used_ind = []
|
||||
used_ind_vertex = []
|
||||
for i, v in enumerate(mesh.vertices):
|
||||
try:
|
||||
if i not in used_ind:
|
||||
if i not in used_ind_vertex:
|
||||
v0 = [mesh.vertices[i], mesh.vertices[i + 1], mesh.vertices[i + 2]]
|
||||
v1 = [
|
||||
mesh.vertices[i + 3],
|
||||
|
@ -291,135 +391,28 @@ def trianglateQuadMesh(mesh: Mesh) -> Mesh:
|
|||
int(i / 12) + 5,
|
||||
]
|
||||
)
|
||||
used_ind.extend(list(range(i, i + 12)))
|
||||
used_ind_vertex.extend(list(range(i, i + 12)))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
new_mesh = Mesh.create(new_v, new_f, new_c)
|
||||
new_mesh.units = mesh.units
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
return None
|
||||
return new_mesh
|
||||
|
||||
|
||||
def getPolyPtsSegments(geom, dataStorage):
|
||||
vertices = []
|
||||
vertices3d = []
|
||||
segmList = []
|
||||
holes = []
|
||||
gv = list(geom.vertices())
|
||||
try:
|
||||
extRing = geom.exteriorRing()
|
||||
pt_iterator = extRing.vertices()
|
||||
except:
|
||||
try:
|
||||
extRing = geom.constGet().exteriorRing()
|
||||
pt_iterator = extRing.vertices()
|
||||
except:
|
||||
pt_iterator = geom.vertices()
|
||||
|
||||
pointListLocal = []
|
||||
startLen = len(vertices)
|
||||
for i, pt in enumerate(pt_iterator):
|
||||
if (
|
||||
len(pointListLocal) > 0
|
||||
and pt.x() == pointListLocal[0].x()
|
||||
and pt.y() == pointListLocal[0].y()
|
||||
): # don't repeat 1st point
|
||||
pass
|
||||
else:
|
||||
pointListLocal.append(pt)
|
||||
for i, pt in enumerate(pointListLocal):
|
||||
x, y = applyOffsetsRotation(pt.x(), pt.y(), dataStorage)
|
||||
vertices.append([x, y])
|
||||
try:
|
||||
vertices3d.append([x, y, pt.z()])
|
||||
except:
|
||||
vertices3d.append([x, y, 0])
|
||||
|
||||
# try: vertices3d.append([pt.x(),pt.y(),pt.z()])
|
||||
# except: vertices3d.append([pt.x(),pt.y(), 0]) # project boundary to 0
|
||||
if i > 0:
|
||||
segmList.append([startLen + i - 1, startLen + i])
|
||||
# if i == len(pointListLocal)-1: #also add a cap
|
||||
# segmList.append([startLen+i, startLen])
|
||||
|
||||
########### get voids
|
||||
try:
|
||||
geom = geom.constGet()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
# logToUser(geom)
|
||||
intRingsNum = geom.numInteriorRings()
|
||||
# except:
|
||||
# intRingsNum = len(geom.constParts())
|
||||
|
||||
for k in range(intRingsNum):
|
||||
intRing = geom.interiorRing(k)
|
||||
pt_iterator = intRing.vertices()
|
||||
# pt_iterator = intRing.vertices()
|
||||
# intRing = geom.constParts(k)
|
||||
# intRing = geom.childGeometry(k)
|
||||
# pt_iterator = intRing.vertices()
|
||||
|
||||
pt_list = list(pt_iterator)
|
||||
pointListLocal = []
|
||||
startLen = len(vertices)
|
||||
for i, pt in enumerate(pt_list):
|
||||
if (
|
||||
len(pointListLocal) > 0
|
||||
and pt.x() == pointListLocal[0].x()
|
||||
and pt.y() == pointListLocal[0].y()
|
||||
): # don't repeat 1st point
|
||||
continue
|
||||
elif [
|
||||
pt.x(),
|
||||
pt.y(),
|
||||
] not in vertices: # in case it's not the inner part of geometry
|
||||
pointListLocal.append(pt)
|
||||
# try:
|
||||
# pointListLocal.append([pt.x(), pt.y(), pt.z()])
|
||||
# except:
|
||||
# pointListLocal.append([pt.x(), pt.y(), None])
|
||||
|
||||
# hole = getHolePt(pointListLocal)
|
||||
# x, y = applyOffsetsRotation(hole[0], hole[1], dataStorage)
|
||||
|
||||
if len(pointListLocal) > 2:
|
||||
holes.append(
|
||||
[
|
||||
applyOffsetsRotation(p.x(), p.y(), dataStorage)
|
||||
for p in pointListLocal
|
||||
]
|
||||
)
|
||||
for i, pt in enumerate(pointListLocal):
|
||||
x, y = applyOffsetsRotation(pt.x(), pt.y(), dataStorage)
|
||||
# vertices.append([x, y])
|
||||
try:
|
||||
vertices3d.append([x, y, pt.z()])
|
||||
except:
|
||||
vertices3d.append([x, y, None])
|
||||
|
||||
if i > 0:
|
||||
segmList.append([startLen + i - 1, startLen + i])
|
||||
# if i == len(pointListLocal)-1: #also add a cap
|
||||
# segmList.append([startLen+i, startLen])
|
||||
except Exception as e:
|
||||
logToUser(e, level=1, func=inspect.stack()[0][3])
|
||||
return vertices, vertices3d, segmList, holes
|
||||
|
||||
|
||||
def fix_orientation(polyBorder, positive=True, coef=1):
|
||||
# polyBorder = [QgsPoint(-1.42681236722918436,0.25275926575812246), QgsPoint(-1.42314917758289616,0.78756097253123281), QgsPoint(-0.83703883417681257,0.77290957257654203), QgsPoint(-0.85169159276196471,0.24176979917208921), QgsPoint(-1.42681236722918436,0.25275926575812246)]
|
||||
def fix_orientation(
|
||||
polyBorder: List[Point | "QgsPoint"], positive: bool = True, coef: int = 1
|
||||
):
|
||||
sum_orientation = 0
|
||||
for k, ptt in enumerate(polyBorder): # pointList:
|
||||
for k, _ in enumerate(polyBorder):
|
||||
index = k + 1
|
||||
if k == len(polyBorder) - 1:
|
||||
index = 0
|
||||
pt = polyBorder[k * coef]
|
||||
pt2 = polyBorder[index * coef]
|
||||
# print(pt)
|
||||
|
||||
try:
|
||||
sum_orientation += (pt2.x - pt.x) * (pt2.y + pt.y) # if Speckle Points
|
||||
except:
|
||||
|
@ -789,3 +782,28 @@ def getArcNormal(poly: Arc, midPt: Point, dataStorage):
|
|||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return
|
||||
|
||||
|
||||
def applyOffsetsRotation(x: float, y: float, dataStorage): # on Send
|
||||
try:
|
||||
offset_x = dataStorage.crs_offset_x
|
||||
offset_y = dataStorage.crs_offset_y
|
||||
rotation = dataStorage.crs_rotation
|
||||
if offset_x is not None and isinstance(offset_x, float):
|
||||
x -= offset_x
|
||||
if offset_y is not None and isinstance(offset_y, float):
|
||||
y -= offset_y
|
||||
if (
|
||||
rotation is not None
|
||||
and isinstance(rotation, float)
|
||||
and -360 < rotation < 360
|
||||
):
|
||||
a = rotation * math.pi / 180
|
||||
x2 = x * math.cos(a) + y * math.sin(a)
|
||||
y2 = -x * math.sin(a) + y * math.cos(a)
|
||||
x = x2
|
||||
y = y2
|
||||
return x, y
|
||||
except Exception as e:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return None, None
|
||||
|
|
|
@ -28,18 +28,21 @@ from specklepy.objects.other import RevitParameter
|
|||
from typing import Dict, Any
|
||||
|
||||
from PyQt5.QtCore import QVariant, QDate, QDateTime
|
||||
from speckle.converter import geometry
|
||||
from speckle.converter.geometry import convertToSpeckle, transform
|
||||
from speckle.converter.geometry.conversions import (
|
||||
convertToNative,
|
||||
convertToNativeMulti,
|
||||
convertToSpeckle,
|
||||
)
|
||||
from speckle.converter.geometry import transform
|
||||
from specklepy.objects.GIS.geometry import (
|
||||
GisRasterElement,
|
||||
GisPolygonGeometry,
|
||||
GisNonGeometryElement,
|
||||
GisTopography,
|
||||
)
|
||||
from speckle.converter.geometry.mesh import constructMesh, constructMeshFromRaster
|
||||
from specklepy.objects.GIS.layers import RasterLayer
|
||||
from speckle.converter.geometry.mesh import constructMeshFromRaster
|
||||
|
||||
from speckle.converter.geometry.point import applyOffsetsRotation
|
||||
from speckle.converter.geometry.utils import applyOffsetsRotation
|
||||
|
||||
# from speckle.utils.panel_logging import logger
|
||||
from speckle.converter.layers.utils import (
|
||||
|
@ -1102,13 +1105,13 @@ def featureToNative(feature: Base, fields: QgsFields, dataStorage):
|
|||
speckle_geom = feature # for created in other software
|
||||
|
||||
if not isinstance(speckle_geom, list):
|
||||
qgsGeom = geometry.convertToNative(speckle_geom, dataStorage)
|
||||
qgsGeom = convertToNative(speckle_geom, dataStorage)
|
||||
|
||||
elif isinstance(speckle_geom, list):
|
||||
if len(speckle_geom) == 1:
|
||||
qgsGeom = geometry.convertToNative(speckle_geom[0], dataStorage)
|
||||
qgsGeom = convertToNative(speckle_geom[0], dataStorage)
|
||||
elif len(speckle_geom) > 1:
|
||||
qgsGeom = geometry.convertToNativeMulti(speckle_geom, dataStorage)
|
||||
qgsGeom = convertToNativeMulti(speckle_geom, dataStorage)
|
||||
else:
|
||||
logToUser(
|
||||
f"Feature '{feature.id}' does not contain geometry",
|
||||
|
@ -1215,9 +1218,9 @@ def cadFeatureToNative(feature: Base, fields: QgsFields, dataStorage):
|
|||
speckle_geom = feature # for created in other software
|
||||
|
||||
if isinstance(speckle_geom, list):
|
||||
qgsGeom = geometry.convertToNativeMulti(speckle_geom, dataStorage)
|
||||
qgsGeom = convertToNativeMulti(speckle_geom, dataStorage)
|
||||
else:
|
||||
qgsGeom = geometry.convertToNative(speckle_geom, dataStorage)
|
||||
qgsGeom = convertToNative(speckle_geom, dataStorage)
|
||||
|
||||
if qgsGeom is not None:
|
||||
exist_feat.setGeometry(qgsGeom)
|
||||
|
|
|
@ -1,32 +1,27 @@
|
|||
import threading
|
||||
from specklepy_qt_ui.qt_ui.dockwidget_main import SpeckleQGISDialog as SpeckleQGISDialog_UI
|
||||
from specklepy_qt_ui.qt_ui.dockwidget_main import (
|
||||
SpeckleQGISDialog as SpeckleQGISDialog_UI,
|
||||
)
|
||||
import specklepy_qt_ui.qt_ui
|
||||
|
||||
from speckle.ui_widgets.widget_transforms import MappingSendDialogQGIS
|
||||
|
||||
from PyQt5 import QtWidgets, uic
|
||||
from PyQt5 import uic
|
||||
import os
|
||||
import inspect
|
||||
from specklepy.logging.exceptions import (SpeckleException, GraphQLException)
|
||||
from specklepy.logging import metrics
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
|
||||
from PyQt5 import QtWidgets, uic
|
||||
from PyQt5.QtWidgets import QCheckBox, QListWidgetItem, QHBoxLayout, QWidget
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
|
||||
|
||||
from specklepy_qt_ui.qt_ui.widget_transforms import MappingSendDialog
|
||||
from specklepy_qt_ui.qt_ui.LogWidget import LogWidget
|
||||
from specklepy_qt_ui.qt_ui.logger import logToUser
|
||||
from specklepy_qt_ui.qt_ui.DataStorage import DataStorage
|
||||
|
||||
FORM_CLASS, _ = uic.loadUiType(
|
||||
os.path.join(os.path.dirname(specklepy_qt_ui.qt_ui.__file__), os.path.join("ui", "dockwidget_main.ui") )
|
||||
os.path.join(
|
||||
os.path.dirname(specklepy_qt_ui.qt_ui.__file__),
|
||||
os.path.join("ui", "dockwidget_main.ui"),
|
||||
)
|
||||
)
|
||||
|
||||
class SpeckleQGISDialog(SpeckleQGISDialog_UI, FORM_CLASS):
|
||||
|
||||
class SpeckleQGISDialog(SpeckleQGISDialog_UI, FORM_CLASS):
|
||||
def __init__(self, parent=None):
|
||||
"""Constructor."""
|
||||
super(SpeckleQGISDialog_UI, self).__init__(parent)
|
||||
|
@ -35,7 +30,6 @@ class SpeckleQGISDialog(SpeckleQGISDialog_UI, FORM_CLASS):
|
|||
self.runAllSetup()
|
||||
|
||||
def createMappingDialog(self):
|
||||
|
||||
if self.mappingSendDialog is None:
|
||||
self.mappingSendDialog = MappingSendDialogQGIS(None)
|
||||
self.mappingSendDialog.dataStorage = self.dataStorage
|
||||
|
@ -44,61 +38,79 @@ class SpeckleQGISDialog(SpeckleQGISDialog_UI, FORM_CLASS):
|
|||
|
||||
def completeStreamSection(self, plugin):
|
||||
try:
|
||||
self.streams_remove_button.clicked.connect( lambda: self.onStreamRemoveButtonClicked(plugin) )
|
||||
self.streamList.currentIndexChanged.connect( lambda: self.onActiveStreamChanged(plugin) )
|
||||
self.streamBranchDropdown.currentIndexChanged.connect( lambda: self.populateActiveCommitDropdown(plugin) )
|
||||
self.streams_remove_button.clicked.connect(
|
||||
lambda: self.onStreamRemoveButtonClicked(plugin)
|
||||
)
|
||||
self.streamList.currentIndexChanged.connect(
|
||||
lambda: self.onActiveStreamChanged(plugin)
|
||||
)
|
||||
self.streamBranchDropdown.currentIndexChanged.connect(
|
||||
lambda: self.populateActiveCommitDropdown(plugin)
|
||||
)
|
||||
return
|
||||
except Exception as e:
|
||||
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
|
||||
return
|
||||
|
||||
def onStreamRemoveButtonClicked(self, plugin):
|
||||
try:
|
||||
from speckle.utils.project_vars import set_project_streams
|
||||
if not self: return
|
||||
|
||||
if not self:
|
||||
return
|
||||
index = self.streamList.currentIndex()
|
||||
if len(plugin.current_streams) > 0: plugin.current_streams.pop(index)
|
||||
if len(plugin.current_streams) > 0:
|
||||
plugin.current_streams.pop(index)
|
||||
plugin.active_stream = None
|
||||
self.streamBranchDropdown.clear()
|
||||
self.commitDropdown.clear()
|
||||
#self.streamIdField.setText("")
|
||||
# self.streamIdField.setText("")
|
||||
|
||||
set_project_streams(plugin)
|
||||
self.populateProjectStreams(plugin)
|
||||
except Exception as e:
|
||||
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
|
||||
return
|
||||
|
||||
def populateProjectStreams(self, plugin):
|
||||
try:
|
||||
from speckle.utils.project_vars import set_project_streams
|
||||
if not self: return
|
||||
|
||||
if not self:
|
||||
return
|
||||
self.streamList.clear()
|
||||
for stream in plugin.current_streams:
|
||||
self.streamList.addItems(
|
||||
[f"Stream not accessible - {stream[0].stream_id}" if stream[1] is None or isinstance(stream[1], SpeckleException) else f"{stream[1].name}, {stream[1].id} | {stream[0].stream_url.split('/streams')[0].split('/projects')[0]}"]
|
||||
)
|
||||
if len(plugin.current_streams)==0: self.streamList.addItems([""])
|
||||
[
|
||||
f"Stream not accessible - {stream[0].stream_id}"
|
||||
if stream[1] is None or isinstance(stream[1], SpeckleException)
|
||||
else f"{stream[1].name}, {stream[1].id} | {stream[0].stream_url.split('/streams')[0].split('/projects')[0]}"
|
||||
]
|
||||
)
|
||||
if len(plugin.current_streams) == 0:
|
||||
self.streamList.addItems([""])
|
||||
self.streamList.addItems(["Create New Stream"])
|
||||
set_project_streams(plugin)
|
||||
index = self.streamList.currentIndex()
|
||||
if index == -1: self.streams_remove_button.setEnabled(False)
|
||||
else: self.streams_remove_button.setEnabled(True)
|
||||
if index == -1:
|
||||
self.streams_remove_button.setEnabled(False)
|
||||
else:
|
||||
self.streams_remove_button.setEnabled(True)
|
||||
|
||||
if len(plugin.current_streams)>0: plugin.active_stream = plugin.current_streams[0]
|
||||
if len(plugin.current_streams) > 0:
|
||||
plugin.active_stream = plugin.current_streams[0]
|
||||
except Exception as e:
|
||||
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
|
||||
return
|
||||
|
||||
def cancelOperations(self):
|
||||
#print("____cancelOperations______")
|
||||
# print("____cancelOperations______")
|
||||
for t in threading.enumerate():
|
||||
#print(t.name)
|
||||
if 'speckle_' in t.name:
|
||||
#print(f"thread to kill: {t}")
|
||||
# print(t.name)
|
||||
if "speckle_" in t.name:
|
||||
# print(f"thread to kill: {t}")
|
||||
t.kill()
|
||||
t.join()
|
||||
# not printed if same thread
|
||||
#print("Remaining threads: ")
|
||||
#print(threading.enumerate())
|
||||
|
||||
# print("Remaining threads: ")
|
||||
# print(threading.enumerate())
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
"""Logging Utility Module for Speckle QGIS"""
|
||||
import inspect
|
||||
from qgis.core import Qgis, QgsMessageLog
|
||||
from qgis.PyQt.QtWidgets import QPushButton
|
||||
from specklepy_qt_ui.qt_ui.logger import logToUser as logToUser_UI
|
||||
import webbrowser
|
||||
from specklepy_qt_ui.qt_ui.logger import logToUser as logToUser_UI
|
||||
|
||||
|
||||
def logToUser(
|
||||
|
@ -22,66 +20,71 @@ class Logging:
|
|||
def __init__(self, iface) -> None:
|
||||
self.qgisInterface = iface
|
||||
|
||||
def log(self, message: str, level: Qgis.MessageLevel = Qgis.Info):
|
||||
def log(self, message: str, level: int = 0):
|
||||
"""Logs a specific message to the Speckle messages panel."""
|
||||
try:
|
||||
from qgis.core import Qgis, QgsMessageLog
|
||||
|
||||
if level == 0:
|
||||
level = Qgis.Info
|
||||
if level == 1:
|
||||
elif level == 1:
|
||||
level = Qgis.Warning
|
||||
if level == 2:
|
||||
elif level == 2:
|
||||
level = Qgis.Critical
|
||||
# return
|
||||
QgsMessageLog.logMessage(message, "Speckle", level=level)
|
||||
except ImportError:
|
||||
pass
|
||||
except Exception as e:
|
||||
try:
|
||||
logToUser(e, level=2, func=inspect.stack()[0][3])
|
||||
return
|
||||
except:
|
||||
pass
|
||||
|
||||
def btnClicked(url):
|
||||
try:
|
||||
if url == "":
|
||||
return
|
||||
webbrowser.open(url, new=0, autoraise=True)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
def logToUserWithAction(
|
||||
self,
|
||||
message: str,
|
||||
action_text: str,
|
||||
url: str = "",
|
||||
level: Qgis.MessageLevel = Qgis.Info,
|
||||
level: int = 0,
|
||||
duration: int = 120,
|
||||
):
|
||||
self.log(message, level)
|
||||
|
||||
if not self.qgisInterface:
|
||||
return
|
||||
try:
|
||||
from qgis.core import Qgis
|
||||
from qgis.PyQt.QtWidgets import QPushButton
|
||||
|
||||
if level == 0:
|
||||
level = Qgis.Info
|
||||
if level == 1:
|
||||
level = Qgis.Warning
|
||||
if level == 2:
|
||||
level = Qgis.Critical
|
||||
if level == 0:
|
||||
level = Qgis.Info
|
||||
elif level == 1:
|
||||
level = Qgis.Warning
|
||||
elif level == 2:
|
||||
level = Qgis.Critical
|
||||
|
||||
def btnClicked(url):
|
||||
try:
|
||||
if url == "":
|
||||
return
|
||||
webbrowser.open(url, new=0, autoraise=True)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
widget = self.qgisInterface.messageBar().createMessage("Speckle", message)
|
||||
button = QPushButton(widget)
|
||||
button.setText(action_text)
|
||||
button.pressed.connect(lambda: btnClicked(url))
|
||||
widget.layout().addWidget(button)
|
||||
self.qgisInterface.messageBar().pushWidget(widget, level, duration)
|
||||
|
||||
# def logToUser(self, message: str, level: Qgis.MessageLevel = Qgis.Info, duration: int =10, func=None, plugin=None):
|
||||
# return
|
||||
widget = self.qgisInterface.messageBar().createMessage("Speckle", message)
|
||||
button = QPushButton(widget)
|
||||
button.setText(action_text)
|
||||
button.pressed.connect(lambda: self.btnClicked(url))
|
||||
widget.layout().addWidget(button)
|
||||
self.qgisInterface.messageBar().pushWidget(widget, level, duration)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
def logToUserPanel(
|
||||
self,
|
||||
message: str,
|
||||
level: Qgis.MessageLevel = Qgis.Info,
|
||||
level: int = 0,
|
||||
duration: int = 20,
|
||||
func=None,
|
||||
plugin=None,
|
||||
|
@ -92,18 +95,22 @@ class Logging:
|
|||
|
||||
if not self.qgisInterface:
|
||||
return
|
||||
try:
|
||||
from qgis.core import Qgis
|
||||
|
||||
if level == 0:
|
||||
level = Qgis.Info
|
||||
if level == 1:
|
||||
level = Qgis.Warning
|
||||
if level == 2:
|
||||
level = Qgis.Critical
|
||||
if level == 0:
|
||||
level = Qgis.Info
|
||||
if level == 1:
|
||||
level = Qgis.Warning
|
||||
if level == 2:
|
||||
level = Qgis.Critical
|
||||
|
||||
if self.qgisInterface:
|
||||
self.qgisInterface.messageBar().pushMessage(
|
||||
"Speckle", message, level=level, duration=duration
|
||||
)
|
||||
if self.qgisInterface:
|
||||
self.qgisInterface.messageBar().pushMessage(
|
||||
"Speckle", message, level=level, duration=duration
|
||||
)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
def writeToLog(self, msg: str = "", level: int = 2, func=None, plugin=None):
|
||||
msg = str(msg)
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
import sys
|
||||
import os
|
||||
import traceback
|
||||
import subprocess
|
||||
from qgis.core import QgsMessageLog, Qgis
|
||||
|
||||
# from speckle.utils.panel_logging import logger
|
||||
|
||||
MESSAGE_CATEGORY = "Speckle"
|
||||
|
||||
|
@ -18,42 +14,3 @@ def get_qgis_python_path():
|
|||
else:
|
||||
pythonExec += "/bin/python3"
|
||||
return pythonExec
|
||||
|
||||
|
||||
def enable_remote_debugging():
|
||||
try:
|
||||
import ptvsd
|
||||
except:
|
||||
r"""
|
||||
QgsMessageLog.logMessage(
|
||||
"PTVSD not installed, setting up now", MESSAGE_CATEGORY, Qgis.Info
|
||||
)
|
||||
"""
|
||||
subprocess.call([get_qgis_python_path(), "-m", "pip", "install", "ptvsd"])
|
||||
try:
|
||||
import ptvsd
|
||||
|
||||
if ptvsd.is_attached():
|
||||
r"""
|
||||
QgsMessageLog.logMessage(
|
||||
"Remote Debug for Visual Studio is already active",
|
||||
MESSAGE_CATEGORY,
|
||||
Qgis.Info,
|
||||
)
|
||||
"""
|
||||
return
|
||||
ptvsd.enable_attach(address=("localhost", 5678))
|
||||
|
||||
# Enable this if you want to be able to hit early breakpoints. Execution will stop until IDE attaches to the port, but QGIS will appear to be unresponsive!!!!
|
||||
# ptvsd.wait_for_attach()
|
||||
r"""
|
||||
QgsMessageLog.logMessage(
|
||||
"Attached remote Debug for Visual Studio", MESSAGE_CATEGORY, Qgis.Success
|
||||
)
|
||||
"""
|
||||
except Exception as e:
|
||||
r"""
|
||||
QgsMessageLog.logMessage(
|
||||
"Failed to attach to PTVSD", MESSAGE_CATEGORY, Qgis.Info
|
||||
)
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import pytest
|
||||
|
||||
r"""
|
||||
from specklepy_qt_ui.qt_ui.DataStorage import DataStorage
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def data_storage():
|
||||
sample_obj = DataStorage()
|
||||
return sample_obj
|
||||
"""
|
|
@ -1,5 +1,5 @@
|
|||
r"""
|
||||
from speckle.converter.geometry.point import (
|
||||
applyOffsetsRotation,
|
||||
pointToSpeckle,
|
||||
transformSpecklePt,
|
||||
pointToNativeWithoutTransforms,
|
||||
|
@ -8,13 +8,14 @@ from speckle.converter.geometry.point import (
|
|||
scalePointToNative,
|
||||
)
|
||||
|
||||
|
||||
def test():
|
||||
assert 0 == 0
|
||||
|
||||
|
||||
def test_applyOffsetsRotation():
|
||||
x = 0
|
||||
y = 0
|
||||
dataStorage = None
|
||||
assert applyOffsetsRotation(x, y, dataStorage) == (None, None)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def test():
|
||||
assert 0 == 0
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
import pytest
|
||||
|
||||
from speckle.converter.geometry.utils import (
|
||||
cross_product,
|
||||
dot,
|
||||
normalize,
|
||||
createPlane,
|
||||
project_to_plane_on_z,
|
||||
projectToPolygon,
|
||||
triangulatePolygon,
|
||||
to_triangles,
|
||||
trianglateQuadMesh,
|
||||
getPolyPtsSegments,
|
||||
fix_orientation,
|
||||
getHolePt,
|
||||
getPolygonFeatureHeight,
|
||||
specklePolycurveToPoints,
|
||||
speckleArcCircleToPoints,
|
||||
speckleBoundaryToSpecklePts,
|
||||
addCorrectUnits,
|
||||
getArcRadianAngle,
|
||||
getArcAngles,
|
||||
getArcNormal,
|
||||
applyOffsetsRotation,
|
||||
)
|
||||
|
||||
from specklepy.objects.geometry import (
|
||||
Point,
|
||||
Line,
|
||||
Mesh,
|
||||
Polyline,
|
||||
Circle,
|
||||
Arc,
|
||||
Polycurve,
|
||||
Vector,
|
||||
)
|
||||
|
||||
|
||||
def test_cross_product_input_error():
|
||||
pt1 = [0.0, 0.0]
|
||||
pt2 = [1.0]
|
||||
with pytest.raises(ValueError) as e:
|
||||
cross_product(pt1, pt2)
|
||||
assert (
|
||||
str(e.value) == f"Not enough arguments for 3-dimentional point {pt1} or {pt2}"
|
||||
)
|
||||
|
||||
|
||||
def test_cross_product_wrong_input_format():
|
||||
pt1 = ["0", 0.0, 0.0]
|
||||
pt2 = [0.0, 0.0, 0.0]
|
||||
try:
|
||||
cross_product(pt1, pt2)
|
||||
assert False
|
||||
except TypeError:
|
||||
assert True
|
||||
|
||||
|
||||
def test_cross_product_zero_vectors():
|
||||
pt1 = [0.0, 0.0, 0.0]
|
||||
pt2 = [0.0, 0.0, 0.0]
|
||||
assert cross_product(pt1, pt2) == [0.0, 0.0, 0.0]
|
||||
|
||||
|
||||
def test_dot_input_error():
|
||||
pt1 = [0.0, 0.0]
|
||||
pt2 = [1.0]
|
||||
with pytest.raises(ValueError) as e:
|
||||
dot(pt1, pt2)
|
||||
assert (
|
||||
str(e.value) == f"Not enough arguments for 3-dimentional point {pt1} or {pt2}"
|
||||
)
|
||||
|
||||
|
||||
def test_dot_wrong_input_format():
|
||||
pt1 = ["0", 0.0, 0.0]
|
||||
pt2 = [0.0, 0.0, 0.0]
|
||||
try:
|
||||
dot(pt1, pt2)
|
||||
assert False
|
||||
except TypeError:
|
||||
assert True
|
||||
|
||||
|
||||
def test_dot_zero_vectors():
|
||||
pt1 = [0.0, 0.0, 0.0]
|
||||
pt2 = [0.0, 0.0, 0.0]
|
||||
assert dot(pt1, pt2) == 0.0
|
||||
|
||||
|
||||
def test_normalize_zero_vector():
|
||||
pt = [0.0, 0.0, 0.0]
|
||||
assert normalize(pt) == pt
|
||||
|
||||
|
||||
def test_normalize_normalized_vector():
|
||||
pt = [1.0, 0.0, 0.0]
|
||||
assert normalize(pt) == pt
|
||||
|
||||
|
||||
def test_normalize_other_vector():
|
||||
pt = [2.0, 0.0, 0.0]
|
||||
assert normalize(pt) == [1.0, 0.0, 0.0]
|
||||
|
||||
|
||||
def test_createPlane_zero_vectors():
|
||||
pt1 = [0.0, 0.0, 0.0]
|
||||
pt2 = [0.0, 0.0, 0.0]
|
||||
pt3 = [0.0, 0.0, 0.0]
|
||||
assert createPlane(pt1, pt2, pt3) == {"origin": pt1, "normal": [0.0, 0.0, 0.0]}
|
||||
|
||||
|
||||
def test_createPlane_input_error():
|
||||
pt1 = [0.0, 0.0]
|
||||
pt2 = [1.0]
|
||||
pt3 = [1.0]
|
||||
with pytest.raises(ValueError) as e:
|
||||
createPlane(pt1, pt2, pt3)
|
||||
assert (
|
||||
str(e.value)
|
||||
== f"Not enough arguments for 3-dimentional point {pt1}, {pt2} or {pt3}"
|
||||
)
|
||||
|
||||
|
||||
def test_project_to_plane_basic():
|
||||
point = [0.0, 0.0, 0.0]
|
||||
plane = {"origin": point, "normal": [0.0, 0.0, 1.0]}
|
||||
assert project_to_plane_on_z(point, plane) == 0.0
|
||||
|
||||
|
||||
def test_project_to_plane_input_error():
|
||||
point = [0.0]
|
||||
plane = {"some key": "some value"}
|
||||
with pytest.raises(ValueError) as e:
|
||||
project_to_plane_on_z(point, plane)
|
||||
assert str(e.value) == f"Invalid arguments for a point {point} or a plane {plane}"
|
||||
|
||||
|
||||
def test_projectToPolygon_basic():
|
||||
point = [0.0, 0.0, 0.0]
|
||||
polygonPts = [[1.0, 0.0, 1.0], [0.0, 1.0, 1.0], [0.0, 2.0, 1.0]]
|
||||
assert projectToPolygon(point, polygonPts) == 1.0
|
||||
|
||||
|
||||
def test_projectToPolygon_input_error():
|
||||
point = [0.0]
|
||||
polygonPts = [[1.0, 0.0, 1.0], [0.0, 1.0, 1.0], [0.0, 2.0, 1.0]]
|
||||
with pytest.raises(ValueError) as e:
|
||||
projectToPolygon(point, polygonPts)
|
||||
assert str(e.value) == f"Not enough arguments for a point {point}"
|
||||
|
||||
|
||||
def test_projectToPolygon_input_error_polygon():
|
||||
point = [0.0, 0.0]
|
||||
polygonPts = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]
|
||||
assert projectToPolygon(point, polygonPts) == 0
|
||||
|
||||
|
||||
def test_to_triangles_basic():
|
||||
data = {
|
||||
"vertices": [[-10, -5], [0, 5], [10, -5]],
|
||||
"holes": [[[-2, -1], [0, 1], [2, -2]]],
|
||||
}
|
||||
result = to_triangles(data)
|
||||
assert isinstance(result, tuple)
|
||||
assert isinstance(result[0]["triangles"], list)
|
||||
assert [0, 1, 2] in result[0]["triangles"]
|
||||
assert isinstance(result[0]["vertices"], list)
|
||||
assert [10.0, -5.0] in result[0]["vertices"]
|
||||
assert isinstance(result[1], int)
|
||||
|
||||
|
||||
def test_to_triangles_invalid_shape():
|
||||
data = {
|
||||
"vertices": [[-10, -5], [0, 5], [10, -5]],
|
||||
"holes": [[[-20, -1], [0, 1], [2, -2]]],
|
||||
}
|
||||
result = to_triangles(data)
|
||||
assert isinstance(result, tuple)
|
||||
assert result[0] is None
|
||||
assert result[1] > 3
|
||||
|
||||
|
||||
def test_trianglateQuadMesh():
|
||||
mesh = Mesh.create([-4, -4, 0, -4, 4, 0, 4, 4, 0, 4, -4, 0], [4, 0, 1, 2, 3])
|
||||
new_mesh = trianglateQuadMesh(mesh)
|
||||
assert isinstance(new_mesh, Mesh)
|
||||
assert new_mesh.faces[0] == 3
|
||||
assert len(new_mesh.vertices) == 18
|
||||
|
||||
|
||||
def test_fix_orientation():
|
||||
polyBorder = [Point(-4, -4, 0), Point(0, 4, 0), Point(4, 4, 0)]
|
||||
positive = True
|
||||
coef = 1
|
||||
result = fix_orientation(polyBorder, positive, coef)
|
||||
assert result == polyBorder
|
|
@ -1,3 +1,4 @@
|
|||
r"""
|
||||
from speckle.converter.layers.feature import (
|
||||
addFeatVariant,
|
||||
updateFeat,
|
||||
|
@ -8,6 +9,7 @@ from speckle.converter.layers.feature import (
|
|||
nonGeomFeatureToNative,
|
||||
cadFeatureToNative,
|
||||
)
|
||||
"""
|
||||
|
||||
|
||||
def test():
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
r"""
|
||||
from qgis.core import (
|
||||
QgsRasterLayer,
|
||||
QgsVectorLayer,
|
||||
QgsLayerTree,
|
||||
QgsLayerTreeGroup,
|
||||
QgsLayerTreeNode,
|
||||
QgsLayerTreeLayer,
|
||||
)
|
||||
"""
|
||||
# from speckle.converter.layers import getAllLayers
|
||||
|
||||
|
||||
def test_get_all_layers_empty_parent():
|
||||
tree = None # QgsLayerTree()
|
||||
parent = None
|
||||
# result = getAllLayers(tree, parent)
|
||||
# assert isinstance(result, list)
|
Загрузка…
Ссылка в новой задаче