diff --git a/src/components/Map/MapView.jsx b/src/components/Map/MapView.jsx
index 2e772a8..2b0d323 100644
--- a/src/components/Map/MapView.jsx
+++ b/src/components/Map/MapView.jsx
@@ -608,7 +608,7 @@ export default function MapView({
diff --git a/src/components/Map/markerManager.js b/src/components/Map/markerManager.js
index 66b46c6..0c53710 100644
--- a/src/components/Map/markerManager.js
+++ b/src/components/Map/markerManager.js
@@ -294,6 +294,18 @@ function openCenteredOverlay({ map, post, theme }) {
};
}
+function hashPostId(id) {
+ // Simple hash function for stable offset calculation
+ const str = String(id);
+ let hash = 0;
+ for (let i = 0; i < str.length; i++) {
+ const char = str.charCodeAt(i);
+ hash = ((hash << 5) - hash) + char;
+ hash = hash & hash; // Convert to 32bit integer
+ }
+ return Math.abs(hash);
+}
+
function calculateSmartOffset(post, markersRef, map) {
if (!map || !markersRef.current) return { x: 0, y: 0 };
@@ -302,35 +314,31 @@ function calculateSmartOffset(post, markersRef, map) {
if (lat == null || lon == null) return { x: 0, y: 0 };
const currentPoint = map.project([lon, lat]);
- const PROXIMITY_THRESHOLD = 80; // pixels
- const MAX_OFFSET = 60; // max offset in pixels
+ const PROXIMITY_THRESHOLD = 100; // pixels - larger to detect more overlaps
- const nearbyMarkers = [];
-
- for (const marker of markersRef.current) {
- if (!marker.post || marker.post.id === post.id) continue;
+ // Check distance in geographic coordinates (more stable across zoom levels)
+ const nearbyCount = markersRef.current.filter(marker => {
+ if (!marker.post || marker.post.id === post.id) return false;
const mLat = typeof marker.post.lat === "number" ? marker.post.lat : marker.post.latitude;
const mLon = typeof marker.post.lon === "number" ? marker.post.lon : marker.post.lng;
- if (mLat == null || mLon == null) continue;
+ if (mLat == null || mLon == null) return false;
- const mPoint = map.project([mLon, mLat]);
- const dist = Math.sqrt(
- Math.pow(currentPoint.x - mPoint.x, 2) +
- Math.pow(currentPoint.y - mPoint.y, 2)
- );
+ // Geographic distance (rough approximation)
+ const latDiff = Math.abs(lat - mLat);
+ const lonDiff = Math.abs(lon - mLon);
+ const geoDist = Math.sqrt(latDiff * latDiff + lonDiff * lonDiff);
- if (dist < PROXIMITY_THRESHOLD) {
- nearbyMarkers.push({ marker, distance: dist, point: mPoint });
- }
- }
+ // Small threshold for geographic clustering (~100m at equator)
+ return geoDist < 0.001;
+ }).length;
- if (nearbyMarkers.length === 0) return { x: 0, y: 0 };
+ if (nearbyCount === 0) return { x: 0, y: 0 };
- // Create a cascade effect: offset markers in a circular pattern
- const index = nearbyMarkers.length;
- const angle = (index * 45) * (Math.PI / 180); // 45 degrees apart
- const offsetMagnitude = Math.min(20 + (index * 12), MAX_OFFSET);
+ // Use deterministic hash-based offset so it's stable across zoom/pan
+ const hash = hashPostId(post.id);
+ const angle = (hash % 8) * 45 * (Math.PI / 180); // 8 positions, 45° apart
+ const offsetMagnitude = 25 + ((hash % 3) * 15); // 25, 40, or 55 pixels
return {
x: Math.cos(angle) * offsetMagnitude,