expo-osm-sdk
Version:
OpenStreetMap component for React Native with Expo
252 lines • 9.18 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateMarkerConfig = exports.validateMarkerAnimation = exports.validateMarkerIcon = exports.calculateBoundingBox = exports.isWithinBounds = exports.calculateMidpoint = exports.calculateBearing = exports.calculateDistance = exports.coordinateToString = exports.normalizeCoordinate = exports.validateCoordinate = exports.isValidCoordinate = void 0;
/**
* Validates if a coordinate is within valid geographic bounds
*/
const isValidCoordinate = (coordinate) => {
if (!coordinate || typeof coordinate !== 'object') {
return false;
}
const { latitude, longitude } = coordinate;
if (typeof latitude !== 'number' || typeof longitude !== 'number') {
return false;
}
if (isNaN(latitude) || isNaN(longitude)) {
return false;
}
return (latitude >= -90 && latitude <= 90 &&
longitude >= -180 && longitude <= 180);
};
exports.isValidCoordinate = isValidCoordinate;
/**
* Validates and normalizes a coordinate
*/
const validateCoordinate = (coordinate) => {
if (!(0, exports.isValidCoordinate)(coordinate)) {
throw new Error(`Invalid coordinate: ${JSON.stringify(coordinate)}`);
}
return {
latitude: coordinate.latitude,
longitude: coordinate.longitude,
};
};
exports.validateCoordinate = validateCoordinate;
/**
* Normalizes a coordinate to valid ranges
*/
const normalizeCoordinate = (coordinate) => {
let { latitude, longitude } = coordinate;
// Clamp latitude to valid range
latitude = Math.max(-90, Math.min(90, latitude));
// Wrap longitude to valid range
longitude = ((longitude + 180) % 360) - 180;
if (longitude < -180)
longitude += 360;
return { latitude, longitude };
};
exports.normalizeCoordinate = normalizeCoordinate;
/**
* Converts coordinate to string representation
*/
const coordinateToString = (coordinate, precision = 4) => {
const { latitude, longitude } = coordinate;
return `${latitude.toFixed(precision)},${longitude.toFixed(precision)}`;
};
exports.coordinateToString = coordinateToString;
/**
* Calculates the distance between two coordinates using the Haversine formula
* Returns distance in meters
*/
const calculateDistance = (from, to) => {
const R = 6371000; // Earth's radius in meters
const φ1 = toRadians(from.latitude);
const φ2 = toRadians(to.latitude);
const Δφ = toRadians(to.latitude - from.latitude);
const Δλ = toRadians(to.longitude - from.longitude);
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
};
exports.calculateDistance = calculateDistance;
/**
* Calculates the bearing (direction) from one coordinate to another
* Returns bearing in degrees (0-360)
*/
const calculateBearing = (from, to) => {
const φ1 = toRadians(from.latitude);
const φ2 = toRadians(to.latitude);
const Δλ = toRadians(to.longitude - from.longitude);
const y = Math.sin(Δλ) * Math.cos(φ2);
const x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ);
const bearing = toDegrees(Math.atan2(y, x));
return (bearing + 360) % 360;
};
exports.calculateBearing = calculateBearing;
/**
* Converts degrees to radians
*/
const toRadians = (degrees) => {
return degrees * (Math.PI / 180);
};
/**
* Converts radians to degrees
*/
const toDegrees = (radians) => {
return radians * (180 / Math.PI);
};
/**
* Calculates the midpoint between two coordinates
*/
const calculateMidpoint = (from, to) => {
const φ1 = toRadians(from.latitude);
const φ2 = toRadians(to.latitude);
const Δλ = toRadians(to.longitude - from.longitude);
const Bx = Math.cos(φ2) * Math.cos(Δλ);
const By = Math.cos(φ2) * Math.sin(Δλ);
const φ3 = Math.atan2(Math.sin(φ1) + Math.sin(φ2), Math.sqrt((Math.cos(φ1) + Bx) * (Math.cos(φ1) + Bx) + By * By));
const λ3 = toRadians(from.longitude) + Math.atan2(By, Math.cos(φ1) + Bx);
return {
latitude: toDegrees(φ3),
longitude: toDegrees(λ3),
};
};
exports.calculateMidpoint = calculateMidpoint;
/**
* Checks if a coordinate is within a bounding box
*/
const isWithinBounds = (coordinate, bounds) => {
const { latitude, longitude } = coordinate;
const { north, south, east, west } = bounds;
// Handle bounds crossing the date line
if (west > east) {
// Bounds cross the date line
return (latitude >= south &&
latitude <= north &&
(longitude >= west || longitude <= east));
}
return (latitude >= south &&
latitude <= north &&
longitude >= west &&
longitude <= east);
};
exports.isWithinBounds = isWithinBounds;
/**
* Calculates a bounding box around a coordinate with given radius
*/
const calculateBoundingBox = (center, radiusMeters) => {
const R = 6371000; // Earth's radius in meters
const φ = toRadians(center.latitude);
const λ = toRadians(center.longitude);
const δφ = radiusMeters / R;
const δλ = radiusMeters / (R * Math.cos(φ));
const north = Math.min(90, toDegrees(φ + δφ));
const south = Math.max(-90, toDegrees(φ - δφ));
const east = toDegrees(λ + δλ);
const west = toDegrees(λ - δλ);
return {
north,
south,
east,
west,
};
};
exports.calculateBoundingBox = calculateBoundingBox;
/**
* Validates a marker icon configuration
*/
const validateMarkerIcon = (icon) => {
if (!icon)
return true;
if (typeof icon !== 'object')
return false;
// Validate size
if (icon.size !== undefined && (typeof icon.size !== 'number' || icon.size <= 0)) {
return false;
}
// Validate color
if (icon.color !== undefined && typeof icon.color !== 'string') {
return false;
}
// Validate anchor
if (icon.anchor !== undefined) {
if (typeof icon.anchor !== 'object' ||
typeof icon.anchor.x !== 'number' ||
typeof icon.anchor.y !== 'number' ||
icon.anchor.x < 0 || icon.anchor.x > 1 ||
icon.anchor.y < 0 || icon.anchor.y > 1) {
return false;
}
}
return true;
};
exports.validateMarkerIcon = validateMarkerIcon;
/**
* Validates a marker animation configuration
*/
const validateMarkerAnimation = (animation) => {
if (!animation)
return true;
if (typeof animation !== 'object')
return false;
const validTypes = ['bounce', 'pulse', 'fade', 'scale', 'drop'];
if (!validTypes.includes(animation.type)) {
return false;
}
if (animation.duration !== undefined && (typeof animation.duration !== 'number' || animation.duration <= 0)) {
return false;
}
if (animation.delay !== undefined && (typeof animation.delay !== 'number' || animation.delay < 0)) {
return false;
}
if (animation.repeat !== undefined && typeof animation.repeat !== 'boolean') {
return false;
}
return true;
};
exports.validateMarkerAnimation = validateMarkerAnimation;
/**
* Validates a complete marker configuration
*/
const validateMarkerConfig = (marker, propName = 'marker') => {
if (!marker || typeof marker !== 'object') {
throw new Error(`${propName} must be an object`);
}
if (!marker.id || typeof marker.id !== 'string') {
throw new Error(`${propName}.id must be a non-empty string`);
}
// Validate coordinate
(0, exports.validateCoordinate)(marker.coordinate);
// Validate icon
if (!(0, exports.validateMarkerIcon)(marker.icon)) {
throw new Error(`${propName}.icon configuration is invalid`);
}
// Validate animation
if (!(0, exports.validateMarkerAnimation)(marker.animation)) {
throw new Error(`${propName}.animation configuration is invalid`);
}
// Validate numeric properties
if (marker.zIndex !== undefined && typeof marker.zIndex !== 'number') {
throw new Error(`${propName}.zIndex must be a number`);
}
if (marker.opacity !== undefined && (typeof marker.opacity !== 'number' || marker.opacity < 0 || marker.opacity > 1)) {
throw new Error(`${propName}.opacity must be a number between 0 and 1`);
}
if (marker.rotation !== undefined && typeof marker.rotation !== 'number') {
throw new Error(`${propName}.rotation must be a number`);
}
// Validate boolean properties
if (marker.draggable !== undefined && typeof marker.draggable !== 'boolean') {
throw new Error(`${propName}.draggable must be a boolean`);
}
if (marker.visible !== undefined && typeof marker.visible !== 'boolean') {
throw new Error(`${propName}.visible must be a boolean`);
}
if (marker.clustered !== undefined && typeof marker.clustered !== 'boolean') {
throw new Error(`${propName}.clustered must be a boolean`);
}
};
exports.validateMarkerConfig = validateMarkerConfig;
//# sourceMappingURL=coordinate.js.map