Filters checkboxes using features' properties
This commit is contained in:
parent
9b0986c16b
commit
d69f965f79
6 changed files with 153 additions and 8 deletions
|
@ -508,6 +508,7 @@ i.info {
|
|||
.umap-layer-properties-container,
|
||||
.umap-footer-container,
|
||||
.umap-browse-data,
|
||||
.umap-filter-data,
|
||||
.umap-browse-datalayers {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
|
|
@ -695,7 +695,7 @@ L.U.Map.include({
|
|||
var build = function () {
|
||||
ul.innerHTML = '';
|
||||
datalayer.eachFeature(function (feature) {
|
||||
if (filterValue && !feature.matchFilter(filterValue, filterKeys)) return;
|
||||
if ((filterValue && !feature.matchFilter(filterValue, filterKeys)) || feature.properties.isVisible === false) return;
|
||||
ul.appendChild(addFeature(feature));
|
||||
});
|
||||
};
|
||||
|
@ -732,6 +732,114 @@ L.U.Map.include({
|
|||
label.textContent = label.title = L._('About');
|
||||
L.DomEvent.on(link, 'click', this.displayCaption, this);
|
||||
this.ui.openPanel({data: {html: browserContainer}, actions: [link]});
|
||||
},
|
||||
|
||||
_openFilter: function () {
|
||||
var filterContainer = L.DomUtil.create('div', 'umap-filter-data'),
|
||||
title = L.DomUtil.add('h3', 'umap-filter-title', filterContainer, this.options.name),
|
||||
propertiesContainer = L.DomUtil.create('div', 'umap-filter-properties', filterContainer),
|
||||
advancedFilterKeys = this.getAdvancedFilterKeys();
|
||||
|
||||
var advancedFiltersFull = {};
|
||||
var filtersAlreadyLoaded = true;
|
||||
if (!this.getMap().options.advancedFilters) {
|
||||
this.getMap().options.advancedFilters = {};
|
||||
filtersAlreadyLoaded = false;
|
||||
}
|
||||
advancedFilterKeys.forEach(property => {
|
||||
advancedFiltersFull[property] = [];
|
||||
if (!filtersAlreadyLoaded) {
|
||||
this.getMap().options.advancedFilters[property] = [];
|
||||
}
|
||||
});
|
||||
this.eachDataLayer(function (datalayer) {
|
||||
datalayer.eachFeature(function (feature) {
|
||||
advancedFilterKeys.forEach(property => {
|
||||
if (feature.properties[property]) {
|
||||
if (!advancedFiltersFull[property].includes(feature.properties[property])) {
|
||||
advancedFiltersFull[property].push(feature.properties[property]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var addPropertyValue = function (property, value) {
|
||||
var property_li = L.DomUtil.create('li', ''),
|
||||
filter_check = L.DomUtil.create('input', '', property_li),
|
||||
filter_label = L.DomUtil.create('label', '', property_li);
|
||||
filter_check.type = 'checkbox';
|
||||
filter_check.id = `checkbox_${property}_${value}`;
|
||||
filter_check.checked = this.getMap().options.advancedFilters[property] && this.getMap().options.advancedFilters[property].includes(value);
|
||||
filter_check.setAttribute('data-property', property);
|
||||
filter_check.setAttribute('data-value', value);
|
||||
filter_label.htmlFor = `checkbox_${property}_${value}`;
|
||||
filter_label.innerHTML = value;
|
||||
L.DomEvent.on(filter_check, 'change', function (e) {
|
||||
var property = e.srcElement.dataset.property;
|
||||
var value = e.srcElement.dataset.value;
|
||||
if (e.srcElement.checked) {
|
||||
this.getMap().options.advancedFilters[property].push(value);
|
||||
} else {
|
||||
this.getMap().options.advancedFilters[property].splice(this.getMap().options.advancedFilters[property].indexOf(value), 1);
|
||||
}
|
||||
L.bind(filterFeatures, this)();
|
||||
}, this);
|
||||
return property_li
|
||||
};
|
||||
|
||||
var addProperty = function (property) {
|
||||
var container = L.DomUtil.create('div', 'property-container', propertiesContainer),
|
||||
headline = L.DomUtil.add('h5', '', container, property);
|
||||
var ul = L.DomUtil.create('ul', '', container);
|
||||
var orderedValues = advancedFiltersFull[property];
|
||||
orderedValues.sort();
|
||||
orderedValues.forEach(value => {
|
||||
ul.appendChild(L.bind(addPropertyValue, this)(property, value));
|
||||
});
|
||||
};
|
||||
|
||||
var filterFeatures = function () {
|
||||
var noResults = true;
|
||||
this.eachDataLayer(function (datalayer) {
|
||||
datalayer.eachFeature(function (feature) {
|
||||
feature.properties.isVisible = true;
|
||||
for (const [property, values] of Object.entries(this.map.options.advancedFilters)) {
|
||||
if (values.length > 0) {
|
||||
if (!feature.properties[property] || !values.includes(feature.properties[property])) {
|
||||
feature.properties.isVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (feature.properties.isVisible) {
|
||||
noResults = false;
|
||||
if (!this.isLoaded()) this.fetchData();
|
||||
this.map.addLayer(feature);
|
||||
this.fire('show');
|
||||
} else {
|
||||
this.map.removeLayer(feature);
|
||||
this.fire('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
if (noResults) {
|
||||
this.help.show('advancedFiltersNoResults');
|
||||
} else {
|
||||
this.help.hide();
|
||||
}
|
||||
};
|
||||
|
||||
propertiesContainer.innerHTML = '';
|
||||
advancedFilterKeys.forEach(property => {
|
||||
L.bind(addProperty, this)(property);
|
||||
});
|
||||
|
||||
var link = L.DomUtil.create('li', '');
|
||||
L.DomUtil.create('i', 'umap-icon-16 umap-caption', link);
|
||||
var label = L.DomUtil.create('span', '', link);
|
||||
label.textContent = label.title = L._('About');
|
||||
L.DomEvent.on(link, 'click', this.displayCaption, this);
|
||||
this.ui.openPanel({ data: { html: filterContainer }, actions: [link] });
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -455,6 +455,8 @@ L.U.Help = L.Class.extend({
|
|||
sortKey: L._('Property to use for sorting features'),
|
||||
slugKey: L._('The name of the property to use as feature unique identifier.'),
|
||||
filterKey: L._('Comma separated list of properties to use when filtering features'),
|
||||
advancedFilterKey: L._('Comma separated list of properties to use for checkbox filtering'),
|
||||
advancedFiltersNoResults: L._('No results for these filters'),
|
||||
interactive: L._('If false, the polygon will act as a part of the underlying map.'),
|
||||
outlink: L._('Define link to open in a new window on polygon click.'),
|
||||
dynamicRemoteData: L._('Fetch data each time map view changes.'),
|
||||
|
|
|
@ -287,7 +287,8 @@ L.FormBuilder.onLoadPanel = L.FormBuilder.Select.extend({
|
|||
selectOptions: [
|
||||
['none', L._('None')],
|
||||
['caption', L._('Caption')],
|
||||
['databrowser', L._('Data browser')]
|
||||
['databrowser', L._('Data browser')],
|
||||
['datafilters', L._('Data filters')]
|
||||
]
|
||||
|
||||
});
|
||||
|
|
|
@ -210,6 +210,7 @@ L.U.Map.include({
|
|||
this.onceDatalayersLoaded(function () {
|
||||
if (this.options.onLoadPanel === 'databrowser') this.openBrowser();
|
||||
else if (this.options.onLoadPanel === 'caption') this.displayCaption();
|
||||
else if (this.options.onLoadPanel === 'datafilters') this.openFilter();
|
||||
});
|
||||
this.onceDataLoaded(function () {
|
||||
const slug = L.Util.queryString('feature');
|
||||
|
@ -893,6 +894,12 @@ L.U.Map.include({
|
|||
});
|
||||
},
|
||||
|
||||
openFilter: function () {
|
||||
this.onceDatalayersLoaded(function () {
|
||||
this._openFilter();
|
||||
});
|
||||
},
|
||||
|
||||
displayCaption: function () {
|
||||
var container = L.DomUtil.create('div', 'umap-caption'),
|
||||
title = L.DomUtil.create('h3', '', container);
|
||||
|
@ -948,10 +955,15 @@ L.U.Map.include({
|
|||
umapCredit.innerHTML = L._('Powered by <a href="{leaflet}">Leaflet</a> and <a href="{django}">Django</a>, glued by <a href="{umap}">uMap project</a>.', urls);
|
||||
var browser = L.DomUtil.create('li', '');
|
||||
L.DomUtil.create('i', 'umap-icon-16 umap-list', browser);
|
||||
var label = L.DomUtil.create('span', '', browser);
|
||||
label.textContent = label.title = L._('Browse data');
|
||||
var labelBrowser = L.DomUtil.create('span', '', browser);
|
||||
labelBrowser.textContent = labelBrowser.title = L._('Browse data');
|
||||
L.DomEvent.on(browser, 'click', this.openBrowser, this);
|
||||
this.ui.openPanel({data: {html: container}, actions: [browser]});
|
||||
var filter = L.DomUtil.create('li', '');
|
||||
L.DomUtil.create('i', 'umap-icon-16 umap-add', filter);
|
||||
var labelFilter = L.DomUtil.create('span', '', filter);
|
||||
labelFilter.textContent = labelFilter.title = L._('Filter data');
|
||||
L.DomEvent.on(filter, 'click', this.openFilter, this);
|
||||
this.ui.openPanel({data: {html: container}, actions: [browser, filter]});
|
||||
},
|
||||
|
||||
eachDataLayer: function (method, context) {
|
||||
|
@ -1057,6 +1069,7 @@ L.U.Map.include({
|
|||
'sortKey',
|
||||
'labelKey',
|
||||
'filterKey',
|
||||
'advancedFilterKey',
|
||||
'slugKey',
|
||||
'showLabel',
|
||||
'labelDirection',
|
||||
|
@ -1253,6 +1266,7 @@ L.U.Map.include({
|
|||
'options.labelKey',
|
||||
['options.sortKey', {handler: 'BlurInput', helpEntries: 'sortKey', placeholder: L._('Default: name'), label: L._('Sort key'), inheritable: true}],
|
||||
['options.filterKey', {handler: 'Input', helpEntries: 'filterKey', placeholder: L._('Default: name'), label: L._('Filter keys'), inheritable: true}],
|
||||
['options.advancedFilterKey', {handler: 'Input', helpEntries: 'advancedFilterKey', placeholder: L._('Example: key1,key2,key3'), label: L._('Advanced filter keys'), inheritable: true}],
|
||||
['options.slugKey', {handler: 'BlurInput', helpEntries: 'slugKey', placeholder: L._('Default: name'), label: L._('Feature identifier key')}]
|
||||
];
|
||||
|
||||
|
@ -1435,6 +1449,10 @@ L.U.Map.include({
|
|||
browser.href = '#';
|
||||
L.DomEvent.on(browser, 'click', L.DomEvent.stop)
|
||||
.on(browser, 'click', this.openBrowser, this);
|
||||
var filter = L.DomUtil.add('a', 'umap-open-filter-link', container, ' | ' + L._('Filter data'));
|
||||
filter.href = '#';
|
||||
L.DomEvent.on(filter, 'click', L.DomEvent.stop)
|
||||
.on(filter, 'click', this.openFilter, this);
|
||||
var setName = function () {
|
||||
name.textContent = this.getDisplayName();
|
||||
};
|
||||
|
@ -1620,6 +1638,10 @@ L.U.Map.include({
|
|||
text: L._('Browse data'),
|
||||
callback: this.openBrowser
|
||||
},
|
||||
{
|
||||
text: L._('Filter data'),
|
||||
callback: this.openFilter
|
||||
},
|
||||
{
|
||||
text: L._('About'),
|
||||
callback: this.displayCaption
|
||||
|
@ -1698,6 +1720,10 @@ L.U.Map.include({
|
|||
|
||||
getFilterKeys: function () {
|
||||
return (this.options.filterKey || this.options.sortKey || 'name').split(',');
|
||||
},
|
||||
|
||||
getAdvancedFilterKeys: function () {
|
||||
return (this.options.advancedFilterKey || '').split(",");
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -729,20 +729,24 @@ a.add-datalayer:hover,
|
|||
margin-bottom: 14px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.umap-browse-features h5, .umap-filter-data h5 {
|
||||
margin-bottom: 0;
|
||||
overflow: hidden;
|
||||
padding-left: 5px;
|
||||
}
|
||||
.umap-browse-features h5 {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
background-color: #eeeee0;
|
||||
margin-bottom: 0;
|
||||
color: #666;
|
||||
overflow: hidden;
|
||||
padding-left: 5px;
|
||||
}
|
||||
.umap-browse-features h5 span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.umap-browse-features li {
|
||||
padding: 2px 0;
|
||||
}
|
||||
.umap-browse-features li, .umap-filter-data li {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
@ -775,6 +779,9 @@ a.add-datalayer:hover,
|
|||
.umap-browse-features .polygon .feature-color {
|
||||
background-position: -32px -16px;
|
||||
}
|
||||
.umap-filter-data .property-container:not(:first-child) {
|
||||
margin-top: 14px;
|
||||
}
|
||||
.show-on-edit {
|
||||
display: none!important;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue