You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift如何从MariaDB的POINT类型字段提取经纬度?

解决MariaDB POINT类型字段的Swift读取问题

方法一:SQL查询时直接提取经纬度(推荐)

MariaDB提供ST_X()和ST_Y()函数可直接从POINT类型中提取经度(X值)和纬度(Y值),无需在Swift端处理二进制数据,实现成本最低。

示例代码:

假设你使用Fluent MySQL驱动,可通过原生SQL查询获取解析后的经纬度:

// 定义包含经纬度的临时结构体
struct UserWithLocation: Decodable {
    var id: UUID?
    var name: String
    var longitude: Double  // 对应ST_X(location)
    var latitude: Double   // 对应ST_Y(location)
}

// 执行查询
let users = try await User.query(on: db)
    .raw("SELECT *, ST_X(location) as longitude, ST_Y(location) as latitude FROM users")
    .decode(UserWithLocation.self)

方法二:Swift端解码POINT二进制数据

如果需要直接处理POINT类型的原始空间数据,可以自定义类型并实现解码逻辑,解析MySQL的WKB(Well-Known Binary)格式POINT数据。

步骤1:定义Point结构体并实现解码

struct Point: Codable {
    let latitude: Double
    let longitude: Double
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let data = try container.decode(Data.self)
        
        // 验证POINT数据长度:字节序(1) + 类型码(4) + X(8) + Y(8) = 21字节
        guard data.count == 21 else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "无效的POINT数据长度")
        }
        
        let byteOrder = data[0]
        // 读取类型码(POINT类型的编码为1)
        let type = data.subdata(in: 1..<5).withUnsafeBytes { $0.load(as: UInt32.self) }
        guard type == 1 else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "不是POINT类型数据")
        }
        
        // 提取经度(X)和纬度(Y)的字节数据
        let xBytes = data.subdata(in: 5..<13)
        let yBytes = data.subdata(in: 13..<21)
        
        // 根据字节序转换为Double值
        if byteOrder == 0x00 { // 小端字节序
            self.longitude = xBytes.reversed().withUnsafeBytes { $0.load(as: Double.self) }
            self.latitude = yBytes.reversed().withUnsafeBytes { $0.load(as: Double.self) }
        } else { // 大端字节序
            self.longitude = xBytes.withUnsafeBytes { $0.load(as: Double.self) }
            self.latitude = yBytes.withUnsafeBytes { $0.load(as: Double.self) }
        }
    }
}

步骤2:在模型中使用自定义Point类型

如果使用Fluent,需要让Point实现MySQLDataConvertible协议以适配驱动:

extension Point: MySQLDataConvertible {
    var mysqlData: MySQLData {
        // 若需要将Point编码回数据库,可在此实现逻辑,仅读取时可暂时抛出错误
        fatalError("编码逻辑未实现")
    }
    
    static func fromMySQLData(_ data: MySQLData) throws -> Self {
        guard case .geometry(let geometryData) = data.storage else {
            throw DecodingError.dataCorruptedError(forKey: .init(stringValue: "location"), in: nil, debugDescription: "不是GEOMETRY类型数据")
        }
        return try JSONDecoder().decode(Self.self, from: geometryData)
    }
}

// 在模型中定义字段
final class User: Model {
    static let schema = "users"
    
    @ID(key: .id)
    var id: UUID?
    
    @Field(key: "name")
    var name: String
    
    @Field(key: "location")
    var location: Point?
}

错误原因说明

你之前直接用String类型读取POINT字段失败,是因为MariaDB的POINT类型存储的是二进制GEOMETRY数据,无法直接解码为字符串类型,必须通过上述两种方法处理。

内容的提问来源于stack exchange,提问作者Andrey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 08:05:42