perliedman / leaflet-control-geocoder

A simple geocoder form to locate places. Easily extended to multiple data providers.
http://www.liedman.net/leaflet-control-geocoder/
BSD 2-Clause "Simplified" License
560 stars 220 forks source link

Google API Key with Restrictions #215

Open shazahm1 opened 6 years ago

shazahm1 commented 6 years ago

First I want to say thanks for your excellent plugin for Leaflet!

I'm new to Leaflet and have limited basic skill in regards to JavaScript so I may have approached this completely wrong.

I have two Google API keys. One has a referrer restriction, the other an IP restriction. Using either of those keys Google returns an error. The key with the referrer restriction returns an error about the API not supporting referring restriction. I forget the error for the IP restricted API key. Regardless neither worked. I suspect an unrestricted API would work just fine but I did not test that because that is not secure in the sense that someone could scrape that key and use it. Since Google has implemented "pay-as-you-go" billing, this could cause you to be charged for another using your API key.

Following, the DAWA Geocoder as an example I was able to extend yor plugin to add support for the Google "native" geocoder using the Google Maps JavaScript API.

This requires you to load the Maps JavaScript API using the API key on the page before using:

<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY" async defer></script>

Address geocoding example:

var geocoder = new L.Control.Geocoder.GoogleNative();
geocoder.geocode( '1600 Pennsylvania Ave Washington DC 20500', function( results ) { 
    console.log( results ); 
});

Reverse geocoding example:

geocoder.reverse( { lat: 38.8977, lng: -77.0365 }, 0, function( results ) {
    console.log( results );
});

And finally my code:

(function (L) {

    var GoogleNative = {

        class: L.Class.extend({
            options: {
                // serviceUrl: 'https://maps.googleapis.com/maps/api/geocode/json',
                geocodingQueryParams: {},
                reverseQueryParams: {}
            },

            initialize: function( key, options ) {

                this._key = key;
                L.setOptions( this, options );
            },

            geocode: function( query, cb, context ) {

                var params = {
                    address: query
                };

                params = L.Util.extend( params, this.options.geocodingQueryParams );

                var geocoder = new google.maps.Geocoder();

                geocoder.geocode( params, function( response, status ) {

                    var results = [],
                        loc,
                        latLng,
                        latLngBounds;

                    if ( status === 'OK' ) {

                        // console.log( response );

                        for ( var i = 0; i <= response.length - 1; i++ ) {

                            loc = response[ i ];

                            latLng = L.latLng( loc.geometry.location.lat(), loc.geometry.location.lng() );

                            latLngBounds = L.latLngBounds(
                                L.latLng( loc.geometry.viewport.northeast ),
                                L.latLng( loc.geometry.viewport.southwest )
                            );

                            results[ i ] = {
                                name:       loc.formatted_address,
                                bbox:       latLngBounds,
                                center:     latLng,
                                properties: loc.address_components
                            };
                        }

                    } else {

                        console.log( 'Geocode was not successful for the following reason: ' + status );
                    }

                    cb.call( context, results );
                });
            },

            reverse: function( location, scale, cb, context ) {

                var params = {
                    location: { lat: parseFloat( location.lat ), lng: parseFloat( location.lng ) }
                };

                params = L.Util.extend( params, this.options.reverseQueryParams );

                var geocoder = new google.maps.Geocoder;

                geocoder.geocode( params, function( response, status ) {

                    var results = [],
                        loc,
                        latLng,
                        latLngBounds;

                    if ( status === 'OK' ) {

                        // console.log( response );

                        for ( var i = 0; i <= response.length - 1; i++ ) {

                            loc = response[ i ];

                            latLng = L.latLng( loc.geometry.location.lat(), loc.geometry.location.lng() );

                            latLngBounds = L.latLngBounds(
                                L.latLng( loc.geometry.viewport.northeast ),
                                L.latLng( loc.geometry.viewport.southwest )
                            );

                            results[ i ] = {
                                name:       loc.formatted_address,
                                bbox:       latLngBounds,
                                center:     latLng,
                                properties: loc.address_components
                            };
                        }

                    } else {

                        console.log( 'Geocoder failed due to: ' + status );
                    }

                    cb.call( context, results );
                });

            }
        }),

        factory: function( key, options ) {

            return new L.Control.Geocoder.Google( key, options );
        }
    };

    L.Util.extend( L.Control.Geocoder, {
        GoogleNative:  GoogleNative.class,
        google_native: GoogleNative.factory
    });

}(L));

Perhaps this can be added? If not, at least this issue might help others.

perliedman commented 6 years ago

Hi there!

Thanks for taking the time to investigate, code and finally post your results here! No doubt it will help someone coming here with the same or similar problem.

A lot has happened in the Google world since I first wrote the code to support the Google geocoder. Most importantly, checking their usage policy, I now see:

You can display Geocoding API results on a Google Map, or without a map. If you want to display Geocoding API results on a map, then these results must be displayed on a Google Map. It is prohibited to use Geocoding API data on a map that is not a Google map.

I think it's safe to say that for most users of Leaflet Control Geocoder, this means that using the Google geocoder is a violation of their policy. My current thinking is that removing the Google geocoder is probably the best solution.

shazahm1 commented 6 years ago

@perliedman

In my specific use case, I only use the Google Geocoder when I'm displaying the Google Map Tile based on whether the user has setup a Google API key or not. So one can in be compliance with Google policy while using Leaflet Control Geocoder. So leaving support for Google in the plugin should be fine. Just update the readme to point to the Google policy so users are informed.