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.
620 righe
17 KiB
620 righe
17 KiB
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
|
|
|