Skip to content

Instantly share code, notes, and snippets.

@djtfmartin
Created October 2, 2014 04:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save djtfmartin/d9b0832a370606e79b60 to your computer and use it in GitHub Desktop.
Save djtfmartin/d9b0832a370606e79b60 to your computer and use it in GitHub Desktop.
ALA mapping example
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>ALA Occurrence Records Map Example</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">
<style>
body {
padding-top: 50px;
padding-bottom: 20px;
}
.mapContainer {
height: 650px;
width: 100%;
}
/* typeahead styles */
.typeahead,
.tt-query,
.tt-hint {
width: 396px;
height: 34px;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
/* border: 2px solid #ccc;*/
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
outline: none;
}
.typeahead {
background-color: #fff;
}
.typeahead:focus {
border: 2px solid #0097cf;
}
.input-group > .twitter-typeahead > .form-control.tt-input {
-webkit-border-top-left-radius: 4px;
-webkit-border-bottom-left-radius: 4px;
-moz-border-radius-topleft: 4px;
-moz-border-radius-bottomleft: 4px;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.tt-query {
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
.tt-hint {
color: #999
}
.tt-dropdown-menu {
width: 422px;
margin-top: 12px;
padding: 8px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
}
.tt-suggestion {
padding: 3px 20px;
font-size: 14px;
line-height: 20px;
}
.tt-suggestion.tt-cursor {
color: #fff;
background-color: #0097cf;
}
.tt-suggestion p {
margin: 0;
}
.twitter-typeahead {
width: 100%;
position: relative;
}
.twitter-typeahead .tt-query,
.twitter-typeahead .tt-hint {
margin-bottom: 0;
width: 100%;
height: 34px;
position: absolute;
top: 0;
left: 0;
}
.input-group .twitter-typeahead {
display: table-cell !important;
}
.twitter-typeahead .tt-hint {
color: #a1a1a1;
z-index: 1;
padding: 6px 12px;
border: 1px solid transparent;
}
.twitter-typeahead .tt-query {
z-index: 2;
border-radius: 4px !important;
/* add these 2 statements if you have an appended input group */
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
/* add these 2 statements if you have an prepended input group */
/* border-top-left-radius: 0!important;
border-bottom-left-radius: 0!important; */
}
.tt-dropdown-menu {
min-width: 160px;
margin-top: 2px;
padding: 5px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, .2);
*border-right-width: 2px;
*border-bottom-width: 2px;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}
.tt-suggestion {
display: block;
padding: 3px 20px;
}
.tt-suggestion.tt-is-under-cursor {
color: #fff;
background-color: #0081c2;
background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
background-image: -o-linear-gradient(top, #0088cc, #0077b3);
background-image: linear-gradient(to bottom, #0088cc, #0077b3);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0)
}
.tt-suggestion.tt-is-under-cursor a {
color: #fff;
}
.tt-suggestion p {
margin: 0;
}
.queryRow .label {
opacity: 0.8;
}
.info {
padding:5px;
margin:10px 0;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
font-weight: bold;
}
.info span {
font-weight: normal;
}
#downloadUrl span {
font-family: "Lucida Console", Monaco, monospace;
font-size: 11px;
}
.form-horizontal .form-control {
padding: 4px 10px;
margin: 5px;
height: auto;
}
.control-label {
font-weight: normal;
}
.form-group {
margin: 0;
}
</style>
<link rel="stylesheet" type="text/css" href="http://dev.openlayers.org/theme/default/style.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/3.1.1/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://dev.openlayers.org/OpenLayers.js"></script>
<script type="text/javascript">
var map, ala_wms, boxLayer, polygonLayer, drawControls, wkt;
var alaBaseUrl = "http://biocache.ala.org.au/ws";
var downloadPath = '/occurrences/index/download?';
var downloadExtraParams = {
email: '', // email address of person (optional)
sourceTypeId: 0, // ALA records
reasonTypeId: 10, // use of data (see all values at http://logger.ala.org.au/service/logger/reasons) not optional
file: 'ala_occurrence_records', // filename of download
extra: 'dataResourceUid,dataResourceName.p' // additional fields to include in download
};
var wmsBaseUrl = alaBaseUrl + "/mapping/wms/reflect"; // see http://api.ala.org.au/#ws9 for options
var defaultParamsObj = {
q: "*:*", // '*:* for all records OR species_group:Fish, etc
fq: '' // E.g. "geospatial_kosher:true" or ""
}; // constant
var searchParams = $.extend({}, defaultParamsObj); // clone obj - this one changes
var defaultENV = "name:circle;size:2;opacity:1.0";
$(document).ready(function () {
map = new OpenLayers.Map("map", {
//controls: [new OpenLayers.Control.PanZoom()]
} );
var ol_wms = new OpenLayers.Layer.WMS(
"OpenLayers WMS",
"http://vmap0.tiles.osgeo.org/wms/vmap0",
{layers: "basic"}
);
//var osm_layer = new OpenLayers.Layer.OSM( "Simple OSM Map" );
var mapParams = {
layers: 'ALA:occurrences',
transparent: "true",
format: "image/png",
outline: false,
ENV: "color:ff0000;" + defaultENV //"colormode:grid" f49421
}
ala_wms = new OpenLayers.Layer.WMS(
"Occurrence records",
wmsBaseUrl,
$.extend(mapParams, defaultParamsObj),
{ opacity: 0.8, isBaseLayer: false, visibility: true, sphericalMercator: true }
);
boxLayer = new OpenLayers.Layer.Vector("Box layer");
polygonLayer = new OpenLayers.Layer.Vector("Polygon Layer");
updateDownloadUrl();
updateTotalRecords();
map.addLayers([ol_wms, ala_wms, boxLayer, polygonLayer]);
map.addControl(new OpenLayers.Control.LayerSwitcher());
map.addControl(new OpenLayers.Control.MousePosition());
map.setCenter(new OpenLayers.LonLat(134, -25), 4);
drawControls = {
polygon: new OpenLayers.Control.DrawFeature(polygonLayer,
OpenLayers.Handler.Polygon),
box: new OpenLayers.Control.DrawFeature(boxLayer,
OpenLayers.Handler.RegularPolygon, {
handlerOptions: {
sides: 4,
irregular: true
}
}
)
};
for (var key in drawControls) {
map.addControl(drawControls[key]);
var type = (key == 'box') ? 'create' : 'point';
drawControls[key].handler.callbacks[type] = function(data) {
if (boxLayer.features.length > 0) {
boxLayer.removeAllFeatures();
}
if (polygonLayer.features.length > 0) {
polygonLayer.removeAllFeatures();
}
}
drawControls[key].events.register("featureadded", ' ' , function() {
searchParams.wkt = getBBoxWkt();
//updateDownloadUrl();
//updateTotalRecords();
updatePanel();
$('#clearFeature').removeClass('hide');
});
}
document.getElementById('noneToggle').checked = true;
wkt = new OpenLayers.Format.WKT();
//updateLegend(defaultParamsObj); // inactive (hidden in DOM)
$(window).resize(function () {
$('#map').height($(window).height() - $(".navbar").height()
- parseInt($(".navbar").css("margin-bottom"))
- parseInt($(".navbar").css("margin-top"))
- parseInt($('#map').css("margin-bottom"))
);
});
$(window).resize();
var bieJson = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
//prefetch: '../data/films/post_1960.json',
remote: {
url: 'http://bie.ala.org.au/search/auto.jsonp?q=%QUERY',
filter: function (resp) {
var results = [];
$.each(resp.autoCompleteList, function(i, el) {
if (el.matchedNames.length > 0) {
results.push({name: el.matchedNames[0] });
} else {
results.push({name: el.name });
}
});
//return resp.autoCompleteList;
return results;
},
ajax: { dataType: 'jsonp' }}
});
bieJson.initialize();
var ta = $('.typeahead').typeahead(null, {
name: 'species-lookup',
displayKey: 'name',
source: bieJson.ttAdapter()
}).on('typeahead:autocompleted typeahead:selected', function (obj, datum) {
//console.log('typeahead:autocompleted', obj, datum);
$('#lsid').val(datum.guid);
}).on('typeahead:cursorchanged', function () {
//console.log('typeahead:cursorchanged');
$('#lsid').val('');
});
// on submit
$('#searchForm').submit(function (e) {
e.preventDefault();
var name = $('#query').val();
if (name) {
lookupTaxaGuid(name);
//reset typeahead
$('#query').val('');
$('.typeahead').typeahead('close');
}
});
// remove species
$(document).on('click', '.removeItem', function (e) {
$(this).closest('tr').remove();
updateMap();
});
$('#vectorInfo').click(function(e) {
if (confirm('Do you want to download all ' + $('#totalRecords').text() + ' records?')) {
window.location.href = $('#downloadUrl span').text();
}
});
$('#clearFeature').click(function(e) {
searchParams.wkt = '';
updatePanel(true);
$(this).addClass('hide');
});
//$('input[name="type"]').change(function(e) {
$(document).on('change', 'input[name="type"]', function (e) {
var type = $(this).val();
if (type != 'none') {
updatePanel(true);
}
var element = this;
for (key in drawControls) {
var control = drawControls[key];
if (element.value == key && element.checked) {
control.activate();
} else {
control.deactivate();
}
}
});
// date filtering
$('.dataRange').change(function() {
var thisDate = $(this).val();
var date_regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/; // YYYY-MM-DD
if (!thisDate || (thisDate && date_regex.test(thisDate))) {
// date good
$(this).closest('.form-group').removeClass('has-error');
updateMap();
} else {
// error
alert('date is not provided in the expected format (YYYY-MM-DD)');
$(this).closest('.form-group').addClass('has-error');
}
});
});
function getAlaWmsBaseurl() {
var mappingUrl = "http://biocache.ala.org.au/ws";
return mappingUrl + "/webportal/wms/reflect" + params;
}
function getBBoxWkt() {
var str = '';
if (boxLayer.features[0]) {
str = wkt.write(boxLayer.features[0]);
} else if (polygonLayer.features[0]) {
str = wkt.write(polygonLayer.features[0]);
}
return str;
}
function updateDownloadUrl() {
//console.log('searchParams', searchParams);
var dlParams = $.extend({}, searchParams, downloadExtraParams);
var url = alaBaseUrl + downloadPath + $.param(dlParams);
$('#downloadUrl span').text(url);
}
function updateTotalRecords() {
var url = alaBaseUrl + '/occurrences/search.json?' + $.param(searchParams) + '&pageSize=0&facet=off&callback=?';
$.getJSON(url, function (data) {
var total = data.totalRecords;
try {
total = total.toLocaleString();
} catch (e) {
// return e​.name === "RangeError";
}
$('#totalRecords').text(total);
});
}
function updateMap() {
var queries = [];
$('.queryRow .taxonName').each(function (i, el) {
var name = $(this).text();
var guid = $(this).data('guid');
if (guid) {
queries.push("lsid:" + guid);
} else {
queries.push("text:" + name);
}
});
var newParams = $.extend({}, searchParams, defaultParamsObj); // take a copy
if (queries.length > 0) {
//console.log('queries', queries);
newParams.q = queries.join(" OR ");
newParams.ENV = "colormode:species_guid;" + defaultENV;
} else {
newParams.ENV = "color:ff0000;" + defaultENV;
$('#firstRow').show();
}
var startDate = $('#startDate').val();
var endDate = $('#endDate').val();
var isoSuffix = 'T00:00:00Z';
if (startDate || endDate) {
var start = (startDate) ? startDate + isoSuffix : '*';
var end = (endDate) ? endDate + isoSuffix : '*';
newParams.fq = 'occurrence_date:[' + start + ' TO ' + end + ']';
} else {
newParams.fq = '';
}
searchParams.q = newParams.q;
searchParams.fq = newParams.fq;
ala_wms.mergeNewParams(newParams);
updatePanel();
//updateLegend(newParams);
}
function lookupTaxaGuid(name) {
var url = 'http://bie.ala.org.au/ws/guid/batch.json?q=' + name + '&callback=?'; // q=
$.getJSON(url, function (data) {
var items = [];
$.each(data, function (key, val) {
//console.log('hit', key, val);
if (val[0] && val[0].identifier) {
//console.log('val', val[0].identifier);
updateSpeciesTable(val[0].name, val[0].identifier)
} else {
updateSpeciesTable(name, '')
}
});
updateMap();
});
}
function updateSpeciesTable(name, guid) {
$('#firstRow').hide();
var tmpl = $('.rowTemplate').clone();
tmpl.removeClass('hide').removeClass('rowTemplate').addClass('queryRow');
tmpl.find('.taxonName').html(name);
if (guid) {
tmpl.find('.taxonName').data('guid', guid);
tmpl.find('.matched').removeClass('hide');
} else {
tmpl.find('.taxonName').data('guid', '');
tmpl.find('.unmatched').removeClass('hide');
}
//console.log('params', name, guid);
$('#results tbody').append(tmpl);
//$('#results tbody').append('<tr><td data-guid="">' + name + '</td><td><button class="btn btn-default btn-xs" title="remove item"><span class="glyphicon glyphicon-remove"></span> </button></td></tr>');
}
function updatePanel(removeFeature) {
if (removeFeature) {
boxLayer.removeAllFeatures();
polygonLayer.removeAllFeatures();
searchParams.wkt = '';
}
updateTotalRecords();
updateDownloadUrl();
}
function updateLegend(params) {
$('#legend span').html('<img id="gridLegendImg" src="' + alaBaseUrl + '/density/legend?' + $.param(params) + '"/>');
}
</script>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">ALA map demo</a>
</div>
<div class="navbar-collapse collapse">
<form class="hide navbar-form navbar-right" role="form">
<div class="form-group">
<input type="text" placeholder="Email" class="form-control">
</div>
<div class="form-group">
<input type="password" placeholder="Password" class="form-control">
</div>
<button type="submit" class="btn btn-success">Sign in</button>
</form>
</div>
<!--/.navbar-collapse -->
</div>
</div>
<div class="container-fluid">
<!-- Example row of columns -->
<div class="row">
<div class="col-md-4 sidebar">
<h3>Step 2: Create a subset</h3>
<form id="searchForm">
<div class="input-group">
<input type="text" id="query" class="form-control typeahead" value=""
placeholder="Search for species"/>
<input type="hidden" id="lsid" value=""/>
<span class="input-group-btn">
<button class="btn btn-default" type="submit" id="searchBtn">Add</button>
</span>
</div>
<!-- /input-group -->
</form>
<div>&nbsp;</div>
<table id="results" class="table table-bordered table-striped">
<thead>
<tr>
<th>Species</th>
</tr>
<tbody>
<tr id='firstRow'>
<td style="color:#999;">Currently showing all records. Use the search box above to filter by a species or higher taxon</td>
</tr>
<tr class="hide rowTemplate">
<td>
<span class="taxonName"></span>&nbsp;&nbsp;
<span class="hide label label-success matched">Matched name</span>
<span class="hide label label-warning unmatched">Unmatched name</span>
<button class="removeItem btn btn-default btn-xs pull-right" title="remove item">
<span class="glyphicon glyphicon-remove"></span>
</button>
</td>
</tr>
</tbody>
</table>
<div class="form-group">
<b>Map control</b>
<button id="clearFeature" class="hide btn btn-info pull-right" title="clear bounding box">
<span class="glyphicon glyphicon-remove-circle"></span> Clear bounding box
</button>
<div class="radio">
<label>
<input type="radio" name="type" id="noneToggle" value="none" checked>
Navigate map
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="type" id="boxToggle" value="box" >
Draw box
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="type" id="polygonToggle" value="polygon">
Draw polygon
</label>
</div>
</div>
<form class="form-horizontal" role="form">
<!--Filter by date: <b>1900</b> <input id="dateRange" type="text" class="span2" value="" data-slider-min="1900" data-slider-max="2015" data-slider-step="5" data-slider-value="[2000,2015]"/> <b>2015</b>-->
<b>Date (UTC)</b><br/>
between<br>
<div class="form-group ">
<label class="control-label col-sm-3" for="startDate">start date</label>
<div class="col-sm-6">
<input type="text" id="startDate" class="dataRange form-control" value="" placeholder="yyyy-mm-dd"/>
</div>
</div>
<div class="form-group ">
<label class="control-label col-sm-3" for="endDate">end date</label>
<div class="col-sm-6">
<input type="text" id="endDate" class="dataRange form-control" value="" placeholder="yyyy-mm-dd"/>
</div>
</div>
</form>
<div class="hide" id="legend">Record density legend:<br/><span></span></div>
<div class="info bg-danger">
Total records: <span id="totalRecords"></span>
</div>
<div class="info bg-info" id="downloadUrl" style="word-wrap:break-word;">
download URL: <span></span>
</div>
<button id="vectorInfo" class="btn btn-default btn-sm" title="get vector info">
Next &nbsp;&nbsp;<span class="glyphicon glyphicon-forward"></span>
</button>
</div>
<div class="col-md-8 main">
<div id="map" class="mapContainer"></div>
</div>
</div>
<!-- ><hr>
<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