Last active
July 20, 2018 08:08
-
-
Save ramiroaznar/3ce20b177abac5634602ed40b3f15f85 to your computer and use it in GitHub Desktop.
CARTO.js GoT Distance Calculator
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
html, body { | |
margin: 0; | |
padding: 0; | |
font-family: 'MedievalSharp', sans-serif; | |
} | |
.title { | |
margin: 10px 0; | |
font-weight: bold; | |
font-size: 36px; | |
text-align: center; | |
} | |
.text { | |
font-family: 'Lato', sans-serif; | |
font-size: 12px; | |
color: #333; | |
text-align: center; | |
} | |
#map { | |
position: absolute; | |
top: 0; left: 0; | |
width: 100%; | |
height: 100%; | |
} | |
#season-selector{ | |
z-index: 9000; | |
margin: 30px 30px 24px; | |
border: none; | |
border-radius: 3px; | |
height: 9px; | |
} | |
#sidebar { | |
position: absolute; | |
bottom: 0; | |
left: 0; | |
padding: 20px; | |
font-size: 2em; | |
background-color: rgba(255, 255, 255, 0.8); | |
z-index: 1000; | |
} | |
.legend{ | |
max-width: 400px; | |
} | |
.ui-widget-content { | |
background: #D1BDA2; | |
} | |
.slider-range { | |
background: #792427; | |
border-radius: 3px; | |
} | |
.slider-handle { | |
background:url(https://res.cloudinary.com/dh6mm17sc/image/upload/v1511284538/Bitmap_profpd.png) !important; | |
border: none !important; | |
top: -14px !important; | |
} | |
.slider-handle:first-of-type { | |
background:url(https://res.cloudinary.com/dh6mm17sc/image/upload/v1511284542/Group_xmflvz.png) !important; | |
top: -16px !important; | |
height: 50px !important; | |
} | |
#season-labels { | |
display: flex; | |
justify-content: space-between; | |
margin: 0 30px 20px; | |
} | |
.season-label { | |
font-size: 16px; | |
font-weight: bold; | |
} |
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
function main(){ | |
// declare map, query & style | |
const map = L.map('map').setView([7, 20], 5); | |
const linesQuery = $('#lines-query').text(); | |
const linesStyle = $('#lines-style').text(); | |
const pointsQuery = $('#points-query').text(); | |
const pointsStyle = $('#points-style').text(); | |
const labelsQuery = $('#labels-query').text(); | |
const labelsStyle = $('#labels-style').text(); | |
// create season selector | |
const seasonSelector = $("#season-selector"); | |
seasonSelector.slider({ | |
range: true, | |
min: 1, | |
max: 7, | |
step: 1, | |
values: [ 1, 3 ], | |
classes: { | |
"ui-slider": "slider-bar", | |
"ui-slider-handle": "slider-handle", | |
"ui-slider-range": "slider-range" | |
}, | |
labels: ['I', 'II', 'III', 'IV', 'V', 'VI', 'VII'], | |
}); | |
const labels = $('#season-selector').data().uiSlider.options.labels | |
for (var label of labels) { | |
const el = $('<label>').addClass('season-label').text(label); | |
$( "#season-labels" ).append(el); | |
} | |
// add dark GoT basemap | |
L.tileLayer('https://cartocdn-gusc-d.global.ssl.fastly.net/ramirocartodb/api/v1/map/named/tpl_d44e8b0f_a525_4d23_b93a_71aba54674bc/all/{z}/{x}/{y}.png', { | |
maxZoom: 18, | |
attribution: '<a href="https://carto.com/attribution">CARTO</a>' | |
}).addTo(map); | |
map.removeControl(map.zoomControl); | |
// create CARTO client | |
const client = new carto.Client({ | |
apiKey: 'default_public', | |
username: 'ramirocartodb' | |
}); | |
// create a source, style & lines layer | |
const linesSource = new carto.source.SQL(linesQuery); | |
const linesCartoCSS = new carto.style.CartoCSS(linesStyle); | |
const linesLayer = new carto.layer.Layer(linesSource, linesCartoCSS, { | |
featureOverColumns: ['character', 'distance'] | |
}); | |
// create a source, style & labels layer | |
const labelsSource = new carto.source.SQL(labelsQuery); | |
const labelsCartoCSS = new carto.style.CartoCSS(labelsStyle); | |
const labelsLayer = new carto.layer.Layer(labelsSource, labelsCartoCSS); | |
// create a source, style & points layer | |
// GoT emojis source: https://downloademoji.com/game-of-thrones/ | |
const pointsSource = new carto.source.SQL(pointsQuery); | |
const pointsCartoCSS = new carto.style.CartoCSS(pointsStyle); | |
const pointsLayer = new carto.layer.Layer(pointsSource, pointsCartoCSS); | |
// add layers to the client | |
client.addLayers([linesLayer, labelsLayer, pointsLayer]); | |
// add the client layer to the map | |
client.getLeafletLayer().addTo(map); | |
// add popup | |
const popup = L.popup({ closeButton: false }); | |
linesLayer.on('featureOver', function (featureEvent) { | |
popup.setLatLng(featureEvent.latLng); | |
const name = featureEvent.data.character; | |
const dist = featureEvent.data.distance.toFixed(0); | |
popup.setContent(` | |
<h2 style="font-size: 14px; font-family: 'MedievalSharp', sans-serif;"> ${name} </h2> | |
<p style="font-size: 10px; font-family: 'MedievalSharp', sans-serif;"> ${dist} Km</p> | |
`); | |
popup.openOn(map); | |
console.log(featureEvent.data, featureEvent.distance); | |
}); | |
linesLayer.on('featureOut', function (featureEvent) { | |
popup.removeFrom(map); | |
}); | |
// add character data view | |
const charCategory = new carto.dataview.Category( | |
linesSource, 'character', { | |
limit: 16, | |
operation: carto.operation.SUM, | |
operationColumn: 'distance' | |
}); | |
const names = [], distances = []; | |
charCategory.on('dataChanged', function (newData) { | |
console.log('---Distance by character---'); | |
names.length = 0; | |
distances.length = 0; | |
for (category of newData.categories){ | |
names.push(category.name); | |
distances.push(category.value); | |
console.log(category.name, category.value); | |
} | |
const topBackgrounds = []; | |
// assign color to character name | |
for (const name of names) { | |
if (name =="Theon") { | |
topBackgrounds.push("#F2B701"); | |
} | |
else if (name =="Eddard" || name =="Bran" || name =="Arya" || name =="Sansa" || name =="Jon" || name =="Robb") { | |
topBackgrounds.push("#88CCEE"); | |
} | |
else if (name =="Jaime" || name =="Cersei" || name =="Tyrion") { | |
topBackgrounds.push("#882255"); | |
} | |
else if (name == "Sam") { | |
topBackgrounds.push("#661100"); | |
} | |
else if (name == "Brienne") { | |
topBackgrounds.push("#332288"); | |
} | |
else if (name == "Davos") { | |
topBackgrounds.push("#888888"); | |
} | |
else if (name == "Daenerys") { | |
topBackgrounds.push("#DDCC77"); | |
} | |
else {topBackgrounds.push("#117733"); | |
} | |
} | |
// add bar chart widget | |
const widget = document.getElementById("chart").getContext("2d"); | |
const chartOptions = { | |
legend: { | |
display:false, | |
}, | |
title: { | |
display: true, | |
text: 'Distance in Kms', | |
position: 'bottom', | |
}, | |
scales: { | |
yAxes: [{ | |
barPercentage: 0.2, | |
gridLines: { | |
display: false, | |
} | |
}], | |
xAxes: [{ | |
barPercentage: 0.2, | |
gridLines: { | |
display: false, | |
} | |
}] | |
}, | |
elements: { | |
rectangle: { | |
borderSkipped: 'left', | |
} | |
} | |
}; | |
Chart.defaults.global.defaultFontFamily = "'MedievalSharp', sans-serif"; | |
Chart.defaults.global.defaultFontSize = 14; | |
const myChart = new Chart(widget, { | |
type: 'horizontalBar', | |
data: { | |
labels: names, | |
datasets: [{ | |
label: '', | |
data: distances, | |
backgroundColor: topBackgrounds, | |
borderWidth: 0 | |
}] | |
}, | |
options: chartOptions | |
}); | |
console.log(names); | |
}); | |
client.addDataview(charCategory); | |
// add bbox filters | |
const bboxFilter = new carto.filter.BoundingBoxLeaflet(map); | |
charCategory.addFilter(bboxFilter); | |
/* Slider on change */ | |
// filter query when selecting season range | |
seasonSelector.on("slidechange", function( event, ui ) { | |
let seasons = seasonSelector.slider( "values" ); | |
let rangeSeasons = Array.apply(null, {length: (seasons[1]-seasons[0]+1)}).map(function(value, index){ | |
return index + seasons[0]; | |
}); | |
filterSeason(rangeSeasons); | |
console.log(rangeSeasons); | |
}); | |
const filterSeason = function (season) { | |
let filterLinesQuery = linesQuery; | |
let filterPointsQuery = pointsQuery; | |
if (season) { | |
filterLinesQuery = ` | |
select | |
row_number() over() as cartodb_id, | |
array_agg(distinct season) as seasons, | |
max(color) as color, | |
st_makeline(the_geom order by cartodb_id asc) as the_geom, | |
st_length(st_makeline(the_geom order by cartodb_id asc)::geography)*0.138980150292748/1000 as distance, | |
st_transform(st_makeline(the_geom order by cartodb_id asc), 3857) as the_geom_webmercator, | |
max(house) as house, | |
character | |
from | |
episodes_locations | |
where season in (${season}) | |
group by | |
character`; | |
filterPointsQuery = ` | |
select | |
row_number() over() as cartodb_id, | |
array_agg(distinct season) as seasons, | |
max(color) as color, | |
st_endpoint(st_makeline(the_geom order by cartodb_id asc)) as the_geom, | |
st_transform(st_endpoint(st_makeline(the_geom order by cartodb_id asc)), 3857) as the_geom_webmercator, | |
max(house) as house, | |
character | |
from | |
episodes_locations | |
where season in (${season}) | |
group by | |
character`; | |
} | |
linesSource.setQuery(filterLinesQuery); | |
pointsSource.setQuery(filterPointsQuery); | |
}; | |
} | |
window.load = main(); |
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 GoT Distance Calculator</title> | |
<link rel="shortcut icon" href="egg.ico" /> | |
<!-- family fonts --> | |
<link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet"> | |
<link href="https://fonts.googleapis.com/css?family=MedievalSharp" rel="stylesheet"> | |
<!-- leaflet + jquery --> | |
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" /> | |
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> | |
<!-- chart.js --> | |
<script src="http://www.chartjs.org/dist/2.7.1/Chart.bundle.js"></script> | |
<script src="http://www.chartjs.org/samples/latest/utils.js"></script> | |
<!-- carto.js --> | |
<script src="https://cdn.rawgit.com/CartoDB/cartodb.js/@4.0.0-alpha.28/carto.js"></script> | |
<!-- jquery-ui --> | |
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> | |
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> | |
<link rel="stylesheet" type="text/css" href="app.css"> | |
<script type="text/sql" id="labels-query"> | |
select * from locations | |
</script> | |
<script type="text/cartocss" id="labels-style"> | |
Map{ | |
buffer-size: 512; | |
} | |
#layer[type='City']{ | |
::inner{ | |
marker-fill-opacity: 1; | |
marker-fill:#fff; | |
marker-line-width: 0.7; | |
marker-line-opacity: 0.8; | |
marker-placement: point; | |
marker-type: ellipse; | |
marker-width: 4; | |
marker-line-color: fadeout(lighten(#bac9ad,12),70); | |
marker-allow-overlap: true; | |
[zoom>=5]{marker-width: 6;} | |
[zoom>=6]{marker-width: 7;} | |
[zoom>=7]{marker-width: 10;} | |
} | |
::labels { | |
text-name: [name]; | |
text-face-name: "Lato Bold Italic"; | |
text-size: 13; | |
text-fill: #fff; | |
text-halo-fill:fadeout(lighten(#bac9ad,12),70); | |
text-halo-radius: 1.5; | |
text-placement-type: simple; | |
text-placements: "E,W,NW,NE,SE,8"; | |
text-dx:-7; | |
text-dy:-4; | |
text-character-spacing: 0; | |
[zoom>=5]{text-size: 14;} | |
[zoom>=6]{text-size: 15;} | |
[zoom>=7]{text-size: 16;} | |
} | |
} | |
</script> | |
<script type="text/sql" id="lines-query"> | |
select | |
row_number() over() as cartodb_id, | |
st_makeline(the_geom order by cartodb_id asc) as the_geom, | |
st_transform(st_makeline(the_geom order by cartodb_id asc), 3857) as the_geom_webmercator, | |
array_agg(distinct season) as seasons, | |
st_length(st_makeline(the_geom order by cartodb_id asc)::geography)*0.138980150292748/1000 as distance, | |
character, | |
max(color) as color, | |
max(house) as house | |
from | |
episodes_locations | |
group by | |
character | |
</script> | |
<script type="text/cartocss" id="lines-style"> | |
#layer { | |
line-width: 4; | |
line-opacity: 0.5; | |
[ house = "Stark" ] { | |
line-color: fadeout(lighten(#bac9ad,12),70); | |
} | |
[ house = "Lannister" ] { | |
line-color: fadeout(lighten(#DDCC77,12),70); | |
} | |
[ house = "Baelish" ] { | |
line-color: fadeout(lighten(#bac9ad,12),70); | |
} | |
[ house = "Greyjoy" ] { | |
line-color: fadeout(lighten(#DDCC77,12),70); | |
} | |
[ house = "Seaworth" ] { | |
line-color: fadeout(lighten(#bac9ad,12),70); | |
} | |
[ house = "Targaryen" ] { | |
line-color: fadeout(lighten(#332288,12),70); | |
} | |
[ house = "Tarly" ] { | |
line-color: fadeout(lighten(#117733,12),70); | |
} | |
[ house = "Tarth" ] { | |
line-color: fadeout(lighten(#CC6677,12),70); | |
} | |
::inner{ | |
line-width: 2; | |
line-opacity: 1; | |
line-dasharray: 10, 4; | |
line-color: [color]; | |
} | |
} | |
</script> | |
<script type="text/sql" id="points-query"> | |
select | |
row_number() over() as cartodb_id, | |
st_endpoint(st_makeline(the_geom order by cartodb_id asc)) as the_geom, | |
st_endpoint(st_transform(st_makeline(the_geom order by cartodb_id asc), 3857)) as the_geom_webmercator, | |
array_agg(distinct season) as seasons, | |
character, | |
max(color) as color, | |
max(house) as house | |
from | |
episodes_locations | |
group by | |
character | |
</script> | |
<script type="text/cartocss" id="points-style"> | |
#layer{ | |
marker-width: 20; | |
marker-allow-overlap: true; | |
marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/ramirocartodb/assets/20171129092439Group_xmflvz.png'); | |
[character = 'Daenerys']{ | |
marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/ramirocartodb/assets/2017112909220702_daenerys.png'); | |
} | |
[character = 'Jon']{ | |
marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/ramirocartodb/assets/2017112909223403_jonsnow.png'); | |
} | |
[character = 'Tyrion']{ | |
marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/ramirocartodb/assets/2017112909225804_tyrion.png'); | |
} | |
[character = 'Jaime']{ | |
marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/ramirocartodb/assets/2017112909245705_jaime.png'); | |
} | |
[character = 'Arya']{ | |
marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/ramirocartodb/assets/2017112909251508_arya.png'); | |
} | |
[character = 'Sam']{ | |
marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/ramirocartodb/assets/2017112909252409_sam.png'); | |
} | |
[character = 'Sansa']{ | |
marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/ramirocartodb/assets/2017112909261616_sansa.png'); | |
} | |
[character = 'Cersei']{ | |
marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/ramirocartodb/assets/2017112909253711_cersei.png'); | |
} | |
[character = 'Robb']{ | |
marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/ramirocartodb/assets/2017112909255816_robb.png'); | |
} | |
[character = 'Littlefinger']{ | |
marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/ramirocartodb/assets/2017112909262717_petyr.png'); | |
} | |
[character = 'Brienne']{ | |
marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/ramirocartodb/assets/2017112909263720_brienne.png'); | |
} | |
} | |
</script> | |
</head> | |
<body> | |
<div id="map"></div> | |
<div id="sidebar"> | |
<div class="legend"> | |
<h6 class="title">Game of Thrones Distance Calculator</h6> | |
<p class="text">Is winter coming at the speed of light? Is Littlefinger using teleportation? Are ravens faster than dragons? In order to answer this questions, check the GoT Distance Calculator built with CARTO.js. <strong>Discover which character has traveled the most depending on the season.</strong></p> | |
</div> | |
<div id="season-selector"></div> | |
<div id="season-labels"></div> | |
<canvas id="chart" width="400" height="400"></canvas> | |
</div> | |
<script type="text/javascript" src="app.js"></script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment