From 1d533fa5a7dd9be9e7704c1c51bfb2819b0f967a Mon Sep 17 00:00:00 2001 From: savvasha Date: Sat, 16 Feb 2019 19:45:56 +0200 Subject: [PATCH] Add search function to OpenStreetMap --- assets/css/Control.Geocoder.css | 126 ++ assets/css/images/geocoder.png | Bin 0 -> 490 bytes assets/css/images/throbber.gif | Bin 0 -> 4831 bytes assets/js/Control.Geocoder.js | 1348 ++++++++++++++++++ includes/admin/class-sp-admin-assets.php | 14 +- includes/admin/class-sp-admin-taxonomies.php | 55 +- 6 files changed, 1535 insertions(+), 8 deletions(-) create mode 100644 assets/css/Control.Geocoder.css create mode 100644 assets/css/images/geocoder.png create mode 100644 assets/css/images/throbber.gif create mode 100644 assets/js/Control.Geocoder.js diff --git a/assets/css/Control.Geocoder.css b/assets/css/Control.Geocoder.css new file mode 100644 index 00000000..00c32255 --- /dev/null +++ b/assets/css/Control.Geocoder.css @@ -0,0 +1,126 @@ +.leaflet-control-geocoder { + border-radius: 4px; + background: white; + min-width: 26px; + min-height: 26px; +} + +.leaflet-touch .leaflet-control-geocoder { + min-width: 30px; + min-height: 30px; +} + +.leaflet-control-geocoder a, +.leaflet-control-geocoder .leaflet-control-geocoder-icon { + border-bottom: none; + display: inline-block; +} + +.leaflet-control-geocoder .leaflet-control-geocoder-alternatives a { + width: inherit; + height: inherit; + line-height: inherit; +} + +.leaflet-control-geocoder a:hover, +.leaflet-control-geocoder .leaflet-control-geocoder-icon:hover { + border-bottom: none; + display: inline-block; +} + +.leaflet-control-geocoder-form { + display: none; + vertical-align: middle; +} +.leaflet-control-geocoder-expanded .leaflet-control-geocoder-form { + display: inline-block; +} +.leaflet-control-geocoder-form input { + font-size: 120%; + border: 0; + background-color: transparent; + width: 246px; +} + +.leaflet-control-geocoder-icon { + border-radius: 4px; + width: 26px; + height: 26px; + border: none; + background-color: white; + background-image: url(images/geocoder.png); + background-repeat: no-repeat; + background-position: center; + cursor: pointer; +} + +.leaflet-touch .leaflet-control-geocoder-icon { + width: 30px; + height: 30px; +} + +.leaflet-control-geocoder-throbber .leaflet-control-geocoder-icon { + background-image: url(images/throbber.gif); +} + +.leaflet-control-geocoder-form-no-error { + display: none; +} + +.leaflet-control-geocoder-form input:focus { + outline: none; +} + +.leaflet-control-geocoder-form button { + display: none; +} +.leaflet-control-geocoder-error { + margin-top: 8px; + margin-left: 8px; + display: block; + color: #444; +} +.leaflet-control-geocoder-alternatives { + display: block; + width: 272px; + list-style: none; + padding: 0; + margin: 0; +} + +.leaflet-control-geocoder-alternatives-minimized { + display: none; + height: 0; +} +.leaflet-control-geocoder-alternatives li { + white-space: nowrap; + display: block; + overflow: hidden; + padding: 5px 8px; + text-overflow: ellipsis; + border-bottom: 1px solid #ccc; + cursor: pointer; +} + +.leaflet-control-geocoder-alternatives li a, +.leaflet-control-geocoder-alternatives li a:hover { + width: inherit; + height: inherit; + line-height: inherit; + background: inherit; + border-radius: inherit; + text-align: left; +} + +.leaflet-control-geocoder-alternatives li:last-child { + border-bottom: none; +} +.leaflet-control-geocoder-alternatives li:hover, +.leaflet-control-geocoder-selected { + background-color: #f5f5f5; +} +.leaflet-control-geocoder-address-detail { +} +.leaflet-control-geocoder-address-context { + color: #666; +} diff --git a/assets/css/images/geocoder.png b/assets/css/images/geocoder.png new file mode 100644 index 0000000000000000000000000000000000000000..d82a0170e62ccacac814cef91bbefdacc62071ca GIT binary patch literal 490 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"'`]/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 Nominatim = { + class: L.Class.extend({ + options: { + serviceUrl: 'https://nominatim.openstreetmap.org/', + geocodingQueryParams: {}, + reverseQueryParams: {}, + htmlTemplate: function(r) { + var a = r.address, + parts = []; + if (a.road || a.building) { + parts.push('{building} {road} {house_number}'); + } + + if (a.city || a.town || a.village || a.hamlet) { + parts.push( + '{postcode} {city} {town} {village} {hamlet}' + ); + } + + if (a.state || a.country) { + parts.push( + '{state} {country}' + ); + } + + return template(parts.join('
'), a, true); + } + }, + + initialize: function(options) { + L.Util.setOptions(this, options); + }, + + geocode: function(query, cb, context) { + getJSON( + this.options.serviceUrl + 'search', + L.extend( + { + q: query, + limit: 5, + format: 'json', + addressdetails: 1 + }, + this.options.geocodingQueryParams + ), + L.bind(function(data) { + var results = []; + for (var i = data.length - 1; i >= 0; i--) { + var bbox = data[i].boundingbox; + for (var j = 0; j < 4; j++) bbox[j] = parseFloat(bbox[j]); + results[i] = { + icon: data[i].icon, + name: data[i].display_name, + html: this.options.htmlTemplate ? this.options.htmlTemplate(data[i]) : undefined, + bbox: L.latLngBounds([bbox[0], bbox[2]], [bbox[1], bbox[3]]), + center: L.latLng(data[i].lat, data[i].lon), + properties: data[i] + }; + } + cb.call(context, results); + }, this) + ); + }, + + reverse: function(location, scale, cb, context) { + getJSON( + this.options.serviceUrl + 'reverse', + L.extend( + { + lat: location.lat, + lon: location.lng, + zoom: Math.round(Math.log(scale / 256) / Math.log(2)), + addressdetails: 1, + format: 'json' + }, + this.options.reverseQueryParams + ), + L.bind(function(data) { + var result = [], + loc; + + if (data && data.lat && data.lon) { + loc = L.latLng(data.lat, data.lon); + result.push({ + name: data.display_name, + html: this.options.htmlTemplate ? this.options.htmlTemplate(data) : undefined, + center: loc, + bounds: L.latLngBounds(loc, loc), + properties: data + }); + } + + cb.call(context, result); + }, this) + ); + } + }), + + factory: function(options) { + return new L.Control.Geocoder.Nominatim(options); + } +}; + +var Control = { + class: L.Control.extend({ + options: { + showResultIcons: false, + collapsed: true, + expand: 'touch', // options: touch, click, anythingelse + position: 'topright', + placeholder: 'Search...', + errorMessage: 'Nothing found.', + 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 Nominatim.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 Bing = { + class: L.Class.extend({ + initialize: function(key) { + this.key = key; + }, + + geocode: function(query, cb, context) { + jsonp( + 'https://dev.virtualearth.net/REST/v1/Locations', + { + query: query, + key: this.key + }, + function(data) { + var results = []; + if (data.resourceSets.length > 0) { + for (var i = data.resourceSets[0].resources.length - 1; i >= 0; i--) { + var resource = data.resourceSets[0].resources[i], + bbox = resource.bbox; + results[i] = { + name: resource.name, + bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]), + center: L.latLng(resource.point.coordinates) + }; + } + } + cb.call(context, results); + }, + this, + 'jsonp' + ); + }, + + reverse: function(location, scale, cb, context) { + jsonp( + '//dev.virtualearth.net/REST/v1/Locations/' + location.lat + ',' + location.lng, + { + key: this.key + }, + function(data) { + var results = []; + for (var i = data.resourceSets[0].resources.length - 1; i >= 0; i--) { + var resource = data.resourceSets[0].resources[i], + bbox = resource.bbox; + results[i] = { + name: resource.name, + bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]), + center: L.latLng(resource.point.coordinates) + }; + } + cb.call(context, results); + }, + this, + 'jsonp' + ); + } + }), + + factory: function(key) { + return new L.Control.Geocoder.Bing(key); + } +}; + +var MapQuest = { + class: L.Class.extend({ + options: { + serviceUrl: 'https://www.mapquestapi.com/geocoding/v1' + }, + + initialize: function(key, options) { + // MapQuest seems to provide URI encoded API keys, + // so to avoid encoding them twice, we decode them here + this._key = decodeURIComponent(key); + + L.Util.setOptions(this, options); + }, + + _formatName: function() { + var r = [], + i; + for (i = 0; i < arguments.length; i++) { + if (arguments[i]) { + r.push(arguments[i]); + } + } + + return r.join(', '); + }, + + geocode: function(query, cb, context) { + getJSON( + this.options.serviceUrl + '/address', + { + key: this._key, + location: query, + limit: 5, + outFormat: 'json' + }, + L.bind(function(data) { + var results = [], + loc, + latLng; + if (data.results && data.results[0].locations) { + for (var i = data.results[0].locations.length - 1; i >= 0; i--) { + loc = data.results[0].locations[i]; + latLng = L.latLng(loc.latLng); + results[i] = { + name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1), + bbox: L.latLngBounds(latLng, latLng), + center: latLng + }; + } + } + + cb.call(context, results); + }, this) + ); + }, + + reverse: function(location, scale, cb, context) { + getJSON( + this.options.serviceUrl + '/reverse', + { + key: this._key, + location: location.lat + ',' + location.lng, + outputFormat: 'json' + }, + L.bind(function(data) { + var results = [], + loc, + latLng; + if (data.results && data.results[0].locations) { + for (var i = data.results[0].locations.length - 1; i >= 0; i--) { + loc = data.results[0].locations[i]; + latLng = L.latLng(loc.latLng); + results[i] = { + name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1), + bbox: L.latLngBounds(latLng, latLng), + center: latLng + }; + } + } + + cb.call(context, results); + }, this) + ); + } + }), + + factory: function(key, options) { + return new L.Control.Geocoder.MapQuest(key, options); + } +}; + +var Mapbox = { + class: L.Class.extend({ + options: { + serviceUrl: 'https://api.tiles.mapbox.com/v4/geocode/mapbox.places-v1/', + geocodingQueryParams: {}, + reverseQueryParams: {} + }, + + initialize: function(accessToken, options) { + L.setOptions(this, options); + this.options.geocodingQueryParams.access_token = accessToken; + this.options.reverseQueryParams.access_token = accessToken; + }, + + geocode: function(query, cb, context) { + var params = this.options.geocodingQueryParams; + if ( + typeof params.proximity !== 'undefined' && + params.proximity.hasOwnProperty('lat') && + params.proximity.hasOwnProperty('lng') + ) { + params.proximity = params.proximity.lng + ',' + params.proximity.lat; + } + getJSON(this.options.serviceUrl + encodeURIComponent(query) + '.json', params, function( + data + ) { + var results = [], + loc, + latLng, + latLngBounds; + if (data.features && data.features.length) { + for (var i = 0; i <= data.features.length - 1; i++) { + loc = data.features[i]; + latLng = L.latLng(loc.center.reverse()); + if (loc.hasOwnProperty('bbox')) { + latLngBounds = L.latLngBounds( + L.latLng(loc.bbox.slice(0, 2).reverse()), + L.latLng(loc.bbox.slice(2, 4).reverse()) + ); + } else { + latLngBounds = L.latLngBounds(latLng, latLng); + } + results[i] = { + name: loc.place_name, + 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) { + getJSON( + this.options.serviceUrl + + encodeURIComponent(location.lng) + + ',' + + encodeURIComponent(location.lat) + + '.json', + this.options.reverseQueryParams, + function(data) { + var results = [], + loc, + latLng, + latLngBounds; + if (data.features && data.features.length) { + for (var i = 0; i <= data.features.length - 1; i++) { + loc = data.features[i]; + latLng = L.latLng(loc.center.reverse()); + if (loc.hasOwnProperty('bbox')) { + latLngBounds = L.latLngBounds( + L.latLng(loc.bbox.slice(0, 2).reverse()), + L.latLng(loc.bbox.slice(2, 4).reverse()) + ); + } else { + latLngBounds = L.latLngBounds(latLng, latLng); + } + results[i] = { + name: loc.place_name, + bbox: latLngBounds, + center: latLng + }; + } + } + + cb.call(context, results); + } + ); + } + }), + + factory: function(accessToken, options) { + return new L.Control.Geocoder.Mapbox(accessToken, options); + } +}; + +var What3Words = { + class: L.Class.extend({ + options: { + serviceUrl: 'https://api.what3words.com/v2/' + }, + + initialize: function(accessToken) { + this._accessToken = accessToken; + }, + + geocode: function(query, cb, context) { + //get three words and make a dot based string + getJSON( + this.options.serviceUrl + 'forward', + { + key: this._accessToken, + addr: query.split(/\s+/).join('.') + }, + function(data) { + var results = [], + latLng, + latLngBounds; + if (data.hasOwnProperty('geometry')) { + latLng = L.latLng(data.geometry['lat'], data.geometry['lng']); + latLngBounds = L.latLngBounds(latLng, latLng); + results[0] = { + name: data.words, + 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) { + getJSON( + this.options.serviceUrl + 'reverse', + { + key: this._accessToken, + coords: [location.lat, location.lng].join(',') + }, + function(data) { + var results = [], + latLng, + latLngBounds; + if (data.status.status == 200) { + latLng = L.latLng(data.geometry['lat'], data.geometry['lng']); + latLngBounds = L.latLngBounds(latLng, latLng); + results[0] = { + name: data.words, + bbox: latLngBounds, + center: latLng + }; + } + cb.call(context, results); + } + ); + } + }), + + factory: function(accessToken) { + return new L.Control.Geocoder.What3Words(accessToken); + } +}; + +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 Photon = { + class: L.Class.extend({ + options: { + serviceUrl: 'https://photon.komoot.de/api/', + reverseUrl: 'https://photon.komoot.de/reverse/', + nameProperties: ['name', 'street', 'suburb', 'hamlet', 'town', 'city', 'state', 'country'] + }, + + initialize: function(options) { + L.setOptions(this, options); + }, + + geocode: function(query, cb, context) { + var params = L.extend( + { + q: query + }, + this.options.geocodingQueryParams + ); + + getJSON( + this.options.serviceUrl, + params, + L.bind(function(data) { + cb.call(context, this._decodeFeatures(data)); + }, this) + ); + }, + + suggest: function(query, cb, context) { + return this.geocode(query, cb, context); + }, + + reverse: function(latLng, scale, cb, context) { + var params = L.extend( + { + lat: latLng.lat, + lon: latLng.lng + }, + this.options.reverseQueryParams + ); + + getJSON( + this.options.reverseUrl, + params, + L.bind(function(data) { + cb.call(context, this._decodeFeatures(data)); + }, this) + ); + }, + + _decodeFeatures: function(data) { + var results = [], + i, + f, + c, + latLng, + extent, + bbox; + + if (data && data.features) { + for (i = 0; i < data.features.length; i++) { + f = data.features[i]; + c = f.geometry.coordinates; + latLng = L.latLng(c[1], c[0]); + extent = f.properties.extent; + + if (extent) { + bbox = L.latLngBounds([extent[1], extent[0]], [extent[3], extent[2]]); + } else { + bbox = L.latLngBounds(latLng, latLng); + } + + results.push({ + name: this._deocodeFeatureName(f), + html: this.options.htmlTemplate ? this.options.htmlTemplate(f) : undefined, + center: latLng, + bbox: bbox, + properties: f.properties + }); + } + } + + return results; + }, + + _deocodeFeatureName: function(f) { + var j, name; + for (j = 0; !name && j < this.options.nameProperties.length; j++) { + name = f.properties[this.options.nameProperties[j]]; + } + + return name; + } + }), + + factory: function(options) { + return new L.Control.Geocoder.Photon(options); + } +}; + +var Mapzen = { + class: L.Class.extend({ + options: { + serviceUrl: 'https://search.mapzen.com/v1', + geocodingQueryParams: {}, + reverseQueryParams: {} + }, + + initialize: function(apiKey, options) { + L.Util.setOptions(this, options); + this._apiKey = apiKey; + this._lastSuggest = 0; + }, + + geocode: function(query, cb, context) { + var _this = this; + getJSON( + this.options.serviceUrl + '/search', + L.extend( + { + api_key: this._apiKey, + text: query + }, + this.options.geocodingQueryParams + ), + function(data) { + cb.call(context, _this._parseResults(data, 'bbox')); + } + ); + }, + + suggest: function(query, cb, context) { + var _this = this; + getJSON( + this.options.serviceUrl + '/autocomplete', + L.extend( + { + api_key: this._apiKey, + text: query + }, + this.options.geocodingQueryParams + ), + L.bind(function(data) { + if (data.geocoding.timestamp > this._lastSuggest) { + this._lastSuggest = data.geocoding.timestamp; + cb.call(context, _this._parseResults(data, 'bbox')); + } + }, this) + ); + }, + + reverse: function(location, scale, cb, context) { + var _this = this; + getJSON( + this.options.serviceUrl + '/reverse', + L.extend( + { + api_key: this._apiKey, + 'point.lat': location.lat, + 'point.lon': location.lng + }, + this.options.reverseQueryParams + ), + function(data) { + cb.call(context, _this._parseResults(data, 'bounds')); + } + ); + }, + + _parseResults: function(data, bboxname) { + var results = []; + L.geoJson(data, { + pointToLayer: function(feature, latlng) { + return L.circleMarker(latlng); + }, + onEachFeature: function(feature, layer) { + var result = {}, + bbox, + center; + + if (layer.getBounds) { + bbox = layer.getBounds(); + center = bbox.getCenter(); + } else { + center = layer.getLatLng(); + bbox = L.latLngBounds(center, center); + } + + result.name = layer.feature.properties.label; + result.center = center; + result[bboxname] = bbox; + result.properties = layer.feature.properties; + results.push(result); + } + }); + return results; + } + }), + + factory: function(apiKey, options) { + return new L.Control.Geocoder.Mapzen(apiKey, 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 HERE = { + class: L.Class.extend({ + options: { + geocodeUrl: 'http://geocoder.api.here.com/6.2/geocode.json', + reverseGeocodeUrl: 'http://reverse.geocoder.api.here.com/6.2/reversegeocode.json', + app_id: '', + app_code: '', + geocodingQueryParams: {}, + reverseQueryParams: {} + }, + + initialize: function(options) { + L.setOptions(this, options); + }, + + geocode: function(query, cb, context) { + var params = { + searchtext: query, + gen: 9, + app_id: this.options.app_id, + app_code: this.options.app_code, + jsonattributes: 1 + }; + params = L.Util.extend(params, this.options.geocodingQueryParams); + this.getJSON(this.options.geocodeUrl, params, cb, context); + }, + + reverse: function(location, scale, cb, context) { + var params = { + prox: encodeURIComponent(location.lat) + ',' + encodeURIComponent(location.lng), + mode: 'retrieveAddresses', + app_id: this.options.app_id, + app_code: this.options.app_code, + gen: 9, + jsonattributes: 1 + }; + params = L.Util.extend(params, this.options.reverseQueryParams); + this.getJSON(this.options.reverseGeocodeUrl, params, cb, context); + }, + + getJSON: function(url, params, cb, context) { + getJSON(url, params, function(data) { + var results = [], + loc, + latLng, + latLngBounds; + if (data.response.view && data.response.view.length) { + for (var i = 0; i <= data.response.view[0].result.length - 1; i++) { + loc = data.response.view[0].result[i].location; + latLng = L.latLng(loc.displayPosition.latitude, loc.displayPosition.longitude); + latLngBounds = L.latLngBounds( + L.latLng(loc.mapView.topLeft.latitude, loc.mapView.topLeft.longitude), + L.latLng(loc.mapView.bottomRight.latitude, loc.mapView.bottomRight.longitude) + ); + results[i] = { + name: loc.address.label, + bbox: latLngBounds, + center: latLng + }; + } + } + cb.call(context, results); + }); + } + }), + + factory: function(options) { + return new L.Control.Geocoder.HERE(options); + } +}; + +var Geocoder = L.Util.extend(Control.class, { + Nominatim: Nominatim.class, + nominatim: Nominatim.factory, + Bing: Bing.class, + bing: Bing.factory, + MapQuest: MapQuest.class, + mapQuest: MapQuest.factory, + Mapbox: Mapbox.class, + mapbox: Mapbox.factory, + What3Words: What3Words.class, + what3words: What3Words.factory, + Google: Google.class, + google: Google.factory, + Photon: Photon.class, + photon: Photon.factory, + Mapzen: Mapzen.class, + mapzen: Mapzen.factory, + ArcGis: ArcGis.class, + arcgis: ArcGis.factory, + HERE: HERE.class, + here: HERE.factory +}); + +L.Util.extend(L.Control, { + Geocoder: Geocoder, + geocoder: Control.factory +}); + +return Geocoder; + +}(L)); +//# sourceMappingURL=Control.Geocoder.js.map diff --git a/includes/admin/class-sp-admin-assets.php b/includes/admin/class-sp-admin-assets.php index 86129ced..e40d0baa 100755 --- a/includes/admin/class-sp-admin-assets.php +++ b/includes/admin/class-sp-admin-assets.php @@ -37,7 +37,9 @@ class SP_Admin_Assets { if ( in_array( $screen->id, sp_get_screen_ids() ) ) { // Admin styles for SP pages only wp_enqueue_style( 'jquery-chosen', SP()->plugin_url() . '/assets/css/chosen.css', array(), '1.1.0' ); + //OpenStreetMaps wp_enqueue_style( 'leaflet_stylesheet', SP()->plugin_url() . '/assets/css/leaflet.css', array(), '1.4.0' ); + wp_enqueue_style( 'control-geocoder', SP()->plugin_url() . '/assets/css/Control.Geocoder.css', array() ); wp_enqueue_style( 'wp-color-picker' ); wp_enqueue_style( 'sportspress-admin', SP()->plugin_url() . '/assets/css/admin.css', array(), SP_VERSION ); } elseif ( strpos( $screen->id, 'sportspress-config' ) !== false ) { @@ -85,12 +87,15 @@ class SP_Admin_Assets { wp_register_script( 'jquery-fitvids', SP()->plugin_url() . '/assets/js/jquery.fitvids.js', array( 'jquery' ), '1.1', true ); - wp_register_script( 'google-maps', '//tboy.co/maps_js' ); + //wp_register_script( 'google-maps', '//tboy.co/maps_js' ); + + //OpenStreetMaps wp_register_script( 'leaflet_js', SP()->plugin_url() . '/assets/js/leaflet.js', array(), '1.4.0' ); + wp_register_script( 'control-geocoder', SP()->plugin_url() . '/assets/js/Control.Geocoder.js', array( 'leaflet_js' ) ); - wp_register_script( 'jquery-locationpicker', SP()->plugin_url() . '/assets/js/locationpicker.jquery.js', array( 'jquery', 'google-maps' ), '0.1.6', true ); + //wp_register_script( 'jquery-locationpicker', SP()->plugin_url() . '/assets/js/locationpicker.jquery.js', array( 'jquery', 'google-maps' ), '0.1.6', true ); - wp_register_script( 'sportspress-admin-locationpicker', SP()->plugin_url() . '/assets/js/admin/locationpicker.js', array( 'jquery', 'google-maps', 'jquery-locationpicker' ), SP_VERSION, true ); + //wp_register_script( 'sportspress-admin-locationpicker', SP()->plugin_url() . '/assets/js/admin/locationpicker.js', array( 'jquery', 'google-maps', 'jquery-locationpicker' ), SP_VERSION, true ); wp_register_script( 'sportspress-admin-equationbuilder', SP()->plugin_url() . '/assets/js/admin/equationbuilder.js', array( 'jquery', 'jquery-ui-core', 'jquery-ui-draggable', 'jquery-ui-droppable' ), SP_VERSION, true ); @@ -134,8 +139,9 @@ class SP_Admin_Assets { // Edit venue pages if ( in_array( $screen->id, array( 'edit-sp_venue' ) ) ) { - wp_enqueue_script( 'google-maps' ); + //wp_enqueue_script( 'google-maps' ); wp_enqueue_script( 'leaflet_js' ); + wp_enqueue_script( 'control-geocoder' ); wp_enqueue_script( 'jquery-locationpicker' ); wp_enqueue_script( 'sportspress-admin-locationpicker' ); } diff --git a/includes/admin/class-sp-admin-taxonomies.php b/includes/admin/class-sp-admin-taxonomies.php index 3e30360e..fdcd1f2c 100644 --- a/includes/admin/class-sp-admin-taxonomies.php +++ b/includes/admin/class-sp-admin-taxonomies.php @@ -113,9 +113,9 @@ class SP_Admin_Taxonomies { endif; ?>
- - -

+ +

@@ -124,15 +124,62 @@ class SP_Admin_Taxonomies { var lat = ; var lon = ; // initialize map - map = L.map('mapDiv').setView([lat, lon], 15); + + var map = L.map('mapDiv').setView([lat, lon], 15); // set map tiles source L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: 'Map data © OpenStreetMap contributors', maxZoom: 15, }).addTo(map); + + //L.Control.geocoder().addTo(map); + + var geocoder = L.Control.geocoder({ + defaultMarkGeocode: true + }) + .on('markgeocode', function(e) { + var center = e.geocode.center; + document.getElementById('term_meta[sp_latitude]').value = center.lat; + document.getElementById('term_meta[sp_longitude]').value = center.lng; + }) + .addTo(map); + // add marker to the map marker = L.marker([lat, lon],{draggable: true, autoPan: true}).addTo(map); + //get new coordinates and pass them to the fields + marker.on('dragend', function (e) { + document.getElementById('term_meta[sp_latitude]').value = marker.getLatLng().lat; + document.getElementById('term_meta[sp_longitude]').value = marker.getLatLng().lng; + }); +