From 6a8b5df87c6259698e02df0e151b43426596d7f7 Mon Sep 17 00:00:00 2001 From: KatKatKateryna <89912278+KatKatKateryna@users.noreply.github.com> Date: Wed, 4 Sep 2024 23:55:19 +0100 Subject: [PATCH] Polishing (#9) * colors and materials * traverse for colors * fix multipolygons in 3d * prioritize attributes by default, less geometry divisions * fix multipolygons * split polygons in leaflet * ensure speckleURL argument * avoid multiple loops * URL exceptions handling * add bbox per feature * leaflet controls to the right * html syntax --- pygeoapi/api/itemtypes.py | 55 ----- pygeoapi/provider/speckle.py | 22 +- .../provider/speckle_utils/display_utils.py | 43 +++- .../provider/speckle_utils/feature_utils.py | 38 ++- pygeoapi/provider/speckle_utils/url_utils.py | 96 +++++--- .../templates/collections/items/index.html | 217 +++++++++++------- pygeoapi/templates/exception.html | 3 + 7 files changed, 285 insertions(+), 189 deletions(-) diff --git a/pygeoapi/api/itemtypes.py b/pygeoapi/api/itemtypes.py index 042e26a..dce1861 100644 --- a/pygeoapi/api/itemtypes.py +++ b/pygeoapi/api/itemtypes.py @@ -574,61 +574,6 @@ def get_collection_items( if isinstance(url_saved_as_data, str): 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 # (if it supports language) and/or otherwise the requested pygeoapi # locale (or fallback default locale) diff --git a/pygeoapi/provider/speckle.py b/pygeoapi/provider/speckle.py index 3442646..8148811 100644 --- a/pygeoapi/provider/speckle.py +++ b/pygeoapi/provider/speckle.py @@ -125,15 +125,17 @@ class SpeckleProvider(BaseProvider): self.crs_dict = None self.requested_data_type: str = "polygons (default)" # points, lines, polygons, projectcomments - self.preserve_attributes: str = "false (default)" - + self.preserve_attributes: str = "true (default)" self.lat: float = 48.76755913928929 #51.52486388756923 self.lon: float = 11.408741923664028 #0.1621445437168942 self.north_degrees: float = 0 - self.extent = [-180,-90,180,90] + self.crs_authid = "" self.limit = 10000 + + self.missing_url = "" self.limit_message = "" + self.extent = [-180,-90,180,90] self.material_color_proxies = {} @@ -173,7 +175,7 @@ class SpeckleProvider(BaseProvider): if self.data == "": 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) new_request = False @@ -266,6 +268,18 @@ class SpeckleProvider(BaseProvider): if data is None: 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"]) if resulttype == "hits": diff --git a/pygeoapi/provider/speckle_utils/display_utils.py b/pygeoapi/provider/speckle_utils/display_utils.py index 96e0b04..8408ae8 100644 --- a/pygeoapi/provider/speckle_utils/display_utils.py +++ b/pygeoapi/provider/speckle_utils/display_utils.py @@ -168,9 +168,11 @@ def get_single_display_object(displayValForColor: List) -> "Base": displayValForColor = item mesh = Mesh.create(faces= faces, vertices=verts, colors=colors) - for prop in displayValForColor[0].get_member_names(): - if prop not in ["colors", "vertices", "faces"]: - mesh[prop] = getattr(displayValForColor[0], prop) + + if len(displayValForColor)>0: + for prop in displayValForColor[0].get_member_names(): + if prop not in ["colors", "vertices", "faces"]: + mesh[prop] = getattr(displayValForColor[0], prop) displayValForColor = mesh return displayValForColor @@ -258,10 +260,35 @@ def set_default_color(context_list: List["TraversalContext"]) -> None: DEFAULT_COLOR = (255 << 24) + (10 << 16) + (132 << 8) + 255 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.""" - from specklepy.objects.geometry import Base, Mesh, Brep + from specklepy.objects.geometry import Mesh, Brep + + 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] @@ -274,6 +301,8 @@ def assign_color(self: "SpeckleProvider", obj_display, props) -> None: color = DEFAULT_COLOR opacity = None + obj_display = obj_display_tc.current + try: # prioritize renderMaterials for Meshes & Brep if isinstance(obj_display, Mesh) or isinstance(obj_display, Brep): @@ -336,12 +365,12 @@ def get_r_g_b(rgb: int) -> Tuple[int, int, int]: a = 255 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.""" 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"] # other properties for rendering diff --git a/pygeoapi/provider/speckle_utils/feature_utils.py b/pygeoapi/provider/speckle_utils/feature_utils.py index 992afb5..a7697ec 100644 --- a/pygeoapi/provider/speckle_utils/feature_utils.py +++ b/pygeoapi/provider/speckle_utils/feature_utils.py @@ -10,6 +10,9 @@ 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.display_utils import find_display_obj, assign_display_properties, find_list_of_display_obj + from specklepy.objects.graph_traversal.traversal import TraversalContext + from specklepy.objects.other import Collection + print(f"Creating features..") time1 = datetime.now() @@ -19,6 +22,9 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d if self.requested_data_type != "projectcomments": 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: self.limit_message = f" (feature count limited to {self.limit})" break @@ -28,9 +34,13 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d f_fid = feature_count + 1 # initialize feature + speckle_type = item.current.speckle_type + if ":" in speckle_type: + speckle_type = speckle_type.split(":")[-1] + feature: Dict = { "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": {}, "displayProperties":{ "object_type": "geometry", @@ -38,7 +48,7 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d "properties": { "id": f_id, "FID": f_fid, - "speckle_type": item.current.speckle_type.split(":")[-1], + "speckle_type": speckle_type, }, } @@ -67,8 +77,11 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d if prop not in all_props: 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["bbox"] = get_feature_bbox(coords) data["features"].append(feature) feature_count += 1 @@ -81,7 +94,7 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d f_fid = feature_count + 1 feature_new: Dict = { "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": {}, "displayProperties":{ "object_type": "geometry", @@ -107,11 +120,14 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d all_coords.extend(coords) all_coord_counts.append(coord_counts) - assign_display_properties(self, feature_new, f_base, obj_get_color) + obj_get_color_tc = TraversalContext(obj_get_color, "", 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["bbox"] = get_feature_bbox(coords) data["features"].append(feature_new) feature_count +=1 - + assign_missing_props(data["features"], all_props) else: ####################### create comment features @@ -158,6 +174,16 @@ def initialize_features(self: "SpeckleProvider", all_coords, all_coord_counts, d time2 = datetime.now() print(f"Creating features time: {(time2-time1).total_seconds()}") +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): """Create html text to display for the thread.""" diff --git a/pygeoapi/provider/speckle_utils/url_utils.py b/pygeoapi/provider/speckle_utils/url_utils.py index 7365295..21bdab6 100644 --- a/pygeoapi/provider/speckle_utils/url_utils.py +++ b/pygeoapi/provider/speckle_utils/url_utils.py @@ -1,65 +1,89 @@ -from typing import Dict +import inspect def get_set_url_parameters(self: "SpeckleProvider"): + """Parse and save URL parameters.""" from pygeoapi.provider.speckle_utils.crs_utils import create_crs_from_authid - - if ( - isinstance(self.data, str) - and "speckleurl=" in self.data.lower() - and "projects" in self.data - and "models" in self.data - ): - crs_authid = "" + + crsauthid = False + + if (isinstance(self.data, str)): + for item in self.data.lower().split("&"): - # if CRS authid is found, rest will be ignored - if "datatype=" in item: + if "speckleurl=" in item: try: - self.requested_data_type = item.split("datatype=")[1] - if self.requested_data_type not in ["points", "lines", "polygons", "projectcomments"]: - self.requested_data_type = "polygons (default)" + speckle_url = item.split("speckleurl=")[1] + if "/projects/" not in speckle_url or "/models/" not in speckle_url: + 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: - 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 + except: + raise ValueError(f"Provide valid dataType parameter (points/lines/polygons/projectcomments): {item}") + elif "preserveattributes=" in item: try: - self.preserve_attributes = item.split("preserveattributes=")[1] - if self.preserve_attributes not in ["true", "false"]: - self.preserve_attributes = "false (default)" + preserve_attributes = item.split("preserveattributes=")[1] + if preserve_attributes in ["true", "false"]: + self.preserve_attributes = preserve_attributes 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: crs_authid = item.split("crsauthid=")[1] + if isinstance(crs_authid, str) and len(crs_authid)>3: + crsauthid = True + self.crs_authid = crs_authid + elif "lat=" in item: try: - self.lat = float(item.split("lat=")[1]) + lat = float(item.split("lat=")[1]) + self.lat = lat except: - pass - # raise ValueError(f"Invalid Lat input, must be numeric: {item.split('lat=')[1]}") + raise ValueError(f"Invalid Lat input, must be numeric: {item}") elif "lon=" in item: try: - self.lon = float(item.split("lon=")[1]) + lon = float(item.split("lon=")[1]) + self.lon = lon except: - pass - # raise ValueError(f"Invalid Lon input, must be numeric: {item.split('lon=')[1]}") + raise ValueError(f"Invalid Lon input, must be numeric: {item}") elif "northdegrees=" in item: try: - self.north_degrees = float(item.split("northdegrees=")[1]) + north_degrees = float(item.split("northdegrees=")[1]) + self.north_degrees = north_degrees except: - pass - # raise ValueError(f"Invalid NorthDegrees input, must be numeric: {item.split('northdegrees=')[1]}") + raise ValueError(f"Invalid northDegrees input, must be numeric: {item}") + elif "limit=" in item: + try: + limit = int(item.split("limit=")[1]) + if limit>0: + self.limit = limit + 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 = str(self.north_degrees) + " (not applied)" # if CRS parameter present, create and assign CRS: - if len(crs_authid)>3: + if len(self.crs_authid)>3: create_crs_from_authid(self) diff --git a/pygeoapi/templates/collections/items/index.html b/pygeoapi/templates/collections/items/index.html index ea4f273..5d371c4 100644 --- a/pygeoapi/templates/collections/items/index.html +++ b/pygeoapi/templates/collections/items/index.html @@ -15,7 +15,7 @@
- {% if not data['missing_url'] %} + {% if data['speckle_url'] %}
@@ -53,7 +53,7 @@

- {% if data['missing_url'] %} + {% if not data['speckle_url'] %}

@@ -357,67 +357,97 @@ } } + 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 geojson_data = {{ data['features'] | to_json | safe }}; - + var geojson_data_original = {{ data['features'] | to_json | safe }}; + // Leaflet 2d map function initialize2d() { - var map = L.map('map2d').setView([45, 0], 2); - map.addLayer(new L.TileLayer( - '{{ config['server']['map']['url'] }}', { - maxZoom: 22, - attribution: '{{ config['server']['map']['attribution'] | safe }} © Data: Speckle Systems' - } - )); + var map = L.map('map2d', {zoomControl: false}).setView([45, 0], 2); + L.control.zoom({ + position: 'topright' + }).addTo(map); + map.addLayer(new L.TileLayer( + '{{ config['server']['map']['url'] }}', { + maxZoom: 22, + attribution: '{{ config['server']['map']['attribution'] | safe }} © Data: Speckle Systems' + } + )); - var items = new L.GeoJSON(geojson_data, { - filter: (feature) => { - return feature.displayProperties["object_type"] == "geometry" - }, - pointToLayer: (feature, latlng) => { - return new L.circleMarker(latlng) - }, - onEachFeature: function (feature, layer) { - var url = '{{ data['speckle_project_url'] }}/models/' + feature.id.split("_")[0] - var html = '

' + feature['properties']['speckle_type'] + '

' + feature['properties']['id'].split("_")[0] + '
'; - layer.bindPopup(html); + geojson_data = split_polygons(geojson_data_original); - var myFillColor = feature.displayProperties['color']; - var mylineWeight = feature.displayProperties['lineWidth']; - var myRadius = feature.displayProperties['radius']; + var items = new L.GeoJSON(geojson_data, { + filter: (feature) => { + return feature.displayProperties["object_type"] == "geometry" + }, + pointToLayer: (feature, latlng) => { + return new L.circleMarker(latlng) + }, + onEachFeature: function (feature, layer) { + var url = '{{ data['speckle_project_url'] }}/models/' + feature.id.split("_")[0] + var html = '

' + feature['properties']['speckle_type'] + '

' + feature['properties']['id'].split("_")[0] + '
'; + layer.bindPopup(html); - layer.setStyle({ - 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 = '{{ data['speckle_project_url'] }}/models/' + feature.properties.resource_id + '#threadId=' + feature.id; - var html = 'Go to thread

' + feature['properties']['text_html'] + '

'; - layer.bindPopup(html); - } - }); //.addTo(map); + var myFillColor = feature.displayProperties['color']; + var mylineWeight = feature.displayProperties['lineWidth']; + var myRadius = feature.displayProperties['radius']; - var group = L.featureGroup([items, comments]) - .addTo(map); + layer.setStyle({ + 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 = '{{ data['speckle_project_url'] }}/models/' + feature.properties.resource_id + '#threadId=' + feature.id; + var html = 'Go to thread

' + feature['properties']['text_html'] + '

'; + layer.bindPopup(html); + } + }); //.addTo(map); - //map.addLayer(lines); + var group = L.featureGroup([items, comments]) + .addTo(map); - map.fitBounds(group.getBounds()); - // map.setZoom(19); // in order for the tiles to load + //map.addLayer(lines); + + map.fitBounds(group.getBounds()); + // map.setZoom(19); // in order for the tiles to load }; @@ -504,11 +534,20 @@ coords = feature.geometry.coordinates; 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++) { + + // check orientation of each PolygonPart, if vertical - shift points slightly + polygon_part = []; + inner = false; + for (let c = 0; c < coords[p].length; c++) { + polygon_part_loop = []; + if (c>0){ + inner = true; + } sum_orientation = 0; polygon_pts = coords[p][c]; // usually 3 for Mesh faces @@ -521,7 +560,9 @@ }; 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][1] += 0.0000001; @@ -530,46 +571,60 @@ coords[p][c][2][0] += 0.0000001; coords[p][c][2][1] += 0.0000001; + if(polygon_pts.length==3) { createdPolygon = true; - multipolygon_coords = [coords[p][c]]; - polygons.push({"id": speckle_features.length, "type":"Feature", - "geometry": {"type": "MultiPolygon", "coordinates":[multipolygon_coords]}, - "properties": speckle_data.features[i].properties, - "displayProperties": speckle_data.features[i].displayProperties }); + multipolygon_coords = coords[p][c]; + polygon_part_loop = multipolygon_coords; + + polygon_part = [polygon_part_loop]; + polygon_all_parts.push(polygon_part); } else if (polygon_pts.length==4) { createdPolygon = true; - multipolygon_coords = [coords[p][c].slice(0,3)]; - polygons.push({"id": speckle_features.length, "type":"Feature", - "geometry": {"type": "MultiPolygon", "coordinates":[multipolygon_coords]}, - "properties": speckle_data.features[i].properties, - "displayProperties": speckle_data.features[i].displayProperties }); + multipolygon_coords = coords[p][c].slice(0,3); + polygon_part_loop = multipolygon_coords; - multipolygon_coords = [[coords[p][c][2], coords[p][c][3], coords[p][c][0]]]; - polygons.push({"id": speckle_features.length, "type":"Feature", - "geometry": {"type": "MultiPolygon", "coordinates":[multipolygon_coords]}, - "properties": speckle_data.features[i].properties , - "displayProperties": speckle_data.features[i].displayProperties }); + polygon_part = [polygon_part_loop]; + polygon_all_parts.push(polygon_part); + + ///////// + 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){ - multipolygon_coords = [coords[p][c]]; - polygons.push({"id": speckle_features.length, "type":"Feature", - "geometry": {"type": "MultiPolygon", "coordinates":[multipolygon_coords]}, - "properties": speckle_data.features[i].properties, - "displayProperties": speckle_data.features[i].displayProperties }); + if (createdPolygon == false){ // if non-vertical, or vertical with more than 4 vertices + + multipolygon_coords = coords[p][c]; + polygon_part_loop = multipolygon_coords; + + 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 - speckle_features.push(element); - }); + + new_polygon = {"id": speckle_features.length, "type":"Feature", + "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") diff --git a/pygeoapi/templates/exception.html b/pygeoapi/templates/exception.html index e0bc3b2..ff4c80a 100644 --- a/pygeoapi/templates/exception.html +++ b/pygeoapi/templates/exception.html @@ -5,4 +5,7 @@

{% trans %}Exception{% endtrans %}

{{ data['description'] }}

+ {% endblock %}