Compare commits
173 Commits
0.2.0
...
2.13.0-rc4
| Author | SHA1 | Date | |
|---|---|---|---|
| 34e0d1cacf | |||
| 3538891247 | |||
| f0fcf4e20f | |||
| ad26a5bbfc | |||
| 26c0b14d84 | |||
| 362f44983e | |||
| ae282bb02b | |||
| 2ea3b64801 | |||
| fe170c9094 | |||
| c1727e9048 | |||
| fda7462577 | |||
| 2071515db0 | |||
| 50f1aa3d79 | |||
| 5a83cb333d | |||
| 54c466ebcf | |||
| 472eb1967c | |||
| 3df8f0ea87 | |||
| 618f529636 | |||
| 3f838e579c | |||
| 52e0f19f0e | |||
| 7bfd81e121 | |||
| 8930984e41 | |||
| 459ca3fdc9 | |||
| a357904247 | |||
| 05aef10054 | |||
| 8c7aad350f | |||
| 4f16b8e38c | |||
| 350df33719 | |||
| 49915857dd | |||
| 8f6d399281 | |||
| da85f29089 | |||
| 04e9740f15 | |||
| 9639fb00ba | |||
| 941cd42ee2 | |||
| 9312364dc9 | |||
| a89e6e398c | |||
| fceeb6a9d7 | |||
| 8a27a3a8e2 | |||
| 2540d05181 | |||
| 4cb57d9631 | |||
| 007e6263a6 | |||
| 4a93b40e8e | |||
| 95c1495977 | |||
| fc07cbf60e | |||
| 07029675e4 | |||
| f24ef49450 | |||
| 52a531e040 | |||
| d3be4f0377 | |||
| 865964249c | |||
| 1d11e702dc | |||
| b454ac543c | |||
| fcbdc9a200 | |||
| 7cbdab0471 | |||
| 464bcf0f61 | |||
| 97c8cebdb4 | |||
| cd3a05103b | |||
| cc11402470 | |||
| ae0f15023c | |||
| 0eed167715 | |||
| f60cd064f3 | |||
| b86799856e | |||
| 14e805cf99 | |||
| 6d35d13f99 | |||
| 5e0ff316f0 | |||
| b22ac2ef17 | |||
| da1ebb04e4 | |||
| 29c4fde0c5 | |||
| e132b16878 | |||
| 2e1dc329b3 | |||
| ee3cc81391 | |||
| 78063bc976 | |||
| 8dcdfbbfc1 | |||
| 221a050df4 | |||
| 67480f2f70 | |||
| 0a24379984 | |||
| 6437be9d25 | |||
| 644b64bbb3 | |||
| 037469966b | |||
| e8d4b8035b | |||
| 8721ae246a | |||
| 261d324ed4 | |||
| 07fe49a1f8 | |||
| 6f90081af7 | |||
| 67911fdb5d | |||
| a9e48db570 | |||
| d284c5415a | |||
| a6ec7b4a0b | |||
| e70debc606 | |||
| a651fbd732 | |||
| 7eba1ba98b | |||
| 4828f5b55e | |||
| 0a5e49fb07 | |||
| b651bfd401 | |||
| 52a28eae3f | |||
| 50c37315f8 | |||
| d9706b4f5f | |||
| aad3be3962 | |||
| 013e9e27d6 | |||
| 3131ba8950 | |||
| 9f8637d670 | |||
| f9f2628d3a | |||
| 22b4672b4a | |||
| b963228da0 | |||
| faa8fb9cfa | |||
| a1de8be5b0 | |||
| bf10c4f00b | |||
| f88a9e3966 | |||
| b4149fa5f7 | |||
| 68d8c1f8f3 | |||
| 8b4fbe88ef | |||
| 67564631d2 | |||
| b3ffdd9d0b | |||
| d7f467c6a6 | |||
| a14550d7ad | |||
| 79b8b363f0 | |||
| d9b1b11036 | |||
| e47d89ce57 | |||
| faadba8e73 | |||
| 18234800dd | |||
| f0213b7186 | |||
| 2949142140 | |||
| 5c9138238a | |||
| 276c78603b | |||
| 323d23cfaf | |||
| ab8805d5f2 | |||
| 8b0ee41ed7 | |||
| 92ce4c6abb | |||
| 9596e14c2d | |||
| cb5240c4c0 | |||
| edc686526a | |||
| ed97d83468 | |||
| 5b86638749 | |||
| 1e308bf1ab | |||
| 971d71fc7e | |||
| 821acf93d8 | |||
| 0f4494936e | |||
| 5791667bfe | |||
| 364c8fe507 | |||
| 791dd045c3 | |||
| 999c67ea8d | |||
| 772ed3334d | |||
| 3b9188dabe | |||
| 895eded593 | |||
| fa4908ed1a | |||
| 3cf079acaf | |||
| 6d6df92d42 | |||
| 4fcd408759 | |||
| 7bf8957989 | |||
| e409e6d578 | |||
| 98cfc35cbc | |||
| 1ba8aaaa15 | |||
| 1f95fbd169 | |||
| 42f9f8c815 | |||
| 019f67ffbc | |||
| 3f6a21f65e | |||
| 3ce685f77a | |||
| be8996ebaa | |||
| 03d8a20a44 | |||
| 057cbb8cc5 | |||
| a68fdbc999 | |||
| be565cd1fa | |||
| ebde9a77ca | |||
| 0d2e62786d | |||
| f6b478c978 | |||
| c774ea167c | |||
| d9f3bfe143 | |||
| 5371788b46 | |||
| 69b771924a | |||
| 06a30beda5 | |||
| 6ec5594d1c | |||
| b216fc31b3 | |||
| c73db1f0e3 | |||
| 9364ccc1e0 |
@@ -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:
|
||||
@@ -42,14 +55,38 @@ jobs:
|
||||
root: ./
|
||||
paths:
|
||||
- speckle-sharp-ci-tools/Installers
|
||||
- speckle_arcgis_installer
|
||||
publish-github-release:
|
||||
docker:
|
||||
- image: cimg/go:1.20.0
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- run:
|
||||
name: "Publish Release on GitHub"
|
||||
command: |
|
||||
set -x
|
||||
go install github.com/tcnksm/ghr@v0.16.0
|
||||
VERSION="${CIRCLE_TAG:-0.0.0}"
|
||||
VERSION_SHORT=$(echo "${VERSION}" | cut -d- -f1)
|
||||
ghr -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete "${VERSION}" "./speckle_arcgis_installer/speckle_toolbox-${VERSION_SHORT}-py3-none-any.whl"
|
||||
|
||||
get-ci-tools: # Clones our ci tools and persists them to the workspace
|
||||
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 +119,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 +133,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 +156,25 @@ workflows: #happens with every PR to main
|
||||
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
||||
- deploy-manager2:
|
||||
slug: arcgis
|
||||
os: Win
|
||||
extension: exe
|
||||
context: innosetup
|
||||
- publish-github-release:
|
||||
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: /.*/
|
||||
context: arcgis-github-release
|
||||
- deploy-manager2:
|
||||
slug: arcgis
|
||||
os: Win
|
||||
extension: exe
|
||||
requires:
|
||||
- build-deploy-connector-win
|
||||
filters:
|
||||
tags:
|
||||
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?$/
|
||||
branches:
|
||||
ignore: /.*/ # For testing only! /ci\/.*/
|
||||
context: do-spaces-speckle-releases
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -118,4 +118,6 @@ scratch.py
|
||||
settings.json
|
||||
**/.DS_Store
|
||||
zip_build
|
||||
.qt_for_python
|
||||
.qt_for_python
|
||||
*.pyt.xml
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ What is Speckle? Check our , Blender and more!
|
||||
- **Built for the AEC industry:** Speckle connectors are plugins for the most common software used in the industry such as Revit, Rhino, Grasshopper, AutoCAD, Civil 3D, Excel, Unreal Engine, Unity, QGIS, ArcGIS (you are here), Blender and more!
|
||||
|
||||
### Try Speckle now!
|
||||
|
||||
@@ -44,7 +44,7 @@ Give Speckle a try in no time by:
|
||||
|
||||
## Repo Structure
|
||||
|
||||
This repo contains the QGIS plugin for Speckle 2.0. It is written in `python` and uses our fantastic [Python SDK](https://github.com/specklesystems/speckle-py). The [Speckle Server](https://github.com/specklesystems/Server) is providing all the web-facing functionality and can be found [here](https://github.com/specklesystems/Server).
|
||||
This repo contains the ArcGIS plugin for Speckle 2.0. It is written in `python` and uses our fantastic [Python SDK](https://github.com/specklesystems/speckle-py). The [Speckle Server](https://github.com/specklesystems/Server) is providing all the web-facing functionality and can be found [here](https://github.com/specklesystems/Server).
|
||||
|
||||
> **Try it out!!**
|
||||
> Although we're still in early development stages, we encourage you to try out the latest stable release.
|
||||
@@ -52,7 +52,7 @@ This repo contains the QGIS plugin for Speckle 2.0. It is written in `python` an
|
||||
>
|
||||
> **What can it do?**
|
||||
>
|
||||
> Currently, the plugin allows to send data from a single layer to a Speckle server using one of the accounts configured on your computer. It will extract all the features of that layer along side their properties and, when possible, geometry too.
|
||||
> Currently, the plugin allows to receive the data from Speckle and send data from a AcrGIS Pro layers to a Speckle server using one of the accounts configured on your computer. It will extract all the features of that layer along side their properties.
|
||||
> The following geometry types are supported for now:
|
||||
>
|
||||
> - Point
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -4,16 +4,52 @@ import sys
|
||||
def patch_installer(tag):
|
||||
"""Patches the installer with the correct connector version and specklepy version"""
|
||||
iss_file = "speckle-sharp-ci-tools/arcgis.iss"
|
||||
setup_whl_file = "setup.py"
|
||||
conda_file = "speckle_arcgis_installer/conda_clone_activate.py"
|
||||
#toolbox_install_file = "speckle_arcgis_installer/toolbox_install.py"
|
||||
toolbox_manual_install_file = "speckle_arcgis_installer/toolbox_install_manual.py"
|
||||
|
||||
#py_tag = get_specklepy_version()
|
||||
with open(iss_file, "r") as file:
|
||||
lines = file.readlines()
|
||||
lines.insert(12, f'#define AppVersion "{tag.split("-")[0]}"\n')
|
||||
lines.insert(13, f'#define AppInfoVersion "{tag}"\n')
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if "#define AppVersion " in line:
|
||||
lines[i] = f'#define AppVersion "{tag.split("-")[0]}"\n'
|
||||
if "#define AppInfoVersion " in line:
|
||||
lines[i] = f'#define AppInfoVersion "{tag}"\n'
|
||||
with open(iss_file, "w") as file:
|
||||
file.writelines(lines)
|
||||
print(f"Patched installer with connector v{tag} and specklepy ")
|
||||
file.close()
|
||||
|
||||
with open(setup_whl_file, "r") as file:
|
||||
lines = file.readlines()
|
||||
for i, line in enumerate(lines):
|
||||
if "version=" in line:
|
||||
lines[i] = f'\t\t\tversion="{tag.split("-")[0]}",\n'
|
||||
break
|
||||
with open(setup_whl_file, "w") as file:
|
||||
file.writelines(lines)
|
||||
print(f"Patched whl setup with connector v{tag} and specklepy ")
|
||||
file.close()
|
||||
|
||||
def whlFileRename(fileName: str):
|
||||
with open(fileName, "r") as file:
|
||||
lines = file.readlines()
|
||||
for i, line in enumerate(lines):
|
||||
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]
|
||||
lines[i] = p1+"-"+p2+"-py3-none-any.whl"+p3
|
||||
with open(fileName, "w") as file:
|
||||
file.writelines(lines)
|
||||
print(f"Patched toolbox_installer with connector v{tag} and specklepy ")
|
||||
file.close()
|
||||
|
||||
whlFileRename(conda_file)
|
||||
#whlFileRename(toolbox_install_file)
|
||||
whlFileRename(toolbox_manual_install_file)
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
specklepy==2.9.1
|
||||
panda3d==1.10.11
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
print("Hello")
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
|
||||
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, SelectLayerByAttribute, GetCount )
|
||||
|
||||
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
|
||||
|
||||
################################ select/ clear selection ###########################
|
||||
for layer in project.activeMap.listLayers():
|
||||
if (layer.isFeatureLayer) or layer.isRasterLayer:
|
||||
arcpy.SelectLayerByAttribute_management(layer.longName,"ADD_TO_SELECTION",'"OBJECTID" = 1')
|
||||
print(arcpy.GetCount_management(layer.longName).getOutput(0))
|
||||
arcpy.SelectLayerByAttribute_management(layer.longName, "CLEAR_SELECTION")
|
||||
|
||||
################## 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.symbology 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)
|
||||
|
||||
@@ -1,27 +1,35 @@
|
||||
# to build an installer: run cmd from this folder:
|
||||
# "%PROGRAMFILES%\\ArcGIS\\Pro\\bin\\Python\\envs\\arcgispro-py3\\python.exe" C:\\Users\\username\\Documents\\00_Speckle\\GitHub\\speckle-arcgis\\setup.py sdist bdist_wheel
|
||||
# to build an installer: run cmd from this folder or use terminal: "%PROGRAMFILES%\\ArcGIS\\Pro\\bin\\Python\\envs\\arcgispro-py3\\python.exe"
|
||||
#
|
||||
# 1) python patch_version.py 2.x.x
|
||||
# 2) python setup.py sdist bdist_wheel #C:\\Users\\username\\Documents\\00_Speckle\\GitHub\\speckle-arcgis\\setup.py sdist bdist_wheel
|
||||
# copy .whl from "dist" to "speckle_arcgis_installer"
|
||||
|
||||
# ref: https://pro.arcgis.com/en/pro-app/2.8/arcpy/geoprocessing_and_python/distributing-python-modules.htm
|
||||
|
||||
import os
|
||||
from setuptools import setup
|
||||
|
||||
# https://pro.arcgis.com/en/pro-app/2.8/arcpy/geoprocessing_and_python/distributing-python-modules.htm
|
||||
|
||||
def read(fname):
|
||||
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
||||
|
||||
setup(name='speckle_toolbox',
|
||||
version='0.1',
|
||||
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',
|
||||
setup_requires=['wheel'],
|
||||
packages=['speckle_toolbox'],
|
||||
package_data={'speckle_toolbox':['esri/toolboxes/*',
|
||||
package_data={'speckle_toolbox':[
|
||||
'esri/arcpy/*',
|
||||
'esri/help/gp/*', 'esri/help/gp/toolboxes/*', 'esri/help/gp/messages/*',
|
||||
'esri/toolboxes/*','esri/toolboxes/speckle/*',
|
||||
'esri/toolboxes/speckle/converter/*', 'esri/toolboxes/speckle/converter/geometry/*', 'esri/toolboxes/speckle/converter/layers/*',
|
||||
'esri/toolboxes/speckle/plugin_utils/*']
|
||||
'esri/toolboxes/speckle/plugin_utils/*',
|
||||
'esri/toolboxes/speckle/ui/*']
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
### Manual installation
|
||||
|
||||
1. From the [latest release](https://github.com/specklesystems/speckle-arcgis/releases) download the whl file and the source code zip, unzip and locate the subfolder "speckle_arcgis_installer" on your machine and place whl file in it.
|
||||
2. Clone the default ArcGIS Pro conda environment (or set the one you use, except the default one) and restart ArcGIS Pro
|
||||
- for 2.9.0: Project-> Python-> Manage Environments-> Clone Default
|
||||
- for 3.0.0: Project-> Package Manager-> Active Environment (Environment Manager)-> Clone arcgispro-py3
|
||||
3. Adjust the path to your new environment python executable (variable "pythonPath" in "speckle_arcgis_installer/toolbox_install_manual.py")
|
||||
4. Enter the location of 'toolbox_install_manual.py' in the following command and run this command in ArcGIS Python console (View -> Python Window)
|
||||
|
||||
```python
|
||||
import sysconfig; import subprocess; x = sysconfig.get_paths()['data'] + r"\python.exe"; subprocess.run((x, 'C:\\Users\\pathToFolder\\speckle_arcgis_installer\\toolbox_install_manual.py'), capture_output=True, text=True, shell=True, timeout=1000 )
|
||||
```
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
try: from speckle.speckle_arcgis import *
|
||||
except: from speckle_toolbox.esri.toolboxes.speckle.speckle_arcgis import *
|
||||
@@ -8,50 +8,155 @@ import subprocess
|
||||
from subprocess import CalledProcessError
|
||||
from subprocess_call import subprocess_call
|
||||
|
||||
from msilib.schema import Error
|
||||
import sys
|
||||
|
||||
import arcpy
|
||||
|
||||
def setup():
|
||||
#print(plugin_dir)
|
||||
pythonExec = get_python_path() # import numpy; import os; print(os.path.abspath(numpy.__file__))
|
||||
#print(pythonExec)
|
||||
|
||||
if pythonExec is None: # env is default, need to restart ArcGIS
|
||||
return False
|
||||
return pythonExec # None if not successful
|
||||
|
||||
def get_python_path(): # create a full copy of default env
|
||||
#print("Get Python path")
|
||||
# or: import site; site.getsitepackages()[0]
|
||||
# import specklepy; import os; print(os.path.abspath(specklepy.__file__)) ##currentPythonExec = sysconfig.get_paths()['data'] + r"\python.exe"
|
||||
|
||||
pythonExec = os.environ["ProgramFiles"] + r'\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\python.exe' #(r"%PROGRAMFILES%\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\python.exe") #os.path.dirname(sys.executable) + "\\python.exe" # default python.exe
|
||||
#print(pythonExec)
|
||||
if not os.path.exists(pythonExec):
|
||||
pythonExec = os.getenv('APPDATA').replace("Roaming", "Local") + r'\Programs\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\python.exe'
|
||||
if not os.path.exists(pythonExec): return None
|
||||
#print(os.getenv('APPDATA') + r'\Programs\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\python.exe')
|
||||
|
||||
if sys.platform == "win32":
|
||||
env_new_name = "arcgispro-py3-speckle"
|
||||
#clone_env(pythonExec, env_new_name) # only if doesn't exist yet
|
||||
newExec = clone_env(pythonExec, env_new_name) # only if doesn't exist yet
|
||||
if not os.path.exists(newExec): return None
|
||||
|
||||
activate_env(env_new_name)
|
||||
return pythonExec
|
||||
else: pass
|
||||
return newExec
|
||||
else: return None
|
||||
|
||||
def clone_env(pythonExec_old: str, env_new_name: str):
|
||||
install_folder = os.getenv('APPDATA').replace("\\Roaming","") + r"\Local\ESRI\conda\envs" #r"%LOCALAPPDATA%\ESRI\conda\envs"
|
||||
#print("Clone default ArcGIS Pro conda env")
|
||||
#print(install_folder)
|
||||
#if not os.path.exists(install_folder): os.makedirs(install_folder)
|
||||
if not os.path.exists(install_folder): os.makedirs(install_folder)
|
||||
|
||||
default_env = pythonExec_old.replace("Pro\\bin\\Python\\envs\\arcgispro-py3\\python.exe","Pro\\bin\\Python\\envs\\arcgispro-py3") # + "\\" + 'arcgispro-py3'
|
||||
conda_exe = pythonExec_old.replace("Pro\\bin\\Python\\envs\\arcgispro-py3\\python.exe","Pro\\bin\\Python\\Scripts\\conda.exe") #%PROGRAMFILES%\ArcGIS\Pro\bin\Python\Scripts\conda.exe #base: %PROGRAMDATA%\Anaconda3\condabin\conda.bat
|
||||
new_env = install_folder + "\\" + env_new_name # %LOCALAPPDATA%\ESRI\conda\envs\...
|
||||
|
||||
subprocess_call( [ conda_exe, 'create', '--clone', default_env, '-p', new_env] ) # will not execute if already exists
|
||||
if os.path.exists(conda_exe) and os.path.exists(default_env) and os.path.exists(new_env) and not os.path.exists(new_env + "\\python.exe"):
|
||||
# conda environment invalid: delete it's folder
|
||||
print(f"Removing invalid environment {new_env}")
|
||||
os.remove(new_env)
|
||||
|
||||
if os.path.exists(conda_exe) and os.path.exists(default_env) and not os.path.exists(new_env):
|
||||
print("Wait for the default ArcGIS Pro conda environment to be cloned")
|
||||
subprocess_call( [ conda_exe, 'config', '--set', 'ssl_verify', 'False'] )
|
||||
subprocess_call( [ conda_exe, 'create', '--clone', default_env, '-p', new_env] ) # will not execute if already exists
|
||||
subprocess_call( [ conda_exe, 'config', '--set', 'ssl_verify', 'True'] )
|
||||
|
||||
elif os.path.exists(new_env) and os.path.exists(new_env + "\\python.exe"):
|
||||
print(f"Environment {new_env} already exists, preparing to install packages..")
|
||||
|
||||
print(new_env + "\\python.exe")
|
||||
return new_env + "\\python.exe"
|
||||
|
||||
def activate_env(env_new_name: str):
|
||||
# using Popen, because process does not return result; subprocess.run will hang indefinitely
|
||||
variable = subprocess.Popen((f'proswap {env_new_name}'),stdout = subprocess.PIPE,stderr = subprocess.PIPE,text = True,shell = True)
|
||||
#print(variable)
|
||||
# activate new env : https://support.esri.com/en/technical-article/000024206
|
||||
|
||||
setup()
|
||||
|
||||
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" )
|
||||
print(whl_file)
|
||||
subprocess_call([newExec, '-m','pip','install','--upgrade', '--force-reinstall', whl_file])
|
||||
# to uninstall: cmd.exe "X:\\xxx.whl
|
||||
return
|
||||
|
||||
def clearToolbox(pythonExec: str):
|
||||
# install pip
|
||||
print("CLEAR toolbox")
|
||||
print(pythonExec)
|
||||
try:
|
||||
|
||||
speckle_path = pythonExec.replace("python.exe","Lib\\site-packages\\")
|
||||
|
||||
print(speckle_path)
|
||||
paths = os.listdir(speckle_path)
|
||||
for p in paths:
|
||||
if "speckle_toolbox" in p:
|
||||
print("remove: " + str(p))
|
||||
os.remove(p)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
pass
|
||||
|
||||
def installDependencies(pythonExec: str, pkgName: str, pkgVersion: str):
|
||||
# install pip
|
||||
print(pythonExec)
|
||||
try:
|
||||
import pip
|
||||
except:
|
||||
getPipFilePath = os.path.join(os.path.dirname(__file__), "get_pip.py") #TODO: give actual folder path
|
||||
exec(open(getPipFilePath).read())
|
||||
# just in case the included version is old
|
||||
subprocess_call([pythonExec, "-m", "pip", "install", "--upgrade", "pip"])
|
||||
|
||||
# install package
|
||||
try:
|
||||
#import importlib #importlib.import_module(pkgName)
|
||||
if pkgName == "specklepy":
|
||||
import specklepy
|
||||
if pythonExec.replace("\\python.exe","") not in (os.path.abspath(specklepy.__file__)):
|
||||
print(f"Installing {pkgName} to {pythonExec}")
|
||||
#subprocess_call( [pythonExec, "-m", "pip", "uninstall", f"{pkgName}"])
|
||||
subprocess_call( [pythonExec, "-m", "pip", "install", f"{pkgName}=={pkgVersion}"])
|
||||
elif pkgName == "panda3d":
|
||||
import panda3d
|
||||
if pythonExec.replace("\\python.exe","") not in (os.path.abspath(panda3d.__file__)):
|
||||
print(f"Installing {pkgName} to {pythonExec}")
|
||||
subprocess_call( [pythonExec, "-m", "pip", "install", f"{pkgName}=={pkgVersion}"])
|
||||
elif pkgName == "PyQt5":
|
||||
import PyQt5
|
||||
if pythonExec.replace("\\python.exe","") not in (os.path.abspath(PyQt5.__file__)):
|
||||
print(f"Installing {pkgName} to {pythonExec}")
|
||||
subprocess_call( [pythonExec, "-m", "pip", "install", f"{pkgName}=={pkgVersion}"])
|
||||
except Exception as e:
|
||||
print(f"{pkgName} not installed")
|
||||
subprocess_call( [pythonExec, "-m", "pip", "install", f"{pkgName}=={pkgVersion}"])
|
||||
|
||||
# Check if package needs updating
|
||||
r'''
|
||||
try:
|
||||
print(f"Attempting to update {pkgName} to {pkgVersion}")
|
||||
result = subprocess_call(
|
||||
[
|
||||
pythonExec,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"--upgrade",
|
||||
f"{pkgName}=={pkgVersion}",
|
||||
]
|
||||
)
|
||||
if result == True:
|
||||
print(f"{pkgName} upgraded")
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(e.with_traceback)
|
||||
'''
|
||||
return True
|
||||
|
||||
pythonPath = setup()
|
||||
if pythonPath is not None:
|
||||
clearToolbox(pythonPath)
|
||||
installToolbox(pythonPath)
|
||||
installDependencies(pythonPath, "specklepy", "2.9.0" )
|
||||
installDependencies(pythonPath, "panda3d", "1.10.11" )
|
||||
installDependencies(pythonPath, "PyQt5", "5.15.9" )
|
||||
|
||||
# manual: import sysconfig; import subprocess; x = sysconfig.get_paths()['data'] + r"\python.exe"; subprocess.run((x, "-m", "pip", "install", "PyQt5==5.15.9"), capture_output=True, text=True, shell=True, timeout=1000 )
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
# MANUAL INSTALLATION:
|
||||
# 1. enter correct path to your new environemnt in line 10
|
||||
# 2. enter the location of 'manual_toolbox_install.py' 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:\\...\\manual_toolbox_install.py'), capture_output=True, text=True, shell=True, timeout=1000 )
|
||||
# then restart
|
||||
|
||||
from subprocess_call import subprocess_call
|
||||
import os
|
||||
|
||||
pythonPath = "C:\\ ...\\custom_environment_name\\python.exe"
|
||||
|
||||
def installToolbox(newExec: str):
|
||||
print("Installing Speckle Toolbox")
|
||||
whl_file = os.path.join(os.path.dirname(__file__), "foo-0.1-py3-none-any.whl" )
|
||||
subprocess_call([newExec, '-m','pip','install','--upgrade', '--force-reinstall', whl_file])
|
||||
return
|
||||
|
||||
def installDependencies(pythonExec: str):
|
||||
print("Installing dependencies")
|
||||
print(pythonExec)
|
||||
try:
|
||||
import pip
|
||||
except:
|
||||
getPipFilePath = os.path.join(os.path.dirname(__file__), "get_pip.py")
|
||||
exec(open(getPipFilePath).read())
|
||||
|
||||
# just in case the included version is old
|
||||
subprocess_call([pythonExec, "-m", "pip", "install", "--upgrade", "pip"])
|
||||
|
||||
pkgVersion = "2.7.4"
|
||||
pkgName = "specklepy"
|
||||
try:
|
||||
import specklepy
|
||||
except Exception as e:
|
||||
subprocess_call([ pythonExec, "-m", "pip", "install", f"{pkgName}=={pkgVersion}"])
|
||||
|
||||
|
||||
pkgVersion = "1.10.11"
|
||||
pkgName = "panda3d"
|
||||
try:
|
||||
import panda3d
|
||||
except Exception as e:
|
||||
print("panda3d not installed")
|
||||
subprocess_call( [pythonExec, "-m", "pip", "install", f"{pkgName}=={pkgVersion}"])
|
||||
|
||||
# Check if specklpy needs updating
|
||||
try:
|
||||
print(f"Attempting to update specklepy to {pkgVersion}")
|
||||
# pip.main(['install', "specklepy==2.7.4"])
|
||||
result = subprocess_call(
|
||||
[
|
||||
pythonExec,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"--upgrade",
|
||||
f"{pkgName}=={pkgVersion}",
|
||||
]
|
||||
)
|
||||
if result == True:
|
||||
print("specklepy upgraded")
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(e.with_traceback)
|
||||
return True
|
||||
|
||||
installToolbox(pythonPath)
|
||||
installDependencies(pythonPath)
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
"%PROGRAMFILES%\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\python.exe" "conda_clone_activate.py"
|
||||
"%LOCALAPPDATA%\ESRI\conda\envs\arcgispro-py3-speckle\python.exe" "toolbox_install.py"
|
||||
@@ -14,12 +14,12 @@ def subprocess_call(*args, **kwargs):
|
||||
startupinfo.dwFlags = subprocess.CREATE_NEW_CONSOLE | subprocess.STARTF_USESHOWWINDOW
|
||||
startupinfo.wShowWindow = subprocess.SW_HIDE
|
||||
kwargs['startupinfo'] = startupinfo
|
||||
print("start")
|
||||
#print("start")
|
||||
#print(*args)
|
||||
try:
|
||||
# if manually: cmd.exe -> conda activate [env folder] -> pip install specklepy
|
||||
result = subprocess.run(*args, capture_output=True, text=True, shell=True, timeout=1000)
|
||||
#print(result)
|
||||
print(result)
|
||||
#result = subprocess.Popen( arg, shell=True, stdout=subprocess.PIPE) #, stderr=subprocess.STDOUT)
|
||||
#retcode = subprocess.check_call(*args, **kwargs) # Creates infinite loop, known issue: https://github.com/python/cpython/issues/87512
|
||||
except CalledProcessError as e:
|
||||
@@ -32,7 +32,7 @@ def subprocess_call(*args, **kwargs):
|
||||
#print(str(e))
|
||||
return False
|
||||
except: print("unknown error")
|
||||
print("end")
|
||||
#print("end")
|
||||
return True
|
||||
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
|
||||
from subprocess_call import subprocess_call
|
||||
import os
|
||||
|
||||
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-0.1-py3-none-any.whl" )
|
||||
subprocess_call([newExec, '-m','pip','install','--upgrade', '--force-reinstall', whl_file])
|
||||
# to uninstall: cmd.exe "C:\\Users\\username\\AppData\\Local\\ESRI\\conda\\envs\\arcgispro-py3-speckle\\python.exe" -m pip uninstall C:\\Users\\username\\Downloads\\speckle-arcgis\\foo-0.1-py3-none-any.whl
|
||||
return
|
||||
|
||||
def installDependencies(pythonExec: str):
|
||||
#print("Installing dependencies")
|
||||
print(pythonExec)
|
||||
try:
|
||||
import pip
|
||||
except:
|
||||
getPipFilePath = os.path.join(os.path.dirname(__file__), "get_pip.py") #TODO: give actual folder path
|
||||
exec(open(getPipFilePath).read())
|
||||
|
||||
# just in case the included version is old
|
||||
subprocess_call([pythonExec, "-m", "pip", "install", "--upgrade", "pip"])
|
||||
|
||||
pkgVersion = "2.7.4"
|
||||
pkgName = "specklepy"
|
||||
try:
|
||||
import specklepy # C:\Users\username\AppData\Roaming\Python\Python37\site-packages\specklepy\__init__.py
|
||||
except Exception as e:
|
||||
subprocess_call([ pythonExec, "-m", "pip", "install", f"{pkgName}=={pkgVersion}"])
|
||||
|
||||
|
||||
pkgVersion = "1.10.11"
|
||||
pkgName = "panda3d"
|
||||
try:
|
||||
import panda3d
|
||||
except Exception as e:
|
||||
print("panda3d not installed")
|
||||
subprocess_call( [pythonExec, "-m", "pip", "install", f"{pkgName}=={pkgVersion}"])
|
||||
|
||||
# Check if specklpy needs updating
|
||||
try:
|
||||
print(f"Attempting to update specklepy to {pkgVersion}")
|
||||
# pip.main(['install', "specklepy==2.7.4"])
|
||||
result = subprocess_call(
|
||||
[
|
||||
pythonExec,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"--upgrade",
|
||||
f"{pkgName}=={pkgVersion}",
|
||||
]
|
||||
)
|
||||
if result == True:
|
||||
print("specklepy upgraded")
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(e.with_traceback)
|
||||
return True
|
||||
|
||||
installToolbox(pythonPath)
|
||||
installDependencies(pythonPath)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# MANUAL INSTALLATION:
|
||||
# 1. Clone the default ArcGIS Pro conda environment
|
||||
# for 2.9.0: Project-> Python-> Manage Environments-> Clone Default
|
||||
# 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\\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")
|
||||
mypath = os.path.dirname(__file__)
|
||||
onlyfiles = [f for f in listdir(mypath) if (isfile(join(mypath, f)) and "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 = mypath + "\\" + onlyfiles[len(onlyfiles)-1]
|
||||
#whl_file = os.path.join(os.path.dirname(__file__), "speckle_toolbox-2.11.3-py3-none-any.whl" )
|
||||
subprocess_call([newExec, '-m','pip','install','--upgrade', '--force-reinstall', whl_file])
|
||||
return
|
||||
|
||||
def installDependencies(pythonExec: str, pkgName: str, pkgVersion: str):
|
||||
# install package
|
||||
try:
|
||||
#import importlib #importlib.import_module(pkgName)
|
||||
if pkgName == "specklepy": import specklepy
|
||||
elif pkgName == "panda3d": import panda3d
|
||||
except Exception as e:
|
||||
print(f"{pkgName} not installed")
|
||||
subprocess_call( [pythonExec, "-m", "pip", "install", f"{pkgName}=={pkgVersion}"])
|
||||
|
||||
# Check if package needs updating
|
||||
try:
|
||||
print(f"Attempting to update {pkgName} to {pkgVersion}")
|
||||
result = subprocess_call(
|
||||
[
|
||||
pythonExec,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"--upgrade",
|
||||
f"{pkgName}=={pkgVersion}",
|
||||
]
|
||||
)
|
||||
if result == True:
|
||||
print(f"{pkgName} upgraded")
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(e.with_traceback)
|
||||
return True
|
||||
|
||||
installToolbox(pythonPath)
|
||||
installDependencies(pythonPath, "specklepy", "2.9.0" )
|
||||
installDependencies(pythonPath, "panda3d", "1.10.11" )
|
||||
|
||||
@@ -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>20220826</ModDate><ModTime>150722</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>20230303</ModDate><ModTime>104937</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
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
|
||||
from regex import F
|
||||
from specklepy.objects import Base
|
||||
from specklepy.objects.geometry import Line, Mesh, Point, Polyline, Curve, Arc, Circle, Polycurve, Ellipse
|
||||
|
||||
import arcpy
|
||||
from typing import Any, List, Union, Sequence
|
||||
|
||||
import inspect
|
||||
|
||||
try:
|
||||
from speckle.converter.geometry.polygon import polygonToNative, polygonToSpeckle, multiPolygonToSpeckle, polygonToSpeckleMesh
|
||||
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.ui.logger import logToUser
|
||||
from speckle.converter.geometry.mesh import meshToNative
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.polygon import polygonToNative, polygonToSpeckle, multiPolygonToSpeckle, polygonToSpeckleMesh
|
||||
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
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
import numpy as np
|
||||
|
||||
def convertToSpeckle(feature, index: str, layer, geomType, featureType) -> Union[Base, Sequence[Base], None]:
|
||||
"""Converts the provided layer feature to Speckle objects"""
|
||||
print("___convertToSpeckle____________")
|
||||
try:
|
||||
geom = feature
|
||||
print(geom.isMultipart) # e.g. False
|
||||
print(geom.partCount)
|
||||
geomMultiType = geom.isMultipart
|
||||
hasCurves = feature.hasCurves
|
||||
|
||||
# feature is <geoprocessing describe geometry object object at 0x000002A75D6A4BD0>
|
||||
|
||||
#print(featureType) # e.g. Simple
|
||||
#print(geomType) # e.g. Polygon
|
||||
#geomSingleType = (featureType=="Simple") # Simple,SimpleJunction,SimpleJunction,ComplexEdge,Annotation,CoverageAnnotation,Dimension,RasterCatalogItem
|
||||
|
||||
if geomType == "Point": #Polygon, Point, Polyline, Multipoint, MultiPatch
|
||||
for pt in geom:
|
||||
return pointToSpeckle(pt, feature, layer)
|
||||
elif geomType == "Polyline":
|
||||
#if geom.hasCurves:
|
||||
# geom, feature = curvesToSegments(geom, feature, layer, geomMultiType)
|
||||
# geomMultiType = geom.isMultipart
|
||||
# return polylineToSpeckle(geom, feature, layer, geomMultiType)
|
||||
#else:
|
||||
if geom.partCount > 1: return multiPolylineToSpeckle(geom, feature, layer, geomMultiType)
|
||||
else: return polylineToSpeckle(geom, feature, layer, geomMultiType)
|
||||
elif geomType == "Polygon":
|
||||
if geom.partCount > 1: return multiPolygonToSpeckle(geom, index, layer, geomMultiType)
|
||||
else: return polygonToSpeckle(geom, index, layer, geomMultiType)
|
||||
elif geomType == "Multipoint":
|
||||
return multiPointToSpeckle(geom, feature, layer, geomMultiType)
|
||||
elif geomType == "MultiPatch":
|
||||
return polygonToSpeckleMesh(geom, index, layer, False)
|
||||
else:
|
||||
logToUser("Unsupported or invalid geometry in layer " + layer.name, level=1, func = inspect.stack()[0][3])
|
||||
return None
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
|
||||
def convertToNative(base: Base, sr: arcpy.SpatialReference) -> Union[Any, None]:
|
||||
"""Converts any given base object to QgsGeometry."""
|
||||
print("___Convert to Native SingleType___")
|
||||
converted = None
|
||||
try:
|
||||
#print(base)
|
||||
conversions = [
|
||||
(Point, pointToNative),
|
||||
(Line, lineToNative),
|
||||
(Polyline, polylineToNative),
|
||||
(Curve, curveToNative),
|
||||
(Arc, arcToNative),
|
||||
(Circle, circleToNative),
|
||||
(Ellipse, ellipseToNative),
|
||||
#(Mesh, meshToNative),
|
||||
(Polycurve, polycurveToNative),
|
||||
(Base, polygonToNative), # temporary solution for polygons (Speckle has no type Polygon yet)
|
||||
]
|
||||
|
||||
for conversion in conversions:
|
||||
if isinstance(base, conversion[0]):
|
||||
#print(conversion[0])
|
||||
converted = conversion[1](base, sr)
|
||||
break
|
||||
#print(converted)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return converted
|
||||
|
||||
def multiPointToNative(items: List[Point], sr: arcpy.SpatialReference):
|
||||
print("___Create MultiPoint")
|
||||
features = None
|
||||
try:
|
||||
all_pts = []
|
||||
# example https://pro.arcgis.com/en/pro-app/2.8/arcpy/classes/multipoint.htm
|
||||
for item in items:
|
||||
pt = pointToCoord(item) # [x, y, z]
|
||||
all_pts.append( arcpy.Point(pt[0], pt[1], pt[2]) )
|
||||
#print(all_pts)
|
||||
features = arcpy.Multipoint( arcpy.Array(all_pts) )
|
||||
#if len(features)==0: features = None
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return features
|
||||
|
||||
def multiPolylineToNative(items: List[Polyline], sr: arcpy.SpatialReference):
|
||||
print("_______Drawing Multipolylines____")
|
||||
poly = None
|
||||
try:
|
||||
#print(items)
|
||||
poly = None
|
||||
full_array_list = []
|
||||
for item in items: # will be 1 item
|
||||
pointsSpeckle = []
|
||||
try: pointsSpeckle = item.as_points()
|
||||
except: continue
|
||||
pts = [pointToCoord(pt) for pt in pointsSpeckle]
|
||||
|
||||
if item.closed is True:
|
||||
pts.append( pointToCoord(item.as_points()[0]) )
|
||||
|
||||
arr = [arcpy.Point(*coords) for coords in pts]
|
||||
full_array_list.append(arr)
|
||||
|
||||
poly = arcpy.Polyline( arcpy.Array(full_array_list), sr, has_z=True )
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return poly
|
||||
|
||||
def multiPolygonToNative(items: List[Base], sr: arcpy.SpatialReference): #TODO fix multi features
|
||||
|
||||
print("_______Drawing Multipolygons____")
|
||||
polygon = None
|
||||
try:
|
||||
#print(items)
|
||||
full_array_list = []
|
||||
|
||||
for item in items: # will be 1 item
|
||||
#print(item)
|
||||
#pts = [pointToCoord(pt) for pt in item["boundary"].as_points()]
|
||||
pointsSpeckle = []
|
||||
if isinstance(item["boundary"], Circle) or isinstance(item["boundary"], Arc):
|
||||
pointsSpeckle = speckleArcCircleToPoints(item["boundary"])
|
||||
elif isinstance(item["boundary"], Polycurve):
|
||||
pointsSpeckle = specklePolycurveToPoints(item["boundary"])
|
||||
elif isinstance(item["boundary"], Line): pass
|
||||
else:
|
||||
try: pointsSpeckle = item["boundary"].as_points()
|
||||
except: pass # if Line
|
||||
|
||||
pts = [pointToCoord(pt) for pt in pointsSpeckle]
|
||||
#print(pts)
|
||||
|
||||
outer_arr = [arcpy.Point(*coords) for coords in pts]
|
||||
outer_arr.append(outer_arr[0])
|
||||
geomPart = []
|
||||
try:
|
||||
for void in item["voids"]:
|
||||
#print(void)
|
||||
#pts = [pointToCoord(pt) for pt in void.as_points()]
|
||||
pointsSpeckle = []
|
||||
if isinstance(void, Circle) or isinstance(void, Arc):
|
||||
pointsSpeckle = speckleArcCircleToPoints(void)
|
||||
elif isinstance(void, Polycurve):
|
||||
pointsSpeckle = specklePolycurveToPoints(void)
|
||||
elif isinstance(void, Line): pass
|
||||
else:
|
||||
try: pointsSpeckle = void.as_points()
|
||||
except: pass # if Line
|
||||
pts = [pointToCoord(pt) for pt in pointsSpeckle]
|
||||
|
||||
inner_arr = [arcpy.Point(*coords) for coords in pts]
|
||||
inner_arr.append(inner_arr[0])
|
||||
geomPart.append(arcpy.Array(inner_arr))
|
||||
except:pass
|
||||
|
||||
geomPart.insert(0, arcpy.Array(outer_arr))
|
||||
full_array_list.extend(geomPart) # outlines are written one by one, with no separation to "parts"
|
||||
|
||||
geomPartArray = arcpy.Array(full_array_list)
|
||||
polygon = arcpy.Polygon(geomPartArray, sr, has_z=True)
|
||||
|
||||
print(polygon)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
return polygon
|
||||
|
||||
def convertToNativeMulti(items: List[Base], sr: arcpy.SpatialReference):
|
||||
print("___Convert to Native MultiType___")
|
||||
try:
|
||||
first = items[0]
|
||||
if isinstance(first, Point):
|
||||
return multiPointToNative(items, sr)
|
||||
elif isinstance(first, Line) or isinstance(first, Polyline):
|
||||
return multiPolylineToNative(items, sr)
|
||||
elif isinstance(first, Base):
|
||||
try:
|
||||
if first["boundary"] is not None and first["voids"] is not None:
|
||||
return multiPolygonToNative(items, sr)
|
||||
except: return None
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
|
||||
from regex import F
|
||||
from specklepy.objects import Base
|
||||
from specklepy.objects.geometry import Line, Mesh, Point, Polyline, Curve, Arc, Circle, Polycurve
|
||||
|
||||
import arcpy
|
||||
from typing import Any, List, Union, Sequence
|
||||
from speckle.converter.geometry.polygon import polygonToNative, polygonToSpeckle
|
||||
from speckle.converter.geometry.polyline import arcToNative, circleToNative, curveToNative, lineToNative, polycurveToNative, polylineFromVerticesToSpeckle, polylineToNative, polylineToSpeckle
|
||||
from speckle.converter.geometry.point import pointToCoord, pointToNative, pointToSpeckle, multiPointToSpeckle
|
||||
|
||||
|
||||
def convertToSpeckle(feature, layer, geomType, featureType) -> Union[Base, Sequence[Base], None]:
|
||||
"""Converts the provided layer feature to Speckle objects"""
|
||||
print("___convertToSpeckle____________")
|
||||
geom = feature
|
||||
#print(geom.isMultipart) # e.g. False
|
||||
geomMultiType = geom.isMultipart
|
||||
hasCurves = feature.hasCurves
|
||||
|
||||
#print(featureType) # e.g. Simple
|
||||
#print(geomType) # e.g. Polygon
|
||||
#geomSingleType = (featureType=="Simple") # Simple,SimpleJunction,SimpleJunction,ComplexEdge,Annotation,CoverageAnnotation,Dimension,RasterCatalogItem
|
||||
|
||||
if geomType == "Point": #Polygon, Point, Polyline, Multipoint, MultiPatch
|
||||
for pt in geom:
|
||||
return pointToSpeckle(pt, feature, layer)
|
||||
elif geomType == "Polyline":
|
||||
return polylineToSpeckle(geom, feature, layer, geomMultiType)
|
||||
elif geomType == "Polygon":
|
||||
return polygonToSpeckle(geom, feature, layer, geomMultiType)
|
||||
elif geomType == "Multipoint":
|
||||
return multiPointToSpeckle(geom, feature, layer, geomMultiType)
|
||||
else:
|
||||
arcpy.AddWarning("Unsupported or invalid geometry in layer " + layer.name)
|
||||
return None
|
||||
|
||||
|
||||
def convertToNative(base: Base, sr: arcpy.SpatialReference) -> Union[Any, None]:
|
||||
"""Converts any given base object to QgsGeometry."""
|
||||
print("___Convert to Native SingleType___")
|
||||
#print(base)
|
||||
converted = None
|
||||
conversions = [
|
||||
(Point, pointToNative),
|
||||
(Line, lineToNative),
|
||||
(Polyline, polylineToNative),
|
||||
(Curve, curveToNative),
|
||||
(Arc, arcToNative),
|
||||
(Circle, circleToNative),
|
||||
#(Mesh, meshToNative),
|
||||
(Polycurve, polycurveToNative),
|
||||
(Base, polygonToNative), # temporary solution for polygons (Speckle has no type Polygon yet)
|
||||
]
|
||||
|
||||
for conversion in conversions:
|
||||
if isinstance(base, conversion[0]):
|
||||
#print(conversion[0])
|
||||
converted = conversion[1](base, sr)
|
||||
break
|
||||
#print(converted)
|
||||
return converted
|
||||
|
||||
def multiPointToNative(items: List[Point], sr: arcpy.SpatialReference):
|
||||
print("___Create MultiPoint")
|
||||
all_pts = []
|
||||
# example https://pro.arcgis.com/en/pro-app/2.8/arcpy/classes/multipoint.htm
|
||||
for item in items:
|
||||
pt = pointToCoord(item) # [x, y, z]
|
||||
all_pts.append( arcpy.Point(pt[0], pt[1], pt[2]) )
|
||||
#print(all_pts)
|
||||
features = arcpy.Multipoint( arcpy.Array(all_pts) )
|
||||
#if len(features)==0: features = None
|
||||
return features
|
||||
|
||||
def multiPolylineToNative(items: List[Polyline], sr: arcpy.SpatialReference):
|
||||
print("_______Drawing Multipolylines____")
|
||||
#print(items)
|
||||
poly = None
|
||||
|
||||
return poly
|
||||
|
||||
def multiPolygonToNative(items: List[Base], sr: arcpy.SpatialReference): #TODO fix multi features
|
||||
|
||||
print("_______Drawing Multipolygons____")
|
||||
#print(items)
|
||||
for item in items: # will be 1 item
|
||||
#print(item)
|
||||
pts = [pointToCoord(pt) for pt in item["boundary"].as_points()]
|
||||
outer_arr = [arcpy.Point(*coords) for coords in pts]
|
||||
outer_arr.append(outer_arr[0])
|
||||
list_of_arrs = []
|
||||
try:
|
||||
for void in item["voids"]:
|
||||
#print(void)
|
||||
pts = [pointToCoord(pt) for pt in void.as_points()]
|
||||
#print(pts)
|
||||
inner_arr = [arcpy.Point(*coords) for coords in pts]
|
||||
inner_arr.append(inner_arr[0])
|
||||
list_of_arrs.append(arcpy.Array(inner_arr))
|
||||
except:pass
|
||||
|
||||
list_of_arrs.insert(0, arcpy.Array(outer_arr))
|
||||
array = arcpy.Array(list_of_arrs)
|
||||
polygon = arcpy.Polygon(array, sr, has_z=True)
|
||||
|
||||
return polygon
|
||||
|
||||
def convertToNativeMulti(items: List[Base], sr: arcpy.SpatialReference):
|
||||
print("___Convert to Native MultiType___")
|
||||
first = items[0]
|
||||
if isinstance(first, Point):
|
||||
return multiPointToNative(items, sr)
|
||||
elif isinstance(first, Line) or isinstance(first, Polyline):
|
||||
return multiPolylineToNative(items, sr)
|
||||
elif first["boundary"] is not None and first["voids"] is not None:
|
||||
return multiPolygonToNative(items, sr)
|
||||
@@ -1,11 +1,281 @@
|
||||
from specklepy.objects.geometry import Mesh
|
||||
from typing import List
|
||||
import arcpy
|
||||
import math
|
||||
|
||||
from specklepy.objects.geometry import Mesh, Point
|
||||
from specklepy.objects.other import RenderMaterial
|
||||
|
||||
import inspect
|
||||
|
||||
import shapefile
|
||||
from shapefile import TRIANGLE_STRIP, TRIANGLE_FAN, OUTER_RING
|
||||
try:
|
||||
from speckle.converter.layers.utils import get_scale_factor
|
||||
from speckle.converter.geometry.point import pointToNative
|
||||
from speckle.converter.layers.symbology import featureColorfromNativeRenderer
|
||||
from speckle.converter.layers.utils import get_scale_factor
|
||||
from speckle.ui.logger import logToUser
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.utils import get_scale_factor
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.point import pointToNative
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.symbology import featureColorfromNativeRenderer
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.utils import get_scale_factor
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
from panda3d.core import Triangulator
|
||||
|
||||
def meshToNative(meshes: List[Mesh], path: str):
|
||||
"""Converts a Speckle Mesh to MultiPatch"""
|
||||
print("06___________________Mesh to Native")
|
||||
try:
|
||||
#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()
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return path
|
||||
|
||||
|
||||
def meshToNative(mesh: Mesh):
|
||||
"""Converts a Speckle Mesh to QgsGeometry. Currently UNSUPPORTED"""
|
||||
return None
|
||||
def writeMeshToShp(meshes: List[Mesh], path: str):
|
||||
"""Converts a Speckle Mesh to native geometry"""
|
||||
print("06___________________Mesh to Native")
|
||||
try:
|
||||
#print(meshes)
|
||||
#print(mesh.units)
|
||||
w = shapefile.Writer(path)
|
||||
w.field('speckle_id', 'C')
|
||||
|
||||
def rasterToMesh(vertices, faces, colors):
|
||||
mesh = Mesh.create(vertices, faces, colors)
|
||||
mesh.units = "m"
|
||||
shapes = []
|
||||
for geom in meshes:
|
||||
|
||||
if geom.speckle_type =='Objects.Geometry.Mesh' and isinstance(geom, Mesh):
|
||||
mesh = geom
|
||||
w = fill_mesh_parts(w, mesh, geom.id)
|
||||
else:
|
||||
try:
|
||||
if geom.displayValue and isinstance(geom.displayValue, Mesh):
|
||||
mesh = geom.displayValue
|
||||
w = fill_mesh_parts(w, mesh, geom.id)
|
||||
elif geom.displayValue and isinstance(geom.displayValue, List):
|
||||
w = fill_multi_mesh_parts(w, geom.displayValue, geom.id)
|
||||
except:
|
||||
try:
|
||||
if geom["@displayValue"] and isinstance(geom["@displayValue"], Mesh):
|
||||
mesh = geom["@displayValue"]
|
||||
w = fill_mesh_parts(w, mesh, geom.id)
|
||||
elif geom["@displayValue"] and isinstance(geom["@displayValue"], List):
|
||||
w = fill_multi_mesh_parts(w, geom["@displayValue"], geom.id)
|
||||
except:
|
||||
try:
|
||||
if geom.displayMesh and isinstance(geom.displayMesh, Mesh):
|
||||
mesh = geom.displayMesh
|
||||
w = fill_mesh_parts(w, mesh, geom.id)
|
||||
elif geom.displayMesh and isinstance(geom.displayMesh, List):
|
||||
w = fill_multi_mesh_parts(w, geom.displayMesh, geom.id)
|
||||
except: pass
|
||||
w.close()
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return path
|
||||
|
||||
|
||||
def fill_multi_mesh_parts(w: shapefile.Writer, meshes: List[Mesh], geom_id: str):
|
||||
try:
|
||||
parts_list = []
|
||||
types_list = []
|
||||
for mesh in meshes:
|
||||
if not isinstance(mesh, Mesh): continue
|
||||
try:
|
||||
#print(f"Fill multi-mesh parts # {geom_id}")
|
||||
parts_list_x, types_list_x = deconstructSpeckleMesh(mesh)
|
||||
parts_list.extend(parts_list_x)
|
||||
types_list.extend(types_list_x)
|
||||
except Exception as e: pass
|
||||
|
||||
w.multipatch(parts_list, partTypes=types_list ) # one type for each part
|
||||
w.record(geom_id)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return w
|
||||
|
||||
def fill_mesh_parts(w: shapefile.Writer, mesh: Mesh, geom_id: str):
|
||||
|
||||
try:
|
||||
#print(f"Fill mesh parts # {geom_id}")
|
||||
parts_list, types_list = deconstructSpeckleMesh(mesh)
|
||||
w.multipatch(parts_list, partTypes=types_list ) # one type for each part
|
||||
w.record(geom_id)
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return w
|
||||
|
||||
def deconstructSpeckleMesh(mesh: Mesh):
|
||||
parts_list = []
|
||||
types_list = []
|
||||
try:
|
||||
scale = get_scale_factor(mesh.units)
|
||||
|
||||
count = 0 # sequence of vertex (not of flat coord list)
|
||||
for f in mesh.faces: # real number of loops will be at least 3 times less
|
||||
try:
|
||||
vertices = mesh.faces[count]
|
||||
if mesh.faces[count] == 0: vertices = 3
|
||||
if mesh.faces[count] == 1: vertices = 4
|
||||
|
||||
face = []
|
||||
for i in range(vertices):
|
||||
index_faces = count + 1 + i
|
||||
index_vertices = mesh.faces[index_faces]*3
|
||||
face.append([ scale * mesh.vertices[index_vertices], scale * mesh.vertices[index_vertices+1], scale * mesh.vertices[index_vertices+2] ])
|
||||
|
||||
parts_list.append(face)
|
||||
types_list.append(OUTER_RING)
|
||||
count += vertices + 1
|
||||
except: break # when out of range
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return parts_list, types_list
|
||||
|
||||
def constructMeshFromRaster(vertices, faces, colors):
|
||||
mesh = None
|
||||
try:
|
||||
mesh = Mesh.create(vertices, faces, colors)
|
||||
mesh.units = "m"
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return mesh
|
||||
|
||||
def constructMesh(vertices, faces, colors):
|
||||
mesh = None
|
||||
try:
|
||||
mesh = Mesh.create(vertices, faces, colors)
|
||||
mesh.units = "m"
|
||||
material = RenderMaterial()
|
||||
material.diffuse = colors[0]
|
||||
mesh.renderMaterial = material
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return mesh
|
||||
|
||||
def meshPartsFromPolygon(polyBorder: List[Point], voidsAsPts: List[List[Point]], existing_vert: int, index: int, layer):
|
||||
|
||||
try:
|
||||
#print("__meshPartsFromPolygon__")
|
||||
vertices = []
|
||||
total_vertices = 0
|
||||
#print(layer)
|
||||
try: sr = arcpy.Describe(layer.dataSource).spatialReference
|
||||
except Exception as e:
|
||||
print(e)
|
||||
sr = None
|
||||
#print(sr)
|
||||
coef = 1
|
||||
maxPoints = 5000
|
||||
if len(polyBorder) >= maxPoints: coef = int(len(polyBorder)/maxPoints)
|
||||
|
||||
if len(voidsAsPts) == 0: # only if there is a mesh with no voids and large amount of points
|
||||
#print("mesh with no voids")
|
||||
for k, ptt in enumerate(polyBorder): #pointList:
|
||||
pt = polyBorder[k*coef]
|
||||
if k < maxPoints:
|
||||
#if isinstance(pt, QgsPointXY):
|
||||
# pt = QgsPoint(pt)
|
||||
#print(pt)
|
||||
if isinstance(pt,Point):
|
||||
pt = pointToNative(pt, sr).getPart()
|
||||
x = pt.X
|
||||
y = pt.Y
|
||||
z = 0 if math.isnan(pt.Z) else pt.Z
|
||||
vertices.extend([x, y, z])
|
||||
total_vertices += 1
|
||||
else: break
|
||||
|
||||
ran = range(0, total_vertices)
|
||||
faces = [total_vertices]
|
||||
faces.extend([i + existing_vert for i in ran])
|
||||
# else: https://docs.panda3d.org/1.10/python/reference/panda3d.core.Triangulator
|
||||
else: # if there are voids
|
||||
print("mesh with voids")
|
||||
# if its a large polygon with voids to be triangualted, lower the coef even more:
|
||||
maxPoints = 100
|
||||
if len(polyBorder) >= maxPoints: coef = int(len(polyBorder)/maxPoints)
|
||||
|
||||
trianglator = Triangulator()
|
||||
faces = []
|
||||
|
||||
pt_count = 0
|
||||
# add extra middle point for border
|
||||
for k, ptt in enumerate(polyBorder): #pointList:
|
||||
pt = polyBorder[k*coef]
|
||||
if k < maxPoints:
|
||||
if pt_count < len(polyBorder)-1 and k < (maxPoints-1):
|
||||
pt2 = polyBorder[(k+1)*coef]
|
||||
else: pt2 = polyBorder[0]
|
||||
|
||||
trianglator.addPolygonVertex(trianglator.addVertex(pt.x, pt.y))
|
||||
vertices.extend([pt.x, pt.y, pt.z])
|
||||
trianglator.addPolygonVertex(trianglator.addVertex((pt.x+pt2.x)/2, (pt.y+pt2.y)/2))
|
||||
vertices.extend([(pt.x+pt2.x)/2, (pt.y+pt2.y)/2, (pt.z+pt2.z)/2])
|
||||
total_vertices += 2
|
||||
pt_count += 1
|
||||
else: break
|
||||
|
||||
#add void points
|
||||
for pts in voidsAsPts:
|
||||
trianglator.beginHole()
|
||||
|
||||
coefVoid = 1
|
||||
if len(pts) >= maxPoints: coefVoid = int(len(pts)/maxPoints)
|
||||
for k, ptt in enumerate(pts):
|
||||
pt = pts[k*coefVoid]
|
||||
if k < maxPoints:
|
||||
trianglator.addHoleVertex(trianglator.addVertex(pt.x, pt.y))
|
||||
vertices.extend([pt.x, pt.y, pt.z])
|
||||
total_vertices += 1
|
||||
else: break
|
||||
|
||||
trianglator.triangulate()
|
||||
i = 0
|
||||
#print(trianglator.getNumTriangles())
|
||||
while i < trianglator.getNumTriangles():
|
||||
tr = [trianglator.getTriangleV0(i),trianglator.getTriangleV1(i),trianglator.getTriangleV2(i)]
|
||||
faces.extend([3, tr[0] + existing_vert, tr[1] + existing_vert, tr[2] + existing_vert])
|
||||
i+=1
|
||||
ran = range(0, total_vertices)
|
||||
|
||||
#print("color")
|
||||
col = featureColorfromNativeRenderer(index, layer) #(100<<16) + (100<<8) + 100
|
||||
colors = [col for i in ran] # apply same color for all vertices
|
||||
|
||||
return total_vertices, vertices, faces, colors
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None, None, None, None
|
||||
|
||||
@@ -3,23 +3,29 @@ from typing import List
|
||||
from specklepy.objects.geometry import Point
|
||||
import arcpy
|
||||
|
||||
from speckle.converter.layers.utils import get_scale_factor
|
||||
import inspect
|
||||
|
||||
try:
|
||||
from speckle.converter.layers.utils import get_scale_factor
|
||||
from speckle.ui.logger import logToUser
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.utils import get_scale_factor
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
|
||||
def multiPointToSpeckle(geom, feature, layer, multiType: bool):
|
||||
"""Converts a Point to Speckle"""
|
||||
#try:
|
||||
#print("___Point to Speckle____")
|
||||
#point = Point(units = "m")
|
||||
|
||||
pointList = []
|
||||
#print(geom) # <geoprocessing describe geometry object object at 0x0000020F1D94AB10>
|
||||
#print(multiType)
|
||||
|
||||
if multiType is False:
|
||||
for pt in geom:
|
||||
#print(pt) # 284394.58100903 5710688.11602606 NaN NaN <class 'arcpy.arcobjects.arcobjects.Point'>
|
||||
#print(type(pt))
|
||||
if pt != None: pointList.append(pointToSpeckle(pt, feature, layer))
|
||||
try:
|
||||
if multiType is False:
|
||||
for pt in geom:
|
||||
#print(pt) # 284394.58100903 5710688.11602606 NaN NaN <class 'arcpy.arcobjects.arcobjects.Point'>
|
||||
#print(type(pt))
|
||||
if pt != None: pointList.append(pointToSpeckle(pt, feature, layer))
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return pointList
|
||||
|
||||
def pointToSpeckle(pt, feature, layer):
|
||||
@@ -29,42 +35,67 @@ def pointToSpeckle(pt, feature, layer):
|
||||
# when unset, z() returns "nan"
|
||||
#print(pt) # 4.9046319 52.3592043 NaN NaN
|
||||
#print("____Point to Speckle___")
|
||||
x = pt.X
|
||||
y = pt.Y
|
||||
if pt.Z: z = pt.Z
|
||||
else: z = 0
|
||||
specklePoint = Point(units = "m")
|
||||
specklePoint.x = x
|
||||
specklePoint.y = y
|
||||
specklePoint.z = z
|
||||
'''
|
||||
col = featureColorfromNativeRenderer(feature, layer)
|
||||
specklePoint['displayStyle'] = {}
|
||||
specklePoint['displayStyle']['color'] = col
|
||||
'''
|
||||
#print(specklePoint)
|
||||
return specklePoint
|
||||
try:
|
||||
x = pt.X
|
||||
y = pt.Y
|
||||
if pt.Z: z = pt.Z
|
||||
else: z = 0
|
||||
specklePoint = Point(units = "m")
|
||||
specklePoint.x = x
|
||||
specklePoint.y = y
|
||||
specklePoint.z = z
|
||||
'''
|
||||
if feature is not None and layer is not None: # can be if it's a point from raster layer
|
||||
col = featureColorfromNativeRenderer(feature, layer)
|
||||
specklePoint['displayStyle'] = {}
|
||||
specklePoint['displayStyle']['color'] = col
|
||||
'''
|
||||
#print(specklePoint)
|
||||
return specklePoint
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def pointToNative(pt: Point, sr: arcpy.SpatialReference) -> arcpy.PointGeometry:
|
||||
"""Converts a Speckle Point to QgsPoint"""
|
||||
print("___pointToNative__")
|
||||
#print(pt)
|
||||
pt = scalePointToNative(pt, pt.units)
|
||||
geom = arcpy.PointGeometry(arcpy.Point(pt.x, pt.y, pt.z), sr, has_z = True)
|
||||
#print(geom)
|
||||
return geom
|
||||
try:
|
||||
pt = scalePointToNative(pt, pt.units)
|
||||
geom = arcpy.PointGeometry(arcpy.Point(pt.x, pt.y, pt.z), sr, has_z = True)
|
||||
#print(geom)
|
||||
return geom
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def pointToCoord(pt: Point) -> List[float]:
|
||||
def pointToCoord(point: Point) -> List[float]:
|
||||
"""Converts a Speckle Point to QgsPoint"""
|
||||
pt = scalePointToNative(pt, pt.units)
|
||||
coords = [pt.x, pt.y, pt.z]
|
||||
#print(coords)
|
||||
return coords
|
||||
try:
|
||||
pt = scalePointToNative(point, point.units)
|
||||
coords = [pt.x, pt.y, pt.z]
|
||||
#print(coords)
|
||||
return coords
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return [None, None, None]
|
||||
|
||||
def scalePointToNative(pt: Point, units: str) -> Point:
|
||||
def scalePointToNative(point: Point, units: str) -> Point:
|
||||
"""Scale point coordinates to meters"""
|
||||
scaleFactor = get_scale_factor(units)
|
||||
pt.x = pt.x * scaleFactor
|
||||
pt.y = pt.y * scaleFactor
|
||||
pt.z = 0 if math.isnan(pt.z) else pt.z * scaleFactor
|
||||
return pt
|
||||
try:
|
||||
scaleFactor = get_scale_factor(units)
|
||||
pt = Point(units = "m")
|
||||
pt.x = point.x * scaleFactor
|
||||
pt.y = point.y * scaleFactor
|
||||
pt.z = 0 if math.isnan(point.z) else point.z * scaleFactor
|
||||
return pt
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def addZtoPoint(coords: List):
|
||||
try:
|
||||
if len(coords) == 2: coords.append(0)
|
||||
return coords
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
@@ -1,165 +1,294 @@
|
||||
from typing import Sequence
|
||||
from typing import List, Sequence, Union
|
||||
import arcpy
|
||||
import json
|
||||
import json
|
||||
from arcpy.arcobjects.arcobjects import SpatialReference
|
||||
|
||||
from specklepy.objects import Base
|
||||
from specklepy.objects.geometry import Point
|
||||
from speckle.converter.geometry.mesh import rasterToMesh
|
||||
from speckle.converter.geometry.point import pointToCoord
|
||||
from speckle.converter.geometry.polyline import polylineFromVerticesToSpeckle, circleToSpeckle
|
||||
from specklepy.objects.geometry import Point, Arc, Circle, Polycurve, Polyline, Line
|
||||
|
||||
import inspect
|
||||
|
||||
try:
|
||||
from speckle.converter.geometry.mesh import constructMesh, constructMeshFromRaster, meshPartsFromPolygon
|
||||
from speckle.converter.geometry.point import pointToCoord, pointToNative
|
||||
from speckle.converter.layers.symbology import featureColorfromNativeRenderer
|
||||
from speckle.converter.geometry.polyline import (polylineFromVerticesToSpeckle,
|
||||
circleToSpeckle,
|
||||
speckleArcCircleToPoints,
|
||||
curveToSpeckle,
|
||||
specklePolycurveToPoints
|
||||
)
|
||||
from speckle.converter.geometry.utils import speckleBoundaryToSpecklePts
|
||||
from speckle.ui.logger import logToUser
|
||||
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.mesh import constructMeshFromRaster, constructMesh, meshPartsFromPolygon
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.point import pointToCoord, pointToNative
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.symbology import featureColorfromNativeRenderer
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.polyline import (polylineFromVerticesToSpeckle,
|
||||
circleToSpeckle,
|
||||
speckleArcCircleToPoints,
|
||||
curveToSpeckle,
|
||||
specklePolycurveToPoints
|
||||
)
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.utils import speckleBoundaryToSpecklePts
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
|
||||
import math
|
||||
from panda3d.core import Triangulator
|
||||
|
||||
|
||||
def polygonToSpeckle(geom, feature, layer, multiType: bool):
|
||||
"""Converts a Polygon to Speckle"""
|
||||
#try:
|
||||
print("___Polygon to Speckle____")
|
||||
def polygonToSpeckleMesh(feature, index: int, layer, multitype: bool):
|
||||
print("________polygonToSpeckleMesh_____")
|
||||
print(feature)
|
||||
polygon = Base(units = "m")
|
||||
pointList = []
|
||||
voidPointList = []
|
||||
voids = []
|
||||
boundary = None
|
||||
|
||||
if geom.hasCurves:
|
||||
# geometry SHAPE@ tokens: https://pro.arcgis.com/en/pro-app/latest/arcpy/get-started/reading-geometries.htm
|
||||
print(geom.JSON)
|
||||
# look for "curvePaths" or "curveRings"[[ (startPt, {arcs, beziers etc}, optional(endPt))],[],...], "rings"
|
||||
# examples: https://developers.arcgis.com/documentation/common-data-types/geometry-objects.htm
|
||||
# e.g. {"hasZ":true,"curveRings":[[[631307.05960000027,5803698.4477999993,0],{"a":[[631307.05960000027,5803698.4477999993,0],[631307.05960000027,5803414.92656173],0,1]}]],"spatialReference":{"wkid":32631,"latestWkid":32631}}
|
||||
# b - bezier curve (endPt, controlPts)
|
||||
# a - elliptical arc (endPt, centralPt)
|
||||
# c - circular arc (endPt, throughPt)
|
||||
try:
|
||||
|
||||
#startPtCoords = geom.JSON.curveRings[0][0]
|
||||
r'''
|
||||
segments = []
|
||||
for key, val in json.loads(geom.JSON).items():
|
||||
if key == "curveRings":
|
||||
for segm in val:
|
||||
print(segm)
|
||||
segmStartCoord = segm[0]
|
||||
print(segmStartCoord)
|
||||
|
||||
segmData = segm[1]
|
||||
for key2, val2 in segmData.items():
|
||||
if key2 == "a":
|
||||
segmToCoord = val2[0] # elliptical arc
|
||||
segmCenter = val2[1]
|
||||
print(segmToCoord)
|
||||
print(segmCenter)
|
||||
if segmStartCoord == segmToCoord:
|
||||
print("full circle")
|
||||
boundary = circleToSpeckle(segmCenter, segmToCoord, layer)
|
||||
|
||||
try:
|
||||
segmToCoord = segm[1].c # circular arc
|
||||
except:
|
||||
try:
|
||||
segmToCoord = segm[1].b # bezier curve
|
||||
except: pass
|
||||
|
||||
segmEndCoord = None
|
||||
if len(segm)>2:
|
||||
segmEndCoord = segm[2]
|
||||
'''
|
||||
|
||||
if multiType is False:
|
||||
print("single type")
|
||||
for p in geom:
|
||||
for pt in p:
|
||||
if pt != None: pointList.append(pt)
|
||||
boundary = polylineFromVerticesToSpeckle(pointList, True, feature, layer)
|
||||
else:
|
||||
print("multi type")
|
||||
for i, p in enumerate(geom):
|
||||
for pt in p:
|
||||
#print(pt) # 284394.58100903 5710688.11602606 NaN NaN
|
||||
if pt == None and boundary == None: # first break
|
||||
boundary = polylineFromVerticesToSpeckle(pointList, True, feature, layer)
|
||||
pointList = []
|
||||
elif pt == None and boundary != None: # breaks btw voids
|
||||
void = polylineFromVerticesToSpeckle(pointList, True, feature, layer)
|
||||
voids.append(void)
|
||||
pointList = []
|
||||
elif pt != None: # add points to whatever list (boundary or void)
|
||||
pointList.append(pt)
|
||||
|
||||
if boundary != None and len(pointList)>0: # remaining polyline
|
||||
void = polylineFromVerticesToSpeckle(pointList, True, feature, layer)
|
||||
voids.append(void)
|
||||
|
||||
polygon.boundary = boundary
|
||||
polygon.voids = voids
|
||||
polygon.displayValue = [ boundary ] + voids
|
||||
|
||||
############# mesh
|
||||
vertices = []
|
||||
total_vertices = 0
|
||||
polyBorder = boundary.as_points()
|
||||
|
||||
#print(polyBorder)
|
||||
|
||||
if len(polyBorder)>2:
|
||||
print("make meshes from polygons")
|
||||
if len(voids) == 0: # if there is a mesh with no voids
|
||||
for pt in polyBorder:
|
||||
x = pt.x
|
||||
y = pt.y
|
||||
z = 0 if math.isnan(pt.z) else pt.z
|
||||
vertices.extend([x, y, z])
|
||||
total_vertices += 1
|
||||
#print(vertices)
|
||||
ran = range(0, total_vertices)
|
||||
faces = [total_vertices]
|
||||
faces.extend([i for i in ran])
|
||||
#print(faces)
|
||||
# else: https://docs.panda3d.org/1.10/python/reference/panda3d.core.Triangulator
|
||||
else:
|
||||
trianglator = Triangulator()
|
||||
faces = []
|
||||
|
||||
# add boundary points
|
||||
#polyBorder = boundary.as_points()
|
||||
pt_count = 0
|
||||
# add extra middle point for border
|
||||
for pt in polyBorder:
|
||||
if pt_count < len(polyBorder)-1:
|
||||
pt2 = polyBorder[pt_count+1]
|
||||
else: pt2 = polyBorder[0]
|
||||
|
||||
trianglator.addPolygonVertex(trianglator.addVertex(pt.x, pt.y))
|
||||
vertices.extend([pt.x, pt.y, pt.z])
|
||||
trianglator.addPolygonVertex(trianglator.addVertex((pt.x+pt2.x)/2, (pt.y+pt2.y)/2))
|
||||
vertices.extend([(pt.x+pt2.x)/2, (pt.y+pt2.y)/2, (pt.z+pt2.z)/2])
|
||||
total_vertices += 2
|
||||
pt_count += 1
|
||||
|
||||
#add void points
|
||||
for i in range(len(voids)):
|
||||
trianglator.beginHole()
|
||||
pts = voids[i].as_points()
|
||||
for pt in pts:
|
||||
trianglator.addHoleVertex(trianglator.addVertex(pt.x, pt.y))
|
||||
vertices.extend([pt.x, pt.y, pt.z])
|
||||
total_vertices += 1
|
||||
|
||||
trianglator.triangulate()
|
||||
i = 0
|
||||
while i < trianglator.getNumTriangles():
|
||||
tr = [trianglator.getTriangleV0(i),trianglator.getTriangleV1(i),trianglator.getTriangleV2(i)]
|
||||
faces.extend([3, tr[0], tr[1], tr[2]])
|
||||
i+=1
|
||||
ran = range(0, total_vertices)
|
||||
vertices = []
|
||||
faces = []
|
||||
colors = []
|
||||
existing_vert = 0
|
||||
|
||||
for i, p in enumerate(feature):
|
||||
#print("____start enumerate feature")
|
||||
#print(p) #<geoprocessing array object object at 0x0000026796C77110>
|
||||
|
||||
boundary, voids = getPolyBoundaryVoids(p, layer, multitype)
|
||||
#print(boundary)
|
||||
#print(voids)
|
||||
polyBorder = speckleBoundaryToSpecklePts(boundary)
|
||||
#print(polyBorder)
|
||||
voidsAsPts = []
|
||||
for v in voids:
|
||||
pts = speckleBoundaryToSpecklePts(v)
|
||||
voidsAsPts.append(pts)
|
||||
#print(voidsAsPts)
|
||||
#print("__to start meshPartsFromPolygon")
|
||||
total_vert, vertices_x, faces_x, colors_x = meshPartsFromPolygon(polyBorder, voidsAsPts, existing_vert, index, layer)
|
||||
|
||||
existing_vert += total_vert
|
||||
vertices.extend(vertices_x)
|
||||
faces.extend(faces_x)
|
||||
colors.extend(colors_x)
|
||||
|
||||
#print("Colors: ")
|
||||
#print(colors)
|
||||
mesh = constructMesh(vertices, faces, colors)
|
||||
polygon.displayValue = [ mesh ]
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return polygon
|
||||
|
||||
def getPolyBoundaryVoids(geom, layer, multiType: bool):
|
||||
#print("__getPolyBoundaryVoids__")
|
||||
voids: List[Union[None, Polyline, Arc, Line, Polycurve]] = []
|
||||
#print(voids)
|
||||
boundary = None
|
||||
pointList = []
|
||||
try:
|
||||
partsBoundaries = []
|
||||
partsVoids = []
|
||||
if multiType is False: # Multipolygon
|
||||
try: # might be no property "has curves"
|
||||
if geom.hasCurves:
|
||||
print("has curves")
|
||||
# geometry SHAPE@ tokens: https://pro.arcgis.com/en/pro-app/latest/arcpy/get-started/reading-geometries.htm
|
||||
print(geom.JSON)
|
||||
boundary = curveToSpeckle(geom, "Polygon", geom, layer)
|
||||
else:
|
||||
print("no curves")
|
||||
for p in geom:
|
||||
for pt in p:
|
||||
#print(pt)
|
||||
if pt != None: pointList.append(pt)
|
||||
boundary = polylineFromVerticesToSpeckle(pointList, True, geom, layer)
|
||||
print(boundary)
|
||||
except: # for multipatches, no property "has curves"
|
||||
#print(geom)
|
||||
for pt in geom:
|
||||
#print(pt)
|
||||
if pt != None: pointList.append(pt)
|
||||
boundary = polylineFromVerticesToSpeckle(pointList, True, geom, layer)
|
||||
|
||||
partsBoundaries.append(boundary)
|
||||
partsVoids.append([])
|
||||
|
||||
else:
|
||||
print("multi type")
|
||||
for i, p in enumerate(geom):
|
||||
#print(p)
|
||||
for pt in p:
|
||||
#print(pt) # 284394.58100903 5710688.11602606 NaN NaN
|
||||
if pt == None and boundary == None: # first break
|
||||
boundary = polylineFromVerticesToSpeckle(pointList, True, geom, layer)
|
||||
pointList = []
|
||||
elif pt == None and boundary != None: # breaks btw voids
|
||||
void = polylineFromVerticesToSpeckle(pointList, True, geom, layer)
|
||||
voids.append(void)
|
||||
pointList = []
|
||||
elif pt != None: # add points to whatever list (boundary or void)
|
||||
pointList.append(pt)
|
||||
|
||||
if boundary != None and len(pointList)>0: # remaining polyline
|
||||
void = polylineFromVerticesToSpeckle(pointList, True, geom, layer)
|
||||
voids.append(void)
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return boundary, voids
|
||||
|
||||
def multiPolygonToSpeckle(geom, index: str, layer, multiType: bool):
|
||||
|
||||
print("___MultiPolygon to Speckle____")
|
||||
polygon = []
|
||||
try:
|
||||
#print(enumerate(geom.getPart())) # this method ignores curvature and voids
|
||||
#print(json.loads(geom.JSON))
|
||||
#js = json.loads(geom.JSON)['rings']
|
||||
#https://desktop.arcgis.com/en/arcmap/latest/analyze/python/reading-geometries.htm
|
||||
for i,x in enumerate(geom): # [[x,x,x]
|
||||
print("Part # " + str(i+1))
|
||||
print(x)
|
||||
boundaryFinished = 0
|
||||
arrBoundary = []
|
||||
arrInnerRings = []
|
||||
for ptn in x: # arcpy.Point
|
||||
if ptn is None:
|
||||
boundaryFinished += 1
|
||||
arrInnerRings.append([]) # start of new Inner Ring
|
||||
elif boundaryFinished == 0 and ptn is not None:
|
||||
arrBoundary.append(ptn)
|
||||
elif boundaryFinished == 1 and ptn is not None:
|
||||
arrInnerRings[len(arrInnerRings)-1].append(ptn)
|
||||
|
||||
full_arr = [arrBoundary] + arrInnerRings
|
||||
#print(full_arr)
|
||||
poly = arcpy.Polygon(arcpy.Array(full_arr), arcpy.Describe(layer.dataSource).SpatialReference, has_z = True)
|
||||
#print(poly) #<geoprocessing describe geometry object object at 0x000002B2D3E338D0>
|
||||
polygon.append(polygonToSpeckle(poly, index, layer, poly.isMultipart))
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return polygon
|
||||
|
||||
|
||||
def polygonToSpeckle(geom, index: int, layer, multitype: bool):
|
||||
"""Converts a Polygon to Speckle"""
|
||||
polygon = Base(units = "m")
|
||||
try:
|
||||
print("___Polygon to Speckle____")
|
||||
print(geom)
|
||||
|
||||
boundary, voids = getPolyBoundaryVoids(geom, layer, multitype)
|
||||
|
||||
data = arcpy.Describe(layer.dataSource)
|
||||
sr = data.spatialReference
|
||||
|
||||
if boundary is None: return None
|
||||
polygon.boundary = boundary
|
||||
polygon.voids = voids
|
||||
polygon.displayValue = [ boundary ] + voids
|
||||
#print(boundary)
|
||||
|
||||
############# mesh
|
||||
vertices = []
|
||||
polyBorder = []
|
||||
total_vertices = 0
|
||||
if isinstance(boundary, Circle) or isinstance(boundary, Arc):
|
||||
polyBorder = speckleArcCircleToPoints(boundary)
|
||||
elif isinstance(boundary, Polycurve):
|
||||
polyBorder = specklePolycurveToPoints(boundary)
|
||||
#polygon.boundary.displayValue.closed = True
|
||||
elif isinstance(boundary, Line): pass
|
||||
elif isinstance(boundary, Polyline):
|
||||
try: polyBorder = boundary.as_points()
|
||||
except: pass # if Line
|
||||
#print(polyBorder)
|
||||
|
||||
|
||||
if len(polyBorder)>2: # at least 3 points
|
||||
print("make meshes from polygons")
|
||||
if len(voids) == 0: # if there is a mesh with no voids
|
||||
for pt in polyBorder:
|
||||
if isinstance(pt, Point): pt = pointToNative(pt, sr).getPart() # SR unknown
|
||||
x = pt.X
|
||||
y = pt.Y
|
||||
z = 0 if math.isnan(pt.Z) else pt.Z
|
||||
vertices.extend([x, y, z])
|
||||
total_vertices += 1
|
||||
#print(vertices)
|
||||
ran = range(0, total_vertices)
|
||||
faces = [total_vertices]
|
||||
faces.extend([i for i in ran])
|
||||
#print(faces)
|
||||
# else: https://docs.panda3d.org/1.10/python/reference/panda3d.core.Triangulator
|
||||
else:
|
||||
trianglator = Triangulator()
|
||||
faces = []
|
||||
|
||||
# add boundary points
|
||||
#polyBorder = boundary.as_points()
|
||||
pt_count = 0
|
||||
# add extra middle point for border
|
||||
for pt in polyBorder:
|
||||
if pt_count < len(polyBorder)-1:
|
||||
pt2 = polyBorder[pt_count+1]
|
||||
else: pt2 = polyBorder[0]
|
||||
|
||||
trianglator.addPolygonVertex(trianglator.addVertex(pt.x, pt.y))
|
||||
vertices.extend([pt.x, pt.y, pt.z])
|
||||
|
||||
#trianglator.addPolygonVertex(trianglator.addVertex((pt.x+pt2.x)/4*3, (pt.y+pt2.y)/4*3))
|
||||
#vertices.extend([(pt.x+pt2.x)/4*3, (pt.y+pt2.y)/4*3, (pt.z+pt2.z)/4*3])
|
||||
|
||||
trianglator.addPolygonVertex(trianglator.addVertex((pt.x+pt2.x)/2, (pt.y+pt2.y)/2))
|
||||
vertices.extend([(pt.x+pt2.x)/2, (pt.y+pt2.y)/2, (pt.z+pt2.z)/2])
|
||||
|
||||
#trianglator.addPolygonVertex(trianglator.addVertex((pt.x+pt2.x)/4, (pt.y+pt2.y)/4))
|
||||
#vertices.extend([(pt.x+pt2.x)/4, (pt.y+pt2.y)/4, (pt.z+pt2.z)/4])
|
||||
|
||||
total_vertices += 2
|
||||
pt_count += 1
|
||||
|
||||
#add void points
|
||||
for i in range(len(voids)):
|
||||
trianglator.beginHole()
|
||||
|
||||
|
||||
pts = []
|
||||
if isinstance(voids[i], Circle) or isinstance(voids[i], Arc):
|
||||
pts = speckleArcCircleToPoints(voids[i])
|
||||
elif isinstance(voids[i], Polycurve):
|
||||
pts = specklePolycurveToPoints(voids[i])
|
||||
elif isinstance(voids[i], Line): pass
|
||||
else:
|
||||
try: pts = voids[i].as_points()
|
||||
except: pass # if Line
|
||||
#pts = voids[i].as_points()
|
||||
for pt in pts:
|
||||
trianglator.addHoleVertex(trianglator.addVertex(pt.x, pt.y))
|
||||
vertices.extend([pt.x, pt.y, pt.z])
|
||||
total_vertices += 1
|
||||
|
||||
trianglator.triangulate()
|
||||
i = 0
|
||||
while i < trianglator.getNumTriangles():
|
||||
tr = [trianglator.getTriangleV0(i),trianglator.getTriangleV1(i),trianglator.getTriangleV2(i)]
|
||||
faces.extend([3, tr[0], tr[1], tr[2]])
|
||||
i+=1
|
||||
ran = range(0, total_vertices)
|
||||
|
||||
#print(polygon)
|
||||
col = featureColorfromNativeRenderer(index, layer)
|
||||
colors = [col for i in ran] # apply same color for all vertices
|
||||
mesh = constructMesh(vertices, faces, colors)
|
||||
polygon.displayValue = mesh
|
||||
#print("print resulted polygon")
|
||||
#print(polygon)
|
||||
col = (100<<16) + (100<<8) + 100 #featureColorfromNativeRenderer(feature, layer)
|
||||
colors = [col for i in ran] # apply same color for all vertices
|
||||
mesh = rasterToMesh(vertices, faces, colors)
|
||||
polygon.displayValue = mesh
|
||||
#print("print resulted polygon")
|
||||
#print(polygon)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return polygon
|
||||
|
||||
def polygonToNative(poly: Base, sr: arcpy.SpatialReference) -> arcpy.Polygon:
|
||||
@@ -168,21 +297,48 @@ def polygonToNative(poly: Base, sr: arcpy.SpatialReference) -> arcpy.Polygon:
|
||||
Each being a Speckle Polyline and List of polylines respectively."""
|
||||
|
||||
print("_______Drawing polygons____")
|
||||
pts = [pointToCoord(pt) for pt in poly["boundary"].as_points()]
|
||||
outer_arr = [arcpy.Point(*coords) for coords in pts]
|
||||
outer_arr.append(outer_arr[0])
|
||||
list_of_arrs = []
|
||||
try:
|
||||
for void in poly["voids"]:
|
||||
#print(void)
|
||||
pts = [pointToCoord(pt) for pt in void.as_points()]
|
||||
#print(pts)
|
||||
inner_arr = [arcpy.Point(*coords) for coords in pts]
|
||||
inner_arr.append(inner_arr[0])
|
||||
list_of_arrs.append(arcpy.Array(inner_arr))
|
||||
except:pass
|
||||
list_of_arrs.insert(0, outer_arr)
|
||||
array = arcpy.Array(list_of_arrs)
|
||||
polygon = arcpy.Polygon(array, sr, has_z=True)
|
||||
polygon = None
|
||||
try:
|
||||
#pts = [pointToCoord(pt) for pt in poly["boundary"].as_points()]
|
||||
pointsSpeckle = []
|
||||
if isinstance(poly["boundary"], Circle) or isinstance(poly["boundary"], Arc):
|
||||
pointsSpeckle = speckleArcCircleToPoints(poly["boundary"])
|
||||
elif isinstance(poly["boundary"], Polycurve):
|
||||
pointsSpeckle = specklePolycurveToPoints(poly["boundary"])
|
||||
elif isinstance(poly["boundary"], Line): pass
|
||||
else:
|
||||
try: pointsSpeckle = poly["boundary"].as_points()
|
||||
except: pass # if Line
|
||||
|
||||
pts = [pointToCoord(pt) for pt in pointsSpeckle]
|
||||
#print(pts)
|
||||
|
||||
outer_arr = [arcpy.Point(*coords) for coords in pts]
|
||||
outer_arr.append(outer_arr[0])
|
||||
geomPart = []
|
||||
try:
|
||||
for void in poly["voids"]:
|
||||
#print(void)
|
||||
#pts = [pointToCoord(pt) for pt in void.as_points()]
|
||||
pointsSpeckle = []
|
||||
if isinstance(void, Circle) or isinstance(void, Arc):
|
||||
pointsSpeckle = speckleArcCircleToPoints(void)
|
||||
elif isinstance(void, Polycurve):
|
||||
pointsSpeckle = specklePolycurveToPoints(void)
|
||||
elif isinstance(void, Line): pass
|
||||
else:
|
||||
try: pointsSpeckle = void.as_points()
|
||||
except: pass # if Line
|
||||
pts = [pointToCoord(pt) for pt in pointsSpeckle]
|
||||
|
||||
inner_arr = [arcpy.Point(*coords) for coords in pts]
|
||||
inner_arr.append(inner_arr[0])
|
||||
geomPart.append(arcpy.Array(inner_arr))
|
||||
except:pass
|
||||
geomPart.insert(0, outer_arr)
|
||||
geomPartArray = arcpy.Array(geomPart)
|
||||
polygon = arcpy.Polygon(geomPartArray, sr, has_z=True)
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return polygon
|
||||
|
||||
@@ -1,146 +1,485 @@
|
||||
|
||||
from math import atan, cos, sin
|
||||
import math
|
||||
from typing import List, Union
|
||||
from specklepy.objects.geometry import Point, Line, Polyline, Curve, Arc, Circle, Polycurve, Plane, Interval
|
||||
import json
|
||||
from typing import List, Union, Tuple
|
||||
from specklepy.objects import Base
|
||||
from specklepy.objects.geometry import Box, Vector, Point, Line, Polyline, Curve, Ellipse, Arc, Circle, Polycurve, Plane, Interval
|
||||
import arcpy
|
||||
import numpy as np
|
||||
|
||||
from speckle.converter.geometry.point import pointToCoord, pointToNative, pointToSpeckle
|
||||
from speckle.converter.layers.utils import get_scale_factor
|
||||
import inspect
|
||||
|
||||
def circleToSpeckle(center, point, layer):
|
||||
try:
|
||||
from speckle.converter.geometry.point import pointToCoord, pointToSpeckle, addZtoPoint
|
||||
from speckle.converter.layers.utils import get_scale_factor
|
||||
from speckle.ui.logger import logToUser
|
||||
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
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
|
||||
def circleToSpeckle(center, point):
|
||||
print("___Circle to Speckle____")
|
||||
rad = math.sqrt(math.pow((center[0] - point[0]),2) + math.pow((center[1] - point[1]),2) )
|
||||
#print(rad)
|
||||
if len(center)>2: center_z = center[2]
|
||||
else: center_z = 0
|
||||
length = rad*2*math.pi
|
||||
domain = [0, length]
|
||||
plane = [center[0], center[1], center_z, 0,0,1, 1,0,0, 0,1,0]
|
||||
units = 3 #"m"
|
||||
try:
|
||||
rad = math.sqrt(math.pow((center[0] - point[0]),2) + math.pow((center[1] - point[1]),2) )
|
||||
#print(rad)
|
||||
if len(center)>2: center_z = center[2]
|
||||
else: center_z = 0
|
||||
length = rad*2*math.pi
|
||||
domain = [0, length]
|
||||
plane = [center[0], center[1], center_z, 0,0,1, 1,0,0, 0,1,0]
|
||||
units = 3 #"m"
|
||||
|
||||
args = [0] + [rad] + domain + plane + [units]
|
||||
#print(args)
|
||||
c = Circle().from_list(args)
|
||||
#print(c)
|
||||
return c
|
||||
args = [0] + [rad] + domain + plane + [units]
|
||||
#print(args)
|
||||
c = Circle.from_list(args)
|
||||
c.plane.origin.units = "m"
|
||||
c.units = "m"
|
||||
#print(c)
|
||||
return c
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def multiPolylineToSpeckle(geom, feature, layer, multiType: bool):
|
||||
|
||||
print("___MultiPolyline to Speckle____")
|
||||
polyline = []
|
||||
try:
|
||||
print(enumerate(geom.getPart()))
|
||||
for i,x in enumerate(geom.getPart()):
|
||||
poly = arcpy.Polyline(x, arcpy.Describe(layer.dataSource).SpatialReference, has_z = True)
|
||||
print(poly)
|
||||
polyline.append(polylineToSpeckle(poly, feature, layer, poly.isMultipart))
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return polyline
|
||||
|
||||
def polylineToSpeckle(geom, feature, layer, multiType: bool):
|
||||
print("___Polyline to Speckle____")
|
||||
polyline = None
|
||||
pointList = []
|
||||
#print(geom.hasCurves)
|
||||
|
||||
if multiType is False:
|
||||
for p in geom:
|
||||
for pt in p:
|
||||
if pt != None: pointList.append(pt)#; print(pt.Z)
|
||||
closed = False
|
||||
if pointList[0] == pointList[len(pointList)-1]:
|
||||
closed = True
|
||||
pointList = pointList[:-1]
|
||||
polyline = polylineFromVerticesToSpeckle(pointList, closed, feature, layer)
|
||||
try:
|
||||
pointList = []
|
||||
print(geom.hasCurves)
|
||||
|
||||
if multiType is False:
|
||||
if geom.hasCurves:
|
||||
print("has curves")
|
||||
# geometry SHAPE@ tokens: https://pro.arcgis.com/en/pro-app/latest/arcpy/get-started/reading-geometries.htm
|
||||
print(geom.JSON)
|
||||
polyline = curveToSpeckle(geom, "Polyline", feature, layer)
|
||||
else:
|
||||
for p in geom:
|
||||
for pt in p:
|
||||
if pt != None: pointList.append(pt)#; print(pt.Z)
|
||||
closed = False
|
||||
if pointList[0] == pointList[len(pointList)-1]:
|
||||
closed = True
|
||||
pointList = pointList[:-1]
|
||||
polyline = polylineFromVerticesToSpeckle(pointList, closed, feature, layer)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return polyline
|
||||
|
||||
def polylineFromVerticesToSpeckle(vertices, closed, feature, layer):
|
||||
def polylineFromVerticesToSpeckle(vertices: List[Point], closed: bool, feature, layer) -> Polyline:
|
||||
"""Converts a Polyline to Speckle"""
|
||||
|
||||
print("___Polyline from vertices to Speckle____")
|
||||
specklePts = []
|
||||
for pt in vertices:
|
||||
newPt = pointToSpeckle(pt, feature, layer)
|
||||
specklePts.append(newPt)
|
||||
|
||||
# TODO: Replace with `from_points` function when fix is pushed.
|
||||
polyline = Polyline(units = "m")
|
||||
polyline.value = []
|
||||
polyline.closed = closed
|
||||
polyline.units = specklePts[0].units
|
||||
for i, point in enumerate(specklePts):
|
||||
if closed and i == len(specklePts) - 1:
|
||||
continue
|
||||
polyline.value.extend([point.x, point.y, point.z])
|
||||
try:
|
||||
|
||||
if isinstance(vertices, list):
|
||||
if len(vertices) > 0 and isinstance(vertices[0], Point):
|
||||
specklePts = vertices
|
||||
else: specklePts = [pointToSpeckle(pt, feature, layer) for pt in vertices] #breaks unexplainably
|
||||
#elif isinstance(vertices, QgsVertexIterator):
|
||||
# specklePts = [pointToSpeckle(pt, feature, layer) for pt in vertices]
|
||||
else: return None
|
||||
|
||||
specklePts = []
|
||||
for pt in vertices:
|
||||
newPt = pointToSpeckle(pt, feature, layer)
|
||||
specklePts.append(newPt)
|
||||
|
||||
# TODO: Replace with `from_points` function when fix is pushed.
|
||||
polyline.value = []
|
||||
polyline.closed = closed
|
||||
polyline.units = specklePts[0].units
|
||||
for i, point in enumerate(specklePts):
|
||||
if closed and i == len(specklePts) - 1 and specklePts[0] == point:
|
||||
continue # if we consider the last pt, do not add is coincides with the first (and type is Closed)
|
||||
polyline.value.extend([point.x, point.y, point.z])
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return polyline
|
||||
|
||||
def arc3ptToSpeckle(p0: List, p1: List, p2: List, feature, layer) -> Arc:
|
||||
print("____arc 3pt to Speckle___")
|
||||
arc = Arc()
|
||||
try:
|
||||
p0 = addZtoPoint(p0)
|
||||
p1 = addZtoPoint(p1)
|
||||
p2 = addZtoPoint(p2)
|
||||
arc.startPoint = pointToSpeckle(arcpy.Point(*p0), feature, layer)
|
||||
arc.midPoint = pointToSpeckle(arcpy.Point(*p1), feature, layer)
|
||||
arc.endPoint = pointToSpeckle(arcpy.Point(*p2), feature, layer)
|
||||
center, radius = getArcCenter(Point.from_list(p0), Point.from_list(p1), Point.from_list(p2))
|
||||
arc.plane = Plane() #.from_list(Point(), Vector(Point(0, 0, 1)), Vector(Point(0,1,0)), Vector(Point(-1,0,0)))
|
||||
arc.plane.origin = Point.from_list(center)
|
||||
arc.plane.origin.units = "m"
|
||||
arc.units = "m"
|
||||
arc.angleRadians, startAngle, endAngle = getArcRadianAngle(arc)
|
||||
|
||||
arc.radius = radius
|
||||
|
||||
arc.plane.normal = getArcNormal(arc, arc.midPoint)
|
||||
|
||||
#arc.angleRadians = abs(angle1 + angle2)
|
||||
#print(arc.angleRadians)
|
||||
|
||||
#col = featureColorfromNativeRenderer(feature, layer)
|
||||
#arc['displayStyle'] = {}
|
||||
#arc['displayStyle']['color'] = col
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return arc
|
||||
|
||||
def curveBezierToSpeckle(segmStartCoord, segmEndCoord, knots, feature, layer):
|
||||
print("____bezier curve to Speckle____")
|
||||
try:
|
||||
degree = 3
|
||||
points = [
|
||||
tuple(knots[0]), tuple(segmStartCoord), tuple(knots[1]), tuple(segmEndCoord)
|
||||
] #[segmStartCoord, *coords]
|
||||
print(points)
|
||||
num_points = len(points) #2
|
||||
|
||||
knot_count = num_points + degree - 1 #4
|
||||
knots = [0] * knot_count
|
||||
print(knots)
|
||||
for i in range(1, len(knots)):
|
||||
knots[i] = i // 3
|
||||
print(knots[i])
|
||||
|
||||
length = 1 #spline.calc_length()
|
||||
domain = Interval(start=0, end=length, totalChildrenCount=0)
|
||||
points = [tuple(pt) for pt in points]
|
||||
curve = Curve(
|
||||
degree = degree,
|
||||
closed = False,
|
||||
periodic= True if (segmStartCoord == segmEndCoord) else False,
|
||||
points= list(sum(points, ())), # magic (flatten list of tuples)
|
||||
weights=[1] * num_points,
|
||||
knots=knots,
|
||||
rational=False,
|
||||
area=0,
|
||||
volume=0,
|
||||
length=length,
|
||||
domain=domain,
|
||||
units="m",
|
||||
bbox=Box(area=0.0, volume=0.0),
|
||||
)
|
||||
print(curve)
|
||||
return curve
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
|
||||
def curveToSpeckle(geom, geomType, feature, layer) -> Union[Circle, Arc, Polyline, Polycurve]:
|
||||
print("____curve to Speckle____")
|
||||
boundary = Polycurve(units = "m")
|
||||
print(geomType)
|
||||
try:
|
||||
# look for "curvePaths" or "curveRings"[[ (startPt, {arcs, beziers etc}, optional(endPt))],[],...], "rings"
|
||||
# examples: https://developers.arcgis.com/documentation/common-data-types/geometry-objects.htm
|
||||
# e.g. {"hasZ":true,
|
||||
# "curveRings":[[[631307.05960000027,5803698.4477999993,0],{"a":[[631307.05960000027,5803698.4477999993,0],[631307.05960000027,5803414.92656173],0,1]}]],
|
||||
# "spatialReference":{"wkid":32631,"latestWkid":32631}}
|
||||
|
||||
# b - bezier curve (endPt, controlPts)
|
||||
# a - elliptical arc (endPt, centralPt) e.g. for circle: [[[631307.05960000027,5803698.4477999993,0],{"a":[[631307.05960000027,5803698.4477999993,0],[631307.05960000027,5803414.92656173],0,1]}]]
|
||||
# c - circular arc (endPt, throughPt) e.g. [[[633242.45179999992,5803058.0354999993,0],{"c":[[633718.26040000003,5803496.4210000001,0],[633337.75764975848,5803431.9997026781]]},[633242.45179999992,5803058.0354999993,0]]]
|
||||
|
||||
if geomType == "Polyline": boundary.closed = False
|
||||
else: boundary.closed = True
|
||||
segments = []
|
||||
|
||||
for key, val in json.loads(geom.JSON).items():
|
||||
print(key)
|
||||
if key == "curveRings" or key == "curvePaths":
|
||||
|
||||
#boundary.closed = True
|
||||
includesLines = 0
|
||||
|
||||
for segm in val: # segm: List
|
||||
print(segm) #e.g. [[631307.05960000027,5803698.4477999993,0], {"a":[[631307.05960000027,5803698.4477999993,0],[631307.05960000027,5803414.92656173],0,1]}]
|
||||
segmStartCoord: List = addZtoPoint(segm[0])
|
||||
|
||||
# go through all elements (points, a, c, ...)
|
||||
for k in range(1, len(segm)):
|
||||
# e.g. one from the list: "curveRings":[[[631750.87200000044,5803159.6126000006,0],
|
||||
# {"c":[[632429.8348000003,5803507.1132999994,0],[631988.22772700491,5803532.9008129537]]},
|
||||
# {"c":[[632590.21970000025,5803127.5355999991,0],[633018.51899157302,5803532.1801161235]]},
|
||||
# [631750.87200000044,5803159.6126000006,0]]]
|
||||
|
||||
# if previous segments exist
|
||||
if len(segments) > 0:
|
||||
segmOldData = segm[k-1]
|
||||
if isinstance(segmOldData, dict): # get "end point" of previous segment
|
||||
for key3, val3 in segmOldData.items():
|
||||
segmStartCoord: List = addZtoPoint(val2[0])
|
||||
elif isinstance(segmOldData, list) and isinstance(segmOldData[0], float):
|
||||
segmStartCoord: List = segmOldData
|
||||
segmStartCoord = addZtoPoint(segmStartCoord)
|
||||
|
||||
if isinstance(segm[k], dict):
|
||||
for key2, val2 in segm[k].items():
|
||||
if key2 == "a": # elliptical arc (endPt, centralPt)
|
||||
# e.g. {'a': [[633883.1035000002, 5802972.5812, 0], [634028.3379278888, 5802908.342895357], 0, 1, 1.1543577096027686, 473.59966687227444, 0.33531864204900685]}
|
||||
segmEndCoord = addZtoPoint(val2[0]) # [631307.05960000027,5803698.4477999993,0]
|
||||
segmCenter = addZtoPoint(val2[1]) # [631307.05960000027,5803414.92656173]
|
||||
|
||||
if segmStartCoord == segmEndCoord:
|
||||
if len(val2) == 4:
|
||||
print("full circle")
|
||||
#boundary.closed = True
|
||||
segmentLocal = circleToSpeckle(segmCenter, segmEndCoord)
|
||||
segments.append(segmentLocal)
|
||||
lastPt = segmEndCoord
|
||||
print("segmentLocal:")
|
||||
print(segmentLocal)
|
||||
print(segmStartCoord)
|
||||
print(segmEndCoord)
|
||||
else: # ellipse
|
||||
logToUser("SpeckleWarning: ellipse geometry not supported yet", level=1, func = inspect.stack()[0][3])
|
||||
segments = []
|
||||
break
|
||||
else: # elliptical curve
|
||||
logToUser("SpeckleWarning: ellipse geometry not supported yet", level=1, func = inspect.stack()[0][3])
|
||||
segments = []
|
||||
break
|
||||
|
||||
if key2 == "c": # circular arc (endPt, throughPt)
|
||||
|
||||
segmEndCoord: List = addZtoPoint(val2[0]) # [633718.26040000003,5803496.4210000001,0]
|
||||
segmThrough: List = addZtoPoint(val2[1]) # [633337.7576497585, 5803431.999702678]
|
||||
|
||||
segmentLocal = arc3ptToSpeckle(segmStartCoord, segmThrough, segmEndCoord, geom, layer)
|
||||
segments.append(segmentLocal)
|
||||
print("segmentLocal:")
|
||||
print(segmentLocal)
|
||||
print(segmStartCoord)
|
||||
print(segmEndCoord)
|
||||
lastPt = segmEndCoord
|
||||
|
||||
if key2 == "b": # bezier curve (endPt, controlPts)
|
||||
logToUser("SpeckleWarning: bezier curve geometry not supported yet", level=1, func = inspect.stack()[0][3])
|
||||
segments = []
|
||||
break
|
||||
r'''
|
||||
segmEndCoord: List = addZtoPoint(val2[0]) # [633718.26040000003,5803496.4210000001,0]
|
||||
#segmThrough: List = val2[1] # [633337.7576497585, 5803431.999702678]
|
||||
coords = val2[1:]
|
||||
segmentLocal = curveBezierToSpeckle(segmStartCoord, segmEndCoord, coords, feature, layer)
|
||||
segments.append(segmentLocal)
|
||||
print("segmentLocal:")
|
||||
print(segmentLocal)
|
||||
print(segmStartCoord)
|
||||
print(segmEndCoord)
|
||||
|
||||
lastPt = segmEndCoord
|
||||
'''
|
||||
|
||||
elif isinstance(segm[k], list) and isinstance(segm[k][0], float): # add line to point
|
||||
print("add line")
|
||||
segm[k] = addZtoPoint(segm[k])
|
||||
segmentLocal = lineFrom2pt(segmStartCoord, segm[k])
|
||||
includesLines = 1
|
||||
segments.append(segmentLocal)
|
||||
lastPt = segm[k]
|
||||
|
||||
print("segmentLocal:")
|
||||
print(segmentLocal)
|
||||
print(segmentLocal.start)
|
||||
print(segmentLocal.end)
|
||||
|
||||
# for the last point
|
||||
if k == len(segm)-1 and isinstance(segm[k], list):
|
||||
print("last element is a point (adding line)")
|
||||
lastPt = addZtoPoint(lastPt)
|
||||
if lastPt != segm[0]:
|
||||
#segmentLocal = lineFrom2pt(lastPt, segm[0])
|
||||
#segments.append(segmentLocal)
|
||||
#includesLines = 1
|
||||
#print("segmentLocal:")
|
||||
#print(segmentLocal)
|
||||
#print(segmentLocal.start)
|
||||
#print(segmentLocal.end)
|
||||
boundary.closed = True
|
||||
#pts = speckleArcCircleToPoints(segmentLocal)
|
||||
#pts.append(segm[k])
|
||||
#arcgisPts = [arcpy.Point(pt[0], pt[1], pt[2]) for pt in pts]
|
||||
#segmentLocal = polylineFromVerticesToSpeckle(arcgisPts, True, feature, layer)
|
||||
|
||||
boundary.segments = segments
|
||||
print(segments)
|
||||
|
||||
if len(segments) == 1:
|
||||
boundary = segments[0]
|
||||
#if isinstance(boundary, Arc) or isinstance(boundary, Circle):
|
||||
# boundary.displayValue = Polyline.from_points(speckleArcCircleToPoints(boundary))
|
||||
|
||||
elif len(segments) > 1: # and includesLines == 0:
|
||||
#boundary.displayValue = Polyline.from_points(specklePolycurveToPoints(boundary))
|
||||
pass
|
||||
#boundary.closed = True
|
||||
#elif len(segments) > 1 and includesLines == 1:
|
||||
# print("includes lines!")
|
||||
# points = specklePolycurveToPoints(boundary)
|
||||
# boundary = Polyline.from_points(points)
|
||||
else: return None
|
||||
|
||||
#boundary.displayValue = Polyline.from_points(specklePolycurveToPoints(boundary))
|
||||
|
||||
print(boundary)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return boundary
|
||||
|
||||
|
||||
|
||||
def lineFrom2pt(pt1: List[float], pt2: List[float]):
|
||||
line = Line(units = "m" )#.from_list([*pt1, *pt2, *domain])
|
||||
try:
|
||||
pt1 = addZtoPoint(pt1)
|
||||
pt2 = addZtoPoint(pt2)
|
||||
dist = math.sqrt( math.pow((pt2[0] - pt1[0]), 2) + math.pow((pt2[1] - pt1[1]), 2) + math.pow((pt2[2] - pt1[2]), 2) )
|
||||
print(dist)
|
||||
domain = [0, dist, 0, 0]
|
||||
line.start = Point.from_list(pt1)
|
||||
line.end = Point.from_list(pt2)
|
||||
line.start.units = line.end.units = "m"
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return line
|
||||
|
||||
def polylineToNative(poly: Polyline, sr: arcpy.SpatialReference) -> arcpy.Polyline:
|
||||
"""Converts a Speckle Polyline to QgsLineString"""
|
||||
print("__ convert poly to native __")
|
||||
pts = [pointToCoord(pt) for pt in poly.as_points()]
|
||||
if poly.closed is True:
|
||||
pts.append( pointToCoord(poly.as_points()[0]) )
|
||||
print("__ convert polyline to native __")
|
||||
polyline = None
|
||||
try:
|
||||
|
||||
pts_coord_list = [arcpy.Point(*coords) for coords in pts]
|
||||
polyline = arcpy.Polyline( arcpy.Array(pts_coord_list), sr, has_z=True )
|
||||
#print(polyline.JSON)
|
||||
if isinstance(poly, Polycurve):
|
||||
poly = specklePolycurveToPoints(poly)
|
||||
if isinstance(poly, Arc) or isinstance(poly, Circle):
|
||||
try: poly = poly["displayValue"]
|
||||
except: poly = speckleArcCircleToPoints(poly)
|
||||
|
||||
if isinstance(poly, list): pts = [pointToCoord(pt) for pt in poly]
|
||||
else: pts = [pointToCoord(pt) for pt in poly.as_points()]
|
||||
|
||||
if poly.closed is True:
|
||||
pts.append( pointToCoord(poly.as_points()[0]) )
|
||||
|
||||
pts_coord_list = [arcpy.Point(*coords) for coords in pts]
|
||||
polyline = arcpy.Polyline( arcpy.Array(pts_coord_list), sr, has_z=True )
|
||||
#print(polyline.JSON)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return polyline
|
||||
|
||||
|
||||
def lineToNative(line: Line, sr: arcpy.SpatialReference) -> arcpy.Polyline:
|
||||
"""Converts a Speckle Line to QgsLineString"""
|
||||
"""Converts a Speckle Line to Native"""
|
||||
print("___Line to Native___")
|
||||
pts = [pointToCoord(pt) for pt in [line.start, line.end]]
|
||||
line = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in pts]), sr , has_z=True)
|
||||
return line
|
||||
try:
|
||||
pts = [pointToCoord(pt) for pt in [line.start, line.end]]
|
||||
line = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in pts]), sr , has_z=True)
|
||||
return line
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def curveToNative(poly: Curve, sr: arcpy.SpatialReference) -> arcpy.Polyline:
|
||||
"""Converts a Speckle Curve to QgsLineString"""
|
||||
display = poly.displayValue
|
||||
curve = polylineToNative(display, sr)
|
||||
return curve
|
||||
"""Converts a Speckle Curve to Native"""
|
||||
try:
|
||||
display = poly.displayValue
|
||||
curve = polylineToNative(display, sr)
|
||||
return curve
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def arcToNative(poly: Arc, sr: arcpy.SpatialReference) -> arcpy.Polyline:
|
||||
"""Converts a Speckle Arc to QgsCircularString"""
|
||||
arc = arcToNativePoints(poly, sr) #QgsCircularString(pointToNative(poly.startPoint), pointToNative(poly.midPoint), pointToNative(poly.endPoint))
|
||||
return arc
|
||||
"""Converts a Speckle Arc to Native"""
|
||||
try:
|
||||
arc = arcToNativePolyline(poly, sr) #QgsCircularString(pointToNative(poly.startPoint), pointToNative(poly.midPoint), pointToNative(poly.endPoint))
|
||||
return arc
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def ellipseToNative(poly: Ellipse, sr: arcpy.SpatialReference):
|
||||
logToUser("Ellipse geometry is not supported yet", level=1)
|
||||
return
|
||||
|
||||
def circleToNative(poly: Circle, sr: arcpy.SpatialReference) -> arcpy.Polyline:
|
||||
"""Converts a Speckle Circle to QgsLineString"""
|
||||
print("___Convert Circle from Native___")
|
||||
points = []
|
||||
angle1 = math.pi/2
|
||||
print("___Convert Circle to Native___")
|
||||
curve = None
|
||||
try:
|
||||
points = []
|
||||
angle1 = math.pi/2
|
||||
|
||||
pointsNum = math.floor(math.pi*2) * 12
|
||||
if pointsNum <4: pointsNum = 4
|
||||
points.append(pointToCoord(poly.plane.origin))
|
||||
|
||||
radScaled = poly.radius * get_scale_factor(poly.units)
|
||||
points[0][1] += radScaled
|
||||
|
||||
for i in range(1, pointsNum + 1):
|
||||
k = i/pointsNum # to reset values from 1/10 to 1
|
||||
if poly.plane.normal.z == 0: normal = 1
|
||||
else: normal = poly.plane.normal.z
|
||||
angle = angle1 + k * math.pi*2 * normal
|
||||
pt = Point( x = poly.plane.origin.x * get_scale_factor(poly.units) + radScaled * cos(angle), y = poly.plane.origin.y * get_scale_factor(poly.units) + radScaled * sin(angle), z = 0)
|
||||
print(pt)
|
||||
pt.units = "m"
|
||||
points.append(pointToCoord(pt))
|
||||
points.append(points[0])
|
||||
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr , has_z=True)
|
||||
|
||||
pointsNum = math.floor(math.pi*2) * 12
|
||||
if pointsNum <4: pointsNum = 4
|
||||
points.append(pointToCoord(poly.plane.origin))
|
||||
|
||||
radScaled = poly.radius * get_scale_factor(poly.units)
|
||||
points[0][1] += radScaled
|
||||
|
||||
for i in range(1, pointsNum + 1):
|
||||
k = i/pointsNum # to reset values from 1/10 to 1
|
||||
if poly.plane.normal.z == 0: normal = 1
|
||||
else: normal = poly.plane.normal.z
|
||||
angle = angle1 + k * math.pi*2 * normal
|
||||
pt = Point( x = poly.plane.origin.x + radScaled * cos(angle), y = poly.plane.origin.y + radScaled * sin(angle), z = 0)
|
||||
pt.units = "m"
|
||||
points.append(pointToCoord(pt))
|
||||
points.append(points[0])
|
||||
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr , has_z=True)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return curve
|
||||
|
||||
def polycurveToNative(poly: Polycurve, sr: arcpy.SpatialReference) -> arcpy.Polyline:
|
||||
points = []
|
||||
curve = None
|
||||
print("___Polycurve to native___")
|
||||
|
||||
try:
|
||||
for segm in poly.segments: # Line, Polyline, Curve, Arc, Circle
|
||||
#print(segm)
|
||||
|
||||
try:
|
||||
for i, segm in enumerate(poly.segments): # Line, Polyline, Curve, Arc, Circle
|
||||
print("___start segment")
|
||||
if isinstance(segm,Line): converted = lineToNative(segm, sr) # QgsLineString
|
||||
elif isinstance(segm,Polyline): converted = polylineToNative(segm, sr) # QgsLineString
|
||||
elif isinstance(segm,Curve): converted = curveToNative(segm, sr) # QgsLineString
|
||||
elif isinstance(segm,Circle): converted = circleToNative(segm, sr) # QgsLineString
|
||||
elif isinstance(segm,Arc): converted = arcToNativePoints(segm, sr) # QgsLineString
|
||||
elif isinstance(segm,Arc): converted = arcToNativePolyline(segm, sr) # QgsLineString
|
||||
else: # either return a part of the curve, of skip this segment and try next
|
||||
arcpy.AddWarning(f"Part of the polycurve cannot be converted")
|
||||
logToUser(f"Part of the polycurve cannot be converted", level=1, func = inspect.stack()[0][3])
|
||||
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr , has_z=True)
|
||||
return curve
|
||||
if converted is not None:
|
||||
#print(converted) # <geoprocessing describe geometry object object at 0x000002B2D3E338D0>
|
||||
for part in converted:
|
||||
#print("Part: ")
|
||||
#print(part) # <geoprocessing array object object at 0x000002B2D2E09530>
|
||||
for pt in part:
|
||||
#print(pt) # 64.4584221540162 5.5 NaN NaN
|
||||
@@ -149,57 +488,194 @@ def polycurveToNative(poly: Polycurve, sr: arcpy.SpatialReference) -> arcpy.Poly
|
||||
#print(pt_z)
|
||||
#print(len(points))
|
||||
if len(points)>0 and pt.X == points[len(points)-1][0] and pt.Y == points[len(points)-1][1] and pt_z == points[len(points)-1][2]: pass
|
||||
else: points.append(pointToCoord(Point(x=pt.X, y = pt.Y, z = pt_z)))
|
||||
#print(points)
|
||||
else: points.append(pointToCoord(Point(x=pt.X, y = pt.Y, z = pt_z, units = "m"))) # e.g. [[64.4584221540162, 5.499999999999999, 0.0], [64.45461685210796, 5.587155742747657, 0.0]]
|
||||
else:
|
||||
arcpy.AddWarning(f"Part of the polycurve cannot be converted")
|
||||
logToUser(f"Part of the polycurve cannot be converted", level=1, func = inspect.stack()[0][3])
|
||||
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True )
|
||||
return curve
|
||||
except: curve = None
|
||||
#print(curve)
|
||||
|
||||
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True )
|
||||
#print(curve)
|
||||
|
||||
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True )
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return curve
|
||||
|
||||
def arcToNativePoints(poly: Arc, sr: arcpy.SpatialReference):
|
||||
print("__Arc to native__")
|
||||
def arcToNativePolyline(poly: Union[Arc, Circle], sr: arcpy.SpatialReference):
|
||||
print("__Arc/Circle to native polyline__")
|
||||
curve = None
|
||||
try:
|
||||
pointsSpeckle = speckleArcCircleToPoints(poly)
|
||||
points = [pointToCoord(p) for p in pointsSpeckle]
|
||||
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True )
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return curve
|
||||
|
||||
|
||||
def specklePolycurveToPoints(poly: Polycurve) -> List[Point]:
|
||||
print("_____Speckle Polycurve to points____")
|
||||
points = []
|
||||
if poly.startPoint.x == poly.plane.origin.x: angle1 = math.pi/2
|
||||
else: angle1 = atan( abs ((poly.startPoint.y - poly.plane.origin.y) / (poly.startPoint.x - poly.plane.origin.x) )) # between 0 and pi/2
|
||||
#print(angle1)
|
||||
if poly.plane.origin.x < poly.startPoint.x and poly.plane.origin.y > poly.startPoint.y: angle1 = 2*math.pi - angle1
|
||||
if poly.plane.origin.x > poly.startPoint.x and poly.plane.origin.y > poly.startPoint.y: angle1 = math.pi + angle1
|
||||
if poly.plane.origin.x > poly.startPoint.x and poly.plane.origin.y < poly.startPoint.y: angle1 = math.pi - angle1
|
||||
print(angle1)
|
||||
try:
|
||||
for segm in poly.segments:
|
||||
#print(segm)
|
||||
pts = []
|
||||
if isinstance(segm, Arc) or isinstance(segm, Circle): # or isinstance(segm, Curve):
|
||||
print("Arc or Curve")
|
||||
pts: List[Point] = speckleArcCircleToPoints(segm)
|
||||
elif isinstance(segm, Line):
|
||||
print("Line")
|
||||
pts: List[Point] = [segm.start, segm.end]
|
||||
elif isinstance(segm, Polyline):
|
||||
print("Polyline")
|
||||
pts: List[Point] = segm.as_points()
|
||||
|
||||
if poly.endPoint.x == poly.plane.origin.x: angle2 = math.pi/2
|
||||
else: angle2 = atan( abs ((poly.endPoint.y - poly.plane.origin.y) / (poly.endPoint.x - poly.plane.origin.x) )) # between 0 and pi/2
|
||||
#print(angle2)
|
||||
if poly.plane.origin.x < poly.endPoint.x and poly.plane.origin.y > poly.endPoint.y: angle2 = 2*math.pi - angle2
|
||||
if poly.plane.origin.x > poly.endPoint.x and poly.plane.origin.y > poly.endPoint.y: angle2 = math.pi + angle2
|
||||
if poly.plane.origin.x > poly.endPoint.x and poly.plane.origin.y < poly.endPoint.y: angle2 = math.pi - angle2
|
||||
print(angle2)
|
||||
points.extend(pts)
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return points
|
||||
|
||||
try: interval = math.floor(poly.endAngle - poly.startAngle)
|
||||
except: interval = math.floor(angle2-angle1)
|
||||
pointsNum = math.floor( abs(interval)) * 12
|
||||
if pointsNum <4: pointsNum = 4
|
||||
points.append(pointToCoord(poly.startPoint))
|
||||
print(points)
|
||||
print(interval)
|
||||
print(pointsNum)
|
||||
for i in range(1, pointsNum + 1):
|
||||
k = i/pointsNum # to reset values from 1/10 to 1
|
||||
if poly.plane.normal.z == 0: normal = 1
|
||||
else: normal = poly.plane.normal.z
|
||||
angle = angle1 + k * interval * normal
|
||||
print(f"k: {str(i)} multiplied: {str(k*interval)} angle: {str(angle1 + k * interval)}")
|
||||
#print(cos(angle))
|
||||
pt = Point( x = poly.plane.origin.x + poly.radius * cos(angle), y = poly.plane.origin.y + poly.radius * sin(angle), z = 0)
|
||||
pt.units = poly.startPoint.units
|
||||
points.append(pointToCoord(pt))
|
||||
#print(pointToCoord(pt))
|
||||
points.append(pointToCoord(poly.endPoint))
|
||||
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True )
|
||||
return curve
|
||||
def speckleArcCircleToPoints(poly: Union[Arc, Circle]) -> List[Point]:
|
||||
print("__Arc or Circle to Points___")
|
||||
points = []
|
||||
try:
|
||||
#print(poly.plane)
|
||||
#print(poly.plane.normal)
|
||||
if poly.plane is None or poly.plane.normal.z == 0: normal = 1
|
||||
else: normal = poly.plane.normal.z
|
||||
#print(poly.plane.origin)
|
||||
if isinstance(poly, Circle):
|
||||
interval = 2*math.pi
|
||||
range_start = 0
|
||||
angle1 = 0
|
||||
|
||||
else: # if Arc
|
||||
points.append(poly.startPoint)
|
||||
range_start = 0
|
||||
|
||||
#angle1, angle2 = getArcAngles(poly)
|
||||
|
||||
interval, angle1, angle2 = getArcRadianAngle(poly)
|
||||
interval = abs(angle2 - angle1)
|
||||
|
||||
#print(angle1)
|
||||
#print(angle2)
|
||||
|
||||
if (angle1 > angle2 and normal == -1) or (angle2 > angle1 and normal == 1): pass
|
||||
if angle1 > angle2 and normal == 1: interval = abs( (2*math.pi-angle1) + angle2)
|
||||
if angle2 > angle1 and normal == -1: interval = abs( (2*math.pi-angle2) + angle1)
|
||||
|
||||
#print(interval)
|
||||
#print(normal)
|
||||
|
||||
pointsNum = math.floor( abs(interval)) * 12
|
||||
if pointsNum <4: pointsNum = 4
|
||||
|
||||
for i in range(range_start, pointsNum + 1):
|
||||
k = i/pointsNum # to reset values from 1/10 to 1
|
||||
angle = angle1 + k * interval * normal
|
||||
#print(k)
|
||||
#print(angle)
|
||||
pt = Point( x = poly.plane.origin.x + poly.radius * cos(angle), y = poly.plane.origin.y + poly.radius * sin(angle), z = 0)
|
||||
|
||||
pt.units = poly.plane.origin.units
|
||||
points.append(pt)
|
||||
if isinstance(poly, Arc): points.append(poly.endPoint)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return points
|
||||
|
||||
|
||||
def getArcRadianAngle(arc: Arc) -> List[float]:
|
||||
try:
|
||||
interval = None
|
||||
normal = arc.plane.normal.z
|
||||
angle1, angle2 = getArcAngles(arc)
|
||||
if angle1 is None or angle2 is None: return None
|
||||
interval = abs(angle2 - angle1)
|
||||
|
||||
if (angle1 > angle2 and normal == -1) or (angle2 > angle1 and normal == 1): pass
|
||||
if angle1 > angle2 and normal == 1: interval = abs( (2*math.pi-angle1) + angle2)
|
||||
if angle2 > angle1 and normal == -1: interval = abs( (2*math.pi-angle2) + angle1)
|
||||
return interval, angle1, angle2
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None, None, None
|
||||
|
||||
def getArcAngles(poly: Arc) -> Tuple[float]:
|
||||
try:
|
||||
if poly.startPoint.x == poly.plane.origin.x: angle1 = math.pi/2
|
||||
else: angle1 = atan( abs ((poly.startPoint.y - poly.plane.origin.y) / (poly.startPoint.x - poly.plane.origin.x) )) # between 0 and pi/2
|
||||
|
||||
if poly.plane.origin.x < poly.startPoint.x and poly.plane.origin.y > poly.startPoint.y: angle1 = 2*math.pi - angle1
|
||||
if poly.plane.origin.x > poly.startPoint.x and poly.plane.origin.y > poly.startPoint.y: angle1 = math.pi + angle1
|
||||
if poly.plane.origin.x > poly.startPoint.x and poly.plane.origin.y < poly.startPoint.y: angle1 = math.pi - angle1
|
||||
|
||||
if poly.endPoint.x == poly.plane.origin.x: angle2 = math.pi/2
|
||||
else: angle2 = atan( abs ((poly.endPoint.y - poly.plane.origin.y) / (poly.endPoint.x - poly.plane.origin.x) )) # between 0 and pi/2
|
||||
|
||||
if poly.plane.origin.x < poly.endPoint.x and poly.plane.origin.y > poly.endPoint.y: angle2 = 2*math.pi - angle2
|
||||
if poly.plane.origin.x > poly.endPoint.x and poly.plane.origin.y > poly.endPoint.y: angle2 = math.pi + angle2
|
||||
if poly.plane.origin.x > poly.endPoint.x and poly.plane.origin.y < poly.endPoint.y: angle2 = math.pi - angle2
|
||||
|
||||
return angle1, angle2
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None, None
|
||||
|
||||
def getArcNormal(poly: Arc, midPt: Point):
|
||||
print("____getArcNormal___")
|
||||
try:
|
||||
angle1, angle2 = getArcAngles(poly)
|
||||
|
||||
if midPt.x == poly.plane.origin.x: angle = math.pi/2
|
||||
else: angle = atan( abs ((midPt.y - poly.plane.origin.y) / (midPt.x - poly.plane.origin.x) )) # between 0 and pi/2
|
||||
|
||||
if poly.plane.origin.x < midPt.x and poly.plane.origin.y > midPt.y: angle = 2*math.pi - angle
|
||||
if poly.plane.origin.x > midPt.x and poly.plane.origin.y > midPt.y: angle = math.pi + angle
|
||||
if poly.plane.origin.x > midPt.x and poly.plane.origin.y < midPt.y: angle = math.pi - angle
|
||||
|
||||
normal = Vector()
|
||||
normal.x = normal.y = 0
|
||||
|
||||
if angle1 > angle > angle2: normal.z = -1
|
||||
if angle1 > angle2 > angle: normal.z = 1
|
||||
|
||||
if angle2 > angle1 > angle: normal.z = -1
|
||||
if angle > angle1 > angle2: normal.z = 1
|
||||
|
||||
if angle2 > angle > angle1: normal.z = 1
|
||||
if angle > angle2 > angle1: normal.z = -1
|
||||
|
||||
print(angle1)
|
||||
print(angle)
|
||||
print(angle2)
|
||||
print(normal)
|
||||
return normal
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
|
||||
def getArcCenter(p1: Point, p2: Point, p3: Point) -> Tuple[List, float]:
|
||||
#print(p1)
|
||||
try:
|
||||
p1 = np.array(p1.to_list())
|
||||
p2 = np.array(p2.to_list())
|
||||
p3 = np.array(p3.to_list())
|
||||
a = np.linalg.norm(p3 - p2)
|
||||
b = np.linalg.norm(p3 - p1)
|
||||
c = np.linalg.norm(p2 - p1)
|
||||
s = (a + b + c) / 2
|
||||
radius = a*b*c / 4 / np.sqrt(s * (s - a) * (s - b) * (s - c))
|
||||
b1 = a*a * (b*b + c*c - a*a)
|
||||
b2 = b*b * (a*a + c*c - b*b)
|
||||
b3 = c*c * (a*a + b*b - c*c)
|
||||
center = np.column_stack((p1, p2, p3)).dot(np.hstack((b1, b2, b3)))
|
||||
center /= b1 + b2 + b3
|
||||
center = center.tolist()
|
||||
return center, radius
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None, None
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
from specklepy.objects.geometry import Point, Line, Polyline, Circle, Arc, Polycurve
|
||||
from specklepy.objects import Base
|
||||
from typing import List, Union
|
||||
|
||||
import inspect
|
||||
|
||||
try:
|
||||
from speckle.converter.geometry.polyline import speckleArcCircleToPoints, specklePolycurveToPoints
|
||||
from speckle.ui.logger import logToUser
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry.polyline import speckleArcCircleToPoints, specklePolycurveToPoints
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
|
||||
def speckleBoundaryToSpecklePts(boundary: Union[None, Polyline, Arc, Line, Polycurve]) -> List[Point]:
|
||||
#print("__speckleBoundaryToSpecklePts__")
|
||||
# add boundary points
|
||||
polyBorder = []
|
||||
try:
|
||||
if isinstance(boundary, Circle) or isinstance(boundary, Arc):
|
||||
polyBorder = speckleArcCircleToPoints(boundary)
|
||||
elif isinstance(boundary, Polycurve):
|
||||
polyBorder = specklePolycurveToPoints(boundary)
|
||||
elif isinstance(boundary, Line): pass
|
||||
else:
|
||||
try: polyBorder = boundary.as_points()
|
||||
except: pass # if Line or None
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return polyBorder
|
||||
@@ -1,7 +1,11 @@
|
||||
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
|
||||
|
||||
|
||||
|
||||
class Layer(Base, chunkable={"features": 100}):
|
||||
@@ -27,14 +31,37 @@ class Layer(Base, chunkable={"features": 100}):
|
||||
self.geomType = geomType
|
||||
self.renderer = renderer
|
||||
|
||||
class RasterLayer(Base, chunkable={"features": 100}):
|
||||
"""A GIS Layer"""
|
||||
class VectorLayer(Base, chunkable={"features": 100}):
|
||||
"""A GIS Vector Layer"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name=None,
|
||||
crs=None,
|
||||
rasterCrs=None,
|
||||
name: Optional[str] = None,
|
||||
crs: Optional[CRS] = None,
|
||||
datum: Optional[CRS] = None,
|
||||
features: List[Base] = [],
|
||||
layerType: str = "None",
|
||||
geomType: str = "None",
|
||||
renderer: dict = {},
|
||||
**kwargs
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.name = name
|
||||
self.crs = crs
|
||||
self.datum = datum
|
||||
self.type = layerType
|
||||
self.features = features
|
||||
self.geomType = geomType
|
||||
self.renderer = renderer
|
||||
|
||||
class RasterLayer(Base, chunkable={"features": 100}):
|
||||
"""A GIS Raster Layer"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
crs: Optional[CRS] =None,
|
||||
rasterCrs: Optional[CRS] = None,
|
||||
features: List[Base] = [],
|
||||
layerType: str = "None",
|
||||
geomType: str = "None",
|
||||
|
||||
@@ -1,492 +0,0 @@
|
||||
"""
|
||||
Contains all Layer related classes and methods.
|
||||
"""
|
||||
import os
|
||||
from typing import Any, List, Union
|
||||
|
||||
from regex import D
|
||||
from speckle.converter.layers.CRS import CRS
|
||||
from speckle.converter.layers.Layer import Layer, RasterLayer
|
||||
from speckle.converter.layers.feature import featureToNative, featureToSpeckle, cadFeatureToNative
|
||||
from specklepy.objects import Base
|
||||
|
||||
import arcgisscripting
|
||||
import pandas as pd
|
||||
import arcpy
|
||||
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
|
||||
|
||||
|
||||
def convertSelectedLayers(all_layers: List[arcpy._mp.Layer], selected_layers: List[str], project: arcpy.mp.ArcGISProject) -> List[Layer]:
|
||||
"""Converts the current selected layers to Speckle"""
|
||||
print("________Convert Layers_________")
|
||||
result = []
|
||||
for layer in selected_layers:
|
||||
layerToSend = None
|
||||
for c in range(len(all_layers)):
|
||||
if int(layer.split("-",1)[0]) == c:
|
||||
layerToSend = all_layers[c]
|
||||
break
|
||||
if layerToSend is not None:
|
||||
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 layer.name() in selectedLayerNames:
|
||||
result.append(layerToSpeckle(layer, projectCRS, project))
|
||||
'''
|
||||
#print(result)
|
||||
return result
|
||||
|
||||
def layerToSpeckle(layer: arcLayer, project: ArcGISProject) -> Layer: #now the input is QgsVectorLayer instead of qgis._core.QgsLayerTreeLayer
|
||||
"""Converts a given QGIS Layer to Speckle"""
|
||||
print("________Convert Feature Layer_________")
|
||||
|
||||
projectCRS = project.activeMap.spatialReference
|
||||
try: data = arcpy.Describe(layer.dataSource)
|
||||
except OSError as e: arcpy.AddWarning(str(e.args[0])); return
|
||||
#print(projectCRS)
|
||||
#print(projectCRS.name)
|
||||
crs = CRS(name = projectCRS.name, wkt = projectCRS.exportToString(), units = "m")
|
||||
|
||||
layer_geo_crs = None
|
||||
datum = None
|
||||
#if data.spatialReference.type == "Projected":
|
||||
# #layer_geo_crs =
|
||||
# datum = CRS(name = layer_geo_crs.name, wkt = layer_geo_crs.exportToString(), units = "m")
|
||||
|
||||
speckleLayer = Layer()
|
||||
speckleLayer.type="VectorLayer"
|
||||
speckleLayer.name = layer.name
|
||||
speckleLayer.crs = crs
|
||||
speckleLayer.datum = datum
|
||||
|
||||
try: # https://pro.arcgis.com/en/pro-app/2.8/arcpy/get-started/the-spatial-reference-object.htm
|
||||
layerObjs = []
|
||||
print(data.datasetType)
|
||||
if data.datasetType == "FeatureClass": #FeatureClass, ?Table Properties, ?Datasets
|
||||
# write feature attributes
|
||||
fieldnames = [field.name for field in data.fields]
|
||||
#print(layer.longName) # e.g. 17b0b76d13_custom_crs_04dcfaa936\04dcfaa936_Vector_lineGeom
|
||||
#print(fieldnames) # e.g. ['OBJECTID', 'Shape', 'Shape_Length', 'Speckle_ID', 'number', 'area']
|
||||
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(rows_shapes) # <da.SearchCursor object at 0x00000172565E6C10>
|
||||
print("__ start iterating features")
|
||||
# write feature attributes
|
||||
for i, features in enumerate(rows_shapes):
|
||||
print("____Feature # " + str(i+1))
|
||||
if features[0] == None: continue
|
||||
#print(features[0].hasCurves)
|
||||
if features[0].hasCurves: continue
|
||||
rows_attributes = arcpy.da.SearchCursor(layer.longName, fieldnames)
|
||||
row_attr = []
|
||||
for k, attrs in enumerate(rows_attributes):
|
||||
if i == k: row_attr = attrs; break
|
||||
|
||||
#print(features) #(<Polygon object at 0x172592ae8c8[0x17258d2a600]>,)
|
||||
#print(features[0].pointCount)
|
||||
#print(features[0].partCount)
|
||||
if features[0]:
|
||||
b = featureToSpeckle(fieldnames, row_attr, features[0], projectCRS, project, layer)
|
||||
layerObjs.append(b)
|
||||
#print(layerObjs)
|
||||
|
||||
print("__ finish iterating features")
|
||||
speckleLayer.features=layerObjs
|
||||
speckleLayer.geomType = data.shapeType
|
||||
|
||||
except OSError as e:
|
||||
arcpy.AddWarning(str(e))
|
||||
return
|
||||
|
||||
return speckleLayer
|
||||
|
||||
def layerToNative(layer: Union[Layer, RasterLayer], streamBranch: str, project: ArcGISProject):
|
||||
|
||||
if layer.type is None:
|
||||
# Handle this case
|
||||
return
|
||||
elif layer.type.endswith("VectorLayer"):
|
||||
return vectorLayerToNative(layer, streamBranch, project)
|
||||
elif layer.type.endswith("RasterLayer"):
|
||||
return rasterLayerToNative(layer, streamBranch, project)
|
||||
return None
|
||||
|
||||
def cadLayerToNative(layerContentList:Base, layerName: str, streamBranch: str, project: ArcGISProject) :
|
||||
|
||||
geom_points = []
|
||||
geom_polylines = []
|
||||
geom_polygones = []
|
||||
geom_meshes = []
|
||||
#filter speckle objects by type within each layer, create sub-layer for each type (points, lines, polygons, mesh?)
|
||||
for geom in layerContentList:
|
||||
#print(geom)
|
||||
if geom.speckle_type == "Objects.Geometry.Point":
|
||||
geom_points.append(geom)
|
||||
if geom.speckle_type == "Objects.Geometry.Line" or geom.speckle_type == "Objects.Geometry.Polyline" or geom.speckle_type == "Objects.Geometry.Curve" or geom.speckle_type == "Objects.Geometry.Arc" or geom.speckle_type == "Objects.Geometry.Circle" or geom.speckle_type == "Objects.Geometry.Polycurve":
|
||||
geom_polylines.append(geom)
|
||||
|
||||
if len(geom_points)>0: layer_points = cadVectorLayerToNative(geom_points, layerName, "Points", streamBranch, project)
|
||||
if len(geom_polylines)>0: layer_polylines = cadVectorLayerToNative(geom_polylines, layerName, "Polylines", streamBranch, project)
|
||||
|
||||
return [layer_points, layer_polylines]
|
||||
|
||||
def cadVectorLayerToNative(geomList, layerName: str, geomType: str, streamBranch: str, project: ArcGISProject):
|
||||
print("_________CAD vector layer to native_____")
|
||||
#get Project CRS, use it by default for the new received layer
|
||||
vl = None
|
||||
layerName = layerName.replace("[","_").replace("]","_").replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
|
||||
layerName = layerName + "_" + geomType
|
||||
|
||||
sr = arcpy.SpatialReference(project.activeMap.spatialReference.name)
|
||||
active_map = project.activeMap
|
||||
path = project.filePath.replace("aprx","gdb") #"\\".join(project.filePath.split("\\")[:-1]) + "\\speckle_layers\\" #arcpy.env.workspace + "\\" #
|
||||
|
||||
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}'
|
||||
|
||||
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 "polygon" in geomType.lower(): geomType = "Polygon"
|
||||
if "line" in geomType.lower(): geomType = "Polyline"
|
||||
if "multipoint" in geomType.lower(): geomType = "Multipoint"
|
||||
elif "point" in geomType.lower(): geomType = "Point"
|
||||
#print(geomType)
|
||||
|
||||
#print(newName)
|
||||
#path = r"C:\Users\username\Documents\ArcGIS\Projects\MyProject-test\MyProject-test.gdb\\"
|
||||
#https://community.esri.com/t5/arcgis-pro-questions/is-it-possible-to-create-a-new-group-layer-with/td-p/1068607
|
||||
|
||||
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)
|
||||
#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"]
|
||||
matrix = []
|
||||
all_keys = []
|
||||
all_key_types = []
|
||||
max_len = 52
|
||||
for key, value in newFields.items():
|
||||
existingFields = [fl.name for fl in arcpy.ListFields(class_name)]
|
||||
if key not in existingFields and key.lower() not in fields_to_ignore: # exclude geometry and default existing fields
|
||||
# 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(matrix)
|
||||
if len(matrix)>0: AddFields(str(f_class), matrix)
|
||||
|
||||
fets = []
|
||||
for f in geomList[:]:
|
||||
new_feat = cadFeatureToNative(f, newFields, sr)
|
||||
if new_feat != "" and new_feat != None:
|
||||
fets.append(new_feat)
|
||||
#print("features created")
|
||||
#print(fets)
|
||||
|
||||
count = 0
|
||||
rowValues = []
|
||||
for feat in fets:
|
||||
try: feat['applicationId']
|
||||
except: feat.update({'applicationId': count})
|
||||
|
||||
row = [feat['arcGisGeomFromSpeckle'], feat['applicationId']]
|
||||
heads = [ 'Shape@', 'OBJECTID']
|
||||
|
||||
for key,value in feat.items():
|
||||
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) )
|
||||
for row in rowValues:
|
||||
#print(tuple(heads))
|
||||
#print(tuple(row))
|
||||
cur.insertRow(tuple(row))
|
||||
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)
|
||||
|
||||
return vl
|
||||
|
||||
def vectorLayerToNative(layer: Layer, streamBranch: str, project: ArcGISProject):
|
||||
print("_________Vector Layer to Native_________")
|
||||
vl = None
|
||||
layerName = layer.name.replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
|
||||
|
||||
print(layerName)
|
||||
sr = arcpy.SpatialReference(text=layer.crs.wkt)
|
||||
active_map = project.activeMap
|
||||
path = project.filePath.replace("aprx","gdb") #"\\".join(project.filePath.split("\\")[:-1]) + "\\speckle_layers\\" #arcpy.env.workspace + "\\" #
|
||||
#if not os.path.exists(path): os.makedirs(path)
|
||||
#print(path)
|
||||
|
||||
#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}'
|
||||
|
||||
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
|
||||
geomType = layer.geomType # for ArcGIS: Polygon, Point, Polyline, Multipoint, MultiPatch
|
||||
print(geomType)
|
||||
if "polygon" in geomType.lower(): geomType = "Polygon"
|
||||
if "line" in geomType.lower(): geomType = "Polyline"
|
||||
if "multipoint" in geomType.lower(): geomType = "Multipoint"
|
||||
elif "point" in geomType.lower(): geomType = "Point"
|
||||
#print(geomType)
|
||||
|
||||
#print(newName)
|
||||
#path = r"C:\Users\username\Documents\ArcGIS\Projects\MyProject-test\MyProject-test.gdb\\"
|
||||
#https://community.esri.com/t5/arcgis-pro-questions/is-it-possible-to-create-a-new-group-layer-with/td-p/1068607
|
||||
#print(project.filePath.replace("aprx","gdb"))
|
||||
#print("_________create feature class___________________________________")
|
||||
# should be created inside the workspace to be a proper Feature class (not .shp) with Nullable Fields
|
||||
class_name = "f_class_" + newName
|
||||
#print(class_name)
|
||||
try: f_class = CreateFeatureclass(path, class_name, geomType, has_z="ENABLED", spatial_reference = sr)
|
||||
except arcgisscripting.ExecuteError: class_name+="_"; f_class = CreateFeatureclass(path, class_name, geomType, has_z="ENABLED", spatial_reference = sr)
|
||||
|
||||
# get and set Layer attribute fields
|
||||
# example: https://resource.esriuk.com/blog/an-introductory-slice-of-arcpy-in-arcgis-pro/
|
||||
newFields = getLayerAttributes(layer.features)
|
||||
fields_to_ignore = ["arcgisgeomfromspeckle", "shape", "objectid"]
|
||||
matrix = []
|
||||
all_keys = []
|
||||
all_key_types = []
|
||||
max_len = 52
|
||||
for key, value in newFields.items():
|
||||
existingFields = [fl.name for fl in arcpy.ListFields(class_name)]
|
||||
if key not in existingFields and key.lower() not in fields_to_ignore: # exclude geometry and default existing fields
|
||||
# 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(matrix)
|
||||
if len(matrix)>0: AddFields(str(f_class), matrix)
|
||||
|
||||
fets = []
|
||||
for f in layer.features:
|
||||
new_feat = featureToNative(f, newFields, geomType, sr)
|
||||
if new_feat != "" and new_feat!= None: fets.append(new_feat)
|
||||
|
||||
print(fets)
|
||||
count = 0
|
||||
rowValues = []
|
||||
for feat in fets:
|
||||
#print(feat)
|
||||
try: feat['applicationId']
|
||||
except: feat.update({'applicationId': count})
|
||||
|
||||
row = [feat['arcGisGeomFromSpeckle'], feat['applicationId']]
|
||||
heads = [ 'Shape@', 'OBJECTID']
|
||||
|
||||
for key,value in feat.items():
|
||||
if key in all_keys and key.lower() not in fields_to_ignore:
|
||||
heads.append(key)
|
||||
row.append(value)
|
||||
rowValues.append(row)
|
||||
count += 1
|
||||
cur = arcpy.da.InsertCursor(str(f_class), tuple(heads) )
|
||||
for row in rowValues:
|
||||
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)
|
||||
r'''
|
||||
# rename back the layer if was renamed due to existing duplicate
|
||||
if layerExists:
|
||||
vl.name = newName[:len(newName)-2]
|
||||
for lyr in project.activeMap.listLayers():
|
||||
print(lyr.longName)
|
||||
if (streamBranch + "\\" + newName) == lyr.longName:
|
||||
lyr.name = lyr.name.replace( lyr.name, lyr.name[:len(lyr.name)-2] )
|
||||
lyr.longName = lyr.longName.replace( lyr.longName, lyr.longName[:len(newName)-2] )
|
||||
break
|
||||
'''
|
||||
|
||||
r'''
|
||||
pr.addFeatures(fets)
|
||||
vl.updateExtents()
|
||||
vl.commitChanges()
|
||||
#layerGroup.addLayer(vl)
|
||||
|
||||
rendererNew = vectorRendererToNative(layer)
|
||||
if rendererNew is None:
|
||||
symbol = QgsSymbol.defaultSymbol(QgsWkbTypes.geometryType(QgsWkbTypes.parseType(geomType)))
|
||||
rendererNew = QgsSingleSymbolRenderer(symbol)
|
||||
|
||||
try: vl.setRenderer(rendererNew)
|
||||
except: pass
|
||||
'''
|
||||
return vl
|
||||
|
||||
def rasterLayerToNative(layer: RasterLayer, streamBranch: str, project: ArcGISProject):
|
||||
|
||||
raster_layer = None
|
||||
'''
|
||||
crs = QgsCoordinateReferenceSystem.fromWkt(layer.crs.wkt) #moved up, because CRS of existing layer needs to be rewritten
|
||||
# try, in case of older version "rasterCrs" will not exist
|
||||
try: crsRaster = QgsCoordinateReferenceSystem.fromWkt(layer.rasterCrs.wkt) #moved up, because CRS of existing layer needs to be rewritten
|
||||
except:
|
||||
crsRaster = crs
|
||||
logger.logToUser(f"Raster layer {layer.name} might have been sent from the older version of plugin. Try sending it again for more accurate results.", Qgis.Warning)
|
||||
|
||||
#CREATE A GROUP "received blabla" with sublayers
|
||||
newGroupName = f'{streamBranch}'
|
||||
root = QgsProject.instance().layerTreeRoot()
|
||||
layerGroup = QgsLayerTreeGroup(newGroupName)
|
||||
|
||||
if root.findGroup(newGroupName) is not None:
|
||||
layerGroup = root.findGroup(newGroupName)
|
||||
else:
|
||||
root.addChildNode(layerGroup)
|
||||
layerGroup.setExpanded(True)
|
||||
layerGroup.setItemVisibilityChecked(True)
|
||||
|
||||
#find ID of the layer with a matching name in the "latest" group
|
||||
newName = f'{streamBranch}/{layer.name}'
|
||||
|
||||
######################## testing, only for receiving layers #################
|
||||
source_folder = QgsProject.instance().absolutePath()
|
||||
|
||||
if(source_folder == ""):
|
||||
logger.logToUser(f"Raster layers can only be received in an existing saved project. Layer {layer.name} will be ignored", Qgis.Warning)
|
||||
return None
|
||||
|
||||
project = QgsProject.instance()
|
||||
projectCRS = QgsCoordinateReferenceSystem.fromWkt(layer.crs.wkt)
|
||||
crsid = crsRaster.authid()
|
||||
try: epsg = int(crsid.split(":")[1])
|
||||
except:
|
||||
epsg = int(str(projectCRS).split(":")[len(str(projectCRS).split(":"))-1].split(">")[0])
|
||||
logger.logToUser(f"CRS of the received raster cannot be identified. Project CRS will be used.", Qgis.Warning)
|
||||
|
||||
feat = layer.features[0]
|
||||
bandNames = feat["Band names"]
|
||||
bandValues = [feat["@(10000)" + name + "_values"] for name in bandNames]
|
||||
|
||||
#newName = f'{streamBranch}_latest_{layer.name}'
|
||||
|
||||
###########################################################################
|
||||
|
||||
## https://opensourceoptions.com/blog/pyqgis-create-raster/
|
||||
# creating file in temporary folder: https://stackoverflow.com/questions/56038742/creating-in-memory-qgsrasterlayer-from-the-rasterization-of-a-qgsvectorlayer-wit
|
||||
|
||||
fn = source_folder + '/' + newName.replace("/","_") + '.tif' #'_received_raster.tif'
|
||||
driver = gdal.GetDriverByName('GTiff')
|
||||
# create raster dataset
|
||||
ds = driver.Create(fn, xsize=feat["X pixels"], ysize=feat["Y pixels"], bands=feat["Band count"], eType=gdal.GDT_Float32)
|
||||
|
||||
# Write data to raster band
|
||||
for i in range(feat["Band count"]):
|
||||
rasterband = np.array(bandValues[i])
|
||||
rasterband = np.reshape(rasterband,(feat["Y pixels"], feat["X pixels"]))
|
||||
ds.GetRasterBand(i+1).WriteArray(rasterband) # or "rasterband.T"
|
||||
|
||||
# create GDAL transformation in format [top-left x coord, cell width, 0, top-left y coord, 0, cell height]
|
||||
pt = pointToNative(feat["displayValue"][0])
|
||||
xform = QgsCoordinateTransform(crs, crsRaster, project)
|
||||
pt.transform(xform)
|
||||
ds.SetGeoTransform([pt.x(), feat["X resolution"], 0, pt.y(), 0, feat["Y resolution"]])
|
||||
# create a spatial reference object
|
||||
srs = osr.SpatialReference()
|
||||
# For the Universal Transverse Mercator the SetUTM(Zone, North=1 or South=2)
|
||||
srs.ImportFromEPSG(epsg) # from https://gis.stackexchange.com/questions/34082/creating-raster-layer-from-numpy-array-using-pyqgis
|
||||
ds.SetProjection(srs.ExportToWkt())
|
||||
# close the rater datasource by setting it equal to None
|
||||
ds = None
|
||||
|
||||
raster_layer = QgsRasterLayer(fn, newName, 'gdal')
|
||||
QgsProject.instance().addMapLayer(raster_layer, False)
|
||||
layerGroup.addLayer(raster_layer)
|
||||
|
||||
dataProvider = raster_layer.dataProvider()
|
||||
rendererNew = rasterRendererToNative(layer, dataProvider)
|
||||
|
||||
try: raster_layer.setRenderer(rendererNew)
|
||||
except: pass
|
||||
'''
|
||||
return raster_layer
|
||||
@@ -1,144 +1,640 @@
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
|
||||
from typing import Dict, Any, Callable, List, Optional, Tuple
|
||||
|
||||
from specklepy.objects import Base
|
||||
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 getVariantFromValue
|
||||
import inspect
|
||||
|
||||
def featureToSpeckle(fieldnames, attr_list, f_shape, projectCRS: arcpy.SpatialReference, project: arcpy.mp.ArcGISProject, selectedLayer):
|
||||
try:
|
||||
from speckle.converter.geometry 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 constructMeshFromRaster, meshToNative
|
||||
from speckle.converter.layers.symbology import jsonFromLayerStyle
|
||||
from speckle.ui.logger import logToUser
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.geometry 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 constructMeshFromRaster, meshToNative
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.symbology import jsonFromLayerStyle
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
import numpy as np
|
||||
import colorsys
|
||||
|
||||
|
||||
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(layer_sr.name)
|
||||
#print(projectCRS.name)
|
||||
|
||||
#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(tr0)
|
||||
|
||||
if geomType != "Point" and geomType != "Polyline" and geomType != "Polygon" and geomType != "Multipoint":
|
||||
#print(geomType)
|
||||
arcpy.AddWarning("Unsupported or invalid geometry in layer " + selectedLayer.name)
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
######################################### Convert geometry ##########################################
|
||||
try:
|
||||
geom = convertToSpeckle(f_shape, selectedLayer, geomType, featureType)
|
||||
if geom is not None: print(geom); b["geometry"] = geom
|
||||
except Exception as error:
|
||||
print("Error converting geometry: " + str(error))
|
||||
print(selectedLayer)
|
||||
arcpy.AddError("Error converting geometry: " + str(error))
|
||||
#print(geomType)
|
||||
#print(featureType)
|
||||
for i, name in enumerate(fieldnames):
|
||||
corrected = name.replace("/", "_").replace(".", "-")
|
||||
if corrected != "Shape" and corrected != "Shape@":
|
||||
# different ID behaviors: https://support.esri.com/en/technical-article/000010834
|
||||
# save all attribute, duplicate one into applicationId
|
||||
b[corrected] = attr_list[i]
|
||||
if corrected == "FID" or corrected == "OID" or corrected == "OBJECTID": b["applicationId"] = str(attr_list[i])
|
||||
#print(b)
|
||||
print("______end of __Feature to Speckle____________________")
|
||||
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 hasattr(data, "isRevit") or hasattr(data, "isIFC") or hasattr(data, "bimLevels"):
|
||||
print(f"Layer {selectedLayer.name} has unsupported data type")
|
||||
logToUser(f"Layer {selectedLayer.name} has unsupported data type", level=1, func = inspect.stack()[0][3])
|
||||
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, index, selectedLayer, geomType, featureType)
|
||||
if geom is not None: print(geom); b["geometry"] = geom
|
||||
else: b["geometry"] = []
|
||||
except Exception as error:
|
||||
print("Error converting geometry: " + str(error))
|
||||
print(selectedLayer)
|
||||
logToUser("Error converting geometry: " + str(error), level=2, func = inspect.stack()[0][3])
|
||||
print(geom)
|
||||
#print(featureType)
|
||||
for i, name in enumerate(fieldnames):
|
||||
corrected = name.replace("/", "_").replace(".", "-")
|
||||
if corrected != "Shape" and corrected != "Shape@":
|
||||
# different ID behaviors: https://support.esri.com/en/technical-article/000010834
|
||||
# save all attribute, duplicate one into applicationId
|
||||
b[corrected] = attr_list[i]
|
||||
if corrected == "FID" or corrected == "OID" or corrected == "OBJECTID": b["applicationId"] = str(attr_list[i])
|
||||
#print(b)
|
||||
print("______end of __Feature to Speckle____________________")
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return b
|
||||
|
||||
def featureToNative(feature: Base, fields: dict, geomType: str, sr: arcpy.SpatialReference):
|
||||
print("Feature To Native____________")
|
||||
print("04_____Feature To Native____________")
|
||||
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)
|
||||
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)
|
||||
else:
|
||||
arcGisGeom = convertToNative(speckle_geom, sr)
|
||||
try:
|
||||
try: speckle_geom = feature["geometry"] # for created in QGIS / ArcGIS Layer type
|
||||
except: speckle_geom = feature # for created in other software
|
||||
#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)
|
||||
else:
|
||||
arcGisGeom = convertToNative(speckle_geom, sr)
|
||||
|
||||
if arcGisGeom is not None:
|
||||
feat.update({"arcGisGeomFromSpeckle": arcGisGeom})
|
||||
else:
|
||||
return None
|
||||
print(feat)
|
||||
for key, variant in fields.items():
|
||||
if arcGisGeom is not None:
|
||||
feat.update({"arcGisGeomFromSpeckle": arcGisGeom})
|
||||
else:
|
||||
return None
|
||||
|
||||
value = feature[key]
|
||||
if variant == "TEXT": value = str(feature[key])
|
||||
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})
|
||||
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
|
||||
|
||||
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})
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return feat
|
||||
|
||||
def bimFeatureToNative(feature: Base, fields: dict, sr: arcpy.SpatialReference, path: str):
|
||||
#print("04_________BIM Feature To Native____________")
|
||||
feat_updated = {}
|
||||
try:
|
||||
feat = {}
|
||||
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)
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return feat_updated
|
||||
|
||||
|
||||
def cadFeatureToNative(feature: Base, fields: dict, sr: arcpy.SpatialReference):
|
||||
print("_________CAD Feature To Native____________")
|
||||
print("04_________CAD Feature To Native____________")
|
||||
feat = {}
|
||||
try: speckle_geom = feature["geometry"] # for created in QGIS Layer type
|
||||
except: speckle_geom = feature # for created in other software
|
||||
#print(feature)
|
||||
#print(speckle_geom)
|
||||
if isinstance(speckle_geom, list):
|
||||
if len(speckle_geom)>1: arcGisGeom = convertToNativeMulti(speckle_geom, sr)
|
||||
else: arcGisGeom = convertToNative(speckle_geom[0], sr)
|
||||
else:
|
||||
arcGisGeom = convertToNative(speckle_geom, sr)
|
||||
try:
|
||||
try: speckle_geom = feature["geometry"] # for created in QGIS Layer type
|
||||
except: speckle_geom = feature # for created in other software
|
||||
|
||||
if arcGisGeom is not None:
|
||||
feat.update({"arcGisGeomFromSpeckle": arcGisGeom})
|
||||
else: return None
|
||||
for key, variant in fields.items():
|
||||
value = feature[key]
|
||||
if variant == "TEXT": value = str(feature[key])
|
||||
if variant == getVariantFromValue(value) and value != "NULL" and value != "None":
|
||||
feat.update({key: value})
|
||||
if isinstance(speckle_geom, list):
|
||||
if len(speckle_geom)>1: arcGisGeom = convertToNativeMulti(speckle_geom, sr)
|
||||
else: arcGisGeom = convertToNative(speckle_geom[0], sr)
|
||||
else:
|
||||
arcGisGeom = convertToNative(speckle_geom, sr)
|
||||
|
||||
if arcGisGeom is not None:
|
||||
feat.update({"arcGisGeomFromSpeckle": arcGisGeom})
|
||||
else: return None
|
||||
|
||||
try:
|
||||
if "Speckle_ID" not in fields.keys() and feature["id"]: feat.update("Speckle_ID", "TEXT")
|
||||
except: pass
|
||||
|
||||
#### setting attributes to feature
|
||||
feat_updated = updateFeat(feat, fields, feature)
|
||||
print(feat)
|
||||
print(fields)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return feat_updated
|
||||
|
||||
def addFeatVariant(key, variant, value, f):
|
||||
#print("Add feat variant")
|
||||
feat = f
|
||||
try:
|
||||
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})
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return feat
|
||||
|
||||
def updateFeat(feat:dict, fields: dict, feature: Base) -> Dict[str, Any]:
|
||||
#print("Update feat")
|
||||
feat_sorted = {}
|
||||
try:
|
||||
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_________________________")
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return feat_sorted
|
||||
|
||||
def rasterFeatureToSpeckle(selectedLayer: arcLayer, projectCRS: arcpy.SpatialReference, project: ArcGISProject) -> Base:
|
||||
print("_________ Raster feature to speckle______")
|
||||
b = Base(units = "m")
|
||||
try:
|
||||
# https://pro.arcgis.com/en/pro-app/latest/arcpy/classes/raster-object.htm
|
||||
|
||||
# get Raster object of entire raster dataset
|
||||
my_raster = arcpy.Raster(selectedLayer.dataSource)
|
||||
print(my_raster.mdinfo) # None
|
||||
|
||||
rasterBandCount = my_raster.bandCount
|
||||
rasterBandNames = my_raster.bandNames
|
||||
rasterDimensions = [my_raster.width, my_raster.height]
|
||||
if rasterDimensions[0]*rasterDimensions[1] > 1000000 :
|
||||
arcpy.AddWarning("Large layer: ")
|
||||
|
||||
#ds = gdal.Open(selectedLayer.source(), gdal.GA_ReadOnly)
|
||||
extent = my_raster.extent
|
||||
print(extent.XMin)
|
||||
print(extent.YMin)
|
||||
rasterOriginPoint = arcpy.PointGeometry(arcpy.Point(extent.XMin, extent.YMax, extent.ZMin), my_raster.spatialReference, has_z = True)
|
||||
#if extent.YMin>0: rasterOriginPoint = arcpy.PointGeometry(arcpy.Point(extent.XMin, extent.YMax, extent.ZMin), my_raster.spatialReference, has_z = True)
|
||||
print(rasterOriginPoint)
|
||||
rasterResXY = [my_raster.meanCellWidth, my_raster.meanCellHeight] #[float(ds.GetGeoTransform()[1]), float(ds.GetGeoTransform()[5])]
|
||||
rasterBandNoDataVal = [] #list(my_raster.noDataValues)
|
||||
rasterBandMinVal = []
|
||||
rasterBandMaxVal = []
|
||||
rasterBandVals = []
|
||||
|
||||
|
||||
# Try to extract geometry
|
||||
reprojectedPt = None
|
||||
|
||||
try:
|
||||
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]
|
||||
print(geom)
|
||||
except Exception as error:
|
||||
logToUser("Error converting point geometry: " + str(error), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
for i, item in enumerate(rasterBandNames):
|
||||
print(item)
|
||||
rb = my_raster.getRasterBands(item)
|
||||
print(rb)
|
||||
print(np.shape(rb.read()))
|
||||
valMin = rb.minimum
|
||||
valMax = rb.maximum
|
||||
bandVals = np.swapaxes(rb.read(), 1, 2).flatten() #.tolist() np.flip( , 0)
|
||||
|
||||
bandValsFlat = []
|
||||
bandValsFlat.extend(bandVals.tolist())
|
||||
#print(bandValsFlat)
|
||||
|
||||
const = float(-1* math.pow(10,30))
|
||||
defaultNoData = rb.noDataValue
|
||||
|
||||
# check whether NA value is too small or raster has too small values
|
||||
# assign min value of an actual list; re-assign NA val; replace list items to new NA val
|
||||
try:
|
||||
# create "safe" fake NA value; replace extreme values with it
|
||||
fakeNA = max(bandValsFlat) + 1
|
||||
bandValsFlatFake = [fakeNA if val<=const else val for val in bandValsFlat] # replace all values corresponding to NoData value
|
||||
|
||||
#if default NA value is too small
|
||||
if (isinstance(defaultNoData, float) or isinstance(defaultNoData, int)) and defaultNoData < const:
|
||||
# find and rewrite min of actual band values; create new NA value
|
||||
valMin = min(bandValsFlatFake)
|
||||
noDataValNew = valMin - 1000 # use new adequate value
|
||||
rasterBandNoDataVal.append(noDataValNew)
|
||||
# replace fake NA with new NA
|
||||
bandValsFlat = [noDataValNew if val == fakeNA else val for val in bandValsFlatFake] # replace all values corresponding to NoData value
|
||||
|
||||
# if default val unaccessible and minimum val is too small
|
||||
elif (isinstance(defaultNoData, str) or defaultNoData is None) and valMin < const: # if there are extremely small values but default NA unaccessible
|
||||
noDataValNew = valMin
|
||||
rasterBandNoDataVal.append(noDataValNew)
|
||||
# replace fake NA with new NA
|
||||
bandValsFlat = [noDataValNew if val == fakeNA else val for val in bandValsFlatFake] # replace all values corresponding to NoData value
|
||||
# last, change minValto actual one
|
||||
valMin = min(bandValsFlatFake)
|
||||
|
||||
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)
|
||||
rasterBandMinVal.append(valMin)
|
||||
rasterBandMaxVal.append(valMax)
|
||||
|
||||
#print(rb.getColormap()) #None
|
||||
|
||||
b["@(10000)" + item + "_values"] = bandValsFlat #[0:int(max_values/rasterBandCount)]
|
||||
|
||||
b["X resolution"] = rasterResXY[0]
|
||||
b["Y resolution"] = -1* rasterResXY[1]
|
||||
b["X pixels"] = rasterDimensions[0]
|
||||
b["Y pixels"] = rasterDimensions[1]
|
||||
b["Band count"] = rasterBandCount
|
||||
b["Band names"] = rasterBandNames
|
||||
b["NoDataVal"] = rasterBandNoDataVal
|
||||
# creating a mesh
|
||||
vertices = []
|
||||
faces = []
|
||||
colors = []
|
||||
count = 0
|
||||
|
||||
#print(my_raster.variables)
|
||||
print(selectedLayer.symbology) #None
|
||||
colorizer = None
|
||||
#renderer = selectedLayer.symbology.renderer
|
||||
if hasattr(selectedLayer.symbology, 'colorizer'):
|
||||
colorizer = selectedLayer.symbology.colorizer
|
||||
|
||||
print(colorizer) # <arcpy._colorizer.RasterStretchColorizer object at 0x000001780497FBC8>
|
||||
print(colorizer.type) # RasterStretchColorizer
|
||||
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
|
||||
|
||||
#RGB colorizer
|
||||
root_path = "\\".join(project.filePath.split("\\")[:-1])
|
||||
if not os.path.exists(root_path + '\\Layers_Speckle\\raster_bands'): os.makedirs(root_path + '\\Layers_Speckle\\raster_bands')
|
||||
path_style = root_path + '\\Layers_Speckle\\raster_bands\\' + 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
|
||||
# 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']}
|
||||
|
||||
bandIndex = 0
|
||||
r'''
|
||||
if colorizer.type == "RasterStretchColorizer":
|
||||
print("___Color cell: RasterStretchColorizer___")
|
||||
print(colorizer.band)
|
||||
#colorRamps = project.listColorRamps()
|
||||
bandIndex = colorizer.band
|
||||
|
||||
colorizerData = None
|
||||
colorRamp = None
|
||||
|
||||
colorizerData = traverseDictByKey(layerFileContent, "colorizer", None)
|
||||
print(colorizerData) # {'type': 'CIMRasterStretchColorizer', 'resamplingType':
|
||||
|
||||
#noDataColor: List[float] = traverseDictByKey(colorizerData, "noDataColor")['values']
|
||||
colorRamp = traverseDictByKey(colorizerData, "colorRamp", None)
|
||||
|
||||
colorsFromRamp: List[List[float]] = []
|
||||
colorsFromRampType = []
|
||||
try:
|
||||
for i, item in enumerate(colorRamp['colorRamps']):
|
||||
colorsFromRamp.append(item['fromColor']['values'])
|
||||
colorsFromRampType.append(item['fromColor']['type'])
|
||||
if i == len(colorRamp['colorRamps'])-1 :
|
||||
colorsFromRamp.append(item['toColor']['values'])
|
||||
colorsFromRampType.append(item['toColor']['type'])
|
||||
except: pass
|
||||
print(colorsFromRamp) # [[220, 100, 45, 100], [214.12, 100, 100, 100], [201, 25, 100, 100]]
|
||||
rangesNumber = len(colorsFromRamp) - 1 # 2 (if 3 colors)
|
||||
|
||||
colorsFromRampRGB = []
|
||||
for i, item in enumerate(colorsFromRamp):
|
||||
if ("CIMHSVColor" in colorsFromRampType[i]):
|
||||
# https://pro.arcgis.com/en/pro-app/latest/arcpy/mapping/rasterclassbreak-class.htm
|
||||
newR, newG, newB = colorsys.hsv_to_rgb(item[0],item[1],item[2])
|
||||
colorsFromRampRGB.append( ( int(newR*255), int( newG*255), int(newB*255) ) )
|
||||
elif ("HSL" in colorsFromRampType[i]):
|
||||
# https://pro.arcgis.com/en/pro-app/latest/arcpy/mapping/rasterclassbreak-class.htm
|
||||
newR, newG, newB = colorsys.hsl_to_rgb(item[0],item[1],item[2])
|
||||
colorsFromRampRGB.append( ( int(newR*255), int( newG*255), int(newB*255) ) )
|
||||
else:
|
||||
colorsFromRampRGB.append(item)
|
||||
|
||||
rs = [float(x[0]) for x in colorsFromRampRGB]
|
||||
gs = [float(x[1]) for x in colorsFromRampRGB]
|
||||
bs = [float(x[2]) for x in colorsFromRampRGB]
|
||||
'''
|
||||
# identify symbology type and if Multiband, which band is which color
|
||||
for v in range(rasterDimensions[1] ): #each row, Y
|
||||
for h in range(rasterDimensions[0] ): #item in a row, X
|
||||
pt1 = arcpy.PointGeometry(arcpy.Point(extent.XMin+h*rasterResXY[0],extent.YMax-v*rasterResXY[1]), my_raster.spatialReference, has_z = True)
|
||||
pt2 = arcpy.PointGeometry(arcpy.Point(extent.XMin+h*rasterResXY[0], extent.YMax-(v+1)*rasterResXY[1]), my_raster.spatialReference, has_z = True)
|
||||
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.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)
|
||||
pt4 = findTransformation(pt4, "Point", my_raster.spatialReference, projectCRS, selectedLayer)
|
||||
vertices.extend([pt1.getPart().X, pt1.getPart().Y, pt1.getPart().Z, pt2.getPart().X, pt2.getPart().Y, pt2.getPart().Z, pt3.getPart().X, pt3.getPart().Y, pt3.getPart().Z, pt4.getPart().X, pt4.getPart().Y, pt4.getPart().Z]) ## add 4 points
|
||||
faces.extend([4, count, count+1, count+2, count+3])
|
||||
|
||||
# color vertices according to QGIS renderer
|
||||
color = (0<<16) + (0<<8) + 0
|
||||
noValColor = [0,0,0] #selectedLayer.renderer().nodataColor().getRgb()
|
||||
|
||||
r'''
|
||||
if colorizer.type == "RasterStretchColorizer":
|
||||
|
||||
# find position of the alue on the range
|
||||
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])
|
||||
position = (rasterBandVals[bandIndex][int(count/4)] - float(colorizer.minLabel)) / valRange
|
||||
|
||||
print(position) # 0.8461538461538461
|
||||
print("calc range")
|
||||
localPosition = 0
|
||||
for n in range(rangesNumber): # 0, 1
|
||||
print(n)
|
||||
start = n/rangesNumber
|
||||
end = (n+1)/rangesNumber
|
||||
if position <= end and position >= start:
|
||||
localRange = end-start
|
||||
localPosition = position - start
|
||||
break # n - is the range we need, number bentween n and (n+1)
|
||||
print(localPosition) # 0.34615384615384615
|
||||
print(n)
|
||||
|
||||
localColor = []
|
||||
for c in [rs,gs,bs]:
|
||||
print(c)
|
||||
# go through each color:
|
||||
localColor.append( int( (c[n+1] - c[n]) * localPosition + c[n] ) )
|
||||
print(localColor)
|
||||
color = (localColor[0]<<16) + (localColor[1]<<8) + localColor[2]
|
||||
print(color)
|
||||
|
||||
|
||||
elif colorizer.type == "RasterClassifyColorizer":
|
||||
print(colorizer.noDataColor)
|
||||
print(colorizer.breakCount) # number of classes
|
||||
print(colorizer.classBreaks)
|
||||
print(colorizer.classificationField)
|
||||
print(colorizer.classificationMethod)
|
||||
print(colorizer.colorRamp)
|
||||
elif colorizer.type == "RasterUniqueValueColorizer":
|
||||
print(colorizer.noDataColor)
|
||||
print(colorizer.colorRamp)
|
||||
print(colorizer.field)
|
||||
print(colorizer.groups)
|
||||
'''
|
||||
#else:
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
mesh = constructMeshFromRaster(vertices, faces, colors)
|
||||
if(b['displayValue'] is None):
|
||||
b['displayValue'] = []
|
||||
b['displayValue'].append(mesh)
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return b
|
||||
|
||||
r'''
|
||||
# example raster stretch colorizer
|
||||
|
||||
{'type': 'CIMRasterStretchColorizer', 'resamplingType': 'NearestNeighbor',
|
||||
'noDataColor': {'type': 'CIMRGBColor', 'values': [255, 255, 255, 0]},
|
||||
'backgroundColor': {'type': 'CIMRGBColor', 'values': [255, 255, 255, 0]},
|
||||
'colorRamp':
|
||||
{
|
||||
'type': 'CIMMultipartColorRamp',
|
||||
'colorRamps':
|
||||
[{
|
||||
'type': 'CIMPolarContinuousColorRamp',
|
||||
'colorSpace': {'type': 'CIMICCColorSpace', 'url': 'Default RGB'},
|
||||
'fromColor': {'type': 'CIMHSVColor', 'values': [220, 100, 45, 100]},
|
||||
'toColor': {'type': 'CIMHSVColor', 'values': [214, 100, 100, 100]},
|
||||
'interpolationSpace': 'HSV', 'polarDirection': 'Counterclockwise'
|
||||
},
|
||||
{
|
||||
'type': 'CIMPolarContinuousColorRamp',
|
||||
'colorSpace': {'type': 'CIMICCColorSpace', 'url': 'Default RGB'},
|
||||
'fromColor': {'type': 'CIMHSVColor', 'values': [214.12, 100, 100, 100]},
|
||||
'toColor': {'type': 'CIMHSVColor', 'values': [201, 25, 100, 100]},
|
||||
'interpolationSpace': 'HSV', 'polarDirection': 'Counterclockwise'
|
||||
}],
|
||||
'weights': [1, 1]},
|
||||
'colorScheme': 'Bathymetry #3', 'customStretchMax': 1, 'gammaValue': 1, 'hillshadeZFactor': 1,
|
||||
'maxPercent': 2, 'minPercent': 2, 'standardDeviationParam': 2, 'statsType': 'Dataset',
|
||||
'stretchClasses': [
|
||||
{'type': 'CIMRasterStretchClass', 'label': '3', 'value': 3},
|
||||
{'type': 'CIMRasterStretchClass', 'value': 22.5},
|
||||
{'type': 'CIMRasterStretchClass', 'label': '42', 'value': 42}
|
||||
],
|
||||
'stretchStats': {'type': 'StatsHistogram', 'min': 3, 'max': 42, 'mean': 21.761538461538, 'stddev': 11.387670241563, 'resolution': 0.15294117647058825},
|
||||
'stretchType': 'StandardDeviations'}
|
||||
'''
|
||||
|
||||
|
||||
@@ -0,0 +1,704 @@
|
||||
import json
|
||||
from typing import Any, List, Tuple, Union
|
||||
import copy
|
||||
import os
|
||||
|
||||
from typing import Dict
|
||||
|
||||
import inspect
|
||||
|
||||
import arcpy
|
||||
from arcpy._mp import ArcGISProject, Layer as arcLayer
|
||||
from arcpy.management import (CreateFeatureclass, MakeFeatureLayer,
|
||||
AddFields, AlterField, DefineProjection )
|
||||
|
||||
from specklepy.objects import Base
|
||||
from specklepy.objects.other import RenderMaterial
|
||||
|
||||
try:
|
||||
from speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
|
||||
from speckle.ui.logger import logToUser
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
def jsonFromLayerStyle(layerArcgis, path_style):
|
||||
# write updated renderer to file and get layerStyle variable
|
||||
try:
|
||||
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
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
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 Exception as e:
|
||||
logToUser(str(e), level=1, func = inspect.stack()[0][3])
|
||||
return newColor
|
||||
|
||||
|
||||
def colorFromRenderMaterial(material):
|
||||
|
||||
color = {'RGB': [245, 245, 245, 100]} #Objects.Other.RenderMaterial
|
||||
if material is not None:
|
||||
try:
|
||||
rgb = material.diffuse
|
||||
r = (rgb & 0xFF0000) >> 16
|
||||
g = (rgb & 0xFF00) >> 8
|
||||
b = rgb & 0xFF
|
||||
color = {'RGB': [r, g, b, 100]}
|
||||
#print(color)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=1, func = inspect.stack()[0][3])
|
||||
return color
|
||||
|
||||
def cadBimRendererToNative(project: ArcGISProject, active_map, layerGroup, fetColors: List[RenderMaterial], layerArcgis, f_class, existingAttrs: List) -> Union[None, Dict[str, Any]] :
|
||||
print("___________APPLY VECTOR RENDERER______________")
|
||||
print(layerArcgis)
|
||||
print(f_class)
|
||||
print(fetColors)
|
||||
|
||||
attribute = "Speckle_ID"
|
||||
try:
|
||||
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
|
||||
|
||||
cursor = arcpy.da.SearchCursor(f_class, attribute)
|
||||
class_shapes = [shp_id[0] for n, shp_id in enumerate(cursor)]
|
||||
del cursor
|
||||
|
||||
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
|
||||
#print(transVal)
|
||||
for i in range(len(class_shapes)):
|
||||
label = class_shapes[i]
|
||||
#print(label)
|
||||
if label is None or label=="" or str(label)=="": label = "<Null>"
|
||||
|
||||
if str(transVal) == label:
|
||||
#print("found label")
|
||||
material = fetColors[i]
|
||||
#print(material)
|
||||
itm.symbol.color = colorFromRenderMaterial(material)
|
||||
itm.label = label
|
||||
break
|
||||
layerArcgis.symbology = sym
|
||||
#print(layerArcgis)
|
||||
return layerArcgis
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
|
||||
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)
|
||||
try:
|
||||
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
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
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]:
|
||||
try:
|
||||
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
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return 0, 0, 0
|
||||
|
||||
|
||||
|
||||
def rasterRendererToNative(project: ArcGISProject, active_map, layerGroup, layer: RasterLayer, arcLayer, rasterPathsToMerge, newName):
|
||||
print("_____rasterRenderer ToNative______")
|
||||
try:
|
||||
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\\raster_bands'): os.makedirs(root_path + '\\Layers_Speckle\\raster_bands')
|
||||
path_style = root_path + '\\Layers_Speckle\\raster_bands\\' + newName + '_old.lyrx'
|
||||
path_style2 = root_path + '\\Layers_Speckle\\raster_bands\\' + 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
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
return arcLayer
|
||||
|
||||
def rendererToSpeckle(project: ArcGISProject, active_map, arcLayer, rasterFeat: Base):
|
||||
print("_____renderer To Speckle______")
|
||||
try:
|
||||
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\\raster_bands'): os.makedirs(root_path + '\\Layers_Speckle\\raster_bands')
|
||||
path_style = root_path + '\\Layers_Speckle\\raster_bands\\' + 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.dataSource, 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
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
|
||||
def featureColorfromNativeRenderer(index: int, arcLayer: arcLayer) -> int:
|
||||
# case with one color for the entire layer
|
||||
#try:
|
||||
color = {'RGB': [100,100,100,100]}
|
||||
try:
|
||||
sym = arcLayer.symbology
|
||||
|
||||
if sym.renderer.type == 'SimpleRenderer':
|
||||
print('SimpleRenderer')
|
||||
color = sym.renderer.symbol.color
|
||||
|
||||
elif sym.renderer.type == 'UniqueValueRenderer':
|
||||
#print('Unique Value Renderer')
|
||||
#print(index)
|
||||
#print(arcLayer)
|
||||
|
||||
attribute = sym.renderer.fields[0]
|
||||
color = sym.renderer.defaultSymbol.color
|
||||
categories = sym.renderer.groups
|
||||
|
||||
rows_attributes = arcpy.da.SearchCursor(arcLayer.dataSource, 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.dataSource, 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.dataSource, 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
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
#print("final color: ")
|
||||
#print(color)
|
||||
# construct RGB color
|
||||
col = symbol_color_to_speckle(color)
|
||||
#print(col)
|
||||
return col
|
||||
|
||||
@@ -1,75 +1,195 @@
|
||||
from typing import Any, List, Union
|
||||
from typing import Dict, Any, List, Union
|
||||
import json
|
||||
from specklepy.objects import Base
|
||||
import arcpy
|
||||
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
|
||||
import os
|
||||
|
||||
import inspect
|
||||
|
||||
|
||||
try:
|
||||
from speckle.converter.layers.emptyLayerTemplates import createGroupLayer
|
||||
from speckle.plugin_utils.helpers import findOrCreatePath
|
||||
from speckle.ui.logger import logToUser
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.emptyLayerTemplates import createGroupLayer
|
||||
from speckle_toolbox.esri.toolboxes.speckle.plugin_utils.helpers import findOrCreatePath
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
#ATTRS_REMOVE = ['geometry','applicationId','bbox','displayStyle', 'id', 'renderMaterial', 'displayMesh', 'displayValue']
|
||||
ATTRS_REMOVE = ['speckleTyp','speckle_id','geometry','applicationId','bbox','displayStyle', 'id', 'renderMaterial', 'displayMesh', 'displayValue']
|
||||
|
||||
|
||||
def findAndClearLayerGroup(gis_project: ArcGISProject, newGroupName: str = ""):
|
||||
print("find And Clear LayerGroup")
|
||||
try:
|
||||
groupExists = 0
|
||||
print(newGroupName)
|
||||
for l in gis_project.activeMap.listLayers():
|
||||
#print(l.longName)
|
||||
if l.longName.startswith(newGroupName + "\\"):
|
||||
#print(l.longName)
|
||||
gis_project.activeMap.removeLayer(l)
|
||||
groupExists+=1
|
||||
elif l.longName == newGroupName:
|
||||
groupExists+=1
|
||||
print(newGroupName)
|
||||
if groupExists == 0:
|
||||
# create empty group layer file "\\Layers_Speckle\\
|
||||
path = "\\".join(gis_project.filePath.split("\\")[:-1]) + "\\Layers_Speckle\\"
|
||||
findOrCreatePath(path)
|
||||
lyr_path = path + newGroupName + ".lyrx"
|
||||
print(lyr_path)
|
||||
try:
|
||||
f = open(lyr_path, "w")
|
||||
content = createGroupLayer().replace("TestGroupLayer", newGroupName)
|
||||
f.write(content)
|
||||
f.close()
|
||||
newGroupLayer = arcpy.mp.LayerFile(lyr_path)
|
||||
layerGroup = gis_project.activeMap.addLayer(newGroupLayer)[0]
|
||||
print(layerGroup)
|
||||
except: # for 3.0.0
|
||||
if gis_project.active_map is not None:
|
||||
print("try creating the group")
|
||||
layerGroup = gis_project.activeMap.createGroupLayer(newGroupName)
|
||||
print(layerGroup)
|
||||
else:
|
||||
logToUser("The map didn't fully load, try selecting the project Map or/and refreshing the plugin.", level=1, func = inspect.stack()[0][3])
|
||||
return
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
|
||||
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"
|
||||
]
|
||||
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
|
||||
try:
|
||||
pairs = [
|
||||
(str, "TEXT"), # 10
|
||||
(float, "FLOAT"),
|
||||
(int, "LONG"),
|
||||
(bool, "SHORT")
|
||||
]
|
||||
for p in pairs:
|
||||
if isinstance(value, p[0]):
|
||||
res = p[1]
|
||||
try:
|
||||
if res == "LONG" and (value>= 2147483647 or value<= -2147483647):
|
||||
#https://pro.arcgis.com/en/pro-app/latest/help/data/geodatabases/overview/arcgis-field-data-types.htm
|
||||
res = "FLOAT"
|
||||
except Exception as e: print(e)
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
return res
|
||||
|
||||
def getLayerAttributes(features: List[Base]) -> dict:
|
||||
print("________ get layer attributes___")
|
||||
def getLayerAttributes(featuresList: List[Base], attrsToRemove: List[str] = ATTRS_REMOVE ) -> Dict[str, str]:
|
||||
print("03________ get layer attributes")
|
||||
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', 'userDictionary', 'userStrings','geometry']
|
||||
for att in attrsToRemove:
|
||||
try: dynamicProps.remove(att)
|
||||
except: pass
|
||||
try:
|
||||
if not isinstance(featuresList, list): features = [featuresList]
|
||||
else: features = featuresList[:]
|
||||
|
||||
all_props = []
|
||||
for feature in features:
|
||||
#get object properties to add as attributes
|
||||
dynamicProps = feature.get_dynamic_member_names()
|
||||
for att in ATTRS_REMOVE:
|
||||
try: dynamicProps.remove(att)
|
||||
except: pass
|
||||
|
||||
dynamicProps.sort()
|
||||
|
||||
dynamicProps.sort()
|
||||
#print(dynamicProps)
|
||||
# add field names and variands
|
||||
for name in dynamicProps:
|
||||
#if name not in all_props: all_props.append(name)
|
||||
|
||||
# add field names and variands
|
||||
#variants = []
|
||||
for name in dynamicProps:
|
||||
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
|
||||
|
||||
value = feature[name]
|
||||
variant = getVariantFromValue(value)
|
||||
#print(variant)
|
||||
if not variant: variant = None #LongLong #4
|
||||
# go thought the dictionary object
|
||||
if value and isinstance(value, list):
|
||||
#all_props.remove(name) # remove generic dict name
|
||||
for i, val_item in enumerate(value):
|
||||
newF, newVals = traverseDict( {}, {}, name+"_"+str(i), val_item)
|
||||
|
||||
# add a field if not existing yet AND if variant is known
|
||||
if variant and (name not in fields.keys()):
|
||||
fields.update({name: variant})
|
||||
|
||||
elif name in fields.keys(): #check if the field was empty previously:
|
||||
#nameIndex = fields.indexFromName(name)
|
||||
oldVariant = fields[name]
|
||||
#print(oldVariant)
|
||||
# replace if new one is NOT LongLong or IS String
|
||||
if oldVariant != "TEXT" and variant == "TEXT":
|
||||
fields.update({name: variant})
|
||||
print(all_props)
|
||||
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'})
|
||||
|
||||
# replace all empty ones wit String
|
||||
for name in all_props:
|
||||
if name not in fields.keys():
|
||||
fields.update({name: "TEXT"})
|
||||
print(fields)
|
||||
#fields_sorted = {k: v for k, v in sorted(fields.items(), key=lambda item: item[0])}
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return fields
|
||||
|
||||
def traverseDict(newF: dict, newVals: dict, nam: str, val: Any):
|
||||
try:
|
||||
if isinstance(val, dict):
|
||||
for i, (k,v) in enumerate(val.items()):
|
||||
newF, newVals = traverseDict( newF, newVals, nam+"_"+k, v)
|
||||
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 var is None:
|
||||
var = 'TEXT'
|
||||
val = str(val)
|
||||
#print(var)
|
||||
newF.update({nam: var})
|
||||
newVals.update({nam: val})
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return newF, newVals
|
||||
|
||||
def get_scale_factor(units: str) -> float:
|
||||
unit_scale = {
|
||||
"meters": 1.0,
|
||||
@@ -89,6 +209,190 @@ def get_scale_factor(units: str) -> float:
|
||||
}
|
||||
if units is not None and units.lower() in unit_scale.keys():
|
||||
return unit_scale[units]
|
||||
arcpy.AddWarning(f"Units {units} are not supported. Meters will be applied by default.")
|
||||
logToUser(f"Units {units} are not supported. Meters will be applied by default.", level=0, func = inspect.stack()[0][3])
|
||||
return 1.0
|
||||
|
||||
def findTransformation(f_shape, geomType, layer_sr: arcpy.SpatialReference, projectCRS: arcpy.SpatialReference, selectedLayer: arcLayer):
|
||||
#apply transformation if needed
|
||||
try:
|
||||
if layer_sr.name != projectCRS.name:
|
||||
tr0 = tr1 = tr2 = tr_custom = None
|
||||
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: logToUser("Unsupported or invalid geometry in layer " + selectedLayer.name, level=2, func = inspect.stack()[0][3])
|
||||
except: logToUser("Unsupported or invalid geometry", level=2, func = inspect.stack()[0][3])
|
||||
|
||||
# reproject geometry using chosen transformstion(s)
|
||||
if tr0 is not None:
|
||||
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:
|
||||
logToUser(f"Spatial Transformation not found for layer {selectedLayer.name}", level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return f_shape
|
||||
|
||||
def traverseDictByKey(d: Dict, key:str ="", result = None) -> Dict:
|
||||
print("__traverse")
|
||||
try:
|
||||
result = None
|
||||
#print(d)
|
||||
for k, v in d.items():
|
||||
|
||||
try: v = json.loads(v)
|
||||
except: pass
|
||||
if isinstance(v, dict):
|
||||
#print("__dict__")
|
||||
if k == key: print("__break loop"); result = v; return result
|
||||
else:
|
||||
result = traverseDictByKey(v, key, result)
|
||||
if result is not None: return result
|
||||
if isinstance(v, list):
|
||||
for item in v:
|
||||
#print(item)
|
||||
if isinstance(item, dict):
|
||||
result = traverseDictByKey(item, key, result)
|
||||
if result is not None: return result
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
#print("__result is: ____________")
|
||||
#return result
|
||||
|
||||
def hsv_to_rgb(listHSV):
|
||||
try:
|
||||
h, s, v = listHSV[0], listHSV[1], listHSV[2]
|
||||
if s == 0.0: v*=255; return (v, v, v)
|
||||
i = int(h*6.) # XXX assume int() truncates!
|
||||
f = (h*6.)-i; p,q,t = int(255*(v*(1.-s))), int(255*(v*(1.-s*f))), int(255*(v*(1.-s*(1.-f)))); v*=255; i%=6
|
||||
if i == 0: return (v, t, p)
|
||||
if i == 1: return (q, v, p)
|
||||
if i == 2: return (p, v, t)
|
||||
if i == 3: return (p, q, v)
|
||||
if i == 4: return (t, p, v)
|
||||
if i == 5: return (v, p, q)
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return (0,0,0)
|
||||
|
||||
def cmyk_to_rgb(c, m, y, k, cmyk_scale, rgb_scale=255):
|
||||
try:
|
||||
r = rgb_scale * (1.0 - c / float(cmyk_scale)) * (1.0 - k / float(cmyk_scale))
|
||||
g = rgb_scale * (1.0 - m / float(cmyk_scale)) * (1.0 - k / float(cmyk_scale))
|
||||
b = rgb_scale * (1.0 - y / float(cmyk_scale)) * (1.0 - k / float(cmyk_scale))
|
||||
return r, g, b
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return 0,0,0
|
||||
|
||||
def newLayerGroupAndName(layerName: str, streamBranch: str, project: ArcGISProject) -> str:
|
||||
print("___new Layer Group and Name")
|
||||
layerGroup = None
|
||||
newGroupName = f'{streamBranch}'
|
||||
try:
|
||||
#CREATE A GROUP "received blabla" with sublayers
|
||||
print(newGroupName)
|
||||
for l in project.activeMap.listLayers():
|
||||
if l.longName == newGroupName: layerGroup = l; break
|
||||
|
||||
#find a layer with a matching name in the "latest" group
|
||||
newName = f'{streamBranch.split("_")[len(streamBranch.split("_"))-1]}_{layerName}'
|
||||
|
||||
all_layer_names = []
|
||||
layerExists = 0
|
||||
for l in project.activeMap.listLayers():
|
||||
if l.longName.startswith(newGroupName + "\\"):
|
||||
all_layer_names.append(l.longName)
|
||||
#print(all_layer_names)
|
||||
print(newName)
|
||||
|
||||
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
|
||||
print(newName)
|
||||
return newName, layerGroup
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None, None
|
||||
|
||||
|
||||
def curvedFeatureClassToSegments(layer) -> str:
|
||||
print("___densify___")
|
||||
try:
|
||||
data = arcpy.Describe(layer.dataSource)
|
||||
dataPath = data.catalogPath
|
||||
print(dataPath)
|
||||
newPath = dataPath+"_backup"
|
||||
|
||||
arcpy.management.CopyFeatures(dataPath, newPath) # features copied like this do not preserve curved segments
|
||||
|
||||
arcpy.edit.Densify(in_features = newPath, densification_method = "ANGLE", max_angle = 0.01, max_vertex_per_segment = 100) # https://pro.arcgis.com/en/pro-app/latest/tool-reference/editing/densify.htm
|
||||
print(newPath)
|
||||
return newPath
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def validate_path(path: str):
|
||||
"""If our path contains a DB name, make sure we have a valid DB name and not a standard file name."""
|
||||
try:
|
||||
# https://github.com/EsriOceans/btm/commit/a9c0529485c9b0baa78c1f094372c0f9d83c0aaf
|
||||
dirname, file_name = os.path.split(path)
|
||||
#print(dirname)
|
||||
#print(file_name)
|
||||
file_base = os.path.splitext(file_name)[0]
|
||||
if dirname == '':
|
||||
# a relative path only, relying on the workspace
|
||||
dirname = arcpy.env.workspace
|
||||
path_ext = os.path.splitext(dirname)[1].lower()
|
||||
if path_ext in ['.mdb', '.gdb', '.sde']:
|
||||
# we're working in a database
|
||||
file_name = arcpy.ValidateTableName(file_base) # e.g. add a letter in front of the name
|
||||
validated_path = os.path.join(dirname, file_name)
|
||||
#msg("validated path: %s; (from %s)" % (validated_path, path))
|
||||
return validated_path
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import os
|
||||
from typing import List
|
||||
import inspect
|
||||
|
||||
def findOrCreatePath(path: str):
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
def splitTextIntoLines(text: str = "", number: int= 40) -> str:
|
||||
print("__splitTextIntoLines")
|
||||
print(text)
|
||||
msg = ""
|
||||
try:
|
||||
if len(text)>number:
|
||||
try:
|
||||
for i, x in enumerate(text):
|
||||
msg += x
|
||||
if i!=0 and i%number == 0: msg += "\n"
|
||||
except Exception as e: print(e)
|
||||
else:
|
||||
msg = text
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(text)
|
||||
|
||||
return msg
|
||||
|
||||
def validateNewFclassName(newName: str, prefix: str, all_layer_names: List[str]) -> str:
|
||||
|
||||
fixed_name = newName
|
||||
|
||||
if (prefix + fixed_name) in all_layer_names:
|
||||
|
||||
layerNameCreated = 0
|
||||
for index, letter in enumerate('234567890abcdefghijklmnopqrstuvwxyz'):
|
||||
if ((prefix + fixed_name) + "_" + letter) not in all_layer_names:
|
||||
fixed_name += "_"+letter
|
||||
layerNameCreated +=1
|
||||
break
|
||||
if layerNameCreated == 0:
|
||||
for index, letter in enumerate('234567890abcdefghijklmnopqrstuvwxyz'):
|
||||
test_fixed_name = validateNewFclassName((fixed_name + "_" + letter), prefix, all_layer_names)
|
||||
if (prefix + test_fixed_name) not in all_layer_names:
|
||||
fixed_name = test_fixed_name
|
||||
layerNameCreated +=1
|
||||
break
|
||||
#else: layerNameCreated +=1
|
||||
|
||||
if layerNameCreated == 0:
|
||||
pass #logToUser('Feature class name already exists', level=2, func = inspect.stack()[0][3])
|
||||
#return fixed_name
|
||||
|
||||
return fixed_name
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
r'''
|
||||
import arcpy
|
||||
|
||||
class Toolbox(object):
|
||||
def __init__(self):
|
||||
"""Define the toolbox (the name of the toolbox is the name of the
|
||||
.pyt file)."""
|
||||
self.label = "Speckle Toolbox"
|
||||
self.alias = "speckle_toolbox_"
|
||||
self.label = "Speckle something"
|
||||
self.alias = "speckle_toolbox"
|
||||
self.tools = [Speckle]
|
||||
|
||||
class Speckle(object):
|
||||
@@ -45,4 +45,4 @@ class Speckle(object):
|
||||
def execute(self, parameters):
|
||||
return
|
||||
|
||||
|
||||
'''
|
||||
@@ -0,0 +1,142 @@
|
||||
|
||||
from typing import Any, Callable, List, Optional
|
||||
|
||||
import inspect
|
||||
|
||||
from specklepy.objects import Base
|
||||
|
||||
try:
|
||||
from speckle.ui.logger import logToUser
|
||||
from speckle.converter.layers.Layer import VectorLayer, RasterLayer, Layer
|
||||
from speckle.converter.layers import bimLayerToNative, cadLayerToNative, layerToNative
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.Layer import VectorLayer, RasterLayer, Layer
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers import bimLayerToNative, cadLayerToNative, layerToNative
|
||||
|
||||
import arcpy
|
||||
|
||||
SPECKLE_TYPES_TO_READ = ["Objects.Geometry.", "Objects.BuiltElements.", "IFC"] # will properly traverse and check for displayValue
|
||||
|
||||
def traverseObject(
|
||||
base: Base,
|
||||
callback: Optional[Callable[[Base, str], bool]],
|
||||
check: Optional[Callable[[Base], bool]],
|
||||
streamBranch: str,
|
||||
):
|
||||
try:
|
||||
#print("traverse Object")
|
||||
#print(base)
|
||||
if check and check(base):
|
||||
res = callback(base, streamBranch) if callback else False
|
||||
#print(res)
|
||||
if res:
|
||||
return
|
||||
memberNames = base.get_member_names()
|
||||
#print(base)
|
||||
#print(memberNames)
|
||||
for name in memberNames:
|
||||
try:
|
||||
if ["id", "applicationId", "units", "speckle_type"].index(name):
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
#print(name)
|
||||
traverseValue(base[name], callback, check, streamBranch)
|
||||
logToUser("Data received", level=0)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def traverseValue(
|
||||
value: Any,
|
||||
callback: Optional[Callable[[Base, str], bool]],
|
||||
check: Optional[Callable[[Base], bool]],
|
||||
streamBranch: str,
|
||||
):
|
||||
try:
|
||||
#print("traverse Value")
|
||||
#print(value)
|
||||
if isinstance(value, Base):
|
||||
traverseObject(value, callback, check, streamBranch)
|
||||
if isinstance(value, List):
|
||||
for item in value:
|
||||
traverseValue(item, callback, check, streamBranch)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def callback(base: Base, streamBranch: str) -> bool:
|
||||
try:
|
||||
#print("callback")
|
||||
if isinstance(base, VectorLayer) or isinstance(base, Layer) or isinstance(base, RasterLayer):
|
||||
if isinstance(base, Layer):
|
||||
logToUser(f"Speckle class \"Layer\" will be deprecated in future updates in favour of \"VectorLayer\" or \"RasterLayer\"", level=0, func = inspect.stack()[0][3])
|
||||
layer = layerToNative(base, streamBranch)
|
||||
#print(layer)
|
||||
#if layer is not None:
|
||||
# logToUser("Layer created: " + layer.name(), level=0)
|
||||
else:
|
||||
loopObj(base, "", streamBranch)
|
||||
return True
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return False
|
||||
|
||||
def loopObj(base: Base, baseName: str, streamBranch: str):
|
||||
try:
|
||||
memberNames = base.get_member_names()
|
||||
for name in memberNames:
|
||||
if name in ["id", "applicationId", "units", "speckle_type"]: continue
|
||||
# skip if traversal goes to displayValue of an object, that will be readable anyway:
|
||||
if not isinstance(base, Base): logToUser("NOT BASE: "+type(base), level=1, func = inspect.stack()[0][3]); continue
|
||||
if (name == "displayValue" or name == "@displayValue") and base.speckle_type.startswith(tuple(SPECKLE_TYPES_TO_READ)): continue
|
||||
|
||||
try: loopVal(base[name], baseName + "/" + name, streamBranch)
|
||||
except: pass
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def loopVal(value: Any, name: str, streamBranch: str): # "name" is the parent object/property/layer name
|
||||
try:
|
||||
if isinstance(value, Base):
|
||||
try: # loop through objects with Speckletype prop, but don't go through parts of Speckle Geometry object
|
||||
if not value.speckle_type.startswith("Objects.Geometry."):
|
||||
loopObj(value, name, streamBranch)
|
||||
except:
|
||||
loopObj(value, name, streamBranch)
|
||||
|
||||
elif isinstance(value, List):
|
||||
streamBranch = streamBranch.replace("[","_").replace("]","_").replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
|
||||
|
||||
objectListConverted = 0
|
||||
#print("loop val - List")
|
||||
for i, item in enumerate(value):
|
||||
loopVal(item, name, streamBranch)
|
||||
if not isinstance(item, Base): continue
|
||||
if item.speckle_type and item.speckle_type.startswith("IFC"):
|
||||
# keep traversing infinitely, just don't run repeated conversion for the same list of objects
|
||||
try:
|
||||
if item["displayValue"] is not None and objectListConverted == 0:
|
||||
bimLayerToNative(value, name, streamBranch)
|
||||
objectListConverted += 1
|
||||
except:
|
||||
try:
|
||||
if item["@displayValue"] is not None and objectListConverted == 0:
|
||||
bimLayerToNative(value, name, streamBranch)
|
||||
objectListConverted += 1
|
||||
except: pass
|
||||
elif item.speckle_type and item.speckle_type.endswith(".ModelCurve"):
|
||||
if item["baseCurve"] is not None:
|
||||
cadLayerToNative(value, name, streamBranch)
|
||||
break
|
||||
elif item.speckle_type and (item.speckle_type == "Objects.Geometry.Mesh" or item.speckle_type == "Objects.Geometry.Brep" or item.speckle_type.startswith("Objects.BuiltElements.")):
|
||||
bimLayerToNative(value, name, streamBranch)
|
||||
break
|
||||
elif item.speckle_type and item.speckle_type != "Objects.Geometry.Mesh" and item.speckle_type != "Objects.Geometry.Brep" and item.speckle_type.startswith("Objects.Geometry."): # or item.speckle_type == 'Objects.BuiltElements.Alignment'):
|
||||
pt, pl = cadLayerToNative(value, name, streamBranch)
|
||||
#if pt is not None: arcpy.AddMessage("Layer group created: " + str(pt.name))
|
||||
#if pl is not None: arcpy.AddMessage("Layer group created: " + str(pl.name))
|
||||
break
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,787 @@
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Any, Callable, List, Optional, Tuple
|
||||
#r'''
|
||||
from collections import defaultdict
|
||||
|
||||
import arcpy
|
||||
#from arcpy import toolbox
|
||||
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
|
||||
from arcpy import metadata as md
|
||||
|
||||
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 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
|
||||
from specklepy.api import operations
|
||||
from specklepy.logging.exceptions import (
|
||||
GraphQLException,
|
||||
SpeckleException,
|
||||
SpeckleWarning,
|
||||
)
|
||||
#from specklepy.api.credentials import StreamWrapper
|
||||
from specklepy.api.wrapper import StreamWrapper
|
||||
from specklepy.objects import Base
|
||||
from specklepy.logging import metrics
|
||||
|
||||
#'''
|
||||
|
||||
def traverseObject(
|
||||
base: Base,
|
||||
callback: Optional[Callable[[Base], bool]],
|
||||
check: Optional[Callable[[Base], bool]],
|
||||
):
|
||||
if check and check(base):
|
||||
res = callback(base) if callback else False
|
||||
if res:
|
||||
return
|
||||
memberNames = base.get_member_names()
|
||||
for name in memberNames:
|
||||
try:
|
||||
if ["id", "applicationId", "units", "speckle_type"].index(name):
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
traverseValue(base[name], callback, check)
|
||||
|
||||
def traverseValue(
|
||||
value: Any,
|
||||
callback: Optional[Callable[[Base], bool]],
|
||||
check: Optional[Callable[[Base], bool]],
|
||||
):
|
||||
if isinstance(value, Base):
|
||||
traverseObject(value, callback, check)
|
||||
if isinstance(value, List):
|
||||
for item in value:
|
||||
traverseValue(item, callback, check)
|
||||
|
||||
|
||||
class Toolbox:
|
||||
def __init__(self):
|
||||
"""Define the toolbox (the name of the toolbox is the name of the
|
||||
.pyt file)."""
|
||||
print("___ping_Toolbox")
|
||||
self.label = "Speckle Tools"
|
||||
self.alias = "speckle_toolbox_"
|
||||
# List of tool classes associated with this toolbox
|
||||
self.tools = [Speckle]
|
||||
|
||||
try:
|
||||
version = arcpy.GetInstallInfo()['Version']
|
||||
python_version = f"python {'.'.join(map(str, sys.version_info[:2]))}"
|
||||
metrics.set_host_app("ArcGIS", ', '.join([f"ArcGIS {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("__________________INIT SPECKLE TOOL_________")
|
||||
self.label = "Speckle"
|
||||
self.description = "Allows you to send and receive your layers " + \
|
||||
"to/from other software using Speckle server."
|
||||
|
||||
self.toRefresh = False
|
||||
self.speckleInputs = None
|
||||
self.toolboxInputs = None
|
||||
|
||||
total = len(speckleInputsClass.instances)
|
||||
print(total)
|
||||
for i in range(total):
|
||||
if speckleInputsClass.instances[total-i-1] is not None:
|
||||
try:
|
||||
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)
|
||||
break
|
||||
except: pass
|
||||
if self.speckleInputs is None or isinstance(self.speckleInputs.streams_default, SpeckleException): self.speckleInputs = speckleInputsClass()
|
||||
#print(self.speckleInputs.streams_default)
|
||||
print(len(speckleInputsClass.instances))
|
||||
|
||||
total = len(toolboxInputsClass.instances)
|
||||
|
||||
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)
|
||||
break
|
||||
if self.toolboxInputs is None: self.toolboxInputs = toolboxInputsClass()
|
||||
|
||||
#print(self.speckleInputs.accounts)
|
||||
if len(self.speckleInputs.accounts) == 0:
|
||||
arcpy.AddError("Speckle accounts not found")
|
||||
|
||||
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
|
||||
# parameter details: https://pro.arcgis.com/en/pro-app/latest/arcpy/geoprocessing_and_python/customizing-tool-behavior-in-a-python-toolbox.htm
|
||||
print("Get parameter values")
|
||||
cat1 = "Add Streams"
|
||||
cat2 = "Send/Receive"
|
||||
cat3 = "Create custom Spatial Reference"
|
||||
|
||||
streamsDefalut = arcpy.Parameter(
|
||||
displayName="Add stream from default account",
|
||||
name="streamsDefalut",
|
||||
datatype="GPString",
|
||||
parameterType="Optional",
|
||||
direction="Input",
|
||||
category=cat1
|
||||
)
|
||||
streamsDefalut.filter.type = 'ValueList'
|
||||
if isinstance(self.speckleInputs.streams_default, SpeckleException):
|
||||
arcpy.AddError("Speckle account not accessible")
|
||||
streamsDefalut.filter.list = []
|
||||
elif self.speckleInputs.streams_default is not None:
|
||||
streamsDefalut.filter.list = [ (str(st.name) + " - " + str(st.id)) for st in self.speckleInputs.streams_default ]
|
||||
else:
|
||||
streamsDefalut.filter.list = []
|
||||
arcpy.AddError("Error connecting to default Speckle account")
|
||||
|
||||
addDefStreams = arcpy.Parameter(
|
||||
displayName="Add",
|
||||
name="addDefStreams",
|
||||
datatype="GPBoolean",
|
||||
parameterType="Optional",
|
||||
direction="Input",
|
||||
category=cat1
|
||||
)
|
||||
addDefStreams.value = False
|
||||
|
||||
streamUrl = arcpy.Parameter(
|
||||
displayName="Add stream by URL",
|
||||
name="streamUrl",
|
||||
datatype="GPString",
|
||||
parameterType="Optional",
|
||||
direction="Input",
|
||||
category=cat1
|
||||
)
|
||||
streamUrl.value = ""
|
||||
|
||||
addUrlStreams = arcpy.Parameter(
|
||||
displayName="Add",
|
||||
name="addUrlStreams",
|
||||
datatype="GPBoolean",
|
||||
parameterType="Optional",
|
||||
direction="Input",
|
||||
category=cat1
|
||||
)
|
||||
addUrlStreams.value = False
|
||||
|
||||
############################################################################
|
||||
|
||||
lat = arcpy.Parameter(
|
||||
displayName="Origin point LAT",
|
||||
name="lat",
|
||||
datatype="GPString",
|
||||
parameterType="Optional",
|
||||
direction="Input",
|
||||
category=cat3
|
||||
)
|
||||
lat.value = str(self.toolboxInputs.lat)
|
||||
|
||||
lon = arcpy.Parameter(
|
||||
displayName="Origin point LON",
|
||||
name="lon",
|
||||
datatype="GPString",
|
||||
parameterType="Optional",
|
||||
direction="Input",
|
||||
category=cat3
|
||||
)
|
||||
lon.value = str(self.toolboxInputs.lon)
|
||||
|
||||
setLatLon = arcpy.Parameter(
|
||||
displayName="Create and apply",
|
||||
name="setLatLon",
|
||||
datatype="GPBoolean",
|
||||
parameterType="Optional",
|
||||
direction="Input",
|
||||
category=cat3
|
||||
)
|
||||
setLatLon.value = False
|
||||
|
||||
####################################################################################################
|
||||
|
||||
savedStreams = arcpy.Parameter(
|
||||
displayName="Select Stream",
|
||||
name="savedStreams",
|
||||
datatype="GPString",
|
||||
parameterType="Required",
|
||||
direction="Input",
|
||||
multiValue=False,
|
||||
)
|
||||
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)]
|
||||
|
||||
removeStream = arcpy.Parameter(
|
||||
displayName="Remove",
|
||||
name="removeStream",
|
||||
datatype="GPBoolean",
|
||||
parameterType="Optional",
|
||||
direction="Input"
|
||||
)
|
||||
removeStream.value = False
|
||||
|
||||
branch = arcpy.Parameter(
|
||||
displayName="Branch",
|
||||
name="branch",
|
||||
datatype="GPString",
|
||||
parameterType="Required",
|
||||
direction="Input",
|
||||
)
|
||||
branch.value = ""
|
||||
branch.filter.type = 'ValueList'
|
||||
|
||||
commit = arcpy.Parameter(
|
||||
displayName="Commit",
|
||||
name="commit",
|
||||
datatype="GPString",
|
||||
parameterType="Optional",
|
||||
direction="Input",
|
||||
)
|
||||
commit.value = ""
|
||||
commit.filter.type = 'ValueList'
|
||||
|
||||
msg = arcpy.Parameter(
|
||||
displayName="Message",
|
||||
name="msg",
|
||||
datatype="GPString",
|
||||
parameterType="Optional",
|
||||
direction="Input",
|
||||
multiValue=False,
|
||||
)
|
||||
msg.value = ""
|
||||
|
||||
selectedLayers = arcpy.Parameter(
|
||||
displayName="Selected Layers",
|
||||
name="selectedLayers",
|
||||
datatype="GPString",
|
||||
parameterType="Optional",
|
||||
direction="Input",
|
||||
multiValue=True,
|
||||
)
|
||||
selectedLayers.filter.list = [str(i) + "-" + l.longName for i,l in enumerate(self.speckleInputs.all_layers)] #"Polyline"
|
||||
|
||||
|
||||
action = arcpy.Parameter(
|
||||
displayName="",
|
||||
name="action",
|
||||
datatype="GPString",
|
||||
parameterType="Required",
|
||||
direction="Input",
|
||||
multiValue=False,
|
||||
)
|
||||
action.value = "Send"
|
||||
action.filter.list = ["Send", "Receive"]
|
||||
|
||||
refresh = arcpy.Parameter(
|
||||
displayName="Refresh",
|
||||
name="refresh",
|
||||
datatype="GPBoolean",
|
||||
parameterType="Optional",
|
||||
direction="Input"
|
||||
)
|
||||
refresh.value = False
|
||||
|
||||
parameters = [streamsDefalut, addDefStreams, streamUrl, addUrlStreams, lat, lon, setLatLon, savedStreams, removeStream, branch, commit, selectedLayers, msg, action, refresh]
|
||||
return parameters
|
||||
|
||||
def isLicensed(self): #optional
|
||||
return True
|
||||
|
||||
def updateParameters(self, parameters: List, toRefresh = False): #optional
|
||||
print("UPDATING PARAMETERS")
|
||||
|
||||
for i, par in enumerate(parameters):
|
||||
|
||||
|
||||
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[:]
|
||||
|
||||
for stream in self.speckleInputs.streams_default:
|
||||
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}")
|
||||
self.toolboxInputs.setProjectStreams(wr)
|
||||
|
||||
for p_saved in parameters:
|
||||
if p_saved.name == "savedStreams":
|
||||
saved_streams = self.speckleInputs.getProjectStreams()
|
||||
self.speckleInputs.saved_streams = saved_streams
|
||||
p_saved.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 len(p_saved.filter.list)>0: print(p_saved.filter.list); p_saved.value = p_saved.filter.list[0]
|
||||
break
|
||||
p.value = None
|
||||
par.value = False
|
||||
|
||||
if par.name == "addUrlStreams" and par.altered and par.value == True:
|
||||
for p in parameters:
|
||||
if p.name == "streamUrl" and p.valueAsText is not None:
|
||||
|
||||
# add value from streamsDefault to saved streams
|
||||
query = p.valueAsText[:]
|
||||
if "http" in query and len(query.split("/")) >= 3: # URL
|
||||
steamId = query
|
||||
try: steamId = query.split("/streams/")[1].split("/")[0]
|
||||
except: pass
|
||||
|
||||
# query stream, add to saved
|
||||
stream = self.speckleInputs.speckle_client.stream.get(id = steamId, branch_limit = 100, commit_limit = 100)
|
||||
if isinstance(stream, Stream):
|
||||
print("_____Add by URL___")
|
||||
wr = StreamWrapper(f"{self.speckleInputs.account.serverInfo.url}/streams/{stream.id}?u={self.speckleInputs.account.userInfo.id}")
|
||||
self.toolboxInputs.setProjectStreams(wr)
|
||||
|
||||
for p_saved in parameters:
|
||||
if p_saved.name == "savedStreams":
|
||||
saved_streams = self.speckleInputs.getProjectStreams()
|
||||
self.speckleInputs.saved_streams = saved_streams
|
||||
p_saved.filter.list = [f"Stream not accessible - {st[0].stream_id}" if st[1] is None or isinstance(st[1], SpeckleException) else f"{st[1].name} - {st[1].id}" for i,st in enumerate(saved_streams)]
|
||||
if len(p_saved.filter.list)>0: print(p_saved.filter.list); p_saved.value = p_saved.filter.list[0]
|
||||
else: pass
|
||||
|
||||
p.value = None
|
||||
break
|
||||
par.value = False
|
||||
|
||||
|
||||
if par.name == "removeStream" and par.altered and par.value == True:
|
||||
for p in parameters:
|
||||
if p.name == "savedStreams" and p.valueAsText is not None:
|
||||
|
||||
# get value from savedStreams
|
||||
selected_stream_name = p.valueAsText[:]
|
||||
for streamTup in self.speckleInputs.saved_streams:
|
||||
stream = streamTup[1]
|
||||
if stream.name == selected_stream_name.split(" - ")[0]:
|
||||
print("_____Remove stream___")
|
||||
wr = StreamWrapper(f"{self.speckleInputs.account.serverInfo.url}/streams/{stream.id}?u={self.speckleInputs.account.userInfo.id}")
|
||||
self.toolboxInputs.setProjectStreams(wr, False)
|
||||
|
||||
for p_saved in parameters:
|
||||
if p_saved.name == "savedStreams":
|
||||
saved_streams = self.speckleInputs.getProjectStreams()
|
||||
self.speckleInputs.saved_streams = saved_streams
|
||||
p_saved.filter.list = [f"Stream not accessible - {st[0].stream_id}" if st[1] is None or isinstance(st[1], SpeckleException) else f"{st[1].name} - {st[1].id}" for i,st in enumerate(saved_streams)]
|
||||
p_saved.value = None
|
||||
break
|
||||
p.value = None
|
||||
par.value = False
|
||||
|
||||
#######################################################################
|
||||
if par.name == "setLatLon" and par.altered and par.value == True:
|
||||
lat = lon = 0
|
||||
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("_","")
|
||||
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("_","")
|
||||
try: lon = float(lon)
|
||||
except: lon = 0; p.value = "0.0"
|
||||
coords = [lat, lon]
|
||||
self.toolboxInputs.set_survey_point(coords)
|
||||
par.value = False
|
||||
|
||||
#######################################################################
|
||||
|
||||
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[:]:
|
||||
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
|
||||
branch_list = [branch.name for branch in self.toolboxInputs.active_stream.branches.items]
|
||||
for p in parameters:
|
||||
if p.name == "branch":
|
||||
p.filter.list = branch_list
|
||||
|
||||
if p.valueAsText not in branch_list:
|
||||
p.value = "main"
|
||||
for b in self.toolboxInputs.active_stream.branches.items:
|
||||
if b.name == p.value:
|
||||
self.toolboxInputs.active_branch = b
|
||||
break
|
||||
|
||||
# setting commit value and list
|
||||
for p in parameters:
|
||||
if p.name == "commit":
|
||||
try:
|
||||
p.filter.list = [f"{commit.id}"+ " - " + f"{commit.message}" for commit in self.toolboxInputs.active_branch.commits.items]
|
||||
if p.valueAsText not in p.filter.list:
|
||||
p.value = self.toolboxInputs.active_branch.commits.items[0].id + " - " + self.toolboxInputs.active_branch.commits.items[0].message
|
||||
self.toolboxInputs.active_commit = self.toolboxInputs.active_branch.commits.items[0]
|
||||
except:
|
||||
p.filter.list = []
|
||||
p.value = None
|
||||
self.toolboxInputs.active_commit = None
|
||||
else: par.value = None
|
||||
|
||||
if par.name == "branch" and par.altered: # branches
|
||||
if par.value is not None:
|
||||
selected_branch_name = par.valueAsText[:]
|
||||
self.toolboxInputs.active_branch = None
|
||||
if self.toolboxInputs.active_stream is not None:
|
||||
for br in self.toolboxInputs.active_stream.branches.items:
|
||||
if br.name == selected_branch_name:
|
||||
self.toolboxInputs.active_branch = br
|
||||
break
|
||||
# edit commit values
|
||||
if self.toolboxInputs.active_branch is not None:
|
||||
for p in parameters:
|
||||
if p.name == "commit":
|
||||
try:
|
||||
p.filter.list = [f"{commit.id}"+ " - " + f"{commit.message}" for commit in self.toolboxInputs.active_branch.commits.items]
|
||||
if p.valueAsText not in p.filter.list:
|
||||
p.value = self.toolboxInputs.active_branch.commits.items[0].id + " - " + self.toolboxInputs.active_branch.commits.items[0].message
|
||||
self.toolboxInputs.active_commit = self.toolboxInputs.active_branch.commits.items[0]
|
||||
except:
|
||||
p.filter.list = []
|
||||
p.value = None
|
||||
self.toolboxInputs.active_commit = None
|
||||
|
||||
if par.name == "commit" and par.altered: # commits
|
||||
if par.value is not None:
|
||||
selected_commit_id = par.valueAsText[:].split(" - ")[0]
|
||||
self.toolboxInputs.active_commit = None
|
||||
if self.toolboxInputs.active_branch is not None:
|
||||
for c in self.toolboxInputs.active_branch.commits.items:
|
||||
if c.id == selected_commit_id:
|
||||
self.toolboxInputs.active_commit = c
|
||||
break
|
||||
|
||||
if par.name == "selectedLayers" and par.altered: # selected layers
|
||||
if par.value is not None:
|
||||
self.toolboxInputs.selected_layers = par.values
|
||||
|
||||
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:
|
||||
if par.valueAsText == "Send": self.toolboxInputs.action = 1
|
||||
else: self.toolboxInputs.action = 0
|
||||
|
||||
if par.name == "refresh" and par.altered: # refresh btn
|
||||
if par.value == True:
|
||||
self.refresh(parameters)
|
||||
if self.toRefresh == True:
|
||||
self.refresh(parameters)
|
||||
self.toRefresh = False
|
||||
|
||||
print("____________________________parameters___________________________")
|
||||
return
|
||||
|
||||
def refresh(self, parameters: List[Any]):
|
||||
print("Refresh______")
|
||||
self.speckleInputs: speckleInputsClass = speckleInputsClass()
|
||||
self.toolboxInputs: toolboxInputsClass = toolboxInputsClass()
|
||||
|
||||
for par in parameters:
|
||||
if par.name == "streamUrl": par.value = None
|
||||
if par.name == "streamsDefalut": par.value = None
|
||||
if par.name == "savedStreams": par.value = None
|
||||
if par.name == "branch": par.value = ""; par.filter.list = []
|
||||
if par.name == "commit": par.value = None; par.filter.list = []
|
||||
if par.name == "selectedLayers": par.value = None
|
||||
if par.name == "msg": par.value = ""
|
||||
if par.name == "action": par.value = "Send"
|
||||
if par.name == "refresh": par.value = False
|
||||
|
||||
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":
|
||||
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":
|
||||
saved_streams = self.speckleInputs.getProjectStreams()
|
||||
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)]
|
||||
|
||||
return parameters
|
||||
|
||||
def updateMessages(self, parameters): #optional
|
||||
return
|
||||
|
||||
def execute(self, parameters: List, messages):
|
||||
# https://pro.arcgis.com/en/pro-app/latest/arcpy/get-started/what-is-arcpy-.htm
|
||||
#Warning if any of the fields is invalid/empty
|
||||
print("___________________________Run___________________________")
|
||||
check = self.validateStreamBranch(parameters) # apparently pdate needed to assign proper self.values
|
||||
|
||||
print(self.toolboxInputs.selected_layers)
|
||||
print(self.toolboxInputs.action)
|
||||
|
||||
if self.toolboxInputs.action == 1 and check is True: self.onSend(parameters)
|
||||
elif self.toolboxInputs.action == 0 and check is True: self.onReceive(parameters)
|
||||
print("__________________________Run_end___________________________")
|
||||
|
||||
def validateStreamBranch(self, parameters: List):
|
||||
|
||||
self.updateParameters(parameters)
|
||||
if self.toolboxInputs.active_stream is None:
|
||||
arcpy.AddError("Choose a valid stream")
|
||||
return False
|
||||
if self.toolboxInputs.active_branch is None:
|
||||
arcpy.AddError("Choose a valid branch")
|
||||
return False
|
||||
return True
|
||||
|
||||
def onSend(self, parameters: List):
|
||||
|
||||
print("______________SEND_______________")
|
||||
|
||||
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.toolboxInputs.active_stream_wrapper.get_client()
|
||||
#client = self.speckleInputs.speckle_client # ?
|
||||
|
||||
# Get the stream wrapper
|
||||
#streamWrapper = StreamWrapper(None)
|
||||
#client = streamWrapper.get_client()
|
||||
# Ensure the stream actually exists
|
||||
#try:
|
||||
# client.stream.get(streamId)
|
||||
#except SpeckleException as error:
|
||||
# print(str(error))
|
||||
# return
|
||||
|
||||
# next create a server transport - this is the vehicle through which you will send and receive
|
||||
transport = ServerTransport(client=client, stream_id=streamId)
|
||||
|
||||
##################################### conversions ################################################
|
||||
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])
|
||||
except SpeckleException as error:
|
||||
arcpy.AddError("Error sending data")
|
||||
#print("Error sending data")
|
||||
return
|
||||
except SpeckleWarning as warning:
|
||||
arcpy.AddMessage("SpeckleWarning: " + str(warning.args[0]))
|
||||
|
||||
|
||||
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(
|
||||
stream_id=streamId,
|
||||
object_id=objId,
|
||||
branch_name=self.toolboxInputs.active_branch.name,
|
||||
message=message,
|
||||
source_application="ArcGIS",
|
||||
)
|
||||
arcpy.AddMessage("Successfully sent data to stream: " + streamId)
|
||||
except:
|
||||
arcpy.AddError("Error creating commit")
|
||||
|
||||
def onReceive(self, parameters: List[Any]):
|
||||
|
||||
print("______________RECEIVE_______________")
|
||||
|
||||
#if self.validateStreamBranch(parameters) == False: return
|
||||
|
||||
try:
|
||||
streamId = self.toolboxInputs.active_stream.id #stream_id
|
||||
client = self.toolboxInputs.active_stream_wrapper.get_client()
|
||||
#client = self.speckleInputs.speckle_client #
|
||||
except SpeckleWarning as warning:
|
||||
arcpy.AddWarning(str(warning.args[0]))
|
||||
|
||||
# get commit
|
||||
commit = None
|
||||
try:
|
||||
#commit = self.toolboxInputs.active_branch.commits.items[0]
|
||||
commit = self.toolboxInputs.active_commit
|
||||
commitId = commit.id # text to make sure commit exists
|
||||
except:
|
||||
try:
|
||||
commit = self.toolboxInputs.active_branch.commits.items[0]
|
||||
commitId = commit.id
|
||||
arcpy.AddWarning("Failed to find a commit. Getting the last commit of the branch")
|
||||
except:
|
||||
arcpy.AddError("Failed to find a commit")
|
||||
return
|
||||
|
||||
# next create a server transport - this is the vehicle through which you will send and receive
|
||||
try:
|
||||
transport = ServerTransport(client=client, stream_id=streamId)
|
||||
|
||||
client.commit.received(
|
||||
streamId,
|
||||
commit.id,
|
||||
source_application="ArcGIS",
|
||||
message="Received commit in ArcGIS",
|
||||
)
|
||||
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
|
||||
commitObj = operations.receive(objId, transport, None)
|
||||
|
||||
if app != "QGIS" and app != "ArcGIS":
|
||||
if self.speckleInputs.project.activeMap.spatialReference.type == "Geographic" or self.speckleInputs.project.activeMap.spatialReference is None: #TODO test with invalid CRS
|
||||
arcpy.AddMessage("It is advisable to set the project Spatial reference to Projected type before receiving CAD geometry (e.g. EPSG:32631), or create a custom one from geographic coordinates")
|
||||
print("It is advisable to set the project Spatial reference to Projected type before receiving CAD geometry (e.g. EPSG:32631), or create a custom one from geographic coordinates")
|
||||
print(f"Successfully received {objId}")
|
||||
|
||||
# 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
|
||||
print(newGroupName)
|
||||
for l in self.speckleInputs.project.activeMap.listLayers():
|
||||
#print(l.longName)
|
||||
if l.longName.startswith(newGroupName + "\\"):
|
||||
#print(l.longName)
|
||||
self.speckleInputs.project.activeMap.removeLayer(l)
|
||||
groupExists+=1
|
||||
elif l.longName == newGroupName:
|
||||
groupExists+=1
|
||||
print(newGroupName)
|
||||
if groupExists == 0:
|
||||
# create empty group layer file
|
||||
path = self.speckleInputs.project.filePath.replace("aprx","gdb") #"\\".join(self.toolboxInputs.project.filePath.split("\\")[:-1]) + "\\speckle_layers\\"
|
||||
print(path)
|
||||
try:
|
||||
f = open(path + "\\" + newGroupName + ".lyrx", "w")
|
||||
content = createGroupLayer().replace("TestGroupLayer", newGroupName)
|
||||
f.write(content)
|
||||
f.close()
|
||||
newGroupLayer = arcpy.mp.LayerFile(path + "\\" + newGroupName + ".lyrx")
|
||||
layerGroup = self.speckleInputs.project.activeMap.addLayer(newGroupLayer)[0]
|
||||
except: # for 3.0.0
|
||||
if self.speckleInputs.active_map is not None:
|
||||
layerGroup = self.speckleInputs.active_map.createGroupLayer(newGroupName)
|
||||
else:
|
||||
arcpy.AddWarning("The map didn't fully load, try refreshing the plugin.")
|
||||
return
|
||||
|
||||
print(layerGroup)
|
||||
print("layer added")
|
||||
layerGroup.name = newGroupName
|
||||
print(newGroupName)
|
||||
|
||||
if app == "QGIS" or app == "ArcGIS": check: Callable[[Base], bool] = lambda base: isinstance(base, Layer) or isinstance(base, VectorLayer) or isinstance(base, RasterLayer)
|
||||
else: check: Callable[[Base], bool] = lambda base: isinstance(base, Base)
|
||||
|
||||
def callback(base: Base) -> bool:
|
||||
print("callback")
|
||||
#print(base)
|
||||
if isinstance(base, Layer) or isinstance(base, VectorLayer) or isinstance(base, RasterLayer):
|
||||
layer = layerToNative(base, streamBranch, self.speckleInputs.project)
|
||||
if layer is not None:
|
||||
print("Layer created: " + layer.name)
|
||||
else:
|
||||
loopObj(base, "")
|
||||
return True
|
||||
|
||||
def loopObj(base: Base, baseName: str):
|
||||
memberNames = base.get_member_names()
|
||||
for name in memberNames:
|
||||
if name in ["id", "applicationId", "units", "speckle_type"]: continue
|
||||
try: loopVal(base[name], baseName + "/" + name) # loop properties not included above
|
||||
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)
|
||||
if value.speckle_type.startswith("Objects.Geometry."): pass #.Brep") or value.speckle_type.startswith("Objects.Geometry.Mesh") or value.speckle_type.startswith("Objects.Geometry.Surface") or value.speckle_type.startswith("Objects.Geometry.Extrusion"): pass
|
||||
else: loopObj(value, name)
|
||||
except: loopObj(value, name)
|
||||
|
||||
if isinstance(value, List):
|
||||
for item in value:
|
||||
loopVal(item, name)
|
||||
|
||||
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, GraphQLException) as e:
|
||||
print("Receive failed: " + str(e))
|
||||
arcpy.AddError("Receive failed: " + str(e))
|
||||
return
|
||||
|
||||
print("received")
|
||||
#self.updateParameters(parameters, True)
|
||||
#self.refresh(parameters)
|
||||
|
||||
|
||||
#__all__ = ["Toolbox", "Speckle"]
|
||||
@@ -0,0 +1,160 @@
|
||||
|
||||
import time
|
||||
from typing import List
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtCore import QCoreApplication, QSettings, Qt, QTranslator, QRect, QObject
|
||||
from PyQt5.QtWidgets import QAction, QDockWidget, QVBoxLayout, QWidget, QPushButton
|
||||
from PyQt5 import QtWidgets
|
||||
import webbrowser
|
||||
|
||||
import inspect
|
||||
|
||||
try:
|
||||
from speckle.ui.logger import logToUser
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
SPECKLE_COLOR = (59,130,246)
|
||||
SPECKLE_COLOR_LIGHT = (69,140,255)
|
||||
|
||||
BACKGR_COLOR = f"background-color: rgb{str(SPECKLE_COLOR)};"
|
||||
BACKGR_COLOR_LIGHT = f"background-color: rgb{str(SPECKLE_COLOR_LIGHT)};"
|
||||
|
||||
BACKGR_COLOR_GREY = f"background-color: Gainsboro;"
|
||||
|
||||
class LogWidget(QWidget):
|
||||
|
||||
msgs: List[str] = []
|
||||
used_btns: List[int] = []
|
||||
btns: List[QPushButton]
|
||||
|
||||
# constructor
|
||||
def __init__(self, parent=None):
|
||||
super(LogWidget, self).__init__(parent)
|
||||
print("start LogWidget")
|
||||
self.parentWidget = parent
|
||||
print(self.parentWidget)
|
||||
|
||||
# create a temporary floating button
|
||||
width = 0 #parent.frameSize().width()
|
||||
height = 0# parent.frameSize().height()
|
||||
|
||||
self.setAttribute(QtCore.Qt.WA_StyledBackground, True)
|
||||
self.setStyleSheet("background-color: rgba(250,250,250,80);")
|
||||
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.layout.setContentsMargins(10, 60, 10, 40)
|
||||
self.layout.setAlignment(Qt.AlignBottom)
|
||||
self.setGeometry(0, 0, width, height)
|
||||
|
||||
# generate 100 buttons to use later
|
||||
self.btns = []
|
||||
for i in range(10):
|
||||
button = QPushButton(f"👌 Error") # to '{streamName}' Sent , v
|
||||
button.setStyleSheet("QPushButton {color: black; border: 0px;border-radius: 17px;padding: 20px;height: 40px;text-align: left;"+ f"{BACKGR_COLOR_GREY}" + "}")
|
||||
button.clicked.connect(lambda: self.hide())
|
||||
self.btns.append(button)
|
||||
|
||||
self.hide()
|
||||
|
||||
# overriding the mouseReleaseEvent method
|
||||
def mouseReleaseEvent(self, event):
|
||||
print("Mouse Release Event")
|
||||
self.hide()
|
||||
#self.parentWidget.hideError()
|
||||
|
||||
def hide(self):
|
||||
|
||||
self.setGeometry(0, 0, 0, 0)
|
||||
|
||||
# remove all buttons
|
||||
for i in reversed(range(self.layout.count())):
|
||||
self.layout.itemAt(i).widget().setParent(None)
|
||||
|
||||
# remove list of used btns
|
||||
self.used_btns.clear()
|
||||
self.msgs.clear()
|
||||
|
||||
|
||||
def addButton(self, text: str = "something went wrong", level: int = 2):
|
||||
print("Add button")
|
||||
|
||||
self.setGeometry(0, 0, self.parentWidget.frameSize().width(), self.parentWidget.frameSize().height())
|
||||
|
||||
# find index of the first unused button
|
||||
btn = self.getNextBtn()
|
||||
|
||||
btn.setStyleSheet("QPushButton {color: black; border: 0px;border-radius: 17px;padding: 20px;height: 40px;text-align: left;"+ f"{BACKGR_COLOR_GREY}" + "}")
|
||||
btn.setText(text)
|
||||
self.resizeToText(btn)
|
||||
|
||||
#btn.resize(btn.sizeHint())
|
||||
self.layout.addWidget(btn) #, alignment=Qt.AlignCenter)
|
||||
|
||||
self.msgs.append(text)
|
||||
self.used_btns.append(1)
|
||||
|
||||
def addInfoButton(self, text: str = "link here", level: int = 2, url = ""):
|
||||
print("Add blue button")
|
||||
|
||||
self.setGeometry(0, 0, self.parentWidget.frameSize().width(), self.parentWidget.frameSize().height())
|
||||
|
||||
# find index of the first unused button
|
||||
btn: QPushButton = self.getNextBtn()
|
||||
|
||||
# style the button
|
||||
btn.setStyleSheet("QPushButton {color: white;border: 0px;border-radius: 17px;padding: 20px;height: 40px;text-align: left;"+ f"{BACKGR_COLOR}" + "}")
|
||||
btn.setText(text)
|
||||
btn = self.resizeToText(btn)
|
||||
|
||||
self.layout.addWidget(btn) #, alignment=Qt.AlignCenter)
|
||||
|
||||
self.msgs.append(text)
|
||||
self.used_btns.append(1)
|
||||
|
||||
|
||||
def addLinkButton(self, text: str = "link here", level: int = 2, url = ""):
|
||||
print("Add link button")
|
||||
|
||||
self.setGeometry(0, 0, self.parentWidget.frameSize().width(), self.parentWidget.frameSize().height())
|
||||
|
||||
# find index of the first unused button
|
||||
btn = self.getNextBtn()
|
||||
|
||||
# style the button
|
||||
btn.setStyleSheet("QPushButton {color: white;border: 0px;border-radius: 17px;padding: 20px;height: 40px;text-align: left;"+ f"{BACKGR_COLOR}" + "} QPushButton:hover { "+ f"{BACKGR_COLOR_LIGHT}" + " }")
|
||||
btn.setText(text)
|
||||
self.resizeToText(btn)
|
||||
btn.clicked.connect(lambda: self.openLink(url))
|
||||
|
||||
self.layout.addWidget(btn) #, alignment=Qt.AlignCenter)
|
||||
|
||||
self.msgs.append(text)
|
||||
self.used_btns.append(1)
|
||||
|
||||
def openLink(self, url = ""):
|
||||
try:
|
||||
webbrowser.open(url, new=0, autoraise=True)
|
||||
self.hide()
|
||||
except Exception as e:
|
||||
pass #logger.logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def getNextBtn(self) -> QPushButton:
|
||||
index = len(self.used_btns)
|
||||
if index >= len(self.btns):
|
||||
self.used_btns.clear()
|
||||
index = 0
|
||||
btn = self.btns[index] # get the next "free" button
|
||||
return btn
|
||||
|
||||
def resizeToText(self, btn):
|
||||
try:
|
||||
text = btn.text()
|
||||
if len(text.split("\n"))>=2:
|
||||
height = len(text.split("\n"))*25
|
||||
print(height)
|
||||
btn.setMinimumHeight(height)
|
||||
return btn
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return btn
|
||||
@@ -0,0 +1,163 @@
|
||||
import os
|
||||
from typing import List, Union
|
||||
#import ui.speckle_qgis_dialog
|
||||
|
||||
from PyQt5 import QtWidgets, uic, QtCore
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
from specklepy.api.models import Stream
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
from specklepy.api.credentials import get_local_accounts #, StreamWrapper
|
||||
from specklepy.api.wrapper import StreamWrapper
|
||||
from gql import gql
|
||||
|
||||
import inspect
|
||||
|
||||
try:
|
||||
from speckle.ui.logger import logToUser
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
import arcpy
|
||||
|
||||
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
|
||||
ui_class = os.path.dirname(os.path.abspath(__file__)) + "/add_stream_modal.ui"
|
||||
|
||||
class AddStreamModalDialog(QtWidgets.QWidget):
|
||||
|
||||
search_button: QtWidgets.QPushButton = None
|
||||
search_text_field: QtWidgets.QLineEdit = None
|
||||
search_results_list: QtWidgets.QListWidget = None
|
||||
dialog_button_box: QtWidgets.QDialogButtonBox = None
|
||||
accounts_dropdown: QtWidgets.QComboBox
|
||||
|
||||
stream_results: List[Stream] = []
|
||||
speckle_client: Union[SpeckleClient, None] = None
|
||||
|
||||
#Events
|
||||
handleStreamAdd = pyqtSignal(StreamWrapper)
|
||||
|
||||
def __init__(self, parent=None, speckle_client: SpeckleClient = None):
|
||||
super(AddStreamModalDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint)
|
||||
uic.loadUi(ui_class, self) # Load the .ui file
|
||||
self.show()
|
||||
try:
|
||||
|
||||
self.speckle_client = speckle_client
|
||||
|
||||
self.setWindowTitle("Add Speckle stream")
|
||||
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
|
||||
|
||||
self.search_button.clicked.connect(self.onSearchClicked)
|
||||
self.search_results_list.currentItemChanged.connect( self.searchResultChanged )
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.onOkClicked)
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.onCancelClicked)
|
||||
self.accounts_dropdown.currentIndexChanged.connect(self.onAccountSelected)
|
||||
self.populate_accounts_dropdown()
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def searchResultChanged(self):
|
||||
try:
|
||||
index = self.search_results_list.currentIndex().row()
|
||||
if index == -1: self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
|
||||
else: self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def onSearchClicked(self):
|
||||
try:
|
||||
query = self.search_text_field.text()
|
||||
sw = None
|
||||
results = []
|
||||
if "http" in query and len(query.split("/")) >= 3: # URL
|
||||
sw = StreamWrapper(query)
|
||||
stream = sw.get_client().stream.get(sw.stream_id)
|
||||
if isinstance(stream, Stream): results = [stream]
|
||||
else: results = []
|
||||
|
||||
elif self.speckle_client is not None:
|
||||
results = self.speckle_client.stream.search(query)
|
||||
elif self.speckle_client is None:
|
||||
logToUser(f"Account cannot be authenticated: {self.accounts_dropdown.currentText()}", level=2, func = inspect.stack()[0][3])
|
||||
|
||||
self.stream_results = results
|
||||
self.populateResultsList(sw)
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def populateResultsList(self, sw):
|
||||
try:
|
||||
self.search_results_list.clear()
|
||||
if isinstance(self.stream_results, SpeckleException):
|
||||
logToUser("Some streams cannot be accessed", level=1, func = inspect.stack()[0][3])
|
||||
return
|
||||
for stream in self.stream_results:
|
||||
host = ""
|
||||
if sw is not None:
|
||||
host = sw.get_account().serverInfo.url
|
||||
else:
|
||||
host = self.speckle_client.account.serverInfo.url
|
||||
|
||||
if isinstance(stream, SpeckleException):
|
||||
logToUser("Some streams cannot be accessed", level=1, func = inspect.stack()[0][3])
|
||||
else:
|
||||
self.search_results_list.addItems([
|
||||
f"{stream.name}, {stream.id} | {host}" #for stream in self.stream_results
|
||||
])
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def onOkClicked(self):
|
||||
try:
|
||||
if isinstance(self.stream_results, SpeckleException):
|
||||
logToUser("Selected stream cannot be accessed", level=1, func = inspect.stack()[0][3])
|
||||
return
|
||||
#elif index == -1 or len(self.stream_results) == 0:
|
||||
# logger.logToUser("Select stream from \"Search Results\". No stream selected", Qgis.Warning)
|
||||
# return
|
||||
else:
|
||||
try:
|
||||
index = self.search_results_list.currentIndex().row()
|
||||
stream = self.stream_results[index]
|
||||
item = self.search_results_list.item(index)
|
||||
url = item.text().split(" | ")[1] + "/streams/" + item.text().split(", ")[1].split(" | ")[0]
|
||||
sw = StreamWrapper(url)
|
||||
#acc = sw.get_account() #get_local_accounts()[self.accounts_dropdown.currentIndex()]
|
||||
self.handleStreamAdd.emit(sw) #StreamWrapper(f"{acc.serverInfo.url}/streams/{stream.id}?u={acc.userInfo.id}"))
|
||||
self.close()
|
||||
except Exception as e:
|
||||
logToUser("Some streams cannot be accessed: " + str(e), level=1, func = inspect.stack()[0][3])
|
||||
return
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def onCancelClicked(self):
|
||||
self.close()
|
||||
|
||||
def onAccountSelected(self, index):
|
||||
try:
|
||||
account = self.speckle_accounts[index]
|
||||
self.speckle_client = SpeckleClient(account.serverInfo.url, account.serverInfo.url.startswith("https"))
|
||||
self.speckle_client.authenticate_with_token(token=account.token)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def populate_accounts_dropdown(self):
|
||||
# Populate the accounts comboBox
|
||||
try:
|
||||
self.speckle_accounts = get_local_accounts()
|
||||
self.accounts_dropdown.clear()
|
||||
self.accounts_dropdown.addItems(
|
||||
[
|
||||
f"{acc.userInfo.name}, {acc.userInfo.email} | {acc.serverInfo.url}"
|
||||
for acc in self.speckle_accounts
|
||||
]
|
||||
)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AddStreamDialog</class>
|
||||
<widget class="QWidget" name="AddStreamDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::NonModal</enum>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetNoConstraint</enum>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="search_form">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="search_label">
|
||||
<property name="text">
|
||||
<string>Search Stream by name or URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="search_text_field"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="search_button">
|
||||
<property name="text">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="accounts_dropdown"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="accounts_label">
|
||||
<property name="text">
|
||||
<string>Account</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="search_results_label">
|
||||
<property name="text">
|
||||
<string>Search Results</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="search_results_list">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="dialog_button_box">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,83 @@
|
||||
import os
|
||||
from typing import List, Tuple, Union
|
||||
#import ui.speckle_qgis_dialog
|
||||
|
||||
from PyQt5 import QtWidgets, uic, QtCore
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
from specklepy.api.models import Stream
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
from specklepy.api.credentials import Account, get_local_accounts #, StreamWrapper
|
||||
from specklepy.api.wrapper import StreamWrapper
|
||||
from gql import gql
|
||||
|
||||
import inspect
|
||||
|
||||
import arcpy
|
||||
try:
|
||||
from speckle.ui.logger import logToUser
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
|
||||
|
||||
ui_class = os.path.dirname(os.path.abspath(__file__)) + "/create_branch.ui"
|
||||
|
||||
class CreateBranchModalDialog(QtWidgets.QWidget):
|
||||
|
||||
name_field: QtWidgets.QLineEdit = None
|
||||
description_field: QtWidgets.QLineEdit = None
|
||||
dialog_button_box: QtWidgets.QDialogButtonBox = None
|
||||
speckle_client: Union[SpeckleClient, None] = None
|
||||
|
||||
#Events
|
||||
handleBranchCreate = pyqtSignal(str,str)
|
||||
|
||||
def __init__(self, parent=None, speckle_client: SpeckleClient = None):
|
||||
super(CreateBranchModalDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint)
|
||||
uic.loadUi(ui_class, self) # Load the .ui file
|
||||
self.show()
|
||||
try:
|
||||
|
||||
self.speckle_client = speckle_client
|
||||
|
||||
self.setWindowTitle("Create New Branch")
|
||||
|
||||
self.name_field.textChanged.connect(self.nameCheck)
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.onOkClicked)
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.onCancelClicked)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def nameCheck(self):
|
||||
try:
|
||||
if len(self.name_field.text()) >= 3:
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
|
||||
else:
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
|
||||
return
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def onOkClicked(self):
|
||||
try:
|
||||
name = self.name_field.text()
|
||||
description = self.description_field.text()
|
||||
self.handleBranchCreate.emit(name, description)
|
||||
self.close()
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return
|
||||
|
||||
def onCancelClicked(self):
|
||||
self.close()
|
||||
|
||||
def onAccountSelected(self, index):
|
||||
try:
|
||||
account = self.speckle_accounts[index]
|
||||
self.speckle_client = SpeckleClient(account.serverInfo.url, account.serverInfo.url.startswith("https"))
|
||||
self.speckle_client.authenticate_with_token(token=account.token)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CreateStreamDialog</class>
|
||||
<widget class="QWidget" name="AddBranchDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::NonModal</enum>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetNoConstraint</enum>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="search_form">
|
||||
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="name_label">
|
||||
<property name="text">
|
||||
<string>Branch Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="name_label">
|
||||
<property name="text">
|
||||
<string>Description</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="name_field"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="description_field"/>
|
||||
</item>
|
||||
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="dialog_button_box">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,108 @@
|
||||
import os
|
||||
from typing import List, Tuple, Union
|
||||
#import ui.speckle_qgis_dialog
|
||||
|
||||
|
||||
from PyQt5 import QtWidgets, uic, QtCore
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
|
||||
import arcpy
|
||||
|
||||
from specklepy.api.models import Stream
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
|
||||
from specklepy.api.credentials import Account, get_local_accounts #, StreamWrapper
|
||||
from specklepy.api.wrapper import StreamWrapper
|
||||
from gql import gql
|
||||
import inspect
|
||||
|
||||
try:
|
||||
from speckle.ui.logger import logToUser
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
|
||||
|
||||
ui_class = os.path.dirname(os.path.abspath(__file__)) + "/create_stream.ui"
|
||||
|
||||
class CreateStreamModalDialog(QtWidgets.QWidget):
|
||||
|
||||
name_field: QtWidgets.QLineEdit = None
|
||||
description_field: QtWidgets.QLineEdit = None
|
||||
dialog_button_box: QtWidgets.QDialogButtonBox = None
|
||||
accounts_dropdown: QtWidgets.QComboBox
|
||||
public_toggle: QtWidgets.QCheckBox
|
||||
|
||||
speckle_client: Union[SpeckleClient, None] = None
|
||||
|
||||
#Events
|
||||
handleStreamCreate = pyqtSignal(Account, str, str, bool)
|
||||
|
||||
def __init__(self, parent=None, speckle_client: SpeckleClient = None):
|
||||
super(CreateStreamModalDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint)
|
||||
uic.loadUi(ui_class, self) # Load the .ui file
|
||||
self.show()
|
||||
try:
|
||||
|
||||
self.speckle_client = speckle_client
|
||||
|
||||
self.setWindowTitle("Create New Stream")
|
||||
|
||||
self.name_field.textChanged.connect(self.nameCheck)
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.onOkClicked)
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.onCancelClicked)
|
||||
self.accounts_dropdown.currentIndexChanged.connect(self.onAccountSelected)
|
||||
self.populate_accounts_dropdown()
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def nameCheck(self):
|
||||
try:
|
||||
if len(self.name_field.text()) == 0 or len(self.name_field.text()) >= 3:
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
|
||||
else:
|
||||
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
|
||||
return
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def onOkClicked(self):
|
||||
try:
|
||||
acc = get_local_accounts()[self.accounts_dropdown.currentIndex()]
|
||||
name = self.name_field.text()
|
||||
description = self.description_field.text()
|
||||
public = self.public_toggle.isChecked()
|
||||
self.handleStreamCreate.emit(acc,name,description,public)
|
||||
self.close()
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return
|
||||
|
||||
def onCancelClicked(self):
|
||||
#self.handleCancelStreamCreate.emit()
|
||||
self.close()
|
||||
|
||||
def onAccountSelected(self, index):
|
||||
try:
|
||||
account = self.speckle_accounts[index]
|
||||
self.speckle_client = SpeckleClient(account.serverInfo.url, account.serverInfo.url.startswith("https"))
|
||||
self.speckle_client.authenticate_with_token(token=account.token)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def populate_accounts_dropdown(self):
|
||||
try:
|
||||
# Populate the accounts comboBox
|
||||
self.speckle_accounts = get_local_accounts()
|
||||
self.accounts_dropdown.clear()
|
||||
self.accounts_dropdown.addItems(
|
||||
[
|
||||
f"{acc.userInfo.name}, {acc.userInfo.email} | {acc.serverInfo.url}"
|
||||
for acc in self.speckle_accounts
|
||||
]
|
||||
)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CreateStreamDialog</class>
|
||||
<widget class="QWidget" name="AddStreamDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::NonModal</enum>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetNoConstraint</enum>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="search_form">
|
||||
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="accounts_label">
|
||||
<property name="text">
|
||||
<string>Account</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="name_label">
|
||||
<property name="text">
|
||||
<string>Stream Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="name_label">
|
||||
<property name="text">
|
||||
<string>Description</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="name_label">
|
||||
<property name="text">
|
||||
<string>Public</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="accounts_dropdown"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="name_field"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="description_field"/>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="public_toggle"/>
|
||||
</item>
|
||||
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="dialog_button_box">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 345 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,55 @@
|
||||
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5 import QtCore
|
||||
import arcpy
|
||||
try:
|
||||
from speckle.plugin_utils.helpers import splitTextIntoLines
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.plugin_utils.helpers import splitTextIntoLines
|
||||
|
||||
import inspect
|
||||
|
||||
def logToUser(msg: str, func=None, level: int = 2, plugin = None, blue = False):
|
||||
print("Log to user")
|
||||
|
||||
msg = str(msg)
|
||||
dockwidget = plugin
|
||||
|
||||
try:
|
||||
if func is not None: msg += "::" + str(func)
|
||||
writeToLog(msg, level)
|
||||
if dockwidget is None: return
|
||||
|
||||
new_msg = splitTextIntoLines(msg, 70)
|
||||
|
||||
if blue is True:
|
||||
dockwidget.msgLog.addInfoButton(new_msg, level=level)
|
||||
else:
|
||||
new_msg = addLevelSymbol(new_msg, level)
|
||||
dockwidget.msgLog.addButton(new_msg, level=level)
|
||||
|
||||
except Exception as e: print(e); return
|
||||
|
||||
def logToUserWithAction(msg: str, level: int = 0, plugin = None, url = ""):
|
||||
print("Log to user with action")
|
||||
|
||||
msg = str(msg)
|
||||
dockwidget = plugin
|
||||
if dockwidget is None: return
|
||||
try:
|
||||
new_msg = splitTextIntoLines(msg, 70)
|
||||
dockwidget.msgLog.addLinkButton(new_msg, level=level, url=url)
|
||||
writeToLog(new_msg, level)
|
||||
except Exception as e: print(e); return
|
||||
|
||||
def addLevelSymbol(msg: str, level: int):
|
||||
if level == 0: msg = "🛈 " + msg
|
||||
if level == 1: msg = "⚠️ " + msg
|
||||
if level == 2: msg = "❗ " + msg
|
||||
return msg
|
||||
|
||||
def writeToLog(msg: str = "", level: int = 2):
|
||||
print("write log")
|
||||
if level == 0: arcpy.AddMessage(msg)
|
||||
if level == 1: arcpy.AddWarning(msg)
|
||||
if level == 2: arcpy.AddError(msg)
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1,601 @@
|
||||
|
||||
from typing import Any, List, Optional, Tuple, Union
|
||||
import arcpy
|
||||
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
|
||||
from arcpy.management import CreateTable
|
||||
|
||||
import os.path
|
||||
|
||||
from specklepy.api.credentials import Account, get_local_accounts
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.logging.exceptions import (
|
||||
GraphQLException,
|
||||
SpeckleException,
|
||||
)
|
||||
from specklepy.api.wrapper import StreamWrapper
|
||||
from specklepy.api.models import Branch, Stream, Streams
|
||||
from osgeo import osr
|
||||
|
||||
import inspect
|
||||
|
||||
try:
|
||||
from speckle.ui.validation import tryGetStream
|
||||
from speckle.speckle_arcgis import SpeckleGIS
|
||||
from speckle.converter.layers import getAllProjLayers
|
||||
from speckle.ui.logger import logToUser
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.validation import tryGetStream
|
||||
from speckle_toolbox.esri.toolboxes.speckle.speckle_arcgis import SpeckleGIS
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers import getAllProjLayers
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
FIELDS = ["project_streams","project_layer_selection", "lat_lon"]
|
||||
|
||||
def get_project_streams(self: SpeckleGIS, content: str = None):
|
||||
try:
|
||||
print("get proj streams")
|
||||
|
||||
print("GET proj streams")
|
||||
project = self.gis_project
|
||||
table = findOrCreateSpeckleTable(project)
|
||||
if table is None: return
|
||||
|
||||
rows = arcpy.da.SearchCursor(table, "project_streams")
|
||||
saved_streams = []
|
||||
for x in rows:
|
||||
saved_streams.append(x[0])
|
||||
|
||||
temp = []
|
||||
######### need to check whether saved streams are available (account reachable)
|
||||
if len(saved_streams) > 0:
|
||||
for url in saved_streams:
|
||||
try:
|
||||
sw = StreamWrapper(url)
|
||||
try:
|
||||
stream = tryGetStream(sw)
|
||||
except SpeckleException as e:
|
||||
logToUser(e.message, level=2, func = inspect.stack()[0][3])
|
||||
stream = None
|
||||
#strId = stream.id # will cause exception if invalid
|
||||
temp.append((sw, stream))
|
||||
except SpeckleException as e:
|
||||
logToUser(e.message, 2)
|
||||
#except GraphQLException as e:
|
||||
# logger.logToUser(e.message, Qgis.Warning)
|
||||
self.current_streams = temp
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def set_project_streams(self: SpeckleGIS):
|
||||
try:
|
||||
print("SET proj streams")
|
||||
project = self.gis_project
|
||||
table = findOrCreateSpeckleTable(project)
|
||||
print("SET proj streams 2")
|
||||
|
||||
value = [stream[0].stream_url for stream in self.current_streams] #",".join()
|
||||
print(value)
|
||||
|
||||
if table is not None:
|
||||
proj_layers = []
|
||||
lan_lot = ""
|
||||
with arcpy.da.UpdateCursor(table, FIELDS) as cursor:
|
||||
for row in cursor: # just one row
|
||||
if row[1] is not None and row[1] != "": proj_layers.append(row[1])
|
||||
if row[2] is not None and row[2] != "": lan_lot = row[2]
|
||||
cursor.deleteRow()
|
||||
del cursor
|
||||
if len(proj_layers) == 0: proj_layers.append("")
|
||||
if len(value) == 0: value.append("")
|
||||
|
||||
cursor = arcpy.da.InsertCursor(table, FIELDS )
|
||||
length = max(len(proj_layers), len(value))
|
||||
|
||||
for i in range(length):
|
||||
if i==0:
|
||||
cursor.insertRow([value[i], proj_layers[i] , lan_lot])
|
||||
else:
|
||||
try:
|
||||
cursor.insertRow([value[i], proj_layers[i] , ""])
|
||||
except:
|
||||
if len(value) <= i: cursor.insertRow(["", proj_layers[i] , ""])
|
||||
if len(proj_layers) <= i: cursor.insertRow([value[i], "" , ""])
|
||||
del cursor
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def get_project_layer_selection(self: SpeckleGIS):
|
||||
try:
|
||||
print("GET project layer selection from the table")
|
||||
project = self.gis_project
|
||||
table = findOrCreateSpeckleTable(project)
|
||||
if table is None: return
|
||||
|
||||
rows = arcpy.da.SearchCursor(table, "project_layer_selection")
|
||||
saved_layers = []
|
||||
for x in rows:
|
||||
saved_layers.append(x[0])
|
||||
|
||||
|
||||
temp = []
|
||||
proj_layers = getAllProjLayers(project)
|
||||
######### need to check whether saved streams are available (account reachable)
|
||||
if len(saved_layers) > 0:
|
||||
for layerPath in saved_layers:
|
||||
if layerPath == "": continue
|
||||
found = 0
|
||||
for layer in proj_layers:
|
||||
print(layer.dataSource)
|
||||
if layer.dataSource == layerPath:
|
||||
temp.append((layer.name, layer))
|
||||
found += 1
|
||||
break
|
||||
if found == 0:
|
||||
logToUser(f'Saved layer not found: "{layerPath}"', level=1, func = inspect.stack()[0][3])
|
||||
self.current_layers = temp
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def set_project_layer_selection(self: SpeckleGIS):
|
||||
try:
|
||||
print("SET project layer selection function")
|
||||
project = self.gis_project
|
||||
value: List[str] = [layer[1].dataSource for layer in self.current_layers] #",".join([layer[1].dataSource for layer in self.current_layers])
|
||||
print(value)
|
||||
|
||||
table = findOrCreateSpeckleTable(project)
|
||||
#print(table)
|
||||
if table is not None:
|
||||
lan_lot = ""
|
||||
proj_streams = []
|
||||
with arcpy.da.UpdateCursor(table, FIELDS) as cursor:
|
||||
for row in cursor: # just one row
|
||||
if row[0] is not None and row[0] != "": proj_streams.append(row[0])
|
||||
if row[2] is not None and row[2] != "": lan_lot = row[2]
|
||||
cursor.deleteRow()
|
||||
del cursor
|
||||
if len(proj_streams) == 0: proj_streams.append("")
|
||||
if len(value) == 0: value.append("")
|
||||
#print(proj_streams)
|
||||
|
||||
cursor = arcpy.da.InsertCursor(table, FIELDS )
|
||||
length = max(len(proj_streams), len(value))
|
||||
#print(length)
|
||||
for i in range(length):
|
||||
#print(i)
|
||||
if i==0:
|
||||
cursor.insertRow([proj_streams[i], value[i] , lan_lot])
|
||||
print(i)
|
||||
else:
|
||||
try:
|
||||
cursor.insertRow([proj_streams[i], value[i] , ""])
|
||||
except:
|
||||
if len(proj_streams) <= i: cursor.insertRow(["", value[i] , ""])
|
||||
if len(value) <= i: cursor.insertRow([proj_streams[i], "" , ""])
|
||||
#print(i)
|
||||
del cursor
|
||||
#print(table)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
print("SET project layer selection 2")
|
||||
|
||||
def get_survey_point(self: SpeckleGIS, content = None):
|
||||
try:
|
||||
print("get survey point")
|
||||
project = self.gis_project
|
||||
table = findOrCreateSpeckleTable(project)
|
||||
if table is None: return
|
||||
|
||||
rows = arcpy.da.SearchCursor(table, "lat_lon")
|
||||
points = ""
|
||||
for x in rows:
|
||||
points = x[0]
|
||||
break
|
||||
|
||||
if points != "":
|
||||
vals: List[str] = points.replace(" ","").split(";")[:2]
|
||||
self.lat, self.lon = [float(i) for i in vals]
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def set_survey_point(self: SpeckleGIS):
|
||||
|
||||
try:
|
||||
# from widget (2 strings) to local vars + update SR of the map
|
||||
print("SET survey point")
|
||||
|
||||
project = self.gis_project
|
||||
vals =[ str(self.dockwidget.surveyPointLat.text()), str(self.dockwidget.surveyPointLon.text()) ]
|
||||
|
||||
self.lat, self.lon = [float(i.replace(" ","")) for i in vals]
|
||||
pt = str(self.lat) + ";" + str(self.lon)
|
||||
|
||||
table = findOrCreateSpeckleTable(project)
|
||||
if table is not None:
|
||||
with arcpy.da.UpdateCursor(table, ["lat_lon"]) as cursor:
|
||||
for row in cursor: # just one row
|
||||
cursor.updateRow([pt])
|
||||
break
|
||||
del cursor
|
||||
|
||||
setProjectReferenceSystem(self)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.dockwidget.surveyPointLat.setText(str(self.lat))
|
||||
self.dockwidget.surveyPointLon.setText(str(self.lon))
|
||||
logToUser("Lat, Lon values invalid: " + str(e), level=2, func = inspect.stack()[0][3])
|
||||
return False
|
||||
|
||||
def setProjectReferenceSystem(self: SpeckleGIS):
|
||||
try:
|
||||
# save to project; create SR
|
||||
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.gis_project.activeMap.spatialReference = newProjSR
|
||||
logToUser("Custom project Spatial Reference successfully applied", level=0, func = inspect.stack()[0][3])
|
||||
else:
|
||||
logToUser("Custom Spatial Reference could not be created", level=1, func = inspect.stack()[0][3])
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return False
|
||||
|
||||
def findOrCreateSpeckleTable(project: ArcGISProject) -> Union[str, None]:
|
||||
try:
|
||||
path = project.filePath.replace("aprx","gdb") #"\\".join(project.filePath.split("\\")[:-1]) + "\\speckle_layers\\" #arcpy.env.workspace + "\\" #
|
||||
|
||||
if 'speckle_gis' not in arcpy.ListTables():
|
||||
try:
|
||||
table = CreateTable(path, "speckle_gis")
|
||||
arcpy.management.AddField(table, "project_streams", "TEXT")
|
||||
arcpy.management.AddField(table, "project_layer_selection", "TEXT")
|
||||
arcpy.management.AddField(table, "lat_lon", "TEXT")
|
||||
|
||||
cursor = arcpy.da.InsertCursor(table, FIELDS )
|
||||
cursor.insertRow(["",""])
|
||||
del cursor
|
||||
|
||||
except Exception as e:
|
||||
logToUser("Error creating a table: " + str(e), level=1, func = inspect.stack()[0][3])
|
||||
return None
|
||||
else:
|
||||
#print("table already exists")
|
||||
# make sure fileds exist
|
||||
table = path + "\\speckle_gis"
|
||||
findOrCreateTableField(table, FIELDS[0])
|
||||
findOrCreateTableField(table, FIELDS[1])
|
||||
findOrCreateTableField(table, FIELDS[2])
|
||||
|
||||
findOrCreateRow(table, FIELDS)
|
||||
|
||||
return table
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def findOrCreateTableField(table: str, field: str):
|
||||
try:
|
||||
with arcpy.da.UpdateCursor(table, [field]) as cursor:
|
||||
value = None
|
||||
for row in cursor:
|
||||
value = row # tuple(val,)
|
||||
if value[0] is None: cursor.updateRow("")
|
||||
break # look at the 1st row only
|
||||
del cursor
|
||||
|
||||
#if value is None: # if there are no rows
|
||||
# cursor = arcpy.da.InsertCursor(table, [field])
|
||||
# cursor.insertRow([""])
|
||||
# del cursor
|
||||
|
||||
except: # if field doesn't exist
|
||||
arcpy.management.AddField(table, field, "TEXT")
|
||||
#cursor = arcpy.da.InsertCursor(table, [field] )
|
||||
#cursor.insertRow([""])
|
||||
del cursor
|
||||
|
||||
def findOrCreateRow(table:str, fields: List[str]):
|
||||
try:
|
||||
# check if the row exists
|
||||
cursor = arcpy.da.SearchCursor(table, fields)
|
||||
k=-1
|
||||
for k, row in enumerate(cursor):
|
||||
#print(row)
|
||||
break
|
||||
del cursor
|
||||
|
||||
# if no rows
|
||||
if k == -1:
|
||||
cursor = arcpy.da.InsertCursor(table, fields)
|
||||
cursor.insertRow(["", "", ""])
|
||||
del cursor
|
||||
else:
|
||||
with arcpy.da.UpdateCursor(table, fields) as cursor:
|
||||
for row in cursor:
|
||||
if None in row: cursor.updateRow(["","",""])
|
||||
break # look at the 1st row only
|
||||
del cursor
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
r'''
|
||||
class speckleInputsClass:
|
||||
#def __init__(self):
|
||||
print("CREATING speckle inputs first time________")
|
||||
instances = []
|
||||
accounts: List[Account] = get_local_accounts()
|
||||
account = None
|
||||
streams_default: Optional[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
|
||||
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(
|
||||
account.serverInfo.url,
|
||||
account.serverInfo.url.startswith("https")
|
||||
)
|
||||
speckle_client.authenticate_with_token(token=account.token)
|
||||
streams_default = speckle_client.stream.search("")
|
||||
|
||||
def __init__(self) -> None:
|
||||
print("___start speckle inputs________")
|
||||
self.all_layers = []
|
||||
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():
|
||||
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):
|
||||
try:
|
||||
f = open(self.stream_file_path, "r")
|
||||
content = f.read()
|
||||
self.saved_streams = self.getProjectStreams(content)
|
||||
f.close()
|
||||
except: pass
|
||||
|
||||
elif len(self.stream_file_path) >10:
|
||||
f = open(self.stream_file_path, "x")
|
||||
f.close()
|
||||
f = open(self.stream_file_path, "w")
|
||||
content = ""
|
||||
f.write(content)
|
||||
f.close()
|
||||
except: self.project = None; print("Project not found")
|
||||
self.instances.append(self)
|
||||
|
||||
def getProjectStreams(self, content: str = None):
|
||||
print("get proj streams")
|
||||
if not content:
|
||||
content = self.stream_file_path
|
||||
try:
|
||||
f = open(self.stream_file_path, "r")
|
||||
content = f.read()
|
||||
f.close()
|
||||
except: pass
|
||||
|
||||
######### need to check whether saved streams are available (account reachable)
|
||||
if content:
|
||||
streamsTuples = []
|
||||
for i, url in enumerate(content.split(",")):
|
||||
|
||||
streamExists = 0
|
||||
index = 0
|
||||
try:
|
||||
#print(url)
|
||||
sw = StreamWrapper(url)
|
||||
stream = self.tryGetStream(sw)
|
||||
|
||||
for st in streamsTuples:
|
||||
if isinstance(stream, Stream) and st[0].stream_id == stream.id:
|
||||
streamExists = 1;
|
||||
break
|
||||
index += 1
|
||||
if streamExists == 1: del streamsTuples[index]
|
||||
streamsTuples.insert(0,(sw, stream))
|
||||
|
||||
except SpeckleException as e:
|
||||
arcpy.AddMessage(str(e.args))
|
||||
return streamsTuples
|
||||
else: return []
|
||||
|
||||
def tryGetStream (self,sw: StreamWrapper) -> 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(id = steamId, branch_limit = 100, commit_limit = 100)
|
||||
if isinstance(stream, GraphQLException):
|
||||
raise SpeckleException(stream.errors[0]['message'])
|
||||
return stream
|
||||
else:
|
||||
raise SpeckleException('Invalid StreamWrapper provided')
|
||||
|
||||
class toolboxInputsClass:
|
||||
|
||||
print("CREATING UI inputs first time________")
|
||||
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] = []
|
||||
messageSpeckle: str = ""
|
||||
action: int = 1 #send
|
||||
project = None
|
||||
stream_file_path: str = ""
|
||||
# Get the target item's Metadata object
|
||||
|
||||
def __init__(self) -> None:
|
||||
print("___start UI inputs________")
|
||||
try:
|
||||
aprx = ArcGISProject('CURRENT')
|
||||
project = aprx
|
||||
self.stream_file_path: str = aprx.filePath.replace("aprx","gdb") + "\\speckle_streams.txt"
|
||||
if os.path.exists(self.stream_file_path):
|
||||
try:
|
||||
f = open(self.stream_file_path, "r")
|
||||
content = f.read()
|
||||
self.lat, self.lon = self.get_survey_point(content)
|
||||
f.close()
|
||||
except: pass
|
||||
except: print("Project not found")
|
||||
try:
|
||||
aprx = ArcGISProject('CURRENT')
|
||||
self.project = aprx
|
||||
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:
|
||||
print("SET proj streams")
|
||||
|
||||
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()
|
||||
|
||||
f = open(self.stream_file_path, "w")
|
||||
if str(wr.stream_url) in existing_content:
|
||||
new_content = existing_content.replace(str(wr.stream_url) + "," , "")
|
||||
else:
|
||||
new_content = existing_content
|
||||
|
||||
if add == True: new_content += str(wr.stream_url) + "," # add stream
|
||||
else: pass # remove stream
|
||||
|
||||
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(str(wr.stream_url) + ",")
|
||||
f.close()
|
||||
|
||||
def get_survey_point(self, content = None) -> Tuple[float]:
|
||||
# get from saved project
|
||||
print("get survey point")
|
||||
x = y = 0
|
||||
if not content:
|
||||
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:
|
||||
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)
|
||||
|
||||
def set_survey_point(self, coords: List[float]):
|
||||
# from widget (2 strings) to local vars + update SR of the map
|
||||
print("SET survey point")
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
# 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")
|
||||
|
||||
else:
|
||||
arcpy.AddWarning("Custom CRS could not be created: not enough coordinates provided")
|
||||
|
||||
return True
|
||||
'''
|
||||
|
||||
|
After Width: | Height: | Size: 400 B |
@@ -0,0 +1,620 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
from typing import List
|
||||
#from speckle.converter.layers import getLayers
|
||||
|
||||
#import ui.speckle_qgis_dialog
|
||||
|
||||
from specklepy.logging.exceptions import (SpeckleException, GraphQLException)
|
||||
from PyQt5 import QtWidgets, uic
|
||||
from PyQt5 import QtGui
|
||||
from PyQt5.QtGui import QIcon, QPixmap
|
||||
from PyQt5.QtWidgets import (QMainWindow, QApplication, QWidget,
|
||||
QListWidgetItem, QAction, QDockWidget, QVBoxLayout,
|
||||
QHBoxLayout, QWidget, QLabel)
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtCore import pyqtSignal, Qt, QSize, QEvent
|
||||
from PyQt5 import QtGui, uic
|
||||
|
||||
from specklepy.api.credentials import get_local_accounts
|
||||
|
||||
import importlib
|
||||
|
||||
from specklepy.api.wrapper import StreamWrapper
|
||||
from specklepy.api.client import SpeckleClient
|
||||
|
||||
import arcpy
|
||||
|
||||
import inspect
|
||||
|
||||
try:
|
||||
#from speckle.speckle_arcgis_new import Speckle
|
||||
from speckle.converter.layers import getLayers
|
||||
from speckle.converter.layers import getAllProjLayers
|
||||
from speckle.ui.logger import logToUser
|
||||
from speckle.ui.LogWidget import LogWidget
|
||||
except:
|
||||
#from speckle_toolbox.esri.toolboxes.speckle.speckle_arcgis_new import Speckle
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers import getLayers
|
||||
from speckle_toolbox.esri.toolboxes.speckle.converter.layers import getAllProjLayers
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.LogWidget import LogWidget
|
||||
|
||||
#from ui.validation import tryGetStream
|
||||
|
||||
# Create module-like object
|
||||
#pytPath = os.path.dirname(os.path.abspath(__file__)).replace("/speckle/ui","/Speckle.pyt")
|
||||
#print(pytPath)
|
||||
#pytModule = importlib.machinery.SourceFileLoader("specklePyt", pytPath )
|
||||
#specklePyt = pytModule.load_module("specklePyt")
|
||||
|
||||
|
||||
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
|
||||
|
||||
COLOR_HIGHLIGHT = (210,210,210)
|
||||
|
||||
SPECKLE_COLOR = (59,130,246)
|
||||
SPECKLE_COLOR_LIGHT = (69,140,255)
|
||||
ICON_LOGO = os.path.dirname(os.path.abspath(__file__)) + "/logo-slab-white@0.5x.png"
|
||||
|
||||
ICON_SEARCH = os.path.dirname(os.path.abspath(__file__)) + "/magnify.png"
|
||||
|
||||
ICON_DELETE = os.path.dirname(os.path.abspath(__file__)) + "/delete.png"
|
||||
ICON_DELETE_BLUE = os.path.dirname(os.path.abspath(__file__)) + "/delete-blue.png"
|
||||
|
||||
ICON_SEND = os.path.dirname(os.path.abspath(__file__)) + "/cube-send.png"
|
||||
ICON_RECEIVE = os.path.dirname(os.path.abspath(__file__)) + "/cube-receive.png"
|
||||
|
||||
ICON_SEND_BLACK = os.path.dirname(os.path.abspath(__file__)) + "/cube-send-black.png"
|
||||
ICON_RECEIVE_BLACK = os.path.dirname(os.path.abspath(__file__)) + "/cube-receive-black.png"
|
||||
|
||||
ICON_SEND_BLUE = os.path.dirname(os.path.abspath(__file__)) + "/cube-send-blue.png"
|
||||
ICON_RECEIVE_BLUE = os.path.dirname(os.path.abspath(__file__)) + "/cube-receive-blue.png"
|
||||
|
||||
ui_class = os.path.dirname(os.path.abspath(__file__)) + "/speckle_qgis_dialog_base.ui"
|
||||
print(os.path.dirname(__file__))
|
||||
|
||||
class SpeckleGISDialog(QMainWindow):
|
||||
|
||||
closingPlugin = pyqtSignal()
|
||||
streamList: QtWidgets.QComboBox
|
||||
sendModeButton: QtWidgets.QPushButton
|
||||
receiveModeButton: QtWidgets.QPushButton
|
||||
streamBranchDropdown: QtWidgets.QComboBox
|
||||
layerSendModeDropdown: QtWidgets.QComboBox
|
||||
commitDropdown: QtWidgets.QComboBox
|
||||
layersWidget: QtWidgets.QListWidget
|
||||
saveLayerSelection: QtWidgets.QPushButton
|
||||
runButton: QtWidgets.QPushButton
|
||||
msgLog: LogWidget = None
|
||||
|
||||
gridLayoutTitleBar = QtWidgets.QGridLayout
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor."""
|
||||
print("START MAIN WINDOW")
|
||||
super(SpeckleGISDialog, self).__init__(None)#, QtCore.Qt.WindowStaysOnTopHint)
|
||||
uic.loadUi(ui_class, self) # Load the .ui file
|
||||
#self.installEventFilter(self)
|
||||
self.show()
|
||||
#self.instances.append(1)
|
||||
try:
|
||||
self.streamBranchDropdown.setMaxCount(100)
|
||||
self.commitDropdown.setMaxCount(100)
|
||||
|
||||
self.streams_add_button.setFlat(True)
|
||||
self.streams_remove_button.setFlat(True)
|
||||
self.saveSurveyPoint.setFlat(True)
|
||||
self.saveLayerSelection.setFlat(True)
|
||||
self.reloadButton.setFlat(True)
|
||||
self.closeButton.setFlat(True)
|
||||
|
||||
color = f"color: rgb{str(SPECKLE_COLOR)};"
|
||||
backgr_color = f"background-color: rgb{str(SPECKLE_COLOR)};"
|
||||
backgr_color_light = f"background-color: rgb{str(SPECKLE_COLOR_LIGHT)};"
|
||||
backgr_image_del = f"border-image: url({ICON_DELETE_BLUE});"
|
||||
self.streams_add_button.setIcon(QIcon(ICON_SEARCH))
|
||||
self.streams_add_button.setMaximumWidth(25)
|
||||
self.streams_add_button.setStyleSheet("QPushButton {padding:3px;padding-left:5px;border: none; text-align: left;} QPushButton:hover { " + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + f"{color}" + " }")
|
||||
self.streams_remove_button.setIcon(QIcon(ICON_DELETE))
|
||||
self.streams_remove_button.setMaximumWidth(25)
|
||||
self.streams_remove_button.setStyleSheet("QPushButton {padding:3px;padding-left:5px;border: none; text-align: left; image-position:right} QPushButton:hover { " + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + f"{color}" + " }") #+ f"{backgr_image_del}"
|
||||
|
||||
self.saveLayerSelection.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{color}" + " }")
|
||||
self.saveSurveyPoint.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{color}" + " }")
|
||||
self.reloadButton.setStyleSheet("QPushButton {text-align: left;} QPushButton:hover { " + f"{color}" + " }")
|
||||
self.closeButton.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{color}" + " }")
|
||||
|
||||
exitIcon = QPixmap(ICON_LOGO)
|
||||
exitActIcon = QIcon(exitIcon)
|
||||
|
||||
backgr_color = f"background-color: rgb{str(SPECKLE_COLOR)};"
|
||||
backgr_color_light = f"background-color: rgb{str(SPECKLE_COLOR_LIGHT)};"
|
||||
|
||||
# create a label
|
||||
text_label = QtWidgets.QPushButton(" for ArcGIS")
|
||||
text_label.setStyleSheet("border: 0px;"
|
||||
"color: white;"
|
||||
f"{backgr_color}"
|
||||
"top-margin: 40 px;"
|
||||
"padding: 10px;"
|
||||
"padding-left: 20px;"
|
||||
"font-size: 15px;"
|
||||
"height: 30px;"
|
||||
"text-align: left;"
|
||||
)
|
||||
text_label.setIcon(exitActIcon)
|
||||
text_label.setIconSize(QSize(300, 93))
|
||||
text_label.setMinimumSize(QSize(100, 40))
|
||||
text_label.setMaximumWidth(220)
|
||||
|
||||
version = ""
|
||||
try:
|
||||
metadata_file = os.path.dirname(__file__)[:-2] + "metadata.txt"
|
||||
with open(metadata_file, "r") as file:
|
||||
lines = file.readlines()
|
||||
for i, line in enumerate(lines):
|
||||
if "version=" in line:
|
||||
version = "v " + line.replace("version=", "")
|
||||
break
|
||||
except: pass
|
||||
|
||||
version_label = QtWidgets.QPushButton(f"{version}")
|
||||
version_label.setStyleSheet("border: 0px;"
|
||||
"color: white;"
|
||||
f"{backgr_color}"
|
||||
"padding-top: 15px;"
|
||||
"padding-left: 0px;"
|
||||
"margin-left: 0px;"
|
||||
"font-size: 10px;"
|
||||
"height: 30px;"
|
||||
"text-align: left;"
|
||||
)
|
||||
|
||||
widget = QWidget()
|
||||
widget.setStyleSheet(f"{backgr_color}")
|
||||
connect_box = QHBoxLayout(widget)
|
||||
connect_box.addWidget(text_label) #, alignment=Qt.AlignCenter)
|
||||
connect_box.addWidget(version_label)
|
||||
connect_box.setContentsMargins(0, 0, 0, 0)
|
||||
self.gridLayoutTitleBar.addWidget(widget) # fro QMainWindow
|
||||
#self.setTitleBarWidget(widget) # for QDockWidget
|
||||
|
||||
self.sendModeButton.setStyleSheet("QPushButton {padding: 10px; border: 0px; " + f"color: rgb{str(SPECKLE_COLOR)};"+ "} QPushButton:hover { " + "}" )
|
||||
self.sendModeButton.setIcon(QIcon(ICON_SEND_BLUE))
|
||||
|
||||
self.receiveModeButton.setFlat(True)
|
||||
self.receiveModeButton.setStyleSheet("QPushButton {padding: 10px; border: 0px;}"+ "QPushButton:hover { " + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + "}" )
|
||||
self.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLACK))
|
||||
|
||||
self.runButton.setStyleSheet("QPushButton {color: white;border: 0px;border-radius: 17px;padding: 10px;"+ f"{backgr_color}" + "} QPushButton:hover { "+ f"{backgr_color_light}" + " }")
|
||||
self.runButton.setMaximumWidth(200)
|
||||
self.runButton.setIcon(QIcon(ICON_SEND))
|
||||
|
||||
# add widgets that will only show on event trigger
|
||||
logWidget = LogWidget(parent=self)
|
||||
self.layout().addWidget(logWidget)
|
||||
self.msgLog = logWidget
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
try:
|
||||
print("resize")
|
||||
QtWidgets.QMainWindow.resizeEvent(self, event)
|
||||
if self.msgLog.size().height() != 0: # visible
|
||||
self.msgLog.setGeometry(0, 0, self.msgLog.parentWidget.frameSize().width(), self.msgLog.parentWidget.frameSize().height()) #.resize(self.frameSize().width(), self.frameSize().height())
|
||||
except Exception as e:
|
||||
#logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
|
||||
return
|
||||
|
||||
def closeEvent(self, event):
|
||||
try:
|
||||
#import threading
|
||||
print("Close event")
|
||||
#threads = threading.enumerate()
|
||||
#print(f"Threads total: {str(len(threads))}: {str(threads)}")
|
||||
|
||||
#print(self.instances)
|
||||
|
||||
self.closingPlugin.emit()
|
||||
event.accept()
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def clearDropdown(self):
|
||||
try:
|
||||
#self.streamIdField.clear()
|
||||
self.streamBranchDropdown.clear()
|
||||
self.commitDropdown.clear()
|
||||
#self.layerSendModeDropdown.clear()
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def reloadDialogUI(self, plugin):
|
||||
try:
|
||||
self.clearDropdown()
|
||||
self.populateUI(plugin)
|
||||
self.enableElements(plugin)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
|
||||
|
||||
def run(self, plugin):
|
||||
try:
|
||||
print("dockwidget run")
|
||||
# Setup events on first load only!
|
||||
self.setupOnFirstLoad(plugin)
|
||||
# Connect streams section events
|
||||
self.completeStreamSection(plugin)
|
||||
# Populate the UI dropdowns
|
||||
self.populateUI(plugin)
|
||||
print("dockwidget run end")
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
|
||||
def setupOnFirstLoad(self, plugin):
|
||||
try:
|
||||
self.runButton.clicked.connect(plugin.onRunButtonClicked)
|
||||
|
||||
self.streams_add_button.clicked.connect( plugin.onStreamAddButtonClicked )
|
||||
self.reloadButton.clicked.connect(plugin.reloadUI)
|
||||
self.closeButton.clicked.connect(plugin.onClosePlugin)
|
||||
self.saveSurveyPoint.clicked.connect(plugin.set_survey_point)
|
||||
self.saveLayerSelection.clicked.connect(lambda: self.populateLayerDropdown(plugin))
|
||||
self.sendModeButton.clicked.connect(lambda: self.setSendMode(plugin))
|
||||
self.layerSendModeDropdown.currentIndexChanged.connect( lambda: self.layerSendModeChange(plugin) )
|
||||
self.receiveModeButton.clicked.connect(lambda: self.setReceiveMode(plugin))
|
||||
|
||||
self.streamBranchDropdown.currentIndexChanged.connect( lambda: self.runBtnStatusChanged(plugin) )
|
||||
self.commitDropdown.currentIndexChanged.connect( lambda: self.runBtnStatusChanged(plugin) )
|
||||
|
||||
self.closingPlugin.connect(plugin.onClosePlugin)
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def setSendMode(self, plugin):
|
||||
try:
|
||||
plugin.btnAction = 0 # send
|
||||
color = f"color: rgb{str(SPECKLE_COLOR)};"
|
||||
self.sendModeButton.setStyleSheet("border: 0px;"
|
||||
f"color: rgb{str(SPECKLE_COLOR)};"
|
||||
"padding: 10px;")
|
||||
self.sendModeButton.setIcon(QIcon(ICON_SEND_BLUE))
|
||||
self.sendModeButton.setFlat(False)
|
||||
self.receiveModeButton.setFlat(True)
|
||||
self.receiveModeButton.setStyleSheet("QPushButton {border: 0px; color: black; padding: 10px; } QPushButton:hover { " + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + " };")
|
||||
self.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLACK))
|
||||
#self.receiveModeButton.setFlat(True)
|
||||
self.runButton.setProperty("text", " SEND")
|
||||
self.runButton.setIcon(QIcon(ICON_SEND))
|
||||
|
||||
# enable sections only if in "saved streams" mode
|
||||
if self.layerSendModeDropdown.currentIndex() == 1: self.layersWidget.setEnabled(True)
|
||||
if self.layerSendModeDropdown.currentIndex() == 1: self.saveLayerSelection.setEnabled(True)
|
||||
self.commitDropdown.setEnabled(False)
|
||||
self.messageInput.setEnabled(True)
|
||||
self.layerSendModeDropdown.setEnabled(True)
|
||||
|
||||
self.runBtnStatusChanged(plugin)
|
||||
return
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def setReceiveMode(self, plugin):
|
||||
try:
|
||||
plugin.btnAction = 1 # receive
|
||||
color = f"color: rgb{str(SPECKLE_COLOR)};"
|
||||
self.receiveModeButton.setStyleSheet("border: 0px;"
|
||||
f"color: rgb{str(SPECKLE_COLOR)};"
|
||||
"padding: 10px;")
|
||||
self.sendModeButton.setIcon(QIcon(ICON_SEND_BLACK))
|
||||
self.sendModeButton.setStyleSheet("QPushButton {border: 0px; color: black; padding: 10px;} QPushButton:hover { " + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + " };")
|
||||
self.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLUE))
|
||||
self.sendModeButton.setFlat(True)
|
||||
self.receiveModeButton.setFlat(False)
|
||||
#self.sendModeButton.setFlat(True)
|
||||
self.runButton.setProperty("text", " RECEIVE")
|
||||
self.runButton.setIcon(QIcon(ICON_RECEIVE))
|
||||
#self.layerSendModeChange(plugin, 1)
|
||||
self.commitDropdown.setEnabled(True)
|
||||
self.layersWidget.setEnabled(False)
|
||||
self.messageInput.setEnabled(False)
|
||||
self.saveLayerSelection.setEnabled(False)
|
||||
self.layerSendModeDropdown.setEnabled(False)
|
||||
|
||||
self.runBtnStatusChanged(plugin)
|
||||
return
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def completeStreamSection(self, plugin):
|
||||
self.streams_remove_button.clicked.connect( lambda: self.onStreamRemoveButtonClicked(plugin) )
|
||||
self.streamList.currentIndexChanged.connect( lambda: self.onActiveStreamChanged(plugin) )
|
||||
self.streamBranchDropdown.currentIndexChanged.connect( lambda: self.populateActiveCommitDropdown(plugin) )
|
||||
return
|
||||
|
||||
def populateUI(self, plugin):
|
||||
try:
|
||||
self.populateLayerSendModeDropdown()
|
||||
self.populateLayerDropdown(plugin, False)
|
||||
#items = [self.layersWidget.item(x).text() for x in range(self.layersWidget.count())]
|
||||
self.populateProjectStreams(plugin)
|
||||
self.populateSurveyPoint(plugin)
|
||||
|
||||
self.runBtnStatusChanged(plugin)
|
||||
self.runButton.setEnabled(False)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def runBtnStatusChanged(self, plugin):
|
||||
try:
|
||||
commitStr = str(self.commitDropdown.currentText())
|
||||
branchStr = str(self.streamBranchDropdown.currentText())
|
||||
|
||||
if plugin.btnAction == 1: # on receive
|
||||
if commitStr == "":
|
||||
self.runButton.setEnabled(False)
|
||||
else:
|
||||
self.runButton.setEnabled(True)
|
||||
|
||||
if plugin.btnAction == 0: # on send
|
||||
if branchStr == "":
|
||||
self.runButton.setEnabled(False)
|
||||
elif branchStr != "" and self.layerSendModeDropdown.currentIndex() == 1 and len(plugin.current_layers) == 0: # saved layers; but the list is empty
|
||||
self.runButton.setEnabled(False)
|
||||
else:
|
||||
self.runButton.setEnabled(True)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
|
||||
def layerSendModeChange(self, plugin, runMode = None):
|
||||
try:
|
||||
print("Send mode changed")
|
||||
|
||||
if self.layerSendModeDropdown.currentIndex() == 0 or runMode == 1: # by manual selection OR receive mode
|
||||
self.current_layers = []
|
||||
self.layersWidget.setEnabled(False)
|
||||
self.saveLayerSelection.setEnabled(False)
|
||||
|
||||
elif self.layerSendModeDropdown.currentIndex() == 1 and (runMode == 0 or runMode is None): # by saved AND when Send mode
|
||||
self.layersWidget.setEnabled(True)
|
||||
self.saveLayerSelection.setEnabled(True)
|
||||
|
||||
branchStr = str(self.streamBranchDropdown.currentText())
|
||||
if self.layerSendModeDropdown.currentIndex() == 0:
|
||||
if branchStr == "": self.runButton.setEnabled(False) # by manual selection
|
||||
else: self.runButton.setEnabled(True) # by manual selection
|
||||
elif self.layerSendModeDropdown.currentIndex() == 1: self.runBtnStatusChanged(plugin) # by saved
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
|
||||
def populateLayerDropdown(self, plugin, bySelection: bool = True):
|
||||
print("populate layer dropdown / clicked save selection")
|
||||
if not self: return
|
||||
try:
|
||||
from speckle.ui.project_vars import set_project_layer_selection
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import set_project_layer_selection
|
||||
|
||||
try:
|
||||
self.layersWidget.clear()
|
||||
nameDisplay = []
|
||||
project = plugin.gis_project
|
||||
|
||||
if bySelection is False: # read from project data
|
||||
print("populate layers from saved data")
|
||||
#print(project)
|
||||
#print(project.activeMap)
|
||||
|
||||
all_layers_ids = [l.dataSource for l in getAllProjLayers(project)]
|
||||
for layer_tuple in plugin.current_layers:
|
||||
if layer_tuple[1].dataSource in all_layers_ids:
|
||||
listItem = self.fillLayerList(layer_tuple[1])
|
||||
self.layersWidget.addItem(listItem)
|
||||
|
||||
else: # read selected layers
|
||||
# Fetch selected layers
|
||||
print("populate layers from selection")
|
||||
|
||||
plugin.current_layers = []
|
||||
layers = getLayers(plugin, bySelection) # List[QgsLayerTreeNode]
|
||||
print(layers)
|
||||
for i, layer in enumerate(layers):
|
||||
plugin.current_layers.append((layer.name, layer))
|
||||
listItem = self.fillLayerList(layer)
|
||||
self.layersWidget.addItem(listItem)
|
||||
print("populate layers from selection 2")
|
||||
set_project_layer_selection(plugin)
|
||||
print("populate layers from selection 3")
|
||||
|
||||
self.layersWidget.setIconSize(QSize(20, 20))
|
||||
self.runBtnStatusChanged(plugin)
|
||||
|
||||
return
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def fillLayerList(self, layer):
|
||||
print("Fill layer list")
|
||||
|
||||
try:
|
||||
ICON_XXL = os.path.dirname(os.path.abspath(__file__)) + "/size-xxl.png"
|
||||
ICON_RASTER = os.path.dirname(os.path.abspath(__file__)) + "/legend_raster.png"
|
||||
ICON_POLYGON = os.path.dirname(os.path.abspath(__file__)) + "/legend_polygon.png"
|
||||
ICON_LINE = os.path.dirname(os.path.abspath(__file__)) + "/legend_line.png"
|
||||
ICON_POINT = os.path.dirname(os.path.abspath(__file__)) + "/legend_point.png"
|
||||
|
||||
listItem = QListWidgetItem(layer.name)
|
||||
#print(listItem)
|
||||
|
||||
if layer.isRasterLayer: # and layer.width()*layer.height() > 1000000:
|
||||
listItem.setIcon(QIcon(ICON_RASTER))
|
||||
|
||||
elif layer.isFeatureLayer: # and layer.featureCount() > 20000:
|
||||
geomType = arcpy.Describe(layer.dataSource).shapeType
|
||||
if geomType == "Polygon": listItem.setIcon(QIcon(ICON_POLYGON))
|
||||
elif geomType == "Polyline": listItem.setIcon(QIcon(ICON_LINE))
|
||||
elif geomType == "Point" or geomType == "Multipoint": listItem.setIcon(QIcon(ICON_POINT))
|
||||
else:
|
||||
listItem.setIcon(QIcon(ICON_XXL))
|
||||
#else:
|
||||
# icon = QgsIconUtils().iconForLayer(layer)
|
||||
# listItem.setIcon(icon)
|
||||
|
||||
return listItem
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def populateSurveyPoint(self, plugin):
|
||||
if not self:
|
||||
return
|
||||
try:
|
||||
self.surveyPointLat.clear()
|
||||
self.surveyPointLat.setText(str(plugin.lat))
|
||||
self.surveyPointLon.clear()
|
||||
self.surveyPointLon.setText(str(plugin.lon))
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def enableElements(self, plugin):
|
||||
try:
|
||||
self.sendModeButton.setEnabled(plugin.is_setup)
|
||||
self.receiveModeButton.setEnabled(plugin.is_setup)
|
||||
self.runButton.setEnabled(plugin.is_setup)
|
||||
self.streams_add_button.setEnabled(plugin.is_setup)
|
||||
if plugin.is_setup is False: self.streams_remove_button.setEnabled(plugin.is_setup)
|
||||
self.streamBranchDropdown.setEnabled(plugin.is_setup)
|
||||
self.layerSendModeDropdown.setEnabled(plugin.is_setup)
|
||||
self.commitDropdown.setEnabled(False)
|
||||
self.show()
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def populateProjectStreams(self, plugin):
|
||||
|
||||
try:
|
||||
from speckle.ui.project_vars import set_project_streams
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import set_project_streams
|
||||
|
||||
try:
|
||||
if not self: return
|
||||
self.streamList.clear()
|
||||
for stream in plugin.current_streams:
|
||||
self.streamList.addItems(
|
||||
[f"Stream not accessible - {stream[0].stream_id}" if stream[1] is None or isinstance(stream[1], SpeckleException) else f"{stream[1].name}, {stream[1].id} | {stream[0].stream_url.split('/streams')[0]}"]
|
||||
)
|
||||
if len(plugin.current_streams)==0: self.streamList.addItems([""])
|
||||
self.streamList.addItems(["Create New Stream"])
|
||||
set_project_streams(plugin)
|
||||
index = self.streamList.currentIndex()
|
||||
if index == -1: self.streams_remove_button.setEnabled(False)
|
||||
else: self.streams_remove_button.setEnabled(True)
|
||||
|
||||
if len(plugin.current_streams)>0: plugin.active_stream = plugin.current_streams[0]
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
|
||||
def onActiveStreamChanged(self, plugin):
|
||||
|
||||
if not self: return
|
||||
try:
|
||||
index = self.streamList.currentIndex()
|
||||
if (len(plugin.current_streams) == 0 and index ==1) or (len(plugin.current_streams)>0 and index == len(plugin.current_streams)):
|
||||
self.populateProjectStreams(plugin)
|
||||
plugin.onStreamCreateClicked()
|
||||
return
|
||||
if len(plugin.current_streams) == 0: return
|
||||
if index == -1: return
|
||||
|
||||
try: plugin.active_stream = plugin.current_streams[index]
|
||||
except: plugin.active_stream = None
|
||||
|
||||
self.populateActiveStreamBranchDropdown(plugin)
|
||||
self.populateActiveCommitDropdown(plugin)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def populateLayerSendModeDropdown(self):
|
||||
if not self: return
|
||||
try:
|
||||
self.layerSendModeDropdown.clear()
|
||||
self.layerSendModeDropdown.addItems(
|
||||
["Send visible layers", "Send saved layers"]
|
||||
)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def populateActiveStreamBranchDropdown(self, plugin):
|
||||
if not self: return
|
||||
if plugin.active_stream is None: return
|
||||
try:
|
||||
self.streamBranchDropdown.clear()
|
||||
if isinstance(plugin.active_stream[1], SpeckleException):
|
||||
#logger.logToUser("Some streams cannot be accessed", Qgis.Warning)
|
||||
return
|
||||
elif plugin.active_stream is None or plugin.active_stream[1] is None or plugin.active_stream[1].branches is None:
|
||||
return
|
||||
self.streamBranchDropdown.addItems(
|
||||
[f"{branch.name}" for branch in plugin.active_stream[1].branches.items]
|
||||
)
|
||||
self.streamBranchDropdown.addItems(["Create New Branch"])
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def populateActiveCommitDropdown(self, plugin):
|
||||
if not self: return
|
||||
try:
|
||||
self.commitDropdown.clear()
|
||||
if plugin.active_stream is None: return
|
||||
branchName = self.streamBranchDropdown.currentText()
|
||||
if branchName == "": return
|
||||
if branchName == "Create New Branch":
|
||||
self.streamBranchDropdown.setCurrentText("main")
|
||||
plugin.onBranchCreateClicked()
|
||||
return
|
||||
branch = None
|
||||
if isinstance(plugin.active_stream[1], SpeckleException):
|
||||
#logger.logToUser("Some streams cannot be accessed", Qgis.Warning)
|
||||
return
|
||||
elif plugin.active_stream[1]:
|
||||
for b in plugin.active_stream[1].branches.items:
|
||||
if b.name == branchName:
|
||||
branch = b
|
||||
break
|
||||
try:
|
||||
self.commitDropdown.addItems(
|
||||
[f"{commit.id}"+ " | " + f"{commit.message}" for commit in branch.commits.items]
|
||||
)
|
||||
except: pass
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
def onStreamRemoveButtonClicked(self, plugin):
|
||||
try:
|
||||
#from ui.project_vars import set_project_streams
|
||||
if not self: return
|
||||
index = self.streamList.currentIndex()
|
||||
if len(plugin.current_streams) > 0: plugin.current_streams.pop(index)
|
||||
plugin.active_stream = None
|
||||
self.streamBranchDropdown.clear()
|
||||
self.commitDropdown.clear()
|
||||
#self.streamIdField.setText("")
|
||||
|
||||
#set_project_streams(plugin)
|
||||
self.populateProjectStreams(plugin)
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
|
||||
|
||||
@@ -0,0 +1,316 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>SpeckleQArcGISDialog</class>
|
||||
<widget class="QMainWindow" name="SpeckleQArcGISDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayoutTitleBar">
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="leftMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
|
||||
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="streamListButtons">
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="streamListLabel">
|
||||
<property name="text">
|
||||
<string>Stream</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="streamListButtons" stretch="20,1">
|
||||
<item>
|
||||
<widget class="QComboBox" name="streamList"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="streams_add_button">
|
||||
<property name="text">
|
||||
<string> </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="streams_remove_button">
|
||||
<property name="text">
|
||||
<string> </string>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>10</width>
|
||||
<height>10</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
|
||||
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="streamBranchLabel">
|
||||
<property name="text">
|
||||
<string>Branch</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="streamBranchDropdown"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="commitLabel">
|
||||
<property name="text">
|
||||
<string>Commit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="commitDropdown"/>
|
||||
</item>
|
||||
|
||||
<item row="4" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
|
||||
<item row="5" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="sendModeButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Send</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="receiveModeButton">
|
||||
<property name="text">
|
||||
<string>Receive</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
<item row="6" column="1">
|
||||
<widget class="QComboBox" name="layerSendModeDropdown"/>
|
||||
</item>
|
||||
|
||||
|
||||
|
||||
<item row="7" column="1">
|
||||
<widget class="QListWidget" name="layersWidget">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustToContents</enum>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QListView::Fixed</enum>
|
||||
</property>
|
||||
<property name="viewMode">
|
||||
<enum>QListView::ListMode</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
|
||||
<item row="8" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<widget class="QPushButton" name="saveLayerSelection">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set visible layers as selection</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="messageLabel">
|
||||
<property name="text">
|
||||
<string>Message</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QLineEdit" name="messageInput">
|
||||
<property name="placeholderText">
|
||||
<string>Sent XXX objects from ArcGIS</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
|
||||
|
||||
<item row="10" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<widget class="QPushButton" name="runButton">
|
||||
<property name="text">
|
||||
<string> SEND</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="surveyPointLabel">
|
||||
<property name="text">
|
||||
<string>Lat, Lon</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="surveyPointLat">
|
||||
<property name="placeholderText">
|
||||
<string>0.0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="surveyPointLon">
|
||||
<property name="placeholderText">
|
||||
<string>0.0</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<widget class="QPushButton" name="saveSurveyPoint">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set as a project center</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
<item row="12" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
|
||||
<item>
|
||||
<widget class="QPushButton" name="reloadButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
|
||||
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,19 @@
|
||||
import os
|
||||
from PyQt5 import QtWidgets, uic
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
|
||||
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
|
||||
|
||||
ui_class = os.path.dirname(os.path.abspath(__file__)) + "/streamlist_dialog.ui"
|
||||
|
||||
class StreamListDialog(QtWidgets.QWidget):
|
||||
streams_add_button: QtWidgets.QPushButton
|
||||
streams_reload_button: QtWidgets.QPushButton
|
||||
streams_remove_button: QtWidgets.QPushButton
|
||||
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(StreamListDialog, self).__init__(parent)
|
||||
uic.loadUi(ui_class, self) # Load the .ui file
|
||||
self.show()
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>StreamListDialog</class>
|
||||
<widget class="QWidget" name="StreamListDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>70</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_3">
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_2">
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,94 @@
|
||||
|
||||
from typing import Union
|
||||
from specklepy.api.wrapper import StreamWrapper
|
||||
from specklepy.api.models import Stream, Branch, Commit
|
||||
from specklepy.transports.server import ServerTransport
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.logging.exceptions import SpeckleException, GraphQLException
|
||||
|
||||
import inspect
|
||||
|
||||
import arcpy
|
||||
try:
|
||||
from speckle.ui.logger import logToUser
|
||||
except:
|
||||
from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser
|
||||
|
||||
def tryGetStream (sw: StreamWrapper) -> Union[Stream, None]:
|
||||
try:
|
||||
client = sw.get_client()
|
||||
stream = client.stream.get(id = sw.stream_id, branch_limit = 100, commit_limit = 100)
|
||||
if isinstance(stream, GraphQLException):
|
||||
raise SpeckleException(stream.errors[0]['message'])
|
||||
return stream
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def validateStream(streamWrapper: StreamWrapper) -> Union[Stream, None]:
|
||||
try:
|
||||
stream = tryGetStream(streamWrapper)
|
||||
if isinstance(stream, SpeckleException): return None
|
||||
|
||||
if stream.branches is None:
|
||||
logToUser("Stream has no branches", level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
return stream
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
|
||||
def validateBranch(stream: Stream, branchName: str, checkCommits: bool) -> Union[Branch, None]:
|
||||
try:
|
||||
branch = None
|
||||
if not stream.branches or not stream.branches.items:
|
||||
return None
|
||||
for b in stream.branches.items:
|
||||
if b.name == branchName:
|
||||
branch = b
|
||||
break
|
||||
if branch is None:
|
||||
logToUser("Failed to find a branch", level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
if checkCommits == True:
|
||||
if branch.commits is None:
|
||||
logToUser("Failed to find a branch", level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
if len(branch.commits.items)==0:
|
||||
logToUser("Branch contains no commits", level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
return branch
|
||||
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
|
||||
def validateCommit(branch: Branch, commitId: str) -> Union[Commit, None]:
|
||||
try:
|
||||
commit = None
|
||||
try: commitId = commitId.split(" | ")[0]
|
||||
except: logToUser("Commit ID is not valid", level=2, func = inspect.stack()[0][3])
|
||||
|
||||
for i in branch.commits.items:
|
||||
if i.id == commitId:
|
||||
commit = i
|
||||
break
|
||||
if commit is None:
|
||||
try:
|
||||
commit = branch.commits.items[0]
|
||||
logToUser("Failed to find a commit. Receiving Latest", level=2, func = inspect.stack()[0][3])
|
||||
except:
|
||||
logToUser("Failed to find a commit", level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
return commit
|
||||
except Exception as e:
|
||||
logToUser(str(e), level=2, func = inspect.stack()[0][3])
|
||||
|
||||
def validateTransport(client: SpeckleClient, streamId: str) -> Union[ServerTransport, None]:
|
||||
try:
|
||||
transport = ServerTransport(client=client, stream_id=streamId)
|
||||
return transport
|
||||
except Exception as e:
|
||||
logToUser("Make sure you have sufficient permissions: " + str(e), level=2, func = inspect.stack()[0][3])
|
||||
return None
|
||||
@@ -0,0 +1,104 @@
|
||||
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
|
||||
# remove SetUp
|
||||
# add different scenqrios for Streqm Wrapper including wrng ones
|
||||
# tree of all options for input or class outcome
|
||||
# use dict for types chech
|
||||
# issue with untestable class init
|
||||
# "mocking objects" for tests or "faking"
|
||||
# get functions ut of INIT
|
||||
|
||||
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_something(self):
|
||||
# Arrange
|
||||
toolbox_input: toolboxInputsClass = toolboxInputsClass()
|
||||
|
||||
# Act
|
||||
toolbox_input.setProjectStreams(StreamWrapper(self.test_stream))
|
||||
|
||||
# Assert
|
||||
os.path.exists(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.assertIsInstance()
|
||||
|
||||
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()
|
||||
|
||||