From our sponsor: Ready to show your plugin skills? Enter the Penpot Plugins Contest (Nov 15-Dec 15) to win cash prizes!
Hi guys! In today’s tutorial we will make a simple real-time application that will determine and show the locations of currently connected users directly on a map. For this purpose we will use Node.js and the HTML5 Geolocation API. As you may know node.js is an asynchronous web server which is built on the Google V8 JavaScript engine and is a perfect solution as a back-end for real-time apps. Our app will let users see each other on the map with the help of the socket.io library that uses Web Sockets (and its alternatives like AJAX long polling for older browsers – more) for a real-time data channel. Our example will work in all modern browsers that support the HTML5 Geolocation API.
Installing node
First, you’ll need to install node.js. You can get pre-compiled Node.js binaries for several platforms from the download section of the official website: http://nodejs.org/download.
After the installation is complete you will get access to the node package manager (npm), with the help of which we will install all needed modules for this tutorial. We will use socket.io and node-static, which will serve all the client side files with ease. Go to the directory of your app and run this command in your terminal or command line:
npm install socket.io node-static
Tip: I advice you to install a utility like nodemon that will keep an eye on your files and you won’t need to restart your server after every change:
npm install nodemon -g
“-g” means that it will be installed globally and accessible from every node repo.
The HTML
Let’s first create an “index.html” in our public directory.
<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="author" content="Dmitri Voronianski"> <title>Real-Time Geolocation with Web Sockets</title> <link href='http://fonts.googleapis.com/css?family=Lato:300,400' rel='stylesheet' type='text/css'> <link rel="stylesheet" href="./css/styles.css"> <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.4/leaflet.css" /> <!--[if lt IE 9]> <script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> </head> <body> <div class="wrapper"> <header> <h1>Real-Time Geolocation Service with Node.js</h1> <div class="description">Using HTML5 Geolocation API and Web Sockets to show connected locations.</div> </header> <div class="app"> <div class="loading"></div> <div id="infobox" class="infobox"></div> <div id="map">To get this app to work you need to share your geolocation.</div> </div> </div> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script> <script src="./js/lib/leaflet.js"></script> <script src="/socket.io/socket.io.js?x23087"></script> <script src="./js/application.js"></script> </body> </html>
As you can see it’s pretty simple. For rendering our map on the page we will use an incredible open-source JavaScript library for interactive maps – Leaflet.js. It’s free and highly customizable. The API documentation is available on the website. Our styles.css file is inside the “./public/css/” folder. It will include some simple styles for the app. The leaflet.css in the same folder contains the styles for the map.
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
Server side
Now we are ready to start with the back-end of our app. Let’s take a look at “server.js”:
// including libraries var http = require('http'); var static = require('node-static'); var app = http.createServer(handler); var io = require('socket.io').listen(app); // define port var port = 8080; // make html, js & css files accessible var files = new static.Server('./public'); // serve files on request function handler(request, response) { request.addListener('end', function() { files.serve(request, response); }); } // listen for incoming connections from client io.sockets.on('connection', function (socket) { // start listening for coords socket.on('send:coords', function (data) { // broadcast your coordinates to everyone except you socket.broadcast.emit('load:coords', data); }); }); // starts app on specified port app.listen(port); console.log('Your server goes on localhost:' + port);
The code is not complicated at all; everything that it does is serving files and listening to the data from the client. Now we can start our app from the terminal or command line and take a look:
node server.js
Or, if you have followed my advice and used nodemon, write this:
nodemon server.js
Now go to localhost:8080 in your browser (you can change the port to whatever you like). Everything will be static because our main JavaScript function is not ready, yet.
The Client Side
It’s time to open the “./public/js/application.js” file and to write a couple of functions (we’ll be using jQuery):
$(function() { // generate unique user id var userId = Math.random().toString(16).substring(2,15); var socket = io.connect("/"); var map; var info = $("#infobox"); var doc = $(document); // custom marker's icon styles var tinyIcon = L.Icon.extend({ options: { shadowUrl: "../assets/marker-shadow.png", iconSize: [25, 39], iconAnchor: [12, 36], shadowSize: [41, 41], shadowAnchor: [12, 38], popupAnchor: [0, -30] } }); var redIcon = new tinyIcon({ iconUrl: "../assets/marker-red.png" }); var yellowIcon = new tinyIcon({ iconUrl: "../assets/marker-yellow.png" }); var sentData = {} var connects = {}; var markers = {}; var active = false; socket.on("load:coords", function(data) { // remember users id to show marker only once if (!(data.id in connects)) { setMarker(data); } connects[data.id] = data; connects[data.id].updated = $.now(); // shorthand for (new Date).getTime() }); // check whether browser supports geolocation api if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(positionSuccess, positionError, { enableHighAccuracy: true }); } else { $(".map").text("Your browser is out of fashion, there's no geolocation!"); } function positionSuccess(position) { var lat = position.coords.latitude; var lng = position.coords.longitude; var acr = position.coords.accuracy; // mark user's position var userMarker = L.marker([lat, lng], { icon: redIcon }); // load leaflet map map = L.map("map"); // leaflet API key tiler L.tileLayer("http://{s}.tile.cloudmade.com/BC9A493B41014CAABB98F0471D759707/997/256/{z}/{x}/{y}.png", { maxZoom: 18, detectRetina: true }).addTo(map); // set map bounds map.fitWorld(); userMarker.addTo(map); userMarker.bindPopup("You are there! Your ID is " + userId + "
").openPopup(); // send coords on when user is active doc.on("mousemove", function() { active = true; sentData = { id: userId, active: active, coords: [{ lat: lat, lng: lng, acr: acr }] } socket.emit("send:coords", sentData); }); } doc.bind("mouseup mouseleave", function() { active = false; }); // showing markers for connections function setMarker(data) { for (i = 0; i < data.coords.length; i++) { var marker = L.marker([data.coords[i].lat, data.coords[i].lng], { icon: yellowIcon }).addTo(map); marker.bindPopup("One more external user is here!
"); markers[data.id] = marker; } } // handle geolocation api errors function positionError(error) { var errors = { 1: "Authorization fails", // permission denied 2: "Can't detect your location", //position unavailable 3: "Connection timeout" // timeout }; showError("Error:" + errors[error.code]); } function showError(msg) { info.addClass("error").text(msg); } // delete inactive users every 15 sec setInterval(function() { for (ident in connects){ if ($.now() - connects[ident].updated > 15000) { delete connects[ident]; map.removeLayer(markers[ident]); } } }, 15000); });
Magic happens when we use socket.emit to send a message to our node web server on every mouse move. It means that our user is active on the page. We also receive the data from the server with socket.on and after getting initialize markers on the map. The main things that we need for the markers are the latitude and longitude which we receive from the browser. If the user is inactive for more then 15 seconds we remove their marker from our map. If the user’s browser doesn’t support the Geolocation API we’ll show a message that the browser is out-of-date. You can read more about the HTML5 Geolocation API here: Geolocation – Dive Into HTML5.
Demo Repository
You can take a look at the whole app in my github repo, clone it and experiment with it locally. If you have questions feel free to ask in the comments or you can also e-mail me. Thanks!
Please note that the demo might not load properly sometimes due to server overload.
Hi,
Just tried the demo and I get the following error:
An error has occurred: {“code”:”ECONNRESET”}
Regards
Hi, sorry I’m currently in Venezuela and the demo doesn’t work here (Chrome/Safari/Firefox/Opera) :
‹‹ An error has occurred: {“code”:”ECONNRESET”} ››.
I suppose it has to do with my location — Geo Api doesn’t work either (says ‹‹ rejected demand : “sensor” parameter should indicate “true” or “false” ››)…
Let me try the tutorial and see what I can do ^^
it breaks then kinda works … tho does not pin point my location.. it is out by about 100miles…
Pretty cool, thx!
@ James, just ctrl+f5 and it will work. Same happened to me and after ctrl+f5 it worled 🙂
Bye bye
Ok thx, worked ok after ctrl+f5 (even if it pined 5miles away from my exact location).
Seems like Dmitri fixed the error. There’s still another problem though, which is the links to the images marker-red and marker-shadow.
Hi guys, just wanted to say that there seems to be some issue with the server. Dmitri and we are looking into it! Thanks, ML
Hi,
Same error as James…
Guys, sorry for the error, everything is already fixed, check it out!
Thanks!
Im sorry.. it stil error on me ;_O it said: ERROR CAN’T DETECT YOUR LOCATION.
and {To get this app to work you need to share your geolocation.} <– what should i do about this, Guys?
It seems that it’s browser issue, in what browser did you test the demo?
please look at this
error code 2: “Can\’t detect your location”, //position unavailable
POSITION_UNAVAILABLE (numeric value 2)*
The position of the device could not be determined. For instance, one or more of the location providers used in the location acquisition process reported an internal error that caused the process to fail entirely.
_______
*http://dev.w3.org/geo/api/spec-source.html#position_error_interface
Ah.. thanks 4 the link.. btw, i tried it on chrome, Bro.. 🙂
Wow, this is very impressive! Very cool tutorial.
I’ve never had a chance to mess around with socket.io or node yet, but both are very appealing to me. More tutorials of this kind would be greatly appreaciated.
Amazing tutorial, thanks a lot. But i have a question – i have an IIS installed on my server. I need to install Node.JS and some other components on it in order to make this tutorial work. But i heard that there is some analogue of the socket.io called SignalR that works natively with ASP.NET applications. How do you think is it possible to implement the same functionality using SignalR library without Node.JS installation?
Thanks in advance
Oh, one more question – as i guess this tutorial tracks only users who visit a current page. Is there a way to track the whole website?
Works very well and is very accurate. Amazing tutorial, there should be more node.js tutorials like this in the future that would be very helpful
Andrey, why not? i think there are several implementations for sockets in different environments.
According second question – you will need to check user’s position on every page change.
Just awesome, I say this is the finest of all those geo-location demos.
Actually its really awesome tutorial… It response time is really fast. Its just awesome .Thanks..
Its awesome. I hope node.js is going to rock the world in coming days.
Nice job…awesome.. thanks for sharing…
Very nice 🙂
For spreading the word on Twitter, in the demo you could probably prompt users for their name / handle and display that as well.
it says im sitting in the parlament of my country oO
how to get real time geolocation name ?
I just spent way to long looking for a more in depth tutorial on how to install node.js on my web host “Bluehost” on a non dedicated portal and wanted to share this helpful URL:
http://rcrisman.net/article/10/installing-nodejs-on-hostmonster-bluehost-accounts
Is ist somehow possible to get the map tiles from cloudmade or any other provider via https?
Or do you know a working php proxy for this?
Has anyone tried deploying this to heroku? does heroku let you use websockets?
Heroku does not work with websockets, but the socket.io library falls back to XHR long polling, which does work.
This does not work in safari…Is there a way to make it work with?Thanks
Antonio
404 error on the demo application “No application found for “geotest.jit.su” “
when i view this on an iphone 4 i receive: “error: Authorization fails” – why would this be? Works on desktop however
Hi Dimitri,
Great to share your work. Do you think it works with watchPosition function ? if user has GPS mobile device, is the marker moving ?
Tx
Flo
this demo do not work with it, but you can add it 🙂
Hi, why not using map.locate function to call geolocation ?
wow ………….
can we do this without requesting permission of user to allow, if not can we have it as a custom message not browser message… as users might not know how to use.
how to run it on heroku server ?
Hi Dimitri, thank you for sharing that cool stuff. I’m trying to install it on my server for testing it and I have a few problem with the socket.io package (I have correctly installed socket.io in node). Everytime I tried to run my webpage it says that “require is not defined” in this line:
var client = require(‘socket.io-client’);
in the file “socket.io.js”.
So correct me if i’m wrong but this require is a part of ressources that my node server run, isn’t it ?
I’m sur that my node server is running and have correctly lauch the socket.io ressources.
Is this talk to you ?
Thank you
It’s strange but it maybe caused of your node version, is it 0.10.x? But nevertheless I already fixed this cross version issues in my demo repo at github. So my advice is to do everything from scratch again:
1. clone repo
2. go to repo
3. run “npm install“
4. run “node server.js“
5. go to localhost:8080 – everything should be ok
Or let me know if there’s something wrong again.
Excellent work. thank you dmitiri.
I followed the same steps as per your documentation.
I am unable to see the map.
Can you please explain the code in application.js
L.tileLayer(‘https://{s}.tiles.mapbox.com/v3/examples.map-i87786ca/{z}/{x}/{y}.png’, { maxZoom: 18, detectRetina: true }).addTo(map);
I’m getting the following ERRORS:-
https://a.tiles.mapbox.com/v3/examples.map-i87786ca/1/0/0.png 404 (Not Found)
https://b.tiles.mapbox.com/v3/examples.map-i87786ca/1/1/0.png 404 (Not Found)
https://b.tiles.mapbox.com/v3/examples.map-i87786ca/1/0/1.png 404 (Not Found)
https://c.tiles.mapbox.com/v3/examples.map-i87786ca/1/1/1.png 404 (Not Found)
Hi,
I believe map box no longer offers free access to their map tiles
Hi Dimitri,
Map is not loading icon is loading . Click event also not working . HTML 5 loader problem?
Thanks
Sat