@deck.gl-community/editable-layers 9.2.0-beta.5 → 9.2.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2938,48 +2938,53 @@ var DrawLineStringMode = class extends GeoJsonEditMode {
2938
2938
  // dist/edit-modes/draw-polygon-mode.js
2939
2939
  var import_line_intersect2 = __toESM(require("@turf/line-intersect"), 1);
2940
2940
  var import_helpers9 = require("@turf/helpers");
2941
+ var import_boolean_within = __toESM(require("@turf/boolean-within"), 1);
2941
2942
  var DrawPolygonMode = class extends GeoJsonEditMode {
2943
+ holeSequence = [];
2944
+ isDrawingHole = false;
2942
2945
  createTentativeFeature(props) {
2943
2946
  const { lastPointerMoveEvent } = props;
2944
2947
  const clickSequence = this.getClickSequence();
2948
+ const holeSequence = this.holeSequence;
2945
2949
  const lastCoords = lastPointerMoveEvent ? [lastPointerMoveEvent.mapCoords] : [];
2946
- let tentativeFeature;
2947
- if (clickSequence.length === 1 || clickSequence.length === 2) {
2948
- tentativeFeature = {
2949
- type: "Feature",
2950
- properties: {
2951
- guideType: "tentative"
2952
- },
2953
- geometry: {
2954
- type: "LineString",
2955
- coordinates: [...clickSequence, ...lastCoords]
2956
- }
2950
+ let geometry;
2951
+ if (this.isDrawingHole && holeSequence.length > 1) {
2952
+ geometry = {
2953
+ type: "Polygon",
2954
+ coordinates: [
2955
+ [...clickSequence, clickSequence[0]],
2956
+ [...holeSequence, ...lastCoords, holeSequence[0]]
2957
+ ]
2957
2958
  };
2958
2959
  } else if (clickSequence.length > 2) {
2959
- tentativeFeature = {
2960
- type: "Feature",
2961
- properties: {
2962
- guideType: "tentative"
2963
- },
2964
- geometry: {
2965
- type: "Polygon",
2966
- coordinates: [[...clickSequence, ...lastCoords, clickSequence[0]]]
2967
- }
2960
+ geometry = {
2961
+ type: "Polygon",
2962
+ coordinates: [[...clickSequence, ...lastCoords, clickSequence[0]]]
2963
+ };
2964
+ } else {
2965
+ geometry = {
2966
+ type: "LineString",
2967
+ coordinates: [...clickSequence, ...lastCoords]
2968
2968
  };
2969
2969
  }
2970
- return tentativeFeature;
2970
+ return {
2971
+ type: "Feature",
2972
+ properties: {
2973
+ guideType: "tentative"
2974
+ },
2975
+ geometry
2976
+ };
2971
2977
  }
2972
2978
  getGuides(props) {
2973
- const clickSequence = this.getClickSequence();
2974
2979
  const guides = {
2975
2980
  type: "FeatureCollection",
2976
2981
  features: []
2977
2982
  };
2978
- const tentativeFeature = this.createTentativeFeature(props);
2979
- if (tentativeFeature) {
2980
- guides.features.push(tentativeFeature);
2981
- }
2982
- const editHandles = clickSequence.map((clickedCoord, index) => ({
2983
+ const tentative = this.createTentativeFeature(props);
2984
+ if (tentative)
2985
+ guides.features.push(tentative);
2986
+ const sequence = this.isDrawingHole ? this.holeSequence : this.getClickSequence();
2987
+ const handles = sequence.map((coord, index) => ({
2983
2988
  type: "Feature",
2984
2989
  properties: {
2985
2990
  guideType: "editHandle",
@@ -2989,51 +2994,55 @@ var DrawPolygonMode = class extends GeoJsonEditMode {
2989
2994
  },
2990
2995
  geometry: {
2991
2996
  type: "Point",
2992
- coordinates: clickedCoord
2997
+ coordinates: coord
2993
2998
  }
2994
2999
  }));
2995
- guides.features.push(...editHandles);
3000
+ guides.features.push(...handles);
2996
3001
  return guides;
2997
3002
  }
2998
- finishDrawing(props) {
2999
- const clickSequence = this.getClickSequence();
3000
- if (clickSequence.length > 2) {
3001
- const polygonToAdd = {
3002
- type: "Polygon",
3003
- coordinates: [[...clickSequence, clickSequence[0]]]
3004
- };
3005
- this.resetClickSequence();
3006
- const editAction = this.getAddFeatureOrBooleanPolygonAction(polygonToAdd, props);
3007
- if (editAction) {
3008
- props.onEdit(editAction);
3009
- }
3010
- }
3011
- }
3012
- // eslint-disable-next-line complexity
3003
+ // eslint-disable-next-line complexity, max-statements
3013
3004
  handleClick(event, props) {
3014
3005
  const { picks } = event;
3015
3006
  const clickedEditHandle = getPickedEditHandle(picks);
3016
3007
  const clickSequence = this.getClickSequence();
3017
- let overlappingLines = false;
3018
- if (clickSequence.length > 2 && props.modeConfig && props.modeConfig.preventOverlappingLines) {
3019
- const currentLine = (0, import_helpers9.lineString)([
3020
- clickSequence[clickSequence.length - 1],
3021
- event.mapCoords
3022
- ]);
3023
- const otherLines = (0, import_helpers9.lineString)([...clickSequence.slice(0, clickSequence.length - 1)]);
3024
- const intersectingPoints = (0, import_line_intersect2.default)(currentLine, otherLines);
3025
- if (intersectingPoints.features.length > 0) {
3026
- overlappingLines = true;
3008
+ const coords = event.mapCoords;
3009
+ if (!this.isDrawingHole && clickSequence.length > 2 && clickedEditHandle && Array.isArray(clickedEditHandle.properties.positionIndexes) && (clickedEditHandle.properties.positionIndexes[0] === 0 || clickedEditHandle.properties.positionIndexes[0] === clickSequence.length - 1)) {
3010
+ this.finishDrawing(props);
3011
+ return;
3012
+ }
3013
+ if (!this.isDrawingHole && clickSequence.length > 2) {
3014
+ if (isNearFirstPoint(coords, clickSequence[0])) {
3015
+ this.finishDrawing(props);
3016
+ this.resetClickSequence();
3017
+ return;
3027
3018
  }
3028
3019
  }
3020
+ if (this.isDrawingHole) {
3021
+ const current = this.holeSequence;
3022
+ current.push(coords);
3023
+ if (current.length > 2) {
3024
+ const poly = {
3025
+ type: "Polygon",
3026
+ coordinates: [
3027
+ [...clickSequence, clickSequence[0]],
3028
+ [...current, current[0]]
3029
+ ]
3030
+ };
3031
+ this.resetClickSequence();
3032
+ this.holeSequence = [];
3033
+ this.isDrawingHole = false;
3034
+ const editAction = this.getAddFeatureOrBooleanPolygonAction(poly, props);
3035
+ if (editAction)
3036
+ props.onEdit(editAction);
3037
+ }
3038
+ return;
3039
+ }
3029
3040
  let positionAdded = false;
3030
- if (!clickedEditHandle && !overlappingLines) {
3041
+ if (!clickedEditHandle) {
3031
3042
  this.addClickSequence(event);
3032
3043
  positionAdded = true;
3033
3044
  }
3034
- if (clickSequence.length > 2 && clickedEditHandle && Array.isArray(clickedEditHandle.properties.positionIndexes) && (clickedEditHandle.properties.positionIndexes[0] === 0 || clickedEditHandle.properties.positionIndexes[0] === clickSequence.length - 1)) {
3035
- this.finishDrawing(props);
3036
- } else if (positionAdded) {
3045
+ if (positionAdded) {
3037
3046
  props.onEdit({
3038
3047
  // data is the same
3039
3048
  updatedData: props.data,
@@ -3044,16 +3053,19 @@ var DrawPolygonMode = class extends GeoJsonEditMode {
3044
3053
  });
3045
3054
  }
3046
3055
  }
3047
- handleDoubleClick(event, props) {
3056
+ handleDoubleClick(_event, props) {
3048
3057
  this.finishDrawing(props);
3058
+ this.resetClickSequence();
3049
3059
  }
3050
3060
  handleKeyUp(event, props) {
3051
3061
  if (event.key === "Enter") {
3052
3062
  this.finishDrawing(props);
3063
+ this.resetClickSequence();
3053
3064
  } else if (event.key === "Escape") {
3054
3065
  this.resetClickSequence();
3066
+ this.holeSequence = [];
3067
+ this.isDrawingHole = false;
3055
3068
  props.onEdit({
3056
- // Because the new drawing feature is dropped, so the data will keep as the same.
3057
3069
  updatedData: props.data,
3058
3070
  editType: "cancelFeature",
3059
3071
  editContext: {}
@@ -3064,7 +3076,119 @@ var DrawPolygonMode = class extends GeoJsonEditMode {
3064
3076
  props.onUpdateCursor("cell");
3065
3077
  super.handlePointerMove(event, props);
3066
3078
  }
3079
+ // eslint-disable-next-line max-statements, complexity
3080
+ finishDrawing(props) {
3081
+ const clickSequence = this.getClickSequence();
3082
+ const polygon3 = [...clickSequence, clickSequence[0]];
3083
+ const newPolygon = (0, import_helpers9.polygon)([polygon3]);
3084
+ const canAddHole = canAddHoleToPolygon(props);
3085
+ const canOverlap = canPolygonOverlap(props);
3086
+ if (!canOverlap) {
3087
+ const overlapping = (0, import_line_intersect2.default)(newPolygon, newPolygon).features.filter((intersection) => !newPolygon.geometry.coordinates[0].some((coord) => coord[0] === intersection.geometry.coordinates[0] && coord[1] === intersection.geometry.coordinates[1]));
3088
+ if (overlapping.length > 0) {
3089
+ props.onEdit({
3090
+ updatedData: props.data,
3091
+ editType: "invalidPolygon",
3092
+ editContext: { reason: "overlaps" }
3093
+ });
3094
+ this.resetClickSequence();
3095
+ return;
3096
+ }
3097
+ }
3098
+ if (canAddHole) {
3099
+ const holeResult = this.tryAddHoleToExistingPolygon(newPolygon, polygon3, props);
3100
+ if (holeResult.handled) {
3101
+ this.resetClickSequence();
3102
+ return;
3103
+ }
3104
+ }
3105
+ const editAction = this.getAddFeatureOrBooleanPolygonAction({
3106
+ type: "Polygon",
3107
+ coordinates: [[...this.getClickSequence(), this.getClickSequence()[0]]]
3108
+ }, props);
3109
+ if (editAction)
3110
+ props.onEdit(editAction);
3111
+ this.resetClickSequence();
3112
+ return;
3113
+ }
3114
+ tryAddHoleToExistingPolygon(newPolygon, polygon3, props) {
3115
+ for (const [featureIndex, feature] of props.data.features.entries()) {
3116
+ if (feature.geometry.type === "Polygon") {
3117
+ const result = this.validateAndCreateHole(feature, featureIndex, newPolygon, polygon3, props);
3118
+ if (result.handled) {
3119
+ return result;
3120
+ }
3121
+ }
3122
+ }
3123
+ return { handled: false };
3124
+ }
3125
+ validateAndCreateHole(feature, featureIndex, newPolygon, polygon3, props) {
3126
+ const outer = (0, import_helpers9.polygon)(feature.geometry.coordinates);
3127
+ for (let i = 1; i < feature.geometry.coordinates.length; i++) {
3128
+ const hole = (0, import_helpers9.polygon)([feature.geometry.coordinates[i]]);
3129
+ const intersection = (0, import_line_intersect2.default)(hole, newPolygon);
3130
+ if (intersection.features.length > 0) {
3131
+ props.onEdit({
3132
+ updatedData: props.data,
3133
+ editType: "invalidHole",
3134
+ editContext: { reason: "intersects-existing-hole" }
3135
+ });
3136
+ return { handled: true };
3137
+ }
3138
+ if ((0, import_boolean_within.default)(hole, newPolygon) || (0, import_boolean_within.default)(newPolygon, hole)) {
3139
+ props.onEdit({
3140
+ updatedData: props.data,
3141
+ editType: "invalidHole",
3142
+ editContext: { reason: "contains-or-contained-by-existing-hole" }
3143
+ });
3144
+ return { handled: true };
3145
+ }
3146
+ }
3147
+ const intersectionWithOuter = (0, import_line_intersect2.default)(outer, newPolygon);
3148
+ if (intersectionWithOuter.features.length > 0) {
3149
+ props.onEdit({
3150
+ updatedData: props.data,
3151
+ editType: "invalidPolygon",
3152
+ editContext: { reason: "intersects-existing-polygon" }
3153
+ });
3154
+ return { handled: true };
3155
+ }
3156
+ if ((0, import_boolean_within.default)(outer, newPolygon)) {
3157
+ props.onEdit({
3158
+ updatedData: props.data,
3159
+ editType: "invalidPolygon",
3160
+ editContext: { reason: "contains-existing-polygon" }
3161
+ });
3162
+ return { handled: true };
3163
+ }
3164
+ if ((0, import_boolean_within.default)(newPolygon, outer)) {
3165
+ const updatedData = new ImmutableFeatureCollection(props.data).replaceGeometry(featureIndex, {
3166
+ ...feature.geometry,
3167
+ coordinates: [...feature.geometry.coordinates, polygon3]
3168
+ }).getObject();
3169
+ props.onEdit({
3170
+ updatedData,
3171
+ editType: "addHole",
3172
+ editContext: { hole: newPolygon.geometry }
3173
+ });
3174
+ return { handled: true };
3175
+ }
3176
+ return { handled: false };
3177
+ }
3067
3178
  };
3179
+ function isNearFirstPoint(click, first, threshold = 1e-4) {
3180
+ const dx = click[0] - first[0];
3181
+ const dy = click[1] - first[1];
3182
+ return dx * dx + dy * dy < threshold * threshold;
3183
+ }
3184
+ function canAddHoleToPolygon(props) {
3185
+ var _a;
3186
+ return ((_a = props.modeConfig) == null ? void 0 : _a.allowHoles) ?? false;
3187
+ }
3188
+ function canPolygonOverlap(props) {
3189
+ var _a;
3190
+ return ((_a = props.modeConfig) == null ? void 0 : _a.allowSelfIntersection) ?? false;
3191
+ }
3068
3192
 
3069
3193
  // dist/edit-modes/draw-rectangle-mode.js
3070
3194
  var import_bbox_polygon4 = __toESM(require("@turf/bbox-polygon"), 1);