maplibre-gl-geo-editor 0.1.0
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/LICENSE +21 -0
- package/README.md +286 -0
- package/dist/index.cjs +19269 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +19269 -0
- package/dist/index.mjs.map +1 -0
- package/dist/maplibre-gl-geo-editor.css +499 -0
- package/dist/react.cjs +157 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.mjs +158 -0
- package/dist/react.mjs.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/react.d.ts +1 -0
- package/package.json +103 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Qiusheng Wu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# maplibre-gl-geo-editor
|
|
2
|
+
|
|
3
|
+
A powerful MapLibre GL plugin for creating and editing geometries. Extends the free [Geoman](https://geoman.io/docs/maplibre/) control with advanced editing features including Union, Split, Scale, Difference, Simplify, Copy, and Lasso selection.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/maplibre-gl-geo-editor)
|
|
6
|
+
[](https://www.npmjs.com/package/maplibre-gl-geo-editor)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
### Draw Tools
|
|
12
|
+
- **Polygon** - Draw polygons by clicking points
|
|
13
|
+
- **Line** - Draw polylines
|
|
14
|
+
- **Rectangle** - Draw rectangles
|
|
15
|
+
- **Circle** - Draw circles
|
|
16
|
+
- **Marker** - Place point markers
|
|
17
|
+
|
|
18
|
+
### Basic Edit Tools (via Geoman Free)
|
|
19
|
+
- **Drag** - Move features on the map
|
|
20
|
+
- **Edit** - Modify feature vertices
|
|
21
|
+
- **Rotate** - Rotate features
|
|
22
|
+
- **Cut** - Cut holes in polygons
|
|
23
|
+
- **Delete** - Remove selected features (supports multi-select)
|
|
24
|
+
|
|
25
|
+
### Advanced Edit Tools (Custom Implementation)
|
|
26
|
+
- **Scale** - Resize features with interactive handles
|
|
27
|
+
- **Copy** - Duplicate features (Ctrl+C/V support)
|
|
28
|
+
- **Split** - Split polygons/lines with a drawn line
|
|
29
|
+
- **Union** - Merge multiple polygons into one
|
|
30
|
+
- **Difference** - Subtract one polygon from another
|
|
31
|
+
- **Simplify** - Reduce vertices using Douglas-Peucker algorithm
|
|
32
|
+
- **Lasso** - Select multiple features by drawing a polygon (supports union/difference/drag)
|
|
33
|
+
- **Reset** - Clear selection and disable active tools (toolbar button)
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install maplibre-gl-geo-editor @geoman-io/maplibre-geoman-free maplibre-gl
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
### Basic Usage (Vanilla JS/TS)
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
47
|
+
import '@geoman-io/maplibre-geoman-free/dist/maplibre-geoman.css';
|
|
48
|
+
import 'maplibre-gl-geo-editor/style.css';
|
|
49
|
+
|
|
50
|
+
import maplibregl from 'maplibre-gl';
|
|
51
|
+
import { Geoman } from '@geoman-io/maplibre-geoman-free';
|
|
52
|
+
import { GeoEditor } from 'maplibre-gl-geo-editor';
|
|
53
|
+
|
|
54
|
+
// Create the map
|
|
55
|
+
const map = new maplibregl.Map({
|
|
56
|
+
container: 'map',
|
|
57
|
+
style: 'https://demotiles.maplibre.org/style.json',
|
|
58
|
+
center: [0, 0],
|
|
59
|
+
zoom: 2,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
map.on('load', () => {
|
|
63
|
+
// Initialize Geoman
|
|
64
|
+
const geoman = new Geoman(map, {});
|
|
65
|
+
|
|
66
|
+
map.on('gm:loaded', () => {
|
|
67
|
+
// Create GeoEditor
|
|
68
|
+
const geoEditor = new GeoEditor({
|
|
69
|
+
position: 'top-left',
|
|
70
|
+
toolbarOrientation: 'vertical',
|
|
71
|
+
drawModes: ['polygon', 'line', 'rectangle', 'circle', 'marker'],
|
|
72
|
+
editModes: [
|
|
73
|
+
'drag', 'change', 'rotate', 'cut', 'delete',
|
|
74
|
+
'scale', 'copy', 'split', 'union', 'difference', 'simplify', 'lasso'
|
|
75
|
+
],
|
|
76
|
+
onFeatureCreate: (feature) => console.log('Created:', feature),
|
|
77
|
+
onSelectionChange: (features) => console.log('Selected:', features.length),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Connect with Geoman
|
|
81
|
+
geoEditor.setGeoman(geoman);
|
|
82
|
+
|
|
83
|
+
// Add to map
|
|
84
|
+
map.addControl(geoEditor, 'top-left');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### React Usage
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
import { useEffect, useRef, useState } from 'react';
|
|
93
|
+
import maplibregl from 'maplibre-gl';
|
|
94
|
+
import { Geoman } from '@geoman-io/maplibre-geoman-free';
|
|
95
|
+
import { GeoEditorReact } from 'maplibre-gl-geo-editor/react';
|
|
96
|
+
|
|
97
|
+
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
98
|
+
import '@geoman-io/maplibre-geoman-free/dist/maplibre-geoman.css';
|
|
99
|
+
import 'maplibre-gl-geo-editor/style.css';
|
|
100
|
+
|
|
101
|
+
function App() {
|
|
102
|
+
const mapContainer = useRef(null);
|
|
103
|
+
const [map, setMap] = useState(null);
|
|
104
|
+
const [geoman, setGeoman] = useState(null);
|
|
105
|
+
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
const newMap = new maplibregl.Map({
|
|
108
|
+
container: mapContainer.current,
|
|
109
|
+
style: 'https://demotiles.maplibre.org/style.json',
|
|
110
|
+
center: [0, 0],
|
|
111
|
+
zoom: 2,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
newMap.on('load', () => {
|
|
115
|
+
const gm = new Geoman(newMap, {});
|
|
116
|
+
newMap.on('gm:loaded', () => {
|
|
117
|
+
setMap(newMap);
|
|
118
|
+
setGeoman(gm);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return () => newMap.remove();
|
|
123
|
+
}, []);
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div style={{ width: '100%', height: '100vh' }}>
|
|
127
|
+
<div ref={mapContainer} style={{ width: '100%', height: '100%' }} />
|
|
128
|
+
{map && geoman && (
|
|
129
|
+
<GeoEditorReact
|
|
130
|
+
map={map}
|
|
131
|
+
geoman={geoman}
|
|
132
|
+
position="top-left"
|
|
133
|
+
drawModes={['polygon', 'line', 'marker']}
|
|
134
|
+
editModes={['drag', 'change', 'scale', 'copy', 'union', 'split']}
|
|
135
|
+
/>
|
|
136
|
+
)}
|
|
137
|
+
</div>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## API Reference
|
|
143
|
+
|
|
144
|
+
### GeoEditorOptions
|
|
145
|
+
|
|
146
|
+
| Option | Type | Default | Description |
|
|
147
|
+
|--------|------|---------|-------------|
|
|
148
|
+
| `position` | `'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right'` | `'top-left'` | Position of the control |
|
|
149
|
+
| `collapsed` | `boolean` | `false` | Start with toolbar collapsed |
|
|
150
|
+
| `drawModes` | `DrawMode[]` | All modes | Draw modes to enable |
|
|
151
|
+
| `editModes` | `EditMode[]` | All modes | Edit modes to enable |
|
|
152
|
+
| `toolbarOrientation` | `'vertical' \| 'horizontal'` | `'vertical'` | Toolbar layout |
|
|
153
|
+
| `showLabels` | `boolean` | `false` | Show text labels on buttons |
|
|
154
|
+
| `simplifyTolerance` | `number` | `0.001` | Default simplification tolerance |
|
|
155
|
+
| `onFeatureCreate` | `(feature) => void` | - | Callback when feature is created |
|
|
156
|
+
| `onFeatureEdit` | `(feature, oldFeature) => void` | - | Callback when feature is edited |
|
|
157
|
+
| `onFeatureDelete` | `(featureId) => void` | - | Callback when feature is deleted |
|
|
158
|
+
| `onSelectionChange` | `(features) => void` | - | Callback when selection changes |
|
|
159
|
+
| `onModeChange` | `(mode) => void` | - | Callback when mode changes |
|
|
160
|
+
|
|
161
|
+
### Methods
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// Mode management
|
|
165
|
+
geoEditor.enableDrawMode('polygon');
|
|
166
|
+
geoEditor.enableEditMode('scale');
|
|
167
|
+
geoEditor.disableAllModes();
|
|
168
|
+
|
|
169
|
+
// Selection
|
|
170
|
+
geoEditor.selectFeatures(features);
|
|
171
|
+
geoEditor.clearSelection();
|
|
172
|
+
geoEditor.getSelectedFeatures();
|
|
173
|
+
geoEditor.getSelectedFeatureCollection();
|
|
174
|
+
|
|
175
|
+
// Clipboard
|
|
176
|
+
geoEditor.copySelectedFeatures();
|
|
177
|
+
geoEditor.pasteFeatures();
|
|
178
|
+
geoEditor.deleteSelectedFeatures();
|
|
179
|
+
|
|
180
|
+
// Get all features
|
|
181
|
+
geoEditor.getFeatures();
|
|
182
|
+
geoEditor.getAllFeatureCollection();
|
|
183
|
+
|
|
184
|
+
// Operation snapshots
|
|
185
|
+
geoEditor.getLastCreatedFeature();
|
|
186
|
+
geoEditor.getLastEditedFeature();
|
|
187
|
+
geoEditor.getLastDeletedFeature();
|
|
188
|
+
geoEditor.getLastDeletedFeatureId();
|
|
189
|
+
|
|
190
|
+
// Get state
|
|
191
|
+
geoEditor.getState();
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Events
|
|
195
|
+
|
|
196
|
+
Listen for events on the map container:
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
map.getContainer().addEventListener('gm:union', (e) => {
|
|
200
|
+
console.log('Union result:', e.detail);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
map.getContainer().addEventListener('gm:split', (e) => {
|
|
204
|
+
console.log('Split result:', e.detail);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
map.getContainer().addEventListener('gm:simplify', (e) => {
|
|
208
|
+
console.log('Simplify result:', e.detail);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
map.getContainer().addEventListener('gm:lassoend', (e) => {
|
|
212
|
+
console.log('Lasso selection:', e.detail);
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Keyboard Shortcuts
|
|
217
|
+
|
|
218
|
+
| Shortcut | Action |
|
|
219
|
+
|----------|--------|
|
|
220
|
+
| `Ctrl+C` | Copy selected features |
|
|
221
|
+
| `Ctrl+V` | Paste features |
|
|
222
|
+
| `Delete` | Delete selected features |
|
|
223
|
+
| `Escape` | Cancel operation / Clear selection |
|
|
224
|
+
|
|
225
|
+
## Logging
|
|
226
|
+
|
|
227
|
+
GeoEditor logs the current selected FeatureCollection to the console whenever a feature is selected, created, edited, or deleted.
|
|
228
|
+
|
|
229
|
+
## Standalone Feature Classes
|
|
230
|
+
|
|
231
|
+
You can also use the feature classes directly:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import {
|
|
235
|
+
CopyFeature,
|
|
236
|
+
SimplifyFeature,
|
|
237
|
+
UnionFeature,
|
|
238
|
+
DifferenceFeature,
|
|
239
|
+
ScaleFeature,
|
|
240
|
+
SplitFeature,
|
|
241
|
+
} from 'maplibre-gl-geo-editor';
|
|
242
|
+
|
|
243
|
+
// Union polygons
|
|
244
|
+
const union = new UnionFeature();
|
|
245
|
+
const result = union.union([polygon1, polygon2]);
|
|
246
|
+
|
|
247
|
+
// Simplify a feature
|
|
248
|
+
const simplify = new SimplifyFeature();
|
|
249
|
+
const simplified = simplify.simplify(feature, { tolerance: 0.01 });
|
|
250
|
+
|
|
251
|
+
// Get simplification stats
|
|
252
|
+
const stats = simplify.getSimplificationStats(feature, 0.01);
|
|
253
|
+
console.log(`Reduced vertices by ${stats.reduction}%`);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Development
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# Install dependencies
|
|
260
|
+
npm install
|
|
261
|
+
|
|
262
|
+
# Start development server
|
|
263
|
+
npm run dev
|
|
264
|
+
|
|
265
|
+
# Run tests
|
|
266
|
+
npm test
|
|
267
|
+
|
|
268
|
+
# Build
|
|
269
|
+
npm run build
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Dependencies
|
|
273
|
+
|
|
274
|
+
- [MapLibre GL JS](https://maplibre.org/) - Map rendering
|
|
275
|
+
- [@geoman-io/maplibre-geoman-free](https://geoman.io/) - Basic drawing/editing
|
|
276
|
+
- [@turf/turf](https://turfjs.org/) - Geometry operations
|
|
277
|
+
|
|
278
|
+
## License
|
|
279
|
+
|
|
280
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
281
|
+
|
|
282
|
+
## Credits
|
|
283
|
+
|
|
284
|
+
- [Geoman](https://geoman.io/) for the excellent free drawing/editing plugin
|
|
285
|
+
- [Turf.js](https://turfjs.org/) for powerful geometry operations
|
|
286
|
+
- Inspired by [maplibre-gl-layer-control](https://github.com/opengeos/maplibre-gl-layer-control)
|