Skip to content

Commit

Permalink
Fix 667 - Allow multiple selection in query
Browse files Browse the repository at this point in the history
  • Loading branch information
adube authored and fredj committed Dec 12, 2018
1 parent 57f5e25 commit 889ac7d
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 37 deletions.
15 changes: 15 additions & 0 deletions options/ngeox.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ ngeox.Download;
* The options to use when sending GetFeature/GetFeatureInfo requests using
* the querent or map query service.
* @typedef {{
* action: (string|undefined),
* coordinate: (ol.Coordinate|undefined),
* dataSources: (Array.<ngeox.datasource.DataSource>|undefined),
* extent: (ol.Extent|undefined),
Expand All @@ -233,6 +234,20 @@ ngeox.Download;
ngeox.IssueGetFeaturesOptions;


/**
* The action the MapQuerent should take regarding the queried
* features. Possible values are:
*
* - `replace`: newly queried features are used as result
* - `add`: newly queried features are added to the existing ones
* - `remove`: newly queried features are removed from the existing ones
*
* Defaults to `replace`.
* @type {action|undefined}
*/
ngeox.IssueGetFeaturesOptions.prototype.action;


/**
* The coordinate to issue the requests with, which can end up with either
* WMS or WFS requests.
Expand Down
18 changes: 18 additions & 0 deletions src/misc/FeatureHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -1355,6 +1355,24 @@ exports.prototype.clearNonSpatialProperties = function(feature) {
};


/**
* @param {!Array.<!ol.Feature>} features Features.
* @param {string} fid Feature id
* @return {number} Index of found feature
* @export
*/
exports.prototype.findFeatureIndexByFid = function(features, fid) {
let index = -1;
for (let i = 0, ii = features.length; i < ii; i++) {
if (features[i].getId() == fid) {
index = i;
break;
}
}
return index;
};


// === FORMAT TYPES ===


Expand Down
5 changes: 5 additions & 0 deletions src/query/Action.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
ADD: 'add',
REMOVE: 'remove',
REPLACE: 'replace'
};
95 changes: 95 additions & 0 deletions src/query/Keyboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import ngeoQueryAction from 'ngeo/query/Action.js';

import {listen as olEventsListen} from 'ol/events.js';

/**
* @module ngeo.query.Keyboard
*/
class Keyboard {

/**
* Listens to the keyboard `keydown` and `keyup` events to keep
* track of the query action to do depending on the key being
* pressed.
*/
constructor() {

// Constants

/**
* The key to press for "ADD" action
* @type {string}
* @private
*/
this.keyAdd_ = 'a';

/**
* The key to press for "REMOVE" action
* @type {string}
* @private
*/
this.keyRemove_ = 'x';

/**
* @type {Array.<string>}
* @private
*/
this.keys_ = [this.keyAdd_, this.keyRemove_];

// Variables

/**
* The key currently being pressed. Only those registered in
* `this.keys_` can be active.
* @type {?string}
* @private
*/
this.activeKey_ = null;

// Event listeners
olEventsListen(document, 'keydown', this.handleKeyDown_, this);
olEventsListen(document, 'keyup', this.handleKeyUp_, this);
}

// Getters

/**
* @return {string} The query action depending on the key currently
* being pressed.
*/
get action() {
let action = ngeoQueryAction.REPLACE;
if (this.activeKey_) {
if (this.activeKey_ === this.keyAdd_) {
action = ngeoQueryAction.ADD;
} else if (this.activeKey_ === this.keyRemove_) {
action = ngeoQueryAction.REMOVE;
}
}
return action;
}

// Handlers

/**
* @param {KeyboardEvent} evt Event.
* @private
*/
handleKeyDown_(evt) {
if (!this.activeKey_ && this.keys_.includes(evt.key)) {
this.activeKey_ = evt.key;
}
}

/**
* @param {KeyboardEvent} evt Event.
* @private
*/
handleKeyUp_(evt) {
if (this.activeKey_ && this.activeKey_ === evt.key) {
this.activeKey_ = null;
}
}
}

export default new Keyboard();
116 changes: 95 additions & 21 deletions src/query/MapQuerent.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
* @module ngeo.query.MapQuerent
*/
import googAsserts from 'goog/asserts.js';
import ngeoQueryAction from 'ngeo/query/Action.js';
import ngeoQueryQuerent from 'ngeo/query/Querent.js';
import ngeoDatasourceDataSources from 'ngeo/datasource/DataSources.js';
import ngeoDatasourceHelper from 'ngeo/datasource/Helper.js';
import ngeoMiscFeatureHelper from 'ngeo/misc/FeatureHelper.js';

const exports = class {

Expand All @@ -17,14 +19,16 @@ const exports = class {
* @param {ngeo.datasource.DataSources} ngeoDataSources Ngeo data sources service.
* @param {ngeo.datasource.Helper} ngeoDataSourcesHelper Ngeo data
* sources helper service.
* @param {ngeo.misc.FeatureHelper} ngeoFeatureHelper Ngeo feature
* helper service.
* @param {ngeo.query.Querent} ngeoQuerent The ngeo querent service.
* @param {ngeox.QueryResult} ngeoQueryResult The ngeo query result service.
* @ngdoc service
* @ngname ngeoQuerent
* @ngInject
*/
constructor($injector, ngeoDataSources, ngeoDataSourcesHelper, ngeoQuerent,
ngeoQueryResult) {
constructor($injector, ngeoDataSources, ngeoDataSourcesHelper,
ngeoFeatureHelper, ngeoQuerent, ngeoQueryResult) {

const options = /** @type {ngeox.QueryOptions} */ (
$injector.has('ngeoQueryOptions') ?
Expand All @@ -36,6 +40,12 @@ const exports = class {
*/
this.dataSources_ = ngeoDataSources.collection;

/**
* @type {ngeo.misc.FeatureHelper}
* @private
*/
this.featureHelper_ = ngeoFeatureHelper;

/**
* @type {ngeo.datasource.Helper}
* @private
Expand Down Expand Up @@ -85,15 +95,24 @@ const exports = class {
* @private
*/
this.dataSourceNames_ = {};

/**
* Flag turned on after clearing to make sure that we clear only once.
* @type {boolean}
* @private
*/
this.cleared_ = false;
}

/**
* @param {ngeox.IssueGetFeaturesOptions} options Options.
* @export
*/
issue(options) {
const action = options.action ? options.action : ngeoQueryAction.REPLACE;

// (1) Clear previous result
this.clear();
this.clear(action !== ngeoQueryAction.REPLACE);

// (2) Get queryable data sources, unless they are already set
let queryableDataSources;
Expand All @@ -116,35 +135,48 @@ const exports = class {
wfsCount: this.queryCountFirst_
});
this.result_.pending = true;
this.ngeoQuerent_.issue(options).then(this.handleResult_.bind(this));
this.ngeoQuerent_.issue(options).then(
this.handleResult_.bind(this, action));
}

/**
* Clear result, i.e. clear all 'result source' from their features and other
* information.
* @param {boolean} keep Whether to keep the existing features and sources
* @export
*/
clear() {
clear(keep = false) {

if (this.cleared_) {
return;
}

this.result_.total = 0;
for (const source of this.result_.sources) {
source.features.length = 0;
if (!keep) {
source.features.length = 0;
source.totalFeatureCount = undefined;
}
source.pending = false;
source.queried = false;
source.tooManyResults = false;
source.totalFeatureCount = undefined;
}
this.result_.sources.length = 0; // Clear previous result sources
if (!keep) {
this.result_.sources.length = 0; // Clear previous result sources
}
this.result_.pending = false;
this.cleared_ = true;
}

/**
* Called after a request to the querent service. Update the result.
*
* @param {string} action Query action
* @param {ngeox.QuerentResult} response Response
* @private
*/
handleResult_(response) {
let total = 0;
handleResult_(action, response) {
let total = action === ngeoQueryAction.REPLACE ? 0 : this.result_.total;

// (1) Update result sources, i.e. add them
for (const idStr in response) {
Expand Down Expand Up @@ -186,23 +218,64 @@ const exports = class {
for (const type in typeSeparatedFeatures) {
label = type ? type : label;
const featuresByType = typeSeparatedFeatures[type];
this.result_.sources.push({
features: featuresByType,
id: id,
label: label,
limit: limit,
pending: false,
queried: true,
tooManyResults: tooManyResults,
totalFeatureCount: totalFeatureCount
});
total += features.length;
let shouldPush = false;

if (action === ngeoQueryAction.REPLACE) {
shouldPush = true;
} else {
let existingSource;
for (const source of this.result_.sources) {
if (source.id === id && source.label === label) {
existingSource = source;
break;
}
}

if (existingSource) {
for (const newFeature of featuresByType) {
const existingFeatureIndex =
this.featureHelper_.findFeatureIndexByFid(
existingSource.features, newFeature.getId()
);
if (existingFeatureIndex === -1) {
if (action === ngeoQueryAction.ADD) {
existingSource.features.push(newFeature);
total += 1;
}
} else {
if (action === ngeoQueryAction.REMOVE) {
existingSource.features.splice(existingFeatureIndex, 1);
total -= 1;
}
}
}
} else {
if (action === ngeoQueryAction.ADD) {
shouldPush = true;
}
}
}

if (shouldPush) {
this.result_.sources.push({
features: featuresByType,
id: id,
label: label,
limit: limit,
pending: false,
queried: true,
tooManyResults: tooManyResults,
totalFeatureCount: totalFeatureCount
});
total += features.length;
}
}
}

// (2) Update total & pending
this.result_.total = total;
this.result_.pending = false;
this.cleared_ = false;
}

};
Expand All @@ -215,6 +288,7 @@ exports.module = angular.module('ngeoMapQuerent', [
ngeoDatasourceDataSources.module.name,
ngeoDatasourceHelper.module.name,
ngeoQueryQuerent.module.name,
ngeoMiscFeatureHelper.module.name,
]);
exports.module.service('ngeoMapQuerent', exports);

Expand Down
10 changes: 7 additions & 3 deletions src/query/bboxQueryComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* @module ngeo.query.bboxQueryComponent
*/
import ngeoQueryMapQuerent from 'ngeo/query/MapQuerent.js';
import ngeoQueryKeyboard from 'ngeo/query/Keyboard.js';

import olInteractionDragBox from 'ol/interaction/DragBox.js';
import * as olEventsCondition from 'ol/events/condition.js';
Expand Down Expand Up @@ -60,11 +61,14 @@ exports.directive_ = function(ngeoMapQuerent) {
* @param {ol.interaction.DragBox.Event} evt Event.
*/
const handleBoxEnd = function(evt) {
const action = ngeoQueryKeyboard.action;
const extent = interaction.getGeometry().getExtent();
const limit = scope.$eval(attrs['ngeoBboxQueryLimit']);
ngeoMapQuerent.issue({
limit: scope.$eval(attrs['ngeoBboxQueryLimit']),
extent: extent,
map: map
action,
extent,
limit,
map
});
};
interaction.on('boxend', handleBoxEnd);
Expand Down

0 comments on commit 889ac7d

Please sign in to comment.