maplibre-gl
Version:
BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
129 lines (109 loc) • 5.26 kB
text/typescript
import {type QueryIntersectsFeatureParams, StyleLayer} from '../style_layer';
import {LineBucket} from '../../data/bucket/line_bucket';
import {polygonIntersectsBufferedMultiLine} from '../../util/intersection_tests';
import {getMaximumPaintValue, translateDistance, translate, offsetLine} from '../query_utils';
import properties, {type LineLayoutPropsPossiblyEvaluated, type LinePaintPropsPossiblyEvaluated} from './line_style_layer_properties.g';
import {extend} from '../../util/util';
import {EvaluationParameters} from '../evaluation_parameters';
import {type Transitionable, type Transitioning, type Layout, type PossiblyEvaluated, DataDrivenProperty} from '../properties';
import {isZoomExpression, Step} from '@maplibre/maplibre-gl-style-spec';
import type {LayerSpecification} from '@maplibre/maplibre-gl-style-spec';
import type {Bucket, BucketParameters} from '../../data/bucket';
import type {LineLayoutProps, LinePaintProps} from './line_style_layer_properties.g';
export class LineFloorwidthProperty extends DataDrivenProperty<number> {
useIntegerZoom: true;
possiblyEvaluate(value, parameters) {
parameters = new EvaluationParameters(Math.floor(parameters.zoom), {
now: parameters.now,
fadeDuration: parameters.fadeDuration,
zoomHistory: parameters.zoomHistory,
transition: parameters.transition
});
return super.possiblyEvaluate(value, parameters);
}
evaluate(value, globals, feature, featureState) {
globals = extend({}, globals, {zoom: Math.floor(globals.zoom)});
return super.evaluate(value, globals, feature, featureState);
}
}
let lineFloorwidthProperty: LineFloorwidthProperty;
export const isLineStyleLayer = (layer: StyleLayer): layer is LineStyleLayer => layer.type === 'line';
export class LineStyleLayer extends StyleLayer {
_unevaluatedLayout: Layout<LineLayoutProps>;
layout: PossiblyEvaluated<LineLayoutProps, LineLayoutPropsPossiblyEvaluated>;
gradientVersion: number;
stepInterpolant: boolean;
_transitionablePaint: Transitionable<LinePaintProps>;
_transitioningPaint: Transitioning<LinePaintProps>;
paint: PossiblyEvaluated<LinePaintProps, LinePaintPropsPossiblyEvaluated>;
constructor(layer: LayerSpecification) {
super(layer, properties);
this.gradientVersion = 0;
if (!lineFloorwidthProperty) {
lineFloorwidthProperty =
new LineFloorwidthProperty(properties.paint.properties['line-width'].specification);
lineFloorwidthProperty.useIntegerZoom = true;
}
}
_handleSpecialPaintPropertyUpdate(name: string) {
if (name === 'line-gradient') {
const expression = this.gradientExpression();
if (isZoomExpression(expression)) {
this.stepInterpolant = expression._styleExpression.expression instanceof Step;
} else {
this.stepInterpolant = false;
}
this.gradientVersion = (this.gradientVersion + 1) % Number.MAX_SAFE_INTEGER;
}
}
gradientExpression() {
return this._transitionablePaint._values['line-gradient'].value.expression;
}
recalculate(parameters: EvaluationParameters, availableImages: Array<string>) {
super.recalculate(parameters, availableImages);
(this.paint._values as any)['line-floorwidth'] =
lineFloorwidthProperty.possiblyEvaluate(this._transitioningPaint._values['line-width'].value, parameters);
}
createBucket(parameters: BucketParameters<any>) {
return new LineBucket(parameters);
}
queryRadius(bucket: Bucket): number {
const lineBucket: LineBucket = (bucket as any);
const width = getLineWidth(
getMaximumPaintValue('line-width', this, lineBucket),
getMaximumPaintValue('line-gap-width', this, lineBucket));
const offset = getMaximumPaintValue('line-offset', this, lineBucket);
return width / 2 + Math.abs(offset) + translateDistance(this.paint.get('line-translate'));
}
queryIntersectsFeature({
queryGeometry,
feature,
featureState,
geometry,
transform,
pixelsToTileUnits}: QueryIntersectsFeatureParams
): boolean {
const translatedPolygon = translate(queryGeometry,
this.paint.get('line-translate'),
this.paint.get('line-translate-anchor'),
-transform.bearingInRadians, pixelsToTileUnits);
const halfWidth = pixelsToTileUnits / 2 * getLineWidth(
this.paint.get('line-width').evaluate(feature, featureState),
this.paint.get('line-gap-width').evaluate(feature, featureState));
const lineOffset = this.paint.get('line-offset').evaluate(feature, featureState);
if (lineOffset) {
geometry = offsetLine(geometry, lineOffset * pixelsToTileUnits);
}
return polygonIntersectsBufferedMultiLine(translatedPolygon, geometry, halfWidth);
}
isTileClipped() {
return true;
}
}
function getLineWidth(lineWidth, lineGapWidth) {
if (lineGapWidth > 0) {
return lineGapWidth + 2 * lineWidth;
} else {
return lineWidth;
}
}