Monday, October 11, 2010

Great Blog Post on Custom Dojo Dijits

In his new blog "Enterprise Dojo", Dan Lee has an outstanding article titled, "Introduction to Custom Dojo Widgets". It gives a great first taste of developing your own custom Dojo Dijits. I was really excited to learn about the slick way to reference DOM nodes within your dijit using dojoattachpoint. Take 10 minutes and give it a read!

Vehicle Tracking With The JavaScript API

Recently, we have been investigating alternatives to our current AVL solution. The problems with our current solution are licensing costs (big $$ per workstation) and ease of use. I thought that I'd give it a try with ESRI's JavaScript API and this is what I came up with.


The vehicles are graphics that are refreshed from the server every 5 seconds using the JavaScript setTimeout() function. I was surprised at how quickly the browser could clear the graphics and reload them from the server. It looks like they are animated instead of reloaded. Here's a sample of the code that I used:

function AddVehicleGraphics(){ SANDY.qt.execute(SANDY.q, function(fSet){ function sortGraphics(a,b){ return (a.attributes.fleetID < b.attributes.fleetID) ? -1 : 1; } // record bread crumbs, if any dojo.forEach(SANDY.crumbsVehicles, function(key){ var crumbGraphic = dojo.filter(SANDY.avlGraphicsLayer.graphics, function(item){ return item.attributes.mapKey == key; })[0]; SANDY.avlGraphicsLayer.remove(crumbGraphic); // required to allow appropriate refresh on history graphics layer crumbGraphic.setInfoTemplate(SANDY.crumbsTipTemplate); crumbGraphic.setSymbol(SANDY.crumbSymbol); // get associate graphics layer var gLayer = SANDY.mapDij.map.getLayer(key); gLayer.add(crumbGraphic); }); // clear graphics layer SANDY.avlGraphicsLayer.clear(); SANDY.chartsDij.clearContent(); // sort the graphics alphabetically so they are sorted when adding to active vehicles table. fSet.features.sort(sortGraphics); dojo.forEach(fSet.features, function(g){ var symbol; switch (g.attributes.defaultIcon) { case "PLOW TRUCK": symbol = dojo.clone(SANDY.truckSymbol); break; case "SWEEPER": symbol = dojo.clone(SANDY.sweeperSymbol); break; case "SUV": symbol = dojo.clone(SANDY.puSymbol); break; } // rotate symbol symbol.setAngle(g.attributes.headingDegrees); g.setSymbol(symbol); // round mph g.attributes.speedMPH = Math.round(g.attributes.speedMPH); // add to graphics layer SANDY.avlGraphicsLayer.add(g); // add to vehicles list / bold all vehicles that have updated in the last 30 minutes var content; var activeCutOff = new Date().setMinutes(new Date().getMinutes() - 30); if (new Date(g.attributes.timeAtCoordinate) > activeCutOff){ content = "<b>" + g.attributes.fleetID + "</b>"; } else { content = g.attributes.fleetID; } SANDY.chartsDij._addContent(content); // update date updated text var now = new Date(); dojo.byId("update-date").innerHTML = now.toLocaleTimeString(); dojo.style("update-date-background", "backgroundColor", "white"); }); if (fSet.features.length === 0){ SANDY.chartsDij._addContent(SANDY.noVehiclesMsg); } }, function(error){ // change background to indicate error dojo.style("update-date-background", "backgroundColor", "red"); }); }


The other cool thing that I was able to do in this project was link to live UDOT traffic cameras. After getting permission from them, I was able to determine the unique URL for each of them from the CommuterLink website and build a feature class with their locations and urls. It was easy after that to publish them as a map service and load them as graphics onto my map. I used a custom infoTip window from ESRI's code gallery and some mouse events to show the camera images.




// camera graphics dojo.connect(SANDY.mapDij.map.graphics, "onMouseOver", function(evt){ // store old symbol to save for mouse out function SANDY.origSymbol = evt.graphic.symbol; // get orig color rgb's var r = SANDY.origSymbol.color.r; var g = SANDY.origSymbol.color.g; var b = SANDY.origSymbol.color.b; // highlight opacity var a = 0.5; // change symbol to highlight symbol var biggerSize = evt.graphic.symbol.size + 4; evt.graphic.setSymbol(dojo.clone(evt.graphic.symbol).setSize(biggerSize).setColor([r, g, b, a])); SANDY.iTip.setContent(evt.graphic.getContent()); SANDY.iTip.show(evt.screenPoint, SANDY.mapDij.map.height, SANDY.mapDij.map.width); }); dojo.connect(SANDY.mapDij.map.graphics, "onMouseOut", function(evt){ // change symbol back evt.graphic.setSymbol(SANDY.origSymbol); SANDY.iTip.hide(); }); dojo.connect(SANDY.mapDij.map.graphics, "onClick", function(evt){ // hide infowindow SANDY.mapDij.map.infoWindow.hide(); });


The next step after we upgrade to ArcGIS Server 10 is to use the new TimeSlider dijit to enable users to view historical data. Sadly, because I'm changing jobs, I will not be around to implement this cool functionality.  :(

Any other suggestions? Anyone else using the JavaScript API for vehicle tracking? I'd love to hear your experiences.

Friday, October 8, 2010

Onward and Upward - New Job

Recently I was offered a new position at the State of Utah's AGRC. I'm way excited to be working with such an amazing group of people and can't wait to get started. I'll miss all of the cool people and projects that I'll be leaving behind here at Sandy. But the good news is that I'll be doing a lot more programming. Hopefully I'll still have the time and motivation to keep up the blog. Even though I'm pretty sure that the only people that read it are me and my wife...OK...truthfully, I have to read it to my wife to get her to read it. ;)

If you are interested in filling my position at Sandy, you can go here for the job announcement.

I start next week. Wish me luck!