Functions for working with map tiles and tile coordinates. These utilities are useful for advanced map applications that need to work with Google Maps' tile system for custom overlays, caching, or tile-based operations.
Convert between geographic coordinates (lat/lng) and tile coordinates.
/**
* Converts tile coordinates to latitude/longitude at specified zoom level
* @param tile - Tile coordinates (x, y)
* @param zoom - Map zoom level
* @returns Geographic coordinates
*/
function tile2LatLng(
tile: { x: number; y: number },
zoom: number
): { lat: number; lng: number };
/**
* Converts latitude/longitude to tile coordinates at specified zoom level
* @param coords - Geographic coordinates
* @param zoom - Map zoom level
* @returns Tile coordinates
*/
function latLng2Tile(
coords: { lat: number; lng: number },
zoom: number
): { x: number; y: number };Usage Examples:
import { tile2LatLng, latLng2Tile } from 'google-map-react';
// Convert tile to coordinates
const tileCoords = { x: 1234, y: 5678 };
const zoom = 10;
const latLng = tile2LatLng(tileCoords, zoom);
console.log(latLng); // { lat: 40.7128, lng: -74.0060 }
// Convert coordinates to tile
const coords = { lat: 37.7749, lng: -122.4194 };
const tile = latLng2Tile(coords, zoom);
console.log(tile); // { x: 163, y: 395 }
// Round trip conversion (should match original)
const roundTrip = latLng2Tile(tile2LatLng(tileCoords, zoom), zoom);
console.log(roundTrip); // Should match tileCoordsGet all tile IDs within a specified range for batch operations.
/**
* Gets array of tile IDs for given tile range and zoom level
* @param range - Tile range with from/to coordinates
* @param zoom - Map zoom level
* @returns Array of tile IDs as [zoom, x, y] tuples
*/
function getTilesIds(
range: {
from: { x: number; y: number };
to: { x: number; y: number }
},
zoom: number
): Array<[number, number, number]>;Usage Examples:
import { getTilesIds } from 'google-map-react';
// Get all tiles in a region
const range = {
from: { x: 100, y: 200 },
to: { x: 105, y: 205 }
};
const zoom = 12;
const tileIds = getTilesIds(range, zoom);
console.log(tileIds);
// Output: [[12, 100, 200], [12, 100, 201], ..., [12, 105, 205]]
// Use for tile preloading or caching
const tilesToLoad = getTilesIds(range, zoom);
tilesToLoad.forEach(([z, x, y]) => {
const tileUrl = `https://mt1.google.com/vt/lyrs=m&x=${x}&y=${y}&z=${z}`;
preloadTile(tileUrl);
});Create custom tile overlays using tile coordinate calculations:
import { latLng2Tile, tile2LatLng, getTilesIds } from 'google-map-react';
class CustomTileOverlay {
constructor(map, zoom) {
this.map = map;
this.zoom = zoom;
}
// Calculate visible tiles for current map bounds
getVisibleTiles() {
const bounds = this.map.getBounds();
const ne = bounds.getNorthEast();
const sw = bounds.getSouthWest();
// Convert bounds to tile coordinates
const neTile = latLng2Tile({ lat: ne.lat(), lng: ne.lng() }, this.zoom);
const swTile = latLng2Tile({ lat: sw.lat(), lng: sw.lng() }, this.zoom);
// Get all tiles in the visible area
return getTilesIds({
from: { x: swTile.x, y: neTile.y },
to: { x: neTile.x, y: swTile.y }
}, this.zoom);
}
// Create overlay for specific tile
createTileOverlay(tileId) {
const [zoom, x, y] = tileId;
// Get tile bounds
const nw = tile2LatLng({ x, y }, zoom);
const se = tile2LatLng({ x: x + 1, y: y + 1 }, zoom);
// Create overlay rectangle
const overlay = new google.maps.Rectangle({
bounds: {
north: nw.lat,
south: se.lat,
east: se.lng,
west: nw.lng
},
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35,
map: this.map
});
return overlay;
}
}
// Usage with GoogleMapReact
function MapWithTileOverlay() {
const [overlays, setOverlays] = useState([]);
const handleApiLoaded = ({ map }) => {
const tileOverlay = new CustomTileOverlay(map, 10);
const visibleTiles = tileOverlay.getVisibleTiles();
const newOverlays = visibleTiles.slice(0, 10).map(tileId =>
tileOverlay.createTileOverlay(tileId)
);
setOverlays(newOverlays);
};
return (
<GoogleMapReact
defaultCenter={{ lat: 37.7749, lng: -122.4194 }}
defaultZoom={10}
onGoogleApiLoaded={handleApiLoaded}
yesIWantToUseGoogleMapApiInternals
/>
);
}Load data based on visible map tiles:
import { latLng2Tile, getTilesIds } from 'google-map-react';
function useMapTileData(center, zoom, mapBounds) {
const [tileData, setTileData] = useState({});
useEffect(() => {
if (!mapBounds) return;
// Calculate current tile range
const ne = mapBounds.ne;
const sw = mapBounds.sw;
const neTile = latLng2Tile(ne, zoom);
const swTile = latLng2Tile(sw, zoom);
const tileIds = getTilesIds({
from: { x: swTile.x, y: neTile.y },
to: { x: neTile.x, y: swTile.y }
}, zoom);
// Load data for each tile
tileIds.forEach(async ([z, x, y]) => {
const tileKey = `${z}-${x}-${y}`;
if (!tileData[tileKey]) {
try {
const data = await fetchTileData(x, y, z);
setTileData(prev => ({
...prev,
[tileKey]: data
}));
} catch (error) {
console.error(`Failed to load tile ${tileKey}:`, error);
}
}
});
}, [center, zoom, mapBounds]);
return tileData;
}
// Usage
function DataDrivenMap() {
const [mapBounds, setMapBounds] = useState(null);
const [zoom, setZoom] = useState(10);
const [center, setCenter] = useState({ lat: 37.7749, lng: -122.4194 });
const tileData = useMapTileData(center, zoom, mapBounds);
return (
<GoogleMapReact
center={center}
zoom={zoom}
onChange={({ center, zoom, bounds }) => {
setCenter(center);
setZoom(zoom);
setMapBounds({
ne: { lat: bounds[0], lng: bounds[3] },
sw: { lat: bounds[2], lng: bounds[1] }
});
}}
>
{Object.entries(tileData).map(([tileKey, data]) => (
<TileDataMarkers key={tileKey} data={data} />
))}
</GoogleMapReact>
);
}Validate and normalize tile coordinates:
import { tile2LatLng, latLng2Tile } from 'google-map-react';
class TileUtils {
static isValidTile(x, y, zoom) {
const maxTile = Math.pow(2, zoom);
return x >= 0 && x < maxTile && y >= 0 && y < maxTile;
}
static normalizeTile(x, y, zoom) {
const maxTile = Math.pow(2, zoom);
return {
x: ((x % maxTile) + maxTile) % maxTile,
y: Math.max(0, Math.min(y, maxTile - 1))
};
}
static getTileUrl(x, y, zoom, server = 'mt1') {
if (!this.isValidTile(x, y, zoom)) {
const normalized = this.normalizeTile(x, y, zoom);
x = normalized.x;
y = normalized.y;
}
return `https://${server}.google.com/vt/lyrs=m&x=${x}&y=${y}&z=${zoom}`;
}
}
// Usage
const tileCoords = { x: 1000000, y: -50 }; // Invalid coordinates
const zoom = 10;
if (!TileUtils.isValidTile(tileCoords.x, tileCoords.y, zoom)) {
const normalized = TileUtils.normalizeTile(tileCoords.x, tileCoords.y, zoom);
console.log('Normalized:', normalized);
}