Skip to content

Instantly share code, notes, and snippets.

@ramiroaznar
Last active July 10, 2018 15:36
Show Gist options
  • Save ramiroaznar/8c055a821e3446d8a4f11656402de705 to your computer and use it in GitHub Desktop.
Save ramiroaznar/8c055a821e3446d8a4f11656402de705 to your computer and use it in GitHub Desktop.
Dynamic legends with CARTO.js v4
<!DOCTYPE html>
<html>
<head>
<title>Dynamic legends with CARTO.js 4</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<link rel="shortcut icon" href="https://cartodb.com/assets/favicon.ico" />
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,600,700|Open+Sans:300,400,600" rel="stylesheet">
<!-- leaflet, jquery & axios-->
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
<link href="https://unpkg.com/[email protected]/dist/leaflet.css" rel="stylesheet">
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!-- selec2 -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
<!-- carto.js -->
<script src="https://cartodb-libs.global.ssl.fastly.net/carto.js/v4.0.0-beta.4/carto.min.js"></script>
<link href="style.css" rel="stylesheet"></style>
</head>
<body>
<!-- map div -->
<div id="map"></div>
<!-- dropdown field selector -->
<div id="wraper-field-selector">
<select class="js-field-selector">
<option disabled="disabled" selected>Select field</option>
</select>
</div>
<!-- legend -->
<div id="legend">
<h1>Dynamic Legends</h1>
<div id="legend-content">
</div>
</div>
<script type="text/cartocss" id="style">
#layer {
marker-width: 7;
marker-fill: #EE4D5A;
marker-fill-opacity: 0.9;
marker-line-color: #FFFFFF;
marker-line-width: 1;
marker-line-opacity: 1;
marker-type: ellipse;
marker-allow-overlap: true;
}
</script>
<script type="text/sql" id="query">
SELECT
*
FROM
populated_places
WHERE
pop_max > 0
ORDER BY
pop_max DESC
</script>
<script typ="text/javascript" src="main.js"></script>
</body>
</html>
function main() {
/* Add CARTO map */
// get styles, query, legend & selector
const style = $("#style").text(), query = $("#query").text(),
// add map variable
map = L.map('map', {
zoomControl: false,
center: [41, -25],
zoom: 3
});
// add Voyager Basemap
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}.png', {
maxZoom: 18,
}).addTo(map);
// Adding Voyager Labels
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager_only_labels/{z}/{x}/{y}.png', {
maxZoom: 18,
zIndex: 100
}).addTo(map);
// add client
const client = new carto.Client({
apiKey: 'default_public',
username: 'ramirocartodb'
});
// define layer configuration
const citiesDataset = new carto.source.SQL(query),
citiesStyle = new carto.style.CartoCSS(style),
citiesLayer = new carto.layer.Layer(citiesDataset, citiesStyle);
// add layer
client.addLayer(citiesLayer);
client.getLeafletLayer().addTo(map);
/* Add field selector */
const selector = $(".js-field-selector"),
fields = [
{name: "pop_max", text: "Population", type: "numeric"},
{name: "featurecla", text: "Type of city", type: "string"}
];
fields.forEach(field => {
console.log(field)
selector.append(`<option value="${field.name}" data-type="${field.type}">${field.text}</option>`);
});
selector.select2();
/* Change style when selecting a field */
selector.change(function(){
// get field name & data type
let inputField = $( ".js-field-selector option:selected" ).val(),
inputType = $( ".js-field-selector option:selected" ).attr('data-type');
console.log(inputField, inputType);
// change style
if (inputType === 'numeric'){
citiesStyle.setContent(`
#layer {
marker-width: ramp(${inputField}, range(1.5,12), quantiles);
marker-fill: ramp(${inputField}, cartocolor(Sunset), quantiles);
marker-fill-opacity: 0.7;
marker-line-width: 0;
marker-type: ellipse;
marker-allow-overlap: true;
}
`);
} else {
citiesStyle.setContent(`
#layer {
marker-width: 7;
marker-fill: ramp(${inputField}, cartocolor(Bold), category);
marker-fill-opacity: 0.7;
marker-line-width: 0;
marker-type: ellipse;
marker-allow-overlap: true;
}
`);
}
});
/* change legend when style changes */
const legend = $("#legend-content");
citiesLayer.on('metadataChanged', function(event){
legend.empty();
console.log(event);
const styles = event.styles;
// append classes to the legend
if (styles.length == 0){
console.log("Load default legend.");
legend.append(`<li><div class="circle" style="background: #EE4D5A"></div> Populated Place</li>`)
} else if (styles.length == 1) {
console.log("Load category legend.")
const categories = styles[0].getCategories();
console.log(categories);
for (category of categories){
legend.append(`<li><div class="circle" style="background:${category.value}"></div> ${category.name}</li>`)
}
} else {
console.log("Load gradient legend.");
const styles1 = styles[0].getBuckets(), styles2 = styles[1].getBuckets();
$(styles1).each(function(i, e) {
if (typeof(styles1[i].value) === "string"){
legend.prepend(`<li><div class="circle" style="background:${styles1[i].value}; width:${styles2[i].value}px; height:${styles2[i].value}px;"></div> ${styles1[i].min} - ${styles1[i].max}</li>`);
} else {
legend.prepend(`<li><div class="circle" style="background:${styles2[i].value}; width:${styles1[i].value}px; height:${styles1[i].value}px;"></div> ${styles1[i].min} - ${styles1[i].max}</li>`)
}
});
}
});
}
window.onload = main;
* {
box-sizing: border-box;
}
body, *{ margin: 0; padding: 0; }
#map {
position: absolute;
height: 100%;
width: 100%;
z-index: 0;
}
#wraper-field-selector {
position: absolute;
top: 20px;
right: 20px;
z-index: 2;
}
.js-field-selector {
width: 160px;
}
#legend {
position: absolute;
bottom: 12px;
left: 12px;
height: auto;
width: auto;
padding: 20px 24px;
background: white;
box-shadow: 0 0 16px rgba(0, 0, 0, 0.12);
border-radius: 4px;
opacity: 0.7;
z-index: 1;
display: block;
list-style-type: none;
}
#legend h1 {
font: 800 12px/16px 'Montserrat';
text-transform: uppercase;
color: #2D3C43;
margin-bottom: 12px;
}
#legend-content{
font: 800 12px/16px 'Open Sans';
margin-bottom: 6px;
}
#legend-content > li {
margin-bottom: 6px;
display: flex;
align-items: center;
text-transform: uppercase;
}
#legend-content > li:last-child {
margin-bottom: 0;
}
.circle {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 8px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment