Skip to content

Instantly share code, notes, and snippets.

@jsanz
Forked from padawannn/index.html
Last active August 14, 2018 15:02
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 jsanz/823d7b215d5928ea67cdbd62c4cd54f4 to your computer and use it in GitHub Desktop.
Save jsanz/823d7b215d5928ea67cdbd62c4cd54f4 to your computer and use it in GitHub Desktop.
HaCkARTO.js · Javier Aragón submission
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CARTO.js App</title>
<link href="https://fonts.googleapis.com/css?family=Fira+Sans:200,200i,300,300i,400,400i,500,500i,600,600i,700,700i,800,800i,900,900i" rel="stylesheet">
<!-- Include Leaflet 1.2.0 Library -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.2.0/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"></script>
<!-- Include cartodb.js Library -->
<script src="https://rawgit.com/CartoDB/cartodb.js/dist/carto.js"></script>
<style>
html, body { margin: 0; padding: 0; font-family: "Fira Sans";}
h1, h2, h3, h4, h5 { margin: 0; padding: 0;}
ul {list-style: none; margin: 0; padding: 0;}
#map { position: absolute; top: 0; left: 0; right: 517px; height: 100%; z-index: 1;}
select {
margin-left: 10px;
}
#mainWidget {
position: absolute;
z-index: 2;
right: 0;
top: 0;
bottom: 0;
width: 517px;
background-color: #fff;
display: flex;
flex-direction: column;
}
#mainWidget .header {
background-color: #A42806;
padding: 24px;
}
#mainWidget .header h1{
color: #FFFFFF;
font-size: 12px;
letter-spacing: 2px;
line-height: 12px;
font-weight: 500;
text-transform: uppercase;
display: flex;
align-items: center;
}
#mainWidget .header h2{
color: #FFFFFF;
font-size: 36px;
font-weight: 900;
line-height: 36px;
opacity: 0.85;
margin-top: 10px;
display: flex;
align-items: center;
}
#mainWidget .header h3{
color: rgba(255,255,255,0.6);
font-size: 21px;
font-style: italic;
line-height: 24px;
margin-top: 6px;
font-weight: 300;
}
#mainWidget .flange {
display: flex;
}
#mainWidget .flange li {
cursor: pointer;
flex: 1 1 50%;
height: 47px;
color: #911909;
font-size: 12px;
font-weight: 500;
letter-spacing: 2px;
text-transform: capitalize;
display: flex;
align-items: center;
justify-content: center;
text-transform: uppercase;
}
#mainWidget .flange li.selected {
border-bottom: 2px solid #A42806;
}
#mainWidget .content {
flex: 1 1 0%;
overflow-y: auto;
}
#mainWidget .content >* {
padding: 0 24px;
}
#mainWidget .content h4{
font-size: 12px;
font-weight: 500;
letter-spacing: 2px;
line-height: 12px;
color: #39393A;
text-transform: uppercase;
margin-top: 24px;
}
#mainWidget .content h5 {
opacity: 0.85;
color: #000000;
font-size: 72px;
font-weight: 900;
line-height: 72px;
margin-top: 5px;
border-bottom: 1px solid #E8E8E8;
padding-bottom: 24px;
}
#mainWidget .content h5 span {
color: #898989;
opacity: 0.85;
font-size: 48px;
font-weight: 300;
}
#info {
margin-bottom: 40px;
}
#info >img {
width: calc(100% + 48px);
margin: 0 -24px;
}
#info p {
font-size: 16px;
font-weight: 300;
line-height: 24px;
color: #000000;
}
#info a, #info span {
display: block;
font-size: 16px;
font-weight: 300;
line-height: 24px;
color: #911909;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
margin-top: 10px;
}
#mainWidget .content .ranking .rankingList {
margin-top: 20px;
}
#rankingList .elem {
margin-top: 20px;
}
#rankingList .elem .label {
display: flex;
justify-content: space-between;
color: #000000;
font-size: 16px;
line-height: 16px;
}
#rankingList .elem .bar {
width: 469px;
height: 6px;
border-radius: 2px;
background-color: #E8E8E8;
margin-top: 6px;
position: relative;
}
#rankingList .elem .bar >div {
position: absolute;
left: 0;
top: 0;
z-index: 1;
border-radius: 2px;
background-color: #A42806;
height: 100%;
}
.hide {
display: none;
}
</style>
</head>
<body>
<div id="map"></div>
<div id="mainWidget">
<div class="header">
<h1>
Migration movement
<select id="seasons">
<option value="-1">All seasons</option>
<option value="spring">Spring</option>
<option value="summer">Summer</option>
<option value="autumn">Autumn</option>
<option value="winter">Winter</option>
</select>
</h1>
<h2>
Red-backed shrike
<select id="bird">
<option value="38">#1</option>
<option value="45">#2</option>
<option value="47">#3</option>
<option value="59">#4</option>
<option value="139">#5</option>
<option value="647">#6</option>
<option value="649">#7</option>
</select>
</h2>
<h3>Lanius collurio</h3>
</div>
<ul class="flange">
<li id="widgetSelect" class="selected">Widgets</li>
<li id="infoSelect">Info</li>
</ul>
<div class="content">
<div id="data">
<h4>Distance traveled</h4>
<h5 id="distance"> <span>Km</span></h5>
<div class="ranking">
<h4>Ranking of presence in regions</h4>
<div id="rankingList">
</div>
</div>
</div>
<div id="info" class="hide">
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/Red-backed_shrike.jpg/640px-Red-backed_shrike.jpg">
<p>The red-backed shrike (<i>Lanius collurio</i>) is a carnivorous passerine bird and member of the shrike family Laniidae. This bird breeds in most of Europe and western Asia and winters in tropical Africa [1].</p>
<p>This website shows seasonal movements of seven red-backed shrike individuals. You can see in the map full annual cycle migration for each individual which it consists of four main staging sites [2]:<br>
- South-eastern Europe (autumn). <br>
- Sahelian north-eastern Africa (autumn).<br>
- Southern Africa (austral summer).<br>
- North-eastern Africa (spring).</p>
<p>Data source is Movebank data repository [3].</p>
<h4>Resources</h4>
<a href="https://en.wikipedia.org/wiki/Red-backed_shrike">1. Wikipedia: https://en.wikipedia.org/wiki/Red-backed_shrike</a>
<a href="http://onlinelibrary.wiley.com/doi/10.1111/jav.01352/abstract;jsessionid=084613A923C661781FFBD7A0BF47F9A6.f04t02">2. http://onlinelibrary.wiley.com/doi/10.1111/jav.01352/abstract;jsessionid=084613A923C661781FFBD7A0BF47F9A6.f04t02</a>
<a href="https://www.datarepository.movebank.org/handle/10255/move.639">3. Movebank data repository: https://www.datarepository.movebank.org/handle/10255/move.639</a>
<span>4. Photo: A red-backed shrike at lake Kerkini-Beles Natura 2000 site", Antonios Tsaknakis. CC BY-SA 4.0</span>
</div>
</div>
</div>
<script>
var alcaudonDorsirrojoDotted = null;
var alcaudonDorsirrojoBuffer = null;
var originDestination = null;
var distanceDataview = null;
var rankingDataview = null;
var client = null;
function main () {
var map = L.map('map');
map.fitBounds([
[-7.810460353,-18.79636209],
[42.03279798,46.54188273]
])
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: '&copy;<a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>, &copy;<a href="https://carto.com/attribution">CARTO</a>'
}).addTo(map);
// Adding Voyager Labels
// L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager_only_labels/{z}/{x}/{y}.png', {
// maxZoom: 18,
// attribution: '&copy;<a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>, &copy;<a href="https://carto.com/attribution">CARTO</a>',
// zIndex: 100
// }).addTo(map);
client = new carto.Client({
apiKey: 'default_public',
username: 'jcamacho'
});
refreshData();
drawLayerOriginDestination(client, 59);
client.getLeafletLayer().addTo(map);
document.getElementById('bird').addEventListener("change", function() {
refreshData();
});
document.getElementById('seasons').addEventListener("change", function() {
refreshData();
});
document.getElementById('widgetSelect').addEventListener("click", function() {
document.getElementById('widgetSelect').className = 'selected';
document.getElementById('infoSelect').className = '';
document.getElementById('data').className = '';
document.getElementById('info').className = 'hide';
});
document.getElementById('infoSelect').addEventListener("click", function() {
document.getElementById('widgetSelect').className = '';
document.getElementById('infoSelect').className = 'selected';
document.getElementById('data').className = 'hide';
document.getElementById('info').className = '';
});
}
function refreshData() {
drawLayerRoute();
drawLayerOriginDestination();
getDistance();
getRanking();
}
function drawLayerOriginDestination(){
var alcaudonDorsirrojoDataset = new carto.source.SQL(`
SELECT ST_Transform(the_geom, 3857) AS the_geom_webmercator, 0 AS cartodb_id
FROM (
SELECT * FROM alcaudon_dorsirrojo
WHERE tag_local_identifier = ${document.getElementById('bird').value} ${getSeasonCondition()}
ORDER BY timestamp ASC LIMIT 1
) q
UNION ALL
SELECT ST_Transform(the_geom, 3857) AS the_geom_webmercator, 1 AS cartodb_id
FROM (
SELECT * FROM alcaudon_dorsirrojo
WHERE tag_local_identifier = ${document.getElementById('bird').value} ${getSeasonCondition()}
ORDER BY timestamp DESC LIMIT 1
) q
`);
var style = new carto.style.CartoCSS(`
#layer {
marker-fill-opacity: 1;
marker-width: 10.0;
marker-height: 10.0;
[cartodb_id = 0]
{marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/jsanz/assets/20180814145932angle-right.svg');}
[cartodb_id = 1]
{marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/jsanz/assets/20180814145932angle-right.svg');}
}
`);
if (!originDestination) {
originDestination = new carto.layer.Layer(alcaudonDorsirrojoDataset,style);
client.addLayer(originDestination);
}
originDestination.getSource().setQuery(alcaudonDorsirrojoDataset.getQuery())
}
function drawLayerRoute() {
var alcaudonDorsirrojoDataset = new carto.source.SQL(`
SELECT ST_Transform(ST_MakeLine(the_geom), 3857) AS the_geom_webmercator, 0 AS cartodb_id
FROM (
SELECT * FROM alcaudon_dorsirrojo
WHERE tag_local_identifier = ${document.getElementById('bird').value} ${getSeasonCondition()} ORDER BY timestamp ASC
) q
`);
var alcaudonDorsirrojoDottedStyle = new carto.style.CartoCSS(`
#layer {
line-width: 1.5;
line-color: #A42806;
line-opacity: 1;
line-smooth: 0.5;
line-dasharray: 1.5,3;
}
`);
var alcaudonDorsirrojoBufferStyle = new carto.style.CartoCSS(`
#layer {
line-width: 8;
line-color: #A42806;
line-opacity: 0.2;
line-smooth: 0.5;
}
`);
if (!alcaudonDorsirrojoBuffer) {
alcaudonDorsirrojoBuffer = new carto.layer.Layer(alcaudonDorsirrojoDataset,alcaudonDorsirrojoBufferStyle);
client.addLayer(alcaudonDorsirrojoBuffer);
}
if (!alcaudonDorsirrojoDotted) {
alcaudonDorsirrojoDotted = new carto.layer.Layer(alcaudonDorsirrojoDataset,alcaudonDorsirrojoDottedStyle);
client.addLayer(alcaudonDorsirrojoDotted);
}
alcaudonDorsirrojoDotted.getSource().setQuery(alcaudonDorsirrojoDataset.getQuery())
alcaudonDorsirrojoBuffer.getSource().setQuery(alcaudonDorsirrojoDataset.getQuery())
}
function getDistance() {
if(distanceDataview) {
client.removeDataview(distanceDataview)
}
distanceDataview = new carto.dataview.Category(new carto.source.SQL(`
SELECT trunc(ST_Length(the_geom_webmercator) / 1000) AS distance
FROM (
SELECT ST_Transform(ST_MakeLine(the_geom), 3857) AS the_geom_webmercator
FROM (
SELECT * FROM alcaudon_dorsirrojo
WHERE tag_local_identifier = ${document.getElementById('bird').value} ${getSeasonCondition()} ORDER BY timestamp ASC
) q
) p
`), 'distance');
distanceDataview.on('dataChanged', function (data) {
distance = data.categories.map(function (category) { return category.name; }).sort();
document.getElementById('distance').innerHTML = `${distance[0]} <span>Km</span>`;
});
client.addDataview(distanceDataview);
}
function getRanking() {
if (rankingDataview ) {
client.removeDataview(rankingDataview)
}
var rankingDataview = new carto.dataview.Category(new carto.source.SQL(`
SELECT (region || '#' || trunc((distance / total * 100)::numeric, 2)) AS data
FROM (
SELECT ST_Length(the_geom) AS distance, region, (
SELECT sum(distance) FROM (
SELECT region, distance
FROM (
SELECT ST_Length(the_geom) AS distance, region
FROM (
SELECT ST_MakeLine(the_geom) AS the_geom, region FROM alcaudon_dorsirrojo
WHERE region IS NOT NULL AND tag_local_identifier = ${document.getElementById('bird').value} ${getSeasonCondition()}
GROUP BY region
) q
) p
WHERE distance <> 0
ORDER BY distance DESC ) m
) AS total
FROM (
SELECT ST_MakeLine(the_geom) AS the_geom, region FROM alcaudon_dorsirrojo
WHERE region IS NOT NULL AND tag_local_identifier = ${document.getElementById('bird').value} ${getSeasonCondition()}
GROUP BY region
) q
) p
WHERE distance <> 0
ORDER BY distance DESC
`), 'data');
rankingDataview.on('dataChanged', function (data) {
data = data.categories.map(function (category) { return category.name; }).sort();
document.getElementById('rankingList').innerHTML = '';
for (var d of data) {
document.getElementById('rankingList').innerHTML +=
`<div class="elem">
<div class="label">
<span>${d.split('#')[0]}</span>
<span>${d.split('#')[1]} %</span>
</div>
<div class="bar">
<div style="width:${d.split('#')[1]}%;"></div>
</div>
</div>`
;
}
});
client.addDataview(rankingDataview);
}
function getSeasonCondition() {
var season = document.getElementById('seasons').value;
if (season != '-1'){
return ` AND season='${document.getElementById('seasons').value}'`
}
return '';
}
document.addEventListener('DOMContentLoaded', main);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment