mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-06-03 12:03:02 +00:00
224 lines
5.8 KiB
TypeScript
224 lines
5.8 KiB
TypeScript
/**
|
|
* Utility functions to parse map URLs from various sources
|
|
* Supports: mapy.cz, Google Maps
|
|
*/
|
|
|
|
export interface MapCoordinates {
|
|
latitude: number;
|
|
longitude: number;
|
|
zoom?: number;
|
|
address?: string;
|
|
source: 'mapy.cz' | 'google-maps' | 'unknown';
|
|
// Detailed address components from reverse geocoding
|
|
street?: string;
|
|
houseNumber?: string;
|
|
city?: string;
|
|
zip?: string;
|
|
country?: string;
|
|
}
|
|
|
|
/**
|
|
* Parse mapy.cz URL
|
|
* Example: mapy.cz/en/letecka?q=krnov%20stadion&source=firm&id=12954454&ds=2&x=17.6996859&y=50.0947150&z=19
|
|
*/
|
|
export function parseMapyCzUrl(url: string): MapCoordinates | null {
|
|
try {
|
|
const urlObj = new URL(url);
|
|
|
|
// Check if it's a mapy.cz domain
|
|
if (!urlObj.hostname.includes('mapy.cz') && !urlObj.hostname.includes('mapy.com')) {
|
|
return null;
|
|
}
|
|
|
|
const params = urlObj.searchParams;
|
|
const xParam = params.get('x');
|
|
const yParam = params.get('y');
|
|
const zParam = params.get('z');
|
|
const qParam = params.get('q');
|
|
|
|
if (xParam && yParam) {
|
|
const longitude = parseFloat(xParam);
|
|
const latitude = parseFloat(yParam);
|
|
const zoom = zParam ? parseInt(zParam) : undefined;
|
|
|
|
if (isNaN(latitude) || isNaN(longitude)) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
latitude,
|
|
longitude,
|
|
zoom,
|
|
address: qParam ? decodeURIComponent(qParam) : undefined,
|
|
source: 'mapy.cz',
|
|
};
|
|
}
|
|
|
|
return null;
|
|
} catch (error) {
|
|
console.error('Error parsing mapy.cz URL:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse Google Maps URL
|
|
* Supports various formats:
|
|
* - /maps/place/.../@lat,lng,zoom
|
|
* - /maps?q=lat,lng
|
|
* - /maps/@lat,lng,zoom
|
|
*/
|
|
export function parseGoogleMapsUrl(url: string): MapCoordinates | null {
|
|
try {
|
|
const urlObj = new URL(url);
|
|
|
|
// Check if it's a Google Maps domain
|
|
if (!urlObj.hostname.includes('google.com') && !urlObj.hostname.includes('google.cz')) {
|
|
return null;
|
|
}
|
|
|
|
// Try to extract from pathname (/@lat,lng,zoom format)
|
|
const pathMatch = urlObj.pathname.match(/@(-?\d+\.\d+),(-?\d+\.\d+),(\d+)(?:[mz])/);
|
|
if (pathMatch) {
|
|
const latitude = parseFloat(pathMatch[1]);
|
|
const longitude = parseFloat(pathMatch[2]);
|
|
const zoom = parseInt(pathMatch[3]);
|
|
|
|
if (!isNaN(latitude) && !isNaN(longitude)) {
|
|
// Try to extract place name from pathname
|
|
const placeMatch = urlObj.pathname.match(/\/place\/([^/]+)/);
|
|
const address = placeMatch ? decodeURIComponent(placeMatch[1].replace(/\+/g, ' ')) : undefined;
|
|
|
|
return {
|
|
latitude,
|
|
longitude,
|
|
zoom: !isNaN(zoom) ? zoom : undefined,
|
|
address,
|
|
source: 'google-maps',
|
|
};
|
|
}
|
|
}
|
|
|
|
// Try to extract from query parameters
|
|
const params = urlObj.searchParams;
|
|
const qParam = params.get('q');
|
|
|
|
if (qParam) {
|
|
// Check if q parameter contains coordinates (lat,lng format)
|
|
const coordMatch = qParam.match(/(-?\d+\.\d+),(-?\d+\.\d+)/);
|
|
if (coordMatch) {
|
|
const latitude = parseFloat(coordMatch[1]);
|
|
const longitude = parseFloat(coordMatch[2]);
|
|
|
|
if (!isNaN(latitude) && !isNaN(longitude)) {
|
|
return {
|
|
latitude,
|
|
longitude,
|
|
address: qParam.includes(',') ? undefined : qParam,
|
|
source: 'google-maps',
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try data parameter (some Google Maps links use this)
|
|
const dataMatch = urlObj.pathname.match(/!3d(-?\d+\.\d+)!4d(-?\d+\.\d+)/);
|
|
if (dataMatch) {
|
|
const latitude = parseFloat(dataMatch[1]);
|
|
const longitude = parseFloat(dataMatch[2]);
|
|
|
|
if (!isNaN(latitude) && !isNaN(longitude)) {
|
|
return {
|
|
latitude,
|
|
longitude,
|
|
source: 'google-maps',
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
} catch (error) {
|
|
console.error('Error parsing Google Maps URL:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Main function to parse any supported map URL
|
|
*/
|
|
export function parseMapUrl(url: string): MapCoordinates | null {
|
|
if (!url || typeof url !== 'string') {
|
|
return null;
|
|
}
|
|
|
|
// Normalize the URL (add protocol if missing)
|
|
let normalizedUrl = url.trim();
|
|
if (!normalizedUrl.startsWith('http://') && !normalizedUrl.startsWith('https://')) {
|
|
normalizedUrl = 'https://' + normalizedUrl;
|
|
}
|
|
|
|
// Try parsing as mapy.cz
|
|
const mapyCzResult = parseMapyCzUrl(normalizedUrl);
|
|
if (mapyCzResult) {
|
|
return mapyCzResult;
|
|
}
|
|
|
|
// Try parsing as Google Maps
|
|
const googleMapsResult = parseGoogleMapsUrl(normalizedUrl);
|
|
if (googleMapsResult) {
|
|
return googleMapsResult;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Validate if coordinates are within valid ranges
|
|
*/
|
|
export function validateCoordinates(lat: number, lng: number): boolean {
|
|
return (
|
|
!isNaN(lat) &&
|
|
!isNaN(lng) &&
|
|
lat >= -90 &&
|
|
lat <= 90 &&
|
|
lng >= -180 &&
|
|
lng <= 180
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Reverse geocode coordinates to get detailed address information
|
|
* Uses Nominatim API (OpenStreetMap)
|
|
*/
|
|
export async function reverseGeocode(lat: number, lng: number): Promise<Partial<MapCoordinates>> {
|
|
try {
|
|
const response = await fetch(
|
|
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&addressdetails=1&accept-language=cs`,
|
|
{
|
|
headers: {
|
|
'User-Agent': 'FotbalClub/1.0',
|
|
},
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Reverse geocoding failed');
|
|
}
|
|
|
|
const data = await response.json();
|
|
const addr = data.address || {};
|
|
|
|
return {
|
|
address: data.display_name,
|
|
street: addr.road || addr.street || addr.pedestrian || addr.footway,
|
|
houseNumber: addr.house_number,
|
|
city: addr.city || addr.town || addr.village || addr.municipality,
|
|
zip: addr.postcode,
|
|
country: addr.country || 'Česká republika',
|
|
};
|
|
} catch (error) {
|
|
console.error('Reverse geocoding error:', error);
|
|
return {};
|
|
}
|
|
}
|