/**
* Map anchor utilities for positioning glassmorphism cards
* relative to map coordinates
*/
/**
* Convert lat/lng coordinates to screen pixel position
* @param {Object} map - Mapbox map instance
* @param {number} lng - Longitude
* @param {number} lat - Latitude
* @returns {Object} {x, y} screen coordinates
*/
export const lngLatToScreen = (map, lng, lat) => {
if (!map) return { x: 0, y: 0 };
const point = map.project([lng, lat]);
return {
x: point.x,
y: point.y
};
};
/**
* Adjust card position to prevent overflow off screen edges
* @param {Object} position - {x, y} screen position
* @param {Object} cardDimensions - {width, height} of card
* @param {Object} viewport - {width, height} of viewport
* @returns {Object} Adjusted {x, y, placement} position
*/
export const adjustCardPosition = (position, cardDimensions, viewport) => {
const PADDING = 20;
const OFFSET_Y = -20; // Offset above the point
let x = position.x;
let y = position.y + OFFSET_Y;
let placement = 'top'; // 'top', 'bottom', 'left', 'right'
// Check right edge
if (x + cardDimensions.width + PADDING > viewport.width) {
x = viewport.width - cardDimensions.width - PADDING;
placement = 'left';
}
// Check left edge
if (x < PADDING) {
x = PADDING;
placement = 'right';
}
// Check top edge
if (y < PADDING) {
y = position.y + 40; // Position below point instead
placement = 'bottom';
}
// Check bottom edge
if (y + cardDimensions.height + PADDING > viewport.height) {
y = viewport.height - cardDimensions.height - PADDING;
}
return { x, y, placement };
};
/**
* Calculate anchor line path (optional visual connection)
* @param {Object} cardPosition - Card {x, y} position
* @param {Object} pointPosition - Point {x, y} position
* @returns {string} SVG path string
*/
export const getAnchorLinePath = (cardPosition, pointPosition) => {
const controlPointOffset = 20;
// Create a curved line from point to card
return `M ${pointPosition.x} ${pointPosition.y}
Q ${pointPosition.x} ${pointPosition.y - controlPointOffset},
${cardPosition.x + 20} ${cardPosition.y}`;
};
/**
* Check if position is within viewport bounds
* @param {Object} position - {x, y} position
* @param {Object} viewport - {width, height} viewport dimensions
* @returns {boolean} True if within bounds
*/
export const isInViewport = (position, viewport) => {
return (
position.x >= 0 &&
position.x <= viewport.width &&
position.y >= 0 &&
position.y <= viewport.height
);
};
/**
* Smoothly interpolate between two positions
* @param {Object} from - Starting {x, y} position
* @param {Object} to - Target {x, y} position
* @param {number} progress - Progress from 0 to 1
* @returns {Object} Interpolated {x, y} position
*/
export const interpolatePosition = (from, to, progress) => {
return {
x: from.x + (to.x - from.x) * progress,
y: from.y + (to.y - from.y) * progress
};
};