Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9cc56e6e97 | |||
| 3da41021b1 | |||
| 36fb66e72e | |||
| 8325724ab0 | |||
| 1c749f09c8 | |||
| 62a37aa7fb | |||
| 660b37384b | |||
| d525d58111 | |||
| 46aecdac41 | |||
| a4122673ec | |||
| b07ed87d03 | |||
| 6833aaef21 | |||
| eda0268524 | |||
| a29b0c3e73 | |||
| 9f63ef8ced | |||
| e94ca610b7 | |||
| 46a7b968e1 | |||
| 82fcfe6661 | |||
| 3e4af97056 | |||
| 99b397b648 | |||
| 1bcd4a67f5 | |||
| a7de3ee43e | |||
| 39b81f0a15 | |||
| 4bfcd9e6b7 | |||
| cc4a7f6d3a | |||
| d0a0d7efae | |||
| 805b148366 | |||
| 7740958608 | |||
| 63d2879709 | |||
| 396916bb7c | |||
| c7c7ef8b85 | |||
| 3389528861 | |||
| 8c691454e6 | |||
| 5ef776ada0 | |||
| e5077e1677 | |||
| 672c29671e | |||
| 0d2de03cd6 | |||
| 860bcc1086 | |||
| e11a1338a5 | |||
| 525955c058 | |||
| 3bcea48c32 | |||
| 6e48c4b8b0 | |||
| f7f499aa81 | |||
| fb933e0472 | |||
| 7f4dfe4272 | |||
| 92e016b004 | |||
| eb287480c9 | |||
| c84e81451d | |||
| 6ec6725947 | |||
| f814592ae1 | |||
| 4097aff759 | |||
| 8f845c95bb | |||
| e2bf4ee3b1 | |||
| 5b37680d73 | |||
| 661d0e9104 | |||
| 434f712cbf | |||
| ebfb49c4a1 | |||
| 8759fc9e09 | |||
| 4e43085ef2 | |||
| 079e8e5dda | |||
| f5e52de572 | |||
| 66d7bd69fb | |||
| 6b7112ccef | |||
| 6bf99294db | |||
| 8bbd50265e | |||
| dd2f3f3bb0 | |||
| a20daffc4b | |||
| 6a8b5df87c |
+1
-1
@@ -530,7 +530,7 @@ paths:
|
|||||||
- hello-world
|
- hello-world
|
||||||
servers:
|
servers:
|
||||||
- description: pygeoapi provides an API to geospatial data
|
- description: pygeoapi provides an API to geospatial data
|
||||||
url: http://localhost:5000
|
url: https://geo.speckle.systems
|
||||||
tags:
|
tags:
|
||||||
- description: pygeoapi provides an API to geospatial data
|
- description: pygeoapi provides an API to geospatial data
|
||||||
externalDocs:
|
externalDocs:
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
access_log
|
access_log
|
||||||
error_log
|
error_log*
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ Then you can add it to the base map (e.g. using Leaflet and OpenStreetMap basema
|
|||||||
loadSpeckleData();
|
loadSpeckleData();
|
||||||
|
|
||||||
async function loadSpeckleData() => {
|
async function loadSpeckleData() => {
|
||||||
var speckle_model_url = 'https://geo.speckle.systems/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=polygons';
|
var speckle_model_url = 'https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=polygons';
|
||||||
const speckle_data = await fetch(speckle_model_url, {
|
const speckle_data = await fetch(speckle_model_url, {
|
||||||
headers: {'Accept': 'application/geo+json'}
|
headers: {'Accept': 'application/geo+json'}
|
||||||
}).then(response => response.json());
|
}).then(response => response.json());
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ LOGGER = logging.getLogger(__name__)
|
|||||||
#: Return headers for requests (e.g:X-Powered-By)
|
#: Return headers for requests (e.g:X-Powered-By)
|
||||||
HEADERS = {
|
HEADERS = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-Powered-By': f'pygeoapi {__version__}'
|
# 'X-Powered-By': f'pygeoapi {__version__}'
|
||||||
}
|
}
|
||||||
|
|
||||||
CHARSET = ['utf-8']
|
CHARSET = ['utf-8']
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ def get_collection_items(
|
|||||||
# clear data if no URL params
|
# clear data if no URL params
|
||||||
load_data = False
|
load_data = False
|
||||||
for item in request.params:
|
for item in request.params:
|
||||||
if item == 'speckleUrl' and len(request.params[item])>40 and ('speckleUrl=' + request.params[item]) in provider_def['data']:
|
if item.lower() == 'speckleurl' and len(request.params[item])>40 and ('speckleurl=' + request.params[item]) in provider_def['data'].lower():
|
||||||
load_data = True
|
load_data = True
|
||||||
break
|
break
|
||||||
if load_data is False:
|
if load_data is False:
|
||||||
@@ -574,61 +574,6 @@ def get_collection_items(
|
|||||||
if isinstance(url_saved_as_data, str):
|
if isinstance(url_saved_as_data, str):
|
||||||
url_props = url_saved_as_data.lower().split("&")
|
url_props = url_saved_as_data.lower().split("&")
|
||||||
|
|
||||||
content['missing_url'] = content['missing_url_href'] = ""
|
|
||||||
content['requested_data_type'] = "polygons (default)"
|
|
||||||
content['preserve_attributes'] = "false (default)"
|
|
||||||
content['speckle_url'] = content['speckle_project_url'] = content['crs_authid'] = content['lat'] = content['lon'] = content['north_degrees'] = content['limit'] = "-"
|
|
||||||
crsauthid = False
|
|
||||||
for item in url_props:
|
|
||||||
# if CRS authid is found, rest will be ignored
|
|
||||||
if "speckleurl=" in item:
|
|
||||||
content['speckle_url'] = item.split("speckleurl=")[1]
|
|
||||||
if content['speckle_url'][-1] == "/":
|
|
||||||
content['speckle_url'] = content['speckle_url'][:-1]
|
|
||||||
content['speckle_project_url'] = content['speckle_url'].split("/models")[0]
|
|
||||||
elif "datatype=" in item:
|
|
||||||
content['requested_data_type'] = item.split("datatype=")[1]
|
|
||||||
if content['requested_data_type'] not in ["points", "lines", "polygons", "projectcomments"]:
|
|
||||||
content['requested_data_type'] = "polygons (default)"
|
|
||||||
elif "preserveattributes=" in item:
|
|
||||||
content['preserve_attributes'] = item.split("preserveattributes=")[1]
|
|
||||||
if content['preserve_attributes'] not in ["true", "false"]:
|
|
||||||
content['preserve_attributes'] = "false (default)"
|
|
||||||
elif "crsauthid=" in item:
|
|
||||||
content['crs_authid'] = item.split("crsauthid=")[1]
|
|
||||||
crsauthid = True
|
|
||||||
elif "lat=" in item:
|
|
||||||
try:
|
|
||||||
content['lat'] = float(item.split("lat=")[1])
|
|
||||||
except:
|
|
||||||
content['lat'] = f"Invalid input, must be numeric: {item.split('lat=')[1]}"
|
|
||||||
elif "lon=" in item:
|
|
||||||
try:
|
|
||||||
content['lon'] = float(item.split("lon=")[1])
|
|
||||||
except:
|
|
||||||
content['lon'] = f"Invalid input, must be numeric: {item.split('lon=')[1]}"
|
|
||||||
elif "northdegrees=" in item:
|
|
||||||
try:
|
|
||||||
content['north_degrees'] = float(item.split("northdegrees=")[1])
|
|
||||||
except:
|
|
||||||
content['north_degrees'] = f"Invalid input, must be numeric: {item.split('northdegrees=')[1]}"
|
|
||||||
elif "limit=" in item:
|
|
||||||
try:
|
|
||||||
content['limit'] = float(item.split("limit=")[1])
|
|
||||||
except:
|
|
||||||
content['limit'] = f"Invalid input, must be integer: {item.split('limit=')[1]}"
|
|
||||||
|
|
||||||
if content['speckle_url'] == "-":
|
|
||||||
content['missing_url'] = "true"
|
|
||||||
|
|
||||||
if crsauthid:
|
|
||||||
content['lat'] += " (not applied)"
|
|
||||||
content['lon'] += " (not applied)"
|
|
||||||
content['north_degrees'] += " (not applied)"
|
|
||||||
|
|
||||||
if content['limit'] == "-":
|
|
||||||
content['limit'] = f"{api.config['server']['limit']} (default)"
|
|
||||||
|
|
||||||
# Set response language to requested provider locale
|
# Set response language to requested provider locale
|
||||||
# (if it supports language) and/or otherwise the requested pygeoapi
|
# (if it supports language) and/or otherwise the requested pygeoapi
|
||||||
# locale (or fallback default locale)
|
# locale (or fallback default locale)
|
||||||
|
|||||||
+81
-24
@@ -34,10 +34,14 @@ import os
|
|||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
from datetime import datetime, timezone
|
||||||
from flask import (Flask, Blueprint, make_response, request,
|
from flask import (Flask, Blueprint, make_response, request,
|
||||||
send_from_directory, Response, Request, stream_with_context)
|
send_from_directory, Response, Request, stream_with_context)
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
|
|
||||||
|
import json
|
||||||
|
from urllib.request import urlopen
|
||||||
|
|
||||||
from pygeoapi.api import API, APIRequest, apply_gzip
|
from pygeoapi.api import API, APIRequest, apply_gzip
|
||||||
import pygeoapi.api.coverages as coverages_api
|
import pygeoapi.api.coverages as coverages_api
|
||||||
import pygeoapi.api.environmental_data_retrieval as edr_api
|
import pygeoapi.api.environmental_data_retrieval as edr_api
|
||||||
@@ -46,6 +50,7 @@ import pygeoapi.api.maps as maps_api
|
|||||||
import pygeoapi.api.processes as processes_api
|
import pygeoapi.api.processes as processes_api
|
||||||
import pygeoapi.api.stac as stac_api
|
import pygeoapi.api.stac as stac_api
|
||||||
import pygeoapi.api.tiles as tiles_api
|
import pygeoapi.api.tiles as tiles_api
|
||||||
|
from pygeoapi.provider.speckle_utils.legal import COUNTRY_CODES
|
||||||
from pygeoapi.openapi import load_openapi_document
|
from pygeoapi.openapi import load_openapi_document
|
||||||
from pygeoapi.config import get_config
|
from pygeoapi.config import get_config
|
||||||
from pygeoapi.util import get_mimetype, get_api_rules, render_j2_template
|
from pygeoapi.util import get_mimetype, get_api_rules, render_j2_template
|
||||||
@@ -168,6 +173,62 @@ def execute_from_flask(api_function, request: Request, *args,
|
|||||||
return get_response((headers, status, content))
|
return get_response((headers, status, content))
|
||||||
|
|
||||||
|
|
||||||
|
def handle_client(url_route: str):
|
||||||
|
|
||||||
|
# if called fromm the browser, Exceptions from this function will result in infinite load
|
||||||
|
agent = request.headers.get('User-Agent')
|
||||||
|
if request.environ.get('HTTP_X_FORWARDED_FOR') is None:
|
||||||
|
ip_address = request.environ['REMOTE_ADDR']
|
||||||
|
else:
|
||||||
|
ip_address = request.environ['HTTP_X_FORWARDED_FOR']
|
||||||
|
|
||||||
|
if agent is not None and "(https://www.checklyhq.com)" not in agent:
|
||||||
|
print(f"_______________________{datetime.now().astimezone(timezone.utc)} _URL access")
|
||||||
|
print(f"_Agent {url_route}: {agent}")
|
||||||
|
print(f"_IP Address: {ip_address}")
|
||||||
|
print(f"_Request URL: {request.url}")
|
||||||
|
|
||||||
|
request.url += f"&userAgent={agent}"
|
||||||
|
|
||||||
|
# by Agent:
|
||||||
|
if agent is not None and ("YaBrowser/" in agent or "yandex" in agent.lower()):
|
||||||
|
raise ValueError("Your browser is not supported.")
|
||||||
|
|
||||||
|
# by IP:
|
||||||
|
try:
|
||||||
|
url = 'https://ipinfo.io/' + ip_address + '/json'
|
||||||
|
res = urlopen(url)
|
||||||
|
data = json.load(res)
|
||||||
|
if isinstance(data, dict) and isinstance(data["country"], str):
|
||||||
|
if data["country"].lower() in COUNTRY_CODES:
|
||||||
|
raise PermissionError("Review Speckle Terms and Conditions")
|
||||||
|
else:
|
||||||
|
print(f"Error validating client: DATA {data}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error validating client from start: {e}")
|
||||||
|
|
||||||
|
def generate():
|
||||||
|
collection_id = "speckle"
|
||||||
|
|
||||||
|
yield loading_screen().data
|
||||||
|
|
||||||
|
handle_client("/")
|
||||||
|
CONFIG = get_config(request=request)
|
||||||
|
api_ = API(CONFIG, OPENAPI)
|
||||||
|
|
||||||
|
try:
|
||||||
|
browser_response = execute_from_flask(itemtypes_api.get_collection_items,
|
||||||
|
request, collection_id,
|
||||||
|
skip_valid_check=True)
|
||||||
|
yield browser_response.data
|
||||||
|
|
||||||
|
except PermissionError as ex:
|
||||||
|
raise ex
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
yield error_screen(ex).data
|
||||||
|
|
||||||
|
|
||||||
@BLUEPRINT.route('/')
|
@BLUEPRINT.route('/')
|
||||||
def landing_page():
|
def landing_page():
|
||||||
"""
|
"""
|
||||||
@@ -175,41 +236,24 @@ def landing_page():
|
|||||||
|
|
||||||
:returns: HTTP response
|
:returns: HTTP response
|
||||||
"""
|
"""
|
||||||
|
|
||||||
collection_id = "speckle"
|
|
||||||
|
|
||||||
agent = request.headers.get('User-Agent')
|
agent = request.headers.get('User-Agent')
|
||||||
# Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
|
|
||||||
# Mozilla/5.0 QGIS/32815/Windows 10 Version 2009
|
|
||||||
# ArcGIS Pro 3.3.0 (00000000000) - ArcGISPro
|
|
||||||
|
|
||||||
browser_agent = False
|
browser_agent = False
|
||||||
browser_list = ["Chrome", "Safari", "Firefox", "Edg/", "Trident/"]
|
browser_list = ["Chrome", "Safari", "Firefox", "Edg/", "Trident/"]
|
||||||
print(agent)
|
|
||||||
|
|
||||||
if "YaBrowser/" in agent:
|
|
||||||
raise ValueError("Your browser is not supported.")
|
|
||||||
for br in browser_list:
|
for br in browser_list:
|
||||||
if br in agent:
|
if agent is not None and br in agent:
|
||||||
browser_agent = True
|
browser_agent = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
# if requested from the browser, return this, otherwise ignore IF statement
|
# if requested from the browser, return this, otherwise ignore IF statement
|
||||||
if request.method == 'GET' and browser_agent: # list items
|
if request.method == 'GET' and browser_agent: # list items
|
||||||
|
|
||||||
def generate():
|
|
||||||
yield loading_screen().data
|
|
||||||
try:
|
|
||||||
browser_response = execute_from_flask(itemtypes_api.get_collection_items,
|
|
||||||
request, collection_id,
|
|
||||||
skip_valid_check=True)
|
|
||||||
yield browser_response.data
|
|
||||||
|
|
||||||
except Exception as ex:
|
|
||||||
yield error_screen(ex).data
|
|
||||||
|
|
||||||
return Response(stream_with_context(generate()))
|
return Response(stream_with_context(generate()))
|
||||||
|
|
||||||
|
# for non-browsers
|
||||||
|
handle_client("/")
|
||||||
|
CONFIG = get_config(request=request)
|
||||||
|
api_ = API(CONFIG, OPENAPI)
|
||||||
return get_response(api_.landing_page(request))
|
return get_response(api_.landing_page(request))
|
||||||
|
|
||||||
def error_screen(ex: Exception):
|
def error_screen(ex: Exception):
|
||||||
@@ -294,10 +338,20 @@ def collections(collection_id=None):
|
|||||||
:returns: HTTP response
|
:returns: HTTP response
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# raise NotImplementedError()
|
handle_client("/collections")
|
||||||
return get_response(api_.describe_collections(request, collection_id))
|
return get_response(api_.describe_collections(request, collection_id))
|
||||||
|
|
||||||
|
|
||||||
|
@BLUEPRINT.route('/speckle')
|
||||||
|
def speckle_collection():
|
||||||
|
|
||||||
|
handle_client("/speckle")
|
||||||
|
|
||||||
|
collection_id="speckle"
|
||||||
|
|
||||||
|
return collection_items(collection_id=collection_id)
|
||||||
|
|
||||||
|
|
||||||
@BLUEPRINT.route('/collections/<path:collection_id>/schema')
|
@BLUEPRINT.route('/collections/<path:collection_id>/schema')
|
||||||
def collection_schema(collection_id):
|
def collection_schema(collection_id):
|
||||||
"""
|
"""
|
||||||
@@ -343,6 +397,9 @@ def collection_items(collection_id, item_id=None):
|
|||||||
|
|
||||||
:returns: HTTP response
|
:returns: HTTP response
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
handle_client(f"/collections/{collection_id}/items")
|
||||||
|
|
||||||
collection_id = 'speckle'
|
collection_id = 'speckle'
|
||||||
|
|
||||||
if item_id is None:
|
if item_id is None:
|
||||||
|
|||||||
@@ -119,21 +119,33 @@ class SpeckleProvider(BaseProvider):
|
|||||||
self.speckle_url = self.url.lower().split("speckleurl=")[-1].split("&")[0].split("@")[0].split("?")[0]
|
self.speckle_url = self.url.lower().split("speckleurl=")[-1].split("&")[0].split("@")[0].split("?")[0]
|
||||||
|
|
||||||
self.speckle_data = None
|
self.speckle_data = None
|
||||||
|
self.project_name = ""
|
||||||
|
self.project_id = ""
|
||||||
self.model_name = ""
|
self.model_name = ""
|
||||||
|
self.sourceApp = ""
|
||||||
|
|
||||||
self.crs = None
|
self.crs = None
|
||||||
self.crs_dict = None
|
self.crs_dict = None
|
||||||
|
|
||||||
self.requested_data_type: str = "polygons (default)" # points, lines, polygons, projectcomments
|
self.commit_gis = False
|
||||||
self.preserve_attributes: str = "false (default)"
|
self.url_params = {"url_data_type":"", "url_preserve_attributes":"", "url_crs_authid":"", "url_lat":"","url_lon":"","url_north_degrees":"","url_limit":""}
|
||||||
|
self.times = {}
|
||||||
|
self.country_code = ""
|
||||||
|
|
||||||
|
self.requested_data_type: str = "polygons (default)" # points, lines, polygons, projectcomments
|
||||||
|
self.preserve_attributes: str = "true (default)"
|
||||||
self.lat: float = 48.76755913928929 #51.52486388756923
|
self.lat: float = 48.76755913928929 #51.52486388756923
|
||||||
self.lon: float = 11.408741923664028 #0.1621445437168942
|
self.lon: float = 11.408741923664028 #0.1621445437168942
|
||||||
self.north_degrees: float = 0
|
self.north_degrees: float = 0
|
||||||
self.extent = [-180,-90,180,90]
|
self.crs_authid = ""
|
||||||
self.limit = 10000
|
self.limit = 10000
|
||||||
|
self.user_agent = ""
|
||||||
|
|
||||||
|
self.missing_url = ""
|
||||||
self.limit_message = ""
|
self.limit_message = ""
|
||||||
|
|
||||||
|
self.extent = [-180,-90,180,90]
|
||||||
|
self.extent3d = [-180,-90,0,180,90,1000]
|
||||||
self.material_color_proxies = {}
|
self.material_color_proxies = {}
|
||||||
|
|
||||||
|
|
||||||
@@ -173,7 +185,7 @@ class SpeckleProvider(BaseProvider):
|
|||||||
if self.data == "":
|
if self.data == "":
|
||||||
return
|
return
|
||||||
|
|
||||||
get_set_url_parameters(self)
|
get_set_url_parameters(self) # possible ValueError
|
||||||
|
|
||||||
# check if it's a new request (self.data was updated and doesn't match self.url)
|
# check if it's a new request (self.data was updated and doesn't match self.url)
|
||||||
new_request = False
|
new_request = False
|
||||||
@@ -266,6 +278,18 @@ class SpeckleProvider(BaseProvider):
|
|||||||
if data is None:
|
if data is None:
|
||||||
return {"features":[], "comments":[], "extent": [-180,-90,180,90]}
|
return {"features":[], "comments":[], "extent": [-180,-90,180,90]}
|
||||||
|
|
||||||
|
# add URL parameters
|
||||||
|
data['speckle_url'] = self.speckle_url
|
||||||
|
data['requested_data_type'] = self.requested_data_type
|
||||||
|
data['preserve_attributes'] = self.preserve_attributes
|
||||||
|
data['crs_authid'] = self.crs_authid
|
||||||
|
data['lat'] = self.lat
|
||||||
|
data['lon'] = self.lon
|
||||||
|
data['north_degrees'] = self.north_degrees
|
||||||
|
data['limit'] = self.limit
|
||||||
|
data['missing_url'] = self.missing_url
|
||||||
|
|
||||||
|
|
||||||
data["numberMatched"] = len(data["features"])
|
data["numberMatched"] = len(data["features"])
|
||||||
|
|
||||||
if resulttype == "hits":
|
if resulttype == "hits":
|
||||||
@@ -324,7 +348,8 @@ class SpeckleProvider(BaseProvider):
|
|||||||
def load_speckle_data(self: str) -> Dict:
|
def load_speckle_data(self: str) -> Dict:
|
||||||
"""Receive and process Speckle data, return geojson."""
|
"""Receive and process Speckle data, return geojson."""
|
||||||
|
|
||||||
from pygeoapi.provider.speckle_utils.server_utils import get_stream_branch, get_client, get_comments
|
from datetime import datetime, timezone
|
||||||
|
from pygeoapi.provider.speckle_utils.server_utils import get_stream_branch, get_client, get_comments, set_actions
|
||||||
|
|
||||||
from specklepy.objects.base import Base
|
from specklepy.objects.base import Base
|
||||||
from specklepy.logging.exceptions import SpeckleException
|
from specklepy.logging.exceptions import SpeckleException
|
||||||
@@ -346,6 +371,10 @@ class SpeckleProvider(BaseProvider):
|
|||||||
# get stream and branch data
|
# get stream and branch data
|
||||||
client = get_client(wrapper, url_proj)
|
client = get_client(wrapper, url_proj)
|
||||||
stream, branch = get_stream_branch(self, client, wrapper)
|
stream, branch = get_stream_branch(self, client, wrapper)
|
||||||
|
if stream is None:
|
||||||
|
raise ValueError(f"Project from URL '{url_proj}' not found")
|
||||||
|
if branch is None:
|
||||||
|
raise ValueError(f"Model '{wrapper.model_id}' of the project '{stream['name']}' not found")
|
||||||
|
|
||||||
if self.requested_data_type == "projectcomments":
|
if self.requested_data_type == "projectcomments":
|
||||||
comments = get_comments(client, wrapper.stream_id, wrapper.model_id)
|
comments = get_comments(client, wrapper.stream_id, wrapper.model_id)
|
||||||
@@ -354,16 +383,20 @@ class SpeckleProvider(BaseProvider):
|
|||||||
comments = {}
|
comments = {}
|
||||||
|
|
||||||
# set the Model name
|
# set the Model name
|
||||||
|
self.project_id = wrapper.stream_id
|
||||||
|
self.project_name = stream['name']
|
||||||
self.model_name = branch['name']
|
self.model_name = branch['name']
|
||||||
|
|
||||||
commit = branch["commits"]["items"][0]
|
commit = branch["commits"]["items"][0]
|
||||||
objId = commit["referencedObject"]
|
objId = commit["referencedObject"]
|
||||||
|
self.sourceApp = commit["sourceApplication"]
|
||||||
|
|
||||||
transport = ServerTransport(client=client, account=client.account, stream_id=wrapper.stream_id)
|
transport = ServerTransport(client=client, account=client.account, stream_id=wrapper.stream_id)
|
||||||
if transport == None:
|
if transport == None:
|
||||||
raise SpeckleException("Transport not found")
|
raise SpeckleException("Transport not found")
|
||||||
|
|
||||||
# receive commit
|
# receive commit
|
||||||
|
set_actions(self, client)
|
||||||
try:
|
try:
|
||||||
commit_obj = operations.receive(objId, transport, None)
|
commit_obj = operations.receive(objId, transport, None)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@@ -377,18 +410,21 @@ class SpeckleProvider(BaseProvider):
|
|||||||
message="Received commit in pygeoapi",
|
message="Received commit in pygeoapi",
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"Rendering model '{branch['name']}' of the project '{stream['name']}'")
|
print(f"_{datetime.now().astimezone(timezone.utc)} _Rendering model '{branch['name']}' of the project '{stream['name']}'")
|
||||||
speckle_data = self.traverse_data(commit_obj, comments)
|
speckle_data = self.traverse_data(commit_obj, comments)
|
||||||
|
|
||||||
|
set_actions(self, client, "GEO post-receive")
|
||||||
|
|
||||||
speckle_data["features"].extend(speckle_data["comments"])
|
speckle_data["features"].extend(speckle_data["comments"])
|
||||||
speckle_data["comments"] = []
|
speckle_data["comments"] = []
|
||||||
|
|
||||||
|
speckle_data["project_id"] = wrapper.stream_id
|
||||||
speckle_data["project"] = stream['name']
|
speckle_data["project"] = stream['name']
|
||||||
speckle_data["model"] = branch['name']
|
speckle_data["model"] = branch['name']
|
||||||
speckle_data["model_last_version_date"] = datetime.strptime(commit['createdAt'].replace("T", " ").replace("Z","").split(".")[0], '%Y-%m-%d %H:%M:%S')
|
speckle_data["model_last_version_date"] = datetime.strptime(commit['createdAt'].replace("T", " ").replace("Z","").split(".")[0], '%Y-%m-%d %H:%M:%S')
|
||||||
speckle_data["model_id"] = wrapper.model_id
|
speckle_data["model_id"] = wrapper.model_id
|
||||||
speckle_data["extent"] = self.extent
|
speckle_data["extent"] = self.extent
|
||||||
|
speckle_data["extent3d"] = self.extent3d
|
||||||
speckle_data["limit_message"] = self.limit_message
|
speckle_data["limit_message"] = self.limit_message
|
||||||
|
|
||||||
return speckle_data
|
return speckle_data
|
||||||
@@ -406,7 +442,7 @@ class SpeckleProvider(BaseProvider):
|
|||||||
)
|
)
|
||||||
from pygeoapi.provider.speckle_utils.crs_utils import get_set_crs_settings
|
from pygeoapi.provider.speckle_utils.crs_utils import get_set_crs_settings
|
||||||
from pygeoapi.provider.speckle_utils.feature_utils import create_features
|
from pygeoapi.provider.speckle_utils.feature_utils import create_features
|
||||||
from pygeoapi.provider.speckle_utils.display_utils import set_default_color, get_material_color_proxies
|
from pygeoapi.provider.speckle_utils.display_utils import isDisplayable, set_default_color, get_material_color_proxies
|
||||||
|
|
||||||
supported_classes = [GisFeature, GisPolygonElement, Mesh, Brep, Point, Line, Polyline, Curve, Arc, Circle, Ellipse, Polycurve]
|
supported_classes = [GisFeature, GisPolygonElement, Mesh, Brep, Point, Line, Polyline, Curve, Arc, Circle, Ellipse, Polycurve]
|
||||||
supported_types = [y().speckle_type for y in supported_classes]
|
supported_types = [y().speckle_type for y in supported_classes]
|
||||||
@@ -428,16 +464,22 @@ class SpeckleProvider(BaseProvider):
|
|||||||
"extent": [-180,-90,180,90],
|
"extent": [-180,-90,180,90],
|
||||||
"model_crs": "-",
|
"model_crs": "-",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# rule to keep traversing the object's "x" attribute "item" (both conditions need to be fulfilled)
|
||||||
|
# 1. if the item type is not in supported (convertible) types or is GIS VectorLayer
|
||||||
|
# 2. if the item's value is a list or a GH object
|
||||||
rule = TraversalRule(
|
rule = TraversalRule(
|
||||||
[lambda _: True],
|
[lambda _: True],
|
||||||
lambda x: [
|
lambda x: [
|
||||||
item
|
item
|
||||||
for item in x.get_member_names()
|
for item in x.get_member_names()
|
||||||
if isinstance(getattr(x, item, None), list)
|
if (x.speckle_type.split(":")[-1] not in supported_types or isinstance(x, VectorLayer))
|
||||||
and (x.speckle_type.split(":")[-1] not in supported_types or isinstance(x, VectorLayer))
|
and (isinstance(getattr(x, item, None), list) or (self.sourceApp is not None and "grasshopper" in self.sourceApp.lower() and x.speckle_type == "Base") )
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
context_list = [x for x in GraphTraversal([rule]).traverse(commit_obj)]
|
|
||||||
|
# for the context list, save the displayable objects and Layers (for getting CRS for now)
|
||||||
|
context_list = [x for x in GraphTraversal([rule]).traverse(commit_obj) if isDisplayable(x.current) or x.current.speckle_type.endswith("VectorLayer")]
|
||||||
|
|
||||||
get_set_crs_settings(self, commit_obj, context_list, data)
|
get_set_crs_settings(self, commit_obj, context_list, data)
|
||||||
|
|
||||||
@@ -459,7 +501,10 @@ class SpeckleProvider(BaseProvider):
|
|||||||
sorted_list[i]["properties"]["FID"] = i+1
|
sorted_list[i]["properties"]["FID"] = i+1
|
||||||
data['features'] = sorted_list
|
data['features'] = sorted_list
|
||||||
time2 = datetime.now()
|
time2 = datetime.now()
|
||||||
print(f"Sorting time: {(time2-time1).total_seconds()}")
|
|
||||||
|
time_operation = (time2-time1).total_seconds()
|
||||||
|
self.times["time_sort"] = time_operation
|
||||||
|
# print(f"Sorting time: {time_operation}")
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|||||||
@@ -296,8 +296,24 @@ def assign_geometry(self: "SpeckleProvider", feature: Dict, f_base) -> Tuple[ Li
|
|||||||
geometry["type"] = "MultiPolygon"
|
geometry["type"] = "MultiPolygon"
|
||||||
coord_counts.append(None)
|
coord_counts.append(None)
|
||||||
|
|
||||||
for geom in f_base["geometry"]:
|
polygon_3d = False
|
||||||
convert_polygon(geom, coords, coord_counts)
|
|
||||||
|
for mesh in f_base["displayValue"]:
|
||||||
|
for i, coord in enumerate(mesh.vertices):
|
||||||
|
if i>60:
|
||||||
|
break
|
||||||
|
if i%3 !=0:
|
||||||
|
continue
|
||||||
|
elif coord != 0:
|
||||||
|
polygon_3d = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if polygon_3d is False:
|
||||||
|
for geom in f_base["geometry"]:
|
||||||
|
convert_polygon(geom, coords, coord_counts)
|
||||||
|
else:
|
||||||
|
for geom in f_base["displayValue"]:
|
||||||
|
convert_mesh_or_brep(geom, coords, coord_counts)
|
||||||
|
|
||||||
|
|
||||||
elif self.requested_data_type == "points":
|
elif self.requested_data_type == "points":
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import copy
|
import copy
|
||||||
import math
|
import math
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from pygeoapi.provider.speckle_utils.legal import COUNTRY_CODES, STATES, POSTCODES
|
||||||
|
|
||||||
|
|
||||||
def reproject_bulk(self, all_coords: List[List[List[float]]], all_coord_counts: List[List[None| List[int]]], geometries) -> None:
|
def reproject_bulk(self, all_coords: List[List[List[float]]], all_coord_counts: List[List[None| List[int]]], geometries) -> None:
|
||||||
@@ -13,7 +14,12 @@ def reproject_bulk(self, all_coords: List[List[List[float]]], all_coord_counts:
|
|||||||
time1 = datetime.now()
|
time1 = datetime.now()
|
||||||
flat_coords = reproject_2d_coords_list(self, all_coords)
|
flat_coords = reproject_2d_coords_list(self, all_coords)
|
||||||
time2 = datetime.now()
|
time2 = datetime.now()
|
||||||
print(f"Reproject time: {(time2-time1).total_seconds()}")
|
time_operation = (time2-time1).total_seconds()
|
||||||
|
self.times["time_reproject"] = time_operation
|
||||||
|
|
||||||
|
validate_coords(self, flat_coords[0])
|
||||||
|
if len(flat_coords)>2:
|
||||||
|
validate_coords(self, flat_coords[len(flat_coords)-1])
|
||||||
|
|
||||||
# define type of features
|
# define type of features
|
||||||
feat_coord_group_is_multi = [True if None in x else False for x in all_coord_counts]
|
feat_coord_group_is_multi = [True if None in x else False for x in all_coord_counts]
|
||||||
@@ -49,7 +55,15 @@ def reproject_bulk(self, all_coords: List[List[List[float]]], all_coord_counts:
|
|||||||
if geometry["type"] == "MultiPoint":
|
if geometry["type"] == "MultiPoint":
|
||||||
poly_part.extend([local_flat_coords[ind] for ind in range_coords_indices])
|
poly_part.extend([local_flat_coords[ind] for ind in range_coords_indices])
|
||||||
else:
|
else:
|
||||||
poly_part.append([local_flat_coords[ind] for ind in range_coords_indices])
|
new_list = []
|
||||||
|
for ind in range_coords_indices:
|
||||||
|
try:
|
||||||
|
new_list.append(local_flat_coords[ind])
|
||||||
|
except Exception as e: # corrupted geometry, ignore altogether
|
||||||
|
new_list = []
|
||||||
|
break
|
||||||
|
if len(new_list)>0:
|
||||||
|
poly_part.append(new_list)
|
||||||
|
|
||||||
start_index += part_count
|
start_index += part_count
|
||||||
|
|
||||||
@@ -61,7 +75,10 @@ def reproject_bulk(self, all_coords: List[List[List[float]]], all_coord_counts:
|
|||||||
geometry["coordinates"].extend(polygon_parts)
|
geometry["coordinates"].extend(polygon_parts)
|
||||||
|
|
||||||
time3 = datetime.now()
|
time3 = datetime.now()
|
||||||
print(f"Construct back geometry time: {(time3-time2).total_seconds()}")
|
|
||||||
|
time_operation = (time3-time2).total_seconds()
|
||||||
|
self.times["time_reconstruct_geometry"] = time_operation
|
||||||
|
# print(f"Construct back geometry time: {time_operation}")
|
||||||
|
|
||||||
def reproject_2d_coords_list(self, coords_in: List[List[float]]) -> List[List[float]]:
|
def reproject_2d_coords_list(self, coords_in: List[List[float]]) -> List[List[float]]:
|
||||||
"""Return coordinates in a CRS of SpeckleProvider."""
|
"""Return coordinates in a CRS of SpeckleProvider."""
|
||||||
@@ -80,7 +97,9 @@ def reproject_2d_coords_list(self, coords_in: List[List[float]]) -> List[List[fl
|
|||||||
|
|
||||||
all_x = [x[0] for x in transformed]
|
all_x = [x[0] for x in transformed]
|
||||||
all_y = [x[1] for x in transformed]
|
all_y = [x[1] for x in transformed]
|
||||||
|
all_z = [x[2] for x in transformed]
|
||||||
self.extent = [min(all_x), min(all_y), max(all_x), max(all_y)]
|
self.extent = [min(all_x), min(all_y), max(all_x), max(all_y)]
|
||||||
|
self.extent3d = [min(all_x), min(all_y), min(all_z), max(all_x), max(all_y), max(all_z)]
|
||||||
return transformed
|
return transformed
|
||||||
|
|
||||||
def offset_rotate(self, coords_in: List[list]) -> List[List[float]]:
|
def offset_rotate(self, coords_in: List[list]) -> List[List[float]]:
|
||||||
@@ -106,3 +125,25 @@ def offset_rotate(self, coords_in: List[list]) -> List[List[float]]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
return final_coords
|
return final_coords
|
||||||
|
|
||||||
|
def validate_coords(self, coords):
|
||||||
|
from geopy.geocoders import Nominatim
|
||||||
|
country_code = ""
|
||||||
|
state = ""
|
||||||
|
postcode = ""
|
||||||
|
try:
|
||||||
|
geolocator = Nominatim(user_agent="specklePygeoapi")
|
||||||
|
coord = f"{coords[1]}, {coords[0]}"
|
||||||
|
location = geolocator.reverse(coord, exactly_one=True)
|
||||||
|
if location is not None:
|
||||||
|
address = location.raw['address']
|
||||||
|
country_code = address.get('country_code', '')
|
||||||
|
state = address.get('state', '')
|
||||||
|
postcode = address.get('postcode', '')
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error validating project location: {e}")
|
||||||
|
self.country_code = country_code
|
||||||
|
|
||||||
|
if country_code in COUNTRY_CODES or state in STATES or postcode in POSTCODES:
|
||||||
|
print(f"Validating project location: blocked LAT LON {coords[1]}, {coords[0]}, {country_code}, {state}, {postcode}")
|
||||||
|
raise PermissionError("Review Speckle Terms and Conditions")
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ def get_set_crs_settings(self: "SpeckleProvider", commit_obj: "Base", context_li
|
|||||||
|
|
||||||
root_objects = []
|
root_objects = []
|
||||||
try:
|
try:
|
||||||
root_objects = [commit_obj] + commit_obj.elements
|
root_objects = [commit_obj] + commit_obj.elements + [c.current for c in context_list]
|
||||||
except AttributeError as ex:
|
except AttributeError as ex:
|
||||||
pass # old commit structure
|
pass # old commit structure
|
||||||
|
|
||||||
@@ -73,6 +73,7 @@ def get_set_crs_settings(self: "SpeckleProvider", commit_obj: "Base", context_li
|
|||||||
offset_y = crs["offset_y"]
|
offset_y = crs["offset_y"]
|
||||||
self.north_degrees = crs["rotation"]
|
self.north_degrees = crs["rotation"]
|
||||||
create_crs_from_wkt(self, crs["wkt"])
|
create_crs_from_wkt(self, crs["wkt"])
|
||||||
|
self.commit_gis = True
|
||||||
|
|
||||||
if self.crs.to_authority() is not None:
|
if self.crs.to_authority() is not None:
|
||||||
data["model_crs"] = f"{self.crs.to_authority()}, {self.crs.name} "
|
data["model_crs"] = f"{self.crs.to_authority()}, {self.crs.name} "
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ def separate_display_vals(displayValue: List) -> List[Tuple["Base"]]:
|
|||||||
count = 0
|
count = 0
|
||||||
all_count = len(item.faces)
|
all_count = len(item.faces)
|
||||||
|
|
||||||
|
sub_meshes = []
|
||||||
for _ in item.faces:
|
for _ in item.faces:
|
||||||
if count < all_count:
|
if count < all_count:
|
||||||
faces = []
|
faces = []
|
||||||
@@ -61,33 +62,67 @@ def separate_display_vals(displayValue: List) -> List[Tuple["Base"]]:
|
|||||||
colors = []
|
colors = []
|
||||||
|
|
||||||
vert_num = item.faces[count]
|
vert_num = item.faces[count]
|
||||||
|
if vert_num == 0:
|
||||||
|
vert_num = 3
|
||||||
|
elif vert_num == 1:
|
||||||
|
vert_num = 4
|
||||||
|
|
||||||
faces.append(vert_num)
|
faces.append(vert_num)
|
||||||
faces.extend([ x for x in list(range(vert_num))])
|
faces.extend([ x for x in list(range(vert_num))])
|
||||||
|
|
||||||
for ind in range(vert_num):
|
try:
|
||||||
face_vert_index = count+1+ind
|
for ind in range(vert_num):
|
||||||
vert_index = item.faces[face_vert_index]
|
face_vert_index = count+1+ind
|
||||||
|
#print(face_vert_index)
|
||||||
|
vert_index = item.faces[face_vert_index]
|
||||||
|
|
||||||
new_vert = item.vertices[3*vert_index : 3*vert_index + 3]
|
new_vert = item.vertices[3*vert_index : 3*vert_index + 3]
|
||||||
verts.extend(new_vert)
|
verts.extend(new_vert)
|
||||||
|
|
||||||
if isinstance(item.colors, List) and len(item.colors) > vert_index:
|
if isinstance(item.colors, List) and len(item.colors) > vert_index:
|
||||||
color = item.colors[vert_index]
|
color = item.colors[vert_index]
|
||||||
colors.append(color)
|
colors.append(color)
|
||||||
|
|
||||||
|
count += vert_num+1
|
||||||
|
if len(colors)>0:
|
||||||
|
mesh = Mesh.create(faces= faces, vertices=verts, colors=colors)
|
||||||
|
else:
|
||||||
|
mesh = Mesh.create(faces= faces, vertices=verts)
|
||||||
|
|
||||||
|
sub_meshes.append((mesh, item))
|
||||||
|
|
||||||
count += vert_num+1
|
except IndexError: # corrupted mesh, drop altogether
|
||||||
if len(colors)>0:
|
sub_meshes = []
|
||||||
mesh = Mesh.create(faces= faces, vertices=verts, colors=colors)
|
break
|
||||||
else:
|
|
||||||
mesh = Mesh.create(faces= faces, vertices=verts)
|
display_objs.extend(sub_meshes)
|
||||||
display_objs.append((mesh, item))
|
|
||||||
|
|
||||||
elif item is not None:
|
elif item is not None:
|
||||||
display_objs.append((item, item))
|
display_objs.append((item, item))
|
||||||
|
|
||||||
return display_objs
|
return display_objs
|
||||||
|
|
||||||
|
def isDisplayable(obj: "Base") -> bool:
|
||||||
|
|
||||||
|
if is_primitive(obj):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if obj.speckle_type.endswith("Feature"):
|
||||||
|
return True
|
||||||
|
|
||||||
|
displayValue = None
|
||||||
|
if hasattr(obj, 'displayValue'):
|
||||||
|
displayValue = getattr(obj, 'displayValue')
|
||||||
|
elif hasattr(obj, '@displayValue'):
|
||||||
|
displayValue = getattr(obj, '@displayValue')
|
||||||
|
|
||||||
|
# merge to sigle object, if List
|
||||||
|
if isinstance(displayValue, List):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def find_display_obj(obj) -> Tuple["Base", "Base"]:
|
def find_display_obj(obj) -> Tuple["Base", "Base"]:
|
||||||
"""Get displayable object."""
|
"""Get displayable object."""
|
||||||
|
|
||||||
@@ -121,11 +156,12 @@ def find_display_obj(obj) -> Tuple["Base", "Base"]:
|
|||||||
def is_convertible(obj) -> bool:
|
def is_convertible(obj) -> bool:
|
||||||
"""Check if the object can be converted directly."""
|
"""Check if the object can be converted directly."""
|
||||||
|
|
||||||
from specklepy.objects.geometry import Base, Point, Line, Arc, Circle, Curve, Polycurve, Mesh, Brep
|
from specklepy.objects.geometry import Base, Point, Line, Polyline, Arc, Circle, Curve, Polycurve, Mesh, Brep
|
||||||
|
|
||||||
if ( (isinstance(obj, Base) and obj.speckle_type.endswith("Feature")) or
|
if ( (isinstance(obj, Base) and obj.speckle_type.endswith("Feature")) or
|
||||||
isinstance(obj, Point) or
|
isinstance(obj, Point) or
|
||||||
isinstance(obj, Line) or
|
isinstance(obj, Line) or
|
||||||
|
isinstance(obj, Polyline) or
|
||||||
isinstance(obj, Arc) or
|
isinstance(obj, Arc) or
|
||||||
isinstance(obj, Circle) or
|
isinstance(obj, Circle) or
|
||||||
isinstance(obj, Curve) or
|
isinstance(obj, Curve) or
|
||||||
@@ -135,6 +171,23 @@ def is_convertible(obj) -> bool:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def is_primitive(obj) -> bool:
|
||||||
|
"""Check if the object can be converted directly."""
|
||||||
|
|
||||||
|
from specklepy.objects.geometry import Polyline, Point, Line, Arc, Circle, Curve, Polycurve, Mesh, Brep
|
||||||
|
|
||||||
|
if (
|
||||||
|
isinstance(obj, Point) or
|
||||||
|
isinstance(obj, Line) or
|
||||||
|
isinstance(obj, Polyline) or
|
||||||
|
isinstance(obj, Arc) or
|
||||||
|
isinstance(obj, Circle) or
|
||||||
|
isinstance(obj, Curve) or
|
||||||
|
isinstance(obj, Mesh)
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def get_single_display_object(displayValForColor: List) -> "Base":
|
def get_single_display_object(displayValForColor: List) -> "Base":
|
||||||
"""Get a merged Mesh or a first item from displayValue list."""
|
"""Get a merged Mesh or a first item from displayValue list."""
|
||||||
|
|
||||||
@@ -165,12 +218,14 @@ def get_single_display_object(displayValForColor: List) -> "Base":
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
break
|
break
|
||||||
elif item is not None:
|
elif item is not None:
|
||||||
displayValForColor = item
|
return item
|
||||||
|
|
||||||
mesh = Mesh.create(faces= faces, vertices=verts, colors=colors)
|
mesh = Mesh.create(faces= faces, vertices=verts, colors=colors)
|
||||||
for prop in displayValForColor[0].get_member_names():
|
|
||||||
if prop not in ["colors", "vertices", "faces"]:
|
if isinstance(displayValForColor, List) and len(displayValForColor)>0:
|
||||||
mesh[prop] = getattr(displayValForColor[0], prop)
|
for prop in displayValForColor[0].get_member_names():
|
||||||
|
if prop not in ["colors", "vertices", "faces"]:
|
||||||
|
mesh[prop] = getattr(displayValForColor[0], prop)
|
||||||
|
|
||||||
displayValForColor = mesh
|
displayValForColor = mesh
|
||||||
return displayValForColor
|
return displayValForColor
|
||||||
@@ -254,46 +309,94 @@ def set_default_color(context_list: List["TraversalContext"]) -> None:
|
|||||||
|
|
||||||
for item in context_list:
|
for item in context_list:
|
||||||
# for GIS-commits, use default blue color
|
# for GIS-commits, use default blue color
|
||||||
if isinstance(item.current, VectorLayer):
|
if isinstance(item.current, VectorLayer) or (item.parent is not None and isinstance(item.parent.current, VectorLayer)):
|
||||||
DEFAULT_COLOR = (255 << 24) + (10 << 16) + (132 << 8) + 255
|
DEFAULT_COLOR = (255 << 24) + (10 << 16) + (132 << 8) + 255 # speckle blue, speckle_blue
|
||||||
break
|
break
|
||||||
|
|
||||||
def assign_color(self: "SpeckleProvider", obj_display, props) -> None:
|
def getAllParents(tc: "TraversalContext"):
|
||||||
|
|
||||||
|
all_tc = [tc]
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
parent = tc.parent
|
||||||
|
if parent:
|
||||||
|
all_tc.append(parent)
|
||||||
|
tc = parent
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
break
|
||||||
|
|
||||||
|
return all_tc
|
||||||
|
|
||||||
|
def assign_color(self: "SpeckleProvider", obj_display_tc: "TraversalContext", props: Dict) -> None:
|
||||||
"""Get and assign color to feature displayProperties."""
|
"""Get and assign color to feature displayProperties."""
|
||||||
|
|
||||||
from specklepy.objects.geometry import Base, Mesh, Brep
|
from specklepy.objects.geometry import Mesh, Brep
|
||||||
|
|
||||||
try:
|
|
||||||
color = self.material_color_proxies[obj_display.applicationId]
|
|
||||||
props['color'] = color
|
|
||||||
return
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# initialize Speckle Blue color
|
# initialize Speckle Blue color
|
||||||
color = DEFAULT_COLOR
|
color = DEFAULT_COLOR
|
||||||
opacity = None
|
opacity = None
|
||||||
|
|
||||||
|
obj_display = obj_display_tc.current
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# first, choose if get color from the parent obj or displayValue
|
||||||
|
if hasattr(obj_display, 'displayStyle') or hasattr(obj_display, '@displayStyle') or hasattr(obj_display, 'renderMaterial') or hasattr(obj_display, '@renderMaterial'):
|
||||||
|
obj_display = obj_display_tc.current
|
||||||
|
else:
|
||||||
|
# this option will be not very reliable:
|
||||||
|
# there could be different colors for diff displayValues in the list
|
||||||
|
if hasattr(obj_display, 'displayValue'):
|
||||||
|
try:
|
||||||
|
displayVal = obj_display['displayValue']
|
||||||
|
except:
|
||||||
|
displayVal = obj_display.displayValue
|
||||||
|
if isinstance(displayVal, list) and len(displayVal)>0:
|
||||||
|
obj_display = displayVal[0]
|
||||||
|
|
||||||
|
elif hasattr(obj_display, '@displayValue') and isinstance(obj_display['@displayValue'], list) and len(obj_display['@displayValue'])>0:
|
||||||
|
obj_display = obj_display['@displayValue'][0]
|
||||||
|
|
||||||
# prioritize renderMaterials for Meshes & Brep
|
# prioritize renderMaterials for Meshes & Brep
|
||||||
if isinstance(obj_display, Mesh) or isinstance(obj_display, Brep):
|
if isinstance(obj_display, Mesh) or isinstance(obj_display, Brep):
|
||||||
# print(obj_display.get_member_names())
|
# print(obj_display.get_member_names())
|
||||||
if hasattr(obj_display, 'renderMaterial'):
|
if hasattr(obj_display, 'renderMaterial'):
|
||||||
color = obj_display['renderMaterial']['diffuse']
|
try:
|
||||||
opacity = obj_display['renderMaterial']['opacity']
|
renderMaterial = obj_display['renderMaterial']
|
||||||
|
except:
|
||||||
|
renderMaterial = obj_display.renderMaterial
|
||||||
|
color = renderMaterial['diffuse']
|
||||||
|
opacity = renderMaterial['opacity']
|
||||||
elif hasattr(obj_display, '@renderMaterial'):
|
elif hasattr(obj_display, '@renderMaterial'):
|
||||||
color = obj_display['@renderMaterial']['diffuse']
|
color = obj_display['@renderMaterial']['diffuse']
|
||||||
opacity = obj_display['@renderMaterial']['opacity']
|
opacity = obj_display['@renderMaterial']['opacity']
|
||||||
|
|
||||||
elif isinstance(obj_display, Mesh) and isinstance(obj_display.colors, List) and len(obj_display.colors)>1:
|
elif isinstance(obj_display, Mesh) and isinstance(obj_display.colors, List) and len(obj_display.colors)>1:
|
||||||
sameColors = True
|
colors_number = 0
|
||||||
color1 = obj_display.colors[0]
|
all_colors = []
|
||||||
for c in obj_display.colors:
|
for c in obj_display.colors:
|
||||||
if c != color1:
|
if c not in all_colors:
|
||||||
sameColors = False
|
colors_number += 1
|
||||||
break
|
all_colors.append(c)
|
||||||
if sameColors is True:
|
|
||||||
color = color1
|
if colors_number>1:
|
||||||
|
all_a = 0
|
||||||
|
all_r = 0
|
||||||
|
all_g = 0
|
||||||
|
all_b = 0
|
||||||
|
for col in all_colors:
|
||||||
|
a, r, g, b = get_r_g_b(col)
|
||||||
|
all_a += a
|
||||||
|
all_r += r
|
||||||
|
all_g += g
|
||||||
|
all_b += b
|
||||||
|
color = (
|
||||||
|
(int(all_a/len(obj_display.colors)) << 24) + (int(all_r/len(obj_display.colors)) << 16)
|
||||||
|
+ (int(all_g/len(obj_display.colors)) << 8) + int(all_b/len(obj_display.colors))
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
color = obj_display.colors[0]
|
||||||
|
|
||||||
elif hasattr(obj_display, 'displayStyle'):
|
elif hasattr(obj_display, 'displayStyle'):
|
||||||
color = obj_display['displayStyle']['color']
|
color = obj_display['displayStyle']['color']
|
||||||
@@ -321,6 +424,24 @@ def assign_color(self: "SpeckleProvider", obj_display, props) -> None:
|
|||||||
# hex_color = '#%02x%02x%02x' % (r, g, b)
|
# hex_color = '#%02x%02x%02x' % (r, g, b)
|
||||||
props['color'] = f'rgba({r},{g},{b},{a})'
|
props['color'] = f'rgba({r},{g},{b},{a})'
|
||||||
|
|
||||||
|
# if still not found, check proxies:
|
||||||
|
if color == DEFAULT_COLOR:
|
||||||
|
for tc in getAllParents(obj_display_tc):
|
||||||
|
|
||||||
|
try:
|
||||||
|
color = self.material_color_proxies[tc.current.applicationId]
|
||||||
|
props['color'] = color
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
color = self.material_color_proxies[obj_display.applicationId]
|
||||||
|
props['color'] = color
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def get_r_g_b(rgb: int) -> Tuple[int, int, int]:
|
def get_r_g_b(rgb: int) -> Tuple[int, int, int]:
|
||||||
"""Get R, G, B values from int."""
|
"""Get R, G, B values from int."""
|
||||||
|
|
||||||
@@ -336,12 +457,12 @@ def get_r_g_b(rgb: int) -> Tuple[int, int, int]:
|
|||||||
a = 255
|
a = 255
|
||||||
return a, r, g, b
|
return a, r, g, b
|
||||||
|
|
||||||
def assign_display_properties(self: "SpeckleProvider", feature: Dict, f_base: "Base", obj_display: "Base") -> None:
|
def assign_display_properties(self: "SpeckleProvider", feature: Dict, f_base: "Base", obj_display_tc: "TraversalContext") -> None:
|
||||||
"""Assign displayProperties to the feature."""
|
"""Assign displayProperties to the feature."""
|
||||||
|
|
||||||
from specklepy.objects.geometry import Mesh, Brep
|
from specklepy.objects.geometry import Mesh, Brep
|
||||||
|
|
||||||
assign_color(self, obj_display, feature["displayProperties"])
|
assign_color(self, obj_display_tc, feature["displayProperties"])
|
||||||
feature["properties"]["color"] = feature["displayProperties"]["color"]
|
feature["properties"]["color"] = feature["displayProperties"]["color"]
|
||||||
|
|
||||||
# other properties for rendering
|
# other properties for rendering
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d
|
|||||||
from pygeoapi.provider.speckle_utils.converter_utils import assign_geometry
|
from pygeoapi.provider.speckle_utils.converter_utils import assign_geometry
|
||||||
from pygeoapi.provider.speckle_utils.display_utils import find_display_obj, assign_display_properties, find_list_of_display_obj
|
from pygeoapi.provider.speckle_utils.display_utils import find_display_obj, assign_display_properties, find_list_of_display_obj
|
||||||
|
|
||||||
print(f"Creating features..")
|
from specklepy.objects.graph_traversal.traversal import TraversalContext
|
||||||
|
from specklepy.objects.other import Collection
|
||||||
|
|
||||||
|
# print(f"Creating features..")
|
||||||
time1 = datetime.now()
|
time1 = datetime.now()
|
||||||
|
|
||||||
all_props = []
|
all_props = []
|
||||||
@@ -19,6 +22,9 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d
|
|||||||
if self.requested_data_type != "projectcomments":
|
if self.requested_data_type != "projectcomments":
|
||||||
for item in context_list:
|
for item in context_list:
|
||||||
|
|
||||||
|
if item.current.speckle_type.endswith("Collection") or item.current.speckle_type.endswith("Layer") or item.current.speckle_type.endswith("Proxy"):
|
||||||
|
continue
|
||||||
|
|
||||||
if feature_count >= self.limit:
|
if feature_count >= self.limit:
|
||||||
self.limit_message = f" (feature count limited to {self.limit})"
|
self.limit_message = f" (feature count limited to {self.limit})"
|
||||||
break
|
break
|
||||||
@@ -28,9 +34,13 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d
|
|||||||
f_fid = feature_count + 1
|
f_fid = feature_count + 1
|
||||||
|
|
||||||
# initialize feature
|
# initialize feature
|
||||||
|
speckle_type = item.current.speckle_type
|
||||||
|
if ":" in speckle_type:
|
||||||
|
speckle_type = speckle_type.split(":")[-1]
|
||||||
|
|
||||||
feature: Dict = {
|
feature: Dict = {
|
||||||
"type": "Feature",
|
"type": "Feature",
|
||||||
# "bbox": [-180.0, -90.0, 180.0, 90.0],
|
#"bbox": [-180.0, -90.0, 180.0, 90.0], should not be in degrees
|
||||||
"geometry": {},
|
"geometry": {},
|
||||||
"displayProperties":{
|
"displayProperties":{
|
||||||
"object_type": "geometry",
|
"object_type": "geometry",
|
||||||
@@ -38,7 +48,7 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d
|
|||||||
"properties": {
|
"properties": {
|
||||||
"id": f_id,
|
"id": f_id,
|
||||||
"FID": f_fid,
|
"FID": f_fid,
|
||||||
"speckle_type": item.current.speckle_type.split(":")[-1],
|
"speckle_type": speckle_type,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,13 +77,16 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d
|
|||||||
if prop not in all_props:
|
if prop not in all_props:
|
||||||
all_props.append(prop)
|
all_props.append(prop)
|
||||||
|
|
||||||
assign_display_properties(self, feature, f_base, obj_get_color)
|
obj_get_color_tc = TraversalContext(obj_get_color, "", item)
|
||||||
|
|
||||||
|
assign_display_properties(self, feature, f_base, obj_get_color_tc)
|
||||||
feature["max_height"] = max([c[2] for c in coords])
|
feature["max_height"] = max([c[2] for c in coords])
|
||||||
|
feature["bbox"] = get_feature_bbox(coords)
|
||||||
data["features"].append(feature)
|
data["features"].append(feature)
|
||||||
feature_count += 1
|
feature_count += 1
|
||||||
|
|
||||||
else:
|
else:
|
||||||
list_of_display_obj = find_list_of_display_obj(f_base)
|
list_of_display_obj = find_list_of_display_obj(f_base) # tuple
|
||||||
|
|
||||||
for k, vals in enumerate(list_of_display_obj):
|
for k, vals in enumerate(list_of_display_obj):
|
||||||
obj_display, obj_get_color = vals
|
obj_display, obj_get_color = vals
|
||||||
@@ -81,7 +94,7 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d
|
|||||||
f_fid = feature_count + 1
|
f_fid = feature_count + 1
|
||||||
feature_new: Dict = {
|
feature_new: Dict = {
|
||||||
"type": "Feature",
|
"type": "Feature",
|
||||||
# "bbox": [-180.0, -90.0, 180.0, 90.0],
|
#"bbox": [-180.0, -90.0, 180.0, 90.0], should not be in degrees
|
||||||
"geometry": {},
|
"geometry": {},
|
||||||
"displayProperties":{
|
"displayProperties":{
|
||||||
"object_type": "geometry",
|
"object_type": "geometry",
|
||||||
@@ -107,11 +120,14 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d
|
|||||||
all_coords.extend(coords)
|
all_coords.extend(coords)
|
||||||
all_coord_counts.append(coord_counts)
|
all_coord_counts.append(coord_counts)
|
||||||
|
|
||||||
assign_display_properties(self, feature_new, f_base, obj_get_color)
|
obj_get_color_tc = TraversalContext(obj_display, "", item)
|
||||||
|
|
||||||
|
assign_display_properties(self, feature_new, f_base, obj_get_color_tc)
|
||||||
feature_new["max_height"] = max([c[2] for c in coords])
|
feature_new["max_height"] = max([c[2] for c in coords])
|
||||||
|
feature_new["bbox"] = get_feature_bbox(coords)
|
||||||
data["features"].append(feature_new)
|
data["features"].append(feature_new)
|
||||||
feature_count +=1
|
feature_count +=1
|
||||||
|
|
||||||
assign_missing_props(data["features"], all_props)
|
assign_missing_props(data["features"], all_props)
|
||||||
else:
|
else:
|
||||||
####################### create comment features
|
####################### create comment features
|
||||||
@@ -153,10 +169,23 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d
|
|||||||
########################
|
########################
|
||||||
|
|
||||||
if len(data["features"])==0 and len(data["comments"])==0:
|
if len(data["features"])==0 and len(data["comments"])==0:
|
||||||
raise ValueError("No supported features found")
|
raise ValueError(f"No supported features of type '{self.requested_data_type}' found. Make sure correct type is requested by adding a URL parameter (e.g. '&dataType=points').")
|
||||||
|
|
||||||
time2 = datetime.now()
|
time2 = datetime.now()
|
||||||
print(f"Creating features time: {(time2-time1).total_seconds()}")
|
|
||||||
|
time_operation = (time2-time1).total_seconds()
|
||||||
|
self.times["time_creating_features"] = time_operation
|
||||||
|
# print(f"Creating features time: {time_operation}")
|
||||||
|
|
||||||
|
def get_feature_bbox(coords) -> List[float]:
|
||||||
|
"""Get min max coordinates of the feature."""
|
||||||
|
|
||||||
|
x0 = min([c[0] for c in coords])
|
||||||
|
x1 = max([c[0] for c in coords])
|
||||||
|
y0 = min([c[1] for c in coords])
|
||||||
|
y1 = max([c[1] for c in coords])
|
||||||
|
|
||||||
|
return [x0, y0, x1, y1]
|
||||||
|
|
||||||
def assign_comment_data(comments, properties):
|
def assign_comment_data(comments, properties):
|
||||||
"""Create html text to display for the thread."""
|
"""Create html text to display for the thread."""
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
COUNTRY_CODES = ["ru"]
|
||||||
|
STATES = ['Автономна Республіка Крим', 'Севастополь', 'Донецька область', 'Луганська область']
|
||||||
|
POSTCODES = [str(i) for i in range(95000,99999)]
|
||||||
@@ -31,10 +31,7 @@ def safe_json_loads(obj: str, obj_id=None) -> Any:
|
|||||||
f" int error - falling back to json. \nError: {err}",
|
f" int error - falling back to json. \nError: {err}",
|
||||||
SpeckleWarning,
|
SpeckleWarning,
|
||||||
)
|
)
|
||||||
try:
|
return json.loads(obj)
|
||||||
return ujson.loads(obj[:-2])
|
|
||||||
except:
|
|
||||||
return json.loads(obj)
|
|
||||||
|
|
||||||
|
|
||||||
class BaseObjectSerializer:
|
class BaseObjectSerializer:
|
||||||
@@ -327,7 +324,6 @@ class BaseObjectSerializer:
|
|||||||
# make sure an obj was passed and create dict if string was somehow passed
|
# make sure an obj was passed and create dict if string was somehow passed
|
||||||
if not obj:
|
if not obj:
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(obj, str):
|
if isinstance(obj, str):
|
||||||
obj = safe_json_loads(obj)
|
obj = safe_json_loads(obj)
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class ServerTransport(AbstractTransport):
|
|||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
|
|
||||||
if self.account.token is not None:
|
if self.account.token is not None:
|
||||||
self._batch_sender = BatchSender(
|
self._batch_sender = BatchSender(
|
||||||
self.url, self.stream_id, self.account.token, max_batch_size_mb=1
|
self.url, self.stream_id, self.account.token, max_batch_size_mb=1
|
||||||
@@ -157,25 +157,28 @@ class ServerTransport(AbstractTransport):
|
|||||||
id for id in children_found_map if not children_found_map[id]
|
id for id in children_found_map if not children_found_map[id]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# save headers and assign them back later
|
||||||
|
headers = self.session.headers
|
||||||
|
self.session.headers.update(
|
||||||
|
{
|
||||||
|
"Accept": "text/plain",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Get the new children
|
# Get the new children
|
||||||
endpoint = f"{self.url}/api/getobjects/{self.stream_id}"
|
endpoint = f"{self.url}/api/getobjects/{self.stream_id}"
|
||||||
r = self.session.post(
|
r = self.session.post(
|
||||||
endpoint, data={"objects": json.dumps(new_children_ids)}, stream=True
|
endpoint, data={"objects": json.dumps(new_children_ids)}, stream=True
|
||||||
)
|
)
|
||||||
r.encoding = "utf-8"
|
r.encoding = "utf-8"
|
||||||
lines = r.iter_lines(decode_unicode=True, delimiter="},{")
|
lines = r.iter_lines(decode_unicode=True)
|
||||||
|
self.session.headers = headers # return previous headers
|
||||||
|
|
||||||
# iter through returned objects saving them as we go
|
# iter through returned objects saving them as we go
|
||||||
target_transport.begin_write()
|
target_transport.begin_write()
|
||||||
all_lines = [line for _,line in enumerate(lines)]
|
for line in lines:
|
||||||
for i, line in enumerate(all_lines):
|
|
||||||
if line:
|
if line:
|
||||||
hash = line.split('"id": "')[1].split('"')[0]
|
hash, obj = line.split("\t")
|
||||||
obj = "{" + line + "}"
|
|
||||||
if i==0:
|
|
||||||
obj = obj[2:]
|
|
||||||
elif i==len(all_lines)-1:
|
|
||||||
obj = obj[:-2]
|
|
||||||
target_transport.save_object(hash, obj)
|
target_transport.save_object(hash, obj)
|
||||||
|
|
||||||
target_transport.save_object(id, root_obj_serialized)
|
target_transport.save_object(id, root_obj_serialized)
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ def get_info_from_comment(comment: Dict, project_id: str, model_id: str) -> Tupl
|
|||||||
res_id = model_id
|
res_id = model_id
|
||||||
viewer_state = comment["viewerState"]
|
viewer_state = comment["viewerState"]
|
||||||
if viewer_state is not None: # can be None for Replies
|
if viewer_state is not None: # can be None for Replies
|
||||||
position: List[float] = viewer_state["ui"]["camera"]["target"]
|
position: List[float] = viewer_state["ui"]["selection"]
|
||||||
try:
|
try:
|
||||||
res_id = viewer_state["resources"]["request"]["resourceIdString"]
|
res_id = viewer_state["resources"]["request"]["resourceIdString"]
|
||||||
except:
|
except:
|
||||||
@@ -216,4 +216,19 @@ def get_attachment(project_id: str, attachment_id: str, attachment_name: str) ->
|
|||||||
raise Exception(
|
raise Exception(
|
||||||
f"Request not successful: Response code {r.status_code}"
|
f"Request not successful: Response code {r.status_code}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def set_actions(self: "SpeckleProvider", client: "SpeckleClient", action: str = "GEO receive"):
|
||||||
|
from specklepy.logging.metrics import track
|
||||||
|
try:
|
||||||
|
full_dict = {**self.url_params, **self.times}
|
||||||
|
full_dict["GIS commit"] = self.commit_gis
|
||||||
|
full_dict["project_id"] = f"{self.project_id}"
|
||||||
|
full_dict["sourceHostApp"] = self.sourceApp
|
||||||
|
full_dict["model"] = f"{self.project_name}, {self.model_name}"
|
||||||
|
full_dict["time_TOTAL"] = sum([x[1] for x in self.times.items()])
|
||||||
|
full_dict["model_url"] = self.speckle_url
|
||||||
|
full_dict["model_country_code"] = self.country_code
|
||||||
|
track(action, client.account, full_dict)
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"_Cannot set action '{action}': {ex}")
|
||||||
|
pass
|
||||||
@@ -1,65 +1,104 @@
|
|||||||
|
|
||||||
|
|
||||||
from typing import Dict
|
import inspect
|
||||||
|
|
||||||
|
|
||||||
def get_set_url_parameters(self: "SpeckleProvider"):
|
def get_set_url_parameters(self: "SpeckleProvider"):
|
||||||
|
"""Parse and save URL parameters."""
|
||||||
|
|
||||||
from pygeoapi.provider.speckle_utils.crs_utils import create_crs_from_authid
|
from pygeoapi.provider.speckle_utils.crs_utils import create_crs_from_authid
|
||||||
|
|
||||||
if (
|
crsauthid = False
|
||||||
isinstance(self.data, str)
|
|
||||||
and "speckleurl=" in self.data.lower()
|
if (isinstance(self.data, str)):
|
||||||
and "projects" in self.data
|
|
||||||
and "models" in self.data
|
|
||||||
):
|
|
||||||
crs_authid = ""
|
|
||||||
for item in self.data.lower().split("&"):
|
for item in self.data.lower().split("&"):
|
||||||
|
|
||||||
# if CRS authid is found, rest will be ignored
|
# if CRS authid is found, rest will be ignored
|
||||||
if "datatype=" in item:
|
if "speckleurl=" in item:
|
||||||
try:
|
try:
|
||||||
self.requested_data_type = item.split("datatype=")[1]
|
speckle_url = item.split("speckleurl=")[1]
|
||||||
if self.requested_data_type not in ["points", "lines", "polygons", "projectcomments"]:
|
if "/projects/" not in speckle_url or "/models/" not in speckle_url:
|
||||||
self.requested_data_type = "polygons (default)"
|
raise ValueError(f"Provide valid Speckle Model URL: {item}")
|
||||||
|
|
||||||
|
if speckle_url[-1] == "/":
|
||||||
|
speckle_url = speckle_url[:-1]
|
||||||
|
self.speckle_project_url = speckle_url.split("/models")[0]
|
||||||
except:
|
except:
|
||||||
pass
|
raise ValueError(f"Provide valid Speckle Model URL: {item}")
|
||||||
|
|
||||||
|
elif "datatype=" in item:
|
||||||
|
try:
|
||||||
|
requested_data_type = item.split("datatype=")[1]
|
||||||
|
if requested_data_type in ["points", "lines", "polygons", "projectcomments"]:
|
||||||
|
self.requested_data_type = requested_data_type
|
||||||
|
self.url_params["url_data_type"] = requested_data_type
|
||||||
|
except:
|
||||||
|
raise ValueError(f"Provide valid dataType parameter (points/lines/polygons/projectcomments): {item}")
|
||||||
|
|
||||||
elif "preserveattributes=" in item:
|
elif "preserveattributes=" in item:
|
||||||
try:
|
try:
|
||||||
self.preserve_attributes = item.split("preserveattributes=")[1]
|
preserve_attributes = item.split("preserveattributes=")[1]
|
||||||
if self.preserve_attributes not in ["true", "false"]:
|
if preserve_attributes in ["true", "false"]:
|
||||||
self.preserve_attributes = "false (default)"
|
self.preserve_attributes = preserve_attributes
|
||||||
|
self.url_params["url_preserve_attributes"] = preserve_attributes
|
||||||
except:
|
except:
|
||||||
pass
|
ValueError(f"Provide valid preserverAttributes parameter (true/false): {item}")
|
||||||
|
|
||||||
elif "limit=" in item:
|
|
||||||
try:
|
|
||||||
self.limit = int(item.split("limit=")[1])
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
elif "crsauthid=" in item:
|
elif "crsauthid=" in item:
|
||||||
crs_authid = item.split("crsauthid=")[1]
|
crs_authid = item.split("crsauthid=")[1]
|
||||||
|
if isinstance(crs_authid, str) and len(crs_authid)>3:
|
||||||
|
crsauthid = True
|
||||||
|
self.crs_authid = crs_authid
|
||||||
|
self.url_params["url_crs_authid"] = crs_authid
|
||||||
|
|
||||||
elif "lat=" in item:
|
elif "lat=" in item:
|
||||||
try:
|
try:
|
||||||
self.lat = float(item.split("lat=")[1])
|
lat = float(item.split("lat=")[1])
|
||||||
|
self.lat = lat
|
||||||
|
self.url_params["url_lat"] = lat
|
||||||
except:
|
except:
|
||||||
pass
|
raise ValueError(f"Invalid Lat input, must be numeric: {item}")
|
||||||
# raise ValueError(f"Invalid Lat input, must be numeric: {item.split('lat=')[1]}")
|
|
||||||
elif "lon=" in item:
|
elif "lon=" in item:
|
||||||
try:
|
try:
|
||||||
self.lon = float(item.split("lon=")[1])
|
lon = float(item.split("lon=")[1])
|
||||||
|
self.lon = lon
|
||||||
|
self.url_params["url_lon"] = lon
|
||||||
except:
|
except:
|
||||||
pass
|
raise ValueError(f"Invalid Lon input, must be numeric: {item}")
|
||||||
# raise ValueError(f"Invalid Lon input, must be numeric: {item.split('lon=')[1]}")
|
|
||||||
elif "northdegrees=" in item:
|
elif "northdegrees=" in item:
|
||||||
try:
|
try:
|
||||||
self.north_degrees = float(item.split("northdegrees=")[1])
|
north_degrees = float(item.split("northdegrees=")[1])
|
||||||
|
self.north_degrees = north_degrees
|
||||||
|
self.url_params["url_north_degrees"] = north_degrees
|
||||||
except:
|
except:
|
||||||
pass
|
raise ValueError(f"Invalid northDegrees input, must be numeric: {item}")
|
||||||
# raise ValueError(f"Invalid NorthDegrees input, must be numeric: {item.split('northdegrees=')[1]}")
|
elif "limit=" in item:
|
||||||
|
try:
|
||||||
|
limit = int(item.split("limit=")[1])
|
||||||
|
if limit>0:
|
||||||
|
self.limit = limit
|
||||||
|
self.url_params["url_limit"] = limit
|
||||||
|
except:
|
||||||
|
ValueError(f"Invalid limit input, must be a positive integer: {item}")
|
||||||
|
|
||||||
|
elif "useragent=" in item:
|
||||||
|
try:
|
||||||
|
agent = item.split("useragent=")[1]
|
||||||
|
self.user_agent = agent
|
||||||
|
self.url_params["user_agent"] = agent
|
||||||
|
except:
|
||||||
|
ValueError(f"Invalid limit input, must be a positive integer: {item}")
|
||||||
|
|
||||||
|
|
||||||
|
if self.speckle_url == "-":
|
||||||
|
self.missing_url = "true"
|
||||||
|
|
||||||
|
# if CRS authid is found, rest will be ignored
|
||||||
|
if crsauthid:
|
||||||
|
self.lat = str(self.lat) + " (not applied)"
|
||||||
|
self.lon = str(self.lon) + " (not applied)"
|
||||||
|
self.north_degrees = 0 # default to 0: rotation ignored when AuthId is used #str(self.north_degrees) + " (not applied)"
|
||||||
|
|
||||||
# if CRS parameter present, create and assign CRS:
|
# if CRS parameter present, create and assign CRS:
|
||||||
if len(crs_authid)>3:
|
if len(self.crs_authid)>3:
|
||||||
create_crs_from_authid(self)
|
create_crs_from_authid(self, self.crs_authid)
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -71,7 +71,7 @@
|
|||||||
<meta name="language" content="{{ config['server']['language'] }}">
|
<meta name="language" content="{{ config['server']['language'] }}">
|
||||||
<meta name="description" content="{{ config['metadata']['identification']['title'] }}">
|
<meta name="description" content="{{ config['metadata']['identification']['title'] }}">
|
||||||
<meta name="keywords" content="{{ config['metadata']['identification']['keywords']|join(',') }}">
|
<meta name="keywords" content="{{ config['metadata']['identification']['keywords']|join(',') }}">
|
||||||
<link rel="shortcut icon" href="{{ config['server']['url'] }}/static/img/speckle_geo.png" type="image/x-icon">
|
<link rel="shortcut icon" href="https://github.com/specklesystems/pygeoapi/blob/dev/pygeoapi/static/img/speckle_geo.png" type="image/x-icon">
|
||||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
<link rel="stylesheet" href="https://unpkg.com/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||||
<link rel="stylesheet" href="{{ config['server']['url'] }}/static/css/default.css">
|
<link rel="stylesheet" href="{{ config['server']['url'] }}/static/css/default.css">
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
@@ -109,11 +109,9 @@
|
|||||||
title="{{ config['metadata']['identification']['title'] }}" style="height:30px;vertical-align: middle;" />
|
title="{{ config['metadata']['identification']['title'] }}" style="height:30px;vertical-align: middle;" />
|
||||||
<b style="text-align:left;padding-left: 10px;">Speckle</b>
|
<b style="text-align:left;padding-left: 10px;">Speckle</b>
|
||||||
</a>
|
</a>
|
||||||
<p style="text-align:left;padding-left: 10px;" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none">
|
<a href="https://geo.speckle.systems/" target="_blank" style="text-align:left;padding-left: 10px;" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none">
|
||||||
<a href="https://geo.speckle.systems/" target="_blank" style="text-align:left;padding-left: 10px;" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none">
|
|
||||||
> Geolocating your data
|
> Geolocating your data
|
||||||
</a>
|
</a>
|
||||||
</p>
|
|
||||||
{% if (data["model"] and data["model"]!="") %}
|
{% if (data["model"] and data["model"]!="") %}
|
||||||
<a href="{{data['speckle_project_url']}}" target="_blank" style="text-align:left;padding-left: 10px;" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none">
|
<a href="{{data['speckle_project_url']}}" target="_blank" style="text-align:left;padding-left: 10px;" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none">
|
||||||
> {{data["project"]}} >
|
> {{data["project"]}} >
|
||||||
@@ -133,6 +131,14 @@
|
|||||||
<ul class="nav nav-pills"> </ul>
|
<ul class="nav nav-pills"> </ul>
|
||||||
</header>
|
</header>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style="max-height:fit-content;margin:0px;padding:0px;background-color: rgb(10,132,255);">
|
||||||
|
<p style="text-align: center; margin:0px;padding:5px;">
|
||||||
|
<a href = "https://docs.google.com/forms/d/e/1FAIpQLScKW2pkcWll3deXEwoV_G5ozLtuU06_prw8rf8HFuCk4tmOPQ/viewform?usp=sf_link"
|
||||||
|
style="color:rgb(255, 255, 255)" target="_blank">We would love to hear your feedback!</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="crumbs">
|
<div class="crumbs">
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% if not data['missing_url'] %}
|
{% if data['speckle_url'] %}
|
||||||
|
|
||||||
|
|
||||||
<div id="map2d" style="height: 80vh;"></div>
|
<div id="map2d" style="height: 80vh;"></div>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{% if data['missing_url'] %}
|
{% if not data['speckle_url'] %}
|
||||||
<tr>
|
<tr>
|
||||||
<p>
|
<p>
|
||||||
<div style="height: fit-content;">
|
<div style="height: fit-content;">
|
||||||
@@ -202,7 +202,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
{% set title_field = data.title_field %}
|
{% set title_field = data.title_field %}
|
||||||
<td data-label="{{ title_field }}">
|
<td data-label="{{ title_field }}">
|
||||||
<a title="{{ ft.properties.get(title_field) }}" href="{{ data['speckle_project_url'] }}/models/{{ft.id.split('_')[0]}}" target="_blank">
|
<a title="{{ ft.properties.get(title_field) }}" href="{{data['speckle_url'].split('/models')[0]}}/models/{{ft.id.split('_')[0]}}" target="_blank">
|
||||||
{{ ft.properties.get(title_field) | string | truncate( 35 ) }}
|
{{ ft.properties.get(title_field) | string | truncate( 35 ) }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@@ -335,7 +335,11 @@
|
|||||||
{% block extrafoot %}
|
{% block extrafoot %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.getElementById("loading_screen").remove();
|
try {
|
||||||
|
document.getElementById("loading_screen").remove();
|
||||||
|
document.getElementById("loading_screen_band").remove();
|
||||||
|
}
|
||||||
|
catch(err) {}
|
||||||
|
|
||||||
// attach even to modeSwitch btn
|
// attach even to modeSwitch btn
|
||||||
document.getElementById("modeSwitch").onclick = switchMode;
|
document.getElementById("modeSwitch").onclick = switchMode;
|
||||||
@@ -357,67 +361,123 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function split_polygons(geojson_data){
|
||||||
|
features = []
|
||||||
|
geojson_data.forEach((element, index) => {
|
||||||
|
if (element.geometry.type == "MultiPolygon"){
|
||||||
|
// mesh faces are stored as parts, so most buildings might have hundreds of parts with just 1 loop or 3-4pts
|
||||||
|
all_parts = element.geometry.coordinates;
|
||||||
|
|
||||||
|
// loops are usually simple (3-4 vertices), and usually only loop
|
||||||
|
for (polyPart of all_parts){
|
||||||
|
new_coordinates = [polyPart];
|
||||||
|
new_element = {"id": element.properties.id, "type":"Feature",
|
||||||
|
"geometry": {"type": "MultiPolygon", "coordinates": new_coordinates},
|
||||||
|
"properties": element.properties,
|
||||||
|
"displayProperties": element.displayProperties };
|
||||||
|
|
||||||
|
features.push(new_element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
features.push(element)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return features
|
||||||
|
}
|
||||||
|
|
||||||
var data = {{ data | to_json | safe }};
|
var data = {{ data | to_json | safe }};
|
||||||
var geojson_data = {{ data['features'] | to_json | safe }};
|
var geojson_data_original = {{ data['features'] | to_json | safe }};
|
||||||
|
|
||||||
// Leaflet 2d map
|
// Leaflet 2d map
|
||||||
function initialize2d() {
|
function initialize2d() {
|
||||||
var map = L.map('map2d').setView([45, 0], 2);
|
var map = L.map('map2d', {zoomControl: false}).setView([45, 0], 2);
|
||||||
map.addLayer(new L.TileLayer(
|
L.control.zoom({
|
||||||
'{{ config['server']['map']['url'] }}', {
|
position: 'topright'
|
||||||
maxZoom: 22,
|
}).addTo(map);
|
||||||
attribution: '{{ config['server']['map']['attribution'] | safe }} © Data: <a href="https://speckle.systems/">Speckle Systems</a>'
|
var tileLayer = new L.TileLayer(
|
||||||
}
|
'{{ config['server']['map']['url'] }}', {
|
||||||
));
|
maxZoom: 22,
|
||||||
|
minZoom: 12,
|
||||||
|
attribution: '{{ config['server']['map']['attribution'] | safe }} © Data: <a href="https://speckle.systems/">Speckle Systems</a>'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
var items = new L.GeoJSON(geojson_data, {
|
geojson_data = split_polygons(geojson_data_original);
|
||||||
filter: (feature) => {
|
project_url = ""
|
||||||
return feature.displayProperties["object_type"] == "geometry"
|
try {
|
||||||
},
|
project_url = data['speckle_url'].split("/models")[0]
|
||||||
pointToLayer: (feature, latlng) => {
|
}
|
||||||
return new L.circleMarker(latlng)
|
catch(err) {}
|
||||||
},
|
|
||||||
onEachFeature: function (feature, layer) {
|
|
||||||
var url = '{{ data['speckle_project_url'] }}/models/' + feature.id.split("_")[0]
|
|
||||||
var html = '<span><td><p>' + feature['properties']['speckle_type'] + '</p></td><a href="' + url + '" target="_blank">' + feature['properties']['id'].split("_")[0] + '</a></span>';
|
|
||||||
layer.bindPopup(html);
|
|
||||||
|
|
||||||
var myFillColor = feature.displayProperties['color'];
|
var items = new L.GeoJSON(geojson_data, {
|
||||||
var mylineWeight = feature.displayProperties['lineWidth'];
|
filter: (feature) => {
|
||||||
var myRadius = feature.displayProperties['radius'];
|
return feature.displayProperties["object_type"] == "geometry"
|
||||||
|
},
|
||||||
|
pointToLayer: (feature, latlng) => {
|
||||||
|
return new L.circleMarker(latlng)
|
||||||
|
},
|
||||||
|
onEachFeature: function (feature, layer) {
|
||||||
|
var url = project_url + '/models/' + feature.id.split("_")[0]
|
||||||
|
var html = '<span><td><p>' + feature['properties']['speckle_type'] + '</p></td><a href="' + url + '" target="_blank">' + feature['properties']['id'].split("_")[0] + '</a></span>';
|
||||||
|
layer.bindPopup(html);
|
||||||
|
|
||||||
layer.setStyle({
|
var myFillColor = feature.displayProperties['color'];
|
||||||
fillColor: myFillColor,
|
var mylineWeight = feature.displayProperties['lineWidth'];
|
||||||
color: myFillColor,
|
var myRadius = feature.displayProperties['radius'];
|
||||||
fillOpacity: 0.8,
|
|
||||||
weight: mylineWeight,
|
|
||||||
radius: myRadius
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}); //.addTo(map);
|
|
||||||
|
|
||||||
var comments = new L.GeoJSON(geojson_data, {
|
|
||||||
filter: (feature) => {
|
|
||||||
return feature.displayProperties["object_type"] == "comment"
|
|
||||||
},
|
|
||||||
pointToLayer: (feature, latlng) => {
|
|
||||||
return new L.marker(latlng)
|
|
||||||
},
|
|
||||||
onEachFeature: function (feature, layer) {
|
|
||||||
var url = '{{ data['speckle_project_url'] }}/models/' + feature.properties.resource_id + '#threadId=' + feature.id;
|
|
||||||
var html = '<span><td><a href="' + url + '" target="_blank">Go to thread</a></td> <td><p>' + feature['properties']['text_html'] + '</p></td> </span>';
|
|
||||||
layer.bindPopup(html);
|
|
||||||
}
|
|
||||||
}); //.addTo(map);
|
|
||||||
|
|
||||||
var group = L.featureGroup([items, comments])
|
layer.setStyle({
|
||||||
.addTo(map);
|
fillColor: myFillColor,
|
||||||
|
color: myFillColor,
|
||||||
|
fillOpacity: 0.8,
|
||||||
|
weight: mylineWeight,
|
||||||
|
radius: myRadius
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}); //.addTo(map);
|
||||||
|
|
||||||
|
var comments = new L.GeoJSON(geojson_data, {
|
||||||
|
filter: (feature) => {
|
||||||
|
return feature.displayProperties["object_type"] == "comment"
|
||||||
|
},
|
||||||
|
pointToLayer: (feature, latlng) => {
|
||||||
|
return new L.marker(latlng)
|
||||||
|
},
|
||||||
|
onEachFeature: function (feature, layer) {
|
||||||
|
var url = project_url + '/models/' + feature.properties.resource_id + '#threadId=' + feature.id;
|
||||||
|
var html = '<span><td><a href="' + url + '" target="_blank">Go to thread</a></td> <td><p>' + feature['properties']['text_html'] + '</p></td> </span>';
|
||||||
|
layer.bindPopup(html);
|
||||||
|
}
|
||||||
|
}); //.addTo(map);
|
||||||
|
|
||||||
|
|
||||||
|
var group = L.featureGroup([items, comments]);
|
||||||
|
// load proper basemap for Speckle models; but only zoomed-out one for empty data
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bounds = group.getBounds();
|
||||||
|
map.fitBounds(bounds);
|
||||||
|
|
||||||
//map.addLayer(lines);
|
tileLayer.addTo(map);
|
||||||
|
group.addTo(map);
|
||||||
|
}
|
||||||
|
catch (err){
|
||||||
|
tileLayer = new L.TileLayer(
|
||||||
|
'{{ config['server']['map']['url'] }}', {
|
||||||
|
maxZoom: 2,
|
||||||
|
minZoom: 2,
|
||||||
|
attribution: '{{ config['server']['map']['attribution'] | safe }} © Data: <a href="https://speckle.systems/">Speckle Systems</a>'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
tileLayer.addTo(map);
|
||||||
|
}
|
||||||
|
|
||||||
map.fitBounds(group.getBounds());
|
|
||||||
// map.setZoom(19); // in order for the tiles to load
|
|
||||||
|
//map.addLayer(lines);
|
||||||
|
// map.setZoom(19); // in order for the tiles to load
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -504,11 +564,20 @@
|
|||||||
coords = feature.geometry.coordinates;
|
coords = feature.geometry.coordinates;
|
||||||
|
|
||||||
if (feature.geometry.type.includes("Polygon")) {
|
if (feature.geometry.type.includes("Polygon")) {
|
||||||
polygons = []
|
|
||||||
|
|
||||||
// check orientation of each PolygonPart, if vertical - shift points slightly
|
polygon_all_parts = []
|
||||||
|
// iterate through Polygon Parts
|
||||||
for (let p = 0; p < coords.length; p++) {
|
for (let p = 0; p < coords.length; p++) {
|
||||||
|
|
||||||
|
// check orientation of each PolygonPart, if vertical - shift points slightly
|
||||||
|
polygon_part = [];
|
||||||
|
inner = false;
|
||||||
|
|
||||||
for (let c = 0; c < coords[p].length; c++) {
|
for (let c = 0; c < coords[p].length; c++) {
|
||||||
|
polygon_part_loop = [];
|
||||||
|
if (c>0){
|
||||||
|
inner = true;
|
||||||
|
}
|
||||||
|
|
||||||
sum_orientation = 0;
|
sum_orientation = 0;
|
||||||
polygon_pts = coords[p][c]; // usually 3 for Mesh faces
|
polygon_pts = coords[p][c]; // usually 3 for Mesh faces
|
||||||
@@ -521,7 +590,9 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
createdPolygon = false;
|
createdPolygon = false;
|
||||||
if (-0.01 < sum_orientation && sum_orientation <0.01){
|
|
||||||
|
if (-0.000000001 < sum_orientation && sum_orientation <0.000000001){
|
||||||
|
|
||||||
coords[p][c][0][0] += 0.0000001;
|
coords[p][c][0][0] += 0.0000001;
|
||||||
coords[p][c][0][1] += 0.0000001;
|
coords[p][c][0][1] += 0.0000001;
|
||||||
|
|
||||||
@@ -530,46 +601,60 @@
|
|||||||
|
|
||||||
coords[p][c][2][0] += 0.0000001;
|
coords[p][c][2][0] += 0.0000001;
|
||||||
coords[p][c][2][1] += 0.0000001;
|
coords[p][c][2][1] += 0.0000001;
|
||||||
|
|
||||||
if(polygon_pts.length==3) {
|
if(polygon_pts.length==3) {
|
||||||
createdPolygon = true;
|
createdPolygon = true;
|
||||||
|
|
||||||
multipolygon_coords = [coords[p][c]];
|
multipolygon_coords = coords[p][c];
|
||||||
polygons.push({"id": speckle_features.length, "type":"Feature",
|
polygon_part_loop = multipolygon_coords;
|
||||||
"geometry": {"type": "MultiPolygon", "coordinates":[multipolygon_coords]},
|
|
||||||
"properties": speckle_data.features[i].properties,
|
polygon_part = [polygon_part_loop];
|
||||||
"displayProperties": speckle_data.features[i].displayProperties });
|
polygon_all_parts.push(polygon_part);
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (polygon_pts.length==4) {
|
else if (polygon_pts.length==4) {
|
||||||
createdPolygon = true;
|
createdPolygon = true;
|
||||||
|
|
||||||
multipolygon_coords = [coords[p][c].slice(0,3)];
|
multipolygon_coords = coords[p][c].slice(0,3);
|
||||||
polygons.push({"id": speckle_features.length, "type":"Feature",
|
polygon_part_loop = multipolygon_coords;
|
||||||
"geometry": {"type": "MultiPolygon", "coordinates":[multipolygon_coords]},
|
|
||||||
"properties": speckle_data.features[i].properties,
|
|
||||||
"displayProperties": speckle_data.features[i].displayProperties });
|
|
||||||
|
|
||||||
multipolygon_coords = [[coords[p][c][2], coords[p][c][3], coords[p][c][0]]];
|
polygon_part = [polygon_part_loop];
|
||||||
polygons.push({"id": speckle_features.length, "type":"Feature",
|
polygon_all_parts.push(polygon_part);
|
||||||
"geometry": {"type": "MultiPolygon", "coordinates":[multipolygon_coords]},
|
|
||||||
"properties": speckle_data.features[i].properties ,
|
/////////
|
||||||
"displayProperties": speckle_data.features[i].displayProperties });
|
multipolygon_coords = [coords[p][c][2], coords[p][c][3], coords[p][c][0]];
|
||||||
|
polygon_part_loop = multipolygon_coords;
|
||||||
|
|
||||||
|
polygon_part = [polygon_part_loop];
|
||||||
|
polygon_all_parts.push(polygon_part);
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
if (createdPolygon == false){
|
if (createdPolygon == false){ // if non-vertical, or vertical with more than 4 vertices
|
||||||
multipolygon_coords = [coords[p][c]];
|
|
||||||
polygons.push({"id": speckle_features.length, "type":"Feature",
|
multipolygon_coords = coords[p][c];
|
||||||
"geometry": {"type": "MultiPolygon", "coordinates":[multipolygon_coords]},
|
polygon_part_loop = multipolygon_coords;
|
||||||
"properties": speckle_data.features[i].properties,
|
|
||||||
"displayProperties": speckle_data.features[i].displayProperties });
|
if (inner == false){
|
||||||
|
polygon_part = [polygon_part_loop];
|
||||||
|
polygon_all_parts.push(polygon_part);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
polygon_all_parts[polygon_all_parts.length-1].push(polygon_part_loop);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
polygons.forEach((element, index, array) => {
|
|
||||||
element.displayProperties.lineWidth = 0.05
|
new_polygon = {"id": speckle_features.length, "type":"Feature",
|
||||||
speckle_features.push(element);
|
"geometry": {"type": "MultiPolygon", "coordinates":polygon_all_parts},
|
||||||
});
|
"properties": speckle_data.features[i].properties,
|
||||||
|
"displayProperties": speckle_data.features[i].displayProperties };
|
||||||
|
|
||||||
|
new_polygon.displayProperties.lineWidth = 0.05
|
||||||
|
speckle_features.push(new_polygon);
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (speckle_data.features[i].displayProperties.object_type == "comment")
|
else if (speckle_data.features[i].displayProperties.object_type == "comment")
|
||||||
@@ -620,6 +705,7 @@
|
|||||||
longitude: extent[0] + (extent[2]-extent[0])/2,
|
longitude: extent[0] + (extent[2]-extent[0])/2,
|
||||||
latitude: extent[1] + (extent[3]-extent[1])/2,
|
latitude: extent[1] + (extent[3]-extent[1])/2,
|
||||||
zoom: 22,
|
zoom: 22,
|
||||||
|
minZoom: 12,
|
||||||
pitch: 60,
|
pitch: 60,
|
||||||
bearing: 1.469387755102039
|
bearing: 1.469387755102039
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,7 +9,10 @@
|
|||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.getElementById("loading_screen").remove();
|
try {
|
||||||
|
document.getElementById("loading_screen").remove();
|
||||||
|
}
|
||||||
|
catch(err) {}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -5,4 +5,10 @@
|
|||||||
<h2>{% trans %}Exception{% endtrans %}</h2>
|
<h2>{% trans %}Exception{% endtrans %}</h2>
|
||||||
<p>{{ data['description'] }}</p>
|
<p>{{ data['description'] }}</p>
|
||||||
</section>
|
</section>
|
||||||
|
<script>
|
||||||
|
try {
|
||||||
|
document.getElementById("loading_screen").remove();
|
||||||
|
}
|
||||||
|
catch(err) {}
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/bootstrap@5.1.3/dist/css/bootstrap.min.css">
|
||||||
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<div id="loading_screen_band" style="position:absolute;z-index:-1;width:100%;max-height:fit-content;margin:0px;padding:0px;background-color: rgb(10,132,255);">
|
||||||
|
<p style="text-align: center; margin:0px;padding:15px;">
|
||||||
|
<a href = "https://docs.google.com/forms/d/e/1FAIpQLScKW2pkcWll3deXEwoV_G5ozLtuU06_prw8rf8HFuCk4tmOPQ/viewform?usp=sf_link"
|
||||||
|
style="color:rgb(255, 255, 255)" target="_blank">Why not share a feedback while waiting?</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="loading_screen" style="position: absolute;top:100px;left:0;right:0;width: fit-content;margin-inline: auto;height:200px;">
|
<div id="loading_screen" style="position: absolute;top:100px;left:0;right:0;width: fit-content;margin-inline: auto;height:200px;">
|
||||||
<img style="height:200px;width:218.6px" src="https://raw.githubusercontent.com/specklesystems/pygeoapi/dev/pygeoapi/static/img/speckle_cube_loading.gif" alt="Loading data.." >
|
<img style="height:200px;width:218.6px" src="https://raw.githubusercontent.com/specklesystems/pygeoapi/dev/pygeoapi/static/img/speckle_cube_loading_winter.gif" alt="Loading data.." >
|
||||||
<h3 style="margin-bottom: 0px;font-family: 'Verdana'; text-align: center; color:rgb(40, 127, 209);font-size: x-large;">"I'm on my way!"</h3>
|
<h3 style="margin-bottom: 0px;text-align: center; color:rgb(40, 127, 209);font-size: x-large;">"I'm on my way!"</h3>
|
||||||
<p style="font-family: 'Verdana'; text-align: right; color:rgb(40, 127, 209)">- your data </p>
|
<p style="text-align: right; color:rgb(40, 127, 209)">- your data </p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ Babel
|
|||||||
click
|
click
|
||||||
filelock
|
filelock
|
||||||
Flask
|
Flask
|
||||||
|
geopy==2.4.1
|
||||||
jinja2
|
jinja2
|
||||||
jsonschema
|
jsonschema
|
||||||
pydantic<2.0
|
pydantic<2.0
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// construnt data URL
|
// construnt data URL
|
||||||
var link = "https://geo.speckle.systems/?speckleUrl=";
|
var link = "https://geo.speckle.systems/speckle/?speckleUrl=";
|
||||||
if (document.getElementById("speckle_model").value!=""){
|
if (document.getElementById("speckle_model").value!=""){
|
||||||
link += document.getElementById("speckle_model").value.replace(" ", "")
|
link += document.getElementById("speckle_model").value.replace(" ", "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
));
|
));
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const speckle_data = await fetch('https://geo.speckle.systems/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=projectcomments', {
|
const speckle_data = await fetch('https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=projectcomments', {
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/geo+json'
|
'Accept': 'application/geo+json'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
));
|
));
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const speckle_data = await fetch('https://geo.speckle.systems/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=polygons', {
|
const speckle_data = await fetch('https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=polygons', {
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/geo+json'
|
'Accept': 'application/geo+json'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const speckle_data2 = await fetch('https://geo.speckle.systems/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/37d93c5d32&preserveAttributes=true', {
|
const speckle_data2 = await fetch('https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/37d93c5d32&preserveAttributes=true', {
|
||||||
//const speckle_data = await fetch('http://localhost:5000/?speckleUrl=https://app.speckle.systems/projects/64753f52b7/models/338b386787&limit=1000000&lat=-0.031405&lon=109.335828&preserveAttributes=true', {
|
//const speckle_data = await fetch('http://localhost:5000/?speckleUrl=https://app.speckle.systems/projects/64753f52b7/models/338b386787&limit=1000000&lat=-0.031405&lon=109.335828&preserveAttributes=true', {
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/geo+json'
|
'Accept': 'application/geo+json'
|
||||||
@@ -155,7 +155,7 @@
|
|||||||
|
|
||||||
//map.fitBounds(speckle_layer.getBounds())
|
//map.fitBounds(speckle_layer.getBounds())
|
||||||
|
|
||||||
const speckle_data = await fetch('https://geo.speckle.systems/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=projectcomments', {
|
const speckle_data = await fetch('https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=projectcomments', {
|
||||||
//const speckle_data = await fetch('http://localhost:5000/?speckleUrl=https://app.speckle.systems/projects/64753f52b7/models/338b386787&limit=1000000&lat=-0.031405&lon=109.335828&preserveAttributes=true', {
|
//const speckle_data = await fetch('http://localhost:5000/?speckleUrl=https://app.speckle.systems/projects/64753f52b7/models/338b386787&limit=1000000&lat=-0.031405&lon=109.335828&preserveAttributes=true', {
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/geo+json'
|
'Accept': 'application/geo+json'
|
||||||
|
|||||||
@@ -30,12 +30,12 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
//const speckle_data = await fetch('https://geo.speckle.systems/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=projectcomments', {
|
//const speckle_data = await fetch('https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=projectcomments', {
|
||||||
//var speckle_url = 'http://localhost:5000/?speckleUrl=https://app.speckle.systems/projects/5feae56049/models/9c43d7569c&limit=1000000&datatype=polygons&preserveattributes=false';
|
//var speckle_url = 'http://localhost:5000/speckle/?speckleUrl=https://app.speckle.systems/projects/5feae56049/models/9c43d7569c&limit=1000000&datatype=polygons&preserveattributes=false';
|
||||||
// https://app.speckle.systems/projects/5feae56049/models/01c4183677
|
// https://app.speckle.systems/projects/5feae56049/models/01c4183677
|
||||||
var speckle_url = 'http://localhost:5000/?speckleUrl=https://app.speckle.systems/projects/5feae56049/models/01c4183677&limit=1000000&datatype=polygons&preserveattributes=true';
|
var speckle_url = 'https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/5feae56049/models/01c4183677&limit=1000000&datatype=polygons&preserveattributes=true';
|
||||||
|
|
||||||
//var speckle_url = 'https://geo.speckle.systems/?speckleUrl=https://app.speckle.systems/projects/5feae56049/models/9c43d7569c&northDegrees=-30&preserveAttributes=true';
|
//var speckle_url = 'https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/5feae56049/models/9c43d7569c&northDegrees=-30&preserveAttributes=true';
|
||||||
const speckle_data = await fetch(speckle_url, {
|
const speckle_data = await fetch(speckle_url, {
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/geo+json'
|
'Accept': 'application/geo+json'
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
|
|
||||||
//////// add Speckle layer
|
//////// add Speckle layer
|
||||||
(async () => {
|
(async () => {
|
||||||
const geojson = await fetch('https://geo.speckle.systems/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=polygons', {
|
const geojson = await fetch('https://geo.speckle.systems/speckle/?speckleUrl=https://app.speckle.systems/projects/344f803f81/models/5582ab673e&datatype=polygons', {
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/geo+json'
|
'Accept': 'application/geo+json'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user