223 lines
6.9 KiB
Python
223 lines
6.9 KiB
Python
from __future__ import annotations
|
|
|
|
import datetime as dt
|
|
import sqlite3
|
|
from pathlib import Path
|
|
|
|
|
|
SOURCE_DB = Path("out/navsea_kyushu_semantic.sqlite")
|
|
OUT = Path("out/navsea_kyushu_core.sqlite")
|
|
KEEP_GROUNDING = ("干潮帯", "0-2m", "0-5m", "0-10m", "2-5m", "2-10m", "5-10m")
|
|
|
|
|
|
def open_db() -> sqlite3.Connection:
|
|
if OUT.exists():
|
|
OUT.unlink()
|
|
OUT.parent.mkdir(parents=True, exist_ok=True)
|
|
conn = sqlite3.connect(OUT)
|
|
conn.execute("PRAGMA journal_mode=OFF")
|
|
conn.execute("PRAGMA synchronous=OFF")
|
|
conn.execute("PRAGMA temp_store=MEMORY")
|
|
conn.execute("PRAGMA cache_size=150000")
|
|
conn.execute("PRAGMA locking_mode=EXCLUSIVE")
|
|
return conn
|
|
|
|
|
|
def create_schema(conn: sqlite3.Connection) -> None:
|
|
conn.executescript(
|
|
"""
|
|
CREATE TABLE meta (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE semantic_object (
|
|
feature_id INTEGER PRIMARY KEY,
|
|
fid TEXT,
|
|
source_layer TEXT,
|
|
canonical_object_type TEXT NOT NULL,
|
|
object_family TEXT NOT NULL,
|
|
detection_class TEXT NOT NULL,
|
|
capability TEXT,
|
|
z INTEGER,
|
|
x INTEGER,
|
|
y INTEGER,
|
|
geom_type TEXT,
|
|
is_land INTEGER NOT NULL DEFAULT 0,
|
|
is_hole INTEGER NOT NULL DEFAULT 0,
|
|
is_collision INTEGER NOT NULL DEFAULT 0,
|
|
is_grounding INTEGER NOT NULL DEFAULT 0
|
|
);
|
|
|
|
CREATE INDEX idx_semantic_object_fid ON semantic_object(fid);
|
|
CREATE INDEX idx_semantic_object_tile ON semantic_object(z, x, y);
|
|
CREATE INDEX idx_semantic_object_capability ON semantic_object(capability, canonical_object_type);
|
|
|
|
CREATE TABLE grid_cell (
|
|
tile_key TEXT PRIMARY KEY,
|
|
z INTEGER NOT NULL,
|
|
x INTEGER NOT NULL,
|
|
y INTEGER NOT NULL,
|
|
land_count INTEGER NOT NULL DEFAULT 0,
|
|
hole_count INTEGER NOT NULL DEFAULT 0,
|
|
collision_count INTEGER NOT NULL DEFAULT 0,
|
|
grounding_count INTEGER NOT NULL DEFAULT 0,
|
|
blocked INTEGER NOT NULL DEFAULT 0,
|
|
blocked_reason TEXT
|
|
);
|
|
|
|
CREATE INDEX idx_grid_cell_zxy ON grid_cell(z, x, y);
|
|
CREATE INDEX idx_grid_cell_blocked ON grid_cell(blocked, z);
|
|
"""
|
|
)
|
|
conn.executemany(
|
|
"INSERT INTO meta(key, value) VALUES (?, ?)",
|
|
[
|
|
("source_db", str(SOURCE_DB)),
|
|
("source_scope", "九州"),
|
|
("build_time", dt.datetime.now().isoformat(timespec="seconds")),
|
|
("notes", "九州核心版语义索引库,只保留陆地、碰撞物、浅水搁浅层"),
|
|
("keep_grounding", ",".join(KEEP_GROUNDING)),
|
|
],
|
|
)
|
|
conn.commit()
|
|
|
|
|
|
def main() -> None:
|
|
if not SOURCE_DB.exists():
|
|
raise SystemExit(f"missing source db: {SOURCE_DB}")
|
|
|
|
src = sqlite3.connect(SOURCE_DB)
|
|
src.row_factory = sqlite3.Row
|
|
conn = open_db()
|
|
create_schema(conn)
|
|
|
|
placeholders = ",".join("?" for _ in KEEP_GROUNDING)
|
|
sql = f"""
|
|
SELECT
|
|
feature_id, fid, source_layer, canonical_object_type, object_family, detection_class,
|
|
capability, z, x, y, geom_type, is_land, is_hole, is_collision, is_grounding
|
|
FROM semantic_object
|
|
WHERE is_land = 1
|
|
OR is_hole = 1
|
|
OR is_collision = 1
|
|
OR (is_grounding = 1 AND canonical_object_type IN ({placeholders}))
|
|
ORDER BY z, x, y, feature_id
|
|
"""
|
|
|
|
batch = []
|
|
grid = {}
|
|
count = 0
|
|
batch_size = 4000
|
|
|
|
for row in src.execute(sql, KEEP_GROUNDING):
|
|
row = dict(row)
|
|
batch.append(
|
|
(
|
|
row["feature_id"],
|
|
row["fid"],
|
|
row["source_layer"],
|
|
row["canonical_object_type"],
|
|
row["object_family"],
|
|
row["detection_class"],
|
|
row["capability"],
|
|
row["z"],
|
|
row["x"],
|
|
row["y"],
|
|
row["geom_type"],
|
|
row["is_land"],
|
|
row["is_hole"],
|
|
row["is_collision"],
|
|
row["is_grounding"],
|
|
)
|
|
)
|
|
tile_key = f'{row["z"]}/{row["x"]}/{row["y"]}'
|
|
c = grid.get(tile_key)
|
|
if c is None:
|
|
c = {"z": row["z"], "x": row["x"], "y": row["y"], "land": 0, "hole": 0, "collision": 0, "grounding": 0}
|
|
grid[tile_key] = c
|
|
c["land"] += int(row["is_land"])
|
|
c["hole"] += int(row["is_hole"])
|
|
c["collision"] += int(row["is_collision"])
|
|
c["grounding"] += int(row["is_grounding"])
|
|
count += 1
|
|
|
|
if len(batch) >= batch_size:
|
|
conn.executemany(
|
|
"""
|
|
INSERT INTO semantic_object(
|
|
feature_id, fid, source_layer, canonical_object_type, object_family, detection_class,
|
|
capability, z, x, y, geom_type, is_land, is_hole, is_collision, is_grounding
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
""",
|
|
batch,
|
|
)
|
|
conn.commit()
|
|
batch.clear()
|
|
print(f"已写入 {count} 条核心对象")
|
|
|
|
if batch:
|
|
conn.executemany(
|
|
"""
|
|
INSERT INTO semantic_object(
|
|
feature_id, fid, source_layer, canonical_object_type, object_family, detection_class,
|
|
capability, z, x, y, geom_type, is_land, is_hole, is_collision, is_grounding
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
""",
|
|
batch,
|
|
)
|
|
conn.commit()
|
|
|
|
cell_rows = []
|
|
for tile_key, c in grid.items():
|
|
blocked = 0
|
|
reason = None
|
|
if c["land"] > c["hole"] and c["land"] > 0:
|
|
blocked = 1
|
|
reason = "land"
|
|
elif c["collision"] > 0:
|
|
blocked = 1
|
|
reason = "collision"
|
|
elif c["grounding"] > 0:
|
|
blocked = 1
|
|
reason = "grounding"
|
|
cell_rows.append(
|
|
(
|
|
tile_key,
|
|
c["z"],
|
|
c["x"],
|
|
c["y"],
|
|
c["land"],
|
|
c["hole"],
|
|
c["collision"],
|
|
c["grounding"],
|
|
blocked,
|
|
reason,
|
|
)
|
|
)
|
|
|
|
conn.executemany(
|
|
"""
|
|
INSERT INTO grid_cell(
|
|
tile_key, z, x, y, land_count, hole_count, collision_count, grounding_count,
|
|
blocked, blocked_reason
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
""",
|
|
cell_rows,
|
|
)
|
|
conn.commit()
|
|
conn.execute("ANALYZE")
|
|
conn.commit()
|
|
|
|
obj_count = conn.execute("SELECT COUNT(*) FROM semantic_object").fetchone()[0]
|
|
cell_count = conn.execute("SELECT COUNT(*) FROM grid_cell").fetchone()[0]
|
|
conn.close()
|
|
src.close()
|
|
|
|
print(f"完成: objects={obj_count}, cells={cell_count}")
|
|
print(f"输出文件: {OUT.resolve()}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|