ansys-pyensight-core 0.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ansys/pyensight/core/__init__.py +41 -0
- ansys/pyensight/core/common.py +341 -0
- ansys/pyensight/core/deep_pixel_view.html +98 -0
- ansys/pyensight/core/dockerlauncher.py +1124 -0
- ansys/pyensight/core/dvs.py +872 -0
- ansys/pyensight/core/enscontext.py +345 -0
- ansys/pyensight/core/enshell_grpc.py +641 -0
- ansys/pyensight/core/ensight_grpc.py +874 -0
- ansys/pyensight/core/ensobj.py +515 -0
- ansys/pyensight/core/launch_ensight.py +296 -0
- ansys/pyensight/core/launcher.py +388 -0
- ansys/pyensight/core/libuserd.py +2110 -0
- ansys/pyensight/core/listobj.py +280 -0
- ansys/pyensight/core/locallauncher.py +579 -0
- ansys/pyensight/core/py.typed +0 -0
- ansys/pyensight/core/renderable.py +880 -0
- ansys/pyensight/core/session.py +1923 -0
- ansys/pyensight/core/sgeo_poll.html +24 -0
- ansys/pyensight/core/utils/__init__.py +21 -0
- ansys/pyensight/core/utils/adr.py +111 -0
- ansys/pyensight/core/utils/dsg_server.py +1220 -0
- ansys/pyensight/core/utils/export.py +606 -0
- ansys/pyensight/core/utils/omniverse.py +769 -0
- ansys/pyensight/core/utils/omniverse_cli.py +614 -0
- ansys/pyensight/core/utils/omniverse_dsg_server.py +1196 -0
- ansys/pyensight/core/utils/omniverse_glb_server.py +848 -0
- ansys/pyensight/core/utils/parts.py +1221 -0
- ansys/pyensight/core/utils/query.py +487 -0
- ansys/pyensight/core/utils/readers.py +300 -0
- ansys/pyensight/core/utils/resources/Materials/000_sky.exr +0 -0
- ansys/pyensight/core/utils/support.py +128 -0
- ansys/pyensight/core/utils/variables.py +2019 -0
- ansys/pyensight/core/utils/views.py +674 -0
- ansys_pyensight_core-0.11.0.dist-info/METADATA +309 -0
- ansys_pyensight_core-0.11.0.dist-info/RECORD +37 -0
- ansys_pyensight_core-0.11.0.dist-info/WHEEL +4 -0
- ansys_pyensight_core-0.11.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Copyright (C) 2022 - 2026 ANSYS, Inc. and/or its affiliates.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
# copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
# SOFTWARE.
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
import importlib.metadata as importlib_metadata # type: ignore
|
|
25
|
+
except ModuleNotFoundError: # pragma: no cover
|
|
26
|
+
import importlib_metadata # type: ignore
|
|
27
|
+
__version__ = importlib_metadata.version(__name__.replace(".", "-"))
|
|
28
|
+
|
|
29
|
+
VERSION = __version__
|
|
30
|
+
DEFAULT_ANSYS_VERSION = "251"
|
|
31
|
+
|
|
32
|
+
# Ansys version number that this release is associated with
|
|
33
|
+
__ansys_version__ = DEFAULT_ANSYS_VERSION
|
|
34
|
+
__ansys_version_str__ = f"{2000+(int(__ansys_version__) // 10)} R{int(__ansys_version__) % 10}"
|
|
35
|
+
|
|
36
|
+
from ansys.pyensight.core.dockerlauncher import DockerLauncher
|
|
37
|
+
from ansys.pyensight.core.launch_ensight import launch_ensight, launch_libuserd
|
|
38
|
+
from ansys.pyensight.core.launcher import Launcher
|
|
39
|
+
from ansys.pyensight.core.listobj import ensobjlist
|
|
40
|
+
from ansys.pyensight.core.locallauncher import LocalLauncher
|
|
41
|
+
from ansys.pyensight.core.session import Session
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# Copyright (C) 2022 - 2026 ANSYS, Inc. and/or its affiliates.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
# copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
# SOFTWARE.
|
|
22
|
+
|
|
23
|
+
""" This module provides a list of common utilities shared between different PyEnSight modules."""
|
|
24
|
+
|
|
25
|
+
import os
|
|
26
|
+
import random
|
|
27
|
+
import re
|
|
28
|
+
import socket
|
|
29
|
+
import time
|
|
30
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
|
|
31
|
+
|
|
32
|
+
from ansys.pyensight.core import enshell_grpc
|
|
33
|
+
import urllib3
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
from simple_upload_server.client import Client
|
|
37
|
+
|
|
38
|
+
simple_upload_server_is_available = True # pragma: no cover
|
|
39
|
+
except Exception:
|
|
40
|
+
simple_upload_server_is_available = False
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from docker import DockerClient
|
|
44
|
+
|
|
45
|
+
GRPC_VERSIONS = ["2025 R2.3", "2025 R1.4", "2024 R2.5"]
|
|
46
|
+
GRPC_WARNING_MESSAGE = "The EnSight version being used uses an insecure gRPC connection."
|
|
47
|
+
GRPC_WARNING_MESSAGE += "Consider upgrading to a version higher than 2025 R2.3, or "
|
|
48
|
+
GRPC_WARNING_MESSAGE += "2024 R2.5 or higher service packs, or 2025 R1.4 or higher service packs."
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def find_unused_ports(count: int, avoid: Optional[List[int]] = None) -> Optional[List[int]]:
|
|
52
|
+
"""Find "count" unused ports on the host system
|
|
53
|
+
|
|
54
|
+
A port is considered unused if it does not respond to a "connect" attempt. Walk
|
|
55
|
+
the ports from 'start' to 'end' looking for unused ports and avoiding any ports
|
|
56
|
+
in the 'avoid' list. Stop once the desired number of ports have been
|
|
57
|
+
found. If an insufficient number of ports were found, return None.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
count: int :
|
|
62
|
+
Number of unused ports to find
|
|
63
|
+
avoid: Optional[List[int]] :
|
|
64
|
+
An optional list of ports not to check
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
The detected ports or None on failure
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
if avoid is None:
|
|
72
|
+
avoid = []
|
|
73
|
+
ports = list()
|
|
74
|
+
|
|
75
|
+
# pick a starting port number
|
|
76
|
+
start = random.randint(1024, 64000)
|
|
77
|
+
# We will scan for 65530 ports unless end is specified
|
|
78
|
+
port_mod = 65530
|
|
79
|
+
end = start + port_mod - 1
|
|
80
|
+
# walk the "virtual" port range
|
|
81
|
+
for base_port in range(start, end + 1):
|
|
82
|
+
# Map to physical port range
|
|
83
|
+
# There have been some issues with 65534+ so we stop at 65530
|
|
84
|
+
port = base_port % port_mod
|
|
85
|
+
# port 0 is special
|
|
86
|
+
if port == 0: # pragma: no cover
|
|
87
|
+
continue # pragma: no cover
|
|
88
|
+
# avoid admin ports
|
|
89
|
+
if port < 1024: # pragma: no cover
|
|
90
|
+
continue # pragma: no cover
|
|
91
|
+
# are we supposed to skip this one?
|
|
92
|
+
if port in avoid: # pragma: no cover
|
|
93
|
+
continue # pragma: no cover
|
|
94
|
+
# is anyone listening?
|
|
95
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
96
|
+
result = sock.connect_ex(("127.0.0.1", port))
|
|
97
|
+
if result != 0:
|
|
98
|
+
ports.append(port)
|
|
99
|
+
else:
|
|
100
|
+
sock.close() # pragma: no cover
|
|
101
|
+
if len(ports) >= count:
|
|
102
|
+
return ports
|
|
103
|
+
# in case we failed...
|
|
104
|
+
if len(ports) < count: # pragma: no cover
|
|
105
|
+
return None # pragma: no cover
|
|
106
|
+
return ports # pragma: no cover
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def get_host_port(uri: str) -> Tuple[str, int]:
|
|
110
|
+
"""Get the host port for the input uri
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
|
|
115
|
+
uri: str
|
|
116
|
+
The Uri to inspect
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
(tuple):
|
|
121
|
+
A tuple containing the host and the port of the input uri
|
|
122
|
+
"""
|
|
123
|
+
parse_results = urllib3.util.parse_url(uri)
|
|
124
|
+
port = (
|
|
125
|
+
parse_results.port
|
|
126
|
+
if parse_results.port
|
|
127
|
+
else (443 if re.search(r"^https|wss$", parse_results.scheme) else None)
|
|
128
|
+
)
|
|
129
|
+
return (parse_results.host, port)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def get_file_service(pim_instance: Any) -> Optional[Any]: # pragma: no cover
|
|
133
|
+
"""Get the file service object for the input pim instance.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
|
|
138
|
+
pim_instance:
|
|
139
|
+
the PIM instance to get the service from.
|
|
140
|
+
|
|
141
|
+
Returns
|
|
142
|
+
-------
|
|
143
|
+
|
|
144
|
+
pim_file_service:
|
|
145
|
+
the PIM file service object
|
|
146
|
+
"""
|
|
147
|
+
if simple_upload_server_is_available is False:
|
|
148
|
+
return None
|
|
149
|
+
if pim_instance is None:
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
if "http-simple-upload-server" in pim_instance.services:
|
|
153
|
+
pim_file_service = Client(
|
|
154
|
+
token="token",
|
|
155
|
+
url=pim_instance.services["http-simple-upload-server"].uri,
|
|
156
|
+
headers=pim_instance.services["http-simple-upload-server"].headers,
|
|
157
|
+
)
|
|
158
|
+
return pim_file_service
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def populate_service_host_port( # pragma: no cover
|
|
163
|
+
pim_instance: Any, service_host_port: Dict[str, Tuple[str, int]], webui: bool = False
|
|
164
|
+
) -> Dict[str, Tuple[str, int]]:
|
|
165
|
+
"""Populate the service host port dictionary with the services available in the PIM instance.
|
|
166
|
+
|
|
167
|
+
Parameters
|
|
168
|
+
----------
|
|
169
|
+
pim_instance:
|
|
170
|
+
the PIM instance to get the servicea from.
|
|
171
|
+
service_host_port: dict
|
|
172
|
+
the dictionary to be updated with the services from the PIM instance
|
|
173
|
+
webui: bool
|
|
174
|
+
if True retrieve also the webUI service
|
|
175
|
+
|
|
176
|
+
Returns
|
|
177
|
+
-------
|
|
178
|
+
service_host_port: dict
|
|
179
|
+
the dictionary updated with the services from the PIM instance
|
|
180
|
+
"""
|
|
181
|
+
if not set(("grpc_private", "http", "ws")).issubset(pim_instance.services):
|
|
182
|
+
raise RuntimeError(
|
|
183
|
+
"If channel is specified, the PIM instance must have a list of length 3 "
|
|
184
|
+
+ "containing the appropriate service URIs. It does not."
|
|
185
|
+
)
|
|
186
|
+
service_host_port["grpc_private"] = get_host_port(pim_instance.services["grpc_private"].uri)
|
|
187
|
+
service_host_port["http"] = get_host_port(pim_instance.services["http"].uri)
|
|
188
|
+
service_host_port["ws"] = get_host_port(pim_instance.services["ws"].uri)
|
|
189
|
+
service_host_port["grpc"] = ("127.0.0.1", -1)
|
|
190
|
+
if webui:
|
|
191
|
+
service_host_port["webui"] = get_host_port(pim_instance.services["webui"].uri)
|
|
192
|
+
return service_host_port
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def launch_enshell_interface(
|
|
196
|
+
enshell_grpc_channel: Any,
|
|
197
|
+
grpc_port: int,
|
|
198
|
+
timeout: float,
|
|
199
|
+
secret_key: str,
|
|
200
|
+
grpc_use_tcp_sockets: bool = False,
|
|
201
|
+
grpc_allow_network_connections: bool = False,
|
|
202
|
+
grpc_disable_tls: bool = False,
|
|
203
|
+
grpc_uds_pathname: Optional[str] = None,
|
|
204
|
+
disable_grpc_options: bool = False,
|
|
205
|
+
) -> enshell_grpc.EnShellGRPC:
|
|
206
|
+
"""Launch the EnShell gRPC Interface.
|
|
207
|
+
|
|
208
|
+
Parameters
|
|
209
|
+
----------
|
|
210
|
+
enshell_grpc_channel:
|
|
211
|
+
An eventual gRPC channel already available, like in the PIM case
|
|
212
|
+
grpc_port: int
|
|
213
|
+
the gRPC port to connect to
|
|
214
|
+
timeout: float
|
|
215
|
+
a timeout to wait for the gRPC connection
|
|
216
|
+
grpc_use_tcp_sockets: bool
|
|
217
|
+
If using gRPC, and if True, then allow TCP Socket based connections
|
|
218
|
+
instead of only local connections.
|
|
219
|
+
grpc_allow_network_connections: bool
|
|
220
|
+
If using gRPC and using TCP Socket based connections, listen on all networks.
|
|
221
|
+
grpc_disable_tls: bool
|
|
222
|
+
If using gRPC and using TCP Socket based connections, disable TLS.
|
|
223
|
+
grpc_uds_pathname: bool
|
|
224
|
+
If using gRPC and using Unix Domain Socket based connections, explicitly
|
|
225
|
+
set the pathname to the shared UDS file instead of using the default.
|
|
226
|
+
disable_grpc_options: bool, optional
|
|
227
|
+
Whether to disable the gRPC options check, and allow to run older
|
|
228
|
+
versions of EnSight
|
|
229
|
+
|
|
230
|
+
Returns
|
|
231
|
+
-------
|
|
232
|
+
enshell: enshell_grpc.EnShellGRPC
|
|
233
|
+
the enshell gRPC interface
|
|
234
|
+
|
|
235
|
+
WARNING:
|
|
236
|
+
Overriding the default values for these options: grpc_use_tcp_sockets, grpc_allow_network_connections,
|
|
237
|
+
and grpc_disable_tls
|
|
238
|
+
can possibly permit control of this computer and any data which resides on it.
|
|
239
|
+
Modification of this configuration is not recommended. Please see the
|
|
240
|
+
documentation for your installed product for additional information.
|
|
241
|
+
"""
|
|
242
|
+
if enshell_grpc_channel: # pragma: no cover
|
|
243
|
+
enshell = enshell_grpc.EnShellGRPC(
|
|
244
|
+
grpc_use_tcp_sockets=grpc_use_tcp_sockets,
|
|
245
|
+
grpc_allow_network_connections=grpc_allow_network_connections,
|
|
246
|
+
grpc_disable_tls=grpc_disable_tls,
|
|
247
|
+
grpc_uds_pathname=grpc_uds_pathname,
|
|
248
|
+
disable_grpc_options=disable_grpc_options,
|
|
249
|
+
) # pragma: no cover
|
|
250
|
+
# If disable_grpc_options is True, it means that in the launcher
|
|
251
|
+
# the ensight version was checked and it hasn't got the new
|
|
252
|
+
# grpc options. This means that enshell doesn't accept a string
|
|
253
|
+
# as security token
|
|
254
|
+
if not disable_grpc_options:
|
|
255
|
+
enshell.security_token = secret_key
|
|
256
|
+
enshell.connect_existing_channel(enshell_grpc_channel) # pragma: no cover
|
|
257
|
+
else:
|
|
258
|
+
enshell = enshell_grpc.EnShellGRPC(
|
|
259
|
+
port=grpc_port,
|
|
260
|
+
grpc_use_tcp_sockets=grpc_use_tcp_sockets,
|
|
261
|
+
grpc_allow_network_connections=grpc_allow_network_connections,
|
|
262
|
+
grpc_disable_tls=grpc_disable_tls,
|
|
263
|
+
grpc_uds_pathname=grpc_uds_pathname,
|
|
264
|
+
disable_grpc_options=disable_grpc_options,
|
|
265
|
+
)
|
|
266
|
+
# If disable_grpc_options is True, it means that in the launcher
|
|
267
|
+
# the ensight version was checked and it hasn't got the new
|
|
268
|
+
# grpc options. This means that enshell doesn't accept a string
|
|
269
|
+
# as security token
|
|
270
|
+
if not disable_grpc_options:
|
|
271
|
+
enshell.security_token = secret_key
|
|
272
|
+
time_start = time.time()
|
|
273
|
+
while time.time() - time_start < timeout: # pragma: no cover
|
|
274
|
+
if enshell.is_connected():
|
|
275
|
+
break
|
|
276
|
+
try:
|
|
277
|
+
enshell.connect(timeout=timeout)
|
|
278
|
+
except OSError: # pragma: no cover
|
|
279
|
+
pass # pragma: no cover
|
|
280
|
+
return enshell
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def pull_image(docker_client: "DockerClient", image_name: str) -> None:
|
|
284
|
+
"""Pull the input docker image using the input Docker Client
|
|
285
|
+
|
|
286
|
+
Parameters
|
|
287
|
+
----------
|
|
288
|
+
docker_client: DockerClient
|
|
289
|
+
the current DockerClient to pull the image with
|
|
290
|
+
image_name: str
|
|
291
|
+
the image to pull
|
|
292
|
+
"""
|
|
293
|
+
try:
|
|
294
|
+
if docker_client is not None: # pragma: no cover
|
|
295
|
+
docker_client.images.pull(image_name)
|
|
296
|
+
except Exception:
|
|
297
|
+
raise RuntimeError(f"Can't pull Docker image: {image_name}")
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _is_within_directory(directory, target):
|
|
301
|
+
"""Check if target is inside of the input directory."""
|
|
302
|
+
abs_directory = os.path.abspath(directory)
|
|
303
|
+
abs_target = os.path.abspath(target)
|
|
304
|
+
return os.path.commonprefix([abs_directory, abs_target]) == abs_directory
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def safe_extract(tar, path=".", members=None, *, numeric_owner=False):
|
|
308
|
+
"""Utility to check tar extraction to avoid bandit check issue."""
|
|
309
|
+
for member in tar.getmembers():
|
|
310
|
+
member_path = os.path.join(path, member.name)
|
|
311
|
+
if not _is_within_directory(path, member_path):
|
|
312
|
+
raise Exception("Attempted Path Traversal in Tar File")
|
|
313
|
+
tar.extractall(path, members, numeric_owner=numeric_owner)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def grpc_version_check(internal_version, ensight_full_version):
|
|
317
|
+
"""Check if the gRPC security options apply to the EnSight install."""
|
|
318
|
+
if int(internal_version) < 242:
|
|
319
|
+
return False
|
|
320
|
+
# From 261 onward always available
|
|
321
|
+
if int(internal_version) >= 261:
|
|
322
|
+
return True
|
|
323
|
+
# Exactly matches one of the service pack that introduced the changes
|
|
324
|
+
if ensight_full_version in GRPC_VERSIONS:
|
|
325
|
+
return True
|
|
326
|
+
split_version = ensight_full_version.split(" ")
|
|
327
|
+
year = split_version[0]
|
|
328
|
+
# Not a service pack
|
|
329
|
+
if len(split_version[1].split(".")) == 1:
|
|
330
|
+
return False
|
|
331
|
+
r_version = split_version[1].split(".")[0]
|
|
332
|
+
dot_version = int(split_version[1].split(".")[1])
|
|
333
|
+
# Check service pack version
|
|
334
|
+
if year == "2024" and dot_version < 5:
|
|
335
|
+
return False
|
|
336
|
+
if year == "2025":
|
|
337
|
+
if r_version == "R1" and dot_version < 4:
|
|
338
|
+
return False
|
|
339
|
+
if r_version == "R2" and dot_version < 3:
|
|
340
|
+
return False
|
|
341
|
+
return True
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<link rel="stylesheet" type="text/css" href="/bootstrap.min.cssOPTIONAL_QUERY"/>
|
|
5
|
+
|
|
6
|
+
<script src='/jqueryJQUERY_VERSION.min.jsOPTIONAL_QUERY'></script>
|
|
7
|
+
<script src='/geotiff.jsOPTIONAL_QUERY'></script>
|
|
8
|
+
<script src='/geotiff_nexus.jsOPTIONAL_QUERY'></script>
|
|
9
|
+
<script src="/bootstrap.min.jsOPTIONAL_QUERY"></script>
|
|
10
|
+
|
|
11
|
+
<script>
|
|
12
|
+
function updatepick_ITEMID(e) {
|
|
13
|
+
let x = e.offsetX;
|
|
14
|
+
let y = e.offsetY;
|
|
15
|
+
let image_elem = document.getElementById('nexus_image_ITEMID');
|
|
16
|
+
const origWidth = image_elem.nexus_width;
|
|
17
|
+
const origHeight = image_elem.nexus_height;
|
|
18
|
+
x = (x * (origWidth / image_elem.width)) | 0;
|
|
19
|
+
y = (y * (origHeight / image_elem.height)) | 0;
|
|
20
|
+
x = x >= origWidth ? origWidth - 1 : x;
|
|
21
|
+
x = x < 0 ? 0 : x;
|
|
22
|
+
y = y >= origHeight ? origHeight - 1 : y;
|
|
23
|
+
y = y < 0 ? 0 : y;
|
|
24
|
+
let idx = y * origWidth + x;
|
|
25
|
+
|
|
26
|
+
let probeResult = "";
|
|
27
|
+
if ('nexus_pick_buffer' in image_elem) {
|
|
28
|
+
let partid = image_elem.nexus_pick_buffer[idx * 4] + 256 * image_elem.nexus_pick_buffer[idx * 4 + 1];
|
|
29
|
+
if (partid in image_elem.nexus_metadata) {
|
|
30
|
+
let partinfo = image_elem.nexus_metadata[partid];
|
|
31
|
+
probeResult += " Part: '" + partinfo.name + "'";
|
|
32
|
+
if ('variable' in partinfo) {
|
|
33
|
+
if ('nexus_var_buffer' in image_elem) {
|
|
34
|
+
let vardata = image_elem.nexus_var_buffer[idx];
|
|
35
|
+
let varinfo = image_elem.nexus_varinfo[partinfo.variable];
|
|
36
|
+
probeResult += " Var: '" + varinfo.name + "' " + vardata.toExponential(5);
|
|
37
|
+
if (varinfo.unit_label.length) {
|
|
38
|
+
probeResult += " [" + varinfo.unit_label + "]";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
probeResult += "Part: None";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let probeDisplay = $('#probe_display_ITEMID');
|
|
48
|
+
// dispose first so it doesn't use tooltips created previously
|
|
49
|
+
probeDisplay.tooltip('dispose');
|
|
50
|
+
probeDisplay.tooltip('show');
|
|
51
|
+
// display coordinates
|
|
52
|
+
$("#probe_xy_ITEMID").text(`${x}, ${y}`);
|
|
53
|
+
// display values
|
|
54
|
+
$("#probe_result_ITEMID").html(probeResult);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function clearpick_ITEMID() {
|
|
58
|
+
let probeDisplay = $('#probe_display_ITEMID');
|
|
59
|
+
// hide and dispose existing tooltips
|
|
60
|
+
probeDisplay.tooltip('hide');
|
|
61
|
+
probeDisplay.tooltip('dispose');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function tiff_image_ITEMID_loader() {
|
|
65
|
+
const response = await fetch("TIFF_URL");
|
|
66
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
67
|
+
const tiff_promise_ITEMID = GeoTIFF.fromArrayBuffer(arrayBuffer);
|
|
68
|
+
tiff_promise_ITEMID.then(nexus_image_load_tiff_image.bind(null, "nexus_image_ITEMID"), nexus_image_general_error);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
tiff_image_ITEMID_loader();
|
|
72
|
+
|
|
73
|
+
</script>
|
|
74
|
+
</head>
|
|
75
|
+
<body style="margin:0px;padding:0px;">
|
|
76
|
+
<div style="margin: 0 auto; display:flex; justify-content:center;">
|
|
77
|
+
<!-- tooltip parent for img --->
|
|
78
|
+
<div id="probe_display_ITEMID"
|
|
79
|
+
data-BS_PREFIXtoggle="tooltip"
|
|
80
|
+
data-BS_PREFIXplacement="bottom"
|
|
81
|
+
data-BS_PREFIXfallbackPlacement="['top', 'right']"
|
|
82
|
+
data-BS_PREFIXhtml="true"
|
|
83
|
+
data-BS_PREFIXcontainer="body"
|
|
84
|
+
data-BS_PREFIXboundary="viewport"
|
|
85
|
+
data-BS_PREFIXanimation="false"
|
|
86
|
+
data-BS_PREFIXtrigger="manual"
|
|
87
|
+
data-BS_PREFIXtitle='<span class="f-1r">
|
|
88
|
+
<span>X, Y : <span id="probe_xy_ITEMID">0, 0</span></span>
|
|
89
|
+
<br>
|
|
90
|
+
<span id="probe_result_ITEMID"></span>
|
|
91
|
+
</span>'
|
|
92
|
+
>
|
|
93
|
+
<img id="nexus_image_ITEMID" onmousemove="updatepick_ITEMID(event)"
|
|
94
|
+
onmouseout="clearpick_ITEMID()" class="img-fluid"><br>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
</body>
|
|
98
|
+
</html>
|