diff --git a/web/client/utils/FilterUtils.jsx b/web/client/utils/FilterUtils.jsx index 88a33c30a2..8da178ff22 100644 --- a/web/client/utils/FilterUtils.jsx +++ b/web/client/utils/FilterUtils.jsx @@ -1,3 +1,4 @@ + /** * Copyright 2016, GeoSolutions Sas. * All rights reserved. @@ -18,77 +19,377 @@ const normalizeVersion = (version) => { } return version; }; +var ogcVersion = "2.0"; +var ogcComparisonOperators = { + "=": {startTag: "<{namespace}:PropertyIsEqualTo>", endTag: ""}, + ">": {startTag: "<{namespace}:PropertyIsGreaterThan>", endTag: ""}, + "<": {startTag: "<{namespace}:PropertyIsLessThan>", endTag: ""}, + ">=": {startTag: "<{namespace}:PropertyIsGreaterThanOrEqualTo>", endTag: ""}, + "<=": {startTag: "<{namespace}:PropertyIsLessThanOrEqualTo>", endTag: ""}, + "<>": {startTag: "<{namespace}:PropertyIsNotEqualTo>", endTag: ""}, + "><": {startTag: "<{namespace}:PropertyIsBetween>", endTag: ""}, + "like": {startTag: "<{namespace}:PropertyIsLike matchCase=\"true\" wildCard=\"*\" singleChar=\".\" escapeChar=\"!\">", endTag: ""}, + "ilike": {startTag: "<{namespace}:PropertyIsLike matchCase=\"false\" wildCard=\"*\" singleChar=\".\" escapeChar=\"!\"> ", endTag: ""}, + "isNull": {startTag: "<{namespace}:PropertyIsNull>", endTag: ""} +}; +var ogcLogicalOperator = { + "AND": {startTag: "<{namespace}:And>", endTag: ""}, + "OR": {startTag: "<{namespace}:Or>", endTag: ""}, + "AND NOT": {startTag: "<{namespace}:Not>", endTag: ""} +}; +var ogcSpatialOperator = { + "INTERSECTS": {startTag: "<{namespace}:Intersects>", endTag: ""}, + "BBOX": {startTag: "<{namespace}:BBOX>", endTag: ""}, + "CONTAINS": {startTag: "<{namespace}:Contains>", endTag: ""}, + "DWITHIN": {startTag: "<{namespace}:DWithin>", endTag: ""}, + "WITHIN": {startTag: "<{namespace}:Within>", endTag: ""} +}; +var propertyTagReference = { + "ogc": {startTag: "", endTag: ""}, + "fes": {startTag: "", endTag: ""} +}; +var getCQLGeometryElement = function(coordinates, type) { + let geometry = type + "("; + + switch (type) { + case "Point": + geometry += coordinates.join(" "); + break; + case "MultiPoint": + coordinates.forEach((position, index) => { + geometry += position.join(" "); + geometry += index < coordinates.length - 1 ? ", " : ""; + }); + break; + case "Polygon": + coordinates.forEach((element, index) => { + geometry += "("; + let coords = element.map((coordinate) => { + return coordinate[0] + " " + coordinate[1]; + }); + + geometry += coords.join(", "); + geometry += ")"; + + geometry += index < coordinates.length - 1 ? ", " : ""; + }); + break; + case "MultiPolygon": + coordinates.forEach((polygon, idx) => { + geometry += "("; + polygon.forEach((element, index) => { + geometry += "("; + let coords = element.map((coordinate) => { + return coordinate[0] + " " + coordinate[1]; + }); + + geometry += coords.join(", "); + geometry += ")"; + + geometry += index < polygon.length - 1 ? ", " : ""; + }); + geometry += ")"; + geometry += idx < coordinates.length - 1 ? ", " : ""; + }); + break; + default: + break; + } + + geometry += ")"; + return geometry; +}; +var processCQLSpatialFilter = function(json) { + let cql = json.spatialField.operation + "(" + + json.spatialField.attribute + ", "; + + cql += getCQLGeometryElement(json.spatialField.geometry.coordinates, json.spatialField.geometry.type); + + return cql + ")"; +}; +var cqlDateField = function(attribute, operator, value) { + let fieldFilter; + if (operator === "><") { + if (value.startDate && value.endDate) { + fieldFilter = "(" + attribute + ">='" + value.startDate.toISOString() + + "' AND " + attribute + "<='" + value.endDate.toISOString() + "')"; + } + } else { + if (value.startDate) { + fieldFilter = attribute + operator + "'" + value.startDate.toISOString() + "'"; + } + } + return fieldFilter; +}; +var cqlStringField = function(attribute, operator, value) { + let fieldFilter; + if (value) { + if (operator === "isNull") { + fieldFilter = "isNull(" + attribute + ")=true"; + } else if (operator === "=") { + let val = "'" + value + "'"; + fieldFilter = attribute + operator + val; + } else if (operator === "ilike") { + let val = "'%" + value.toLowerCase() + "%'"; + fieldFilter = "strToLowerCase(" + attribute + ") LIKE " + val; + } else { + let val = "'%" + value + "%'"; + fieldFilter = attribute + "LIKE " + val; + } + } + return fieldFilter; +}; +var cqlNumberField = function(attribute, operator, value) { + let fieldFilter; + if (operator === "><") { + if (value && (value.lowBound !== null && value.lowBound !== undefined) && (value.upBound === null || value.upBound === undefined)) { + fieldFilter = "(" + attribute + ">='" + value.lowBound + "')"; + }else if (value && (value.upBound !== null && value.upBound !== undefined) && (value.lowBound === null || value.lowBound === undefined)) { + fieldFilter = "(" + attribute + "<='" + value.upBound + "')"; + } else if (value && (value.upBound !== null && value.upBound !== undefined) && (value.lowBound !== null && value.lowBound !== undefined)) { + fieldFilter = "(" + attribute + ">='" + value.lowBound + + "' AND " + attribute + "<='" + value.upBound + "')"; + } + } else { + let val = value && (value.lowBound !== null && value.lowBound !== undefined) ? value.lowBound : value; + if (val ) { + fieldFilter = attribute + operator + "'" + val + "'"; + } + } + return fieldFilter; +}; +var findSubGroups = function(root, groups) { + let subGroups = groups.filter((g) => g.groupId === root.id); + return subGroups; +}; +var cqlListField = function(attribute, operator, value) { + return cqlStringField(attribute, operator, value); +}; +var processCQLSimpleFilterField = function(field) { + let strFilter = false; + switch (field.type) { + case "date": + strFilter = cqlDateField(field.attribute, field.operator, field.values); + break; + case "number": + strFilter = cqlNumberField(field.attribute, field.operator, field.values); + break; + case "string": + strFilter = cqlStringField(field.attribute, field.operator, field.values); + break; + case "list": { + if (field.values.length !== field.optionsValues.length) { + let addNull = false; + let filter = field.values.reduce((arr, value) => { + if (value === null || value === "null") { + addNull = true; + } else { + arr.push( "'" + value + "'"); + } + return arr; + }, []); + strFilter = filter.length > 0 ? field.attribute + " IN(" + filter.join(",") + ")" : strFilter; + if (addNull) { + strFilter = (strFilter) ? strFilter + " OR isNull(" + field.attribute + ")=true" : "isNull(" + field.attribute + ")=true"; + } + } + break; + } + default: + break; + } + + return (strFilter && strFilter.length > 0) ? strFilter : false; +}; +var processCQLFilterFields = function(group, json) { + let fields = json.filterFields.filter((field) => field.groupId === group.id); + + let filter = []; + if (fields) { + fields.forEach((field) => { + let fieldFilter; + + switch (field.type) { + case "date": + fieldFilter = cqlDateField(field.attribute, field.operator, field.value); + break; + case "number": + fieldFilter = cqlNumberField(field.attribute, field.operator, field.value); + break; + case "string": + fieldFilter = cqlStringField(field.attribute, field.operator, field.value); + break; + case "list": + fieldFilter = cqlListField(field.attribute, field.operator, field.value); + break; + default: + break; + } + if (fieldFilter) { + filter.push(fieldFilter); + } + }); + + filter = filter.join(" " + group.logic + " "); + } + + return filter; +}; +var getGmlPolygonElement = function(coordinates, srsName, version) { + let gmlPolygon = '' : '>'; + + // /////////////////////////////////////////////////////////////////////////////////////////////////////// + // Array of LinearRing coordinate array. The first element in the array represents the exterior ring. + // Any subsequent elements represent interior rings (or holes). + // /////////////////////////////////////////////////////////////////////////////////////////////////////// + coordinates.forEach((element, index) => { + let coords = element.map((coordinate) => { + return coordinate[0] + (version === "1.0.0" ? "," : " ") + coordinate[1]; + }); + const exterior = (version === "1.0.0" ? "outerBoundaryIs" : "exterior"); + const interior = (version === "1.0.0" ? "innerBoundaryIs" : "exterior"); + gmlPolygon += + (index < 1 ? '' : '') + + '' + + (version === "1.0.0" ? '' : '') + + coords.join(" ") + + (version === "1.0.0" ? '' : '') + + '' + + (index < 1 ? '' : ''); + }); + + gmlPolygon += ''; + return gmlPolygon; +}; +var getGmlPointElement = function(coordinates, srsName, version) { + let gmlPoint = '' : '>'; + + // /////////////////////////////////////////////////////////////////////////////////////////////////////// + // Array of LinearRing coordinate array. The first element in the array represents the exterior ring. + // Any subsequent elements represent interior rings (or holes). + // /////////////////////////////////////////////////////////////////////////////////////////////////////// + coordinates.forEach((element) => { + let coords = element.map((coordinate) => { + return coordinate[0] + " " + coordinate[1]; + }); + if (version === "1.0.0") { + gmlPoint += '' + element[0][0] + '' + element[0][1] + ''; + } else { + gmlPoint += '' + coords.join(" ") + ''; + } + }); + + gmlPoint += ''; + return gmlPoint; +}; +var getGetFeatureBase = function(version, pagination, hits, format) { + let ver = normalizeVersion(version); + + let getFeature = ''; + break; + case "1.1.0": + getFeature += pagination && pagination.maxFeatures ? 'maxFeatures="' + pagination.maxFeatures + '" ' : ""; + + getFeature = hits ? getFeature + ' resultType="hits"' : getFeature; + + getFeature += 'service="WFS" version="' + ver + '" ' + + 'xmlns:gml="http://www.opengis.net/gml" ' + + 'xmlns:wfs="http://www.opengis.net/wfs" ' + + 'xmlns:ogc="http://www.opengis.net/ogc" ' + + 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + + 'xsi:schemaLocation="http://www.opengis.net/wfs ' + + 'http://schemas.opengis.net/wfs/1.1.0/wfs.xsd">'; + break; + default: // default is wfs 2.0 + getFeature += pagination && pagination.maxFeatures ? 'count="' + pagination.maxFeatures + '" ' : ""; + + getFeature = hits && !pagination ? getFeature + ' resultType="hits"' : getFeature; + + getFeature += 'service="WFS" version="' + ver + '" ' + + 'xmlns:wfs="http://www.opengis.net/wfs/2.0" ' + + 'xmlns:fes="http://www.opengis.net/fes/2.0" ' + + 'xmlns:gml="http://www.opengis.net/gml/3.2" ' + + 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + + 'xsi:schemaLocation="http://www.opengis.net/wfs/2.0 ' + + 'http://schemas.opengis.net/wfs/2.0/wfs.xsd ' + + 'http://www.opengis.net/gml/3.2 ' + + 'http://schemas.opengis.net/gml/3.2.1/gml.xsd">'; + } + + return getFeature; +}; + +var processCQLFilterGroup = function(root, json) { + let cql = processCQLFilterFields(root, json); + + let subGroups = findSubGroups(root, json.groupFields); + if (subGroups.length > 0) { + subGroups.forEach((subGroup) => { + cql += " " + root.logic + " (" + this.processFilterGroup(subGroup) + ")"; + }); + } + + return cql; +}; const FilterUtils = { - ogcVersion: "2.0", - ogcLogicalOperator: { - "AND": {startTag: "<{namespace}:And>", endTag: ""}, - "OR": {startTag: "<{namespace}:Or>", endTag: ""}, - "AND NOT": {startTag: "<{namespace}:Not>", endTag: ""} - }, - ogcComparisonOperators: { - "=": {startTag: "<{namespace}:PropertyIsEqualTo>", endTag: ""}, - ">": {startTag: "<{namespace}:PropertyIsGreaterThan>", endTag: ""}, - "<": {startTag: "<{namespace}:PropertyIsLessThan>", endTag: ""}, - ">=": {startTag: "<{namespace}:PropertyIsGreaterThanOrEqualTo>", endTag: ""}, - "<=": {startTag: "<{namespace}:PropertyIsLessThanOrEqualTo>", endTag: ""}, - "<>": {startTag: "<{namespace}:PropertyIsNotEqualTo>", endTag: ""}, - "><": {startTag: "<{namespace}:PropertyIsBetween>", endTag: ""}, - "like": {startTag: "<{namespace}:PropertyIsLike matchCase=\"true\" wildCard=\"*\" singleChar=\".\" escapeChar=\"!\">", endTag: ""}, - "ilike": {startTag: "<{namespace}:PropertyIsLike matchCase=\"false\" wildCard=\"*\" singleChar=\".\" escapeChar=\"!\"> ", endTag: ""}, - "isNull": {startTag: "<{namespace}:PropertyIsNull>", endTag: ""} - }, - ogcSpatialOperator: { - "INTERSECTS": {startTag: "<{namespace}:Intersects>", endTag: ""}, - "BBOX": {startTag: "<{namespace}:BBOX>", endTag: ""}, - "CONTAINS": {startTag: "<{namespace}:Contains>", endTag: ""}, - "DWITHIN": {startTag: "<{namespace}:DWithin>", endTag: ""}, - "WITHIN": {startTag: "<{namespace}:Within>", endTag: ""} - }, - propertyTagReference: { - "ogc": {startTag: "", endTag: ""}, - "fes": {startTag: "", endTag: ""} - }, + toOGCFilter: function(ftName, json, version, sortOptions = null, hits = false, format = null, propertyNames = null) { - try { - this.objFilter = (json instanceof Object) ? json : JSON.parse(json); - } catch(e) { - return e; - } - const versionOGC = normalizeVersion(version || this.ogcVersion); - this.nsplaceholder = versionOGC === "2.0" ? "fes" : "ogc"; + const versionOGC = normalizeVersion(version || ogcVersion); + FilterUtils.nsplaceholder = versionOGC === "2.0" ? "fes" : "ogc"; - this.setOperatorsPlaceholders("{namespace}", this.nsplaceholder); + this.setOperatorsPlaceholders("{namespace}", FilterUtils.nsplaceholder); - let ogcFilter = this.getGetFeatureBase(versionOGC, this.objFilter.pagination, hits, format); + let ogcFilter = getGetFeatureBase(versionOGC, json.pagination, hits, format); let filters = []; let attributeFilter; - if (this.objFilter.filterFields && this.objFilter.filterFields.length > 0) { - if (this.objFilter.groupFields && this.objFilter.groupFields.length > 0) { - attributeFilter = this.processOGCFilterGroup(this.objFilter.groupFields[0]); + if (json.filterFields && json.filterFields.length > 0) { + if (json.groupFields && json.groupFields.length > 0) { + attributeFilter = this.processOGCFilterGroup(json.groupFields[0], json); } else { - attributeFilter = this.processOGCFilterFields(); + attributeFilter = this.processOGCFilterFields(json); } filters.push(attributeFilter); - }else if (this.objFilter.simpleFilterFields && this.objFilter.simpleFilterFields.length > 0) { + }else if (json.simpleFilterFields && json.simpleFilterFields.length > 0) { let ogc = ""; - ogc += this.ogcLogicalOperator.AND.startTag; - this.objFilter.simpleFilterFields.forEach((filter) => { + ogc += ogcLogicalOperator.AND.startTag; + json.simpleFilterFields.forEach((filter) => { ogc += this.processOGCSimpleFilterField(filter); }, this); - ogc += this.ogcLogicalOperator.AND.endTag; + ogc += ogcLogicalOperator.AND.endTag; filters.push(ogc); } let spatialFilter; - if (this.objFilter.spatialField && this.objFilter.spatialField.geometry && this.objFilter.spatialField.operation) { - spatialFilter = this.processOGCSpatialFilter(versionOGC); + if (json.spatialField && json.spatialField.geometry && json.spatialField.operation) { + spatialFilter = this.processOGCSpatialFilter(versionOGC, json); filters.push(spatialFilter); } - if (this.objFilter.crossLayerFilter) { - let crossLayerFilter = this.objFilter.crossLayerFilter; + if (json.crossLayerFilter) { + let crossLayerFilter = json.crossLayerFilter; if (Array.isArray()) { crossLayerFilter.forEach( f => filters.push(this.processOGCCrossLayerFilter(f))); } else { @@ -96,55 +397,55 @@ const FilterUtils = { } } - let filter = "<" + this.nsplaceholder + ":Filter>"; + let filter = "<" + FilterUtils.nsplaceholder + ":Filter>"; if (filters.length > 1) { - filter += "<" + this.nsplaceholder + ":And>"; + filter += "<" + FilterUtils.nsplaceholder + ":And>"; filters.forEach((subFilter) => { filter += subFilter; }); - filter += ""; + filter += ""; } else { filter += filters[0]; } - filter += ""; + filter += ""; ogcFilter += ''; ogcFilter += filter; if (propertyNames) { ogcFilter += propertyNames.map( name => ( - this.propertyTagReference[this.nsplaceholder].startTag + + propertyTagReference[FilterUtils.nsplaceholder].startTag + name + - this.propertyTagReference[this.nsplaceholder].endTag )).join(""); + propertyTagReference[FilterUtils.nsplaceholder].endTag )).join(""); } if (sortOptions && sortOptions.sortBy && sortOptions.sortOrder) { ogcFilter += - "<" + this.nsplaceholder + ":SortBy>" + - "<" + this.nsplaceholder + ":SortProperty>" + - this.propertyTagReference[this.nsplaceholder].startTag + + "<" + FilterUtils.nsplaceholder + ":SortBy>" + + "<" + FilterUtils.nsplaceholder + ":SortProperty>" + + propertyTagReference[FilterUtils.nsplaceholder].startTag + sortOptions.sortBy + - this.propertyTagReference[this.nsplaceholder].endTag + - "<" + this.nsplaceholder + ":SortOrder>" + + propertyTagReference[FilterUtils.nsplaceholder].endTag + + "<" + FilterUtils.nsplaceholder + ":SortOrder>" + sortOptions.sortOrder + - "" + - "" + - ""; + "" + + "" + + ""; } ogcFilter += '' + ''; - this.setOperatorsPlaceholders(this.nsplaceholder, "{namespace}"); + this.setOperatorsPlaceholders(FilterUtils.nsplaceholder, "{namespace}"); return ogcFilter; }, setOperatorsPlaceholders: function(placeholder, replacement) { [ - this.ogcLogicalOperator, - this.ogcComparisonOperators, - this.ogcSpatialOperator + ogcLogicalOperator, + ogcComparisonOperators, + ogcSpatialOperator ].forEach((operator) => { for (let op in operator) { if (operator.hasOwnProperty(op)) { @@ -154,77 +455,24 @@ const FilterUtils = { } }); }, - getGetFeatureBase: function(version, pagination, hits, format) { - let ver = normalizeVersion(version); - - let getFeature = ''; - break; - case "1.1.0": - getFeature += pagination && pagination.maxFeatures ? 'maxFeatures="' + pagination.maxFeatures + '" ' : ""; - - getFeature = hits ? getFeature + ' resultType="hits"' : getFeature; - - getFeature += 'service="WFS" version="' + ver + '" ' + - 'xmlns:gml="http://www.opengis.net/gml" ' + - 'xmlns:wfs="http://www.opengis.net/wfs" ' + - 'xmlns:ogc="http://www.opengis.net/ogc" ' + - 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + - 'xsi:schemaLocation="http://www.opengis.net/wfs ' + - 'http://schemas.opengis.net/wfs/1.1.0/wfs.xsd">'; - break; - default: // default is wfs 2.0 - getFeature += pagination && pagination.maxFeatures ? 'count="' + pagination.maxFeatures + '" ' : ""; - - getFeature = hits && !pagination ? getFeature + ' resultType="hits"' : getFeature; - - getFeature += 'service="WFS" version="' + ver + '" ' + - 'xmlns:wfs="http://www.opengis.net/wfs/2.0" ' + - 'xmlns:fes="http://www.opengis.net/fes/2.0" ' + - 'xmlns:gml="http://www.opengis.net/gml/3.2" ' + - 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' + - 'xsi:schemaLocation="http://www.opengis.net/wfs/2.0 ' + - 'http://schemas.opengis.net/wfs/2.0/wfs.xsd ' + - 'http://www.opengis.net/gml/3.2 ' + - 'http://schemas.opengis.net/gml/3.2.1/gml.xsd">'; - } - - return getFeature; - }, - processOGCFilterGroup: function(root) { + processOGCFilterGroup: function(root, json) { let ogc = - this.ogcLogicalOperator[root.logic].startTag + - this.processOGCFilterFields(root); + ogcLogicalOperator[root.logic].startTag + + this.processOGCFilterFields(root, json); - let subGroups = this.findSubGroups(root, this.objFilter.groupFields); + let subGroups = findSubGroups(root, json.groupFields); if (subGroups.length > 0) { subGroups.forEach((subGroup) => { ogc += this.processOGCFilterGroup(subGroup); }); } - ogc += this.ogcLogicalOperator[root.logic].endTag; + ogc += ogcLogicalOperator[root.logic].endTag; return ogc; }, - processOGCFilterFields: function(group) { - let fields = group ? this.objFilter.filterFields.filter((field) => field.groupId === group.id) : this.objFilter.filterFields; + processOGCFilterFields: function(group, json) { + let fields = group ? json.filterFields.filter((field) => field.groupId === group.id) : json.filterFields; let filter = []; if (fields) { @@ -274,8 +522,8 @@ const FilterUtils = { filter = field.values.reduce((ogc, val) => { let op = (val === null || val === "null") ? "isNull" : "="; return ogc + this.ogcStringField(field.attribute, op, val); - }, this.ogcLogicalOperator.OR.startTag); - filter += this.ogcLogicalOperator.OR.endTag; + }, ogcLogicalOperator.OR.startTag); + filter += ogcLogicalOperator.OR.endTag; } break; } @@ -289,27 +537,27 @@ const FilterUtils = { if (operator === "><") { if (value.startDate && value.endDate) { fieldFilter = - this.ogcComparisonOperators[operator].startTag + - this.propertyTagReference[this.nsplaceholder].startTag + + ogcComparisonOperators[operator].startTag + + propertyTagReference[FilterUtils.nsplaceholder].startTag + attribute + - this.propertyTagReference[this.nsplaceholder].endTag + - "<" + this.nsplaceholder + ":LowerBoundary>" + - "<" + this.nsplaceholder + ":Literal>" + value.startDate.toISOString() + "" + - "" + - "<" + this.nsplaceholder + ":UpperBoundary>" + - "<" + this.nsplaceholder + ":Literal>" + value.endDate.toISOString() + "" + - "" + - this.ogcComparisonOperators[operator].endTag; + propertyTagReference[FilterUtils.nsplaceholder].endTag + + "<" + FilterUtils.nsplaceholder + ":LowerBoundary>" + + "<" + FilterUtils.nsplaceholder + ":Literal>" + value.startDate.toISOString() + "" + + "" + + "<" + FilterUtils.nsplaceholder + ":UpperBoundary>" + + "<" + FilterUtils.nsplaceholder + ":Literal>" + value.endDate.toISOString() + "" + + "" + + ogcComparisonOperators[operator].endTag; } } else { if (value.startDate) { fieldFilter = - this.ogcComparisonOperators[operator].startTag + - this.propertyTagReference[this.nsplaceholder].startTag + + ogcComparisonOperators[operator].startTag + + propertyTagReference[FilterUtils.nsplaceholder].startTag + attribute + - this.propertyTagReference[this.nsplaceholder].endTag + - "<" + this.nsplaceholder + ":Literal>" + value.startDate.toISOString() + "" + - this.ogcComparisonOperators[operator].endTag; + propertyTagReference[FilterUtils.nsplaceholder].endTag + + "<" + FilterUtils.nsplaceholder + ":Literal>" + value.startDate.toISOString() + "" + + ogcComparisonOperators[operator].endTag; } } return fieldFilter; @@ -318,12 +566,12 @@ const FilterUtils = { let fieldFilter; if (value) { fieldFilter = - this.ogcComparisonOperators[operator].startTag + - this.propertyTagReference[this.nsplaceholder].startTag + + ogcComparisonOperators[operator].startTag + + propertyTagReference[FilterUtils.nsplaceholder].startTag + attribute + - this.propertyTagReference[this.nsplaceholder].endTag + - "<" + this.nsplaceholder + ":Literal>" + value + "" + - this.ogcComparisonOperators[operator].endTag; + propertyTagReference[FilterUtils.nsplaceholder].endTag + + "<" + FilterUtils.nsplaceholder + ":Literal>" + value + "" + + ogcComparisonOperators[operator].endTag; } return fieldFilter; }, @@ -332,27 +580,27 @@ const FilterUtils = { if (value) { if (operator === "isNull") { fieldFilter = - this.ogcComparisonOperators[operator].startTag + - this.propertyTagReference[this.nsplaceholder].startTag + + ogcComparisonOperators[operator].startTag + + propertyTagReference[FilterUtils.nsplaceholder].startTag + attribute + - this.propertyTagReference[this.nsplaceholder].endTag + - this.ogcComparisonOperators[operator].endTag; + propertyTagReference[FilterUtils.nsplaceholder].endTag + + ogcComparisonOperators[operator].endTag; }else if (operator === "=") { fieldFilter = - this.ogcComparisonOperators[operator].startTag + - this.propertyTagReference[this.nsplaceholder].startTag + + ogcComparisonOperators[operator].startTag + + propertyTagReference[FilterUtils.nsplaceholder].startTag + attribute + - this.propertyTagReference[this.nsplaceholder].endTag + - "<" + this.nsplaceholder + ":Literal>" + value + "" + - this.ogcComparisonOperators[operator].endTag; + propertyTagReference[FilterUtils.nsplaceholder].endTag + + "<" + FilterUtils.nsplaceholder + ":Literal>" + value + "" + + ogcComparisonOperators[operator].endTag; }else { fieldFilter = - this.ogcComparisonOperators[operator].startTag + - this.propertyTagReference[this.nsplaceholder].startTag + + ogcComparisonOperators[operator].startTag + + propertyTagReference[FilterUtils.nsplaceholder].startTag + attribute + - this.propertyTagReference[this.nsplaceholder].endTag + - "<" + this.nsplaceholder + ":Literal>*" + value + "*" + - this.ogcComparisonOperators[operator].endTag; + propertyTagReference[FilterUtils.nsplaceholder].endTag + + "<" + FilterUtils.nsplaceholder + ":Literal>*" + value + "*" + + ogcComparisonOperators[operator].endTag; } } return fieldFilter; @@ -361,74 +609,74 @@ const FilterUtils = { let fieldFilter; if (operator === "><") { if (value && (value.lowBound !== null && value.lowBound !== undefined) && (value.upBound === null || value.upBound === undefined)) { - fieldFilter = this.ogcComparisonOperators[">="].startTag + - this.propertyTagReference[this.nsplaceholder].startTag + + fieldFilter = ogcComparisonOperators[">="].startTag + + propertyTagReference[FilterUtils.nsplaceholder].startTag + attribute + - this.propertyTagReference[this.nsplaceholder].endTag + - "<" + this.nsplaceholder + ":Literal>" + value.lowBound + "" + - this.ogcComparisonOperators[">="].endTag; + propertyTagReference[FilterUtils.nsplaceholder].endTag + + "<" + FilterUtils.nsplaceholder + ":Literal>" + value.lowBound + "" + + ogcComparisonOperators[">="].endTag; }else if (value && (value.upBound !== null && value.upBound !== undefined) && (value.lowBound === null || value.lowBound === undefined)) { - fieldFilter = this.ogcComparisonOperators["<="].startTag + - this.propertyTagReference[this.nsplaceholder].startTag + + fieldFilter = ogcComparisonOperators["<="].startTag + + propertyTagReference[FilterUtils.nsplaceholder].startTag + attribute + - this.propertyTagReference[this.nsplaceholder].endTag + - "<" + this.nsplaceholder + ":Literal>" + value.upBound + "" + - this.ogcComparisonOperators["<="].endTag; + propertyTagReference[FilterUtils.nsplaceholder].endTag + + "<" + FilterUtils.nsplaceholder + ":Literal>" + value.upBound + "" + + ogcComparisonOperators["<="].endTag; }else if (value && (value.upBound !== null && value.upBound !== undefined) && (value.lowBound !== null && value.lowBound !== undefined)) { fieldFilter = - this.ogcComparisonOperators[operator].startTag + - this.propertyTagReference[this.nsplaceholder].startTag + + ogcComparisonOperators[operator].startTag + + propertyTagReference[FilterUtils.nsplaceholder].startTag + attribute + - this.propertyTagReference[this.nsplaceholder].endTag + - "<" + this.nsplaceholder + ":LowerBoundary>" + - "<" + this.nsplaceholder + ":Literal>" + value.lowBound + "" + - "" + - "<" + this.nsplaceholder + ":UpperBoundary>" + - "<" + this.nsplaceholder + ":Literal>" + value.upBound + "" + - "" + - this.ogcComparisonOperators[operator].endTag; + propertyTagReference[FilterUtils.nsplaceholder].endTag + + "<" + FilterUtils.nsplaceholder + ":LowerBoundary>" + + "<" + FilterUtils.nsplaceholder + ":Literal>" + value.lowBound + "" + + "" + + "<" + FilterUtils.nsplaceholder + ":UpperBoundary>" + + "<" + FilterUtils.nsplaceholder + ":Literal>" + value.upBound + "" + + "" + + ogcComparisonOperators[operator].endTag; } } else { let val = value && (value.lowBound !== null && value.lowBound !== undefined) ? value.lowBound : value; if (val) { - fieldFilter = this.ogcComparisonOperators[operator].startTag + - this.propertyTagReference[this.nsplaceholder].startTag + + fieldFilter = ogcComparisonOperators[operator].startTag + + propertyTagReference[FilterUtils.nsplaceholder].startTag + attribute + - this.propertyTagReference[this.nsplaceholder].endTag + - "<" + this.nsplaceholder + ":Literal>" + val + "" + - this.ogcComparisonOperators[operator].endTag; + propertyTagReference[FilterUtils.nsplaceholder].endTag + + "<" + FilterUtils.nsplaceholder + ":Literal>" + val + "" + + ogcComparisonOperators[operator].endTag; } } return fieldFilter; }, - processOGCSpatialFilter: function(version) { - let ogc = this.ogcSpatialOperator[this.objFilter.spatialField.operation].startTag; + processOGCSpatialFilter: function(version, json) { + let ogc = ogcSpatialOperator[json.spatialField.operation].startTag; ogc += - this.propertyTagReference[this.nsplaceholder].startTag + - this.objFilter.spatialField.attribute + - this.propertyTagReference[this.nsplaceholder].endTag; + propertyTagReference[FilterUtils.nsplaceholder].startTag + + json.spatialField.attribute + + propertyTagReference[FilterUtils.nsplaceholder].endTag; - switch (this.objFilter.spatialField.operation) { + switch (json.spatialField.operation) { case "INTERSECTS": case "DWITHIN": case "WITHIN": case "CONTAINS": { - switch (this.objFilter.spatialField.geometry.type) { + switch (json.spatialField.geometry.type) { case "Point": - ogc += this.getGmlPointElement(this.objFilter.spatialField.geometry.coordinates, - this.objFilter.spatialField.geometry.projection || "EPSG:4326", version); + ogc += getGmlPointElement(json.spatialField.geometry.coordinates, + json.spatialField.geometry.projection || "EPSG:4326", version); break; case "MultiPoint": - ogc += ''; + ogc += ''; // ////////////////////////////////////////////////////////////////////////// // Coordinates of a MultiPoint are an array of positions // ////////////////////////////////////////////////////////////////////////// - this.objFilter.spatialField.geometry.coordinates.forEach((element) => { + json.spatialField.geometry.coordinates.forEach((element) => { let point = element; if (point) { ogc += ""; - ogc += this.getGmlPointElement(point, version); + ogc += getGmlPointElement(point, version); ogc += ""; } }); @@ -436,23 +684,23 @@ const FilterUtils = { ogc += ''; break; case "Polygon": - ogc += this.getGmlPolygonElement(this.objFilter.spatialField.geometry.coordinates, - this.objFilter.spatialField.geometry.projection || "EPSG:4326", version); + ogc += getGmlPolygonElement(json.spatialField.geometry.coordinates, + json.spatialField.geometry.projection || "EPSG:4326", version); break; case "MultiPolygon": const multyPolygonTagName = version === "2.0" ? "MultiSurface" : "MultiPolygon"; const polygonMemberTagName = version === "2.0" ? "surfaceMembers" : "polygonMember"; - ogc += ''; + ogc += ''; // ////////////////////////////////////////////////////////////////////////// // Coordinates of a MultiPolygon are an array of Polygon coordinate arrays // ////////////////////////////////////////////////////////////////////////// - this.objFilter.spatialField.geometry.coordinates.forEach((element) => { + json.spatialField.geometry.coordinates.forEach((element) => { let polygon = element; if (polygon) { ogc += ""; - ogc += this.getGmlPolygonElement(polygon, version); + ogc += getGmlPolygonElement(polygon, version); ogc += ""; } }); @@ -463,18 +711,18 @@ const FilterUtils = { break; } - if (this.objFilter.spatialField.operation === "DWITHIN") { - ogc += '<' + this.nsplaceholder + ':Distance units="m">' + (this.objFilter.spatialField.geometry.distance || 0) + ''; + if (json.spatialField.operation === "DWITHIN") { + ogc += '<' + FilterUtils.nsplaceholder + ':Distance units="m">' + (json.spatialField.geometry.distance || 0) + ''; } break; } case "BBOX": { - let lowerCorner = this.objFilter.spatialField.geometry.extent[0] + " " + this.objFilter.spatialField.geometry.extent[1]; - let upperCorner = this.objFilter.spatialField.geometry.extent[2] + " " + this.objFilter.spatialField.geometry.extent[3]; + let lowerCorner = json.spatialField.geometry.extent[0] + " " + json.spatialField.geometry.extent[1]; + let upperCorner = json.spatialField.geometry.extent[2] + " " + json.spatialField.geometry.extent[3]; ogc += - '' + + '' + '' + lowerCorner + '' + '' + upperCorner + '' + ''; @@ -485,7 +733,7 @@ const FilterUtils = { break; } - ogc += this.ogcSpatialOperator[this.objFilter.spatialField.operation].endTag; + ogc += ogcSpatialOperator[json.spatialField.operation].endTag; return ogc; }, /** @@ -514,11 +762,11 @@ const FilterUtils = { * } */ processOGCCrossLayerFilter: function(crossLayerFilter) { - let ogc = this.ogcSpatialOperator[crossLayerFilter.operation].startTag; + let ogc = ogcSpatialOperator[crossLayerFilter.operation].startTag; ogc += - this.propertyTagReference[this.nsplaceholder].startTag + + propertyTagReference[FilterUtils.nsplaceholder].startTag + crossLayerFilter.attribute + - this.propertyTagReference[this.nsplaceholder].endTag; + propertyTagReference[FilterUtils.nsplaceholder].endTag; // only collectGeometries is supported now if (crossLayerFilter.collectGeometries) { ogc += `` + @@ -530,80 +778,24 @@ const FilterUtils = { ``; } if (crossLayerFilter.operation === "DWITHIN") { - ogc += '<' + this.nsplaceholder + ':Distance units="m">' + (crossLayerFilter.distance || 0) + ''; + ogc += '<' + FilterUtils.nsplaceholder + ':Distance units="m">' + (crossLayerFilter.distance || 0) + ''; } - ogc += this.ogcSpatialOperator[crossLayerFilter.operation].endTag; + ogc += ogcSpatialOperator[crossLayerFilter.operation].endTag; return ogc; }, - getGmlPointElement: function(coordinates, srsName, version) { - let gmlPoint = '' : '>'; - - // /////////////////////////////////////////////////////////////////////////////////////////////////////// - // Array of LinearRing coordinate array. The first element in the array represents the exterior ring. - // Any subsequent elements represent interior rings (or holes). - // /////////////////////////////////////////////////////////////////////////////////////////////////////// - coordinates.forEach((element) => { - let coords = element.map((coordinate) => { - return coordinate[0] + " " + coordinate[1]; - }); - if (version === "1.0.0") { - gmlPoint += '' + element[0][0] + '' + element[0][1] + ''; - } else { - gmlPoint += '' + coords.join(" ") + ''; - } - }); - - gmlPoint += ''; - return gmlPoint; - }, - getGmlPolygonElement: function(coordinates, srsName, version) { - let gmlPolygon = '' : '>'; - - // /////////////////////////////////////////////////////////////////////////////////////////////////////// - // Array of LinearRing coordinate array. The first element in the array represents the exterior ring. - // Any subsequent elements represent interior rings (or holes). - // /////////////////////////////////////////////////////////////////////////////////////////////////////// - coordinates.forEach((element, index) => { - let coords = element.map((coordinate) => { - return coordinate[0] + (version === "1.0.0" ? "," : " ") + coordinate[1]; - }); - const exterior = (version === "1.0.0" ? "outerBoundaryIs" : "exterior"); - const interior = (version === "1.0.0" ? "innerBoundaryIs" : "exterior"); - gmlPolygon += - (index < 1 ? '' : '') + - '' + - (version === "1.0.0" ? '' : '') + - coords.join(" ") + - (version === "1.0.0" ? '' : '') + - '' + - (index < 1 ? '' : ''); - }); - - gmlPolygon += ''; - return gmlPolygon; - }, toCQLFilter: function(json) { - try { - this.objFilter = (json instanceof Object) ? json : JSON.parse(json); - } catch(e) { - return e; - } let filters = []; let attributeFilter; - if (this.objFilter.filterFields && this.objFilter.filterFields.length > 0) { - attributeFilter = this.processCQLFilterGroup(this.objFilter.groupFields[0]); + if (json.filterFields && json.filterFields.length > 0) { + attributeFilter = processCQLFilterGroup(json.groupFields[0], json); filters.push(attributeFilter); - }else if (this.objFilter.simpleFilterFields && this.objFilter.simpleFilterFields.length > 0) { - let simpleFilter = this.objFilter.simpleFilterFields.reduce((cql, field) => { + }else if (json.simpleFilterFields && json.simpleFilterFields.length > 0) { + let simpleFilter = json.simpleFilterFields.reduce((cql, field) => { let tmp = cql; - let strFilter = this.processCQLSimpleFilterField(field); + let strFilter = processCQLSimpleFilterField(field); if (strFilter !== false) { tmp = cql.length > 0 ? cql + " AND (" + strFilter + ")" : "(" + strFilter + ")"; } @@ -614,211 +806,12 @@ const FilterUtils = { } let spatialFilter; - if (this.objFilter.spatialField && this.objFilter.spatialField.geometry && this.objFilter.spatialField.operation) { - spatialFilter = this.processCQLSpatialFilter(); + if (json.spatialField && json.spatialField.geometry && json.spatialField.operation) { + spatialFilter = processCQLSpatialFilter(json); filters.push(spatialFilter); } return "(" + (filters.length > 1 ? filters.join(") AND (") : filters[0]) + ")"; - }, - processCQLFilterGroup: function(root) { - let cql = this.processCQLFilterFields(root); - - let subGroups = this.findSubGroups(root, this.objFilter.groupFields); - if (subGroups.length > 0) { - subGroups.forEach((subGroup) => { - cql += " " + root.logic + " (" + this.processFilterGroup(subGroup) + ")"; - }); - } - - return cql; - }, - processCQLFilterFields: function(group) { - let fields = this.objFilter.filterFields.filter((field) => field.groupId === group.id); - - let filter = []; - if (fields) { - fields.forEach((field) => { - let fieldFilter; - - switch (field.type) { - case "date": - fieldFilter = this.cqlDateField(field.attribute, field.operator, field.value); - break; - case "number": - fieldFilter = this.cqlNumberField(field.attribute, field.operator, field.value); - break; - case "string": - fieldFilter = this.cqlStringField(field.attribute, field.operator, field.value); - break; - case "list": - fieldFilter = this.cqlListField(field.attribute, field.operator, field.value); - break; - default: - break; - } - if (fieldFilter) { - filter.push(fieldFilter); - } - }); - - filter = filter.join(" " + group.logic + " "); - } - - return filter; - }, - processCQLSimpleFilterField: function(field) { - let strFilter = false; - switch (field.type) { - case "date": - strFilter = this.cqlDateField(field.attribute, field.operator, field.values); - break; - case "number": - strFilter = this.cqlNumberField(field.attribute, field.operator, field.values); - break; - case "string": - strFilter = this.cqlStringField(field.attribute, field.operator, field.values); - break; - case "list": { - if (field.values.length !== field.optionsValues.length) { - let addNull = false; - let filter = field.values.reduce((arr, value) => { - if (value === null || value === "null") { - addNull = true; - } else { - arr.push( "'" + value + "'"); - } - return arr; - }, []); - strFilter = filter.length > 0 ? field.attribute + " IN(" + filter.join(",") + ")" : strFilter; - if (addNull) { - strFilter = (strFilter) ? strFilter + " OR isNull(" + field.attribute + ")=true" : "isNull(" + field.attribute + ")=true"; - } - } - break; - } - default: - break; - } - - return (strFilter && strFilter.length > 0) ? strFilter : false; - }, - cqlListField: function(attribute, operator, value) { - return this.cqlStringField(attribute, operator, value); - }, - cqlStringField: function(attribute, operator, value) { - let fieldFilter; - if (value) { - if (operator === "isNull") { - fieldFilter = "isNull(" + attribute + ")=true"; - } else if (operator === "=") { - let val = "'" + value + "'"; - fieldFilter = attribute + operator + val; - } else if (operator === "ilike") { - let val = "'%" + value.toLowerCase() + "%'"; - fieldFilter = "strToLowerCase(" + attribute + ") LIKE " + val; - } else { - let val = "'%" + value + "%'"; - fieldFilter = attribute + "LIKE " + val; - } - } - return fieldFilter; - }, - cqlNumberField: function(attribute, operator, value) { - let fieldFilter; - if (operator === "><") { - if (value && (value.lowBound !== null && value.lowBound !== undefined) && (value.upBound === null || value.upBound === undefined)) { - fieldFilter = "(" + attribute + ">='" + value.lowBound + "')"; - }else if (value && (value.upBound !== null && value.upBound !== undefined) && (value.lowBound === null || value.lowBound === undefined)) { - fieldFilter = "(" + attribute + "<='" + value.upBound + "')"; - } else if (value && (value.upBound !== null && value.upBound !== undefined) && (value.lowBound !== null && value.lowBound !== undefined)) { - fieldFilter = "(" + attribute + ">='" + value.lowBound + - "' AND " + attribute + "<='" + value.upBound + "')"; - } - } else { - let val = value && (value.lowBound !== null && value.lowBound !== undefined) ? value.lowBound : value; - if (val ) { - fieldFilter = attribute + operator + "'" + val + "'"; - } - } - return fieldFilter; - }, - cqlDateField: function(attribute, operator, value) { - let fieldFilter; - if (operator === "><") { - if (value.startDate && value.endDate) { - fieldFilter = "(" + attribute + ">='" + value.startDate.toISOString() + - "' AND " + attribute + "<='" + value.endDate.toISOString() + "')"; - } - } else { - if (value.startDate) { - fieldFilter = attribute + operator + "'" + value.startDate.toISOString() + "'"; - } - } - return fieldFilter; - }, - processCQLSpatialFilter: function() { - let cql = this.objFilter.spatialField.operation + "(" + - this.objFilter.spatialField.attribute + ", "; - - cql += this.getCQLGeometryElement(this.objFilter.spatialField.geometry.coordinates, this.objFilter.spatialField.geometry.type); - - return cql + ")"; - }, - getCQLGeometryElement: function(coordinates, type) { - let geometry = type + "("; - - switch (type) { - case "Point": - geometry += coordinates.join(" "); - break; - case "MultiPoint": - coordinates.forEach((position, index) => { - geometry += position.join(" "); - geometry += index < coordinates.length - 1 ? ", " : ""; - }); - break; - case "Polygon": - coordinates.forEach((element, index) => { - geometry += "("; - let coords = element.map((coordinate) => { - return coordinate[0] + " " + coordinate[1]; - }); - - geometry += coords.join(", "); - geometry += ")"; - - geometry += index < coordinates.length - 1 ? ", " : ""; - }); - break; - case "MultiPolygon": - coordinates.forEach((polygon, idx) => { - geometry += "("; - polygon.forEach((element, index) => { - geometry += "("; - let coords = element.map((coordinate) => { - return coordinate[0] + " " + coordinate[1]; - }); - - geometry += coords.join(", "); - geometry += ")"; - - geometry += index < polygon.length - 1 ? ", " : ""; - }); - geometry += ")"; - geometry += idx < coordinates.length - 1 ? ", " : ""; - }); - break; - default: - break; - } - - geometry += ")"; - return geometry; - }, - findSubGroups: function(root, groups) { - let subGroups = groups.filter((g) => g.groupId === root.id); - return subGroups; } };