Heatmap optimized
This commit is contained in:
parent
f34ce5723e
commit
40173c269f
4 changed files with 84 additions and 26 deletions
|
|
@ -40,7 +40,7 @@ public class UserHeatmapGrid {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Center point of the grid cell.
|
* Center point of the grid cell.
|
||||||
* Grid cells are ~100m x 100m (0.001 degrees).
|
* Grid cells are ~10m x 10m (0.0001 degrees).
|
||||||
*/
|
*/
|
||||||
@Column(name = "grid_cell", nullable = false, columnDefinition = "geometry(Point,4326)")
|
@Column(name = "grid_cell", nullable = false, columnDefinition = "geometry(Point,4326)")
|
||||||
private Point gridCell;
|
private Point gridCell;
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,11 @@ public class HeatmapGridService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Grid resolution in degrees (~100m at equator).
|
* Grid resolution in degrees (~10m at equator).
|
||||||
* 0.001 degrees = ~111 meters
|
* 0.0001 degrees = ~11 meters
|
||||||
|
* Finer grid provides better granularity when zoomed in.
|
||||||
*/
|
*/
|
||||||
private static final double GRID_SIZE = 0.001;
|
private static final double GRID_SIZE = 0.0001;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SRID for WGS84 coordinate system.
|
* SRID for WGS84 coordinate system.
|
||||||
|
|
@ -66,9 +67,10 @@ public class HeatmapGridService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sampling rate for large activities.
|
* Sampling rate for large activities.
|
||||||
* Process every Nth point to avoid overwhelming the grid.
|
* Process every Nth point to balance detail vs performance.
|
||||||
|
* Lower value = more detail, higher processing time.
|
||||||
*/
|
*/
|
||||||
private static final int SAMPLING_RATE = 10;
|
private static final int SAMPLING_RATE = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update heatmap grid for a single activity.
|
* Update heatmap grid for a single activity.
|
||||||
|
|
|
||||||
|
|
@ -115,8 +115,11 @@ function renderHeatmap(data) {
|
||||||
const lat = feature.geometry.coordinates[1];
|
const lat = feature.geometry.coordinates[1];
|
||||||
const intensity = feature.properties.intensity;
|
const intensity = feature.properties.intensity;
|
||||||
|
|
||||||
// Normalize intensity to 0-1 range
|
// Use logarithmic scaling for better differentiation between low and high values
|
||||||
const normalizedIntensity = Math.min(intensity / data.maxIntensity, 1.0);
|
// log(1 + x) ensures that intensity=1 is still visible
|
||||||
|
const logMax = Math.log(1 + data.maxIntensity);
|
||||||
|
const logIntensity = Math.log(1 + intensity);
|
||||||
|
const normalizedIntensity = Math.min(logIntensity / logMax, 1.0);
|
||||||
|
|
||||||
return [lat, lon, normalizedIntensity];
|
return [lat, lon, normalizedIntensity];
|
||||||
});
|
});
|
||||||
|
|
@ -126,29 +129,81 @@ function renderHeatmap(data) {
|
||||||
heatmapMap.removeLayer(heatLayer);
|
heatmapMap.removeLayer(heatLayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create heat layer with red color scheme
|
// Get current zoom level for dynamic radius
|
||||||
|
const currentZoom = heatmapMap.getZoom();
|
||||||
|
|
||||||
|
// Calculate dynamic radius based on zoom level
|
||||||
|
// Higher zoom = smaller radius for more detail
|
||||||
|
// Lower zoom = larger radius for better visibility
|
||||||
|
const dynamicRadius = calculateDynamicRadius(currentZoom);
|
||||||
|
const dynamicBlur = Math.max(4, dynamicRadius * 0.4); // Reduced blur for sharper appearance
|
||||||
|
|
||||||
|
// Create heat layer with red color scheme and improved gradient
|
||||||
heatLayer = L.heatLayer(heatData, {
|
heatLayer = L.heatLayer(heatData, {
|
||||||
radius: 10, // Reduced for more detail
|
radius: dynamicRadius,
|
||||||
blur: 5, // Reduced for sharper appearance
|
blur: dynamicBlur,
|
||||||
maxZoom: 17,
|
maxZoom: 18,
|
||||||
max: 0.8, // Reduced from 1.0 to make colors more intense
|
max: 0.75, // Increased to concentrate color at hotspots
|
||||||
minOpacity: 0.3, // Minimum opacity for better visibility
|
minOpacity: 0.25, // Reduced for more transparency over streets
|
||||||
gradient: {
|
gradient: {
|
||||||
0.0: 'rgba(0, 0, 0, 0)', // Transparent for low values
|
0.0: 'rgba(0, 0, 0, 0)', // Transparent
|
||||||
0.2: 'rgba(139, 0, 0, 0.5)', // Dark red with transparency
|
0.1: 'rgba(0, 0, 139, 0.2)', // Dark blue (very low values) - more transparent
|
||||||
0.4: 'rgba(178, 34, 34, 0.7)', // Firebrick red
|
0.2: 'rgba(0, 0, 255, 0.35)', // Blue (low values) - more transparent
|
||||||
0.6: 'rgb(220, 20, 60)', // Crimson
|
0.3: 'rgba(0, 128, 255, 0.45)', // Light blue - more transparent
|
||||||
0.75: 'rgb(255, 69, 0)', // Red-orange
|
0.4: 'rgba(0, 255, 255, 0.55)', // Cyan - more transparent
|
||||||
0.85: 'rgb(255, 140, 0)', // Dark orange
|
0.5: 'rgba(0, 255, 0, 0.6)', // Green - more transparent
|
||||||
0.95: 'rgb(255, 215, 0)', // Gold
|
0.6: 'rgba(255, 255, 0, 0.65)', // Yellow - more transparent
|
||||||
1.0: 'rgb(255, 255, 0)' // Yellow (highest intensity)
|
0.7: 'rgba(255, 165, 0, 0.7)', // Orange - more transparent
|
||||||
|
0.85: 'rgba(255, 69, 0, 0.8)', // Red-orange
|
||||||
|
1.0: 'rgba(255, 0, 0, 0.85)' // Red (highest intensity) - slightly transparent
|
||||||
}
|
}
|
||||||
}).addTo(heatmapMap);
|
}).addTo(heatmapMap);
|
||||||
|
|
||||||
|
// Update radius dynamically when zoom changes
|
||||||
|
heatmapMap.on('zoomend', function() {
|
||||||
|
if (heatLayer) {
|
||||||
|
const newZoom = heatmapMap.getZoom();
|
||||||
|
const newRadius = calculateDynamicRadius(newZoom);
|
||||||
|
const newBlur = Math.max(4, newRadius * 0.4); // Reduced blur for sharper appearance
|
||||||
|
|
||||||
|
// Update heat layer options
|
||||||
|
heatLayer.setOptions({
|
||||||
|
radius: newRadius,
|
||||||
|
blur: newBlur
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Fit map bounds to heatmap data
|
// Fit map bounds to heatmap data
|
||||||
fitMapToBounds(data.features);
|
fitMapToBounds(data.features);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate dynamic radius based on zoom level.
|
||||||
|
* Higher zoom = smaller radius for more granular detail.
|
||||||
|
* Lower zoom = larger radius for better visibility.
|
||||||
|
*
|
||||||
|
* @param {number} zoom - Current map zoom level (0-18)
|
||||||
|
* @returns {number} Radius in pixels
|
||||||
|
*/
|
||||||
|
function calculateDynamicRadius(zoom) {
|
||||||
|
// Zoom levels:
|
||||||
|
// 2-8: World/continent view - large radius
|
||||||
|
// 9-12: City view - medium radius
|
||||||
|
// 13-15: Neighborhood view - small radius
|
||||||
|
// 16-18: Street view - very small radius
|
||||||
|
|
||||||
|
if (zoom <= 8) {
|
||||||
|
return 25; // Large radius for world view
|
||||||
|
} else if (zoom <= 12) {
|
||||||
|
return 20 - (zoom - 8); // 20 -> 16
|
||||||
|
} else if (zoom <= 15) {
|
||||||
|
return 16 - (zoom - 12) * 2; // 16 -> 10
|
||||||
|
} else {
|
||||||
|
return Math.max(6, 10 - (zoom - 15) * 2); // 10 -> 6 (minimum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fit map to show all heatmap data
|
* Fit map to show all heatmap data
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -121,10 +121,11 @@
|
||||||
<!-- Legend -->
|
<!-- Legend -->
|
||||||
<div class="mt-3 text-center text-muted" id="legend" style="display: none;">
|
<div class="mt-3 text-center text-muted" id="legend" style="display: none;">
|
||||||
<small>
|
<small>
|
||||||
<span style="color: rgba(139, 0, 0, 0.7); font-size: 1.2rem;">●</span> Low Activity
|
<span style="color: rgb(0, 0, 255); font-size: 1.2rem;">●</span> Very Low
|
||||||
<span class="ms-2" style="color: rgb(220, 20, 60); font-size: 1.2rem;">●</span> Moderate
|
<span class="ms-2" style="color: rgb(0, 255, 255); font-size: 1.2rem;">●</span> Low
|
||||||
<span class="ms-2" style="color: rgb(255, 140, 0); font-size: 1.2rem;">●</span> High
|
<span class="ms-2" style="color: rgb(0, 255, 0); font-size: 1.2rem;">●</span> Moderate
|
||||||
<span class="ms-2" style="color: rgb(255, 215, 0); font-size: 1.2rem;">●</span> Very High
|
<span class="ms-2" style="color: rgb(255, 255, 0); font-size: 1.2rem;">●</span> High
|
||||||
|
<span class="ms-2" style="color: rgb(255, 69, 0); font-size: 1.2rem;">●</span> Very High
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue