Non puoi selezionare più di 25 argomenti
Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
621 righe
17 KiB
621 righe
17 KiB
2 anni fa
|
this.L = this.L || {};
|
||
|
this.L.Control = this.L.Control || {};
|
||
|
this.L.Control.Geocoder = (function (L) {
|
||
|
'use strict';
|
||
|
|
||
|
L = L && L.hasOwnProperty('default') ? L['default'] : L;
|
||
|
|
||
|
var lastCallbackId = 0;
|
||
|
|
||
|
// Adapted from handlebars.js
|
||
|
// https://github.com/wycats/handlebars.js/
|
||
|
var badChars = /[&<>"'`]/g;
|
||
|
var possible = /[&<>"'`]/;
|
||
|
var escape = {
|
||
|
'&': '&',
|
||
|
'<': '<',
|
||
|
'>': '>',
|
||
|
'"': '"',
|
||
|
"'": ''',
|
||
|
'`': '`'
|
||
|
};
|
||
|
|
||
|
function escapeChar(chr) {
|
||
|
return escape[chr];
|
||
|
}
|
||
|
|
||
|
function htmlEscape(string) {
|
||
|
if (string == null) {
|
||
|
return '';
|
||
|
} else if (!string) {
|
||
|
return string + '';
|
||
|
}
|
||
|
|
||
|
// Force a string conversion as this will be done by the append regardless and
|
||
|
// the regex test will do this transparently behind the scenes, causing issues if
|
||
|
// an object's to string has escaped characters in it.
|
||
|
string = '' + string;
|
||
|
|
||
|
if (!possible.test(string)) {
|
||
|
return string;
|
||
|
}
|
||
|
return string.replace(badChars, escapeChar);
|
||
|
}
|
||
|
|
||
|
function jsonp(url, params, callback, context, jsonpParam) {
|
||
|
var callbackId = '_l_geocoder_' + lastCallbackId++;
|
||
|
params[jsonpParam || 'callback'] = callbackId;
|
||
|
window[callbackId] = L.Util.bind(callback, context);
|
||
|
var script = document.createElement('script');
|
||
|
script.type = 'text/javascript';
|
||
|
script.src = url + L.Util.getParamString(params);
|
||
|
script.id = callbackId;
|
||
|
document.getElementsByTagName('head')[0].appendChild(script);
|
||
|
}
|
||
|
|
||
|
function getJSON(url, params, callback) {
|
||
|
var xmlHttp = new XMLHttpRequest();
|
||
|
xmlHttp.onreadystatechange = function() {
|
||
|
if (xmlHttp.readyState !== 4) {
|
||
|
return;
|
||
|
}
|
||
|
if (xmlHttp.status !== 200 && xmlHttp.status !== 304) {
|
||
|
callback('');
|
||
|
return;
|
||
|
}
|
||
|
callback(JSON.parse(xmlHttp.response));
|
||
|
};
|
||
|
xmlHttp.open('GET', url + L.Util.getParamString(params), true);
|
||
|
xmlHttp.setRequestHeader('Accept', 'application/json');
|
||
|
xmlHttp.send(null);
|
||
|
}
|
||
|
|
||
|
function template(str, data) {
|
||
|
return str.replace(/\{ *([\w_]+) *\}/g, function(str, key) {
|
||
|
var value = data[key];
|
||
|
if (value === undefined) {
|
||
|
value = '';
|
||
|
} else if (typeof value === 'function') {
|
||
|
value = value(data);
|
||
|
}
|
||
|
return htmlEscape(value);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
var Control = {
|
||
|
class: L.Control.extend({
|
||
|
options: {
|
||
|
showResultIcons: false,
|
||
|
collapsed: true,
|
||
|
expand: 'touch', // options: touch, click, anythingelse
|
||
|
position: 'topleft',
|
||
|
placeholder: 'Cerca indirizzo...',
|
||
|
errorMessage: 'Nessuna risultato trovato.',
|
||
|
suggestMinLength: 3,
|
||
|
suggestTimeout: 250,
|
||
|
defaultMarkGeocode: true
|
||
|
},
|
||
|
|
||
|
includes: L.Evented.prototype || L.Mixin.Events,
|
||
|
|
||
|
initialize: function(options) {
|
||
|
L.Util.setOptions(this, options);
|
||
|
if (!this.options.geocoder) {
|
||
|
this.options.geocoder = new arcgis.class();
|
||
|
}
|
||
|
|
||
|
this._requestCount = 0;
|
||
|
},
|
||
|
|
||
|
onAdd: function(map) {
|
||
|
var className = 'leaflet-control-geocoder',
|
||
|
container = L.DomUtil.create('div', className + ' leaflet-bar'),
|
||
|
icon = L.DomUtil.create('button', className + '-icon', container),
|
||
|
form = (this._form = L.DomUtil.create('div', className + '-form', container)),
|
||
|
input;
|
||
|
|
||
|
this._map = map;
|
||
|
this._container = container;
|
||
|
|
||
|
icon.innerHTML = ' ';
|
||
|
icon.type = 'button';
|
||
|
|
||
|
input = this._input = L.DomUtil.create('input', '', form);
|
||
|
input.type = 'text';
|
||
|
input.placeholder = this.options.placeholder;
|
||
|
|
||
|
this._errorElement = L.DomUtil.create('div', className + '-form-no-error', container);
|
||
|
this._errorElement.innerHTML = this.options.errorMessage;
|
||
|
|
||
|
this._alts = L.DomUtil.create(
|
||
|
'ul',
|
||
|
className + '-alternatives leaflet-control-geocoder-alternatives-minimized',
|
||
|
container
|
||
|
);
|
||
|
L.DomEvent.disableClickPropagation(this._alts);
|
||
|
|
||
|
L.DomEvent.addListener(input, 'keydown', this._keydown, this);
|
||
|
if (this.options.geocoder.suggest) {
|
||
|
L.DomEvent.addListener(input, 'input', this._change, this);
|
||
|
}
|
||
|
L.DomEvent.addListener(
|
||
|
input,
|
||
|
'blur',
|
||
|
function() {
|
||
|
if (this.options.collapsed && !this._preventBlurCollapse) {
|
||
|
this._collapse();
|
||
|
}
|
||
|
this._preventBlurCollapse = false;
|
||
|
},
|
||
|
this
|
||
|
);
|
||
|
|
||
|
if (this.options.collapsed) {
|
||
|
if (this.options.expand === 'click') {
|
||
|
L.DomEvent.addListener(
|
||
|
container,
|
||
|
'click',
|
||
|
function(e) {
|
||
|
if (e.button === 0 && e.detail !== 2) {
|
||
|
this._toggle();
|
||
|
}
|
||
|
},
|
||
|
this
|
||
|
);
|
||
|
} else if (L.Browser.touch && this.options.expand === 'touch') {
|
||
|
L.DomEvent.addListener(
|
||
|
container,
|
||
|
'touchstart mousedown',
|
||
|
function(e) {
|
||
|
this._toggle();
|
||
|
e.preventDefault(); // mobile: clicking focuses the icon, so UI expands and immediately collapses
|
||
|
e.stopPropagation();
|
||
|
},
|
||
|
this
|
||
|
);
|
||
|
} else {
|
||
|
L.DomEvent.addListener(container, 'mouseover', this._expand, this);
|
||
|
L.DomEvent.addListener(container, 'mouseout', this._collapse, this);
|
||
|
this._map.on('movestart', this._collapse, this);
|
||
|
}
|
||
|
} else {
|
||
|
this._expand();
|
||
|
if (L.Browser.touch) {
|
||
|
L.DomEvent.addListener(
|
||
|
container,
|
||
|
'touchstart',
|
||
|
function() {
|
||
|
this._geocode();
|
||
|
},
|
||
|
this
|
||
|
);
|
||
|
} else {
|
||
|
L.DomEvent.addListener(
|
||
|
container,
|
||
|
'click',
|
||
|
function() {
|
||
|
this._geocode();
|
||
|
},
|
||
|
this
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (this.options.defaultMarkGeocode) {
|
||
|
this.on('markgeocode', this.markGeocode, this);
|
||
|
}
|
||
|
|
||
|
this.on(
|
||
|
'startgeocode',
|
||
|
function() {
|
||
|
L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-throbber');
|
||
|
},
|
||
|
this
|
||
|
);
|
||
|
this.on(
|
||
|
'finishgeocode',
|
||
|
function() {
|
||
|
L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-throbber');
|
||
|
},
|
||
|
this
|
||
|
);
|
||
|
|
||
|
L.DomEvent.disableClickPropagation(container);
|
||
|
|
||
|
return container;
|
||
|
},
|
||
|
|
||
|
_geocodeResult: function(results, suggest) {
|
||
|
if (!suggest && results.length === 1) {
|
||
|
this._geocodeResultSelected(results[0]);
|
||
|
} else if (results.length > 0) {
|
||
|
this._alts.innerHTML = '';
|
||
|
this._results = results;
|
||
|
L.DomUtil.removeClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
|
||
|
for (var i = 0; i < results.length; i++) {
|
||
|
this._alts.appendChild(this._createAlt(results[i], i));
|
||
|
}
|
||
|
} else {
|
||
|
L.DomUtil.addClass(this._errorElement, 'leaflet-control-geocoder-error');
|
||
|
}
|
||
|
},
|
||
|
|
||
|
markGeocode: function(result) {
|
||
|
result = result.geocode || result;
|
||
|
|
||
|
this._map.fitBounds(result.bbox);
|
||
|
|
||
|
if (this._geocodeMarker) {
|
||
|
this._map.removeLayer(this._geocodeMarker);
|
||
|
}
|
||
|
|
||
|
this._geocodeMarker = new L.Marker(result.center)
|
||
|
.bindPopup(result.html || result.name)
|
||
|
.addTo(this._map)
|
||
|
.openPopup();
|
||
|
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
_geocode: function(suggest) {
|
||
|
var requestCount = ++this._requestCount,
|
||
|
mode = suggest ? 'suggest' : 'geocode',
|
||
|
eventData = { input: this._input.value };
|
||
|
|
||
|
this._lastGeocode = this._input.value;
|
||
|
if (!suggest) {
|
||
|
this._clearResults();
|
||
|
}
|
||
|
|
||
|
this.fire('start' + mode, eventData);
|
||
|
this.options.geocoder[mode](
|
||
|
this._input.value,
|
||
|
function(results) {
|
||
|
if (requestCount === this._requestCount) {
|
||
|
eventData.results = results;
|
||
|
this.fire('finish' + mode, eventData);
|
||
|
this._geocodeResult(results, suggest);
|
||
|
}
|
||
|
},
|
||
|
this
|
||
|
);
|
||
|
},
|
||
|
|
||
|
_geocodeResultSelected: function(result) {
|
||
|
this.fire('markgeocode', { geocode: result });
|
||
|
},
|
||
|
|
||
|
_toggle: function() {
|
||
|
if (L.DomUtil.hasClass(this._container, 'leaflet-control-geocoder-expanded')) {
|
||
|
this._collapse();
|
||
|
} else {
|
||
|
this._expand();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_expand: function() {
|
||
|
L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-expanded');
|
||
|
this._input.select();
|
||
|
this.fire('expand');
|
||
|
},
|
||
|
|
||
|
_collapse: function() {
|
||
|
L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-expanded');
|
||
|
L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
|
||
|
L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
|
||
|
this._input.blur(); // mobile: keyboard shouldn't stay expanded
|
||
|
this.fire('collapse');
|
||
|
},
|
||
|
|
||
|
_clearResults: function() {
|
||
|
L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
|
||
|
this._selection = null;
|
||
|
L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
|
||
|
},
|
||
|
|
||
|
_createAlt: function(result, index) {
|
||
|
var li = L.DomUtil.create('li', ''),
|
||
|
a = L.DomUtil.create('a', '', li),
|
||
|
icon = this.options.showResultIcons && result.icon ? L.DomUtil.create('img', '', a) : null,
|
||
|
text = result.html ? undefined : document.createTextNode(result.name),
|
||
|
mouseDownHandler = function mouseDownHandler(e) {
|
||
|
// In some browsers, a click will fire on the map if the control is
|
||
|
// collapsed directly after mousedown. To work around this, we
|
||
|
// wait until the click is completed, and _then_ collapse the
|
||
|
// control. Messy, but this is the workaround I could come up with
|
||
|
// for #142.
|
||
|
this._preventBlurCollapse = true;
|
||
|
L.DomEvent.stop(e);
|
||
|
this._geocodeResultSelected(result);
|
||
|
L.DomEvent.on(
|
||
|
li,
|
||
|
'click',
|
||
|
function() {
|
||
|
if (this.options.collapsed) {
|
||
|
this._collapse();
|
||
|
} else {
|
||
|
this._clearResults();
|
||
|
}
|
||
|
},
|
||
|
this
|
||
|
);
|
||
|
};
|
||
|
|
||
|
if (icon) {
|
||
|
icon.src = result.icon;
|
||
|
}
|
||
|
|
||
|
li.setAttribute('data-result-index', index);
|
||
|
|
||
|
if (result.html) {
|
||
|
a.innerHTML = a.innerHTML + result.html;
|
||
|
} else {
|
||
|
a.appendChild(text);
|
||
|
}
|
||
|
|
||
|
// Use mousedown and not click, since click will fire _after_ blur,
|
||
|
// causing the control to have collapsed and removed the items
|
||
|
// before the click can fire.
|
||
|
L.DomEvent.addListener(li, 'mousedown touchstart', mouseDownHandler, this);
|
||
|
|
||
|
return li;
|
||
|
},
|
||
|
|
||
|
_keydown: function(e) {
|
||
|
var _this = this,
|
||
|
select = function select(dir) {
|
||
|
if (_this._selection) {
|
||
|
L.DomUtil.removeClass(_this._selection, 'leaflet-control-geocoder-selected');
|
||
|
_this._selection = _this._selection[dir > 0 ? 'nextSibling' : 'previousSibling'];
|
||
|
}
|
||
|
if (!_this._selection) {
|
||
|
_this._selection = _this._alts[dir > 0 ? 'firstChild' : 'lastChild'];
|
||
|
}
|
||
|
|
||
|
if (_this._selection) {
|
||
|
L.DomUtil.addClass(_this._selection, 'leaflet-control-geocoder-selected');
|
||
|
}
|
||
|
};
|
||
|
|
||
|
switch (e.keyCode) {
|
||
|
// Escape
|
||
|
case 27:
|
||
|
if (this.options.collapsed) {
|
||
|
this._collapse();
|
||
|
}
|
||
|
break;
|
||
|
// Up
|
||
|
case 38:
|
||
|
select(-1);
|
||
|
break;
|
||
|
// Up
|
||
|
case 40:
|
||
|
select(1);
|
||
|
break;
|
||
|
// Enter
|
||
|
case 13:
|
||
|
if (this._selection) {
|
||
|
var index = parseInt(this._selection.getAttribute('data-result-index'), 10);
|
||
|
this._geocodeResultSelected(this._results[index]);
|
||
|
this._clearResults();
|
||
|
} else {
|
||
|
this._geocode();
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
},
|
||
|
_change: function() {
|
||
|
var v = this._input.value;
|
||
|
if (v !== this._lastGeocode) {
|
||
|
clearTimeout(this._suggestTimeout);
|
||
|
if (v.length >= this.options.suggestMinLength) {
|
||
|
this._suggestTimeout = setTimeout(
|
||
|
L.bind(function() {
|
||
|
this._geocode(true);
|
||
|
}, this),
|
||
|
this.options.suggestTimeout
|
||
|
);
|
||
|
} else {
|
||
|
this._clearResults();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}),
|
||
|
factory: function(options) {
|
||
|
return new L.Control.Geocoder(options);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var Google = {
|
||
|
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);
|
||
|
// Backwards compatibility
|
||
|
this.options.serviceUrl = this.options.service_url || this.options.serviceUrl;
|
||
|
},
|
||
|
|
||
|
geocode: function(query, cb, context) {
|
||
|
var params = {
|
||
|
address: query
|
||
|
};
|
||
|
|
||
|
if (this._key && this._key.length) {
|
||
|
params.key = this._key;
|
||
|
}
|
||
|
|
||
|
params = L.Util.extend(params, this.options.geocodingQueryParams);
|
||
|
|
||
|
getJSON(this.options.serviceUrl, params, function(data) {
|
||
|
var results = [],
|
||
|
loc,
|
||
|
latLng,
|
||
|
latLngBounds;
|
||
|
if (data.results && data.results.length) {
|
||
|
for (var i = 0; i <= data.results.length - 1; i++) {
|
||
|
loc = data.results[i];
|
||
|
latLng = L.latLng(loc.geometry.location);
|
||
|
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
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cb.call(context, results);
|
||
|
});
|
||
|
},
|
||
|
|
||
|
reverse: function(location, scale, cb, context) {
|
||
|
var params = {
|
||
|
latlng: encodeURIComponent(location.lat) + ',' + encodeURIComponent(location.lng)
|
||
|
};
|
||
|
params = L.Util.extend(params, this.options.reverseQueryParams);
|
||
|
if (this._key && this._key.length) {
|
||
|
params.key = this._key;
|
||
|
}
|
||
|
|
||
|
getJSON(this.options.serviceUrl, params, function(data) {
|
||
|
var results = [],
|
||
|
loc,
|
||
|
latLng,
|
||
|
latLngBounds;
|
||
|
if (data.results && data.results.length) {
|
||
|
for (var i = 0; i <= data.results.length - 1; i++) {
|
||
|
loc = data.results[i];
|
||
|
latLng = L.latLng(loc.geometry.location);
|
||
|
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
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cb.call(context, results);
|
||
|
});
|
||
|
}
|
||
|
}),
|
||
|
|
||
|
factory: function(key, options) {
|
||
|
return new L.Control.Geocoder.Google(key, options);
|
||
|
}
|
||
|
};
|
||
|
var ArcGis = {
|
||
|
class: L.Class.extend({
|
||
|
options: {
|
||
|
service_url: 'http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer'
|
||
|
},
|
||
|
|
||
|
initialize: function(accessToken, options) {
|
||
|
L.setOptions(this, options);
|
||
|
this._accessToken = accessToken;
|
||
|
},
|
||
|
|
||
|
geocode: function(query, cb, context) {
|
||
|
var params = {
|
||
|
SingleLine: query,
|
||
|
outFields: 'Addr_Type',
|
||
|
forStorage: false,
|
||
|
maxLocations: 10,
|
||
|
f: 'json'
|
||
|
};
|
||
|
|
||
|
if (this._key && this._key.length) {
|
||
|
params.token = this._key;
|
||
|
}
|
||
|
|
||
|
getJSON(this.options.service_url + '/findAddressCandidates', params, function(data) {
|
||
|
var results = [],
|
||
|
loc,
|
||
|
latLng,
|
||
|
latLngBounds;
|
||
|
|
||
|
if (data.candidates && data.candidates.length) {
|
||
|
for (var i = 0; i <= data.candidates.length - 1; i++) {
|
||
|
loc = data.candidates[i];
|
||
|
latLng = L.latLng(loc.location.y, loc.location.x);
|
||
|
latLngBounds = L.latLngBounds(
|
||
|
L.latLng(loc.extent.ymax, loc.extent.xmax),
|
||
|
L.latLng(loc.extent.ymin, loc.extent.xmin)
|
||
|
);
|
||
|
results[i] = {
|
||
|
name: loc.address,
|
||
|
bbox: latLngBounds,
|
||
|
center: latLng
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cb.call(context, results);
|
||
|
});
|
||
|
},
|
||
|
|
||
|
suggest: function(query, cb, context) {
|
||
|
return this.geocode(query, cb, context);
|
||
|
},
|
||
|
|
||
|
reverse: function(location, scale, cb, context) {
|
||
|
var params = {
|
||
|
location: encodeURIComponent(location.lng) + ',' + encodeURIComponent(location.lat),
|
||
|
distance: 100,
|
||
|
f: 'json'
|
||
|
};
|
||
|
|
||
|
getJSON(this.options.service_url + '/reverseGeocode', params, function(data) {
|
||
|
var result = [],
|
||
|
loc;
|
||
|
|
||
|
if (data && !data.error) {
|
||
|
loc = L.latLng(data.location.y, data.location.x);
|
||
|
result.push({
|
||
|
name: data.address.Match_addr,
|
||
|
center: loc,
|
||
|
bounds: L.latLngBounds(loc, loc)
|
||
|
});
|
||
|
}
|
||
|
|
||
|
cb.call(context, result);
|
||
|
});
|
||
|
}
|
||
|
}),
|
||
|
|
||
|
factory: function(accessToken, options) {
|
||
|
return new L.Control.Geocoder.ArcGis(accessToken, options);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var Geocoder = L.Util.extend(Control.class, {
|
||
|
Google: Google.class,
|
||
|
google: Google.factory,
|
||
|
ArcGis: ArcGis.class,
|
||
|
arcgis: ArcGis.factory,
|
||
|
});
|
||
|
|
||
|
L.Util.extend(L.Control, {
|
||
|
Geocoder: Geocoder,
|
||
|
geocoder: Control.factory
|
||
|
});
|
||
|
|
||
|
return Geocoder;
|
||
|
|
||
|
}(L));
|
||
|
//# sourceMappingURL=Control.Geocoder.js.map
|