What's GeoHappening?



What's GeoHappening?

0 0


geohappenings-prez


On Github jmfolds / geohappenings-prez

What's GeoHappening?

Our path to winning the (ArcGIS.js <= 100) Responsive Edition App Challenge

Featuring Bootstrap, Backbone.js and much more!

With Jeremy Folds and Nick Volpe

GIS Developers with DTS Agile

Why are we here?

To Gloat

To talk JavaScripts + Maps

How did we get here?

In the beginning...

GIS/LiDar/Orthoimagery

Automation

Intro to JavaScript/Web Applications

Becoming a Developer

University of Nebraska

at Kearney

GIS Principles

but NO CODE!

USFWS

Hired in Fort Collins

Flex!

My boss's Flex Skills:

My Flex Skills:

Getting Started?

Just do it.

Make mistakes

Debug

Make more mistakes

Debug

Getting started ::Tools and Training

What Not to Do

Learn Code the Hard Way

Eloquent JavaScript

Pluralsight

JavaScript Weekly

(ArcGIS.js <= 100) Responsive Edition

Demo

GeoHappenings App

Some notes

GeoHappenings :: App.js

dojo.require('esri.map', 'esri.tasks.locator', 'esri.geometry.webMercatorUtils');
dojo.addOnLoad(function () {
var AppView = Backbone.View.extend({
el: 'body',
initialize: function() {
_.bindAll.apply(_, [this].concat(_.functions(this)));
var $this = this;
this.model = new (Backbone.Model.extend({}))();
this.model.on('change', this.toggleShare, this);
this.fb = new Firebase('https://luminous-fire-5575.firebaseio.com/users');
this.symbol = new esri.symbol.SimpleMarkerSymbol().setColor(new dojo.Color([0, 255, 0, 0.25]));
this.map = new esri.Map('map', {basemap: 'osm', center: [-98.737039, 38.737039], zoom: 4 });
$('.current-location').on('click',function() { $this.getLocation($this.model) });
$('#search-input').on('typeahead:selected', function (evt, datum, name) {
$this.map.centerAndZoom(new esri.geometry.Point(datum.lon, datum.lat), 15);
$('#search-modal').modal('hide');
});
$('#dev-summit').on('click',function() { $this.map.centerAndZoom([-116.5382, 33.8260], 16)});
this.fb.on('value', function (ss) {
$this.messages = [];
_.each(ss.val(), function (item) { _.each(item.messages, function (item2) {
$this.messages.push(item2) });
});
$this.displayChatMessages() & $this.activateClickListener() & $this.initTypeahead();
});
},
events: {
'keyup #message-input': 'toggleShare',	'keyup #name-input': 'toggleShare',
'click .share-message': 'saveMsg',	'click #add-event-btn': 'enableEventClickHandler'
},
toggleShare: function (model) {
$('#loader').modal('hide');
if ($('#name-input').val() && $('#message-input').val() && (this.model.get('loc'))) {
$('.share-message').removeClass('disabled');
} else { $('.share-message').addClass('disabled') };
},
saveMsg: function (evt) {
var loc = this.model.get('loc');
var exists; var tC = new Date().getTime();
var name = $('#name-input').val(); var text = $('#message-input').val();
if (!name || !text) { $('#alert-modal').modal(); return; }
if (!loc || !loc.lat || !loc.lon) {	$('#no-location-modal').modal(); return; };
this.fb.on('value', function (ss) {	exists = (ss.val() !== null) });
if(!exists){ this.fb.child(name).set({text: name}) };
this.fb.child(name).child('messages').push({ name: name, text: text,
lat: loc.lat, lon: loc.lon, timeStamp: tC });
$('#share-modal').modal('hide'); $('#message-input').val(''); this.model.set('loc', null);
},getLocation: function (model) {
if (navigator.geolocation) {
$('#loader').modal('show');
navigator.geolocation.getCurrentPosition(function (p) {
model.set('loc', {lat: String(p.coords.latitude), lon: String(p.coords.longitude)});
});
} else { $('#alert-modal').modal(); }
},enableEventClickHandler: function() {
if (this.mch){ dojo.disconnect(this.mch) };
this.mch = dojo.connect(this.map, 'onClick', dojo.hitch(this, this.onMapClick));
$('#share-modal').modal('hide');
},onMapClick: function (evt) {
var x = esri.geometry.xyToLngLat(evt.mapPoint.x, evt.mapPoint.y, true);
this.model.set('loc', { lat: x[1], lon: x[0] });
dojo.disconnect(this.mch) & $('#share-modal').modal('show');
},activateClickListener: function() {
var $this = this;
$('.chat-item').on('click', function(evt) {
var d = evt.currentTarget.dataset;
$this.map.centerAndZoom(new esri.geometry.Point(d.lon, d.lat), 15);
$('#chat-modal').modal('hide');
});
},displayChatMessages: function() {
var $this = this; $('#chat-container').empty();
this.messages.sort(function (a, b) { if (a.timeStamp > b.timeStamp) { return 1; }
if (a.timeStamp < b.timeStamp) { return -1; } return 0;
});
_.each(this.messages, function (msg) {
var tC = new Date().getTime();
tE = Math.floor((tC - msg.timeStamp) / 1000 / 60); //get time elapsed since the previous messages in firebase
tS = (tE > 60) ? Math.floor((tE * 60) / 3600)  + ' hours ago' :  tE + ' minutes ago';
$('<li class="list-group-item chat-item"></li>').append('<div class="chat-date">' +
msg.name +':  '+ tS +  '</div><div>'+ msg.text + '</div>')
.attr('data-lat', msg.lat).attr('data-lon',msg.lon).prependTo($('#chat-container'));
if (msg.lat && msg.lon && $this.map.graphics) {
var pt = new esri.geometry.Point(msg.lon, msg.lat);
var g = new esri.Graphic(pt, $this.symbol);
$this.map.graphics.add(g);
};
g.setInfoTemplate(new esri.InfoTemplate().setTitle(msg.name +' '+ tS).setContent(msg.text));
});
},initTypeahead: function () {
$('#search-input').typeahead('destroy');
var bloodhound = new Bloodhound({
datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.text); },
queryTokenizer: Bloodhound.tokenizers.whitespace, local: this.messages });
bloodhound.initialize();
var options = {	displayKey: 'text',	source: bloodhound.ttAdapter(),
templates: { suggestion: _.template('<strong><%=text%></strong>')}};
$('#search-input').typeahead(null, options);
}});
new AppView();});

Our Toolset

Backbone.js (jQuery, underscore.js)

Bootstrap

ESRI JavaScript API (dojo)

Firebase

typeahead.js

GitHub

Sublime Text

Bower

Python SimpleHTTPServer

Backbone.js

  • Structure
  • Separation of Concerns (unless you cram everything into a single 100 line file)

Examples FTW!!!

Bootstrap Starter Template

Firebase

Lessons Learned: events issue (single val added vs val change) Timing issue with click event handler?

Firebase Dashboard

Instant REST endpoint

Twitter is awesome, if we didn’t mention that. Easily create your own autocomplete with REST endpoints, local arrays, or local files

Examples

Go build something cool.