2018-01-28 23:08
浏览 220


I'm working with OpenStreeMap data dump into a MongoDB instance, the following collections exists nodes, ways and relations.

I'm querying all nodes within a radius from a given geospatial point, and to know how these nodes relate I'm working with the ways collection trying to retrieve all ways that contain any node from my previous geospatial query.

Then, I'm trying to include the geospatial coordinates in the way document (it already have a loc.coordinates field which is empty for some reason) using the node IDs it contains in the field loc.nodes. Along with the help provided in this answer I have come to the following code:

package main

import (

mgo "gopkg.in/mgo.v2"


// GeoJSON Holds data of geospatial points
type GeoJSON struct {
Type string json:"-"
Coordinates []float64 json:"coordinates"

type waynodes struct {
Type string
Coordinates []float64
Nodes []int

// OSMNode Represet a single point in space.
// https://wiki.openstreetmap.org/wiki/Node
// A node is one of the core elements in the OpenStreetMap data model. It
// consists of a single point in space defined by its latitude, longitude and
// node id. A third, optional dimension (altitude) can also be included:
// key:ele (abrev. for "elevation"). A node can also be defined as part of a
// particular layer=* or level=*, where distinct features pass over or under
// one another; say, at a bridge. Nodes can be used to define standalone point
// features, but are more often used to define the shape or "path" of a way.
type OSMNode struct {
ID int bson:"_id"
Location GeoJSON bson:"loc"
Tags map[string]interface{} bson:"tags,omitempty"

// OSMWay Represent an ordered list of nodes
// https://wiki.openstreetmap.org/wiki/Way
// A way is an ordered list of nodes which normally also has at least one tag
// or is included within a Relation. A way can have between 2 and 2,000 nodes,
// although it's possible that faulty ways with zero or a single node exist. A
// way can be open or closed. A closed way is one whose last node on the way is
// also the first on that way. A closed way may be interpreted either as a
// closed polyline, or an area, or both.
// The nodes defining the geometry of the way are enumerated in the correct
// order, and indicated only by reference using their unique identifier. These
// nodes must have been already defined separately with their coordinates.
type OSMWay struct {
ID int bson:"_id"
Location waynodes bson:"loc"
Tags map[string]interface{}

// km2miles convert a distance in kilometers to miles and then return the
// radius of such distance.
func km2miles(dist float64) float64 {
r := dist * 0.621371
// https://en.wikipedia.org/wiki/Earth_radius#Fixed_radius
return r / 3963.2

// nodes2list return a string list of node IDs from a list of OSMNode objects
func nodes2list(l []OSMNode) []int {
var list []int
for _, v := range l {
list = append(list, v.ID)
return list

// GetGeoWithinPos Return all points in a given point of Earth within the
// radius of dist.
func (db *DB) GetGeoWithinPos(long, lat, dist float64) ([]OSMWay, error) {
// Look at nodes document in our osm database
c := db.m.DB("osm").C("nodes")
// Query all nodes within a range from a spatial point: It should be
// equivalent to:
// db.nodes.find(
// {loc:
// {$geoWithin:
// {$centerSphere: [[-83.4995983, 10.1033002], 0.186411 / 3963.2]
// }
// }
// }, {"_id": 1});
var nodesresult []OSMNode
err := c.Find(bson.M{
"loc": bson.M{
"$geoWithin": bson.M{
"$centerSphere": []interface{}{
[]interface{}{long, lat}, km2miles(dist),
}).Select(bson.M{"_id": 1}).All(&nodesresult)

if err != nil {
    return nil, err
} else if nodesresult == nil {
    return nil, fmt.Errorf("Nodes not found on %f lat, %f long in a radius of %f km", lat, long, dist)
} else if nodesresult[0].ID == 0 {
    return nil, fmt.Errorf("Nodes incorrectly unmarshall: %#v", nodesresult[0:3])

// Prepare a pipeline
pipe := []bson.M{
        // Match only ways that contains the ID of the nodes
        // from the query on `qsn`
        "$match": bson.M{
            "loc.nodes": bson.M{
                "$in": nodes2list(nodesresult), // Return []int
        // Now look for the nodes at `nodes` collection present
        // at `loc.nodes` field...
        "$lookup": bson.M{
            "from":         "nodes",
            "localField":   "loc.nodes",
            "foreignField": "_id",
            "as":           "loc.coordinates",
        // ...and set the field `loc.coordinates` with the
        // coordinates of all nodes.
        "$addField": bson.M{
            "loc.coordinates": bson.M{
                "$reduce": bson.M{
                    "input":        "$loc.coordinates.loc.coordinates",
                    "initialValue": []float64{},
                    "in":           bson.M{"$concatArrays": []string{"$$this", "$$value"}},
// Query ways collection
w := db.m.DB("osm").C("ways")
var ways []OSMWay
// Execute the pipeline 
  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • drxdai15012937753 2018-02-01 07:01

    This is because there is a typo in your aggregation pipeline. The operator is called $addFields not $addField (missing an s).

    The method invocation of w.Pipe() should throw an error something along the lines of Unrecognized pipeline stage name: '$addField'. However, your code is not checking the err variable that is returned by Pipe(). Since you're only checking variable ways which would be nil due to the error, your method returns (nil, "Ways not found within %0.2f km/radius (%f mil/radius)"); thus masking the pipeline error.

    I would suggest to check the content check err first:

    err = w.Pipe(pipe).All(&ways)
    if err != nil {
      //handle error
    点赞 打赏 评论

相关推荐 更多相似问题