MongoDB存GIS数据?GeoJSON如何查询?

GIS基础理论
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

引言:很多做 WebGIS、移动采集或空间数据接口的同学都会遇到这个问题:MongoDB存GIS数据?GeoJSON如何查询? 这篇文章不讨论“MongoDB 能不能替代专业 GIS 数据库”这种泛泛问题,而是围绕一个具体场景:把点、线、面等 GeoJSON 存进 MongoDB 后,如何建立空间索引,并完成范围查询、附近查询和相交查询。

如果你的业务数据本身就是 JSON,例如设备位置、巡检轨迹、兴趣点 POI、行政区边界、事件上报点,那么 MongoDB 存储 GeoJSON 会比较顺手。但要让查询结果正确、性能稳定,必须理解 MongoDB 对 GeoJSON 坐标、字段结构和空间索引的要求。

MongoDB存GIS数据 GeoJSON空间查询流程示意图
MongoDB 存储 GeoJSON 后,通常通过 2dsphere 空间索引完成附近、范围和相交查询。

背景:MongoDB适合存哪些GIS数据

背景:MongoDB 是文档数据库,天然适合保存结构灵活的 JSON 文档。GeoJSON 也是 JSON 格式,所以很多 GIS 项目会把空间字段直接作为文档的一部分保存。

典型场景包括:

  • 移动端上报的人员、车辆、设备实时位置。
  • WebGIS 中的兴趣点、门店、井盖、摄像头等点位数据。
  • 巡检轨迹、道路中心线、管线等线状数据。
  • 小规模管理范围、网格、行政区、服务区等面状数据。
  • 业务属性变化频繁,但空间查询需求相对明确的系统。

但需要注意:MongoDB 存 GIS 数据并不等于它是完整的 GIS 分析平台。它适合做在线业务查询,例如“查附近 1 公里的门店”“查某个行政区内的设备”“查与某个范围相交的事件”。如果你要做复杂叠加分析、拓扑检查、栅格分析或大规模空间处理,PostGIS、QGIS、ArcGIS Pro 仍然更合适。

原理:MongoDB如何识别GeoJSON空间字段

原理:MongoDB 支持 GeoJSON 对象,但它不是看到一个 JSON 就自动当作 GIS 数据。空间字段必须符合 GeoJSON 基本结构,并且要创建 2dsphere 空间索引。

一个标准的 GeoJSON 点对象通常长这样:

{
  "type": "Point",
  "coordinates": [116.397128, 39.916527]
}

这里有两个关键点:

  • type 表示几何类型,例如 PointLineStringPolygon
  • coordinates 坐标顺序必须是 经度在前,纬度在后,也就是 [longitude, latitude]

在 MongoDB 中,一条包含空间字段的业务文档可以这样设计:

{
  "name": "样例门店A",
  "category": "便利店",
  "city": "北京",
  "location": {
    "type": "Point",
    "coordinates": [116.397128, 39.916527]
  }
}

其中 location 就是 GeoJSON 空间字段。后续附近查询、范围查询、相交查询都会围绕这个字段进行。

步骤:MongoDB存GIS数据并查询GeoJSON

步骤:下面用一个门店点位示例说明从入库、建索引到查询的完整流程。示例命令可在 MongoDB Shell 或支持 MongoDB 查询语法的客户端中执行。

1. 准备集合并插入GeoJSON点数据

先向 stores 集合插入几条点位数据:

db.stores.insertMany([
  {
    "name": "门店A",
    "category": "便利店",
    "city": "北京",
    "location": {
      "type": "Point",
      "coordinates": [116.397128, 39.916527]
    }
  },
  {
    "name": "门店B",
    "category": "超市",
    "city": "北京",
    "location": {
      "type": "Point",
      "coordinates": [116.410000, 39.920000]
    }
  },
  {
    "name": "门店C",
    "category": "便利店",
    "city": "北京",
    "location": {
      "type": "Point",
      "coordinates": [116.350000, 39.900000]
    }
  }
])

如果你是从前端地图、GeoJSON 文件或采集 App 写入数据,要特别检查坐标是否已经转换为 WGS84 经纬度。MongoDB 的 GeoJSON 查询通常按地球球面模型处理,最常见的输入就是 EPSG:4326 坐标。

2. 创建2dsphere空间索引

MongoDB 查询 GeoJSON 空间数据时,最常用的是 2dsphere 索引:

db.stores.createIndex({ location: "2dsphere" })

没有空间索引时,小数据量可能还能查出结果,但数据一多就会变慢,甚至查询成本不可控。实际项目中,只要该字段用于空间查询,就应该创建 2dsphere 索引。

可以用下面命令检查索引是否创建成功:

db.stores.getIndexes()

3. 查询某个点附近的GeoJSON数据

“附近查询”是 MongoDB 存 GIS 数据最常见的需求。例如查询距离天安门附近 2 公里内的门店:

db.stores.find({
  location: {
    $near: {
      $geometry: {
        type: "Point",
        coordinates: [116.397128, 39.916527]
      },
      $maxDistance: 2000
    }
  }
})

$maxDistance 的单位是米。这个查询会返回距离目标点 2000 米范围内的数据,并按距离由近到远排序。

如果你只想判断是否在范围内,不需要按距离排序,也可以使用 $geoWithin 配合圆形范围。但在“附近搜索”场景下,$near 更直观。

4. 查询多边形范围内的数据

如果要查询某个面范围内的点,例如查询一个矩形范围内的门店,可以使用 $geoWithin$geometry

db.stores.find({
  location: {
    $geoWithin: {
      $geometry: {
        type: "Polygon",
        coordinates: [[
          [116.30, 39.85],
          [116.50, 39.85],
          [116.50, 40.00],
          [116.30, 40.00],
          [116.30, 39.85]
        ]]
      }
    }
  }
})

注意 Polygon 的坐标环必须闭合,也就是第一个点和最后一个点相同。很多 GeoJSON 查询失败,原因不是 MongoDB 语法错,而是 Polygon 本身不符合 GeoJSON 规范。

5. 查询与某个范围相交的数据

如果集合中存的是线或面,例如道路、管线、网格边界,可以用 $geoIntersects 查询与指定范围相交的要素。

示例:查询与一个多边形范围相交的管线:

db.pipelines.find({
  geometry: {
    $geoIntersects: {
      $geometry: {
        type: "Polygon",
        coordinates: [[
          [116.30, 39.85],
          [116.50, 39.85],
          [116.50, 40.00],
          [116.30, 40.00],
          [116.30, 39.85]
        ]]
      }
    }
  }
})

这里假设管线集合的空间字段叫 geometry,并且已经创建索引:

db.pipelines.createIndex({ geometry: "2dsphere" })

6. 同时按属性和空间条件查询

实际项目很少只查空间条件,通常还会加城市、类型、状态等属性过滤。例如查询北京 2 公里内的便利店:

db.stores.find({
  city: "北京",
  category: "便利店",
  location: {
    $near: {
      $geometry: {
        type: "Point",
        coordinates: [116.397128, 39.916527]
      },
      $maxDistance: 2000
    }
  }
})

如果数据量较大,可以结合业务字段建立复合索引,但空间索引字段的使用要结合实际查询模式测试。常见做法是先保证空间字段有 2dsphere 索引,再根据高频属性过滤字段补充普通索引。

常见坑:GeoJSON查询不到或结果不准

常见坑:MongoDB GeoJSON 查询的问题,多数集中在坐标、索引、几何格式和数据范围上。

1. 经纬度顺序写反

GeoJSON 坐标顺序是 [经度, 纬度],不是 [纬度, 经度]。例如北京坐标应写成:

[116.397128, 39.916527]

如果写成:

[39.916527, 116.397128]

点位会跑到错误位置,附近查询自然查不到正确结果。

2. 坐标系不是WGS84经纬度

很多国内项目数据来自 CGCS2000、高斯投影、Web Mercator 或地方坐标。如果你把米制投影坐标直接写入 GeoJSON,MongoDB 的球面空间查询会得到错误结果。

入库前应确认:

  • 是否为经纬度坐标,而不是米制平面坐标。
  • 坐标顺序是否为经度、纬度。
  • 数据是否需要从其他坐标系转换到 WGS84 或项目约定的经纬度坐标。
  • 前端地图显示坐标与数据库查询坐标是否使用同一套坐标。

3. 忘记创建2dsphere索引

没有 2dsphere 索引时,空间查询性能会明显下降。某些空间操作也依赖合适的索引才能稳定运行。上线前务必检查:

db.stores.getIndexes()

4. Polygon没有闭合

GeoJSON Polygon 的外环必须闭合。例如下面是正确写法:

[
  [116.30, 39.85],
  [116.50, 39.85],
  [116.50, 40.00],
  [116.30, 40.00],
  [116.30, 39.85]
]

最后一个坐标必须与第一个坐标一致。否则可能出现插入失败、查询失败或结果异常。

5. 把FeatureCollection直接当空间字段

MongoDB 空间索引通常建立在具体几何对象字段上,例如:

{
  "location": {
    "type": "Point",
    "coordinates": [116.397128, 39.916527]
  }
}

不要把完整的 GeoJSON FeatureCollection 直接塞进一个空间字段再指望它被当作单个几何对象查询。更推荐的方式是:一个 Feature 对应 MongoDB 中的一条文档,properties 拆为普通属性字段,geometry 作为空间字段。

方法比较:MongoDB、PostGIS和文件型GeoJSON怎么选

方法比较:MongoDB 可以存 GeoJSON,但不是所有 GIS 数据都应该放进 MongoDB。下面是几个常见方案的对比。

方案 适合场景 优势 限制
MongoDB + GeoJSON 业务文档、点位查询、附近搜索、接口服务 JSON 结构灵活,适合 Web 和移动端业务 复杂 GIS 分析能力有限,拓扑和栅格不是强项
PostGIS 空间分析、叠加计算、复杂查询、专业 GIS 后端 空间函数丰富,索引和 SQL 能力强 数据模型相对严格,学习成本略高
GeoJSON 文件 小数据交换、前端展示、临时共享 简单直观,浏览器和 WebGIS 支持好 不适合高并发查询和大规模更新
Shapefile或GeoPackage 桌面 GIS 编辑、传统数据交付、离线处理 GIS 软件兼容性好 不适合作为在线业务系统的高并发查询数据库

简单判断:如果你的核心需求是“业务数据 + 空间检索”,MongoDB 存 GIS 数据是可行的;如果你的核心需求是“专业空间分析 + 严格空间关系计算”,优先考虑 PostGIS。

检查清单:上线前确认这些配置

检查清单:在把 MongoDB GeoJSON 查询用于生产环境前,建议逐项检查下面内容。

  • 空间字段是否是标准 GeoJSON 几何对象,而不是字符串或随意 JSON。
  • 点坐标是否按 [经度, 纬度] 顺序保存。
  • 坐标是否为合理经纬度范围:经度约为 -180 到 180,纬度约为 -90 到 90。
  • Polygon 坐标环是否闭合。
  • 空间字段是否已创建 2dsphere 索引。
  • 高频属性过滤字段是否有必要创建普通索引。
  • 查询半径单位是否按米理解,没有误写成公里。
  • 前端地图坐标、后端查询坐标、数据库存储坐标是否一致。
  • 大范围查询是否分页,避免一次返回过多 GeoJSON。
  • 是否对用户传入的坐标和范围参数做合法性校验。

实务建议:先用 10 到 100 条样例数据验证查询逻辑,再导入全量数据。空间查询错误往往在小样本阶段更容易定位。

FAQ:MongoDB存GIS数据常见问题

FAQ:下面整理几个 MongoDB 存 GIS 数据和 GeoJSON 查询中最常见的问题。

1. MongoDB可以直接存GeoJSON吗?

可以。MongoDB 支持将 GeoJSON 几何对象作为文档字段保存,例如 PointLineStringPolygon 等。要进行空间查询,应在该字段上创建 2dsphere 索引。

2. MongoDB GeoJSON查询为什么查不到数据?

优先检查四件事:坐标顺序是否写反、坐标系是否正确、空间字段是否建立 2dsphere 索引、查询 Polygon 是否闭合。很多“查不到”的问题都不是查询语句本身错,而是数据格式不符合 GeoJSON 要求。

3. MongoDB附近查询的距离单位是什么?

使用 GeoJSON 和 2dsphere 索引进行 $near 查询时,$maxDistance 通常按米理解。例如 2000 表示 2000 米,不是 2 公里字符串,也不是经纬度度数。

4. GeoJSON的经纬度顺序到底是什么?

GeoJSON 坐标顺序是 [longitude, latitude],即经度在前、纬度在后。中文习惯常说“纬度、经度”,这很容易导致写反。做 MongoDB GeoJSON 查询时一定要按 GeoJSON 规范来。

5. MongoDB和PostGIS哪个更适合GIS项目?

如果项目以业务文档、实时点位、附近查询、Web API 为主,MongoDB 使用起来比较自然。如果项目需要大量空间叠加、缓冲区分析、拓扑关系、复杂空间 SQL,PostGIS 更合适。两者也可以组合使用:MongoDB 存业务事件和实时状态,PostGIS 管理基础地理数据和分析结果。

结论:MongoDB存GeoJSON的关键是格式、索引和查询类型

结论:MongoDB存GIS数据?GeoJSON如何查询? 这个问题的核心并不复杂:用标准 GeoJSON 保存几何字段,确认坐标是经度在前、纬度在后,为空间字段创建 2dsphere 索引,然后根据需求选择 $near$geoWithin$geoIntersects

对于 WebGIS 和业务系统来说,MongoDB 存 GIS 数据适合做轻量、灵活、面向接口的空间检索。但如果你要做复杂 GIS 分析,不要勉强用 MongoDB 解决所有问题。正确的架构通常是:MongoDB 负责业务文档和快速查询,PostGIS 或桌面 GIS 工具负责专业空间处理。

实际落地时,建议先从一个最小闭环开始:插入一批标准 GeoJSON 点数据,创建 2dsphere 索引,完成附近查询和范围查询,再逐步扩展到线、面和复合属性过滤。这样最容易发现坐标、格式和性能问题。