Adding search to Shade Map
I added search functionality to shademap.app today. The correct term is actually Geocoding, which is the process of converting search terms into latitude and longitude coordinates. Because Google is a leader in search technology, I decided to use Google Maps API for the task.
I like the way the Google Maps search bar looks, so I used Developer Tools to copy the relevant markup and styles into my project as a React component. The search bar stretches from edge-to-edge on mobile devices but has a bit of margin on larger screens. I chose not to implement an auto-complete dropdown at this point because I have no revenue and the Google Autocomplete api is an extra cost. Without autocomplete, the Google Maps free-tier allows for 40,000 geocode requests per month.
Some errors I encountered. The first was the result of using the wrong NPM library.
"error_message" : "API keys with referer restrictions cannot be used with this API.",
I used @googlemaps/google-maps-services-js
, but the correct package was @googlemaps/js-api-loader
. You also want @types/google.maps
if you are coding in Typescript.
"Google Maps JavaScript API error: ApiNotActivatedMapError\nhttps://developers.google.com/maps/documentation/javascript/error-messages#api-not-activated-map-error"
Geocoding Service: This API project is not authorized to use this API. For more information on authentication and Google Maps JavaScript API services please see: https://developers.google.com/maps/documentation/javascript/get-api-key
The above error was because I restricted my credentials to only use the Maps JavaScript API
. I needed to extend the restriction to also include the Geocoding API
.
Using the Google Maps Javascript API with Leaflet was very easy. Here’s the basic code:
// Initialize the Geocoder
const loader = new Loader({
apiKey: process.env.REACT_APP_GOOGLE_MAPS_KEY as string,
});
let Geocoder: google.maps.Geocoder;
// Lookup query
const map = useMap();
Geocoder.geocode({
address: query,
}, (results) => {
if (results && results.length) {
const lat = results[0].geometry.location.lat();
const lng = results[0].geometry.location.lng();
const ne = results[0].geometry.viewport.getNorthEast();
const sw = results[0].geometry.viewport.getSouthWest();
map.fitBounds([
[ne.lat(), ne.lng()],
[sw.lat(), sw.lng()],
]);
setMarkerPosition({ lat, lng }); // state setter for map marker
}
})