In this article, we'll explore how to create a Google Map Viewer using React and Next.js. This app allows users to input a Google Maps link, which then displays the corresponding location on a map. You can check out the demo here.
Let's walk through these components and their functionalities in a logical order.
1. map-utils.ts
This utility module handles URL expansion and extraction of latitude and longitude coordinates from Google Maps links. It contains two main functions:
expandUrl: Expands shortened URLs to their full version.
extractLatLong: Extracts latitude and longitude coordinates from either DMS (Degrees, Minutes, Seconds) or decimal formats.
tsx
"use server";
import axios from"axios";
exportconst expandUrl = async (shortUrl: string): Promise<string | null> => {
try {
const response = await axios.get(shortUrl, {
maxRedirects: 0,
validateStatus: (status) => status === 302, // Handle redirect
});
return response.headers.location; // This contains the expanded URL
} catch (error) {
console.error("Error expanding URL:", error);
returnnull;
}
};
const dmsToDecimal = (
degrees: number,
minutes: number,
seconds: number,
direction: string
): number => {
const decimal = degrees + minutes / 60 + seconds / 3600;
return direction === "S" || direction === "W" ? -decimal : decimal;
};
exportconst extractLatLong = (
url: string
): { lat: number; lng: number } | null => {
// Decode the URL to handle URL-encoded charactersconst decodedUrl = decodeURIComponent(url);
// console.log("Decoded URL:", decodedUrl);// Regex to match DMS coordinatesconst dmsPattern = /(\d{1,3})°(\d{1,2})'(\d{1,2}(?:\.\d+)?)"?([NSEW])/g;
// Regex to match decimal coordinatesconst decimalPattern = /@(-?\d+\.\d+),(-?\d+\.\d+)/;
// Regex to match accurate 3d/4d coordinatesconst accurateDecimalPattern = /3d(-?\d+\.\d+)!4d(-?\d+\.\d+)/;
let match;
let lat, lng;
// Try to extract accurate 3d/4d coordinates
match = accurateDecimalPattern.exec(decodedUrl);
if (match) {
return {
lat: parseFloat(match[1]),
lng: parseFloat(match[2]),
};
}
// Try to extract DMS coordinates for latitude and longitudeconst dmsMatches = decodedUrl.match(dmsPattern);
if (dmsMatches && dmsMatches.length >= 2) {
const latMatch = dmsMatches[0].match(
/(\d{1,3})°(\d{1,2})'(\d{1,2}(?:\.\d+)?)"?([NSEW])/
);
const lngMatch = dmsMatches[1].match(
/(\d{1,3})°(\d{1,2})'(\d{1,2}(?:\.\d+)?)"?([NSEW])/
);
if (latMatch && lngMatch) {
lat = dmsToDecimal(
parseInt(latMatch[1]),
parseInt(latMatch[2]),
parseFloat(latMatch[3]),
latMatch[4]
);
lng = dmsToDecimal(
parseInt(lngMatch[1]),
parseInt(lngMatch[2]),
parseFloat(lngMatch[3]),
lngMatch[4]
);
return { lat, lng };
}
}
// Fallback to extract decimal coordinates if accurate coordinates are not found
match = decimalPattern.exec(decodedUrl);
if (match) {
return {
lat: parseFloat(match[1]),
lng: parseFloat(match[2]),
};
}
returnnull;
};
2. map-provider.tsx
The MapProvider component loads the Google Maps JavaScript API and provides it to the rest of the app. This component uses the useJsApiLoader hook from the @react-google-maps/api library to load the API asynchronously.
tsx
"use client";
import { Libraries, useJsApiLoader } from"@react-google-maps/api";
import { ReactNode } from"react";
const libraries = ["places", "drawing", "geometry"];
exportfunctionMapProvider({ children }: { children: ReactNode }) {
const { isLoaded: scriptLoaded, loadError } = useJsApiLoader({
googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_APIasstring,
libraries: libraries asLibraries,
});
if (loadError) return<p>Encountered error while loading google maps</p>;
if (!scriptLoaded) return<p>Map Script is loading ...</p>;
return children;
}
3. map-component.tsx
The MapComponent is the core component that renders the map, marker, and info window. It also provides an input form for users to enter a Google Maps link, title, and address.
The GoogleMap component serves as a container for the MapProvider and MapComponent. It ensures that the Google Maps API is loaded before rendering the map.
By structuring our components logically and leveraging the power of React and Next.js, we have created a robust Google Map Viewer application. The MapComponent handles user input and displays the map, while MapProvider ensures the Google Maps API is loaded correctly. The map-utils.ts module provides essential utility functions for URL and coordinate handling.
Feel free to explore the demo and try out different Google Maps links. Happy coding!