我不知道是否有一个服务器端的库可以为你完成这项工作,但是我可以给你一些指导,告诉你如何自己实现。
聚类的基本方法就是计算标记之间的距离,当两个标记足够接近时,用它们之间的中点来代替它们。
除了限制标记之间的最小距离外,您也可以(或者说只需)选择限制要作为结果的聚类/标记数目。
为此,您可以计算所有标记对之间的距离,将它们排序,然后从上往下合并,直到您只有所需的标记/聚类数量。
在形成聚类时,如果要精确确定中点位置,可以考虑每个要合并的标记所代表的实际标记数。可以将该数字看作权重,并将两个标记之间的线看作天平,则可以选择平衡天平的点而不仅仅是选择中点。
如果您具有有限数量的标记,则我猜这种简单的聚类形式已经足够好了。如果您的数据集(标记数和它们的位置)大致保持不变,您可以在服务器上定期计算聚类,将其缓存,并直接从缓存为客户提供服务。
然而,如果您需要支持具有潜在遍布全球的标记的大规模情况,则需要更复杂的方法。
上述的聚类算法不可扩展。事实上,它的计算成本通常会随着标记数量的增加呈指数级增长。
为了解决这个问题,您可以将世界分成若干部分,从每个部分计算聚类,然后为客户端提供服务。这确实可以支持扩展,因为工作负载可以分割并由多个(大致)独立的服务器执行。
然后问题在于如何找到一个好的分区方案。您可能还想考虑在不同的缩放级别上提供不同的标记聚类,并且您的分区方案也应该包括这一点以允许扩展。
谷歌将地图划分为具有x、y和z坐标的瓦片,其中x和y是瓦片从地图的西北角开始的水平和垂直位置,z表示缩放级别。
在最小缩放级别(零)下,整个地图由单个瓦片组成。 (所有瓦片均为256x256像素)。在下一个缩放级别中,该瓦片被分成四个子瓦片。这种方式一直持续下去,因此在缩放级别2中,每个瓦片都被分成四个子瓦片,这给我们总共16个瓦片。缩放级别3有64个瓦片,级别4有256个瓦片,以此类推。(任何缩放级别上的瓦片数可以表示为4^z
。)
使用这种分区方案,可以从最低缩放级别(最高z坐标)开始计算每个瓦片的聚类,一直向上冒泡直到达到顶部。
单个瓦片要进行聚类的标记集合是其四个子瓦片的所有标记(其中一些可能表示聚类)的并集。
这样做可以得到有限的计算成本,并且可以很好地将数据分块发送到客户端。 客户端不必请求给定缩放级别下的所有标记(这将不扩展),而是可以在加载到地图中的每个瓦片时按瓦片逐个请求标记。
然而,这种方法存在一个缺陷:考虑两个相邻的瓦片,一个在左边,另一个在右边。 如果左侧瓦片包含其极右侧的标记/聚类,而右侧瓦片包含其极左侧的标记/聚类,则应合并这两个标记/聚类,但由于我们是针对每个瓦片单独执行聚类机制,因此不会发生合并。
为了解决这个问题,您可以在聚类后对瓦片进行后处理,以便合并位于四条边上的标记/聚类,并考虑给定瓦片的八个相邻瓦片。此后合并机制仅在我们可以假设任何单个聚类
足够小,不会影响不在同一子瓦片中的周围标记的情况下才能正常工作。然而,这是一个合理的假设。
最后注意:使用缩放的方法,您将使客户端发出多个小请求。这些请求将具有局部性(即不随机请求瓦片,而是通常同时访问地理位置接近的瓦片)。
为提高查找/查询性能,您将受益于使用具有此局部性质的搜索键(表示瓦片),因为这将在磁盘上的相邻数据块中存储相邻瓦片的数据-提高读取时间和缓存利用率。
您可以使用瓦片/子瓦片分区方案形成这样的键。让顶部瓦片(跨越整个地图的单个瓦片)使用空字符串作为键。接下来,让其每个子瓦片具有A、B、C和D等键。下一个级别将具有AA、AB、AC、AD、BA、BC等键,依此类推。
递归应用这一点,您将得到一个分区键,该分区键标识您的瓦片,允许快速转换为x、y、z坐标并具有局部性质。这个键命名方案有时被称为
Quad Key,因为分区方案形成了一个
四叉树。局部性质与使用Z序曲线将2D值映射到1D值时获得的属性相同。
如果您需要更多细节,请告诉我。