1 | import Point from '@mapbox/point-geometry';
|
2 |
|
3 | import StyleLayer from '../style_layer';
|
4 | import LineBucket from '../../data/bucket/line_bucket';
|
5 | import {polygonIntersectsBufferedMultiLine} from '../../util/intersection_tests';
|
6 | import {getMaximumPaintValue, translateDistance, translate, offsetLine} from '../query_utils';
|
7 | import properties, {LineLayoutPropsPossiblyEvaluated, LinePaintPropsPossiblyEvaluated} from './line_style_layer_properties.g';
|
8 | import {extend} from '../../util/util';
|
9 | import EvaluationParameters from '../evaluation_parameters';
|
10 | import {Transitionable, Transitioning, Layout, PossiblyEvaluated, DataDrivenProperty} from '../properties';
|
11 |
|
12 | import Step from '../../style-spec/expression/definitions/step';
|
13 | import type {FeatureState, ZoomConstantExpression} from '../../style-spec/expression';
|
14 | import type {Bucket, BucketParameters} from '../../data/bucket';
|
15 | import type {LineLayoutProps, LinePaintProps} from './line_style_layer_properties.g';
|
16 | import type Transform from '../../geo/transform';
|
17 | import type {LayerSpecification} from '../../style-spec/types.g';
|
18 | import type {VectorTileFeature} from '@mapbox/vector-tile';
|
19 |
|
20 | class LineFloorwidthProperty extends DataDrivenProperty<number> {
|
21 | useIntegerZoom: true;
|
22 |
|
23 | possiblyEvaluate(value, parameters) {
|
24 | parameters = new EvaluationParameters(Math.floor(parameters.zoom), {
|
25 | now: parameters.now,
|
26 | fadeDuration: parameters.fadeDuration,
|
27 | zoomHistory: parameters.zoomHistory,
|
28 | transition: parameters.transition
|
29 | });
|
30 | return super.possiblyEvaluate(value, parameters);
|
31 | }
|
32 |
|
33 | evaluate(value, globals, feature, featureState) {
|
34 | globals = extend({}, globals, {zoom: Math.floor(globals.zoom)});
|
35 | return super.evaluate(value, globals, feature, featureState);
|
36 | }
|
37 | }
|
38 |
|
39 | const lineFloorwidthProperty = new LineFloorwidthProperty(properties.paint.properties['line-width'].specification);
|
40 | lineFloorwidthProperty.useIntegerZoom = true;
|
41 |
|
42 | class LineStyleLayer extends StyleLayer {
|
43 | _unevaluatedLayout: Layout<LineLayoutProps>;
|
44 | layout: PossiblyEvaluated<LineLayoutProps, LineLayoutPropsPossiblyEvaluated>;
|
45 |
|
46 | gradientVersion: number;
|
47 | stepInterpolant: boolean;
|
48 |
|
49 | _transitionablePaint: Transitionable<LinePaintProps>;
|
50 | _transitioningPaint: Transitioning<LinePaintProps>;
|
51 | paint: PossiblyEvaluated<LinePaintProps, LinePaintPropsPossiblyEvaluated>;
|
52 |
|
53 | constructor(layer: LayerSpecification) {
|
54 | super(layer, properties);
|
55 | this.gradientVersion = 0;
|
56 | }
|
57 |
|
58 | _handleSpecialPaintPropertyUpdate(name: string) {
|
59 | if (name === 'line-gradient') {
|
60 | const expression: ZoomConstantExpression<'source'> = (this._transitionablePaint._values['line-gradient'].value.expression as any);
|
61 | this.stepInterpolant = expression._styleExpression.expression instanceof Step;
|
62 | this.gradientVersion = (this.gradientVersion + 1) % Number.MAX_SAFE_INTEGER;
|
63 | }
|
64 | }
|
65 |
|
66 | gradientExpression() {
|
67 | return this._transitionablePaint._values['line-gradient'].value.expression;
|
68 | }
|
69 |
|
70 | recalculate(parameters: EvaluationParameters, availableImages: Array<string>) {
|
71 | super.recalculate(parameters, availableImages);
|
72 |
|
73 | (this.paint._values as any)['line-floorwidth'] =
|
74 | lineFloorwidthProperty.possiblyEvaluate(this._transitioningPaint._values['line-width'].value, parameters);
|
75 | }
|
76 |
|
77 | createBucket(parameters: BucketParameters<any>) {
|
78 | return new LineBucket(parameters);
|
79 | }
|
80 |
|
81 | queryRadius(bucket: Bucket): number {
|
82 | const lineBucket: LineBucket = (bucket as any);
|
83 | const width = getLineWidth(
|
84 | getMaximumPaintValue('line-width', this, lineBucket),
|
85 | getMaximumPaintValue('line-gap-width', this, lineBucket));
|
86 | const offset = getMaximumPaintValue('line-offset', this, lineBucket);
|
87 | return width / 2 + Math.abs(offset) + translateDistance(this.paint.get('line-translate'));
|
88 | }
|
89 |
|
90 | queryIntersectsFeature(
|
91 | queryGeometry: Array<Point>,
|
92 | feature: VectorTileFeature,
|
93 | featureState: FeatureState,
|
94 | geometry: Array<Array<Point>>,
|
95 | zoom: number,
|
96 | transform: Transform,
|
97 | pixelsToTileUnits: number
|
98 | ): boolean {
|
99 | const translatedPolygon = translate(queryGeometry,
|
100 | this.paint.get('line-translate'),
|
101 | this.paint.get('line-translate-anchor'),
|
102 | transform.angle, pixelsToTileUnits);
|
103 | const halfWidth = pixelsToTileUnits / 2 * getLineWidth(
|
104 | this.paint.get('line-width').evaluate(feature, featureState),
|
105 | this.paint.get('line-gap-width').evaluate(feature, featureState));
|
106 | const lineOffset = this.paint.get('line-offset').evaluate(feature, featureState);
|
107 | if (lineOffset) {
|
108 | geometry = offsetLine(geometry, lineOffset * pixelsToTileUnits);
|
109 | }
|
110 |
|
111 | return polygonIntersectsBufferedMultiLine(translatedPolygon, geometry, halfWidth);
|
112 | }
|
113 |
|
114 | isTileClipped() {
|
115 | return true;
|
116 | }
|
117 | }
|
118 |
|
119 | export default LineStyleLayer;
|
120 |
|
121 | function getLineWidth(lineWidth, lineGapWidth) {
|
122 | if (lineGapWidth > 0) {
|
123 | return lineGapWidth + 2 * lineWidth;
|
124 | } else {
|
125 | return lineWidth;
|
126 | }
|
127 | }
|