├── .gitignore
├── .jshintrc
├── LICENSE.md
├── README.md
├── examples
├── Control.Geocoder.js
├── index.css
├── index.html
└── index.js
├── package.json
├── scripts
├── dist.sh
└── publish.sh
└── src
└── L.Routing.GraphHopper.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | /*
3 | * ENVIRONMENTS
4 | * =================
5 | */
6 |
7 | // Define globals exposed by modern browsers.
8 | "browser": true,
9 |
10 | // Define globals exposed by Node.js.
11 | "node": true,
12 |
13 | "globals": {"L": false},
14 |
15 | /*
16 | * ENFORCING OPTIONS
17 | * =================
18 | */
19 |
20 | // Force all variable names to use either camelCase style or UPPER_CASE
21 | // with underscores.
22 | "camelcase": true,
23 |
24 | // Prohibit use of == and != in favor of === and !==.
25 | "eqeqeq": true,
26 |
27 | // Suppress warnings about == null comparisons.
28 | "eqnull": true,
29 |
30 | // Enforce tab width of 2 spaces.
31 | "indent": 2,
32 |
33 | "smarttabs": true,
34 |
35 | // Prohibit use of a variable before it is defined.
36 | "latedef": true,
37 |
38 | // Require capitalized names for constructor functions.
39 | "newcap": true,
40 |
41 | // Enforce use of single quotation marks for strings.
42 | "quotmark": "single",
43 |
44 | // Prohibit trailing whitespace.
45 | "trailing": true,
46 |
47 | // Prohibit use of explicitly undeclared variables.
48 | "undef": true,
49 |
50 | // Warn when variables are defined but never used.
51 | "unused": true,
52 |
53 | // All loops and conditionals should have braces.
54 | "curly": true
55 | }
56 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | ## ISC License
2 |
3 | Copyright (c) 2015, Per Liedman (per@liedman.net)
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Leaflet Routing Machine / GraphHopper
2 | =====================================
3 |
4 | ## Maintainers Wanted
5 |
6 | *I no longer use this module, and have not done so in a long while. If you use it, please consider taking over maintenance of this module (it's not a lot of work). Because of this situation, I have very limited time and expertise to answer any questions regarding this module*
7 |
8 | [](https://www.npmjs.com/package/lrm-graphhopper)
9 |
10 | Extends [Leaflet Routing Machine](https://github.com/perliedman/leaflet-routing-machine) with support for [GraphHopper](https://graphhopper.com/).
11 |
12 | Some brief instructions follow below, but the [Leaflet Routing Machine tutorial on alternative routers](http://www.liedman.net/leaflet-routing-machine/tutorials/alternative-routers/) is recommended.
13 |
14 | ## Installing
15 |
16 | Go to the [releases page](https://github.com/perliedman/lrm-graphhopper/releases) to get the script to include in your page. Put the script after Leaflet and Leaflet Routing Machine has been loaded.
17 |
18 | To use with for example Browserify:
19 |
20 | ```sh
21 | npm install --save lrm-graphhopper
22 | ```
23 |
24 | ## Using
25 |
26 | There's a single class exported by this module, `L.Routing.GraphHopper`. It implements the [`IRouter`](http://www.liedman.net/leaflet-routing-machine/api/#irouter) interface. Use it to replace Leaflet Routing Machine's default OSRM router implementation:
27 |
28 | ```javascript
29 | var L = require('leaflet');
30 | require('leaflet-routing-machine');
31 | require('lrm-graphhopper'); // This will tack on the class to the L.Routing namespace
32 |
33 | L.Routing.control({
34 | router: new L.Routing.GraphHopper('your GraphHopper API key'),
35 | }).addTo(map);
36 | ```
37 |
38 | Note that you will need to pass a valid GraphHopper API key to the constructor.
39 |
40 | To keep track of the GraphHopper credits consumption, the application may listen to the `response` event fired by the Router object. This event holds the values from [GraphHopper's response HTTP headers](https://graphhopper.com/api/1/docs/#http-headers):
41 | * `status`: The HTTP status code (see [GraphHopper error codes](https://graphhopper.com/api/1/docs/#http-error-codes))
42 | * `limit`: The `X-RateLimit-Limit` header
43 | * `remaining`: The `X-RateLimit-Remaining` header
44 | * `reset`: The `X-RateLimit-Reset` header
45 | * `credits`: The `X-RateLimit-Credits` header
46 |
47 | ```javascript
48 | var router = myRoutingControl.getRouter();
49 | router.on('response',function(e){
50 | console.log('This routing request consumed ' + e.credits + ' credit(s)');
51 | console.log('You have ' + e.remaining + ' left');
52 | });
53 | ```
54 |
--------------------------------------------------------------------------------
/examples/Control.Geocoder.js:
--------------------------------------------------------------------------------
1 | (function (factory) {
2 | // Packaging/modules magic dance
3 | var L;
4 | if (typeof define === 'function' && define.amd) {
5 | // AMD
6 | define(['leaflet'], factory);
7 | } else if (typeof module !== 'undefined') {
8 | // Node/CommonJS
9 | L = require('leaflet');
10 | module.exports = factory(L);
11 | } else {
12 | // Browser globals
13 | if (typeof window.L === 'undefined')
14 | throw 'Leaflet must be loaded first';
15 | factory(window.L);
16 | }
17 | }(function (L) {
18 | 'use strict';
19 | L.Control.Geocoder = L.Control.extend({
20 | options: {
21 | showResultIcons: false,
22 | collapsed: true,
23 | expand: 'click',
24 | position: 'topright',
25 | placeholder: 'Search...',
26 | errorMessage: 'Nothing found.'
27 | },
28 |
29 | _callbackId: 0,
30 |
31 | initialize: function (options) {
32 | L.Util.setOptions(this, options);
33 | if (!this.options.geocoder) {
34 | this.options.geocoder = new L.Control.Geocoder.Nominatim();
35 | }
36 | },
37 |
38 | onAdd: function (map) {
39 | var className = 'leaflet-control-geocoder',
40 | container = L.DomUtil.create('div', className),
41 | icon = L.DomUtil.create('div', 'leaflet-control-geocoder-icon', container),
42 | form = this._form = L.DomUtil.create('form', className + '-form', container),
43 | input;
44 |
45 | this._map = map;
46 | this._container = container;
47 | input = this._input = L.DomUtil.create('input');
48 | input.type = 'text';
49 | input.placeholder = this.options.placeholder;
50 |
51 | L.DomEvent.addListener(input, 'keydown', this._keydown, this);
52 | //L.DomEvent.addListener(input, 'onpaste', this._clearResults, this);
53 | //L.DomEvent.addListener(input, 'oninput', this._clearResults, this);
54 |
55 | this._errorElement = document.createElement('div');
56 | this._errorElement.className = className + '-form-no-error';
57 | this._errorElement.innerHTML = this.options.errorMessage;
58 |
59 | this._alts = L.DomUtil.create('ul', className + '-alternatives leaflet-control-geocoder-alternatives-minimized');
60 |
61 | form.appendChild(input);
62 | form.appendChild(this._errorElement);
63 | container.appendChild(this._alts);
64 |
65 | L.DomEvent.addListener(form, 'submit', this._geocode, this);
66 |
67 | if (this.options.collapsed) {
68 | if (this.options.expand === 'click') {
69 | L.DomEvent.addListener(icon, 'click', function(e) {
70 | // TODO: touch
71 | if (e.button === 0 && e.detail === 1) {
72 | this._toggle();
73 | }
74 | }, this);
75 | } else {
76 | L.DomEvent.addListener(icon, 'mouseover', this._expand, this);
77 | L.DomEvent.addListener(icon, 'mouseout', this._collapse, this);
78 | this._map.on('movestart', this._collapse, this);
79 | }
80 | } else {
81 | this._expand();
82 | }
83 |
84 | L.DomEvent.disableClickPropagation(container);
85 |
86 | return container;
87 | },
88 |
89 | _geocodeResult: function (results) {
90 | L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-throbber');
91 | if (results.length === 1) {
92 | this._geocodeResultSelected(results[0]);
93 | } else if (results.length > 0) {
94 | this._alts.innerHTML = '';
95 | this._results = results;
96 | L.DomUtil.removeClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
97 | for (var i = 0; i < results.length; i++) {
98 | this._alts.appendChild(this._createAlt(results[i], i));
99 | }
100 | } else {
101 | L.DomUtil.addClass(this._errorElement, 'leaflet-control-geocoder-error');
102 | }
103 | },
104 |
105 | markGeocode: function(result) {
106 | this._map.fitBounds(result.bbox);
107 |
108 | if (this._geocodeMarker) {
109 | this._map.removeLayer(this._geocodeMarker);
110 | }
111 |
112 | this._geocodeMarker = new L.Marker(result.center)
113 | .bindPopup(result.html || result.name)
114 | .addTo(this._map)
115 | .openPopup();
116 |
117 | return this;
118 | },
119 |
120 | _geocode: function(event) {
121 | L.DomEvent.preventDefault(event);
122 |
123 | L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-throbber');
124 | this._clearResults();
125 | this.options.geocoder.geocode(this._input.value, this._geocodeResult, this);
126 |
127 | return false;
128 | },
129 |
130 | _geocodeResultSelected: function(result) {
131 | if (this.options.collapsed) {
132 | this._collapse();
133 | } else {
134 | this._clearResults();
135 | }
136 | this.markGeocode(result);
137 | },
138 |
139 | _toggle: function() {
140 | if (this._container.className.indexOf('leaflet-control-geocoder-expanded') >= 0) {
141 | this._collapse();
142 | } else {
143 | this._expand();
144 | }
145 | },
146 |
147 | _expand: function () {
148 | L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-expanded');
149 | this._input.select();
150 | },
151 |
152 | _collapse: function () {
153 | this._container.className = this._container.className.replace(' leaflet-control-geocoder-expanded', '');
154 | L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
155 | L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
156 | },
157 |
158 | _clearResults: function () {
159 | L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
160 | this._selection = null;
161 | L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
162 | },
163 |
164 | _createAlt: function(result, index) {
165 | var li = document.createElement('li'),
166 | a = L.DomUtil.create('a', '', li),
167 | icon = this.options.showResultIcons && result.icon ? L.DomUtil.create('img', '', a) : null,
168 | text = result.html ? undefined : document.createTextNode(result.name);
169 |
170 | if (icon) {
171 | icon.src = result.icon;
172 | }
173 |
174 | a.href = '#';
175 | a.setAttribute('data-result-index', index);
176 |
177 | if (result.html) {
178 | a.innerHTML = result.html;
179 | } else {
180 | a.appendChild(text);
181 | }
182 |
183 | L.DomEvent.addListener(li, 'click', function clickHandler(e) {
184 | L.DomEvent.preventDefault(e);
185 | this._geocodeResultSelected(result);
186 | }, this);
187 |
188 | return li;
189 | },
190 |
191 | _keydown: function(e) {
192 | var _this = this,
193 | select = function select(dir) {
194 | if (_this._selection) {
195 | L.DomUtil.removeClass(_this._selection.firstChild, 'leaflet-control-geocoder-selected');
196 | _this._selection = _this._selection[dir > 0 ? 'nextSibling' : 'previousSibling'];
197 | }
198 | if (!_this._selection) {
199 | _this._selection = _this._alts[dir > 0 ? 'firstChild' : 'lastChild'];
200 | }
201 |
202 | if (_this._selection) {
203 | L.DomUtil.addClass(_this._selection.firstChild, 'leaflet-control-geocoder-selected');
204 | }
205 | };
206 |
207 | switch (e.keyCode) {
208 | // Escape
209 | case 27:
210 | this._collapse();
211 | break;
212 | // Up
213 | case 38:
214 | select(-1);
215 | L.DomEvent.preventDefault(e);
216 | break;
217 | // Up
218 | case 40:
219 | select(1);
220 | L.DomEvent.preventDefault(e);
221 | break;
222 | // Enter
223 | case 13:
224 | if (this._selection) {
225 | var index = parseInt(this._selection.firstChild.getAttribute('data-result-index'), 10);
226 | this._geocodeResultSelected(this._results[index]);
227 | this._clearResults();
228 | L.DomEvent.preventDefault(e);
229 | }
230 | }
231 | return true;
232 | }
233 | });
234 |
235 | L.Control.geocoder = function(id, options) {
236 | return new L.Control.Geocoder(id, options);
237 | };
238 |
239 | L.Control.Geocoder.callbackId = 0;
240 | L.Control.Geocoder.jsonp = function(url, params, callback, context, jsonpParam) {
241 | var callbackId = '_l_geocoder_' + (L.Control.Geocoder.callbackId++);
242 | params[jsonpParam || 'callback'] = callbackId;
243 | window[callbackId] = L.Util.bind(callback, context);
244 | var script = document.createElement('script');
245 | script.type = 'text/javascript';
246 | script.src = url + L.Util.getParamString(params);
247 | script.id = callbackId;
248 | document.getElementsByTagName('head')[0].appendChild(script);
249 | };
250 | L.Control.Geocoder.getJSON = function(url, params, callback) {
251 | var xmlHttp = new XMLHttpRequest();
252 | xmlHttp.open( "GET", url + L.Util.getParamString(params), true);
253 | xmlHttp.send(null);
254 | xmlHttp.onreadystatechange = function () {
255 | if (xmlHttp.readyState != 4) return;
256 | if (xmlHttp.status != 200 && req.status != 304) return;
257 | callback(JSON.parse(xmlHttp.response));
258 | };
259 | };
260 |
261 | L.Control.Geocoder.template = function (str, data, htmlEscape) {
262 | return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {
263 | var value = data[key];
264 | if (value === undefined) {
265 | value = '';
266 | } else if (typeof value === 'function') {
267 | value = value(data);
268 | }
269 | return L.Control.Geocoder.htmlEscape(value);
270 | });
271 | };
272 |
273 | // Adapted from handlebars.js
274 | // https://github.com/wycats/handlebars.js/
275 | L.Control.Geocoder.htmlEscape = (function() {
276 | var badChars = /[&<>"'`]/g;
277 | var possible = /[&<>"'`]/;
278 | var escape = {
279 | '&': '&',
280 | '<': '<',
281 | '>': '>',
282 | '"': '"',
283 | '\'': ''',
284 | '`': '`'
285 | };
286 |
287 | function escapeChar(chr) {
288 | return escape[chr];
289 | }
290 |
291 | return function(string) {
292 | if (string == null) {
293 | return '';
294 | } else if (!string) {
295 | return string + '';
296 | }
297 |
298 | // Force a string conversion as this will be done by the append regardless and
299 | // the regex test will do this transparently behind the scenes, causing issues if
300 | // an object's to string has escaped characters in it.
301 | string = '' + string;
302 |
303 | if (!possible.test(string)) {
304 | return string;
305 | }
306 | return string.replace(badChars, escapeChar);
307 | };
308 | })();
309 |
310 | L.Control.Geocoder.Nominatim = L.Class.extend({
311 | options: {
312 | serviceUrl: '//nominatim.openstreetmap.org/',
313 | geocodingQueryParams: {},
314 | reverseQueryParams: {},
315 | htmlTemplate: function(r) {
316 | var a = r.address,
317 | parts = [];
318 | if (a.road || a.building) {
319 | parts.push('{building} {road} {house_number}');
320 | }
321 |
322 | if (a.city || a.town || a.village) {
323 | parts.push('{postcode} {city}{town}{village}');
325 | }
326 |
327 | if (a.state || a.country) {
328 | parts.push('{state} {country}');
330 | }
331 |
332 | return L.Control.Geocoder.template(parts.join('
'), a, true);
333 | }
334 | },
335 |
336 | initialize: function(options) {
337 | L.Util.setOptions(this, options);
338 | },
339 |
340 | geocode: function(query, cb, context) {
341 | L.Control.Geocoder.jsonp(this.options.serviceUrl + 'search/', L.extend({
342 | q: query,
343 | limit: 5,
344 | format: 'json',
345 | addressdetails: 1
346 | }, this.options.geocodingQueryParams),
347 | function(data) {
348 | var results = [];
349 | for (var i = data.length - 1; i >= 0; i--) {
350 | var bbox = data[i].boundingbox;
351 | for (var j = 0; j < 4; j++) bbox[j] = parseFloat(bbox[j]);
352 | results[i] = {
353 | icon: data[i].icon,
354 | name: data[i].display_name,
355 | html: this.options.htmlTemplate ?
356 | this.options.htmlTemplate(data[i])
357 | : undefined,
358 | bbox: L.latLngBounds([bbox[0], bbox[2]], [bbox[1], bbox[3]]),
359 | center: L.latLng(data[i].lat, data[i].lon),
360 | properties: data[i]
361 | };
362 | }
363 | cb.call(context, results);
364 | }, this, 'json_callback');
365 | },
366 |
367 | reverse: function(location, scale, cb, context) {
368 | L.Control.Geocoder.jsonp(this.options.serviceUrl + 'reverse/', L.extend({
369 | lat: location.lat,
370 | lon: location.lng,
371 | zoom: Math.round(Math.log(scale / 256) / Math.log(2)),
372 | addressdetails: 1,
373 | format: 'json'
374 | }, this.options.reverseQueryParams), function(data) {
375 | var result = [],
376 | loc;
377 |
378 | if (data && data.lat && data.lon) {
379 | loc = L.latLng(data.lat, data.lon);
380 | result.push({
381 | name: data.display_name,
382 | html: this.options.htmlTemplate ?
383 | this.options.htmlTemplate(data)
384 | : undefined,
385 | center: loc,
386 | bounds: L.latLngBounds(loc, loc),
387 | properties: data
388 | });
389 | }
390 |
391 | cb.call(context, result);
392 | }, this, 'json_callback');
393 | }
394 | });
395 |
396 | L.Control.Geocoder.nominatim = function(options) {
397 | return new L.Control.Geocoder.Nominatim(options);
398 | };
399 |
400 | L.Control.Geocoder.Bing = L.Class.extend({
401 | initialize: function(key) {
402 | this.key = key;
403 | },
404 |
405 | geocode : function (query, cb, context) {
406 | L.Control.Geocoder.jsonp('//dev.virtualearth.net/REST/v1/Locations', {
407 | query: query,
408 | key : this.key
409 | }, function(data) {
410 | var results = [];
411 | for (var i = data.resourceSets[0].resources.length - 1; i >= 0; i--) {
412 | var resource = data.resourceSets[0].resources[i],
413 | bbox = resource.bbox;
414 | results[i] = {
415 | name: resource.name,
416 | bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]),
417 | center: L.latLng(resource.point.coordinates)
418 | };
419 | }
420 | cb.call(context, results);
421 | }, this, 'jsonp');
422 | },
423 |
424 | reverse: function(location, scale, cb, context) {
425 | L.Control.Geocoder.jsonp('//dev.virtualearth.net/REST/v1/Locations/' + location.lat + ',' + location.lng, {
426 | key : this.key
427 | }, function(data) {
428 | var results = [];
429 | for (var i = data.resourceSets[0].resources.length - 1; i >= 0; i--) {
430 | var resource = data.resourceSets[0].resources[i],
431 | bbox = resource.bbox;
432 | results[i] = {
433 | name: resource.name,
434 | bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]),
435 | center: L.latLng(resource.point.coordinates)
436 | };
437 | }
438 | cb.call(context, results);
439 | }, this, 'jsonp');
440 | }
441 | });
442 |
443 | L.Control.Geocoder.bing = function(key) {
444 | return new L.Control.Geocoder.Bing(key);
445 | };
446 |
447 | L.Control.Geocoder.RaveGeo = L.Class.extend({
448 | options: {
449 | querySuffix: '',
450 | deepSearch: true,
451 | wordBased: false
452 | },
453 |
454 | jsonp: function(params, callback, context) {
455 | var callbackId = '_l_geocoder_' + (L.Control.Geocoder.callbackId++),
456 | paramParts = [];
457 | params.prepend = callbackId + '(';
458 | params.append = ')';
459 | for (var p in params) {
460 | paramParts.push(p + '=' + escape(params[p]));
461 | }
462 |
463 | window[callbackId] = L.Util.bind(callback, context);
464 | var script = document.createElement('script');
465 | script.type = 'text/javascript';
466 | script.src = this._serviceUrl + '?' + paramParts.join('&');
467 | script.id = callbackId;
468 | document.getElementsByTagName('head')[0].appendChild(script);
469 | },
470 |
471 | initialize: function(serviceUrl, scheme, options) {
472 | L.Util.setOptions(this, options);
473 |
474 | this._serviceUrl = serviceUrl;
475 | this._scheme = scheme;
476 | },
477 |
478 | geocode: function(query, cb, context) {
479 | L.Control.Geocoder.jsonp(this._serviceUrl, {
480 | address: query + this.options.querySuffix,
481 | scheme: this._scheme,
482 | outputFormat: 'jsonp',
483 | deepSearch: this.options.deepSearch,
484 | wordBased: this.options.wordBased
485 | }, function(data) {
486 | var results = [];
487 | for (var i = data.length - 1; i >= 0; i--) {
488 | var r = data[i],
489 | c = L.latLng(r.y, r.x);
490 | results[i] = {
491 | name: r.address,
492 | bbox: L.latLngBounds([c]),
493 | center: c
494 | };
495 | }
496 | cb.call(context, results);
497 | }, this);
498 | }
499 | });
500 |
501 | L.Control.Geocoder.raveGeo = function(serviceUrl, scheme, options) {
502 | return new L.Control.Geocoder.RaveGeo(serviceUrl, scheme, options);
503 | };
504 |
505 | L.Control.Geocoder.MapQuest = L.Class.extend({
506 | initialize: function(key) {
507 | // MapQuest seems to provide URI encoded API keys,
508 | // so to avoid encoding them twice, we decode them here
509 | this._key = decodeURIComponent(key);
510 | },
511 |
512 | _formatName: function() {
513 | var r = [],
514 | i;
515 | for (i = 0; i < arguments.length; i++) {
516 | if (arguments[i]) {
517 | r.push(arguments[i]);
518 | }
519 | }
520 |
521 | return r.join(', ');
522 | },
523 |
524 | geocode: function(query, cb, context) {
525 | L.Control.Geocoder.jsonp('//www.mapquestapi.com/geocoding/v1/address', {
526 | key: this._key,
527 | location: query,
528 | limit: 5,
529 | outFormat: 'json'
530 | }, function(data) {
531 | var results = [],
532 | loc,
533 | latLng;
534 | if (data.results && data.results[0].locations) {
535 | for (var i = data.results[0].locations.length - 1; i >= 0; i--) {
536 | loc = data.results[0].locations[i];
537 | latLng = L.latLng(loc.latLng);
538 | results[i] = {
539 | name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1),
540 | bbox: L.latLngBounds(latLng, latLng),
541 | center: latLng
542 | };
543 | }
544 | }
545 |
546 | cb.call(context, results);
547 | }, this);
548 | },
549 |
550 | reverse: function(location, scale, cb, context) {
551 | L.Control.Geocoder.jsonp('//www.mapquestapi.com/geocoding/v1/reverse', {
552 | key: this._key,
553 | location: location.lat + ',' + location.lng,
554 | outputFormat: 'json'
555 | }, function(data) {
556 | var results = [],
557 | loc,
558 | latLng;
559 | if (data.results && data.results[0].locations) {
560 | for (var i = data.results[0].locations.length - 1; i >= 0; i--) {
561 | loc = data.results[0].locations[i];
562 | latLng = L.latLng(loc.latLng);
563 | results[i] = {
564 | name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1),
565 | bbox: L.latLngBounds(latLng, latLng),
566 | center: latLng
567 | };
568 | }
569 | }
570 |
571 | cb.call(context, results);
572 | }, this);
573 | }
574 | });
575 |
576 | L.Control.Geocoder.mapQuest = function(key) {
577 | return new L.Control.Geocoder.MapQuest(key);
578 | };
579 |
580 | L.Control.Geocoder.Mapbox = L.Class.extend({
581 | options: {
582 | service_url: 'https://api.tiles.mapbox.com/v4/geocode/mapbox.places-v1/'
583 | },
584 |
585 | initialize: function(access_token) {
586 | this._access_token = access_token;
587 | },
588 |
589 | geocode: function(query, cb, context) {
590 | L.Control.Geocoder.getJSON(this.options.service_url + encodeURIComponent(query) + '.json', {
591 | access_token: this._access_token,
592 | }, function(data) {
593 | var results = [],
594 | loc,
595 | latLng,
596 | latLngBounds;
597 | if (data.features && data.features.length) {
598 | for (var i = 0; i <= data.features.length - 1; i++) {
599 | loc = data.features[i];
600 | latLng = L.latLng(loc.center.reverse());
601 | if(loc.hasOwnProperty('bbox'))
602 | {
603 | latLngBounds = L.latLngBounds(L.latLng(loc.bbox.slice(0, 2).reverse()), L.latLng(loc.bbox.slice(2, 4).reverse()));
604 | }
605 | else
606 | {
607 | latLngBounds = L.latLngBounds(latLng, latLng);
608 | }
609 | results[i] = {
610 | name: loc.place_name,
611 | bbox: latLngBounds,
612 | center: latLng
613 | };
614 | }
615 | }
616 |
617 | cb.call(context, results);
618 | });
619 | },
620 |
621 | reverse: function(location, scale, cb, context) {
622 | L.Control.Geocoder.getJSON(this.options.service_url + encodeURIComponent(location.lng) + ',' + encodeURIComponent(location.lat) + '.json', {
623 | access_token: this._access_token,
624 | }, function(data) {
625 | var results = [],
626 | loc,
627 | latLng,
628 | latLngBounds;
629 | if (data.features && data.features.length) {
630 | for (var i = 0; i <= data.features.length - 1; i++) {
631 | loc = data.features[i];
632 | latLng = L.latLng(loc.center.reverse());
633 | if(loc.hasOwnProperty('bbox'))
634 | {
635 | latLngBounds = L.latLngBounds(L.latLng(loc.bbox.slice(0, 2).reverse()), L.latLng(loc.bbox.slice(2, 4).reverse()));
636 | }
637 | else
638 | {
639 | latLngBounds = L.latLngBounds(latLng, latLng);
640 | }
641 | results[i] = {
642 | name: loc.place_name,
643 | bbox: latLngBounds,
644 | center: latLng
645 | };
646 | }
647 | }
648 |
649 | cb.call(context, results);
650 | });
651 | }
652 | });
653 |
654 | L.Control.Geocoder.mapbox = function(access_token) {
655 | return new L.Control.Geocoder.Mapbox(access_token);
656 | };
657 |
658 | L.Control.Geocoder.Google = L.Class.extend({
659 | options: {
660 | service_url: 'https://maps.googleapis.com/maps/api/geocode/json'
661 | },
662 |
663 | initialize: function(key) {
664 | this._key = key;
665 | },
666 |
667 | geocode: function(query, cb, context) {
668 | var params = {
669 | address: query,
670 | };
671 | if(this._key && this._key.length)
672 | {
673 | params['key'] = this._key
674 | }
675 |
676 | L.Control.Geocoder.getJSON(this.options.service_url, params, function(data) {
677 | var results = [],
678 | loc,
679 | latLng,
680 | latLngBounds;
681 | if (data.results && data.results.length) {
682 | for (var i = 0; i <= data.results.length - 1; i++) {
683 | loc = data.results[i];
684 | latLng = L.latLng(loc.geometry.location);
685 | latLngBounds = L.latLngBounds(L.latLng(loc.geometry.viewport.northeast), L.latLng(loc.geometry.viewport.southwest));
686 | results[i] = {
687 | name: loc.formatted_address,
688 | bbox: latLngBounds,
689 | center: latLng
690 | };
691 | }
692 | }
693 |
694 | cb.call(context, results);
695 | });
696 | },
697 |
698 | reverse: function(location, scale, cb, context) {
699 | var params = {
700 | latlng: encodeURIComponent(location.lat) + ',' + encodeURIComponent(location.lng)
701 | };
702 | if(this._key && this._key.length)
703 | {
704 | params['key'] = this._key
705 | }
706 | L.Control.Geocoder.getJSON(this.options.service_url, params, function(data) {
707 | var results = [],
708 | loc,
709 | latLng,
710 | latLngBounds;
711 | if (data.results && data.results.length) {
712 | for (var i = 0; i <= data.results.length - 1; i++) {
713 | loc = data.results[i];
714 | latLng = L.latLng(loc.geometry.location);
715 | latLngBounds = L.latLngBounds(L.latLng(loc.geometry.viewport.northeast), L.latLng(loc.geometry.viewport.southwest));
716 | results[i] = {
717 | name: loc.formatted_address,
718 | bbox: latLngBounds,
719 | center: latLng
720 | };
721 | }
722 | }
723 |
724 | cb.call(context, results);
725 | });
726 | }
727 | });
728 |
729 | L.Control.Geocoder.google = function(key) {
730 | return new L.Control.Geocoder.Google(key);
731 | };
732 | return L.Control.Geocoder;
733 | }));
734 |
--------------------------------------------------------------------------------
/examples/index.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-family: sans-serif;
3 | font-size: 11pt;
4 | }
5 |
6 | .map {
7 | position: fixed;
8 | top: 0px;
9 | left: 0px;
10 | right: 0px;
11 | bottom: 0px;
12 | }
13 |
14 | .results {
15 | background-color: white;
16 | opacity: 0.8;
17 | position: absolute;
18 | top: 12px;
19 | right: 12px;
20 | width: 320px;
21 | height: 480px;
22 | overflow-y: scroll;
23 | }
24 |
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |