661 lines
22 KiB
Python
661 lines
22 KiB
Python
import json
|
|
from collections import Counter, defaultdict
|
|
from pathlib import Path
|
|
|
|
import pymysql
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
STYLE_JSON_PATH = ROOT / "src" / "pbf" / "style.json"
|
|
|
|
MANUAL_OBJECT_RULES = {
|
|
"P754ククリ": {
|
|
"family": "line",
|
|
"allowed_geom_types": ["LineString", "MultiLineString"],
|
|
"notes": "Source-preserved clip/outline layer.",
|
|
},
|
|
"P危険界ククリ": {
|
|
"family": "line",
|
|
"allowed_geom_types": ["LineString", "MultiLineString"],
|
|
"notes": "Source-preserved clip/outline layer.",
|
|
},
|
|
"P基本線ククリ": {
|
|
"family": "line",
|
|
"allowed_geom_types": ["LineString", "MultiLineString"],
|
|
"notes": "Source-preserved clip/outline layer.",
|
|
},
|
|
"P投錨注意障害物ククリ": {
|
|
"family": "line",
|
|
"allowed_geom_types": ["LineString", "MultiLineString"],
|
|
"notes": "Source-preserved clip/outline layer.",
|
|
},
|
|
"P施設・境界線等ククリ": {
|
|
"family": "line",
|
|
"allowed_geom_types": ["LineString", "MultiLineString"],
|
|
"notes": "Source-preserved clip/outline layer.",
|
|
},
|
|
"P航路ククリ": {
|
|
"family": "line",
|
|
"allowed_geom_types": ["LineString", "MultiLineString"],
|
|
"notes": "Source-preserved clip/outline layer.",
|
|
},
|
|
"P穴": {
|
|
"family": "surface",
|
|
"allowed_geom_types": ["Polygon", "MultiPolygon"],
|
|
"notes": "Source-preserved area layer.",
|
|
},
|
|
"P陸域": {
|
|
"family": "surface",
|
|
"allowed_geom_types": ["Polygon", "MultiPolygon"],
|
|
"notes": "Source-preserved area layer.",
|
|
},
|
|
"L海底地形": {
|
|
"family": "line",
|
|
"allowed_geom_types": ["LineString", "MultiLineString"],
|
|
"notes": "Source-preserved bathymetry line layer.",
|
|
},
|
|
"p地名": {
|
|
"family": "symbol",
|
|
"allowed_geom_types": ["Point"],
|
|
"notes": "Source-preserved place label layer.",
|
|
},
|
|
"p地名陸": {
|
|
"family": "symbol",
|
|
"allowed_geom_types": ["Point"],
|
|
"notes": "Source-preserved place label layer.",
|
|
},
|
|
"p高さ制限": {
|
|
"family": "symbol",
|
|
"allowed_geom_types": ["Point"],
|
|
"notes": "Source-preserved point annotation layer.",
|
|
},
|
|
"p錨泊地等": {
|
|
"family": "symbol",
|
|
"allowed_geom_types": ["Point"],
|
|
"notes": "Source-preserved point annotation layer.",
|
|
},
|
|
"サンドウェーブ": {
|
|
"family": "symbol",
|
|
"allowed_geom_types": ["Point"],
|
|
"notes": "Same object appears in multiple source layers but remains a point hazard.",
|
|
},
|
|
"海底設置物、放水口、取水口": {
|
|
"family": "symbol",
|
|
"allowed_geom_types": ["Point"],
|
|
"notes": "Stable point object despite multiple source layers.",
|
|
},
|
|
"全沈没船 (危険なし)": {
|
|
"family": "symbol",
|
|
"allowed_geom_types": ["Point"],
|
|
"notes": "Stable point object despite multiple source layers.",
|
|
},
|
|
"測定済みの沈船": {
|
|
"family": "symbol",
|
|
"allowed_geom_types": ["Point"],
|
|
"notes": "Stable point object despite multiple source layers.",
|
|
},
|
|
"道路": {
|
|
"family": "line",
|
|
"allowed_geom_types": ["LineString", "MultiLineString"],
|
|
"notes": "Road centerlines legitimately appear in multi-part line form.",
|
|
},
|
|
"等深線": {
|
|
"family": "line",
|
|
"allowed_geom_types": ["LineString", "MultiLineString"],
|
|
"notes": "Depth contours legitimately appear in multi-part line form.",
|
|
},
|
|
"防波堤": {
|
|
"family": "surface",
|
|
"allowed_geom_types": ["Polygon", "MultiPolygon"],
|
|
"notes": "Breakwaters are modeled as areas, including multipart polygons.",
|
|
},
|
|
"障害物": {
|
|
"family": "mixed",
|
|
"allowed_geom_types": ["Point", "Polygon", "MultiPolygon"],
|
|
"notes": "Obstacle objects can be rendered as symbols or area extents.",
|
|
},
|
|
"未測海域": {
|
|
"family": "mixed",
|
|
"allowed_geom_types": ["LineString", "Polygon", "MultiPolygon"],
|
|
"notes": "Unsurveyed regions appear as boundaries and areas.",
|
|
},
|
|
"浮施設・桟橋": {
|
|
"family": "mixed",
|
|
"allowed_geom_types": ["LineString", "MultiLineString", "Polygon", "MultiPolygon"],
|
|
"notes": "Floating facilities and piers appear as line and area geometry.",
|
|
},
|
|
"航路 (法律による航路)": {
|
|
"family": "mixed",
|
|
"allowed_geom_types": ["LineString", "Polygon", "MultiPolygon"],
|
|
"notes": "Legally defined routes appear as lines and areas.",
|
|
},
|
|
"険悪物": {
|
|
"family": "mixed",
|
|
"allowed_geom_types": ["Point", "Polygon", "MultiPolygon"],
|
|
"notes": "Hazards may be represented as point symbols or area extents.",
|
|
},
|
|
"魚礁": {
|
|
"family": "mixed",
|
|
"allowed_geom_types": ["Point", "Polygon", "MultiPolygon"],
|
|
"notes": "Artificial reefs may be represented as point symbols or area extents.",
|
|
},
|
|
"養殖場": {
|
|
"family": "surface",
|
|
"allowed_geom_types": ["Polygon", "MultiPolygon"],
|
|
"notes": "Aquaculture zones are area features.",
|
|
},
|
|
"漁網": {
|
|
"family": "surface",
|
|
"allowed_geom_types": ["Polygon", "MultiPolygon"],
|
|
"notes": "Fishing nets are area extents in this dataset.",
|
|
},
|
|
"錨泊 (指定)地": {
|
|
"family": "mixed",
|
|
"allowed_geom_types": ["Point", "Polygon", "MultiPolygon"],
|
|
"notes": "Anchorage can appear as symbol or area depending on source layer.",
|
|
},
|
|
"ケーソン仮置き場": {
|
|
"family": "surface",
|
|
"allowed_geom_types": ["Polygon", "MultiPolygon"],
|
|
"notes": "Caisson storage areas are polygons.",
|
|
},
|
|
"土砂捨て場": {
|
|
"family": "surface",
|
|
"allowed_geom_types": ["Polygon", "MultiPolygon"],
|
|
"notes": "Spoil grounds are polygons.",
|
|
},
|
|
}
|
|
|
|
DEFAULT_GEOMETRIES = {
|
|
"symbol": ["Point"],
|
|
"line": ["LineString", "MultiLineString"],
|
|
"surface": ["Polygon", "MultiPolygon"],
|
|
"mixed": [],
|
|
}
|
|
|
|
|
|
def connect():
|
|
return pymysql.connect(
|
|
host="localhost",
|
|
user="root",
|
|
password="2chi9ks2",
|
|
database="pbf_analysis",
|
|
charset="utf8mb4",
|
|
unix_socket="/tmp/mysql.sock",
|
|
autocommit=False,
|
|
)
|
|
|
|
|
|
def load_style_source_layers():
|
|
style = json.loads(STYLE_JSON_PATH.read_text(encoding="utf-8"))
|
|
layers = {}
|
|
for layer in style.get("layers", []):
|
|
source_layer = layer.get("source-layer")
|
|
if not source_layer:
|
|
continue
|
|
layers.setdefault(source_layer, {"style_rows": 0, "layer_types": set()})
|
|
layers[source_layer]["style_rows"] += 1
|
|
if layer.get("type"):
|
|
layers[source_layer]["layer_types"].add(layer["type"])
|
|
return layers
|
|
|
|
|
|
def geom_bucket(geom_type: str | None) -> str:
|
|
if geom_type in {"Point", "MultiPoint"}:
|
|
return "symbol"
|
|
if geom_type in {"LineString", "MultiLineString"}:
|
|
return "line"
|
|
if geom_type in {"Polygon", "MultiPolygon"}:
|
|
return "surface"
|
|
return "mixed"
|
|
|
|
|
|
def infer_family(geom_types: set[str]) -> str:
|
|
buckets = {geom_bucket(geom_type) for geom_type in geom_types if geom_type}
|
|
if not buckets:
|
|
return "mixed"
|
|
if len(buckets) == 1:
|
|
return next(iter(buckets))
|
|
return "mixed"
|
|
|
|
|
|
def choose_allowed_geometries(object_type: str, family: str, observed_geom_types: list[str]) -> tuple[list[str], str]:
|
|
manual = MANUAL_OBJECT_RULES.get(object_type)
|
|
if manual:
|
|
return manual["allowed_geom_types"], "manual_override"
|
|
defaults = DEFAULT_GEOMETRIES.get(family, [])
|
|
if defaults:
|
|
return defaults, "family_default"
|
|
return observed_geom_types, "observed_fallback"
|
|
|
|
|
|
def build_canonical_layer_rules(cur, style_layers):
|
|
cur.execute("DROP TABLE IF EXISTS canonical_layer_rules")
|
|
cur.execute(
|
|
"""
|
|
CREATE TABLE canonical_layer_rules (
|
|
source_layer VARCHAR(100) NOT NULL,
|
|
semantic_granularity VARCHAR(50) NOT NULL,
|
|
canonical_family VARCHAR(100) NOT NULL,
|
|
render_strategy VARCHAR(50) NOT NULL,
|
|
preserve_source_layer TINYINT(1) NOT NULL DEFAULT 1,
|
|
style_bound TINYINT(1) NOT NULL DEFAULT 1,
|
|
style_rows INT NOT NULL DEFAULT 0,
|
|
style_types VARCHAR(200) NULL,
|
|
notes TEXT NULL,
|
|
PRIMARY KEY (source_layer)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
"""
|
|
)
|
|
|
|
cur.execute(
|
|
"""
|
|
SELECT
|
|
vt_layer,
|
|
SUM(feature_count) AS feature_count,
|
|
COUNT(DISTINCT COALESCE(NULLIF(class_name, ''), NULLIF(shape_name, ''), vt_layer)) AS semantic_type_count,
|
|
SUM(CASE WHEN class_name IS NULL OR class_name='' THEN feature_count ELSE 0 END) AS missing_class_count,
|
|
SUM(CASE WHEN shape_name IS NULL OR shape_name='' THEN feature_count ELSE 0 END) AS missing_shape_count
|
|
FROM object_catalog
|
|
GROUP BY vt_layer
|
|
"""
|
|
)
|
|
|
|
inserts = []
|
|
for vt_layer, feature_count, semantic_type_count, missing_class_count, missing_shape_count in cur.fetchall():
|
|
style_info = style_layers.get(vt_layer, {"style_rows": 0, "layer_types": set()})
|
|
|
|
if vt_layer.startswith("p"):
|
|
canonical_family = "symbol"
|
|
elif vt_layer.startswith("L"):
|
|
canonical_family = "line"
|
|
elif vt_layer.startswith("P"):
|
|
canonical_family = "surface"
|
|
else:
|
|
canonical_family = "mixed"
|
|
|
|
if semantic_type_count >= 5 and (vt_layer.startswith("P") or vt_layer.startswith("L")):
|
|
semantic_granularity = "container_layer"
|
|
notes = (
|
|
f"Layer contains {semantic_type_count} semantic types across {feature_count} features; "
|
|
"retain source layer for rendering and derive semantic overlays for analysis."
|
|
)
|
|
elif missing_class_count == feature_count and missing_shape_count == feature_count:
|
|
semantic_granularity = "style_or_source_layer"
|
|
notes = "Layer has no semantic class/shape annotations; source layer remains the stable identity."
|
|
else:
|
|
semantic_granularity = "semantic_layer"
|
|
notes = "Layer is close to a stable semantic object and can be preserved as-is."
|
|
|
|
inserts.append(
|
|
(
|
|
vt_layer,
|
|
semantic_granularity,
|
|
canonical_family,
|
|
"keep_source_layer",
|
|
1,
|
|
1 if style_info["style_rows"] > 0 else 0,
|
|
style_info["style_rows"],
|
|
",".join(sorted(style_info["layer_types"])) or None,
|
|
notes,
|
|
)
|
|
)
|
|
|
|
cur.executemany(
|
|
"""
|
|
INSERT INTO canonical_layer_rules (
|
|
source_layer,
|
|
semantic_granularity,
|
|
canonical_family,
|
|
render_strategy,
|
|
preserve_source_layer,
|
|
style_bound,
|
|
style_rows,
|
|
style_types,
|
|
notes
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
""",
|
|
inserts,
|
|
)
|
|
|
|
|
|
def build_canonical_object_rules(cur):
|
|
cur.execute("DROP TABLE IF EXISTS canonical_object_rules")
|
|
cur.execute(
|
|
"""
|
|
CREATE TABLE canonical_object_rules (
|
|
canonical_object_type VARCHAR(191) NOT NULL,
|
|
canonical_family VARCHAR(100) NOT NULL,
|
|
classification_basis VARCHAR(50) NOT NULL,
|
|
source_layer_scope VARCHAR(50) NOT NULL,
|
|
source_layer_count INT NOT NULL,
|
|
geom_type_count INT NOT NULL,
|
|
object_type_sources VARCHAR(100) NOT NULL,
|
|
source_layers TEXT NULL,
|
|
observed_geom_types VARCHAR(200) NULL,
|
|
allowed_geom_types VARCHAR(200) NULL,
|
|
preferred_geom_type VARCHAR(20) NULL,
|
|
geometry_rule_source VARCHAR(50) NOT NULL,
|
|
notes TEXT NULL,
|
|
PRIMARY KEY (canonical_object_type)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
"""
|
|
)
|
|
|
|
cur.execute(
|
|
"""
|
|
SELECT
|
|
COALESCE(NULLIF(class_name, ''), NULLIF(shape_name, ''), vt_layer) AS object_type,
|
|
GROUP_CONCAT(
|
|
DISTINCT CASE
|
|
WHEN class_name IS NOT NULL AND class_name <> '' THEN 'class_name'
|
|
WHEN shape_name IS NOT NULL AND shape_name <> '' THEN 'shape_name'
|
|
ELSE 'vt_layer'
|
|
END
|
|
ORDER BY 1 SEPARATOR ','
|
|
) AS object_type_sources,
|
|
SUM(feature_count) AS feature_count,
|
|
COUNT(DISTINCT vt_layer) AS source_layer_count,
|
|
GROUP_CONCAT(DISTINCT vt_layer ORDER BY vt_layer SEPARATOR ',') AS source_layers,
|
|
COUNT(DISTINCT geom_type) AS geom_type_count,
|
|
GROUP_CONCAT(DISTINCT geom_type ORDER BY geom_type SEPARATOR ',') AS observed_geom_types
|
|
FROM object_catalog
|
|
GROUP BY COALESCE(NULLIF(class_name, ''), NULLIF(shape_name, ''), vt_layer)
|
|
"""
|
|
)
|
|
object_rows = cur.fetchall()
|
|
|
|
cur.execute(
|
|
"""
|
|
SELECT
|
|
object_type,
|
|
geom_type,
|
|
feature_count
|
|
FROM object_type_stats
|
|
"""
|
|
)
|
|
geom_counts: dict[str, Counter[str]] = defaultdict(Counter)
|
|
for object_type, geom_type, feature_count in cur.fetchall():
|
|
geom_counts[object_type][geom_type] = feature_count
|
|
|
|
inserts = []
|
|
for (
|
|
object_type,
|
|
object_type_sources,
|
|
feature_count,
|
|
source_layer_count,
|
|
source_layers,
|
|
geom_type_count,
|
|
observed_geom_types,
|
|
) in object_rows:
|
|
observed_geom_list = [part for part in (observed_geom_types or "").split(",") if part]
|
|
observed_geom_set = set(observed_geom_list)
|
|
manual = MANUAL_OBJECT_RULES.get(object_type)
|
|
canonical_family = manual["family"] if manual else infer_family(observed_geom_set)
|
|
classification_basis = (
|
|
"source_layer_preserved"
|
|
if object_type_sources == "vt_layer"
|
|
else "semantic_object"
|
|
)
|
|
source_layer_scope = "multi_source" if source_layer_count > 1 else "single_source"
|
|
allowed_geom_types, geometry_rule_source = choose_allowed_geometries(
|
|
object_type,
|
|
canonical_family,
|
|
observed_geom_list,
|
|
)
|
|
preferred_geom_type = None
|
|
if geom_counts.get(object_type):
|
|
preferred_geom_type = geom_counts[object_type].most_common(1)[0][0]
|
|
notes = (
|
|
manual["notes"]
|
|
if manual
|
|
else (
|
|
"Source-preserved object type derived from vt_layer only."
|
|
if classification_basis == "source_layer_preserved"
|
|
else "Semantic object type derived from class_name/shape_name."
|
|
)
|
|
)
|
|
inserts.append(
|
|
(
|
|
object_type,
|
|
canonical_family,
|
|
classification_basis,
|
|
source_layer_scope,
|
|
source_layer_count,
|
|
geom_type_count,
|
|
object_type_sources,
|
|
source_layers,
|
|
",".join(observed_geom_list) or None,
|
|
",".join(allowed_geom_types) or None,
|
|
preferred_geom_type,
|
|
geometry_rule_source,
|
|
notes,
|
|
)
|
|
)
|
|
|
|
cur.executemany(
|
|
"""
|
|
INSERT INTO canonical_object_rules (
|
|
canonical_object_type,
|
|
canonical_family,
|
|
classification_basis,
|
|
source_layer_scope,
|
|
source_layer_count,
|
|
geom_type_count,
|
|
object_type_sources,
|
|
source_layers,
|
|
observed_geom_types,
|
|
allowed_geom_types,
|
|
preferred_geom_type,
|
|
geometry_rule_source,
|
|
notes
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
""",
|
|
inserts,
|
|
)
|
|
|
|
|
|
def build_object_geometry_allowlist(cur):
|
|
cur.execute("DROP TABLE IF EXISTS object_geometry_allowlist")
|
|
cur.execute(
|
|
"""
|
|
CREATE TABLE object_geometry_allowlist (
|
|
canonical_object_type VARCHAR(191) NOT NULL,
|
|
geometry_type VARCHAR(20) NOT NULL,
|
|
rule_source VARCHAR(50) NOT NULL,
|
|
is_allowed TINYINT(1) NOT NULL DEFAULT 1,
|
|
notes TEXT NULL,
|
|
PRIMARY KEY (canonical_object_type, geometry_type)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
"""
|
|
)
|
|
|
|
cur.execute(
|
|
"""
|
|
SELECT
|
|
canonical_object_type,
|
|
geometry_rule_source,
|
|
allowed_geom_types,
|
|
notes
|
|
FROM canonical_object_rules
|
|
"""
|
|
)
|
|
|
|
inserts = []
|
|
for canonical_object_type, geometry_rule_source, allowed_geom_types, notes in cur.fetchall():
|
|
for geometry_type in [part for part in (allowed_geom_types or "").split(",") if part]:
|
|
inserts.append(
|
|
(
|
|
canonical_object_type,
|
|
geometry_type,
|
|
geometry_rule_source,
|
|
1,
|
|
notes,
|
|
)
|
|
)
|
|
|
|
if inserts:
|
|
cur.executemany(
|
|
"""
|
|
INSERT INTO object_geometry_allowlist (
|
|
canonical_object_type,
|
|
geometry_type,
|
|
rule_source,
|
|
is_allowed,
|
|
notes
|
|
)
|
|
VALUES (%s, %s, %s, %s, %s)
|
|
""",
|
|
inserts,
|
|
)
|
|
|
|
|
|
def build_relayer_outputs(cur):
|
|
cur.execute("DROP TABLE IF EXISTS pbf_relayer_candidates")
|
|
cur.execute(
|
|
"""
|
|
CREATE TABLE pbf_relayer_candidates AS
|
|
SELECT
|
|
o.feature_id,
|
|
s.z,
|
|
s.x,
|
|
s.y,
|
|
s.vt_layer AS source_layer,
|
|
s.geom_type,
|
|
s.class_name,
|
|
s.shape_name,
|
|
s.layer_name,
|
|
o.object_type_source,
|
|
o.object_type AS canonical_object_type,
|
|
r.semantic_granularity,
|
|
obj.canonical_family,
|
|
obj.classification_basis,
|
|
obj.source_layer_scope,
|
|
r.render_strategy,
|
|
r.preserve_source_layer,
|
|
r.style_bound,
|
|
CASE
|
|
WHEN obj.classification_basis = 'source_layer_preserved' THEN CONCAT('source:', s.vt_layer)
|
|
ELSE CONCAT(obj.canonical_family, ':', o.object_type)
|
|
END AS semantic_key,
|
|
CASE
|
|
WHEN obj.classification_basis = 'source_layer_preserved' THEN CONCAT(obj.canonical_family, ':', s.vt_layer)
|
|
ELSE CONCAT(obj.canonical_family, ':', o.object_type)
|
|
END AS detection_key,
|
|
CASE
|
|
WHEN r.preserve_source_layer = 1 THEN s.vt_layer
|
|
ELSE o.object_type
|
|
END AS render_layer
|
|
FROM feature_semantic s
|
|
JOIN object_type_candidates o
|
|
ON s.feature_id = o.feature_id
|
|
JOIN canonical_layer_rules r
|
|
ON s.vt_layer = r.source_layer
|
|
JOIN canonical_object_rules obj
|
|
ON o.object_type = obj.canonical_object_type
|
|
"""
|
|
)
|
|
|
|
cur.execute("DROP TABLE IF EXISTS pbf_source_object_stats")
|
|
cur.execute(
|
|
"""
|
|
CREATE TABLE pbf_source_object_stats AS
|
|
SELECT
|
|
source_layer,
|
|
render_layer,
|
|
canonical_family,
|
|
classification_basis,
|
|
source_layer_scope,
|
|
semantic_granularity,
|
|
style_bound,
|
|
canonical_object_type,
|
|
geom_type,
|
|
detection_key,
|
|
semantic_key,
|
|
COUNT(*) AS feature_count
|
|
FROM pbf_relayer_candidates
|
|
GROUP BY
|
|
source_layer,
|
|
render_layer,
|
|
canonical_family,
|
|
classification_basis,
|
|
source_layer_scope,
|
|
semantic_granularity,
|
|
style_bound,
|
|
canonical_object_type,
|
|
geom_type,
|
|
detection_key,
|
|
semantic_key
|
|
"""
|
|
)
|
|
cur.execute("ALTER TABLE pbf_source_object_stats ADD KEY idx_source_layer (source_layer(100))")
|
|
cur.execute("ALTER TABLE pbf_source_object_stats ADD KEY idx_render_layer (render_layer(100))")
|
|
cur.execute("ALTER TABLE pbf_source_object_stats ADD KEY idx_detection_key (detection_key(50))")
|
|
|
|
cur.execute("DROP TABLE IF EXISTS pbf_render_compatibility")
|
|
cur.execute(
|
|
"""
|
|
CREATE TABLE pbf_render_compatibility AS
|
|
SELECT
|
|
s.source_layer,
|
|
s.render_layer,
|
|
s.canonical_family,
|
|
s.semantic_granularity,
|
|
s.style_bound,
|
|
SUM(s.feature_count) AS feature_count,
|
|
COUNT(*) AS canonical_object_types
|
|
FROM pbf_source_object_stats s
|
|
GROUP BY
|
|
s.source_layer,
|
|
s.render_layer,
|
|
s.canonical_family,
|
|
s.semantic_granularity,
|
|
s.style_bound
|
|
"""
|
|
)
|
|
|
|
cur.execute("DROP TABLE IF EXISTS pbf_detection_catalog")
|
|
cur.execute(
|
|
"""
|
|
CREATE TABLE pbf_detection_catalog AS
|
|
SELECT
|
|
detection_key,
|
|
canonical_family,
|
|
canonical_object_type,
|
|
geom_type,
|
|
SUM(feature_count) AS feature_count,
|
|
COUNT(DISTINCT source_layer) AS source_layers
|
|
FROM pbf_source_object_stats
|
|
GROUP BY
|
|
detection_key,
|
|
canonical_family,
|
|
canonical_object_type,
|
|
geom_type
|
|
"""
|
|
)
|
|
|
|
|
|
def refresh_relayer_tables():
|
|
style_layers = load_style_source_layers()
|
|
|
|
with connect() as conn:
|
|
with conn.cursor() as cur:
|
|
build_canonical_layer_rules(cur, style_layers)
|
|
build_canonical_object_rules(cur)
|
|
build_object_geometry_allowlist(cur)
|
|
build_relayer_outputs(cur)
|
|
conn.commit()
|
|
|
|
|
|
def main():
|
|
refresh_relayer_tables()
|
|
print(
|
|
"Built canonical_layer_rules, canonical_object_rules, object_geometry_allowlist, "
|
|
"pbf_relayer_candidates, pbf_render_compatibility, pbf_detection_catalog"
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|