splitting qgis-using functions, testing others

This commit is contained in:
KatKatKateryna 2024-01-16 12:09:16 +00:00
Родитель 4530aab115
Коммит 968231680f
21 изменённых файлов: 1611 добавлений и 1247 удалений

Просмотреть файл

@ -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"
)

1006
poetry.lock сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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,41 +1,35 @@
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 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
import inspect
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)
self.setupUi(self)
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:
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}")
t.kill()
# 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())
# not printed if same thread
# 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)