Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature spatial distance. #214

Draft
wants to merge 5 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Change Log

## [Unreleased]
### Added
- The `distance` method for the `SpatialPredicate` predicate with implementation for the `ODataAdapter` adapter.

### Changed
- The `GeographyPredicate` and `GeometryPredicate` predicates are the `SpatialPredicate` predicate subtypes.

## [2.6.0] - 2021-07-02
### Added
Expand Down
12 changes: 3 additions & 9 deletions addon/query/indexeddb-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import {
StringPredicate,
DetailPredicate,
DatePredicate,
GeographyPredicate,
GeometryPredicate,
SpatialPredicate,
TruePredicate,
FalsePredicate
} from './predicate';
Expand Down Expand Up @@ -610,13 +609,8 @@ function updateWhereClause(store, table, query) {
}
}

if (predicate instanceof GeographyPredicate) {
Ember.warn('GeographyPredicate is not supported in indexedDB-adapter');
return table;
}

if (predicate instanceof GeometryPredicate) {
Ember.warn('GeometryPredicate is not supported in indexedDB-adapter');
if (predicate instanceof SpatialPredicate) {
Ember.warn('SpatialPredicate subtypes are not supported in indexedDB-adapter');
return table;
}

Expand Down
21 changes: 6 additions & 15 deletions addon/query/js-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import {
StringPredicate,
DetailPredicate,
DatePredicate,
GeographyPredicate,
GeometryPredicate,
SpatialPredicate,
TruePredicate,
FalsePredicate
} from './predicate';
Expand Down Expand Up @@ -253,24 +252,16 @@ export default class JSAdapter extends BaseAdapter {
let b2 = predicate instanceof StringPredicate;
let b3 = predicate instanceof DetailPredicate;
let b4 = predicate instanceof DatePredicate;
let b5 = predicate instanceof GeographyPredicate;
let b6 = predicate instanceof GeometryPredicate;
let b7 = predicate instanceof TruePredicate;
let b8 = predicate instanceof FalsePredicate;
if (b1 || b2 || b3 || b4 || b7 || b8) {
let b5 = predicate instanceof SpatialPredicate;
let b6 = predicate instanceof TruePredicate;
let b7 = predicate instanceof FalsePredicate;
if (b1 || b2 || b3 || b4 || b6 || b7) {
let filterFunction = this.getAttributeFilterFunction(predicate, options);
return this.getFilterFunctionAnd([filterFunction]);
}

if (b5) {
Ember.warn('GeographyPredicate is not supported in js-adapter');
return function (data) {
return data;
};
}

if (b6) {
Ember.warn('GeometryPredicate is not supported in js-adapter');
Ember.warn('SpatialPredicate subtypes are not supported in js-adapter');
return function (data) {
return data;
};
Expand Down
28 changes: 17 additions & 11 deletions addon/query/odata-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import {
StringPredicate,
DetailPredicate,
DatePredicate,
GeographyPredicate,
GeometryPredicate,
SpatialPredicate,
NotPredicate,
IsOfPredicate,
TruePredicate,
Expand Down Expand Up @@ -268,22 +267,29 @@ export default class ODataAdapter extends BaseAdapter {
return `isof(${expression},'${namespace}.${className}')`;
}

if (predicate instanceof GeographyPredicate) {
if (predicate instanceof SpatialPredicate) {
let attribute = this._getODataAttributeName(modelName, predicate.attributePath);
if (prefix) {
attribute = `${prefix}/${attribute}`;
}

return `geo.intersects(geography1=${attribute},geography2=geography'${predicate.intersectsValue}')`;
}

if (predicate instanceof GeometryPredicate) {
let attribute = this._getODataAttributeName(modelName, predicate.attributePath);
if (prefix) {
attribute = `${prefix}/${attribute}`;
let ns = predicate.spatialNamespace;
let fn = predicate.spatialFunction;
let tp = predicate.spatialType;
let sp = predicate.spatial;

let expression = `${ns}.${fn}(${tp}1=${attribute},${tp}2=${tp}'${sp}')`;
switch (fn) {
case 'distance': {
let operator = this._getODataFilterOperator(predicate.operator);
let value = predicate.value;
return `${expression} ${operator} ${value}`;
}
case 'intersects':
return expression;
}

return `geom.intersects(geometry1=${attribute},geometry2=geometry'${predicate.intersectsValue}')`;
throw new Error(`Unsupported '${fn}' spatial function.`);
}

if (predicate instanceof DetailPredicate) {
Expand Down
182 changes: 134 additions & 48 deletions addon/query/predicate.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,25 +293,25 @@ export class StringPredicate extends BasePredicate {
}

/**
* The predicate class for geography attributes.
* The base class of logical predicate for spatial attributes.
*
* @namespace Query
* @class GeographyPredicate
* @class SpatialPredicate
* @extends BasePredicate
*
* @param {String} attributePath The path to the attribute for predicate.
* @constructor
*/
export class GeographyPredicate extends BasePredicate {
export class SpatialPredicate extends BasePredicate {
constructor(attributePath) {
super();

if (!attributePath) {
throw new Error('Attribute path is required for GeographyPredicate constructor.');
throw new Error('Attribute path is required for SpatialPredicate constructor.');
}

this._attributePath = attributePath;
this._intersectsValue = null;
this._options = null;
}

/**
Expand All @@ -326,85 +326,171 @@ export class GeographyPredicate extends BasePredicate {
}

/**
* The geography value that has to intersect with the attribute.
* The filter operator.
*
* @property operator
* @type Query.FilterOperator
* @public
*/
get operator() {
return this._options.operator;
}

/**
* The spatial type value that has to be the second argument of the spatial function,
* assuming the attribute is the first argument.
*
* @property intersectsValue
* @property spatial
* @type {String}
* @public
*/
get intersectsValue() {
return this._intersectsValue;
get spatial() {
return this._options.spatial;
}

/**
* Sets the value that the attribute has to contain.
* The spatial function to call.
*
* @method contains
* @param {String} geography The geography value that has to intersect with the attribute.
* @return {Query.StringPredicate} Returns this instance.
* @property spatialFunction
* @type {String}
* @public
*/
get spatialFunction() {
return this._function;
}

/**
* The spatial type namespace.
*
* @property spatialNamespace
* @type {String}
* @public
*/
get spatialNamespace() {
throw this._getAbstractPropertyError('spatialNamespace');
}

/**
* The spatial type.
*
* @property spatialType
* @type {String}
* @public
*/
get spatialType() {
throw this._getAbstractPropertyError('spatialType');
}

/**
* The value for filtering.
*
* @property value
* @type String|Number
* @public
*/
get value() {
return this._options.value;
}

/**
* Sets the spatial predicate metadata that has to be used on build the distance condition with the attribute.
*
* @method distance
* @param {String} spatial The spatial type value that has to have a distance from the attribute.
* @param operator {Query.FilterOperator|String} The filter operator.
* @param value {String|Number} The value for filtering.
* @return {Query.SpatialPredicate} Returns this instance.
* @chainable
*/
intersects(geography) {
this._intersectsValue = geography;
distance(spatial, operator, value) {
this._function = 'distance';
this._options = {
spatial: spatial,
operator: FilterOperator.tryCreate(operator),
value: value,
}
return this;
}

/**
* Sets the spatial predicate metadata that has to be used on build the intersection condition with the attribute.
*
* @method intesects
* @param {String} spatial The spatial type value that has to intersect with the attribute.
* @return {Query.SpatialPredicate} Returns this instance.
* @chainable
*/
intersects(spatial) {
this._function = 'intersects';
this._options = {
spatial: spatial,
}
return this;
}

_getAbstractPropertyError(property) {
return new Error(`The ${property} property must be overridden in the SpatialPredicate subtypes.`);
}
}

/**
* The predicate class for geometry attributes.
* The predicate class for geography attributes.
*
* @namespace Query
* @class GeometryPredicate
* @extends BasePredicate
*
* @param {String} attributePath The path to the attribute for predicate.
* @constructor
* @class GeographyPredicate
* @extends SpatialPredicate
*/
export class GeometryPredicate extends BasePredicate {
constructor(attributePath) {
super();

if (!attributePath) {
throw new Error('Attribute path is required for GeometryPredicate constructor.');
}

this._attributePath = attributePath;
this._intersectsValue = null;
export class GeographyPredicate extends SpatialPredicate {
/**
* The geography type namespace.
*
* @property spatialNamespace
* @type {String}
* @public
*/
get spatialNamespace() {
return 'geo';
}

/**
* The path to the attribute for predicate.
* The geography type.
*
* @property attributePath
* @property spatialType
* @type {String}
* @public
*/
get attributePath() {
return this._attributePath;
get spatialType() {
return 'geography';
}
}

/**
* The geometry value that has to intersect with the attribute.
/**
* The predicate class for geometry attributes.
*
* @namespace Query
* @class GeometryPredicate
* @extends SpatialPredicate
*/
export class GeometryPredicate extends SpatialPredicate { /**
* The geometry type namespace.
*
* @property intersectsValue
* @property spatialNamespace
* @type {String}
* @public
*/
get intersectsValue() {
return this._intersectsValue;
get spatialNamespace() {
return 'geom';
}

/**
* Sets the value that the attribute has to contain.
* The geometry type.
*
* @method contains
* @param {String} geometry The geometry value that has to intersect with the attribute.
* @return {Query.StringPredicate} Returns this instance.
* @chainable
* @property spatialType
* @type {String}
* @public
*/
intersects(geometry) {
this._intersectsValue = geometry;
return this;
get spatialType() {
return 'geometry';
}
}

Expand Down
Loading