Skip to content

Instantly share code, notes, and snippets.

@nickdos
Last active August 29, 2015 14:06
Show Gist options
  • Save nickdos/eb7555a584bb3a309cba to your computer and use it in GitHub Desktop.
Save nickdos/eb7555a584bb3a309cba to your computer and use it in GitHub Desktop.
Pigeonhole demo

Pigeonhole Demo

A species identification tool/widget that narrows down an identification based on a location and higher taxa hints.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>ALA Pigeonhole Demo</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/2.3.2/css/bootstrap.min.css">
<style type="text/css">
#locationLatLng {
color: #DDD;
}
/* Base class */
.bs-docs-example {
position: relative;
margin: 15px 0;
padding: 50px 15px 14px;
*padding-top: 19px;
background-color: #fff;
border: 1px solid #ddd;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
/* Echo out a label for the example */
.bs-docs-example:after {
/* content: "Example"; */
content: attr(data-content);
position: absolute;
top: -1px;
left: -1px;
padding: 6px 12px 8px 12px;
font-size: 18px;
font-weight: bold;
background-color: #f5f5f5;
border: 1px solid #ddd;
color: #666;
-webkit-border-radius: 4px 0 4px 0;
-moz-border-radius: 4px 0 4px 0;
border-radius: 4px 0 4px 0;
}
/* Remove spacing between an example and it's code */
.bs-docs-example + .prettyprint {
margin-top: -20px;
padding-top: 15px;
}
.select-mini {
width: auto !important;
height: 24px;
font-size: 12px;
line-height: 20px;
margin-bottom: 2px;
}
#speciesGroup {
/*width: 18%;*/
/*float: left;*/
margin-bottom: 10px;
}
#speciesSubGroup {
/*width: 80%;*/
/*float: left;*/
/*padding-left: 15px;*/
/*margin-top: 20px;*/
}
#speciesSubGroup .btn-group {
margin-left: 0 !important;
margin-top: 10px;
}
.sub-groups {
/*display: inline-block;*/
/*margin-top: 10px;*/
}
.sub-groups .btn {
margin-bottom: 4px;
margin-right: 4px;
}
.leaflet-popup-content {
font-size: 11px;
}
/* Gallery styling */
.imgCon {
display: inline-block;
/* margin-right: 8px; */
text-align: center;
line-height: 1.3em;
background-color: #DDD;
color: #DDD;
font-size: 12px;
/*text-shadow: 2px 2px 6px rgba(255, 255, 255, 1);*/
/* padding: 5px; */
/* margin-bottom: 8px; */
margin: 2px 4px 2px 0;
position: relative;
}
.imgCon img {
height: 170px;
min-width: 150px;
}
.imgCon .meta {
opacity: 0.8;
position: absolute;
bottom: 0;
left: 0;
right: 0;
overflow: hidden;
text-align: left;
padding: 4px 5px 2px 5px;
}
.imgCon .brief {
color: black;
background-color: white;
}
.imgCon .detail {
color: white;
background-color: black;
opacity: 0.7;
}
</style>
<link rel="stylesheet" type="text/css" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css">
<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/2.3.2/js/bootstrap.min.js"></script>
<script src="http://twitter.github.io/typeahead.js/releases/latest/typeahead.bundle.js"></script>
<script type='text/javascript' src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<script type="text/javascript">
var map, geocoding, marker, circle, radius, initalBounds;
var biocacheBaseUrl = "http://biocache.ala.org.au/ws";
$(document).ready(function() {
var osm = L.tileLayer('https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, ' +
'<a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' +
'Imagery © <a href="http://mapbox.com">Mapbox</a>',
id: 'examples.map-i875mjb7'
});
var OpenStreetMap_Mapnik = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'
});
var MapQuestOpen_OSM = L.tileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpeg', {
attribution: 'Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a> &mdash; Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>',
subdomains: '1234'
});
var MapQuestOpen_Aerial = L.tileLayer('http://oatile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg', {
attribution: 'Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a> &mdash; Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency',
subdomains: '1234'
});
var Esri_WorldImagery = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles &copy; Esri &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community',
maxZoom: 17
});
map = L.map('map', {
center: [-28, 134],
zoom: 3//,
//layers: [osm, MapQuestOpen_Aerial]
});
initalBounds = map.getBounds().toBBoxString(); // save for geocoding lookups
var baseLayers = {
"Street": osm,
"Satellite": Esri_WorldImagery
};
map.addLayer(osm);
L.control.layers(baseLayers).addTo(map);
marker = L.marker(null, {draggable: true}).on('dragend', function() {
updateLocation(this.getLatLng());
});
radius = $('#radius').val();
circle = L.circle(null, radius * 1000, {color: '#df4a21'}); // #bada55
//L.Icon.Default.imagePath = "${g.createLink(uri:'/js/leaflet-0.7.3/images')}";
map.on('locationfound', onLocationFound);
function onLocationFound(e) {
// create a marker at the users "latlng" and add it to the map
marker.setLatLng(e.latlng).addTo(map);
updateLocation(e.latlng);
}
$('#geocodeinput').on('keydown', function(e) {
if (e.keyCode == 13 ) {
e.preventDefault();
geocodeAddress();
}
});
$('#radius').change(function() {
updateLocation(marker.getLatLng());
});
$('#speciesGroup').on('click', '.groupBtn', function(e) {
$('#speciesGroup .btn').removeClass('btn-primary');
$(this).addClass('btn-primary');
$('#speciesSubGroup .sub-groups').addClass('hide'); // hide all subgroups
$('#subgroup_' + $(this).data('group')).removeClass('hide'); // expose requested subgroup
//updateSubGroups($(this).data('group'));
loadSpeciesGroupImages('species_group:' + unescape($(this).data('group')));
});
$('#speciesSubGroup').on('click', '.subGroupBtn', function(e) {
$('#speciesSubGroup .btn').removeClass('btn-primary');
$(this).addClass('btn-primary');
loadSpeciesGroupImages('species_subgroup:' + unescape($(this).data('group')));
});
// mouse over affect on thumbnail images
$('#speciesImages').on('hover', '.imgCon', function() {
$(this).find('.brief, .detail').toggleClass('hide');
});
}); // end document load
function imgError(image){
image.onerror = "";
image.src = "http://bie.ala.org.au/images/noImage.jpg";
return true;
}
function geolocate() {
map.locate({setView: true, maxZoom: 16});
}
function geocode() {
geocodeAddress();
}
function updateSubGroups(group) {
var radius = $('#radius').val();
var latlng = $('#locationLatLng span').data('latlng');
$.ajax({
url : biocacheBaseUrl + '/explore/hierarchy/groups.json'
, dataType : 'jsonp'
, jsonp : 'callback'
, data : {
'lat' : latlng.lat
, 'lon' : latlng.lng
, 'radius' : radius
, 'speciesGroup': group
}
})
.done(function(data){
var group = "<div id='speciesGroup1' class='btn-group '>";
$('#speciesSubGroup').html('');
$.each(data, function(index, value){
// console.log(index, value);
var btn = ''; //(index == 0) ? 'btn-primary' : '';
group += "<div class='btn groupBtn " + btn + "' data-group='" + escape(value.name) + "'>" + value.name + " <span class='badge badge-infoX'>" + value.speciesCount + "</span></div>";
if (value.childGroups.length > 0) {
var hide = 'hide'; //(index == 0) ? '' : 'hide';
var subGroup = "<div id='subgroup_" + value.name + "' class='sub-groups " + hide + "'>";
$.each(value.childGroups, function(i, el){
subGroup += "<div class='btn subGroupBtn' data-group='" + escape(el.name) + "'>" + el.name + " <span class='badge badge-infoX'>" + el.speciesCount + "</span></div>";
});
$('#speciesSubGroup').append(subGroup);
}
});
$('#speciesGroup').html(group);
$('#speciesImages').empty();
$('#species_group p.hide').removeClass('hide');
})
.fail(function( jqXHR, textStatus, errorThrown ) {
alert("Error: " + textStatus + " - " + errorThrown);
});
}
function loadSpeciesGroupImages(speciesGroup) {
var radius = $('#radius').val();
var latlng = $('#locationLatLng span').data('latlng');
jQuery.ajaxSettings.traditional = true; // so multiple params with same key are formatted right
//var url = "http://biocache.ala.org.au/ws/occurrences/search?q=species_subgroup:Parrots&fq=geospatial_kosher%3Atrue&fq=multimedia:Image&facets=multimedia&lat=-35.2792511&lon=149.1113017&radius=5"
$.ajax({
url : biocacheBaseUrl + '/occurrences/search.json',
dataType : 'jsonp',
jsonp : 'callback',
data : {
'q' : '*:*',
'fq': [ speciesGroup
//'geospatial_kosher:true'],
],
//'fq': speciesGroup,
'facets': 'common_name_and_lsid',
'flimit': 999,
'pageSize': 0,
'lat' : latlng.lat,
'lon' : latlng.lng,
'radius' : radius
}
})
.done(function(data){
if (data.facetResults && data.facetResults.length > 0 && data.facetResults[0].fieldResult.length > 0) {
//console.log(speciesGroup + ': species count = ' + data.facetResults[0].fieldResult.length);
var images = "<div id='imagesGrid'>";
$.each(data.facetResults[0].fieldResult, function(i, el){
if (i >= 30) return false;
var parts = el.label.split("|");
var lsid = parts[2];
var displayName = (parts[0]) ? parts[0] : "<i>" + parts[1] + "</i>";
var imgUrl = "http://bie.ala.org.au/ws/species/image/small/" + lsid; // http://bie.ala.org.au/ws/species/image/thumbnail/urn:lsid:biodiversity.org.au:afd.taxon:aa745ff0-c776-4d0e-851d-369ba0e6f537
images += "<div class='imgCon'><a class='cbLink thumbImage tooltips' rel='thumbs' href='http://bie.ala.org.au/species/" +
lsid + "' target='species'><img src='" + imgUrl +
"' alt='species thumbnail' onerror='imgError(this);'/><div class='meta brief'>" +
displayName + "</div><div class='meta detail hide'><i>" +
parts[1] + "</i><br>" + parts[0] + "<br>Records: " + el.count + "</div></a></div>";
});
images += "</div>";
$('#speciesImages').html(images);
} else {
$('#speciesImages').html("No species found.");
}
})
.fail(function( jqXHR, textStatus, errorThrown ) {
// alert("Error: " + textStatus + " - " + errorThrown);
$('#speciesImages').html("Error: " + textStatus + " - " + errorThrown);
});
}
function updateLocation(latlng) {
//alert("Marker moved to: "+this.getLatLng().toString());
$('#speciesGroup span, #speciesImages span').hide();
$('.spinner').show();
$('#locationLatLng span').html(latlng.toString());
$('#locationLatLng span').data('latlng', latlng);
marker.setLatLng(latlng).bindPopup('your location', { maxWidth:250 }).addTo(map);
circle.setLatLng(latlng).setRadius($('#radius').val() * 1000).addTo(map);
map.fitBounds(circle.getBounds());
//updateSpeciesGroups()
updateSubGroups();
//console.log("zoom", map.getZoom());
}
function geocodeAddress() {
var query = $('#geocodeinput').val();
$.ajax({
// https://api.opencagedata.com/geocode/v1/json?q=Canberra,+ACT&key=577ca677f86a3a4589b17814ec399112
url : 'https://api.opencagedata.com/geocode/v1/json',
dataType : 'jsonp',
jsonp : 'callback',
data : {
'q' : query,
'key': '577ca677f86a3a4589b17814ec399112', // key for username 'nickdos' with pw 'ac..on',
'bounds': initalBounds // restricts search to initla map view
}
})
.done(function(data){
//console.log("geonames", data);
if (data.results.length > 0) {
var res = data.results[0];
var latlng = new L.LatLng(res.geometry.lat, res.geometry.lng);
var bounds = new L.LatLngBounds([res.bounds.southwest.lat, res.bounds.southwest.lng], [res.bounds.northeast.lat, res.bounds.northeast.lng]);
map.fitBounds(bounds);
updateLocation(latlng);
marker.setPopupContent(res.formatted + " - " + latlng.toString());
//marker = L.marker(latlng, {draggable: true}).addTo(map);
//marker.setLatLng(latlng).addTo(map);
} else {
alert('location was not found, try a different address or place name');
}
})
.fail(function( jqXHR, textStatus, errorThrown ) {
alert("Error: " + textStatus + " - " + errorThrown);
})
.always(function() { $('.spinner').hide(); });
}
</script>
</head>
<body>
<div class="container-fluid">
<!-- Example row of columns -->
<h2>Help with species identification</h2>
<div class="bs-docs-example" id="location" data-content="Location">
<div class="row">
<div class="span5">
<p>Specify a location for the sighting:</p>
<button class="btn" onClick="geolocate()"><i class="icon-map-marker" style="margin-left:-5px;"></i> Use my location</button>
<div style="margin: 10px 0;"><span class="label label-info">OR</span></div>
<div class="hide">Enter an address, location or coordinates</div>
<div class="input-append">
<input class="span4" id="geocodeinput" type="text" placeholder="Enter an address, location or lat/lng">
<button id="geocodebutton" class="btn" onclick="geocode()">Lookup</button>
</div>
<div>Show known species in a
<select name="radius" id="radius" class="select-mini">
<option value="1">1</option>
<option value="2">2</option>
<option value="5" selected="selected">5</option>
<option value="10">10</option>
<option value="20">20</option>
</select>
km area surrounding this location
</div>
<div id="locationLatLng"><span></span></div>
</div>
<div class="span4">
<div id="map" style="width: 100%; height: 250px"></div>
<div class="" id="mapTips">Tip: drag the marker to fine-tune your location</div>
</div>
</div>
</div>
<div class="bs-docs-example" id="species_group" data-content="Species group">
<p>Narrow down the identification by first choosing a species group.</p>
<div id="speciesGroup"><span>[Specify a location first]</span><r:img uri="/images/spinner.gif" class="spinner hide"/></div>
<p class="hide">Select a species sub-group (optional)</p>
<div id="speciesSubGroup"></div>
<div class="clearfix"></div>
</div>
<div class="bs-docs-example" id="browse_species_images" data-content="Browse species images">
<p>Narrow down the identification by browsing species images</p>
<div id="speciesImages"><span>[Specify a location first]</span></div>
</div>
<footer>
<p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/deed.en_US">Creative Commons Attribution 3.0 Unported License</a></p>
</footer>
</div> <!-- /container -->
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment