How to Turn Greyscale an Interactive or Static Google Map
Giving a greyscale touch to an interactive or static Google map with React is not an ordinary task, but with the help of this guide it will look really as such.
A greyscale Google map makes markers, polylines and whatever you might want to draw on it, to stand out naturally and with style. There's no doubt about that. Perhaps, you might think that with React repainting a Google map in greyscale is a tricky affair... Trust me, with a little help provided from an online tool and a handful of custom code, you can add to your React app an interactive or static greyscale Google map in really no time. Let's see how!
The JSON Styling Code
The online tool I'm talking about is the "Legacy JSON Styling Wizard" you can find at mapstyle.withgoogle.com. Through this tool we can get a collection of styling presets in the form of a JSON array — exactly the data type we need — which lets us give a greyscale touch to whichever kind of Google map we want to display into a React app, may it an interactive or a static one.
As I don't know for how much longer the JSON styling code will be publicly available, below is a copy — let's assume we have saved it to a file named gmap-styles.json
:
1[
2 {
3 "elementType": "geometry",
4 "stylers": [{ "color": "#f5f5f5" }]
5 },
6 {
7 "elementType": "labels.icon",
8 "stylers": [{ "visibility": "off" }]
9 },
10 {
11 "elementType": "labels.text.fill",
12 "stylers": [{ "color": "#616161" }]
13 },
14 {
15 "elementType": "labels.text.stroke",
16 "stylers": [{ "color": "#f5f5f5" }]
17 },
18 {
19 "featureType": "administrative.land_parcel",
20 "elementType": "labels.text.fill",
21 "stylers": [{ "color": "#bdbdbd" }]
22 },
23 {
24 "featureType": "poi",
25 "elementType": "geometry",
26 "stylers": [{ "color": "#eeeeee" }]
27 },
28 {
29 "featureType": "poi",
30 "elementType": "labels.text.fill",
31 "stylers": [{ "color": "#757575" }]
32 },
33 {
34 "featureType": "poi.park",
35 "elementType": "geometry",
36 "stylers": [{ "color": "#e5e5e5" }]
37 },
38 {
39 "featureType": "poi.park",
40 "elementType": "labels.text.fill",
41 "stylers": [{ "color": "#9e9e9e" }]
42 },
43 {
44 "featureType": "road",
45 "elementType": "geometry",
46 "stylers": [{ "color": "#ffffff" }]
47 },
48 {
49 "featureType": "road.arterial",
50 "elementType": "labels.text.fill",
51 "stylers": [{ "color": "#757575" }]
52 },
53 {
54 "featureType": "road.highway",
55 "elementType": "geometry",
56 "stylers": [{ "color": "#dadada" }]
57 },
58 {
59 "featureType": "road.highway",
60 "elementType": "labels.text.fill",
61 "stylers": [{ "color": "#616161" }]
62 },
63 {
64 "featureType": "road.local",
65 "elementType": "labels.text.fill",
66 "stylers": [{ "color": "#9e9e9e" }]
67 },
68 {
69 "featureType": "transit.line",
70 "elementType": "geometry",
71 "stylers": [{ "color": "#e5e5e5" }]
72 },
73 {
74 "featureType": "transit.station",
75 "elementType": "geometry",
76 "stylers": [{ "color": "#eeeeee" }]
77 },
78 {
79 "featureType": "water",
80 "elementType": "geometry",
81 "stylers": [{ "color": "#c9c9c9" }]
82 },
83 {
84 "featureType": "water",
85 "elementType": "labels.text.fill",
86 "stylers": [{ "color": "#9e9e9e" }]
87 }
88]
Interactive Greyscale Google Map
If you haven't already chosen a React tool to add an interactive Google map to your app, I would suggest the @react-google-maps/api package, it's a well coded and supported project built on top of the official JavaScript Google Maps API.
Once installed, writing a reusable Map
component is something that doesn't require a steep learning curve — eventually, you can help yourself through the extensive documentation available on this legacy website hosted by GitHub Pages.
As my aim is to provide you with just a brief how-to, a minimal component like the following can serve our purpose:
1import { GoogleMap, useJsApiLoader } from "@react-google-maps/api";
2import mapStyles from "../../config/gmap-styles.json";
3
4
5export default function Map({ center }: { center: google.maps.LatLngLiteral }) {
6 const { isLoaded } = useJsApiLoader({
7 id: "google-map",
8 googleMapsApiKey: ( import.meta.env.VITE_GMAPS_API_KEY || "" )
9 });
10
11 return ( isLoaded && (
12 <GoogleMap
13 mapContainerStyle={{
14 width: "100%",
15 height: "100%",
16 }}
17 zoom={10}
18 options={{ styles: mapStyles }}
19 center={center}
20 />
21 ));
22}
Now, all we have to do in order to display our interactive Google map in greyscale is to import the JSON array of stylers, and appropriately pass it as-is to the GoogleMap
component through its options
prop, like this:
1import { GoogleMap, useJsApiLoader } from "@react-google-maps/api";
2import mapStyles from "../../config/gmap-styles.json";
3
4
5export default function Map( /* ... */ ) {
6 // ...
7
8 return ( isLoaded && (
9 <GoogleMap
10 // ...
11 options={{ styles: mapStyles }}
12 />
13 ));
14}
Done!
Yes, that's all what was needed to let the users of our React app to interact with a greyscale Google map. Just to give you a visual feedback, the following is the Map
component in action in an example application:
Static Greyscale Google Map
In the case of a static Google map, to show it in a React app we don't need a React-specific package, and that keeps being true even if we were dealing with an app developed with Angular, Vue.js, or any other front-end framework or library. What we need instead, is to have access to the Google Maps Static API — your API key will have to be allowed, to use the service — the rest of the work can be easily done with some vanilla TypeScript.
The key point in styling a static Google map is stated in the 'Style syntax' section of the official documentation, which I report here for convenience:
To create a customized styled map, include one or more style parameters in the request URL.
So, in order for the Google Maps Static service to return a greyscale map, we have to convert our JSON array of stylers into a set of style
query parameters whose values must follow this pattern:
1style=feature:myFeatureArgument|element:myElementArgument|myRule1:myRule1Argument|myRule2:myRule2Argument
Here we are, the handful of custom code which I was referring to in the introduction of this post it's exactly what we need now, a block of code to which to delegate all the encoding work. I encapsulated such code into a function I named getEncodedMapStyles()
,
here is the implementation:
1import mapStyles from "../config/gmap-styles.json";
2
3
4const getEncodedMapStyles = () => {
5 const styles: string[] = [];
6
7 for ( const style of mapStyles ) {
8 const parts = [];
9
10 if ( !( "elementType" in style && "stylers" in style ) ) {
11 continue;
12 }
13
14 if ( "featureType" in style ) {
15 parts.push( "feature:" + style.featureType );
16 }
17
18 parts.push( "element:" + style.elementType );
19
20 const styler = style.stylers[0];
21 const stylerKey = Object.keys( styler )[0] as keyof typeof styler;
22 const stylerValue = String( styler[stylerKey] ).replace( "#", "0x" );
23
24 parts.push( stylerKey + ":" + stylerValue );
25 styles.push( parts.join( "|" ) );
26 }
27
28 return styles.join( "&style=" );
29};
Comments on the Code
- The type cast
Object.keys( styler )[0] as keyof typeof styler
is necessary only to prevent TypeScript from throwing an error when we try to usestylerKey
to dynamically access the properties of thestyler
object. - As per the documentation, colours must be sent to the Google Maps Static service in their standard hexadecimal form, where the HEX number is prefixed by the "zero-x notation" (
0x
) — hence the instructionreplace( '#', '0x' )
.
How to Use getEncodedMapStyles()
We can exemplify the use of getEncodedMapStyles()
with the following code, where we put together a request URL, something we definitely need in order to fetch through the Google Maps Static API the image file that we want our app to show to the user:
1import mapStyles from "../config/gmap-styles.json";
2
3
4const getEncodedMapStyles = () => { /* ... */ };
5
6export const getGoogleMapStaticUrl = ( center: { lat: number, lng: number } ) => {
7 const baseUrl = "https://maps.googleapis.com/maps/api/staticmap?";
8 const queryParams = [
9 "format=png",
10 "maptype=roadmap",
11 "size=640x640",
12 "scale=1",
13 "zoom=10",
14 "center=" + ( center.lat + "," + center.lng ),
15 "style=" + getEncodedMapStyles(),
16 "key=" + "YOUR_GMAPS_API_KEY",
17 ];
18
19 return baseUrl + queryParams.join( "&" );
20};
Such a straightforward function is more than enough to allow our React app to fetch the static greyscale Google map depicted in the screenshot below:
That's all! Now it's your turn, happy coding!