diff --git a/umap/static/umap/base.css b/umap/static/umap/base.css
index e4d9eed4..8e4c928f 100644
--- a/umap/static/umap/base.css
+++ b/umap/static/umap/base.css
@@ -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;
}
diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js
index b7b9bff7..2ca538b9 100644
--- a/umap/static/umap/js/umap.controls.js
+++ b/umap/static/umap/js/umap.controls.js
@@ -434,6 +434,43 @@ L.U.MoreControls = L.Control.extend({
});
+L.U.PermanentCreditsControl = L.Control.extend({
+
+ options: {
+ position: 'bottomleft'
+ },
+
+ initialize: function (map, options) {
+ this.map = map;
+ L.Control.prototype.initialize.call(this, options);
+ },
+
+ onAdd: function () {
+ var paragraphContainer = L.DomUtil.create('div', 'umap-permanent-credits-container'),
+ creditsParagraph = L.DomUtil.create('p', '', paragraphContainer);
+
+ this.paragraphContainer = paragraphContainer;
+ this.setCredits();
+ this.setBackground();
+
+ return paragraphContainer;
+ },
+
+ setCredits: function () {
+ this.paragraphContainer.innerHTML = L.Util.toHTML(this.map.options.permanentCredit);
+ },
+
+ setBackground: function () {
+ if (this.map.options.permanentCreditBackground) {
+ this.paragraphContainer.style.backgroundColor = '#FFFFFFB0';
+ } else {
+ this.paragraphContainer.style.backgroundColor = '';
+ }
+ }
+
+});
+
+
L.U.DataLayersControl = L.Control.extend({
options: {
@@ -695,7 +732,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 +769,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.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] });
}
});
@@ -805,12 +950,14 @@ L.U.AttributionControl = L.Control.Attribution.extend({
if (this._map.options.shortCredit) {
L.DomUtil.add('span', '', this._container, ' — ' + L.Util.toHTML(this._map.options.shortCredit));
}
- var link = L.DomUtil.add('a', '', this._container, ' — ' + L._('About'));
- L.DomEvent
- .on(link, 'click', L.DomEvent.stop)
- .on(link, 'click', this._map.displayCaption, this._map)
- .on(link, 'dblclick', L.DomEvent.stop);
- if (window.top === window.self) {
+ if (this._map.options.captionMenus) {
+ var link = L.DomUtil.add('a', '', this._container, ' — ' + L._('About'));
+ L.DomEvent
+ .on(link, 'click', L.DomEvent.stop)
+ .on(link, 'click', this._map.displayCaption, this._map)
+ .on(link, 'dblclick', L.DomEvent.stop);
+ }
+ if (window.top === window.self && this._map.options.captionMenus) {
// We are not in iframe mode
var home = L.DomUtil.add('a', '', this._container, ' — ' + L._('Home'));
home.href = '/';
@@ -1001,7 +1148,8 @@ L.U.IframeExporter = L.Evented.extend({
embedControl: null,
datalayersControl: true,
onLoadPanel: 'none',
- captionBar: false
+ captionBar: false,
+ captionMenus: true
},
dimensions: {
diff --git a/umap/static/umap/js/umap.core.js b/umap/static/umap/js/umap.core.js
index 02afe2cd..30aaa4cb 100644
--- a/umap/static/umap/js/umap.core.js
+++ b/umap/static/umap/js/umap.core.js
@@ -452,9 +452,12 @@ L.U.Help = L.Class.extend({
fillColor: L._('Optional. Same as color if not set.'),
shortCredit: L._('Will be displayed in the bottom right corner of the map'),
longCredit: L._('Will be visible in the caption of the map'),
+ permanentCredit: L._('Will be permanently visible in the bottom left corner of the map'),
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.'),
diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js
index 88c03703..b3a37733 100644
--- a/umap/static/umap/js/umap.forms.js
+++ b/umap/static/umap/js/umap.forms.js
@@ -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')]
]
});
@@ -758,6 +759,7 @@ L.U.FormBuilder = L.FormBuilder.extend({
onLoadPanel: {handler: 'onLoadPanel', label: L._('Do you want to display a panel on load?')},
displayPopupFooter: {handler: 'Switch', label: L._('Do you want to display popup footer?')},
captionBar: {handler: 'Switch', label: L._('Do you want to display a caption bar?')},
+ captionMenus: {handler: 'Switch', label: L._('Do you want to display caption menus?')},
zoomTo: {handler: 'IntInput', placeholder: L._('Inherit'), helpEntries: 'zoomTo', label: L._('Default zoom level'), inheritable: true},
showLabel: {handler: 'LabelChoice', label: L._('Display label'), inheritable: true},
labelDirection: {handler: 'LabelDirection', label: L._('Label direction'), inheritable: true},
diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js
index 92ca44a2..7365afac 100644
--- a/umap/static/umap/js/umap.js
+++ b/umap/static/umap/js/umap.js
@@ -43,10 +43,12 @@ L.Map.mergeOptions({
],
moreControl: true,
captionBar: false,
+ captionMenus: true,
slideshow: {},
clickable: true,
easing: true,
- permissions: {}
+ permissions: {},
+ permanentCreditBackground: true,
});
L.U.Map.include({
@@ -88,6 +90,7 @@ L.U.Map.include({
L.Util.setBooleanFromQueryString(this.options, 'displayDataBrowserOnLoad');
L.Util.setBooleanFromQueryString(this.options, 'displayCaptionOnLoad');
L.Util.setBooleanFromQueryString(this.options, 'captionBar');
+ L.Util.setBooleanFromQueryString(this.options, 'captionMenus');
for (var i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
L.Util.setNullableBooleanFromQueryString(this.options, this.HIDDABLE_CONTROLS[i] + 'Control');
}
@@ -210,6 +213,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');
@@ -273,6 +277,7 @@ L.U.Map.include({
this._controls.measure = (new L.MeasureControl()).initHandler(this);
this._controls.more = new L.U.MoreControls();
this._controls.scale = L.control.scale();
+ this._controls.permanentCredit = new L.U.PermanentCreditsControl(this);
if (this.options.scrollWheelZoom) this.scrollWheelZoom.enable();
else this.scrollWheelZoom.disable();
this.renderControls();
@@ -305,6 +310,7 @@ L.U.Map.include({
if (status === undefined || status === null) L.DomUtil.addClass(control._container, 'display-on-more');
else L.DomUtil.removeClass(control._container, 'display-on-more');
}
+ if (this.options.permanentCredit) this._controls.permanentCredit.addTo(this);
if (this.options.moreControl) this._controls.more.addTo(this);
if (this.options.scaleControl) this._controls.scale.addTo(this);
},
@@ -625,7 +631,8 @@ L.U.Map.include({
'queryString.miniMap',
'queryString.scaleControl',
'queryString.onLoadPanel',
- 'queryString.captionBar'
+ 'queryString.captionBar',
+ 'queryString.captionMenus'
];
for (var i = 0; i < this.HIDDABLE_CONTROLS.length; i++) {
UIFields.push('queryString.' + this.HIDDABLE_CONTROLS[i] + 'Control');
@@ -893,6 +900,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 +961,19 @@ L.U.Map.include({
umapCredit.innerHTML = L._('Powered by Leaflet and Django, glued by uMap project.', 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 actions = [browser];
+ if (this.options.advancedFilterKey) {
+ 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._('Select data');
+ L.DomEvent.on(filter, 'click', this.openFilter, this);
+ actions.push(filter)
+ }
+ this.ui.openPanel({data: {html: container}, actions: actions});
},
eachDataLayer: function (method, context) {
@@ -1053,16 +1075,20 @@ L.U.Map.include({
'popupContentTemplate',
'zoomTo',
'captionBar',
+ 'captionMenus',
'slideshow',
'sortKey',
'labelKey',
'filterKey',
+ 'advancedFilterKey',
'slugKey',
'showLabel',
'labelDirection',
'labelInteractive',
'shortCredit',
'longCredit',
+ 'permanentCredit',
+ 'permanentCreditBackground',
'zoomControl',
'datalayersControl',
'searchControl',
@@ -1215,10 +1241,14 @@ L.U.Map.include({
'options.scaleControl',
'options.onLoadPanel',
'options.displayPopupFooter',
- 'options.captionBar'
+ 'options.captionBar',
+ 'options.captionMenus'
]);
builder = new L.U.FormBuilder(this, UIFields, {
- callback: this.renderControls,
+ callback: function() {
+ this.renderControls();
+ this.initCaptionBar();
+ },
callbackContext: this
});
var controlsOptions = L.DomUtil.createFieldset(container, L._('User interface options'));
@@ -1253,11 +1283,13 @@ 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')}]
];
builder = new L.U.FormBuilder(this, optionsFields, {
callback: function (e) {
+ this.initCaptionBar();
this.eachDataLayer(function (datalayer) {
if (e.helper.field === 'options.sortKey') datalayer.reindex();
datalayer.redraw();
@@ -1365,10 +1397,12 @@ L.U.Map.include({
var creditsFields = [
['options.licence', {handler: 'LicenceChooser', label: L._('licence')}],
['options.shortCredit', {handler: 'Input', label: L._('Short credits'), helpEntries: ['shortCredit', 'textFormatting']}],
- ['options.longCredit', {handler: 'Textarea', label: L._('Long credits'), helpEntries: ['longCredit', 'textFormatting']}]
+ ['options.longCredit', {handler: 'Textarea', label: L._('Long credits'), helpEntries: ['longCredit', 'textFormatting']}],
+ ['options.permanentCredit', {handler: 'Textarea', label: L._('Permanent credits'), helpEntries: ['permanentCredit', 'textFormatting']}],
+ ['options.permanentCreditBackground', {handler: 'Switch', label: L._('Permanent credits background')}]
];
var creditsBuilder = new L.U.FormBuilder(this, creditsFields, {
- callback: function () {this._controls.attribution._update();},
+ callback: this.renderControls,
callbackContext: this
});
credits.appendChild(creditsBuilder.build());
@@ -1428,13 +1462,21 @@ L.U.Map.include({
name = L.DomUtil.create('h3', '', container);
L.DomEvent.disableClickPropagation(container);
this.permissions.addOwnerLink('span', container);
- var about = L.DomUtil.add('a', 'umap-about-link', container, ' — ' + L._('About'));
- about.href = '#';
- L.DomEvent.on(about, 'click', this.displayCaption, this);
- var browser = L.DomUtil.add('a', 'umap-open-browser-link', container, ' | ' + L._('Browse data'));
- browser.href = '#';
- L.DomEvent.on(browser, 'click', L.DomEvent.stop)
- .on(browser, 'click', this.openBrowser, this);
+ if (this.options.captionMenus) {
+ var about = L.DomUtil.add('a', 'umap-about-link', container, ' — ' + L._('About'));
+ about.href = '#';
+ L.DomEvent.on(about, 'click', this.displayCaption, this);
+ var browser = L.DomUtil.add('a', 'umap-open-browser-link', container, ' | ' + L._('Browse data'));
+ browser.href = '#';
+ L.DomEvent.on(browser, 'click', L.DomEvent.stop)
+ .on(browser, 'click', this.openBrowser, this);
+ if (this.options.advancedFilterKey) {
+ var filter = L.DomUtil.add('a', 'umap-open-filter-link', container, ' | ' + L._('Select data'));
+ filter.href = '#';
+ L.DomEvent.on(filter, 'click', L.DomEvent.stop)
+ .on(filter, 'click', this.openFilter, this);
+ }
+ }
var setName = function () {
name.textContent = this.getDisplayName();
};
@@ -1615,11 +1657,17 @@ L.U.Map.include({
});
}
}
- items.push('-',
- {
- text: L._('Browse data'),
- callback: this.openBrowser
- },
+ items.push('-', {
+ text: L._('Browse data'),
+ callback: this.openBrowser
+ });
+ if (this.options.advancedFilterKey) {
+ items.push({
+ text: L._('Select data'),
+ callback: this.openFilter
+ })
+ }
+ items.push(
{
text: L._('About'),
callback: this.displayCaption
@@ -1698,6 +1746,10 @@ L.U.Map.include({
getFilterKeys: function () {
return (this.options.filterKey || this.options.sortKey || 'name').split(',');
+ },
+
+ getAdvancedFilterKeys: function () {
+ return (this.options.advancedFilterKey || '').split(",");
}
});
diff --git a/umap/static/umap/locale/fr.js b/umap/static/umap/locale/fr.js
index 08d54ad8..bce3005f 100644
--- a/umap/static/umap/locale/fr.js
+++ b/umap/static/umap/locale/fr.js
@@ -262,6 +262,7 @@ var locale = {
"See all": "Tout voir",
"See data layers": "Voir les calques",
"See full screen": "Voir en plein écran",
+ "Select data": "Sélectionner les données",
"Set it to false to hide this layer from the slideshow, the data browser, the popup navigation…": "Désactiver pour masquer ce calque du diaporama, du navigateur de données…",
"Shape properties": "Propriétés de la forme",
"Short URL": "URL courte",
diff --git a/umap/static/umap/locale/fr.json b/umap/static/umap/locale/fr.json
index 9259f226..a0fb23b8 100644
--- a/umap/static/umap/locale/fr.json
+++ b/umap/static/umap/locale/fr.json
@@ -262,6 +262,7 @@
"See all": "Tout voir",
"See data layers": "Voir les calques",
"See full screen": "Voir en plein écran",
+ "Select data": "Sélectionner les données",
"Set it to false to hide this layer from the slideshow, the data browser, the popup navigation…": "Désactiver pour masquer ce calque du diaporama, du navigateur de données…",
"Shape properties": "Propriétés de la forme",
"Short URL": "URL courte",
diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css
index 707821c7..91371c83 100644
--- a/umap/static/umap/map.css
+++ b/umap/static/umap/map.css
@@ -117,6 +117,12 @@ a.umap-control-text {
.leaflet-control-edit-enable a:hover {
background-color: #4d5759;
}
+.umap-permanent-credits-container {
+ max-width: 20rem;
+ margin-left: 5px!important;
+ margin-bottom: 5px!important;
+ padding: 0.5rem;
+}
@@ -729,20 +735,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 +785,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;
}
@@ -1359,6 +1372,10 @@ a.add-datalayer:hover,
.leaflet-control-layers-expanded {
margin-left: 10px;
}
+
+ .umap-permanent-credits-container {
+ max-width: 100%;
+ }
}
/* ****** */