Skip to main content
Glama
geometryUtils.test.ts7.73 kB
/* * Copyright (C) 2025 TomTom NV * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { describe, it, expect } from 'vitest'; import { generateCirclePoints, calculateOptimalZoom, calculateEnhancedBounds, extractCoordinates, Point, MapMarker, MapPolygon } from './geometryUtils'; describe('generateCirclePoints', () => { it('generates correct number of points', () => { const points = generateCirclePoints(0, 0, 1000, 32); expect(points).toHaveLength(32); }); it('generates points roughly equidistant from center', () => { const center: Point = { lat: 52.3731663, lon: 4.8906596 }; const radius = 1000; // 1km const points = generateCirclePoints(center.lat, center.lon, radius); // Check a few random points are roughly the same distance from center const distances = points.map(point => { const dLat = (point.lat - center.lat) * Math.PI / 180; const dLon = (point.lon - center.lon) * Math.PI / 180; const a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(center.lat * Math.PI / 180) * Math.cos(point.lat * Math.PI / 180) * Math.sin(dLon/2) * Math.sin(dLon/2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); return 6371000 * c; // Earth's radius in meters }); // All distances should be roughly equal to radius distances.forEach(d => { expect(d).toBeCloseTo(radius, -1); // Less precise comparison due to spherical math }); }); it('returns points within valid coordinate ranges', () => { const points = generateCirclePoints(0, 0, 10000, 32); points.forEach(point => { expect(point.lat).toBeGreaterThanOrEqual(-90); expect(point.lat).toBeLessThanOrEqual(90); expect(point.lon).toBeGreaterThanOrEqual(-180); expect(point.lon).toBeLessThanOrEqual(180); }); }); }); describe('calculateOptimalZoom', () => { it('returns correct zoom for small area', () => { const bounds = { north: 52.38, south: 52.37, east: 4.90, west: 4.89 }; const zoom = calculateOptimalZoom(bounds, 800, 600); expect(zoom).toBeGreaterThan(12); // Should be zoomed in for small area }); it('returns correct zoom for large area', () => { const bounds = { north: 55.0, south: 50.0, east: 10.0, west: 0.0 }; const zoom = calculateOptimalZoom(bounds, 800, 600); expect(zoom).toBeLessThan(8); // Should be zoomed out for large area }); it('respects minimum and maximum zoom levels', () => { const bounds = { north: 90, south: -90, east: 180, west: -180 }; const zoom = calculateOptimalZoom(bounds, 800, 600); expect(zoom).toBeGreaterThanOrEqual(1); expect(zoom).toBeLessThanOrEqual(17); }); }); describe('calculateEnhancedBounds', () => { const amsterdamMarker: MapMarker = { lat: 52.3731663, lon: 4.8906596, label: 'Amsterdam' }; const berlinMarker: MapMarker = { lat: 52.5234292, lon: 13.4114365, label: 'Berlin' }; it('calculates bounds for single marker', () => { const result = calculateEnhancedBounds([amsterdamMarker], [], 800, 600); // For a single marker, bounds should include the marker and have padding expect(result.bounds.north).toBeGreaterThanOrEqual(amsterdamMarker.lat); expect(result.bounds.south).toBeLessThanOrEqual(amsterdamMarker.lat); expect(result.bounds.east).toBeGreaterThanOrEqual(amsterdamMarker.lon); expect(result.bounds.west).toBeLessThanOrEqual(amsterdamMarker.lon); // Verify padding exists const latPadding = result.bounds.north - result.bounds.south; const lonPadding = result.bounds.east - result.bounds.west; expect(latPadding).toBeGreaterThan(0); expect(lonPadding).toBeGreaterThan(0); }); it('calculates bounds for multiple markers', () => { const result = calculateEnhancedBounds([amsterdamMarker, berlinMarker], [], 800, 600); expect(result.bounds.north).toBeGreaterThan(Math.max(amsterdamMarker.lat, berlinMarker.lat)); expect(result.bounds.south).toBeLessThan(Math.min(amsterdamMarker.lat, berlinMarker.lat)); expect(result.bounds.east).toBeGreaterThan(Math.max(amsterdamMarker.lon, berlinMarker.lon)); expect(result.bounds.west).toBeLessThan(Math.min(amsterdamMarker.lon, berlinMarker.lon)); }); it('includes polygon coordinates in bounds calculation', () => { const polygon: MapPolygon = { type: 'polygon', coordinates: [ [4.8906596, 52.3731663], [13.4114365, 52.5234292], [4.8906596, 52.3731663] ] }; const result = calculateEnhancedBounds([], [], 800, 600, [polygon]); expect(result.bounds.north).toBeGreaterThan(52.3731663); expect(result.bounds.east).toBeGreaterThan(13.4114365); }); it('includes circle points in bounds calculation', () => { const circle: MapPolygon = { type: 'circle', center: { lat: 52.3731663, lon: 4.8906596 }, radius: 1000 }; const result = calculateEnhancedBounds([], [], 800, 600, [circle]); expect(result.bounds.north).toBeGreaterThan(52.3731663); expect(result.bounds.south).toBeLessThan(52.3731663); }); it('adds appropriate padding for different scenarios', () => { // Single marker const singleResult = calculateEnhancedBounds([amsterdamMarker], [], 800, 600); const singleLatSpan = singleResult.bounds.north - singleResult.bounds.south; const singleLonSpan = singleResult.bounds.east - singleResult.bounds.west; // Multiple markers const multiResult = calculateEnhancedBounds([amsterdamMarker, berlinMarker], [], 800, 600); const multiLatSpan = multiResult.bounds.north - multiResult.bounds.south; const multiLonSpan = multiResult.bounds.east - multiResult.bounds.west; // Verify that both scenarios have appropriate padding expect(singleLatSpan).toBeGreaterThan(0); expect(singleLonSpan).toBeGreaterThan(0); expect(multiLatSpan).toBeGreaterThan(0); expect(multiLonSpan).toBeGreaterThan(0); // Check that padding is proportional to the area being mapped expect(multiLatSpan).toBeGreaterThan(singleLatSpan); expect(multiLonSpan).toBeGreaterThan(singleLonSpan); }); }); describe('extractCoordinates', () => { it('extracts coordinates from array format', () => { const coords = extractCoordinates([52.3731663, 4.8906596], 0); expect(coords).toEqual({ lat: 52.3731663, lon: 4.8906596 }); }); it('extracts coordinates from object format', () => { const coords = extractCoordinates({ lat: 52.3731663, lon: 4.8906596 }, 0); expect(coords).toEqual({ lat: 52.3731663, lon: 4.8906596 }); }); it('extracts coordinates from coordinates object', () => { const coords = extractCoordinates({ coordinates: [52.3731663, 4.8906596] }, 0); expect(coords).toEqual({ lat: 52.3731663, lon: 4.8906596 }); }); it('validates coordinate ranges', () => { const coords = extractCoordinates({ lat: 100, lon: 200 }, 0); expect(coords).toBeNull(); }); it('handles invalid input gracefully', () => { const coords = extractCoordinates({ latitude: 52, longitude: 4 }, 0); expect(coords).toBeNull(); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tomtom-international/tomtom-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server