mapbox-gl-draw-snap-mode
Version:
Snapping mode for mapbox-gl-draw
161 lines (129 loc) • 4.27 kB
JavaScript
import MapboxDraw from "@mapbox/mapbox-gl-draw";
import {
addPointToVertices,
createSnapList,
getGuideFeature,
IDS,
shouldHideGuide,
snap,
} from "./../utils/index.js";
const { doubleClickZoom } = MapboxDraw.lib;
const { geojsonTypes, modes, cursors } = MapboxDraw.constants;
const DrawLine = MapboxDraw.modes.draw_line_string;
const SnapLineMode = { ...DrawLine };
SnapLineMode.onSetup = function (options) {
const line = this.newFeature({
type: geojsonTypes.FEATURE,
properties: {},
geometry: {
type: geojsonTypes.LINE_STRING,
coordinates: [[]],
},
});
const verticalGuide = this.newFeature(getGuideFeature(IDS.VERTICAL_GUIDE));
const horizontalGuide = this.newFeature(
getGuideFeature(IDS.HORIZONTAL_GUIDE)
);
this.addFeature(line);
this.addFeature(verticalGuide);
this.addFeature(horizontalGuide);
const selectedFeatures = this.getSelected();
this.clearSelectedFeatures();
doubleClickZoom.disable(this);
const [snapList, vertices] = createSnapList(
this.map,
this._ctx.api,
line,
this._ctx.options.snapOptions?.snapGetFeatures
);
const state = {
map: this.map,
line,
currentVertexPosition: 0,
vertices,
snapList,
selectedFeatures,
verticalGuide,
horizontalGuide,
direction: "forward", // expected by DrawLineString
};
state.options = this._ctx.options;
const moveendCallback = () => {
const [snapList, vertices] = createSnapList(
this.map,
this._ctx.api,
line,
this._ctx.options.snapOptions?.snapGetFeatures
);
state.vertices = vertices;
state.snapList = snapList;
};
// for removing listener later on close
state["moveendCallback"] = moveendCallback;
const optionsChangedCallback = (options) => {
state.options = options;
};
// for removing listener later on close
state["optionsChangedCallback"] = optionsChangedCallback;
this.map.on("moveend", moveendCallback);
this.map.on("draw.snap.options_changed", optionsChangedCallback);
return state;
};
SnapLineMode.onClick = function (state) {
// We save some processing by rounding on click, not mousemove
const lng = state.snappedLng;
const lat = state.snappedLat;
// End the drawing if this click is on the previous position
// Note: not bothering with 'direction'
if (state.currentVertexPosition > 0) {
const lastVertex = state.line.coordinates[state.currentVertexPosition - 1];
state.lastVertex = lastVertex;
if (lastVertex[0] === lng && lastVertex[1] === lat) {
return this.changeMode(modes.SIMPLE_SELECT, {
featureIds: [state.line.id],
});
}
}
// const point = state.map.project({ lng: lng, lat: lat });
addPointToVertices(state.map, state.vertices, { lng, lat });
state.line.updateCoordinate(state.currentVertexPosition, lng, lat);
state.currentVertexPosition++;
state.line.updateCoordinate(state.currentVertexPosition, lng, lat);
};
SnapLineMode.onMouseMove = function (state, e) {
const { lng, lat } = snap(state, e);
state.line.updateCoordinate(state.currentVertexPosition, lng, lat);
state.snappedLng = lng;
state.snappedLat = lat;
if (
state.lastVertex &&
state.lastVertex[0] === lng &&
state.lastVertex[1] === lat
) {
this.updateUIClasses({ mouse: cursors.POINTER });
// cursor options:
// ADD: "add"
// DRAG: "drag"
// MOVE: "move"
// NONE: "none"
// POINTER: "pointer"
} else {
this.updateUIClasses({ mouse: cursors.ADD });
}
};
// This is 'extending' DrawLine.toDisplayFeatures
SnapLineMode.toDisplayFeatures = function (state, geojson, display) {
if (shouldHideGuide(state, geojson)) return;
// This relies on the the state of SnapLineMode being similar to DrawLine
DrawLine.toDisplayFeatures(state, geojson, display);
};
// This is 'extending' DrawLine.onStop
SnapLineMode.onStop = function (state) {
this.deleteFeature(IDS.VERTICAL_GUIDE, { silent: true });
this.deleteFeature(IDS.HORIZONTAL_GUIDE, { silent: true });
// remove moveend callback
this.map.off("moveend", state.moveendCallback);
// This relies on the the state of SnapLineMode being similar to DrawLine
DrawLine.onStop.call(this, state);
};
export default SnapLineMode;