@vis.gl/react-google-maps
Version:
React components and hooks for the Google Maps JavaScript API
360 lines (351 loc) • 12.1 kB
JavaScript
import React from 'react';
function _extends() {
return _extends = Object.assign ? Object.assign.bind() : function (n) {
for (var e = 1; e < arguments.length; e++) {
var t = arguments[e];
for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
}
return n;
}, _extends.apply(null, arguments);
}
/**
* Formats a location into a string representation suitable for Google Static Maps API.
*
* @param location - The location to format, can be either a string or an object with lat/lng properties
* @returns A string representation of the location in the format "lat,lng" or the original string
*
* @example
* // Returns "40.714728,-73.998672"
* formatLocation({ lat: 40.714728, lng: -73.998672 })
*
* @example
* // Returns "New York, NY"
* formatLocation("New York, NY")
*/
function formatLocation(location) {
return typeof location === 'string' ? location : `${location.lat},${location.lng}`;
}
// Used for removing the leading pipe from the param string
function formatParam(string) {
return string.slice(1);
}
/**
* Assembles marker parameters for static maps.
*
* This function takes an array of markers and groups them by their style properties.
* It then creates a string representation of these markers, including their styles and locations,
* which can be used as parameters for static map APIs.
*
* @param {StaticMapsMarker[]} [markers=[]] - An array of markers to be processed. Each marker can have properties such as color, label, size, scale, icon, anchor, and location.
* @returns {string[]} An array of strings, each representing a group of markers with their styles and locations.
*
* @example
* const markers = [
* { color: 'blue', label: 'A', size: 'mid', location: '40.714728,-73.998672' },
* { color: 'blue', label: 'B', size: 'mid', location: '40.714728,-73.998672' },
* { icon: 'http://example.com/icon.png', location: { lat: 40.714728, lng: -73.998672 } }
* ];
* const params = assembleMarkerParams(markers);
* // Params will be an array of strings representing the marker parameters
* Example output: [
* "color:blue|label:A|size:mid|40.714728,-73.998672|40.714728,-73.998672",
* "color:blue|label:B|size:mid|40.714728,-73.998672|40.714728,-73.998672",
* "icon:http://example.com/icon.png|40.714728,-73.998672"
* ]
*/
function assembleMarkerParams(markers = []) {
const markerParams = [];
// Group markers by style
const markersByStyle = markers == null ? void 0 : markers.reduce((styles, marker) => {
const {
color = 'red',
label,
size,
scale,
icon,
anchor
} = marker;
// Create a unique style key based on either icon properties or standard marker properties
const relevantProps = icon ? [icon, anchor, scale] : [color, label, size];
const key = relevantProps.filter(Boolean).join('-');
styles[key] = styles[key] || [];
styles[key].push(marker);
return styles;
}, {});
Object.values(markersByStyle != null ? markersByStyle : {}).forEach(markers => {
let markerParam = '';
const {
icon
} = markers[0];
// Create marker style from first marker in group since all markers share the same style.
Object.entries(markers[0]).forEach(([key, value]) => {
// Determine which properties to include based on whether marker uses custom icon
const relevantKeys = icon ? ['icon', 'anchor', 'scale'] : ['color', 'label', 'size'];
if (relevantKeys.includes(key)) {
markerParam += `|${key}:${value}`;
}
});
// Add location coordinates for each marker in the style group
// Handles both string locations and lat/lng object formats.
for (const marker of markers) {
const location = typeof marker.location === 'string' ? marker.location : `${marker.location.lat},${marker.location.lng}`;
markerParam += `|${location}`;
}
markerParams.push(markerParam);
});
return markerParams.map(formatParam);
}
// Style properties that can be applied to paths in the Static Maps API
const PATH_STYLE_KEYS = ['color', 'weight', 'fillcolor', 'geodesic'];
/**
* Builds the style portion of a path parameter string.
* @param path - The path object containing style properties
* @returns A string with style parameters in the format "|key:value"
*/
function buildStyleParams(path) {
let styleParams = '';
PATH_STYLE_KEYS.forEach(key => {
if (path[key] !== undefined) {
styleParams += `|${key}:${path[key]}`;
}
});
return styleParams;
}
/**
* Builds the coordinates portion of a path parameter string.
* @param coordinates - Either a string or array of location objects
* @returns A string with coordinates in the format "|lat,lng|lat,lng"
*/
function buildCoordinateParams(coordinates) {
if (typeof coordinates === 'string') {
return `|${decodeURIComponent(coordinates)}`;
}
return coordinates.map(location => `|${formatLocation(location)}`).join('');
}
/**
* Assembles path parameters for the Static Maps API from an array of paths.
*
* This function constructs a string of path parameters for each path. Each path parameter string
* includes the style properties and the coordinates of the paths.
*
* @param {Array<StaticMapsPath>} [paths=[]] - An array of paths to be assembled into path parameters.
* @returns {Array<string>} An array of path parameter strings.
*
* @example
* ```typescript
* const paths = [
* {
* color: 'red',
* weight: 5,
* coordinates: [
* { lat: 40.714728, lng: -73.998672 },
* { lat: 40.718217, lng: -73.998284 }
* ]
* }
* ];
*
* const pathParams = assemblePathParams(paths);
* // Output: ['color:red|weight:5|40.714728,-73.998672|40.718217,-73.998284']
* ```
*/
function assemblePathParams(paths = []) {
return paths.map(path => {
const styleParams = buildStyleParams(path);
const coordinateParams = buildCoordinateParams(path.coordinates);
const pathParam = styleParams + coordinateParams;
return formatParam(pathParam);
});
}
/**
* Converts an array of Google Maps style objects into an array of style strings
* compatible with the Google Static Maps API.
*
* @param styles - An array of Google Maps MapTypeStyle objects that define the styling rules
* @returns An array of formatted style strings ready to be used with the Static Maps API
*
* @example
* const styles = [{
* featureType: "road",
* elementType: "geometry",
* stylers: [{color: "#ff0000"}, {weight: 1}]
* }];
*
* const styleStrings = assembleMapTypeStyles(styles);
* // Returns: ["|feature:road|element:geometry|color:0xff0000|weight:1"]
*
* Each style string follows the format:
* "feature:{featureType}|element:{elementType}|{stylerName}:{stylerValue}"
*
* Note: Color values with hexadecimal notation (#) are automatically converted
* to the required 0x format for the Static Maps API.
*/
function assembleMapTypeStyles(styles) {
return styles.map(mapTypeStyle => {
const {
featureType,
elementType,
stylers = []
} = mapTypeStyle;
let styleString = '';
if (featureType) {
styleString += `|feature:${featureType}`;
}
if (elementType) {
styleString += `|element:${elementType}`;
}
for (const styler of stylers) {
Object.entries(styler).forEach(([name, value]) => {
styleString += `|${name}:${String(value).replace('#', '0x')}`;
});
}
return styleString;
}).map(formatParam);
}
const STATIC_MAPS_BASE = 'https://maps.googleapis.com/maps/api/staticmap';
/**
* Creates a URL for the Google Static Maps API with the specified parameters.
*
* @param {Object} options - The configuration options for the static map
* @param {string} options.apiKey - Your Google Maps API key (required)
* @param {number} options.width - The width of the map image in pixels (required)
* @param {number} options.height - The height of the map image in pixels (required)
* @param {StaticMapsLocation} [options.center] - The center point of the map (lat/lng or address).
* Required if no markers or paths or "visible locations" are provided.
* @param {number} [options.zoom] - The zoom level of the map. Required if no markers or paths or "visible locations" are provided.
* @param {1|2|4} [options.scale] - The resolution of the map (1, 2, or 4)
* @param {string} [options.format] - The image format (png, png8, png32, gif, jpg, jpg-baseline)
* @param {string} [options.mapType] - The type of map (roadmap, satellite, terrain, hybrid)
* @param {string} [options.language] - The language of the map labels
* @param {string} [options.region] - The region code for the map
* @param {string} [options.map_id] - The Cloud-based map style ID
* @param {StaticMapsMarker[]} [options.markers=[]] - Array of markers to display on the map
* @param {StaticMapsPath[]} [options.paths=[]] - Array of paths to display on the map
* @param {StaticMapsLocation[]} [options.visible=[]] - Array of locations that should be visible on the map
* @param {MapTypeStyle[]} [options.style=[]] - Array of style objects to customize the map appearance
*
* @returns {string} The complete Google Static Maps API URL
*
* @throws {Error} If API key is not provided
* @throws {Error} If width or height is not provided
*
* @example
* const url = createStaticMapsUrl({
* apiKey: 'YOUR_API_KEY',
* width: 600,
* height: 400,
* center: { lat: 40.714728, lng: -73.998672 },
* zoom: 12,
* markers: [
* {
* location: { lat: 40.714728, lng: -73.998672 },
* color: 'red',
* label: 'A'
* }
* ],
* paths: [
* {
* coordinates: [
* { lat: 40.714728, lng: -73.998672 },
* { lat: 40.719728, lng: -73.991672 }
* ],
* color: '0x0000ff',
* weight: 5
* }
* ],
* style: [
* {
* featureType: 'road',
* elementType: 'geometry',
* stylers: [{color: '#00ff00'}]
* }
* ]
* });
*
* // Results in URL similar to:
* // https://maps.googleapis.com/maps/api/staticmap?key=YOUR_API_KEY
* // &size=600x400
* // ¢er=40.714728,-73.998672&zoom=12
* // &markers=color:red|label:A|40.714728,-73.998672
* // &path=color:0x0000ff|weight:5|40.714728,-73.998672|40.719728,-73.991672
* // &style=feature:road|element:geometry|color:0x00ff00
*/
function createStaticMapsUrl({
apiKey,
width,
height,
center,
zoom,
scale,
format,
mapType,
language,
region,
mapId,
markers = [],
paths = [],
visible = [],
style = []
}) {
if (!apiKey) {
console.warn('API key is required');
}
if (!width || !height) {
console.warn('Width and height are required');
}
const params = _extends({
key: apiKey,
size: `${width}x${height}`
}, center && {
center: formatLocation(center)
}, zoom && {
zoom
}, scale && {
scale
}, format && {
format
}, mapType && {
maptype: mapType
}, language && {
language
}, region && {
region
}, mapId && {
map_id: mapId
});
const url = new URL(STATIC_MAPS_BASE);
// Params that don't need special handling
Object.entries(params).forEach(([key, value]) => {
url.searchParams.append(key, String(value));
});
// Assemble Markers
for (const markerParam of assembleMarkerParams(markers)) {
url.searchParams.append('markers', markerParam);
}
// Assemble Paths
for (const pathParam of assemblePathParams(paths)) {
url.searchParams.append('path', pathParam);
}
// Assemble visible locations
if (visible.length) {
url.searchParams.append('visible', visible.map(location => formatLocation(location)).join('|'));
}
// Assemble Map Type Styles
for (const styleString of assembleMapTypeStyles(style)) {
url.searchParams.append('style', styleString);
}
return url.toString();
}
const StaticMap = props => {
const {
url,
className
} = props;
if (!url) throw new Error('URL is required');
return /*#__PURE__*/React.createElement("img", {
className: className,
src: url,
width: "100%"
});
};
export { StaticMap, createStaticMapsUrl };
//# sourceMappingURL=index.modern.mjs.map