在 GeoJson MultiLineString 中查询哪个 LineString

Posted

技术标签:

【中文标题】在 GeoJson MultiLineString 中查询哪个 LineString【英文标题】:Query which LineString in GeoJson MultiLineString 【发布时间】: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,第一条或第二条。

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

【问题讨论】:

【参考方案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。让我试试看。最坏的情况是我将创建另一个包含多个查询的文档。

以上是关于在 GeoJson MultiLineString 中查询哪个 LineString的主要内容,如果未能解决你的问题,请参考以下文章

维护 MultiLineString 上的拓扑

访问与 LINESTRING M 和 MULTILINESTRING M 几何相关的 M 值

是否可以使用 JavaScript 确定 GeoJSON 点是否在 GeoJSON 多边形内?

在 GeoJson 对象中包含 JSON 流?

GIS风暴GeoJSON数据格式案例全解

python 在Python中将GeoJSON转换为WKT或从WKT转换。 #python #geojson #geometry