-
-
Notifications
You must be signed in to change notification settings - Fork 355
/
Copy pathMortonClusteringAlgorithm.php
76 lines (64 loc) · 2.14 KB
/
MortonClusteringAlgorithm.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\UX\Map\Cluster;
use Symfony\UX\Map\Point;
/**
* Clustering algorithm based on Morton codes (Z-order curves).
*
* This approach is optimized for spatial data and preserves locality efficiently.
*
* Best for:
* - Large-scale spatial clustering
* - Hierarchical clustering with fast locality-based grouping
* - Datasets where preserving spatial proximity is crucial
*
* Slower for:
* - High-dimensional data (beyond 2D/3D) due to Morton code limitations
* - Non-spatial or categorical data
* - Scenarios requiring dynamic cluster adjustments (e.g., streaming data)
*
* @author Simon André <[email protected]>
*/
final readonly class MortonClusteringAlgorithm implements ClusteringAlgorithmInterface
{
/**
* @param Point[] $points
*
* @return Cluster[]
*/
public function cluster(iterable $points, float $zoom): array
{
$resolution = 1 << (int) $zoom;
$clustersMap = [];
foreach ($points as $point) {
$xNorm = ($point->getLatitude() + 180) / 360;
$yNorm = ($point->getLongitude() + 90) / 180;
$x = (int) floor($xNorm * $resolution);
$y = (int) floor($yNorm * $resolution);
$x &= 0xFFFF;
$y &= 0xFFFF;
$x = ($x | ($x << 8)) & 0x00FF00FF;
$x = ($x | ($x << 4)) & 0x0F0F0F0F;
$x = ($x | ($x << 2)) & 0x33333333;
$x = ($x | ($x << 1)) & 0x55555555;
$y = ($y | ($y << 8)) & 0x00FF00FF;
$y = ($y | ($y << 4)) & 0x0F0F0F0F;
$y = ($y | ($y << 2)) & 0x33333333;
$y = ($y | ($y << 1)) & 0x55555555;
$code = ($y << 1) | $x;
if (!isset($clustersMap[$code])) {
$clustersMap[$code] = new Cluster($point);
} else {
$clustersMap[$code]->addPoint($point);
}
}
return array_values($clustersMap);
}
}