Skip to content

Commit dccb0ac

Browse files
committed
02/26 optimize projection algorithm more.
1 parent cbede11 commit dccb0ac

4 files changed

Lines changed: 145 additions & 50 deletions

File tree

pygeoapi-config.yml

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,24 @@ logging:
2525

2626
metadata:
2727
identification:
28-
title: PNU STEMLab IndoorGML API
28+
title: IndoorFeature API Demonstration
2929
description: Developing IndoorGML and IndoorJSON support for OGC API Standards.
3030
keywords:
3131
- IndoorGML
3232
- PNU
33-
- STEMLab
33+
- AIST
34+
- OGC
3435
terms_of_service: https://creativecommons.org/licenses/by/4.0/ # Add this line!
3536
url: http://localhost:5000
3637
provider:
37-
name: PNU STEMLab
38+
name: PNU STEMLab and AIST IPRI
3839
url: https://github.com/STEMLab
3940
license:
4041
name: MIT
4142
url: https://opensource.org/licenses/MIT
4243
contact:
43-
name: Tonny
44+
name: Taehoon KIM
45+
email: kim.taehoon@aist.go.jp
4446
url: https://github.com/STEMLab/IndoorGML_API
4547

4648
resources:
@@ -245,10 +247,3 @@ resources:
245247
type: process
246248
processor:
247249
name: HelloWorld
248-
249-
database:
250-
host: localhost
251-
port: 5432
252-
database: indoordb
253-
username: user
254-
password: password

pygeoapi/provider/postgresql_indoordb.py

Lines changed: 137 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,8 +1094,8 @@ def _post_primal_members(self, collection_pk:int, feature_pk:int, layer_pk:int,
10941094
sql = """
10951095
INSERT INTO cell_space_n_boundary
10961096
(id_str, type, collection_id, indoorfeature_id, thematiclayer_id,
1097-
cell_name, level, "2D_geometry","3D_geometry", poi)
1098-
VALUES (%s, 'space', %s, %s, %s, %s, %s, ST_GeomFromText(%s, 0), %s, %s)
1097+
cell_name, level, "2D_geometry","3D_geometry", poi, external_reference)
1098+
VALUES (%s, 'space', %s, %s, %s, %s, %s, ST_GeomFromText(%s, 0), %s, %s, %s)
10991099
RETURNING id
11001100
"""
11011101
cur.execute(sql, (
@@ -1107,7 +1107,8 @@ def _post_primal_members(self, collection_pk:int, feature_pk:int, layer_pk:int,
11071107
str(cell.get('level')),
11081108
self.json_to_wkt(geom_2d),
11091109
json.dumps(geom_3d),
1110-
cell.get('poi')
1110+
cell.get('poi'),
1111+
json.dumps(cell.get('externalReference')) if cell.get('externalReference') else None
11111112
))
11121113
# Store cell pk for duality
11131114
cell_pk = cur.fetchone()
@@ -1124,7 +1125,7 @@ def _post_primal_members(self, collection_pk:int, feature_pk:int, layer_pk:int,
11241125
geom_raw = bound.get('cellBoundaryGeom', {})
11251126
geom_2d = geom_raw.get('geometry2D', None)
11261127
geom_3d = geom_raw.get('geometry3D', None)
1127-
1128+
external_ref = json.dumps(bound.get('externalReference')) if bound.get('externalReference') else None
11281129
duplicate_sql = """
11291130
SELECT n.id_str
11301131
FROM node_n_edge n
@@ -1142,8 +1143,8 @@ def _post_primal_members(self, collection_pk:int, feature_pk:int, layer_pk:int,
11421143
sql = """
11431144
INSERT INTO cell_space_n_boundary
11441145
(id_str, type, collection_id, indoorfeature_id, thematiclayer_id,
1145-
is_virtual, "2D_geometry", "3D_geometry", bounded_by_cell_id)
1146-
VALUES (%s, 'boundary', %s, %s, %s, %s, ST_GeomFromText(%s, 0), %s, %s)
1146+
is_virtual, "2D_geometry", "3D_geometry", bounded_by_cell_id, external_reference)
1147+
VALUES (%s, 'boundary', %s, %s, %s, %s, ST_GeomFromText(%s, 0), %s, %s, %s)
11471148
RETURNING id
11481149
"""
11491150
cur.execute(sql, (
@@ -1154,7 +1155,8 @@ def _post_primal_members(self, collection_pk:int, feature_pk:int, layer_pk:int,
11541155
bound.get('isVirtual', False),
11551156
self.json_to_wkt(geom_2d),
11561157
json.dumps(geom_3d),
1157-
boundingCell
1158+
boundingCell,
1159+
external_ref
11581160
))
11591161

11601162
# Store boundary pk for duality
@@ -1224,7 +1226,82 @@ def _post_primal_members(self, collection_pk:int, feature_pk:int, layer_pk:int,
12241226
WHERE c.id = u.id
12251227
AND c.thematiclayer_id = %s;
12261228
"""
1227-
cur.execute(sql_project_shell,(layer_pk,layer_pk))
1229+
sql_project_shell = """
1230+
WITH targets AS (
1231+
SELECT id, "3D_geometry"
1232+
FROM cell_space_n_boundary
1233+
WHERE thematiclayer_id = %s
1234+
AND type='space'
1235+
AND "2D_geometry" IS NULL
1236+
AND "3D_geometry" IS NOT NULL
1237+
),
1238+
faces AS (
1239+
SELECT
1240+
t.id,
1241+
s.shell_idx,
1242+
f.face
1243+
FROM targets t
1244+
CROSS JOIN LATERAL jsonb_array_elements(t."3D_geometry"->'coordinates')
1245+
WITH ORDINALITY AS s(shell, shell_idx)
1246+
CROSS JOIN LATERAL jsonb_array_elements(s.shell) AS f(face)
1247+
),
1248+
face_z AS (
1249+
SELECT
1250+
id, shell_idx, face,
1251+
(SELECT min((pt->>2)::float) FROM jsonb_array_elements(face) pt) AS zmin,
1252+
(SELECT max((pt->>2)::float) FROM jsonb_array_elements(face) pt) AS zmax
1253+
FROM faces
1254+
),
1255+
floor_faces AS (
1256+
-- horizontal faces only + choose the global minimum z (floor) per id
1257+
SELECT f.*
1258+
FROM face_z f
1259+
JOIN (
1260+
SELECT id, min(zmin) AS floor_z
1261+
FROM face_z
1262+
WHERE zmin = zmax
1263+
GROUP BY id
1264+
) m USING (id)
1265+
WHERE f.zmin = f.zmax
1266+
AND f.zmin = m.floor_z
1267+
),
1268+
proj AS (
1269+
SELECT
1270+
id,
1271+
shell_idx,
1272+
ST_Force2D(
1273+
ST_GeomFromGeoJSON(
1274+
jsonb_build_object(
1275+
'type','Polygon',
1276+
'coordinates',
1277+
CASE
1278+
WHEN jsonb_typeof(face->0->0) = 'array' THEN face
1279+
ELSE jsonb_build_array(face)
1280+
END
1281+
)::text
1282+
)
1283+
) AS g2d
1284+
FROM floor_faces
1285+
),
1286+
u AS (
1287+
SELECT
1288+
id,
1289+
ST_UnaryUnion(ST_Collect(g2d) FILTER (WHERE shell_idx = 1)) AS ext2d,
1290+
ST_UnaryUnion(ST_Collect(g2d) FILTER (WHERE shell_idx > 1)) AS int2d
1291+
FROM proj
1292+
GROUP BY id
1293+
)
1294+
UPDATE cell_space_n_boundary c
1295+
SET "2D_geometry" = ST_SetSRID(
1296+
CASE
1297+
WHEN u.int2d IS NULL THEN u.ext2d
1298+
ELSE ST_Difference(u.ext2d, u.int2d)
1299+
END
1300+
, 0)
1301+
FROM u
1302+
WHERE c.id = u.id;
1303+
"""
1304+
cur.execute(sql_project_shell,(layer_pk,))
12281305

12291306
return dual_cell, dual_boundary
12301307

@@ -1915,15 +1992,15 @@ def post_primal_member(self, collection_id:str, feature_id:str, layer_id:str, da
19151992
update_bounded_by.append(row.id)
19161993

19171994
geom_root = data.get('cellSpaceGeom', {})
1918-
geom_2d_wkt = self.json_to_wkt(geom_root['geometry2D'])
1919-
geom_3d_json = json.dumps(geom_root['geometry3D'])
1995+
geom_2d_wkt = self.json_to_wkt(geom_root['geometry2D']) if geom_root.get('geometry2D') else None
1996+
geom_3d_json = json.dumps(geom_root['geometry3D']) if geom_root.get('geometry3D') else None
19201997

19211998
elif f_type == 'CellBoundary':
19221999
db_type = 'boundary'
19232000
is_virtual = data.get('isVirtual', False)
19242001
geom_root = data.get('cellBoundaryGeom', {})
1925-
geom_2d_wkt = self.json_to_wkt(geom_root.get('geometry2D'))
1926-
geom_3d_json = json.dumps(geom_root.get('geometry3D'))
2002+
geom_2d_wkt = self.json_to_wkt(geom_root.get('geometry2D')) if geom_root.get('geometry2D') else None
2003+
geom_3d_json = json.dumps(geom_root.get('geometry3D')) if geom_root.get('geometry3D') else None
19272004

19282005
if duality_raw: # validation check: CellBoundary can have duality to edge which has no duality
19292006
duality_id = duality_raw.split(':')[-1]
@@ -1983,7 +2060,8 @@ def post_primal_member(self, collection_id:str, feature_id:str, layer_id:str, da
19832060
update_bounded_by,
19842061
layer_row[0]
19852062
))
1986-
if f_type == 'CellBoundary' and duality_edge_pk:
2063+
2064+
elif f_type == 'CellBoundary' and duality_edge_pk:
19872065
update_edgeDuality_sql = """
19882066
UPDATE node_n_edge
19892067
SET duality_id = %s
@@ -1994,65 +2072,87 @@ def post_primal_member(self, collection_id:str, feature_id:str, layer_id:str, da
19942072
# project cellspace's 3D geometry to 2D if it has no 2D geometry.
19952073
LOGGER.debug("Project geometry 3D to 2D ")
19962074
sql_project_shell = """
1997-
WITH faces AS (
2075+
WITH targets AS (
2076+
SELECT id, "3D_geometry"
2077+
FROM cell_space_n_boundary
2078+
WHERE id = %s
2079+
AND type='space'
2080+
AND "2D_geometry" IS NULL
2081+
AND "3D_geometry" IS NOT NULL
2082+
),
2083+
faces AS (
19982084
SELECT
1999-
c.id,
2085+
t.id,
20002086
s.shell_idx,
20012087
f.face
2002-
FROM cell_space_n_boundary c
2003-
CROSS JOIN LATERAL jsonb_array_elements(c."3D_geometry"->'coordinates')
2088+
FROM targets t
2089+
CROSS JOIN LATERAL jsonb_array_elements(t."3D_geometry"->'coordinates')
20042090
WITH ORDINALITY AS s(shell, shell_idx)
20052091
CROSS JOIN LATERAL jsonb_array_elements(s.shell) AS f(face)
2006-
WHERE c."3D_geometry" IS NOT NULL
2007-
AND c.type = 'space'
2008-
AND c."2D_geometry" IS NULL
2009-
AND c.thematiclayer_id = %s
2010-
AND (
2011-
s.shell_idx = 1
2012-
OR jsonb_array_length(c."3D_geometry"->'coordinates') > 1
2013-
)
2092+
),
2093+
face_z AS (
2094+
SELECT
2095+
id, shell_idx, face,
2096+
(SELECT min((pt->>2)::float) FROM jsonb_array_elements(face) pt) AS zmin,
2097+
(SELECT max((pt->>2)::float) FROM jsonb_array_elements(face) pt) AS zmax
2098+
FROM faces
2099+
),
2100+
floor_faces AS (
2101+
SELECT f.*
2102+
FROM face_z f
2103+
JOIN (
2104+
SELECT id, min(zmin) AS floor_z
2105+
FROM face_z
2106+
WHERE zmin = zmax
2107+
GROUP BY id
2108+
) m USING (id)
2109+
WHERE f.zmin = f.zmax
2110+
AND f.zmin = m.floor_z
2111+
),
2112+
use_faces AS (
2113+
SELECT * FROM floor_faces
2114+
UNION ALL
2115+
SELECT * FROM face_z
2116+
WHERE NOT EXISTS (SELECT 1 FROM floor_faces)
20142117
),
20152118
proj AS (
20162119
SELECT
20172120
id,
20182121
shell_idx,
2019-
ST_SetSRID(
20202122
ST_Force2D(
20212123
ST_GeomFromGeoJSON(
20222124
jsonb_build_object(
2023-
'type', 'Polygon',
2125+
'type','Polygon',
20242126
'coordinates',
20252127
CASE
2026-
-- if face is already [ring,...], keep it; else wrap to [ring]
20272128
WHEN jsonb_typeof(face->0->0) = 'array' THEN face
20282129
ELSE jsonb_build_array(face)
20292130
END
20302131
)::text
20312132
)
2032-
),
2033-
0
20342133
) AS g2d
2035-
FROM faces
2134+
FROM use_faces
20362135
),
20372136
u AS (
20382137
SELECT
20392138
id,
2040-
ST_UnaryUnion( ST_Collect(g2d) FILTER (WHERE shell_idx = 1) ) AS ext2d,
2041-
ST_UnaryUnion( ST_Collect(g2d) FILTER (WHERE shell_idx > 1) ) AS int2d
2139+
ST_UnaryUnion(ST_Collect(g2d) FILTER (WHERE shell_idx = 1)) AS ext2d,
2140+
ST_UnaryUnion(ST_Collect(g2d) FILTER (WHERE shell_idx > 1)) AS int2d
20422141
FROM proj
20432142
GROUP BY id
20442143
)
20452144
UPDATE cell_space_n_boundary c
2046-
SET "2D_geometry" =
2145+
SET "2D_geometry" = ST_SetSRID(
20472146
CASE
20482147
WHEN u.int2d IS NULL THEN u.ext2d
20492148
ELSE ST_Difference(u.ext2d, u.int2d)
20502149
END
2150+
, 0)
20512151
FROM u
20522152
WHERE c.id = u.id
2053-
AND c.thematiclayer_id = %s;
2054-
"""
2055-
cur.execute(sql_project_shell,(new_internal_id, new_internal_id))
2153+
AND c.id = %s
2154+
"""
2155+
cur.execute(sql_project_shell,(new_internal_id,new_internal_id))
20562156
# 4. FIX: Commit only if we get here successfully
20572157
self.connection.commit()
20582158
return new_str_id

pygeoapi/static/workSpace/api.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,8 @@ export async function routingQuery(colId, featId, tId, sn, dn){
235235
return await response.json();
236236
}
237237

238-
export async function geometricQuery(colId, featId, tId, op, geometry, level){
239-
const url = `${API_BASE}/collections/${colId}/items/${featId}/layers/${tId}/geoquery?op=${op}&geometry=${geometry}&level=${level}`;
238+
export async function geometricQuery(colId, featId, tId, rel, geometry, level){
239+
const url = `${API_BASE}/collections/${colId}/items/${featId}/layers/${tId}/geoquery?rel=${rel}&geometry=${geometry}&level=${level}`;
240240
const response = await fetch(url);
241241
if (!response.ok) throw new Error(`${method} Failed: ${response.status}`);
242242
return await response.json();

0 commit comments

Comments
 (0)