• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

keplergl / kepler.gl / 25205206405

01 May 2026 06:33AM UTC coverage: 59.129% (-0.05%) from 59.174%
25205206405

Pull #3402

github

web-flow
Merge f1b2ba04e into a9ca4a80c
Pull Request #3402: feat: Streamlined rectangle drag-to-filter for map layers

6926 of 14072 branches covered (49.22%)

Branch coverage included in aggregate %.

6 of 27 new or added lines in 6 files covered. (22.22%)

166 existing lines in 6 files now uncovered.

14279 of 21790 relevant lines covered (65.53%)

79.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

13.43
/src/layers/src/editor-layer/editor-layer.ts
1
// SPDX-License-Identifier: MIT
2
// Copyright contributors to the kepler.gl project
3

4
import {EditableGeoJsonLayer} from '@deck.gl-community/editable-layers';
5
import {Layer as DeckLayer, LayerProps as DeckLayerProps} from '@deck.gl/core';
6
import {
7
  DrawPolygonMode,
8
  TranslateMode,
9
  CompositeMode,
10
  GeoJsonEditMode
11
} from '@deck.gl-community/editable-layers';
12
import {PathStyleExtension} from '@deck.gl/extensions';
13

14
import {EDITOR_LAYER_ID, EDITOR_MODES, EDITOR_LAYER_PICKING_RADIUS} from '@kepler.gl/constants';
15
import {Viewport, Editor, Feature, FeatureSelectionContext} from '@kepler.gl/types';
16
import {generateHashId} from '@kepler.gl/common-utils';
17

18
import {EDIT_TYPES} from './constants';
19
import {LINE_STYLE, FEATURE_STYLE, EDIT_HANDLE_STYLE} from './feature-styles';
20
import {ModifyModeExtended} from './modify-mode-extended';
21
import {DrawRectangleModeExtended} from './draw-rectangle-mode-extended';
22
import {isDrawingActive} from './editor-layer-utils';
23

24
const DEFAULT_COMPOSITE_MODE = new CompositeMode([
13✔
25
  new TranslateMode() as unknown as GeoJsonEditMode,
26
  new ModifyModeExtended() as unknown as GeoJsonEditMode
27
]);
28

29
export type GetEditorLayerProps = {
30
  editorMenuActive: boolean;
31
  editor: Editor;
32
  onSetFeatures: (features: Feature[]) => any;
33
  setSelectedFeature: (feature: Feature | null, selectionContext?: FeatureSelectionContext) => any;
34
  onApplyPolygonFilterAll?: (feature: Feature) => any;
35
  viewport: Viewport;
36
  featureCollection: {
37
    type: string;
38
    features: Feature[];
39
  };
40
  selectedFeatureIndexes: number[];
41
};
42

43
/**
44
 * Returns editable layer to edit polygon filters.
45
 * @param params
46
 * @param params.editorMenuActive Indicates whether the editor side menu is active.
47
 * @param params.editor
48
 * @param params.onSetFeatures A callback to set features.
49
 * @param params.setSelectedFeature A callback to set selected feature and selection context.
50
 * @param params.viewport Current viewport.
51
 * @param params.featureCollection Feature collection with an array of features
52
 * @param params.selectedFeatureIndexes An array with index of currently selected feature.
53
 */
54
export function getEditorLayer({
55
  editorMenuActive,
56
  editor,
57
  onSetFeatures,
58
  setSelectedFeature,
59
  onApplyPolygonFilterAll,
60
  featureCollection,
61
  selectedFeatureIndexes,
62
  viewport
63
}: GetEditorLayerProps): DeckLayer<DeckLayerProps> {
64
  const {mode: editorMode} = editor;
30✔
65

66
  let mode = DEFAULT_COMPOSITE_MODE;
30✔
67
  if (editorMenuActive) {
30✔
68
    // @ts-ignore
69
    if (editorMode === EDITOR_MODES.DRAW_POLYGON) mode = DrawPolygonMode;
1!
70
    // @ts-ignore
NEW
71
    else if (editorMode === EDITOR_MODES.DRAW_RECTANGLE) mode = DrawRectangleModeExtended;
×
72
  }
73

74
  // @ts-ignore
75
  return new EditableGeoJsonLayer({
30✔
76
    id: EDITOR_LAYER_ID,
77
    mode,
78
    // @ts-ignore
79
    data: featureCollection,
80
    selectedFeatureIndexes,
81
    visible: editor.visible,
82
    pickable: true,
83
    pickingRadius: EDITOR_LAYER_PICKING_RADIUS,
84
    modeConfig: {
85
      viewport,
86
      screenSpace: true,
87
      lockRectangles: true
88
    },
89

90
    pickingLineWidthExtraPixels: 5,
91

92
    // Only show fill when polygons are selected,
93
    // there is no way atm to enable fill for only one feature
94
    filled: selectedFeatureIndexes.length > 0,
95

96
    onEdit: ({updatedData, editType}) => {
97
      switch (editType) {
×
98
        case EDIT_TYPES.ADD_FEATURE: {
99
          const {features: _features} = updatedData;
×
100
          if (_features.length) {
×
101
            const lastFeature = _features[_features.length - 1];
×
102
            if (lastFeature.properties) lastFeature.properties.isClosed = true;
×
103
            lastFeature.id = generateHashId(6);
×
104
            onSetFeatures(updatedData.features as unknown as Feature[]);
×
105

NEW
106
            const isRectangle = lastFeature.properties?.shape === 'Rectangle';
×
NEW
107
            if (isRectangle && onApplyPolygonFilterAll) {
×
NEW
108
              onApplyPolygonFilterAll(lastFeature as unknown as Feature);
×
109
            } else {
NEW
110
              setSelectedFeature(lastFeature as unknown as Feature);
×
111
            }
112
          }
113
          break;
×
114
        }
115
        case EDIT_TYPES.ADD_POSITION:
116
        case EDIT_TYPES.MOVE_POSITION:
117
        case EDIT_TYPES.TRANSLATING:
118
          onSetFeatures(updatedData.features as unknown as Feature[]);
×
119
          break;
×
120
        default:
121
          break;
×
122
      }
123
    },
124

125
    // prevent self-highlights with tentative features
126
    autoHighlight: !isDrawingActive(editorMenuActive, editorMode),
127
    // @ts-ignore
128
    highlightColor: info => {
129
      // Note: lines are reported as parent polygon
130
      const {object} = info;
×
131
      if (object) {
×
132
        if (object.id === editor.selectedFeature?.id) {
×
133
          return FEATURE_STYLE.highlightMultiplierNone;
×
134
        }
135

136
        const type = object.properties.editHandleType;
×
137
        if (type === 'intermediate') return EDIT_HANDLE_STYLE.highlightMultiplierNone;
×
138
        else if (type === 'existing') return EDIT_HANDLE_STYLE.highlightMultiplier;
×
139
      }
140

141
      // Note: highlight color affects even transparent filled polygons
142
      return selectedFeatureIndexes.length
×
143
        ? FEATURE_STYLE.highlightMultiplier
144
        : LINE_STYLE.highlightMultiplier;
145
    },
146

147
    extensions: [new PathStyleExtension({dash: true})],
148
    dashGapPickable: true,
149
    getDashArray: feature => {
150
      if (feature?.properties?.guideType === 'tentative') {
×
151
        return LINE_STYLE.dashArray;
×
152
      }
153

154
      if (feature?.id === editor.selectedFeature?.id) return LINE_STYLE.solidArray;
×
155

156
      return LINE_STYLE.dashArray;
×
157
    },
158

159
    getLineColor: LINE_STYLE.getColor,
160
    getFillColor: FEATURE_STYLE.getColor,
161

162
    getRadius: EDIT_HANDLE_STYLE.getRadius,
163
    // @ts-ignore
164
    getLineWidth: LINE_STYLE.getWidth,
165

166
    getEditHandlePointRadius: EDIT_HANDLE_STYLE.getRadius,
167
    getEditHandlePointColor: EDIT_HANDLE_STYLE.getFillColor,
168
    getEditHandlePointOutlineColor: EDIT_HANDLE_STYLE.getOutlineColor,
169

170
    getTentativeLineColor: LINE_STYLE.getTentativeLineColor,
171
    // @ts-ignore
172
    getTentativeLineWidth: LINE_STYLE.getTentativeLineWidth,
173
    getTentativeFillColor: LINE_STYLE.getTentativeFillColor,
174

175
    parameters: {},
176
    shadowEnabled: false,
177
    _subLayerProps: {
178
      geojson: {shadowEnabled: false},
179
      guides: {shadowEnabled: false},
180
      tooltips: {
181
        shadowEnabled: false,
182
        _subLayerProps: {
183
          characters: {shadowEnabled: false}
184
        }
185
      }
186
    }
187
  });
188
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc