Compare commits
53 Commits
0.2.0
...
2.10.2-beta
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
@@ -34,6 +34,8 @@ jobs:
|
||||
$version = "$($ver).$($env:CIRCLE_BUILD_NUM)"
|
||||
echo $semver
|
||||
python patch_version.py $semver
|
||||
python setup.py sdist bdist_wheel
|
||||
Copy-Item -Path "dist\speckle_toolbox-$($ver)-py3-none-any.whl" -Destination "speckle_arcgis_installer"
|
||||
speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\arcgis.iss
|
||||
- when:
|
||||
condition: << parameters.installer >>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+39
-3
@@ -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:
|
||||
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():
|
||||
|
||||
@@ -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. Download present "speckle_arcgis_installer" folder
|
||||
2. Clone the default ArcGIS Pro conda environment 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. Change the path to your new environemnt Python.exe if necessary (variable "pythonPath" in "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 @@
|
||||
from speckle.speckle_arcgis import *
|
||||
@@ -8,50 +8,128 @@ 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 "C:\\Users\\username\\AppData\\Local\\ESRI\\conda\\envs\\arcgispro-2.9.4-py3-none-any.whl
|
||||
return
|
||||
|
||||
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}"])
|
||||
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:
|
||||
installToolbox(pythonPath)
|
||||
installDependencies(pythonPath, "specklepy", "2.9.0" )
|
||||
installDependencies(pythonPath, "panda3d", "1.10.11" )
|
||||
|
||||
Binary file not shown.
@@ -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)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
|
||||
|
||||
@@ -6,43 +6,34 @@ pythonPath = os.getenv('APPDATA').replace("\\Roaming","") + r"\Local\ESRI\conda\
|
||||
|
||||
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" )
|
||||
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 "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
|
||||
# to uninstall: cmd.exe "C:\\Users\\username\\AppData\\Local\\ESRI\\conda\\envs\\arcgispro-2.9.4-py3-none-any.whl
|
||||
return
|
||||
|
||||
def installDependencies(pythonExec: str):
|
||||
#print("Installing dependencies")
|
||||
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"])
|
||||
|
||||
pkgVersion = "2.7.4"
|
||||
pkgName = "specklepy"
|
||||
|
||||
# install package
|
||||
try:
|
||||
import specklepy # C:\Users\username\AppData\Roaming\Python\Python37\site-packages\specklepy\__init__.py
|
||||
import importlib
|
||||
importlib.import_module(pkgName)
|
||||
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")
|
||||
print(f"{pkgName} not installed")
|
||||
subprocess_call( [pythonExec, "-m", "pip", "install", f"{pkgName}=={pkgVersion}"])
|
||||
|
||||
# Check if specklpy needs updating
|
||||
# Check if package needs updating
|
||||
try:
|
||||
print(f"Attempting to update specklepy to {pkgVersion}")
|
||||
# pip.main(['install', "specklepy==2.7.4"])
|
||||
print(f"Attempting to update {pkgName} to {pkgVersion}")
|
||||
result = subprocess_call(
|
||||
[
|
||||
pythonExec,
|
||||
@@ -54,16 +45,17 @@ def installDependencies(pythonExec: str):
|
||||
]
|
||||
)
|
||||
if result == True:
|
||||
print("specklepy upgraded")
|
||||
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)
|
||||
|
||||
installToolbox(pythonPath)
|
||||
installDependencies(pythonPath, "specklepy", "2.9.0" )
|
||||
installDependencies(pythonPath, "panda3d", "1.10.11" )
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# 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\\manual_toolbox_install.py'), capture_output=True, text=True, shell=True, timeout=1000 )
|
||||
# 4. Restart ArcGIS Pro
|
||||
|
||||
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-2.9.4-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 @@
|
||||
from speckle.speckle_arcgis import *
|
||||
from 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>20221031</ModDate><ModTime>212303</ModTime><scaleRange><minScale>150000000</minScale><maxScale>5000</maxScale></scaleRange><ArcGISProfile>ItemDescription</ArcGISProfile></Esri><toolbox name="Speckle" alias="speckle_toolbox_"><arcToolboxHelpPath>c:\program files\arcgis\pro\Resources\Help\gp</arcToolboxHelpPath><toolsets/></toolbox><dataIdInfo><idCitation><resTitle>Speckle</resTitle></idCitation><idPurp>Speckle connector for ArcGIS</idPurp><searchKeys><keyword>speckle3d</keyword></searchKeys></dataIdInfo><distInfo><distributor><distorFormat><formatName>ArcToolbox Toolbox</formatName></distorFormat></distributor></distInfo><mdHrLv><ScopeCd value="005"></ScopeCd></mdHrLv><Binary><Thumbnail><Data EsriPropertyType="PictureX">/9j/4AAQSkZJRgABAQEAYABgAAD/4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdC
|
||||
IFhZWiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA9tYAAQAA
|
||||
AADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFj
|
||||
cHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0AAACBAAAABRyWFlaAAACGAAA
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
|
||||
from regex import F
|
||||
from specklepy.objects import Base
|
||||
from specklepy.objects.geometry import Line, Mesh, Point, Polyline, Curve, Arc, Circle, Polycurve
|
||||
from specklepy.objects.geometry import Line, Mesh, Point, Polyline, Curve, Arc, Circle, Polycurve, Ellipse
|
||||
|
||||
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.polygon import polygonToNative, polygonToSpeckle, multiPolygonToSpeckle
|
||||
from speckle.converter.geometry.polyline import arcToNative, ellipseToNative, circleToNative, curveToNative, lineToNative, polycurveToNative, polylineFromVerticesToSpeckle, polylineToNative, polylineToSpeckle
|
||||
from speckle.converter.geometry.point import pointToCoord, pointToNative, pointToSpeckle, multiPointToSpeckle
|
||||
|
||||
from speckle.converter.geometry.polyline import speckleArcCircleToPoints, specklePolycurveToPoints, multiPolylineToSpeckle
|
||||
from speckle.converter.geometry.mesh import meshToNative
|
||||
import numpy as np
|
||||
|
||||
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
|
||||
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
|
||||
@@ -26,9 +31,16 @@ def convertToSpeckle(feature, layer, geomType, featureType) -> Union[Base, Seque
|
||||
for pt in geom:
|
||||
return pointToSpeckle(pt, feature, layer)
|
||||
elif geomType == "Polyline":
|
||||
return polylineToSpeckle(geom, feature, layer, geomMultiType)
|
||||
#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":
|
||||
return polygonToSpeckle(geom, feature, layer, geomMultiType)
|
||||
if geom.partCount > 1: return multiPolygonToSpeckle(geom, feature, layer, geomMultiType)
|
||||
else: return polygonToSpeckle(geom, feature, layer, geomMultiType)
|
||||
elif geomType == "Multipoint":
|
||||
return multiPointToSpeckle(geom, feature, layer, geomMultiType)
|
||||
else:
|
||||
@@ -48,6 +60,7 @@ def convertToNative(base: Base, sr: arcpy.SpatialReference) -> Union[Any, None]:
|
||||
(Curve, curveToNative),
|
||||
(Arc, arcToNative),
|
||||
(Circle, circleToNative),
|
||||
(Ellipse, ellipseToNative),
|
||||
#(Mesh, meshToNative),
|
||||
(Polycurve, polycurveToNative),
|
||||
(Base, polygonToNative), # temporary solution for polygons (Speckle has no type Polygon yet)
|
||||
@@ -77,33 +90,75 @@ def multiPolylineToNative(items: List[Polyline], sr: arcpy.SpatialReference):
|
||||
print("_______Drawing Multipolylines____")
|
||||
#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 )
|
||||
return poly
|
||||
|
||||
def multiPolygonToNative(items: List[Base], sr: arcpy.SpatialReference): #TODO fix multi features
|
||||
|
||||
print("_______Drawing Multipolygons____")
|
||||
#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()]
|
||||
#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])
|
||||
list_of_arrs = []
|
||||
geomPart = []
|
||||
try:
|
||||
for void in item["voids"]:
|
||||
#print(void)
|
||||
pts = [pointToCoord(pt) for pt in void.as_points()]
|
||||
#print(pts)
|
||||
#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])
|
||||
list_of_arrs.append(arcpy.Array(inner_arr))
|
||||
geomPart.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)
|
||||
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)
|
||||
|
||||
return polygon
|
||||
|
||||
def convertToNativeMulti(items: List[Base], sr: arcpy.SpatialReference):
|
||||
@@ -113,5 +168,8 @@ def convertToNativeMulti(items: List[Base], sr: arcpy.SpatialReference):
|
||||
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)
|
||||
elif isinstance(first, Base):
|
||||
try:
|
||||
if first["boundary"] is not None and first["voids"] is not None:
|
||||
return multiPolygonToNative(items, sr)
|
||||
except: return None
|
||||
|
||||
@@ -1,10 +1,58 @@
|
||||
from typing import List
|
||||
import arcpy
|
||||
|
||||
from specklepy.objects.geometry import Mesh
|
||||
|
||||
import shapefile
|
||||
from shapefile import TRIANGLE_STRIP, TRIANGLE_FAN
|
||||
from speckle.converter.layers.utils import get_scale_factor
|
||||
|
||||
def meshToNative(mesh: Mesh):
|
||||
"""Converts a Speckle Mesh to QgsGeometry. Currently UNSUPPORTED"""
|
||||
return None
|
||||
def meshToNative(meshes: List[Mesh], path: str):
|
||||
"""Converts a Speckle Mesh to MultiPatch"""
|
||||
print("06___________________Mesh to Native")
|
||||
#print(meshes)
|
||||
#print(mesh.units)
|
||||
w = shapefile.Writer(path)
|
||||
w.field('speckleTyp', 'C')
|
||||
|
||||
shapes = []
|
||||
for mesh_full in meshes:
|
||||
#print(mesh_full)
|
||||
#print(mesh_full.get_dynamic_member_names())
|
||||
mesh = mesh_full.displayMesh
|
||||
#print(mesh)
|
||||
w = fill_mesh_parts(w, mesh)
|
||||
w.close()
|
||||
return path
|
||||
|
||||
def fill_mesh_parts(w: shapefile.Writer, mesh: Mesh):
|
||||
scale = get_scale_factor(mesh.units)
|
||||
|
||||
parts_list = []
|
||||
types_list = []
|
||||
count = 0 # sequence of vertex (not of flat coord list)
|
||||
try:
|
||||
#print(len(mesh.faces))
|
||||
if len(mesh.faces) % 4 == 0 and mesh.faces[0] == 0:
|
||||
for f in mesh.faces:
|
||||
try:
|
||||
if mesh.faces[count] == 0 or mesh.faces[count] == 3: # only handle triangles
|
||||
f1 = [ scale*mesh.vertices[mesh.faces[count+1]*3], scale*mesh.vertices[mesh.faces[count+1]*3+1], scale*mesh.vertices[mesh.faces[count+1]*3+2] ]
|
||||
f2 = [ scale*mesh.vertices[mesh.faces[(count+2)]*3], scale*mesh.vertices[mesh.faces[(count+2)]*3+1], scale*mesh.vertices[mesh.faces[(count+2)]*3+2] ]
|
||||
f3 = [ scale*mesh.vertices[mesh.faces[(count+3)]*3], scale*mesh.vertices[mesh.faces[(count+3)]*3+1], scale*mesh.vertices[mesh.faces[(count+3)]*3+2] ]
|
||||
parts_list.append([ f1, f2, f3 ])
|
||||
types_list.append(TRIANGLE_FAN)
|
||||
count += 4
|
||||
else:
|
||||
count += mesh.faces[count+1]
|
||||
except: break
|
||||
w.multipatch(parts_list, partTypes=types_list ) # one type for each part
|
||||
w.record('displayMesh')
|
||||
else: print("not triangulated mesh")
|
||||
|
||||
except Exception as e: pass #; print(e)
|
||||
return w
|
||||
|
||||
def rasterToMesh(vertices, faces, colors):
|
||||
mesh = Mesh.create(vertices, faces, colors)
|
||||
mesh.units = "m"
|
||||
|
||||
@@ -38,33 +38,39 @@ def pointToSpeckle(pt, feature, layer):
|
||||
specklePoint.y = y
|
||||
specklePoint.z = z
|
||||
'''
|
||||
col = featureColorfromNativeRenderer(feature, layer)
|
||||
specklePoint['displayStyle'] = {}
|
||||
specklePoint['displayStyle']['color'] = col
|
||||
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
|
||||
|
||||
def pointToNative(pt: Point, sr: arcpy.SpatialReference) -> arcpy.PointGeometry:
|
||||
"""Converts a Speckle Point to QgsPoint"""
|
||||
print("___pointToNative__")
|
||||
#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
|
||||
|
||||
def pointToCoord(pt: Point) -> List[float]:
|
||||
def pointToCoord(point: Point) -> List[float]:
|
||||
"""Converts a Speckle Point to QgsPoint"""
|
||||
pt = scalePointToNative(pt, pt.units)
|
||||
pt = scalePointToNative(point, point.units)
|
||||
coords = [pt.x, pt.y, pt.z]
|
||||
#print(coords)
|
||||
return coords
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
def addZtoPoint(coords: List):
|
||||
if len(coords) == 2: coords.append(0)
|
||||
return coords
|
||||
@@ -1,79 +1,93 @@
|
||||
from typing import Sequence
|
||||
import arcpy
|
||||
import json
|
||||
import json
|
||||
from arcpy.arcobjects.arcobjects import SpatialReference
|
||||
|
||||
from specklepy.objects import Base
|
||||
from specklepy.objects.geometry import Point
|
||||
from specklepy.objects.geometry import Point, Arc, Circle, Polycurve, Polyline, Line
|
||||
from speckle.converter.geometry.mesh import rasterToMesh
|
||||
from speckle.converter.geometry.point import pointToCoord
|
||||
from speckle.converter.geometry.polyline import polylineFromVerticesToSpeckle, circleToSpeckle
|
||||
from speckle.converter.geometry.point import pointToCoord, pointToNative
|
||||
from speckle.converter.geometry.polyline import (polylineFromVerticesToSpeckle,
|
||||
circleToSpeckle,
|
||||
speckleArcCircleToPoints,
|
||||
curveToSpeckle,
|
||||
specklePolycurveToPoints
|
||||
)
|
||||
|
||||
import math
|
||||
from panda3d.core import Triangulator
|
||||
|
||||
|
||||
def multiPolygonToSpeckle(geom, feature, layer, multiType: bool):
|
||||
|
||||
print("___MultiPolygon to Speckle____")
|
||||
polygon = []
|
||||
#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(feature): # [[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, feature, layer, poly.isMultipart))
|
||||
|
||||
return polygon
|
||||
|
||||
|
||||
def polygonToSpeckle(geom, feature, layer, multiType: bool):
|
||||
"""Converts a Polygon to Speckle"""
|
||||
#try:
|
||||
print("___Polygon to Speckle____")
|
||||
print(geom)
|
||||
polygon = Base(units = "m")
|
||||
pointList = []
|
||||
voidPointList = []
|
||||
voids = []
|
||||
boundary = None
|
||||
data = arcpy.Describe(layer.dataSource)
|
||||
sr = data.spatialReference
|
||||
|
||||
print(multiType)
|
||||
partsBoundaries = []
|
||||
partsVoids = []
|
||||
|
||||
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)
|
||||
|
||||
#else: # no curves
|
||||
if multiType is False: # Multipolygon
|
||||
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", feature, layer)
|
||||
else:
|
||||
print("no curves")
|
||||
for p in geom:
|
||||
for pt in p:
|
||||
if pt != None: pointList.append(pt)
|
||||
boundary = polylineFromVerticesToSpeckle(pointList, True, feature, layer)
|
||||
partsBoundaries.append(boundary)
|
||||
partsVoids.append([])
|
||||
|
||||
#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):
|
||||
print(p)
|
||||
for pt in p:
|
||||
#print(pt) # 284394.58100903 5710688.11602606 NaN NaN
|
||||
if pt == None and boundary == None: # first break
|
||||
@@ -90,24 +104,39 @@ def polygonToSpeckle(geom, feature, layer, multiType: bool):
|
||||
void = polylineFromVerticesToSpeckle(pointList, True, feature, layer)
|
||||
voids.append(void)
|
||||
|
||||
#partsBoundaries.append(boundary)
|
||||
#partsVoids.append(local_voids)
|
||||
|
||||
if boundary is None: return None
|
||||
polygon.boundary = boundary
|
||||
polygon.voids = voids
|
||||
polygon.displayValue = [ boundary ] + voids
|
||||
#print(boundary)
|
||||
|
||||
############# mesh
|
||||
vertices = []
|
||||
polyBorder = []
|
||||
total_vertices = 0
|
||||
polyBorder = boundary.as_points()
|
||||
|
||||
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:
|
||||
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:
|
||||
x = pt.x
|
||||
y = pt.y
|
||||
z = 0 if math.isnan(pt.z) else pt.z
|
||||
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)
|
||||
@@ -131,15 +160,34 @@ def polygonToSpeckle(geom, feature, layer, multiType: bool):
|
||||
|
||||
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 = voids[i].as_points()
|
||||
|
||||
|
||||
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])
|
||||
@@ -168,21 +216,44 @@ 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()]
|
||||
#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])
|
||||
list_of_arrs = []
|
||||
geomPart = []
|
||||
try:
|
||||
for void in poly["voids"]:
|
||||
#print(void)
|
||||
pts = [pointToCoord(pt) for pt in void.as_points()]
|
||||
#print(pts)
|
||||
#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])
|
||||
list_of_arrs.append(arcpy.Array(inner_arr))
|
||||
geomPart.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)
|
||||
geomPart.insert(0, outer_arr)
|
||||
geomPartArray = arcpy.Array(geomPart)
|
||||
polygon = arcpy.Polygon(geomPartArray, sr, has_z=True)
|
||||
|
||||
return polygon
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
|
||||
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.geometry.point import pointToCoord, pointToSpeckle, addZtoPoint
|
||||
from speckle.converter.layers.utils import get_scale_factor
|
||||
|
||||
def circleToSpeckle(center, point, layer):
|
||||
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)
|
||||
@@ -21,32 +24,60 @@ def circleToSpeckle(center, point, layer):
|
||||
|
||||
args = [0] + [rad] + domain + plane + [units]
|
||||
#print(args)
|
||||
c = Circle().from_list(args)
|
||||
c = Circle.from_list(args)
|
||||
c.plane.origin.units = "m"
|
||||
c.units = "m"
|
||||
#print(c)
|
||||
return c
|
||||
|
||||
def multiPolylineToSpeckle(geom, feature, layer, multiType: bool):
|
||||
|
||||
print("___MultiPolyline to Speckle____")
|
||||
polyline = []
|
||||
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))
|
||||
|
||||
return polyline
|
||||
|
||||
def polylineToSpeckle(geom, feature, layer, multiType: bool):
|
||||
print("___Polyline to Speckle____")
|
||||
polyline = None
|
||||
pointList = []
|
||||
#print(geom.hasCurves)
|
||||
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)
|
||||
|
||||
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)
|
||||
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____")
|
||||
|
||||
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)
|
||||
@@ -58,17 +89,264 @@ def polylineFromVerticesToSpeckle(vertices, closed, feature, layer):
|
||||
polyline.closed = closed
|
||||
polyline.units = specklePts[0].units
|
||||
for i, point in enumerate(specklePts):
|
||||
if closed and i == len(specklePts) - 1:
|
||||
continue
|
||||
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])
|
||||
|
||||
return polyline
|
||||
|
||||
def arc3ptToSpeckle(p0: List, p1: List, p2: List, feature, layer) -> Arc:
|
||||
print("____arc 3pt to Speckle___")
|
||||
p0 = addZtoPoint(p0)
|
||||
p1 = addZtoPoint(p1)
|
||||
p2 = addZtoPoint(p2)
|
||||
arc = Arc()
|
||||
arc.startPoint = pointToSpeckle(arcpy.Point(*p0), feature, layer)
|
||||
arc.midPoint = pointToSpeckle(arcpy.Point(*p1), feature, layer)
|
||||
arc.endPoint = pointToSpeckle(arcpy.Point(*p2), feature, layer)
|
||||
center, radius = getArcCenter(Point.from_list(p0), Point.from_list(p1), Point.from_list(p2))
|
||||
arc.plane = Plane() #.from_list(Point(), Vector(Point(0, 0, 1)), Vector(Point(0,1,0)), Vector(Point(-1,0,0)))
|
||||
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
|
||||
|
||||
return arc
|
||||
|
||||
def curveBezierToSpeckle(segmStartCoord, segmEndCoord, knots, feature, layer):
|
||||
print("____bezier curve to Speckle____")
|
||||
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
|
||||
|
||||
|
||||
def curveToSpeckle(geom, geomType, feature, layer) -> Union[Circle, Arc, Polyline, Polycurve]:
|
||||
print("____curve to Speckle____")
|
||||
print(geomType)
|
||||
# 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]]]
|
||||
|
||||
boundary = Polycurve(units = "m")
|
||||
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
|
||||
arcpy.AddMessage("SpeckleWarning: ellipse geometry not supported yet")
|
||||
segments = []
|
||||
break
|
||||
else: # elliptical curve
|
||||
arcpy.AddMessage("SpeckleWarning: ellipse geometry not supported yet")
|
||||
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, feature, layer)
|
||||
segments.append(segmentLocal)
|
||||
print("segmentLocal:")
|
||||
print(segmentLocal)
|
||||
print(segmStartCoord)
|
||||
print(segmEndCoord)
|
||||
lastPt = segmEndCoord
|
||||
|
||||
if key2 == "b": # bezier curve (endPt, controlPts)
|
||||
arcpy.AddMessage("SpeckleWarning: bezier curve geometry not supported yet")
|
||||
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)
|
||||
return boundary
|
||||
|
||||
|
||||
|
||||
def lineFrom2pt(pt1: List[float], pt2: List[float]):
|
||||
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 = Line(units = "m" )#.from_list([*pt1, *pt2, *domain])
|
||||
line.start = Point.from_list(pt1)
|
||||
line.end = Point.from_list(pt2)
|
||||
line.start.units = line.end.units = "m"
|
||||
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 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]) )
|
||||
|
||||
@@ -79,26 +357,29 @@ def polylineToNative(poly: Polyline, sr: arcpy.SpatialReference) -> arcpy.Polyli
|
||||
|
||||
|
||||
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
|
||||
|
||||
def curveToNative(poly: Curve, sr: arcpy.SpatialReference) -> arcpy.Polyline:
|
||||
"""Converts a Speckle Curve to QgsLineString"""
|
||||
"""Converts a Speckle Curve to Native"""
|
||||
display = poly.displayValue
|
||||
curve = polylineToNative(display, sr)
|
||||
return curve
|
||||
|
||||
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))
|
||||
"""Converts a Speckle Arc to Native"""
|
||||
arc = arcToNativePolyline(poly, sr) #QgsCircularString(pointToNative(poly.startPoint), pointToNative(poly.midPoint), pointToNative(poly.endPoint))
|
||||
return arc
|
||||
|
||||
def ellipseToNative():
|
||||
return
|
||||
|
||||
def circleToNative(poly: Circle, sr: arcpy.SpatialReference) -> arcpy.Polyline:
|
||||
"""Converts a Speckle Circle to QgsLineString"""
|
||||
print("___Convert Circle from Native___")
|
||||
print("___Convert Circle to Native___")
|
||||
points = []
|
||||
angle1 = math.pi/2
|
||||
|
||||
@@ -114,7 +395,8 @@ def circleToNative(poly: Circle, sr: arcpy.SpatialReference) -> arcpy.Polyline:
|
||||
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 = 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])
|
||||
@@ -125,15 +407,15 @@ def polycurveToNative(poly: Polycurve, sr: arcpy.SpatialReference) -> arcpy.Poly
|
||||
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")
|
||||
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr , has_z=True)
|
||||
@@ -141,6 +423,7 @@ def polycurveToNative(poly: Polycurve, sr: arcpy.SpatialReference) -> arcpy.Poly
|
||||
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,8 +432,7 @@ 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")
|
||||
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True )
|
||||
@@ -161,45 +443,158 @@ def polycurveToNative(poly: Polycurve, sr: arcpy.SpatialReference) -> arcpy.Poly
|
||||
curve = arcpy.Polyline( arcpy.Array([arcpy.Point(*coords) for coords in points]), sr, has_z=True )
|
||||
return curve
|
||||
|
||||
def arcToNativePoints(poly: Arc, sr: arcpy.SpatialReference):
|
||||
print("__Arc to native__")
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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))
|
||||
def arcToNativePolyline(poly: Union[Arc, Circle], sr: arcpy.SpatialReference):
|
||||
print("__Arc/Circle to native polyline__")
|
||||
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 )
|
||||
return curve
|
||||
|
||||
|
||||
|
||||
def specklePolycurveToPoints(poly: Polycurve) -> List[Point]:
|
||||
print("_____Speckle Polycurve to points____")
|
||||
points = []
|
||||
for segm in poly.segments:
|
||||
print(segm)
|
||||
pts = []
|
||||
if isinstance(segm, Arc) or isinstance(segm, Circle): # or isinstance(segm, Curve):
|
||||
print("Arc or Curve")
|
||||
pts: List[Point] = speckleArcCircleToPoints(segm)
|
||||
elif isinstance(segm, Line):
|
||||
print("Line")
|
||||
pts: List[Point] = [segm.start, segm.end]
|
||||
elif isinstance(segm, Polyline):
|
||||
print("Polyline")
|
||||
pts: List[Point] = segm.as_points()
|
||||
|
||||
points.extend(pts)
|
||||
return points
|
||||
|
||||
def speckleArcCircleToPoints(poly: Union[Arc, Circle]) -> List[Point]:
|
||||
print("__Arc or Circle to Points___")
|
||||
points = []
|
||||
#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)
|
||||
return points
|
||||
|
||||
|
||||
def getArcRadianAngle(arc: Arc) -> List[float]:
|
||||
|
||||
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
|
||||
|
||||
def getArcAngles(poly: Arc) -> Tuple[float]:
|
||||
|
||||
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
|
||||
|
||||
def getArcNormal(poly: Arc, midPt: Point):
|
||||
print("____getArcNormal___")
|
||||
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
|
||||
|
||||
|
||||
def getArcCenter(p1: Point, p2: Point, p3: Point) -> Tuple[List, float]:
|
||||
#print(p1)
|
||||
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
|
||||
|
||||
@@ -4,6 +4,7 @@ from specklepy.objects.base import Base
|
||||
from speckle.converter.layers.CRS import CRS
|
||||
|
||||
|
||||
|
||||
class Layer(Base, chunkable={"features": 100}):
|
||||
"""A GIS Layer"""
|
||||
|
||||
@@ -27,14 +28,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",
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
Contains all Layer related classes and methods.
|
||||
"""
|
||||
import os
|
||||
from typing import Any, List, Union
|
||||
from typing import Any, List, Tuple, Union
|
||||
|
||||
from regex import D
|
||||
from speckle.converter.layers.CRS import CRS
|
||||
from speckle.converter.layers.Layer import Layer, RasterLayer
|
||||
from speckle.converter.layers.feature import featureToNative, featureToSpeckle, cadFeatureToNative
|
||||
from speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
|
||||
from speckle.converter.layers.feature import featureToNative, featureToSpeckle, cadFeatureToNative, bimFeatureToNative, rasterFeatureToSpeckle
|
||||
from specklepy.objects import Base
|
||||
from specklepy.objects.geometry import Mesh
|
||||
from speckle.converter.geometry.mesh import rasterToMesh, meshToNative
|
||||
|
||||
import arcgisscripting
|
||||
import pandas as pd
|
||||
@@ -17,10 +19,13 @@ 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
|
||||
from speckle.converter.layers.utils import getLayerAttributes, newLayerGroupAndName, validate_path
|
||||
import numpy as np
|
||||
|
||||
from speckle.converter.layers.utils import findTransformation
|
||||
|
||||
|
||||
def convertSelectedLayers(all_layers: List[arcpy._mp.Layer], selected_layers: List[str], project: arcpy.mp.ArcGISProject) -> List[Layer]:
|
||||
def convertSelectedLayers(all_layers: List[arcLayer], selected_layers: List[str], project: ArcGISProject) -> List[Union[VectorLayer,Layer]]:
|
||||
"""Converts the current selected layers to Speckle"""
|
||||
print("________Convert Layers_________")
|
||||
result = []
|
||||
@@ -32,82 +37,123 @@ def convertSelectedLayers(all_layers: List[arcpy._mp.Layer], selected_layers: Li
|
||||
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)
|
||||
|
||||
#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)
|
||||
print(result)
|
||||
|
||||
return result
|
||||
|
||||
def layerToSpeckle(layer: arcLayer, project: ArcGISProject) -> Layer: #now the input is QgsVectorLayer instead of qgis._core.QgsLayerTreeLayer
|
||||
def layerToSpeckle(layer: arcLayer, project: ArcGISProject) -> Union[VectorLayer, RasterLayer]: #now the input is QgsVectorLayer instead of qgis._core.QgsLayerTreeLayer
|
||||
"""Converts a given QGIS Layer to Speckle"""
|
||||
print("________Convert Feature Layer_________")
|
||||
|
||||
speckleLayer = None
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
#renderer = selectedLayer.renderer()
|
||||
#layerRenderer = rendererToSpeckle(renderer)
|
||||
|
||||
if layer.isFeatureLayer:
|
||||
print("VECTOR LAYER HERE")
|
||||
|
||||
speckleLayer = VectorLayer(units = "m")
|
||||
speckleLayer.type="VectorLayer"
|
||||
speckleLayer.name = layerName
|
||||
speckleLayer.crs = speckleReprojectedCrs
|
||||
#speckleLayer.datum = datum
|
||||
|
||||
|
||||
try: # https://pro.arcgis.com/en/pro-app/2.8/arcpy/get-started/the-spatial-reference-object.htm
|
||||
|
||||
#print(data.datasetType) # FeatureClass
|
||||
if data.datasetType == "FeatureClass": #FeatureClass, ?Table Properties, ?Datasets
|
||||
|
||||
print("__ finish iterating features")
|
||||
speckleLayer.features=layerObjs
|
||||
speckleLayer.geomType = data.shapeType
|
||||
# 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):
|
||||
|
||||
except OSError as e:
|
||||
arcpy.AddWarning(str(e))
|
||||
return
|
||||
print("____error Feature # " + str(i+1)) # + " / " + str(sum(1 for _ in enumerate(rows_shapes))))
|
||||
if features[0] is None: continue
|
||||
feat = features[0]
|
||||
#print(feat) # <geoprocessing describe geometry object object at 0x000002A75D6A4BD0>
|
||||
#print(feat.hasCurves)
|
||||
#print(feat.partCount)
|
||||
|
||||
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 curves detected, createa new feature class, turn to segments and get the same feature but in straigt lines
|
||||
#print(feat.hasCurves)
|
||||
if feat.hasCurves:
|
||||
#f_class_modified = curvedFeatureClassToSegments(layer)
|
||||
#rows_shapes_modified = arcpy.da.SearchCursor(f_class_modified, "Shape@")
|
||||
#row_shapes_list_modified = [x for k, x in enumerate(rows_shapes_modified)]
|
||||
|
||||
feat = feat.densify("ANGLE", 1000, 0.12)
|
||||
#print(feat)
|
||||
|
||||
|
||||
b = featureToSpeckle(fieldnames, row_attr, feat, projectCRS, project, layer)
|
||||
if b is not None: layerObjs.append(b)
|
||||
|
||||
print("____End of Feature # " + str(i+1))
|
||||
|
||||
print("__ finish iterating features")
|
||||
speckleLayer.features=layerObjs
|
||||
speckleLayer.geomType = data.shapeType
|
||||
|
||||
if len(speckleLayer.features) == 0: return None
|
||||
|
||||
#layerBase.renderer = layerRenderer
|
||||
#layerBase.applicationId = selectedLayer.id()
|
||||
|
||||
except OSError as e:
|
||||
arcpy.AddWarning(str(e))
|
||||
return
|
||||
|
||||
elif layer.isRasterLayer:
|
||||
print("RASTER IN DA HOUSE")
|
||||
print(layer.name) # London_square.tif
|
||||
print(arcpy.Describe(layer.dataSource)) # <geoprocessing describe data object object at 0x000002507C7F3BB0>
|
||||
print(arcpy.Describe(layer.dataSource).datasetType) # RasterDataset
|
||||
b = rasterFeatureToSpeckle(layer, projectCRS, project)
|
||||
if b is not None: layerObjs.append(b)
|
||||
|
||||
speckleLayer = RasterLayer(units = "m", type="RasterLayer")
|
||||
speckleLayer.name = layerName
|
||||
speckleLayer.crs = speckleReprojectedCrs
|
||||
speckleLayer.rasterCrs = layerCRS
|
||||
speckleLayer.type="RasterLayer"
|
||||
#speckleLayer.geomType="Raster"
|
||||
speckleLayer.features = layerObjs
|
||||
|
||||
#speckleLayer.renderer = layerRenderer
|
||||
#speckleLayer.applicationId = selectedLayer.id()
|
||||
|
||||
return speckleLayer
|
||||
|
||||
def layerToNative(layer: Union[Layer, RasterLayer], streamBranch: str, project: ArcGISProject):
|
||||
def layerToNative(layer: Union[Layer, VectorLayer, RasterLayer], streamBranch: str, project: ArcGISProject):
|
||||
|
||||
if layer.type is None:
|
||||
# Handle this case
|
||||
@@ -118,8 +164,186 @@ def layerToNative(layer: Union[Layer, RasterLayer], streamBranch: str, project:
|
||||
return rasterLayerToNative(layer, streamBranch, project)
|
||||
return None
|
||||
|
||||
def cadLayerToNative(layerContentList:Base, layerName: str, streamBranch: str, project: ArcGISProject) :
|
||||
def bimLayerToNative(layerContentList: List[Base], layerName: str, streamBranch: str, project: ArcGISProject) :
|
||||
print("01______BIM layer to native")
|
||||
print(layerName)
|
||||
geom_meshes = []
|
||||
layer_meshes = None
|
||||
#filter speckle objects by type within each layer, create sub-layer for each type (points, lines, polygons, mesh?)
|
||||
for geom in layerContentList:
|
||||
#print(geom)
|
||||
if geom.displayMesh and isinstance(geom.displayMesh, Mesh):
|
||||
geom_meshes.append(geom)
|
||||
|
||||
if len(geom_meshes)>0: layer_meshes = bimVectorLayerToNative(geom_meshes, layerName, "Mesh", streamBranch, project)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def bimVectorLayerToNative(geomList, layerName: str, geomType: str, streamBranch: str, project: ArcGISProject):
|
||||
# no support for mltipatches, maybe in 3.1: https://community.esri.com/t5/arcgis-pro-ideas/better-support-for-multipatches-in-arcpy/idi-p/953614/page/2#comments
|
||||
print("02_________BIM vector layer to native_____")
|
||||
#get Project CRS, use it by default for the new received layer
|
||||
vl = None
|
||||
layerName = layerName.replace("[","_").replace("]","_").replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
|
||||
layerName = layerName + "_" + geomType
|
||||
#if not "__Structural_Foundations_Mesh" in layerName: return None
|
||||
|
||||
sr = arcpy.SpatialReference(text = project.activeMap.spatialReference.exportToString())
|
||||
active_map = project.activeMap
|
||||
path = project.filePath.replace("aprx","gdb") #
|
||||
path_bim = "\\".join(project.filePath.split("\\")[:-1]) + "\\BIM_layers_speckle\\" + streamBranch+ "\\" + layerName + "\\" #arcpy.env.workspace + "\\" #
|
||||
print(path_bim)
|
||||
if not os.path.exists(path_bim): os.makedirs(path_bim)
|
||||
print(path)
|
||||
|
||||
if sr.type == "Geographic":
|
||||
arcpy.AddMessage(f"Project CRS is set to Geographic type, and objects in linear units might not be received correctly")
|
||||
|
||||
#CREATE A GROUP "received blabla" with sublayers
|
||||
layerGroup = None
|
||||
newGroupName = f'{streamBranch}'
|
||||
#print(newGroupName)
|
||||
for l in active_map.listLayers():
|
||||
if l.longName == newGroupName: layerGroup = l; break
|
||||
|
||||
#find ID of the layer with a matching name in the "latest" group
|
||||
newName = f'{streamBranch.split("_")[len(streamBranch.split("_"))-1]}_{layerName}'
|
||||
print(newName)
|
||||
|
||||
all_layer_names = []
|
||||
layerExists = 0
|
||||
for l in project.activeMap.listLayers():
|
||||
if l.longName.startswith(newGroupName + "\\"):
|
||||
all_layer_names.append(l.longName)
|
||||
#print(all_layer_names)
|
||||
|
||||
longName = streamBranch + "\\" + newName
|
||||
if longName in all_layer_names:
|
||||
for index, letter in enumerate('234567890abcdefghijklmnopqrstuvwxyz'):
|
||||
if (longName + "_" + letter) not in all_layer_names: newName += "_"+letter; layerExists +=1; break
|
||||
|
||||
# particularly if the layer comes from ArcGIS
|
||||
if "mesh" in geomType.lower(): geomType = "Multipatch"
|
||||
|
||||
#print("Create feature class (cad): ")
|
||||
# should be created inside the workspace to be a proper Feature class (not .shp) with Nullable Fields
|
||||
class_name = ("f_class_" + newName)
|
||||
#f_class = CreateFeatureclass(path, class_name, geomType, has_z="ENABLED", spatial_reference = sr)
|
||||
|
||||
|
||||
shp = meshToNative(geomList, path_bim + newName)
|
||||
print("____ meshes saved___")
|
||||
print(shp)
|
||||
#print(path)
|
||||
#print(class_name)
|
||||
validated_class_path = validate_path(class_name)
|
||||
print(validated_class_path)
|
||||
validated_class_name = validated_class_path.split("\\")[len(validated_class_path.split("\\"))-1]
|
||||
print(validated_class_name)
|
||||
f_class = arcpy.conversion.FeatureClassToFeatureClass(shp, path, validated_class_name)
|
||||
# , spatial_reference = sr
|
||||
#arcpy.management.Project(in_dataset, f_class, sr, in_coor_system=sr)
|
||||
|
||||
print(f_class)
|
||||
#print(geomList)
|
||||
|
||||
# get and set Layer attribute fields
|
||||
# example: https://resource.esriuk.com/blog/an-introductory-slice-of-arcpy-in-arcgis-pro/
|
||||
newFields = getLayerAttributes(geomList)
|
||||
|
||||
fields_to_ignore = ["arcgisgeomfromspeckle", "shape", "objectid", "displayMesh"]
|
||||
matrix = []
|
||||
all_keys = []
|
||||
all_key_types = []
|
||||
max_len = 52
|
||||
|
||||
print("___ after layer attributes: ___________")
|
||||
print(newFields.items())
|
||||
#try:
|
||||
for key, value in newFields.items():
|
||||
existingFields = [fl.name for fl in arcpy.ListFields(validated_class_name)]
|
||||
#print(existingFields)
|
||||
if key not in existingFields and key.lower() not in fields_to_ignore: # exclude geometry and default existing fields
|
||||
#print(key)
|
||||
# signs that should not be used as field names and table names: https://support.esri.com/en/technical-article/000005588
|
||||
key = key.replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
|
||||
if key[0] in ['0','1','2','3','4','5','6','7','8','9']: key = "_"+key
|
||||
if len(key)>max_len: key = key[:max_len]
|
||||
#print(all_keys)
|
||||
if key in all_keys:
|
||||
for index, letter in enumerate('1234567890abcdefghijklmnopqrstuvwxyz'):
|
||||
if len(key)<max_len and (key+letter) not in all_keys: key+=letter; break
|
||||
if len(key) == max_len and (key[:9] + letter) not in all_keys: key=key[:9] + letter; break
|
||||
if key not in all_keys:
|
||||
all_keys.append(key)
|
||||
all_key_types.append(value)
|
||||
#print(all_keys)
|
||||
matrix.append([key, value, key, 255])
|
||||
print(all_keys)
|
||||
print(len(all_keys))
|
||||
if len(matrix)>0: AddFields(str(f_class), matrix)
|
||||
|
||||
fets = []
|
||||
print("_________BIM FeatureS To Native___________")
|
||||
for f in geomList[:]:
|
||||
new_feat = bimFeatureToNative(f, newFields, sr, path_bim)
|
||||
if new_feat != "" and new_feat != None:
|
||||
fets.append(new_feat)
|
||||
print(len(fets))
|
||||
|
||||
|
||||
if len(fets) == 0: return None
|
||||
count = 0
|
||||
rowValues = []
|
||||
for i, feat in enumerate(fets):
|
||||
|
||||
row = []
|
||||
heads = []
|
||||
for key in all_keys:
|
||||
try:
|
||||
row.append(feat[key])
|
||||
heads.append(key)
|
||||
except Exception as e:
|
||||
row.append(None)
|
||||
heads.append(key)
|
||||
|
||||
rowValues.append(row)
|
||||
count += 1
|
||||
|
||||
with arcpy.da.UpdateCursor(f_class, heads) as cur:
|
||||
# For each row, evaluate the WELL_YIELD value (index position
|
||||
# of 0), and update WELL_CLASS (index position of 1)
|
||||
shp_num = 0
|
||||
try:
|
||||
for rowShape in cur:
|
||||
for i,r in enumerate(rowShape):
|
||||
rowShape[i] = rowValues[shp_num][i]
|
||||
if isinstance(rowValues[shp_num][i], str): rowShape[i] = rowValues[shp_num][i][:255]
|
||||
|
||||
cur.updateRow(rowShape)
|
||||
shp_num += 1
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(i)
|
||||
print(shp_num)
|
||||
print(len(rowValues))
|
||||
print(rowValues[i-1])
|
||||
print(len(rowValues[i-1]))
|
||||
del cur
|
||||
|
||||
print("create layer:")
|
||||
vl = MakeFeatureLayer(str(f_class), newName).getOutput(0)
|
||||
|
||||
active_map.addLayerToGroup(layerGroup, vl)
|
||||
print("created2")
|
||||
#os.remove(path_bim)
|
||||
|
||||
return True #last one
|
||||
|
||||
def cadLayerToNative(layerContentList: List[Base], layerName: str, streamBranch: str, project: ArcGISProject) :
|
||||
print("01______Cad vector layer to native")
|
||||
print(layerName)
|
||||
geom_points = []
|
||||
geom_polylines = []
|
||||
geom_polygones = []
|
||||
@@ -129,7 +353,7 @@ def cadLayerToNative(layerContentList:Base, layerName: str, streamBranch: str, p
|
||||
#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":
|
||||
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.Ellipse" 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)
|
||||
@@ -138,13 +362,13 @@ def cadLayerToNative(layerContentList:Base, layerName: str, streamBranch: str, p
|
||||
return [layer_points, layer_polylines]
|
||||
|
||||
def cadVectorLayerToNative(geomList, layerName: str, geomType: str, streamBranch: str, project: ArcGISProject):
|
||||
print("_________CAD vector layer to native_____")
|
||||
print("02_________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)
|
||||
sr = arcpy.SpatialReference(text = project.activeMap.spatialReference.exportToString())
|
||||
active_map = project.activeMap
|
||||
path = project.filePath.replace("aprx","gdb") #"\\".join(project.filePath.split("\\")[:-1]) + "\\speckle_layers\\" #arcpy.env.workspace + "\\" #
|
||||
|
||||
@@ -184,11 +408,11 @@ def cadVectorLayerToNative(geomList, layerName: str, geomType: str, streamBranch
|
||||
#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)___________________________________")
|
||||
#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(f_class)
|
||||
#print(geomList)
|
||||
|
||||
# get and set Layer attribute fields
|
||||
@@ -226,8 +450,9 @@ def cadVectorLayerToNative(geomList, layerName: str, geomType: str, streamBranch
|
||||
if new_feat != "" and new_feat != None:
|
||||
fets.append(new_feat)
|
||||
#print("features created")
|
||||
#print(fets)
|
||||
|
||||
print(len(fets))
|
||||
|
||||
if len(fets) == 0: return None
|
||||
count = 0
|
||||
rowValues = []
|
||||
for feat in fets:
|
||||
@@ -243,23 +468,20 @@ def cadVectorLayerToNative(geomList, layerName: str, geomType: str, streamBranch
|
||||
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)
|
||||
print("Layer created")
|
||||
|
||||
return vl
|
||||
|
||||
def vectorLayerToNative(layer: Layer, streamBranch: str, project: ArcGISProject):
|
||||
def vectorLayerToNative(layer: Union[Layer, VectorLayer], 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("^","_")
|
||||
@@ -271,27 +493,7 @@ def vectorLayerToNative(layer: Layer, streamBranch: str, project: ArcGISProject)
|
||||
#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
|
||||
newName, layerGroup = newLayerGroupAndName(layerName, streamBranch, project)
|
||||
|
||||
# particularly if the layer comes from ArcGIS
|
||||
geomType = layer.geomType # for ArcGIS: Polygon, Point, Polyline, Multipoint, MultiPatch
|
||||
@@ -347,8 +549,10 @@ def vectorLayerToNative(layer: Layer, streamBranch: str, project: ArcGISProject)
|
||||
if new_feat != "" and new_feat!= None: fets.append(new_feat)
|
||||
|
||||
print(fets)
|
||||
if len(fets) == 0: return None
|
||||
count = 0
|
||||
rowValues = []
|
||||
heads = None
|
||||
for feat in fets:
|
||||
#print(feat)
|
||||
try: feat['applicationId']
|
||||
@@ -405,88 +609,145 @@ def vectorLayerToNative(layer: Layer, streamBranch: str, project: ArcGISProject)
|
||||
|
||||
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)
|
||||
rasterLayer = None
|
||||
|
||||
layerName = layer.name.replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
|
||||
|
||||
#CREATE A GROUP "received blabla" with sublayers
|
||||
newGroupName = f'{streamBranch}'
|
||||
root = QgsProject.instance().layerTreeRoot()
|
||||
layerGroup = QgsLayerTreeGroup(newGroupName)
|
||||
print(layerName)
|
||||
sr = arcpy.SpatialReference(text=layer.crs.wkt)
|
||||
print(layer.crs.wkt)
|
||||
active_map = project.activeMap
|
||||
path = project.filePath.replace("aprx","gdb")
|
||||
#path = '.'.join(path.split("\\")[:-1])
|
||||
rasterHasSr = False
|
||||
print(path)
|
||||
|
||||
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])
|
||||
try:
|
||||
srRasterWkt = str(layer.rasterCrs.wkt)
|
||||
print(layer.rasterCrs.wkt)
|
||||
srRaster = arcpy.SpatialReference(text=srRasterWkt) # by native raster SR
|
||||
rasterHasSr = True
|
||||
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)
|
||||
srRasterWkt = str(layer.crs.wkt)
|
||||
srRaster: arcpy.SpatialReference = sr # by layer
|
||||
#print(layer.rasterCrs.wkt)
|
||||
print(srRaster)
|
||||
|
||||
newName, layerGroup = newLayerGroupAndName(layerName, streamBranch, project)
|
||||
print(newName)
|
||||
if "." in newName: newName = '.'.join(newName.split(".")[:-1])
|
||||
print(newName)
|
||||
|
||||
feat = layer.features[0]
|
||||
bandNames = feat["Band names"]
|
||||
bandValues = [feat["@(10000)" + name + "_values"] for name in bandNames]
|
||||
|
||||
#newName = f'{streamBranch}_latest_{layer.name}'
|
||||
xsize= int(feat["X pixels"])
|
||||
ysize= int(feat["Y pixels"])
|
||||
xres = float(feat["X resolution"])
|
||||
yres = float(feat["Y resolution"])
|
||||
bandsCount=int(feat["Band count"])
|
||||
originPt = arcpy.Point(feat['displayValue'][0].x, feat['displayValue'][0].y, feat['displayValue'][0].z)
|
||||
print(originPt)
|
||||
#if source projection is different from layer display projection, convert display OriginPt to raster source projection
|
||||
if rasterHasSr is True and srRaster.exportToString() != sr.exportToString():
|
||||
originPt = findTransformation(arcpy.PointGeometry(originPt, sr, has_z = True), "Point", sr, srRaster, None).getPart()
|
||||
print(originPt)
|
||||
|
||||
###########################################################################
|
||||
bandDatasets = ""
|
||||
rastersToMerge = []
|
||||
rasterPathsToMerge = []
|
||||
|
||||
## 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"]):
|
||||
arcpy.env.workspace = path
|
||||
arcpy.env.overwriteOutput = True
|
||||
# https://pro.arcgis.com/en/pro-app/latest/tool-reference/data-management/composite-bands.htm
|
||||
|
||||
|
||||
for i in range(bandsCount):
|
||||
print(i)
|
||||
print(bandNames[i])
|
||||
rasterbandPath = path + "\\" + newName + "_Band_" + str(i+1) #+ ".tif"
|
||||
bandDatasets += rasterbandPath + ";"
|
||||
rasterband = np.array(bandValues[i])
|
||||
rasterband = np.reshape(rasterband,(feat["Y pixels"], feat["X pixels"]))
|
||||
ds.GetRasterBand(i+1).WriteArray(rasterband) # or "rasterband.T"
|
||||
rasterband = np.reshape(rasterband,(ysize, xsize))
|
||||
print(rasterband)
|
||||
print(np.shape(rasterband))
|
||||
print(xsize)
|
||||
print(xres)
|
||||
print(ysize)
|
||||
print(yres)
|
||||
leftLowerCorner = arcpy.Point(originPt.X, originPt.Y + (ysize*yres), originPt.Z)
|
||||
#upperRightCorner = arcpy.Point(originPt.X + (xsize*xres), originPt.Y, originPt.Z)
|
||||
print(leftLowerCorner)
|
||||
#print(upperRightCorner)
|
||||
|
||||
# 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
|
||||
# # Convert array to a geodatabase raster, add to layers
|
||||
try: myRaster = arcpy.NumPyArrayToRaster(rasterband, leftLowerCorner, abs(xres), abs(yres), float(feat["NoDataVal"][i]) )
|
||||
except: myRaster = arcpy.NumPyArrayToRaster(rasterband, leftLowerCorner, abs(xres), abs(yres))
|
||||
|
||||
raster_layer = QgsRasterLayer(fn, newName, 'gdal')
|
||||
QgsProject.instance().addMapLayer(raster_layer, False)
|
||||
layerGroup.addLayer(raster_layer)
|
||||
rasterbandPath = validate_path(rasterbandPath) #solved file saving issue
|
||||
print(rasterbandPath)
|
||||
#mergedRaster = arcpy.ia.Merge(rastersToMerge) # glues all bands together
|
||||
myRaster.save(rasterbandPath)
|
||||
|
||||
dataProvider = raster_layer.dataProvider()
|
||||
rendererNew = rasterRendererToNative(layer, dataProvider)
|
||||
print(myRaster.width)
|
||||
print(myRaster.height)
|
||||
|
||||
try: raster_layer.setRenderer(rendererNew)
|
||||
except: pass
|
||||
rastersToMerge.append(myRaster)
|
||||
rasterPathsToMerge.append(rasterbandPath)
|
||||
print(rasterbandPath)
|
||||
|
||||
#mergedRaster.setProperty("spatialReference", crsRaster)
|
||||
|
||||
full_path = validate_path(path + "\\" + newName) #solved file saving issue
|
||||
if os.path.exists(full_path):
|
||||
for index, letter in enumerate('1234567890abcdefghijklmnopqrstuvwxyz'):
|
||||
if os.path.exists(full_path + letter): pass
|
||||
else: full_path += letter; break
|
||||
|
||||
print(full_path)
|
||||
#mergedRaster = arcpy.ia.Merge(rastersToMerge) # glues all bands together
|
||||
#mergedRaster.save(full_path) # similar errors: https://community.esri.com/t5/python-questions/error-010240-could-not-save-raster-dataset/td-p/321690
|
||||
|
||||
arcpy.management.CompositeBands(rasterPathsToMerge, full_path)
|
||||
print(path + "\\" + newName)
|
||||
arcpy.management.DefineProjection(full_path, srRaster)
|
||||
|
||||
rasterLayer = arcpy.management.MakeRasterLayer(full_path, newName + "_").getOutput(0)
|
||||
print(layerGroup)
|
||||
active_map.addLayerToGroup(layerGroup, rasterLayer)
|
||||
|
||||
r'''
|
||||
if arcpy.Exists(fileout):
|
||||
arcpy.management.Delete(fileout)
|
||||
arcpy.management.Rename(filelist[0], fileout)
|
||||
|
||||
# Remove temporary files
|
||||
for fileitem in filelist:
|
||||
if arcpy.Exists(fileitem):
|
||||
arcpy.management.Delete(fileitem)
|
||||
|
||||
# Release raster objects from memory
|
||||
del myRasterBlock
|
||||
del myRaster
|
||||
'''
|
||||
return raster_layer
|
||||
|
||||
r'''
|
||||
rasterComposite = arcpy.management.CompositeBands(bandDatasets, path + "\\" + newName) # "band1.tif;band2.tif;band3.tif", "compbands.tif"
|
||||
|
||||
# https://pro.arcgis.com/en/pro-app/latest/tool-reference/data-management/make-raster-layer.htm
|
||||
rasterLayer = arcpy.MakeRasterLayer_management(rasterComposite, newName)
|
||||
'''
|
||||
|
||||
|
||||
r'''
|
||||
# WORKS:
|
||||
arcpy.CreateRasterDataset_management(r"C:\Users\Kateryna\Documents\ArcGIS\Projects\MyProject-test",
|
||||
"EmptyTIFF.tif",
|
||||
"2",
|
||||
"8_BIT_UNSIGNED",
|
||||
"PROJCS['DHDN_3_Degree_Gauss_Zone_3',GEOGCS['GCS_Deutsches_Hauptdreiecksnetz',DATUM['D_Deutsches_Hauptdreiecksnetz',SPHEROID['Bessel_1841',6377397.155,299.1528128]],PRIMEM['Greenwich',0.0],UNIT['Degree',0.0174532925199433]],PROJECTION['Gauss_Kruger'],PARAMETER['False_Easting',3500000.0],PARAMETER['False_Northing',0.0],PARAMETER['Central_Meridian',9.0],PARAMETER['Scale_Factor',1.0],PARAMETER['Latitude_Of_Origin',0.0],UNIT['Meter',1.0]]", "3", "", "PYRAMIDS -1 NEAREST JPEG", "128 128", "NONE", "")
|
||||
'''
|
||||
|
||||
return rasterLayer
|
||||
|
||||
@@ -1,66 +1,45 @@
|
||||
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
|
||||
from speckle.converter.layers.utils import (findTransformation, getVariantFromValue, traverseDict,
|
||||
traverseDictByKey, hsv_to_rgb)
|
||||
|
||||
def featureToSpeckle(fieldnames, attr_list, f_shape, projectCRS: arcpy.SpatialReference, project: arcpy.mp.ArcGISProject, selectedLayer):
|
||||
from speckle.converter.geometry.point import pointToSpeckle
|
||||
from speckle.converter.geometry.mesh import rasterToMesh, meshToNative
|
||||
|
||||
import numpy as np
|
||||
import colorsys
|
||||
|
||||
|
||||
def featureToSpeckle(fieldnames, attr_list, f_shape, projectCRS: arcpy.SpatialReference, project: ArcGISProject, selectedLayer: arcLayer):
|
||||
print("___________Feature to Speckle____________")
|
||||
b = Base(units = "m")
|
||||
data = arcpy.Describe(selectedLayer.dataSource)
|
||||
layer_sr = data.spatialReference # if sr.type == "Projected":
|
||||
geomType = data.shapeType #Polygon, Point, Polyline, Multipoint, MultiPatch
|
||||
featureType = data.featureType # Simple,SimpleJunction,SimpleJunction,ComplexEdge,Annotation,CoverageAnnotation,Dimension,RasterCatalogItem
|
||||
|
||||
print(geomType)
|
||||
print(hasattr(data, "isRevit"))
|
||||
print(hasattr(data, "isIFC"))
|
||||
print(hasattr(data, "bimLevels"))
|
||||
print(hasattr(data, "hasSpatialIndex"))
|
||||
if geomType == "MultiPatch" or hasattr(data, "isRevit") or hasattr(data, "isIFC") or hasattr(data, "bimLevels"):
|
||||
print(f"Layer {selectedLayer.name} has unsupported data type")
|
||||
arcpy.AddWarning(f"Layer {selectedLayer.name} has unsupported data type")
|
||||
return None
|
||||
#print(layer_sr.name)
|
||||
#print(projectCRS.name)
|
||||
|
||||
#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
|
||||
|
||||
f_shape = findTransformation(f_shape, geomType, layer_sr, projectCRS, selectedLayer)
|
||||
if f_shape is None: return None
|
||||
|
||||
######################################### Convert geometry ##########################################
|
||||
try:
|
||||
@@ -84,7 +63,7 @@ def featureToSpeckle(fieldnames, attr_list, f_shape, projectCRS: arcpy.SpatialRe
|
||||
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
|
||||
@@ -99,7 +78,7 @@ def featureToNative(feature: Base, fields: dict, geomType: str, sr: arcpy.Spatia
|
||||
feat.update({"arcGisGeomFromSpeckle": arcGisGeom})
|
||||
else:
|
||||
return None
|
||||
print(feat)
|
||||
|
||||
for key, variant in fields.items():
|
||||
|
||||
value = feature[key]
|
||||
@@ -112,33 +91,428 @@ def featureToNative(feature: Base, fields: dict, geomType: str, sr: arcpy.Spatia
|
||||
if variant == "LONG": feat.update({key: None})
|
||||
if variant == "SHORT": feat.update({key: None})
|
||||
return feat
|
||||
|
||||
def bimFeatureToNative(feature: Base, fields: dict, sr: arcpy.SpatialReference, path: str):
|
||||
#print("04_________BIM Feature To Native____________")
|
||||
|
||||
def cadFeatureToNative(feature: Base, fields: dict, sr: arcpy.SpatialReference):
|
||||
print("_________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)
|
||||
|
||||
feat.update({"arcGisGeomFromSpeckle": ""})
|
||||
|
||||
try:
|
||||
if "Speckle_ID" not in fields.keys() and feature["id"]: feat.update("Speckle_ID", "TEXT")
|
||||
except: pass
|
||||
feat_updated = updateFeat(feat, fields, feature)
|
||||
|
||||
return feat_updated
|
||||
|
||||
|
||||
def cadFeatureToNative(feature: Base, fields: dict, sr: arcpy.SpatialReference):
|
||||
#print("04_________CAD Feature To Native____________")
|
||||
feat = {}
|
||||
try: speckle_geom = feature["geometry"] # for created in QGIS Layer type
|
||||
except: speckle_geom = feature # for created in other software
|
||||
|
||||
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
|
||||
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})
|
||||
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
|
||||
|
||||
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)
|
||||
return feat_updated
|
||||
|
||||
def updateFeat(feat:dict, fields: dict, feature: Base) -> dict[str, Any]:
|
||||
|
||||
for key, variant in fields.items():
|
||||
try:
|
||||
if key == "Speckle_ID":
|
||||
value = str(feature["id"])
|
||||
if key != "parameters": print(value)
|
||||
feat[key] = value
|
||||
|
||||
if variant == "TEXT": value = str(value)
|
||||
if variant == getVariantFromValue(value) and value != "NULL" and value != "None": feat.update({key: value})
|
||||
elif variant == "TEXT" or variant == "FLOAT" or variant == "LONG" or variant == "SHORT": feat.update({key: None})
|
||||
|
||||
else:
|
||||
try:
|
||||
value = feature[key]
|
||||
if variant == "TEXT": value = str(value)
|
||||
if variant == getVariantFromValue(value) and value != "NULL" and value != "None": feat.update({key: value})
|
||||
elif variant == "TEXT" or variant == "FLOAT" or variant == "LONG" or variant == "SHORT": feat.update({key: None})
|
||||
|
||||
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
|
||||
if variant == "TEXT": value = str(value)
|
||||
if variant == getVariantFromValue(value) and value != "NULL" and value != "None": feat.update({key: value})
|
||||
elif variant == "TEXT" or variant == "FLOAT" or variant == "LONG" or variant == "SHORT": feat.update({key: None})
|
||||
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
|
||||
#print(variant)
|
||||
if variant == "TEXT": value = str(value)
|
||||
if variant == getVariantFromValue(value) and value != "NULL" and value != "None": feat.update({key: value})
|
||||
elif variant == "TEXT" or variant == "FLOAT" or variant == "LONG" or variant == "SHORT": feat.update({key: None})
|
||||
except Exception as e: feat.update({key: None})
|
||||
except Exception as e:
|
||||
feat.update({key: None})
|
||||
feat_sorted = {k: v for k, v in sorted(feat.items(), key=lambda item: item[0])}
|
||||
#print("_________________end of updating a feature_________________________")
|
||||
return feat_sorted
|
||||
|
||||
def rasterFeatureToSpeckle(selectedLayer: arcLayer, projectCRS: arcpy.SpatialReference, project: ArcGISProject) -> Base:
|
||||
print("_________ Raster feature to speckle______")
|
||||
# https://pro.arcgis.com/en/pro-app/latest/arcpy/classes/raster-object.htm
|
||||
|
||||
r'''
|
||||
# Save layer file to read symbology
|
||||
# https://pro.arcgis.com/en/pro-app/latest/tool-reference/data-management/save-to-layer-file.htm
|
||||
layerFile = project.homeFolder + "\\" + selectedLayer.name.split(".")[0]
|
||||
arcpy.management.SaveToLayerFile(selectedLayer.name, layerFile, "ABSOLUTE")
|
||||
|
||||
# read the file and then delete
|
||||
f = open(layerFile + ".lyrx", "r")
|
||||
layerFileContent = json.loads(f.read())
|
||||
print(layerFileContent)
|
||||
f.close()
|
||||
os.remove(layerFile + ".lyrx")
|
||||
'''
|
||||
|
||||
# get Raster object of entire raster dataset
|
||||
my_raster = arcpy.Raster(selectedLayer.dataSource)
|
||||
print(my_raster.mdinfo) # None
|
||||
|
||||
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 = []
|
||||
|
||||
|
||||
b = Base(units = "m")
|
||||
# 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:
|
||||
arcpy.AddError("Error converting point geometry: " + str(error))
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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
|
||||
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 colorizer:
|
||||
try: bandIndex = int(colorizer.band)
|
||||
except: pass
|
||||
try:
|
||||
if rasterBandVals[bandIndex][int(count/4)] >= float(colorizer.minLabel) and rasterBandVals[bandIndex][int(count/4)] <= float(colorizer.maxLabel) : #rasterBandMinVal[bandIndex]:
|
||||
# REMAP band values to (0,255) range
|
||||
valRange = float(colorizer.maxLabel) - float(colorizer.minLabel) #(rasterBandMaxVal[bandIndex] - rasterBandMinVal[bandIndex])
|
||||
colorVal = int( (rasterBandVals[bandIndex][int(count/4)] - float(colorizer.minLabel)) / valRange * 255 )
|
||||
if colorizer.invertColorRamp is True: colorVal = int( (-rasterBandVals[bandIndex][int(count/4)] + float(colorizer.maxLabel)) / valRange * 255 )
|
||||
color = (colorVal<<16) + (colorVal<<8) + colorVal
|
||||
except: # if no Min Max labels:
|
||||
# REMAP band values to (0,255) range
|
||||
valRange = max(rasterBandVals[bandIndex]) - min(rasterBandVals[bandIndex]) #(rasterBandMaxVal[bandIndex] - rasterBandMinVal[bandIndex])
|
||||
colorVal = int( (rasterBandVals[bandIndex][int(count/4)] - min(rasterBandVals[bandIndex])) / valRange * 255 )
|
||||
color = (colorVal<<16) + (colorVal<<8) + colorVal
|
||||
else:
|
||||
# REMAP band values to (0,255) range
|
||||
rbVals = my_raster.getRasterBands(rasterBandNames[0])
|
||||
try:
|
||||
rbvalMin = rbVals.minimum
|
||||
rbvalMax = rbVals.maximum
|
||||
except:
|
||||
rbvalMin = min(rbVals)
|
||||
rbvalMax = max(rbVals)
|
||||
|
||||
valRange = float(rbvalMax) - float(rbvalMin) #(rasterBandMaxVal[bandIndex] - rasterBandMinVal[bandIndex])
|
||||
colorVal = int( (rasterBandVals[bandIndex][int(count/4)] - float(rbvalMin)) / valRange * 255 )
|
||||
color = (colorVal<<16) + (colorVal<<8) + colorVal
|
||||
|
||||
colors.extend([color,color,color,color])
|
||||
count += 4
|
||||
|
||||
mesh = rasterToMesh(vertices, faces, colors)
|
||||
if(b['displayValue'] is None):
|
||||
b['displayValue'] = []
|
||||
b['displayValue'].append(mesh)
|
||||
|
||||
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'}
|
||||
'''
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
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
|
||||
|
||||
ATTRS_REMOVE = ['geometry','applicationId','bbox','displayStyle', 'id', 'renderMaterial', 'displayMesh']
|
||||
|
||||
def getVariantFromValue(value: Any) -> Union[str, None]:
|
||||
#print("_________get variant from value_______")
|
||||
# TODO add Base object
|
||||
pairs = [
|
||||
(str, "TEXT"), # 10
|
||||
@@ -24,51 +30,94 @@ def getVariantFromValue(value: Any) -> Union[str, None]:
|
||||
|
||||
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")
|
||||
#print(featuresList)
|
||||
if not isinstance(featuresList, List): features = [featuresList]
|
||||
else: features = featuresList[:]
|
||||
fields = {}
|
||||
all_props = []
|
||||
for feature in features:
|
||||
#get object properties to add as attributes
|
||||
dynamicProps = feature.get_dynamic_member_names()
|
||||
attrsToRemove = ['geometry','applicationId','bbox','displayStyle', 'id',
|
||||
'renderMaterial', 'userDictionary', 'userStrings','geometry']
|
||||
for att in attrsToRemove:
|
||||
try: dynamicProps.remove(att)
|
||||
except: pass
|
||||
|
||||
dynamicProps.sort()
|
||||
#print(dynamicProps)
|
||||
|
||||
# add field names and variands
|
||||
#variants = []
|
||||
for name in dynamicProps:
|
||||
if name not in all_props: all_props.append(name)
|
||||
#if name not in all_props: all_props.append(name)
|
||||
|
||||
value = feature[name]
|
||||
variant = getVariantFromValue(value)
|
||||
#print(variant)
|
||||
if not variant: variant = None #LongLong #4
|
||||
|
||||
# add a field if not existing yet AND if variant is known
|
||||
if variant and (name not in fields.keys()):
|
||||
fields.update({name: variant})
|
||||
# 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)
|
||||
for i, (k,v) in enumerate(newF.items()):
|
||||
fields.update({k: v})
|
||||
if k not in all_props: all_props.append(k)
|
||||
#print(fields)
|
||||
|
||||
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)
|
||||
# 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
|
||||
elif k in fields.keys(): #check if the field was empty previously:
|
||||
oldVariant = fields[k]
|
||||
# replace if new one is NOT LongLong or IS String
|
||||
if oldVariant != "TEXT" and variant == "TEXT":
|
||||
fields.update({k: variant})
|
||||
#print(fields)
|
||||
|
||||
# replace all empty ones wit String
|
||||
for name in all_props:
|
||||
if name not in fields.keys():
|
||||
fields.update({name: "TEXT"})
|
||||
print(fields)
|
||||
return fields
|
||||
fields.update({name: 'TEXT'})
|
||||
#print(fields)
|
||||
fields_sorted = {k: v for k, v in sorted(fields.items(), key=lambda item: item[0])}
|
||||
return fields_sorted
|
||||
|
||||
def traverseDict(newF: dict, newVals: dict, nam: str, val: Any):
|
||||
#print("______05___Traverse Dict")
|
||||
#print(nam)
|
||||
#print(val)
|
||||
|
||||
if isinstance(val, dict):
|
||||
#print("DICT")
|
||||
for i, (k,v) in enumerate(val.items()):
|
||||
newF, newVals = traverseDict( newF, newVals, nam+"_"+k, v)
|
||||
elif isinstance(val, Base):
|
||||
#print("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:
|
||||
#print("ELSE")
|
||||
var = getVariantFromValue(val)
|
||||
if var is None:
|
||||
var = 'TEXT'
|
||||
val = str(val)
|
||||
newF.update({nam: var})
|
||||
newVals.update({nam: val})
|
||||
|
||||
return newF, newVals
|
||||
|
||||
def get_scale_factor(units: str) -> float:
|
||||
unit_scale = {
|
||||
@@ -92,3 +141,159 @@ def get_scale_factor(units: str) -> float:
|
||||
arcpy.AddWarning(f"Units {units} are not supported. Meters will be applied by default.")
|
||||
return 1.0
|
||||
|
||||
def findTransformation(f_shape, geomType, layer_sr: arcpy.SpatialReference, projectCRS: arcpy.SpatialReference, selectedLayer: arcLayer):
|
||||
#apply transformation if needed
|
||||
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: arcpy.AddWarning("Unsupported or invalid geometry in layer " + selectedLayer.name)
|
||||
except: arcpy.AddWarning("Unsupported or invalid geometry")
|
||||
|
||||
# reproject geometry using chosen transformstion(s)
|
||||
if tr0 is not None:
|
||||
ptgeo1 = f_shape.projectAs(projectCRS, tr0)
|
||||
f_shape = ptgeo1
|
||||
elif tr1 is not None and tr2 is not None:
|
||||
ptgeo1 = f_shape.projectAs(midSr, tr1)
|
||||
ptgeo2 = ptgeo1.projectAs(projectCRS, tr2)
|
||||
f_shape = ptgeo2
|
||||
else:
|
||||
ptgeo1 = f_shape.projectAs(projectCRS)
|
||||
f_shape = ptgeo1
|
||||
|
||||
except:
|
||||
arcpy.AddWarning(f"Spatial Transformation not found for layer {selectedLayer.name}")
|
||||
return None
|
||||
|
||||
return f_shape
|
||||
|
||||
def traverseDictByKey(d: Dict, key:str ="", result = None) -> Dict:
|
||||
print("__traverse")
|
||||
|
||||
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
|
||||
#print("__result is: ____________")
|
||||
#return result
|
||||
|
||||
def hsv_to_rgb(listHSV):
|
||||
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)
|
||||
|
||||
def cmyk_to_rgb(c, m, y, k, cmyk_scale, rgb_scale=255):
|
||||
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
|
||||
|
||||
def newLayerGroupAndName(layerName: str, streamBranch: str, project: ArcGISProject) -> str:
|
||||
print("___new Layer Group and Name")
|
||||
#CREATE A GROUP "received blabla" with sublayers
|
||||
layerGroup = None
|
||||
newGroupName = f'{streamBranch}'
|
||||
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
|
||||
|
||||
|
||||
def curvedFeatureClassToSegments(layer) -> str:
|
||||
print("___densify___")
|
||||
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
|
||||
|
||||
def validate_path(path: str):
|
||||
# https://github.com/EsriOceans/btm/commit/a9c0529485c9b0baa78c1f094372c0f9d83c0aaf
|
||||
"""If our path contains a DB name, make sure we have a valid DB name and not a standard file name."""
|
||||
dirname, file_name = os.path.split(path)
|
||||
#print(dirname)
|
||||
#print(file_name)
|
||||
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
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
|
||||
import arcpy
|
||||
|
||||
from speckle.converter.layers.CRS import CRS
|
||||
from speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
|
||||
|
||||
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
|
||||
from arcpy.management import (CreateFeatureclass, MakeFeatureLayer,
|
||||
AddFields, AlterField, DefineProjection )
|
||||
|
||||
from specklepy.objects import Base
|
||||
|
||||
project = ArcGISProject('CURRENT')
|
||||
active_map = project.activeMap
|
||||
all_layers = []
|
||||
#get layer of interest
|
||||
for layer in active_map.listLayers():
|
||||
if layer.isFeatureLayer or layer.isRasterLayer: all_layers.append(layer)
|
||||
if layer.name == "ExteriorShell": break
|
||||
|
||||
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\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\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,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
|
||||
|
||||
|
||||
'''
|
||||
@@ -1,16 +1,21 @@
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Any, Callable, List, Optional, Tuple
|
||||
#r'''
|
||||
from collections import defaultdict
|
||||
from typing import Any, Callable, List, Optional
|
||||
from xmlrpc.client import Boolean
|
||||
|
||||
import arcpy
|
||||
#from arcpy import toolbox
|
||||
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
|
||||
from arcpy import metadata as md
|
||||
|
||||
from specklepy.api.models import Branch, Stream, Streams
|
||||
from speckle.converter.layers.Layer import Layer, RasterLayer
|
||||
|
||||
from speckle.converter.layers._init_ import convertSelectedLayers, layerToNative, cadLayerToNative
|
||||
from speckle.converter.layers._init_ import convertSelectedLayers, layerToNative, cadLayerToNative, bimLayerToNative
|
||||
from arcgis.features import FeatureLayer
|
||||
import os
|
||||
import os.path
|
||||
|
||||
import specklepy
|
||||
@@ -19,14 +24,20 @@ 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.objects import Base
|
||||
#from specklepy.api.credentials import StreamWrapper
|
||||
from specklepy.api.wrapper import StreamWrapper
|
||||
from specklepy.objects import Base
|
||||
from specklepy.logging import metrics
|
||||
|
||||
from speckle.ui.project_vars import toolboxInputsClass, speckleInputsClass
|
||||
from speckle.converter.layers.emptyLayerTemplates import createGroupLayer
|
||||
from speckle.converter.layers.Layer import VectorLayer
|
||||
|
||||
#'''
|
||||
|
||||
def traverseObject(
|
||||
base: Base,
|
||||
@@ -58,120 +69,178 @@ def traverseValue(
|
||||
traverseValue(item, callback, check)
|
||||
|
||||
|
||||
class Toolbox(object):
|
||||
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]
|
||||
#self.toolboxInputs = uiInputs() # initialize once together with a toolbox
|
||||
self.tools = [Speckle]
|
||||
metrics.set_host_app("ArcGIS")
|
||||
|
||||
|
||||
#print(self.toolboxInputs.selected_layers)
|
||||
#try:
|
||||
# https://pro.arcgis.com/en/pro-app/2.8/arcpy/mapping/alphabeticallistofclasses.htm#except: print("something happened")
|
||||
|
||||
class uiInputs(object):
|
||||
speckle_client: Any
|
||||
streams: Optional[Streams]
|
||||
active_stream: Optional[Stream]
|
||||
active_branch: Optional[Branch]
|
||||
all_layers: List[arcLayer]
|
||||
selected_layers: List[Any]
|
||||
messageSpeckle: str
|
||||
project: ArcGISProject
|
||||
action: int
|
||||
instances = []
|
||||
|
||||
def __init__(self):
|
||||
#print("start UI inputs________")
|
||||
self.instances.append(self)
|
||||
accounts = get_local_accounts()
|
||||
account = None
|
||||
for acc in accounts:
|
||||
if acc.isDefault: account = acc; break
|
||||
#account.userInfo.name, account.serverInfo.url
|
||||
self.speckle_client = SpeckleClient(account.serverInfo.url, account.serverInfo.url.startswith("https"))
|
||||
self.speckle_client.authenticate_with_token(token=account.token)
|
||||
#print("ping")
|
||||
#print(self.speckle_client)
|
||||
self.streams = self.speckle_client.stream.search("")
|
||||
#print("ping")
|
||||
self.active_stream = None
|
||||
self.active_branch = None
|
||||
self.active_commit = None
|
||||
self.all_layers = []
|
||||
self.selected_layers = []
|
||||
self.messageSpeckle = ""
|
||||
self.project = aprx = None
|
||||
self.action = 1 #send
|
||||
#print(self.streams)
|
||||
try: aprx = ArcGISProject('CURRENT')
|
||||
except:
|
||||
#print(arcpy.env.workspace) # None
|
||||
#arcpy.env.workspace = ""
|
||||
#proj_path = "\\".join(arcpy.env.workspace.split("\\")[:-1]) + "\\"
|
||||
#aprx = ArcGISProject(proj_path)
|
||||
#print(aprx)
|
||||
print("Project not found")
|
||||
self.project = aprx
|
||||
active_map = aprx.activeMap
|
||||
|
||||
if active_map is not None and isinstance(active_map, Map): # if project loaded
|
||||
for layer in active_map.listLayers():
|
||||
#print(layer)
|
||||
if layer.isFeatureLayer: self.all_layers.append(layer) #type: 'arcpy._mp.Layer'
|
||||
|
||||
|
||||
class Speckle(object):
|
||||
class Speckle:
|
||||
def __init__(self):
|
||||
#print("________________reset_______________")
|
||||
self.label = "Speckle"
|
||||
self.description = "Allows you to send and receive your layers " + \
|
||||
"to/from other software using Speckle server."
|
||||
self.toolboxInputs = None
|
||||
self.toRefresh = False
|
||||
|
||||
for instance in uiInputs.instances:
|
||||
#print(instance)
|
||||
if instance is not None:
|
||||
try:
|
||||
x = instance.streams # in case not initialized properly
|
||||
self.toolboxInputs = instance # take latest
|
||||
except: pass
|
||||
if self.toolboxInputs is None:
|
||||
#print("Instance is None")
|
||||
self.toolboxInputs = uiInputs() #in case Toolbox class was not initialized
|
||||
# TODO react on project changes
|
||||
self.toRefresh = False
|
||||
self.speckleInputs = None
|
||||
self.toolboxInputs = None
|
||||
#print("ping_Speckle1")
|
||||
|
||||
#print("______continue reset_______")
|
||||
#print(self.toolboxInputs.all_layers)
|
||||
#print(speckleInputsClass.instances)
|
||||
total = len(speckleInputsClass.instances)
|
||||
#print(total)
|
||||
for i in range(total):
|
||||
#print(i)
|
||||
#print(speckleInputsClass.instances[total-i-1])
|
||||
if speckleInputsClass.instances[total-i-1] is not None:
|
||||
try:
|
||||
#print(speckleInputsClass.instances[total-i-1].streams_default)
|
||||
y = speckleInputsClass.instances[total-i-1].streams_default # in case not initialized properly
|
||||
self.speckleInputs = speckleInputsClass.instances[total-i-1] # take latest (first in reverted list)
|
||||
#print("FOUND INSTANCE")
|
||||
break
|
||||
except: pass
|
||||
#print(self.speckleInputs)
|
||||
if self.speckleInputs is None: self.speckleInputs = speckleInputsClass()
|
||||
|
||||
|
||||
#print(toolboxInputsClass.instances)
|
||||
#print("TOTAL = ...................")
|
||||
total = len(toolboxInputsClass.instances)
|
||||
#print(total)
|
||||
for i in range(total):
|
||||
if toolboxInputsClass.instances[total-i-1] is not None:
|
||||
self.toolboxInputs = toolboxInputsClass.instances[total-i-1] # take latest (first in reverted list)
|
||||
#print("FOUND INSTANCE")
|
||||
break
|
||||
#print(self.toolboxInputs)
|
||||
if self.toolboxInputs is None: self.toolboxInputs = toolboxInputsClass()
|
||||
|
||||
#print("ping_Speckle2")
|
||||
|
||||
def getParameterInfo(self):
|
||||
#data types: https://pro.arcgis.com/en/pro-app/2.8/arcpy/geoprocessing_and_python/defining-parameter-data-types-in-a-python-toolbox.htm
|
||||
# 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"
|
||||
|
||||
stream = arcpy.Parameter(
|
||||
displayName="Stream",
|
||||
name="stream",
|
||||
streamsDefalut = arcpy.Parameter(
|
||||
displayName="Add stream from default account",
|
||||
name="streamsDefalut",
|
||||
datatype="GPString",
|
||||
parameterType="Optional",
|
||||
#category="Sending data",
|
||||
direction="Input",
|
||||
category=cat1
|
||||
)
|
||||
streamsDefalut.filter.type = 'ValueList'
|
||||
streamsDefalut.filter.list = [ (st.name + " - " + st.id) for st in self.speckleInputs.streams_default ]
|
||||
|
||||
addDefStreams = arcpy.Parameter(
|
||||
displayName="Add",
|
||||
name="addDefStreams",
|
||||
datatype="GPBoolean",
|
||||
parameterType="Optional",
|
||||
#category="Sending data",
|
||||
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",
|
||||
#category="Sending data",
|
||||
direction="Input")
|
||||
stream.filter.type = 'ValueList'
|
||||
stream.filter.list = [ (st.name + " | " + st.id) for st in self.toolboxInputs.streams ]
|
||||
|
||||
direction="Input",
|
||||
multiValue=False,
|
||||
#category=cat2
|
||||
)
|
||||
savedStreams.filter.list = [f"Stream not accessible - {stream[0].stream_id}" if stream[1] is None or isinstance(stream[1], SpeckleException) else f"{stream[1].name} - {stream[1].id}" for i,stream in enumerate(self.speckleInputs.saved_streams)]
|
||||
|
||||
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",
|
||||
#category="Sending data",
|
||||
direction="Input")
|
||||
branch.value = "main"
|
||||
direction="Input",
|
||||
#category=cat2
|
||||
)
|
||||
branch.value = ""
|
||||
branch.filter.type = 'ValueList'
|
||||
|
||||
commit = arcpy.Parameter(
|
||||
@@ -180,40 +249,34 @@ class Speckle(object):
|
||||
datatype="GPString",
|
||||
parameterType="Optional",
|
||||
#category="Sending data",
|
||||
direction="Input")
|
||||
direction="Input",
|
||||
#category=cat2
|
||||
)
|
||||
commit.value = ""
|
||||
commit.filter.type = 'ValueList'
|
||||
|
||||
################################################################
|
||||
msg = arcpy.Parameter(
|
||||
displayName="Message",
|
||||
name="message",
|
||||
name="msg",
|
||||
datatype="GPString",
|
||||
parameterType="Optional",
|
||||
direction="Input",
|
||||
multiValue=False)
|
||||
multiValue=False,
|
||||
#category=cat2
|
||||
)
|
||||
msg.value = ""
|
||||
|
||||
selected_layers = arcpy.Parameter(
|
||||
selectedLayers = arcpy.Parameter(
|
||||
displayName="Selected Layers",
|
||||
name="selected_layers",
|
||||
name="selectedLayers",
|
||||
datatype="GPString",
|
||||
parameterType="Optional",
|
||||
direction="Input",
|
||||
multiValue=True
|
||||
multiValue=True,
|
||||
#category=cat2
|
||||
)
|
||||
selected_layers.filter.list = [str(i) + "-" + l.longName for i,l in enumerate(self.toolboxInputs.all_layers)] #"Polyline"
|
||||
selectedLayers.filter.list = [str(i) + "-" + l.longName for i,l in enumerate(self.speckleInputs.all_layers)] #"Polyline"
|
||||
|
||||
refresh = arcpy.Parameter(
|
||||
displayName="Refresh",
|
||||
name="refresh",
|
||||
datatype="GPBoolean",
|
||||
parameterType="Optional",
|
||||
#category="Sending data",
|
||||
direction="Input"
|
||||
)
|
||||
#refresh.filter.type = "ValueList"
|
||||
refresh.value = False
|
||||
|
||||
action = arcpy.Parameter(
|
||||
displayName="",
|
||||
@@ -222,13 +285,24 @@ class Speckle(object):
|
||||
parameterType="Required",
|
||||
#category="Sending data",
|
||||
direction="Input",
|
||||
multiValue=False
|
||||
multiValue=False,
|
||||
#category=cat2
|
||||
)
|
||||
action.value = "Send"
|
||||
#action.filter.type = 'ValueList'
|
||||
action.filter.list = ["Send", "Receive"]
|
||||
|
||||
parameters = [stream, branch, commit, selected_layers, msg, action, refresh]
|
||||
refresh = arcpy.Parameter(
|
||||
displayName="Refresh",
|
||||
name="refresh",
|
||||
datatype="GPBoolean",
|
||||
parameterType="Optional",
|
||||
direction="Input"
|
||||
)
|
||||
#refresh.filter.type = "ValueList"
|
||||
refresh.value = False
|
||||
|
||||
parameters = [streamsDefalut, addDefStreams, streamUrl, addUrlStreams, lat, lon, setLatLon, savedStreams, removeStream, branch, commit, selectedLayers, msg, action, refresh]
|
||||
return parameters
|
||||
|
||||
def isLicensed(self): #optional
|
||||
@@ -237,130 +311,238 @@ class Speckle(object):
|
||||
def updateParameters(self, parameters: List, toRefresh = False): #optional
|
||||
print("UPDATING PARAMETERS")
|
||||
|
||||
if parameters[0].altered:
|
||||
|
||||
# Search for the stream by name
|
||||
if parameters[0].valueAsText is not None:
|
||||
selected_stream_name = parameters[0].valueAsText[:]
|
||||
self.toolboxInputs.active_stream = None
|
||||
#print(self.toolboxInputs.active_stream)
|
||||
#print(self.toolboxInputs.streams)
|
||||
for st in self.toolboxInputs.streams:
|
||||
if st.name == selected_stream_name.split(" | ")[0]:
|
||||
self.toolboxInputs.active_stream = st
|
||||
break
|
||||
|
||||
# edit branches: globals and UI
|
||||
branch_list = [branch.name for branch in self.toolboxInputs.active_stream.branches.items]
|
||||
parameters[1].filter.list = branch_list
|
||||
#print(parameters[1].filter.list)
|
||||
|
||||
if parameters[1].valueAsText not in branch_list:
|
||||
parameters[1].value = "main"
|
||||
for b in self.toolboxInputs.active_stream.branches.items:
|
||||
if b.name == parameters[1].value:
|
||||
self.toolboxInputs.active_branch = b
|
||||
break
|
||||
|
||||
# setting commit value and list
|
||||
try:
|
||||
#print("___editing the stream input")
|
||||
#print(self.toolboxInputs.active_branch.commits.items)
|
||||
parameters[2].filter.list = [f"{commit.id}"+ " | " + f"{commit.message}" for commit in self.toolboxInputs.active_branch.commits.items]
|
||||
#print(parameters[2].filter.list)
|
||||
#print(parameters[2].valueAsText)
|
||||
if parameters[2].valueAsText not in parameters[2].filter.list:
|
||||
parameters[2].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:
|
||||
parameters[2].filter.list = []
|
||||
parameters[2].value = None
|
||||
self.toolboxInputs.active_commit = None
|
||||
|
||||
if parameters[1].altered: # branches
|
||||
if parameters[1].valueAsText is not None:
|
||||
selected_branch_name = parameters[1].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: #.split(" | ")[0]:
|
||||
self.toolboxInputs.active_branch = br
|
||||
break
|
||||
# edit commit values
|
||||
if self.toolboxInputs.active_branch is not None:
|
||||
try:
|
||||
#print("___editing the branch input")
|
||||
#print(self.toolboxInputs.active_branch)
|
||||
parameters[2].filter.list = [f"{commit.id}"+ " | " + f"{commit.message}" for commit in self.toolboxInputs.active_branch.commits.items]
|
||||
#print(parameters[2].filter.list)
|
||||
#print(parameters[2].valueAsText)
|
||||
if parameters[2].valueAsText not in parameters[2].filter.list:
|
||||
parameters[2].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:
|
||||
parameters[2].filter.list = []
|
||||
parameters[2].value = None
|
||||
self.toolboxInputs.active_commit = None
|
||||
|
||||
if parameters[2].altered: # commits
|
||||
if parameters[2].valueAsText is not None:
|
||||
selected_commit_id = parameters[2].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 parameters[3].altered: # selected layers
|
||||
if parameters[3].valueAsText is not None:
|
||||
self.toolboxInputs.selected_layers = parameters[3].values
|
||||
|
||||
if parameters[4].altered:
|
||||
self.toolboxInputs.messageSpeckle = parameters[4].valueAsText
|
||||
|
||||
if parameters[5].altered:
|
||||
if parameters[5].valueAsText == "Send": self.toolboxInputs.action = 1
|
||||
else: self.toolboxInputs.action = 0
|
||||
|
||||
if parameters[6].altered: # refresh btn
|
||||
if parameters[6].value == True:
|
||||
self.refresh(parameters)
|
||||
if self.toRefresh == True:
|
||||
self.refresh(parameters)
|
||||
self.toRefresh = False
|
||||
#if newParams: # apply fresh values
|
||||
# print(newParams[2].filter.list)
|
||||
# parameters = newParams
|
||||
for i, par in enumerate(parameters):
|
||||
|
||||
##parameters = newParams
|
||||
#print("continue UPDATING PARAMETERS")
|
||||
#print(parameters[2].filter.list)
|
||||
#print(parameters[4].valueAsText)
|
||||
return
|
||||
|
||||
if par.name == "addDefStreams" and par.altered and par.value == True:
|
||||
for p in parameters:
|
||||
if p.name == "streamsDefalut" and p.valueAsText is not None:
|
||||
# add value from streamsDefault to saved streams
|
||||
selected_stream_name = p.valueAsText[:]
|
||||
#print(selected_stream_name)
|
||||
for stream in self.speckleInputs.streams_default:
|
||||
#print(stream)
|
||||
if stream.name == selected_stream_name.split(" - ")[0]:
|
||||
print("_____Add from list___")
|
||||
wr = StreamWrapper(f"{self.speckleInputs.account.serverInfo.url}/streams/{stream.id}?u={self.speckleInputs.account.userInfo.id}")
|
||||
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
|
||||
# quesry stream, add to saved
|
||||
stream = self.speckleInputs.speckle_client.stream.get(steamId)
|
||||
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[:]
|
||||
#print(selected_stream_name)
|
||||
for streamTup in self.speckleInputs.saved_streams:
|
||||
#print(stream)
|
||||
stream = streamTup[1]
|
||||
if stream.name == selected_stream_name.split(" - ")[0]:
|
||||
print("_____Remove stream___")
|
||||
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[:]:
|
||||
#print("SAVED STREAMS - selection")
|
||||
selected_stream_name = par.valueAsText[:]
|
||||
self.toolboxInputs.active_stream = None
|
||||
for st in self.speckleInputs.saved_streams:
|
||||
if st[1].name == selected_stream_name.split(" - ")[0]:
|
||||
self.toolboxInputs.active_stream = st[1]
|
||||
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
|
||||
#print(self.toolboxInputs.action)
|
||||
|
||||
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
|
||||
|
||||
#print("selected layers changed")
|
||||
#print(self.toolboxInputs.action)
|
||||
#print(self.toolboxInputs.selected_layers)
|
||||
|
||||
if par.name == "msg" and par.altered and par.valueAsText is not None:
|
||||
self.toolboxInputs.messageSpeckle = par.valueAsText
|
||||
print(self.toolboxInputs.messageSpeckle)
|
||||
|
||||
if par.name == "action" and par.altered:
|
||||
#print("action changed")
|
||||
#print(par.valueAsText)
|
||||
if par.valueAsText == "Send": self.toolboxInputs.action = 1
|
||||
else: self.toolboxInputs.action = 0
|
||||
|
||||
#print(self.toolboxInputs.action)
|
||||
#print(self.toolboxInputs.selected_layers)
|
||||
|
||||
if par.name == "refresh" and par.altered: # refresh btn
|
||||
if par.value == True:
|
||||
self.refresh(parameters)
|
||||
if self.toRefresh == True:
|
||||
self.refresh(parameters)
|
||||
self.toRefresh = False
|
||||
|
||||
print("____________________________parameters___________________________")
|
||||
#[print(str(x.name) + " - " + str(x.valueAsText)) for x in parameters]
|
||||
#[x.clearMessage() for x in parameters] # https://pro.arcgis.com/en/pro-app/latest/arcpy/geoprocessing_and_python/programming-a-toolvalidator-class.htm
|
||||
#[print(x.valueAsText) for x in parameters]
|
||||
return
|
||||
|
||||
def refresh(self, parameters: List[Any]):
|
||||
print("Refresh______")
|
||||
uiInputs()
|
||||
for instance in uiInputs.instances:
|
||||
if instance is not None: self.toolboxInputs = instance # take latest
|
||||
#self.__init__()
|
||||
#self.streams = self.speckle_client.stream.search("")
|
||||
#params_new = []
|
||||
#for i,p in enumerate(parameters):
|
||||
# params_new.append(p)
|
||||
parameters[0].value = None
|
||||
parameters[1].value = "main"
|
||||
parameters[2].value = None
|
||||
parameters[3].value = None
|
||||
parameters[4].value = ""
|
||||
parameters[5].value = "Send"
|
||||
parameters[6].value = False
|
||||
|
||||
parameters[0].filter.list = [ (st.name + " | " + st.id) for st in self.toolboxInputs.streams ]
|
||||
parameters[3].filter.list = [str(i) + "-" + l.longName for i,l in enumerate(self.toolboxInputs.all_layers)]
|
||||
#print("___continue_refresh______")
|
||||
#print(parameters[2].filter.list)
|
||||
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": par.filter.list = [ (st.name + " - " + st.id) for st in self.speckleInputs.streams_default ]
|
||||
if par.name == "savedStreams":
|
||||
#print("par.name")
|
||||
saved_streams = self.speckleInputs.getProjectStreams()
|
||||
#print(saved_streams)
|
||||
par.filter.list = [f"Stream not accessible - {stream[0].stream_id}" if stream[1] is None or isinstance(stream[1], SpeckleException) else f"{stream[1].name} - {stream[1].id}" for i,stream in enumerate(saved_streams)]
|
||||
if par.name == "selectedLayers": par.filter.list = [str(i) + "-" + l.longName for i,l in enumerate(self.speckleInputs.all_layers)]
|
||||
|
||||
return parameters
|
||||
|
||||
@@ -370,10 +552,15 @@ class Speckle(object):
|
||||
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__________________________")
|
||||
#print(self.toolboxInputs.action)
|
||||
if self.toolboxInputs.action == 1: self.onSend(parameters)
|
||||
elif self.toolboxInputs.action == 0: self.onReceive(parameters)
|
||||
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):
|
||||
|
||||
@@ -388,14 +575,16 @@ class Speckle(object):
|
||||
|
||||
def onSend(self, parameters: List):
|
||||
|
||||
if self.validateStreamBranch(parameters) == False: return
|
||||
print("______________SEND_______________")
|
||||
|
||||
#if self.validateStreamBranch(parameters) == False: return
|
||||
|
||||
if len(self.toolboxInputs.selected_layers) == 0:
|
||||
arcpy.AddError("No layers selected")
|
||||
arcpy.AddError("No layers selected for sending")
|
||||
return
|
||||
|
||||
streamId = self.toolboxInputs.active_stream.id #stream_id
|
||||
client = self.toolboxInputs.speckle_client # ?
|
||||
client = self.speckleInputs.speckle_client # ?
|
||||
|
||||
# Get the stream wrapper
|
||||
#streamWrapper = StreamWrapper(None)
|
||||
@@ -411,9 +600,12 @@ class Speckle(object):
|
||||
transport = ServerTransport(client=client, stream_id=streamId)
|
||||
|
||||
##################################### conversions ################################################
|
||||
base_obj = Base()
|
||||
base_obj.layers = convertSelectedLayers(self.toolboxInputs.all_layers, self.toolboxInputs.selected_layers, self.toolboxInputs.project)
|
||||
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])
|
||||
@@ -426,7 +618,9 @@ class Speckle(object):
|
||||
|
||||
|
||||
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(
|
||||
@@ -437,22 +631,18 @@ class Speckle(object):
|
||||
source_application="ArcGIS",
|
||||
)
|
||||
arcpy.AddMessage("Successfully sent data to stream: " + streamId)
|
||||
#print("Successfully sent data to stream: " + streamId)
|
||||
#parameters[2].value = ""
|
||||
except:
|
||||
arcpy.AddError("Error creating commit")
|
||||
|
||||
#print("sent")
|
||||
#self.updateParameters(parameters, True)
|
||||
#self.refresh(parameters)
|
||||
|
||||
def onReceive(self, parameters: List[Any]):
|
||||
|
||||
if self.validateStreamBranch(parameters) == False: return
|
||||
print("______________RECEIVE_______________")
|
||||
|
||||
#if self.validateStreamBranch(parameters) == False: return
|
||||
|
||||
try:
|
||||
streamId = self.toolboxInputs.active_stream.id #stream_id
|
||||
client = self.toolboxInputs.speckle_client #
|
||||
client = self.speckleInputs.speckle_client #
|
||||
except SpeckleWarning as warning:
|
||||
arcpy.AddWarning(str(warning.args[0]))
|
||||
|
||||
@@ -472,7 +662,15 @@ class Speckle(object):
|
||||
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)
|
||||
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
|
||||
@@ -487,46 +685,57 @@ class Speckle(object):
|
||||
commitObj = operations.receive(objId, transport, None)
|
||||
|
||||
if app != "QGIS" and app != "ArcGIS":
|
||||
if self.toolboxInputs.project.activeMap.spatialReference.type == "Geographic" or self.toolboxInputs.project.activeMap.spatialReference is None: #TODO test with invalid CRS
|
||||
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"Succesfully received {objId}")
|
||||
print(f"Successfully received {objId}")
|
||||
|
||||
# Clear 'latest' group
|
||||
streamBranch = streamId + "_" + self.toolboxInputs.active_branch.name + "_" + str(commit.id)
|
||||
newGroupName = f'{streamBranch}'
|
||||
|
||||
groupExists = 0
|
||||
#print(newGroupName)
|
||||
for l in self.toolboxInputs.project.activeMap.listLayers():
|
||||
print(newGroupName)
|
||||
for l in self.speckleInputs.project.activeMap.listLayers():
|
||||
#print(l.longName)
|
||||
if l.longName.startswith(newGroupName + "\\"):
|
||||
#print(l.longName)
|
||||
self.toolboxInputs.project.activeMap.removeLayer(l)
|
||||
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.toolboxInputs.project.filePath.replace("aprx","gdb") #"\\".join(self.toolboxInputs.project.filePath.split("\\")[:-1]) + "\\speckle_layers\\"
|
||||
#print(path)
|
||||
f = open(path + "\\" + newGroupName + ".lyrx", "w")
|
||||
content = createGroupLayer().replace("TestGroupLayer", newGroupName)
|
||||
f.write(content)
|
||||
f.close()
|
||||
smth = arcpy.mp.LayerFile(path + "\\" + newGroupName + ".lyrx")
|
||||
#print(smth)
|
||||
layerGroup = self.toolboxInputs.project.activeMap.addLayer(smth)[0]
|
||||
layerGroup.name = newGroupName
|
||||
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
|
||||
|
||||
if app == "QGIS" or app == "ArcGIS": check: Callable[[Base], bool] = lambda base: isinstance(base, Layer) or isinstance(base, RasterLayer)
|
||||
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, RasterLayer):
|
||||
layer = layerToNative(base, streamBranch, self.toolboxInputs.project)
|
||||
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:
|
||||
@@ -543,6 +752,7 @@ class Speckle(object):
|
||||
def loopVal(value: Any, name: str): # "name" is the parent object/property/layer name
|
||||
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)
|
||||
@@ -550,19 +760,31 @@ class Speckle(object):
|
||||
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.toolboxInputs.project)
|
||||
|
||||
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 "Revit" in item.speckle_type and item.speckle_type.startswith("Objects.BuiltElements."):
|
||||
|
||||
msh_bool = bimLayerToNative(value, name, streamBranch, self.speckleInputs.project)
|
||||
#if msh is not None: print("Layer group created: " + msh.name())
|
||||
break
|
||||
|
||||
traverseObject(commitObj, callback, check)
|
||||
|
||||
except SpeckleException as e:
|
||||
print("Receive failed")
|
||||
except (SpeckleException, GraphQLException) as e:
|
||||
print("Receive failed: " + str(e))
|
||||
arcpy.AddError("Receive failed: " + str(e))
|
||||
return
|
||||
|
||||
print("received")
|
||||
#self.updateParameters(parameters, True)
|
||||
#self.refresh(parameters)
|
||||
|
||||
|
||||
|
||||
#__all__ = ["Toolbox", "Speckle"]
|
||||
@@ -0,0 +1,276 @@
|
||||
|
||||
from typing import Any, List, Optional, Tuple, Union
|
||||
import arcpy
|
||||
from arcpy._mp import ArcGISProject, Map, Layer as arcLayer
|
||||
from specklepy.api.models import Branch, Stream, Streams
|
||||
import os.path
|
||||
|
||||
from specklepy.api.credentials import get_local_accounts
|
||||
from specklepy.api.client import SpeckleClient
|
||||
from specklepy.logging.exceptions import (
|
||||
GraphQLException,
|
||||
SpeckleException,
|
||||
)
|
||||
#from specklepy.api.credentials import StreamWrapper
|
||||
from specklepy.api.wrapper import StreamWrapper
|
||||
from osgeo import osr
|
||||
|
||||
class speckleInputsClass:
|
||||
#def __init__(self):
|
||||
print("CREATING speckle inputs first time________")
|
||||
instances = []
|
||||
accounts = get_local_accounts()
|
||||
account = None
|
||||
streams_default: None or Streams = None
|
||||
|
||||
project = None
|
||||
active_map = None
|
||||
saved_streams: List[Optional[Tuple[StreamWrapper, Stream]]] = []
|
||||
stream_file_path: str = ""
|
||||
all_layers: List[arcLayer] = []
|
||||
clients = []
|
||||
|
||||
for acc in accounts:
|
||||
if acc.isDefault:
|
||||
account = acc
|
||||
#break
|
||||
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
|
||||
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:
|
||||
#print("Try get streams")
|
||||
|
||||
steamId = sw.stream_id
|
||||
try: steamId = sw.stream_id.split("/streams/")[1].split("/")[0]
|
||||
except: pass
|
||||
|
||||
client = sw.get_client()
|
||||
stream = client.stream.get(steamId)
|
||||
if isinstance(stream, GraphQLException):
|
||||
raise SpeckleException(stream.errors[0]['message'])
|
||||
return stream
|
||||
|
||||
|
||||
class toolboxInputsClass:
|
||||
#def __init__(self):
|
||||
print("CREATING UI inputs first time________")
|
||||
# self.instances.append(self)
|
||||
instances = []
|
||||
lat: float = 0.0
|
||||
lon: float = 0.0
|
||||
active_stream: Optional[Stream] = None
|
||||
active_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:
|
||||
# Cannot parse into a stream wrapper class - invalid URL provided.
|
||||
print("SET proj streams")
|
||||
|
||||
if os.path.exists(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 len(self.stream_file_path) >10:
|
||||
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 = self.stream_file_path
|
||||
try:
|
||||
f = open(self.stream_file_path, "r")
|
||||
content = f.read()
|
||||
f.close()
|
||||
except: pass
|
||||
if content:
|
||||
temp = []
|
||||
for i, coords in enumerate(content.split(",")):
|
||||
if "speckle_sr_origin_" in coords:
|
||||
try:
|
||||
x, y = [float(c) for c in coords.replace("speckle_sr_origin_","").split(";")]
|
||||
except: pass
|
||||
return (x, y)
|
||||
|
||||
def set_survey_point(self, coords: List[float]):
|
||||
# from widget (2 strings) to local vars + update SR of the map
|
||||
print("SET survey point")
|
||||
|
||||
pt = "speckle_sr_origin_" + str(coords[0]) + ";" + str(coords[1])
|
||||
if os.path.exists(self.stream_file_path):
|
||||
|
||||
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 len(self.stream_file_path) >10:
|
||||
f = open(self.stream_file_path, "x")
|
||||
f.close()
|
||||
f = open(self.stream_file_path, "w")
|
||||
f.write(pt + ",")
|
||||
f.close()
|
||||
|
||||
# save to project; crearte SR
|
||||
self.lat, self.lon = coords[0], coords[1]
|
||||
newCrsString = "+proj=tmerc +ellps=WGS84 +datum=WGS84 +units=m +no_defs +lon_0=" + str(self.lon) + " lat_0=" + str(self.lat) + " +x_0=0 +y_0=0 +k_0=1"
|
||||
newCrs = osr.SpatialReference()
|
||||
newCrs.ImportFromProj4(newCrsString)
|
||||
newCrs.MorphToESRI() # converts the WKT to an ESRI-compatible format
|
||||
|
||||
|
||||
validate = True if len(newCrs.ExportToWkt())>10 else False
|
||||
|
||||
if validate:
|
||||
newProjSR = arcpy.SpatialReference()
|
||||
newProjSR.loadFromString(newCrs.ExportToWkt())
|
||||
|
||||
#source = osr.SpatialReference()
|
||||
#source.ImportFromWkt(self.project.activeMap.spatialReference.exportToString())
|
||||
#transform = osr.CoordinateTransformation(source, newCrs)
|
||||
|
||||
self.project.activeMap.spatialReference = newProjSR
|
||||
arcpy.AddMessage("Custom project CRS successfully applied")
|
||||
else:
|
||||
arcpy.AddWarning("Custom CRS could not be created")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user