Compare commits

..

65 Commits

Author SHA1 Message Date
KatKatKateryna 67480f2f70 CI: return version 2023-01-31 23:24:53 +08:00
KatKatKateryna 0a24379984 typo 2023-01-31 23:15:56 +08:00
KatKatKateryna 6437be9d25 CI signing 2023-01-31 23:15:05 +08:00
KatKatKateryna 644b64bbb3 ci contexts 2023-01-31 22:42:19 +08:00
KatKatKateryna 037469966b Merge pull request #50 from specklesystems/2.11-fix
typing fixed for older python versions
2023-01-31 21:10:53 +08:00
KatKatKateryna e8d4b8035b typing fixed for older python versions 2023-01-31 21:09:53 +08:00
KatKatKateryna 07fe49a1f8 Merge pull request #48 from specklesystems/kate/2.11-fixes
missing value of 'Speckle_ID' for received GIS features
2023-01-11 20:34:12 +08:00
KatKatKateryna 6f90081af7 linting 2023-01-11 20:29:40 +08:00
KatKatKateryna 67911fdb5d typo 2023-01-11 19:32:50 +08:00
KatKatKateryna a9e48db570 manual install 2.11.0; patching error fixed 2023-01-11 19:21:40 +08:00
KatKatKateryna d284c5415a missing value of 'Speckle_ID' for received GIS features 2023-01-11 19:11:20 +08:00
Alan Rynne a6ec7b4a0b ci: Added issue automation actions 2023-01-09 20:39:59 +01:00
KatKatKateryna e70debc606 Merge pull request #47 from specklesystems/ci/use-contexts
CI: Use contexts and ssh
2023-01-09 15:21:55 +08:00
Alan Rynne a651fbd732 fix(ci): Use correct fingerprint 2023-01-05 18:14:31 +01:00
Alan Rynne 7eba1ba98b ci: Changes in CI config for get-ci-tools and deploy jobs 2023-01-05 18:10:01 +01:00
KatKatKateryna 4828f5b55e extra fix for changed attribute types 2023-01-05 22:45:14 +08:00
KatKatKateryna 0a5e49fb07 SpeckleID prop for CAD geometry on receive; fixed weird bug where object properties changed type before assigning to attribute table 2023-01-05 20:38:23 +08:00
KatKatKateryna b651bfd401 Merge pull request #46 from specklesystems/kate/2.12
2.11.0-fixed
2023-01-05 17:18:39 +08:00
KatKatKateryna 52a28eae3f Merging Main with old commits 2023-01-05 17:15:29 +08:00
KatKatKateryna 50c37315f8 Merge branch 'main' into kate/2.12 2023-01-05 16:14:10 +08:00
KatKatKateryna d9706b4f5f Merge branch 'main' of https://github.com/specklesystems/speckle-arcgis 2023-01-05 16:02:03 +08:00
KatKatKateryna aad3be3962 setting app version, basic unit tests, some error-preventing fixes 2023-01-05 15:53:20 +08:00
KatKatKateryna 013e9e27d6 adjust imports for unittest 2023-01-04 18:11:51 +08:00
KatKatKateryna 3131ba8950 Merge pull request #45 from specklesystems/2.11.0-beta
identify large integers, save into "float" type attribute; override "…
2023-01-04 02:43:59 +08:00
KatKatKateryna 9f8637d670 identify large integers, save into "float" type attribute; override "Long" type if already existed 2023-01-04 01:50:01 +08:00
KatKatKateryna f9f2628d3a Merge pull request #42 from specklesystems/kate/2.11
Kate/2.11
2022-12-25 01:35:37 +08:00
KatKatKateryna 22b4672b4a 2.10.3 whl to release 2022-12-25 01:34:27 +08:00
KatKatKateryna b963228da0 Merge pull request #32 from specklesystems/kate/2.10.3
Kate/2.10.2-beta
2022-12-25 01:34:27 +08:00
KatKatKateryna faa8fb9cfa whl file 2.10.2-beta 2022-12-25 01:34:27 +08:00
KatKatKateryna a1de8be5b0 fixing customCRS location (negative values read properly)) 2022-12-25 01:34:26 +08:00
KatKatKateryna bf10c4f00b use custom SR via WKT (not name) 2022-12-25 01:34:05 +08:00
KatKatKateryna f88a9e3966 attach renderers separately for vector/raster layers 2022-12-25 01:24:44 +08:00
KatKatKateryna b4149fa5f7 calculate raster mesh and symbology colors correctly on send; Vector renderer on send 2022-12-25 01:23:57 +08:00
KatKatKateryna 68d8c1f8f3 temporarily removed unhandled specklepy None value exception; 2022-12-23 21:14:12 +08:00
KatKatKateryna 8b4fbe88ef raster renderer on receive and send; mesh coloring on send 2022-12-20 19:35:13 +08:00
KatKatKateryna 67564631d2 fixed SR on receive cad/bim; raster mesh color on send (WIP); raster renderer send (WIP) 2022-12-20 04:28:01 +08:00
KatKatKateryna b3ffdd9d0b receiving vector gis-layer symbology 2022-12-15 23:55:41 +08:00
KatKatKateryna d7f467c6a6 symbology added ()not applied yet) 2022-12-08 01:15:21 +08:00
KatKatKateryna a14550d7ad send/receive from multiple registered accounts 2022-12-07 15:17:10 +08:00
KatKatKateryna 79b8b363f0 Merge pull request #33 from specklesystems/kate/2.11
Kate/2.10.4
2022-12-01 22:45:33 +08:00
KatKatKateryna d9b1b11036 fix 2022-12-01 22:45:10 +08:00
KatKatKateryna e47d89ce57 fix path by removing "/"; add other types than DisplayMesh; 2022-12-01 02:51:02 +08:00
KatKatKateryna faadba8e73 manual install taking the latest available 2.x whl 2022-11-30 02:36:46 +08:00
KatKatKateryna 18234800dd 2.10.3 whl to release 2022-11-30 01:27:30 +08:00
KatKatKateryna f0213b7186 stream list with no account 2022-11-30 01:26:50 +08:00
KatKatKateryna 2949142140 Merge pull request #32 from specklesystems/kate/2.10.3
Kate/2.10.2-beta
2022-11-24 10:58:29 +08:00
KatKatKateryna 5c9138238a whl file 2.10.2-beta 2022-11-24 10:55:59 +08:00
KatKatKateryna 276c78603b fixing customCRS location (negative values read properly)) 2022-11-24 10:54:12 +08:00
KatKatKateryna 323d23cfaf use custom SR via WKT (not name) 2022-11-23 01:44:59 +08:00
KatKatKateryna ab8805d5f2 whl for manual install 2022-11-22 04:34:02 +08:00
KatKatKateryna 8b0ee41ed7 Merge pull request #31 from specklesystems/kate/2.10_fix
msg
2022-11-22 02:15:04 +08:00
KatKatKateryna 92ce4c6abb catch wrong account exception 2022-11-22 02:11:32 +08:00
KatKatKateryna 9596e14c2d msg 2022-11-22 01:57:16 +08:00
KatKatKateryna cb5240c4c0 Merge pull request #30 from specklesystems/kate/2.10_fix
installer_copying file; authors data
2022-11-22 01:50:48 +08:00
KatKatKateryna edc686526a installer_copying file; authors data 2022-11-22 01:49:55 +08:00
KatKatKateryna ed97d83468 Merge pull request #29 from specklesystems/kate/2.10
Kate/2.10
2022-11-22 01:08:45 +08:00
KatKatKateryna 5b86638749 no multipatch layers in UI 2022-11-22 01:07:02 +08:00
KatKatKateryna 1e308bf1ab cleaning 2022-11-22 00:37:38 +08:00
KatKatKateryna 971d71fc7e yml copying files; flattening Base attributes; fix message on send 2022-11-22 00:26:42 +08:00
KatKatKateryna 821acf93d8 clean print statements 2022-11-21 11:05:15 +08:00
KatKatKateryna 0f4494936e Receiving BIM with attributes (some need to be flattened) 2022-11-18 15:33:55 +08:00
KatKatKateryna 5791667bfe receive BIM meshes (no properties yet) 2022-11-18 04:07:54 +08:00
KatKatKateryna 364c8fe507 added quick test code for arcgis panel 2022-11-17 19:52:10 +08:00
KatKatKateryna 791dd045c3 DO NOT send BIM layers from ArcGIS 2022-11-16 00:02:08 +08:00
KatKatKateryna 999c67ea8d Update Readme.md 2022-11-02 14:47:42 +08:00
33 changed files with 2131 additions and 396 deletions
+31 -6
View File
@@ -23,6 +23,12 @@ jobs:
- checkout
- attach_workspace:
at: ./
- run:
name: Create Innosetup signing cert
shell: powershell.exe
command: |
echo $env:PFX_B64 > "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.txt"
certutil -decode "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.txt" "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.pfx"
- run:
name: Patch
shell: powershell.exe
@@ -34,7 +40,14 @@ jobs:
$version = "$($ver).$($env:CIRCLE_BUILD_NUM)"
echo $semver
python patch_version.py $semver
speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\arcgis.iss
python setup.py sdist bdist_wheel
Copy-Item -Path "dist\speckle_toolbox-$($ver)-py3-none-any.whl" -Destination "speckle_arcgis_installer"
- run:
name: Build Installer
shell: cmd.exe
command:
| # If no tag, use 0.0.0.1 and don't make any YML (for testing only!)
speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\arcgis.iss /Sbyparam=$p
- when:
condition: << parameters.installer >>
steps:
@@ -47,9 +60,18 @@ jobs:
docker:
- image: cimg/base:2021.01
steps:
- run: # Could not get ssh to work, so using a personal token
- add_ssh_keys:
fingerprints:
- "77:64:03:93:c5:f3:1d:a6:fd:bd:fb:d1:05:56:ca:e9"
- run:
name: I know Github as a host
command: |
mkdir ~/.ssh
touch ~/.ssh/known_hosts
ssh-keyscan github.com >> ~/.ssh/known_hosts
- run:
name: Clone
command: git clone https://$GITHUB_TOKEN@github.com/specklesystems/speckle-sharp-ci-tools.git speckle-sharp-ci-tools
command: git clone git@github.com:specklesystems/speckle-sharp-ci-tools.git speckle-sharp-ci-tools
- persist_to_workspace:
root: ./
paths:
@@ -82,6 +104,7 @@ workflows: #happens with every PR to main
build: # build the installers, but don't persist to workspace for deployment
jobs:
- get-ci-tools:
context: github-dev-bot
filters:
branches:
only:
@@ -95,10 +118,12 @@ workflows: #happens with every PR to main
only:
- main
- /ci\/.*/
context: innosetup
deploy: # build installers and deploy
jobs:
- get-ci-tools:
context: github-dev-bot
filters:
tags:
only: /.*/
@@ -116,16 +141,16 @@ workflows: #happens with every PR to main
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
branches:
ignore: /.*/
context: innosetup
- deploy-manager2:
slug: arcgis
os: Win
extension: exe
requires:
- get-ci-tools
- build-deploy-connector-win
filters:
tags:
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
branches:
ignore: /.*/ # For testing only! /ci\/.*/
ignore: /.*/ # For testing only! /ci\/.*/
context: do-spaces-speckle-releases
+12
View File
@@ -0,0 +1,12 @@
name: Update issue Status
on:
issues:
types: [closed]
jobs:
update_issue:
uses: specklesystems/github-actions/.github/workflows/project-add-issue.yml@main
secrets: inherit
with:
issue-id: ${{ github.event.issue.node_id }}
+12
View File
@@ -0,0 +1,12 @@
name: Move new issues into Project
on:
issues:
types: [opened]
jobs:
track_issue:
uses: specklesystems/github-actions/.github/workflows/project-add-issue.yml@main
secrets: inherit
with:
issue-id: ${{ github.event.issue.node_id }}
+3 -1
View File
@@ -118,4 +118,6 @@ scratch.py
settings.json
**/.DS_Store
zip_build
.qt_for_python
.qt_for_python
*.pyt.xml
+1 -1
View File
@@ -74,7 +74,7 @@ Setup is adapted from [this tutorial](https://pro.arcgis.com/en/pro-app/2.8/arcp
#### Dev Environment
For a better development experience in your editor, we recommend creating a [virtual environment in ArcGIS](https://pro.arcgis.com/en/pro-app/2.8/arcpy/get-started/work-with-python-environments.htm). In the venv, you'll just need to install `specklepy`.
For a better development experience in your editor, we recommend creating a [virtual conda environment in ArcGIS](https://pro.arcgis.com/en/pro-app/2.8/arcpy/get-started/work-with-python-environments.htm). In the new conda environment, you'll just need to install `specklepy` and `panda3d`.
### Debugging
+1 -1
View File
@@ -37,7 +37,7 @@ def patch_installer(tag):
with open(fileName, "r") as file:
lines = file.readlines()
for i, line in enumerate(lines):
if "-py3-none-any.whl" in line:
if "-py3-none-any.whl" in line and '.sort' not in line:
p1 = line.split("-py3-none-any.whl")[0].split("-")[0]
p2 = f'{tag.split("-")[0]}'
p3 = line.split("-py3-none-any.whl")[1]
+3
View File
@@ -0,0 +1,3 @@
specklepy==2.9.1
panda3d==1.10.11
+2
View File
@@ -0,0 +1,2 @@
print("Hello")
+324
View File
@@ -0,0 +1,324 @@
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
import arcpy
import json
import os
try:
from speckle.converter.layers.CRS import CRS
from speckle.converter.layers.Layer 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
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
from arcpy.management import (CreateFeatureclass, MakeFeatureLayer,
AddFields, AlterField, DefineProjection )
from specklepy.objects import Base
##################################################### get example layers from the project #######
project = ArcGISProject('CURRENT')
active_map = project.activeMap
all_layers = []
layerPolygon = None
layerPolyline = None
layerPoint = None
layerMultiPoint = None
layerRaster = None
#get layer of interest
for layer in active_map.listLayers():
if layer.isFeatureLayer or layer.isRasterLayer:
all_layers.append(layer)
data = arcpy.Describe(layer.dataSource)
if layer.isRasterLayer and layerRaster is None: layerRaster = layer
if layer.isFeatureLayer:
geomType = data.shapeType
if geomType == "Polygon" and layerPolygon is None: layerPolygon = layer
if geomType == "Polyline" and layerPolyline is None: layerPolyline = layer
if geomType == "Point" and layerPoint is None: layerPoint = layer
if geomType == "Multipoint" and layerMultiPoint is None: layerMultiPoint = layer
################## reset symbology if needed:
sym = layerPolygon.symbology
print(sym.renderer.type)
sym.updateRenderer('UniqueValueRenderer')
layerPolygon.symbology = sym
print(sym.updateRenderer('UniqueValueRenderer'))
print(layerPolygon.symbology.renderer.type)
# SimpleRenderer, GraduatedColorsRenderer, GraduatedSymbolsRenderer, UnclassedColorsRenderer, UniqueValueRenderer
######################################### change symbology #################################
for k, grp in enumerate(sym.renderer.groups):
for itm in grp.items:
print(itm)
print(itm.values)
print(itm.symbol.color)
transVal = itm.values[0][0] #Grab the first "percent" value in the list of potential values
print(transVal)
for i in range(len(cats)):
label = cats[i]['value']
print(label)
if label is None or label=="": label = "<Null>"
print(label)
from speckle.converter.layers.symbologyTemplates import get_polygon_simpleRenderer
from arcpy._mp import ArcGISProject
aprx = ArcGISProject('CURRENT')
root_path = "\\".join(aprx.filePath.split("\\")[:-1])
path_style = root_path + '\\layer_speckle_symbology.lyrx'
path_style2 = root_path + '\\layer_speckle_symbology2.lyrx'
#arcpy.management.SaveToLayerFile(layerPolygon, path_style, False)
print(layerPolygon.dataSource)
arcpy.management.ApplySymbologyFromLayer(
in_layer=layerPolygon.dataSource,
in_symbology_layer=path_style2,
update_symbology='UPDATE')
f = open(path_style, "r")
renderer = json.loads(f.read())
renderer["layerDefinitions"][0]["renderer"] = get_polygon_simpleRenderer(1,2,150)
f = open(path_style2, "w")
f.write(json.dumps(renderer, indent=4))
f.close()
arcpy.management.ApplySymbologyFromLayer(str(layerPolygon), path_style2)
os.remove(path_style)
os.remove(path_style2)
###########################################################################
layer = all_layers[0]
if isinstance(layer, arcLayer):
projectCRS = project.activeMap.spatialReference
try: data = arcpy.Describe(layer.dataSource)
except OSError as e: print(e)
layerName = layer.name
crs = data.SpatialReference
units = "m"
layerObjs = []
# Convert CRS to speckle, use the projectCRS
speckleReprojectedCrs = CRS(name = projectCRS.name, wkt = projectCRS.exportToString(), units = units)
layerCRS = CRS(name=crs.name, wkt=crs.exportToString(), units = units)
if layer.isFeatureLayer:
print("VECTOR LAYER HERE")
speckleLayer = VectorLayer(units = "m")
speckleLayer.type="VectorLayer"
speckleLayer.name = layerName
speckleLayer.crs = speckleReprojectedCrs
if data.datasetType == "FeatureClass": #FeatureClass, ?Table Properties, ?Datasets
# write feature attributes
fieldnames = [field.name for field in data.fields]
rows_shapes = arcpy.da.SearchCursor(layer.longName, "Shape@") # arcpy.da.SearchCursor(in_table, field_names, {where_clause}, {spatial_reference}, {explode_to_points}, {sql_clause})
print("__ start iterating features")
row_shapes_list = [x for k, x in enumerate(rows_shapes)]
for i, features in enumerate(row_shapes_list):
print("____error Feature # " + str(i+1)) # + " / " + str(sum(1 for _ in enumerate(rows_shapes))))
if features[0] is None: continue
feat = features[0]
if feat is not None:
print(feat)
rows_attributes = arcpy.da.SearchCursor(layer.longName, fieldnames)
row_attr = []
for k, attrs in enumerate(rows_attributes):
if i == k: row_attr = attrs; break
if feat.hasCurves: feat = feat.densify("ANGLE", 1000, 0.12)
print("___________Feature to Speckle____________")
b = Base(units = "m")
data = arcpy.Describe(layer.dataSource)
layer_sr = data.spatialReference # if sr.type == "Projected":
geomType = data.shapeType #Polygon, Point, Polyline, Multipoint, MultiPatch
featureType = data.featureType # Simple,SimpleJunction,SimpleJunction,ComplexEdge,Annotation,CoverageAnnotation,Dimension,RasterCatalogItem
print(geomType)
print(hasattr(data, "isRevit"))
print(hasattr(data, "isIFC"))
print(hasattr(data, "bimLevels"))
print(hasattr(data, "hasSpatialIndex"))
if geomType == "MultiPatch" or hasattr(data, "isRevit") or hasattr(data, "isIFC") or hasattr(data, "bimLevels"):
print(f"Layer {layer.name} has unsupported data type")
print("___convertToSpeckle____________")
geom = feat
print(geom.isMultipart) # e.g. False
print(geom.hasCurves)
print(geom.partCount)
geomMultiType = geom.isMultipart
hasCurves = feat.hasCurves
geomPart = []
for i,x in enumerate(feat): # [[x,x,x]
if i==0:
print("Part # " + str(i+1))
print(x)
inner_arr = []
for k,ptn in enumerate(x):
if k<10: print(ptn) # e.g. 6.25128173828125 -9.42138671875 22.2768999999971 NaN
inner_arr.append(ptn)
#inner_arr.append(inner_arr[0]) #add first in the end
geomPart.append(arcpy.Array(inner_arr))
geomPartArray = arcpy.Array(inner_arr)
sr = project.activeMap.spatialReference
multipatch = arcpy.Multipatch(arcpy.Array(x), sr, has_z=True) # error
print(multipatch)
else:
print("___convertToSpeckle____________")
geom = feat
print(geom.isMultipart) # e.g. False
print(geom.hasCurves)
print(geom.partCount)
geomMultiType = geom.isMultipart
hasCurves = feat.hasCurves
for i,x in enumerate(feat): # [[x,x,x]
print("Part # " + str(i+1))
print(x)
for k,ptn in enumerate(x):
if k<10: print(ptn) # e.g. 6.25128173828125 -9.42138671875 22.2768999999971 NaN
path: str = project.filePath.replace("aprx","gdb")
sr = project.activeMap.spatialReference
print(sr)
f_class = CreateFeatureclass(path, "NewTestLayer", "Multipatch", has_z="ENABLED", spatial_reference = sr)
fets = []
print("04_____Feature To Native____________")
new_feat = {}
new_feat.update({"arcGisGeomFromSpeckle": multipatch})
fets.append(new_feat)
vl = MakeFeatureLayer(str(f_class), "NewTestLayer").getOutput(0)
############################# write shapefile ##################################
import shapefile
from shapefile import TRIANGLE_STRIP, TRIANGLE_FAN
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
from arcpy.management import (CreateFeatureclass, MakeFeatureLayer,
AddFields, AlterField, DefineProjection )
from specklepy.objects import Base
project = ArcGISProject('CURRENT')
path: str = project.filePath.replace("aprx","gdb")
#with shapefile.Writer(path + "\contextwriter") as w:
# w.field('field1', 'C')
# pass
w = shapefile.Writer(path + '\\dtype')
w.field('TEXT', 'C')
w.field('SHORT_TEXT', 'C', size=5)
w.field('LONG_TEXT', 'C', size=250)
w.null()
w.record('Hello', 'World', 'World'*50)
w.close()
r = shapefile.Reader(path + '\\dtype')
assert r.record(0) == ['Hello', 'World', 'World'*50]
################################################################### WORKS #################################
w = shapefile.Writer(path + '\\dtype')
w.field('INT', 'N')
w.field('LOWPREC', 'N', decimal=2)
w.field('MEDPREC', 'N', decimal=10)
w.field('HIGHPREC', 'N', decimal=30)
w.field('FTYPE', 'F', decimal=10)
w.field('LARGENR', 'N', 101)
w.field('FIRST_FLD','C','40')
w.field('SECOND_FLD','C','40')
nr = 1.3217328
w.null()
w.null()
w.record(INT=nr, LOWPREC=nr, MEDPREC=nr, HIGHPREC=-3.2302e-25, FTYPE=nr, LARGENR=int(nr)*10**100, FIRST_FLD='First', SECOND_FLD='Line')
w.record(None, None, None, None, None, None, '', '')
w.close()
r = shapefile.Reader(path + '\\dtype')
assert r.record(0) == [1, 1.32, 1.3217328, -3.2302e-25, 1.3217328, 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 'First', 'Line']
assert r.record(1) == [None, None, None, None, None, None, '', '']
################################################################# Add point ####################
w = shapefile.Writer(path + '\\dtypeShapes')
w.field('name', 'C')
w.point(122, 37)
w.record('point1')
w.close()
################################################################# Add Multipatch ####################
w = shapefile.Writer(path + '\\MultipatchTest2')
w.field('name', 'C')
w.multipatch([
[[0,0,0],[0,0,3],[5,0,0],[5,0,3],[5,5,0],[5,5,3],[0,5,0],[0,5,3],[0,0,0],[0,0,3]], # TRIANGLE_STRIP for house walls
[[2.5,2.5,5],[0,0,3],[5,0,3],[5,5,3],[0,5,3],[0,0,3]], # TRIANGLE_FAN for pointed house roof
],
partTypes=[TRIANGLE_STRIP, TRIANGLE_FAN]) # one type for each part
w.record('house1')
w.close()
r = shapefile.Reader(path + '\\MultipatchTest2')
assert r.record(0) == ['house1']
active_map.addDataFromPath(path + '\\MultipatchTest2.shp')
########################################################################## reader
sf = shapefile.Reader(path + '\\MultipatchTest2.shp')
sf.shapeType # e.g. 31 - multipatch
sf.bbox # e.g. [0.0, 0.0, 5.0, 5.0]
shapefile.Shape
##################################################### cerate multipatch layer #################################
result = arcpy.management.CreateFeatureclass(arcpy.env.scratchGDB, "test_multipatch", "MULTIPATCH", has_z="ENABLED", spatial_reference=4326)
feature_class = result[0]
################################# reading shapefile - works ####################
fc = r'C:\Users\katri\Documents\ArcGIS\Projects\MyProject\Layers_Speckle\BIM_layers_speckle\00f70159b9104180f622cca87f5dd2cb.shp'
rows = arcpy.da.SearchCursor(fc, 'Shape@')
for r in rows:
if r is not None: shape = r
print(shape)
cl = arcpy.conversion.FeatureClassToFeatureClass(r'C:\Users\katri\Documents\ArcGIS\Projects\MyProject\Layers_Speckle\BIM_layers_speckle\16d73b756a_main_2f8cfa8644\__Floors_Mesh\00c7696966e4cfda2bd8c03860a414a6', r'C:\Users\katri\Documents\ArcGIS\tests', 'copyclass')
##################################### update rows in feature class - working #############
with arcpy.da.UpdateCursor('f_class_2f8cfa8644___Structural_Framing_Mesh', 'name') as cursor:
# For each row, evaluate the WELL_YIELD value (index position
# of 0), and update WELL_CLASS (index position of 1)
for row in cursor:
row[0] = "newName"
cursor.updateRow(row)
+2
View File
@@ -16,6 +16,8 @@ def read(fname):
setup(name='speckle_toolbox',
author='SpeckleSystems',
version="2.9.4",
author_email="connectors@speckle.systems",
url="https://speckle.systems/",
description=("Example for extending geoprocessing through Python modules"),
long_description=read('Readme.md'),
python_requires='~=3.3',
+2 -1
View File
@@ -1 +1,2 @@
from speckle.speckle_arcgis import *
try: from speckle.speckle_arcgis import *
except: from speckle_toolbox.esri.toolboxes.speckle.speckle_arcgis import *
@@ -4,17 +4,23 @@
# for 3.0.0: Project-> Package Manager-> Active Environment (Environment Manager)-> Clone arcgispro-py3
# 2. Change the path to your new environemnt Python.exe if necessary (in variable "pythonPath" below, line 13)
# 3. Enter the location of 'toolbox_install_manual.py' in the following command and run this command in ArcGIS Python console (View -> Python Window)
# import sysconfig; import subprocess; x = sysconfig.get_paths()['data'] + r"\python.exe"; subprocess.run((x, 'C:\\Users\\myusername\\Documents\\manual_toolbox_install.py'), capture_output=True, text=True, shell=True, timeout=1000 )
# import sysconfig; import subprocess; x = sysconfig.get_paths()['data'] + r"\python.exe"; subprocess.run((x, 'C:\\Users\\myusername\\Documents\\toolbox_install_manual.py'), capture_output=True, text=True, shell=True, timeout=1000 )
# 4. Restart ArcGIS Pro
from subprocess_call import subprocess_call
import os
from os import listdir
from os.path import isfile, join
pythonPath = os.getenv('APPDATA').replace("\\Roaming","") + r"\Local\ESRI\conda\envs\arcgispro-py3-speckle\python.exe"
def installToolbox(newExec: str):
print("Installing Speckle Toolbox")
whl_file = os.path.join(os.path.dirname(__file__), "speckle_toolbox-2.9.4-py3-none-any.whl" )
mypath = os.path.dirname(__file__)
onlyfiles = [f for f in listdir(mypath) if (isfile(join(mypath, f)) and "-2.11.0-py3-none-any.whl" in str(f))]
onlyfiles.sort(key = lambda x: int(x.replace("speckle_toolbox-","").replace("-py3-none-any.whl","").split(".")[1]) )
whl_file = onlyfiles[len(onlyfiles)-1]
#whl_file = os.path.join(os.path.dirname(__file__), "speckle_toolbox-2.11.0-py3-none-any.whl" )
subprocess_call([newExec, '-m','pip','install','--upgrade', '--force-reinstall', whl_file])
return
+2 -1
View File
@@ -1 +1,2 @@
from speckle.speckle_arcgis import *
try: from speckle.speckle_arcgis import *
except: from speckle_toolbox.esri.toolboxes.speckle.speckle_arcgis import *
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<metadata xml:lang="en"><Esri><CreaDate>20220718</CreaDate><CreaTime>13500100</CreaTime><ArcGISFormat>1.0</ArcGISFormat><SyncOnce>TRUE</SyncOnce><ModDate>20221031</ModDate><ModTime>212303</ModTime><scaleRange><minScale>150000000</minScale><maxScale>5000</maxScale></scaleRange><ArcGISProfile>ItemDescription</ArcGISProfile></Esri><toolbox name="Speckle" alias="speckle_toolbox_"><arcToolboxHelpPath>c:\program files\arcgis\pro\Resources\Help\gp</arcToolboxHelpPath><toolsets/></toolbox><dataIdInfo><idCitation><resTitle>Speckle</resTitle></idCitation><idPurp>Speckle connector for ArcGIS</idPurp><searchKeys><keyword>speckle3d</keyword></searchKeys></dataIdInfo><distInfo><distributor><distorFormat><formatName>ArcToolbox Toolbox</formatName></distorFormat></distributor></distInfo><mdHrLv><ScopeCd value="005"></ScopeCd></mdHrLv><Binary><Thumbnail><Data EsriPropertyType="PictureX">/9j/4AAQSkZJRgABAQEAYABgAAD/4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdC
<metadata xml:lang="en"><Esri><CreaDate>20220718</CreaDate><CreaTime>13500100</CreaTime><ArcGISFormat>1.0</ArcGISFormat><SyncOnce>TRUE</SyncOnce><ModDate>20221207</ModDate><ModTime>175959</ModTime><scaleRange><minScale>150000000</minScale><maxScale>5000</maxScale></scaleRange><ArcGISProfile>ItemDescription</ArcGISProfile></Esri><toolbox name="Speckle" alias="speckle_toolbox_"><arcToolboxHelpPath>c:\program files\arcgis\pro\Resources\Help\gp</arcToolboxHelpPath><toolsets/></toolbox><dataIdInfo><idCitation><resTitle>Speckle</resTitle></idCitation><idPurp>Speckle connector for ArcGIS</idPurp><searchKeys><keyword>speckle3d</keyword></searchKeys></dataIdInfo><distInfo><distributor><distorFormat><formatName>ArcToolbox Toolbox</formatName></distorFormat></distributor></distInfo><mdHrLv><ScopeCd value="005"></ScopeCd></mdHrLv><Binary><Thumbnail><Data EsriPropertyType="PictureX">/9j/4AAQSkZJRgABAQEAYABgAAD/4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdC
IFhZWiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA9tYAAQAA
AADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFj
cHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0AAACBAAAABRyWFlaAAACGAAA
@@ -5,13 +5,22 @@ from specklepy.objects.geometry import Line, Mesh, Point, Polyline, Curve, Arc,
import arcpy
from typing import Any, List, Union, Sequence
from speckle.converter.geometry.polygon import polygonToNative, polygonToSpeckle, multiPolygonToSpeckle
from speckle.converter.geometry.polyline import arcToNative, ellipseToNative, circleToNative, curveToNative, lineToNative, polycurveToNative, polylineFromVerticesToSpeckle, polylineToNative, polylineToSpeckle
from speckle.converter.geometry.point import pointToCoord, pointToNative, pointToSpeckle, multiPointToSpeckle
from speckle.converter.geometry.polyline import speckleArcCircleToPoints, specklePolycurveToPoints, multiPolylineToSpeckle
try:
from speckle.converter.geometry.polygon import polygonToNative, polygonToSpeckle, multiPolygonToSpeckle
from speckle.converter.geometry.polyline import arcToNative, ellipseToNative, circleToNative, curveToNative, lineToNative, polycurveToNative, polylineFromVerticesToSpeckle, polylineToNative, polylineToSpeckle
from speckle.converter.geometry.point import pointToCoord, pointToNative, pointToSpeckle, multiPointToSpeckle
from speckle.converter.geometry.polyline import speckleArcCircleToPoints, specklePolycurveToPoints, multiPolylineToSpeckle
from speckle.converter.geometry.mesh import meshToNative
except:
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.polygon import polygonToNative, polygonToSpeckle, multiPolygonToSpeckle
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.polyline import arcToNative, ellipseToNative, circleToNative, curveToNative, lineToNative, polycurveToNative, polylineFromVerticesToSpeckle, polylineToNative, polylineToSpeckle
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.point import pointToCoord, pointToNative, pointToSpeckle, multiPointToSpeckle
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.polyline import speckleArcCircleToPoints, specklePolycurveToPoints, multiPolylineToSpeckle
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.mesh import meshToNative
import numpy as np
def convertToSpeckle(feature, layer, geomType, featureType) -> Union[Base, Sequence[Base], None]:
def convertToSpeckle(feature, index: str, layer, geomType, featureType) -> Union[Base, Sequence[Base], None]:
"""Converts the provided layer feature to Speckle objects"""
print("___convertToSpeckle____________")
geom = feature
@@ -38,8 +47,8 @@ def convertToSpeckle(feature, layer, geomType, featureType) -> Union[Base, Seque
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, feature, layer, geomMultiType)
else: return polygonToSpeckle(geom, feature, layer, geomMultiType)
if geom.partCount > 1: return multiPolygonToSpeckle(geom, feature, index, layer, geomMultiType)
else: return polygonToSpeckle(geom, feature, index, layer, geomMultiType)
elif geomType == "Multipoint":
return multiPointToSpeckle(geom, feature, layer, geomMultiType)
else:
@@ -125,7 +134,7 @@ def multiPolygonToNative(items: List[Base], sr: arcpy.SpatialReference): #TODO f
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])
@@ -1,10 +1,75 @@
from typing import List
import arcpy
from specklepy.objects.geometry import Mesh
import shapefile
from shapefile import TRIANGLE_STRIP, TRIANGLE_FAN
try: from speckle.converter.layers.utils import get_scale_factor
except: from speckle_toolbox.esri.toolboxes.speckle.converter.layers.utils import get_scale_factor
def meshToNative(mesh: Mesh):
"""Converts a Speckle Mesh to QgsGeometry. Currently UNSUPPORTED"""
return None
def meshToNative(meshes: List[Mesh], path: str):
"""Converts a Speckle Mesh to MultiPatch"""
print("06___________________Mesh to Native")
#print(meshes)
#print(mesh.units)
w = shapefile.Writer(path)
w.field('speckleTyp', 'C')
shapes = []
for geom in meshes:
try:
if geom.displayValue and isinstance(geom.displayValue, Mesh):
mesh = geom.displayValue
w = fill_mesh_parts(w, mesh)
elif geom.displayValue and isinstance(geom.displayValue, List):
for part in geom.displayValue:
if isinstance(part, Mesh):
mesh = part
w = fill_mesh_parts(w, mesh)
except:
try:
if geom.displayMesh and isinstance(geom.displayMesh, Mesh):
mesh = geom.displayMesh
w = fill_mesh_parts(w, mesh)
elif geom.displayMesh and isinstance(geom.displayMesh, List):
for part in geom.displayMesh:
if isinstance(part, Mesh):
mesh = part
w = fill_mesh_parts(w, mesh)
except: pass
w.close()
return path
def fill_mesh_parts(w: shapefile.Writer, mesh: Mesh):
scale = get_scale_factor(mesh.units)
parts_list = []
types_list = []
count = 0 # sequence of vertex (not of flat coord list)
try:
#print(len(mesh.faces))
if len(mesh.faces) % 4 == 0 and (mesh.faces[0] == 0 or mesh.faces[0] == 3):
for f in mesh.faces:
try:
if mesh.faces[count] == 0 or mesh.faces[count] == 3: # only handle triangles
f1 = [ scale*mesh.vertices[mesh.faces[count+1]*3], scale*mesh.vertices[mesh.faces[count+1]*3+1], scale*mesh.vertices[mesh.faces[count+1]*3+2] ]
f2 = [ scale*mesh.vertices[mesh.faces[(count+2)]*3], scale*mesh.vertices[mesh.faces[(count+2)]*3+1], scale*mesh.vertices[mesh.faces[(count+2)]*3+2] ]
f3 = [ scale*mesh.vertices[mesh.faces[(count+3)]*3], scale*mesh.vertices[mesh.faces[(count+3)]*3+1], scale*mesh.vertices[mesh.faces[(count+3)]*3+2] ]
parts_list.append([ f1, f2, f3 ])
types_list.append(TRIANGLE_FAN)
count += 4
else:
count += mesh.faces[count+1]
except: break
w.multipatch(parts_list, partTypes=types_list ) # one type for each part
w.record('displayMesh')
else: print("not triangulated mesh")
except Exception as e: pass #; print(e)
return w
def rasterToMesh(vertices, faces, colors):
mesh = Mesh.create(vertices, faces, colors)
mesh.units = "m"
@@ -3,7 +3,8 @@ from typing import List
from specklepy.objects.geometry import Point
import arcpy
from speckle.converter.layers.utils import get_scale_factor
try: from speckle.converter.layers.utils import get_scale_factor
except: from speckle_toolbox.esri.toolboxes.speckle.converter.layers.utils import get_scale_factor
def multiPointToSpeckle(geom, feature, layer, multiType: bool):
@@ -5,20 +5,34 @@ from arcpy.arcobjects.arcobjects import SpatialReference
from specklepy.objects import Base
from specklepy.objects.geometry import Point, Arc, Circle, Polycurve, Polyline, Line
from speckle.converter.geometry.mesh import rasterToMesh
from speckle.converter.geometry.point import pointToCoord, pointToNative
from speckle.converter.geometry.polyline import (polylineFromVerticesToSpeckle,
circleToSpeckle,
speckleArcCircleToPoints,
curveToSpeckle,
specklePolycurveToPoints
)
try:
from speckle.converter.geometry.mesh import rasterToMesh
from speckle.converter.geometry.point import pointToCoord, pointToNative
from speckle.converter.layers.symbologyTemplates import featureColorfromNativeRenderer
from speckle.converter.geometry.polyline import (polylineFromVerticesToSpeckle,
circleToSpeckle,
speckleArcCircleToPoints,
curveToSpeckle,
specklePolycurveToPoints
)
except:
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.mesh import rasterToMesh
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.point import pointToCoord, pointToNative
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.symbologyTemplates import featureColorfromNativeRenderer
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.polyline import (polylineFromVerticesToSpeckle,
circleToSpeckle,
speckleArcCircleToPoints,
curveToSpeckle,
specklePolycurveToPoints
)
import math
from panda3d.core import Triangulator
def multiPolygonToSpeckle(geom, feature, layer, multiType: bool):
def multiPolygonToSpeckle(geom, feature, index: str, layer, multiType: bool):
print("___MultiPolygon to Speckle____")
polygon = []
@@ -42,15 +56,15 @@ def multiPolygonToSpeckle(geom, feature, layer, multiType: bool):
arrInnerRings[len(arrInnerRings)-1].append(ptn)
full_arr = [arrBoundary] + arrInnerRings
print(full_arr)
#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, feature, layer, poly.isMultipart))
#print(poly) #<geoprocessing describe geometry object object at 0x000002B2D3E338D0>
polygon.append(polygonToSpeckle(poly, feature, index, layer, poly.isMultipart))
return polygon
def polygonToSpeckle(geom, feature, layer, multiType: bool):
def polygonToSpeckle(geom, feature, index: int, layer, multiType: bool):
"""Converts a Polygon to Speckle"""
#try:
print("___Polygon to Speckle____")
@@ -202,7 +216,7 @@ def polygonToSpeckle(geom, feature, layer, multiType: bool):
ran = range(0, total_vertices)
#print(polygon)
col = (100<<16) + (100<<8) + 100 #featureColorfromNativeRenderer(feature, layer)
col = featureColorfromNativeRenderer(index, layer)
colors = [col for i in ran] # apply same color for all vertices
mesh = rasterToMesh(vertices, faces, colors)
polygon.displayValue = mesh
@@ -228,7 +242,7 @@ def polygonToNative(poly: Base, sr: arcpy.SpatialReference) -> arcpy.Polygon:
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])
@@ -8,8 +8,13 @@ from specklepy.objects.geometry import Box, Vector, Point, Line, Polyline, Curve
import arcpy
import numpy as np
from speckle.converter.geometry.point import pointToCoord, pointToSpeckle, addZtoPoint
from speckle.converter.layers.utils import get_scale_factor
try:
from speckle.converter.geometry.point import pointToCoord, pointToSpeckle, addZtoPoint
from speckle.converter.layers.utils import get_scale_factor
except:
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.point import pointToCoord, pointToSpeckle, addZtoPoint
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.utils import get_scale_factor
def circleToSpeckle(center, point):
print("___Circle to Speckle____")
@@ -336,7 +341,7 @@ def lineFrom2pt(pt1: List[float], pt2: List[float]):
def polylineToNative(poly: Polyline, sr: arcpy.SpatialReference) -> arcpy.Polyline:
"""Converts a Speckle Polyline to QgsLineString"""
print("__ convert poly to native __")
print("__ convert polyline to native __")
if isinstance(poly, Polycurve):
poly = specklePolycurveToPoints(poly)
@@ -456,7 +461,7 @@ def specklePolycurveToPoints(poly: Polycurve) -> List[Point]:
print("_____Speckle Polycurve to points____")
points = []
for segm in poly.segments:
print(segm)
#print(segm)
pts = []
if isinstance(segm, Arc) or isinstance(segm, Circle): # or isinstance(segm, Curve):
print("Arc or Curve")
@@ -1,7 +1,10 @@
from typing import List, Optional
from specklepy.objects.base import Base
from speckle.converter.layers.CRS import CRS
try:
from speckle.converter.layers.CRS import CRS
except:
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.CRS import CRS
@@ -4,11 +4,29 @@ Contains all Layer related classes and methods.
import os
from typing import Any, List, Tuple, Union
from regex import D
from speckle.converter.layers.CRS import CRS
from speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
from speckle.converter.layers.feature import featureToNative, featureToSpeckle, cadFeatureToNative, rasterFeatureToSpeckle
#from regex import D
try:
from speckle.converter.layers.CRS import CRS
from speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
from speckle.converter.layers.symbologyTemplates import vectorRendererToNative, rasterRendererToNative, rendererToSpeckle
from speckle.converter.layers.feature import featureToNative, featureToSpeckle, cadFeatureToNative, bimFeatureToNative, rasterFeatureToSpeckle
from speckle.converter.geometry.mesh import rasterToMesh, meshToNative
from speckle.converter.layers.utils import findTransformation
from speckle.converter.layers.utils import getLayerAttributes, newLayerGroupAndName, validate_path
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
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.symbologyTemplates import vectorRendererToNative, rasterRendererToNative, rendererToSpeckle
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.feature import featureToNative, featureToSpeckle, cadFeatureToNative, bimFeatureToNative, rasterFeatureToSpeckle
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.mesh import rasterToMesh, meshToNative
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.utils import findTransformation
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.utils import getLayerAttributes, newLayerGroupAndName, validate_path
from specklepy.objects import Base
from specklepy.objects.geometry import Mesh
import arcgisscripting
import pandas as pd
@@ -17,12 +35,8 @@ from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
from arcpy.management import (CreateFeatureclass, MakeFeatureLayer,
AddFields, AlterField, DefineProjection )
from speckle.converter.layers.utils import getLayerAttributes, newLayerGroupAndName, validate_path
import numpy as np
from speckle.converter.layers.utils import findTransformation
def convertSelectedLayers(all_layers: List[arcLayer], selected_layers: List[str], project: ArcGISProject) -> List[Union[VectorLayer,Layer]]:
"""Converts the current selected layers to Speckle"""
print("________Convert Layers_________")
@@ -37,9 +51,10 @@ def convertSelectedLayers(all_layers: List[arcLayer], selected_layers: List[str]
ds = layerToSend.dataSource #file path
#if layerToSend.isFeatureLayer:
newBaseLayer = layerToSpeckle(layerToSend, project)
if newBaseLayer is not None: result.append(newBaseLayer)
elif layerToSend.isRasterLayer: pass
if newBaseLayer is not None:
result.append(newBaseLayer)
#elif layerToSend.isRasterLayer: pass
print(result)
return result
@@ -72,6 +87,7 @@ def layerToSpeckle(layer: arcLayer, project: ArcGISProject) -> Union[VectorLayer
speckleLayer.type="VectorLayer"
speckleLayer.name = layerName
speckleLayer.crs = speckleReprojectedCrs
speckleLayer.renderer = rendererToSpeckle(project, project.activeMap, layer, None)
#speckleLayer.datum = datum
@@ -112,7 +128,7 @@ def layerToSpeckle(layer: arcLayer, project: ArcGISProject) -> Union[VectorLayer
#print(feat)
b = featureToSpeckle(fieldnames, row_attr, feat, projectCRS, project, layer)
b = featureToSpeckle(fieldnames, row_attr, i, feat, projectCRS, project, layer)
if b is not None: layerObjs.append(b)
print("____End of Feature # " + str(i+1))
@@ -121,6 +137,8 @@ def layerToSpeckle(layer: arcLayer, project: ArcGISProject) -> Union[VectorLayer
speckleLayer.features=layerObjs
speckleLayer.geomType = data.shapeType
if len(speckleLayer.features) == 0: return None
#layerBase.renderer = layerRenderer
#layerBase.applicationId = selectedLayer.id()
@@ -144,6 +162,8 @@ def layerToSpeckle(layer: arcLayer, project: ArcGISProject) -> Union[VectorLayer
#speckleLayer.geomType="Raster"
speckleLayer.features = layerObjs
speckleLayer.renderer = rendererToSpeckle(project, project.activeMap, layer, b)
#speckleLayer.renderer = layerRenderer
#speckleLayer.applicationId = selectedLayer.id()
@@ -160,7 +180,203 @@ def layerToNative(layer: Union[Layer, VectorLayer, RasterLayer], streamBranch: s
return rasterLayerToNative(layer, streamBranch, project)
return None
def cadLayerToNative(layerContentList:Base, layerName: str, streamBranch: str, project: ArcGISProject) :
def bimLayerToNative(layerContentList: List[Base], layerName: str, streamBranch: str, project: ArcGISProject) :
print("01______BIM layer to native")
print(layerName)
geom_meshes = []
layer_meshes = None
#filter speckle objects by type within each layer, create sub-layer for each type (points, lines, polygons, mesh?)
for geom in layerContentList:
try:
if geom.displayMesh: geom_meshes.append(geom)
except:
try:
if geom.displayValue: geom_meshes.append(geom)
except: pass
if len(geom_meshes)>0: layer_meshes = bimVectorLayerToNative(geom_meshes, layerName, "Mesh", streamBranch, project)
return True
def bimVectorLayerToNative(geomList, layerName: str, geomType: str, streamBranch: str, project: ArcGISProject):
# no support for mltipatches, maybe in 3.1: https://community.esri.com/t5/arcgis-pro-ideas/better-support-for-multipatches-in-arcpy/idi-p/953614/page/2#comments
print("02_________BIM vector layer to native_____")
#get Project CRS, use it by default for the new received layer
vl = None
layerName = layerName + "_" + geomType
layerName = layerName.replace("[","_").replace("]","_").replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
#if not "__Structural_Foundations_Mesh" in layerName: return None
sr = arcpy.SpatialReference(text = project.activeMap.spatialReference.exportToString())
active_map = project.activeMap
path = project.filePath.replace("aprx","gdb") #
path_bim = "\\".join(project.filePath.split("\\")[:-1]) + "\\Layers_Speckle\\BIM_layers_speckle\\" + streamBranch+ "\\" + layerName + "\\" #arcpy.env.workspace + "\\" #
print(path_bim)
if not os.path.exists(path_bim): os.makedirs(path_bim)
print(path)
if sr.type == "Geographic":
arcpy.AddMessage(f"Project CRS is set to Geographic type, and objects in linear units might not be received correctly")
#CREATE A GROUP "received blabla" with sublayers
layerGroup = None
newGroupName = f'{streamBranch}'
#print(newGroupName)
for l in active_map.listLayers():
if l.longName == newGroupName: layerGroup = l; break
#find ID of the layer with a matching name in the "latest" group
newName = f'{streamBranch.split("_")[len(streamBranch.split("_"))-1]}_{layerName}'
print(newName)
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)
longName = streamBranch + "\\" + newName
if longName in all_layer_names:
for index, letter in enumerate('234567890abcdefghijklmnopqrstuvwxyz'):
if (longName + "_" + letter) not in all_layer_names: newName += "_"+letter; layerExists +=1; break
# particularly if the layer comes from ArcGIS
if "mesh" in geomType.lower(): geomType = "Multipatch"
#print("Create feature class (cad): ")
# should be created inside the workspace to be a proper Feature class (not .shp) with Nullable Fields
class_name = ("f_class_" + newName)
#f_class = CreateFeatureclass(path, class_name, geomType, has_z="ENABLED", spatial_reference = sr)
shp = meshToNative(geomList, path_bim + newName)
print("____ meshes saved___")
print(shp)
#print(path)
#print(class_name)
validated_class_path = validate_path(class_name)
print(validated_class_path)
validated_class_name = validated_class_path.split("\\")[len(validated_class_path.split("\\"))-1]
print(validated_class_name)
f_class = arcpy.conversion.FeatureClassToFeatureClass(shp, path, validated_class_name)
# , spatial_reference = sr
#arcpy.management.Project(in_dataset, f_class, sr, in_coor_system=sr)
print(f_class)
#print(geomList)
# get and set Layer attribute fields
# example: https://resource.esriuk.com/blog/an-introductory-slice-of-arcpy-in-arcgis-pro/
newFields = getLayerAttributes(geomList)
fields_to_ignore = ["arcgisgeomfromspeckle", "shape", "objectid", "displayMesh"]
matrix = []
all_keys = []
all_key_types = []
max_len = 52
print("___ after layer attributes: ___________")
print(newFields.items())
#try:
for key, value in newFields.items():
existingFields = [fl.name for fl in arcpy.ListFields(validated_class_name)]
#print(existingFields)
if key not in existingFields and key.lower() not in fields_to_ignore: # exclude geometry and default existing fields
#print(key)
# signs that should not be used as field names and table names: https://support.esri.com/en/technical-article/000005588
key = key.replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
if key[0] in ['0','1','2','3','4','5','6','7','8','9']: key = "_"+key
if len(key)>max_len: key = key[:max_len]
#print(all_keys)
if key in all_keys:
for index, letter in enumerate('1234567890abcdefghijklmnopqrstuvwxyz'):
if len(key)<max_len and (key+letter) not in all_keys: key+=letter; break
if len(key) == max_len and (key[:9] + letter) not in all_keys: key=key[:9] + letter; break
if key not in all_keys:
all_keys.append(key)
all_key_types.append(value)
#print(all_keys)
matrix.append([key, value, key, 255])
print(all_keys)
print(len(all_keys))
if len(matrix)>0: AddFields(str(f_class), matrix)
print(matrix)
fets = []
print("_________BIM FeatureS To Native___________")
for f in geomList[:]:
new_feat = bimFeatureToNative(f, newFields, sr, path_bim)
if new_feat != "" and new_feat != None:
fets.append(new_feat)
print(len(fets))
if len(fets) == 0: return None
count = 0
rowValues = []
for i, feat in enumerate(fets):
row = []
heads = []
for key in all_keys:
try:
row.append(feat[key])
heads.append(key)
except Exception as e:
row.append(None)
heads.append(key)
rowValues.append(row)
count += 1
print(heads)
with arcpy.da.UpdateCursor(f_class, heads) as cur:
# For each row, evaluate the WELL_YIELD value (index position
# of 0), and update WELL_CLASS (index position of 1)
shp_num = 0
#print(heads)
try:
for rowShape in cur:
#print(rowShape)
for i,r in enumerate(rowShape):
#print(heads[i])
#print(matrix[i])
rowShape[i] = rowValues[shp_num][i]
#print(type(rowShape[i]))
if matrix[i][1] == 'TEXT' and rowShape[i] is not None: rowShape[i] = str(rowValues[shp_num][i])
#print(type(rowShape[i]))
if isinstance(rowValues[shp_num][i], str): # cut if string is too long
rowShape[i] = rowValues[shp_num][i][:255]
#print(rowShape[i])
#print(rowShape)
cur.updateRow(rowShape)
shp_num += 1
#print(shp_num)
except Exception as e:
print("Layer attribute error: " + str(e))
#print(i)
print(shp_num)
print(len(rowValues))
#print(rowValues[i])
#print(len(rowValues[i]))
arcpy.AddWarning("Layer attribute error: " + e)
del cur
print("create layer:")
vl = MakeFeatureLayer(str(f_class), newName).getOutput(0)
active_map.addLayerToGroup(layerGroup, vl)
print("created2")
#os.remove(path_bim)
return True #last one
def cadLayerToNative(layerContentList: List[Base], layerName: str, streamBranch: str, project: ArcGISProject) :
print("01______Cad vector layer to native")
print(layerName)
geom_points = []
@@ -168,6 +384,7 @@ def cadLayerToNative(layerContentList:Base, layerName: str, streamBranch: str, p
geom_polygones = []
geom_meshes = []
#filter speckle objects by type within each layer, create sub-layer for each type (points, lines, polygons, mesh?)
print(layerContentList)
for geom in layerContentList:
#print(geom)
if geom.speckle_type == "Objects.Geometry.Point":
@@ -186,17 +403,21 @@ def cadVectorLayerToNative(geomList, layerName: str, geomType: str, streamBranch
vl = None
layerName = layerName.replace("[","_").replace("]","_").replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
layerName = layerName + "_" + geomType
print(layerName)
sr = arcpy.SpatialReference(project.activeMap.spatialReference.name)
sr = arcpy.SpatialReference(text = project.activeMap.spatialReference.exportToString())
active_map = project.activeMap
path = project.filePath.replace("aprx","gdb") #"\\".join(project.filePath.split("\\")[:-1]) + "\\speckle_layers\\" #arcpy.env.workspace + "\\" #
print(path)
print(streamBranch)
if sr.type == "Geographic":
arcpy.AddMessage(f"Project CRS is set to Geographic type, and objects in linear units might not be received correctly")
#CREATE A GROUP "received blabla" with sublayers
layerGroup = None
newGroupName = f'{streamBranch}'
print(newGroupName)
#print(newGroupName)
for l in active_map.listLayers():
if l.longName == newGroupName: layerGroup = l; break
@@ -231,7 +452,7 @@ def cadVectorLayerToNative(geomList, layerName: str, geomType: str, streamBranch
# should be created inside the workspace to be a proper Feature class (not .shp) with Nullable Fields
class_name = ("f_class_" + newName)
f_class = CreateFeatureclass(path, class_name, geomType, has_z="ENABLED", spatial_reference = sr)
#print(f_class)
print(f_class)
#print(geomList)
# get and set Layer attribute fields
@@ -268,8 +489,9 @@ def cadVectorLayerToNative(geomList, layerName: str, geomType: str, streamBranch
new_feat = cadFeatureToNative(f, newFields, sr)
if new_feat != "" and new_feat != None:
fets.append(new_feat)
#print("features created")
#print(fets)
print("features created")
print(len(fets))
print(all_keys)
if len(fets) == 0: return None
count = 0
@@ -282,24 +504,27 @@ def cadVectorLayerToNative(geomList, layerName: str, geomType: str, streamBranch
heads = [ 'Shape@', 'OBJECTID']
for key,value in feat.items():
#print(key, str(value))
if key in all_keys and key.lower() not in fields_to_ignore:
heads.append(key)
row.append(value)
rowValues.append(row)
count += 1
#print(heads)
cur = arcpy.da.InsertCursor(str(f_class), tuple(heads) )
#print(heads)
for row in rowValues:
#print(tuple(heads))
#print(tuple(row))
cur.insertRow(tuple(row))
try:
#print(row)
cur.insertRow(tuple(row))
except Exception as e:
print(e)
del cur
#print(f_class)
vl = MakeFeatureLayer(str(f_class), newName).getOutput(0)
#adding layers from code solved: https://gis.stackexchange.com/questions/344343/arcpy-makefeaturelayer-management-function-not-creating-feature-layer-in-arcgis
#active_map.addLayer(new_layer)
active_map.addLayerToGroup(layerGroup, vl)
print("Layer created")
return vl
@@ -370,7 +595,7 @@ def vectorLayerToNative(layer: Union[Layer, VectorLayer], streamBranch: str, pro
new_feat = featureToNative(f, newFields, geomType, sr)
if new_feat != "" and new_feat!= None: fets.append(new_feat)
print(fets)
#print(fets)
if len(fets) == 0: return None
count = 0
rowValues = []
@@ -391,16 +616,27 @@ def vectorLayerToNative(layer: Union[Layer, VectorLayer], streamBranch: str, pro
count += 1
cur = arcpy.da.InsertCursor(str(f_class), tuple(heads) )
for row in rowValues:
print(tuple(heads))
print(tuple(row))
#print(tuple(heads))
#print(tuple(row))
cur.insertRow(tuple(row))
del cur
vl = MakeFeatureLayer(str(f_class), newName).getOutput(0)
#adding layers from code solved: https://gis.stackexchange.com/questions/344343/arcpy-makefeaturelayer-management-function-not-creating-feature-layer-in-arcgis
#active_map.addLayer(new_layer)
active_map.addLayerToGroup(layerGroup, vl)
vl2 = None
print(newName)
for l in project.activeMap.listLayers():
#print(l.longName)
if l.longName == layerGroup.longName + "\\" + newName:
vl2 = l
break
path_lyr = vectorRendererToNative(project, active_map, layerGroup, layer, vl2, f_class, heads)
#if path_lyr is not None:
# active_map.removeLayer(path_lyr)
r'''
# rename back the layer if was renamed due to existing duplicate
if layerExists:
@@ -444,6 +680,9 @@ def rasterLayerToNative(layer: RasterLayer, streamBranch: str, project: ArcGISPr
rasterHasSr = False
print(path)
path_bands = "\\".join(path.split("\\")[:-1]) + "\\Layers_Speckle\\rasters_Speckle\\" + streamBranch
if not os.path.exists(path_bands): os.makedirs(path_bands)
try:
srRasterWkt = str(layer.rasterCrs.wkt)
print(layer.rasterCrs.wkt)
@@ -489,7 +728,7 @@ def rasterLayerToNative(layer: RasterLayer, streamBranch: str, project: ArcGISPr
for i in range(bandsCount):
print(i)
print(bandNames[i])
rasterbandPath = path + "\\" + newName + "_Band_" + str(i+1) #+ ".tif"
rasterbandPath = path_bands + "\\" + newName + "_Band_" + str(i+1) + ".tif"
bandDatasets += rasterbandPath + ";"
rasterband = np.array(bandValues[i])
rasterband = np.reshape(rasterband,(ysize, xsize))
@@ -523,23 +762,42 @@ def rasterLayerToNative(layer: RasterLayer, streamBranch: str, project: ArcGISPr
#mergedRaster.setProperty("spatialReference", crsRaster)
full_path = validate_path(path + "\\" + newName) #solved file saving issue
print("RASTER FULL PATH")
print(full_path)
if os.path.exists(full_path):
#print(full_path)
for index, letter in enumerate('1234567890abcdefghijklmnopqrstuvwxyz'):
print(full_path + letter)
if os.path.exists(full_path + letter): pass
else: full_path += letter; break
print("RASTER new PATH")
print(full_path)
#mergedRaster = arcpy.ia.Merge(rastersToMerge) # glues all bands together
#mergedRaster.save(full_path) # similar errors: https://community.esri.com/t5/python-questions/error-010240-could-not-save-raster-dataset/td-p/321690
arcpy.management.CompositeBands(rasterPathsToMerge, full_path)
try:
arcpy.management.CompositeBands(rasterPathsToMerge, full_path)
except: # if already exists
full_path += "_"
arcpy.management.CompositeBands(rasterPathsToMerge, full_path)
print(path + "\\" + newName)
arcpy.management.DefineProjection(full_path, srRaster)
rasterLayer = arcpy.management.MakeRasterLayer(full_path, newName + "_").getOutput(0)
rasterLayer = arcpy.management.MakeRasterLayer(full_path, newName).getOutput(0)
print(layerGroup)
active_map.addLayerToGroup(layerGroup, rasterLayer)
rl2 = None
for l in active_map.listLayers():
if l.longName == layerGroup.longName + "\\" + newName:
print(l.longName)
rl2 = l
break
rasterLayer = rasterRendererToNative(project, active_map, layerGroup, layer, rl2, rasterPathsToMerge, newName)
try: os.remove(path_bands)
except: pass
r'''
if arcpy.Exists(fileout):
arcpy.management.Delete(fileout)
@@ -9,32 +9,49 @@ import arcpy
from arcpy.management import CreateCustomGeoTransformation
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
from speckle.converter.geometry._init_ import convertToSpeckle, convertToNative, convertToNativeMulti
from speckle.converter.layers.utils import (findTransformation, getVariantFromValue, traverseDict,
traverseDictByKey, hsv_to_rgb)
from speckle.converter.geometry.point import pointToSpeckle
from speckle.converter.geometry.mesh import rasterToMesh
try:
from speckle.converter.geometry._init_ import convertToSpeckle, convertToNative, convertToNativeMulti
from speckle.converter.layers.utils import (findTransformation, getVariantFromValue, traverseDict,
traverseDictByKey, hsv_to_rgb)
from speckle.converter.geometry.point import pointToSpeckle
from speckle.converter.geometry.mesh import rasterToMesh, meshToNative
from speckle.converter.layers.symbologyTemplates import jsonFromLayerStyle
except:
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry._init_ import convertToSpeckle, convertToNative, convertToNativeMulti
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.utils import (findTransformation, getVariantFromValue, traverseDict,
traverseDictByKey, hsv_to_rgb)
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.point import pointToSpeckle
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.mesh import rasterToMesh, meshToNative
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.symbologyTemplates import jsonFromLayerStyle
import numpy as np
import colorsys
def featureToSpeckle(fieldnames, attr_list, f_shape, projectCRS: arcpy.SpatialReference, project: ArcGISProject, selectedLayer: arcLayer):
def featureToSpeckle(fieldnames, attr_list, index: int, f_shape, projectCRS: arcpy.SpatialReference, project: ArcGISProject, selectedLayer: arcLayer):
print("___________Feature to Speckle____________")
b = Base(units = "m")
data = arcpy.Describe(selectedLayer.dataSource)
layer_sr = data.spatialReference # if sr.type == "Projected":
geomType = data.shapeType #Polygon, Point, Polyline, Multipoint, MultiPatch
featureType = data.featureType # Simple,SimpleJunction,SimpleJunction,ComplexEdge,Annotation,CoverageAnnotation,Dimension,RasterCatalogItem
print(geomType)
print(hasattr(data, "isRevit"))
print(hasattr(data, "isIFC"))
print(hasattr(data, "bimLevels"))
print(hasattr(data, "hasSpatialIndex"))
if geomType == "MultiPatch" or hasattr(data, "isRevit") or hasattr(data, "isIFC") or hasattr(data, "bimLevels"):
print(f"Layer {selectedLayer.name} has unsupported data type")
arcpy.AddWarning(f"Layer {selectedLayer.name} has unsupported data type")
return None
#print(layer_sr.name)
#print(projectCRS.name)
f_shape = findTransformation(f_shape, geomType, layer_sr, projectCRS, selectedLayer)
if f_shape is None: return None
######################################### Convert geometry ##########################################
try:
geom = convertToSpeckle(f_shape, selectedLayer, geomType, featureType)
geom = convertToSpeckle(f_shape, index, selectedLayer, geomType, featureType)
if geom is not None: print(geom); b["geometry"] = geom
except Exception as error:
print("Error converting geometry: " + str(error))
@@ -58,7 +75,7 @@ def featureToNative(feature: Base, fields: dict, geomType: str, sr: arcpy.Spatia
feat = {}
try: speckle_geom = feature["geometry"] # for created in QGIS / ArcGIS Layer type
except: speckle_geom = feature # for created in other software
print(speckle_geom)
#print(speckle_geom)
if isinstance(speckle_geom, list):
if len(speckle_geom)>1 or geomType == "Multipoint": arcGisGeom = convertToNativeMulti(speckle_geom, sr)
else: arcGisGeom = convertToNative(speckle_geom[0], sr)
@@ -71,9 +88,14 @@ def featureToNative(feature: Base, fields: dict, geomType: str, sr: arcpy.Spatia
return None
for key, variant in fields.items():
try: value = feature[key]
except:
if key == 'Speckle_ID': value = feature['id']
else:
arcpy.AddWarning(f'Field {key} not found')
return None
value = feature[key]
if variant == "TEXT": value = str(feature[key])
if variant == "TEXT": value = str(value)
if variant == getVariantFromValue(value) and value != "NULL" and value != "None":
feat.update({key: value})
else:
@@ -82,6 +104,23 @@ def featureToNative(feature: Base, fields: dict, geomType: str, sr: arcpy.Spatia
if variant == "LONG": feat.update({key: None})
if variant == "SHORT": feat.update({key: None})
return feat
def bimFeatureToNative(feature: Base, fields: dict, sr: arcpy.SpatialReference, path: str):
print("04_________BIM Feature To Native____________")
feat = {}
try: speckle_geom = feature["geometry"] # for created in QGIS Layer type
except: speckle_geom = feature # for created in other software
feat.update({"arcGisGeomFromSpeckle": ""})
try:
if "Speckle_ID" not in fields.keys() and feature["id"]: feat.update("Speckle_ID", "TEXT")
except: pass
feat_updated = updateFeat(feat, fields, feature)
return feat_updated
def cadFeatureToNative(feature: Base, fields: dict, sr: arcpy.SpatialReference):
print("04_________CAD Feature To Native____________")
@@ -94,61 +133,85 @@ def cadFeatureToNative(feature: Base, fields: dict, sr: arcpy.SpatialReference):
else: arcGisGeom = convertToNative(speckle_geom[0], sr)
else:
arcGisGeom = convertToNative(speckle_geom, sr)
print(feat)
if arcGisGeom is not None:
feat.update({"arcGisGeomFromSpeckle": arcGisGeom})
else: return None
print(feat)
try:
if "Speckle_ID" not in fields.keys() and feature["id"]: feat.update("Speckle_ID", "TEXT")
except: pass
print(feat)
#### setting attributes to feature
for key, variant in fields.items():
#value = feature[key]
#print()
if key == "Speckle_ID":
value = str(feature["id"])
feat[key] = value
else:
try: value = feature[key]
except:
rootName = key.split("_")[0]
newF, newVals = traverseDict({}, {}, rootName, feature[rootName][0])
for i, (k,v) in enumerate(newVals.items()):
if k == key: value = v; break
# for all values:
if variant == "TEXT": value = str(value)
if variant == getVariantFromValue(value) and value != "NULL" and value != "None":
feat.update({key: value})
else:
if variant == "TEXT": feat.update({key: None})
if variant == "FLOAT": feat.update({key: None})
if variant == "LONG": feat.update({key: None})
if variant == "SHORT": feat.update({key: None})
print(feat)
return feat
#### setting attributes to feature
feat_updated = updateFeat(feat, fields, feature)
print(feat)
print(fields)
return feat_updated
def addFeatVariant(key, variant, value, f):
feat = f
if variant == "TEXT": value = str(value)
if value != "NULL" and value != "None":
#if key == 'area': print(value); print(type(value)); print(getVariantFromValue(value))
if variant == getVariantFromValue(value) or (variant=="FLOAT" and isinstance(value, int)):
feat.update({key: value})
elif variant == "LONG" and isinstance(value, float): # if object has been modified
feat.update({key: int(value)})
elif variant == "FLOAT" and isinstance(value, int): # if object has been modified
feat.update({key: float(value)})
else: feat.update({key: None})
elif variant == "TEXT" or variant == "FLOAT" or variant == "LONG" or variant == "SHORT": feat.update({key: None})
return feat
def updateFeat(feat:dict, fields: dict, feature: Base) -> Dict[str, Any]:
for key, variant in fields.items():
try:
if key == "Speckle_ID":
value = str(feature["id"])
feat[key] = value
feat = addFeatVariant(key, variant, value, feat)
else:
try:
value = feature[key]
#if key == "area": print(feature[key]); print(type(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])
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])
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.update({key: None})
feat_sorted = {k: v for k, v in sorted(feat.items(), key=lambda item: item[0])}
#print("_________________end of updating a feature_________________________")
return feat_sorted
def rasterFeatureToSpeckle(selectedLayer: arcLayer, projectCRS: arcpy.SpatialReference, project: ArcGISProject) -> Base:
print("_________ Raster feature to speckle______")
# https://pro.arcgis.com/en/pro-app/latest/arcpy/classes/raster-object.htm
r'''
# Save layer file to read symbology
# https://pro.arcgis.com/en/pro-app/latest/tool-reference/data-management/save-to-layer-file.htm
layerFile = project.homeFolder + "\\" + selectedLayer.name.split(".")[0]
arcpy.management.SaveToLayerFile(selectedLayer.name, layerFile, "ABSOLUTE")
# read the file and then delete
f = open(layerFile + ".lyrx", "r")
layerFileContent = json.loads(f.read())
print(layerFileContent)
f.close()
os.remove(layerFile + ".lyrx")
'''
# get Raster object of entire raster dataset
my_raster = arcpy.Raster(selectedLayer.dataSource)
print(my_raster.mdinfo) # None
@@ -181,6 +244,8 @@ def rasterFeatureToSpeckle(selectedLayer: arcLayer, projectCRS: arcpy.SpatialRef
reprojectedPt = rasterOriginPoint
if my_raster.spatialReference.name != projectCRS.name:
reprojectedPt = findTransformation(reprojectedPt, "Point", my_raster.spatialReference, projectCRS, selectedLayer)
if reprojectedPt is None:
reprojectedPt = rasterOriginPoint
geom = pointToSpeckle(reprojectedPt.getPart(), None, None)
if (geom != None):
b['displayValue'] = [geom]
@@ -232,6 +297,8 @@ def rasterFeatureToSpeckle(selectedLayer: arcLayer, projectCRS: arcpy.SpatialRef
else: rasterBandNoDataVal.append(rb.noDataValue)
except: rasterBandNoDataVal.append(rb.noDataValue)
if rasterBandNoDataVal[len(rasterBandNoDataVal)-1] is None:
rasterBandNoDataVal[len(rasterBandNoDataVal)-1] = 'None'
rasterBandVals.append(bandValsFlat)
@@ -255,7 +322,7 @@ def rasterFeatureToSpeckle(selectedLayer: arcLayer, projectCRS: arcpy.SpatialRef
colors = []
count = 0
print(my_raster.variables)
#print(my_raster.variables)
print(selectedLayer.symbology) #None
colorizer = None
#renderer = selectedLayer.symbology.renderer
@@ -264,8 +331,58 @@ def rasterFeatureToSpeckle(selectedLayer: arcLayer, projectCRS: arcpy.SpatialRef
print(colorizer) # <arcpy._colorizer.RasterStretchColorizer object at 0x000001780497FBC8>
print(colorizer.type) # RasterStretchColorizer
else:
#RGB colorizer
root_path = "\\".join(project.filePath.split("\\")[:-1])
if not os.path.exists(root_path + '\\Layers_Speckle\\rasters_Speckle'): os.makedirs(root_path + '\\Layers_Speckle\\rasters_Speckle')
path_style = root_path + '\\Layers_Speckle\\rasters_Speckle\\' + selectedLayer.name + '_temp.lyrx'
symJson = jsonFromLayerStyle(selectedLayer, path_style)
# read from Json
print(symJson["layerDefinitions"][0]["colorizer"])
try: greenBand = symJson["layerDefinitions"][0]["colorizer"]["greenBandIndex"]
except: greenBand = None
try: blueBand = symJson["layerDefinitions"][0]["colorizer"]["blueBandIndex"]
except: blueBand = None
try: redBand = symJson["layerDefinitions"][0]["colorizer"]["redBandIndex"]
except:
if blueBand!=0 and greenBand!=0: redBand= 0
else: redBand = None
print("bands")
print(redBand)
print(greenBand)
print(blueBand)
try:
rbVals = rasterBandVals[redBand] #my_raster.getRasterBands(rasterBandNames[redBand])
rbvalMin = min(rbVals)
rbvalMax = max(rbVals)
rvalRange = float(rbvalMax) - float(rbvalMin)
print(rbvalMin)
print(rbvalMax)
print(rvalRange)
except Exception as e: print(e); rvalRange = None
try:
gbVals = rasterBandVals[greenBand]
gbvalMin = min(gbVals)
gbvalMax = max(gbVals)
gvalRange = float(gbvalMax) - float(gbvalMin)
print(gbvalMin)
print(gbvalMax)
print(gvalRange)
except: gvalRange = None
try:
bbVals = rasterBandVals[blueBand]
bbvalMin = min(bbVals)
bbvalMax = max(bbVals)
bvalRange = float(bbvalMax) - float(bbvalMin)
print(bbvalMin)
print(bbvalMax)
print(bvalRange)
except: bvalRange = None
rendererType = ""
if hasattr(selectedLayer.symbology, 'renderer'): rendererType = selectedLayer.symbology.renderer.type #e.g. SimpleRenderer
#if hasattr(selectedLayer.symbology, 'renderer'): rendererType = selectedLayer.symbology.renderer.type #e.g. SimpleRenderer
# custom color ramp {"type": "algorithmic", "fromColor": [115, 76, 0, 255],"toColor": [255, 25, 86, 255], "algorithm": "esriHSVAlgorithm"}.
# custom color map {'values': [0, 1, 2, 3, 4, 5], 'colors': ['#000000', '#DCFFDF', '#B8FFBE', '#85FF90', '#50FF60','#00AB10']}
@@ -324,7 +441,7 @@ def rasterFeatureToSpeckle(selectedLayer: arcLayer, projectCRS: arcpy.SpatialRef
pt3 = arcpy.PointGeometry(arcpy.Point(extent.XMin+(h+1)*rasterResXY[0], extent.YMax-(v+1)*rasterResXY[1]), my_raster.spatialReference, has_z = True)
pt4 = arcpy.PointGeometry(arcpy.Point(extent.XMin+(h+1)*rasterResXY[0], extent.YMax-v*rasterResXY[1]), my_raster.spatialReference, has_z = True)
# first, get point coordinates with correct position and resolution, then reproject each:
if my_raster.spatialReference.name != projectCRS.name:
if my_raster.spatialReference.exportToString() != projectCRS.exportToString():
pt1 = findTransformation(pt1, "Point", my_raster.spatialReference, projectCRS, selectedLayer)
pt2 = findTransformation(pt2, "Point", my_raster.spatialReference, projectCRS, selectedLayer)
pt3 = findTransformation(pt3, "Point", my_raster.spatialReference, projectCRS, selectedLayer)
@@ -383,34 +500,71 @@ def rasterFeatureToSpeckle(selectedLayer: arcLayer, projectCRS: arcpy.SpatialRef
print(colorizer.groups)
'''
#else:
if colorizer:
try: bandIndex = int(colorizer.band)
except: pass
try:
if rasterBandVals[bandIndex][int(count/4)] >= float(colorizer.minLabel) and rasterBandVals[bandIndex][int(count/4)] <= float(colorizer.maxLabel) : #rasterBandMinVal[bandIndex]:
# REMAP band values to (0,255) range
valRange = float(colorizer.maxLabel) - float(colorizer.minLabel) #(rasterBandMaxVal[bandIndex] - rasterBandMinVal[bandIndex])
colorVal = int( (rasterBandVals[bandIndex][int(count/4)] - float(colorizer.minLabel)) / valRange * 255 )
if colorizer.invertColorRamp is True: colorVal = int( (-rasterBandVals[bandIndex][int(count/4)] + float(colorizer.maxLabel)) / valRange * 255 )
color = (colorVal<<16) + (colorVal<<8) + colorVal
except: # if no Min Max labels:
# REMAP band values to (0,255) range
valRange = max(rasterBandVals[bandIndex]) - min(rasterBandVals[bandIndex]) #(rasterBandMaxVal[bandIndex] - rasterBandMinVal[bandIndex])
colorVal = int( (rasterBandVals[bandIndex][int(count/4)] - min(rasterBandVals[bandIndex])) / valRange * 255 )
color = (colorVal<<16) + (colorVal<<8) + colorVal
else:
# REMAP band values to (0,255) range
rbVals = my_raster.getRasterBands(rasterBandNames[0])
try:
rbvalMin = rbVals.minimum
rbvalMax = rbVals.maximum
except:
rbvalMin = min(rbVals)
rbvalMax = max(rbVals)
if hasattr(selectedLayer.symbology, 'colorizer'): # only 1 band
try: bandIndex = int(colorizer.band) # if stretched
except: pass
if colorizer.type =='RasterUniqueValueColorizer':
# REDO !!!!!!!!!!!!
colorRVal = colorGVal = colorBVal = 0
try:
for br in colorizer.groups:
print(br.heading) #"Value"
# go through all values classified
if br.heading != 'Value': print(int('x')) #call exception
for k, itm in enumerate(br.items):
print(itm.values)
if itm.values[0] == rasterBandVals[bandIndex][int(count/4)]:
print(itm.values[0])
colorRVal, colorGVal, colorBVal = itm.color['RGB'][0], itm.color['RGB'][1], itm.color['RGB'][2]
break
# if string covering float
try:
if float(itm.values[0]) == float(rasterBandVals[bandIndex][int(count/4)]):
print(itm.values[0])
colorRVal, colorGVal, colorBVal = itm.color['RGB'][0], itm.color['RGB'][1], itm.color['RGB'][2]
break
except Exception as e: print(e); pass
valRange = float(rbvalMax) - float(rbvalMin) #(rasterBandMaxVal[bandIndex] - rasterBandMinVal[bandIndex])
colorVal = int( (rasterBandVals[bandIndex][int(count/4)] - float(rbvalMin)) / valRange * 255 )
color = (colorVal<<16) + (colorVal<<8) + colorVal
except Exception as e: # if no Min Max labels:
# REMAP band values to (0,255) range
print(e)
valRange = max(rasterBandVals[bandIndex]) - min(rasterBandVals[bandIndex]) #(rasterBandMaxVal[bandIndex] - rasterBandMinVal[bandIndex])
colorRVal = colorGVal = colorBVal = int( (rasterBandVals[bandIndex][int(count/4)] - min(rasterBandVals[bandIndex])) / valRange * 255 )
print("__pixel color_")
print(colorRVal)
print(colorGVal)
print(colorBVal)
color = (colorRVal<<16) + (colorGVal<<8) + colorBVal
else:
try:
if rasterBandVals[bandIndex][int(count/4)] >= float(colorizer.minLabel) and rasterBandVals[bandIndex][int(count/4)] <= float(colorizer.maxLabel) : #rasterBandMinVal[bandIndex]:
# REMAP band values to (0,255) range
valRange = float(colorizer.maxLabel) - float(colorizer.minLabel) #(rasterBandMaxVal[bandIndex] - rasterBandMinVal[bandIndex])
colorVal = int( (rasterBandVals[bandIndex][int(count/4)] - float(colorizer.minLabel)) / valRange * 255 )
if colorizer.invertColorRamp is True: colorVal = int( (-rasterBandVals[bandIndex][int(count/4)] + float(colorizer.maxLabel)) / valRange * 255 )
color = (colorVal<<16) + (colorVal<<8) + colorVal
except: # if no Min Max labels:
# REMAP band values to (0,255) range
valRange = max(rasterBandVals[bandIndex]) - min(rasterBandVals[bandIndex]) #(rasterBandMaxVal[bandIndex] - rasterBandMinVal[bandIndex])
colorVal = int( (rasterBandVals[bandIndex][int(count/4)] - min(rasterBandVals[bandIndex])) / valRange * 255 )
color = (colorVal<<16) + (colorVal<<8) + colorVal
else: # rgb
# REMAP band values to (0,255) range
if rvalRange is not None and redBand is not None: colorRVal = int( (rasterBandVals[redBand][int(count/4)] - float(rbvalMin)) / rvalRange * 255 )
else: colorRVal = 0
if gvalRange is not None and greenBand is not None: colorGVal = int( (rasterBandVals[greenBand][int(count/4)] - float(gbvalMin)) / gvalRange * 255 )
else: colorGVal = 0
if bvalRange is not None and blueBand is not None: colorBVal = int( (rasterBandVals[blueBand][int(count/4)] - float(bbvalMin)) / bvalRange * 255 )
else: colorBVal = 0
print("__pixel color_")
print(colorRVal)
print(colorGVal)
print(colorBVal)
color = (colorRVal<<16) + (colorGVal<<8) + colorBVal
colors.extend([color,color,color,color])
count += 4
@@ -0,0 +1,606 @@
import json
from typing import Any, List, Union
import copy
import os
from typing import Dict
import arcpy
from arcpy._mp import ArcGISProject
from arcpy.management import (CreateFeatureclass, MakeFeatureLayer,
AddFields, AlterField, DefineProjection )
from specklepy.objects import Base
try:
from speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
except:
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
def jsonFromLayerStyle(layerArcgis, path_style):
# write updated renderer to file and get layerStyle variable
arcpy.management.SaveToLayerFile(layerArcgis, path_style, False)
f = open(path_style, "r")
layerStyle = json.loads(f.read())
f.close()
os.remove(path_style)
return layerStyle
def symbol_color_to_speckle(color: dict):
newColor = (0<<16) + (0<<8) + 0
try:
r = int(color['RGB'][0])
g = int(color['RGB'][1])
b = int(color['RGB'][2])
newColor = (r<<16) + (g<<8) + b
except: pass
return newColor
def vectorRendererToNative(project: ArcGISProject, active_map, layerGroup, layerSpeckle: Union[Layer, VectorLayer], layerArcgis, f_class, existingAttrs: List) -> Union[None, Dict[str, Any]] :
print("___________APPLY VECTOR RENDERER______________")
print(layerArcgis)
print(f_class)
renderer = layerSpeckle.renderer
if renderer and renderer['type']:
print(renderer['type'])
root_path = "\\".join(project.filePath.split("\\")[:-1])
#path_style = root_path + '\\' + str(f_class).split('\\')[-1] + '_old.lyrx'
data = arcpy.Describe(layerArcgis.dataSource)
if layerArcgis.isFeatureLayer:
geomType = data.shapeType
sym = layerArcgis.symbology
if renderer['type'] == 'singleSymbol':
print("RENDERER SINGLE")
print(renderer)
r,g,b = get_rgb_from_speckle(renderer['properties']['symbol']['symbColor'])
#print(r,g,b)
#print(sym.renderer.symbol.color)
sym.renderer.symbol.color = {'RGB': [r, g, b, 100]}
#print(sym.renderer.symbol.color)
layerArcgis.symbology = sym # SimpleRenderer
#print(layerArcgis)
return layerArcgis
elif renderer['type'] == 'categorizedSymbol':
print("RENDERER CATEGORIZED")
print(renderer)
cats = renderer['properties']['categories']
attribute = renderer['properties']['attribute']
if attribute not in existingAttrs: return layerArcgis
#vl2 = active_map.addLayer(layerArcgis)[0]
#sym = layerArcgis.symbology
sym.updateRenderer('UniqueValueRenderer')
print(sym.renderer.type)
print(existingAttrs)
print(attribute)
sym.renderer.fields = [attribute]
for k, grp in enumerate(sym.renderer.groups):
for itm in grp.items:
transVal = itm.values[0][0] #Grab the first "percent" value in the list of potential values
for i in range(len(cats)):
label = cats[i]['value']
if label is None or label=="" or str(label)=="": label = "<Null>"
r,g,b = get_rgb_from_speckle(cats[i]['symbColor'])
if str(transVal) == label:
itm.symbol.color = {'RGB': [r, g, b, 100]}
itm.label = label
break
layerArcgis.symbology = sym
return layerArcgis
elif renderer['type'] == 'graduatedSymbol':
print("RENDERER GRADUATED")
print(renderer)
attribute = renderer['properties']['attribute']
gradMetod = renderer['properties']['gradMethod'] # by color or by size
if gradMetod != 0:
r,g,b = get_rgb_from_speckle(renderer['properties']['sourceSymbColor'] )
sym.renderer.symbol.color = {'RGB': [r, g, b, 100]}
layerArcgis.symbology = sym # SimpleRenderer
return layerArcgis
if attribute not in existingAttrs or gradMetod != 0: return layerArcgis # by color, not line width
sym.updateRenderer('GraduatedColorsRenderer')
print(sym.renderer.type)
r,g,b = get_rgb_from_speckle(renderer['properties']['sourceSymbColor'])
ramp = renderer['properties']['ramp'] # {discrete, rampType, stops}
ranges = renderer['properties']['ranges'] # []
# get all existing values
all_values = []
with arcpy.da.UpdateCursor(f_class, attribute) as cur:
for rowShape in cur:
all_values.append(rowShape[0])
print(all_values)
del cur
print(len(ranges))
sym.renderer.classificationField = attribute
print(sym.renderer.breakCount)
sym.renderer.breakCount = len(ranges)
print(sym.renderer.breakCount)
if len(sym.renderer.classBreaks) > 0:
totalClasses = 0
for k, br in enumerate(ranges):
print(totalClasses)
if sym.renderer.breakCount < len(ranges):
valFits = 0
# check if any existing value fits in this range:
for val in all_values:
if val <= ranges[k]["upper"] and (totalClasses==0 or (totalClasses>0 and sym.renderer.classBreaks[totalClasses-1].upperBound<val)):
valFits+=1
break
if valFits == 0: continue
r,g,b = get_rgb_from_speckle(ranges[k]['symbColor'])
#classBreak.upperBound = ranges[k]["upper"]
sym.renderer.classBreaks[totalClasses].upperBound = ranges[k]["upper"]
sym.renderer.classBreaks[totalClasses].label = ranges[k]["label"]
sym.renderer.classBreaks[totalClasses].symbol.color = {'RGB': [r, g, b, 100]}
totalClasses += 1
#layerArcgis.symbology = sym
print(ranges[k]["label"])
print(ranges[k]["upper"])
sym.renderer.classBreaks[0].upperBound = ranges[0]["upper"] # otherwise its assigned maximum value
layerArcgis.symbology = sym
return layerArcgis
else: return None
def get_rgb_from_speckle(rgb: int) -> tuple[int, int, int]:
r = g = b = 0
try:
r = (rgb & 0xFF0000) >> 16
g = (rgb & 0xFF00) >> 8
b = rgb & 0xFF
except: r = g = b = 0
r,g,b = check_rgb(r,g,b)
return r,g,b
def check_rgb(r:int, g:int, b:int) -> tuple[int, int, int]:
if not isinstance(r, int) or r<0 or r>255: r=g=b=0
if not isinstance(g, int) or g<0 or g>255: r=g=b=0
if not isinstance(b, int) or b<0 or b>255: r=g=b=0
return r,g,b
def rasterRendererToNative(project: ArcGISProject, active_map, layerGroup, layer: RasterLayer, arcLayer, rasterPathsToMerge, newName):
print("_____rasterRenderer ToNative______")
renderer = layer.renderer
rendererNew = None
print(renderer)
feat = layer.features[0]
print(feat)
bandNames = feat["Band names"]
print(bandNames)
sym = arcLayer.symbology
symJson = None
path_style = ""
path_style2 = ""
print(sym)
if renderer and renderer['type']:
if not hasattr(arcLayer.symbology, 'colorizer'):
# multiband raster, CIMRasterRGBColorizer
# arcpy doesnt support multiband raster symbology: https://community.esri.com/t5/arcgis-api-for-python-questions/why-does-arcpy-mp-arcgis-pro-2-6-mosaic-dataset/td-p/1016312
root_path = "\\".join(project.filePath.split("\\")[:-1])
if not os.path.exists(root_path + '\\Layers_Speckle\\rasters_Speckle'): os.makedirs(root_path + '\\Layers_Speckle\\rasters_Speckle')
path_style = root_path + '\\Layers_Speckle\\rasters_Speckle\\' + newName + '_old.lyrx'
path_style2 = root_path + '\\Layers_Speckle\\rasters_Speckle\\' + newName + '_new.lyrx'
symJson = jsonFromLayerStyle(arcLayer, path_style)
if renderer['type'] == 'singlebandgray':
print("Singleband grey")
band_index = renderer['properties']['band']-1
if symJson is None:
sym.updateColorizer('RasterStretchColorizer')
sym.colorizer.band = band_index
arcLayer.symbology = sym
else:
temp = arcpy.management.MakeRasterLayer(rasterPathsToMerge[band_index], newName + "_temp").getOutput(0)
active_map.addLayerToGroup(layerGroup, temp)
temp_layer = None
for l in active_map.listLayers():
if l.longName == layerGroup.longName + "\\" + newName + "_temp":
print(l.longName)
temp_layer = l
break
sym = temp_layer.symbology
sym.updateColorizer('RasterStretchColorizer')
sym.colorizer.band = band_index
arcLayer.symbology = sym
active_map.removeLayer(temp_layer)
elif renderer['type'] == 'multibandcolor':
print("Multiband")
if symJson is None:
sym.updateColorizer('RasterStretchColorizer')
arcLayer.symbology = sym
else:
redSt = copy.deepcopy(symJson["layerDefinitions"][0]["colorizer"]["stretchStatsRed"])
greenSt = copy.deepcopy(symJson["layerDefinitions"][0]["colorizer"]["stretchStatsGreen"])
blueSt = copy.deepcopy(symJson["layerDefinitions"][0]["colorizer"]["stretchStatsBlue"])
redBand = renderer['properties']['redBand']
greenBand = renderer['properties']['greenBand']
blueBand = renderer['properties']['blueBand']
try: symJson["layerDefinitions"][0]["colorizer"]["greenBandIndex"] = greenBand-1
except: symJson["layerDefinitions"][0]["colorizer"]["greenBandIndex"] = 0
try: symJson["layerDefinitions"][0]["colorizer"]["redBandIndex"] = redBand-1
except: symJson["layerDefinitions"][0]["colorizer"]["redBandIndex"] = 0
try: symJson["layerDefinitions"][0]["colorizer"]["blueBandIndex"] = blueBand-1
except: symJson["layerDefinitions"][0]["colorizer"]["blueBandIndex"] = 0
print(symJson)
f = open(path_style2, "w")
f.write(json.dumps(symJson, indent=2))
f.close()
active_map.removeLayer(arcLayer)
lyrFile = arcpy.mp.LayerFile(path_style2)
active_map.addLayerToGroup(layerGroup, lyrFile )
os.remove(path_style2)
elif renderer['type'] == 'paletted':
print("Paletted")
band_index = renderer['properties']['band']-1
if symJson is None:
for br in sym.colorizer.groups:
print(br.heading) #"Value"
# go through all values classified
for k, itm in enumerate(br.items):
if k< len(renderer['properties']['classes']):
#go through saved renderer classes
for n, cl in enumerate(renderer['properties']['classes']):
if k == n:
r,g,b = get_rgb_from_speckle(cl['color'])
itm.color = {'RGB': [r,g,b, 100]}
itm.label = cl['label']
itm.values = cl['value']
else: pass
arcLayer.symbology = sym
else:
sym.updateColorizer('RasterStretchColorizer')
arcLayer.symbology = sym
return arcLayer
def rendererToSpeckle(project: ArcGISProject, active_map, arcLayer, rasterFeat: Base):
print("_____rasterRenderer To Speckle______")
if arcLayer.isRasterLayer:
try:
rType = arcLayer.symbology.colorizer.type # 'singleSymbol','categorizedSymbol','graduatedSymbol',
if rType =='RasterStretchColorizer': rType = 'singlebandgray'
elif rType =='RasterUniqueValueColorizer': rType = 'paletted' # only for 1-band raster
else: rType = 'singlebandgray'
except:
rType = "multibandcolor"
root_path = "\\".join(project.filePath.split("\\")[:-1])
if not os.path.exists(root_path + '\\Layers_Speckle\\rasters_Speckle'): os.makedirs(root_path + '\\Layers_Speckle\\rasters_Speckle')
path_style = root_path + '\\Layers_Speckle\\rasters_Speckle\\' + arcLayer.name + '_temp.lyrx'
#path_style2 = root_path + '\\' + newName + '_new.lyrx'
symJson = jsonFromLayerStyle(arcLayer, path_style)
layerRenderer: Dict[str, Any] = {}
layerRenderer['type'] = rType
print(rType)
my_raster = arcpy.Raster(arcLayer.dataSource)
rasterBandNames = my_raster.bandNames
#bandNames = rasterFeat["Band names"]
bandValues = [rasterFeat["@(10000)" + name + "_values"] for name in rasterBandNames]
if rType == "singlebandgray":
try: band = arcLayer.symbology.colorizer.band
except: band = 0
try:
bVals = bandValues[band]
bvalMin = min(bVals)
bvalMax = max(bVals)
except:
bvalMin = 0
bvalMax = 255
layerRenderer.update({'properties': {'max':bvalMax,'min':bvalMin,'band':band+1,'contrast':1}})
elif rType == "multibandcolor":
try: greenBand = symJson["layerDefinitions"][0]["colorizer"]["greenBandIndex"] +1
except: greenBand = None
try: blueBand = symJson["layerDefinitions"][0]["colorizer"]["blueBandIndex"] +1
except: blueBand = None
try: redBand = symJson["layerDefinitions"][0]["colorizer"]["redBandIndex"] +1
except:
print(greenBand)
print(blueBand)
if blueBand!=1 and greenBand!=1: redBand= 1
else: redBand = None
print(redBand)
try:
rbVals = bandValues[redBand-1]
rbvalMin = min(rbVals)
rbvalMax = max(rbVals)
print(rbvalMin)
print(rbvalMax)
except:
rbvalMin = 0
rbvalMax = 255
try:
gbVals = bandValues[greenBand-1]
gbvalMin = min(gbVals)
gbvalMax = max(gbVals)
except:
gbvalMin = 0
gbvalMax = 255
try:
bbVals = bandValues[blueBand-1]
bbvalMin = min(bbVals)
bbvalMax = max(bbVals)
except:
bbvalMin = 0
bbvalMax = 255
layerRenderer.update({'properties': {'greenBand':greenBand,'blueBand':blueBand,'redBand':redBand}})
layerRenderer['properties'].update({'redContrast':1,'redMin':rbvalMin,'redMax':rbvalMax})
layerRenderer['properties'].update({'greenContrast':1,'greenMin':gbvalMin,'greenMax':gbvalMax})
layerRenderer['properties'].update({'blueContrast':1,'blueMin':bbvalMin,'blueMax':bbvalMax})
elif rType == "paletted":
band = 0
rendererClasses = arcLayer.symbology.colorizer.groups
classes = []
sourceRamp = {}
for i, cl in enumerate(rendererClasses):
if cl.heading == 'Value':
for k, itm in enumerate(cl.items):
value = itm.values[0]
label = itm.label
try:
r,g,b = itm.color['RGB'][0], itm.color['RGB'][1], itm.color['RGB'][2]
sColor = (r<<16) + (g<<8) + b
classes.append({'color':sColor,'value':value,'label':label})
except: pass
layerRenderer.update({'properties': {'classes':classes,'ramp':sourceRamp,'band':band+1}})
return layerRenderer
elif arcLayer.isFeatureLayer:
layerRenderer: Dict[str, Any] = {}
sym = arcLayer.symbology
print(sym.renderer.type)
if sym.renderer.type == 'SimpleRenderer':
layerRenderer['type'] = 'singleSymbol'
layerRenderer['properties'] = {'symbol':{}, 'symbType':""}
symbolColor = symbol_color_to_speckle(sym.renderer.symbol.color)
layerRenderer['properties'].update({'symbol':{'symbColor': symbolColor}, 'symbType':''})
elif sym.renderer.type == 'UniqueValueRenderer':
layerRenderer['type'] = 'categorizedSymbol'
layerRenderer['properties'] = {'attribute': '', 'symbType': ''} #{'symbol':{}, 'ramp':{}, 'ranges':{}, 'gradMethod':"", 'symbType':"", 'legendClassificationAttribute': ""}
attribute = sym.renderer.fields[0]
layerRenderer['properties']['attribute'] = attribute
sourceSymbColor = symbol_color_to_speckle(sym.renderer.defaultSymbol.color)
layerRenderer['properties'].update( {'sourceSymbColor': sourceSymbColor} )
categories = sym.renderer.groups
layerRenderer['properties']['categories'] = []
for i, grp in enumerate(categories):
for itm in grp.items:
value = itm.values[0][0]
symbColor = symbol_color_to_speckle(itm.symbol.color)
label = itm.label
layerRenderer['properties']['categories'].append({'value':value,'symbColor':symbColor,'symbOpacity':1, 'sourceSymbColor': sourceSymbColor,'label':label})
elif sym.renderer.type == 'GraduatedColorsRenderer' or sym.renderer.type == 'GraduatedSymbolsRenderer':
layerRenderer['type'] = 'graduatedSymbol'
layerRenderer['properties'] = {'symbol':{}, 'ramp':{}, 'ranges':{}, 'gradMethod':"", 'symbType':""}
attribute = sym.renderer.classificationField
sourceSymbColor = (0<<16) + (0<<8) + 0
layerRenderer['properties'].update( {'attribute': attribute, 'symbType': '', 'gradMethod': 0, 'sourceSymbColor': sourceSymbColor} )
rRamp = sym.renderer.colorRamp # QgsGradientColorRamp
layerRenderer['properties']['ramp'] = {} # gradientColorRampToSpeckle(rRamp)
rRanges = sym.renderer.classBreaks
layerRenderer['properties']['ranges'] = []
for itm in rRanges:
try: lower = float(itm.label.split(" - ")[0]) if (" - " in rRanges.label) else float(rRanges.label[0])
except: lower = 0
upper = itm.upperBound
symbColor = symbol_color_to_speckle(itm.symbol.color)
label = itm.label
width = 0.26
# {'label': '1 - 1.4', 'lower': 1.0, 'symbColor': <PyQt5.QtGui.QColor ...BD9B9D4A0>, 'symbOpacity': 1.0, 'upper': 1.4}
layerRenderer['properties']['ranges'].append({'lower':lower,'upper':upper,'symbColor':symbColor,'symbOpacity':1,'label':label,'width':width})
elif sym.renderer.type == 'UnclassedColorsRenderer':
layerRenderer['type'] = 'graduatedSymbol'
layerRenderer['properties'] = {'symbol':{}, 'ramp':{}, 'ranges':{}, 'gradMethod':"", 'symbType':""}
attribute = sym.renderer.field
sourceSymbColor = (0<<16) + (0<<8) + 0
layerRenderer['properties'].update( {'attribute': attribute, 'symbType': '', 'gradMethod': 0, 'sourceSymbColor': sourceSymbColor} )
layerRenderer['properties']['ramp'] = {} # gradientColorRampToSpeckle(rRamp)
lowest = sym.renderer.lowerLabel
highest = sym.renderer.upperLabel
# trick to get colors
rRamp = sym.renderer.colorRamp # QgsGradientColorRamp
arcRamp = project.listColorRamps('White to Black')[0]
sym.updateRenderer('GraduatedColorsRenderer')
sym.renderer.colorRamp = arcRamp
sym.renderer.classificationField = attribute
rows_attributes = arcpy.da.SearchCursor(arcLayer.longName, attribute)
row_attrs = []
row_max = -1000000000
row_min = 1000000000
for k, attrs in enumerate(rows_attributes):
row_attrs.append(attrs[0])
if attrs[0] < row_min: row_min = attrs[0]
if attrs[0] > row_max: row_max = attrs[0]
row_range = row_max - row_min
breakCount = len(list(set(row_attrs))) # only unique values
sym.renderer.breakCount = breakCount
# run as gradient colors
rRanges = sym.renderer.classBreaks
layerRenderer['properties']['ranges'] = []
for itm in rRanges:
try: lower = float(itm.label.split(" - ")[0]) if (" - " in rRanges.label) else float(rRanges.label[0])
except: lower = 0
upper = itm.upperBound
if row_range==0: rgb = 0
else: rgb = 255 - int((itm.upperBound - row_min) / row_range * 255 )
symbColor = (rgb<<16) + (rgb<<8) + rgb
label = itm.label
width = 0.26
# {'label': '1 - 1.4', 'lower': 1.0, 'symbColor': <PyQt5.QtGui.QColor ...BD9B9D4A0>, 'symbOpacity': 1.0, 'upper': 1.4}
layerRenderer['properties']['ranges'].append({'lower':lower,'upper':upper,'symbColor':symbColor,'symbOpacity':1,'label':label,'width':width})
else: return None
return layerRenderer
else: return None
def featureColorfromNativeRenderer(index: int, arcLayer) -> int:
# case with one color for the entire layer
#try:
sym = arcLayer.symbology
color = {'RGB': [100,100,100,100]}
if sym.renderer.type == 'SimpleRenderer':
print('SimpleRenderer')
color = sym.renderer.symbol.color
elif sym.renderer.type == 'UniqueValueRenderer':
print('Unique Value Renderer')
attribute = sym.renderer.fields[0]
color = sym.renderer.defaultSymbol.color
categories = sym.renderer.groups
rows_attributes = arcpy.da.SearchCursor(arcLayer.longName, attribute)
row_shapes_list = [x for k, x in enumerate(rows_attributes)]
color_found = 0
for i, grp in enumerate(categories):
if color_found == 1: break
for n, itm in enumerate(grp.items):
for k, attrs in enumerate(row_shapes_list):
if str(itm.values[0][0]) == "<Null>": itm.values[0][0] = None
if k == index and ( str(attrs[0]) == str(itm.values[0][0]) or (attrs[0] is None and str(itm.values[0][0]) == "<Null>") ):
color = itm.symbol.color
print("symbol color: ")
print(color)
color_found = 1
break
elif sym.renderer.type == 'GraduatedColorsRenderer' or sym.renderer.type == 'GraduatedSymbolsRenderer':
print('Graduated Colors / Sybmols Renderer')
attribute = sym.renderer.classificationField
rows_attributes = arcpy.da.SearchCursor(arcLayer.longName, attribute)
row_shapes_list = [x for k, x in enumerate(rows_attributes)]
rRanges = sym.renderer.classBreaks
upperBounds = [-10000000000000000000]
color_found = 0
for itm in rRanges:
print(itm)
if color_found == 1: break
for k, attrs in enumerate(row_shapes_list):
try:
if k == index and float(attrs[0]) <= float(itm.upperBound) and (k==0 or float(attrs[0]) > float(upperBounds[-1]) ):
color = itm.symbol.color
color_found = 1
break
except: pass
upperBounds.append(itm.upperBound)
elif sym.renderer.type == 'UnclassedColorsRenderer':
print('UnclassedColorsRenderer')
attribute = sym.renderer.field
sym.updateRenderer('GraduatedColorsRenderer')
sym.renderer.classificationField = attribute
rows_attributes = arcpy.da.SearchCursor(arcLayer.longName, attribute)
row_shapes_list = [x for k, x in enumerate(rows_attributes)]
row_attrs = []
row_max = -10000000000000000000
row_min = 10000000000000000000
feat_value = None
for k, attrs in enumerate(row_shapes_list):
row_attrs.append(attrs[0])
if attrs[0] < row_min: row_min = attrs[0]
if attrs[0] > row_max: row_max = attrs[0]
if k == index: feat_value = attrs[0]
row_range = row_max - row_min
breakCount = len(list(set(row_attrs))) # only unique values
sym.renderer.breakCount = breakCount
# run as gradient colors
upperBounds = [-10000000000000000000]
rRanges = sym.renderer.classBreaks
for itm in rRanges:
print(itm)
try:
if row_range!=0 and float(feat_value) <= float(itm.upperBound) and (len(upperBounds)==0 or float(feat_value) > float(upperBounds[-1])):
rgb = 255 - int((itm.upperBound - row_min) / row_range * 255 )
color = {'RGB':[rgb,rgb,rgb,100]}
print(color)
break
except: pass
upperBounds.append(itm.upperBound)
else:
print('Else')
return (100<<16) + (100<<8) + 100
print("final color: ")
print(color)
# construct RGB color
col = symbol_color_to_speckle(color)
print(col)
return col
@@ -6,95 +6,126 @@ import arcpy
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
import os
ATTRS_REMOVE = ['geometry','applicationId','bbox','displayStyle', 'id', 'renderMaterial', 'displayMesh', 'displayValue']
def getVariantFromValue(value: Any) -> Union[str, None]:
#print("_________get variant from value_______")
# TODO add Base object
pairs = [
(str, "TEXT"), # 10
(float, "FLOAT"),
(int, "LONG"),
(bool, "SHORT")
#date: "SHORT"
(bool, "SHORT")
]
res = None
for p in pairs:
if isinstance(value, p[0]): res = p[1]; break
#t = type(value)
#try: res = pairs[t]
#except: pass
#if isinstance(value, str) and "PyQt5.QtCore.QDate(" in value: res = QVariant.Date #14
#elif isinstance(value, str) and "PyQt5.QtCore.QDateTime(" in value: res = QVariant.DateTime #16
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
res = "FLOAT"
except Exception as e: print(e)
break
return res
def getLayerAttributes(features: List[Base], attrsToRemove: List[str] =['geometry','applicationId','bbox','displayStyle', 'id', 'renderMaterial', 'geometry'] ) -> dict:
def getLayerAttributes(featuresList: List[Base], attrsToRemove: List[str] = ATTRS_REMOVE ) -> Dict[str, str]:
print("03________ get layer attributes")
if not isinstance(featuresList, list): features = [featuresList]
else: features = featuresList[:]
fields = {}
all_props = []
for feature in features:
#get object properties to add as attributes
dynamicProps = feature.get_dynamic_member_names()
#attrsToRemove = ['geometry','applicationId','bbox','displayStyle', 'id', 'renderMaterial', 'geometry']
for att in attrsToRemove:
try: dynamicProps.remove(att)
except: pass
dynamicProps.sort()
print(dynamicProps)
# 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
# go thought the dictionary object
print("go thought the dictionary object")
if value and isinstance(value, list) and isinstance(value[0], dict) :
all_props.remove(name) # remove generic dict name
newF, newVals = traverseDict( {}, {}, name, value[0])
#print(newF)
#print(newF.items())
for i, (k,v) in enumerate(newF.items()):
fields.update({k: v})
if k not in all_props: all_props.append(k)
#print(fields)
# add a field if not existing yet AND if variant is known
elif variant and (name not in fields.keys()):
fields.update({name: variant})
elif name in fields.keys(): #check if the field was empty previously:
oldVariant = fields[name]
# replace if new one is NOT LongLong or IS String
if oldVariant != "TEXT" and variant == "TEXT":
fields.update({name: variant})
#print(all_props)
if value and isinstance(value, list):
#all_props.remove(name) # remove generic dict name
for i, val_item in enumerate(value):
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:
oldVariant = fields[k]
# replace if new one is NOT Float (too large integers)
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)
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":
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)
# 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"})
print(fields)
return fields
fields.update({name: 'TEXT'})
fields_sorted = {k: v for k, v in sorted(fields.items(), key=lambda item: item[0])}
return fields_sorted
def traverseDict(newF: dict, newVals: dict, nam: str, val: Any):
#print("Traverse Dict")
if isinstance(val, dict):
for i, (k,v) in enumerate(val.items()):
traverseDict( newF, newVals, nam+"_"+k, v)
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
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)
else:
var = getVariantFromValue(val)
if not var: var = None #LongLong #4
else:
newF.update({nam: var})
newVals.update({nam: val})
#print(newF)
#print(newVals)
#print("traverse end")
if var is None:
var = 'TEXT'
val = str(val)
#print(var)
newF.update({nam: var})
newVals.update({nam: val})
return newF, newVals
def get_scale_factor(units: str) -> float:
@@ -123,46 +154,52 @@ def findTransformation(f_shape, geomType, layer_sr: arcpy.SpatialReference, proj
#apply transformation if needed
if layer_sr.name != projectCRS.name:
tr0 = tr1 = tr2 = tr_custom = None
transformations = arcpy.ListTransformations(layer_sr, projectCRS)
customTransformName = "layer_sr.name"+"_To_"+ projectCRS.name
if len(transformations) == 0:
midSr = arcpy.SpatialReference("WGS 1984") # GCS_WGS_1984
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)
tr_custom = customTransformName
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) )
selecterTr.update({tr: diff})
selecterTr = dict(sorted(selecterTr.items(), key=lambda item: item[1]))
tr0 = list(selecterTr.keys())[0]
print(layer_sr)
try:
transformations = arcpy.ListTransformations(layer_sr, projectCRS)
customTransformName = "layer_sr.name"+"_To_"+ projectCRS.name
if len(transformations) == 0:
midSr = arcpy.SpatialReference("WGS 1984") # GCS_WGS_1984
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)
tr_custom = customTransformName
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) )
selecterTr.update({tr: diff})
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":
try: arcpy.AddWarning("Unsupported or invalid geometry in layer " + selectedLayer.name)
except: arcpy.AddWarning("Unsupported or invalid geometry")
if geomType != "Point" and geomType != "Polyline" and geomType != "Polygon" and geomType != "Multipoint":
try: arcpy.AddWarning("Unsupported or invalid geometry in layer " + selectedLayer.name)
except: arcpy.AddWarning("Unsupported or invalid geometry")
# reproject geometry using chosen transformstion(s)
if tr0 is not None:
ptgeo1 = f_shape.projectAs(projectCRS, tr0)
f_shape = ptgeo1
elif tr1 is not None and tr2 is not None:
ptgeo1 = f_shape.projectAs(midSr, tr1)
ptgeo2 = ptgeo1.projectAs(projectCRS, tr2)
f_shape = ptgeo2
else:
ptgeo1 = f_shape.projectAs(projectCRS)
f_shape = ptgeo1
# reproject geometry using chosen transformstion(s)
if tr0 is not None:
ptgeo1 = f_shape.projectAs(projectCRS, tr0)
f_shape = ptgeo1
elif tr1 is not None and tr2 is not None:
ptgeo1 = f_shape.projectAs(midSr, tr1)
ptgeo2 = ptgeo1.projectAs(projectCRS, tr2)
f_shape = ptgeo2
else:
ptgeo1 = f_shape.projectAs(projectCRS)
f_shape = ptgeo1
except:
arcpy.AddWarning(f"Spatial Transformation not found for layer {selectedLayer.name}")
return None
return f_shape
@@ -252,12 +289,12 @@ def curvedFeatureClassToSegments(layer) -> str:
print(newPath)
return newPath
def validate_path(path):
def validate_path(path: str):
# https://github.com/EsriOceans/btm/commit/a9c0529485c9b0baa78c1f094372c0f9d83c0aaf
"""If our path contains a DB name, make sure we have a valid DB name and not a standard file name."""
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 == '':
# a relative path only, relying on the workspace
@@ -0,0 +1,73 @@
#python speckle_toolbox\esri\toolboxes\speckle\plugin_utils\testing_from_file.py
import arcpy
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
import json
import os
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
from arcpy.management import (CreateFeatureclass, MakeFeatureLayer,
AddFields, AlterField, DefineProjection )
##################################################### get example layers from the project #######
path = r'C:\Users\katri\Documents\ArcGIS\Projects\MyProject\MyProject.gdb'
arcpy.env.workspace = path
project = ArcGISProject(path.replace("gdb","aprx"))
active_map = project.listMaps()[0] #.activeMap
all_layers = []
layerPolygon = None
layerPolyline = None
layerPoint = None
layerMultiPoint = None
#get layer of interest
for layer in active_map.listLayers():
if layer.isFeatureLayer or layer.isRasterLayer:
all_layers.append(layer)
data = arcpy.Describe(layer.dataSource)
if layer.isFeatureLayer:
geomType = data.shapeType
if geomType == "Polygon" and layerPolygon is None: layerPolygon = layer
if geomType == "Polyline" and layerPolyline is None: layerPolyline = layer
if geomType == "Point" and layerPoint is None: layerPoint = layer
if geomType == "Multipoint" and layerMultiPoint is None: layerMultiPoint = layer
root_path = "\\".join(project.filePath.split("\\")[:-1])
#path_style = root_path + '\\layer_speckle_symbology.lyrx'
path_style2 = root_path + '\\layer_speckle_symbology2.lyrx'
for layer in active_map.listLayers():
if layer.longName == layerPolygon.longName:
layerPolygon = layer
break
print(layerPolygon.dataSource)
r'''
source = str(layerPolygon.dataSource).split('\\')
layerPolygon = arcpy.ApplySymbologyFromLayer_management(
in_layer= str(layerPolygon.dataSource),
in_symbology_layer=path_style2,
update_symbology='UPDATE')[0]
'''
#vl2 = MakeFeatureLayer(layerPolygon.dataSource, 'someName').getOutput(0)
#active_map.addLayer(arcpy.mp.LayerFile(path_style2))
################## reset symbology if needed:
sym = layerPolygon.symbology
print(sym.renderer.type)
sym.updateRenderer('UniqueValueRenderer')
print(sym.renderer.type)
layerPolygon.symbology = sym
print(sym.renderer.type)
r'''
sym = layerPolygon.symbology
print(sym.renderer.type)
sym.updateRenderer('UniqueValueRenderer')
layerPolygon.symbology = sym
print(sym.updateRenderer('UniqueValueRenderer'))
print(layerPolygon.symbology.renderer.type)
# SimpleRenderer, GraduatedColorsRenderer, GraduatedSymbolsRenderer, UnclassedColorsRenderer, UniqueValueRenderer
'''
project.save()
@@ -0,0 +1,13 @@
import arcpy
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
import json
import os
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
from arcpy.management import (CreateFeatureclass, MakeFeatureLayer,
AddFields, AlterField, DefineProjection )
import unittest
@@ -10,15 +10,26 @@ import arcpy
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
from arcpy import metadata as md
from specklepy.api.models import Branch, Stream, Streams
from speckle.converter.layers.Layer import Layer, RasterLayer
from speckle.converter.layers._init_ import convertSelectedLayers, layerToNative, cadLayerToNative
try:
from speckle.converter.layers.Layer import Layer, RasterLayer
from speckle.converter.layers._init_ import convertSelectedLayers, layerToNative, cadLayerToNative, bimLayerToNative
from speckle.ui.project_vars import toolboxInputsClass, speckleInputsClass
from speckle.converter.layers.emptyLayerTemplates import createGroupLayer
from speckle.converter.layers.Layer import VectorLayer
except:
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.Layer import Layer, RasterLayer
from speckle_toolbox.esri.toolboxes.speckle.converter.layers._init_ import convertSelectedLayers, layerToNative, cadLayerToNative, bimLayerToNative
from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import toolboxInputsClass, speckleInputsClass
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.emptyLayerTemplates import createGroupLayer
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.Layer import VectorLayer
from arcgis.features import FeatureLayer
import os
import os.path
import sys
import specklepy
from specklepy.api.models import Branch, Stream, Streams
from specklepy.transports.server.server import ServerTransport
from specklepy.api.credentials import get_local_accounts
from specklepy.api.client import SpeckleClient
@@ -33,10 +44,6 @@ from specklepy.api.wrapper import StreamWrapper
from specklepy.objects import Base
from specklepy.logging import metrics
from speckle.ui.project_vars import toolboxInputsClass, speckleInputsClass
from speckle.converter.layers.emptyLayerTemplates import createGroupLayer
from speckle.converter.layers.Layer import VectorLayer
#'''
def traverseObject(
@@ -78,13 +85,18 @@ class Toolbox:
self.alias = "speckle_toolbox_"
# List of tool classes associated with this toolbox
self.tools = [Speckle]
metrics.set_host_app("ArcGIS")
try:
version = arcpy.GetInstallInfo()['Version']
python_version = f"python {'.'.join(map(str, sys.version_info[:2]))}"
metrics.set_host_app("ArcGIS", ', '.join([version, python_version]))
except:
metrics.set_host_app("ArcGIS")
# https://pro.arcgis.com/en/pro-app/2.8/arcpy/mapping/alphabeticallistofclasses.htm#except: print("something happened")
class Speckle:
def __init__(self):
#print("________________reset_______________")
def __init__(self):
print("__________________INIT SPECKLE TOOL_________")
self.label = "Speckle"
self.description = "Allows you to send and receive your layers " + \
"to/from other software using Speckle server."
@@ -92,39 +104,28 @@ class Speckle:
self.toRefresh = False
self.speckleInputs = None
self.toolboxInputs = None
#print("ping_Speckle1")
#print(speckleInputsClass.instances)
total = len(speckleInputsClass.instances)
#print(total)
print(total)
for i in range(total):
#print(i)
#print(speckleInputsClass.instances[total-i-1])
if speckleInputsClass.instances[total-i-1] is not None:
try:
#print(speckleInputsClass.instances[total-i-1].streams_default)
y = speckleInputsClass.instances[total-i-1].streams_default # in case not initialized properly
y = speckleInputsClass.instances[total-i-1].streams_default
#if not isinstance(speckleInputsClass.instances[total-i-1].streams_default, SpeckleException): # also will throw exception in case not initialized properly
self.speckleInputs = speckleInputsClass.instances[total-i-1] # take latest (first in reverted list)
#print("FOUND INSTANCE")
break
except: pass
#print(self.speckleInputs)
if self.speckleInputs is None: self.speckleInputs = speckleInputsClass()
if self.speckleInputs is None or isinstance(self.speckleInputs.streams_default, SpeckleException): self.speckleInputs = speckleInputsClass()
#print(self.speckleInputs.streams_default)
print(len(speckleInputsClass.instances))
#print(toolboxInputsClass.instances)
#print("TOTAL = ...................")
total = len(toolboxInputsClass.instances)
#print(total)
for i in range(total):
if toolboxInputsClass.instances[total-i-1] is not None:
self.toolboxInputs = toolboxInputsClass.instances[total-i-1] # take latest (first in reverted list)
#print("FOUND INSTANCE")
break
#print(self.toolboxInputs)
if self.toolboxInputs is None: self.toolboxInputs = toolboxInputsClass()
#print("ping_Speckle2")
def getParameterInfo(self):
#data types: https://pro.arcgis.com/en/pro-app/2.8/arcpy/geoprocessing_and_python/defining-parameter-data-types-in-a-python-toolbox.htm
@@ -139,19 +140,21 @@ class Speckle:
name="streamsDefalut",
datatype="GPString",
parameterType="Optional",
#category="Sending data",
direction="Input",
category=cat1
)
streamsDefalut.filter.type = 'ValueList'
streamsDefalut.filter.list = [ (st.name + " - " + st.id) for st in self.speckleInputs.streams_default ]
if isinstance(self.speckleInputs.streams_default, SpeckleException):
arcpy.AddError("Speckle account not accessible")
streamsDefalut.filter.list = []
else:
streamsDefalut.filter.list = [ (st.name + " - " + st.id) for st in self.speckleInputs.streams_default ]
addDefStreams = arcpy.Parameter(
displayName="Add",
name="addDefStreams",
datatype="GPBoolean",
parameterType="Optional",
#category="Sending data",
direction="Input",
category=cat1
)
@@ -218,7 +221,6 @@ class Speckle:
parameterType="Required",
direction="Input",
multiValue=False,
#category=cat2
)
savedStreams.filter.list = [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}" for i,stream in enumerate(self.speckleInputs.saved_streams)]
@@ -236,9 +238,7 @@ class Speckle:
name="branch",
datatype="GPString",
parameterType="Required",
#category="Sending data",
direction="Input",
#category=cat2
)
branch.value = ""
branch.filter.type = 'ValueList'
@@ -248,21 +248,18 @@ class Speckle:
name="commit",
datatype="GPString",
parameterType="Optional",
#category="Sending data",
direction="Input",
#category=cat2
)
commit.value = ""
commit.filter.type = 'ValueList'
msg = arcpy.Parameter(
displayName="Message",
name="message",
name="msg",
datatype="GPString",
parameterType="Optional",
direction="Input",
multiValue=False,
#category=cat2
)
msg.value = ""
@@ -273,7 +270,6 @@ class Speckle:
parameterType="Optional",
direction="Input",
multiValue=True,
#category=cat2
)
selectedLayers.filter.list = [str(i) + "-" + l.longName for i,l in enumerate(self.speckleInputs.all_layers)] #"Polyline"
@@ -283,13 +279,10 @@ class Speckle:
name="action",
datatype="GPString",
parameterType="Required",
#category="Sending data",
direction="Input",
multiValue=False,
#category=cat2
)
action.value = "Send"
#action.filter.type = 'ValueList'
action.filter.list = ["Send", "Receive"]
refresh = arcpy.Parameter(
@@ -298,8 +291,7 @@ class Speckle:
datatype="GPBoolean",
parameterType="Optional",
direction="Input"
)
#refresh.filter.type = "ValueList"
)
refresh.value = False
parameters = [streamsDefalut, addDefStreams, streamUrl, addUrlStreams, lat, lon, setLatLon, savedStreams, removeStream, branch, commit, selectedLayers, msg, action, refresh]
@@ -317,11 +309,11 @@ class Speckle:
if par.name == "addDefStreams" and par.altered and par.value == True:
for p in parameters:
if p.name == "streamsDefalut" and p.valueAsText is not None:
# add value from streamsDefault to saved streams
selected_stream_name = p.valueAsText[:]
#print(selected_stream_name)
for stream in self.speckleInputs.streams_default:
#print(stream)
if stream.name == selected_stream_name.split(" - ")[0]:
print("_____Add from list___")
wr = StreamWrapper(f"{self.speckleInputs.account.serverInfo.url}/streams/{stream.id}?u={self.speckleInputs.account.userInfo.id}")
@@ -347,7 +339,8 @@ class Speckle:
steamId = query
try: steamId = query.split("/streams/")[1].split("/")[0]
except: pass
# quesry stream, add to saved
# query stream, add to saved
stream = self.speckleInputs.speckle_client.stream.get(steamId)
if isinstance(stream, Stream):
print("_____Add by URL___")
@@ -371,11 +364,9 @@ class Speckle:
for p in parameters:
if p.name == "savedStreams" and p.valueAsText is not None:
# get value from savedStreams
# get value from savedStreams
selected_stream_name = p.valueAsText[:]
#print(selected_stream_name)
for streamTup in self.speckleInputs.saved_streams:
#print(stream)
stream = streamTup[1]
if stream.name == selected_stream_name.split(" - ")[0]:
print("_____Remove stream___")
@@ -398,12 +389,13 @@ class Speckle:
for p in parameters:
if p.name == "lat" and p.valueAsText is not None:
# add value from the UI to saved lat
lat = p.valueAsText[:].replace(",","").replace(" ","").replace(";","").replace("-","").replace("_","")
lat = p.valueAsText[:].replace(",","").replace(" ","").replace(";","").replace("_","")
try: lat = float(lat)
except: lat = 0; p.value = "0.0"
if p.name == "lon" and p.valueAsText is not None:
# add value from the UI to saved lat
lon = p.valueAsText[:].replace(",","").replace(" ","").replace(";","").replace("-","").replace("_","")
lon = p.valueAsText[:].replace(",","").replace(" ","").replace(";","").replace("_","")
try: lon = float(lon)
except: lon = 0; p.value = "0.0"
coords = [lat, lon]
@@ -415,12 +407,13 @@ class Speckle:
if par.name == "savedStreams" and par.altered:
# Search for the stream by name
if par.value is not None and "Stream not accessible" not in par.valueAsText[:]:
#print("SAVED STREAMS - selection")
selected_stream_name = par.valueAsText[:]
self.toolboxInputs.active_stream = None
self.toolboxInputs.active_stream_wrapper = None
for st in self.speckleInputs.saved_streams:
if st[1].name == selected_stream_name.split(" - ")[0]:
self.toolboxInputs.active_stream = st[1]
self.toolboxInputs.active_stream_wrapper = st[0]
break
# edit branches: globals and UI
@@ -449,7 +442,6 @@ class Speckle:
p.value = None
self.toolboxInputs.active_commit = None
else: par.value = None
#print(self.toolboxInputs.action)
if par.name == "branch" and par.altered: # branches
if par.value is not None:
@@ -488,22 +480,14 @@ class Speckle:
if par.value is not None:
self.toolboxInputs.selected_layers = par.values
#print("selected layers changed")
#print(self.toolboxInputs.action)
#print(self.toolboxInputs.selected_layers)
if par.name == "msg" and par.altered and par.valueAsText is not None:
self.toolboxInputs.messageSpeckle = par.valueAsText
print(self.toolboxInputs.messageSpeckle)
if par.name == "action" and par.altered:
#print("action changed")
#print(par.valueAsText)
if par.valueAsText == "Send": self.toolboxInputs.action = 1
else: self.toolboxInputs.action = 0
#print(self.toolboxInputs.action)
#print(self.toolboxInputs.selected_layers)
if par.name == "refresh" and par.altered: # refresh btn
if par.value == True:
self.refresh(parameters)
@@ -512,9 +496,6 @@ class Speckle:
self.toRefresh = False
print("____________________________parameters___________________________")
#[print(str(x.name) + " - " + str(x.valueAsText)) for x in parameters]
#[x.clearMessage() for x in parameters] # https://pro.arcgis.com/en/pro-app/latest/arcpy/geoprocessing_and_python/programming-a-toolvalidator-class.htm
#[print(x.valueAsText) for x in parameters]
return
def refresh(self, parameters: List[Any]):
@@ -535,11 +516,14 @@ class Speckle:
if par.name == "lat": par.value = str(self.toolboxInputs.get_survey_point()[0])
if par.name == "lon": par.value = str(self.toolboxInputs.get_survey_point()[1])
if par.name == "streamsDefalut": par.filter.list = [ (st.name + " - " + st.id) for st in self.speckleInputs.streams_default ]
if par.name == "streamsDefalut":
if isinstance(self.speckleInputs.streams_default, SpeckleException):
arcpy.AddError("Speckle account not accessible")
par.filter.list = []
else:
par.filter.list = [ (st.name + " - " + st.id) for st in self.speckleInputs.streams_default ]
if par.name == "savedStreams":
#print("par.name")
saved_streams = self.speckleInputs.getProjectStreams()
#print(saved_streams)
par.filter.list = [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}" for i,stream in enumerate(saved_streams)]
if par.name == "selectedLayers": par.filter.list = [str(i) + "-" + l.longName for i,l in enumerate(self.speckleInputs.all_layers)]
@@ -576,14 +560,13 @@ class Speckle:
print("______________SEND_______________")
#if self.validateStreamBranch(parameters) == False: return
if len(self.toolboxInputs.selected_layers) == 0:
arcpy.AddError("No layers selected for sending")
return
streamId = self.toolboxInputs.active_stream.id #stream_id
client = self.speckleInputs.speckle_client # ?
client = self.toolboxInputs.active_stream_wrapper.get_client()
#client = self.speckleInputs.speckle_client # ?
# Get the stream wrapper
#streamWrapper = StreamWrapper(None)
@@ -602,6 +585,9 @@ class Speckle:
base_obj = Base(units = "m")
base_obj.layers = convertSelectedLayers(self.speckleInputs.all_layers, self.toolboxInputs.selected_layers, self.speckleInputs.project)
if len(base_obj.layers) == 0:
arcpy.AddMessage("No data sent to stream " + streamId)
return
try:
# this serialises the block and sends it to the transport
objId = operations.send(base=base_obj, transports=[transport])
@@ -614,7 +600,9 @@ class Speckle:
message = self.toolboxInputs.messageSpeckle
print(message)
if message is None or ( isinstance(message, str) and len(message) == 0): message = "Sent from ArcGIS"
print(message)
try:
# you can now create a commit on your stream with this object
client.commit.create(
@@ -636,7 +624,8 @@ class Speckle:
try:
streamId = self.toolboxInputs.active_stream.id #stream_id
client = self.speckleInputs.speckle_client #
client = self.toolboxInputs.active_stream_wrapper.get_client()
#client = self.speckleInputs.speckle_client #
except SpeckleWarning as warning:
arcpy.AddWarning(str(warning.args[0]))
@@ -668,11 +657,13 @@ class Speckle:
except:
arcpy.AddError("Make sure your account has access to the chosen stream")
return
try:
#print(commit)
objId = commit.referencedObject
commitDetailed = client.commit.get(streamId, commit.id)
if isinstance(commitDetailed, GraphQLException):
arcpy.AddError("Access error")
return
app = commitDetailed.sourceApplication
if objId is None:
return
@@ -686,6 +677,8 @@ class Speckle:
# Clear 'latest' group
streamBranch = streamId + "_" + self.toolboxInputs.active_branch.name + "_" + str(commit.id)
streamBranch = streamBranch.replace("[","_").replace("]","_").replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
newGroupName = f'{streamBranch}'
groupExists = 0
@@ -744,6 +737,8 @@ class Speckle:
except: pass
def loopVal(value: Any, name: str): # "name" is the parent object/property/layer name
if name.endswith('/displayValue'): return
if isinstance(value, Base):
try: # dont go through parts of Speckle Geometry object
print("objects to loop through: " + value.speckle_type)
@@ -754,18 +749,29 @@ class Speckle:
if isinstance(value, List):
for item in value:
loopVal(item, name)
#print(item)
print(item)
pt = None
if item.speckle_type and item.speckle_type.startswith("Objects.Geometry."):
pt, pl = cadLayerToNative(value, name, streamBranch, self.speckleInputs.project)
if pt is not None: print("Layer group created: " + pt.name())
if pl is not None: print("Layer group created: " + pl.name())
break
if item.speckle_type and (item.speckle_type.startswith("Objects.BuiltElements.") or item.speckle_type.startswith("Objects.Structural.Geometry")): # and "Revit" in item.speckle_type
print("__receiving structures__")
msh_bool = bimLayerToNative(value, name, streamBranch, self.speckleInputs.project)
#if msh is not None: print("Layer group created: " + msh.name())
break
traverseObject(commitObj, callback, check)
except SpeckleException as e:
print("Receive failed")
except (SpeckleException, GraphQLException) as e:
print("Receive failed: " + str(e))
arcpy.AddError("Receive failed: " + str(e))
return
print("received")
#self.updateParameters(parameters, True)
#self.refresh(parameters)
@@ -11,7 +11,6 @@ from specklepy.logging.exceptions import (
GraphQLException,
SpeckleException,
)
#from specklepy.api.credentials import StreamWrapper
from specklepy.api.wrapper import StreamWrapper
from osgeo import osr
@@ -21,18 +20,24 @@ class speckleInputsClass:
instances = []
accounts = get_local_accounts()
account = None
streams_default: None or Streams = None
streams_default: None or List[Stream] = None
project = None
active_map = None
saved_streams: List[Optional[Tuple[StreamWrapper, Stream]]] = []
stream_file_path: str = ""
all_layers: List[arcLayer] = []
clients: List[SpeckleClient] = []
for acc in accounts:
if acc.isDefault:
account = acc
break
if acc.isDefault: account = acc
new_client = SpeckleClient(
acc.serverInfo.url,
acc.serverInfo.url.startswith("https")
)
new_client.authenticate_with_token(token=acc.token)
clients.append(new_client)
speckle_client = None
if account:
speckle_client = SpeckleClient(
@@ -48,12 +53,14 @@ class speckleInputsClass:
try:
aprx = ArcGISProject('CURRENT')
self.project = aprx
# following will fail if no project found
self.active_map = aprx.activeMap
if self.active_map is not None and isinstance(self.active_map, Map): # if project loaded
for layer in self.active_map.listLayers():
#print(layer)
if layer.isFeatureLayer or layer.isRasterLayer: self.all_layers.append(layer) #type: 'arcpy._mp.Layer'
try: geomType = arcpy.Describe(layer.dataSource).shapeType.lower()
except: geomType = '' #print(arcpy.Describe(layer.dataSource)) #and arcpy.Describe(layer.dataSource).shapeType.lower() != "multipatch")
if (layer.isFeatureLayer and geomType != "multipatch") or layer.isRasterLayer: self.all_layers.append(layer) #type: 'arcpy._mp.Layer'
self.stream_file_path: str = aprx.filePath.replace("aprx","gdb") + "\\speckle_streams.txt"
if os.path.exists(self.stream_file_path):
@@ -92,7 +99,7 @@ class speckleInputsClass:
streamExists = 0
index = 0
try:
print(url)
#print(url)
sw = StreamWrapper(url)
stream = self.tryGetStream(sw)
@@ -110,27 +117,27 @@ class speckleInputsClass:
else: return []
def tryGetStream (self,sw: StreamWrapper) -> Stream:
#print("Try get streams")
steamId = sw.stream_id
try: steamId = sw.stream_id.split("/streams/")[1].split("/")[0]
except: pass
client = sw.get_client()
stream = client.stream.get(steamId)
if isinstance(stream, GraphQLException):
raise SpeckleException(stream.errors[0]['message'])
return stream
if isinstance(sw, StreamWrapper):
steamId = sw.stream_id
try: steamId = sw.stream_id.split("/streams/")[1].split("/")[0]
except: pass
client = sw.get_client()
stream = client.stream.get(steamId)
if isinstance(stream, GraphQLException):
raise SpeckleException(stream.errors[0]['message'])
return stream
else:
raise SpeckleException('Invalid StreamWrapper provided')
class toolboxInputsClass:
#def __init__(self):
print("CREATING UI inputs first time________")
# self.instances.append(self)
instances = []
lat: float = 0.0
lon: float = 0.0
active_stream: Optional[Stream] = None
active_stream_wrapper: Optional[StreamWrapper] = None
active_branch: Optional[Branch] = None
active_commit = None
selected_layers: List[Any] = []
@@ -157,15 +164,14 @@ class toolboxInputsClass:
try:
aprx = ArcGISProject('CURRENT')
self.project = aprx
except: self.project = None; print("Project not found")
except: self.project = None; print("Project not found"); arcpy.AddWarning("Project not found")
self.instances.append(self)
def setProjectStreams(self, wr: StreamWrapper, add = True):
# ERROR 032659 Error queueing metrics request:
# Cannot parse into a stream wrapper class - invalid URL provided.
print("SET proj streams")
if os.path.exists(self.stream_file_path):
if os.path.exists(self.stream_file_path) and ".gdb\\speckle_streams.txt" in self.stream_file_path:
new_content = ""
@@ -184,7 +190,7 @@ class toolboxInputsClass:
f.write(new_content)
f.close()
elif len(self.stream_file_path) >10:
elif ".gdb\\speckle_streams.txt" in self.stream_file_path:
f = open(self.stream_file_path, "x")
f.close()
f = open(self.stream_file_path, "w")
@@ -196,65 +202,75 @@ class toolboxInputsClass:
print("get survey point")
x = y = 0
if not content:
content = self.stream_file_path
try:
f = open(self.stream_file_path, "r")
content = f.read()
f.close()
except: pass
content = None
if os.path.exists(self.stream_file_path) and ".gdb\\speckle_streams.txt" in self.stream_file_path:
try:
f = open(self.stream_file_path, "r")
content = f.read()
f.close()
except: pass
if content:
temp = []
for i, coords in enumerate(content.split(",")):
if "speckle_sr_origin_" in coords:
try:
x, y = [float(c) for c in coords.replace("speckle_sr_origin_","").split(";")]
except: pass
return (x, y)
return (x, y)
def set_survey_point(self, coords: List[float]):
# from widget (2 strings) to local vars + update SR of the map
print("SET survey point")
pt = "speckle_sr_origin_" + str(coords[0]) + ";" + str(coords[1])
if os.path.exists(self.stream_file_path):
if len(coords) == 2:
pt = "speckle_sr_origin_" + str(coords[0]) + ";" + str(coords[1])
if os.path.exists(self.stream_file_path) and ".gdb\\speckle_streams.txt" in self.stream_file_path:
new_content = ""
f = open(self.stream_file_path, "r")
existing_content = f.read()
f.close()
new_content = ""
f = open(self.stream_file_path, "r")
existing_content = f.read()
f.close()
f = open(self.stream_file_path, "w")
if pt in existing_content:
new_content = existing_content.replace( pt , "")
else:
new_content = existing_content
f = open(self.stream_file_path, "w")
if pt in existing_content:
new_content = existing_content.replace( pt , "")
else:
new_content = existing_content
new_content += pt + "," # add point
f.write(new_content)
f.close()
elif ".gdb\\speckle_streams.txt" in self.stream_file_path:
f = open(self.stream_file_path, "x")
f.close()
f = open(self.stream_file_path, "w")
f.write(pt + ",")
f.close()
new_content += pt + "," # add point
f.write(new_content)
f.close()
elif len(self.stream_file_path) >10:
f = open(self.stream_file_path, "x")
f.close()
f = open(self.stream_file_path, "w")
f.write(pt + ",")
f.close()
# save to project; crearte SR
self.lat, self.lon = coords[0], coords[1]
newCrsString = "+proj=tmerc +ellps=WGS84 +datum=WGS84 +units=m +no_defs +lon_0=" + str(self.lon) + " lat_0=" + str(self.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(self.project.activeMap.spatialReference.exportToString())
#transform = osr.CoordinateTransformation(source, newCrs)
self.project.activeMap.spatialReference = newProjSR
arcpy.AddMessage("Custom project CRS successfully applied")
else:
arcpy.AddWarning("Custom CRS could not be created")
# save to project; crearte SR
self.lat, self.lon = coords[0], coords[1]
newCrsString = "+proj=tmerc +ellps=WGS84 +datum=WGS84 +units=m +no_defs +lon_0=" + str(self.lon) + " lat_0=" + str(self.lat) + " +x_0=0 +y_0=0 +k_0=1"
newCrs = osr.SpatialReference()
newCrs.ImportFromProj4(newCrsString)
print(newCrs.ExportToWkt())
validate = True if len(newCrs.ExportToWkt())>10 else False
if validate:
newProjSR = arcpy.SpatialReference()
newProjSR.loadFromString(newCrs.ExportToWkt())
self.project.activeMap.spatialReference = newProjSR
arcpy.AddWarning("Custom project CRS successfully applied")
else:
arcpy.AddWarning("Custom CRS could not be created")
arcpy.AddWarning("Custom CRS could not be created: not enough coordinates provided")
return True
+85
View File
@@ -0,0 +1,85 @@
from speckle_toolbox.esri.toolboxes.speckle.speckle_arcgis import Toolbox, Speckle # The code to test
from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import speckleInputsClass, toolboxInputsClass
import arcpy
import os
from specklepy.api.wrapper import StreamWrapper
from specklepy.api.models import Branch, Stream, Streams
from specklepy.logging.exceptions import GraphQLException, SpeckleException
from specklepy.api.credentials import Account
import unittest # The test framework
class Test_InitializingClasses(unittest.TestCase):
def setUp(self) -> None:
self.toolbox_input = toolboxInputsClass()
self.speckle_input = speckleInputsClass()
self.toolbox = Toolbox()
self.speckleTool = Speckle()
self.test_stream = "https://speckle.xyz/streams/17b0b76d13"
def text_all_toolbox(self):
self.assertTrue(isinstance(self.toolbox.tools[0], Speckle))
def test_toolbox_inputs(self):
self.assertEqual(self.toolbox_input.lat, 0)
self.assertEqual(self.toolbox_input.lon, 0)
self.assertIsNone(self.toolbox_input.active_stream)
self.assertIsNone(self.toolbox_input.active_branch)
self.assertIsNone(self.toolbox_input.active_commit)
self.assertEqual(len(self.toolbox_input.selected_layers), 0)
self.assertEqual(self.toolbox_input.messageSpeckle, "")
self.assertEqual(self.toolbox_input.action, 1)
self.assertIsNone(self.toolbox_input.project)
self.assertEqual(self.toolbox_input.stream_file_path, "")
def test_toolbox_inputs_functions(self):
self.toolbox_input.setProjectStreams(StreamWrapper(self.test_stream))
if os.path.exists(self.toolbox_input.stream_file_path):
f = open(self.toolbox_input.stream_file_path, "r")
existing_content = f.read()
f.close()
self.assertTrue(isinstance(existing_content, str))
self.toolbox_input.setProjectStreams(None)
if os.path.exists(self.toolbox_input.stream_file_path):
f = open(self.toolbox_input.stream_file_path, "r")
existing_content = f.read()
f.close()
self.assertTrue(isinstance(existing_content, str))
self.assertTrue( isinstance(self.toolbox_input.get_survey_point(), tuple))
self.assertTrue( isinstance(self.toolbox_input.get_survey_point()[0], float) or isinstance(self.toolbox_input.get_survey_point()[0], int))
self.assertTrue( isinstance(self.toolbox_input.get_survey_point()[1], float) or isinstance(self.toolbox_input.get_survey_point()[1], int))
self.assertTrue( self.toolbox_input.set_survey_point )
def test_speckle_inputs(self):
self.assertTrue(isinstance(self.speckle_input.accounts, list))
self.assertTrue(self.speckle_input.account is None or isinstance(self.speckle_input.account, Account))
self.assertTrue(self.speckle_input.streams_default is None or isinstance(self.speckle_input.streams_default, list))
self.assertIsNone(self.speckle_input.project)
self.assertIsNone(self.speckle_input.active_map)
self.assertEqual(self.speckle_input.stream_file_path, "")
self.assertTrue(isinstance(self.speckle_input.saved_streams, list))
self.assertTrue(isinstance(self.speckle_input.all_layers, list))
self.assertTrue(isinstance(self.speckle_input.clients, list))
def test_speckle_inputs_functions(self):
self.assertTrue(isinstance(self.speckle_input.getProjectStreams(), list))
getStreams = self.speckle_input.getProjectStreams(self.test_stream)
self.assertTrue(isinstance(getStreams[0][0], StreamWrapper) and isinstance(getStreams[0][1], Stream))
self.assertTrue(isinstance(self.speckle_input.tryGetStream(StreamWrapper(self.test_stream)), Stream))
self.assertRaises(SpeckleException, lambda: self.speckle_input.tryGetStream(None))
def test_parameters(self):
actual = len(self.speckleTool.getParameterInfo())
expected = 15
self.assertEqual(actual, expected)
if __name__ == '__main__':
unittest.main()