Skip to content

Commit

Permalink
Implement geometry-type operator in cpu
Browse files Browse the repository at this point in the history
  • Loading branch information
jahow committed Dec 10, 2023
1 parent a6d007f commit c949660
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 44 deletions.
8 changes: 6 additions & 2 deletions src/ol/expr/cpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
* @property {Object} variables The values for variables used in 'var' expressions.
* @property {number} resolution The map resolution.
* @property {string|number|null} featureId The feature id.
* @property {string} geometryType Geometry type of the current object.
*/

/**
Expand All @@ -43,6 +44,7 @@ export function newEvaluationContext() {
properties: {},
resolution: NaN,
featureId: null,
geometryType: '',
};
}

Expand Down Expand Up @@ -129,7 +131,10 @@ function compileExpression(expression, context) {
return compileAccessorExpression(expression, context);
}
case Ops.Id: {
return (expression) => expression.featureId;
return (context) => context.featureId;
}
case Ops.GeometryType: {
return (context) => context.geometryType;
}
case Ops.Concat: {
const args = expression.args.map((e) => compileExpression(e, context));
Expand Down Expand Up @@ -182,7 +187,6 @@ function compileExpression(expression, context) {
throw new Error(`Unsupported operator ${operator}`);
}
// TODO: unimplemented
// Ops.GeometryType
// Ops.Zoom
// Ops.Time
// Ops.Between
Expand Down
43 changes: 42 additions & 1 deletion src/ol/expr/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ export class CallExpression {
* @property {Set<string>} variables Variables referenced with the 'var' operator.
* @property {Set<string>} properties Properties referenced with the 'get' operator.
* @property {boolean} featureId The style uses the feature id.
* @property {boolean} geometryType The style uses the feature geometry type.
* @property {import("../style/flat.js").FlatStyle|import("../style/webgl.js").WebGLStyle} style The style being parsed
*/

Expand All @@ -230,6 +231,7 @@ export function newParsingContext() {
variables: new Set(),
properties: new Set(),
featureId: false,
geometryType: false,
style: {},
};
}
Expand Down Expand Up @@ -400,7 +402,7 @@ const parsers = {
withArgsCount(2, Infinity),
parseArgsOfType(AnyType)
),
[Ops.GeometryType]: createParser(StringType, withNoArgs),
[Ops.GeometryType]: createParser(StringType, withNoArgs, usesGeometryType),
[Ops.Resolution]: createParser(NumberType, withNoArgs),
[Ops.Zoom]: createParser(NumberType, withNoArgs),
[Ops.Time]: createParser(NumberType, withNoArgs),
Expand Down Expand Up @@ -675,6 +677,13 @@ function usesFeatureId(encoded, context) {
context.featureId = true;
}

/**
* @type ArgValidator
*/
function usesGeometryType(encoded, context) {
context.geometryType = true;
}

/**
* @type ArgValidator
*/
Expand Down Expand Up @@ -1097,3 +1106,35 @@ function parseCallExpression(encoded, context, typeHint) {
}
return parser(encoded, context, typeHint);
}

/**
* Returns a simplified geometry type suited for the `geometry-type` operator
* @param {import('../geom/Geometry.js').default|import('../render/Feature.js').default} geometry Geometry object
* @return {'Point'|'LineString'|'Polygon'|''} Simplified geometry type; empty string of no geometry found
*/
export function computeGeometryType(geometry) {
if (!geometry) {
return '';
}
const type = geometry.getType();
switch (type) {
case 'Point':
case 'LineString':
case 'Polygon':
return type;
case 'MultiPoint':
case 'MultiLineString':
case 'MultiPolygon':
return /** @type {'Point'|'LineString'|'Polygon'} */ (type.substring(5));
case 'Circle':
return 'Polygon';
case 'GeometryCollection':
return computeGeometryType(
/** @type {import("../geom/GeometryCollection.js").default} */ (
geometry
).getGeometries()[0]
);
default:
return '';
}
}
21 changes: 2 additions & 19 deletions src/ol/expr/gpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
NumberType,
Ops,
StringType,
computeGeometryType,
isType,
overlapsType,
parse,
Expand Down Expand Up @@ -232,31 +233,13 @@ const compilers = {
},
[Ops.GeometryType]: (context, expression, type) => {
const propName = 'geometryType';
const computeType = (geometry) => {
const type = geometry.getType();
switch (type) {
case 'Point':
case 'LineString':
case 'Polygon':
return type;
case 'MultiPoint':
case 'MultiLineString':
case 'MultiPolygon':
return type.substring(5);
case 'Circle':
return 'Polygon';
case 'GeometryCollection':
return computeType(geometry.getGeometries()[0]);
default:
}
};
const isExisting = propName in context.properties;
if (!isExisting) {
context.properties[propName] = {
name: propName,
type: StringType,
evaluator: (feature) => {
return computeType(feature.getGeometry());
return computeGeometryType(feature.getGeometry());
},
};
}
Expand Down
6 changes: 6 additions & 0 deletions src/ol/render/canvas/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
NumberArrayType,
NumberType,
StringType,
computeGeometryType,
newParsingContext,
} from '../../expr/expression.js';
import {buildExpression, newEvaluationContext} from '../../expr/cpu.js';
Expand Down Expand Up @@ -84,6 +85,11 @@ export function rulesToStyleFunction(rules) {
evaluationContext.featureId = null;
}
}
if (parsingContext.geometryType) {
evaluationContext.geometryType = computeGeometryType(
feature.getGeometry()
);
}
return evaluator(evaluationContext);
};
}
Expand Down
18 changes: 18 additions & 0 deletions test/node/ol/expr/cpu.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,24 @@ describe('ol/expr/cpu.js', () => {
},
expected: 'forty-two',
},
{
name: 'geometry-type',
type: StringType,
expression: ['geometry-type'],
context: {
geometryType: 'LineString',
},
expected: 'LineString',
},
{
name: 'geometry-type (empty)',
type: StringType,
expression: ['geometry-type'],
context: {
geometryType: '',
},
expected: '',
},
{
name: 'resolution',
type: NumberType,
Expand Down
34 changes: 34 additions & 0 deletions test/node/ol/expr/expression.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@ import {
NumberArrayType,
NumberType,
StringType,
computeGeometryType,
includesType,
isType,
newParsingContext,
parse,
typeName,
} from '../../../../src/ol/expr/expression.js';
import {
Circle,
GeometryCollection,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
} from '../../../../src/ol/geom.js';

describe('ol/expr/expression.js', () => {
describe('parse()', () => {
Expand Down Expand Up @@ -624,4 +633,29 @@ describe('ol/expr/expression.js', () => {
expect(isType(AnyType, NumberArrayType)).to.be(false);
});
});
describe('computeGeometryType', () => {
it('returns empty string for falsy geom', () => {
expect(computeGeometryType(undefined)).to.eql('');
});
it('returns Point for Point geom', () => {
expect(computeGeometryType(new Point([0, 1]))).to.eql('Point');
});
it('returns Polygon for MultiPolygon geom', () => {
expect(computeGeometryType(new MultiPolygon([]))).to.eql('Polygon');
});
it('returns LineString for MultiLineString geom', () => {
expect(computeGeometryType(new MultiLineString([]))).to.eql('LineString');
});
it('returns first geom type in geometry collection', () => {
expect(
computeGeometryType(new GeometryCollection([new Circle([0, 1])]))
).to.eql('Polygon');
expect(
computeGeometryType(new GeometryCollection([new MultiPoint([])]))
).to.eql('Point');
});
it('returns empty string for empty geom collection', () => {
expect(computeGeometryType(new GeometryCollection([]))).to.eql('');
});
});
});
25 changes: 3 additions & 22 deletions test/node/ol/expr/gpu.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,7 @@ import {
StringType,
newParsingContext,
} from '../../../../src/ol/expr/expression.js';
import {
Circle,
GeometryCollection,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
} from '../../../../src/ol/geom.js';
import {MultiPolygon} from '../../../../src/ol/geom.js';
import {
arrayToGlsl,
buildExpression,
Expand Down Expand Up @@ -171,20 +164,8 @@ describe('ol/expr/gpu.js', () => {
expect(prop.name).to.equal('geometryType');
expect(prop.type).to.equal(StringType);
expect(prop.evaluator).to.be.an(Function);
const results = [
new Feature(new Point([0, 1])),
new Feature(new MultiPolygon([])),
new Feature(new MultiLineString([])),
new Feature(new GeometryCollection([new Circle([0, 1])])),
new Feature(new GeometryCollection([new MultiPoint([])])),
].map(prop.evaluator);
expect(results).to.eql([
'Point',
'Polygon',
'LineString',
'Polygon',
'Point',
]);
const feature = new Feature(new MultiPolygon([]));
expect(prop.evaluator(feature)).to.eql('Polygon');
},
},
{
Expand Down

0 comments on commit c949660

Please sign in to comment.