truf.js 创建只有两个点 polygon 写入 mongoDb 的 2dsphere 索引报错的问题

博客讲述了在使用Turf.js处理地理数据时遇到的问题,即当LineString对象的bbox生成的bboxPolygon为直线时,MongoDB的2dsphere索引入库会失败。作者分析了原因,指出当两个Y坐标相同时,bboxPolygon将只有一个有效点,不满足MongoDB的最小要求。为解决此问题,作者提出了在形成bbox时检查并微调坐标,确保bboxPolygon至少有3个点的方法,从而避免入库错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

我使用 truf.js 提取一个 lineString 的 bbox ,再根据 bbox 得到一个 bboxPolygon,最后将数据写入带 2dsphere 索引的文档,这时有些特定的 lineString 会出现入库失败,原因是 MongoDb 提示 polygon 至少需要3个点。

// 计算 lineString 对象的矩形边界
const turf = require("@turf/turf");
let lineString = {
        type : "LineString",
        coordinates : [ [10,25],
						[11,25],
						[12,25],
						[15,25],
						[20,25]]
}
const bbox = turf.bbox(lineString);
const bboxPolygon = turf.bboxPolygon(bbox);
// TODO: 写库

原因分析:

在检查 bbox 的数据时发现:

[[10,25],[20,25]]

这是一个直线,两个Y坐标是一致的,结果导致 bbox 生成的 bboxPolygon数据也不对:

[
	[10,25],
	[20,25],
	[20,25],
	[10,25],
	[10,25]
]

排重后只有两个点,这在 MongoDb 入库时的 2dsphere 索引判定就会报错。


解决方案:

对 bbox 的第二个点做微小位移,防止 bboxPolygon 的点少于3个


    /**
     * 计算 lineString 对象的矩形边界
     * 对 bbox 做检查,防止 bbox 成为直线
     * 如果 bboxPolygon 使用 bbox 生成,在入库到mongoDb 时会报错
     */
    calculateRectangleBounds(turfLineString) {
        // 计算 lineString 对象的矩形边界
        let bbox = turf.bbox(turfLineString);
        // 检查 bbox 点集合被压缩成直线的情况
        let [x1, y1, x2, y2] = bbox;
        // 如果被压缩成直线,对后一个点的坐标做微小的位移
        let isFixed = false;
        if (x1 === x2) {
            x2 = this.floatTinyOffset(x2);
            isFixed = true;
        }
        if (y1 === y2) {
            y2 = this.floatTinyOffset(y2);
            isFixed = true;
        }
        if (isFixed) {
            bbox = [x1, y1, x2, y2];
        }
        const bboxPolygon = turf.bboxPolygon(bbox);
        return bboxPolygon;
    }

    /**
     * 对浮点数做微小的加法
     * eg: 38.1280970893173 -> 38.1280970893174
     * @param {Number} floatNumber
     * @returns Number
     */
    floatTinyOffset(floatNumber) {
        floatNumber = _.toNumber(floatNumber);
        let [zs, xs] = floatNumber.toString().split(".");
        return _.toNumber(zs + "." + (_.toInteger(xs) + 1));
    }
    
<think>好的,我现在需要帮助用户了解如何在MongoDB中存储地理空间数据或地图坐标。根据用户提供的引用内容,我需要整理出正确的方法,并确保回答符合用户的要求,比如使用正确的中文和LaTeX格式,生成相关问题,并正确引用资料。 首先,用户提到的引用中有几个关键需要整合。引用[5]提到MongoDB使用2dsphere索引,且地理数据必须符合GeoJSON格式。引用[2]指出只需要type和coordinates两个字段。引用[3]提到了使用百度地图拾取器获取经纬度,然后通过API写入MongoDB。还有引用[1]和[4]提到MongoDB和PostGIS的地理空间处理,但用户的问题集中在MongoDB,所以可能主要关注前三个引用。 接下来,我需要确保回答的结构清晰。首先介绍两种存储方式:GeoJSON格式和传统坐标对。然后说明如何创建地理索引,因为这是查询的关键。最后提到数据采集和验证的注意事项,比如引用[3]中的方法。 需要注意LaTeX格式是否正确。用户要求行内数学表达式用$...$,例如经纬度可能涉及坐标对的表示,如$[经度, 纬度]$,但检查引用中的例子,比如引用[2]中的coordinates字段存储的是数组,所以可能需要用代码块或普通文本表示,而不是LaTeX。可能不需要数学表达式,但需要确保代码示例正确。 然后生成相关问题,参考用户示例中的问题类型,比如存储格式、索引选择、应用场景等,结合引用内容中的,比如GeoJSON要求、2dsphere索引等。 最后,确保引用正确,每个引用来源在回答末尾标注。例如,引用[2]提到只需要type和coordinates,引用[5]提到2dsphere索引需要GeoJSON格式,引用[3]提到数据采集方法。 现在,将这些整合成一个结构化的回答,分步骤说明存储方法,注意事项,并生成相关问题。需要确保语言简洁,符合中文习惯,避免技术术语过于复杂,同时准确引用资料。</think>在MongoDB中存储地理空间数据或地图坐标,需按照以下结构化步骤实现: --- ### 一、地理空间数据存储格式 1. **GeoJSON标准格式** MongoDB要求地理坐标数据以GeoJSON对象形式存储,仅需包含`type`和`coordinates`两个核心字段[^2]。例如: ```json { "location": { "type": "Point", // 数据类型(/线/面等) "coordinates": [116.404, 39.915] // 经纬度数组,顺序为[经度, 纬度] } } ``` 支持的类型包括`Point`()、`LineString`(线)、`Polygon`(多边形)等[^1]。 2. **传统坐标对格式(Legacy Pairs)** 适用简单场景,直接将坐标存储为双元素数组,但需注意**经度在前、纬度在后**: ```json { "location": [116.404, 39.915] } ``` --- ### 二、创建地理索引 需为地理字段创建索引以支持空间查询: 1. **2dsphere索引** 针对GeoJSON数据,支持球面几何计算(如距离、相交等): ```javascript db.collection.createIndex({ "location": "2dsphere" }) [^5] ``` 2. **2d索引** 仅适用于传统坐标对,用于平面坐标系查询: ```javascript db.collection.createIndex({ "location": "2d" }) ``` --- ### 三、数据采集与验证 1. **坐标获取工具** 使用地图工具(如百度地图拾取器[^3])获取经纬度,或通过API(如逆地理位置接口)批量导入数据。 2. **数据验证** 确保坐标范围有效:经度$[-180, 180]$,纬度$[-90, 90]$[^1]。 --- ### 四、应用示例 查询某5公里范围内的所有位置: ```javascript db.places.find({ "location": { $near: { $geometry: { type: "Point", coordinates: [116.404, 39.915] }, $maxDistance: 5000 // 单位:米 } } }) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值