Skip to content

Commit

Permalink
Merge pull request #5152 from neo-garaix/update-sort-and-limit
Browse files Browse the repository at this point in the history
Features Table component : request a maximum number of features & sort data server-side
  • Loading branch information
mdouchin authored Jan 3, 2025
2 parents 1338a5a + 558105f commit e786efa
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 62 deletions.
74 changes: 15 additions & 59 deletions assets/src/components/FeaturesTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import { mainLizmap, mainEventDispatcher } from '../modules/Globals.js';
* withgeometry="1" expressionfilter="quartmno = 'HO'"
* uniquefield="id" layerid="subdistrict_24ceec66_e7fe_46a2_b57a_af5c50389649"
* layertitle="child sub-districts" id="0782b34c-840c-4b0f-821c-1b66c964e371"
* (optionnal) data-show-highlighted-feature-geometry="true"
* (optionnal) data-center-to-highlighted-feature-geometry="true"
* (optional) data-show-highlighted-feature-geometry="true"
* (optional) data-center-to-highlighted-feature-geometry="true"
* (optional) data-max-features
* >
* <lizmap-field data-alias="District's name" data-description="Label of district's name">
* "libsquart"
Expand All @@ -36,7 +37,11 @@ export default class FeaturesTable extends HTMLElement {
super();

// Random element id
this.id = window.crypto.randomUUID();
if (window.isSecureContext) {
this.id = window.crypto.randomUUID();
} else {
this.id = btoa(String.fromCharCode(...new Uint8Array( Array(30).fill().map(() => Math.round(Math.random() * 30)) )));
}

// Layer name
this.layerTitle = this.getAttribute('layerTitle') || 'Features table: error';
Expand All @@ -62,8 +67,10 @@ export default class FeaturesTable extends HTMLElement {
// Get the geometry or NetworkError
this.withGeometry = this.hasAttribute('withGeometry');

// Sorting attribute and direction
// Sorting attribute
this.sortingField = this.getAttribute('sortingField');

// Sorting order
const sortingOrder = this.getAttribute('sortingOrder');
this.sortingOrder = (sortingOrder !== null && ['asc', 'desc'].includes(sortingOrder.toLowerCase())) ? sortingOrder : 'asc';

Expand All @@ -85,6 +92,9 @@ export default class FeaturesTable extends HTMLElement {

// Clicked item line number
this.activeItemLineNumber = null;

// Maximum number of features
this.maxFeatures = (this.dataset.maxFeatures > 0) ? this.dataset.maxFeatures : 1000;
}

/**
Expand Down Expand Up @@ -113,22 +123,16 @@ export default class FeaturesTable extends HTMLElement {
});
}
// Get the features corresponding to the given parameters from attributes
mainLizmap.featuresTable.getFeatures(this.layerId, this.expressionFilter, this.withGeometry, fields, uniqueAdditionalFields)
mainLizmap.featuresTable.getFeatures(this.layerId, this.expressionFilter, this.withGeometry, fields, uniqueAdditionalFields, this.maxFeatures, this.sortingField, this.sortingOrder)
.then(displayExpressions => {
// Check for errors
if (!('status' in displayExpressions)) return;

if (displayExpressions.status != 'success') {
console.error(displayExpressions.error);
} else {

// Set component data property
this.features = displayExpressions.data;

// Sort data if needed
if (this.features.length > 1) {
this.sortFeatures();
}
}

// Render
Expand All @@ -148,54 +152,6 @@ export default class FeaturesTable extends HTMLElement {
})
}

/**
* Sort the features array property
* depending on the options
*/
sortFeatures() {
// Order data by given fields or fallback to descending order by id
let sortingField = 'display_expression';
let sortingOrder = 'asc';
let sortingType = 'string';

// Get first line to check if needed fields are present
const first = this.features[0];
if (first && this.sortingField && this.sortingField in first.properties) {
sortingField = this.sortingField;
sortingOrder = this.sortingOrder;
if (!isNaN(first.properties[sortingField])) {
sortingType = 'number';
}
}

// Reorder the array of data
if (sortingType == 'number') {
if (sortingOrder == 'asc') {
this.features.sort((a, b) => (a.properties[sortingField] || 0) - (b.properties[sortingField] || 0));
} else {
this.features.sort((a, b) => (b.properties[sortingField] || 0) - (a.properties[sortingField] || 0));
}
} else {
if (sortingOrder == 'asc') {
this.features.sort(
(a, b) => (a.properties[sortingField] || '').localeCompare(
b.properties[sortingField] || '',
navigator.language,
{sensitivity: 'accent'}
)
);
} else {
this.features.sort(
(a, b) => (b.properties[sortingField] || '').localeCompare(
a.properties[sortingField] || '',
navigator.language,
{sensitivity: 'accent'}
)
);
}
}
}

/**
* Render component from the template using Lit
*/
Expand Down
8 changes: 7 additions & 1 deletion assets/src/modules/FeaturesTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,16 @@ export default class FeaturesTable {
* @param {boolean} withGeometry If we need to get the geometry
* @param {string|null} fields List of field names separated by comma
* @param {object|array} additionalFields JSON object with the field names and expressions
* @param {number} limit Number of features to return
* @param {string|null} sortingField Field name to sort the features
* @param {string|null} sortingOrder Sorting order
*
* @returns — A Promise that resolves with the result of parsing the response body text as JSON.
* @throws {ResponseError} In case of invalid content type (not application/json or application/vnd.geo+json) or Invalid JSON
* @throws {HttpError} In case of not successful response (status not in the range 200 – 299)
* @throws {NetworkError} In case of catch exceptions
*/
getFeatures(layerId, filter = null, withGeometry = false, fields = 'null', additionalFields = []) {
getFeatures(layerId, filter = null, withGeometry = false, fields = 'null', additionalFields = [], limit = 1000, sortingField = null, sortingOrder = null) {

// Build URL
const url = `${lizUrls.service.replace('service?','features/displayExpression?')}&`;
Expand All @@ -57,6 +60,9 @@ export default class FeaturesTable {
formData.append('with_geometry', withGeometry.toString());
formData.append('fields', fields);
formData.append('additionalFields',JSON.stringify(additionalFields));
formData.append('limit', limit);
formData.append('sorting_field', sortingField);
formData.append('sorting_order', sortingOrder);
// Return promise
return Utils.fetchJSON(url, {
method: "POST",
Expand Down
10 changes: 9 additions & 1 deletion lizmap/modules/lizmap/classes/qgisExpressionUtils.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,13 @@ public static function updateExpressionByUser($layer, $expression, $edition = fa
* @param string $filter A filter to restrict virtual fields creation
* @param string $withGeometry 'true' to get geometries of features
* @param string $fields A list of field names separated by comma. E.g. 'name,code'
* @param int $limit The maximum number of features to return
* @param null|string $sortingField The field to sort by E.g. 'desc'
* @param null|string $sortingOrder The order to sort by E.g. 'name'
*
* @return null|array the features with virtual fields
*/
public static function virtualFields($layer, $virtualFields, $filter = null, $withGeometry = 'true', $fields = '')
public static function virtualFields($layer, $virtualFields, $filter = null, $withGeometry = 'true', $fields = '', $limit = 1000, $sortingField = null, $sortingOrder = 'asc')
{
// Evaluate the expression by qgis
$project = $layer->getProject();
Expand All @@ -171,10 +174,15 @@ public static function virtualFields($layer, $virtualFields, $filter = null, $wi
'virtuals' => json_encode($virtualFields),
'with_geometry' => $withGeometry,
'fields' => $fields,
'limit' => $limit,
);
if ($filter) {
$params['filter'] = $filter;
}
if ($sortingField) {
$params['sorting_field'] = $sortingField;
$params['sorting_order'] = in_array(strtolower($sortingOrder), array('asc', 'desc')) ? $sortingOrder : 'asc';
}

// Request virtual fields
$json = self::request($params, $project);
Expand Down
12 changes: 11 additions & 1 deletion lizmap/modules/lizmap/controllers/features.classic.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,23 @@ public function displayExpression()

$expressions = array_merge($expressions, $additionalFields);

$sortingField = trim($this->param('sorting_field', null));

$sortingOrder = trim($this->param('sorting_order', 'asc'));

// Limit
$limit = trim($this->param('limit', 1000));

// Get the evaluated features for the given layer and parameters
$getDisplayExpressions = qgisExpressionUtils::virtualFields(
$qgisLayer,
$expressions,
$exp_filter,
$withGeometry,
$fields
$fields,
$limit,
$sortingField,
$sortingOrder,
);

// If the returned content is null, an error occurred
Expand Down

0 comments on commit e786efa

Please sign in to comment.