Skip to content

Commit ac95f40

Browse files
committed
expanding Geocoder plugin to support other built-in providers supported by leaflet-control-geocoder
also allows for user-defined geocode providers
1 parent 2586125 commit ac95f40

File tree

1 file changed

+119
-2
lines changed

1 file changed

+119
-2
lines changed

folium/plugins/geocoder.py

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ class Geocoder(JSCSSMixin, MacroElement):
1919
Choose from 'topleft', 'topright', 'bottomleft' or 'bottomright'.
2020
add_marker: bool, default True
2121
If True, adds a marker on the found location.
22+
geocodeProvider: str, default 'nominatim'
23+
see https://github.com/perliedman/leaflet-control-geocoder/tree/2.4.0/src/geocoders for other built-in providers
24+
geocodeProviderOptions: dict, default None
25+
For use with specific providers that may require api keys or other parameters
26+
serviceUrl: str, default None
27+
For use with user-defined geocode provider. If used, geocodeFunction must also be specified, geocodeProvider param will be ignored.
28+
geocodeFunction: str, default None
29+
geocode function of user-defined geocode provider. see https://github.com/perliedman/leaflet-control-geocoder/blob/1.13.0/src/geocoders/nominatim.js for a reference implementation
30+
resultsHandlerFunction: str, default None
31+
For use with user-defined geocode provider, to format geocoding responses into the format expected by leaflet-control-geocoder. Each result should have 'name','center' (L.latLng),'bbox' (L.latLngBounds) properties. see https://github.com/perliedman/leaflet-control-geocoder/blob/1.13.0/src/geocoders/photon.js (_decodeFeatures) for a reference implementation
2232
2333
For all options see https://github.com/perliedman/leaflet-control-geocoder
2434
@@ -27,8 +37,103 @@ class Geocoder(JSCSSMixin, MacroElement):
2737
_template = Template(
2838
"""
2939
{% macro script(this, kwargs) %}
40+
41+
var geocoderOpts_{{ this.get_name() }} = {{ this.options|tojson }};
42+
if ('geocodeFunction' in geocoderOpts_{{ this.get_name() }}) {
43+
eval("geocoderOpts_{{ this.get_name() }}['geocodeFunction'] = " + geocoderOpts_{{ this.get_name() }}['geocodeFunction']);
44+
}
45+
if ('resultsHandlerFunction' in geocoderOpts_{{ this.get_name() }}) {
46+
eval("geocoderOpts_{{ this.get_name() }}['resultsHandlerFunction'] = " + geocoderOpts_{{ this.get_name() }}['resultsHandlerFunction']);
47+
}
48+
var customGeocoderTemplate_{{ this.get_name() }} = {
49+
class: L.Class.extend({
50+
options: {
51+
serviceUrl: geocoderOpts_{{ this.get_name() }}['serviceUrl'],
52+
geocodingQueryParams: geocoderOpts_{{ this.get_name() }}['geocodingQueryParams'] || {},
53+
reverseQueryParams: geocoderOpts_{{ this.get_name() }}['reverseQueryParams'] || {},
54+
},
55+
56+
initialize: function(options) {
57+
L.Util.setOptions(this, options || {});
58+
},
59+
60+
geocode: geocoderOpts_{{ this.get_name() }}['geocodeFunction'],
61+
62+
suggest: function(query,cb,context) {
63+
return this.geocode(query, cb, context);
64+
},
65+
66+
// placeholder: not used by folium.plugin.Geocoder
67+
reverse: function(location, scale, cb, context) {
68+
var params = L.extend(
69+
{
70+
//defaultParam: defaultParamValue
71+
},
72+
this.options.reverseQueryParams
73+
);
74+
75+
this.getJSON(
76+
this.options.serviceUrl + 'reverse',
77+
params,
78+
L.bind(function(data) {
79+
cb.call(context, this._resultsHandler(data));
80+
}, this)
81+
);
82+
},
83+
84+
_resultsHandler: function(data) {
85+
if ('resultsHandlerFunction' in geocoderOpts_{{ this.get_name() }}) {
86+
return geocoderOpts_{{ this.get_name() }}['resultsHandlerFunction'](data);
87+
}
88+
return data;
89+
},
90+
91+
getJSON: function(url, params, callback) {
92+
var xmlHttp = new XMLHttpRequest();
93+
xmlHttp.onreadystatechange = function () {
94+
if (xmlHttp.readyState !== 4){
95+
return;
96+
}
97+
if (xmlHttp.status !== 200 && xmlHttp.status !== 304){
98+
callback('');
99+
return;
100+
}
101+
callback(JSON.parse(xmlHttp.response));
102+
};
103+
xmlHttp.open('GET', url + L.Util.getParamString(params), true);
104+
xmlHttp.setRequestHeader('Accept', 'application/json');
105+
xmlHttp.send(null);
106+
}
107+
108+
}),
109+
110+
factory: function(options) {
111+
return new L.Control.Geocoder.Custom_{{ this.get_name() }}(options);
112+
},
113+
114+
}
115+
116+
L.Util.extend(L.Control.Geocoder, {
117+
Custom_{{ this.get_name() }}: customGeocoderTemplate_{{ this.get_name() }}.class,
118+
custom_{{ this.get_name() }}: customGeocoderTemplate_{{ this.get_name() }}.factory
119+
});
120+
121+
// use lowercase for geocoder name
122+
var geocoderName_{{ this.get_name() }};
123+
if ('serviceUrl' in geocoderOpts_{{ this.get_name() }}) {
124+
geocoderName_{{ this.get_name() }} = 'custom_{{ this.get_name() }}';
125+
}
126+
else {
127+
geocoderName_{{ this.get_name() }} = geocoderOpts_{{ this.get_name() }}["geocodeProvider"];
128+
}
129+
130+
var customGeocoder_{{ this.get_name() }} = L.Control.Geocoder[ geocoderName_{{ this.get_name() }} ](
131+
geocoderOpts_{{ this.get_name() }}['geocodeProviderOptions']
132+
);
133+
geocoderOpts_{{ this.get_name() }}["geocoder"] = customGeocoder_{{ this.get_name() }};
134+
30135
L.Control.geocoder(
31-
{{ this.options|tojson }}
136+
geocoderOpts_{{ this.get_name() }}
32137
).on('markgeocode', function(e) {
33138
{{ this._parent.get_name() }}.setView(e.geocode.center, 11);
34139
}).addTo({{ this._parent.get_name() }});
@@ -50,12 +155,24 @@ class Geocoder(JSCSSMixin, MacroElement):
50155
)
51156
]
52157

53-
def __init__(self, collapsed=False, position="topright", add_marker=True, **kwargs):
158+
def __init__(self, collapsed=False, position="topright", add_marker=True,
159+
geocodeProvider="nominatim",
160+
geocodeProviderOptions=None,
161+
serviceUrl=None,
162+
geocodeFunction=None,
163+
resultsHandlerFunction=None,
164+
**kwargs):
54165
super().__init__()
55166
self._name = "Geocoder"
56167
self.options = parse_options(
57168
collapsed=collapsed,
58169
position=position,
59170
defaultMarkGeocode=add_marker,
171+
geocodeProvider=geocodeProvider,
172+
serviceUrl=serviceUrl,
173+
geocodeFunction=geocodeFunction,
174+
resultsHandlerFunction=resultsHandlerFunction,
60175
**kwargs
61176
)
177+
if geocodeProviderOptions is not None:
178+
self.options['geocodeProviderOptions'] = geocodeProviderOptions

0 commit comments

Comments
 (0)