PageSourceSearch

https://edgarodriguez-clide-brazil.hf.space/freehand-mode-1.0.0/freehand-mode.js

js edgarodriguez-clide-brazil.hf.space collected 2026-10-03 16:50:01 UTC 6,395 bytes, 207 lines download raw bytes

1// This code is derived from the ISC-licensed "mapbox-gl-draw-freehand-mode" plugin
2// by Ben Ehmke, Eric Dong, and Joe Woodhouse.
3// Source: https://github.com/bemky/mapbox-gl-draw-freehand-mode
4(function (MapboxDraw) {
5    const { geojsonTypes, cursors, types, updateActions, modes, events } =
6        MapboxDraw.constants;
7
8    const FreehandMode = {};
9
10    FreehandMode.onSetup = function () {
11        const polygon = this.newFeature({
12            type: geojsonTypes.FEATURE,
13            properties: {},
14            geometry: {
15                type: geojsonTypes.POLYGON,
16                coordinates: [[]],
17            },
18        });
19
20        this.addFeature(polygon);
21        this.clearSelectedFeatures();
22
23        // Disable map dragging
24        setTimeout(() => {
25            if (!this.map || !this.map.dragPan) return;
26            this.map.dragPan.disable();
27        }, 0);
28
29        this.updateUIClasses({ mouse: cursors.ADD });
30        this.activateUIButton(types.POLYGON);
31        this.setActionableState({
32            trash: true,
33        });
34
35        return {
36            polygon,
37            currentVertexPosition: 0,
38            dragMoving: false,
39            isDrawing: false,
40        };
41    };
42
43    FreehandMode.onDrag = FreehandMode.onTouchMove = function (state, e) {
44        state.dragMoving = true;
45        state.isDrawing = true;
46        this.updateUIClasses({ mouse: cursors.ADD });
47        state.polygon.updateCoordinate(
48            `0.${state.currentVertexPosition}`,
49            e.lngLat.lng,
50            e.lngLat.lat,
51        );
52        state.currentVertexPosition++;
53        state.polygon.updateCoordinate(
54            `0.${state.currentVertexPosition}`,
55            e.lngLat.lng,
56            e.lngLat.lat,
57        );
58    };
59
60    FreehandMode.onMouseUp = function (state) {
61        if (state.dragMoving) {
62            this.simplify(state.polygon);
63            this.updateUIClasses({ mouse: cursors.MOVE });
64            this.fireUpdate();
65            this.changeMode(modes.SIMPLE_SELECT, {
66                featureIds: [state.polygon.id],
67            });
68        }
69    };
70
71    FreehandMode.fireCreate = function (polygon) {
72        this.map.fire(events.CREATE, {
73            features: [polygon.toGeoJSON()],
74        });
75    };
76
77    FreehandMode.fireUpdate = function () {
78        this.map.fire(events.UPDATE, {
79            action: updateActions.MOVE,
80            features: this.getSelected().map((f) => f.toGeoJSON()),
81        });
82    };
83
84    FreehandMode.simplify = function (polygon) {
85        if (!this.map.simplify_freehand) return;
86
87        const tolerance = 1 / Math.pow(1.05, 10 * this.map.getZoom());
88        const simplifiedCoords = simplifyGeometry(
89            polygon.coordinates[0],
90            tolerance,
91        );
92        polygon.setCoordinates([simplifiedCoords]);
93    };
94
95    function simplifyGeometry(points, tolerance) {
96        if (points.length <= 2) return points;
97
98        const sqTolerance = tolerance * tolerance;
99        const last = points.length - 1;
100        const simplified = [points[0]];
101        simplifyDPStep(points, 0, last, sqTolerance, simplified);
102        simplified.push(points[last]);
103
104        return simplified;
105    }
106
107    function simplifyDPStep(points, first, last, sqTolerance, simplified) {
108        let maxSqDist = sqTolerance;
109        let index;
110
111        for (let i = first + 1; i < last; i++) {
112            const sqDist = getSquareSegmentDistance(
113                points[i],
114                points[first],
115                points[last],
116            );
117            if (sqDist > maxSqDist) {
118                index = i;
119                maxSqDist = sqDist;
120            }
121        }
122
123        if (maxSqDist > sqTolerance) {
124            if (index - first > 1)
125                simplifyDPStep(points, first, index, sqTolerance, simplified);
126            simplified.push(points[index]);
127            if (last - index > 1)
128                simplifyDPStep(points, index, last, sqTolerance, simplified);
129        }
130    }
131
132    function getSquareSegmentDistance(p, p1, p2) {
133        let x = p1[0],
134            y = p1[1];
135        let dx = p2[0] - x,
136            dy = p2[1] - y;
137
138        if (dx !== 0 || dy !== 0) {
139            const t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy);
140            if (t > 1) {
141                x = p2[0];
142                y = p2[1];
143            } else if (t > 0) {
144                x += dx * t;
145                y += dy * t;
146            }
147        }
148
149        dx = p[0] - x;
150        dy = p[1] - y;
151
152        return dx * dx + dy * dy;
153    }
154
155    FreehandMode.onStop = function (state) {
156        this.updateUIClasses({ mouse: cursors.NONE });
157        this.activateUIButton();
158
159        // Enable map dragging
160        setTimeout(() => {
161            if (!this.map || !this.map.dragPan) return;
162            this.map.dragPan.enable();
163        }, 0);
164    };
165
166    FreehandMode.toDisplayFeatures = function (state, geojson, display) {
167        const isActivePolygon = geojson.properties.id === state.polygon.id;
168        geojson.properties.active = isActivePolygon ? "true" : "false";
169        if (!isActivePolygon) return display(geojson);
170
171        // Only render the polygon if it has at least three points
172        if (geojson.geometry.coordinates[0].length < 3) return;
173
174        const coordinateCount = geojson.geometry.coordinates[0].length;
175
176        // If we have fewer than three coordinates, we need to create a LineString instead of a Polygon
177        if (coordinateCount < 3) {
178            const lineCoordinates = [
179                [
180                    geojson.geometry.coordinates[0][0][0],
181                    geojson.geometry.coordinates[0][0][1],
182                ],
183                [
184                    geojson.geometry.coordinates[0][1][0],
185                    geojson.geometry.coordinates[0][1][1],
186                ],
187            ];
188            return display({
189                type: geojsonTypes.FEATURE,
190                properties: geojson.properties,
191                geometry: {
192                    coordinates: lineCoordinates,
193                    type: geojsonTypes.LINE_STRING,
194                },
195            });
196        }
197
198        return display(geojson);
199    };
200
201    FreehandMode.onTrash = function (state) {
202        this.deleteFeature([state.polygon.id], { silent: true });
203        this.changeMode(modes.SIMPLE_SELECT);
204    };
205
206    MapboxDraw.modes.draw_freehand = FreehandMode;
207})(MapboxDraw);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.