Skip to content

Commit

Permalink
Spatial Intersection Filtering
Browse files Browse the repository at this point in the history
- User can click on a button within the feature info popup to use geometry as a spatial filter.
- User can toggle spatial filtering from within the table view.
  • Loading branch information
yancymatherne committed Nov 23, 2015
1 parent 5897fa5 commit 00a8346
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 4 deletions.
4 changes: 3 additions & 1 deletion src/app/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,9 @@
'count': 'Count',
'populated_count': 'Populated Count',
'unique_values': 'Unique Values',
'fetch_layers_from_server': 'Fetch Layers from Server'
'fetch_layers_from_server': 'Fetch Layers from Server',
'set_spatial_filter': 'Use this feature in a spatial filter',
'spatial_filter': 'Spatial Filter'
};

var module = angular.module('loom_translations_en', ['pascalprecht.translate']);
Expand Down
8 changes: 8 additions & 0 deletions src/common/featuremanager/FeatureInfoBoxDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@
var loadingTable = layer.get('metadata').loadingTable;
return goog.isDefAndNotNull(loadingTable) && loadingTable === true;
};

scope.setAsSpatialFilter = function() {
var feature = mapService.editLayer.getSource().getFeatures()[0];
var geometryGML = featureManagerService.getGeometryGML3FromFeature(feature);
var layerName = featureManagerService.getSelectedLayer().get('metadata')['title'];
tableViewService.setSpatialFilter(geometryGML, layerName);
featureManagerService.hide();
};
}
};
}
Expand Down
103 changes: 103 additions & 0 deletions src/common/featuremanager/FeatureManagerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,8 @@
});
return deferredResponse.promise;
};

this.getGeometryGML3FromFeature = getGeometryGML3FromFeature;
});

//-- Private functions
Expand Down Expand Up @@ -1133,4 +1135,105 @@
return featureGML;
}

function getGeometryGML3FromFeature(feature) {
// TODO: Copied from the above method, changing Polygon to Surface.
// Only used by the spatial filter. Didn't know what else is using the above method.
// At some point in the future should figure out what needs Surface and what needs Polygon.
var featureGML = '';
var index = 0;
var length = 1;
var geometries = [feature.getGeometry()];
var buildCoordString = function(coords) {
var counter = 0;
return String(coords).replace(/,/g, function(all, match) {
if (counter === 1) {
counter = 0;
return ' ';
}
counter++;
return ',';
});
};
var isGeometryCollection = false;
if (feature.getGeometry().getType().toLowerCase() == 'geometrycollection') {
geometries = feature.getGeometry().getGeometries();
length = geometries.length;
featureGML += '<gml:MultiGeometry xmlns:gml="http://www.opengis.net/gml" srsName="' +
mapService_.map.getView().getProjection().getCode() + '">';
isGeometryCollection = true;
}
for (var geometryIndex = 0; geometryIndex < length; geometryIndex++) {
var geometry = geometries[geometryIndex];
var geometryType = geometry.getType().toLowerCase();
if (isGeometryCollection) {
featureGML += '<gml:geometryMember>';
}
if (geometryType == 'point') {
featureGML += '<gml:Point xmlns:gml="http://www.opengis.net/gml" srsName="' +
mapService_.map.getView().getProjection().getCode() + '">' +
'<gml:coordinates decimal="." cs="," ts=" ">' +
geometry.getCoordinates().toString() +
'</gml:coordinates></gml:Point>';
} else if (geometryType == 'linestring') {
featureGML += '<gml:LineString xmlns:gml="http://www.opengis.net/gml" srsName="' +
mapService_.map.getView().getProjection().getCode() + '">' +
'<gml:coordinates decimal="." cs="," ts=" ">' + buildCoordString(geometry.getCoordinates().toString()) +
'</gml:coordinates></gml:LineString>';
} else if (geometryType == 'polygon') {
featureGML += '<gml:Polygon xmlns:gml="http://www.opengis.net/gml" srsName="' +
mapService_.map.getView().getProjection().getCode() + '">' +
'<gml:outerBoundaryIs><gml:LinearRing><gml:coordinates decimal="." cs="," ts=" ">' +
buildCoordString(geometry.getCoordinates()[0].toString()) + '</gml:coordinates>' +
'</gml:LinearRing></gml:outerBoundaryIs>';
for (index = 1; index < geometry.getCoordinates().length; index++) {
featureGML += '<gml:innerBoundaryIs><gml:LinearRing><gml:coordinates decimal="." cs="," ts=" ">' +
buildCoordString(geometry.getCoordinates()[index].toString()) + '</gml:coordinates>' +
'</gml:LinearRing></gml:innerBoundaryIs>';
}
featureGML += '</gml:Polygon>';
} else if (geometryType == 'multipoint') {
featureGML += '<gml:MultiPoint xmlns:gml="http://www.opengis.net/gml" srsName="' +
mapService_.map.getView().getProjection().getCode() + '">';
for (index = 0; index < geometry.getCoordinates().length; index++) {
featureGML += '<gml:pointMember><gml:Point><gml:coordinates decimal="." cs="," ts=" ">' +
geometry.getCoordinates()[index].toString() +
'</gml:coordinates></gml:Point></gml:pointMember>';
}
featureGML += '</gml:MultiPoint>';
} else if (geometryType == 'multilinestring') {
featureGML += '<gml:MultiLineString xmlns:gml="http://www.opengis.net/gml" srsName="' +
mapService_.map.getView().getProjection().getCode() + '">';
for (index = 0; index < geometry.getCoordinates().length; index++) {
featureGML += '<gml:lineMember><gml:LineString><gml:coordinates decimal="." cs="," ts=" ">' +
buildCoordString(geometry.getCoordinates()[index].toString()) +
'</gml:coordinates></gml:LineString></gml:lineMember>';
}
featureGML += '</gml:MultiLineString>';
} else if (geometryType == 'multipolygon') {
featureGML += '<gml:MultiSurface xmlns:gml="http://www.opengis.net/gml" srsName="' +
mapService_.map.getView().getProjection().getCode() + '">';
for (index = 0; index < geometry.getCoordinates().length; index++) {
featureGML += '<gml:surfaceMember><gml:Polygon>' +
'<gml:exterior><gml:LinearRing><gml:posList>' +
geometry.getCoordinates()[index][0].toString().replace(/,/g, ' ') + '</gml:posList>' +
'</gml:LinearRing></gml:exterior>';
for (var innerIndex = 1; innerIndex < geometry.getCoordinates()[index].length; innerIndex++) {
featureGML += '<gml:interior><gml:LinearRing><gml:posList>' +
geometry.getCoordinates()[index][innerIndex].toString().replace(/,/g, ' ') + '</gml:posList>' +
'</gml:LinearRing></gml:interior>';
}
featureGML += '</gml:Polygon></gml:surfaceMember>';
}
featureGML += '</gml:MultiSurface>';
}
if (isGeometryCollection) {
featureGML += '</gml:geometryMember>';
}
}
if (isGeometryCollection) {
featureGML += '</gml:MultiGeometry>';
}
return featureGML;
}

}());
3 changes: 3 additions & 0 deletions src/common/featuremanager/partial/featureinfobox.tpl.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@

<div id="feature-info-box-bottom">
<div id="feature-info-box-button-group" class="btn-group pull-right">
<button type="button" ng-click="setAsSpatialFilter()" tooltip-append-to-body="true" tooltip-placement="top"
tooltip="{{'set_spatial_filter' | translate}}" class="btn btn-sm btn-default glyphicon glyphicon-filter">
</button>
<button type="button" ng-click="showTable(featureManagerService.getSelectedLayer())" tooltip-append-to-body="true" tooltip-placement="top"
tooltip="{{'show_table' | translate}}" class="btn btn-sm btn-default glyphicon glyphicon-list">
<div class="loom-loading" spinner-radius="16" spinner-hidden="!isLoadingTable(featureManagerService.getSelectedLayer())"></div>
Expand Down
8 changes: 8 additions & 0 deletions src/common/tableview/TableViewDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
//these need to be kept in an object to avoid conflicts between the directive scope and ng-if scope
scope.search = {isSearching: false, text: ''};
scope.advFilters = false;
scope.spatialFilter = tableViewService.getSpatialFilter();

function resizeModal() {
var containerHeight = angular.element('#table-view-window .modal-content')[0].clientHeight;
Expand Down Expand Up @@ -151,10 +152,17 @@
}
};
scope.clearFilters = function() {
$('#spatial-filter-button').removeClass('active');
tableViewService.toggleSpatialFilterIsActive(false);
wipeFilterFields();
scope.applyFilters();
};

scope.toggleSpatialFilters = function() {
tableViewService.toggleSpatialFilterIsActive();
scope.applyFilters();
};

var newTableSession = function() {
scope.restrictions = tableViewService.restrictionList;
scope.selectedRow = null;
Expand Down
62 changes: 59 additions & 3 deletions src/common/tableview/TableViewService.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
this.currentPage = 0;
this.totalPages = 0;
this.totalFeatures = 0;
this.spatialFilter = {};

this.nextPage = function() {
this.currentPage++;
Expand Down Expand Up @@ -175,6 +176,33 @@
return xml;
};

var getSpatialFilterXML = function(geometryGML, geometryColumn) {
return '<ogc:Intersects>' +
'<ogc:PropertyName>' + geometryColumn + '</ogc:PropertyName>' +
geometryGML +
'</ogc:Intersects>';
};

/**
* Loop through the schema properties and find the first one that looks like a geometry type.
* @param {Object} schema Metadata about the properties
* @return {String} property name
*/
var getGeometryColumn = function(schema) {
function contains(string, search) {
return string.toLowerCase().indexOf(search) > -1;
}

for (var key in schema) {
var property = schema[key];
if (contains(property._type, 'point') || contains(property._type, 'line') ||
contains(property._type, 'polygon') || contains(property._type, 'curve') ||
contains(property._type, 'surface')) {
return property._name;
}
}
};

this.getFeaturesPostPayloadXML = function(layer, filters, bbox, resultsPerPage, currentPage, exclude_header) {
var paginationParamsStr = '';
if (goog.isDefAndNotNull(resultsPerPage) && goog.isDefAndNotNull(currentPage)) {
Expand Down Expand Up @@ -254,18 +282,29 @@
' srsName="' + metadata.projection + '"' +
'>';

var spatialFilter = '';
if (this.spatialFilter.active) {
var geometryColumn = getGeometryColumn(metadata.schema);
var geometry = this.spatialFilter.geometryGML;
spatialFilter = getSpatialFilterXML(geometry, geometryColumn);
}

if (xmlFilterBody) {
xml += '<ogc:Filter>';
if (!searching) {
xml += '<And>' + bboxStr + xmlFilterBody + '</And>';
xml += '<And>' + bboxStr + spatialFilter + xmlFilterBody + '</And>';
} else {
if (bboxStr) {
xml += '<And>' + bboxStr + '<Or>' + xmlFilterBody + '</Or></And>';
if (bboxStr || spatialFilter) {
xml += '<And>' + bboxStr + spatialFilter + '<Or>' + xmlFilterBody + '</Or></And>';
} else {
xml += '<Or>' + xmlFilterBody + '</Or>';
}
}
xml += '</ogc:Filter>';
} else if (this.spatialFilter.active) {
xml += '<ogc:Filter>';
xml += spatialFilter;
xml += '</ogc:Filter>';
}

xml += '</wfs:Query>' + '</wfs:GetFeature>';
Expand Down Expand Up @@ -389,6 +428,23 @@
return deferredResponse.promise;
};

this.setSpatialFilter = function(geometryGML, layerName) {
this.spatialFilter.geometryGML = geometryGML;
this.spatialFilter.layerName = layerName;
};

this.getSpatialFilter = function() {
return this.spatialFilter;
};

this.toggleSpatialFilterIsActive = function(active) {
if (active === true || active === false) {
this.spatialFilter.active = active;
} else {
this.spatialFilter.active = !this.spatialFilter.active;
}
};

this.search = function(text) {
searching = true;
searchText = text;
Expand Down
4 changes: 4 additions & 0 deletions src/common/tableview/partial/tableview.tpl.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
<!--<button ng-if="!advFilters" type="button" class="filter-button btn btn-default table-btn" ng-click="toggleAdvancedFilters()"-->
<!--translate="advanced_filters" ng-disabled="tableviewform.$visible">-->
<!--</button>-->
<button id="spatial-filter-button" ng-if="spatialFilter.geometryGML" type="button" class="filter-button btn btn-default table-btn" ng-click="toggleSpatialFilters()"
translate="spatial_filter" ng-disabled="tableviewform.$visible" data-toggle="button">
</button>
{{ spatialFilter.layerName }}
<form editable-form name="tableviewform" onaftersave="saveTable()">
<div class="panel panel-default">
<table class="table-striped table-hover" ng-class="{sortable: isSortable}">
Expand Down

0 comments on commit 00a8346

Please sign in to comment.