【问题标题】:Query which LineString in GeoJson MultiLineString在 GeoJson MultiLineString 中查询哪个 LineString
【发布时间】:2015-09-08 03:52:54
【问题描述】:

鉴于我有一条由 2 个主要巴士站组成的公共巴士路线。路由分为2条子路由,循环互连,但路由编号相同。

所以我将 routes 存储在 GeoJson MultiLineString 中(在 GeoJson 中包含 2 个 LineStrings)。 route 文档类似于:

{
  "routeName": "A123",
  "route": {
    "type": "MultiLineString",
    "coordinates": [
        [ [100.0, 0.0], [101.0, 1.0], [102.0, 2.0] ],// 1st sub route
        [ [102.0, 2.0], [103.0, 3.0], [100.0, 0.0] ] // 2nd sub route
      ]
  }
}

现在如果使用$near查询,我可以得到整个最近的route文档。但是我想知道最近的子路线或LineString,第一条或第二条。

是否有可能,如果可以,是否有可能在一个查询中获得附近的路线和子路线?

【问题讨论】:

    标签: mongodb geolocation mongodb-query aggregation-framework geojson


    【解决方案1】:

    MongoDB 不能像您想要的那样“分解” GeoJSON 对象以匹配“最近点”。因此,您的问题可能更适合另一种结构,该结构将允许独立考虑每个“停止”,而不是仅将其表示为“LineString”上的一个点。

    在查询中使用它的一种非常有效的方法是:

    {
      "routename": "A123",
      "stops": [
        { 
          "type": "out",
          "location": {
            "type": "Point",
            "coordinates": [100.0,0.0]
          }
        },
        { 
          "type": "out",
          "location": {
            "type": "Point",
            "coordinates": [101.0,1.0]
          }
        },
        { 
          "type": "return",
          "location": {
            "type": "Point",
            "coordinates": [102.0,2.0]
          }
        },
        { 
          "type": "return",
          "location": {
            "type": "Point",
            "coordinates": [103.0,3.0]
          }
        }
      ]
    }
    

    这将路线上的每个“停靠点”分解到一个不同的“点”,并保持路线“类型”表示停靠点是在行程的“离开”或“返回”路段。

    然后您可以使用$geoNear 运行此聚合查询,以便匹配和投影数组条目中的“最近点”。下一阶段将匹配的位置与数组中的每个“停靠点”进行比较,以提取该停靠点所属的路线段:

    db.routes.aggregate([ 
      { "$geoNear": {
        "near": {
          "type": "Point",
          "coordinates": [103.0, 2.0]
        },
        "distanceField": "distance",
        "spherical": true,
        "includeLocs": "loc"
      }},
      { "$project": {
        "routeName": 1,
        "distance": 1,
        "route": {
          "$let": {
            "vars": {
              "matchedStop": {
                "$setDifference": [
                  { "$map": {
                    "input": "$stops",
                    "as": "stop",
                    "in": {
                      "$cond": [
                        { "$eq": [ "$$stop.location", "$loc" ] },
                        "$$stop",
                        false
                      ]
                    }
                  }},
                  [false]
                ]
              }
            },
            "in": {
              "$setDifference": [
                { "$map": {
                  "input": "$stops",
                  "as": "stop",
                  "in": {
                    "$cond": [
                      { "$setEquals": [
                        { "$map": { "input": ["A"], "as": "el", "in": "$$stop.type" } },
                        "$$matchedStop.type"
                      ]},
                      { 
                        "type": "$$stop.type",
                        "location": "$$stop.location",
                        "nearest": {
                          "$eq": [ "$$stop.location", "$loc" ]
                        }
                      },
                      false
                    ]
                  }
                }},
                [false]
              ]
            }
          }
        }
      }}
    ])
    

    这会给你这种类型的输出:

    {
        "_id" : ObjectId("55ee6c0a2343a0d2e1650e28"),
        "distance" : 111251.03086891436,
        "route" : [
                {
                        "type" : "return",
                        "location" : {
                                "type" : "Point",
                                "coordinates" : [
                                        102,
                                        2
                                ]
                        },
                        "nearest" : true
                },
                {
                        "type" : "return",
                        "location" : {
                                "type" : "Point",
                                "coordinates" : [
                                        103,
                                        3
                                ]
                        },
                        "nearest" : false
                }
        ]
    }
    

    这主要是大量使用$map 来提取所需的元素。所以第一种情况是从$geoNear中提取“includeLocs”返回中已经识别的匹配元素。然后,这允许您重新处理数组并仅过滤掉包含相同“类型”的“元素”用于路线的腿,并且为了增加美观性,我们还标记了数组成员实际上是“最近”匹配的时间。

    特别是与要匹配的路由“类型”,早先$map返回的变量实际上是一个数组。所以你只能真正将一个数组与另一个“数组”进行比较,这就是为什么$map 再次用于单个字段值,以便将其变成一个数组本身:

    { "$setEquals": [
      { "$map": { "input": ["A"], "as": "el", "in": "$$stop.type" } },
      "$$matchedStop.type"
    ]},
    

    这就是该部分的全部内容,当然$setEquals 会测试两个数组以查看它们是否实际上相同。

    这里的其他内容是$let,以便让“matchedStop”更易于访问,$setDifference 实质上从通过对数组成员进行的测试中过滤掉false 的任何返回值$cond 运营商。

    当然,这里的替代方案基本上只是将每个“站点”分离到集合中它自己的文档中,然后运行一个查询以查找“最近”文档,并运行另一个查询以查找相同的“路线”和其他在您的返回信息中停在该路线的同一“腿”上。但这仍然会被包含在一个文档和匹配中,并且非常有效,尤其是对于多个“路由”匹配。

    【讨论】:

    • 好的,差不多了,但我确实有停靠点之间的航路点数据,这可能会改善“附近”的结果。因此,似乎我需要更新新结构以将索引stops.waypoints 包含为LineString 以供$geoNear 使用,同时保持stops.location Point 未编入索引以供$project 使用,对吗?
    • @CallMeLaNN 我不能为你编写你的项目,但只是告诉你什么不能工作,什么可以工作。不可能将“LineString”用于您的目的,因为您希望每个“节点”作为您需要专门匹配的位置。这就是答案向您展示的内容。
    • 我现在明白我无法从MultiLineStringLineString 中提取Point。由于在两个站点之间提取最小的LineString 是可以接受的,不是说每个节点,应该没问题,我可以得到哪个“子路线”和“站点”。 +1 提及聚合框架。谢谢你的时间。
    • @CallMeLaNN LineString 可能对您不起作用,因为您仍然无法识别离您最近的“停止”。 $geoNear 在此处聚合的唯一好处是内容是如上所示的“数组”,然后您可以匹配最近点的“数组条目”。否则使用文档。但是您无法从MultiLineStringLineString 中获取该数据。因此教训。
    • 从你的例子中得到想法后,让我澄清一下我的想法。这里的LineString 指的是停靠点之间的航路点,而不是停靠点列表。我将查询哪个站点,而不是单个航路点“节点”。我遵循与您建议相同的方式:一系列停靠点包含一个位置Point,但我添加了航点LineString。让我试试看。最坏的情况是我将创建另一个包含多个查询的文档。
    猜你喜欢
    • 2023-03-09
    • 2014-02-13
    • 1970-01-01
    • 2020-12-09
    • 2019-03-07
    • 1970-01-01
    • 1970-01-01
    • 2015-10-03
    • 2021-05-06
    相关资源
    最近更新 更多