Geographic aggregation using the map tile index

Posted on Sep 3, 2022

When dealing with large amounts of scattered data, sometimes we need to provide a read-only query API to visualize on a map. When the amount of data is too large, say millions, it is not appropriate to return it all to the front end for processing on the browser. Some aggregation should be completed within the back-end service to return the aggregated search to the front end. Here’s how to do this with the MongoDB + tile index in Go.

Let’s start with the most basic data.

Let’s assume that a point record on a map can be defined in the following way:

type GeoPoint struct {
	Type        string    `bson:"type" json:"type"`
	Coordinates []float64 `bson:"coordinates" json:"coordinates"`
}

type Record struct {
	ID       primitive.ObjectID `bson:"_id"`                      // ObjectID
	Location GeoPoint           `bson:"location" json:"location"` // Raw point
}

That’s because to store data at different scales, extend the Record field.

type Tile struct {
	X, Y, Z    uint32
	Key        string
}

type Record struct {
	ID       primitive.ObjectID `bson:"_id"`                      // ObjectID
	Location GeoPoint           `bson:"location" json:"location"` // Raw point
	Levels   []Tile             `bson:"levels" json:"-"`          // Not export to outside in JSON
}

Store Zoom’s tile index in [min,max] along with x,y,z . Where Key is the splice format of {x}-{y}-{z}.

Using Mongo’s Aggregate function, you can get the number of occurrences of each ‘Key’ when you need to find it:

l := 12
pipes := bson.A{
    bson.M{
        "$match": bson.M{"levels.z": l},  // Filter out the zoom level to the desired data
    },
    bson.M{
        "$unwind": "$levels",  // Unwind
    },
    bson.M{
        "$match": bson.M{"levels.z": l},  // Remove unwanted zoom after unwind
    },
    bson.M{
        "$group": bson.M{
            "_id":   "$levels.key",
            "count": bson.M{"$sum": 1},  // Count
        },
    },
}
myCollecion.Aggregate(ctx, pipes)

Example of a single record:

{
  "_id": "2413-3079-13",
  "count": 12
}

Then inverse from the Key to the center of the tile coordinates, together with statistical results to the front, with the relevant SDK on the map can be displayed.

The code for this article: https://github.com/ringsaturn/geo-agg-tile-index-example