Arrange map markers to overlap less
This commit is contained in:
parent
c65e60a1f1
commit
dd82470835
155
static/js/map.js
155
static/js/map.js
|
@ -22,39 +22,136 @@ var icons = {
|
|||
"event": emoji_icon("🍷"),
|
||||
}
|
||||
|
||||
|
||||
function build_map(map_id, coordinates, routes) {
|
||||
// Initialize the map
|
||||
var map = L.map(map_id).fitBounds(coordinates.map(function(station) {
|
||||
return [station.latitude, station.longitude];
|
||||
}));
|
||||
var map = L.map(map_id).fitBounds(coordinates.map(station => [station.latitude, station.longitude]));
|
||||
|
||||
// Set up the tile layer
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
}).addTo(map);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
}).addTo(map);
|
||||
|
||||
// Add markers with appropriate icons to the map
|
||||
coordinates.forEach(function(item) {
|
||||
var marker = L.marker([item.latitude, item.longitude], { icon: icons[item.type] }).addTo(map);
|
||||
marker.bindPopup(item.name);
|
||||
});
|
||||
var markers = [];
|
||||
var offset_lines = [];
|
||||
|
||||
// Draw routes
|
||||
routes.forEach(function(route) {
|
||||
var color = {"train": "blue", "flight": "red", "unbooked_flight": "orange"}[route.type];
|
||||
var style = { weight: 3, opacity: 0.5, color: color };
|
||||
if (route.geojson) {
|
||||
// If route is defined as GeoJSON
|
||||
L.geoJSON(JSON.parse(route.geojson), {
|
||||
style: function(feature) { return style; }
|
||||
}).addTo(map);
|
||||
} else if (route.type === "flight" || route.type === "unbooked_flight") {
|
||||
var flightPath = new L.Geodesic([[route.from, route.to]], style).addTo(map);
|
||||
} else {
|
||||
// If route is defined by 'from' and 'to' coordinates
|
||||
L.polyline([route.from, route.to], style).addTo(map);
|
||||
function getIconBounds(latlng) {
|
||||
let iconSize = 30; // Assuming the icon size as a square of 30x30 pixels
|
||||
if (!latlng) return null;
|
||||
let pixel = map.project(latlng, map.getZoom());
|
||||
let sw = map.unproject([pixel.x - iconSize / 2, pixel.y + iconSize / 2], map.getZoom());
|
||||
let ne = map.unproject([pixel.x + iconSize / 2, pixel.y - iconSize / 2], map.getZoom());
|
||||
return L.latLngBounds(sw, ne);
|
||||
}
|
||||
});
|
||||
|
||||
return map;
|
||||
function calculateCentroid(markers) {
|
||||
let latSum = 0, lngSum = 0, count = 0;
|
||||
markers.forEach(marker => {
|
||||
latSum += marker.getLatLng().lat;
|
||||
lngSum += marker.getLatLng().lng;
|
||||
count++;
|
||||
});
|
||||
return count > 0 ? L.latLng(latSum / count, lngSum / count) : null;
|
||||
}
|
||||
|
||||
// Function to detect and group overlapping markers
|
||||
function getOverlappingGroups() {
|
||||
let groups = [];
|
||||
let visited = new Set();
|
||||
|
||||
markers.forEach((marker, index) => {
|
||||
if (visited.has(marker)) {
|
||||
return;
|
||||
}
|
||||
let group = [];
|
||||
let markerBounds = getIconBounds(marker.getLatLng());
|
||||
|
||||
markers.forEach((otherMarker) => {
|
||||
if (marker !== otherMarker && markerBounds.intersects(getIconBounds(otherMarker.getLatLng()))) {
|
||||
group.push(otherMarker);
|
||||
visited.add(otherMarker);
|
||||
}
|
||||
});
|
||||
|
||||
if (group.length > 0) {
|
||||
group.push(marker); // Add the original marker to the group
|
||||
groups.push(group);
|
||||
visited.add(marker);
|
||||
}
|
||||
});
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
function displaceMarkers(group, zoom) {
|
||||
const markerPixelSize = 30; // Width/height of the marker in pixels
|
||||
let map = group[0]._map; // Assuming all markers are on the same map
|
||||
|
||||
let centroid = calculateCentroid(group);
|
||||
let centroidPoint = map.project(centroid, zoom);
|
||||
|
||||
const radius = markerPixelSize; // Set radius for even distribution
|
||||
const angleIncrement = (2 * Math.PI) / group.length; // Evenly space markers
|
||||
|
||||
group.forEach((marker, index) => {
|
||||
let angle = index * angleIncrement;
|
||||
let newX = centroidPoint.x + radius * Math.cos(angle);
|
||||
let newY = centroidPoint.y + radius * Math.sin(angle);
|
||||
let newPoint = L.point(newX, newY);
|
||||
let newLatLng = map.unproject(newPoint, zoom);
|
||||
|
||||
// Store original position for polyline
|
||||
let originalPos = marker.getLatLng();
|
||||
marker.setLatLng(newLatLng);
|
||||
|
||||
marker.polyline = L.polyline([originalPos, newLatLng], {color: "gray", weight: 2}).addTo(map);
|
||||
offset_lines.push(marker.polyline);
|
||||
});
|
||||
}
|
||||
|
||||
coordinates.forEach(function(item, index) {
|
||||
let latlng = L.latLng(item.latitude, item.longitude);
|
||||
let marker = L.marker(latlng, { icon: icons[item.type] }).addTo(map);
|
||||
marker.bindPopup(item.name);
|
||||
markers.push(marker);
|
||||
});
|
||||
|
||||
map.on('zoomend', function() {
|
||||
markers.forEach((marker, index) => {
|
||||
marker.setLatLng([coordinates[index].latitude, coordinates[index].longitude]); // Reset position on zoom
|
||||
if (marker.polyline) {
|
||||
map.removeLayer(marker.polyline);
|
||||
}
|
||||
});
|
||||
|
||||
offset_lines.forEach(polyline => {
|
||||
map.removeLayer(polyline);
|
||||
});
|
||||
|
||||
let overlappingGroups = getOverlappingGroups();
|
||||
// console.log(overlappingGroups); // Process or display groups as needed
|
||||
|
||||
overlappingGroups.forEach(group => displaceMarkers(group, map.getZoom()));
|
||||
});
|
||||
|
||||
let overlappingGroups = getOverlappingGroups();
|
||||
// console.log(overlappingGroups); // Process or display groups as needed
|
||||
|
||||
overlappingGroups.forEach(group => displaceMarkers(group, map.getZoom()));
|
||||
|
||||
|
||||
// Draw routes
|
||||
routes.forEach(function(route) {
|
||||
var color = {"train": "blue", "flight": "red", "unbooked_flight": "orange"}[route.type];
|
||||
var style = { weight: 3, opacity: 0.5, color: color };
|
||||
if (route.geojson) {
|
||||
L.geoJSON(JSON.parse(route.geojson), {
|
||||
style: function(feature) { return style; }
|
||||
}).addTo(map);
|
||||
} else if (route.type === "flight" || route.type === "unbooked_flight") {
|
||||
var flightPath = new L.Geodesic([[route.from, route.to]], style).addTo(map);
|
||||
} else {
|
||||
L.polyline([route.from, route.to], style).addTo(map);
|
||||
}
|
||||
});
|
||||
|
||||
return map;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue