Skip to main content

sz_orm_postgis/
geometry.rs

1//! 几何类型定义
2//!
3//! 提供 PostGIS 兼容的几何类型,支持 EWKT/EWKB 序列化。
4//! 所有几何类型携带 SRID(坐标参考系统 ID),默认 WGS84(SRID=4326)。
5
6use crate::error::PostgisError;
7use serde::{Deserialize, Serialize};
8
9/// 默认 SRID:WGS84 经纬度
10pub const DEFAULT_SRID: i32 = 4326;
11
12/// 二维点
13#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
14pub struct Point {
15    pub x: f64,
16    pub y: f64,
17    pub srid: i32,
18}
19
20impl Point {
21    pub fn new(x: f64, y: f64) -> Self {
22        Self {
23            x,
24            y,
25            srid: DEFAULT_SRID,
26        }
27    }
28
29    pub fn with_srid(x: f64, y: f64, srid: i32) -> Self {
30        Self { x, y, srid }
31    }
32
33    /// 计算到另一点的欧氏距离(不考虑 SRID 投影,仅用于内存实现)
34    pub fn euclidean_distance(&self, other: &Point) -> f64 {
35        let dx = self.x - other.x;
36        let dy = self.y - other.y;
37        (dx * dx + dy * dy).sqrt()
38    }
39
40    /// 计算到另一点的大圆距离(Haversine 公式,假设 SRID=4326 经纬度)
41    pub fn haversine_distance(&self, other: &Point) -> f64 {
42        const EARTH_RADIUS_M: f64 = 6_371_000.0;
43        let to_rad = |deg: f64| deg * std::f64::consts::PI / 180.0;
44        let lat1 = to_rad(self.y);
45        let lat2 = to_rad(other.y);
46        let dlat = to_rad(other.y - self.y);
47        let dlon = to_rad(other.x - self.x);
48        let a = (dlat / 2.0).sin().powi(2) + lat1.cos() * lat2.cos() * (dlon / 2.0).sin().powi(2);
49        let c = 2.0 * a.sqrt().asin();
50        EARTH_RADIUS_M * c
51    }
52
53    /// 转为 EWKT 字符串:`SRID=4326;POINT(x y)`
54    pub fn to_ewkt(&self) -> String {
55        format!("SRID={};POINT({} {})", self.srid, self.x, self.y)
56    }
57
58    /// 转为 WKT 字符串:`POINT(x y)`(不含 SRID 前缀)
59    pub fn to_wkt(&self) -> String {
60        format!("POINT({} {})", self.x, self.y)
61    }
62
63    /// 计算两点中点
64    pub fn midpoint(&self, other: &Point) -> Point {
65        Point::with_srid(
66            (self.x + other.x) / 2.0,
67            (self.y + other.y) / 2.0,
68            self.srid,
69        )
70    }
71
72    /// 计算到另一点的方位角(度数,0=正北,顺时针)
73    pub fn bearing(&self, other: &Point) -> f64 {
74        let to_rad = |deg: f64| deg * std::f64::consts::PI / 180.0;
75        let to_deg = |rad: f64| rad * 180.0 / std::f64::consts::PI;
76        let lat1 = to_rad(self.y);
77        let lat2 = to_rad(other.y);
78        let dlon = to_rad(other.x - self.x);
79        let y = dlon.sin() * lat2.cos();
80        let x = lat1.cos() * lat2.sin() - lat1.sin() * lat2.cos() * dlon.cos();
81        let bearing = to_deg(y.atan2(x));
82        (bearing + 360.0) % 360.0
83    }
84}
85
86/// 线串:由有序点组成
87#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
88pub struct LineString {
89    pub points: Vec<Point>,
90    pub srid: i32,
91}
92
93impl LineString {
94    pub fn new(points: Vec<Point>) -> Self {
95        let srid = points.first().map(|p| p.srid).unwrap_or(DEFAULT_SRID);
96        Self { points, srid }
97    }
98
99    /// 计算线串总长度(欧氏)
100    pub fn euclidean_length(&self) -> f64 {
101        self.points
102            .windows(2)
103            .map(|w| w[0].euclidean_distance(&w[1]))
104            .sum()
105    }
106
107    /// 计算线串总长度(Haversine,假设经纬度)
108    pub fn haversine_length(&self) -> f64 {
109        self.points
110            .windows(2)
111            .map(|w| w[0].haversine_distance(&w[1]))
112            .sum()
113    }
114
115    pub fn to_ewkt(&self) -> String {
116        let coords: Vec<String> = self
117            .points
118            .iter()
119            .map(|p| format!("{} {}", p.x, p.y))
120            .collect();
121        format!("SRID={};LINESTRING({})", self.srid, coords.join(", "))
122    }
123
124    /// 转 WKT 字符串(不含 SRID 前缀)
125    pub fn to_wkt(&self) -> String {
126        let coords: Vec<String> = self
127            .points
128            .iter()
129            .map(|p| format!("{} {}", p.x, p.y))
130            .collect();
131        format!("LINESTRING({})", coords.join(", "))
132    }
133
134    /// 点数
135    pub fn point_count(&self) -> usize {
136        self.points.len()
137    }
138}
139
140/// 多边形:由外环和可选内环(洞)组成
141#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
142pub struct Polygon {
143    pub rings: Vec<Vec<Point>>,
144    pub srid: i32,
145}
146
147impl Polygon {
148    pub fn new(outer: Vec<Point>) -> Self {
149        let srid = outer.first().map(|p| p.srid).unwrap_or(DEFAULT_SRID);
150        Self {
151            rings: vec![outer],
152            srid,
153        }
154    }
155
156    pub fn with_holes(outer: Vec<Point>, holes: Vec<Vec<Point>>) -> Self {
157        let srid = outer.first().map(|p| p.srid).unwrap_or(DEFAULT_SRID);
158        let mut rings = vec![outer];
159        rings.extend(holes);
160        Self { rings, srid }
161    }
162
163    /// 计算多边形面积(Shoelace 公式,仅用外环)
164    pub fn shoelace_area(&self) -> f64 {
165        if self.rings.is_empty() {
166            return 0.0;
167        }
168        let outer = &self.rings[0];
169        if outer.len() < 3 {
170            return 0.0;
171        }
172        let mut sum = 0.0;
173        for i in 0..outer.len() {
174            let j = (i + 1) % outer.len();
175            sum += outer[i].x * outer[j].y;
176            sum -= outer[j].x * outer[i].y;
177        }
178        (sum / 2.0).abs()
179    }
180
181    /// 判断点是否在多边形内(射线法,仅用外环)
182    pub fn contains_point(&self, point: &Point) -> bool {
183        if self.rings.is_empty() {
184            return false;
185        }
186        let outer = &self.rings[0];
187        let mut inside = false;
188        let mut j = outer.len() - 1;
189        for i in 0..outer.len() {
190            let intersect = (outer[i].y > point.y) != (outer[j].y > point.y)
191                && point.x
192                    < (outer[j].x - outer[i].x) * (point.y - outer[i].y)
193                        / (outer[j].y - outer[i].y)
194                        + outer[i].x;
195            if intersect {
196                inside = !inside;
197            }
198            j = i;
199        }
200        // 若在外环内,检查是否在洞内
201        if inside {
202            for hole in self.rings.iter().skip(1) {
203                let mut hole_inside = false;
204                let mut j = hole.len() - 1;
205                for i in 0..hole.len() {
206                    let intersect = (hole[i].y > point.y) != (hole[j].y > point.y)
207                        && point.x
208                            < (hole[j].x - hole[i].x) * (point.y - hole[i].y)
209                                / (hole[j].y - hole[i].y)
210                                + hole[i].x;
211                    if intersect {
212                        hole_inside = !hole_inside;
213                    }
214                    j = i;
215                }
216                if hole_inside {
217                    return false;
218                }
219            }
220        }
221        inside
222    }
223
224    pub fn to_ewkt(&self) -> String {
225        let rings: Vec<String> = self
226            .rings
227            .iter()
228            .map(|ring| {
229                let coords: Vec<String> = ring.iter().map(|p| format!("{} {}", p.x, p.y)).collect();
230                format!("({})", coords.join(", "))
231            })
232            .collect();
233        format!("SRID={};POLYGON({})", self.srid, rings.join(", "))
234    }
235
236    /// 转 WKT 字符串(不含 SRID 前缀)
237    pub fn to_wkt(&self) -> String {
238        let rings: Vec<String> = self
239            .rings
240            .iter()
241            .map(|ring| {
242                let coords: Vec<String> = ring.iter().map(|p| format!("{} {}", p.x, p.y)).collect();
243                format!("({})", coords.join(", "))
244            })
245            .collect();
246        format!("POLYGON({})", rings.join(", "))
247    }
248
249    /// 计算外环周长(欧氏)
250    pub fn perimeter(&self) -> f64 {
251        if self.rings.is_empty() {
252            return 0.0;
253        }
254        let outer = &self.rings[0];
255        if outer.len() < 2 {
256            return 0.0;
257        }
258        let mut perim = 0.0;
259        for i in 0..outer.len() {
260            let j = (i + 1) % outer.len();
261            perim += outer[i].euclidean_distance(&outer[j]);
262        }
263        perim
264    }
265
266    /// 环数(外环 + 洞数)
267    pub fn ring_count(&self) -> usize {
268        self.rings.len()
269    }
270}
271
272/// 几何类型枚举(统一容器)
273#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
274pub enum Geometry {
275    Point(Point),
276    LineString(LineString),
277    Polygon(Polygon),
278    MultiPoint(Vec<Point>),
279    MultiLineString(Vec<LineString>),
280    MultiPolygon(Vec<Polygon>),
281}
282
283impl Geometry {
284    /// 获取 SRID
285    pub fn srid(&self) -> i32 {
286        match self {
287            Geometry::Point(p) => p.srid,
288            Geometry::LineString(ls) => ls.srid,
289            Geometry::Polygon(poly) => poly.srid,
290            Geometry::MultiPoint(pts) => pts.first().map(|p| p.srid).unwrap_or(DEFAULT_SRID),
291            Geometry::MultiLineString(lss) => lss.first().map(|ls| ls.srid).unwrap_or(DEFAULT_SRID),
292            Geometry::MultiPolygon(polys) => polys.first().map(|p| p.srid).unwrap_or(DEFAULT_SRID),
293        }
294    }
295
296    /// 校验 SRID 一致性(多几何体场景)
297    pub fn validate_srid(&self) -> Result<(), PostgisError> {
298        let expected = self.srid();
299        let check = |srid: i32| -> Result<(), PostgisError> {
300            if srid != expected {
301                Err(PostgisError::SridMismatch {
302                    expected,
303                    actual: srid,
304                })
305            } else {
306                Ok(())
307            }
308        };
309        match self {
310            Geometry::MultiPoint(pts) => {
311                for p in pts {
312                    check(p.srid)?;
313                }
314            }
315            Geometry::MultiLineString(lss) => {
316                for ls in lss {
317                    check(ls.srid)?;
318                }
319            }
320            Geometry::MultiPolygon(polys) => {
321                for p in polys {
322                    check(p.srid)?;
323                }
324            }
325            _ => {}
326        }
327        Ok(())
328    }
329
330    /// 类型名称(用于错误信息)
331    pub fn type_name(&self) -> &'static str {
332        match self {
333            Geometry::Point(_) => "Point",
334            Geometry::LineString(_) => "LineString",
335            Geometry::Polygon(_) => "Polygon",
336            Geometry::MultiPoint(_) => "MultiPoint",
337            Geometry::MultiLineString(_) => "MultiLineString",
338            Geometry::MultiPolygon(_) => "MultiPolygon",
339        }
340    }
341
342    /// 转 WKT 字符串(不含 SRID 前缀)
343    pub fn to_wkt(&self) -> String {
344        match self {
345            Geometry::Point(p) => p.to_wkt(),
346            Geometry::LineString(ls) => ls.to_wkt(),
347            Geometry::Polygon(poly) => poly.to_wkt(),
348            Geometry::MultiPoint(pts) => {
349                let coords: Vec<String> = pts.iter().map(|p| format!("{} {}", p.x, p.y)).collect();
350                format!("MULTIPOINT({})", coords.join(", "))
351            }
352            Geometry::MultiLineString(lss) => {
353                let lines: Vec<String> = lss
354                    .iter()
355                    .map(|ls| {
356                        let coords: Vec<String> = ls
357                            .points
358                            .iter()
359                            .map(|p| format!("{} {}", p.x, p.y))
360                            .collect();
361                        format!("({})", coords.join(", "))
362                    })
363                    .collect();
364                format!("MULTILINESTRING({})", lines.join(", "))
365            }
366            Geometry::MultiPolygon(polys) => {
367                let polygons: Vec<String> = polys
368                    .iter()
369                    .map(|poly| {
370                        let rings: Vec<String> = poly
371                            .rings
372                            .iter()
373                            .map(|ring| {
374                                let coords: Vec<String> =
375                                    ring.iter().map(|p| format!("{} {}", p.x, p.y)).collect();
376                                format!("({})", coords.join(", "))
377                            })
378                            .collect();
379                        format!("({})", rings.join(", "))
380                    })
381                    .collect();
382                format!("MULTIPOLYGON({})", polygons.join(", "))
383            }
384        }
385    }
386
387    /// 计算几何体的包围盒 (min_x, min_y, max_x, max_y)
388    pub fn bounding_box(&self) -> Option<(f64, f64, f64, f64)> {
389        let points: Vec<&Point> = match self {
390            Geometry::Point(p) => vec![p],
391            Geometry::LineString(ls) => ls.points.iter().collect(),
392            Geometry::Polygon(poly) => poly.rings.iter().flatten().collect(),
393            Geometry::MultiPoint(pts) => pts.iter().collect(),
394            Geometry::MultiLineString(lss) => lss.iter().flat_map(|ls| ls.points.iter()).collect(),
395            Geometry::MultiPolygon(polys) => polys
396                .iter()
397                .flat_map(|p| p.rings.iter().flatten())
398                .collect(),
399        };
400        if points.is_empty() {
401            return None;
402        }
403        let mut min_x = points[0].x;
404        let mut min_y = points[0].y;
405        let mut max_x = points[0].x;
406        let mut max_y = points[0].y;
407        for p in &points[1..] {
408            min_x = min_x.min(p.x);
409            min_y = min_y.min(p.y);
410            max_x = max_x.max(p.x);
411            max_y = max_y.max(p.y);
412        }
413        Some((min_x, min_y, max_x, max_y))
414    }
415
416    /// 从 EWKT 字符串解析几何体
417    ///
418    /// 支持格式:`SRID=4326;POINT(x y)` / `SRID=4326;LINESTRING(...)` / `SRID=4326;POLYGON(...)`
419    /// 也支持无 SRID 前缀的 WKT(使用 DEFAULT_SRID)
420    ///
421    /// v0.2.2 新增:用于 real_postgis.rs 解析 ST_AsEWKT 返回值
422    pub fn from_ewkt(ewkt: &str) -> Result<Self, PostgisError> {
423        let (srid, wkt) = if let Some(semi) = ewkt.find(';') {
424            let srid_str = &ewkt[..semi];
425            let wkt = &ewkt[semi + 1..];
426            if !srid_str.starts_with("SRID=") {
427                return Err(PostgisError::Query(format!(
428                    "invalid EWKT SRID prefix: {}",
429                    srid_str
430                )));
431            }
432            let srid: i32 = srid_str[5..]
433                .parse()
434                .map_err(|e| PostgisError::Query(format!("invalid SRID: {}", e)))?;
435            (srid, wkt)
436        } else {
437            (DEFAULT_SRID, ewkt)
438        };
439
440        let wkt = wkt.trim();
441        let upper = wkt.to_uppercase();
442
443        if upper.starts_with("POINT") {
444            let coords = extract_paren_content(&upper, "POINT")?;
445            let nums = parse_coord_pair(&coords)?;
446            Ok(Geometry::Point(Point::with_srid(nums.0, nums.1, srid)))
447        } else if upper.starts_with("LINESTRING") {
448            let coords = extract_paren_content(&upper, "LINESTRING")?;
449            let points = parse_coord_list(&coords)?
450                .into_iter()
451                .map(|(x, y)| Point::with_srid(x, y, srid))
452                .collect();
453            Ok(Geometry::LineString(LineString { points, srid }))
454        } else if upper.starts_with("POLYGON") {
455            let rings_str = extract_paren_content(&upper, "POLYGON")?;
456            // rings_str 形如 (x1 y1, x2 y2, ...), (x3 y3, ...)
457            let rings = parse_polygon_rings(&rings_str, srid)?;
458            Ok(Geometry::Polygon(Polygon { rings, srid }))
459        } else if upper.starts_with("MULTIPOINT") {
460            let coords = extract_paren_content(&upper, "MULTIPOINT")?;
461            let points = parse_coord_list(&coords)?
462                .into_iter()
463                .map(|(x, y)| Point::with_srid(x, y, srid))
464                .collect();
465            Ok(Geometry::MultiPoint(points))
466        } else {
467            Err(PostgisError::Query(format!(
468                "unsupported WKT type in: {}",
469                wkt
470            )))
471        }
472    }
473
474    /// 转 EWKT 字符串
475    pub fn to_ewkt(&self) -> String {
476        match self {
477            Geometry::Point(p) => p.to_ewkt(),
478            Geometry::LineString(ls) => ls.to_ewkt(),
479            Geometry::Polygon(poly) => poly.to_ewkt(),
480            Geometry::MultiPoint(pts) => {
481                let srid = pts.first().map(|p| p.srid).unwrap_or(DEFAULT_SRID);
482                let coords: Vec<String> = pts.iter().map(|p| format!("{} {}", p.x, p.y)).collect();
483                format!("SRID={};MULTIPOINT({})", srid, coords.join(", "))
484            }
485            Geometry::MultiLineString(lss) => {
486                let srid = lss.first().map(|ls| ls.srid).unwrap_or(DEFAULT_SRID);
487                let lines: Vec<String> = lss
488                    .iter()
489                    .map(|ls| {
490                        let coords: Vec<String> = ls
491                            .points
492                            .iter()
493                            .map(|p| format!("{} {}", p.x, p.y))
494                            .collect();
495                        format!("({})", coords.join(", "))
496                    })
497                    .collect();
498                format!("SRID={};MULTILINESTRING({})", srid, lines.join(", "))
499            }
500            Geometry::MultiPolygon(polys) => {
501                let srid = polys.first().map(|p| p.srid).unwrap_or(DEFAULT_SRID);
502                let polygons: Vec<String> = polys
503                    .iter()
504                    .map(|poly| {
505                        let rings: Vec<String> = poly
506                            .rings
507                            .iter()
508                            .map(|ring| {
509                                let coords: Vec<String> =
510                                    ring.iter().map(|p| format!("{} {}", p.x, p.y)).collect();
511                                format!("({})", coords.join(", "))
512                            })
513                            .collect();
514                        format!("({})", rings.join(", "))
515                    })
516                    .collect();
517                format!("SRID={};MULTIPOLYGON({})", srid, polygons.join(", "))
518            }
519        }
520    }
521}
522
523/// 从 WKT 中提取括号内容:`POINT(x y)` → `x y`
524fn extract_paren_content(upper_wkt: &str, type_name: &str) -> Result<String, PostgisError> {
525    let start = upper_wkt
526        .find(type_name)
527        .ok_or_else(|| PostgisError::Query(format!("missing type name {}", type_name)))?
528        + type_name.len();
529    let rest = &upper_wkt[start..];
530    let rest = rest.trim_start();
531    if !rest.starts_with('(') {
532        return Err(PostgisError::Query(format!(
533            "missing opening paren after {}: {}",
534            type_name, rest
535        )));
536    }
537    // 找到匹配的右括号(处理嵌套,如 POLYGON((...)(...)))
538    let mut depth = 0i32;
539    let mut end = 0usize;
540    for (i, c) in rest.chars().enumerate() {
541        match c {
542            '(' => depth += 1,
543            ')' => {
544                depth -= 1;
545                if depth == 0 {
546                    end = i;
547                    break;
548                }
549            }
550            _ => {}
551        }
552    }
553    if depth != 0 {
554        return Err(PostgisError::Query(format!(
555            "unbalanced parens in {}: {}",
556            type_name, rest
557        )));
558    }
559    Ok(rest[1..end].to_string())
560}
561
562/// 解析坐标对:`x y` → (f64, f64)
563fn parse_coord_pair(s: &str) -> Result<(f64, f64), PostgisError> {
564    let parts: Vec<&str> = s.split_whitespace().collect();
565    if parts.len() < 2 {
566        return Err(PostgisError::Query(format!(
567            "expected 2 coords, got {}: {}",
568            parts.len(),
569            s
570        )));
571    }
572    let x: f64 = parts[0]
573        .parse()
574        .map_err(|e| PostgisError::Query(format!("invalid x coord: {}", e)))?;
575    let y: f64 = parts[1]
576        .parse()
577        .map_err(|e| PostgisError::Query(format!("invalid y coord: {}", e)))?;
578    Ok((x, y))
579}
580
581/// 解析坐标列表:`x1 y1, x2 y2, ...` → Vec<(x, y)>
582fn parse_coord_list(s: &str) -> Result<Vec<(f64, f64)>, PostgisError> {
583    s.split(',')
584        .map(|pair| parse_coord_pair(pair.trim()))
585        .collect()
586}
587
588/// 解析多边形环:`(x1 y1, x2 y2, ...), (x3 y3, ...)` → Vec<Vec<Point>>
589fn parse_polygon_rings(s: &str, srid: i32) -> Result<Vec<Vec<Point>>, PostgisError> {
590    let mut rings = Vec::new();
591    let mut depth = 0i32;
592    let mut current = String::new();
593    for c in s.chars() {
594        match c {
595            '(' => {
596                depth += 1;
597                if depth == 1 {
598                    current.clear();
599                } else {
600                    current.push(c);
601                }
602            }
603            ')' => {
604                depth -= 1;
605                if depth == 0 {
606                    let ring: Vec<Point> = parse_coord_list(&current)?
607                        .into_iter()
608                        .map(|(x, y)| Point::with_srid(x, y, srid))
609                        .collect();
610                    rings.push(ring);
611                } else {
612                    current.push(c);
613                }
614            }
615            _ if depth >= 1 => {
616                current.push(c);
617            }
618            _ => {} // 顶层(depth==0)的空格/逗号是分隔符
619        }
620    }
621    if rings.is_empty() {
622        return Err(PostgisError::Query(format!("no rings parsed from: {}", s)));
623    }
624    Ok(rings)
625}
626
627#[cfg(test)]
628mod tests {
629    use super::*;
630
631    #[test]
632    fn test_point_ewkt() {
633        let p = Point::new(116.404, 39.915);
634        assert_eq!(p.to_ewkt(), "SRID=4326;POINT(116.404 39.915)");
635    }
636
637    #[test]
638    fn test_point_euclidean_distance() {
639        let p1 = Point::new(0.0, 0.0);
640        let p2 = Point::new(3.0, 4.0);
641        let dist = p1.euclidean_distance(&p2);
642        assert!((dist - 5.0).abs() < 1e-10);
643    }
644
645    #[test]
646    fn test_point_haversine_distance() {
647        // 北京到上海约 1067 km
648        let beijing = Point::new(116.404, 39.915);
649        let shanghai = Point::new(121.474, 31.230);
650        let dist = beijing.haversine_distance(&shanghai);
651        assert!(dist > 1_000_000.0 && dist < 1_200_000.0);
652    }
653
654    #[test]
655    fn test_linestring_length() {
656        let ls = LineString::new(vec![
657            Point::new(0.0, 0.0),
658            Point::new(3.0, 4.0),
659            Point::new(3.0, 9.0),
660        ]);
661        // 0->1: 5, 1->2: 5
662        let len = ls.euclidean_length();
663        assert!((len - 10.0).abs() < 1e-10);
664    }
665
666    #[test]
667    fn test_polygon_area() {
668        let poly = Polygon::new(vec![
669            Point::new(0.0, 0.0),
670            Point::new(4.0, 0.0),
671            Point::new(4.0, 3.0),
672            Point::new(0.0, 3.0),
673        ]);
674        let area = poly.shoelace_area();
675        assert!((area - 12.0).abs() < 1e-10);
676    }
677
678    #[test]
679    fn test_polygon_contains_point() {
680        let poly = Polygon::new(vec![
681            Point::new(0.0, 0.0),
682            Point::new(4.0, 0.0),
683            Point::new(4.0, 3.0),
684            Point::new(0.0, 3.0),
685        ]);
686        assert!(poly.contains_point(&Point::new(2.0, 1.5)));
687        assert!(!poly.contains_point(&Point::new(5.0, 1.5)));
688    }
689
690    #[test]
691    fn test_polygon_with_hole() {
692        let outer = vec![
693            Point::new(0.0, 0.0),
694            Point::new(10.0, 0.0),
695            Point::new(10.0, 10.0),
696            Point::new(0.0, 10.0),
697        ];
698        let hole = vec![
699            Point::new(3.0, 3.0),
700            Point::new(7.0, 3.0),
701            Point::new(7.0, 7.0),
702            Point::new(3.0, 7.0),
703        ];
704        let poly = Polygon::with_holes(outer, vec![hole]);
705        // 外环内、洞外
706        assert!(poly.contains_point(&Point::new(1.0, 1.0)));
707        // 洞内
708        assert!(!poly.contains_point(&Point::new(5.0, 5.0)));
709    }
710
711    #[test]
712    fn test_geometry_srid_validation() {
713        let g = Geometry::MultiPoint(vec![
714            Point::with_srid(0.0, 0.0, 4326),
715            Point::with_srid(1.0, 1.0, 3857),
716        ]);
717        assert!(matches!(
718            g.validate_srid(),
719            Err(PostgisError::SridMismatch {
720                expected: 4326,
721                actual: 3857
722            })
723        ));
724    }
725
726    #[test]
727    fn test_geometry_ewkt() {
728        let g = Geometry::Point(Point::new(116.404, 39.915));
729        assert_eq!(g.to_ewkt(), "SRID=4326;POINT(116.404 39.915)");
730        assert_eq!(g.type_name(), "Point");
731    }
732
733    #[test]
734    fn test_point_wkt() {
735        let p = Point::new(1.0, 2.0);
736        assert_eq!(p.to_wkt(), "POINT(1 2)");
737    }
738
739    #[test]
740    fn test_point_midpoint() {
741        let p1 = Point::new(0.0, 0.0);
742        let p2 = Point::new(4.0, 6.0);
743        let mid = p1.midpoint(&p2);
744        assert!((mid.x - 2.0).abs() < 1e-10);
745        assert!((mid.y - 3.0).abs() < 1e-10);
746    }
747
748    #[test]
749    fn test_point_bearing_north() {
750        let p1 = Point::new(0.0, 0.0);
751        let p2 = Point::new(0.0, 1.0);
752        let bearing = p1.bearing(&p2);
753        assert!((bearing - 0.0).abs() < 1e-6);
754    }
755
756    #[test]
757    fn test_point_bearing_east() {
758        let p1 = Point::new(0.0, 0.0);
759        let p2 = Point::new(1.0, 0.0);
760        let bearing = p1.bearing(&p2);
761        assert!((bearing - 90.0).abs() < 1e-6);
762    }
763
764    #[test]
765    fn test_linestring_wkt() {
766        let ls = LineString::new(vec![Point::new(0.0, 0.0), Point::new(1.0, 1.0)]);
767        assert_eq!(ls.to_wkt(), "LINESTRING(0 0, 1 1)");
768    }
769
770    #[test]
771    fn test_linestring_point_count() {
772        let ls = LineString::new(vec![Point::new(0.0, 0.0), Point::new(1.0, 1.0)]);
773        assert_eq!(ls.point_count(), 2);
774    }
775
776    #[test]
777    fn test_polygon_wkt() {
778        let poly = Polygon::new(vec![
779            Point::new(0.0, 0.0),
780            Point::new(4.0, 0.0),
781            Point::new(4.0, 3.0),
782            Point::new(0.0, 3.0),
783        ]);
784        assert!(poly.to_wkt().starts_with("POLYGON("));
785    }
786
787    #[test]
788    fn test_polygon_perimeter() {
789        let poly = Polygon::new(vec![
790            Point::new(0.0, 0.0),
791            Point::new(4.0, 0.0),
792            Point::new(4.0, 3.0),
793            Point::new(0.0, 3.0),
794        ]);
795        let perim = poly.perimeter();
796        assert!((perim - 14.0).abs() < 1e-10);
797    }
798
799    #[test]
800    fn test_polygon_ring_count() {
801        let poly = Polygon::new(vec![
802            Point::new(0.0, 0.0),
803            Point::new(1.0, 0.0),
804            Point::new(0.0, 1.0),
805        ]);
806        assert_eq!(poly.ring_count(), 1);
807    }
808
809    #[test]
810    fn test_geometry_to_wkt_point() {
811        let g = Geometry::Point(Point::new(1.0, 2.0));
812        assert_eq!(g.to_wkt(), "POINT(1 2)");
813    }
814
815    #[test]
816    fn test_geometry_to_wkt_linestring() {
817        let g = Geometry::LineString(LineString::new(vec![
818            Point::new(0.0, 0.0),
819            Point::new(1.0, 1.0),
820        ]));
821        assert_eq!(g.to_wkt(), "LINESTRING(0 0, 1 1)");
822    }
823
824    #[test]
825    fn test_geometry_bounding_box_point() {
826        let g = Geometry::Point(Point::new(3.0, 5.0));
827        let bb = g.bounding_box().unwrap();
828        assert_eq!(bb, (3.0, 5.0, 3.0, 5.0));
829    }
830
831    #[test]
832    fn test_geometry_bounding_box_polygon() {
833        let poly = Polygon::new(vec![
834            Point::new(0.0, 0.0),
835            Point::new(4.0, 0.0),
836            Point::new(4.0, 3.0),
837            Point::new(0.0, 3.0),
838        ]);
839        let g = Geometry::Polygon(poly);
840        let bb = g.bounding_box().unwrap();
841        assert_eq!(bb, (0.0, 0.0, 4.0, 3.0));
842    }
843
844    #[test]
845    fn test_geometry_bounding_box_empty() {
846        let g = Geometry::MultiPoint(vec![]);
847        assert!(g.bounding_box().is_none());
848    }
849}