Geotile 网格聚合

Geotile 网格聚合 #

geotile_grid 聚合将 geo_point 值按照地图瓦片坐标分组。与 geohash_grid 类似,但使用的是 Web 地图标准的瓦片坐标系统(x/y/zoom),这使得它与 Google Maps、OpenStreetMap、Mapbox 等瓦片地图服务的集成更加方便。

相关指南(先读这些) #


瓦片坐标系统 #

Web 地图使用 Slippy Map 瓦片坐标系统:

  • zoom:缩放级别(0-29),级别越高网格越精细
  • x:水平瓦片索引(从左到右)
  • y:垂直瓦片索引(从上到下)

瓦片标识符格式:{zoom}/{x}/{y},如 14/8532/5765

缩放级别与覆盖范围 #

Zoom瓦片数量每个瓦片覆盖范围
01整个世界
14~ 20,000km
51,024~ 1,250km
10~100万~ 39km
15~10亿~ 1.2km
20~1万亿~ 38m

参数说明 #

参数必需/可选类型说明
field必需String包含 geo_point 值的字段名
precision可选Integer缩放级别(0-29)。默认 7
bounds可选Object限制聚合的地理边界框
size可选Integer返回的最大 bucket 数量。默认 10000
shard_size可选Integer每个分片返回的 bucket 数量

基础用法 #

简单瓦片网格聚合 #

GET /locations/_search
{
  "size": 0,
  "aggs": {
    "tiles": {
      "geotile_grid": {
        "field": "location",
        "precision": 8
      }
    }
  }
}

响应示例 #

{
  "aggregations": {
    "tiles": {
      "buckets": [
        {
          "key": "8/131/84",
          "doc_count": 256
        },
        {
          "key": "8/131/85",
          "doc_count": 189
        },
        {
          "key": "8/132/84",
          "doc_count": 145
        }
      ]
    }
  }
}

每个 bucket 的 key 是瓦片坐标 {zoom}/{x}/{y},可以直接用于请求对应的地图瓦片。


配合边界框过滤 #

限制在特定区域内聚合:

GET /locations/_search
{
  "size": 0,
  "query": {
    "bool": {
      "filter": {
        "geo_bounding_box": {
          "location": {
            "top_left": {
              "lat": 40.8,
              "lon": -74.1
            },
            "bottom_right": {
              "lat": 40.4,
              "lon": -73.7
            }
          }
        }
      }
    }
  },
  "aggs": {
    "nyc_tiles": {
      "geotile_grid": {
        "field": "location",
        "precision": 12
      }
    }
  }
}

使用 bounds 参数 #

直接在聚合中指定边界范围:

GET /locations/_search
{
  "size": 0,
  "aggs": {
    "tiles": {
      "geotile_grid": {
        "field": "location",
        "precision": 10,
        "bounds": {
          "top_left": { "lat": 40.8, "lon": -74.1 },
          "bottom_right": { "lat": 40.4, "lon": -73.7 }
        }
      }
    }
  }
}

嵌套子聚合 #

每个瓦片的中心点 #

GET /locations/_search
{
  "size": 0,
  "aggs": {
    "tiles": {
      "geotile_grid": {
        "field": "location",
        "precision": 10
      },
      "aggs": {
        "centroid": {
          "geo_centroid": {
            "field": "location"
          }
        }
      }
    }
  }
}

每个瓦片的统计信息 #

GET /real_estate/_search
{
  "size": 0,
  "aggs": {
    "tiles": {
      "geotile_grid": {
        "field": "location",
        "precision": 12
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        },
        "price_stats": {
          "stats": {
            "field": "price"
          }
        }
      }
    }
  }
}

在 Composite 聚合中使用 #

geotile_grid 可以作为 composite 聚合的源,实现分页遍历所有瓦片:

GET /locations/_search
{
  "size": 0,
  "aggs": {
    "tiles": {
      "composite": {
        "size": 100,
        "sources": [
          {
            "tile": {
              "geotile_grid": {
                "field": "location",
                "precision": 8
              }
            }
          }
        ]
      }
    }
  }
}

翻页时使用 after 参数:

GET /locations/_search
{
  "size": 0,
  "aggs": {
    "tiles": {
      "composite": {
        "size": 100,
        "sources": [
          {
            "tile": {
              "geotile_grid": {
                "field": "location",
                "precision": 8
              }
            }
          }
        ],
        "after": {
          "tile": "8/131/85"
        }
      }
    }
  }
}

与地图库集成 #

转换瓦片坐标为边界框 #

前端可以使用标准公式将瓦片坐标转换为经纬度边界:

function tile2boundingBox(z, x, y) {
  const n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
  return {
    north: 180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))),
    south: 180 / Math.PI * Math.atan(0.5 * (
      Math.exp(Math.PI - 2 * Math.PI * (y + 1) / Math.pow(2, z)) - 
      Math.exp(-(Math.PI - 2 * Math.PI * (y + 1) / Math.pow(2, z)))
    )),
    west: x / Math.pow(2, z) * 360 - 180,
    east: (x + 1) / Math.pow(2, z) * 360 - 180
  };
}

// 解析瓦片 key
function parseTileKey(key) {
  const [z, x, y] = key.split('/').map(Number);
  return { z, x, y };
}

在 Leaflet 中显示聚合结果 #

// 假设 response 是 Easysearch 返回的聚合结果
const buckets = response.aggregations.tiles.buckets;

buckets.forEach(bucket => {
  const { z, x, y } = parseTileKey(bucket.key);
  const bounds = tile2boundingBox(z, x, y);
  
  L.rectangle([
    [bounds.south, bounds.west],
    [bounds.north, bounds.east]
  ], {
    color: getColorByCount(bucket.doc_count),
    weight: 1,
    fillOpacity: 0.5
  }).bindPopup(`数量: ${bucket.doc_count}`).addTo(map);
});

性能优化 #

1. 根据地图缩放级别动态调整精度 #

function getPrecisionForZoom(mapZoom) {
  // 瓦片精度通常与地图缩放级别相同或略低
  return Math.min(mapZoom, 15);
}

2. 限制返回数量 #

对于高精度查询,限制返回的瓦片数量:

{
  "aggs": {
    "tiles": {
      "geotile_grid": {
        "field": "location",
        "precision": 15,
        "size": 1000
      }
    }
  }
}

3. 配合边界框使用 #

始终使用 boundsgeo_bounding_box 过滤器,避免扫描全量数据。


geotile_grid vs geohash_grid #

特性geotile_gridgeohash_grid
坐标系统Web 瓦片 (x/y/zoom)Geohash 编码
精度范围0-291-12
Key 格式8/131/84dr5rs
地图集成✅ 直接对应瓦片⚠️ 需要转换
网格形状正方形(墨卡托投影)矩形(可变)

选择建议

  • 与 Web 地图服务集成 → geotile_grid
  • 通用地理聚合 → geohash_grid
  • 需要与第三方 Geohash 服务交互 → geohash_grid

相关文档 #