diff --git a/README.md b/README.md index 9d9bd7c..11d6501 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A Node-RED node to provide world map web page for plotting "things" on. ### Changes - - v1.0.0 - now uses socket.io to connect to backend - means this node now has an input connection + - v1.0.x - now uses socket.io to connect to backend - means this node now has an input connection (like "proper" nodes should :-), and you no longer need a websocket node in parallel. Obviously this is a breaking change hence the major version number bump. Also thus adds a `worldmap in` node to handle events coming from the map interaction. (to be documented more fully but are fairly obvious). @@ -50,6 +50,8 @@ However there are several specials... - **unknown** : pseudo Nato style yellow square. - **earthquake** : black circle - diameter proportional to magnitude. +#### Areas + If the payload contains an **area** property - that is an array of co-ordinates, e.g. [ [51.05, -0.08], [51.5, -1], [51.2, -0.047] ] @@ -68,7 +70,7 @@ Right-clicking on an icon will allow you to delete it. If you select the **drawing** layer you can also add polylines, polygons and rectangles. -All these events generate messages that can be received by using a **websocket in** node set to the same endpoint. For example: +All these events generate messages that can be received by using a **worldmap in** node. For example: add:point,50.98523,-1.40625,joe,spot,test del:joe @@ -79,7 +81,7 @@ All these events generate messages that can be received by using a **websocket i ### Control -You can also control the map via the websocket, by sending in a msg.payload containing a **command** object. +You can also control the map via the node, by sending in a msg.payload containing a **command** object. Optional properties include @@ -110,10 +112,10 @@ Demo Flow --------- The following example gets recent earthquakes from USGS, parses the result, -formats up the msg as per above and sends to the websocket to plot on the map. +formats up the msg as per above and sends to the node to plot on the map. It also shows how to zoom and move the map or add a new layer. - [{"id":"f63d823.f09c28","type":"websocket-listener","path":"/ws/worldmap","wholemsg":null},{"id":"6caef267.93510c","type":"inject","name":"","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":217,"y":398,"z":"f307b843.0cf848","wires":[["fb7109d5.048ef8"]]},{"id":"fb7109d5.048ef8","type":"function","name":"add new layer","func":"msg.payload = {};\nmsg.payload.command = {};\n\nvar u = 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png';\nvar o = JSON.stringify({ maxZoom: 19, attribution: '© OpenStreetMap'});\n\nmsg.payload.command.map = {name:\"OSMhot\", url:u, opt:o};\nmsg.payload.command.layer = \"OSMhot\";\n\nreturn msg;","outputs":1,"noerr":0,"x":454,"y":433,"z":"f307b843.0cf848","wires":[["e9c3a4cd.163c58"]]},{"id":"e9c3a4cd.163c58","type":"websocket out","name":"","server":"f63d823.f09c28","client":"","x":753.5,"y":540,"z":"f307b843.0cf848","wires":[]},{"id":"b68e0d77.4971f","type":"function","name":"USGS Quake monitor csv re-parse","func":"msg.payload.lat = msg.payload.latitude;\nmsg.payload.lon = msg.payload.longitude;\nmsg.payload.layer = \"earthquake\";\nmsg.payload.name = msg.payload.id;\nmsg.payload.icon = \"globe\";\nmsg.payload.iconColor = \"orange\";\n\ndelete msg.payload.latitude;\ndelete msg.payload.longitude;\t\nreturn msg;","outputs":1,"noerr":0,"x":416.5,"y":560,"z":"f307b843.0cf848","wires":[["e9c3a4cd.163c58"]]},{"id":"1a0508d4.e5faf7","type":"function","name":"move and zoom","func":"msg.payload = { command:{layer:\"Esri Terrain\",lat:0,lon:0,zoom:3} };\nreturn msg;","outputs":1,"noerr":0,"x":427,"y":476,"z":"f307b843.0cf848","wires":[["e9c3a4cd.163c58"]]},{"id":"8d1dcc2c.72e23","type":"csv","name":"","sep":",","hdrin":true,"hdrout":"","multi":"one","ret":"\\n","temp":"","x":250,"y":500,"z":"f307b843.0cf848","wires":[["b68e0d77.4971f"]]},{"id":"8fbd9df9.70426","type":"inject","name":"","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":163,"y":440,"z":"f307b843.0cf848","wires":[["1a0508d4.e5faf7"]]},{"id":"b8f3fe3f.470c","type":"http request","name":"","method":"GET","url":"http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.csv","x":145.5,"y":560,"z":"f307b843.0cf848","wires":[["8d1dcc2c.72e23"]]},{"id":"47e1240c.b81edc","type":"inject","name":"Quakes","topic":"","payload":"","payloadType":"none","repeat":"900","crontab":"","once":false,"x":90,"y":500,"z":"f307b843.0cf848","wires":[["b8f3fe3f.470c"]]},{"id":"784ff2e9.87b00c","type":"worldmap","name":"","x":798,"y":499,"z":"f307b843.0cf848","wires":[]}] + [{"id":"f7950c21.019f5","type":"worldmap","z":"896b28a8.437658","name":"","x":670,"y":680,"wires":[]},{"id":"bb057b8a.4fe2c8","type":"inject","z":"896b28a8.437658","name":"","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":110,"y":640,"wires":[["b8545e85.5ba4c"]]},{"id":"b8545e85.5ba4c","type":"function","z":"896b28a8.437658","name":"add new layer","func":"msg.payload = {};\nmsg.payload.command = {};\n\nvar u = 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png';\nvar o = JSON.stringify({ maxZoom: 19, attribution: '© OpenStreetMap'});\n\nmsg.payload.command.map = {name:\"OSMhot\", url:u, opt:o};\nmsg.payload.command.layer = \"OSMhot\";\n\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":640,"wires":[["f7950c21.019f5"]]},{"id":"e6cc0a05.14edd8","type":"function","z":"896b28a8.437658","name":"USGS Quake monitor csv re-parse","func":"msg.payload.lat = msg.payload.latitude;\nmsg.payload.lon = msg.payload.longitude;\nmsg.payload.layer = \"earthquake\";\nmsg.payload.name = msg.payload.id;\nmsg.payload.icon = \"globe\";\nmsg.payload.iconColor = \"orange\";\n\ndelete msg.payload.latitude;\ndelete msg.payload.longitude;\t\nreturn msg;","outputs":1,"noerr":0,"x":460,"y":780,"wires":[["f7950c21.019f5"]]},{"id":"84b8388.5e943c8","type":"function","z":"896b28a8.437658","name":"move and zoom","func":"msg.payload = { command:{layer:\"Esri Terrain\",lat:0,lon:0,zoom:3} };\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":680,"wires":[["f7950c21.019f5"]]},{"id":"5c317188.d2f31","type":"csv","z":"896b28a8.437658","name":"","sep":",","hdrin":true,"hdrout":"","multi":"one","ret":"\\n","temp":"","x":310,"y":720,"wires":[["e6cc0a05.14edd8"]]},{"id":"cfafad11.2f299","type":"inject","z":"896b28a8.437658","name":"","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":110,"y":680,"wires":[["84b8388.5e943c8"]]},{"id":"f0d75b03.39d618","type":"http request","z":"896b28a8.437658","name":"","method":"GET","url":"http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.csv","x":190,"y":780,"wires":[["5c317188.d2f31"]]},{"id":"87da03a.eb8a3","type":"inject","z":"896b28a8.437658","name":"Quakes","topic":"","payload":"","payloadType":"none","repeat":"900","crontab":"","once":false,"x":120,"y":720,"wires":[["f0d75b03.39d618"]]}] Car icon made by Freepik from www.flaticon.com is licensed by CC 3.0 BY. diff --git a/examples/Basic Map.json b/examples/Basic Map.json index 3545306..cedf43e 100644 --- a/examples/Basic Map.json +++ b/examples/Basic Map.json @@ -1 +1 @@ -[{"id":"8121855.018af78","type":"websocket-listener","z":"1262f4f5.eb742b","path":"/ws/worldmap","wholemsg":"false"},{"id":"51100e2e.29241","type":"worldmap","z":"1262f4f5.eb742b","name":"","x":620,"y":160,"wires":[]},{"id":"db3c792a.40f0b8","type":"inject","z":"1262f4f5.eb742b","name":"","topic":"","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"x":150,"y":220,"wires":[["600727dc.35b7d8"]]},{"id":"65c65456.04d1ec","type":"websocket out","z":"1262f4f5.eb742b","name":"","server":"8121855.018af78","client":"","x":580,"y":220,"wires":[]},{"id":"600727dc.35b7d8","type":"function","z":"1262f4f5.eb742b","name":"Add thing to map","func":"var thing = {\n name:\"Wally\", \n lat:51, \n lon:-1.45,\n icon:\"car\",\n iconColor:\"darkred\",\n extrainfo:\"Some extra information\"\n};\nmsg.payload = thing;\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":220,"wires":[["65c65456.04d1ec"]]},{"id":"54ae7eea.a705b","type":"comment","z":"1262f4f5.eb742b","name":"Simple map - click inject to send thing to map.","info":"Adds a map at http://(your-server-ip):1880/worldmap. \n\nThe `function` node creates an object with some basic properties required to add to a map.","x":280,"y":160,"wires":[]}] +[{"id":"a728c021.9969f","type":"worldmap","z":"896b28a8.437658","name":"","x":570,"y":940,"wires":[]},{"id":"ec9da974.051b48","type":"inject","z":"896b28a8.437658","name":"","topic":"","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"x":140,"y":940,"wires":[["f77a7ed4.f955d"]]},{"id":"f77a7ed4.f955d","type":"function","z":"896b28a8.437658","name":"Add thing to map","func":"var thing = {\n name:\"Wally\", \n lat:51, \n lon:-1.45,\n icon:\"car\",\n iconColor:\"darkred\",\n extrainfo:\"Some extra information\"\n};\nmsg.payload = thing;\nreturn msg;","outputs":1,"noerr":0,"x":330,"y":940,"wires":[["a728c021.9969f"]]},{"id":"cd09f7be.079518","type":"comment","z":"896b28a8.437658","name":"Simple map - click inject to send thing to map.","info":"Adds a map at http://(your-server-ip):1880/worldmap. \n\nThe `function` node creates an object with some basic properties required to add to a map.","x":270,"y":880,"wires":[]}] diff --git a/examples/Earthquake.json b/examples/Earthquake.json index 1d24876..5d0024a 100644 --- a/examples/Earthquake.json +++ b/examples/Earthquake.json @@ -1 +1 @@ -[{"id":"f874f266.cc706","type":"websocket-listener","path":"/ws/worldmap","wholemsg":"false"},{"id":"586e15fc.0ed3ec","type":"function","z":"be2ce773.41d318","name":"remap property names","func":"msg.payload.lat = msg.payload.latitude;\nmsg.payload.lon = msg.payload.longitude;\nmsg.payload.layer = msg.payload.type;\nmsg.payload.icon = msg.payload.type;\nmsg.payload.name = msg.payload.id;\n\ndelete msg.payload.latitude;\ndelete msg.payload.longitude;\ndelete msg.payload.type;\nreturn msg;","outputs":1,"noerr":0,"x":460,"y":200,"wires":[["1b385a58.bb0c96","e4866a34.a611c8"]]},{"id":"1b385a58.bb0c96","type":"debug","z":"be2ce773.41d318","name":"","active":true,"console":"false","complete":"payload","x":690,"y":140,"wires":[]},{"id":"c2ed9e42.9aaa6","type":"csv","z":"be2ce773.41d318","name":"","sep":",","hdrin":true,"hdrout":"","multi":"one","ret":"\\n","temp":"","x":450,"y":120,"wires":[["586e15fc.0ed3ec"]]},{"id":"781d454f.ede9ac","type":"http request","z":"be2ce773.41d318","name":"","method":"GET","url":"http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.csv","x":290,"y":120,"wires":[["c2ed9e42.9aaa6"]]},{"id":"9afc700f.65ff8","type":"inject","z":"be2ce773.41d318","name":"quakes","topic":"","payload":"","payloadType":"str","repeat":"900","crontab":"","once":true,"x":130,"y":120,"wires":[["781d454f.ede9ac"]]},{"id":"c72fd4ec.7349d8","type":"comment","z":"be2ce773.41d318","name":"Earthquake !!!","info":"Press the button on the `inject` node to get the list of earthquakes from the USGS public API.\n\nThis returns a CSV - which we then parse using the `CSV` node into individual objects.\n\nThese are then passed through a `function` to remap the property names into ones \nsuitable for the map node, and passed to the `web-socket` out node.\n\nThe map will be served at http://localhost:1880/worldmap .\nYou will need to zoom out :-)","x":150,"y":60,"wires":[]},{"id":"884c0ba4.df3668","type":"worldmap","z":"be2ce773.41d318","name":"","x":680,"y":260,"wires":[]},{"id":"e4866a34.a611c8","type":"websocket out","z":"be2ce773.41d318","name":"","server":"f874f266.cc706","client":"","x":710,"y":200,"wires":[]}] +[{"id":"5dc0977b.699a88","type":"function","z":"896b28a8.437658","name":"remap property names","func":"msg.payload.lat = msg.payload.latitude;\nmsg.payload.lon = msg.payload.longitude;\nmsg.payload.layer = msg.payload.type;\nmsg.payload.icon = msg.payload.type;\nmsg.payload.name = msg.payload.id;\n\ndelete msg.payload.latitude;\ndelete msg.payload.longitude;\ndelete msg.payload.type;\nreturn msg;","outputs":1,"noerr":0,"x":460,"y":700,"wires":[["847c1290.11a68","a733a03e.317f2"]]},{"id":"847c1290.11a68","type":"debug","z":"896b28a8.437658","name":"","active":true,"console":"false","complete":"payload","x":690,"y":640,"wires":[]},{"id":"827adf5c.4cff8","type":"csv","z":"896b28a8.437658","name":"","sep":",","hdrin":true,"hdrout":"","multi":"one","ret":"\\n","temp":"","x":450,"y":620,"wires":[["5dc0977b.699a88"]]},{"id":"3dcc8183.4e218e","type":"http request","z":"896b28a8.437658","name":"","method":"GET","url":"http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.csv","x":290,"y":620,"wires":[["827adf5c.4cff8"]]},{"id":"5b57c4b6.56a8fc","type":"inject","z":"896b28a8.437658","name":"quakes","topic":"","payload":"","payloadType":"str","repeat":"900","crontab":"","once":true,"x":130,"y":620,"wires":[["3dcc8183.4e218e"]]},{"id":"e41b836e.f0c66","type":"comment","z":"896b28a8.437658","name":"Earthquake !!!","info":"Press the button on the `inject` node to get the list of earthquakes from the USGS public API.\n\nThis returns a CSV - which we then parse using the `CSV` node into individual objects.\n\nThese are then passed through a `function` to remap the property names into ones \nsuitable for the map node, and passed to the `web-socket` out node.\n\nThe map will be served at http://localhost:1880/worldmap .\nYou will need to zoom out :-)","x":130,"y":560,"wires":[]},{"id":"a733a03e.317f2","type":"worldmap","z":"896b28a8.437658","name":"","x":690,"y":700,"wires":[]},{"id":"854a8d7f.2cbdf","type":"inject","z":"896b28a8.437658","name":"zoom out map","topic":"","payload":"{\"command\":{\"layer\":\"OSM\",\"lat\":20,\"lon\":0,\"zoom\":2}}","payloadType":"json","repeat":"","crontab":"","once":true,"x":460,"y":760,"wires":[["a733a03e.317f2"]]}] diff --git a/package.json b/package.json index 1e4a85c..c622eca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name" : "node-red-contrib-web-worldmap", - "version" : "1.0.4", + "version" : "1.0.5", "description" : "A Node-RED node to provide a web page of a world map for plotting things on.", "dependencies" : { "express": "4.*", diff --git a/worldmap.html b/worldmap.html index d0b5cf5..fe07ed1 100644 --- a/worldmap.html +++ b/worldmap.html @@ -40,6 +40,7 @@
Any other sub-properties of msg.payload
will be added to the icon popup text box as extra information.
Icons of type plane, ship or car will use built in SVG icons that align to the
bearing
value.
There are some extra commands to control the map.
+ @@ -161,15 +163,9 @@ if ( window.localStorage.hasOwnProperty("maxage") ) { // Create the Initial Map object. map = new L.map('map').setView(startpos, startzoom); -// Add the locate my position button -L.easyButton( 'fa-crosshairs fa-lg', function() { - map.locate({setView: true, maxZoom: 15}); -}, "Locate me").addTo(map); - -L.Control.measureControl().addTo(map); - +// Move some bits around if in an iframe if (window.self !== window.top) { - console.log("In an Iframe"); + console.log("In an iframe"); (document.getElementById("topbar").style.display="none"); (document.getElementById("map").style.top="0px"); (document.getElementById("results").style.right="50px"); @@ -183,12 +179,28 @@ if (window.self !== window.top) { L.easyButton( 'fa-bars fa-lg', function() { toggleMenu(); }, "Toggle menu", "topright").addTo(map); } else { - console.log("NOT in an Iframe"); + console.log("NOT in an iframe") window.onbeforeunload = function(e) { return 'Reloading will delete all the local markers, including any drawing on the "drawing" layer'; }; } +// Add the fullscreen button +L.control.fullscreen().addTo(map); + +// map.on('fullscreenchange', function () { +// if (map.isFullscreen()) { console.log('entered fullscreen') } +// else { console.log('exited fullscreen'); } +// }); + +// Add the locate my position button +L.easyButton( 'fa-crosshairs fa-lg', function() { + map.locate({setView: true, maxZoom: 15}); +}, "Locate me").addTo(map); + +// Add the measure/ruler button +L.Control.measureControl().addTo(map); + // Create the clear heatmap button var clrHeat = L.easyButton( 'Reset Heatmap', function() { console.log("reset heatmap"); diff --git a/worldmap/leaflet/Leaflet.fullscreen.min.js b/worldmap/leaflet/Leaflet.fullscreen.min.js new file mode 100755 index 0000000..184cc7f --- /dev/null +++ b/worldmap/leaflet/Leaflet.fullscreen.min.js @@ -0,0 +1 @@ +L.Control.Fullscreen=L.Control.extend({options:{position:"topleft",title:{"false":"View Fullscreen","true":"Exit Fullscreen"}},onAdd:function(map){var container=L.DomUtil.create("div","leaflet-control-fullscreen leaflet-bar leaflet-control");this.link=L.DomUtil.create("a","leaflet-control-fullscreen-button leaflet-bar-part",container);this.link.href="#";this._map=map;this._map.on("fullscreenchange",this._toggleTitle,this);this._toggleTitle();L.DomEvent.on(this.link,"click",this._click,this);return container},_click:function(e){L.DomEvent.stopPropagation(e);L.DomEvent.preventDefault(e);this._map.toggleFullscreen(this.options)},_toggleTitle:function(){this.link.title=this.options.title[this._map.isFullscreen()]}});L.Map.include({isFullscreen:function(){return this._isFullscreen||false},toggleFullscreen:function(options){var container=this.getContainer();if(this.isFullscreen()){if(options&&options.pseudoFullscreen){this._disablePseudoFullscreen(container)}else if(document.exitFullscreen){document.exitFullscreen()}else if(document.mozCancelFullScreen){document.mozCancelFullScreen()}else if(document.webkitCancelFullScreen){document.webkitCancelFullScreen()}else if(document.msExitFullscreen){document.msExitFullscreen()}else{this._disablePseudoFullscreen(container)}}else{if(options&&options.pseudoFullscreen){this._enablePseudoFullscreen(container)}else if(container.requestFullscreen){container.requestFullscreen()}else if(container.mozRequestFullScreen){container.mozRequestFullScreen()}else if(container.webkitRequestFullscreen){container.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT)}else if(container.msRequestFullscreen){container.msRequestFullscreen()}else{this._enablePseudoFullscreen(container)}}},_enablePseudoFullscreen:function(container){L.DomUtil.addClass(container,"leaflet-pseudo-fullscreen");this._setFullscreen(true);this.invalidateSize();this.fire("fullscreenchange")},_disablePseudoFullscreen:function(container){L.DomUtil.removeClass(container,"leaflet-pseudo-fullscreen");this._setFullscreen(false);this.invalidateSize();this.fire("fullscreenchange")},_setFullscreen:function(fullscreen){this._isFullscreen=fullscreen;var container=this.getContainer();if(fullscreen){L.DomUtil.addClass(container,"leaflet-fullscreen-on")}else{L.DomUtil.removeClass(container,"leaflet-fullscreen-on")}},_onFullscreenChange:function(e){var fullscreenElement=document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement;if(fullscreenElement===this.getContainer()&&!this._isFullscreen){this._setFullscreen(true);this.fire("fullscreenchange")}else if(fullscreenElement!==this.getContainer()&&this._isFullscreen){this._setFullscreen(false);this.fire("fullscreenchange")}}});L.Map.mergeOptions({fullscreenControl:false});L.Map.addInitHook(function(){if(this.options.fullscreenControl){this.fullscreenControl=new L.Control.Fullscreen(this.options.fullscreenControl);this.addControl(this.fullscreenControl)}var fullscreenchange;if("onfullscreenchange"in document){fullscreenchange="fullscreenchange"}else if("onmozfullscreenchange"in document){fullscreenchange="mozfullscreenchange"}else if("onwebkitfullscreenchange"in document){fullscreenchange="webkitfullscreenchange"}else if("onmsfullscreenchange"in document){fullscreenchange="MSFullscreenChange"}if(fullscreenchange){var onFullscreenChange=L.bind(this._onFullscreenChange,this);this.whenReady(function(){L.DomEvent.on(document,fullscreenchange,onFullscreenChange)});this.on("unload",function(){L.DomEvent.off(document,fullscreenchange,onFullscreenChange)})}});L.control.fullscreen=function(options){return new L.Control.Fullscreen(options)}; \ No newline at end of file diff --git a/worldmap/leaflet/fullscreen.png b/worldmap/leaflet/fullscreen.png new file mode 100755 index 0000000..7384960 Binary files /dev/null and b/worldmap/leaflet/fullscreen.png differ diff --git a/worldmap/leaflet/fullscreen@2x.png b/worldmap/leaflet/fullscreen@2x.png new file mode 100755 index 0000000..9fca7f8 Binary files /dev/null and b/worldmap/leaflet/fullscreen@2x.png differ diff --git a/worldmap/leaflet/leaflet.fullscreen.css b/worldmap/leaflet/leaflet.fullscreen.css new file mode 100755 index 0000000..f489257 --- /dev/null +++ b/worldmap/leaflet/leaflet.fullscreen.css @@ -0,0 +1,40 @@ +.leaflet-control-fullscreen a { + background:#fff url(fullscreen.png) no-repeat 0 0; + background-size:26px 52px; + } + .leaflet-touch .leaflet-control-fullscreen a { + background-position: 2px 2px; + } + .leaflet-fullscreen-on .leaflet-control-fullscreen a { + background-position:0 -26px; + } + .leaflet-touch.leaflet-fullscreen-on .leaflet-control-fullscreen a { + background-position: 2px -24px; + } + +/* Do not combine these two rules; IE will break. */ +.leaflet-container:-webkit-full-screen { + width:100%!important; + height:100%!important; + } +.leaflet-container.leaflet-fullscreen-on { + width:100%!important; + height:100%!important; + } + +.leaflet-pseudo-fullscreen { + position:fixed!important; + width:100%!important; + height:100%!important; + top:0!important; + left:0!important; + z-index:99999; + } + +@media + (-webkit-min-device-pixel-ratio:2), + (min-resolution:192dpi) { + .leaflet-control-fullscreen a { + background-image:url(fullscreen@2x.png); + } + }