Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Source Filters #2165

Merged
merged 35 commits into from
Jul 10, 2018
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1cf7f1f
Category filter
jesusbotella Jun 28, 2018
f537c0d
Range filter
jesusbotella Jun 28, 2018
fbc5b65
SQLFilterBase for Category and Range filters
jesusbotella Jun 28, 2018
9f2e780
Export Category and Range filter to filters
jesusbotella Jun 28, 2018
7df4bb4
Add tests and refactor
jesusbotella Jun 28, 2018
867f67f
Add OR, and AND filters. Integrate filters into SQL source.
jesusbotella Jun 29, 2018
56d462d
Wrap Date string in single comma. Integrate filters into dataset sour…
jesusbotella Jun 29, 2018
a83a58f
AND and OR filter groups. Some fixes, and add tests.
jesusbotella Jul 2, 2018
46781e9
Update documentation
jesusbotella Jul 2, 2018
112be6a
Test class properties
jesusbotella Jul 2, 2018
edac143
Update docs
jesusbotella Jul 2, 2018
6a6192f
Update docs
jesusbotella Jul 2, 2018
4416e30
Add API to FiltersCollection methods
jesusbotella Jul 2, 2018
f1168c3
Set correct parameter specification
jesusbotella Jul 3, 2018
83f0474
Do not include filters when SQL string is empty. Hack IN operator to …
jesusbotella Jul 3, 2018
25e7fc5
Category filter example
jesusbotella Jul 3, 2018
2ccf062
Add new examples
jesusbotella Jul 3, 2018
839ae00
Change documentation to add category and range methods
jesusbotella Jul 3, 2018
0808c6d
Add AND example. Avoid errors on bad function parameters. Move Catego…
jesusbotella Jul 4, 2018
15d0d56
Complex filters example. Bugfixes. Move code to base class.
jesusbotella Jul 4, 2018
5cd18b4
Update examples
jesusbotella Jul 4, 2018
7b1093b
Get nested filter property with proper helper.
jesusbotella Jul 4, 2018
cbb2604
Fix tests
jesusbotella Jul 4, 2018
516db89
Update complex filter description
jesusbotella Jul 4, 2018
9d72b92
Avoid sending two queries at first. Update complex filter example.
jesusbotella Jul 4, 2018
68ea4b3
Remove commented styles in example
jesusbotella Jul 4, 2018
177dc6a
Bugfix
jesusbotella Jul 4, 2018
22497be
Documentation changes
jesusbotella Jul 5, 2018
8116183
Move super call after checking that table is OK.
jesusbotella Jul 5, 2018
1bed379
Throw error if an SQL filter is passed to dataviews
jesusbotella Jul 5, 2018
08552b3
Documentation changes
jesusbotella Jul 9, 2018
4873cb5
Remove TODO
jesusbotella Jul 9, 2018
c63c663
Add getFilters()
jesusbotella Jul 10, 2018
5d276ae
Remove useless documentation
jesusbotella Jul 10, 2018
33cd867
Change method definition for docs
jesusbotella Jul 10, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions examples/examples.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,31 @@
}
]
},
{
"title": "Filters",
"samples": [
{
"title": "Category Filter",
"desc": "Create a Category filter with checkbox selectors",
"file": "public/filters/category-filter.html"
},
{
"title": "Range Filter",
"desc": "Create a Range filter with a range slider",
"file": "public/filters/range-filter.html"
},
{
"title": "AND Filter",
"desc": "Apply an AND filter to combine multiple filters",
"file": "public/filters/and-filter.html"
},
{
"title": "Complex Filter",
"desc": "Apply several combined filters to a dataset",
"file": "public/filters/complex-filter.html"
}
]
},
{
"title": "Misc",
"samples": [
Expand Down
153 changes: 153 additions & 0 deletions examples/public/filters/and-filter.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<!DOCTYPE html>
<html>
<head>
<title>AND Filter | CARTO</title>
<meta name="viewport" content="initial-scale=1.0">
<meta charset="utf-8">
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,600,700|Open+Sans:300,400,600" rel="stylesheet">
<!-- Include Leaflet -->
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
<link href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css" rel="stylesheet">
<!-- Include CARTO.js -->
<script src="../../../dist/public/carto.js"></script>
<link href="../style.css" rel="stylesheet">
</head>
<body>
<div id="map"></div>
<aside class="toolbox">
<div class="box">
<header>
<h1>AND Filter</h1>
<button class="github-logo js-source-link"></button>
</header>

<section>
<p class="description open-sans">Apply an AND filter to exclude listings that are not within the selected room types and have a higher price than the one we set.</p>

<div class="separator"></div>

<section class="usage">
<header>USAGE</header>
<p class="open-sans">Change the selected price range and the selected room types to filter the listings.</p>
</section>

<div id="controls">
<div id="info">
<h3>Room Types</h3>
</div>

<ul>
<li>
<input type="checkbox" name="roomTypes[]" id="entire" value="Entire home/apt" checked>
<label for="entire">Entire home/apt</label>
</li>
<li>
<input type="checkbox" name="roomTypes[]" id="private" value="Private room" checked>
<label for="private">Private Room</label>
</li>
<li>
<input type="checkbox" name="roomTypes[]" id="shared" value="Shared room" checked>
<label for="shared">Shared Room</label>
</li>
</ul>

<div id="info">
<h3>Price</h3>
</div>

<div class="widget">
<p class="open-sans">Showing listings below and equal to <span class="js-price-placeholder">40€</span></p>
</div>
<input type="range" name="price" class="slider" min="1" max="60" value="40" step="1" min-with-suffix="1€" max-with-suffix="60€">
</div>
</section>

<footer class="js-footer"></footer>
</div>
</aside>

<script>
const map = L.map('map').setView([40.42252398976147, -3.659729361534119], 12);
map.scrollWheelZoom.disable();

L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}.png', {
maxZoom: 18
}).addTo(map);

const client = new carto.Client({
apiKey: 'default_public',
username: 'cartojs-test'
});

const inputRange = document.querySelector('#controls input[type=range]');
inputRange.style.setProperty('--value', (inputRange.value - inputRange.min) / 0.59);

const pricePlaceholder = document.querySelector('#controls .js-price-placeholder');
const roomTypesCheckboxes = document.querySelectorAll('#controls input[type=checkbox]');

function getSelectedRoomTypes () {
const values = [];

roomTypesCheckboxes.forEach(input => input.checked ? values.push(input.value): null);
return values;
}

function applyPriceFilters (e) {
const maximumPrice = parseInt(e.target.value);

pricePlaceholder.innerText = maximumPrice + "€";
priceFilter.setFilters({ lte: maximumPrice });
// or
// priceFilter.set('lte', maximumPrice);
}

function applyRoomFilters () {
roomTypeFilter.setFilters({ in: getSelectedRoomTypes() });
// or
// roomTypeFilter.set('in', getSelectedRoomTypes());
}

function registerListeners () {
inputRange.addEventListener('input', e => {
inputRange.style.setProperty('--value', (inputRange.value - inputRange.min) / 0.59);
});

inputRange.addEventListener('change', e => {
applyPriceFilters(e)
});

roomTypesCheckboxes.forEach(
input => input.addEventListener('click', () => applyRoomFilters())
);
}

const roomTypeFilter = new carto.filter.Category('room_type', { in: getSelectedRoomTypes() });
const priceFilter = new carto.filter.Range('price', { lte: 40 });

const source = new carto.source.SQL('SELECT * FROM airbnb_listings');

// You can apply both filters in two different ways
// 1. Adding both filters to the source
source.addFilters([ priceFilter, roomTypeFilter ]);

// 2. Creating an AND filter to join the two filters and adding it to the source
// const roomAndPriceFilter = new carto.filter.AND([ priceFilter, roomTypeFilter ]);
// source.addFilter(roomAndPriceFilter)

const style = new carto.style.CartoCSS(`
#layer {
marker-width: 7;
marker-fill: #EE4D5A;
marker-line-color: #FFFFFF;
marker-fill: ramp([price], (#ffc6c4, #ee919b, #cc607d, #9e3963, #672044), quantiles);
}
`);
const layer = new carto.layer.Layer(source, style);

client.addLayer(layer);
client.getLeafletLayer().addTo(map);

registerListeners();
</script>
</body>
</html>
113 changes: 113 additions & 0 deletions examples/public/filters/category-filter.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<!DOCTYPE html>
<html>
<head>
<title>Category Filter | CARTO</title>
<meta name="viewport" content="initial-scale=1.0">
<meta charset="utf-8">
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,600,700|Open+Sans:300,400,600" rel="stylesheet">
<!-- Include Leaflet -->
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
<link href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css" rel="stylesheet">
<!-- Include CARTO.js -->
<script src="../../../dist/public/carto.js"></script>
<link href="../style.css" rel="stylesheet">
</head>
<body>
<div id="map"></div>
<aside class="toolbox">
<div class="box">
<header>
<h1>Category Filter</h1>
<button class="github-logo js-source-link"></button>
</header>

<section>
<p class="description open-sans">Apply a category filter to the listings shown in the visualization.</p>

<div class="separator"></div>

<section class="usage">
<header>USAGE</header>
<p class="open-sans">Change the selected room types to filter the listings.</p>
</section>

<div id="controls">
<div id="info">
<h3>Room Types</h3>
</div>
<ul>
<li>
<input type="checkbox" name="roomTypes[]" id="entire" value="Entire home/apt" checked>
<label for="entire">Entire home/apt</label>
</li>
<li>
<input type="checkbox" name="roomTypes[]" id="private" value="Private room" checked>
<label for="private">Private Room</label>
</li>
<li>
<input type="checkbox" name="roomTypes[]" id="shared" value="Shared room" checked>
<label for="shared">Shared Room</label>
</li>
</ul>
</div>
</section>

<footer class="js-footer"></footer>
</div>
</aside>

<script>
function getSelectedRoomTypes () {
const inputControls = document.querySelectorAll('#controls input');
const values = [];

inputControls.forEach(input => input.checked ? values.push(input.value): null);
return values;
}

function applyFilters () {
roomTypeFilter.setFilters({ in: getSelectedRoomTypes() });
// or
// roomTypeFilter.set('in', getSelectedRoomTypes());
}

function registerListeners () {
document.querySelectorAll('#controls input').forEach(
input => input.addEventListener('click', () => applyFilters())
);
}

const map = L.map('map').setView([40.42252398976147, -3.659729361534119], 12);
map.scrollWheelZoom.disable();

L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}.png', {
maxZoom: 18
}).addTo(map);

const client = new carto.Client({
apiKey: 'default_public',
username: 'cartojs-test'
});

const roomTypeFilter = new carto.filter.Category('room_type', { in: getSelectedRoomTypes() });

const source = new carto.source.SQL('SELECT * FROM airbnb_listings');
source.addFilter(roomTypeFilter);

const style = new carto.style.CartoCSS(`
#layer {
marker-width: 7;
marker-fill: #EE4D5A;
marker-line-color: #FFFFFF;
marker-fill: ramp([room_type], (#88CCEE, #CC6677, #DDCC77), ("Entire home/apt", "Private room", "Shared room"), "=");
}
`);
const layer = new carto.layer.Layer(source, style);

client.addLayer(layer);
client.getLeafletLayer().addTo(map);

registerListeners();
</script>
</body>
</html>
90 changes: 90 additions & 0 deletions examples/public/filters/complex-filter.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<!DOCTYPE html>
<html>
<head>
<title>Complex Filter Example | CARTO</title>
<meta name="viewport" content="initial-scale=1.0">
<meta charset="utf-8">
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,600,700|Open+Sans:300,400,600" rel="stylesheet">
<!-- Include Leaflet -->
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
<link href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css" rel="stylesheet">
<!-- Include CARTO.js -->
<script src="../../../dist/public/carto.js"></script>
<link href="../style.css" rel="stylesheet">
</head>
<body>
<div id="map"></div>
<aside class="toolbox">
<div class="box">
<header>
<h1>Complex Example</h1>
<button class="github-logo js-source-link"></button>
</header>

<section>
<p class="description open-sans">This map has applied a filters combination that show listings meeting these conditions:</p>

<ul>
<li class="open-sans">Between 30€ and 40€.</li>
<li class="open-sans">Centro neighbourhood.</li>
<li class="open-sans">Entire home/apartment listings OR listings that have been reviewed after May 2015.</li>
</ul>

<p class="description open-sans">If you want to know more, please go to <a href="https://cartojs-test.carto.com/tables/airbnb_listings/public">Airbnb Listings dataset</a>.</p>

<div class="separator"></div>

<section class="usage">
<header>USAGE</header>
<p class="open-sans">Go to the source code below, and see how filters are applied.</p>
</section>
</section>

<footer class="js-footer"></footer>
</div>
</aside>

<script>
const map = L.map('map').setView([40.4175117794419, -3.6971674673259263], 15);
map.scrollWheelZoom.disable();

L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}.png', {
maxZoom: 18
}).addTo(map);

const client = new carto.Client({
apiKey: 'default_public',
username: 'cartojs-test'
});

const source = new carto.source.Dataset('airbnb_listings');
// or
// const source = new carto.source.SQL('SELECT * FROM airbnb_listings');

const entireHomeFilter = new carto.filter.Category('room_type', { eq: 'Entire home/apt' });
const neighbourhoodFilter = new carto.filter.Category('neighbourhood_group', { in: ['Centro'] });
const reviewsInLastYearFilter = new carto.filter.Range('last_review', { gte: new Date('2015-05-01T00:00:00.000Z') });
const priceFilter = new carto.filter.Range('price', { between: { min: 30, max: 40 } });

const filtersCombination = new carto.filter.AND([
neighbourhoodFilter,
priceFilter,
new carto.filter.OR([ entireHomeFilter, reviewsInLastYearFilter ])
]);

source.addFilter(filtersCombination);

const style = new carto.style.CartoCSS(`
#layer {
marker-width: 7;
marker-fill: #EE4D5A;
marker-line-color: #FFFFFF;
}
`);
const layer = new carto.layer.Layer(source, style);

client.addLayer(layer);
client.getLeafletLayer().addTo(map);
</script>
</body>
</html>
Loading