Rust地理空间数据处理库postgis_diesel的使用:结合PostGIS和Diesel实现高效空间数据库操作
Rust地理空间数据处理库postgis_diesel的使用:结合PostGIS和Diesel实现高效空间数据库操作
PostGIS Diesel是Diesel框架的扩展,用于支持PostGIS类型。它提供了对postgres
后端和可选sqlite
后端的支持。前者默认启用postgres
功能,后者需要启用sqlite
功能。
使用示例
确保Geometry
类型在作用域中,将postgis_diesel::sql_types::*
添加到diesel.toml
文件的import_types键中。
假设表定义如下(PostgreSQL):
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE TABLE geometry_samples
(
id SERIAL PRIMARY KEY,
point geometry(Point,4326) NOT NULL,
linestring geometry(Linestring,4326) NOT NULL
);
或者SQLite版本:
CREATE TABLE geometry_samples
(
id SERIAL PRIMARY KEY,
point BLOB NOT NULL,
linestring BLOB NOT NULL
);
Rust代码(适用于两种后端):
use postgis_diesel::operators::*;
use postgis_diesel::types::*;
use diesel::prelude::*;
#[derive(Insertable)]
#[diesel(table_name = geometry_samples)]
struct NewGeometrySample {
point: Point,
linestring: LineString<Point>,
}
#[derive(Queryable)]
struct GeometrySample {
id: i32,
point: Point,
linestring: LineString<Point>,
}
table! {
use postgis_diesel::sql_types::*;
use diesel::sql_types::*;
geometry_samples (id) {
id -> Int4,
point -> Geometry,
linestring -> Geometry,
}
}
完整示例
以下是一个完整的Rust示例,展示如何使用postgis_diesel进行地理空间数据操作:
use diesel::{insert_into, prelude::*};
use postgis_diesel::{
operators::*,
types::{LineString, Point},
};
// 假设已经建立了数据库连接
fn main() -> Result<(), Box<dyn std::error::Error>> {
let connection = &mut establish_connection();
// 创建新几何样本
let new_sample = NewGeometrySample {
point: Point::new(12.34, 56.78),
linestring: LineString::new(vec![
Point::new(0.0, 0.0),
Point::new(1.0, 1.0),
Point::new(2.0, 2.0),
]),
};
// 插入数据
insert_into(geometry_samples::table)
.values(&new_sample)
.execute(connection)?;
// 查询数据
let results = geometry_samples::table
.filter(geometry_samples::point.st_distance(&Point::new(12.34, 56.78)).lt(0.1))
.load::<GeometrySample>(connection)?;
for sample in results {
println!(
"ID: {}, Point: ({}, {}), Linestring length: {}",
sample.id,
sample.point.x(),
sample.point.y(),
sample.linestring.length()
);
}
Ok(())
}
// 示例表定义
table! {
use postgis_diesel::sql_types::*;
use diesel::sql_types::*;
geometry_samples (id) {
id -> Int4,
point -> Geometry,
linestring -> Geometry,
}
}
#[derive(Insertable)]
#[diesel(table_name = geometry_samples)]
struct NewGeometrySample {
point: Point,
linestring: LineString<Point>,
}
#[derive(Queryable)]
struct GeometrySample {
id: i32,
point: Point,
linestring: LineString<Point>,
}
// 建立数据库连接(示例)
fn establish_connection() -> PgConnection {
let database_url = "postgres://username:password@localhost/database";
PgConnection::establish(&database_url)
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
}
如何从Schema中移除自动生成的类型
- 生成schema文件:
diesel print-schema > src/full_schema.rs
- 移除不需要的SQL类型并保存为
src/schema.rs
- 运行
diff -U6 src/full_schema.rs src/schema.rs > src/schema.patch
- 在diesel.toml中添加
patch_file = "src/schema.patch"
- 移除
src/full_schema.rs
,检查diesel print-schema > src/schema.rs
不会添加Geometry类型
示例patch文件:
@@ -1,12 +1,9 @@
// @generated automatically by Diesel CLI.
pub mod sql_types {
- #[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)]
- #[diesel(postgres_type(name = "geometry"))]
- pub struct Geometry;
#[derive(diesel::query_builder::QueryId, diesel::sql_types::SqlType)]
#[diesel(postgres_type(name = "intensity"))]
pub struct Intensity;
如何运行测试
- 启动Postgis数据库:
docker compose up
- 运行测试:
cargo test
安装
在项目目录中运行以下Cargo命令:
cargo add postgis_diesel
或者在Cargo.toml中添加:
postgis_diesel = "3.0.1"
以下是一个增强版的完整示例demo,包含更详细的注释和功能:
// 引入必要的依赖
use diesel::{insert_into, prelude::*};
use postgis_diesel::{
operators::*,
types::{LineString, Point},
};
use dotenv::dotenv;
use std::env;
// 主函数
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 加载.env文件中的环境变量
dotenv().ok();
// 建立数据库连接
let connection = &mut establish_connection();
// 创建几何样本数据
let samples = vec![
NewGeometrySample {
point: Point::new(12.34, 56.78),
linestring: LineString::new(vec![
Point::new(0.0, 0.0),
Point::new(1.0, 1.0),
]),
},
NewGeometrySample {
point: Point::new(23.45, 67.89),
linestring: LineString::new(vec![
Point::new(1.0, 1.0),
Point::new(2.0, 2.0),
Point::new(3.0, 3.0),
]),
},
];
// 批量插入数据
insert_into(geometry_samples::table)
.values(&samples)
.execute(connection)?;
// 查询距离特定点0.5单位范围内的所有几何样本
let target_point = Point::new(12.34, 56.78);
let results = geometry_samples::table
.filter(geometry_samples::point.st_distance(&target_point).lt(0.5))
.load::<GeometrySample>(connection)?;
// 打印查询结果
println!("Found {} nearby geometries:", results.len());
for (i, sample) in results.iter().enumerate() {
println!(
"{}. ID: {}, Point: ({}, {}), Linestring points: {}",
i + 1,
sample.id,
sample.point.x(),
sample.point.y(),
sample.linestring.points().len()
);
}
Ok(())
}
// 表结构定义
table! {
use postgis_diesel::sql_types::*;
use diesel::sql_types::*;
geometry_samples (id) {
id -> Int4,
point -> Geometry,
linestring -> Geometry,
}
}
// 插入数据结构
#[derive(Insertable)]
#[diesel(table_name = geometry_samples)]
struct NewGeometrySample {
point: Point,
linestring: LineString<Point>,
}
// 查询结果结构
#[derive(Queryable)]
struct GeometrySample {
id: i32,
point: Point,
linestring: LineString<Point>,
}
// 建立数据库连接
fn establish_connection() -> PgConnection {
// 从环境变量获取数据库URL
let database_url = env::var("DATABASE_URL")
.expect("DATABASE_URL must be set in .env file");
// 建立连接
PgConnection::establish(&database_url)
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
}
这个增强版示例包含以下改进:
- 使用dotenv管理数据库连接配置
- 支持批量插入多个几何样本
- 更详细的查询结果输出
- 更完整的错误处理
- 更清晰的结构定义和注释
使用前请确保:
- 创建了.env文件并设置DATABASE_URL环境变量
- 数据库表结构已按前面的SQL示例创建
- 已安装postgis_diesel和相关依赖
1 回复
Rust地理空间数据处理库postgis_diesel的使用:结合PostGIS和Diesel实现高效空间数据库操作
以下是基于您提供的内容整理的完整示例demo:
use diesel::prelude::*;
use postgis_diesel::types::*;
use postgis_diesel::functions::*;
// 定义表结构宏 (通常在schema.rs中)
table! {
locations (id) {
id -> Integer,
name -> Text,
geom -> Geometry,
}
}
// 定义模型结构体
#[derive(Queryable, Debug)]
pub struct Location {
pub id: i32,
pub name: String,
pub geom: Point<f64>,
}
#[derive(Insertable)]
#[diesel(table_name = locations)]
pub struct NewLocation {
pub name: String,
pub geom: Point<f64>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 建立数据库连接
let database_url = "postgres://username:password@localhost/database";
let mut conn = PgConnection::establish(database_url)?;
// 1. 插入地理位置数据
let central_park = NewLocation {
name: "Central Park".to_string(),
geom: Point::new(-73.968285, 40.785091), // 经度, 纬度
};
diesel::insert_into(locations::table)
.values(¢ral_park)
.execute(&mut conn)?;
// 2. 查询所有位置
println!("所有位置信息:");
let all_locations = locations::table
.load::<Location>(&mut conn)?;
for loc in all_locations {
println!("{}: ({}, {})", loc.name, loc.geom.x(), loc.geom.y());
}
// 3. 空间查询 - 查找附近1000米内的地点
let times_square = Point::new(-73.985130, 40.758896);
let distance = 1000.0; // 1000米范围内
println!("\nTimes Square附近1000米内的地点:");
let nearby = locations::table
.filter(st_d_within(locations::geom, times_square, distance))
.load::<Location>(&mut conn)?;
for loc in nearby {
println!("{}: ({}, {})", loc.name, loc.geom.x(), loc.geom.y());
}
// 4. 计算两个点之间的距离
let point_a = Point::new(-73.966, 40.78);
let point_b = Point::new(-73.968, 40.785);
let dist: f64 = select(st_distance(point_a, point_b))
.get_result(&mut conn)?;
println!("\n两点间距离: {:.2} 米", dist);
// 5. 高级空间操作 - 创建缓冲区
let empire_state = Point::new(-73.9857, 40.7484);
let buffered_area = select(st_buffer(empire_state, 300.0))
.get_result::<Polygon<f64>>(&mut conn)?;
println!("\n帝国大厦300米缓冲区坐标点:");
for point in buffered_area.exterior() {
println!("({}, {})", point.x(), point.y());
}
Ok(())
}
这个完整示例演示了如何使用postgis_diesel
库进行以下操作:
- 建立与PostgreSQL+PostGIS数据库的连接
- 插入包含地理空间数据的记录
- 查询并显示所有地理位置数据
- 执行空间查询查找附近地点
- 计算两个地理点之间的距离
- 创建并显示地理点的缓冲区
注意事项:
- 需要先创建数据库表结构(如您提供的SQL)
- 确保PostGIS扩展已启用
- 坐标顺序为经度(X)在前,纬度(Y)在后
- 对于生产环境,建议添加空间索引提高查询性能