谷歌地图API V3的服务器端集群化

10

我正在开发一种谷歌地图概览小部件,该小部件将位置显示为地图上的标记。标记数量从几百个到数千个不等(高达10000个)。目前我正在使用 MarkerClusterer for google maps v3 1.0google maps javascript api v3 (premier),对于例如一百个标记而言效果还不错。由于标记数量会增加,所以我需要一种新的方法来聚类这些标记。据我所知,保持性能的唯一方法是将聚类从客户端移至服务器端。有没有人知道一个能够帮我完成这项工作的好的 PHP5 库?

目前我正在深入研究谷歌地图的层机制。也许有一些领先的 PHP 库可供我开始查看?我也遇到了 FusionTables,但由于我需要聚类,我认为这可能不是正确的解决方案。

提前感谢!


根据我的经验,使用MarkerClusterer更多的是关于谷歌处理单个标记而不是在聚类时显示它们。 - slawekwin
那么您会说MarkerClusterer能够在不花费太长时间渲染的情况下正确地显示如此大量的标记吗?您一般使用MarkerClusterer时聚类了多少个标记? - mayrs
我渲染过的最大数字大约是5000,在一台不太快的计算机上,当数字在1000以内时,标记数量更新非常流畅,后来标记数量在增加几百个后才开始更新,但没有任何明显的性能损失。 - slawekwin
即使标记弹出窗口的实际数据最初未加载,当显示超过5000个标记时,地图仍会变得非常缓慢。不幸的是,我没有找到关于服务器端聚类的太多信息。我也不知道谷歌地图如何获取这些聚类数据,以及是否需要额外的JavaScript库(如markerclusterer)来对它们进行聚类?我在这个主题上有点迷失... - mayrs
1
看起来是一个重复的问题:http://stackoverflow.com/questions/986852/clustering-coordinates-on-server-side - Andrey Adamovich
3个回答

11

我不知道是否有一个服务器端的库可以为你完成这项工作,但是我可以给你一些指导,告诉你如何自己实现。

聚类的基本方法就是计算标记之间的距离,当两个标记足够接近时,用它们之间的中点来代替它们。

除了限制标记之间的最小距离外,您也可以(或者说只需)选择限制要作为结果的聚类/标记数目。

为此,您可以计算所有标记对之间的距离,将它们排序,然后从上往下合并,直到您只有所需的标记/聚类数量。

在形成聚类时,如果要精确确定中点位置,可以考虑每个要合并的标记所代表的实际标记数。可以将该数字看作权重,并将两个标记之间的线看作天平,则可以选择平衡天平的点而不仅仅是选择中点。

如果您具有有限数量的标记,则我猜这种简单的聚类形式已经足够好了。如果您的数据集(标记数和它们的位置)大致保持不变,您可以在服务器上定期计算聚类,将其缓存,并直接从缓存为客户提供服务。

然而,如果您需要支持具有潜在遍布全球的标记的大规模情况,则需要更复杂的方法。

上述的聚类算法不可扩展。事实上,它的计算成本通常会随着标记数量的增加呈指数级增长。

为了解决这个问题,您可以将世界分成若干部分,从每个部分计算聚类,然后为客户端提供服务。这确实可以支持扩展,因为工作负载可以分割并由多个(大致)独立的服务器执行。

然后问题在于如何找到一个好的分区方案。您可能还想考虑在不同的缩放级别上提供不同的标记聚类,并且您的分区方案也应该包括这一点以允许扩展。

谷歌将地图划分为具有x、y和z坐标的瓦片,其中xy是瓦片从地图的西北角开始的水平和垂直位置,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值时获得的属性相同。 如果您需要更多细节,请告诉我。

4

0
你可以试试我的免费聚类应用程序。它能够处理比客户端Google地图API更多的标记。它提供kmeans和基于网格的聚类。

https://github.com/biodiv/anycluster


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接