-
-
Save jsanz/823d7b215d5928ea67cdbd62c4cd54f4 to your computer and use it in GitHub Desktop.
HaCkARTO.js · Javier Aragón submission
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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/[email protected]/dist/leaflet.css" /> | |
<script src="https://unpkg.com/[email protected]/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: '©<a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>, ©<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: '©<a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>, ©<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