|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: oracle通过经纬度过滤范围内数据 |
| 4 | +date: 2025-03-26 10:54:15 |
| 5 | +tags: |
| 6 | + - oracle |
| 7 | + - 经纬度 |
| 8 | + - 地图 |
| 9 | + - WGS84坐标系 |
| 10 | +categories: |
| 11 | + - 编程记录 |
| 12 | +--- |
| 13 | + |
| 14 | +## Start |
| 15 | + |
| 16 | +今天的需求是在地图上撒了很多点,右击这个点时,可以选择一个半径,呈现在这个半径范围内的点。 |
| 17 | +和之前一个在地图上框选多边形区域的需求一样,需要通过经纬度来计算过滤数据。 |
| 18 | + |
| 19 | +这里是通过 Oracle 的空间运算符实现的。 |
| 20 | +主要使用下面两个: |
| 21 | + |
| 22 | +1. [SDO_WITHIN_DISTANCE](https://docs.oracle.com/en/database/oracle/oracle-database/23/spatl/sdo_within_distance.html) |
| 23 | + Identifies the set of spatial objects that are within some specified distance of a given object, such as an area of interest or point of interest. |
| 24 | + 标识位于给定对象(如感兴趣区域或感兴趣点)的某个指定距离内的空间对象集。 |
| 25 | +2. [SDO_INSIDE](https://docs.oracle.com/en/database/oracle/oracle-database/23/spatl/sdo_inside.html) |
| 26 | + Checks if any geometries in a table have the INSIDE topological relationship with a specified geometry. Equivalent to specifying the SDO_RELATE operator with 'mask=INSIDE'. |
| 27 | + 检查表中的任何几何是否与指定几何具有 INSIDE 拓扑关系。等效于使用 'mask=INSIDE' 指定 SDO_RELATE 运算符。 |
| 28 | + |
| 29 | +## 需求实现 |
| 30 | + |
| 31 | +首先需要确保表中有经纬度的字段和数据,并且不为空。 |
| 32 | + |
| 33 | +~~~oraclesqlplus |
| 34 | +delete |
| 35 | +from T_GIS |
| 36 | +where LONGITUDE is null |
| 37 | + or LATITUDE is null; |
| 38 | +~~~ |
| 39 | + |
| 40 | +然后需要新建一个字段,为存储空间类型。存储经纬度转换后的数据。 |
| 41 | + |
| 42 | +~~~oraclesqlplus |
| 43 | +alter table T_GIS |
| 44 | + add POINT_GEOMETRY SDO_GEOMETRY |
| 45 | +~~~ |
| 46 | + |
| 47 | +将转换后的经纬度存储到这个字段中。 |
| 48 | + |
| 49 | +~~~oraclesqlplus |
| 50 | +UPDATE T_GIS a |
| 51 | +SET a.POINT_GEOMETRY = SDO_GEOMETRY( |
| 52 | + 2001, -- 点类型 |
| 53 | + 8307, -- WGS84 坐标系 |
| 54 | + SDO_POINT_TYPE(a.LONGITUDE, a.LATITUDE, NULL), -- 点坐标 |
| 55 | + NULL, -- 无元素信息 |
| 56 | + NULL -- 无坐标数组 |
| 57 | + ) |
| 58 | +where LONGITUDE is not null |
| 59 | + and LATITUDE is not null; |
| 60 | +~~~ |
| 61 | + |
| 62 | +然后需要为这个字段创建空间索引。 |
| 63 | + |
| 64 | +~~~oraclesqlplus |
| 65 | +DROP INDEX T_GIS_SPATIAL_IDX; |
| 66 | +
|
| 67 | +CREATE INDEX T_GIS_SPATIAL_IDX |
| 68 | + ON T_GIS (POINT_GEOMETRY) |
| 69 | + INDEXTYPE IS MDSYS.SPATIAL_INDEX; |
| 70 | +~~~ |
| 71 | + |
| 72 | +然后就可以使用 SDO_WITHIN_DISTANCE 来查询了。 |
| 73 | + |
| 74 | +~~~oraclesqlplus |
| 75 | +select * |
| 76 | +from T_GIS a |
| 77 | +where a.POINT_GEOMETRY is not null |
| 78 | + and a.LONGITUDE is not null |
| 79 | + and a.LATITUDE is not null |
| 80 | + and SDO_WITHIN_DISTANCE( |
| 81 | + a.POINT_GEOMETRY, |
| 82 | + SDO_GEOMETRY( |
| 83 | + 2001, -- 点类型 |
| 84 | + 8307, -- WGS84 坐标系 |
| 85 | + SDO_POINT_TYPE(104.48060937499996, 36.30556423523153, NULL), -- 点坐标 (经度, 纬度, 高度) |
| 86 | + NULL, -- 无元素信息 |
| 87 | + NULL -- 无坐标数组 |
| 88 | + ), |
| 89 | + 'DISTANCE=1000' |
| 90 | + ) = 'TRUE'; |
| 91 | +~~~ |
| 92 | + |
| 93 | +如果报错,可以使用下面的语句查看异常数据: |
| 94 | + |
| 95 | +~~~oraclesqlplus |
| 96 | +SELECT * |
| 97 | +FROM T_GIS |
| 98 | +WHERE SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT(POINT_GEOMETRY, 0.005) != 'TRUE'; |
| 99 | +~~~ |
| 100 | + |
| 101 | +## WGS84 坐标系 |
| 102 | + |
| 103 | +WGS84(**World Geodetic System 1984**)是目前全球最广泛使用的 **大地坐标系(Geodetic Coordinate System)**,由美国国防部(DoD)制定,用于 **GPS 定位、地图绘制、GIS 系统** 等场景。 |
| 104 | + |
| 105 | +- WGS84 是一个 **地球参考坐标系**,用于描述地球表面点的位置(经度、纬度、高程)。 |
| 106 | +- 它基于 **椭球体模型**(参考椭球),并定义了地球的形状、大小和重力场。 |
| 107 | + |
| 108 | +### 核心参数 |
| 109 | + |
| 110 | +| 参数 | 值 | 说明 | |
| 111 | +|-----------------|--------------------------|-------------------| |
| 112 | +| **椭球体长半轴(a)** | 6,378,137 米 | 赤道半径 | |
| 113 | +| **椭球体短半轴(b)** | 6,356,752.3142 米 | 极半径 | |
| 114 | +| **扁率(f)** | 1/298.257223563 | `f = (a - b) / a` | |
| 115 | +| **第一偏心率平方(e²)** | 0.00669437999014 | 用于坐标转换 | |
| 116 | +| **地球自转角速度(ω)** | 7.292115 × 10⁻⁵ rad/s | 影响重力场计算 | |
| 117 | +| **地心引力常数(GM)** | 3.986004418 × 10¹⁴ m³/s² | 用于卫星轨道计算 | |
| 118 | + |
| 119 | +### 坐标表示 |
| 120 | +WGS84 使用 **经度(Longitude)、纬度(Latitude)、高程(Height)** 表示位置: |
| 121 | + |
| 122 | +- **经度(λ)**:东经(0°~180°E)或西经(0°~180°W)。 |
| 123 | +- **纬度(φ)**:北纬(0°~90°N)或南纬(0°~90°S)。 |
| 124 | +- **高程(h)**:相对于 WGS84 椭球面的高度(单位:米)。 |
| 125 | + |
| 126 | +### WGS84 的常见用途 |
| 127 | + |
| 128 | +1. GPS 定位 |
| 129 | + - 全球定位系统(GPS)默认使用 WGS84 坐标系。 |
| 130 | + - 手机、车载导航、无人机等设备的定位数据通常基于 WGS84。 |
| 131 | +2. 地图服务 |
| 132 | + - **Google Maps、百度地图、高德地图** 等在线地图的底层数据采用 WGS84。 |
| 133 | + - 但部分地图(如中国 GCJ-02)会对 WGS84 进行加密偏移。 |
| 134 | +3. GIS 和空间数据库 |
| 135 | + - **Oracle Spatial、PostGIS、ArcGIS** 等支持 WGS84 坐标系。 |
| 136 | + - 在 Oracle 中,WGS84 的 SRID(空间参考 ID)通常为: |
| 137 | + - **4326**(标准 WGS84,经纬度顺序:纬度, 经度) |
| 138 | + - **8307**(WGS84,经纬度顺序:经度, 纬度) |
| 139 | + |
| 140 | +### WGS84 与其他坐标系的区别 |
| 141 | + |
| 142 | +WGS84 是全通通用的、无偏移的 GPS 原始数据,美国标准。GPS、谷歌地图等使用。 |
| 143 | +GCJ-02 对 WGS84 进行非线性偏移。在中国国内使用,高德地图、腾讯地图等使用。 |
| 144 | +CGCS2000 是中国标准,中国官方测绘。 |
| 145 | + |
| 146 | +> 注:CGCS2000 和 WGS84 在 **厘米级精度** 下可以视为一致,但在高精度测量(如卫星定位)时需转换。 |
| 147 | +
|
| 148 | + |
| 149 | +## Oracle Spatial 函数(部分) |
| 150 | + |
| 151 | +### SDO_GEOMETRY |
| 152 | + |
| 153 | +SDO_GEOMETRY 是 Oracle Spatial 的核心数据类型,用于存储空间数据。 |
| 154 | + |
| 155 | +基本语法 |
| 156 | + |
| 157 | +~~~oraclesqlplus |
| 158 | +SDO_GEOMETRY( |
| 159 | + geometry_type NUMBER, -- 几何类型代码 |
| 160 | + srid NUMBER, -- 空间参考系ID |
| 161 | + point SDO_POINT_TYPE,-- 点坐标(仅用于点类型) |
| 162 | + elem_info SDO_ELEM_INFO_ARRAY, -- 元素定义数组 |
| 163 | + ordinates SDO_ORDINATE_ARRAY -- 坐标值数组 |
| 164 | +) |
| 165 | +~~~ |
| 166 | + |
| 167 | +几何类型代码 |
| 168 | + |
| 169 | +| 代码 | 类型说明 | |
| 170 | +|------|--------| |
| 171 | +| 2001 | 点 | |
| 172 | +| 2002 | 线 | |
| 173 | +| 2003 | 多边形 | |
| 174 | +| 2005 | 多点集合 | |
| 175 | +| 2006 | 多线集合 | |
| 176 | +| 2007 | 多多边形集合 | |
| 177 | + |
| 178 | +创建点 |
| 179 | + |
| 180 | +~~~oraclesqlplus |
| 181 | +-- 创建WGS84坐标系的点(经度120.5,纬度30.2) |
| 182 | +SELECT SDO_GEOMETRY( |
| 183 | + 2001, -- 点类型 |
| 184 | + 8307, -- WGS84坐标系SRID |
| 185 | + SDO_POINT_TYPE(120.5, 30.2, NULL), -- 经度,纬度,高程 |
| 186 | + NULL, |
| 187 | + NULL |
| 188 | +) FROM dual; |
| 189 | +~~~ |
| 190 | + |
| 191 | +创建线 |
| 192 | + |
| 193 | +~~~oraclesqlplus |
| 194 | +-- 创建由三个点组成的线 |
| 195 | +SELECT SDO_GEOMETRY( |
| 196 | + 2002, -- 线类型 |
| 197 | + 8307, |
| 198 | + NULL, |
| 199 | + SDO_ELEM_INFO_ARRAY(1, 2, 1), -- 简单线 |
| 200 | + SDO_ORDINATE_ARRAY(120,30, 121,31, 122,30) -- 三个点坐标 |
| 201 | +) FROM dual; |
| 202 | +~~~ |
| 203 | + |
| 204 | +创建多边形 |
| 205 | + |
| 206 | +~~~oraclesqlplus |
| 207 | +-- 创建四边形(必须闭合) |
| 208 | +SELECT SDO_GEOMETRY( |
| 209 | + 2003, -- 多边形类型 |
| 210 | + 8307, |
| 211 | + NULL, |
| 212 | + SDO_ELEM_INFO_ARRAY(1, 1003, 1), -- 外多边形 |
| 213 | + SDO_ORDINATE_ARRAY(120,30, 121,30, 121,31, 120,31, 120,30) -- 闭合坐标 |
| 214 | +) FROM dual; |
| 215 | +~~~ |
| 216 | + |
| 217 | +### SDO_WITHIN_DISTANCE 函数详解 |
| 218 | + |
| 219 | +用于查询在指定距离范围内的空间对象。 |
| 220 | + |
| 221 | +基本语法 |
| 222 | + |
| 223 | +~~~oraclesqlplus |
| 224 | +SDO_WITHIN_DISTANCE( |
| 225 | + geometry1 SDO_GEOMETRY, -- 要检查的几何对象 |
| 226 | + geometry2 SDO_GEOMETRY, -- 参考几何对象 |
| 227 | + params VARCHAR2 -- 距离参数 |
| 228 | +) RETURN VARCHAR2; |
| 229 | +~~~ |
| 230 | + |
| 231 | +参数说明 |
| 232 | + |
| 233 | +- `params` 格式:`'distance=<数值> unit=<单位>'` |
| 234 | + - `distance`:距离值 |
| 235 | + - `unit`:单位(默认为坐标系单位,WGS84为米) |
| 236 | + |
| 237 | +查询某点1公里范围内的所有商店 |
| 238 | + |
| 239 | +~~~oraclesqlplus |
| 240 | +SELECT s.store_id, s.store_name |
| 241 | +FROM stores s |
| 242 | +WHERE SDO_WITHIN_DISTANCE( |
| 243 | + s.location, -- 商店位置字段 |
| 244 | + SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(120.5, 30.2, NULL), NULL, NULL), -- 中心点 |
| 245 | + 'distance=1000' -- 1公里范围内 |
| 246 | +) = 'TRUE'; |
| 247 | +~~~ |
| 248 | + |
| 249 | +查询某区域500米内的所有道路 |
| 250 | + |
| 251 | +~~~oraclesqlplus |
| 252 | +SELECT r.road_id, r.road_name |
| 253 | +FROM roads r, regions reg |
| 254 | +WHERE reg.region_id = 101 |
| 255 | +AND SDO_WITHIN_DISTANCE( |
| 256 | + r.geom, -- 道路几何 |
| 257 | + reg.geom, -- 区域几何 |
| 258 | + 'distance=500' -- 500米内 |
| 259 | +) = 'TRUE'; |
| 260 | +~~~ |
| 261 | + |
| 262 | +### SDO_INSIDE |
| 263 | + |
| 264 | +用于判断一个几何对象是否完全包含在另一个几何对象内部。 |
| 265 | + |
| 266 | +基本语法 |
| 267 | + |
| 268 | +~~~oraclesqlplus |
| 269 | +SDO_INSIDE( |
| 270 | + geometry1 SDO_GEOMETRY, -- 要检查的几何对象 |
| 271 | + geometry2 SDO_GEOMETRY, -- 容器几何对象 |
| 272 | + tol NUMBER -- 容差 |
| 273 | +) RETURN VARCHAR2; |
| 274 | +~~~ |
| 275 | + |
| 276 | +查询完全在某个区域内的所有建筑 |
| 277 | + |
| 278 | +~~~oraclesqlplus |
| 279 | +SELECT b.building_id, b.building_name |
| 280 | +FROM buildings b, city_zones z |
| 281 | +WHERE z.zone_id = 5 |
| 282 | +AND SDO_INSIDE( |
| 283 | + b.geometry, -- 建筑几何 |
| 284 | + z.geometry, -- 区域几何 |
| 285 | + 0.05 -- 容差 |
| 286 | +) = 'TRUE'; |
| 287 | +~~~ |
| 288 | + |
| 289 | +检查点是否在多边形内 |
| 290 | + |
| 291 | +~~~oraclesqlplus |
| 292 | +SELECT |
| 293 | + CASE |
| 294 | + WHEN SDO_INSIDE( |
| 295 | + SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(120.3, 30.5, NULL), NULL, NULL), |
| 296 | + polygon_geom, |
| 297 | + 0.01 |
| 298 | + ) = 'TRUE' THEN 'Inside' |
| 299 | + ELSE 'Outside' |
| 300 | + END AS position_status |
| 301 | +FROM administrative_areas |
| 302 | +WHERE area_id = 101; |
| 303 | +~~~ |
| 304 | + |
0 commit comments