From cbae1d4496f9dcdf76f5dbcc20e6d98189301d61 Mon Sep 17 00:00:00 2001 From: Nikolaus Krismer <niko@krismer.de> Date: Fri, 14 Feb 2014 17:54:41 +0100 Subject: [PATCH] now using requireJS to load js files (can be optimized using r.js for releases later on) --- src/main/webapp/index.html | 28 +- src/main/webapp/js/app.js | 46 ++ src/main/webapp/js/guidelines.txt | 13 + src/main/webapp/js/isochrone/configuration.js | 51 ++- src/main/webapp/js/isochrone/initHelper.js | 112 ++--- .../webapp/js/isochrone/searchExtender.js | 132 +++--- src/main/webapp/js/{ => lib}/console.js | 0 .../{ => lib}/jquery-ui-timepicker-addon.js | 0 src/main/webapp/js/map/bing.js | 117 ----- src/main/webapp/js/map/control/geosearch.js | 412 +++++++++++++++++ .../js/map/control/geosearchProvider/osm.js | 97 ++++ src/main/webapp/js/map/control/help.js | 25 + src/main/webapp/js/map/control/settings.js | 25 + src/main/webapp/js/map/controls.js | 43 -- src/main/webapp/js/map/geosearch.js | 429 ------------------ .../webapp/js/map/geosearch_provider_osm.js | 81 ---- src/main/webapp/js/map/google.js | 176 ------- src/main/webapp/js/map/ipLocator/ipLocator.js | 53 +++ .../webapp/js/map/ipLocator/ipProvider.js | 59 +++ .../webapp/js/map/ipLocator/mapIncluder.js | 49 ++ src/main/webapp/js/map/isoMap.js | 225 +++++++++ src/main/webapp/js/map/isomap.js | 212 --------- src/main/webapp/js/map/layer/bing.js | 125 +++++ src/main/webapp/js/map/layer/google.js | 201 ++++++++ src/main/webapp/js/map/locatebyip.js | 161 ------- .../webapp/js/service/serviceConfiguration.js | 124 ++--- src/main/webapp/js/service/websocket.js | 106 ++--- src/main/webapp/js/versions.txt | 2 +- 28 files changed, 1611 insertions(+), 1493 deletions(-) create mode 100644 src/main/webapp/js/app.js create mode 100644 src/main/webapp/js/guidelines.txt rename src/main/webapp/js/{ => lib}/console.js (100%) rename src/main/webapp/js/{ => lib}/jquery-ui-timepicker-addon.js (100%) delete mode 100644 src/main/webapp/js/map/bing.js create mode 100644 src/main/webapp/js/map/control/geosearch.js create mode 100644 src/main/webapp/js/map/control/geosearchProvider/osm.js create mode 100644 src/main/webapp/js/map/control/help.js create mode 100644 src/main/webapp/js/map/control/settings.js delete mode 100644 src/main/webapp/js/map/controls.js delete mode 100644 src/main/webapp/js/map/geosearch.js delete mode 100644 src/main/webapp/js/map/geosearch_provider_osm.js delete mode 100644 src/main/webapp/js/map/google.js create mode 100644 src/main/webapp/js/map/ipLocator/ipLocator.js create mode 100644 src/main/webapp/js/map/ipLocator/ipProvider.js create mode 100644 src/main/webapp/js/map/ipLocator/mapIncluder.js create mode 100644 src/main/webapp/js/map/isoMap.js delete mode 100644 src/main/webapp/js/map/isomap.js create mode 100644 src/main/webapp/js/map/layer/bing.js create mode 100644 src/main/webapp/js/map/layer/google.js delete mode 100644 src/main/webapp/js/map/locatebyip.js diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index 6d980d68..f2cec7f7 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -14,34 +14,8 @@ <link type="text/css" rel="stylesheet" href="http://domoritz.de/leaflet-locatecontrol/src/L.Control.Locate.ie.css" /> <![endif]--> + <script data-main="js/app" src="js/lib/require.js"></script> <script type="text/javascript" src="http://maps.google.com/maps/api/js?v=3&sensor=false"></script> - <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.0.min.js"></script> - <script type="text/javascript" src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script> - <script type="text/javascript" src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script> - <script type="text/javascript" src="http://maps.stamen.com/js/tile.stamen.js"></script> - <script type="text/javascript" src="http://domoritz.de/leaflet-locatecontrol/src/L.Control.Locate.js"></script> - <script type="text/javascript" src="js/console.js"></script> - <script type="text/javascript" src="js/jquery-ui-timepicker-addon.js"></script> - <script type="text/javascript" src="js/map/locatebyip.js"></script> - <script type="text/javascript" src="js/map/geosearch.js"></script> - <script type="text/javascript" src="js/map/geosearch_provider_osm.js"></script> - <script type="text/javascript" src="js/map/controls.js"></script> - <script type="text/javascript" src="js/map/bing.js"></script> - <script type="text/javascript" src="js/map/google.js"></script> - <script type="text/javascript" src="js/map/isomap.js"></script> - <script type="text/javascript" src="js/service/serviceConfiguration.js"></script> - <script type="text/javascript" src="js/service/websocket.js"></script> - <script type="text/javascript" src="js/isochrone/configuration.js"></script> - <script type="text/javascript" src="js/isochrone/initHelper.js"></script> - <script type="text/javascript" src="js/isochrone/searchExtender.js"></script> - <script type="text/javascript"> - $(function() { - var h = new InitHelper(); - - h.initPreferences(); - h.initMap(); - }); - </script> </head> <body> diff --git a/src/main/webapp/js/app.js b/src/main/webapp/js/app.js new file mode 100644 index 00000000..e628c5dd --- /dev/null +++ b/src/main/webapp/js/app.js @@ -0,0 +1,46 @@ +require.config({ + baseUrl: 'js', + paths: { + 'console': 'lib/console', + 'jQuery': '//code.jquery.com/jquery-2.1.0.min', + 'jQueryUI': '//code.jquery.com/ui/1.10.4/jquery-ui', + 'jQueryUI-timepicker': 'lib/jquery-ui-timepicker-addon', + 'leaflet': '//cdn.leafletjs.com/leaflet-0.7.2/leaflet', + 'locateControl': '//domoritz.de/leaflet-locatecontrol/src/L.Control.Locate', + 'stamenMap': '//maps.stamen.com/js/tile.stamen' + }, + shim: { + 'console': { + exports: 'console' + }, + 'jQuery': { + exports: '$' + }, + 'jQueryUI': { + deps: ['jQuery'], + }, + 'jQueryUI-timepicker': { + deps: ['jQueryUI'], + }, + 'leaflet': { + exports: 'L' + }, + 'jquery-ui-timepicker-addon': { + deps: ['jQueryUI'] + }, + 'locateControl': { + deps: ['leaflet'] + }, + 'stamenMap': { + deps: ['leaflet'] + } + } +}); + +require(['jQuery', 'isochrone/initHelper', 'locateControl', 'map/ipLocator/mapIncluder'], function($, InitHelper) { + $(function() { + var h = new InitHelper(); + h.initPreferences(); + h.initMap(); + }); +}); diff --git a/src/main/webapp/js/guidelines.txt b/src/main/webapp/js/guidelines.txt new file mode 100644 index 00000000..a335dafa --- /dev/null +++ b/src/main/webapp/js/guidelines.txt @@ -0,0 +1,13 @@ +JS coding guidelines: +--------------------- + + - Use jsDoc and document your methods + - Use the gradle task "jsDoc" to check the code documentation + - Use the gradle task "jsHint" to check the coding styleguide + +Class Design: +------------- +https://gist.github.com/lucastan/5421897 + +=> Pattern 1: Self-construction used and applied to AMD +(for a documented example see IsoMap class in js/map/isoMap.js) diff --git a/src/main/webapp/js/isochrone/configuration.js b/src/main/webapp/js/isochrone/configuration.js index 05bd67f4..6d54ed3e 100644 --- a/src/main/webapp/js/isochrone/configuration.js +++ b/src/main/webapp/js/isochrone/configuration.js @@ -2,31 +2,38 @@ * Singleton class configuration. * This is used to store the client's configuration */ -function Configuration() { - if (arguments.callee._singletonInstance) { - return arguments.callee._singletonInstance; - } - arguments.callee._singletonInstance = this; +define(['console'], function(logger) { + instance = null; - var datasetConfigMap = {}; + function Configuration() { + var datasetConfigMap = {}; - this.addDatasetConfig = function(dSetConfig) { - if (!dSetConfig || !dSetConfig.datasetName) { - console.warn('Not adding invalid dataset configuration to client configuration singleton'); - console.debug(' - dSetConfig given:', dSetConfig); - return; - } + this.addDatasetConfig = function(dSetConfig) { + if (!dSetConfig || !dSetConfig.datasetName) { + logger.warn('Not adding invalid dataset configuration to client configuration singleton'); + logger.debug(' - dSetConfig given:', dSetConfig); + return; + } - datasetConfigMap[dSetConfig.datasetName] = dSetConfig; - }; + datasetConfigMap[dSetConfig.datasetName] = dSetConfig; + }; - this.getDatasetConfigMap = function() { - return datasetConfigMap; - }; + this.getDatasetConfigMap = function() { + return datasetConfigMap; + }; - this.getDatasetConfig = function(datasetName) { - return datasetConfigMap[datasetName]; - }; -}; + this.getDatasetConfig = function(datasetName) { + return datasetConfigMap[datasetName]; + }; + } + + return { + getInstance: function() { + if (instance == null) { + instance = new Configuration(); + } -new Configuration(); + return instance; + } + }; +}); diff --git a/src/main/webapp/js/isochrone/initHelper.js b/src/main/webapp/js/isochrone/initHelper.js index a50e92d9..b5e64053 100644 --- a/src/main/webapp/js/isochrone/initHelper.js +++ b/src/main/webapp/js/isochrone/initHelper.js @@ -1,55 +1,61 @@ -function InitHelper() { - var config = null; - var isoMap = null; - var ws = null; - - // Public methods - - this.initMap = function() { - isoMap = new IsoMap('map'); - isoMap.locateAndDraw(); - - ws = new Websocket(); - config = new ServiceConfiguration(ws); - config.getFromServer(); - - $(document).on('isomap_draw', extendSearch.bind(this)); +define(['jQuery', 'console', 'map/isoMap', 'service/websocket', 'service/serviceConfiguration', 'isochrone/searchExtender', 'jQueryUI', 'jQueryUI-timepicker'], function($, logger, IsoMap, Websocket, ServiceConfiguration, SearchExtender) { + function InitHelper() { + var config = null; + var isoMap = null; + var ws = null; + + // Public methods + + this.initMap = function() { + logger.debug('Initializing map'); + + isoMap = new IsoMap('map'); + isoMap.locateAndDraw(); + + ws = new Websocket(); + config = new ServiceConfiguration(ws); + config.getFromServer(); + + $(document).on('isomap_draw', extendSearch.bind(this)); + }; + + this.initPreferences = function() { + $('#help-dialog').dialog({ + autoOpen: false, + modal: true, + resizable: false + }); + + $('#settings-dialog').dialog({ + autoOpen: false, + modal: true, + resizable: false + }); + + $('#speed').spinner({ + step: 0.1, + numberFormat: 'n' + }); + + $('#settings-dialog select').css({'width':'100%'}); + $('#settings-dialog input').css({'width':'100%'}); + $('#datetime').datetimepicker({ + dateFormat: 'dd.mm.yy', + timeFormat: 'HH:mm' + }); + $('#datetime .ui-datepicker-today').click(); + }; + + // Private methods + + extendSearch = function() { + var mapElem = isoMap.getMap(), + searchExtender; + + searchExtender = new SearchExtender(mapElem); + searchExtender.extendResult(isoMap.getMap()); + }; }; - this.initPreferences = function() { - $('#help-dialog').dialog({ - autoOpen: false, - modal: true, - resizable: false - }); - - $('#settings-dialog').dialog({ - autoOpen: false, - modal: true, - resizable: false - }); - - $('#speed').spinner({ - step: 0.1, - numberFormat: 'n' - }); - - $('#settings-dialog select').css({'width':'100%'}); - $('#settings-dialog input').css({'width':'100%'}); - $('#datetime').datetimepicker({ - dateFormat: 'dd.mm.yy', - timeFormat: 'HH:mm' - }); - $('#datetime .ui-datepicker-today').click(); - }; - - // Private methods - - extendSearch = function() { - var mapElem = isoMap.getMap(), - searchExtender; - - searchExtender = new SearchExtender(mapElem); - searchExtender.extendResult(isoMap.getMap()); - }; -}; \ No newline at end of file + return InitHelper; +}); \ No newline at end of file diff --git a/src/main/webapp/js/isochrone/searchExtender.js b/src/main/webapp/js/isochrone/searchExtender.js index 59b95d96..8d08ed42 100644 --- a/src/main/webapp/js/isochrone/searchExtender.js +++ b/src/main/webapp/js/isochrone/searchExtender.js @@ -1,76 +1,88 @@ -function SearchExtender(map) { - var geosearchEventName = 'geosearch_showresult'; - var m = null; +define(['jQuery', 'map/isoMap', 'isochrone/configuration'], function($, isoMap, Configuration) { + function SearchExtender(map) { + var geosearchEventName = 'geosearch_showresult'; + var m = null; - // Constructors + // Constructors - m = map; + m = map; - // Public methods + this.extendResult = function() { + m.on(geosearchEventName, onSearchResult.bind(this)); + }; - this.extendResult = function() { - m.on(geosearchEventName, onSearchResult.bind(this)); - }; + // Private methods - // Private methods + getDSetConfigs = function(point) { + var config = Configuration.getInstance(), + configCandidates = [], + datasetConfigs = config.getDatasetConfigMap(); - isochroneAvailable = function(point) { - var config = Configuration(), - configCandidates = [], - datasetConfigs = config.getDatasetConfigMap(); + for (var dSetConfigName in datasetConfigs) { + var dSetConfig = datasetConfigs[dSetConfigName]; + if (dSetConfig.latLngBBox.contains(point)) { + configCandidates[configCandidates.length] = dSetConfig; + } + } - for (var dSetConfigName in datasetConfigs) { - var dSetConfig = datasetConfigs[dSetConfigName]; - if (dSetConfig.latLngBBox.contains(point)) { - configCandidates[configCandidates.length] = dSetConfig; + // Check if only one datasets bbox contains the query point. + // If so... return the matching dataset config + var resultLength = configCandidates.length; + if (resultLength == 0) { + return null; } - } - - // Check if only one datasets bbox contains the query point. - // If so... return the matching dataset config - var resultLength = configCandidates.length; - if (resultLength == 1) { - // TODO: Should we combine this with the dataset dropdown in the settings? - // Should we remove the combobox? - return configCandidates[0]; - } - - // at least two datasets are available for isochrone configuration - - // FIXME: How do we handle this? Should we combine this with the dataset dropdown in the settings (if so... how) - // By now we use the smallest dataset containing the point - var tmpCandidate = configCandidates[0]; - var tmpSize = tmpCandidate.bBox.getSize(); - for (var i = 1; i < resultLength; ++i) { - var cSize = configCandidates[i].bBox.getSize(); - if (cSize.x * cSize.y < tmpSize.x * tmpSize.y) { - tmpCandidate = configCandidates[i]; - tmpSize = cSize; + + if (resultLength == 1) { + // TODO: Should we combine this with the dataset dropdown in the settings? + // Should we remove the combobox? + return configCandidates[0]; } - } - return tmpCandidate; - }; + // at least two datasets are available for isochrone configuration + + // FIXME: How do we handle this? Should we combine this with the dataset dropdown in the settings (if so... how) + // By now we use the smallest dataset containing the point + var tmpCandidate = configCandidates[0]; + var tmpSize = tmpCandidate.bBox.getSize(); + for (var i = 1; i < resultLength; ++i) { + var cSize = configCandidates[i].bBox.getSize(); + if (cSize.x * cSize.y < tmpSize.x * tmpSize.y) { + tmpCandidate = configCandidates[i]; + tmpSize = cSize; + } + } - onSearchResult = function(data) { - var divIcons, - iconIsochrone = null, - li; + return tmpCandidate; + }; - if (!data.queryPoint || !isochroneAvailable(data.queryPoint)) { - return; - } + onSearchResult = function(data) { + var divIcons, + dSetConfigs = null, + iconIsochrone = null, + li; - divIcons = document.createElement('div'); - li = $(data.element).find('li:first div'); + if (!data.queryPoint) { + return; + } + + dSetConfigs = getDSetConfigs(data.queryPoint); + if (!dSetConfigs) { + return; + } - iconIsochrone = document.createElement('div'); - iconIsochrone.id = 'icon-isochrone'; - iconIsochrone.className = 'icon-isochrone'; - iconIsochrone.appendChild(document.createTextNode('Isochrone')); - divIcons.className = 'geosearch-result-icons'; - divIcons.appendChild(iconIsochrone); + divIcons = document.createElement('div'); + li = $(data.element).find('li:first div'); - $(divIcons).insertBefore(li); + iconIsochrone = document.createElement('div'); + iconIsochrone.id = 'icon-isochrone'; + iconIsochrone.className = 'icon-isochrone'; + iconIsochrone.appendChild(document.createTextNode('Isochrone')); + divIcons.className = 'geosearch-result-icons'; + divIcons.appendChild(iconIsochrone); + + $(divIcons).insertBefore(li); + }; }; -}; + + return SearchExtender; +}); diff --git a/src/main/webapp/js/console.js b/src/main/webapp/js/lib/console.js similarity index 100% rename from src/main/webapp/js/console.js rename to src/main/webapp/js/lib/console.js diff --git a/src/main/webapp/js/jquery-ui-timepicker-addon.js b/src/main/webapp/js/lib/jquery-ui-timepicker-addon.js similarity index 100% rename from src/main/webapp/js/jquery-ui-timepicker-addon.js rename to src/main/webapp/js/lib/jquery-ui-timepicker-addon.js diff --git a/src/main/webapp/js/map/bing.js b/src/main/webapp/js/map/bing.js deleted file mode 100644 index 03f8845b..00000000 --- a/src/main/webapp/js/map/bing.js +++ /dev/null @@ -1,117 +0,0 @@ -L.BingLayer = L.TileLayer.extend({ - options: { - subdomains: [0, 1, 2, 3], - type: 'Aerial', - attribution: 'Bing', - culture: '' - }, - - initialize: function(key, options) { - L.Util.setOptions(this, options); - - this._key = key; - this._url = null; - this.meta = {}; - this.loadMetadata(); - }, - - tile2quad: function(x, y, z) { - var quad = ''; - for (var i = z; i > 0; i--) { - var digit = 0; - var mask = 1 << (i - 1); - if ((x & mask) != 0) digit += 1; - if ((y & mask) != 0) digit += 2; - quad = quad + digit; - } - return quad; - }, - - getTileUrl: function(p, z) { - var z = this._getZoomForUrl(); - var subdomains = this.options.subdomains, - s = this.options.subdomains[Math.abs((p.x + p.y) % subdomains.length)]; - return this._url.replace('{subdomain}', s) - .replace('{quadkey}', this.tile2quad(p.x, p.y, z)) - .replace('{culture}', this.options.culture); - }, - - loadMetadata: function() { - var _this = this; - var cbid = '_bing_metadata_' + L.Util.stamp(this); - window[cbid] = function (meta) { - _this.meta = meta; - window[cbid] = undefined; - var e = document.getElementById(cbid); - e.parentNode.removeChild(e); - if (meta.errorDetails) { - alert("Got metadata" + meta.errorDetails); - return; - } - _this.initMetadata(); - }; - var url = "http://dev.virtualearth.net/REST/v1/Imagery/Metadata/" + this.options.type + "?include=ImageryProviders&jsonp=" + cbid + "&key=" + this._key; - var script = document.createElement("script"); - script.type = "text/javascript"; - script.src = url; - script.id = cbid; - document.getElementsByTagName("head")[0].appendChild(script); - }, - - initMetadata: function() { - var r = this.meta.resourceSets[0].resources[0]; - this.options.subdomains = r.imageUrlSubdomains; - this._url = r.imageUrl; - this._providers = []; - for (var i = 0; i < r.imageryProviders.length; i++) { - var p = r.imageryProviders[i]; - for (var j = 0; j < p.coverageAreas.length; j++) { - var c = p.coverageAreas[j]; - var coverage = {zoomMin: c.zoomMin, zoomMax: c.zoomMax, active: false}; - var bounds = new L.LatLngBounds( - new L.LatLng(c.bbox[0]+0.01, c.bbox[1]+0.01), - new L.LatLng(c.bbox[2]-0.01, c.bbox[3]-0.01) - ); - coverage.bounds = bounds; - coverage.attrib = p.attribution; - this._providers.push(coverage); - } - } - this._update(); - }, - - _update: function() { - if (this._url == null || !this._map) return; - this._update_attribution(); - L.TileLayer.prototype._update.apply(this, []); - }, - - _update_attribution: function() { - var bounds = this._map.getBounds(); - var zoom = this._map.getZoom(); - for (var i = 0; i < this._providers.length; i++) { - var p = this._providers[i]; - if ((zoom <= p.zoomMax && zoom >= p.zoomMin) && - bounds.intersects(p.bounds)) { - if (!p.active) - this._map.attributionControl.addAttribution(p.attrib); - p.active = true; - } else { - if (p.active) - this._map.attributionControl.removeAttribution(p.attrib); - p.active = false; - } - } - }, - - onRemove: function(map) { - for (var i = 0; i < this._providers.length; i++) { - var p = this._providers[i]; - if (p.active) { - this._map.attributionControl.removeAttribution(p.attrib); - p.active = false; - } - } - L.TileLayer.prototype.onRemove.apply(this, [map]); - } -}); \ No newline at end of file diff --git a/src/main/webapp/js/map/control/geosearch.js b/src/main/webapp/js/map/control/geosearch.js new file mode 100644 index 00000000..0e65ce23 --- /dev/null +++ b/src/main/webapp/js/map/control/geosearch.js @@ -0,0 +1,412 @@ +/** + * GeoSearch - search for an address and zoom to its location + * + * @see https://github.com/smeijer/leaflet.control.geosearch + */ +define(['leaflet'], function(L) { + L.Control.GeoSearch = L.Control.extend({ + options: { + doReverseLookup: false, + position: 'topleft', + provider: null, + showMarker: true + }, + + _config: { + country: '', + searchLabel: 'Search for address ...', + notFoundMessage: 'Sorry, that address could not be found.', + messageHideDelay: 3000, + zoomLevel: 18 + }, + + // Public methods + + initialize: function (options) { + L.Util.extend(this.options, options); + L.Util.extend(this._config, options); + }, + + geosearch: function (qry) { + try { + var provider = this._config.provider; + + if(typeof provider.GetLocations == 'function') { + provider.GetLocations(qry, function(results) { + this._processResults(results); + }.bind(this)); + } else { + var url = provider.GetServiceUrl(qry); + this.sendRequest(provider, url); + } + } catch (error) { + this._printError(error); + } + }, + + onAdd: function (map) { + this._map = map; + this._container = L.DomUtil.create('div', 'leaflet-control-geosearch'); + this._suggestSet = []; + + var searchdiv = document.createElement('div'); + searchdiv.id = 'leaflet-control-geosearch-searchdiv'; + searchdiv.className = 'leaflet-control-geosearch-searchdiv'; + this._searchdiv = searchdiv; + + var searchbox = document.createElement('input'); + searchbox.id = 'leaflet-control-geosearch-qry'; + searchbox.type = 'text'; + searchbox.placeholder = this._config.searchLabel; + this._searchbox = searchbox; + + var searchbtn = document.createElement('button'); + searchbtn.id = 'leaflet-control-geosearch-btn'; + searchbtn.className = 'leaflet-control-geosearch-btn'; + this._searchbtn = searchbtn; + + var msgbox = document.createElement('div'); + msgbox.id = 'leaflet-control-geosearch-msg'; + msgbox.className = 'leaflet-control-geosearch-msg'; + this._msgbox = msgbox; + + var result = document.createElement('ul'); + result.id = 'leaflet-control-geosearch-result'; + result.className = 'leaflet-control-geosearch-result'; + this._result = result; + + var suggestlist = document.createElement('ul'); + suggestlist.id = 'leaflet-control-geosearch-suggests'; + suggestlist.className = 'leaflet-control-geosearch-suggests'; + this._suggestlist = suggestlist; + + this._msgbox.appendChild(this._suggestlist); + this._msgbox.appendChild(this._result); + this._searchdiv.appendChild(this._searchbtn); + this._searchdiv.appendChild(this._searchbox); + this._container.appendChild(this._searchdiv); + this._container.appendChild(this._msgbox); + + L.DomEvent + .addListener(this._container, 'click', L.DomEvent.stop) + .addListener(this._searchbtn, 'click', this._onSearchClick, this) + .addListener(this._searchbox, 'keypress', this._onKeyUp, this); + + if (this._config.doReverseLookup && this._config.provider.options.reverseable) { + L.DomEvent.addListener(this._map, 'click', this._onMapClick, this); + } + + L.DomEvent.disableClickPropagation(this._container); + + return this._container; + }, + + sendRequest: function (provider, url) { + var that = this; + + window.parseLocation = function (response) { + var results = provider.ParseJSON(response); + that._processResults(results); + + document.body.removeChild(document.getElementById('getJsonP')); + delete window.parseLocation; + }; + + function getJsonP (url) { + url = url + '&callback=parseLocation'; + var script = document.createElement('script'); + script.id = 'getJsonP'; + script.src = url; + script.async = true; + document.body.appendChild(script); + } + + if (XMLHttpRequest) { + var xhr = new XMLHttpRequest(); + + if ('withCredentials' in xhr) { + var xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + if (xhr.status == 200) { + var response = JSON.parse(xhr.responseText), + results = provider.ParseJSON(response); + + that._processResults(results); + } else if (xhr.status == 0 || xhr.status == 400) { + getJsonP(url); + } else { + that._printError(xhr.responseText); + } + } + }; + + xhr.open('GET', url, true); + xhr.send(); + } else if (XDomainRequest) { + var xdr = new XDomainRequest(); + + xdr.onerror = function (err) { + that._printError(err); + }; + + xdr.onload = function () { + var response = JSON.parse(xdr.responseText), + results = provider.ParseJSON(response); + + that._processResults(results); + }; + + xdr.open('GET', url); + xdr.send(); + } else { + getJsonP(url); + } + } + }, + + // Private methods + + _addressToString : function(address) { + if (!address) { + return ''; + } + + var addressStr = ''; + if (address.road) addressStr += address.road + ', '; + if (address.postcode) addressStr += address.postcode + ', '; + if (address.country) addressStr += address.country; + if (/, $/.test(addressStr)) addressStr = addressStr.substring(0, addressStr.length - 2); + + return addressStr; + }, + + _clearResult : function() { + if (this._timeout != null) { + clearTimeout(this._timeout); + this._timeout = null; + } + + var elem = this._result; + elem.innerHTML = ''; + elem.style.display = 'none'; + }, + + _clearSuggestList : function() { + this._suggestSet = []; + if (this._timeout != null) { + clearTimeout(this._timeout); + this._timeout = null; + } + + var elem = this._suggestlist; + elem.innerHTML = ''; + elem.style.display = 'none'; + }, + + _isInSuggestSet : function(o) { + var i = 0, + set = this._suggestSet; + + for (i = 0; i < set.length; ++i) { + if (set[i].lat == o.lat && set[i].lon == o.lon) { + return true; + } + } + + return false; + }, + + _onKeyUp: function (e) { + var esc = 27, + enter = 13, + queryBox = document.getElementById('leaflet-control-geosearch-qry'); + + if (e.keyCode === esc) { // escape key detection is unreliable + queryBox.value = ''; + this._map._container.focus(); + } else if (e.keyCode === enter) { + this.geosearch(queryBox.value); + } + }, + + _onMapClick: function (e) { + var location = e.latlng, + provider = this._config.provider; + + if (!location || !provider) { + return; + } + + try { + var url = provider.GetServiceUrl(location.lat, location.lng); + this.sendRequest(provider, url); + } catch (error) { + this._printError(error); + } + }, + + _onSuggestClick : function (e) { + var suggestId = e.target.id, + suggestIndex = -1, + suggest = null; + + if (suggestId) { + suggestIndex = suggestId.substring(suggestId.lastIndexOf('-') + 1); + } + + if (suggestIndex >= 0) { + suggest = this._suggestSet[suggestIndex]; + } + + if (suggest) { + this._showLocation(suggest); + } + }, + + _onSearchClick: function (e) { + var queryBox = document.getElementById('leaflet-control-geosearch-qry'); + + this.geosearch(queryBox.value); + }, + + _processResults: function(results) { + if (results instanceof Array) { + this._processForwardResults(results); + } else { + this._processReverseResults(results); + } + }, + + _processForwardResults: function(results) { + var resultCount = results.length; + if (resultCount <= 0) { + this._printError(this._config.notFoundMessage); + return; + } + + this._map.fireEvent('geosearch_foundlocations', {Locations: results}); + if (resultCount == 1) { + // only one result found... show it + this._showLocation(results[0]); + } else { + // multiple results found... add to results list and let user choose which one to show + this._setSuggestList(results); + } + }, + + _processReverseResults: function(result) { + if (!result) { + this._printError(this._config.notFoundMessage); + } + + this._map.fireEvent('geosearch_clicklocation', {Location: result}); + this._setResult(result); + }, + + _setResult: function(result) { + var displayName = null, + displayDescription = null, + divText = document.createElement('div'), + elem = this._result, + index = -1; + li = document.createElement('li'), + txtDescription = null, + txtName = null; + + if (result.lat == undefined || result.lon == undefined) { + return; + } + + this._clearResult(); + + index = result.displayName.indexOf(','); + displayName = result.displayName.substring(0, index); + displayDescription = result.displayName.substring(index + 1); + + li.id = 'geosearch-result'; + li.className = 'geosearch-result'; + txtDescription = document.createElement('span'); + txtDescription.className = 'result-description'; + txtDescription.innerHTML = displayDescription.replace(/^\s+|\s+$/g,''); + txtName = document.createElement('span'); + txtName.className = 'result-name'; + txtName.innerHTML = displayName.replace(/^\s+|\s+$/g,''); + divText.className = 'geosearch-result-text'; + divText.appendChild(txtName); + divText.appendChild(txtDescription); + li.appendChild(divText); + + elem.appendChild(li); + elem.style.display = 'block'; + + this._map.fireEvent('geosearch_showresult', {element: elem, queryPoint: L.latLng({lat: parseFloat(result.lat), lon: parseFloat(result.lon)})}); + }, + + _setSuggestList: function(results) { + var elem = this._suggestlist, + i = 0, + j = 0, + li = null, + liText = null, + r = null; + + this._clearSuggestList(); + + for (i = 0, j = 0; i < results.length; ++i) { + r = results[i]; + if (r.lat == undefined || r.lon == undefined) { + continue; + } + + if (this._isInSuggestSet(r)) { + continue; + } + + this._suggestSet[this._suggestSet.length] = r; + li = document.createElement('li'); + li.id = 'geosearch-suggest-' + j; + li.className = 'geosearch-suggest'; + liText = document.createTextNode(r.displayName); + li.appendChild(liText); + elem.appendChild(li); + L.DomEvent.addListener(li, 'click', this._onSuggestClick, this); + + ++j; + } + + elem.style.display = 'block'; + }, + + _showLocation: function (location) { + this._setResult(location); + this._clearSuggestList(); + + if (this.options.showMarker == true) { + if (typeof this._positionMarker === 'undefined') { + this._positionMarker = L.marker([location.lat, location.lon]).addTo(this._map); + } else { + this._positionMarker.setLatLng([location.lat, location.lon]); + } + } + + this._map.setView([location.lat, location.lon], this._config.zoomLevel, false); + this._map.fireEvent('geosearch_showlocation', {Location: location}); + }, + + _printError: function(message) { + var elem = this._result; + elem.innerHTML = '<li>' + message + '</li>'; + elem.style.display = 'block'; + + var self = this; + self._timeout = setTimeout(function () { + elem.style.display = 'none'; + self._timeout = null; + }, 3000); + } + }); + + return L.Control.GeoSearch; +}); \ No newline at end of file diff --git a/src/main/webapp/js/map/control/geosearchProvider/osm.js b/src/main/webapp/js/map/control/geosearchProvider/osm.js new file mode 100644 index 00000000..4edad207 --- /dev/null +++ b/src/main/webapp/js/map/control/geosearchProvider/osm.js @@ -0,0 +1,97 @@ +/** + * Uses openstreetmap geocoding service for providing geosearch + * + * @see https://github.com/smeijer/leaflet.control.geosearch + */ +define(['leaflet'], function(L) { + ReverseResult = function (lon, lat, address, displayName) { + this.lon = lon; + this.lat = lat; + this.address = address; + this.displayName = displayName; + }; + + Result = function (lon, lat, displayName) { + this.lon = lon; + this.lat = lat; + this.displayName = displayName; + }; + + OsmProvider = L.Class.extend({ + options : { + reverseable: true + }, + + initialize : function(options) { + options = L.Util.setOptions(this, options); + }, + + GetServiceUrl : function(qry, lon) { + if (!lon) { + // only one parameter given (query).. perform a forward lookup + return this._getForwardServiceUrl(qry); + } + + // second parameter set... perform a reverse lookup + return this._getReverseServiceUrl(qry, lon); + }, + + ParseJSON : function(data) { + if (data instanceof Array) { + return this._parseForwardJSON(data); + } + + return this._parseReverseJSON(data); + }, + + _getReverseServiceUrl : function(lat, lon) { + var parameters = L.Util.extend({ + lat: lat, + lon: lon, + format : 'json' + }, this.options); + + return 'http://nominatim.openstreetmap.org/reverse' + + L.Util.getParamString(parameters); + }, + + _getForwardServiceUrl: function(qry) { + var parameters = L.Util.extend({ + q : qry, + format : 'json' + }, this.options); + + return 'http://nominatim.openstreetmap.org/search' + + L.Util.getParamString(parameters); + }, + + _parseForwardJSON : function(data) { + if (data.length == 0) + return []; + + var results = []; + for (var i = 0; i < data.length; i++) + results.push(new Result( + data[i].lon, + data[i].lat, + data[i].display_name + )); + + return results; + }, + + _parseReverseJSON : function(data) { + if (data.length == 0) + return {}; + + return new ReverseResult( + data.lon, + data.lat, + data.address, + data.display_name + ); + } + }); + + return OsmProvider; +}); \ No newline at end of file diff --git a/src/main/webapp/js/map/control/help.js b/src/main/webapp/js/map/control/help.js new file mode 100644 index 00000000..e4274c14 --- /dev/null +++ b/src/main/webapp/js/map/control/help.js @@ -0,0 +1,25 @@ +define(['leaflet', 'console'], function(L, logger) { + L.HelpControl = L.Control.extend({ + options: { + position: 'bottomright' + }, + + onAdd: function (map) { + this._container = L.DomUtil.create('div', 'help-control'); + this._container.id = 'help-control-div'; + this._container.className = 'help-control-div'; + + L.DomEvent.addListener(this._container, 'click', this._onOpen); + L.DomEvent.disableClickPropagation(this._container); + + return this._container; + }, + + _onOpen: function() { + logger.log('Showing helper dialog'); + $('#help-dialog').dialog('open'); + } + }); + + return L.HelpControl; +}); diff --git a/src/main/webapp/js/map/control/settings.js b/src/main/webapp/js/map/control/settings.js new file mode 100644 index 00000000..ad6b413b --- /dev/null +++ b/src/main/webapp/js/map/control/settings.js @@ -0,0 +1,25 @@ +define(['leaflet', 'console'], function(L, logger) { + L.SettingsControl = L.Control.extend({ + options: { + position: 'bottomright' + }, + + onAdd: function (map) { + this._container = L.DomUtil.create('div', 'settings-control'); + this._container.id = 'settings-control-div'; + this._container.className = 'settings-control-div'; + + L.DomEvent.addListener(this._container, 'click', this._onOpen); + L.DomEvent.disableClickPropagation(this._container); + + return this._container; + }, + + _onOpen: function() { + logger.log('Showing settings'); + $('#settings-dialog').dialog('open'); + } + }); + + return L.SettingsControl; +}); diff --git a/src/main/webapp/js/map/controls.js b/src/main/webapp/js/map/controls.js deleted file mode 100644 index 83ab18eb..00000000 --- a/src/main/webapp/js/map/controls.js +++ /dev/null @@ -1,43 +0,0 @@ -var HelpControl = L.Control.extend({ - options: { - position: 'bottomright' - }, - - onAdd: function (map) { - this._container = L.DomUtil.create('div', 'help-control'); - this._container.id = 'help-control-div'; - this._container.className = 'help-control-div'; - - L.DomEvent.addListener(this._container, 'click', this._onOpen); - L.DomEvent.disableClickPropagation(this._container); - - return this._container; - }, - - _onOpen: function() { - console.log('Showing helper dialog'); - $('#help-dialog').dialog('open'); - } -}); - -var SettingsControl = L.Control.extend({ - options: { - position: 'bottomright' - }, - - onAdd: function (map) { - this._container = L.DomUtil.create('div', 'settings-control'); - this._container.id = 'settings-control-div'; - this._container.className = 'settings-control-div'; - - L.DomEvent.addListener(this._container, 'click', this._onOpen); - L.DomEvent.disableClickPropagation(this._container); - - return this._container; - }, - - _onOpen: function() { - console.log('Showing settings'); - $('#settings-dialog').dialog('open'); - } -}); diff --git a/src/main/webapp/js/map/geosearch.js b/src/main/webapp/js/map/geosearch.js deleted file mode 100644 index 0dd3c68d..00000000 --- a/src/main/webapp/js/map/geosearch.js +++ /dev/null @@ -1,429 +0,0 @@ -/* - * L.Control.GeoSearch - search for an address and zoom to its location - * https://github.com/smeijer/leaflet.control.geosearch - */ - -L.GeoSearch = {}; -L.GeoSearch.Provider = {}; -L.GeoSearch.ReverseProvider = {}; - -L.GeoSearch.ReverseResult = function (lon, lat, address, displayName) { - this.lon = lon; - this.lat = lat; - this.address = address; - this.displayName = displayName; -}; - -L.GeoSearch.Result = function (lon, lat, displayName) { - this.lon = lon; - this.lat = lat; - this.displayName = displayName; -}; - -L.Control.GeoSearch = L.Control.extend({ - options: { - doReverseLookup: false, - position: 'topleft', - provider: null, - showMarker: true - }, - - _config: { - country: '', - searchLabel: 'Search for address ...', - notFoundMessage: 'Sorry, that address could not be found.', - messageHideDelay: 3000, - zoomLevel: 18 - }, - - // Public methods - - initialize: function (options) { - L.Util.extend(this.options, options); - L.Util.extend(this._config, options); - }, - - geosearch: function (qry) { - try { - var provider = this._config.provider; - - if(typeof provider.GetLocations == 'function') { - var results = provider.GetLocations(qry, function(results) { - this._processResults(results); - }.bind(this)); - } else { - var url = provider.GetServiceUrl(qry); - this.sendRequest(provider, url); - } - } catch (error) { - this._printError(error); - } - }, - - onAdd: function (map) { - var $controlContainer = map._controlContainer, - nodes = $controlContainer.childNodes; - - this._map = map; - this._container = L.DomUtil.create('div', 'leaflet-control-geosearch'); - this._suggestSet = []; - - var searchdiv = document.createElement('div'); - searchdiv.id = 'leaflet-control-geosearch-searchdiv'; - searchdiv.className = 'leaflet-control-geosearch-searchdiv'; - this._searchdiv = searchdiv; - - var searchbox = document.createElement('input'); - searchbox.id = 'leaflet-control-geosearch-qry'; - searchbox.type = 'text'; - searchbox.placeholder = this._config.searchLabel; - this._searchbox = searchbox; - - var searchbtn = document.createElement('button'); - searchbtn.id = 'leaflet-control-geosearch-btn'; - searchbtn.className = 'leaflet-control-geosearch-btn'; - this._searchbtn = searchbtn; - - var msgbox = document.createElement('div'); - msgbox.id = 'leaflet-control-geosearch-msg'; - msgbox.className = 'leaflet-control-geosearch-msg'; - this._msgbox = msgbox; - - var result = document.createElement('ul'); - result.id = 'leaflet-control-geosearch-result'; - result.className = 'leaflet-control-geosearch-result'; - this._result = result; - - var suggestlist = document.createElement('ul'); - suggestlist.id = 'leaflet-control-geosearch-suggests'; - suggestlist.className = 'leaflet-control-geosearch-suggests'; - this._suggestlist = suggestlist; - - this._msgbox.appendChild(this._suggestlist); - this._msgbox.appendChild(this._result); - this._searchdiv.appendChild(this._searchbtn); - this._searchdiv.appendChild(this._searchbox); - this._container.appendChild(this._searchdiv); - this._container.appendChild(this._msgbox); - - L.DomEvent - .addListener(this._container, 'click', L.DomEvent.stop) - .addListener(this._searchbtn, 'click', this._onSearchClick, this) - .addListener(this._searchbox, 'keypress', this._onKeyUp, this); - - if (this._config.doReverseLookup && this._config.provider.options.reverseable) { - L.DomEvent.addListener(this._map, 'click', this._onMapClick, this); - } - - L.DomEvent.disableClickPropagation(this._container); - - return this._container; - }, - - sendRequest: function (provider, url) { - var that = this; - - window.parseLocation = function (response) { - var results = provider.ParseJSON(response); - that._processResults(results); - - document.body.removeChild(document.getElementById('getJsonP')); - delete window.parseLocation; - }; - - function getJsonP (url) { - url = url + '&callback=parseLocation'; - var script = document.createElement('script'); - script.id = 'getJsonP'; - script.src = url; - script.async = true; - document.body.appendChild(script); - } - - if (XMLHttpRequest) { - var xhr = new XMLHttpRequest(); - - if ('withCredentials' in xhr) { - var xhr = new XMLHttpRequest(); - - xhr.onreadystatechange = function () { - if (xhr.readyState == 4) { - if (xhr.status == 200) { - var response = JSON.parse(xhr.responseText), - results = provider.ParseJSON(response); - - that._processResults(results); - } else if (xhr.status == 0 || xhr.status == 400) { - getJsonP(url); - } else { - that._printError(xhr.responseText); - } - } - }; - - xhr.open('GET', url, true); - xhr.send(); - } else if (XDomainRequest) { - var xdr = new XDomainRequest(); - - xdr.onerror = function (err) { - that._printError(err); - }; - - xdr.onload = function () { - var response = JSON.parse(xdr.responseText), - results = provider.ParseJSON(response); - - that._processResults(results); - }; - - xdr.open('GET', url); - xdr.send(); - } else { - getJsonP(url); - } - } - }, - - // Private methods - - _addressToString : function(address) { - if (!address) { - return ''; - } - - var addressStr = ''; - if (address.road) addressStr += address.road + ', '; - if (address.postcode) addressStr += address.postcode + ', '; - if (address.country) addressStr += address.country; - if (/, $/.test(addressStr)) addressStr = addressStr.substring(0, addressStr.length - 2); - - return addressStr; - }, - - _clearResult : function() { - if (this._timeout != null) { - clearTimeout(this._timeout); - this._timeout = null; - } - - var elem = this._result; - elem.innerHTML = ''; - elem.style.display = 'none'; - }, - - _clearSuggestList : function() { - this._suggestSet = []; - if (this._timeout != null) { - clearTimeout(this._timeout); - this._timeout = null; - } - - var elem = this._suggestlist; - elem.innerHTML = ''; - elem.style.display = 'none'; - }, - - _isInSuggestSet : function(o) { - var i = 0, - set = this._suggestSet; - - for (i = 0; i < set.length; ++i) { - if (set[i].lat == o.lat && set[i].lon == o.lon) { - return true; - } - } - - return false; - }, - - _onKeyUp: function (e) { - var esc = 27, - enter = 13, - queryBox = document.getElementById('leaflet-control-geosearch-qry'); - - if (e.keyCode === esc) { // escape key detection is unreliable - queryBox.value = ''; - this._map._container.focus(); - } else if (e.keyCode === enter) { - this.geosearch(queryBox.value); - } - }, - - _onMapClick: function (e) { - var location = e.latlng, - provider = this._config.provider; - - if (!location || !provider) { - return; - } - - try { - var url = provider.GetServiceUrl(location.lat, location.lng); - this.sendRequest(provider, url); - } catch (error) { - this._printError(error); - } - }, - - _onSuggestClick : function (e) { - var suggestId = e.target.id, - suggestIndex = -1, - suggest = null; - - if (suggestId) { - suggestIndex = suggestId.substring(suggestId.lastIndexOf('-') + 1); - } - - if (suggestIndex >= 0) { - suggest = this._suggestSet[suggestIndex]; - } - - if (suggest) { - this._showLocation(suggest); - } - }, - - _onSearchClick: function (e) { - var queryBox = document.getElementById('leaflet-control-geosearch-qry'); - - this.geosearch(queryBox.value); - }, - - _processResults: function(results) { - if (results instanceof Array) { - this._processForwardResults(results); - } else { - this._processReverseResults(results); - } - }, - - _processForwardResults: function(results) { - var resultCount = results.length; - if (resultCount <= 0) { - this._printError(this._config.notFoundMessage); - return; - } - - this._map.fireEvent('geosearch_foundlocations', {Locations: results}); - if (resultCount == 1) { - // only one result found... show it - this._showLocation(results[0]); - } else { - // multiple results found... add to results list and let user choose which one to show - this._setSuggestList(results); - } - }, - - _processReverseResults: function(result) { - if (!result) { - this._printError(this._config.notFoundMessage); - } - - this._map.fireEvent('geosearch_clicklocation', {Location: result}); - this._setResult(result); - }, - - _setResult: function(result) { - var displayName = null, - displayDescription = null, - divText = document.createElement('div'), - elem = this._result, - index = -1; - li = document.createElement('li'), - txtDescription = null, - txtName = null; - - if (result.lat == undefined || result.lon == undefined) { - return; - } - - this._clearResult(); - - index = result.displayName.indexOf(','); - displayName = result.displayName.substring(0, index); - displayDescription = result.displayName.substring(index + 1); - - li.id = 'geosearch-result'; - li.className = 'geosearch-result'; - txtDescription = document.createElement('span'); - txtDescription.className = 'result-description'; - txtDescription.innerHTML = displayDescription.replace(/^\s+|\s+$/g,''); - txtName = document.createElement('span'); - txtName.className = 'result-name'; - txtName.innerHTML = displayName.replace(/^\s+|\s+$/g,''); - divText.className = 'geosearch-result-text'; - divText.appendChild(txtName); - divText.appendChild(txtDescription); - li.appendChild(divText); - - elem.appendChild(li); - elem.style.display = 'block'; - - this._map.fireEvent('geosearch_showresult', {element: elem, queryPoint: L.latLng({lat: parseFloat(result.lat), lon: parseFloat(result.lon)})}); - }, - - _setSuggestList: function(results) { - var elem = this._suggestlist, - i = 0, - j = 0, - li = null, - liText = null, - r = null; - - this._clearSuggestList(); - - for (i = 0, j = 0; i < results.length; ++i) { - r = results[i]; - if (r.lat == undefined || r.lon == undefined) { - continue; - } - - if (this._isInSuggestSet(r)) { - continue; - } - - this._suggestSet[this._suggestSet.length] = r; - li = document.createElement('li'); - li.id = 'geosearch-suggest-' + j; - li.className = 'geosearch-suggest'; - liText = document.createTextNode(r.displayName); - li.appendChild(liText); - elem.appendChild(li); - L.DomEvent.addListener(li, 'click', this._onSuggestClick, this); - - ++j; - } - - elem.style.display = 'block'; - }, - - _showLocation: function (location) { - this._setResult(location); - this._clearSuggestList(); - - if (this.options.showMarker == true) { - if (typeof this._positionMarker === 'undefined') { - this._positionMarker = L.marker([location.lat, location.lon]).addTo(this._map); - } else { - this._positionMarker.setLatLng([location.lat, location.lon]); - } - } - - this._map.setView([location.lat, location.lon], this._config.zoomLevel, false); - this._map.fireEvent('geosearch_showlocation', {Location: location}); - }, - - _printError: function(message) { - var elem = this._result; - elem.innerHTML = '<li>' + message + '</li>'; - elem.style.display = 'block'; - - var self = this; - self._timeout = setTimeout(function () { - elem.style.display = 'none'; - self._timeout = null; - }, 3000); - } - -}); diff --git a/src/main/webapp/js/map/geosearch_provider_osm.js b/src/main/webapp/js/map/geosearch_provider_osm.js deleted file mode 100644 index 33d5e12d..00000000 --- a/src/main/webapp/js/map/geosearch_provider_osm.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * L.Control.GeoSearch - search for an address and zoom to it's location - * L.GeoSearch.Provider.OpenStreetMap uses openstreetmap geocoding service - * https://github.com/smeijer/leaflet.control.geosearch - */ - -L.GeoSearch.Provider.OpenStreetMap = L.Class.extend({ - options : { - reverseable: true - }, - - initialize : function(options) { - options = L.Util.setOptions(this, options); - }, - - GetServiceUrl : function(qry, lon) { - if (!lon) { - // only one parameter given (query).. perform a forward lookup - return this._getForwardServiceUrl(qry); - } - - // second parameter set... perform a reverse lookup - return this._getReverseServiceUrl(qry, lon); - }, - - ParseJSON : function(data) { - if (data instanceof Array) { - return this._parseForwardJSON(data); - } - - return this._parseReverseJSON(data); - }, - - _getReverseServiceUrl : function(lat, lon) { - var parameters = L.Util.extend({ - lat: lat, - lon: lon, - format : 'json' - }, this.options); - - return 'http://nominatim.openstreetmap.org/reverse' - + L.Util.getParamString(parameters); - }, - - _getForwardServiceUrl: function(qry) { - var parameters = L.Util.extend({ - q : qry, - format : 'json' - }, this.options); - - return 'http://nominatim.openstreetmap.org/search' - + L.Util.getParamString(parameters); - }, - - _parseForwardJSON : function(data) { - if (data.length == 0) - return []; - - var results = []; - for (var i = 0; i < data.length; i++) - results.push(new L.GeoSearch.Result( - data[i].lon, - data[i].lat, - data[i].display_name - )); - - return results; - }, - - _parseReverseJSON : function(data) { - if (data.length == 0) - return {}; - - return new L.GeoSearch.ReverseResult( - data.lon, - data.lat, - data.address, - data.display_name - ); - } -}); diff --git a/src/main/webapp/js/map/google.js b/src/main/webapp/js/map/google.js deleted file mode 100644 index be69b226..00000000 --- a/src/main/webapp/js/map/google.js +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Google layer using Google Maps API - */ -//(function (google, L) { - -L.Google = L.Class.extend({ - includes: L.Mixin.Events, - - options: { - minZoom: 0, - maxZoom: 18, - tileSize: 256, - subdomains: 'abc', - errorTileUrl: '', - attribution: '', - opacity: 1, - continuousWorld: false, - noWrap: false - }, - - // Possible types: SATELLITE, ROADMAP, HYBRID, TERRAIN - initialize: function(type, options) { - L.Util.setOptions(this, options); - - this._ready = google.maps.Map != undefined; - if (!this._ready) L.Google.asyncWait.push(this); - - this._type = type || 'SATELLITE'; - }, - - onAdd: function(map, insertAtTheBottom) { - this._map = map; - this._insertAtTheBottom = insertAtTheBottom; - - // create a container div for tiles - this._initContainer(); - this._initMapObject(); - - // set up events - map.on('viewreset', this._resetCallback, this); - - this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this); - map.on('move', this._update, this); - //map.on('moveend', this._update, this); - - map._controlCorners['bottomright'].style.marginBottom = "1em"; - - this._reset(); - this._update(); - }, - - onRemove: function(map) { - this._map._container.removeChild(this._container); - //this._container = null; - - this._map.off('viewreset', this._resetCallback, this); - - this._map.off('move', this._update, this); - map._controlCorners['bottomright'].style.marginBottom = "0em"; - //this._map.off('moveend', this._update, this); - }, - - getAttribution: function() { - return this.options.attribution; - }, - - setOpacity: function(opacity) { - this.options.opacity = opacity; - if (opacity < 1) { - L.DomUtil.setOpacity(this._container, opacity); - } - }, - - setElementSize: function(e, size) { - e.style.width = size.x + "px"; - e.style.height = size.y + "px"; - }, - - _initContainer: function() { - var tilePane = this._map._container, - first = tilePane.firstChild; - - if (!this._container) { - this._container = L.DomUtil.create('div', 'leaflet-google-layer leaflet-top leaflet-left'); - this._container.id = "_GMapContainer_" + L.Util.stamp(this); - this._container.style.zIndex = "auto"; - } - - if (true) { - tilePane.insertBefore(this._container, first); - - this.setOpacity(this.options.opacity); - this.setElementSize(this._container, this._map.getSize()); - } - }, - - _initMapObject: function() { - if (!this._ready) return; - this._google_center = new google.maps.LatLng(0, 0); - var map = new google.maps.Map(this._container, { - center: this._google_center, - zoom: 0, - tilt: 0, - mapTypeId: google.maps.MapTypeId[this._type], - disableDefaultUI: true, - keyboardShortcuts: false, - draggable: false, - disableDoubleClickZoom: true, - scrollwheel: false, - streetViewControl: false - }); - - var _this = this; - this._reposition = google.maps.event.addListenerOnce(map, "center_changed", - function() { _this.onReposition(); }); - - map.backgroundColor = '#ff0000'; - this._google = map; - }, - - _resetCallback: function(e) { - this._reset(e.hard); - }, - - _reset: function(clearOldContainer) { - this._initContainer(); - }, - - _update: function() { - if (!this._google) return; - this._resize(); - - var bounds = this._map.getBounds(); - var ne = bounds.getNorthEast(); - var sw = bounds.getSouthWest(); - var google_bounds = new google.maps.LatLngBounds( - new google.maps.LatLng(sw.lat, sw.lng), - new google.maps.LatLng(ne.lat, ne.lng) - ); - var center = this._map.getCenter(); - var _center = new google.maps.LatLng(center.lat, center.lng); - - this._google.setCenter(_center); - this._google.setZoom(this._map.getZoom()); - //this._google.fitBounds(google_bounds); - }, - - _resize: function() { - var size = this._map.getSize(); - if (this._container.style.width == size.x && - this._container.style.height == size.y) - return; - this.setElementSize(this._container, size); - this.onReposition(); - }, - - onReposition: function() { - if (!this._google) return; - google.maps.event.trigger(this._google, "resize"); - } -}); - -L.Google.asyncWait = []; -L.Google.asyncInitialize = function() { - var i; - for (i = 0; i < L.Google.asyncWait.length; i++) { - var o = L.Google.asyncWait[i]; - o._ready = true; - if (o._container) { - o._initMapObject(); - o._update(); - } - } - L.Google.asyncWait = []; -} -//})(window.google, L) \ No newline at end of file diff --git a/src/main/webapp/js/map/ipLocator/ipLocator.js b/src/main/webapp/js/map/ipLocator/ipLocator.js new file mode 100644 index 00000000..eb02f837 --- /dev/null +++ b/src/main/webapp/js/map/ipLocator/ipLocator.js @@ -0,0 +1,53 @@ +define(['leaflet'], function IpLocator(L) { + L.IpLocator = L.Class.extend({ + locateByIp: function (source, responseCallback, errorCallback) { + var handlerFn = L.bind(function (data) { + this._handleIpResponse(source, data, responseCallback, errorCallback); + }, this); + + if (source.cbParam === undefined || source.cbParam === null || source.cbParam === '') { + this._loadScript(source.url, handlerFn); + return; + } + + window.cbObject = {}; + window.cbObject.fn = handlerFn; + this._loadScript(source.url + '?' + source.cbParam + '= window.cbObject.fn'); + }, + + _handleIpResponse: function (source, data, responseCallback, errorCallback) { + window.cbObject = null; + delete window.cbObject; + + var pos = source.buildLocationObject(data); + if (pos === null) { + if (errorCallback) { errorCallback('Could not get location.'); } + } else if (responseCallback) { + responseCallback(pos); + } + }, + + _loadScript: function (url, callback, type) { + var script = document.createElement('script'); + script.type = (type === undefined) ? 'text/javascript' : type; + + if (typeof callback === 'function') { + if (script.readyState) { + script.onreadystatechange = function () { + if (script.readyState === 'loaded' || script.readyState === 'complete') { + script.onreadystatechange = null; + callback(); + } + }; + } else { + script.onload = function () { callback(); }; + } + } + + script.src = url; + document.getElementsByTagName('head')[0].appendChild(script); + } + }); + + return L.IpLocator; +}); diff --git a/src/main/webapp/js/map/ipLocator/ipProvider.js b/src/main/webapp/js/map/ipLocator/ipProvider.js new file mode 100644 index 00000000..01bc7e7e --- /dev/null +++ b/src/main/webapp/js/map/ipLocator/ipProvider.js @@ -0,0 +1,59 @@ +define({ + None: null, + FreeGeoIp: { + url: 'http://freegeoip.net/json/', + cbParam: 'callback', + buildLocationObject: function (data) { + if (!data) { + return null; + } + + return { + coords: { + accuracy: 10000, + latitude: parseFloat(data.latitude), + longitude: parseFloat(data.longitude) + }, + timestamp: new Date().getTime() + }; + } + }, + GeoPlugin: { + url: 'http://www.geoplugin.net/json.gp', + cbParam: 'jsoncallback', + buildLocationObject: function (data) { + if (!data) { + return null; + } + + return { + coords: { + accuracy: 10000, + /* jshint ignore:start */ + latitude: parseFloat(data.geoplugin_latitude), + longitude: parseFloat(data.geoplugin_longitude) + /* jshint ignore:end */ + }, + timestamp: new Date().getTime() + }; + } + }, + Wikimedia: { + url: 'http://geoiplookup.wikimedia.org/', + cbParam: '', + buildLocationObject: function () { + var data = window.Geo, + result = { + coords: { + accuracy: 10000, + latitude: parseFloat(data.lat), + longitude: parseFloat(data.lon) + }, + timestamp: new Date().getTime() + }; + + delete window.Geo; + return result; + } + } +}); diff --git a/src/main/webapp/js/map/ipLocator/mapIncluder.js b/src/main/webapp/js/map/ipLocator/mapIncluder.js new file mode 100644 index 00000000..30442d13 --- /dev/null +++ b/src/main/webapp/js/map/ipLocator/mapIncluder.js @@ -0,0 +1,49 @@ +define(['leaflet', 'map/ipLocator/ipProvider', 'map/ipLocator/ipLocator'], function(L, IpProvider, IpLocator) { + L.Map.include({ + _defaultLocateOptions: { + ipProvider: IpProvider.Wikimedia, + timeout: 10000, + watch: false + // setView: false + // maxZoom: <Number> + // maximumAge: 0 + // enableHighAccuracy: false + }, + + locate: function (/*Object*/ options) { + options = this._locateOptions = L.extend(this._defaultLocateOptions, options); + + if (!navigator.geolocation && !options.ipProvider) { + this._handleGeolocationError({ + code: 0, + message: 'Geolocation not supported.' + }); + return this; + } + + var onResponse = L.bind(this._handleGeolocationResponse, this), + onError = L.bind((options.ipProvider) ? this._fallbackToIp : this._handleGeolocationError, this); + + if (options.watch) { + this._locationWatchId = + navigator.geolocation.watchPosition(onResponse, onError, options); + } else { + navigator.geolocation.getCurrentPosition(onResponse, onError, options); + } + + return this; + }, + + _fallbackToIp: function (errMsg) { + if (!this._locateOptions.ipProvider) { + this._handleGeolocationError(errMsg); + return; + } + + var onResponse = L.bind(this._handleGeolocationResponse, this), + onError = L.bind(this._handleGeolocationError, this); + + (new IpLocator).locateByIp(this._locateOptions.ipProvider, onResponse, onError); + } + }); +}); \ No newline at end of file diff --git a/src/main/webapp/js/map/isoMap.js b/src/main/webapp/js/map/isoMap.js new file mode 100644 index 00000000..4ed2db09 --- /dev/null +++ b/src/main/webapp/js/map/isoMap.js @@ -0,0 +1,225 @@ +define(['jQuery', 'leaflet', 'console', 'map/layer/bing','map/layer/google', 'map/control/settings', 'map/control/help', 'map/control/geosearch', 'map/control/geosearchProvider/osm', 'map/ipLocator/ipProvider', 'map/ipLocator/ipLocator'], function($, L, logger, BingLayer, GoogleLayer, SettingsControl, HelpControl, GeoSearch, OSMProvider, IpProvider, IpLocator) { + // Private static fields + var INNSBRUCK = [47.265718, 11.391342]; + var VIENNA = [48.186312, 16.317615]; + + // Class function (a.k.a. constructor) + function IsoMap(div) { + // Private fields + var mDiv = null; + var map = null; + var mapOptions = { + attributionControl: false, + center: INNSBRUCK, + ipProvider: IpProvider.Wikimedia, + zoomControl: false, // hide default zoom control, so we can create a bottomright one + zoom: 12 + }; + + // Constructor code + + mDiv = div; + + // Public methods + + /** + * Gets the internal map object. + * This is only useful after the map has been drawn + * + * @return the map object. Null if the map has not been drawn. + */ + this.getMap = function() { + return map; + }; + + this.draw = function() { + var layerControl = L.control.layers(), + zoomControl = L.control.zoom({position: 'bottomright'}); + + logger.log('Drawing map'); + logger.debug(' - options:', mapOptions); + + // Base + Overlay map definition + mapOptions.layers = addOsmLayer(layerControl); + addStamenLayer(layerControl); + addBlueMarbleLayers(layerControl); + addBingLayers(layerControl); + addGoogleLayers(layerControl); + + // Map creation and control assignment + map = L.map(mDiv, mapOptions); + map.addControl(new SettingsControl({position: 'bottomright'})); + map.addControl(new HelpControl({position: 'bottomright'})); + map.addControl(layerControl); + map.addControl(zoomControl); + map.addControl(L.control.locate({position: 'bottomright'})); + map.addControl(L.control.scale({position: 'bottomleft'})); + map.addControl(new GeoSearch({ + doReverseLookup: true, + position: 'topleft', + provider: new OSMProvider(), + showMarker: true + })); + + $(document).trigger('isomap_draw'); + }; + + this.locateAndDraw = function() { + var locator = new IpLocator(); + locator.locateByIp(mapOptions.ipProvider, onLocationSuccess.bind(this), onLocationError.bind(this)); + }; + + // Private methods + + function addBingLayers(c) { + var apiKey = 'AqTGBsziZHIJYYxgivLBf0hVdrAk9mWO5cQcb8Yux8sW5M8c8opEC2lZqKR1ZZXf', + bAerial, + bHybrid, + bRoad; + + bRoad = new BingLayer(apiKey, { + type: 'Road' + }); + bHybrid = new BingLayer(apiKey, { + type: 'AerialWithLabels' + }); + bAerial = new BingLayer(apiKey, { + type: 'Aerial' + }); + + logger.debug('Adding bing layers'); + + c.addBaseLayer(bRoad, 'Bing Road'); + c.addBaseLayer(bHybrid, 'Bing Hybrid'); + c.addBaseLayer(bAerial, 'Bing Aerial'); + + return bAerial; + } + + function addBlueMarbleLayers(c) { + if (!L.tileLayer) { + return; + } + + var blueMarbleJpg, + blueMarblePng, + openGeoOSM; + + blueMarbleJpg = new L.tileLayer.wms( + 'http://maps.opengeo.org/geowebcache/service/wms', + {layers: 'bluemarble'} + ); + + blueMarblePng = new L.tileLayer.wms( + 'http://maps.opengeo.org/geowebcache/service/wms', + {layers: 'bluemarble', format: 'image/png'} + ); + + openGeoOSM = new L.tileLayer.wms( + 'http://maps.opengeo.org/geowebcache/service/wms', + {layers: 'openstreetmap', format: 'image/png'} + ); + + logger.debug('Adding openGeo layers (blueMarbel and openGeo OSM)'); + + c.addBaseLayer(blueMarbleJpg, 'OpenGeo BlueMarble (jpg)'); + c.addBaseLayer(blueMarblePng, 'OpenGeo BlueMarble (png)'); + c.addBaseLayer(openGeoOSM, 'OpenGeo OSM (png)'); + + return openGeoOSM; + } + + function addGoogleLayers(c) { + var gPhysical, + gStreet, + gHybrid, + gSatellite; + + gPhysical = new GoogleLayer('TERRAIN'); + gStreet = new GoogleLayer('ROADMAP', + {maxZoom: 20 } + ); + gHybrid = new GoogleLayer('HYBRID', + {maxZoom: 20 } + ); + gSatellite = new GoogleLayer('SATELLITE', + {maxZoom: 22 } + ); + + logger.debug('Adding google layers'); + + c.addBaseLayer(gPhysical, 'Google Physical'); + c.addBaseLayer(gStreet, 'Google Streets'); + c.addBaseLayer(gHybrid, 'Google Hybrid'); + c.addBaseLayer(gSatellite, 'Google Satellite'); + + return gSatellite; + } + + function addOsmLayer(c) { + if (!L.tileLayer) { + return; + } + + var layerOsm = new L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { + attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' + }); + + logger.debug('Adding openstreetmap layer'); + + c.addBaseLayer(layerOsm, 'OpenStreetMap'); + return layerOsm; + } + + function addStamenLayer(c) { + if (!L.StamenTileLayer) { + return; + } + + var layerStamen = new L.StamenTileLayer('watercolor'); + + logger.debug('Adding stamen watercolor layer'); + + c.addBaseLayer(layerStamen, 'Stamen Watercolor Map'); + return layerStamen; + } + + function onLocationError(message) { + logger.info('Could not get GeoLocation', message); + logger.debug(' - will continue with default location'); + + this.draw(); + } + + function onLocationSuccess(location) { + if (!location || !location.coords) { + logger.debug('Invalid geoLocation found... no coordinates returned!'); + return; + } + + logger.log('GeoLocation found. It will be used as center'); + logger.debug(' - location:', location); + + mapOptions.center = [location.coords.latitude, location.coords.longitude]; + + this.draw(); + } + } + + // Public static field +// IsoMap.staticVar = 0; + + // Public static method +// IsoMap.capitalize = function(name){ +// return name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); +// }; + + // Private static method +// function createWithName(name) { +// var obj = new cls(); +// obj.setName(cls.capitalize(name)); +// return obj; +// } + + return IsoMap; +}); diff --git a/src/main/webapp/js/map/isomap.js b/src/main/webapp/js/map/isomap.js deleted file mode 100644 index c687d1c7..00000000 --- a/src/main/webapp/js/map/isomap.js +++ /dev/null @@ -1,212 +0,0 @@ -function IsoMap(divId) { - var INNSBRUCK = [47.265718, 11.391342]; -// var VIENNA = [48.186312, 16.317615]; - var mapDivId = null; - var map = null; - var mapOptions = { - attributionControl: false, - center: INNSBRUCK, - ipProvider: L.IpProvider.Wikimedia, - zoomControl: false, // hide default zoom control, so we can create a bottomright one - zoom: 12 - }; - - // Constructor - - mapDivId = divId; - - // Getter - - /** - * Gets the internal map object. - * This is only useful after the map has been drawn - * - * @return the map object. Null if the map has not been drawn. - */ - this.getMap = function() { - return map; - }; - - // Public methods - - this.draw = function() { - var layerControl = L.control.layers({position: 'topright'}), - zoomControl = L.control.zoom({position: 'bottomright'}); - - console.log('Drawing map'); - console.debug(' - options:', mapOptions); - - // Base + Overlay map definition - mapOptions.layers = addOsmLayer(layerControl); - addStamenLayer(layerControl); - addBlueMarbleLayers(layerControl); - addBingLayers(layerControl); - addGoogleLayers(layerControl); - - // Map creation and control assignment - map = L.map(mapDivId, mapOptions); - map.addControl(new SettingsControl({position: 'bottomright'})); - map.addControl(new HelpControl({position: 'bottomright'})); - map.addControl(layerControl); - map.addControl(zoomControl); - map.addControl(L.control.locate({position: 'bottomright'})); - map.addControl(L.control.scale({position: 'bottomleft'})); - map.addControl(new L.Control.GeoSearch({ - doReverseLookup: true, - position: 'topleft', - provider: new L.GeoSearch.Provider.OpenStreetMap(), - showMarker: true - })); - - $(document).trigger('isomap_draw'); - }; - - this.locateAndDraw = function() { - var locator = new L.IpLocator(); - locator.locateByIp(mapOptions.ipProvider, onLocationSuccess.bind(this), onLocationError.bind(this)); - }; - - // Private methods - - addBingLayers = function(c) { - if (!L.BingLayer) { - return; - } - - var apiKey = 'AqTGBsziZHIJYYxgivLBf0hVdrAk9mWO5cQcb8Yux8sW5M8c8opEC2lZqKR1ZZXf', - bAerial, - bHybrid, - bRoad; - - bRoad = new L.BingLayer(apiKey, { - type: 'Road' - }); - bHybrid = new L.BingLayer(apiKey, { - type: 'AerialWithLabels' - }); - bAerial = new L.BingLayer(apiKey, { - type: 'Aerial' - }); - - console.debug('Adding bing layers'); - - c.addBaseLayer(bRoad, 'Bing Road'); - c.addBaseLayer(bHybrid, 'Bing Hybrid'); - c.addBaseLayer(bAerial, 'Bing Aerial'); - - return bAerial; - }; - - addBlueMarbleLayers = function(c) { - if (!L.tileLayer) { - return; - } - - var blueMarbleJpg, - blueMarblePng, - openGeoOSM; - - blueMarbleJpg = new L.tileLayer.wms( - 'http://maps.opengeo.org/geowebcache/service/wms', - {layers: 'bluemarble'} - ); - - blueMarblePng = new L.tileLayer.wms( - 'http://maps.opengeo.org/geowebcache/service/wms', - {layers: 'bluemarble', format: 'image/png'} - ); - - openGeoOSM = new L.tileLayer.wms( - 'http://maps.opengeo.org/geowebcache/service/wms', - {layers: 'openstreetmap', format: 'image/png'} - ); - - console.debug('Adding openGeo layers (blueMarbel and openGeo OSM)'); - - c.addBaseLayer(blueMarbleJpg, 'OpenGeo BlueMarble (jpg)'); - c.addBaseLayer(blueMarblePng, 'OpenGeo BlueMarble (png)'); - c.addBaseLayer(openGeoOSM, 'OpenGeo OSM (png)'); - - return openGeoOSM; - }; - - addGoogleLayers = function(c) { - if (!L.Google) { - return; - } - - var gPhysical, - gStreet, - gHybrid, - gSatellite; - - gPhysical = new L.Google('TERRAIN'); - gStreet = new L.Google('ROADMAP', - {maxZoom: 20 } - ); - gHybrid = new L.Google('HYBRID', - {maxZoom: 20 } - ); - gSatellite = new L.Google('SATELLITE', - {maxZoom: 22 } - ); - - console.debug('Adding google layers'); - - c.addBaseLayer(gPhysical, 'Google Physical'); - c.addBaseLayer(gStreet, 'Google Streets'); - c.addBaseLayer(gHybrid, 'Google Hybrid'); - c.addBaseLayer(gSatellite, 'Google Satellite'); - - return gSatellite; - }; - - addOsmLayer = function(c) { - if (!L.tileLayer) { - return; - } - - var layerOsm = new L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { - attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' - }); - - console.debug('Adding openstreetmap layer'); - - c.addBaseLayer(layerOsm, 'OpenStreetMap'); - return layerOsm; - }; - - addStamenLayer = function(c) { - if (!L.StamenTileLayer) { - return; - } - - var layerStamen = new L.StamenTileLayer('watercolor'); - - console.debug('Adding stamen watercolor layer'); - - c.addBaseLayer(layerStamen, 'Stamen Watercolor Map'); - return layerStamen; - }; - - onLocationError = function(message) { - console.info('Could not get GeoLocation', message); - console.debug(' - will continue with default location'); - - this.draw(); - }; - - onLocationSuccess = function(location) { - if (!location || !location.coords) { - console.debug('Invalid geoLocation found... no coordinates returned!'); - return; - } - - console.log('GeoLocation found. It will be used as center'); - console.debug(' - location:', location); - - mapOptions.center = [location.coords.latitude, location.coords.longitude]; - - this.draw(); - }; -}; diff --git a/src/main/webapp/js/map/layer/bing.js b/src/main/webapp/js/map/layer/bing.js new file mode 100644 index 00000000..6fac75b1 --- /dev/null +++ b/src/main/webapp/js/map/layer/bing.js @@ -0,0 +1,125 @@ +define(['leaflet', 'console'], function(L, logger) { + L.BingLayer = L.TileLayer.extend({ + options: { + subdomains: [0, 1, 2, 3], + type: 'Aerial', + attribution: 'Bing', + culture: '' + }, + + initialize: function(key, options) { + L.Util.setOptions(this, options); + + this._key = key; + this._url = null; + this.meta = {}; + this.loadMetadata(); + }, + + tile2quad: function(x, y, z) { + var quad = ''; + for (var i = z; i > 0; i--) { + var digit = 0; + var mask = 1 << (i - 1); + if ((x & mask) != 0) digit += 1; + if ((y & mask) != 0) digit += 2; + quad = quad + digit; + } + return quad; + }, + + getTileUrl: function(p, z) { + var z = this._getZoomForUrl(); + var subdomains = this.options.subdomains, + s = this.options.subdomains[Math.abs((p.x + p.y) % subdomains.length)]; + return this._url.replace('{subdomain}', s) + .replace('{quadkey}', this.tile2quad(p.x, p.y, z)) + .replace('{culture}', this.options.culture); + }, + + loadMetadata: function() { + var _this = this; + var cbid = '_bing_metadata_' + L.Util.stamp(this); + window[cbid] = function (meta) { + _this.meta = meta; + window[cbid] = undefined; + var e = document.getElementById(cbid); + e.parentNode.removeChild(e); + if (meta.errorDetails) { + logger.log("Leaflet Bing Plugin Error - Got metadata: " + meta.errorDetails); + return; + } + _this.initMetadata(); + }; + var url = document.location.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" + this.options.type + "?include=ImageryProviders&jsonp=" + cbid + + "&key=" + this._key + "&UriScheme=" + document.location.protocol.slice(0, -1); + var script = document.createElement("script"); + script.type = "text/javascript"; + script.src = url; + script.id = cbid; + document.getElementsByTagName("head")[0].appendChild(script); + }, + + initMetadata: function() { + var r = this.meta.resourceSets[0].resources[0]; + this.options.subdomains = r.imageUrlSubdomains; + this._url = r.imageUrl; + this._providers = []; + if (r.imageryProviders) { + for (var i = 0; i < r.imageryProviders.length; i++) { + var p = r.imageryProviders[i]; + for (var j = 0; j < p.coverageAreas.length; j++) { + var c = p.coverageAreas[j]; + var coverage = {zoomMin: c.zoomMin, zoomMax: c.zoomMax, active: false}; + var bounds = new L.LatLngBounds( + new L.LatLng(c.bbox[0]+0.01, c.bbox[1]+0.01), + new L.LatLng(c.bbox[2]-0.01, c.bbox[3]-0.01) + ); + coverage.bounds = bounds; + coverage.attrib = p.attribution; + this._providers.push(coverage); + } + } + } + this._update(); + }, + + _update: function() { + if (this._url == null || !this._map) return; + this._update_attribution(); + L.TileLayer.prototype._update.apply(this, []); + }, + + _update_attribution: function() { + var bounds = this._map.getBounds(); + var zoom = this._map.getZoom(); + for (var i = 0; i < this._providers.length; i++) { + var p = this._providers[i]; + if ((zoom <= p.zoomMax && zoom >= p.zoomMin) && + bounds.intersects(p.bounds)) { + if (!p.active && this._map.attributionControl) + this._map.attributionControl.addAttribution(p.attrib); + p.active = true; + } else { + if (p.active && this._map.attributionControl) + this._map.attributionControl.removeAttribution(p.attrib); + p.active = false; + } + } + }, + + onRemove: function(map) { + for (var i = 0; i < this._providers.length; i++) { + var p = this._providers[i]; + if (p.active && this._map.attributionControl) { + this._map.attributionControl.removeAttribution(p.attrib); + p.active = false; + } + } + + L.TileLayer.prototype.onRemove.apply(this, [map]); + } + }); + + return L.BingLayer; +}); diff --git a/src/main/webapp/js/map/layer/google.js b/src/main/webapp/js/map/layer/google.js new file mode 100644 index 00000000..7b802b54 --- /dev/null +++ b/src/main/webapp/js/map/layer/google.js @@ -0,0 +1,201 @@ +/* + * Google layer using Google Maps API + */ +define(['leaflet'], function(L) { + L.Google = L.Class.extend({ + includes: L.Mixin.Events, + + options: { + minZoom: 0, + maxZoom: 18, + tileSize: 256, + subdomains: 'abc', + errorTileUrl: '', + attribution: '', + opacity: 1, + continuousWorld: false, + noWrap: false, + mapOptions: { + backgroundColor: '#dddddd' + } + }, + + // Possible types: SATELLITE, ROADMAP, HYBRID, TERRAIN + initialize: function(type, options) { + L.Util.setOptions(this, options); + + this._ready = google.maps.Map != undefined; + if (!this._ready) L.Google.asyncWait.push(this); + + this._type = type || 'SATELLITE'; + }, + + onAdd: function(map, insertAtTheBottom) { + this._map = map; + this._insertAtTheBottom = insertAtTheBottom; + + // create a container div for tiles + this._initContainer(); + this._initMapObject(); + + // set up events + map.on('viewreset', this._resetCallback, this); + + this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this); + map.on('move', this._update, this); + + map.on('zoomanim', this._handleZoomAnim, this); + + //20px instead of 1em to avoid a slight overlap with google's attribution + map._controlCorners['bottomright'].style.marginBottom = "20px"; + + this._reset(); + this._update(); + }, + + onRemove: function(map) { + this._map._container.removeChild(this._container); + //this._container = null; + + this._map.off('viewreset', this._resetCallback, this); + + this._map.off('move', this._update, this); + + this._map.off('zoomanim', this._handleZoomAnim, this); + + map._controlCorners['bottomright'].style.marginBottom = "0em"; + //this._map.off('moveend', this._update, this); + }, + + getAttribution: function() { + return this.options.attribution; + }, + + setOpacity: function(opacity) { + this.options.opacity = opacity; + if (opacity < 1) { + L.DomUtil.setOpacity(this._container, opacity); + } + }, + + setElementSize: function(e, size) { + e.style.width = size.x + "px"; + e.style.height = size.y + "px"; + }, + + _initContainer: function() { + var tilePane = this._map._container, + first = tilePane.firstChild; + + if (!this._container) { + this._container = L.DomUtil.create('div', 'leaflet-google-layer leaflet-top leaflet-left'); + this._container.id = "_GMapContainer_" + L.Util.stamp(this); + this._container.style.zIndex = "auto"; + } + + tilePane.insertBefore(this._container, first); + + this.setOpacity(this.options.opacity); + this.setElementSize(this._container, this._map.getSize()); + }, + + _initMapObject: function() { + if (!this._ready) return; + this._google_center = new google.maps.LatLng(0, 0); + var map = new google.maps.Map(this._container, { + center: this._google_center, + zoom: 0, + tilt: 0, + mapTypeId: google.maps.MapTypeId[this._type], + disableDefaultUI: true, + keyboardShortcuts: false, + draggable: false, + disableDoubleClickZoom: true, + scrollwheel: false, + streetViewControl: false, + styles: this.options.mapOptions.styles, + backgroundColor: this.options.mapOptions.backgroundColor + }); + + var _this = this; + this._reposition = google.maps.event.addListenerOnce(map, "center_changed", + function() { _this.onReposition(); }); + this._google = map; + + google.maps.event.addListenerOnce(map, "idle", + function() { _this._checkZoomLevels(); }); + }, + + _checkZoomLevels: function() { + //setting the zoom level on the Google map may result in a different zoom level than the one requested + //(it won't go beyond the level for which they have data). + // verify and make sure the zoom levels on both Leaflet and Google maps are consistent + if (this._google.getZoom() !== this._map.getZoom()) { + //zoom levels are out of sync. Set the leaflet zoom level to match the google one + this._map.setZoom( this._google.getZoom() ); + } + }, + + _resetCallback: function(e) { + this._reset(e.hard); + }, + + _reset: function(clearOldContainer) { + this._initContainer(); + }, + + _update: function(e) { + if (!this._google) return; + this._resize(); + + var center = e && e.latlng ? e.latlng : this._map.getCenter(); + var _center = new google.maps.LatLng(center.lat, center.lng); + + this._google.setCenter(_center); + this._google.setZoom(this._map.getZoom()); + + this._checkZoomLevels(); + //this._google.fitBounds(google_bounds); + }, + + _resize: function() { + var size = this._map.getSize(); + if (this._container.style.width == size.x && + this._container.style.height == size.y) + return; + this.setElementSize(this._container, size); + this.onReposition(); + }, + + + _handleZoomAnim: function (e) { + var center = e.center; + var _center = new google.maps.LatLng(center.lat, center.lng); + + this._google.setCenter(_center); + this._google.setZoom(e.zoom); + }, + + + onReposition: function() { + if (!this._google) return; + google.maps.event.trigger(this._google, "resize"); + } + }); + + L.Google.asyncWait = []; + L.Google.asyncInitialize = function() { + var i; + for (i = 0; i < L.Google.asyncWait.length; i++) { + var o = L.Google.asyncWait[i]; + o._ready = true; + if (o._container) { + o._initMapObject(); + o._update(); + } + } + L.Google.asyncWait = []; + }; + + return L.Google; +}); diff --git a/src/main/webapp/js/map/locatebyip.js b/src/main/webapp/js/map/locatebyip.js deleted file mode 100644 index 1dc278e7..00000000 --- a/src/main/webapp/js/map/locatebyip.js +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Provides L.Map with convenient shortcuts for using ip based geolocation features. - */ -L.IpProvider = {}; -L.IpProvider.None = null; -L.IpProvider.FreeGeoIp = { - url: 'http://freegeoip.net/json/', - cbParam: 'callback', - buildLocationObject: function (data) { - if (!data) { - return null; - } - - return { - coords: { - accuracy: 10000, - latitude: parseFloat(data.latitude), - longitude: parseFloat(data.longitude) - }, - timestamp: new Date().getTime() - }; - } -}; -L.IpProvider.GeoPlugin = { - url: 'http://www.geoplugin.net/json.gp', - cbParam: 'jsoncallback', - buildLocationObject: function (data) { - if (!data) { - return null; - } - - return { - coords: { - accuracy: 10000, - /* jshint ignore:start */ - latitude: parseFloat(data.geoplugin_latitude), - longitude: parseFloat(data.geoplugin_longitude) - /* jshint ignore:end */ - }, - timestamp: new Date().getTime() - }; - } -}; -L.IpProvider.Wikimedia = { - url: 'http://geoiplookup.wikimedia.org/', - cbParam: '', - buildLocationObject: function () { - var data = window.Geo, - result = { - coords: { - accuracy: 10000, - latitude: parseFloat(data.lat), - longitude: parseFloat(data.lon) - }, - timestamp: new Date().getTime() - }; - - delete window.Geo; - return result; - } -}; - -L.IpLocator = L.Class.extend({ - - locateByIp: function (source, responseCallback, errorCallback) { - var handlerFn = L.bind(function (data) { - this._handleIpResponse(source, data, responseCallback, errorCallback); - }, this); - - if (source.cbParam === undefined || source.cbParam === null || source.cbParam === '') { - this._loadScript(source.url, handlerFn); - return; - } - - window.cbObject = {}; - window.cbObject.fn = handlerFn; - this._loadScript(source.url + '?' + source.cbParam + '= window.cbObject.fn'); - }, - - _handleIpResponse: function (source, data, responseCallback, errorCallback) { - window.cbObject = null; - delete window.cbObject; - - var pos = source.buildLocationObject(data); - if (pos === null) { - if (errorCallback) { errorCallback('Could not get location.'); } - } else if (responseCallback) { - responseCallback(pos); - } - }, - - _loadScript: function (url, callback, type) { - var script = document.createElement('script'); - script.type = (type === undefined) ? 'text/javascript' : type; - - if (typeof callback === 'function') { - if (script.readyState) { - script.onreadystatechange = function () { - if (script.readyState === 'loaded' || script.readyState === 'complete') { - script.onreadystatechange = null; - callback(); - } - }; - } else { - script.onload = function () { callback(); }; - } - } - - script.src = url; - document.getElementsByTagName('head')[0].appendChild(script); - } -}); - -L.Map.include({ - _defaultLocateOptions: { - ipProvider: L.IpProvider.Wikimedia, - timeout: 10000, - watch: false - // setView: false - // maxZoom: <Number> - // maximumAge: 0 - // enableHighAccuracy: false - }, - - locate: function (/*Object*/ options) { - options = this._locateOptions = L.extend(this._defaultLocateOptions, options); - - if (!navigator.geolocation && !options.ipProvider) { - this._handleGeolocationError({ - code: 0, - message: 'Geolocation not supported.' - }); - return this; - } - - var onResponse = L.bind(this._handleGeolocationResponse, this), - onError = L.bind((options.ipProvider) ? this._fallbackToIp : this._handleGeolocationError, this); - - if (options.watch) { - this._locationWatchId = - navigator.geolocation.watchPosition(onResponse, onError, options); - } else { - navigator.geolocation.getCurrentPosition(onResponse, onError, options); - } - - return this; - }, - - _fallbackToIp: function (errMsg) { - if (!this._locateOptions.ipProvider) { - this._handleGeolocationError(errMsg); - return; - } - - var onResponse = L.bind(this._handleGeolocationResponse, this), - onError = L.bind(this._handleGeolocationError, this); - - (new L.IpLocator()).locateByIp(this._locateOptions.ipProvider, onResponse, onError); - } - -}); diff --git a/src/main/webapp/js/service/serviceConfiguration.js b/src/main/webapp/js/service/serviceConfiguration.js index f2cab3f9..ba0d19f2 100644 --- a/src/main/webapp/js/service/serviceConfiguration.js +++ b/src/main/webapp/js/service/serviceConfiguration.js @@ -1,74 +1,78 @@ -function ServiceConfiguration(/* Websocket */ ws) { - var actionName = 'getConfiguration'; - var websocket = null; +define(['jQuery', 'leaflet', 'console', 'isochrone/configuration'], function($, L, logger, Configuration) { + function ServiceConfiguration(ws) { + var actionName = 'getConfiguration'; + var websocket = null; - // Constructor + // Constructor - websocket = ws; + websocket = ws; - // Public methods + // Public methods - this.getFromServer = function() { - $(document).on(actionName, onServerResponse.bind(this)); - websocket.sendWsMessage('{"action" : "' + actionName + '"}'); - }; + this.getFromServer = function() { + $(document).on(actionName, onServerResponse.bind(this)); + websocket.sendWsMessage('{"action" : "' + actionName + '"}'); + }; + + // Private methods - // Private methods + /** + * Reads the enabled datasets and stores them in a client side configuration singleton + * each dataset contains: + * <ul> + * <li>bbox the spatial extent of the specified area</li> + * <li>queryPoint the a 2D-coordinate of the query point</li> + * <li>time the arrival or departure time of the query point</li> + * </ul> + */ + onServerResponse = function(data) { + var config = Configuration.getInstance(), + datasets = data.defaultDatasets, + result = {}; - /** - * Reads the enabled datasets and stores them in a client side configuration singleton - * each dataset contains: - * <ul> - * <li>bbox the spatial extent of the specified area</li> - * <li>queryPoint the a 2D-coordinate of the query point</li> - * <li>time the arrival or departure time of the query point</li> - * </ul> - */ - onServerResponse = function(data) { - var config = Configuration(), - datasets = data.defaultDatasets, - result = {}; + logger.debug('Handling event "' + actionName + '":', data); + for (var i = 0; i < datasets.length; i++) { + var dataset = datasets[i]; + var boundLower = new L.Point(dataset.bbox.minX, dataset.bbox.minY); + var boundUpper = new L.Point(dataset.bbox.maxX, dataset.bbox.maxY); + var latlngLower = convertToLatLng(boundLower); + var latlngUpper = convertToLatLng(boundUpper); - console.debug('Handling event "' + actionName + '":', data); - for (var i = 0; i < datasets.length; i++) { - var dataset = datasets[i]; - var boundLower = new L.Point(dataset.bbox.minX, dataset.bbox.minY); - var boundUpper = new L.Point(dataset.bbox.maxX, dataset.bbox.maxY); - var latlngLower = convertToLatLng(boundLower); - var latlngUpper = convertToLatLng(boundUpper); + var dSetConfig = { + bBox: new L.Bounds(boundLower, boundUpper), + datasetName: dataset.name, + date: dataset.date, + isoEdgeLayer: dataset.isoEdgeLayer, + isoVertexLayer: dataset.isoVertexLayer, + isoCoverageLayer: dataset.isoCoverageLayer, + latLngBBox: new L.latLngBounds(latlngLower, latlngUpper), + prefix: dataset.prefix, + queryPoint: new L.LatLng(dataset.queryPoint.x, dataset.queryPoint.y), + time: dataset.time + }; - var dSetConfig = { - bBox: new L.Bounds(boundLower, boundUpper), - datasetName: dataset.name, - date: dataset.date, - isoEdgeLayer: dataset.isoEdgeLayer, - isoVertexLayer: dataset.isoVertexLayer, - isoCoverageLayer: dataset.isoCoverageLayer, - latLngBBox: new L.latLngBounds(latlngLower, latlngUpper), - prefix: dataset.prefix, - queryPoint: new L.LatLng(dataset.queryPoint.x, dataset.queryPoint.y), - time: dataset.time - }; + if (dataset.totalInhabitants) { + dSetConfig.totalInhabitants = dataset.totalInhabitants; + } - if (dataset.totalInhabitants) { - dSetConfig.totalInhabitants = dataset.totalInhabitants; + config.addDatasetConfig(dSetConfig); } - config.addDatasetConfig(dSetConfig); - } + return result; + }; - return result; + /** + * Converts a point from projection EPSG:3857 to LatLng (EPSG:4326) + * + * @see http://developer.tomtom.com/docs/read/map_toolkit/javascript_sdk_2_0/Migration_Guide + * @param L.Point the point to convert (containing EPSG:3857 x and y) + * @return L.latLng the matching L.latLng object (coordinates in EPSG:4326) + */ + convertToLatLng = function(point) { + var earthRadius = 6378137; + return L.Projection.SphericalMercator.unproject(point.divideBy(earthRadius)); + }; }; - /** - * Converts a point from projection EPSG:3857 to LatLng (EPSG:4326) - * - * @see http://developer.tomtom.com/docs/read/map_toolkit/javascript_sdk_2_0/Migration_Guide - * @param L.Point the point to convert (containing EPSG:3857 x and y) - * @return L.latLng the matching L.latLng object (coordinates in EPSG:4326) - */ - convertToLatLng = function(point) { - var earthRadius = 6378137; - return L.Projection.SphericalMercator.unproject(point.divideBy(earthRadius)); - }; -}; + return ServiceConfiguration; +}); diff --git a/src/main/webapp/js/service/websocket.js b/src/main/webapp/js/service/websocket.js index d5754372..cf93427d 100644 --- a/src/main/webapp/js/service/websocket.js +++ b/src/main/webapp/js/service/websocket.js @@ -1,53 +1,57 @@ -function Websocket() { - var wsUri = "ws://localhost:8090/isochrone/websocket"; - var websocket = null; - - // Constructor - - websocket = new WebSocket(wsUri); - websocket.onopen = function(evt) { onOpen(evt); }; - websocket.onclose = function(evt) { onClose(evt); }; - websocket.onmessage = function(evt) { onMessage(evt); }; - websocket.onerror = function(evt) { onError(evt); }; - - // Public methods - - this.sendWsMessage = function(msg) { - if (websocket.readyState === websocket.OPEN) { - websocket.send(msg); - } else if (websocket.readyState === websocket.CONNECTING) { - setTimeout(this.sendWsMessage.bind(this, msg), 100); - } else { - console.error("Could not send message using websocket: socket closed (or closing)"); - } +define(['jQuery', 'console'], function($, logger) { + function Websocket() { + var wsUri = "ws://localhost:8090/isochrone/websocket"; + var websocket = null; + + // Constructor + + websocket = new WebSocket(wsUri); + websocket.onopen = function(evt) { onOpen(evt); }; + websocket.onclose = function(evt) { onClose(evt); }; + websocket.onmessage = function(evt) { onMessage(evt); }; + websocket.onerror = function(evt) { onError(evt); }; + + // Public methods + + this.sendWsMessage = function(msg) { + if (websocket.readyState === websocket.OPEN) { + websocket.send(msg); + } else if (websocket.readyState === websocket.CONNECTING) { + setTimeout(this.sendWsMessage.bind(this, msg), 100); + } else { + logger.error("Could not send message using websocket: socket closed (or closing)"); + } + }; + + // Private methods + + onOpen = function(evt) { + logger.debug("Opening connection to websocket"); + }; + + onClose = function(evt) { + logger.debug("Closing connection to websocket"); + }; + + onMessage = function(evt) { + var jsonString = evt.data; + logger.debug('Configuration returned by server: ', jsonString); + + var jsonResponse = jQuery.parseJSON(jsonString); + if (!jsonResponse.action) { + logger.warn("Invalid server response. No action attribute set"); + return; + } + + var e = jQuery.Event(jsonResponse.action, jsonResponse); + $(document).trigger(e); + }; + + onError = function(evt) { + logger.error('Could not get data from configuration websocket'); + logger.warn('Error data: ' + evt.data); + }; }; - // Private methods - - onOpen = function(evt) { - console.debug("Opening connection to websocket"); - }; - - onClose = function(evt) { - console.debug("Closing connection to websocket"); - }; - - onMessage = function(evt) { - var jsonString = evt.data; - console.debug('Configuration returned by server: ', jsonString); - - var jsonResponse = jQuery.parseJSON(jsonString); - if (!jsonResponse.action) { - console.warn("Invalid server response. No action attribute set"); - return; - } - - var e = jQuery.Event(jsonResponse.action, jsonResponse); - $(document).trigger(e); - }; - - onError = function(evt) { - console.error('Could not get data from configuration websocket'); - console.warn('Error data: ' + evt.data); - }; -}; + return Websocket; +}); diff --git a/src/main/webapp/js/versions.txt b/src/main/webapp/js/versions.txt index 390c2b30..a4b356bc 100644 --- a/src/main/webapp/js/versions.txt +++ b/src/main/webapp/js/versions.txt @@ -1,5 +1,5 @@ Sources: -######### +-------- console.js: https://code.google.com/p/console-js/ (v. 0.1) bing.js: https://github.com/shramov/leaflet-plugins/ -- GitLab