-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
nusamai-gpkg: クレートの作成、空のGeoPackageファイル作成 (#89)
#8 まず、ベースの部分を作ってみました。 このPR後に、ジオメトリデータをバイナリ化したりしたものなど、各情報をSQLiteテーブルへ入れていく。 ## 変更 - [x] `nusamai-gpkg` クレートの新規作成 - [x] SQLxによる、空のGeoPackageファイル作成 - [x] example: CLIからのテスト実行
- Loading branch information
Showing
7 changed files
with
255 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
*.gpkg | ||
*.gpkg-shm | ||
*.gpkg-wal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "nusamai-gpkg" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
sqlx = { version = "0.7.3", features = ["sqlite", "runtime-tokio"] } | ||
tokio = { version = "1.35.1", features = ["full"] } | ||
nusamai-plateau = { path = "../nusamai-plateau" } | ||
thiserror = "1.0.51" | ||
|
||
[dev-dependencies] | ||
citygml = {path = "../nusamai-plateau/citygml" } | ||
clap = { version = "4.4.8", features = ["derive"] } | ||
quick-xml = "0.31.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
use citygml::{CityGMLElement, CityGMLReader, ParseError, SubTreeReader}; | ||
use clap::Parser; | ||
use nusamai_plateau::TopLevelCityObject; | ||
use std::io::BufRead; | ||
|
||
#[derive(Parser)] | ||
struct Args { | ||
#[clap(required = true)] | ||
filename: String, | ||
} | ||
|
||
fn toplevel_dispatcher<R: BufRead>( | ||
st: &mut SubTreeReader<R>, | ||
) -> Result<Vec<TopLevelCityObject>, ParseError> { | ||
let mut cityobjs: Vec<TopLevelCityObject> = vec![]; | ||
|
||
match st.parse_children(|st| match st.current_path() { | ||
b"core:cityObjectMember" => { | ||
let mut cityobj: nusamai_plateau::models::TopLevelCityObject = Default::default(); | ||
cityobj.parse(st)?; | ||
let geometries = st.collect_geometries(); | ||
|
||
if let Some(root) = cityobj.into_object() { | ||
let obj = TopLevelCityObject { root, geometries }; | ||
cityobjs.push(obj); | ||
} | ||
|
||
Ok(()) | ||
} | ||
b"gml:boundedBy" | b"app:appearanceMember" => { | ||
st.skip_current_element()?; | ||
Ok(()) | ||
} | ||
other => Err(ParseError::SchemaViolation(format!( | ||
"Unrecognized element {}", | ||
String::from_utf8_lossy(other) | ||
))), | ||
}) { | ||
Ok(_) => Ok(cityobjs), | ||
Err(e) => { | ||
println!("Err: {:?}", e); | ||
Err(e) | ||
} | ||
} | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
// Parse CityGML | ||
|
||
let args = Args::parse(); | ||
|
||
let reader = std::io::BufReader::new(std::fs::File::open(args.filename).unwrap()); | ||
let mut xml_reader = quick_xml::NsReader::from_reader(reader); | ||
let _cityobjs = match CityGMLReader::new().start_root(&mut xml_reader) { | ||
Ok(mut st) => match toplevel_dispatcher(&mut st) { | ||
Ok(items) => items, | ||
Err(e) => panic!("Err: {:?}", e), | ||
}, | ||
Err(e) => panic!("Err: {:?}", e), | ||
}; | ||
|
||
// GeoPackage | ||
|
||
let output_path = "output.gpkg"; | ||
let _handler = nusamai_gpkg::GpkgHandler::init(output_path).await.unwrap(); | ||
// TODO: handler.add_objects(&cityobjs).await; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
use nusamai_plateau::TopLevelCityObject; | ||
use sqlx::Row; | ||
use sqlx::{migrate::MigrateDatabase, Pool, Sqlite, SqlitePool}; | ||
use std::path::Path; | ||
use thiserror::Error; | ||
|
||
pub struct GpkgHandler { | ||
pool: Pool<Sqlite>, | ||
} | ||
|
||
#[derive(Error, Debug)] | ||
pub enum GpkgError { | ||
#[error("Database file already exists: {0}")] | ||
DatabaseExists(String), | ||
#[error("SQLx error: {0}")] | ||
SqlxError(#[from] sqlx::Error), | ||
} | ||
|
||
impl GpkgHandler { | ||
/// Create and initialize new GeoPackage database | ||
pub async fn init(path: &str) -> Result<Self, GpkgError> { | ||
if Path::new(path).exists() { | ||
return Err(GpkgError::DatabaseExists(path.to_string())); | ||
} | ||
|
||
let db_url = format!("sqlite://{}", path); | ||
|
||
Sqlite::create_database(&db_url).await?; | ||
let pool = SqlitePool::connect(&db_url).await?; | ||
|
||
// Initialize the database with minimum GeoPackage schema | ||
let create_query = include_str!("sql/gpkg_template.sql"); | ||
sqlx::query(create_query).execute(&pool).await?; | ||
|
||
Ok(Self { pool }) | ||
} | ||
|
||
/// Connect to an existing GeoPackage database | ||
pub async fn connect(path: &str) -> Result<Self, GpkgError> { | ||
let db_url = format!("sqlite://{}", path); | ||
let pool = SqlitePool::connect(&db_url).await?; | ||
Ok(Self { pool }) | ||
} | ||
|
||
/// Get the names of all tables in the GeoPackage database | ||
pub async fn table_names(&self) -> Vec<String> { | ||
let result = sqlx::query( | ||
"SELECT name | ||
FROM sqlite_schema | ||
WHERE type ='table' | ||
AND name NOT LIKE 'sqlite_%';", | ||
) | ||
.fetch_all(&self.pool) | ||
.await | ||
.unwrap(); | ||
|
||
let mut table_names: Vec<String> = result | ||
.iter() | ||
.map(|row| row.get::<String, &str>("name")) | ||
.collect(); | ||
table_names.sort(); | ||
table_names | ||
} | ||
|
||
/// Add a TopLevelCityObjects to the GeoPackage database | ||
pub async fn add_object(&self, _object: &TopLevelCityObject) { | ||
todo!(); | ||
} | ||
|
||
/// Add TopLevelCityObjects to the GeoPackage database | ||
pub async fn add_objects(&self, _objects: &[TopLevelCityObject]) { | ||
todo!(); | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[tokio::test] | ||
async fn test_init_connect() { | ||
let handler = GpkgHandler::init("sqlite::memory:").await.unwrap(); | ||
let _handler2 = GpkgHandler::connect("sqlite::memory:").await.unwrap(); | ||
|
||
let table_names = handler.table_names().await; | ||
assert_eq!( | ||
table_names, | ||
vec![ | ||
"gpkg_contents", | ||
"gpkg_extensions", | ||
"gpkg_geometry_columns", | ||
"gpkg_spatial_ref_sys", | ||
"gpkg_tile_matrix", | ||
"gpkg_tile_matrix_set" | ||
] | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
mod handler; | ||
|
||
pub use handler::GpkgHandler; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
CREATE TABLE gpkg_contents ( | ||
table_name TEXT NOT NULL PRIMARY KEY, | ||
data_type TEXT NOT NULL, | ||
identifier TEXT UNIQUE, | ||
description TEXT DEFAULT '', | ||
last_change DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), | ||
min_x DOUBLE, | ||
min_y DOUBLE, | ||
max_x DOUBLE, | ||
max_y DOUBLE, | ||
srs_id INTEGER, | ||
CONSTRAINT fk_gc_r_srs_id FOREIGN KEY (srs_id) REFERENCES gpkg_spatial_ref_sys(srs_id) | ||
); | ||
|
||
CREATE TABLE gpkg_extensions ( | ||
table_name TEXT, | ||
column_name TEXT, | ||
extension_name TEXT NOT NULL, | ||
definition TEXT NOT NULL, | ||
scope TEXT NOT NULL, | ||
CONSTRAINT ge_tce UNIQUE (table_name, column_name, extension_name) | ||
); | ||
|
||
CREATE TABLE gpkg_geometry_columns ( | ||
table_name TEXT NOT NULL, | ||
column_name TEXT NOT NULL, | ||
geometry_type_name TEXT NOT NULL, | ||
srs_id INTEGER NOT NULL, | ||
z TINYINT NOT NULL, | ||
m TINYINT NOT NULL, | ||
CONSTRAINT pk_geom_cols PRIMARY KEY (table_name, column_name), | ||
CONSTRAINT fk_gc_tn FOREIGN KEY (table_name) REFERENCES gpkg_contents(table_name), | ||
CONSTRAINT fk_gc_srs FOREIGN KEY (srs_id) REFERENCES gpkg_spatial_ref_sys (srs_id) | ||
); | ||
|
||
CREATE TABLE gpkg_spatial_ref_sys ( | ||
srs_name TEXT NOT NULL, | ||
srs_id INTEGER NOT NULL PRIMARY KEY, | ||
organization TEXT NOT NULL, | ||
organization_coordsys_id INTEGER NOT NULL, | ||
definition TEXT NOT NULL, | ||
description TEXT | ||
); | ||
|
||
CREATE TABLE gpkg_tile_matrix ( | ||
table_name TEXT NOT NULL, | ||
zoom_level INTEGER NOT NULL, | ||
matrix_width INTEGER NOT NULL, | ||
matrix_height INTEGER NOT NULL, | ||
tile_width INTEGER NOT NULL, | ||
tile_height INTEGER NOT NULL, | ||
pixel_x_size DOUBLE NOT NULL, | ||
pixel_y_size DOUBLE NOT NULL, | ||
CONSTRAINT pk_ttm PRIMARY KEY (table_name, zoom_level), | ||
CONSTRAINT fk_tmm_table_name FOREIGN KEY (table_name) REFERENCES gpkg_contents(table_name) | ||
); | ||
|
||
CREATE TABLE gpkg_tile_matrix_set ( | ||
table_name TEXT NOT NULL PRIMARY KEY, | ||
srs_id INTEGER NOT NULL, | ||
min_x DOUBLE NOT NULL, | ||
min_y DOUBLE NOT NULL, | ||
max_x DOUBLE NOT NULL, | ||
max_y DOUBLE NOT NULL, | ||
CONSTRAINT fk_gtms_table_name FOREIGN KEY (table_name) REFERENCES gpkg_contents(table_name), | ||
CONSTRAINT fk_gtms_srs FOREIGN KEY (srs_id) REFERENCES gpkg_spatial_ref_sys (srs_id) | ||
); |