This commit is contained in:
KatKatKateryna
2024-02-07 00:43:17 +00:00
parent 7770f6c1d1
commit cdfdded829
59 changed files with 7340 additions and 4551 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
specklepy==2.9.1
specklepy==2.17.17
panda3d==1.10.11
+1 -1
View File
@@ -6,7 +6,7 @@ import os
try:
from speckle.speckle.converter.layers.CRS import CRS
from speckle.speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
from specklepy.objects.GIS.layers import Layer, VectorLayer, RasterLayer
except:
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.CRS import CRS
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
@@ -0,0 +1,180 @@
import inspect
import random
from typing import Any, Union
from speckle.speckle.converter.layers.utils import getVariantFromValue, traverseDict
from speckle.speckle.utils.panel_logging import logToUser
from specklepy.objects import Base
def addFeatVariant(key, variant, value, f: "QgsFeature") -> "QgsFeature":
try:
feat = f
if variant == 10:
value = str(value) # string
if value != "NULL" and value != "None":
if variant == getVariantFromValue(value):
feat[key] = value
elif (
isinstance(value, float) and variant == 4
): # float, but expecting Long (integer)
feat[key] = int(value)
elif (
isinstance(value, int) and variant == 6
): # int (longlong), but expecting float
feat[key] = float(value)
else:
feat[key] = None
# print(key); print(value); print(type(value)); print(variant); print(getVariantFromValue(value))
elif isinstance(variant, int):
feat[key] = None
return feat
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return feat
def updateFeat(feat: "QgsFeature", fields: "QgsFields", feature: Base) -> "QgsFeature":
try:
# print("__updateFeat")
all_field_names = fields.names()
for i, key in enumerate(all_field_names):
variant = fields.at(i).type()
try:
if key == "Speckle_ID":
value = str(feature["id"])
# if key != "parameters": print(value)
feat[key] = value
feat = addFeatVariant(key, variant, value, feat)
else:
try:
value = feature[key]
feat = addFeatVariant(key, variant, value, feat)
except:
value = None
rootName = key.split("_")[0]
# try: # if the root category exists
# if its'a list
if isinstance(feature[rootName], list):
for i in range(len(feature[rootName])):
try:
newF, newVals = traverseDict(
{},
{},
rootName + "_" + str(i),
feature[rootName][i],
1,
)
for i, (key, value) in enumerate(newVals.items()):
for k, (x, y) in enumerate(newF.items()):
if key == x:
variant = y
break
feat = addFeatVariant(key, variant, value, feat)
except Exception as e:
print(e)
# except: # if not a list
else:
try:
newF, newVals = traverseDict(
{}, {}, rootName, feature[rootName], 1
)
for i, (key, value) in enumerate(newVals.items()):
for k, (x, y) in enumerate(newF.items()):
if key == x:
variant = y
break
feat = addFeatVariant(key, variant, value, feat)
except Exception as e:
feat.update({key: None})
except Exception as e:
feat[key] = None
# feat_sorted = {k: v for k, v in sorted(feat.items(), key=lambda item: item[0])}
# print("_________________end of updating a feature_________________________")
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return feat
def getPolygonFeatureHeight(
feature: "QgsFeature", layer: "QgsVectorLayer", dataStorage: "DataStorage"
) -> Union[int, float, None]:
height = None
ignore = False
if dataStorage.savedTransforms is not None:
for item in dataStorage.savedTransforms:
layer_name = item.split(" -> ")[0].split(" ('")[0]
transform_name = item.split(" -> ")[1].lower()
if "ignore" in transform_name:
ignore = True
if layer_name == layer.name():
attribute = None
if " ('" in item:
attribute = item.split(" ('")[1].split("') ")[0]
if attribute is None and ignore is False:
logToUser(
"Attribute for extrusion not selected",
level=1,
func=inspect.stack()[0][3],
)
return None
# print("Apply transform: " + transform_name)
if "extrude" in transform_name and "polygon" in transform_name:
# additional check:
try:
if dataStorage.project.crs().isGeographic():
return None
except:
return None
try:
existing_height = float(feature[attribute])
if (
existing_height is None or str(feature[attribute]) == "NULL"
): # if attribute value invalid
if ignore is True:
return None
else: # find approximate value
all_existing_vals = [
f[attribute]
for f in layer.getFeatures()
if (
f[attribute] is not None
and (
isinstance(f[attribute], float)
or isinstance(f[attribute], int)
)
)
]
try:
if len(all_existing_vals) > 5:
height_average = all_existing_vals[
int(len(all_existing_vals) / 2)
]
height = random.randint(
height_average - 5, height_average + 5
)
else:
height = random.randint(10, 20)
except:
height = random.randint(10, 20)
else: # if acceptable value: reading from existing attribute
height = existing_height
except: # if no Height attribute
if ignore is True:
height = None
else:
height = random.randint(10, 20)
return height
@@ -1,222 +0,0 @@
from regex import F
from specklepy.objects import Base
from specklepy.objects.geometry import Line, Mesh, Point, Polyline, Curve, Arc, Circle, Polycurve, Ellipse
import arcpy
from typing import Any, List, Union, Sequence
import inspect
try:
from speckle.speckle.converter.geometry.polygon import polygonToNative, polygonToSpeckle, multiPolygonToSpeckle, polygonToSpeckleMesh
from speckle.speckle.converter.geometry.polyline import arcToNative, ellipseToNative, circleToNative, curveToNative, lineToNative, polycurveToNative, polylineFromVerticesToSpeckle, polylineToNative, polylineToSpeckle
from speckle.speckle.converter.geometry.point import pointToCoord, pointToNative, pointToSpeckle, multiPointToSpeckle
from speckle.speckle.converter.geometry.polyline import speckleArcCircleToPoints, specklePolycurveToPoints, multiPolylineToSpeckle
from speckle.speckle.ui.logger import logToUser
from speckle.speckle.converter.geometry.mesh import meshToNative
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.polygon import polygonToNative, polygonToSpeckle, multiPolygonToSpeckle, polygonToSpeckleMesh
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.polyline import arcToNative, ellipseToNative, circleToNative, curveToNative, lineToNative, polycurveToNative, polylineFromVerticesToSpeckle, polylineToNative, polylineToSpeckle
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.point import pointToCoord, pointToNative, pointToSpeckle, multiPointToSpeckle
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.polyline import speckleArcCircleToPoints, specklePolycurveToPoints, multiPolylineToSpeckle
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.mesh import meshToNative
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
import numpy as np
def convertToSpeckle(feature, index: str, layer, geomType, featureType) -> Union[Base, Sequence[Base], None]:
"""Converts the provided layer feature to Speckle objects"""
print("___convertToSpeckle____________")
try:
geom = feature
print(geom.isMultipart) # e.g. False
print(geom.partCount)
geomMultiType = geom.isMultipart
hasCurves = feature.hasCurves
# feature is <geoprocessing describe geometry object object at 0x000002A75D6A4BD0>
#print(featureType) # e.g. Simple
#print(geomType) # e.g. Polygon
#geomSingleType = (featureType=="Simple") # Simple,SimpleJunction,SimpleJunction,ComplexEdge,Annotation,CoverageAnnotation,Dimension,RasterCatalogItem
if geomType == "Point": #Polygon, Point, Polyline, Multipoint, MultiPatch
for pt in geom:
return pointToSpeckle(pt, feature, layer)
elif geomType == "Polyline":
#if geom.hasCurves:
# geom, feature = curvesToSegments(geom, feature, layer, geomMultiType)
# geomMultiType = geom.isMultipart
# return polylineToSpeckle(geom, feature, layer, geomMultiType)
#else:
if geom.partCount > 1: return multiPolylineToSpeckle(geom, feature, layer, geomMultiType)
else: return polylineToSpeckle(geom, feature, layer, geomMultiType)
elif geomType == "Polygon":
if geom.partCount > 1: return multiPolygonToSpeckle(geom, index, layer, geomMultiType)
else: return polygonToSpeckle(geom, index, layer, geomMultiType)
elif geomType == "Multipoint":
return multiPointToSpeckle(geom, feature, layer, geomMultiType)
elif geomType == "MultiPatch":
return polygonToSpeckleMesh(geom, index, layer, False)
else:
logToUser("Unsupported or invalid geometry in layer " + layer.name, level=1, func = inspect.stack()[0][3])
return None
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None
def convertToNative(base: Base, sr: arcpy.SpatialReference) -> Union[Any, None]:
"""Converts any given base object to QgsGeometry."""
print("___Convert to Native SingleType___")
converted = None
try:
#print(base)
conversions = [
(Point, pointToNative),
(Line, lineToNative),
(Polyline, polylineToNative),
(Curve, curveToNative),
(Arc, arcToNative),
(Circle, circleToNative),
(Ellipse, ellipseToNative),
#(Mesh, meshToNative),
(Polycurve, polycurveToNative),
(Base, multiPolygonToNative), # temporary solution for polygons (Speckle has no type Polygon yet)
]
for conversion in conversions:
if isinstance(base, conversion[0]):
#print(conversion[0])
converted = conversion[1](base, sr)
break
#print(converted)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return converted
def multiPointToNative(items: List[Point], sr: arcpy.SpatialReference):
print("___Create MultiPoint")
features = None
try:
all_pts = []
# example https://pro.arcgis.com/en/pro-app/2.8/arcpy/classes/multipoint.htm
for item in items:
pt = pointToCoord(item) # [x, y, z]
all_pts.append( arcpy.Point(pt[0], pt[1], pt[2]) )
#print(all_pts)
features = arcpy.Multipoint( arcpy.Array(all_pts) )
#if len(features)==0: features = None
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return features
def multiPolylineToNative(items: List[Polyline], sr: arcpy.SpatialReference):
print("_______Drawing Multipolylines____")
poly = None
try:
#print(items)
poly = None
full_array_list = []
for item in items: # will be 1 item
pointsSpeckle = []
try: pointsSpeckle = item.as_points()
except: continue
pts = [pointToCoord(pt) for pt in pointsSpeckle]
if item.closed is True:
pts.append( pointToCoord(item.as_points()[0]) )
arr = [arcpy.Point(*coords) for coords in pts]
full_array_list.append(arr)
poly = arcpy.Polyline( arcpy.Array(full_array_list), sr, has_z=True )
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return poly
def multiPolygonToNative(items: List[Base], sr: arcpy.SpatialReference): #TODO fix multi features
print("_______Drawing Multipolygons____")
polygon = None
if not isinstance(items, List): items = [items]
try:
print(items)
full_array_list = []
for item_geom in items: # will be 1 item
print(item_geom)
try: item_geom = item_geom["geometry"]
except: item_geom = [item_geom]
for item in item_geom:
#print(item)
#pts = [pointToCoord(pt) for pt in item["boundary"].as_points()]
pointsSpeckle = []
if isinstance(item["boundary"], Circle) or isinstance(item["boundary"], Arc):
pointsSpeckle = speckleArcCircleToPoints(item["boundary"])
elif isinstance(item["boundary"], Polycurve):
pointsSpeckle = specklePolycurveToPoints(item["boundary"])
elif isinstance(item["boundary"], Line): pass
else:
try: pointsSpeckle = item["boundary"].as_points()
except Exception as e: print(e) # if Line
#print(pointsSpeckle)
pts = [pointToCoord(pt) for pt in pointsSpeckle]
#print(pts)
outer_arr = [arcpy.Point(*coords) for coords in pts]
outer_arr.append(outer_arr[0])
#print(outer_arr)
geomPart = []
try:
for void in item["voids"]:
#print(void)
#pts = [pointToCoord(pt) for pt in void.as_points()]
pointsSpeckle = []
if isinstance(void, Circle) or isinstance(void, Arc):
pointsSpeckle = speckleArcCircleToPoints(void)
elif isinstance(void, Polycurve):
pointsSpeckle = specklePolycurveToPoints(void)
elif isinstance(void, Line): pass
else:
try: pointsSpeckle = void.as_points()
except: pass # if Line
pts = [pointToCoord(pt) for pt in pointsSpeckle]
inner_arr = [arcpy.Point(*coords) for coords in pts]
inner_arr.append(inner_arr[0])
geomPart.append(arcpy.Array(inner_arr))
except Exception as e: print(e)
geomPart.insert(0, arcpy.Array(outer_arr))
#print(geomPart)
full_array_list.extend(geomPart) # outlines are written one by one, with no separation to "parts"
#print("end of loop1")
print("end of loop2")
geomPartArray = arcpy.Array(full_array_list)
polygon = arcpy.Polygon(geomPartArray, sr, has_z=True)
print(polygon)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return polygon
def convertToNativeMulti(items: List[Base], sr: arcpy.SpatialReference):
print("___Convert to Native MultiType___")
try:
first = items[0]
if isinstance(first, Point):
return multiPointToNative(items, sr)
elif isinstance(first, Line) or isinstance(first, Polyline):
return multiPolylineToNative(items, sr)
elif isinstance(first, Base):
try:
if first["boundary"] is not None and first["voids"] is not None:
return multiPolygonToNative(items, sr)
except: return None
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None
@@ -0,0 +1,279 @@
from regex import F
from specklepy.objects import Base
from specklepy.objects.geometry import (
Line,
Mesh,
Point,
Polyline,
Curve,
Arc,
Circle,
Polycurve,
Ellipse,
)
import arcpy
from typing import Any, List, Union, Sequence
import inspect
from speckle.speckle.converter.geometry.polygon import (
polygonToNative,
polygonToSpeckle,
multiPolygonToSpeckle,
polygonToSpeckleMesh,
)
from speckle.speckle.converter.geometry.utils import specklePolycurveToPoints
from speckle.speckle.converter.geometry.polyline import (
arcToNative,
ellipseToNative,
circleToNative,
curveToNative,
lineToNative,
polycurveToNative,
polylineToNative,
polylineToSpeckle,
speckleArcCircleToPoints,
multiPolylineToSpeckle,
)
from speckle.speckle.converter.geometry.point import (
pointToCoord,
pointToNative,
pointToSpeckle,
multiPointToSpeckle,
)
from speckle.speckle.utils.panel_logging import logToUser
import numpy as np
def convertToSpeckle(
feature, index: str, layer, geomType, featureType
) -> Union[Base, Sequence[Base], None]:
"""Converts the provided layer feature to Speckle objects"""
print("___convertToSpeckle____________")
try:
geom = feature
print(geom.isMultipart) # e.g. False
print(geom.partCount)
geomMultiType = geom.isMultipart
hasCurves = feature.hasCurves
# feature is <geoprocessing describe geometry object object at 0x000002A75D6A4BD0>
# print(featureType) # e.g. Simple
# print(geomType) # e.g. Polygon
# geomSingleType = (featureType=="Simple") # Simple,SimpleJunction,SimpleJunction,ComplexEdge,Annotation,CoverageAnnotation,Dimension,RasterCatalogItem
if geomType == "Point": # Polygon, Point, Polyline, Multipoint, MultiPatch
for pt in geom:
return pointToSpeckle(pt, feature, layer)
elif geomType == "Polyline":
# if geom.hasCurves:
# geom, feature = curvesToSegments(geom, feature, layer, geomMultiType)
# geomMultiType = geom.isMultipart
# return polylineToSpeckle(geom, feature, layer, geomMultiType)
# else:
if geom.partCount > 1:
return multiPolylineToSpeckle(geom, feature, layer, geomMultiType)
else:
return polylineToSpeckle(geom, feature, layer, geomMultiType)
elif geomType == "Polygon":
if geom.partCount > 1:
return multiPolygonToSpeckle(geom, index, layer, geomMultiType)
else:
return polygonToSpeckle(geom, index, layer, geomMultiType)
elif geomType == "Multipoint":
return multiPointToSpeckle(geom, feature, layer, geomMultiType)
elif geomType == "MultiPatch":
return polygonToSpeckleMesh(geom, index, layer, False)
else:
logToUser(
"Unsupported or invalid geometry in layer " + layer.name,
level=1,
func=inspect.stack()[0][3],
)
return None
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
def convertToNative(base: Base, sr: arcpy.SpatialReference) -> Union[Any, None]:
"""Converts any given base object to QgsGeometry."""
print("___Convert to Native SingleType___")
converted = None
try:
# print(base)
conversions = [
(Point, pointToNative),
(Line, lineToNative),
(Polyline, polylineToNative),
(Curve, curveToNative),
(Arc, arcToNative),
(Circle, circleToNative),
(Ellipse, ellipseToNative),
# (Mesh, meshToNative),
(Polycurve, polycurveToNative),
(
Base,
multiPolygonToNative,
), # temporary solution for polygons (Speckle has no type Polygon yet)
]
for conversion in conversions:
if isinstance(base, conversion[0]):
# print(conversion[0])
converted = conversion[1](base, sr)
break
# print(converted)
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return converted
def multiPointToNative(items: List[Point], sr: arcpy.SpatialReference):
print("___Create MultiPoint")
features = None
try:
all_pts = []
# example https://pro.arcgis.com/en/pro-app/2.8/arcpy/classes/multipoint.htm
for item in items:
pt = pointToCoord(item) # [x, y, z]
all_pts.append(arcpy.Point(pt[0], pt[1], pt[2]))
# print(all_pts)
features = arcpy.Multipoint(arcpy.Array(all_pts))
# if len(features)==0: features = None
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return features
def multiPolylineToNative(items: List[Polyline], sr: arcpy.SpatialReference):
print("_______Drawing Multipolylines____")
poly = None
try:
# print(items)
poly = None
full_array_list = []
for item in items: # will be 1 item
pointsSpeckle = []
try:
pointsSpeckle = item.as_points()
except:
continue
pts = [pointToCoord(pt) for pt in pointsSpeckle]
if item.closed is True:
pts.append(pointToCoord(item.as_points()[0]))
arr = [arcpy.Point(*coords) for coords in pts]
full_array_list.append(arr)
poly = arcpy.Polyline(arcpy.Array(full_array_list), sr, has_z=True)
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return poly
def multiPolygonToNative(
items: List[Base], sr: arcpy.SpatialReference
): # TODO fix multi features
print("_______Drawing Multipolygons____")
polygon = None
if not isinstance(items, List):
items = [items]
try:
print(items)
full_array_list = []
for item_geom in items: # will be 1 item
print(item_geom)
try:
item_geom = item_geom["geometry"]
except:
item_geom = [item_geom]
for item in item_geom:
# print(item)
# pts = [pointToCoord(pt) for pt in item["boundary"].as_points()]
pointsSpeckle = []
if isinstance(item["boundary"], Circle) or isinstance(
item["boundary"], Arc
):
pointsSpeckle = speckleArcCircleToPoints(item["boundary"])
elif isinstance(item["boundary"], Polycurve):
pointsSpeckle = specklePolycurveToPoints(item["boundary"])
elif isinstance(item["boundary"], Line):
pass
else:
try:
pointsSpeckle = item["boundary"].as_points()
except Exception as e:
print(e) # if Line
# print(pointsSpeckle)
pts = [pointToCoord(pt) for pt in pointsSpeckle]
# print(pts)
outer_arr = [arcpy.Point(*coords) for coords in pts]
outer_arr.append(outer_arr[0])
# print(outer_arr)
geomPart = []
try:
for void in item["voids"]:
# print(void)
# pts = [pointToCoord(pt) for pt in void.as_points()]
pointsSpeckle = []
if isinstance(void, Circle) or isinstance(void, Arc):
pointsSpeckle = speckleArcCircleToPoints(void)
elif isinstance(void, Polycurve):
pointsSpeckle = specklePolycurveToPoints(void)
elif isinstance(void, Line):
pass
else:
try:
pointsSpeckle = void.as_points()
except:
pass # if Line
pts = [pointToCoord(pt) for pt in pointsSpeckle]
inner_arr = [arcpy.Point(*coords) for coords in pts]
inner_arr.append(inner_arr[0])
geomPart.append(arcpy.Array(inner_arr))
except Exception as e:
print(e)
geomPart.insert(0, arcpy.Array(outer_arr))
# print(geomPart)
full_array_list.extend(
geomPart
) # outlines are written one by one, with no separation to "parts"
# print("end of loop1")
print("end of loop2")
geomPartArray = arcpy.Array(full_array_list)
polygon = arcpy.Polygon(geomPartArray, sr, has_z=True)
print(polygon)
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return polygon
def convertToNativeMulti(items: List[Base], sr: arcpy.SpatialReference):
print("___Convert to Native MultiType___")
try:
first = items[0]
if isinstance(first, Point):
return multiPointToNative(items, sr)
elif isinstance(first, Line) or isinstance(first, Polyline):
return multiPolylineToNative(items, sr)
elif isinstance(first, Base):
try:
if first["boundary"] is not None and first["voids"] is not None:
return multiPolygonToNative(items, sr)
except:
return None
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
@@ -17,14 +17,14 @@ try:
from speckle.speckle.converter.geometry.point import pointToNative
from speckle.speckle.converter.layers.symbology import featureColorfromNativeRenderer
from speckle.speckle.converter.layers.utils import get_scale_factor
from speckle.speckle.ui.logger import logToUser
from speckle.speckle.utils.panel_logging import logToUser
from speckle.speckle.plugin_utils.helpers import findOrCreatePath
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.utils import get_scale_factor
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.point import pointToNative
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.symbology import featureColorfromNativeRenderer
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.utils import get_scale_factor
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
from speckle_toolbox.esri.toolboxes.speckle.speckle.utils.panel_logging import logToUser
from speckle_toolbox.esri.toolboxes.speckle.speckle.plugin_utils.helpers import findOrCreatePath
from panda3d.core import Triangulator
@@ -4,13 +4,13 @@ from specklepy.objects.geometry import Point
import arcpy
import inspect
from speckle.speckle.converter.geometry.utils import (
transform_speckle_pt_on_receive,
apply_pt_transform_matrix,
)
try:
from speckle.speckle.converter.layers.utils import get_scale_factor
from speckle.speckle.ui.logger import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.utils import get_scale_factor
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
from speckle.speckle.converter.layers.utils import get_scale_factor
from speckle.speckle.utils.panel_logging import logToUser
def multiPointToSpeckle(geom, feature, layer, multiType: bool):
@@ -56,10 +56,13 @@ def pointToSpeckle(pt, feature, layer):
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None
def pointToNative(pt: Point, sr: arcpy.SpatialReference) -> arcpy.PointGeometry:
def pointToNative(pt: Point, sr: arcpy.SpatialReference, dataStorage) -> arcpy.PointGeometry:
"""Converts a Speckle Point to QgsPoint"""
try:
pt = scalePointToNative(pt, pt.units)
new_pt = apply_pt_transform_matrix(new_pt, dataStorage)
newPt = transform_speckle_pt_on_receive(new_pt, dataStorage)
geom = arcpy.PointGeometry(arcpy.Point(pt.x, pt.y, pt.z), sr, has_z = True)
#print(geom)
return geom
@@ -68,6 +71,20 @@ def pointToNative(pt: Point, sr: arcpy.SpatialReference) -> arcpy.PointGeometry:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None
def pointToNativeWithoutTransforms(pt: Point, sr: arcpy.SpatialReference, dataStorage):
"""Converts a Speckle Point to QgsPoint"""
try:
new_pt = scalePointToNative(pt, pt.units, dataStorage)
new_pt = apply_pt_transform_matrix(new_pt, dataStorage)
geom = arcpy.PointGeometry(arcpy.Point(pt.x, pt.y, pt.z), sr, has_z = True)
#print(geom)
return geom
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return None
def pointToCoord(point: Point) -> List[float]:
"""Converts a Speckle Point to QgsPoint"""
try:
@@ -8,32 +8,23 @@ from specklepy.objects.geometry import Point, Arc, Circle, Polycurve, Polyline,
import inspect
try:
from speckle.speckle.converter.geometry.mesh import constructMesh, constructMeshFromRaster, meshPartsFromPolygon
from speckle.speckle.converter.geometry.point import pointToCoord, pointToNative
from speckle.speckle.converter.layers.symbology import featureColorfromNativeRenderer
from speckle.speckle.converter.geometry.polyline import (polylineFromVerticesToSpeckle,
circleToSpeckle,
speckleArcCircleToPoints,
curveToSpeckle,
specklePolycurveToPoints
)
from speckle.speckle.converter.geometry.utils import speckleBoundaryToSpecklePts
from speckle.speckle.ui.logger import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.mesh import constructMeshFromRaster, constructMesh, meshPartsFromPolygon
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.point import pointToCoord, pointToNative
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.symbology import featureColorfromNativeRenderer
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.polyline import (polylineFromVerticesToSpeckle,
circleToSpeckle,
speckleArcCircleToPoints,
curveToSpeckle,
specklePolycurveToPoints
)
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.utils import speckleBoundaryToSpecklePts
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
from speckle.speckle.converter.geometry.mesh import (
constructMesh,
constructMeshFromRaster,
meshPartsFromPolygon,
)
from speckle.speckle.converter.geometry.point import pointToCoord, pointToNative
from speckle.speckle.converter.layers.symbology import featureColorfromNativeRenderer
from speckle.speckle.converter.geometry.polyline import (
polylineFromVerticesToSpeckle,
speckleArcCircleToPoints,
curveToSpeckle,
)
from speckle.speckle.converter.geometry.utils import (
speckleBoundaryToSpecklePts,
specklePolycurveToPoints,
)
from speckle.speckle.utils.panel_logging import logToUser
import math
from panda3d.core import Triangulator
@@ -42,7 +33,7 @@ from panda3d.core import Triangulator
def polygonToSpeckleMesh(geom, index: int, layer, multitype: bool):
print("________polygonToSpeckleMesh_____")
print(geom)
polygon = Base(units = "m")
polygon = Base(units="m")
try:
vertices = []
@@ -51,51 +42,58 @@ def polygonToSpeckleMesh(geom, index: int, layer, multitype: bool):
existing_vert = 0
for i, p in enumerate(geom):
#print("____start enumerate feature")
#print(p) #<geoprocessing array object object at 0x0000026796C77110>
# print("____start enumerate feature")
# print(p) #<geoprocessing array object object at 0x0000026796C77110>
print(p)
boundary, voids = getPolyBoundaryVoids(p, layer, multitype)
#print(boundary)
#print(voids)
# print(boundary)
# print(voids)
polyBorder = speckleBoundaryToSpecklePts(boundary)
#print(polyBorder)
# print(polyBorder)
voidsAsPts = []
for v in voids:
pts = speckleBoundaryToSpecklePts(v)
voidsAsPts.append(pts)
#print(voidsAsPts)
#print("__to start meshPartsFromPolygon")
total_vert, vertices_x, faces_x, colors_x = meshPartsFromPolygon(polyBorder, voidsAsPts, existing_vert, index, layer)
# print(voidsAsPts)
# print("__to start meshPartsFromPolygon")
total_vert, vertices_x, faces_x, colors_x = meshPartsFromPolygon(
polyBorder, voidsAsPts, existing_vert, index, layer
)
existing_vert += total_vert
vertices.extend(vertices_x)
faces.extend(faces_x)
colors.extend(colors_x)
#print("Colors: ")
#print(colors)
# print("Colors: ")
# print(colors)
mesh = constructMesh(vertices, faces, colors)
if mesh is not None:
polygon.displayValue = [ mesh ]
polygon.displayValue = [mesh]
else:
logToUser("Mesh creation from Polygon failed. Boundaries will be used as displayValue", level = 1, func = inspect.stack()[0][3])
logToUser(
"Mesh creation from Polygon failed. Boundaries will be used as displayValue",
level=1,
func=inspect.stack()[0][3],
)
return polygon
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
def getPolyBoundaryVoids(geom, layer, multiType: bool):
#print("__getPolyBoundaryVoids__")
# print("__getPolyBoundaryVoids__")
voids: List[Union[None, Polyline, Arc, Line, Polycurve]] = []
#print(voids)
# print(voids)
boundary = None
pointList = []
try:
#partsBoundaries = []
#partsVoids = []
if multiType is False: # Multipolygon
try: # might be no property "has curves"
# partsBoundaries = []
# partsVoids = []
if multiType is False: # Multipolygon
try: # might be no property "has curves"
if geom.hasCurves:
print("has curves")
# geometry SHAPE@ tokens: https://pro.arcgis.com/en/pro-app/latest/arcpy/get-started/reading-geometries.htm
@@ -105,82 +103,95 @@ def getPolyBoundaryVoids(geom, layer, multiType: bool):
print("no curves")
for p in geom:
for pt in p:
#print(pt)
if pt != None: pointList.append(pt)
boundary = polylineFromVerticesToSpeckle(pointList, True, geom, layer)
# print(pt)
if pt != None:
pointList.append(pt)
boundary = polylineFromVerticesToSpeckle(
pointList, True, geom, layer
)
print(boundary)
except: # for multipatches, no property "has curves"
#print("multipatch")
except: # for multipatches, no property "has curves"
# print("multipatch")
for pt in geom:
#print(pt)
if pt != None: pointList.append(pt)
# print(pt)
if pt != None:
pointList.append(pt)
boundary = polylineFromVerticesToSpeckle(pointList, True, geom, layer)
#print(boundary)
#partsBoundaries.append(boundary)
#partsVoids.append([])
# print(boundary)
# partsBoundaries.append(boundary)
# partsVoids.append([])
else:
print("multi type")
for i, p in enumerate(geom):
print(p)
for pt in p:
#print(pt) # 284394.58100903 5710688.11602606 NaN NaN
# print(pt) # 284394.58100903 5710688.11602606 NaN NaN
if pt == None and boundary == None: # first break
boundary = polylineFromVerticesToSpeckle(pointList, True, geom, layer)
boundary = polylineFromVerticesToSpeckle(
pointList, True, geom, layer
)
pointList = []
elif pt == None and boundary != None: # breaks btw voids
void = polylineFromVerticesToSpeckle(pointList, True, geom, layer)
elif pt == None and boundary != None: # breaks btw voids
void = polylineFromVerticesToSpeckle(
pointList, True, geom, layer
)
voids.append(void)
pointList = []
elif pt != None: # add points to whatever list (boundary or void)
elif pt != None: # add points to whatever list (boundary or void)
pointList.append(pt)
if boundary != None and len(pointList)>0: # remaining polyline
if boundary != None and len(pointList) > 0: # remaining polyline
void = polylineFromVerticesToSpeckle(pointList, True, geom, layer)
voids.append(void)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return boundary, voids
def multiPolygonToSpeckle(geom, index: str, layer, multiType: bool):
print("___MultiPolygon to Speckle____")
polygon = []
try:
#print(enumerate(geom.getPart())) # this method ignores curvature and voids
#print(json.loads(geom.JSON))
#js = json.loads(geom.JSON)['rings']
#https://desktop.arcgis.com/en/arcmap/latest/analyze/python/reading-geometries.htm
for i,x in enumerate(geom): # [[x,x,x]
print("Part # " + str(i+1))
# print(enumerate(geom.getPart())) # this method ignores curvature and voids
# print(json.loads(geom.JSON))
# js = json.loads(geom.JSON)['rings']
# https://desktop.arcgis.com/en/arcmap/latest/analyze/python/reading-geometries.htm
for i, x in enumerate(geom): # [[x,x,x]
print("Part # " + str(i + 1))
print(x)
boundaryFinished = 0
arrBoundary = []
arrInnerRings = []
for ptn in x: # arcpy.Point
for ptn in x: # arcpy.Point
if ptn is None:
boundaryFinished += 1
arrInnerRings.append([]) # start of new Inner Ring
arrInnerRings.append([]) # start of new Inner Ring
elif boundaryFinished == 0 and ptn is not None:
arrBoundary.append(ptn)
elif boundaryFinished == 1 and ptn is not None:
arrInnerRings[len(arrInnerRings)-1].append(ptn)
arrInnerRings[len(arrInnerRings) - 1].append(ptn)
full_arr = [arrBoundary] + arrInnerRings
#print(full_arr)
poly = arcpy.Polygon(arcpy.Array(full_arr), arcpy.Describe(layer.dataSource).SpatialReference, has_z = True)
#print(poly) #<geoprocessing describe geometry object object at 0x000002B2D3E338D0>
# print(full_arr)
poly = arcpy.Polygon(
arcpy.Array(full_arr),
arcpy.Describe(layer.dataSource).SpatialReference,
has_z=True,
)
# print(poly) #<geoprocessing describe geometry object object at 0x000002B2D3E338D0>
polygon.append(polygonToSpeckle(poly, index, layer, poly.isMultipart))
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return polygon
def polygonToSpeckle(geom, index: int, layer, multitype: bool):
"""Converts a Polygon to Speckle"""
polygon = Base(units = "m")
polygon = Base(units="m")
try:
print("___Polygon to Speckle____")
print(geom)
@@ -190,11 +201,12 @@ def polygonToSpeckle(geom, index: int, layer, multitype: bool):
data = arcpy.Describe(layer.dataSource)
sr = data.spatialReference
if boundary is None: return None
if boundary is None:
return None
polygon.boundary = boundary
polygon.voids = voids
polygon.displayValue = [ boundary ] + voids
#print(boundary)
polygon.displayValue = [boundary] + voids
# print(boundary)
############# mesh
vertices = []
@@ -204,73 +216,83 @@ def polygonToSpeckle(geom, index: int, layer, multitype: bool):
polyBorder = speckleArcCircleToPoints(boundary)
elif isinstance(boundary, Polycurve):
polyBorder = specklePolycurveToPoints(boundary)
#polygon.boundary.displayValue.closed = True
elif isinstance(boundary, Line): pass
# polygon.boundary.displayValue.closed = True
elif isinstance(boundary, Line):
pass
elif isinstance(boundary, Polyline):
try: polyBorder = boundary.as_points()
except: pass # if Line
#print(polyBorder)
try:
polyBorder = boundary.as_points()
except:
pass # if Line
# print(polyBorder)
if len(polyBorder)>2: # at least 3 points
if len(polyBorder) > 2: # at least 3 points
print("make meshes from polygons")
if len(voids) == 0: # if there is a mesh with no voids
if len(voids) == 0: # if there is a mesh with no voids
for pt in polyBorder:
if isinstance(pt, Point): pt = pointToNative(pt, sr).getPart() # SR unknown
if isinstance(pt, Point):
pt = pointToNative(pt, sr).getPart() # SR unknown
x = pt.X
y = pt.Y
z = 0 if math.isnan(pt.Z) else pt.Z
vertices.extend([x, y, z])
total_vertices += 1
#print(vertices)
# print(vertices)
ran = range(0, total_vertices)
faces = [total_vertices]
faces.extend([i for i in ran])
#print(faces)
# print(faces)
# else: https://docs.panda3d.org/1.10/python/reference/panda3d.core.Triangulator
else:
trianglator = Triangulator()
faces = []
# add boundary points
#polyBorder = boundary.as_points()
# polyBorder = boundary.as_points()
pt_count = 0
# add extra middle point for border
for pt in polyBorder:
if pt_count < len(polyBorder)-1:
pt2 = polyBorder[pt_count+1]
else: pt2 = polyBorder[0]
if pt_count < len(polyBorder) - 1:
pt2 = polyBorder[pt_count + 1]
else:
pt2 = polyBorder[0]
trianglator.addPolygonVertex(trianglator.addVertex(pt.x, pt.y))
vertices.extend([pt.x, pt.y, pt.z])
#trianglator.addPolygonVertex(trianglator.addVertex((pt.x+pt2.x)/4*3, (pt.y+pt2.y)/4*3))
#vertices.extend([(pt.x+pt2.x)/4*3, (pt.y+pt2.y)/4*3, (pt.z+pt2.z)/4*3])
# trianglator.addPolygonVertex(trianglator.addVertex((pt.x+pt2.x)/4*3, (pt.y+pt2.y)/4*3))
# vertices.extend([(pt.x+pt2.x)/4*3, (pt.y+pt2.y)/4*3, (pt.z+pt2.z)/4*3])
trianglator.addPolygonVertex(trianglator.addVertex((pt.x+pt2.x)/2, (pt.y+pt2.y)/2))
vertices.extend([(pt.x+pt2.x)/2, (pt.y+pt2.y)/2, (pt.z+pt2.z)/2])
trianglator.addPolygonVertex(
trianglator.addVertex((pt.x + pt2.x) / 2, (pt.y + pt2.y) / 2)
)
vertices.extend(
[(pt.x + pt2.x) / 2, (pt.y + pt2.y) / 2, (pt.z + pt2.z) / 2]
)
#trianglator.addPolygonVertex(trianglator.addVertex((pt.x+pt2.x)/4, (pt.y+pt2.y)/4))
#vertices.extend([(pt.x+pt2.x)/4, (pt.y+pt2.y)/4, (pt.z+pt2.z)/4])
# trianglator.addPolygonVertex(trianglator.addVertex((pt.x+pt2.x)/4, (pt.y+pt2.y)/4))
# vertices.extend([(pt.x+pt2.x)/4, (pt.y+pt2.y)/4, (pt.z+pt2.z)/4])
total_vertices += 2
pt_count += 1
#add void points
# add void points
for i in range(len(voids)):
trianglator.beginHole()
pts = []
if isinstance(voids[i], Circle) or isinstance(voids[i], Arc):
pts = speckleArcCircleToPoints(voids[i])
elif isinstance(voids[i], Polycurve):
pts = specklePolycurveToPoints(voids[i])
elif isinstance(voids[i], Line): pass
elif isinstance(voids[i], Line):
pass
else:
try: pts = voids[i].as_points()
except: pass # if Line
#pts = voids[i].as_points()
try:
pts = voids[i].as_points()
except:
pass # if Line
# pts = voids[i].as_points()
for pt in pts:
trianglator.addHoleVertex(trianglator.addVertex(pt.x, pt.y))
vertices.extend([pt.x, pt.y, pt.z])
@@ -279,29 +301,42 @@ def polygonToSpeckle(geom, index: int, layer, multitype: bool):
trianglator.triangulate()
i = 0
while i < trianglator.getNumTriangles():
tr = [trianglator.getTriangleV0(i),trianglator.getTriangleV1(i),trianglator.getTriangleV2(i)]
tr = [
trianglator.getTriangleV0(i),
trianglator.getTriangleV1(i),
trianglator.getTriangleV2(i),
]
faces.extend([3, tr[0], tr[1], tr[2]])
i+=1
i += 1
ran = range(0, total_vertices)
#print(polygon)
# print(polygon)
col = featureColorfromNativeRenderer(index, layer)
colors = [col for i in ran] # apply same color for all vertices
colors = [col for i in ran] # apply same color for all vertices
mesh = constructMesh(vertices, faces, colors)
if mesh is not None:
polygon.displayValue = [ mesh ]
polygon.displayValue = [mesh]
else:
logToUser("Mesh creation from Polygon failed. Boundaries will be used as displayValue", level = 1, func = inspect.stack()[0][3])
logToUser(
"Mesh creation from Polygon failed. Boundaries will be used as displayValue",
level=1,
func=inspect.stack()[0][3],
)
return polygon
else:
logToUser("Not enough points for Polygon boundary", level = 1, func = inspect.stack()[0][3])
logToUser(
"Not enough points for Polygon boundary",
level=1,
func=inspect.stack()[0][3],
)
return None
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
def polygonToNative(poly: Base, sr: arcpy.SpatialReference) -> arcpy.Polygon:
"""Converts a Speckle Polygon base object to QgsPolygon.
This object must have a 'boundary' and 'voids' properties.
@@ -312,47 +347,55 @@ def polygonToNative(poly: Base, sr: arcpy.SpatialReference) -> arcpy.Polygon:
try:
try:
poly = poly["geometry"]
except: pass
#pts = [pointToCoord(pt) for pt in poly["boundary"].as_points()]
except:
pass
# pts = [pointToCoord(pt) for pt in poly["boundary"].as_points()]
pointsSpeckle = []
if isinstance(poly["boundary"], Circle) or isinstance(poly["boundary"], Arc):
pointsSpeckle = speckleArcCircleToPoints(poly["boundary"])
elif isinstance(poly["boundary"], Polycurve):
pointsSpeckle = specklePolycurveToPoints(poly["boundary"])
elif isinstance(poly["boundary"], Line): pass
elif isinstance(poly["boundary"], Line):
pass
else:
try: pointsSpeckle = poly["boundary"].as_points()
except: pass # if Line
try:
pointsSpeckle = poly["boundary"].as_points()
except:
pass # if Line
pts = [pointToCoord(pt) for pt in pointsSpeckle]
#print(pts)
# print(pts)
outer_arr = [arcpy.Point(*coords) for coords in pts]
outer_arr.append(outer_arr[0])
geomPart = []
try:
for void in poly["voids"]:
#print(void)
#pts = [pointToCoord(pt) for pt in void.as_points()]
# print(void)
# pts = [pointToCoord(pt) for pt in void.as_points()]
pointsSpeckle = []
if isinstance(void, Circle) or isinstance(void, Arc):
pointsSpeckle = speckleArcCircleToPoints(void)
elif isinstance(void, Polycurve):
pointsSpeckle = specklePolycurveToPoints(void)
elif isinstance(void, Line): pass
elif isinstance(void, Line):
pass
else:
try: pointsSpeckle = void.as_points()
except: pass # if Line
try:
pointsSpeckle = void.as_points()
except:
pass # if Line
pts = [pointToCoord(pt) for pt in pointsSpeckle]
inner_arr = [arcpy.Point(*coords) for coords in pts]
inner_arr.append(inner_arr[0])
geomPart.append(arcpy.Array(inner_arr))
except:pass
except:
pass
geomPart.insert(0, outer_arr)
geomPartArray = arcpy.Array(geomPart)
polygon = arcpy.Polygon(geomPartArray, sr, has_z=True)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return polygon
@@ -1,63 +1,83 @@
from math import atan, cos, sin
import math
import json
from typing import List, Union, Tuple
from specklepy.objects import Base
from specklepy.objects.geometry import Box, Vector, Point, Line, Polyline, Curve, Ellipse, Arc, Circle, Polycurve, Plane, Interval
from specklepy.objects.geometry import (
Box,
Vector,
Point,
Line,
Polyline,
Curve,
Ellipse,
Arc,
Circle,
Polycurve,
Plane,
Interval,
)
import arcpy
import numpy as np
import inspect
try:
from speckle.speckle.converter.geometry.point import pointToCoord, pointToSpeckle, addZtoPoint
from speckle.speckle.converter.layers.utils import get_scale_factor
from speckle.speckle.ui.logger import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.point import pointToCoord, pointToSpeckle, addZtoPoint
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.utils import get_scale_factor
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
from speckle.speckle.converter.geometry.point import (
pointToCoord,
pointToSpeckle,
addZtoPoint,
)
from speckle.speckle.converter.geometry.utils import speckleArcCircleToPoints
from speckle.speckle.converter.layers.utils import get_scale_factor
from speckle.speckle.utils.panel_logging import logToUser
def circleToSpeckle(center, point):
print("___Circle to Speckle____")
try:
rad = math.sqrt(math.pow((center[0] - point[0]),2) + math.pow((center[1] - point[1]),2) )
#print(rad)
if len(center)>2: center_z = center[2]
else: center_z = 0
length = rad*2*math.pi
rad = math.sqrt(
math.pow((center[0] - point[0]), 2) + math.pow((center[1] - point[1]), 2)
)
# print(rad)
if len(center) > 2:
center_z = center[2]
else:
center_z = 0
length = rad * 2 * math.pi
domain = [0, length]
plane = [center[0], center[1], center_z, 0,0,1, 1,0,0, 0,1,0]
units = 3 #"m"
plane = [center[0], center[1], center_z, 0, 0, 1, 1, 0, 0, 0, 1, 0]
units = 3 # "m"
args = [0] + [rad] + domain + plane + [units]
#print(args)
# print(args)
c = Circle.from_list(args)
c.plane.origin.units = "m"
c.units = "m"
#print(c)
# print(c)
return c
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
def multiPolylineToSpeckle(geom, feature, layer, multiType: bool):
print("___MultiPolyline to Speckle____")
polyline = []
try:
print(enumerate(geom.getPart()))
for i,x in enumerate(geom.getPart()):
poly = arcpy.Polyline(x, arcpy.Describe(layer.dataSource).SpatialReference, has_z = True)
for i, x in enumerate(geom.getPart()):
poly = arcpy.Polyline(
x, arcpy.Describe(layer.dataSource).SpatialReference, has_z=True
)
print(poly)
polyline.append(polylineToSpeckle(poly, feature, layer, poly.isMultipart))
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return polyline
def polylineToSpeckle(geom, feature, layer, multiType: bool):
print("___Polyline to Speckle____")
polyline = None
@@ -74,28 +94,38 @@ def polylineToSpeckle(geom, feature, layer, multiType: bool):
else:
for p in geom:
for pt in p:
if pt != None: pointList.append(pt)#; print(pt.Z)
if pt != None:
pointList.append(pt) # ; print(pt.Z)
closed = False
if pointList[0] == pointList[len(pointList)-1]:
if pointList[0] == pointList[len(pointList) - 1]:
closed = True
pointList = pointList[:-1]
polyline = polylineFromVerticesToSpeckle(pointList, closed, feature, layer)
polyline = polylineFromVerticesToSpeckle(
pointList, closed, feature, layer
)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return polyline
def polylineFromVerticesToSpeckle(vertices: List[Point], closed: bool, feature, layer) -> Polyline:
def polylineFromVerticesToSpeckle(
vertices: List[Point], closed: bool, feature, layer
) -> Polyline:
"""Converts a Polyline to Speckle"""
polyline = Polyline(units = "m")
polyline = Polyline(units="m")
try:
if isinstance(vertices, list):
if len(vertices) > 0 and isinstance(vertices[0], Point):
specklePts = vertices
else: specklePts = [pointToSpeckle(pt, feature, layer) for pt in vertices] #breaks unexplainably
#elif isinstance(vertices, QgsVertexIterator):
else:
specklePts = [
pointToSpeckle(pt, feature, layer) for pt in vertices
] # breaks unexplainably
# elif isinstance(vertices, QgsVertexIterator):
# specklePts = [pointToSpeckle(pt, feature, layer) for pt in vertices]
else: return None
else:
return None
specklePts = []
for pt in vertices:
@@ -108,13 +138,14 @@ def polylineFromVerticesToSpeckle(vertices: List[Point], closed: bool, feature,
polyline.units = specklePts[0].units
for i, point in enumerate(specklePts):
if closed and i == len(specklePts) - 1 and specklePts[0] == point:
continue # if we consider the last pt, do not add is coincides with the first (and type is Closed)
continue # if we consider the last pt, do not add is coincides with the first (and type is Closed)
polyline.value.extend([point.x, point.y, point.z])
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return polyline
def arc3ptToSpeckle(p0: List, p1: List, p2: List, feature, layer) -> Arc:
print("____arc 3pt to Speckle___")
arc = Arc()
@@ -125,8 +156,12 @@ def arc3ptToSpeckle(p0: List, p1: List, p2: List, feature, layer) -> Arc:
arc.startPoint = pointToSpeckle(arcpy.Point(*p0), feature, layer)
arc.midPoint = pointToSpeckle(arcpy.Point(*p1), feature, layer)
arc.endPoint = pointToSpeckle(arcpy.Point(*p2), feature, layer)
center, radius = getArcCenter(Point.from_list(p0), Point.from_list(p1), Point.from_list(p2))
arc.plane = Plane() #.from_list(Point(), Vector(Point(0, 0, 1)), Vector(Point(0,1,0)), Vector(Point(-1,0,0)))
center, radius = getArcCenter(
Point.from_list(p0), Point.from_list(p1), Point.from_list(p2)
)
arc.plane = (
Plane()
) # .from_list(Point(), Vector(Point(0, 0, 1)), Vector(Point(0,1,0)), Vector(Point(-1,0,0)))
arc.plane.origin = Point.from_list(center)
arc.plane.origin.units = "m"
arc.units = "m"
@@ -136,42 +171,46 @@ def arc3ptToSpeckle(p0: List, p1: List, p2: List, feature, layer) -> Arc:
arc.plane.normal = getArcNormal(arc, arc.midPoint)
#arc.angleRadians = abs(angle1 + angle2)
#print(arc.angleRadians)
# arc.angleRadians = abs(angle1 + angle2)
# print(arc.angleRadians)
#col = featureColorfromNativeRenderer(feature, layer)
#arc['displayStyle'] = {}
#arc['displayStyle']['color'] = col
# col = featureColorfromNativeRenderer(feature, layer)
# arc['displayStyle'] = {}
# arc['displayStyle']['color'] = col
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return arc
def curveBezierToSpeckle(segmStartCoord, segmEndCoord, knots, feature, layer):
print("____bezier curve to Speckle____")
try:
degree = 3
points = [
tuple(knots[0]), tuple(segmStartCoord), tuple(knots[1]), tuple(segmEndCoord)
] #[segmStartCoord, *coords]
tuple(knots[0]),
tuple(segmStartCoord),
tuple(knots[1]),
tuple(segmEndCoord),
] # [segmStartCoord, *coords]
print(points)
num_points = len(points) #2
num_points = len(points) # 2
knot_count = num_points + degree - 1 #4
knot_count = num_points + degree - 1 # 4
knots = [0] * knot_count
print(knots)
for i in range(1, len(knots)):
knots[i] = i // 3
print(knots[i])
length = 1 #spline.calc_length()
length = 1 # spline.calc_length()
domain = Interval(start=0, end=length, totalChildrenCount=0)
points = [tuple(pt) for pt in points]
curve = Curve(
degree = degree,
closed = False,
periodic= True if (segmStartCoord == segmEndCoord) else False,
points= list(sum(points, ())), # magic (flatten list of tuples)
degree=degree,
closed=False,
periodic=True if (segmStartCoord == segmEndCoord) else False,
points=list(sum(points, ())), # magic (flatten list of tuples)
weights=[1] * num_points,
knots=knots,
rational=False,
@@ -185,13 +224,15 @@ def curveBezierToSpeckle(segmStartCoord, segmEndCoord, knots, feature, layer):
print(curve)
return curve
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
def curveToSpeckle(geom, geomType, feature, layer) -> Union[Circle, Arc, Polyline, Polycurve]:
def curveToSpeckle(
geom, geomType, feature, layer
) -> Union[Circle, Arc, Polyline, Polycurve]:
print("____curve to Speckle____")
boundary = Polycurve(units = "m")
boundary = Polycurve(units="m")
print(geomType)
try:
# look for "curvePaths" or "curveRings"[[ (startPt, {arcs, beziers etc}, optional(endPt))],[],...], "rings"
@@ -204,19 +245,23 @@ def curveToSpeckle(geom, geomType, feature, layer) -> Union[Circle, Arc, Polylin
# a - elliptical arc (endPt, centralPt) e.g. for circle: [[[631307.05960000027,5803698.4477999993,0],{"a":[[631307.05960000027,5803698.4477999993,0],[631307.05960000027,5803414.92656173],0,1]}]]
# c - circular arc (endPt, throughPt) e.g. [[[633242.45179999992,5803058.0354999993,0],{"c":[[633718.26040000003,5803496.4210000001,0],[633337.75764975848,5803431.9997026781]]},[633242.45179999992,5803058.0354999993,0]]]
if geomType == "Polyline": boundary.closed = False
else: boundary.closed = True
if geomType == "Polyline":
boundary.closed = False
else:
boundary.closed = True
segments = []
for key, val in json.loads(geom.JSON).items():
print(key)
if key == "curveRings" or key == "curvePaths":
#boundary.closed = True
# boundary.closed = True
includesLines = 0
for segm in val: # segm: List
print(segm) #e.g. [[631307.05960000027,5803698.4477999993,0], {"a":[[631307.05960000027,5803698.4477999993,0],[631307.05960000027,5803414.92656173],0,1]}]
for segm in val: # segm: List
print(
segm
) # e.g. [[631307.05960000027,5803698.4477999993,0], {"a":[[631307.05960000027,5803698.4477999993,0],[631307.05960000027,5803414.92656173],0,1]}]
segmStartCoord: List = addZtoPoint(segm[0])
# go through all elements (points, a, c, ...)
@@ -228,47 +273,75 @@ def curveToSpeckle(geom, geomType, feature, layer) -> Union[Circle, Arc, Polylin
# if previous segments exist
if len(segments) > 0:
segmOldData = segm[k-1]
if isinstance(segmOldData, dict): # get "end point" of previous segment
for key3, val3 in segmOldData.items():
segmStartCoord: List = addZtoPoint(val2[0])
elif isinstance(segmOldData, list) and isinstance(segmOldData[0], float):
segmStartCoord: List = segmOldData
segmOldData = segm[k - 1]
if isinstance(
segmOldData, dict
): # get "end point" of previous segment
for key3, val3 in segmOldData.items():
segmStartCoord: List = addZtoPoint(val2[0])
elif isinstance(segmOldData, list) and isinstance(
segmOldData[0], float
):
segmStartCoord: List = segmOldData
segmStartCoord = addZtoPoint(segmStartCoord)
if isinstance(segm[k], dict):
for key2, val2 in segm[k].items():
if key2 == "a": # elliptical arc (endPt, centralPt)
if key2 == "a": # elliptical arc (endPt, centralPt)
# e.g. {'a': [[633883.1035000002, 5802972.5812, 0], [634028.3379278888, 5802908.342895357], 0, 1, 1.1543577096027686, 473.59966687227444, 0.33531864204900685]}
segmEndCoord = addZtoPoint(val2[0]) # [631307.05960000027,5803698.4477999993,0]
segmCenter = addZtoPoint(val2[1]) # [631307.05960000027,5803414.92656173]
segmEndCoord = addZtoPoint(
val2[0]
) # [631307.05960000027,5803698.4477999993,0]
segmCenter = addZtoPoint(
val2[1]
) # [631307.05960000027,5803414.92656173]
if segmStartCoord == segmEndCoord:
if len(val2) == 4:
print("full circle")
#boundary.closed = True
segmentLocal = circleToSpeckle(segmCenter, segmEndCoord)
# boundary.closed = True
segmentLocal = circleToSpeckle(
segmCenter, segmEndCoord
)
segments.append(segmentLocal)
lastPt = segmEndCoord
print("segmentLocal:")
print(segmentLocal)
print(segmStartCoord)
print(segmEndCoord)
else: # ellipse
logToUser("SpeckleWarning: ellipse geometry not supported yet", level=1, func = inspect.stack()[0][3])
else: # ellipse
logToUser(
"SpeckleWarning: ellipse geometry not supported yet",
level=1,
func=inspect.stack()[0][3],
)
segments = []
break
else: # elliptical curve
logToUser("SpeckleWarning: ellipse geometry not supported yet", level=1, func = inspect.stack()[0][3])
else: # elliptical curve
logToUser(
"SpeckleWarning: ellipse geometry not supported yet",
level=1,
func=inspect.stack()[0][3],
)
segments = []
break
if key2 == "c": # circular arc (endPt, throughPt)
if key2 == "c": # circular arc (endPt, throughPt)
segmEndCoord: List = addZtoPoint(val2[0]) # [633718.26040000003,5803496.4210000001,0]
segmThrough: List = addZtoPoint(val2[1]) # [633337.7576497585, 5803431.999702678]
segmEndCoord: List = addZtoPoint(
val2[0]
) # [633718.26040000003,5803496.4210000001,0]
segmThrough: List = addZtoPoint(
val2[1]
) # [633337.7576497585, 5803431.999702678]
segmentLocal = arc3ptToSpeckle(segmStartCoord, segmThrough, segmEndCoord, geom, layer)
segmentLocal = arc3ptToSpeckle(
segmStartCoord,
segmThrough,
segmEndCoord,
geom,
layer,
)
segments.append(segmentLocal)
print("segmentLocal:")
print(segmentLocal)
@@ -276,11 +349,15 @@ def curveToSpeckle(geom, geomType, feature, layer) -> Union[Circle, Arc, Polylin
print(segmEndCoord)
lastPt = segmEndCoord
if key2 == "b": # bezier curve (endPt, controlPts)
logToUser("SpeckleWarning: bezier curve geometry not supported yet", level=1, func = inspect.stack()[0][3])
if key2 == "b": # bezier curve (endPt, controlPts)
logToUser(
"SpeckleWarning: bezier curve geometry not supported yet",
level=1,
func=inspect.stack()[0][3],
)
segments = []
break
r'''
r"""
segmEndCoord: List = addZtoPoint(val2[0]) # [633718.26040000003,5803496.4210000001,0]
#segmThrough: List = val2[1] # [633337.7576497585, 5803431.999702678]
coords = val2[1:]
@@ -292,9 +369,11 @@ def curveToSpeckle(geom, geomType, feature, layer) -> Union[Circle, Arc, Polylin
print(segmEndCoord)
lastPt = segmEndCoord
'''
"""
elif isinstance(segm[k], list) and isinstance(segm[k][0], float): # add line to point
elif isinstance(segm[k], list) and isinstance(
segm[k][0], float
): # add line to point
print("add line")
segm[k] = addZtoPoint(segm[k])
segmentLocal = lineFrom2pt(segmStartCoord, segm[k])
@@ -308,65 +387,70 @@ def curveToSpeckle(geom, geomType, feature, layer) -> Union[Circle, Arc, Polylin
print(segmentLocal.end)
# for the last point
if k == len(segm)-1 and isinstance(segm[k], list):
if k == len(segm) - 1 and isinstance(segm[k], list):
print("last element is a point (adding line)")
lastPt = addZtoPoint(lastPt)
if lastPt != segm[0]:
#segmentLocal = lineFrom2pt(lastPt, segm[0])
#segments.append(segmentLocal)
#includesLines = 1
#print("segmentLocal:")
#print(segmentLocal)
#print(segmentLocal.start)
#print(segmentLocal.end)
# segmentLocal = lineFrom2pt(lastPt, segm[0])
# segments.append(segmentLocal)
# includesLines = 1
# print("segmentLocal:")
# print(segmentLocal)
# print(segmentLocal.start)
# print(segmentLocal.end)
boundary.closed = True
#pts = speckleArcCircleToPoints(segmentLocal)
#pts.append(segm[k])
#arcgisPts = [arcpy.Point(pt[0], pt[1], pt[2]) for pt in pts]
#segmentLocal = polylineFromVerticesToSpeckle(arcgisPts, True, feature, layer)
# pts = speckleArcCircleToPoints(segmentLocal)
# pts.append(segm[k])
# arcgisPts = [arcpy.Point(pt[0], pt[1], pt[2]) for pt in pts]
# segmentLocal = polylineFromVerticesToSpeckle(arcgisPts, True, feature, layer)
boundary.segments = segments
print(segments)
if len(segments) == 1:
boundary = segments[0]
#if isinstance(boundary, Arc) or isinstance(boundary, Circle):
# if isinstance(boundary, Arc) or isinstance(boundary, Circle):
# boundary.displayValue = Polyline.from_points(speckleArcCircleToPoints(boundary))
elif len(segments) > 1: # and includesLines == 0:
#boundary.displayValue = Polyline.from_points(specklePolycurveToPoints(boundary))
elif len(segments) > 1: # and includesLines == 0:
# boundary.displayValue = Polyline.from_points(specklePolycurveToPoints(boundary))
pass
#boundary.closed = True
#elif len(segments) > 1 and includesLines == 1:
# boundary.closed = True
# elif len(segments) > 1 and includesLines == 1:
# print("includes lines!")
# points = specklePolycurveToPoints(boundary)
# boundary = Polyline.from_points(points)
else: return None
else:
return None
#boundary.displayValue = Polyline.from_points(specklePolycurveToPoints(boundary))
# boundary.displayValue = Polyline.from_points(specklePolycurveToPoints(boundary))
print(boundary)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return boundary
def lineFrom2pt(pt1: List[float], pt2: List[float]):
line = Line(units = "m" )#.from_list([*pt1, *pt2, *domain])
line = Line(units="m") # .from_list([*pt1, *pt2, *domain])
try:
pt1 = addZtoPoint(pt1)
pt2 = addZtoPoint(pt2)
dist = math.sqrt( math.pow((pt2[0] - pt1[0]), 2) + math.pow((pt2[1] - pt1[1]), 2) + math.pow((pt2[2] - pt1[2]), 2) )
dist = math.sqrt(
math.pow((pt2[0] - pt1[0]), 2)
+ math.pow((pt2[1] - pt1[1]), 2)
+ math.pow((pt2[2] - pt1[2]), 2)
)
print(dist)
domain = [0, dist, 0, 0]
line.start = Point.from_list(pt1)
line.end = Point.from_list(pt2)
line.start.units = line.end.units = "m"
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return line
def polylineToNative(poly: Polyline, sr: arcpy.SpatialReference) -> arcpy.Polyline:
"""Converts a Speckle Polyline to QgsLineString"""
print("__ convert polyline to native __")
@@ -376,20 +460,24 @@ def polylineToNative(poly: Polyline, sr: arcpy.SpatialReference) -> arcpy.Polyli
if isinstance(poly, Polycurve):
poly = specklePolycurveToPoints(poly)
if isinstance(poly, Arc) or isinstance(poly, Circle):
try: poly = poly["displayValue"]
except: poly = speckleArcCircleToPoints(poly)
try:
poly = poly["displayValue"]
except:
poly = speckleArcCircleToPoints(poly)
if isinstance(poly, list): pts = [pointToCoord(pt) for pt in poly]
else: pts = [pointToCoord(pt) for pt in poly.as_points()]
if isinstance(poly, list):
pts = [pointToCoord(pt) for pt in poly]
else:
pts = [pointToCoord(pt) for pt in poly.as_points()]
if poly.closed is True:
pts.append( pointToCoord(poly.as_points()[0]) )
pts.append(pointToCoord(poly.as_points()[0]))
pts_coord_list = [arcpy.Point(*coords) for coords in pts]
polyline = arcpy.Polyline( arcpy.Array(pts_coord_list), sr, has_z=True )
#print(polyline.JSON)
polyline = arcpy.Polyline(arcpy.Array(pts_coord_list), sr, has_z=True)
# print(polyline.JSON)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return polyline
@@ -398,13 +486,16 @@ def lineToNative(line: Line, sr: arcpy.SpatialReference) -> arcpy.Polyline:
print("___Line to Native___")
try:
pts = [pointToCoord(pt) for pt in [line.start, line.end]]
line = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in pts]), sr , has_z=True)
line = arcpy.Polyline(
arcpy.Array([arcpy.Point(*coords) for coords in pts]), sr, has_z=True
)
return line
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
def curveToNative(poly: Curve, sr: arcpy.SpatialReference) -> arcpy.Polyline:
"""Converts a Speckle Curve to Native"""
try:
@@ -412,270 +503,155 @@ def curveToNative(poly: Curve, sr: arcpy.SpatialReference) -> arcpy.Polyline:
curve = polylineToNative(display, sr)
return curve
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
def arcToNative(poly: Arc, sr: arcpy.SpatialReference) -> arcpy.Polyline:
"""Converts a Speckle Arc to Native"""
try:
arc = arcToNativePolyline(poly, sr) #QgsCircularString(pointToNative(poly.startPoint), pointToNative(poly.midPoint), pointToNative(poly.endPoint))
arc = arcToNativePolyline(
poly, sr
) # QgsCircularString(pointToNative(poly.startPoint), pointToNative(poly.midPoint), pointToNative(poly.endPoint))
return arc
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
def ellipseToNative(poly: Ellipse, sr: arcpy.SpatialReference):
logToUser("Ellipse geometry is not supported yet", level=1)
return
def circleToNative(poly: Circle, sr: arcpy.SpatialReference) -> arcpy.Polyline:
"""Converts a Speckle Circle to QgsLineString"""
print("___Convert Circle to Native___")
curve = None
try:
points = []
angle1 = math.pi/2
angle1 = math.pi / 2
pointsNum = math.floor(math.pi*2) * 12
if pointsNum <4: pointsNum = 4
pointsNum = math.floor(math.pi * 2) * 12
if pointsNum < 4:
pointsNum = 4
points.append(pointToCoord(poly.plane.origin))
radScaled = poly.radius * get_scale_factor(poly.units)
points[0][1] += radScaled
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 * math.pi*2 * normal
pt = Point( x = poly.plane.origin.x * get_scale_factor(poly.units) + radScaled * cos(angle), y = poly.plane.origin.y * get_scale_factor(poly.units) + radScaled * sin(angle), z = 0)
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 * math.pi * 2 * normal
pt = Point(
x=poly.plane.origin.x * get_scale_factor(poly.units)
+ radScaled * cos(angle),
y=poly.plane.origin.y * get_scale_factor(poly.units)
+ radScaled * sin(angle),
z=0,
)
print(pt)
pt.units = "m"
points.append(pointToCoord(pt))
points.append(points[0])
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr , has_z=True)
curve = arcpy.Polyline(
arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True
)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return curve
def polycurveToNative(poly: Polycurve, sr: arcpy.SpatialReference) -> arcpy.Polyline:
points = []
curve = None
print("___Polycurve to native___")
try:
for i, segm in enumerate(poly.segments): # Line, Polyline, Curve, Arc, Circle
for i, segm in enumerate(poly.segments): # Line, Polyline, Curve, Arc, Circle
print("___start segment")
if isinstance(segm,Line): converted = lineToNative(segm, sr) # QgsLineString
elif isinstance(segm,Polyline): converted = polylineToNative(segm, sr) # QgsLineString
elif isinstance(segm,Curve): converted = curveToNative(segm, sr) # QgsLineString
elif isinstance(segm,Circle): converted = circleToNative(segm, sr) # QgsLineString
elif isinstance(segm,Arc): converted = arcToNativePolyline(segm, sr) # QgsLineString
else: # either return a part of the curve, of skip this segment and try next
logToUser(f"Part of the polycurve cannot be converted", level=1, func = inspect.stack()[0][3])
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr , has_z=True)
if isinstance(segm, Line):
converted = lineToNative(segm, sr) # QgsLineString
elif isinstance(segm, Polyline):
converted = polylineToNative(segm, sr) # QgsLineString
elif isinstance(segm, Curve):
converted = curveToNative(segm, sr) # QgsLineString
elif isinstance(segm, Circle):
converted = circleToNative(segm, sr) # QgsLineString
elif isinstance(segm, Arc):
converted = arcToNativePolyline(segm, sr) # QgsLineString
else: # either return a part of the curve, of skip this segment and try next
logToUser(
f"Part of the polycurve cannot be converted",
level=1,
func=inspect.stack()[0][3],
)
curve = arcpy.Polyline(
arcpy.Array([arcpy.Point(*coords) for coords in points]),
sr,
has_z=True,
)
return curve
if converted is not None:
#print(converted) # <geoprocessing describe geometry object object at 0x000002B2D3E338D0>
# print(converted) # <geoprocessing describe geometry object object at 0x000002B2D3E338D0>
for part in converted:
#print("Part: ")
#print(part) # <geoprocessing array object object at 0x000002B2D2E09530>
# print("Part: ")
# print(part) # <geoprocessing array object object at 0x000002B2D2E09530>
for pt in part:
#print(pt) # 64.4584221540162 5.5 NaN NaN
if pt.Z != None: pt_z = pt.Z
else: pt_z = 0
#print(pt_z)
#print(len(points))
if len(points)>0 and pt.X == points[len(points)-1][0] and pt.Y == points[len(points)-1][1] and pt_z == points[len(points)-1][2]: pass
else: points.append(pointToCoord(Point(x=pt.X, y = pt.Y, z = pt_z, units = "m"))) # e.g. [[64.4584221540162, 5.499999999999999, 0.0], [64.45461685210796, 5.587155742747657, 0.0]]
# print(pt) # 64.4584221540162 5.5 NaN NaN
if pt.Z != None:
pt_z = pt.Z
else:
pt_z = 0
# print(pt_z)
# print(len(points))
if (
len(points) > 0
and pt.X == points[len(points) - 1][0]
and pt.Y == points[len(points) - 1][1]
and pt_z == points[len(points) - 1][2]
):
pass
else:
points.append(
pointToCoord(Point(x=pt.X, y=pt.Y, z=pt_z, units="m"))
) # e.g. [[64.4584221540162, 5.499999999999999, 0.0], [64.45461685210796, 5.587155742747657, 0.0]]
else:
logToUser(f"Part of the polycurve cannot be converted", level=1, func = inspect.stack()[0][3])
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True )
logToUser(
f"Part of the polycurve cannot be converted",
level=1,
func=inspect.stack()[0][3],
)
curve = arcpy.Polyline(
arcpy.Array([arcpy.Point(*coords) for coords in points]),
sr,
has_z=True,
)
return curve
#print(curve)
# print(curve)
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True )
curve = arcpy.Polyline(
arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True
)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return curve
def arcToNativePolyline(poly: Union[Arc, Circle], sr: arcpy.SpatialReference):
print("__Arc/Circle to native polyline__")
curve = None
try:
pointsSpeckle = speckleArcCircleToPoints(poly)
points = [pointToCoord(p) for p in pointsSpeckle]
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True )
curve = arcpy.Polyline(
arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True
)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return curve
def specklePolycurveToPoints(poly: Polycurve) -> List[Point]:
print("_____Speckle Polycurve to points____")
points = []
try:
for segm in poly.segments:
#print(segm)
pts = []
if isinstance(segm, Arc) or isinstance(segm, Circle): # or isinstance(segm, Curve):
print("Arc or Curve")
pts: List[Point] = speckleArcCircleToPoints(segm)
elif isinstance(segm, Line):
print("Line")
pts: List[Point] = [segm.start, segm.end]
elif isinstance(segm, Polyline):
print("Polyline")
pts: List[Point] = segm.as_points()
points.extend(pts)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return points
def speckleArcCircleToPoints(poly: Union[Arc, Circle]) -> List[Point]:
print("__Arc or Circle to Points___")
points = []
try:
#print(poly.plane)
#print(poly.plane.normal)
if poly.plane is None or poly.plane.normal.z == 0: normal = 1
else: normal = poly.plane.normal.z
#print(poly.plane.origin)
if isinstance(poly, Circle):
interval = 2*math.pi
range_start = 0
angle1 = 0
else: # if Arc
points.append(poly.startPoint)
range_start = 0
#angle1, angle2 = getArcAngles(poly)
interval, angle1, angle2 = getArcRadianAngle(poly)
interval = abs(angle2 - angle1)
#print(angle1)
#print(angle2)
if (angle1 > angle2 and normal == -1) or (angle2 > angle1 and normal == 1): pass
if angle1 > angle2 and normal == 1: interval = abs( (2*math.pi-angle1) + angle2)
if angle2 > angle1 and normal == -1: interval = abs( (2*math.pi-angle2) + angle1)
#print(interval)
#print(normal)
pointsNum = math.floor( abs(interval)) * 12
if pointsNum <4: pointsNum = 4
for i in range(range_start, pointsNum + 1):
k = i/pointsNum # to reset values from 1/10 to 1
angle = angle1 + k * interval * normal
#print(k)
#print(angle)
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.plane.origin.units
points.append(pt)
if isinstance(poly, Arc): points.append(poly.endPoint)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return points
def getArcRadianAngle(arc: Arc) -> List[float]:
try:
interval = None
normal = arc.plane.normal.z
angle1, angle2 = getArcAngles(arc)
if angle1 is None or angle2 is None: return None
interval = abs(angle2 - angle1)
if (angle1 > angle2 and normal == -1) or (angle2 > angle1 and normal == 1): pass
if angle1 > angle2 and normal == 1: interval = abs( (2*math.pi-angle1) + angle2)
if angle2 > angle1 and normal == -1: interval = abs( (2*math.pi-angle2) + angle1)
return interval, angle1, angle2
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None, None, None
def getArcAngles(poly: Arc) -> Tuple[float]:
try:
if poly.startPoint.x == poly.plane.origin.x: angle1 = math.pi/2
else: 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
if poly.endPoint.x == poly.plane.origin.x: angle2 = math.pi/2
else: 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
return angle1, angle2
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None, None
def getArcNormal(poly: Arc, midPt: Point):
print("____getArcNormal___")
try:
angle1, angle2 = getArcAngles(poly)
if midPt.x == poly.plane.origin.x: angle = math.pi/2
else: angle = atan( abs ((midPt.y - poly.plane.origin.y) / (midPt.x - poly.plane.origin.x) )) # between 0 and pi/2
if poly.plane.origin.x < midPt.x and poly.plane.origin.y > midPt.y: angle = 2*math.pi - angle
if poly.plane.origin.x > midPt.x and poly.plane.origin.y > midPt.y: angle = math.pi + angle
if poly.plane.origin.x > midPt.x and poly.plane.origin.y < midPt.y: angle = math.pi - angle
normal = Vector()
normal.x = normal.y = 0
if angle1 > angle > angle2: normal.z = -1
if angle1 > angle2 > angle: normal.z = 1
if angle2 > angle1 > angle: normal.z = -1
if angle > angle1 > angle2: normal.z = 1
if angle2 > angle > angle1: normal.z = 1
if angle > angle2 > angle1: normal.z = -1
print(angle1)
print(angle)
print(angle2)
print(normal)
return normal
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None
def getArcCenter(p1: Point, p2: Point, p3: Point) -> Tuple[List, float]:
#print(p1)
try:
p1 = np.array(p1.to_list())
p2 = np.array(p2.to_list())
p3 = np.array(p3.to_list())
a = np.linalg.norm(p3 - p2)
b = np.linalg.norm(p3 - p1)
c = np.linalg.norm(p2 - p1)
s = (a + b + c) / 2
radius = a*b*c / 4 / np.sqrt(s * (s - a) * (s - b) * (s - c))
b1 = a*a * (b*b + c*c - a*a)
b2 = b*b * (a*a + c*c - b*b)
b3 = c*c * (a*a + b*b - c*c)
center = np.column_stack((p1, p2, p3)).dot(np.hstack((b1, b2, b3)))
center /= b1 + b2 + b3
center = center.tolist()
return center, radius
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None, None
@@ -1,16 +1,132 @@
from specklepy.objects.geometry import Point, Line, Polyline, Circle, Arc, Polycurve
import math
from math import cos, sin, atan
import numpy as np
from specklepy.objects.geometry import Point, Line, Polyline, Circle, Arc, Polycurve, Vector
from specklepy.objects import Base
from typing import List, Union
from typing import List, Tuple, Union
import inspect
try:
from speckle.speckle.converter.geometry.polyline import speckleArcCircleToPoints, specklePolycurveToPoints
from speckle.speckle.ui.logger import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.polyline import speckleArcCircleToPoints, specklePolycurveToPoints
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
from speckle.speckle.utils.panel_logging import logToUser
def apply_pt_offsets_rotation_on_send(
x: float, y: float, dataStorage
) -> Tuple[Union[float, None]]: # 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) or isinstance(rotation, int))
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 transform_speckle_pt_on_receive(pt_original: Point, dataStorage) -> Point:
offset_x = dataStorage.crs_offset_x
offset_y = dataStorage.crs_offset_y
rotation = dataStorage.crs_rotation
pt = Point(
x=pt_original.x, y=pt_original.y, z=pt_original.z, units=pt_original.units
)
gisLayer = None
try:
gisLayer = dataStorage.latestHostApp.lower().endswith("gis")
applyTransforms = False if (gisLayer and gisLayer is True) else True
except Exception as e:
print(e)
applyTransforms = True
# for non-GIS layers
if applyTransforms is True:
if (
rotation is not None
and (isinstance(rotation, float) or isinstance(rotation, int))
and -360 < rotation < 360
):
a = rotation * math.pi / 180
x2 = pt.x
y2 = pt.y
# if a > 0: # turn counterclockwise on receive
x2 = pt.x * math.cos(a) - pt.y * math.sin(a)
y2 = pt.x * math.sin(a) + pt.y * math.cos(a)
pt.x = x2
pt.y = y2
if (
offset_x is not None
and isinstance(offset_x, float)
and offset_y is not None
and isinstance(offset_y, float)
):
pt.x += offset_x
pt.y += offset_y
# for GIS layers
if gisLayer is True:
try:
offset_x = dataStorage.current_layer_crs_offset_x
offset_y = dataStorage.current_layer_crs_offset_y
rotation = dataStorage.current_layer_crs_rotation
if (
rotation is not None
and isinstance(rotation, float)
and -360 < rotation < 360
):
a = rotation * math.pi / 180
x2 = pt.x
y2 = pt.y
# if a > 0: # turn counterclockwise on receive
x2 = pt.x * math.cos(a) - pt.y * math.sin(a)
y2 = pt.x * math.sin(a) + pt.y * math.cos(a)
pt.x = x2
pt.y = y2
if (
offset_x is not None
and isinstance(offset_x, float)
and offset_y is not None
and isinstance(offset_y, float)
):
pt.x += offset_x
pt.y += offset_y
except Exception as e:
print(e)
return pt
def apply_pt_transform_matrix(pt: Point, dataStorage) -> Point:
try:
if dataStorage.matrix is not None:
b = np.matrix([pt.x, pt.y, pt.z, 1])
res = b * dataStorage.matrix
x, y, z = res.item(0), res.item(1), res.item(2)
return Point(x=x, y=y, z=z, units=pt.units)
except Exception as e:
print(e)
return pt
def speckleBoundaryToSpecklePts(boundary: Union[None, Polyline, Arc, Line, Polycurve]) -> List[Point]:
@@ -29,3 +145,175 @@ def speckleBoundaryToSpecklePts(boundary: Union[None, Polyline, Arc, Line, Polyc
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return polyBorder
def speckleArcCircleToPoints(poly: Union[Arc, Circle]) -> List[Point]:
print("__Arc or Circle to Points___")
points = []
try:
#print(poly.plane)
#print(poly.plane.normal)
if poly.plane is None or poly.plane.normal.z == 0: normal = 1
else: normal = poly.plane.normal.z
#print(poly.plane.origin)
if isinstance(poly, Circle):
interval = 2*math.pi
range_start = 0
angle1 = 0
else: # if Arc
points.append(poly.startPoint)
range_start = 0
#angle1, angle2 = getArcAngles(poly)
interval, angle1, angle2 = getArcRadianAngle(poly)
interval = abs(angle2 - angle1)
#print(angle1)
#print(angle2)
if (angle1 > angle2 and normal == -1) or (angle2 > angle1 and normal == 1): pass
if angle1 > angle2 and normal == 1: interval = abs( (2*math.pi-angle1) + angle2)
if angle2 > angle1 and normal == -1: interval = abs( (2*math.pi-angle2) + angle1)
#print(interval)
#print(normal)
pointsNum = math.floor( abs(interval)) * 12
if pointsNum <4: pointsNum = 4
for i in range(range_start, pointsNum + 1):
k = i/pointsNum # to reset values from 1/10 to 1
angle = angle1 + k * interval * normal
#print(k)
#print(angle)
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.plane.origin.units
points.append(pt)
if isinstance(poly, Arc): points.append(poly.endPoint)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return points
def getArcRadianAngle(arc: Arc) -> List[float]:
try:
interval = None
normal = arc.plane.normal.z
angle1, angle2 = getArcAngles(arc)
if angle1 is None or angle2 is None: return None
interval = abs(angle2 - angle1)
if (angle1 > angle2 and normal == -1) or (angle2 > angle1 and normal == 1): pass
if angle1 > angle2 and normal == 1: interval = abs( (2*math.pi-angle1) + angle2)
if angle2 > angle1 and normal == -1: interval = abs( (2*math.pi-angle2) + angle1)
return interval, angle1, angle2
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None, None, None
def getArcAngles(poly: Arc) -> Tuple[float]:
try:
if poly.startPoint.x == poly.plane.origin.x: angle1 = math.pi/2
else: 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
if poly.endPoint.x == poly.plane.origin.x: angle2 = math.pi/2
else: 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
return angle1, angle2
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None, None
def specklePolycurveToPoints(poly: Polycurve) -> List[Point]:
print("_____Speckle Polycurve to points____")
points = []
try:
for segm in poly.segments:
#print(segm)
pts = []
if isinstance(segm, Arc) or isinstance(segm, Circle): # or isinstance(segm, Curve):
print("Arc or Curve")
pts: List[Point] = speckleArcCircleToPoints(segm)
elif isinstance(segm, Line):
print("Line")
pts: List[Point] = [segm.start, segm.end]
elif isinstance(segm, Polyline):
print("Polyline")
pts: List[Point] = segm.as_points()
points.extend(pts)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return points
def getArcNormal(poly: Arc, midPt: Point):
print("____getArcNormal___")
try:
angle1, angle2 = getArcAngles(poly)
if midPt.x == poly.plane.origin.x: angle = math.pi/2
else: angle = atan( abs ((midPt.y - poly.plane.origin.y) / (midPt.x - poly.plane.origin.x) )) # between 0 and pi/2
if poly.plane.origin.x < midPt.x and poly.plane.origin.y > midPt.y: angle = 2*math.pi - angle
if poly.plane.origin.x > midPt.x and poly.plane.origin.y > midPt.y: angle = math.pi + angle
if poly.plane.origin.x > midPt.x and poly.plane.origin.y < midPt.y: angle = math.pi - angle
normal = Vector()
normal.x = normal.y = 0
if angle1 > angle > angle2: normal.z = -1
if angle1 > angle2 > angle: normal.z = 1
if angle2 > angle1 > angle: normal.z = -1
if angle > angle1 > angle2: normal.z = 1
if angle2 > angle > angle1: normal.z = 1
if angle > angle2 > angle1: normal.z = -1
print(angle1)
print(angle)
print(angle2)
print(normal)
return normal
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None
def getArcCenter(p1: Point, p2: Point, p3: Point) -> Tuple[List, float]:
#print(p1)
try:
p1 = np.array(p1.to_list())
p2 = np.array(p2.to_list())
p3 = np.array(p3.to_list())
a = np.linalg.norm(p3 - p2)
b = np.linalg.norm(p3 - p1)
c = np.linalg.norm(p2 - p1)
s = (a + b + c) / 2
radius = a*b*c / 4 / np.sqrt(s * (s - a) * (s - b) * (s - c))
b1 = a*a * (b*b + c*c - a*a)
b2 = b*b * (a*a + c*c - b*b)
b3 = c*c * (a*a + b*b - c*c)
center = np.column_stack((p1, p2, p3)).dot(np.hstack((b1, b2, b3)))
center /= b1 + b2 + b3
center = center.tolist()
return center, radius
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None, None
@@ -11,32 +11,19 @@ from typing import Any, List, Tuple, Union
#from regex import D
import inspect
from specklepy.objects.GIS.CRS import CRS
from specklepy.objects.GIS.layers import VectorLayer, RasterLayer, Layer
try:
from speckle.speckle.converter.layers.CRS import CRS
from speckle.speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
from speckle.speckle.converter.layers.symbology import vectorRendererToNative, rasterRendererToNative, rendererToSpeckle, cadBimRendererToNative
from speckle.speckle.converter.layers.feature import featureToNative, featureToSpeckle, cadFeatureToNative, bimFeatureToNative, rasterFeatureToSpeckle
from speckle.speckle.plugin_utils.helpers import findOrCreatePath, findFeatColors
from speckle.speckle.converter.layers.symbology import vectorRendererToNative, rasterRendererToNative, rendererToSpeckle, cadBimRendererToNative
from speckle.speckle.converter.layers.feature import featureToNative, featureToSpeckle, cadFeatureToNative, bimFeatureToNative, rasterFeatureToSpeckle
from speckle.speckle.plugin_utils.helpers import findOrCreatePath, findFeatColors
from speckle.speckle.converter.geometry.mesh import constructMeshFromRaster, meshToNative, writeMeshToShp
from speckle.speckle.converter.layers.utils import findTransformation
from speckle.speckle.converter.layers.utils import getLayerAttributes, newLayerGroupAndName, validate_path
from speckle.speckle.plugin_utils.helpers import validateNewFclassName, removeSpecialCharacters
from speckle.speckle.ui.logger import logToUser
from speckle.speckle.converter.geometry.mesh import constructMeshFromRaster, meshToNative, writeMeshToShp
from speckle.speckle.converter.layers.utils import findTransformation
from speckle.speckle.converter.layers.utils import getLayerAttributes, newLayerGroupAndName, validate_path
from speckle.speckle.plugin_utils.helpers import validateNewFclassName, removeSpecialCharacters
from speckle.speckle.utils.panel_logging import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.CRS import CRS
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.symbology import vectorRendererToNative, rasterRendererToNative, rendererToSpeckle, cadBimRendererToNative
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.feature import featureToNative, featureToSpeckle, cadFeatureToNative, bimFeatureToNative, rasterFeatureToSpeckle
from speckle_toolbox.esri.toolboxes.speckle.speckle.plugin_utils.helpers import findOrCreatePath, findFeatColors
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.mesh import constructMeshFromRaster, meshToNative, writeMeshToShp
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.utils import findTransformation
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.utils import getLayerAttributes, newLayerGroupAndName, validate_path
from speckle_toolbox.esri.toolboxes.speckle.speckle.plugin_utils.helpers import validateNewFclassName, removeSpecialCharacters
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
from specklepy.objects import Base
from specklepy.objects.geometry import Mesh
@@ -84,7 +71,7 @@ def getLayers(plugin, bySelection = False ) -> List[arcLayer]:
# issue with getting selected layers: https://community.esri.com/t5/python-questions/determining-selected-layers-in-the-table-of/td-p/252098
self = plugin.dockwidget
project = plugin.gis_project
project = plugin.project
all_layers = getAllProjLayers(project)
if bySelection is True: # by selection
@@ -167,6 +154,7 @@ def layerToSpeckle(layer: arcLayer, project: ArcGISProject) -> Union[VectorLayer
try:
projectCRS = project.activeMap.spatialReference
logToUser(str(projectCRS.xy_units()), level=2, func = inspect.stack()[0][3])
try: data = arcpy.Describe(layer.dataSource)
except OSError as e:
logToUser(str(e.args[0]), level=2, func = inspect.stack()[0][3])
@@ -192,8 +180,6 @@ def layerToSpeckle(layer: arcLayer, project: ArcGISProject) -> Union[VectorLayer
speckleLayer.name = layerName
speckleLayer.crs = speckleReprojectedCrs
speckleLayer.renderer = rendererToSpeckle(project, project.activeMap, layer, None)
#speckleLayer.datum = datum
try: # https://pro.arcgis.com/en/pro-app/2.8/arcpy/get-started/the-spatial-reference-object.htm
@@ -283,7 +269,7 @@ def layerToSpeckle(layer: arcLayer, project: ArcGISProject) -> Union[VectorLayer
def layerToNative(layer: Any, streamBranch: str, plugin=None) -> arcLayer:
print("________________________________________Layer to Native")
try:
project = plugin.gis_project
project = plugin.project
layer_elements = layer.elements
if layer_elements is None or len(layer_elements)==0:
@@ -379,7 +365,7 @@ def bimLayerToNative(layerContentList: List[Base], layerName: str, streamBranch:
layerName = removeSpecialCharacters(layerName)
project = plugin.gis_project
project = plugin.project
geom_meshes = []
layer_meshes = None
#filter speckle objects by type within each layer, create sub-layer for each type (points, lines, polygons, mesh?)
@@ -391,7 +377,6 @@ def bimLayerToNative(layerContentList: List[Base], layerName: str, streamBranch:
for p in geom_old.get_dynamic_member_names():
if p not in fields_to_ignore:
geom[p] = geom_old[p]
except: geom = geom_old
if isinstance(geom, List):
@@ -682,7 +667,7 @@ def cadLayerToNative(layerContentList: List[Base], layerName: str, streamBranch:
try:
geom_points = []
geom_polylines = []
project = plugin.gis_project
project = plugin.project
print(layerName)
geom_polygones = []
geom_meshes = []
@@ -14,22 +14,22 @@ from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
import inspect
try:
from speckle.speckle.converter.geometry import convertToSpeckle, convertToNative, convertToNativeMulti
from speckle.speckle.converter.geometry.conversions import convertToSpeckle, convertToNative, convertToNativeMulti
from speckle.speckle.converter.layers.utils import (findTransformation, getVariantFromValue, traverseDict,
traverseDictByKey, hsv_to_rgb)
from speckle.speckle.converter.geometry.point import pointToSpeckle
from speckle.speckle.converter.geometry.mesh import constructMeshFromRaster, meshToNative
from speckle.speckle.converter.layers.symbology import jsonFromLayerStyle
from speckle.speckle.ui.logger import logToUser
from speckle.speckle.utils.panel_logging import logToUser
from speckle.speckle.plugin_utils.helpers import findOrCreatePath
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry import convertToSpeckle, convertToNative, convertToNativeMulti
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.conversions import convertToSpeckle, convertToNative, convertToNativeMulti
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.utils import (findTransformation, getVariantFromValue, traverseDict,
traverseDictByKey, hsv_to_rgb)
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.point import pointToSpeckle
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.geometry.mesh import constructMeshFromRaster, meshToNative
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.symbology import jsonFromLayerStyle
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
from speckle_toolbox.esri.toolboxes.speckle.speckle.utils.panel_logging import logToUser
from speckle_toolbox.esri.toolboxes.speckle.speckle.plugin_utils.helpers import findOrCreatePath
import numpy as np
@@ -16,14 +16,9 @@ from arcpy.management import (CreateFeatureclass, MakeFeatureLayer,
from specklepy.objects import Base
from specklepy.objects.other import RenderMaterial
try:
from speckle.speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
from speckle.speckle.ui.logger import logToUser
from speckle.speckle.plugin_utils.helpers import findOrCreatePath
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
from speckle_toolbox.esri.toolboxes.speckle.speckle.plugin_utils.helpers import findOrCreatePath
from specklepy.objects.GIS.layers import Layer, VectorLayer, RasterLayer
from speckle.speckle.utils.panel_logging import logToUser
from speckle.speckle.plugin_utils.helpers import findOrCreatePath
def jsonFromLayerStyle(layerArcgis, path_style):
# write updated renderer to file and get layerStyle variable
@@ -1,64 +1,165 @@
from datetime import datetime
from typing import Dict, Any, List, Union
import json
import hashlib
from specklepy.objects import Base
from specklepy.objects.geometry import Mesh
from specklepy.objects.other import Collection
import arcpy
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
import os
import inspect
from PyQt5.QtGui import QColor
try:
from speckle.speckle.converter.layers.emptyLayerTemplates import createGroupLayer
from speckle.speckle.plugin_utils.helpers import findOrCreatePath
from speckle.speckle.ui.logger import logToUser
from speckle.speckle.plugin_utils.helpers import validateNewFclassName
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.emptyLayerTemplates import createGroupLayer
from speckle_toolbox.esri.toolboxes.speckle.speckle.plugin_utils.helpers import findOrCreatePath
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
from speckle_toolbox.esri.toolboxes.speckle.speckle.plugin_utils.helpers import validateNewFclassName
#ATTRS_REMOVE = ['geometry','applicationId','bbox','displayStyle', 'id', 'renderMaterial', 'displayMesh', 'displayValue']
ATTRS_REMOVE = ['speckleTyp','speckle_id','geometry','applicationId','bbox','displayStyle', 'id', 'renderMaterial', 'displayMesh', 'displayValue']
from speckle.speckle.converter.layers.emptyLayerTemplates import createGroupLayer
from speckle.speckle.plugin_utils.helpers import findOrCreatePath, SYMBOL
from speckle.speckle.utils.panel_logging import logToUser
from speckle.speckle.plugin_utils.helpers import validateNewFclassName
def findAndClearLayerGroup(gis_project: ArcGISProject, newGroupName: str = ""):
# ATTRS_REMOVE = ['geometry','applicationId','bbox','displayStyle', 'id', 'renderMaterial', 'displayMesh', 'displayValue']
ATTRS_REMOVE = [
"speckleTyp",
"speckle_id",
"geometry",
"applicationId",
"bbox",
"displayStyle",
"id",
"renderMaterial",
"displayMesh",
"displayValue",
]
def generate_qgis_app_id(
base: Base,
layer,
f,
):
"""Generate unique ID for Vector feature."""
return ""
try:
fieldnames = [str(field.name()) for field in layer.fields()]
props = [str(f[prop]) for prop in fieldnames]
try:
geoms = f.geometry()
except Exception as e:
geoms = ""
id_data: str = (
layer.id()
+ str(layer.wkbType())
+ str(fieldnames)
+ str(props)
+ str(geoms)
)
return hashlib.md5(id_data.encode("utf-8")).hexdigest()
except Exception as e:
logToUser(
f"Application ID not generated for feature in layer {layer.name()}: {e}",
level=1,
)
return ""
def generate_qgis_raster_app_id(rasterLayer):
"""Generate unique ID for Raster layer."""
return ""
try:
id_data = str(get_raster_stats(rasterLayer))
file_ds = gdal.Open(rasterLayer.source(), gdal.GA_ReadOnly)
for i in range(rasterLayer.bandCount()):
band = file_ds.GetRasterBand(i + 1)
id_data += str(band.ReadAsArray())
return hashlib.md5(id_data.encode("utf-8")).hexdigest()
except Exception as e:
logToUser(
f"Application ID not generated for layer {rasterLayer.name()}: {e}",
level=1,
)
return ""
def collectionsFromJson(
jsonObj: dict, levels: list, layerConverted, baseCollection: Collection
):
if jsonObj == {} or len(levels) == 0:
# print("RETURN")
baseCollection.elements.append(layerConverted)
return baseCollection
lastLevel = baseCollection
for i, l in enumerate(levels):
sub_collection_found = 0
for item in lastLevel.elements:
# print("___ITEM")
# print(l)
if item.name == l:
# print("___ITEM FOUND")
# print(l)
lastLevel = item
sub_collection_found = 1
break
if sub_collection_found == 0:
# print("___ SUB COLLECTION NOT FOUND")
subCollection = Collection(
units="m", collectionType="QGIS Layer Group", name=l, elements=[]
)
lastLevel.elements.append(subCollection)
lastLevel = lastLevel.elements[
len(lastLevel.elements) - 1
] # reassign last element
if i == len(levels) - 1: # if last level
lastLevel.elements.append(layerConverted)
return baseCollection
def findAndClearLayerGroup(project: ArcGISProject, newGroupName: str = ""):
print("find And Clear LayerGroup")
try:
groupExists = 0
print(newGroupName)
for l in gis_project.activeMap.listLayers():
#print(l.longName)
for l in project.activeMap.listLayers():
# print(l.longName)
if l.longName.startswith(newGroupName + "\\"):
#print(l.longName)
# print(l.longName)
if l.isFeatureLayer:
# condition for feature layers:
fields = [f.name for f in arcpy.ListFields(l.dataSource)]
fields = [f.name for f in arcpy.ListFields(l.dataSource)]
print(fields)
if "Speckle_ID" in fields or "speckle_id" in fields:
gis_project.activeMap.removeLayer(l)
groupExists+=1
project.activeMap.removeLayer(l)
groupExists += 1
elif l.isRasterLayer:
# condition for raster layers:
if "_Speckle" in l.name:
gis_project.activeMap.removeLayer(l)
groupExists+=1
project.activeMap.removeLayer(l)
groupExists += 1
elif l.longName == newGroupName:
groupExists+=1
groupExists += 1
print(newGroupName)
if groupExists == 0:
# create empty group layer file "\\Layers_Speckle\\
path: str = os.path.expandvars(r'%LOCALAPPDATA%') + "\\Temp\\Speckle_ArcGIS_temp\\" + datetime.now().strftime("%Y-%m-%d %H-%M")
path: str = (
os.path.expandvars(r"%LOCALAPPDATA%")
+ "\\Temp\\Speckle_ArcGIS_temp\\"
+ datetime.now().strftime("%Y-%m-%d %H-%M")
)
path += "\\Layers_Speckle\\"
findOrCreatePath(path)
#path = "\\".join(gis_project.filePath.split("\\")[:-1]) + "\\Layers_Speckle\\"
#findOrCreatePath(path)
# path = "\\".join(project.filePath.split("\\")[:-1]) + "\\Layers_Speckle\\"
# findOrCreatePath(path)
lyr_path = path + newGroupName + ".lyrx"
print(lyr_path)
try:
@@ -67,207 +168,375 @@ def findAndClearLayerGroup(gis_project: ArcGISProject, newGroupName: str = ""):
f.write(content)
f.close()
newGroupLayer = arcpy.mp.LayerFile(lyr_path)
layerGroup = gis_project.activeMap.addLayer(newGroupLayer)[0]
layerGroup = project.activeMap.addLayer(newGroupLayer)[0]
print(layerGroup)
except: # for 3.0.0
if gis_project.active_map is not None:
except: # for 3.0.0
if project.active_map is not None:
print("try creating the group")
layerGroup = gis_project.activeMap.createGroupLayer(newGroupName)
layerGroup = project.activeMap.createGroupLayer(newGroupName)
print(layerGroup)
else:
logToUser("The map didn't fully load, try selecting the project Map or/and refreshing the plugin.", level=1, func = inspect.stack()[0][3])
logToUser(
"The map didn't fully load, try selecting the project Map or/and refreshing the plugin.",
level=1,
func=inspect.stack()[0][3],
)
return
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
def getVariantFromValue(value: Any) -> Union[str, None]:
#print("_________get variant from value_______")
# print("_________get variant from value_______")
# TODO add Base object
res = None
try:
pairs = [
(str, "TEXT"), # 10
(float, "FLOAT"),
(int, "LONG"),
(bool, "SHORT")
]
pairs = [(str, "TEXT"), (float, "FLOAT"), (int, "LONG"), (bool, "SHORT")] # 10
for p in pairs:
if isinstance(value, p[0]):
res = p[1]
try:
if res == "LONG" and (value>= 2147483647 or value<= -2147483647):
#https://pro.arcgis.com/en/pro-app/latest/help/data/geodatabases/overview/arcgis-field-data-types.htm
if res == "LONG" and (value >= 2147483647 or value <= -2147483647):
# https://pro.arcgis.com/en/pro-app/latest/help/data/geodatabases/overview/arcgis-field-data-types.htm
res = "FLOAT"
except Exception as e: print(e)
except Exception as e:
print(e)
break
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return res
def getLayerAttributes(featuresList: List[Base], attrsToRemove: List[str] = ATTRS_REMOVE ) -> Dict[str, str]:
def colorFromSpeckle(rgb):
try:
color = QColor.fromRgb(245, 245, 245)
if isinstance(rgb, int):
r = (rgb & 0xFF0000) >> 16
g = (rgb & 0xFF00) >> 8
b = rgb & 0xFF
color = QColor.fromRgb(r, g, b)
return color
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return QColor.fromRgb(245, 245, 245)
def getDisplayValueList(geom: Any) -> List:
try:
# print("___getDisplayValueList")
val = []
# get list of display values for Meshes
if isinstance(geom, Mesh):
val = [geom]
elif isinstance(geom, List) and len(geom) > 0:
if isinstance(geom[0], Mesh):
val = geom
else:
print("not an individual geometry")
else:
try:
val = geom.displayValue # list
except Exception as e:
print(e)
try:
val = geom["@displayValue"] # list
except Exception as e:
print(e)
try:
val = geom.displayMesh
except:
pass
return val
except Exception as e:
print(e)
return []
def getLayerGeomType(layer) -> str:
return
def tryCreateGroupTree(root, fullGroupName, plugin=None):
return
# CREATE A GROUP "received blabla" with sublayers
# print("_________CREATE GROUP TREE: " + fullGroupName)
# receive_layer_tree: dict = plugin.receive_layer_tree
receive_layer_list = fullGroupName.split(SYMBOL)
path_list = []
for x in receive_layer_list:
if len(x) > 0:
path_list.append(x)
group_to_create_name = path_list[0]
layerGroup = QgsLayerTreeGroup(group_to_create_name)
if root.findGroup(group_to_create_name) is not None:
layerGroup = root.findGroup(group_to_create_name) # -> QgsLayerTreeNode
else:
layerGroup = root.insertGroup(
0, group_to_create_name
) # root.addChildNode(layerGroup)
layerGroup.setExpanded(True)
layerGroup.setItemVisibilityChecked(True)
path_list.pop(0)
if len(path_list) > 0:
layerGroup = tryCreateGroupTree(layerGroup, SYMBOL.join(path_list), plugin)
return layerGroup
def validateAttributeName(name: str, fieldnames: List[str]) -> str:
try:
new_list = [x for x in fieldnames if x != name]
corrected = name.replace("/", "_").replace(".", "_")
if corrected == "id":
corrected = "applicationId"
for i, x in enumerate(corrected):
if corrected[0] != "_" and corrected not in new_list:
break
else:
corrected = corrected[1:]
if len(corrected) <= 1 and len(name) > 1:
corrected = "0" + name # if the loop removed the property name completely
return corrected
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def trySaveCRS(crs, streamBranch: str = ""):
return
try:
authid = crs.authid()
wkt = crs.toWkt()
if authid == "":
crs_id = crs.saveAsUserCrs("SpeckleCRS_" + streamBranch)
return crs_id
else:
return crs.srsid()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def getLayerAttributes(
featuresList: List[Base], attrsToRemove: List[str] = ATTRS_REMOVE
) -> Dict[str, str]:
print("03________ get layer attributes")
fields = {}
try:
if not isinstance(featuresList, list): features = [featuresList]
else: features = featuresList[:]
if not isinstance(featuresList, list):
features = [featuresList]
else:
features = featuresList[:]
all_props = []
for feature in features:
#get object properties to add as attributes
# get object properties to add as attributes
dynamicProps = feature.get_dynamic_member_names()
for att in ATTRS_REMOVE:
try: dynamicProps.remove(att)
except: pass
try:
dynamicProps.remove(att)
except:
pass
dynamicProps.sort()
# add field names and variands
for name in dynamicProps:
#if name not in all_props: all_props.append(name)
# if name not in all_props: all_props.append(name)
value = feature[name]
variant = getVariantFromValue(value)
#if name == 'area': print(value); print(variant)
if not variant: variant = None #LongLong #4
# if name == 'area': print(value); print(variant)
if not variant:
variant = None # LongLong #4
# go thought the dictionary object
if value and isinstance(value, list):
#all_props.remove(name) # remove generic dict name
# all_props.remove(name) # remove generic dict name
for i, val_item in enumerate(value):
newF, newVals = traverseDict( {}, {}, name+"_"+str(i), val_item)
newF, newVals = traverseDict(
{}, {}, name + "_" + str(i), val_item
)
for i, (k,v) in enumerate(newF.items()):
if k not in all_props: all_props.append(k)
if k not in fields.keys(): fields.update({k: v})
else: #check if the field was empty previously:
for i, (k, v) in enumerate(newF.items()):
if k not in all_props:
all_props.append(k)
if k not in fields.keys():
fields.update({k: v})
else: # check if the field was empty previously:
oldVariant = fields[k]
# replace if new one is NOT Float (too large integers)
#if oldVariant != "FLOAT" and v == "FLOAT":
# if oldVariant != "FLOAT" and v == "FLOAT":
# fields.update({k: v})
# replace if new one is NOT LongLong or IS String
if oldVariant != "TEXT" and v == "TEXT":
fields.update({k: v})
# add a field if not existing yet
else: # if str, Base, etc
newF, newVals = traverseDict( {}, {}, name, value)
else: # if str, Base, etc
newF, newVals = traverseDict({}, {}, name, value)
for i, (k,v) in enumerate(newF.items()):
if k not in all_props: all_props.append(k)
if k not in fields.keys(): fields.update({k: v}) #if variant is known
else: #check if the field was empty previously:
for i, (k, v) in enumerate(newF.items()):
if k not in all_props:
all_props.append(k)
if k not in fields.keys():
fields.update({k: v}) # if variant is known
else: # check if the field was empty previously:
oldVariant = fields[k]
# replace if new one is NOT Float (too large integers)
#print(oldVariant, v)
#if oldVariant == "LONG" and v == "FLOAT":
# print(oldVariant, v)
# if oldVariant == "LONG" and v == "FLOAT":
# fields.update({k: v})
# replace if new one is NOT LongLong or IS String
if oldVariant != "TEXT" and v == "TEXT":
fields.update({k: v})
#print(fields)
# print(fields)
# replace all empty ones wit String
all_props.append("Speckle_ID")
for name in all_props:
if name not in fields.keys():
fields.update({name: 'TEXT'})
fields.update({name: "TEXT"})
print(fields)
#fields_sorted = {k: v for k, v in sorted(fields.items(), key=lambda item: item[0])}
# fields_sorted = {k: v for k, v in sorted(fields.items(), key=lambda item: item[0])}
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return fields
def traverseDict(newF: dict, newVals: dict, nam: str, val: Any):
try:
if isinstance(val, dict):
for i, (k,v) in enumerate(val.items()):
newF, newVals = traverseDict( newF, newVals, nam+"_"+k, v)
for i, (k, v) in enumerate(val.items()):
newF, newVals = traverseDict(newF, newVals, nam + "_" + k, v)
elif isinstance(val, Base):
dynamicProps = val.get_dynamic_member_names()
for att in ATTRS_REMOVE:
try: dynamicProps.remove(att)
except: pass
try:
dynamicProps.remove(att)
except:
pass
dynamicProps.sort()
item_dict = {}
for prop in dynamicProps:
item_dict.update({prop: val[prop]})
for i, (k,v) in enumerate(item_dict.items()):
newF, newVals = traverseDict( newF, newVals, nam+"_"+k, v)
for i, (k, v) in enumerate(item_dict.items()):
newF, newVals = traverseDict(newF, newVals, nam + "_" + k, v)
else:
var = getVariantFromValue(val)
if var is None:
var = 'TEXT'
var = "TEXT"
val = str(val)
#print(var)
# print(var)
newF.update({nam: var})
newVals.update({nam: val})
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return newF, newVals
def get_scale_factor(units: str) -> float:
unit_scale = {
"meters": 1.0,
"centimeters": 0.01,
"millimeters": 0.001,
"inches": 0.0254,
"feet": 0.3048,
"kilometers": 1000.0,
"mm": 0.001,
"cm": 0.01,
"m": 1.0,
"km": 1000.0,
"in": 0.0254,
"ft": 0.3048,
"yd": 0.9144,
"mi": 1609.340,
"meters": 1.0,
"centimeters": 0.01,
"millimeters": 0.001,
"inches": 0.0254,
"feet": 0.3048,
"kilometers": 1000.0,
"mm": 0.001,
"cm": 0.01,
"m": 1.0,
"km": 1000.0,
"in": 0.0254,
"ft": 0.3048,
"yd": 0.9144,
"mi": 1609.340,
}
if units is not None and units.lower() in unit_scale.keys():
return unit_scale[units]
logToUser(f"Units {units} are not supported. Meters will be applied by default.", level=0, func = inspect.stack()[0][3])
logToUser(
f"Units {units} are not supported. Meters will be applied by default.",
level=0,
func=inspect.stack()[0][3],
)
return 1.0
def findTransformation(f_shape, geomType, layer_sr: arcpy.SpatialReference, projectCRS: arcpy.SpatialReference, selectedLayer: arcLayer):
#apply transformation if needed
def findTransformation(
f_shape,
geomType,
layer_sr: arcpy.SpatialReference,
projectCRS: arcpy.SpatialReference,
selectedLayer: arcLayer,
):
# apply transformation if needed
try:
if layer_sr.name != projectCRS.name:
tr0 = tr1 = tr2 = tr_custom = None
midSr = arcpy.SpatialReference("WGS 1984") # GCS_WGS_1984
#print(layer_sr)
midSr = arcpy.SpatialReference("WGS 1984") # GCS_WGS_1984
# print(layer_sr)
try:
transformations = arcpy.ListTransformations(layer_sr, projectCRS)
#print(transformations)
customTransformName = "layer_sr.name"+"_To_"+ projectCRS.name
# print(transformations)
customTransformName = "layer_sr.name" + "_To_" + projectCRS.name
if len(transformations) == 0:
try:
tr1 = arcpy.ListTransformations(layer_sr, midSr)[0]
tr2 = arcpy.ListTransformations(midSr, projectCRS)[0]
except:
#customGeoTransfm = "GEOGTRAN[METHOD['Geocentric_Translation'],PARAMETER['X_Axis_Translation',''],PARAMETER['Y_Axis_Translation',''],PARAMETER['Z_Axis_Translation','']]"
#CreateCustomGeoTransformation(customTransformName, layer_sr, projectCRS)
# customGeoTransfm = "GEOGTRAN[METHOD['Geocentric_Translation'],PARAMETER['X_Axis_Translation',''],PARAMETER['Y_Axis_Translation',''],PARAMETER['Z_Axis_Translation','']]"
# CreateCustomGeoTransformation(customTransformName, layer_sr, projectCRS)
tr_custom = customTransformName
else:
#print("else")
# print("else")
# choose equation based instead of file-based/grid-based method,
# to be consistent with QGIS: https://desktop.arcgis.com/en/arcmap/latest/map/projections/choosing-an-appropriate-transformation.htm
selecterTr = {}
for tr in transformations:
if "NTv2" not in tr and "NADCON" not in tr:
set1 = set( layer_sr.name.split("_") + projectCRS.name.split("_") )
set2 = set( tr.split("_") )
diff = len( set(set1).symmetric_difference(set2) )
set1 = set(
layer_sr.name.split("_") + projectCRS.name.split("_")
)
set2 = set(tr.split("_"))
diff = len(set(set1).symmetric_difference(set2))
selecterTr.update({tr: diff})
selecterTr = dict(sorted(selecterTr.items(), key=lambda item: item[1]))
selecterTr = dict(
sorted(selecterTr.items(), key=lambda item: item[1])
)
tr0 = list(selecterTr.keys())[0]
if geomType != "Point" and geomType != "Polyline" and geomType != "Polygon" and geomType != "Multipoint" and geomType != "MultiPatch":
try: logToUser("Unsupported or invalid geometry in layer " + selectedLayer.name, level=2, func = inspect.stack()[0][3])
except: logToUser("Unsupported or invalid geometry", level=2, func = inspect.stack()[0][3])
if (
geomType != "Point"
and geomType != "Polyline"
and geomType != "Polygon"
and geomType != "Multipoint"
and geomType != "MultiPatch"
):
try:
logToUser(
"Unsupported or invalid geometry in layer "
+ selectedLayer.name,
level=2,
func=inspect.stack()[0][3],
)
except:
logToUser(
"Unsupported or invalid geometry",
level=2,
func=inspect.stack()[0][3],
)
# reproject geometry using chosen transformstion(s)
if tr0 is not None:
@@ -282,56 +551,85 @@ def findTransformation(f_shape, geomType, layer_sr: arcpy.SpatialReference, proj
f_shape = ptgeo1
except:
logToUser(f"Spatial Transformation not found for layer {selectedLayer.name}", level=2, func = inspect.stack()[0][3])
logToUser(
f"Spatial Transformation not found for layer {selectedLayer.name}",
level=2,
func=inspect.stack()[0][3],
)
return None
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return f_shape
def traverseDictByKey(d: Dict, key:str ="", result = None) -> Dict:
def traverseDictByKey(d: Dict, key: str = "", result=None) -> Dict:
print("__traverse")
try:
result = None
#print(d)
# print(d)
for k, v in d.items():
try: v = json.loads(v)
except: pass
try:
v = json.loads(v)
except:
pass
if isinstance(v, dict):
#print("__dict__")
if k == key: print("__break loop"); result = v; return result
# print("__dict__")
if k == key:
print("__break loop")
result = v
return result
else:
result = traverseDictByKey(v, key, result)
if result is not None: return result
if result is not None:
return result
if isinstance(v, list):
for item in v:
#print(item)
# print(item)
if isinstance(item, dict):
result = traverseDictByKey(item, key, result)
if result is not None: return result
if result is not None:
return result
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
#print("__result is: ____________")
#return result
# print("__result is: ____________")
# return result
def hsv_to_rgb(listHSV):
try:
h, s, v = listHSV[0], listHSV[1], listHSV[2]
if s == 0.0: v*=255; return (v, v, v)
i = int(h*6.) # XXX assume int() truncates!
f = (h*6.)-i; p,q,t = int(255*(v*(1.-s))), int(255*(v*(1.-s*f))), int(255*(v*(1.-s*(1.-f)))); v*=255; i%=6
if i == 0: return (v, t, p)
if i == 1: return (q, v, p)
if i == 2: return (p, v, t)
if i == 3: return (p, q, v)
if i == 4: return (t, p, v)
if i == 5: return (v, p, q)
if s == 0.0:
v *= 255
return (v, v, v)
i = int(h * 6.0) # XXX assume int() truncates!
f = (h * 6.0) - i
p, q, t = (
int(255 * (v * (1.0 - s))),
int(255 * (v * (1.0 - s * f))),
int(255 * (v * (1.0 - s * (1.0 - f)))),
)
v *= 255
i %= 6
if i == 0:
return (v, t, p)
if i == 1:
return (q, v, p)
if i == 2:
return (p, v, t)
if i == 3:
return (p, q, v)
if i == 4:
return (t, p, v)
if i == 5:
return (v, p, q)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return (0,0,0)
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return (0, 0, 0)
def cmyk_to_rgb(c, m, y, k, cmyk_scale, rgb_scale=255):
try:
@@ -340,28 +638,35 @@ def cmyk_to_rgb(c, m, y, k, cmyk_scale, rgb_scale=255):
b = rgb_scale * (1.0 - y / float(cmyk_scale)) * (1.0 - k / float(cmyk_scale))
return r, g, b
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return 0,0,0
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return 0, 0, 0
def newLayerGroupAndName(layerName: str, streamBranch: str, project: ArcGISProject) -> str:
def newLayerGroupAndName(
layerName: str, streamBranch: str, project: ArcGISProject
) -> str:
print("___new Layer Group and Name")
layerGroup = None
newGroupName = f'{streamBranch}'
newGroupName = f"{streamBranch}"
try:
#CREATE A GROUP "received blabla" with sublayers
# CREATE A GROUP "received blabla" with sublayers
print(newGroupName)
for l in project.activeMap.listLayers():
if l.longName == newGroupName: layerGroup = l; break
if l.longName == newGroupName:
layerGroup = l
break
#find a layer with a matching name in the "latest" group
newName = f'{streamBranch.split("_")[len(streamBranch.split("_"))-1]}_{layerName}'
# find a layer with a matching name in the "latest" group
newName = (
f'{streamBranch.split("_")[len(streamBranch.split("_"))-1]}_{layerName}'
)
all_layer_names = []
layerExists = 0
for l in project.activeMap.listLayers():
if l.longName.startswith(newGroupName + "\\"):
all_layer_names.append(l.longName)
#print(all_layer_names)
# print(all_layer_names)
print(newName)
newName = validateNewFclassName(newName, all_layer_names, streamBranch + "\\")
@@ -369,7 +674,7 @@ def newLayerGroupAndName(layerName: str, streamBranch: str, project: ArcGISProje
print(newName)
return newName, layerGroup
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None, None
@@ -379,36 +684,46 @@ def curvedFeatureClassToSegments(layer) -> str:
data = arcpy.Describe(layer.dataSource)
dataPath = data.catalogPath
print(dataPath)
newPath = dataPath+"_backup"
newPath = dataPath + "_backup"
arcpy.management.CopyFeatures(dataPath, newPath) # features copied like this do not preserve curved segments
arcpy.management.CopyFeatures(
dataPath, newPath
) # features copied like this do not preserve curved segments
arcpy.edit.Densify(in_features = newPath, densification_method = "ANGLE", max_angle = 0.01, max_vertex_per_segment = 100) # https://pro.arcgis.com/en/pro-app/latest/tool-reference/editing/densify.htm
arcpy.edit.Densify(
in_features=newPath,
densification_method="ANGLE",
max_angle=0.01,
max_vertex_per_segment=100,
) # https://pro.arcgis.com/en/pro-app/latest/tool-reference/editing/densify.htm
print(newPath)
return newPath
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
def validate_path(path: str):
"""If our path contains a DB name, make sure we have a valid DB name and not a standard file name."""
try:
# https://github.com/EsriOceans/btm/commit/a9c0529485c9b0baa78c1f094372c0f9d83c0aaf
dirname, file_name = os.path.split(path)
#print(dirname)
#print(file_name)
# print(dirname)
# print(file_name)
file_base = os.path.splitext(file_name)[0]
if dirname == '':
if dirname == "":
# a relative path only, relying on the workspace
dirname = arcpy.env.workspace
path_ext = os.path.splitext(dirname)[1].lower()
if path_ext in ['.mdb', '.gdb', '.sde']:
if path_ext in [".mdb", ".gdb", ".sde"]:
# we're working in a database
file_name = arcpy.ValidateTableName(file_base) # e.g. add a letter in front of the name
file_name = arcpy.ValidateTableName(
file_base
) # e.g. add a letter in front of the name
validated_path = os.path.join(dirname, file_name)
#msg("validated path: %s; (from %s)" % (validated_path, path))
# msg("validated path: %s; (from %s)" % (validated_path, path))
return validated_path
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
@@ -2,34 +2,124 @@ import os
from typing import List
import inspect
SYMBOL = "_x_x_"
UNSUPPORTED_PROVIDERS = ["WFS", "wms", "wcs", "vectortile"]
def get_scale_factor(units: str, dataStorage) -> float:
scale_to_meter = get_scale_factor_to_meter(units)
if dataStorage is not None:
scale_back = scale_to_meter / get_scale_factor_to_meter(
dataStorage.currentUnits
)
return scale_back
else:
return scale_to_meter
def get_scale_factor_to_meter(units: str) -> float:
try:
unit_scale = {
"meters": 1.0,
"centimeters": 0.01,
"millimeters": 0.001,
"inches": 0.0254,
"feet": 0.3048,
"kilometers": 1000.0,
"mm": 0.001,
"cm": 0.01,
"m": 1.0,
"km": 1000.0,
"in": 0.0254,
"ft": 0.3048,
"yd": 0.9144,
"mi": 1609.340,
}
if (
units is not None
and isinstance(units, str)
and units.lower() in unit_scale.keys()
):
return unit_scale[units]
try:
from speckle.speckle.utils.panel_logging import logToUser
logToUser(
f"Units {units} are not supported. Meters will be applied by default.",
level=1,
func=inspect.stack()[0][3],
)
return 1.0
except:
print(
f"Units {units} are not supported. Meters will be applied by default."
)
return 1.0
except Exception as e:
try:
from speckle.speckle.utils.panel_logging import logToUser
logToUser(
f"{e}. Meters will be applied by default.",
level=2,
func=inspect.stack()[0][3],
)
return 1.0
except:
print(f"{e}. Meters will be applied by default.")
return 1.0
def getAppName(name: str) -> str:
new_name = ""
for i, x in enumerate(str(name)):
if x.lower() in [a for k,a in enumerate("abcdefghijklmnopqrstuvwxyz")]:
if x.lower() in [a for k, a in enumerate("abcdefghijklmnopqrstuvwxyz")]:
new_name += x
else: break
else:
break
return new_name
def findOrCreatePath(path: str):
if not os.path.exists(path):
os.makedirs(path)
def removeSpecialCharacters(text: str) -> str:
new_text = text.replace("[","_").replace("]","_").replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
#new_text = text.encode('iso-8859-1', errors='ignore').decode('utf-8')
new_text = (
text.replace("[", "_")
.replace("]", "_")
.replace(" ", "_")
.replace("-", "_")
.replace("(", "_")
.replace(")", "_")
.replace(":", "_")
.replace("\\", "_")
.replace("/", "_")
.replace('"', "_")
.replace("&", "_")
.replace("@", "_")
.replace("$", "_")
.replace("%", "_")
.replace("^", "_")
)
# new_text = text.encode('iso-8859-1', errors='ignore').decode('utf-8')
return new_text
def splitTextIntoLines(text: str = "", number: int= 40) -> str:
def splitTextIntoLines(text: str = "", number: int = 40) -> str:
print("__splitTextIntoLines")
print(text)
msg = ""
try:
if len(text)>number:
if len(text) > number:
try:
for i, x in enumerate(text):
msg += x
if i!=0 and i%number == 0: msg += "\n"
except Exception as e: print(e)
if i != 0 and i % number == 0:
msg += "\n"
except Exception as e:
print(e)
else:
msg = text
except Exception as e:
@@ -38,44 +128,67 @@ def splitTextIntoLines(text: str = "", number: int= 40) -> str:
return msg
def validateNewFclassName(newName: str, all_layer_names: List[str], prefix: str = "") -> str:
def jsonFromList(jsonObj: dict, levels: list):
# print("jsonFromList")
if len(levels) == 0:
return jsonObj
lastLevel = jsonObj
for l in levels:
# print(lastLevel)
try:
lastLevel = lastLevel[l]
except:
lastLevel.update({l: {}})
# print(jsonObj)
return jsonObj
def validateNewFclassName(
newName: str, all_layer_names: List[str], prefix: str = ""
) -> str:
fixed_name = newName
if (prefix + fixed_name) in all_layer_names:
layerNameCreated = 0
for index, letter in enumerate('234567890abcdefghijklmnopqrstuvwxyz'):
for index, letter in enumerate("234567890abcdefghijklmnopqrstuvwxyz"):
if ((prefix + fixed_name) + "_" + letter) not in all_layer_names:
fixed_name += "_"+letter
layerNameCreated +=1
fixed_name += "_" + letter
layerNameCreated += 1
break
if layerNameCreated == 0:
for index, letter in enumerate('234567890abcdefghijklmnopqrstuvwxyz'):
test_fixed_name = validateNewFclassName((fixed_name + "_" + letter), all_layer_names, prefix)
for index, letter in enumerate("234567890abcdefghijklmnopqrstuvwxyz"):
test_fixed_name = validateNewFclassName(
(fixed_name + "_" + letter), all_layer_names, prefix
)
if (prefix + test_fixed_name) not in all_layer_names:
fixed_name = test_fixed_name
layerNameCreated +=1
layerNameCreated += 1
break
#else: layerNameCreated +=1
# else: layerNameCreated +=1
if layerNameCreated == 0:
pass #logToUser('Feature class name already exists', level=2, func = inspect.stack()[0][3])
#return fixed_name
pass # logToUser('Feature class name already exists', level=2, func = inspect.stack()[0][3])
# return fixed_name
return fixed_name
def findFeatColors(fetColors, f):
colorFound = 0
try: # get render material from any part of the mesh (list of items in displayValue)
try: # get render material from any part of the mesh (list of items in displayValue)
for k, item in enumerate(f.displayValue):
try:
fetColors.append(item.renderMaterial.diffuse)
colorFound += 1
break
except: pass
if colorFound == 0: fetColors.append(f.renderMaterial.diffuse)
except:
pass
if colorFound == 0:
fetColors.append(f.renderMaterial.diffuse)
except:
try:
for k, item in enumerate(f["@displayValue"]):
@@ -83,17 +196,21 @@ def findFeatColors(fetColors, f):
fetColors.append(item.renderMaterial.diffuse)
colorFound += 1
break
except: pass
if colorFound == 0: fetColors.append(f.renderMaterial.diffuse)
except:
pass
if colorFound == 0:
fetColors.append(f.renderMaterial.diffuse)
except:
# the Mesh itself has a renderer
try: # get render material from any part of the mesh (list of items in displayValue)
try: # get render material from any part of the mesh (list of items in displayValue)
fetColors.append(f.renderMaterial.diffuse)
colorFound += 1
except:
try:
fetColors.append(f.displayStyle.color)
colorFound += 1
except: pass
if colorFound == 0: fetColors.append(None)
except:
pass
if colorFound == 0:
fetColors.append(None)
return fetColors
@@ -1,22 +1,25 @@
from typing import Any, Callable, List, Optional
import inspect
from specklepy.objects import Base
from specklepy.objects.GIS.layers import VectorLayer, RasterLayer, Layer
try:
from speckle.speckle.ui.logger import logToUser
from speckle.speckle.converter.layers.Layer import VectorLayer, RasterLayer, Layer
from speckle.speckle.converter.layers import bimLayerToNative, cadLayerToNative, layerToNative
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.Layer import VectorLayer, RasterLayer, Layer
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers import bimLayerToNative, cadLayerToNative, layerToNative
from speckle.speckle.utils.panel_logging import logToUser
from speckle.speckle.converter.layers import (
bimLayerToNative,
cadLayerToNative,
layerToNative,
)
import arcpy
SPECKLE_TYPES_TO_READ = ["Objects.Geometry.", "Objects.BuiltElements.", "IFC"] # will properly traverse and check for displayValue
SPECKLE_TYPES_TO_READ = [
"Objects.Geometry.",
"Objects.BuiltElements.",
"IFC",
] # will properly traverse and check for displayValue
def traverseObject(
base: Base,
@@ -26,119 +29,175 @@ def traverseObject(
plugin=None,
):
try:
#print("traverse Object")
#print(base)
# print("traverse Object")
# print(base)
if check and check(base):
res = callback(base, streamBranch, plugin) if callback else False
#print(res)
# print(res)
if res:
return
memberNames = base.get_member_names()
#print(base)
#print(memberNames)
# print(base)
# print(memberNames)
for name in memberNames:
try:
if ["id", "applicationId", "units", "speckle_type"].index(name):
continue
except:
pass
#print(name)
# print(name)
traverseValue(base[name], callback, check, streamBranch, plugin)
logToUser("Data received", level=0)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
def traverseValue(
value: Any,
callback: Optional[Callable[[Base, str, Any], bool]],
check: Optional[Callable[[Base], bool]],
streamBranch: str,
plugin = None,
plugin=None,
):
try:
#print("traverse Value")
#print(value)
# print("traverse Value")
# print(value)
if isinstance(value, Base):
traverseObject(value, callback, check, streamBranch, plugin)
if isinstance(value, List):
for item in value:
traverseValue(item, callback, check, streamBranch, plugin)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
def callback(base: Base, streamBranch: str, plugin=None) -> bool:
try:
#print("callback")
if base.speckle_type.endswith("VectorLayer") or base.speckle_type.endswith("RasterLayer"):
# print("callback")
if base.speckle_type.endswith("VectorLayer") or base.speckle_type.endswith(
"RasterLayer"
):
if isinstance(base, Layer):
logToUser(f"Speckle class \"Layer\" will be deprecated in future updates in favour of \"VectorLayer\" or \"RasterLayer\"", level=0, func = inspect.stack()[0][3])
logToUser(
f'Speckle class "Layer" will be deprecated in future updates in favour of "VectorLayer" or "RasterLayer"',
level=0,
func=inspect.stack()[0][3],
)
layerToNative(base, streamBranch, plugin)
#print(layer)
#if layer is not None:
# print(layer)
# if layer is not None:
# logToUser("Layer created: " + layer.name(), level=0)
else:
loopObj(base, "", streamBranch, plugin)
return True
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return False
def loopObj(base: Base, baseName: str, streamBranch: str, plugin=None):
try:
memberNames = base.get_member_names()
for name in memberNames:
if name in ["id", "applicationId", "units", "speckle_type"]: continue
if name in ["id", "applicationId", "units", "speckle_type"]:
continue
# skip if traversal goes to displayValue of an object, that will be readable anyway:
if not isinstance(base, Base): logToUser("NOT BASE: "+type(base), level=1, func = inspect.stack()[0][3]); continue
if (name == "displayValue" or name == "@displayValue") and base.speckle_type.startswith(tuple(SPECKLE_TYPES_TO_READ)): continue
if not isinstance(base, Base):
logToUser(
"NOT BASE: " + type(base), level=1, func=inspect.stack()[0][3]
)
continue
if (
name == "displayValue" or name == "@displayValue"
) and base.speckle_type.startswith(tuple(SPECKLE_TYPES_TO_READ)):
continue
try: loopVal(base[name], baseName + "/" + name, streamBranch, plugin)
except: pass
try:
loopVal(base[name], baseName + "/" + name, streamBranch, plugin)
except:
pass
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
def loopVal(value: Any, name: str, streamBranch: str, plugin=None): # "name" is the parent object/property/layer name
def loopVal(
value: Any, name: str, streamBranch: str, plugin=None
): # "name" is the parent object/property/layer name
try:
if isinstance(value, Base):
try: # loop through objects with Speckletype prop, but don't go through parts of Speckle Geometry object
try: # loop through objects with Speckletype prop, but don't go through parts of Speckle Geometry object
if not value.speckle_type.startswith("Objects.Geometry."):
loopObj(value, name, streamBranch, plugin)
except:
loopObj(value, name, streamBranch, plugin)
elif isinstance(value, List):
streamBranch = streamBranch.replace("[","_").replace("]","_").replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
streamBranch = (
streamBranch.replace("[", "_")
.replace("]", "_")
.replace(" ", "_")
.replace("-", "_")
.replace("(", "_")
.replace(")", "_")
.replace(":", "_")
.replace("\\", "_")
.replace("/", "_")
.replace('"', "_")
.replace("&", "_")
.replace("@", "_")
.replace("$", "_")
.replace("%", "_")
.replace("^", "_")
)
objectListConverted = 0
#print("loop val - List")
# print("loop val - List")
for i, item in enumerate(value):
loopVal(item, name, streamBranch, plugin)
if not isinstance(item, Base): continue
if not isinstance(item, Base):
continue
if item.speckle_type and item.speckle_type.startswith("IFC"):
# keep traversing infinitely, just don't run repeated conversion for the same list of objects
try:
if item["displayValue"] is not None and objectListConverted == 0:
if (
item["displayValue"] is not None
and objectListConverted == 0
):
bimLayerToNative(value, name, streamBranch, None, plugin)
objectListConverted += 1
except:
try:
if item["@displayValue"] is not None and objectListConverted == 0:
bimLayerToNative(value, name, streamBranch, None, plugin)
if (
item["@displayValue"] is not None
and objectListConverted == 0
):
bimLayerToNative(
value, name, streamBranch, None, plugin
)
objectListConverted += 1
except: pass
except:
pass
elif item.speckle_type and item.speckle_type.endswith(".ModelCurve"):
if item["baseCurve"] is not None:
cadLayerToNative(value, name, streamBranch, plugin)
break
elif item.speckle_type and (item.speckle_type == "Objects.Geometry.Mesh" or item.speckle_type == "Objects.Geometry.Brep" or item.speckle_type.startswith("Objects.BuiltElements.")):
elif item.speckle_type and (
item.speckle_type == "Objects.Geometry.Mesh"
or item.speckle_type == "Objects.Geometry.Brep"
or item.speckle_type.startswith("Objects.BuiltElements.")
):
bimLayerToNative(value, name, streamBranch, None, plugin)
break
elif item.speckle_type and item.speckle_type != "Objects.Geometry.Mesh" and item.speckle_type != "Objects.Geometry.Brep" and item.speckle_type.startswith("Objects.Geometry."): # or item.speckle_type == 'Objects.BuiltElements.Alignment'):
elif (
item.speckle_type
and item.speckle_type != "Objects.Geometry.Mesh"
and item.speckle_type != "Objects.Geometry.Brep"
and item.speckle_type.startswith("Objects.Geometry.")
): # or item.speckle_type == 'Objects.BuiltElements.Alignment'):
cadLayerToNative(value, name, streamBranch, plugin)
#if pt is not None: arcpy.AddMessage("Layer group created: " + str(pt.name))
#if pl is not None: arcpy.AddMessage("Layer group created: " + str(pl.name))
# if pt is not None: arcpy.AddMessage("Layer group created: " + str(pt.name))
# if pl is not None: arcpy.AddMessage("Layer group created: " + str(pl.name))
break
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
logToUser(str(e), level=2, func=inspect.stack()[0][3])
@@ -0,0 +1,63 @@
import sys
import trace
import threading
class KThread(threading.Thread):
"""A subclass of threading.Thread, with a kill()
method."""
# https://web.archive.org/web/20130503082442/http://mail.python.org/pipermail/python-list/2004-May/281943.html
def __init__(self, *args, **keywords):
threading.Thread.__init__(self, *args, **keywords)
self.killed = False
def start(self):
"""Start the thread."""
self.__run_backup = self.run
self.run = self.__run # Force the Thread to install our trace.
threading.Thread.start(self)
def __run(self):
"""Hacked run function, which installs the trace."""
sys.settrace(self.globaltrace)
self.__run_backup()
self.run = self.__run_backup
def globaltrace(self, frame, why, arg):
if why == 'call':
return self.localtrace
else:
return None
def localtrace(self, frame, why, arg):
if self.killed:
if why == 'line':
raise SystemExit()
return self.localtrace
def kill(self):
self.killed = True
class KillableThread(threading.Thread):
# is NOT running in the background
# https://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread
def __init__(self, sleep_interval=1):
super().__init__()
self._kill = threading.Event()
self._interval = sleep_interval
def run(self):
while True:
print("Do Something")
# If no kill signal is set, sleep for the interval,
# If kill signal comes in while sleeping, immediately
# wake up and handle
is_killed = self._kill.wait(self._interval)
if is_killed:
break
print("Killing Thread")
def kill(self):
self._kill.set()
File diff suppressed because it is too large Load Diff
@@ -1,153 +0,0 @@
import time
from typing import Any, List, Tuple
from PyQt5 import QtCore
from PyQt5.QtCore import QCoreApplication, QSettings, Qt, QTranslator, QRect, QObject
from PyQt5.QtWidgets import QAction, QDockWidget, QVBoxLayout, QWidget, QPushButton
from PyQt5 import QtWidgets
import webbrowser
from specklepy.logging import metrics
from specklepy.api.credentials import Account
import inspect
try:
from speckle.speckle.ui.logger import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
SPECKLE_COLOR = (59,130,246)
SPECKLE_COLOR_LIGHT = (69,140,255)
BACKGR_COLOR = f"background-color: rgb{str(SPECKLE_COLOR)};"
BACKGR_COLOR_LIGHT = f"background-color: rgb{str(SPECKLE_COLOR_LIGHT)};"
BACKGR_COLOR_GREY = f"background-color: Gainsboro;"
class LogWidget(QWidget):
msgs: List[str] = []
used_btns: List[int] = []
btns: List[QPushButton]
max_msg: int
active_account: Account
speckle_version: str
# constructor
def __init__(self, parent=None):
super(LogWidget, self).__init__(parent)
print("start LogWidget")
self.parentWidget = parent
print(self.parentWidget)
self.max_msg = 10
# create a temporary floating button
width = 0 #parent.frameSize().width()
height = 0# parent.frameSize().height()
self.setAttribute(QtCore.Qt.WA_StyledBackground, True)
self.setStyleSheet("background-color: rgba(250,250,250,80);")
self.layout = QVBoxLayout(self)
self.layout.setContentsMargins(10, 60, 10, 40)
self.layout.setAlignment(Qt.AlignBottom)
self.setGeometry(0, 0, width, height)
# generate 100 buttons to use later
self.btns = []
for i in range(self.max_msg):
button = QPushButton(f"👌 Error") # to '{streamName}' Sent , v
button.setStyleSheet("QPushButton {color: black; border: 0px;border-radius: 17px;padding: 20px;height: 40px;text-align: left;"+ f"{BACKGR_COLOR_GREY}" + "}")
button.clicked.connect(lambda: self.openLink())
button.clicked.connect(lambda: self.hide())
self.btns.append(button)
self.hide()
# overriding the mouseReleaseEvent method
def mouseReleaseEvent(self, event):
print("Mouse Release Event")
self.hide()
#self.parentWidget.hideError()
def hide(self):
self.setGeometry(0, 0, 0, 0)
# remove all buttons
for i in reversed(range(self.layout.count())):
self.layout.itemAt(i).widget().setParent(None)
# remove list of used btns
self.used_btns.clear()
self.msgs.clear()
def addButton(self, text: str = "something went wrong", level: int = 2, url = "", blue = False):
print("Add button")
self.setGeometry(0, 0, self.parentWidget.frameSize().width(), self.parentWidget.frameSize().height())
# find index of the first unused button
btn, index = self.getNextBtn()
btn.setAccessibleName(url)
if url != "":
btn.setStyleSheet("QPushButton {color: white;border: 0px;border-radius: 17px;padding: 20px;height: 40px;text-align: left;"+ f"{BACKGR_COLOR}" + "} QPushButton:hover { "+ f"{BACKGR_COLOR_LIGHT}" + " }")
else:
if blue is False:
btn.setStyleSheet("QPushButton {color: black; border: 0px;border-radius: 17px;padding: 20px;height: 40px;text-align: left;"+ f"{BACKGR_COLOR_GREY}" + "}")
else:
btn.setStyleSheet("QPushButton {color: white;border: 0px;border-radius: 17px;padding: 20px;height: 40px;text-align: left;"+ f"{BACKGR_COLOR}" + "}")
btn.setText(text)
self.resizeToText(btn)
#btn.resize(btn.sizeHint())
self.layout.addWidget(btn) #, alignment=Qt.AlignCenter)
self.msgs.append(text)
self.used_btns.append(1)
def openLink(self, url = ""):
try:
btn = self.sender()
url = btn.accessibleName()
if url == "": return
webbrowser.open(url, new=0, autoraise=True)
try:
metrics.track("Connector Action", self.active_account, {"name": "Open In Web", "connector_version": str(self.speckle_version)})
except:
pass
self.hide()
except Exception as e:
pass #logger.logToUser(str(e), level=2, func = inspect.stack()[0][3])
def getNextBtn(self) -> Tuple[QPushButton, int]:
index = len(self.used_btns) # get the next "free" button
if index >= len(self.btns):
# remove first button
self.layout.itemAt(0).widget().setParent(None)
self.used_btns.clear()
index = 0
btn = self.btns[index]
return btn, index
def resizeToText(self, btn):
try:
text = btn.text()
if len(text.split("\n"))>2:
height = len(text.split("\n"))*25
btn.setMinimumHeight(height)
return btn
except Exception as e:
print(e)
return btn
@@ -1,163 +0,0 @@
import os
from typing import List, Union
#import ui.speckle_qgis_dialog
from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtCore import pyqtSignal
from specklepy.api.models import Stream
from specklepy.api.client import SpeckleClient
from specklepy.logging.exceptions import SpeckleException
from specklepy.api.credentials import get_local_accounts #, StreamWrapper
from specklepy.api.wrapper import StreamWrapper
from gql import gql
import inspect
try:
from speckle.speckle.ui.logger import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
import arcpy
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
ui_class = os.path.dirname(os.path.abspath(__file__)) + "/add_stream_modal.ui"
class AddStreamModalDialog(QtWidgets.QWidget):
search_button: QtWidgets.QPushButton = None
search_text_field: QtWidgets.QLineEdit = None
search_results_list: QtWidgets.QListWidget = None
dialog_button_box: QtWidgets.QDialogButtonBox = None
accounts_dropdown: QtWidgets.QComboBox
stream_results: List[Stream] = []
speckle_client: Union[SpeckleClient, None] = None
#Events
handleStreamAdd = pyqtSignal(StreamWrapper)
def __init__(self, parent=None, speckle_client: SpeckleClient = None):
super(AddStreamModalDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint)
uic.loadUi(ui_class, self) # Load the .ui file
self.show()
try:
self.speckle_client = speckle_client
self.setWindowTitle("Add Speckle stream")
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
self.search_button.clicked.connect(self.onSearchClicked)
self.search_results_list.currentItemChanged.connect( self.searchResultChanged )
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.onOkClicked)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.onCancelClicked)
self.accounts_dropdown.currentIndexChanged.connect(self.onAccountSelected)
self.populate_accounts_dropdown()
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def searchResultChanged(self):
try:
index = self.search_results_list.currentIndex().row()
if index == -1: self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
else: self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def onSearchClicked(self):
try:
query = self.search_text_field.text()
sw = None
results = []
if "http" in query and len(query.split("/")) >= 3: # URL
sw = StreamWrapper(query)
stream = sw.get_client().stream.get(sw.stream_id)
if isinstance(stream, Stream): results = [stream]
else: results = []
elif self.speckle_client is not None:
results = self.speckle_client.stream.search(query)
elif self.speckle_client is None:
logToUser(f"Account cannot be authenticated: {self.accounts_dropdown.currentText()}", level=2, func = inspect.stack()[0][3])
self.stream_results = results
self.populateResultsList(sw)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def populateResultsList(self, sw):
try:
self.search_results_list.clear()
if isinstance(self.stream_results, SpeckleException):
logToUser("Some streams cannot be accessed", level=1, func = inspect.stack()[0][3])
return
for stream in self.stream_results:
host = ""
if sw is not None:
host = sw.get_account().serverInfo.url
else:
host = self.speckle_client.account.serverInfo.url
if isinstance(stream, SpeckleException):
logToUser("Some streams cannot be accessed", level=1, func = inspect.stack()[0][3])
else:
self.search_results_list.addItems([
f"{stream.name}, {stream.id} | {host}" #for stream in self.stream_results
])
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def onOkClicked(self):
try:
if isinstance(self.stream_results, SpeckleException):
logToUser("Selected stream cannot be accessed", level=1, func = inspect.stack()[0][3])
return
#elif index == -1 or len(self.stream_results) == 0:
# logger.logToUser("Select stream from \"Search Results\". No stream selected", Qgis.Warning)
# return
else:
try:
index = self.search_results_list.currentIndex().row()
stream = self.stream_results[index]
item = self.search_results_list.item(index)
url = item.text().split(" | ")[1] + "/streams/" + item.text().split(", ")[1].split(" | ")[0]
sw = StreamWrapper(url)
#acc = sw.get_account() #get_local_accounts()[self.accounts_dropdown.currentIndex()]
self.handleStreamAdd.emit(sw) #StreamWrapper(f"{acc.serverInfo.url}/streams/{stream.id}?u={acc.userInfo.id}"))
self.close()
except Exception as e:
logToUser("Some streams cannot be accessed: " + str(e), level=1, func = inspect.stack()[0][3])
return
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def onCancelClicked(self):
self.close()
def onAccountSelected(self, index):
try:
account = self.speckle_accounts[index]
self.speckle_client = SpeckleClient(account.serverInfo.url, account.serverInfo.url.startswith("https"))
self.speckle_client.authenticate_with_token(token=account.token)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def populate_accounts_dropdown(self):
# Populate the accounts comboBox
try:
self.speckle_accounts = get_local_accounts()
self.accounts_dropdown.clear()
self.accounts_dropdown.addItems(
[
f"{acc.userInfo.name}, {acc.userInfo.email} | {acc.serverInfo.url}"
for acc in self.speckle_accounts
]
)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
@@ -1,87 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AddStreamDialog</class>
<widget class="QWidget" name="AddStreamDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item>
<layout class="QFormLayout" name="search_form">
<item row="1" column="0">
<widget class="QLabel" name="search_label">
<property name="text">
<string>Search Stream by name or URL</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="search_text_field"/>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="search_button">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="accounts_dropdown"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="accounts_label">
<property name="text">
<string>Account</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="search_results_label">
<property name="text">
<string>Search Results</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="search_results_list">
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="dialog_button_box">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -1,83 +0,0 @@
import os
from typing import List, Tuple, Union
#import ui.speckle_qgis_dialog
from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtCore import pyqtSignal
from specklepy.api.models import Stream
from specklepy.api.client import SpeckleClient
from specklepy.logging.exceptions import SpeckleException
from specklepy.api.credentials import Account, get_local_accounts #, StreamWrapper
from specklepy.api.wrapper import StreamWrapper
from gql import gql
import inspect
import arcpy
try:
from speckle.speckle.ui.logger import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
ui_class = os.path.dirname(os.path.abspath(__file__)) + "/create_branch.ui"
class CreateBranchModalDialog(QtWidgets.QWidget):
name_field: QtWidgets.QLineEdit = None
description_field: QtWidgets.QLineEdit = None
dialog_button_box: QtWidgets.QDialogButtonBox = None
speckle_client: Union[SpeckleClient, None] = None
#Events
handleBranchCreate = pyqtSignal(str,str)
def __init__(self, parent=None, speckle_client: SpeckleClient = None):
super(CreateBranchModalDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint)
uic.loadUi(ui_class, self) # Load the .ui file
self.show()
try:
self.speckle_client = speckle_client
self.setWindowTitle("Create New Branch")
self.name_field.textChanged.connect(self.nameCheck)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.onOkClicked)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.onCancelClicked)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def nameCheck(self):
try:
if len(self.name_field.text()) >= 3:
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
else:
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
return
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def onOkClicked(self):
try:
name = self.name_field.text()
description = self.description_field.text()
self.handleBranchCreate.emit(name, description)
self.close()
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return
def onCancelClicked(self):
self.close()
def onAccountSelected(self, index):
try:
account = self.speckle_accounts[index]
self.speckle_client = SpeckleClient(account.serverInfo.url, account.serverInfo.url.startswith("https"))
self.speckle_client.authenticate_with_token(token=account.token)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
@@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CreateStreamDialog</class>
<widget class="QWidget" name="AddBranchDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item>
<layout class="QFormLayout" name="search_form">
<item row="0" column="0">
<widget class="QLabel" name="name_label">
<property name="text">
<string>Branch Name</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="name_label">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="name_field"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="description_field"/>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="dialog_button_box">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -1,108 +0,0 @@
import os
from typing import List, Tuple, Union
#import ui.speckle_qgis_dialog
from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtCore import pyqtSignal
import arcpy
from specklepy.api.models import Stream
from specklepy.api.client import SpeckleClient
from specklepy.logging.exceptions import SpeckleException
from specklepy.api.credentials import Account, get_local_accounts #, StreamWrapper
from specklepy.api.wrapper import StreamWrapper
from gql import gql
import inspect
try:
from speckle.speckle.ui.logger import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
ui_class = os.path.dirname(os.path.abspath(__file__)) + "/create_stream.ui"
class CreateStreamModalDialog(QtWidgets.QWidget):
name_field: QtWidgets.QLineEdit = None
description_field: QtWidgets.QLineEdit = None
dialog_button_box: QtWidgets.QDialogButtonBox = None
accounts_dropdown: QtWidgets.QComboBox
public_toggle: QtWidgets.QCheckBox
speckle_client: Union[SpeckleClient, None] = None
#Events
handleStreamCreate = pyqtSignal(Account, str, str, bool)
def __init__(self, parent=None, speckle_client: SpeckleClient = None):
super(CreateStreamModalDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint)
uic.loadUi(ui_class, self) # Load the .ui file
self.show()
try:
self.speckle_client = speckle_client
self.setWindowTitle("Create New Stream")
self.name_field.textChanged.connect(self.nameCheck)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.onOkClicked)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.onCancelClicked)
self.accounts_dropdown.currentIndexChanged.connect(self.onAccountSelected)
self.populate_accounts_dropdown()
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def nameCheck(self):
try:
if len(self.name_field.text()) == 0 or len(self.name_field.text()) >= 3:
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
else:
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
return
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def onOkClicked(self):
try:
acc = get_local_accounts()[self.accounts_dropdown.currentIndex()]
name = self.name_field.text()
description = self.description_field.text()
public = self.public_toggle.isChecked()
self.handleStreamCreate.emit(acc,name,description,public)
self.close()
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return
def onCancelClicked(self):
#self.handleCancelStreamCreate.emit()
self.close()
def onAccountSelected(self, index):
try:
account = self.speckle_accounts[index]
self.speckle_client = SpeckleClient(account.serverInfo.url, account.serverInfo.url.startswith("https"))
self.speckle_client.authenticate_with_token(token=account.token)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def populate_accounts_dropdown(self):
try:
# Populate the accounts comboBox
self.speckle_accounts = get_local_accounts()
self.accounts_dropdown.clear()
self.accounts_dropdown.addItems(
[
f"{acc.userInfo.name}, {acc.userInfo.email} | {acc.serverInfo.url}"
for acc in self.speckle_accounts
]
)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
@@ -1,85 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CreateStreamDialog</class>
<widget class="QWidget" name="AddStreamDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item>
<layout class="QFormLayout" name="search_form">
<item row="0" column="0">
<widget class="QLabel" name="accounts_label">
<property name="text">
<string>Account</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="name_label">
<property name="text">
<string>Stream Name</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="name_label">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="name_label">
<property name="text">
<string>Public</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="accounts_dropdown"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="name_field"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="description_field"/>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="public_toggle"/>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="dialog_button_box">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

@@ -1,55 +0,0 @@
from PyQt5.QtWidgets import QMessageBox
from PyQt5 import QtCore
import arcpy
try:
from speckle.speckle.plugin_utils.helpers import splitTextIntoLines
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.plugin_utils.helpers import splitTextIntoLines
import inspect
def logToUser(msg: str, func=None, level: int = 2, plugin = None, url = "", blue = False):
print("Log to user")
msg = str(msg)
dockwidget = plugin
try:
if url == "" and blue is False: # only for info messages
msg = addLevelSymbol(msg, level)
if func is not None:
msg += "::" + str(func)
writeToLog(msg, level)
if dockwidget is None: return
new_msg = splitTextIntoLines(msg, 70)
dockwidget.msgLog.addButton(new_msg, level=level, url=url, blue=blue)
except Exception as e: print(e); return
def logToUserWithAction(msg: str, level: int = 0, plugin = None, url = ""):
print("Log to user with action")
return
msg = str(msg)
dockwidget = plugin
if dockwidget is None: return
try:
new_msg = splitTextIntoLines(msg, 70)
dockwidget.msgLog.addButton(new_msg, level=level, url=url)
writeToLog(new_msg, level)
except Exception as e: print(e); return
def addLevelSymbol(msg: str, level: int):
if level == 0: msg = "🛈 " + msg
if level == 1: msg = "⚠️ " + msg
if level == 2: msg = "" + msg
return msg
def writeToLog(msg: str = "", level: int = 2):
print(msg)
if level == 0: arcpy.AddMessage(msg)
if level == 1: arcpy.AddWarning(msg)
if level == 2: arcpy.AddError(msg)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

@@ -1,357 +0,0 @@
from typing import Any, List, Optional, Tuple, Union
import arcpy
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
from arcpy.management import CreateTable
import os.path
from specklepy.api.credentials import Account, get_local_accounts
from specklepy.api.client import SpeckleClient
from specklepy.logging.exceptions import (
GraphQLException,
SpeckleException,
)
from specklepy.api.wrapper import StreamWrapper
from specklepy.api.models import Branch, Stream, Streams
from specklepy.logging import metrics
from osgeo import osr
import inspect
try:
from speckle.speckle.ui.validation import tryGetStream
from speckle.speckle.speckle_arcgis import SpeckleGIS
from speckle.speckle.converter.layers import getAllProjLayers
from speckle.speckle.ui.logger import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.validation import tryGetStream
from speckle_toolbox.esri.toolboxes.speckle.speckle.speckle_arcgis import SpeckleGIS
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers import getAllProjLayers
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
FIELDS = ["project_streams","project_layer_selection", "lat_lon"]
def get_project_streams(plugin: SpeckleGIS, content: str = None):
try:
print("GET proj streams")
project = plugin.gis_project
table = findOrCreateSpeckleTable(project)
logToUser(table, level=0, func = inspect.stack()[0][3])
rows = arcpy.da.SearchCursor(table, "project_streams")
saved_streams = []
for x in rows:
logToUser(x, level=0, func = inspect.stack()[0][3])
saved_streams.append(x[0])
temp = []
######### need to check whether saved streams are available (account reachable)
if len(saved_streams) > 0:
for url in saved_streams:
if url=="": continue
try:
sw = StreamWrapper(url)
try:
stream = tryGetStream(sw, plugin.dataStorage)
except SpeckleException as e:
logToUser(e.message, level=2, func = inspect.stack()[0][3])
stream = None
#strId = stream.id # will cause exception if invalid
temp.append((sw, stream))
except SpeckleException as e:
logToUser(e.message, level=2, func = inspect.stack()[0][3])
#except GraphQLException as e:
# logger.logToUser(e.message, Qgis.Warning)
plugin.current_streams = temp
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def set_project_streams(plugin: SpeckleGIS):
try:
print("SET proj streams")
project = plugin.gis_project
table = findOrCreateSpeckleTable(project)
print("SET proj streams 2")
value = [stream[0].stream_url for stream in plugin.current_streams] #",".join()
print(value)
logToUser(value, level=0, func = inspect.stack()[0][3])
if table is not None:
proj_layers = []
lan_lot = ""
with arcpy.da.UpdateCursor(table, FIELDS) as cursor:
for row in cursor: # just one row
if row[1] is not None and row[1] != "": proj_layers.append(row[1])
if row[2] is not None and row[2] != "": lan_lot = row[2]
cursor.deleteRow()
del cursor
if len(proj_layers) == 0: proj_layers.append("")
if len(value) == 0: value.append("")
cursor = arcpy.da.InsertCursor(table, FIELDS )
length = max(len(proj_layers), len(value))
for i in range(length):
if i==0:
cursor.insertRow([value[i], proj_layers[i] , lan_lot])
else:
try:
cursor.insertRow([value[i], proj_layers[i] , ""])
except:
if len(value) <= i: cursor.insertRow(["", proj_layers[i] , ""])
if len(proj_layers) <= i: cursor.insertRow([value[i], "" , ""])
del cursor
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def get_project_layer_selection(plugin: SpeckleGIS):
try:
print("GET project layer selection from the table")
project = plugin.gis_project
table = findOrCreateSpeckleTable(project)
if table is None: return
rows = arcpy.da.SearchCursor(table, "project_layer_selection")
saved_layers = []
for x in rows:
saved_layers.append(x[0])
temp = []
proj_layers = getAllProjLayers(project)
######### need to check whether saved streams are available (account reachable)
if len(saved_layers) > 0:
for layerPath in saved_layers:
if layerPath == "": continue
found = 0
for layer in proj_layers:
print(layer.dataSource)
if layer.dataSource == layerPath:
temp.append((layer.name, layer))
found += 1
break
if found == 0:
logToUser(f'Saved layer not found: "{layerPath}"', level=1, func = inspect.stack()[0][3])
plugin.current_layers = temp
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def set_project_layer_selection(plugin: SpeckleGIS):
try:
print("SET project layer selection function")
project = plugin.gis_project
value: List[str] = [layer[1].dataSource for layer in plugin.current_layers] #",".join([layer[1].dataSource for layer in plugin.current_layers])
print(value)
table = findOrCreateSpeckleTable(project)
#print(table)
if table is not None:
lan_lot = ""
proj_streams = []
with arcpy.da.UpdateCursor(table, FIELDS) as cursor:
for row in cursor: # just one row
if row[0] is not None and row[0] != "": proj_streams.append(row[0])
if row[2] is not None and row[2] != "": lan_lot = row[2]
cursor.deleteRow()
del cursor
if len(proj_streams) == 0: proj_streams.append("")
if len(value) == 0: value.append("")
#print(proj_streams)
cursor = arcpy.da.InsertCursor(table, FIELDS )
length = max(len(proj_streams), len(value))
#print(length)
for i in range(length):
#print(i)
if i==0:
cursor.insertRow([proj_streams[i], value[i] , lan_lot])
print(i)
else:
try:
cursor.insertRow([proj_streams[i], value[i] , ""])
except:
if len(proj_streams) <= i: cursor.insertRow(["", value[i] , ""])
if len(value) <= i: cursor.insertRow([proj_streams[i], "" , ""])
#print(i)
del cursor
try:
metrics.track("Connector Action", plugin.active_account, {"name": "Save Layer Selection", "connector_version": str(plugin.version)})
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=plugin.dockwidget )
#print(table)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
print("SET project layer selection 2")
def get_survey_point(plugin: SpeckleGIS, content = None):
try:
print("get survey point")
project = plugin.gis_project
table = findOrCreateSpeckleTable(project)
if table is None: return
rows = arcpy.da.SearchCursor(table, "lat_lon")
points = ""
for x in rows:
points = x[0]
break
if points != "":
vals: List[str] = points.replace(" ","").split(";")[:2]
plugin.lat, plugin.lon = [float(i) for i in vals]
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def set_survey_point(plugin: SpeckleGIS):
try:
# from widget (2 strings) to local vars + update SR of the map
print("SET survey point")
project = plugin.gis_project
vals =[ str(plugin.dockwidget.surveyPointLat.text()), str(plugin.dockwidget.surveyPointLon.text()) ]
plugin.lat, plugin.lon = [float(i.replace(" ","")) for i in vals]
if plugin.lat>180 or plugin.lat<-180 or plugin.lon >180 or plugin.lon<-180:
logToUser("LAT LON values must be within (-180, 180). You can right-click on the canvas location to copy coordinates in WGS 84", level = 1, plugin=self.dockwidget)
return True
pt = str(plugin.lat) + ";" + str(plugin.lon)
table = findOrCreateSpeckleTable(project)
if table is not None:
with arcpy.da.UpdateCursor(table, ["lat_lon"]) as cursor:
for row in cursor: # just one row
cursor.updateRow([pt])
break
del cursor
setProjectReferenceSystem(plugin)
try:
metrics.track("Connector Action", plugin.active_account, {"name": "Set As Center Point", "connector_version": str(plugin.version)})
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=plugin.dockwidget )
return True
except Exception as e:
plugin.dockwidget.surveyPointLat.setText(str(plugin.lat))
plugin.dockwidget.surveyPointLon.setText(str(plugin.lon))
logToUser("Lat, Lon values invalid: " + str(e), level=2, func = inspect.stack()[0][3])
return False
def setProjectReferenceSystem(plugin: SpeckleGIS):
try:
# save to project; create SR
newCrsString = "+proj=tmerc +ellps=WGS84 +datum=WGS84 +units=m +no_defs +lon_0=" + str(plugin.lon) + " lat_0=" + str(plugin.lat) + " +x_0=0 +y_0=0 +k_0=1"
newCrs = osr.SpatialReference()
newCrs.ImportFromProj4(newCrsString)
newCrs.MorphToESRI() # converts the WKT to an ESRI-compatible format
validate = True if len(newCrs.ExportToWkt())>10 else False
if validate:
newProjSR = arcpy.SpatialReference()
newProjSR.loadFromString(newCrs.ExportToWkt())
#source = osr.SpatialReference()
#source.ImportFromWkt(plugin.project.activeMap.spatialReference.exportToString())
#transform = osr.CoordinateTransformation(source, newCrs)
plugin.gis_project.activeMap.spatialReference = newProjSR
logToUser("Custom project Spatial Reference successfully applied", level=0, func = inspect.stack()[0][3])
else:
logToUser("Custom Spatial Reference could not be created", level=1, func = inspect.stack()[0][3])
return True
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return False
def findOrCreateSpeckleTable(project: ArcGISProject) -> Union[str, None]:
try:
path = arcpy.env.workspace #project.filePath.replace("aprx","gdb") #"\\".join(project.filePath.split("\\")[:-1]) + "\\speckle_layers\\" #arcpy.env.workspace + "\\" #
if 'speckle_gis' not in arcpy.ListTables():
try:
table = CreateTable(path, "speckle_gis")
arcpy.management.AddField(table, "project_streams", "TEXT")
arcpy.management.AddField(table, "project_layer_selection", "TEXT")
arcpy.management.AddField(table, "lat_lon", "TEXT")
cursor = arcpy.da.InsertCursor(table, FIELDS )
cursor.insertRow(["","",""])
del cursor
except Exception as e:
logToUser("Error creating a table: " + str(e), level=1, func = inspect.stack()[0][3])
raise e
else:
#print("table already exists")
# make sure fileds exist
table = path + "\\speckle_gis"
findOrCreateTableField(table, FIELDS[0])
findOrCreateTableField(table, FIELDS[1])
findOrCreateTableField(table, FIELDS[2])
findOrCreateRow(table, FIELDS)
return table
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None
def findOrCreateTableField(table: str, field: str):
try:
with arcpy.da.UpdateCursor(table, [field]) as cursor:
value = None
for row in cursor:
value = row # tuple(val,)
if value[0] is None: cursor.updateRow("")
break # look at the 1st row only
del cursor
#if value is None: # if there are no rows
# cursor = arcpy.da.InsertCursor(table, [field])
# cursor.insertRow([""])
# del cursor
except: # if field doesn't exist
arcpy.management.AddField(table, field, "TEXT")
#cursor = arcpy.da.InsertCursor(table, [field] )
#cursor.insertRow([""])
del cursor
def findOrCreateRow(table:str, fields: List[str]):
try:
# check if the row exists
cursor = arcpy.da.SearchCursor(table, fields)
k=-1
for k, row in enumerate(cursor):
#print(row)
break
del cursor
# if no rows
if k == -1:
cursor = arcpy.da.InsertCursor(table, fields)
cursor.insertRow(["", "", ""])
del cursor
else:
with arcpy.da.UpdateCursor(table, fields) as cursor:
for row in cursor:
if None in row: cursor.updateRow(["","",""])
break # look at the 1st row only
del cursor
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 B

@@ -1,647 +0,0 @@
import os
import sys
from typing import List
#from speckle.converter.layers import getLayers
#import ui.speckle_qgis_dialog
from specklepy.logging.exceptions import (SpeckleException, GraphQLException)
from PyQt5 import QtWidgets, uic
from PyQt5 import QtGui
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtWidgets import (QMainWindow, QApplication, QWidget,
QListWidgetItem, QAction, QDockWidget, QVBoxLayout,
QHBoxLayout, QWidget, QLabel)
from PyQt5 import QtCore
from PyQt5.QtCore import pyqtSignal, Qt, QSize, QEvent
from PyQt5 import QtGui, uic
from specklepy.api.credentials import get_local_accounts
import importlib
from specklepy.api.wrapper import StreamWrapper
from specklepy.api.client import SpeckleClient
from specklepy.logging import metrics
import arcpy
import inspect
try:
#from speckle.speckle_arcgis_new import Speckle
from speckle.speckle.converter.layers import getLayers, getAllProjLayers
from speckle.speckle.ui.logger import logToUser
from speckle.speckle.ui.LogWidget import LogWidget
except:
#from speckle_toolbox.esri.toolboxes.speckle.speckle_arcgis_new import Speckle
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers import getLayers, getAllProjLayers
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.LogWidget import LogWidget
#from ui.validation import tryGetStream
# Create module-like object
#pytPath = os.path.dirname(os.path.abspath(__file__)).replace("/speckle/ui","/Speckle.pyt")
#print(pytPath)
#pytModule = importlib.machinery.SourceFileLoader("specklePyt", pytPath )
#specklePyt = pytModule.load_module("specklePyt")
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
COLOR_HIGHLIGHT = (210,210,210)
SPECKLE_COLOR = (59,130,246)
SPECKLE_COLOR_LIGHT = (69,140,255)
ICON_LOGO = os.path.dirname(os.path.abspath(__file__)) + "/logo-slab-white@0.5x.png"
ICON_SEARCH = os.path.dirname(os.path.abspath(__file__)) + "/magnify.png"
ICON_DELETE = os.path.dirname(os.path.abspath(__file__)) + "/delete.png"
ICON_DELETE_BLUE = os.path.dirname(os.path.abspath(__file__)) + "/delete-blue.png"
ICON_SEND = os.path.dirname(os.path.abspath(__file__)) + "/cube-send.png"
ICON_RECEIVE = os.path.dirname(os.path.abspath(__file__)) + "/cube-receive.png"
ICON_SEND_BLACK = os.path.dirname(os.path.abspath(__file__)) + "/cube-send-black.png"
ICON_RECEIVE_BLACK = os.path.dirname(os.path.abspath(__file__)) + "/cube-receive-black.png"
ICON_SEND_BLUE = os.path.dirname(os.path.abspath(__file__)) + "/cube-send-blue.png"
ICON_RECEIVE_BLUE = os.path.dirname(os.path.abspath(__file__)) + "/cube-receive-blue.png"
COLOR = f"color: rgb{str(SPECKLE_COLOR)};"
BACKGR_COLOR = f"background-color: rgb{str(SPECKLE_COLOR)};"
BACKGR_COLOR_LIGHT = f"background-color: rgb{str(SPECKLE_COLOR_LIGHT)};"
ui_class = os.path.dirname(os.path.abspath(__file__)) + "/speckle_qgis_dialog_base.ui"
print(os.path.dirname(__file__))
class SpeckleGISDialog(QMainWindow):
closingPlugin = pyqtSignal()
streamList: QtWidgets.QComboBox
sendModeButton: QtWidgets.QPushButton
receiveModeButton: QtWidgets.QPushButton
streamBranchDropdown: QtWidgets.QComboBox
layerSendModeDropdown: QtWidgets.QComboBox
commitDropdown: QtWidgets.QComboBox
layersWidget: QtWidgets.QListWidget
saveLayerSelection: QtWidgets.QPushButton
runButton: QtWidgets.QPushButton
msgLog: LogWidget = None
gridLayoutTitleBar = QtWidgets.QGridLayout
def __init__(self):
"""Constructor."""
print("START MAIN WINDOW")
super(SpeckleGISDialog, self).__init__(None)#, QtCore.Qt.WindowStaysOnTopHint)
uic.loadUi(ui_class, self) # Load the .ui file
#self.installEventFilter(self)
self.show()
#self.instances.append(1)
try:
self.streamBranchDropdown.setMaxCount(100)
self.commitDropdown.setMaxCount(100)
self.streams_add_button.setFlat(True)
self.streams_remove_button.setFlat(True)
self.saveSurveyPoint.setFlat(True)
self.saveLayerSelection.setFlat(True)
self.reloadButton.setFlat(True)
self.closeButton.setFlat(True)
#backgr_color = f"background-color: rgb{str(SPECKLE_COLOR)};"
#backgr_color_light = f"background-color: rgb{str(SPECKLE_COLOR_LIGHT)};"
backgr_image_del = f"border-image: url({ICON_DELETE_BLUE});"
self.streams_add_button.setIcon(QIcon(ICON_SEARCH))
self.streams_add_button.setMaximumWidth(25)
self.streams_add_button.setStyleSheet("QPushButton {padding:3px;padding-left:5px;border: none; text-align: left;} QPushButton:hover { " + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + f"{COLOR}" + " }")
self.streams_remove_button.setIcon(QIcon(ICON_DELETE))
self.streams_remove_button.setMaximumWidth(25)
self.streams_remove_button.setStyleSheet("QPushButton {padding:3px;padding-left:5px;border: none; text-align: left; image-position:right} QPushButton:hover { " + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + f"{COLOR}" + " }") #+ f"{backgr_image_del}"
self.saveLayerSelection.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }")
self.saveSurveyPoint.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }")
self.reloadButton.setStyleSheet("QPushButton {text-align: left;} QPushButton:hover { " + f"{COLOR}" + " }")
self.closeButton.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }")
self.sendModeButton.setStyleSheet("QPushButton {padding: 10px; border: 0px; " + f"color: rgb{str(SPECKLE_COLOR)};"+ "} QPushButton:hover { " + "}" )
self.sendModeButton.setIcon(QIcon(ICON_SEND_BLUE))
self.receiveModeButton.setFlat(True)
self.receiveModeButton.setStyleSheet("QPushButton {padding: 10px; border: 0px;}"+ "QPushButton:hover { " + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + "}" )
self.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLACK))
self.runButton.setStyleSheet("QPushButton {color: white;border: 0px;border-radius: 17px;padding: 10px;"+ f"{BACKGR_COLOR}" + "} QPushButton:hover { "+ f"{BACKGR_COLOR_LIGHT}" + " }")
self.runButton.setMaximumWidth(200)
self.runButton.setIcon(QIcon(ICON_SEND))
# add widgets that will only show on event trigger
logWidget = LogWidget(parent=self)
self.layout().addWidget(logWidget)
self.msgLog = logWidget
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def addProps(self, plugin):
self.msgLog.active_account = plugin.active_account
self.msgLog.speckle_version = plugin.version
def addLabel(self, plugin):
try:
exitIcon = QPixmap(ICON_LOGO)
exitActIcon = QIcon(exitIcon)
# create a label
text_label = QtWidgets.QPushButton(" for ArcGIS")
text_label.setStyleSheet("border: 0px;"
"color: white;"
f"{BACKGR_COLOR}"
"top-margin: 40 px;"
"padding: 10px;"
"padding-left: 20px;"
"font-size: 15px;"
"height: 30px;"
"text-align: left;"
)
text_label.setIcon(exitActIcon)
text_label.setIconSize(QSize(300, 93))
text_label.setMinimumSize(QSize(100, 40))
text_label.setMaximumWidth(220)
version = ""
try:
if isinstance(plugin.version, str): version = str(plugin.version)
except: pass
version_label = QtWidgets.QPushButton(f"{version}")
version_label.setStyleSheet("border: 0px;"
"color: white;"
f"{BACKGR_COLOR}"
"padding-top: 15px;"
"padding-left: 0px;"
"margin-left: 0px;"
"font-size: 10px;"
"height: 30px;"
"text-align: left;"
)
widget = QWidget()
widget.setStyleSheet(f"{BACKGR_COLOR}")
connect_box = QHBoxLayout(widget)
connect_box.addWidget(text_label) #, alignment=Qt.AlignCenter)
connect_box.addWidget(version_label)
connect_box.setContentsMargins(0, 0, 0, 0)
self.gridLayoutTitleBar.addWidget(widget) # fro QMainWindow
#self.setTitleBarWidget(widget) # for QDockWidget
except Exception as e:
logToUser(e)
def resizeEvent(self, event):
try:
#print("resize")
QtWidgets.QMainWindow.resizeEvent(self, event)
if self.msgLog.size().height() != 0: # visible
self.msgLog.setGeometry(0, 0, self.msgLog.parentWidget.frameSize().width(), self.msgLog.parentWidget.frameSize().height()) #.resize(self.frameSize().width(), self.frameSize().height())
except Exception as e:
#logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def closeEvent(self, event):
try:
#import threading
print("Close event")
#threads = threading.enumerate()
#print(f"Threads total: {str(len(threads))}: {str(threads)}")
#print(self.instances)
self.closingPlugin.emit()
event.accept()
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def clearDropdown(self):
try:
#self.streamIdField.clear()
self.streamBranchDropdown.clear()
self.commitDropdown.clear()
#self.layerSendModeDropdown.clear()
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def reloadDialogUI(self, plugin):
try:
self.clearDropdown()
self.populateUI(plugin)
self.enableElements(plugin)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def run(self, plugin):
try:
print("dockwidget run")
# Setup events on first load only!
self.setupOnFirstLoad(plugin)
# Connect streams section events
self.completeStreamSection(plugin)
# Populate the UI dropdowns
self.populateUI(plugin)
print("dockwidget run end")
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def setupOnFirstLoad(self, plugin):
try:
self.runButton.clicked.connect(plugin.onRunButtonClicked)
self.streams_add_button.clicked.connect( plugin.onStreamAddButtonClicked )
self.reloadButton.clicked.connect(lambda: self.refreshClicked(plugin))
self.closeButton.clicked.connect(lambda: self.closeClicked(plugin))
self.saveSurveyPoint.clicked.connect(plugin.set_survey_point)
self.saveLayerSelection.clicked.connect(lambda: self.populateLayerDropdown(plugin))
self.sendModeButton.clicked.connect(lambda: self.setSendMode(plugin))
self.layerSendModeDropdown.currentIndexChanged.connect( lambda: self.layerSendModeChange(plugin) )
self.receiveModeButton.clicked.connect(lambda: self.setReceiveMode(plugin))
self.streamBranchDropdown.currentIndexChanged.connect( lambda: self.runBtnStatusChanged(plugin) )
self.commitDropdown.currentIndexChanged.connect( lambda: self.runBtnStatusChanged(plugin) )
self.closingPlugin.connect(plugin.onClosePlugin)
return
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def refreshClicked(self, plugin):
try:
try:
metrics.track("Connector Action", plugin.active_account, {"name": "Refresh", "connector_version": str(plugin.version)})
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=plugin.dockwidget )
plugin.reloadUI()
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def closeClicked(self, plugin):
try:
try:
metrics.track("Connector Action", plugin.active_account, {"name": "Close", "connector_version": str(plugin.version)})
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=plugin.dockwidget )
plugin.onClosePlugin()
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def setSendMode(self, plugin):
try:
plugin.btnAction = 0 # send
color = f"color: rgb{str(SPECKLE_COLOR)};"
self.sendModeButton.setStyleSheet("border: 0px;"
f"color: rgb{str(SPECKLE_COLOR)};"
"padding: 10px;")
self.sendModeButton.setIcon(QIcon(ICON_SEND_BLUE))
self.sendModeButton.setFlat(False)
self.receiveModeButton.setFlat(True)
self.receiveModeButton.setStyleSheet("QPushButton {border: 0px; color: black; padding: 10px; } QPushButton:hover { " + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + " };")
self.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLACK))
#self.receiveModeButton.setFlat(True)
self.runButton.setProperty("text", " SEND")
self.runButton.setIcon(QIcon(ICON_SEND))
# enable sections only if in "saved streams" mode
if self.layerSendModeDropdown.currentIndex() == 1: self.layersWidget.setEnabled(True)
if self.layerSendModeDropdown.currentIndex() == 1: self.saveLayerSelection.setEnabled(True)
self.commitDropdown.setEnabled(False)
self.messageInput.setEnabled(True)
self.layerSendModeDropdown.setEnabled(True)
self.runBtnStatusChanged(plugin)
return
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def setReceiveMode(self, plugin):
try:
plugin.btnAction = 1 # receive
color = f"color: rgb{str(SPECKLE_COLOR)};"
self.receiveModeButton.setStyleSheet("border: 0px;"
f"color: rgb{str(SPECKLE_COLOR)};"
"padding: 10px;")
self.sendModeButton.setIcon(QIcon(ICON_SEND_BLACK))
self.sendModeButton.setStyleSheet("QPushButton {border: 0px; color: black; padding: 10px;} QPushButton:hover { " + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + " };")
self.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLUE))
self.sendModeButton.setFlat(True)
self.receiveModeButton.setFlat(False)
#self.sendModeButton.setFlat(True)
self.runButton.setProperty("text", " RECEIVE")
self.runButton.setIcon(QIcon(ICON_RECEIVE))
#self.layerSendModeChange(plugin, 1)
self.commitDropdown.setEnabled(True)
self.layersWidget.setEnabled(False)
self.messageInput.setEnabled(False)
self.saveLayerSelection.setEnabled(False)
self.layerSendModeDropdown.setEnabled(False)
self.runBtnStatusChanged(plugin)
return
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def completeStreamSection(self, 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
def populateUI(self, plugin):
try:
self.populateLayerSendModeDropdown()
self.populateLayerDropdown(plugin, False)
#items = [self.layersWidget.item(x).text() for x in range(self.layersWidget.count())]
self.populateProjectStreams(plugin)
self.populateSurveyPoint(plugin)
self.runBtnStatusChanged(plugin)
self.runButton.setEnabled(False)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def runBtnStatusChanged(self, plugin):
try:
commitStr = str(self.commitDropdown.currentText())
branchStr = str(self.streamBranchDropdown.currentText())
if plugin.btnAction == 1: # on receive
if commitStr == "":
self.runButton.setEnabled(False)
else:
self.runButton.setEnabled(True)
if plugin.btnAction == 0: # on send
if branchStr == "":
self.runButton.setEnabled(False)
elif branchStr != "" and self.layerSendModeDropdown.currentIndex() == 1 and len(plugin.current_layers) == 0: # saved layers; but the list is empty
self.runButton.setEnabled(False)
else:
self.runButton.setEnabled(True)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def layerSendModeChange(self, plugin, runMode = None):
try:
print("Send mode changed")
if self.layerSendModeDropdown.currentIndex() == 0 or runMode == 1: # by manual selection OR receive mode
self.current_layers = []
self.layersWidget.setEnabled(False)
self.saveLayerSelection.setEnabled(False)
elif self.layerSendModeDropdown.currentIndex() == 1 and (runMode == 0 or runMode is None): # by saved AND when Send mode
self.layersWidget.setEnabled(True)
self.saveLayerSelection.setEnabled(True)
branchStr = str(self.streamBranchDropdown.currentText())
if self.layerSendModeDropdown.currentIndex() == 0:
if branchStr == "": self.runButton.setEnabled(False) # by manual selection
else: self.runButton.setEnabled(True) # by manual selection
elif self.layerSendModeDropdown.currentIndex() == 1: self.runBtnStatusChanged(plugin) # by saved
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def populateLayerDropdown(self, plugin, bySelection: bool = True):
print("populate layer dropdown / clicked save selection")
if not self: return
try:
from speckle.speckle.ui.project_vars import set_project_layer_selection
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.project_vars import set_project_layer_selection
try:
self.layersWidget.clear()
nameDisplay = []
project = plugin.gis_project
if bySelection is False: # read from project data
print("populate layers from saved data")
#print(project)
#print(project.activeMap)
all_layers_ids = [l.dataSource for l in getAllProjLayers(project)]
for layer_tuple in plugin.current_layers:
if layer_tuple[1].dataSource in all_layers_ids:
listItem = self.fillLayerList(layer_tuple[1])
self.layersWidget.addItem(listItem)
else: # read selected layers
# Fetch selected layers
print("populate layers from selection")
plugin.current_layers = []
layers = getLayers(plugin, bySelection) # List[QgsLayerTreeNode]
print(layers)
for i, layer in enumerate(layers):
plugin.current_layers.append((layer.name, layer))
listItem = self.fillLayerList(layer)
self.layersWidget.addItem(listItem)
print("populate layers from selection 2")
set_project_layer_selection(plugin)
print("populate layers from selection 3")
self.layersWidget.setIconSize(QSize(20, 20))
self.runBtnStatusChanged(plugin)
return
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def fillLayerList(self, layer):
print("Fill layer list")
try:
ICON_XXL = os.path.dirname(os.path.abspath(__file__)) + "/size-xxl.png"
ICON_RASTER = os.path.dirname(os.path.abspath(__file__)) + "/legend_raster.png"
ICON_POLYGON = os.path.dirname(os.path.abspath(__file__)) + "/legend_polygon.png"
ICON_LINE = os.path.dirname(os.path.abspath(__file__)) + "/legend_line.png"
ICON_POINT = os.path.dirname(os.path.abspath(__file__)) + "/legend_point.png"
listItem = QListWidgetItem(layer.name)
#print(listItem)
if layer.isRasterLayer: # and layer.width()*layer.height() > 1000000:
listItem.setIcon(QIcon(ICON_RASTER))
elif layer.isFeatureLayer: # and layer.featureCount() > 20000:
geomType = arcpy.Describe(layer.dataSource).shapeType
if geomType == "Polygon": listItem.setIcon(QIcon(ICON_POLYGON))
elif geomType == "Polyline": listItem.setIcon(QIcon(ICON_LINE))
elif geomType == "Point" or geomType == "Multipoint": listItem.setIcon(QIcon(ICON_POINT))
else:
listItem.setIcon(QIcon(ICON_XXL))
#else:
# icon = QgsIconUtils().iconForLayer(layer)
# listItem.setIcon(icon)
return listItem
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def populateSurveyPoint(self, plugin):
if not self:
return
try:
self.surveyPointLat.clear()
self.surveyPointLat.setText(str(plugin.lat))
self.surveyPointLon.clear()
self.surveyPointLon.setText(str(plugin.lon))
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def enableElements(self, plugin):
try:
self.sendModeButton.setEnabled(plugin.is_setup)
self.receiveModeButton.setEnabled(plugin.is_setup)
self.runButton.setEnabled(plugin.is_setup)
self.streams_add_button.setEnabled(plugin.is_setup)
if plugin.is_setup is False: self.streams_remove_button.setEnabled(plugin.is_setup)
self.streamBranchDropdown.setEnabled(plugin.is_setup)
self.layerSendModeDropdown.setEnabled(plugin.is_setup)
self.commitDropdown.setEnabled(False)
self.show()
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def populateProjectStreams(self, plugin):
try:
from speckle.speckle.ui.project_vars import set_project_streams
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.project_vars import set_project_streams
try:
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]}"]
)
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 len(plugin.current_streams)>0: plugin.active_stream = plugin.current_streams[0]
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def onActiveStreamChanged(self, plugin):
if not self: return
try:
index = self.streamList.currentIndex()
if (len(plugin.current_streams) == 0 and index ==1) or (len(plugin.current_streams)>0 and index == len(plugin.current_streams)):
self.populateProjectStreams(plugin)
plugin.onStreamCreateClicked()
return
if len(plugin.current_streams) == 0: return
if index == -1: return
try: plugin.active_stream = plugin.current_streams[index]
except: plugin.active_stream = None
self.populateActiveStreamBranchDropdown(plugin)
self.populateActiveCommitDropdown(plugin)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def populateLayerSendModeDropdown(self):
if not self: return
try:
self.layerSendModeDropdown.clear()
self.layerSendModeDropdown.addItems(
["Send visible layers", "Send saved layers"]
)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def populateActiveStreamBranchDropdown(self, plugin):
if not self: return
if plugin.active_stream is None: return
try:
self.streamBranchDropdown.clear()
if isinstance(plugin.active_stream[1], SpeckleException):
#logger.logToUser("Some streams cannot be accessed", Qgis.Warning)
return
elif plugin.active_stream is None or plugin.active_stream[1] is None or plugin.active_stream[1].branches is None:
return
self.streamBranchDropdown.addItems(
[f"{branch.name}" for branch in plugin.active_stream[1].branches.items]
)
self.streamBranchDropdown.addItems(["Create New Branch"])
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def populateActiveCommitDropdown(self, plugin):
if not self: return
try:
self.commitDropdown.clear()
if plugin.active_stream is None: return
branchName = self.streamBranchDropdown.currentText()
if branchName == "": return
if branchName == "Create New Branch":
self.streamBranchDropdown.setCurrentText("main")
plugin.onBranchCreateClicked()
return
branch = None
if isinstance(plugin.active_stream[1], SpeckleException):
#logger.logToUser("Some streams cannot be accessed", Qgis.Warning)
return
elif plugin.active_stream[1]:
for b in plugin.active_stream[1].branches.items:
if b.name == branchName:
branch = b
break
try:
self.commitDropdown.addItems(
[f"{commit.id}"+ " | " + f"{commit.message}" for commit in branch.commits.items]
)
except: pass
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def onStreamRemoveButtonClicked(self, plugin):
try:
#from ui.project_vars import set_project_streams
if not self: return
index = self.streamList.currentIndex()
if len(plugin.current_streams) > 0: plugin.current_streams.pop(index)
plugin.active_stream = None
self.streamBranchDropdown.clear()
self.commitDropdown.clear()
#self.streamIdField.setText("")
#set_project_streams(plugin)
self.populateProjectStreams(plugin)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
@@ -1,316 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SpeckleQArcGISDialog</class>
<widget class="QMainWindow" name="SpeckleQArcGISDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>600</height>
</rect>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayoutTitleBar">
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="leftMargin">
<number>20</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>30</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item row="0" column="1">
<layout class="QHBoxLayout" name="streamListButtons">
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="streamListLabel">
<property name="text">
<string>Stream</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="streamListButtons" stretch="20,1">
<item>
<widget class="QComboBox" name="streamList"/>
</item>
<item>
<widget class="QPushButton" name="streams_add_button">
<property name="text">
<string> </string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="streams_remove_button">
<property name="text">
<string> </string>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>10</width>
<height>10</height>
</rect>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="streamBranchLabel">
<property name="text">
<string>Branch</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="streamBranchDropdown"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="commitLabel">
<property name="text">
<string>Commit</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="commitDropdown"/>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
</layout>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="sendModeButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Send</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="receiveModeButton">
<property name="text">
<string>Receive</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="layerSendModeDropdown"/>
</item>
<item row="7" column="1">
<widget class="QListWidget" name="layersWidget">
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="resizeMode">
<enum>QListView::Fixed</enum>
</property>
<property name="viewMode">
<enum>QListView::ListMode</enum>
</property>
</widget>
</item>
<item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="saveLayerSelection">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Set visible layers as selection</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="9" column="0">
<widget class="QLabel" name="messageLabel">
<property name="text">
<string>Message</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLineEdit" name="messageInput">
<property name="placeholderText">
<string>Sent XXX objects from ArcGIS</string>
</property>
</widget>
</item>
<item row="10" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="runButton">
<property name="text">
<string> SEND</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
</layout>
</item>
<item row="11" column="0">
<widget class="QLabel" name="surveyPointLabel">
<property name="text">
<string>Lat °, Lon °</string>
</property>
</widget>
</item>
<item row="11" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="surveyPointLat">
<property name="placeholderText">
<string>0.0</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="surveyPointLon">
<property name="placeholderText">
<string>0.0</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="saveSurveyPoint">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Set as a project center</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="12" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="reloadButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
@@ -1,19 +0,0 @@
import os
from PyQt5 import QtWidgets, uic
from PyQt5.QtCore import pyqtSignal
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
ui_class = os.path.dirname(os.path.abspath(__file__)) + "/streamlist_dialog.ui"
class StreamListDialog(QtWidgets.QWidget):
streams_add_button: QtWidgets.QPushButton
streams_reload_button: QtWidgets.QPushButton
streams_remove_button: QtWidgets.QPushButton
def __init__(self, parent=None):
super(StreamListDialog, self).__init__(parent)
uic.loadUi(ui_class, self) # Load the .ui file
self.show()
@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StreamListDialog</class>
<widget class="QWidget" name="StreamListDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>70</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -1,165 +0,0 @@
from typing import Union
from specklepy.api.wrapper import StreamWrapper
from specklepy.api.models import Stream, Branch, Commit
from specklepy.transports.server import ServerTransport
from specklepy.api.client import SpeckleClient
from specklepy.logging.exceptions import SpeckleException, GraphQLException
import inspect
import arcpy
try:
from speckle.speckle.ui.logger import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
def tryGetStream(
sw: StreamWrapper, dataStorage, write=False, dockwidget=None
) -> Union[Stream, None]:
try:
# print("tryGetStream")
client, stream = tryGetClient(sw, dataStorage, write, dockwidget)
return stream
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=dockwidget)
return None
def tryGetClient(sw: StreamWrapper, dataStorage, write=False, dockwidget=None):
# only streams with write access
try:
client = None
savedRole = None
savedStreamId = None
for acc in dataStorage.accounts:
# only check accounts on selected server
if acc.serverInfo.url in sw.server_url:
client = SpeckleClient(
acc.serverInfo.url, acc.serverInfo.url.startswith("https")
)
try:
client.authenticate_with_account(acc)
if client.account.token is not None:
break
except SpeckleException as ex:
if "already connected" in ex.message:
logToUser(
"Dependencies versioning error.\nClick here for details.",
url="dependencies_error",
level=2,
plugin=dockwidget,
)
return
else:
raise ex
# if token still not found
if client is None or client.account.token is None:
for acc in dataStorage.accounts:
client = sw.get_client()
if client is not None:
break
if client is not None:
stream = client.stream.get(
id=sw.stream_id, branch_limit=100, commit_limit=100
)
if isinstance(stream, Stream):
# print(stream.role)
if write == False:
# try get stream, only read access needed
# print("only read access needed")
return client, stream
else:
# check write access
# print("write access needed")
if stream.role is None or (
isinstance(stream.role, str) and "reviewer" in stream.role
):
savedRole = stream.role
savedStreamId = stream.id
else:
return client, stream
if savedRole is not None and savedStreamId is not None:
logToUser(
f"You don't have write access to the stream '{savedStreamId}'. You role is '{savedRole}'",
level=2,
func=inspect.stack()[0][3],
plugin=dockwidget,
)
return None, None
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=dockwidget)
return None, None
def validateStream(stream: Stream, dockwidget) -> Union[Stream, None]:
try:
if isinstance(stream, SpeckleException):
return None
if stream.branches is None:
logToUser("Stream has no branches", level=1, plugin=dockwidget)
return None
return stream
except Exception as e:
logToUser(e, level=2, plugin=dockwidget)
return
def validateBranch(stream: Stream, branchName: str, checkCommits: bool) -> Union[Branch, None]:
try:
branch = None
if not stream.branches or not stream.branches.items:
return None
for b in stream.branches.items:
if b.name == branchName:
branch = b
break
if branch is None:
logToUser("Failed to find a branch", level=2, func = inspect.stack()[0][3])
return None
if checkCommits == True:
if branch.commits is None:
logToUser("Failed to find a branch", level=2, func = inspect.stack()[0][3])
return None
if len(branch.commits.items)==0:
logToUser("Branch contains no commits", level=2, func = inspect.stack()[0][3])
return None
return branch
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None
def validateCommit(branch: Branch, commitId: str) -> Union[Commit, None]:
try:
commit = None
try: commitId = commitId.split(" | ")[0]
except: logToUser("Commit ID is not valid", level=2, func = inspect.stack()[0][3])
for i in branch.commits.items:
if i.id == commitId:
commit = i
break
if commit is None:
try:
commit = branch.commits.items[0]
logToUser("Failed to find a commit. Receiving Latest", level=2, func = inspect.stack()[0][3])
except:
logToUser("Failed to find a commit", level=2, func = inspect.stack()[0][3])
return None
return commit
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def validateTransport(client: SpeckleClient, streamId: str) -> Union[ServerTransport, None]:
try:
transport = ServerTransport(client=client, stream_id=streamId)
return transport
except Exception as e:
logToUser("Make sure you have sufficient permissions: " + str(e), level=2, func = inspect.stack()[0][3])
return None
@@ -0,0 +1,74 @@
from PyQt5.QtWidgets import QMessageBox
from PyQt5 import QtCore
import arcpy
try:
from speckle.speckle.plugin_utils.helpers import splitTextIntoLines
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.plugin_utils.helpers import (
splitTextIntoLines,
)
import inspect
def logToUser(msg: str, func=None, level: int = 2, plugin=None, url="", blue=False):
print("Log to user")
print(msg)
msg = str(msg)
dockwidget = plugin
try:
if url == "" and blue is False: # only for info messages
msg = addLevelSymbol(msg, level)
if func is not None:
msg += "::" + str(func)
writeToLog(msg, level)
if dockwidget is None:
return
new_msg = splitTextIntoLines(msg, 70)
dockwidget.msgLog.addButton(new_msg, level=level, url=url, blue=blue)
except Exception as e:
print(e)
return
def logToUserWithAction(msg: str, level: int = 0, plugin=None, url=""):
print("Log to user with action")
return
msg = str(msg)
dockwidget = plugin
if dockwidget is None:
return
try:
new_msg = splitTextIntoLines(msg, 70)
dockwidget.msgLog.addButton(new_msg, level=level, url=url)
writeToLog(new_msg, level)
except Exception as e:
print(e)
return
def addLevelSymbol(msg: str, level: int):
if level == 0:
msg = "🛈 " + msg
if level == 1:
msg = "⚠️ " + msg
if level == 2:
msg = "" + msg
return msg
def writeToLog(msg: str = "", level: int = 2):
print(msg)
if level == 0:
arcpy.AddMessage(msg)
if level == 1:
arcpy.AddWarning(msg)
if level == 2:
arcpy.AddError(msg)
@@ -0,0 +1,128 @@
"""Logging Utility Module for Speckle QGIS"""
import inspect
from typing import Union
import webbrowser
import arcpy
def logToUser(
msg: Union[str, Exception],
func=None,
level: int = 2,
plugin=None,
url="",
blue=False,
report=False,
):
from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser as logToUser_UI
msg = str(msg)
print(msg)
logToUser_UI(msg, func, level, plugin, url, blue, report)
logger.writeToLog(msg.replace("\n", ". ") + " " + url, level, func)
class Logging:
"""Holds utility methods for logging messages to QGIS"""
qgisInterface = None
def __init__(self, iface) -> None:
self.qgisInterface = iface
def log(self, message: str, level: int = 0):
"""Logs a specific message to the Speckle messages panel."""
try:
if level == 0:
arcpy.AddMessage(message)
elif level == 1:
arcpy.AddWarning(message)
# elif level == 2: 3 error will quit pluging
# arcpy.AddError(message)
except Exception as e:
try:
logToUser(e, level=2, func=inspect.stack()[0][3])
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: int = 0,
duration: int = 120,
):
self.log(message, level)
return
if not self.qgisInterface:
return
try:
from qgis.core import Qgis
from qgis.PyQt.QtWidgets import QPushButton
if level == 0:
level = Qgis.Info
elif level == 1:
level = Qgis.Warning
elif level == 2:
level = Qgis.Critical
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: int = 0,
duration: int = 20,
func=None,
plugin=None,
):
"""Logs a specific message to the user in QGIS"""
return
self.log(message, level)
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 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)
if func is not None and func != "None":
msg += "::" + str(func)
self.log(msg, level)
logger = Logging(None)
@@ -0,0 +1,572 @@
from typing import Any, List, Optional, Tuple, Union
import arcpy
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
from arcpy.management import CreateTable
import os.path
from specklepy.api.credentials import Account, get_local_accounts
from specklepy.api.client import SpeckleClient
from specklepy.logging.exceptions import (
GraphQLException,
SpeckleException,
)
from specklepy.api.wrapper import StreamWrapper
from specklepy.api.models import Branch, Stream, Streams
from specklepy.logging import metrics
from osgeo import osr
import inspect
try:
from speckle.speckle.utils.validation import tryGetStream
from speckle.speckle.speckle_arcgis import SpeckleGIS
from speckle.speckle.converter.layers import getAllProjLayers
from speckle.speckle.utils.panel_logging import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.utils.validation import (
tryGetStream,
)
from speckle_toolbox.esri.toolboxes.speckle.speckle.speckle_arcgis import SpeckleGIS
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers import (
getAllProjLayers,
)
from speckle_toolbox.esri.toolboxes.speckle.speckle.utils.panel_logging import (
logToUser,
)
FIELDS = ["project_streams", "project_layer_selection", "lat_lon"]
def get_project_streams(plugin: SpeckleGIS, content: str = None):
try:
print("GET proj streams")
project = plugin.project
table = findOrCreateSpeckleTable(project)
logToUser(table, level=0, func=inspect.stack()[0][3])
rows = arcpy.da.SearchCursor(table, "project_streams")
saved_streams = []
for x in rows:
logToUser(x, level=0, func=inspect.stack()[0][3])
saved_streams.append(x[0])
temp = []
######### need to check whether saved streams are available (account reachable)
if len(saved_streams) > 0:
for url in saved_streams:
if url == "":
continue
try:
sw = StreamWrapper(url)
try:
stream = tryGetStream(sw, plugin.dataStorage)
except SpeckleException as e:
logToUser(e.message, level=2, func=inspect.stack()[0][3])
stream = None
# strId = stream.id # will cause exception if invalid
temp.append((sw, stream))
except SpeckleException as e:
logToUser(e.message, level=2, func=inspect.stack()[0][3])
# except GraphQLException as e:
# logger.logToUser(e.message, Qgis.Warning)
plugin.current_streams = temp
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
def set_project_streams(plugin: SpeckleGIS):
try:
print("SET proj streams")
project = plugin.project
table = findOrCreateSpeckleTable(project)
print("SET proj streams 2")
value = [
stream[0].stream_url for stream in plugin.current_streams
] # ",".join()
print(value)
logToUser(value, level=0, func=inspect.stack()[0][3])
if table is not None:
proj_layers = []
lan_lot = ""
with arcpy.da.UpdateCursor(table, FIELDS) as cursor:
for row in cursor: # just one row
if row[1] is not None and row[1] != "":
proj_layers.append(row[1])
if row[2] is not None and row[2] != "":
lan_lot = row[2]
cursor.deleteRow()
del cursor
if len(proj_layers) == 0:
proj_layers.append("")
if len(value) == 0:
value.append("")
cursor = arcpy.da.InsertCursor(table, FIELDS)
length = max(len(proj_layers), len(value))
for i in range(length):
if i == 0:
cursor.insertRow([value[i], proj_layers[i], lan_lot])
else:
try:
cursor.insertRow([value[i], proj_layers[i], ""])
except:
if len(value) <= i:
cursor.insertRow(["", proj_layers[i], ""])
if len(proj_layers) <= i:
cursor.insertRow([value[i], "", ""])
del cursor
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
def get_project_layer_selection(plugin: SpeckleGIS):
try:
print("GET project layer selection from the table")
project = plugin.project
table = findOrCreateSpeckleTable(project)
if table is None:
return
rows = arcpy.da.SearchCursor(table, "project_layer_selection")
saved_layers = []
for x in rows:
saved_layers.append(x[0])
temp = []
proj_layers = getAllProjLayers(project)
######### need to check whether saved streams are available (account reachable)
if len(saved_layers) > 0:
for layerPath in saved_layers:
if layerPath == "":
continue
found = 0
for layer in proj_layers:
print(layer.dataSource)
if layer.dataSource == layerPath:
temp.append((layer.name, layer))
found += 1
break
if found == 0:
logToUser(
f'Saved layer not found: "{layerPath}"',
level=1,
func=inspect.stack()[0][3],
)
plugin.current_layers = temp
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
def set_project_layer_selection(plugin: SpeckleGIS):
try:
print("SET project layer selection function")
project = plugin.project
value: List[str] = [
layer[1].dataSource for layer in plugin.current_layers
] # ",".join([layer[1].dataSource for layer in plugin.current_layers])
print(value)
table = findOrCreateSpeckleTable(project)
# print(table)
if table is not None:
lan_lot = ""
proj_streams = []
with arcpy.da.UpdateCursor(table, FIELDS) as cursor:
for row in cursor: # just one row
if row[0] is not None and row[0] != "":
proj_streams.append(row[0])
if row[2] is not None and row[2] != "":
lan_lot = row[2]
cursor.deleteRow()
del cursor
if len(proj_streams) == 0:
proj_streams.append("")
if len(value) == 0:
value.append("")
# print(proj_streams)
cursor = arcpy.da.InsertCursor(table, FIELDS)
length = max(len(proj_streams), len(value))
# print(length)
for i in range(length):
# print(i)
if i == 0:
cursor.insertRow([proj_streams[i], value[i], lan_lot])
print(i)
else:
try:
cursor.insertRow([proj_streams[i], value[i], ""])
except:
if len(proj_streams) <= i:
cursor.insertRow(["", value[i], ""])
if len(value) <= i:
cursor.insertRow([proj_streams[i], "", ""])
# print(i)
del cursor
try:
metrics.track(
"Connector Action",
plugin.active_account,
{
"name": "Save Layer Selection",
"connector_version": str(plugin.version),
},
)
except Exception as e:
logToUser(
e, level=2, func=inspect.stack()[0][3], plugin=plugin.dockwidget
)
# print(table)
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
print("SET project layer selection 2")
def get_rotation(dataStorage):
dataStorage.crs_rotation = 0
return
try:
# get from saved project, set to local vars
proj = dataStorage.project
points = proj.readEntry("speckle-qgis", "crs_rotation", "")
if points[1] and len(points[0]) > 0:
vals: List[str] = points[0].replace(" ", "").split(";")[0]
dataStorage.crs_rotation = float(vals)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def set_rotation(dataStorage, dockwidget=None):
return
try:
# from widget (3 strings) to local vars AND memory (1 string)
proj = dataStorage.project
r = dataStorage.crs_rotation
if dataStorage.crs_rotation is None:
r = 0
proj.writeEntry("speckle-qgis", "crs_rotation", r)
return True
except Exception as e:
logToUser("Lat, Lon values invalid: " + str(e), level=2)
return False
def get_crs_offsets(dataStorage):
dataStorage.crs_offset_x, dataStorage.crs_offset_y = (0,0)
return
try:
# get from saved project, set to local vars
proj = dataStorage.project
points = proj.readEntry("speckle-qgis", "crs_offsets_rotation", "")
if points[1] and len(points[0]) > 0:
vals: List[str] = points[0].replace(" ", "").split(";")[:2]
dataStorage.crs_offset_x, dataStorage.crs_offset_y = [
float(i) for i in vals
]
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def set_crs_offsets(dataStorage, dockwidget=None):
return
try:
# from widget (3 strings) to local vars AND memory (1 string)
proj = dataStorage.project
x = dataStorage.crs_offset_x
y = dataStorage.crs_offset_y
if dataStorage.crs_offset_x is None or dataStorage.crs_offset_y is None:
x = 0
y = 0
pt = str(x) + ";" + str(y)
proj.writeEntry("speckle-qgis", "crs_offsets_rotation", pt)
return True
except Exception as e:
logToUser("Lat, Lon values invalid: " + str(e), level=2)
return False
def get_project_saved_layers(plugin):
plugin.dataStorage.current_layers = []
plugin.dataStorage.saved_layers = []
return
try:
proj = plugin.project
saved_layers = proj.readEntry("speckle-qgis", "project_layer_selection", "")
temp = []
# print(saved_layers)
if saved_layers[1] and len(saved_layers[0]) != 0:
for id in saved_layers[0].split(","):
found = 0
for layer in proj.mapLayers().values():
if layer.id() == id:
temp.append((layer, layer.name(), ""))
found += 1
break
if found == 0:
logToUser(
f'Saved layer not found: "{id}"',
level=1,
func=inspect.stack()[0][3],
)
plugin.dataStorage.current_layers = temp.copy()
plugin.dataStorage.saved_layers = temp.copy()
# print(temp)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=plugin.dockwidget)
return
def set_project_layer_selection(plugin):
return
try:
proj = plugin.project
# value = ",".join([x.id() for x in self.iface.layerTreeView().selectedLayers()]) #'points_qgis2_b22ed3d0_0ff9_40d2_97f2_bd17a350d698' <qgis._core.QgsVectorDataProvider object at 0x000002627D9D4790>
value = ",".join([x[0].id() for x in plugin.dataStorage.current_layers])
# print(value)
proj.writeEntry("speckle-qgis", "project_layer_selection", value)
try:
metrics.track(
"Connector Action",
plugin.dataStorage.active_account,
{
"name": "Save Layer Selection",
"connector_version": str(plugin.dataStorage.plugin_version),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=plugin.dockwidget)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=plugin.dockwidget)
return
def get_survey_point(plugin: SpeckleGIS, content=None):
try:
print("get survey point")
project = plugin.project
table = findOrCreateSpeckleTable(project)
if table is None:
return
rows = arcpy.da.SearchCursor(table, "lat_lon")
points = ""
for x in rows:
points = x[0]
break
if points != "":
vals: List[str] = points.replace(" ", "").split(";")[:2]
plugin.lat, plugin.lon = [float(i) for i in vals]
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
def set_survey_point(plugin: SpeckleGIS):
try:
# from widget (2 strings) to local vars + update SR of the map
print("SET survey point")
project = plugin.project
vals = [
str(plugin.dockwidget.surveyPointLat.text()),
str(plugin.dockwidget.surveyPointLon.text()),
]
plugin.lat, plugin.lon = [float(i.replace(" ", "")) for i in vals]
if (
plugin.lat > 180
or plugin.lat < -180
or plugin.lon > 180
or plugin.lon < -180
):
logToUser(
"LAT LON values must be within (-180, 180). You can right-click on the canvas location to copy coordinates in WGS 84",
level=1,
plugin=self.dockwidget,
)
return True
pt = str(plugin.lat) + ";" + str(plugin.lon)
table = findOrCreateSpeckleTable(project)
if table is not None:
with arcpy.da.UpdateCursor(table, ["lat_lon"]) as cursor:
for row in cursor: # just one row
cursor.updateRow([pt])
break
del cursor
setProjectReferenceSystem(plugin)
try:
metrics.track(
"Connector Action",
plugin.active_account,
{
"name": "Set As Center Point",
"connector_version": str(plugin.version),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=plugin.dockwidget)
return True
except Exception as e:
plugin.dockwidget.surveyPointLat.setText(str(plugin.lat))
plugin.dockwidget.surveyPointLon.setText(str(plugin.lon))
logToUser(
"Lat, Lon values invalid: " + str(e), level=2, func=inspect.stack()[0][3]
)
return False
def setProjectReferenceSystem(plugin: SpeckleGIS):
try:
# save to project; create SR
newCrsString = (
"+proj=tmerc +ellps=WGS84 +datum=WGS84 +units=m +no_defs +lon_0="
+ str(plugin.lon)
+ " lat_0="
+ str(plugin.lat)
+ " +x_0=0 +y_0=0 +k_0=1"
)
newCrs = osr.SpatialReference()
newCrs.ImportFromProj4(newCrsString)
newCrs.MorphToESRI() # converts the WKT to an ESRI-compatible format
validate = True if len(newCrs.ExportToWkt()) > 10 else False
if validate:
newProjSR = arcpy.SpatialReference()
newProjSR.loadFromString(newCrs.ExportToWkt())
# source = osr.SpatialReference()
# source.ImportFromWkt(plugin.project.activeMap.spatialReference.exportToString())
# transform = osr.CoordinateTransformation(source, newCrs)
plugin.project.activeMap.spatialReference = newProjSR
logToUser(
"Custom project Spatial Reference successfully applied",
level=0,
func=inspect.stack()[0][3],
)
else:
logToUser(
"Custom Spatial Reference could not be created",
level=1,
func=inspect.stack()[0][3],
)
return True
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return False
def findOrCreateSpeckleTable(project: ArcGISProject) -> Union[str, None]:
try:
path = (
arcpy.env.workspace
) # project.filePath.replace("aprx","gdb") #"\\".join(project.filePath.split("\\")[:-1]) + "\\speckle_layers\\" #arcpy.env.workspace + "\\" #
if "speckle_gis" not in arcpy.ListTables():
try:
table = CreateTable(path, "speckle_gis")
arcpy.management.AddField(table, "project_streams", "TEXT")
arcpy.management.AddField(table, "project_layer_selection", "TEXT")
arcpy.management.AddField(table, "lat_lon", "TEXT")
cursor = arcpy.da.InsertCursor(table, FIELDS)
cursor.insertRow(["", "", ""])
del cursor
except Exception as e:
logToUser(
"Error creating a table: " + str(e),
level=1,
func=inspect.stack()[0][3],
)
raise e
else:
# print("table already exists")
# make sure fileds exist
table = path + "\\speckle_gis"
findOrCreateTableField(table, FIELDS[0])
findOrCreateTableField(table, FIELDS[1])
findOrCreateTableField(table, FIELDS[2])
findOrCreateRow(table, FIELDS)
return table
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
return None
def findOrCreateTableField(table: str, field: str):
try:
with arcpy.da.UpdateCursor(table, [field]) as cursor:
value = None
for row in cursor:
value = row # tuple(val,)
if value[0] is None:
cursor.updateRow("")
break # look at the 1st row only
del cursor
# if value is None: # if there are no rows
# cursor = arcpy.da.InsertCursor(table, [field])
# cursor.insertRow([""])
# del cursor
except: # if field doesn't exist
arcpy.management.AddField(table, field, "TEXT")
# cursor = arcpy.da.InsertCursor(table, [field] )
# cursor.insertRow([""])
del cursor
def findOrCreateRow(table: str, fields: List[str]):
try:
# check if the row exists
cursor = arcpy.da.SearchCursor(table, fields)
k = -1
for k, row in enumerate(cursor):
# print(row)
break
del cursor
# if no rows
if k == -1:
cursor = arcpy.da.InsertCursor(table, fields)
cursor.insertRow(["", "", ""])
del cursor
else:
with arcpy.da.UpdateCursor(table, fields) as cursor:
for row in cursor:
if None in row:
cursor.updateRow(["", "", ""])
break # look at the 1st row only
del cursor
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3])
@@ -0,0 +1,174 @@
import inspect
from typing import Union
from specklepy.core.api.credentials import get_default_account
from specklepy.core.api.wrapper import StreamWrapper
from specklepy.core.api.models import Stream, Branch, Commit
from specklepy.transports.server import ServerTransport
from specklepy.core.api.client import SpeckleClient
from specklepy.logging.exceptions import SpeckleException, GraphQLException
from speckle.speckle.utils.panel_logging import logToUser
def tryGetClient(sw: StreamWrapper, dataStorage, write=False, dockwidget=None):
# only streams with write access
client = None
savedRole = None
savedStreamId = None
for acc in dataStorage.accounts:
# only check accounts on selected server
if acc.serverInfo.url in sw.server_url:
client = SpeckleClient(
acc.serverInfo.url, acc.serverInfo.url.startswith("https")
)
try:
client.authenticate_with_account(acc)
if client.account.token is not None:
break
except SpeckleException as ex:
if "already connected" in ex.message:
logToUser(
"Dependencies versioning error.\nClick here for details.",
url="dependencies_error",
level=2,
plugin=dockwidget,
)
return None, None
else:
raise ex
# if token still not found
if client is None or client.account.token is None:
client = sw.get_client()
if client is not None:
stream = client.stream.get(id=sw.stream_id, branch_limit=100, commit_limit=100)
if isinstance(stream, Stream):
if write is False:
# try get stream, only read access needed
return client, stream
else:
# check write access
if stream.role is None:
raise Exception(
f"You don't have write access to the stream '{stream.id}'. You role is '{stream.role}'"
)
elif isinstance(stream.role, str) and "reviewer" in stream.role:
raise Exception(
f"You don't have write access to the stream '{savedStreamId}'. You role is '{savedRole}'"
)
else:
return client, stream
else:
return None, None
else:
return None, None
def tryGetStream(
sw: StreamWrapper, dataStorage, write=False, dockwidget=None
) -> Union[Stream, None]:
try:
# print("tryGetStream")
client, stream = tryGetClient(sw, dataStorage, write, dockwidget)
return stream
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=dockwidget)
return None
def validateStream(stream: Stream, dockwidget) -> Union[Stream, None]:
try:
# dockwidget.dataStorage.check_for_accounts()
# stream = tryGetStream(streamWrapper, dockwidget.dataStorage)
if isinstance(stream, SpeckleException):
return None
if stream.branches is None:
logToUser("Stream has no branches", level=1, plugin=dockwidget)
return None
return stream
except Exception as e:
logToUser(e, level=2, plugin=dockwidget)
return
def validateBranch(
stream: Stream, branchName: str, checkCommits: bool, dockwidget
) -> Union[Branch, None]:
try:
branch = None
if not stream.branches or not stream.branches.items:
return None
for b in stream.branches.items:
if b.name == branchName:
branch = b
break
if branch is None:
logToUser("Failed to find a branch", level=2, plugin=dockwidget)
return None
if checkCommits == True:
if branch.commits is None:
logToUser("Failed to find a branch", level=2, plugin=dockwidget)
return None
if len(branch.commits.items) == 0:
logToUser("Branch contains no commits", level=1, plugin=dockwidget)
return None
return branch
except Exception as e:
logToUser(e, level=2, plugin=dockwidget)
return
def validateCommit(
branch: Branch, commitId: str, dockwidget=None
) -> Union[Commit, None]:
try:
commit = None
try:
commitId = commitId.split(" | ")[0]
except:
logToUser("Commit ID is not valid", level=2, plugin=dockwidget)
if commitId.startswith("Latest") and len(branch.commits.items) > 0:
commit = branch.commits.items[0]
else:
for i in branch.commits.items:
if i.id == commitId:
commit = i
break
if commit is None:
try:
commit = branch.commits.items[0]
logToUser(
"Failed to find a commit. Receiving Latest",
level=1,
plugin=dockwidget,
)
except:
logToUser("Failed to find a commit", level=2, plugin=dockwidget)
return None
return commit
except Exception as e:
logToUser(e, level=2, plugin=dockwidget)
return
def validateTransport(
client: SpeckleClient, streamId: str
) -> Union[ServerTransport, None]:
try:
account = client.account
if not account.token:
account = get_default_account()
transport = ServerTransport(client=client, account=account, stream_id=streamId)
# print(transport)
return transport
except Exception as e:
logToUser(
"Make sure you have sufficient permissions: " + str(e),
level=1,
func=inspect.stack()[0][3],
)
return None
@@ -1,111 +0,0 @@
from typing import Dict, List
from numpy import double
from import UpdateSavedStreams
from import UpdateSelectedStream
from specklepy_qt_ui.qt_ui.ConnectorBindings import ConnectorBindings
from specklepy_qt_ui.qt_ui.Models.StreamState import StreamState
class QGISBindings(ConnectorBindings):
def __init__(self):
pass
def UpdateSavedStreams(self, streams: List[StreamState]):
UpdateSavedStreams(streams)
def UpdateSelectedStream(self):
UpdateSelectedStream()
def Open3DView(self, viewCoordinates: List[double], viewName: str = ""):
'''Opens a 3D view in the host application
viewCoordinates: First three values are the camera position, second three the target.
viewName: Id or Name of the view'''
return
def GetHostAppNameVersion(self)-> str:
'''Gets the current host application name with version.'''
return
def GetHostAppName(self) -> str:
'''Gets the current host application name.'''
return
def GetFileName(self) -> str:
'''Gets the current opened/focused file's name.
Make sure to check regarding unsaved/temporary files.'''
return
def GetDocumentId(self) -> str:
'''Gets the current opened/focused file's id.
Generate one in here if the host app does not provide one.'''
return
def GetDocumentLocation(self) -> str:
'''Gets the current opened/focused file's locations.
Make sure to check regarding unsaved/temporary files.'''
return
def ResetDocument(self):
'''Clears the document state of selections and previews'''
return
def GetActiveViewName(self) -> str:
'''Gets the current opened/focused file's view, if applicable.'''
return
def GetStreamsInFile(self) -> List[StreamState]:
'''Returns the serialised clients present in the current open host file.'''
return
def WriteStreamsToFile(self, streams: List[StreamState]):
'''Writes serialised clients to the current open host file.'''
return
def AddNewStream(self, state: StreamState):
'''Adds a new client and persists the info to the host file'''
return
def PersistAndUpdateStreamInFile(self, state: StreamState):
'''Persists the stream info to the host file; if maintaining a local in memory copy, make sure to update it too.'''
return
def SendStream(self, state: StreamState, progress: ProgressViewModel) -> str:
'''Pushes a client's stream'''
return
def PreviewSend(self, state: StreamState, progress: ProgressViewModel):
'''Previews a send operation'''
def ReceiveStream(self, state: StreamState, progress: ProgressViewModel) -> StreamState:
'''Receives stream data from the server'''
def PreviewReceive(self, state: StreamState, progress: ProgressViewModel) -> StreamState:
'''Previews a receive operation'''
def GetSelectedObjects(self) -> List[str]:
'''Adds the current selection to the provided client.'''
def GetObjectsInView(self) -> List[str]:
'''Gets a list of objects in the currently active view'''
def SelectClientObjects(self, objs: List[str], deselect: bool = False):
'''clients should be able to select/preview/hover one way or another their associated objects'''
def GetSelectionFilters(self) -> List[ISelectionFilter]:
'''Should return a list of filters that the application supports.'''
def GetReceiveModes(self) -> List[ReceiveMode]:
'''Should return a list of receive modes that the application supports.'''
def GetCustomStreamMenuItems(self) -> List[MenuItem]:
'''Return a list of custom menu items for stream cards.'''
def GetSettings(self) -> List[ISetting]:
return
def ImportFamilyCommand(self, Mapping: Dict[str, List[MappingValue]] ) -> Dict[str, List[MappingValue]] :
'''Imports family symbols in Revit'''
return
@@ -1,104 +0,0 @@
import threading
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
import os
import inspect
from specklepy.logging.exceptions import (SpeckleException, GraphQLException)
from specklepy.logging import metrics
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.utils.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") )
)
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
self.mappingSendDialog.runSetup()
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) )
return
except Exception as e:
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
index = self.streamList.currentIndex()
if len(plugin.current_streams) > 0: plugin.current_streams.pop(index)
plugin.active_stream = None
self.streamBranchDropdown.clear()
self.commitDropdown.clear()
#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)
return
def populateProjectStreams(self, plugin):
try:
from speckle.utils.project_vars import set_project_streams
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([""])
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 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)
return
def cancelOperations(self):
#print("____cancelOperations______")
for t in threading.enumerate():
#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())
@@ -0,0 +1,136 @@
import inspect
import os
import threading
from PyQt5 import QtWidgets, uic
from specklepy.logging.exceptions import SpeckleException
from speckle.speckle.utils.panel_logging import logToUser
import speckle.specklepy_qt_ui.qt_ui
from speckle.specklepy_qt_ui.qt_ui.mainWindow import (
SpeckleGISDialog as SpeckleGISDialog_UI,
)
ui_file_path = os.path.join(
os.path.dirname(speckle.specklepy_qt_ui.qt_ui.__file__),
os.path.join("ui", "mainWindow_main.ui"),
)
class SpeckleGISDialog(SpeckleGISDialog_UI):
def __init__(self, parent=None):
"""Constructor."""
super(SpeckleGISDialog, self).__init__(
parent
) # , QtCore.Qt.WindowStaysOnTopHint)
uic.loadUi(ui_file_path, self) # Load the .ui file
# self.show()
self.runAllSetup()
def populateProjectStreams(self, plugin):
try:
from speckle.speckle.utils.project_vars import set_project_streams
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([""])
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 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)
return
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)
)
return
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def onStreamRemoveButtonClicked(self, plugin):
try:
from speckle.speckle.utils.project_vars import set_project_streams
if not self:
return
index = self.streamList.currentIndex()
if len(plugin.current_streams) > 0:
plugin.current_streams.pop(index)
plugin.active_stream = None
self.streamBranchDropdown.clear()
self.commitDropdown.clear()
set_project_streams(plugin)
self.populateProjectStreams(plugin)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def populateProjectStreams(self, plugin):
try:
from speckle.speckle.utils.project_vars import set_project_streams
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([""])
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 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)
return
def cancelOperations(self):
for t in threading.enumerate():
if "speckle_" in t.name:
t.kill()
t.join()
@@ -1,404 +0,0 @@
import inspect
import os
from typing import Any, List, Tuple, Union
from speckle.converter.layers import getAllLayers
from speckle.converter.layers.utils import getElevationLayer, getLayerGeomType
from specklepy_qt_ui.qt_ui.widget_transforms import MappingSendDialog
from specklepy_qt_ui.qt_ui.utils.logger import displayUserMsg
from specklepy_qt_ui.qt_ui.DataStorage import DataStorage
from speckle.utils.panel_logging import logToUser
from qgis.core import QgsVectorLayer, QgsRasterLayer, QgsIconUtils
from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtWidgets import QListWidgetItem
from specklepy.logging import metrics
from osgeo import gdal
import webbrowser
import specklepy_qt_ui.qt_ui
FORM_CLASS, _ = uic.loadUiType(
os.path.join(
os.path.join(
os.path.dirname(specklepy_qt_ui.qt_ui.__file__), "ui", "transforms.ui"
)
)
)
class MappingSendDialogQGIS(MappingSendDialog, FORM_CLASS):
def __init__(self, parent=None):
super(MappingSendDialog, self).__init__(parent, QtCore.Qt.WindowStaysOnTopHint)
self.setupUi(self)
self.runAllSetup()
def runSetup(self):
self.attr_label.setEnabled(False)
self.attrDropdown.setEnabled(False)
self.dialog_button.setText("Apply")
self.populateTransforms()
self.populateLayersByTransform()
self.populateSavedTransforms(self.dataStorage)
self.populateSavedElevationLayer(self.dataStorage)
# self.elevationLayerDropdown.currentIndexChanged.connect(self.saveElevationLayer)
def populateSavedTransforms(
self, dataStorage
): # , savedTransforms: Union[List, None] = None, getLayer: Union[str, None] = None, getTransform: Union[str, None] = None):
if dataStorage is not None:
self.dataStorage = dataStorage # making sure lists are synced
self.transformationsList.clear()
vals = self.dataStorage.savedTransforms
all_l_names = [l.name() for l in self.dataStorage.all_layers]
for item in vals:
layer_name = item.split(" -> ")[0].split(" ('")[0]
transform_name = item.split(" -> ")[1]
layer = None
for l in self.dataStorage.all_layers:
if layer_name == l.name():
layer = l
if layer is None:
logToUser(
f"Layer '{layer_name}' not found in the project.\nTransformation is removed.",
level=2,
)
self.dataStorage.savedTransforms.remove(item)
else:
if transform_name not in self.dataStorage.transformsCatalog:
displayUserMsg(
f"Saved transformation '{transform_name}' is not valid.\nTransformation is removed.",
level=1,
)
self.dataStorage.savedTransforms.remove(item)
elif all_l_names.count(layer.name()) > 1:
displayUserMsg(
f"Layer name '{layer.name()}' is used for more than 1 layer in the project.\nTransformation is removed.",
level=1,
)
self.dataStorage.savedTransforms.remove(item)
else:
listItem = QListWidgetItem(item)
icon = QgsIconUtils().iconForLayer(layer)
listItem.setIcon(icon)
self.transformationsList.addItem(listItem)
def onAddTransform(self):
from speckle.utils.project_vars import set_transformations
root = self.dataStorage.project.layerTreeRoot()
self.dataStorage.all_layers = getAllLayers(root)
if (
len(self.layerDropdown.currentText()) > 1
and len(self.transformDropdown.currentText()) > 1
):
listItem = (
str(self.layerDropdown.currentText())
+ " -> "
+ str(self.transformDropdown.currentText())
)
layer_name = listItem.split(" -> ")[0].split(" ('")[0]
transform_name = listItem.split(" -> ")[1].lower()
exists = 0
for record in self.dataStorage.savedTransforms:
current_layer_name = record.split(" -> ")[0].split(" ('")[0]
current_transf_name = record.split(" -> ")[1].lower()
if layer_name == current_layer_name: # in layers
exists += 1
displayUserMsg(
"Selected layer already has a transformation applied", level=1
)
break
if exists == 0:
layer = None
for l in self.dataStorage.all_layers:
if layer_name == l.name():
layer = l
if layer is not None:
if (
"attribute" in transform_name
and self.attrDropdown.currentText() != ""
):
listItem = (
str(self.layerDropdown.currentText())
+ " ('"
+ str(self.attrDropdown.currentText())
+ "') -> "
+ str(self.transformDropdown.currentText())
)
self.dataStorage.savedTransforms.append(listItem)
self.populateSavedTransforms(self.dataStorage)
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Transformation on Send Add",
"Transformation": listItem.split(" -> ")[1],
"connector_version": str(
self.dataStorage.plugin_version
),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
set_transformations(self.dataStorage)
def onRemoveTransform(self):
from speckle.utils.project_vars import set_transformations
if self.transformationsList.currentItem() is not None:
listItem = self.transformationsList.currentItem().text()
# print(listItem)
if listItem in self.dataStorage.savedTransforms:
self.dataStorage.savedTransforms.remove(listItem)
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Transformation on Send Remove",
"Transformation": listItem.split(" -> ")[1],
"connector_version": str(self.dataStorage.plugin_version),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
self.populateSavedTransforms(self.dataStorage)
set_transformations(self.dataStorage)
def onOkClicked(self):
try:
self.saveElevationLayer()
self.close()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateLayers(self):
try:
self.layerDropdown.clear()
root = self.dataStorage.project.layerTreeRoot()
self.dataStorage.all_layers = getAllLayers(root)
for i, layer in enumerate(self.dataStorage.all_layers):
listItem = layer.name()
self.layerDropdown.addItem(listItem)
icon = QgsIconUtils().iconForLayer(layer)
self.layerDropdown.setItemIcon(i, icon)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateLayersByTransform(self):
try:
self.layerDropdown.clear()
root = self.dataStorage.project.layerTreeRoot()
self.dataStorage.all_layers = getAllLayers(root)
transform = str(self.transformDropdown.currentText())
layers_dropdown = []
for i, layer in enumerate(self.dataStorage.all_layers):
listItem = None
if "extrude" in transform.lower():
if isinstance(layer, QgsVectorLayer):
geom_type = getLayerGeomType(layer)
if "polygon" in geom_type.lower():
listItem = layer.name()
elif "elevation" in transform.lower():
if isinstance(layer, QgsRasterLayer):
# avoiding tiling layers
ds = gdal.Open(layer.source(), gdal.GA_ReadOnly)
if ds is None:
continue
# for satellites
if "texture" in transform.lower():
listItem = layer.name()
# for elevation to mesh
elif "mesh" in transform.lower():
try:
if layer.bandCount() == 1:
listItem = layer.name()
except:
pass
if listItem is not None:
layers_dropdown.append(listItem)
self.layerDropdown.addItem(listItem)
icon = QgsIconUtils().iconForLayer(layer)
self.layerDropdown.setItemIcon(len(layers_dropdown) - 1, icon)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateAttributesByLayer(self):
try:
self.attrDropdown.clear()
root = self.dataStorage.project.layerTreeRoot()
self.dataStorage.all_layers = getAllLayers(root)
layer_name = str(self.layerDropdown.currentText())
transform_name = self.transformDropdown.currentText()
layerForAttributes = None
for i, layer in enumerate(self.dataStorage.all_layers):
if layer_name == layer.name():
if isinstance(layer, QgsVectorLayer):
geom_type = getLayerGeomType(layer)
if "polygon" in geom_type.lower():
layerForAttributes = layer
break
if layerForAttributes is not None and "attribute" in transform_name:
self.attr_label.setEnabled(True)
self.attrDropdown.setEnabled(True)
if "ignore" not in transform_name:
self.attrDropdown.addItem("Random height")
for field in layerForAttributes.fields():
field_type = field.type()
if field_type in [2, 6, 10]:
self.attrDropdown.addItem(str(field.name()))
else:
self.attr_label.setEnabled(False)
self.attrDropdown.setEnabled(False)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateTransforms(self):
try:
self.transformDropdown.clear()
for item in self.dataStorage.transformsCatalog:
self.transformDropdown.addItem(item)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateSavedElevationLayer(
self, dataStorage
): # , savedTransforms: Union[List, None] = None, getLayer: Union[str, None] = None, getTransform: Union[str, None] = None):
try:
if dataStorage is not None:
self.dataStorage = dataStorage # making sure lists are synced
elevationLayer = getElevationLayer(self.dataStorage)
self.elevationLayerDropdown.clear()
root = self.dataStorage.project.layerTreeRoot()
self.dataStorage.all_layers = getAllLayers(root)
self.elevationLayerDropdown.addItem("")
setAsindex = 0
countRaster = 1
for i, layer in enumerate(self.dataStorage.all_layers):
if isinstance(layer, QgsRasterLayer):
# avoiding tiling layers
ds = gdal.Open(layer.source(), gdal.GA_ReadOnly)
if ds is None:
continue
elif layer.bandCount() != 1:
continue
listItem = layer.name()
self.elevationLayerDropdown.addItem(listItem)
icon = QgsIconUtils().iconForLayer(layer)
self.elevationLayerDropdown.setItemIcon(countRaster, icon)
if elevationLayer is not None:
if listItem == elevationLayer.name():
setAsindex = countRaster
countRaster += 1
self.elevationLayerDropdown.setCurrentIndex(setAsindex)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def saveElevationLayer(self):
# print("saveElevationLayer")
from speckle.utils.project_vars import set_elevationLayer
root = self.dataStorage.project.layerTreeRoot()
layer = None
if self.dataStorage is None:
return
layerName = str(self.elevationLayerDropdown.currentText())
try:
if self.dataStorage.elevationLayer.name() == layerName:
return
except:
pass
if len(layerName) < 1:
layer = None
else:
self.dataStorage.all_layers = getAllLayers(root)
all_l_names = [l.name() for l in self.dataStorage.all_layers]
# print(all_l_names)
for l in self.dataStorage.all_layers:
if layerName == l.name():
layer = l
try:
# print(layerName)
if all_l_names.count(layer.name()) > 1:
displayUserMsg(
f"Layer name '{layer.name()}' is used for more than 1 layer in the project",
level=1,
)
layer = None
break
else:
self.dataStorage.elevationLayer = layer
set_elevationLayer(self.dataStorage)
logToUser(
f"Elevation layer '{layerName}' successfully set",
level=0,
)
break
except:
displayUserMsg(
f"Layer '{layer.name()}' is not found in the project",
level=1,
)
layer = None
break
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Add transformation on Send",
"Transformation": "Set Layer as Elevation",
"connector_version": str(self.dataStorage.plugin_version),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
def onMoreInfo(self):
webbrowser.open("https://speckle.guide/user/qgis.html#transformations")