├── .babelrc
├── .github
└── workflows
│ └── publish.yml
├── .gitignore
├── .nvmrc
├── LICENSE
├── Readme.md
├── dist
├── mapbox-gl-draw-snap-mode.cjs.js
├── mapbox-gl-draw-snap-mode.cjs.js.map
├── mapbox-gl-draw-snap-mode.esm.js
└── mapbox-gl-draw-snap-mode.esm.js.map
├── docs
├── demo.gif
└── index.html
├── package-lock.json
├── package.json
├── src
├── index.js
├── modes
│ ├── snap_direct_select.js
│ ├── snap_line.js
│ ├── snap_point.js
│ └── snap_polygon.js
└── utils
│ ├── customDrawStyles.js
│ └── index.js
├── webpack.config.js
└── webpack.development.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"]
3 | }
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to npm registry
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | publish:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v1
13 | - uses: actions/setup-node@v1
14 | with:
15 | node-version: 16
16 | - name: Install and Build 🔧
17 | run: |
18 | npm ci
19 | npm run build
20 | - name: Publish 🔧
21 | uses: JS-DevTools/npm-publish@v1
22 | with:
23 | token: ${{ secrets.NPM_TOKEN }}
24 | check-version: true
25 |
26 | - if: steps.publish.outputs.type != 'none'
27 | run: |
28 | echo "Version changed: ${{ steps.publish.outputs.old-version }} => ${{ steps.publish.outputs.version }}"
29 | - if: steps.publish.outputs.type == 'none'
30 | run: |
31 | echo "Version is not changed. publishing cancels."
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # npm
2 | node_modules
3 |
4 | # temp
5 | /src/docs.js
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v16.17.1
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Mohammad H. Sattarian
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.
22 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Mapbox-GL Draw Snap Mode
2 |
3 | [](https://www.npmjs.com/package/mapbox-gl-draw-snap-mode)
4 |
5 | Custom mode for [Mapbox GL Draw](https://github.com/mapbox/mapbox-gl-draw) that adds snapping ability while drawing features.
6 | It provides options to show guiding lines, control snapping sensibility, and whether to snap to midpoints on each segment.
7 |
8 | ## Demo
9 |
10 | See a full example in the docs folder, or check at the [**Demo**](https://mhsattarian.github.io/mapbox-gl-draw-snap-mode).
11 |
12 | 
13 |
14 | ## Install
15 |
16 | ```shell
17 | npm i mapbox-gl-draw-snap-mode
18 | ```
19 |
20 | or use CDN:
21 |
22 | ```html
23 |
24 | ```
25 |
26 | ## Usage
27 |
28 | ```js
29 | import {
30 | SnapPolygonMode,
31 | SnapPointMode,
32 | SnapLineMode,
33 | SnapModeDrawStyles,
34 | SnapDirectSelect,
35 | } from "mapbox-gl-draw-snap-mode";
36 | // or global variable mapboxGlDrawSnapMode when using script tag
37 |
38 | const draw = new MapboxDraw({
39 | modes: {
40 | ...MapboxDraw.modes,
41 | draw_point: SnapPointMode,
42 | draw_polygon: SnapPolygonMode,
43 | draw_line_string: SnapLineMode,
44 | direct_select: SnapDirectSelect,
45 | },
46 | // Styling guides
47 | styles: SnapModeDrawStyles,
48 | userProperties: true,
49 | // Config snapping features
50 | snap: true,
51 | snapOptions: {
52 | snapPx: 15, // defaults to 15
53 | snapToMidPoints: true, // defaults to false
54 | snapVertexPriorityDistance: 0.0025, // defaults to 1.25
55 | snapGetFeatures: (map, draw) => [
56 | ...map.queryRenderedFeatures({ layers: ["not-editable-layer-name"] }),
57 | ...draw.getAll().features,
58 | ], // defaults to all features from the draw layer (draw.getAll().features)
59 | },
60 | guides: false,
61 | });
62 |
63 | map.addControl(draw, "top-right");
64 |
65 | draw.changeMode("draw_polygon");
66 | ```
67 |
68 | ### options
69 |
70 | #### `snapPx`
71 |
72 | The min distance (in pixels) where snapping to the line/segments would take effect.
73 |
74 | #### `snapToMidPoints`
75 |
76 | Controls whether to snap to line/segments midpoints (an imaginary point in the middle of each segment) or not.
77 |
78 | #### `snapVertexPriorityDistance`
79 |
80 | The min distance (in Kilometers) from each vertex, where snapping to that vertex would take priority over snapping to line/segments.
81 |
82 | #### `overlap`
83 |
84 | Defaults to `true`. When creating polygons, if `false`, will use `turf.difference` on all overlapping polygons to create a polygon that does not overlap existing ones.
85 |
86 | ### Changing settings
87 |
88 | Changing settings would take effect while snapping immediately, so you can control snapping behavior using `draw.options.snap`, like so:
89 |
90 | ```js
91 | // turn snapping off
92 | draw.options.snap = false;
93 |
94 | // and back on
95 | draw.options.snap = true;
96 | ```
97 |
98 | > Snapping can also be disabled holding `Alt (Option)` key.
99 |
100 | You can also create a [custom mapbox-gl draw toolbar](https://github.com/mapbox/mapbox-gl-draw/issues/874#issuecomment-470591089) to control this, take a look at the [example in the `docs` directory](/docs/index.html#L122).
101 |
102 | ## Developing and testing
103 |
104 | Install dependencies, start the dev server:
105 |
106 | ```bash
107 | npm install
108 | npm start
109 | ```
110 |
111 | to preview, change `docs/index.html` as so:
112 |
113 | ```diff
114 | -
115 | +
116 | ```
117 |
118 | > Remember to revert this change before `git push`.
119 |
120 | ## Publishing
121 |
122 | To GitHub and NPM:
123 |
124 | ```
125 | npm version (major|minor|patch)
126 | git push --tags
127 | git push
128 | npm publish
129 | ```
130 |
131 | ## Acknowledgement
132 |
133 | This project is heavily inspired by [this work](https://github.com/mapbox/mapbox-gl-draw/issues/865) by [@davidgilbertson](https://github.com/davidgilbertson) and [`leaflet-geoman` project](https://github.com/geoman-io/leaflet-geoman).
134 |
--------------------------------------------------------------------------------
/dist/mapbox-gl-draw-snap-mode.esm.js:
--------------------------------------------------------------------------------
1 | var t={188:function(t,e,n){t.exports=function(){var t=function(t,e){var n={drag:[],click:[],mousemove:[],mousedown:[],mouseup:[],mouseout:[],keydown:[],keyup:[],touchstart:[],touchmove:[],touchend:[],tap:[]},r={on:function(t,e,r){if(void 0===n[t])throw new Error("Invalid event type: "+t);n[t].push({selector:e,fn:r})},render:function(t){e.store.featureChanged(t)}},o=function(t,o){for(var i=n[t],a=i.length;a--;){var s=i[a];if(s.selector(o)){s.fn.call(r,o)||e.store.render(),e.ui.updateMapClasses();break}}};return t.start.call(r),{render:t.render,stop:function(){t.stop&&t.stop()},trash:function(){t.trash&&(t.trash(),e.store.render())},combineFeatures:function(){t.combineFeatures&&t.combineFeatures()},uncombineFeatures:function(){t.uncombineFeatures&&t.uncombineFeatures()},drag:function(t){o("drag",t)},click:function(t){o("click",t)},mousemove:function(t){o("mousemove",t)},mousedown:function(t){o("mousedown",t)},mouseup:function(t){o("mouseup",t)},mouseout:function(t){o("mouseout",t)},keydown:function(t){o("keydown",t)},keyup:function(t){o("keyup",t)},touchstart:function(t){o("touchstart",t)},touchmove:function(t){o("touchmove",t)},touchend:function(t){o("touchend",t)},tap:function(t){o("tap",t)}}};function e(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}function r(t){if(t.__esModule)return t;var e=t.default;if("function"==typeof e){var n=function t(){if(this instanceof t){var n=[null];return n.push.apply(n,arguments),new(Function.bind.apply(e,n))}return e.apply(this,arguments)};n.prototype=e.prototype}else n={};return Object.defineProperty(n,"__esModule",{value:!0}),Object.keys(t).forEach((function(e){var r=Object.getOwnPropertyDescriptor(t,e);Object.defineProperty(n,e,r.get?r:{enumerable:!0,get:function(){return t[e]}})})),n}var o={},i={RADIUS:6378137,FLATTENING:1/298.257223563,POLAR_RADIUS:6356752.3142};function a(t){var e=0;if(t&&t.length>0){e+=Math.abs(s(t[0]));for(var n=1;n2){for(s=0;s=Math.pow(2,t)?j(t,e):i};j.rack=function(t,e,n){var r=function(r){var i=0;do{if(i++>10){if(!n)throw new Error("too many ID collisions, use more bits");t+=n}var a=j(t,e)}while(Object.hasOwnProperty.call(o,a));return o[a]=r,a},o=r.hats={};return r.get=function(t){return r.hats[t]},r.set=function(t,e){return r.hats[t]=e,r},r.bits=t||128,r.base=e||16,r};var D=e(N.exports),R=function(t,e){this.ctx=t,this.properties=e.properties||{},this.coordinates=e.geometry.coordinates,this.id=e.id||D(),this.type=e.geometry.type};R.prototype.changed=function(){this.ctx.store.featureChanged(this.id)},R.prototype.incomingCoords=function(t){this.setCoordinates(t)},R.prototype.setCoordinates=function(t){this.coordinates=t,this.changed()},R.prototype.getCoordinates=function(){return JSON.parse(JSON.stringify(this.coordinates))},R.prototype.setProperty=function(t,e){this.properties[t]=e},R.prototype.toGeoJSON=function(){return JSON.parse(JSON.stringify({id:this.id,type:f.FEATURE,properties:this.properties,geometry:{coordinates:this.getCoordinates(),type:this.type}}))},R.prototype.internal=function(t){var e={id:this.id,meta:v.FEATURE,"meta:type":this.type,active:m.INACTIVE,mode:t};if(this.ctx.options.userProperties)for(var n in this.properties)e["user_"+n]=this.properties[n];return{type:f.FEATURE,properties:e,geometry:{coordinates:this.getCoordinates(),type:this.type}}};var k=function(t,e){R.call(this,t,e)};(k.prototype=Object.create(R.prototype)).isValid=function(){return"number"==typeof this.coordinates[0]&&"number"==typeof this.coordinates[1]},k.prototype.updateCoordinate=function(t,e,n){this.coordinates=3===arguments.length?[e,n]:[t,e],this.changed()},k.prototype.getCoordinate=function(){return this.getCoordinates()};var U=function(t,e){R.call(this,t,e)};(U.prototype=Object.create(R.prototype)).isValid=function(){return this.coordinates.length>1},U.prototype.addCoordinate=function(t,e,n){this.changed();var r=parseInt(t,10);this.coordinates.splice(r,0,[e,n])},U.prototype.getCoordinate=function(t){var e=parseInt(t,10);return JSON.parse(JSON.stringify(this.coordinates[e]))},U.prototype.removeCoordinate=function(t){this.changed(),this.coordinates.splice(parseInt(t,10),1)},U.prototype.updateCoordinate=function(t,e,n){var r=parseInt(t,10);this.coordinates[r]=[e,n],this.changed()};var G=function(t,e){R.call(this,t,e),this.coordinates=this.coordinates.map((function(t){return t.slice(0,-1)}))};(G.prototype=Object.create(R.prototype)).isValid=function(){return 0!==this.coordinates.length&&this.coordinates.every((function(t){return t.length>2}))},G.prototype.incomingCoords=function(t){this.coordinates=t.map((function(t){return t.slice(0,-1)})),this.changed()},G.prototype.setCoordinates=function(t){this.coordinates=t,this.changed()},G.prototype.addCoordinate=function(t,e,n){this.changed();var r=t.split(".").map((function(t){return parseInt(t,10)}));this.coordinates[r[0]].splice(r[1],0,[e,n])},G.prototype.removeCoordinate=function(t){this.changed();var e=t.split(".").map((function(t){return parseInt(t,10)})),n=this.coordinates[e[0]];n&&(n.splice(e[1],1),n.length<3&&this.coordinates.splice(e[0],1))},G.prototype.getCoordinate=function(t){var e=t.split(".").map((function(t){return parseInt(t,10)})),n=this.coordinates[e[0]];return JSON.parse(JSON.stringify(n[e[1]]))},G.prototype.getCoordinates=function(){return this.coordinates.map((function(t){return t.concat([t[0]])}))},G.prototype.updateCoordinate=function(t,e,n){this.changed();var r=t.split("."),o=parseInt(r[0],10),i=parseInt(r[1],10);void 0===this.coordinates[o]&&(this.coordinates[o]=[]),this.coordinates[o][i]=[e,n]};var V={MultiPoint:k,MultiLineString:U,MultiPolygon:G},B=function(t,e,n,r,o){var i=n.split("."),a=parseInt(i[0],10),s=i[1]?i.slice(1).join("."):null;return t[a][e](s,r,o)},z=function(t,e){if(R.call(this,t,e),delete this.coordinates,this.model=V[e.geometry.type],void 0===this.model)throw new TypeError(e.geometry.type+" is not a valid type");this.features=this._coordinatesToFeatures(e.geometry.coordinates)};function J(t){this.map=t.map,this.drawConfig=JSON.parse(JSON.stringify(t.options||{})),this._ctx=t}(z.prototype=Object.create(R.prototype))._coordinatesToFeatures=function(t){var e=this,n=this.model.bind(this);return t.map((function(t){return new n(e.ctx,{id:D(),type:f.FEATURE,properties:{},geometry:{coordinates:t,type:e.type.replace("Multi","")}})}))},z.prototype.isValid=function(){return this.features.every((function(t){return t.isValid()}))},z.prototype.setCoordinates=function(t){this.features=this._coordinatesToFeatures(t),this.changed()},z.prototype.getCoordinate=function(t){return B(this.features,"getCoordinate",t)},z.prototype.getCoordinates=function(){return JSON.parse(JSON.stringify(this.features.map((function(t){return t.type===f.POLYGON?t.getCoordinates():t.coordinates}))))},z.prototype.updateCoordinate=function(t,e,n){B(this.features,"updateCoordinate",t,e,n),this.changed()},z.prototype.addCoordinate=function(t,e,n){B(this.features,"addCoordinate",t,e,n),this.changed()},z.prototype.removeCoordinate=function(t){B(this.features,"removeCoordinate",t),this.changed()},z.prototype.getFeatures=function(){return this.features},J.prototype.setSelected=function(t){return this._ctx.store.setSelected(t)},J.prototype.setSelectedCoordinates=function(t){var e=this;this._ctx.store.setSelectedCoordinates(t),t.reduce((function(t,n){return void 0===t[n.feature_id]&&(t[n.feature_id]=!0,e._ctx.store.get(n.feature_id).changed()),t}),{})},J.prototype.getSelected=function(){return this._ctx.store.getSelected()},J.prototype.getSelectedIds=function(){return this._ctx.store.getSelectedIds()},J.prototype.isSelected=function(t){return this._ctx.store.isSelected(t)},J.prototype.getFeature=function(t){return this._ctx.store.get(t)},J.prototype.select=function(t){return this._ctx.store.select(t)},J.prototype.deselect=function(t){return this._ctx.store.deselect(t)},J.prototype.deleteFeature=function(t,e){return void 0===e&&(e={}),this._ctx.store.delete(t,e)},J.prototype.addFeature=function(t){return this._ctx.store.add(t)},J.prototype.clearSelectedFeatures=function(){return this._ctx.store.clearSelected()},J.prototype.clearSelectedCoordinates=function(){return this._ctx.store.clearSelectedCoordinates()},J.prototype.setActionableState=function(t){void 0===t&&(t={});var e={trash:t.trash||!1,combineFeatures:t.combineFeatures||!1,uncombineFeatures:t.uncombineFeatures||!1};return this._ctx.events.actionable(e)},J.prototype.changeMode=function(t,e,n){return void 0===e&&(e={}),void 0===n&&(n={}),this._ctx.events.changeMode(t,e,n)},J.prototype.updateUIClasses=function(t){return this._ctx.ui.queueMapClasses(t)},J.prototype.activateUIButton=function(t){return this._ctx.ui.setActiveButton(t)},J.prototype.featuresAt=function(t,e,n){if(void 0===n&&(n="click"),"click"!==n&&"touch"!==n)throw new Error("invalid buffer type");return x[n](t,e,this._ctx)},J.prototype.newFeature=function(t){var e=t.geometry.type;return e===f.POINT?new k(this._ctx,t):e===f.LINE_STRING?new U(this._ctx,t):e===f.POLYGON?new G(this._ctx,t):new z(this._ctx,t)},J.prototype.isInstanceOf=function(t,e){if(t===f.POINT)return e instanceof k;if(t===f.LINE_STRING)return e instanceof U;if(t===f.POLYGON)return e instanceof G;if("MultiFeature"===t)return e instanceof z;throw new Error("Unknown feature class: "+t)},J.prototype.doRender=function(t){return this._ctx.store.featureChanged(t)},J.prototype.onSetup=function(){},J.prototype.onDrag=function(){},J.prototype.onClick=function(){},J.prototype.onMouseMove=function(){},J.prototype.onMouseDown=function(){},J.prototype.onMouseUp=function(){},J.prototype.onMouseOut=function(){},J.prototype.onKeyUp=function(){},J.prototype.onKeyDown=function(){},J.prototype.onTouchStart=function(){},J.prototype.onTouchMove=function(){},J.prototype.onTouchEnd=function(){},J.prototype.onTap=function(){},J.prototype.onStop=function(){},J.prototype.onTrash=function(){},J.prototype.onCombineFeature=function(){},J.prototype.onUncombineFeature=function(){},J.prototype.toDisplayFeatures=function(){throw new Error("You must overwrite toDisplayFeatures")};var Z={drag:"onDrag",click:"onClick",mousemove:"onMouseMove",mousedown:"onMouseDown",mouseup:"onMouseUp",mouseout:"onMouseOut",keyup:"onKeyUp",keydown:"onKeyDown",touchstart:"onTouchStart",touchmove:"onTouchMove",touchend:"onTouchEnd",tap:"onTap"},H=Object.keys(Z);function q(t){var e=Object.keys(t);return function(n,r){void 0===r&&(r={});var o={},i=e.reduce((function(e,n){return e[n]=t[n],e}),new J(n));return{start:function(){var e=this;o=i.onSetup(r),H.forEach((function(n){var r,a=Z[n],s=function(){return!1};t[a]&&(s=function(){return!0}),e.on(n,s,(r=a,function(t){return i[r](o,t)}))}))},stop:function(){i.onStop(o)},trash:function(){i.onTrash(o)},combineFeatures:function(){i.onCombineFeatures(o)},uncombineFeatures:function(){i.onUncombineFeatures(o)},render:function(t,e){i.toDisplayFeatures(o,t,e)}}}}function $(t){return[].concat(t).filter((function(t){return void 0!==t}))}function Y(){var t=this;if(!t.ctx.map||void 0===t.ctx.map.getSource(l.HOT))return u();var e=t.ctx.events.currentModeName();t.ctx.ui.queueMapClasses({mode:e});var n=[],r=[];t.isDirty?r=t.getAllIds():(n=t.getChangedIds().filter((function(e){return void 0!==t.get(e)})),r=t.sources.hot.filter((function(e){return e.properties.id&&-1===n.indexOf(e.properties.id)&&void 0!==t.get(e.properties.id)})).map((function(t){return t.properties.id}))),t.sources.hot=[];var o=t.sources.cold.length;t.sources.cold=t.isDirty?[]:t.sources.cold.filter((function(t){var e=t.properties.id||t.properties.parent;return-1===n.indexOf(e)}));var i=o!==t.sources.cold.length||r.length>0;function a(n,r){var o=t.get(n).internal(e);t.ctx.events.currentModeRender(o,(function(e){t.sources[r].push(e)}))}if(n.forEach((function(t){return a(t,"hot")})),r.forEach((function(t){return a(t,"cold")})),i&&t.ctx.map.getSource(l.COLD).setData({type:f.FEATURE_COLLECTION,features:t.sources.cold}),t.ctx.map.getSource(l.HOT).setData({type:f.FEATURE_COLLECTION,features:t.sources.hot}),t._emitSelectionChange&&(t.ctx.map.fire(g.SELECTION_CHANGE,{features:t.getSelected().map((function(t){return t.toGeoJSON()})),points:t.getSelectedCoordinates().map((function(t){return{type:f.FEATURE,properties:{},geometry:{type:f.POINT,coordinates:t.coordinates}}}))}),t._emitSelectionChange=!1),t._deletedFeaturesToEmit.length){var s=t._deletedFeaturesToEmit.map((function(t){return t.toGeoJSON()}));t._deletedFeaturesToEmit=[],t.ctx.map.fire(g.DELETE,{features:s})}function u(){t.isDirty=!1,t.clearChangedIds()}u(),t.ctx.map.fire(g.RENDER,{})}function W(t){var e,n=this;this._features={},this._featureIds=new w,this._selectedFeatureIds=new w,this._selectedCoordinates=[],this._changedFeatureIds=new w,this._deletedFeaturesToEmit=[],this._emitSelectionChange=!1,this._mapInitialConfig={},this.ctx=t,this.sources={hot:[],cold:[]},this.render=function(){e||(e=requestAnimationFrame((function(){e=null,Y.call(n)})))},this.isDirty=!1}function K(t,e){var n=t._selectedCoordinates.filter((function(e){return t._selectedFeatureIds.has(e.feature_id)}));t._selectedCoordinates.length===n.length||e.silent||(t._emitSelectionChange=!0),t._selectedCoordinates=n}W.prototype.createRenderBatch=function(){var t=this,e=this.render,n=0;return this.render=function(){n++},function(){t.render=e,n>0&&t.render()}},W.prototype.setDirty=function(){return this.isDirty=!0,this},W.prototype.featureChanged=function(t){return this._changedFeatureIds.add(t),this},W.prototype.getChangedIds=function(){return this._changedFeatureIds.values()},W.prototype.clearChangedIds=function(){return this._changedFeatureIds.clear(),this},W.prototype.getAllIds=function(){return this._featureIds.values()},W.prototype.add=function(t){return this.featureChanged(t.id),this._features[t.id]=t,this._featureIds.add(t.id),this},W.prototype.delete=function(t,e){var n=this;return void 0===e&&(e={}),$(t).forEach((function(t){n._featureIds.has(t)&&(n._featureIds.delete(t),n._selectedFeatureIds.delete(t),e.silent||-1===n._deletedFeaturesToEmit.indexOf(n._features[t])&&n._deletedFeaturesToEmit.push(n._features[t]),delete n._features[t],n.isDirty=!0)})),K(this,e),this},W.prototype.get=function(t){return this._features[t]},W.prototype.getAll=function(){var t=this;return Object.keys(this._features).map((function(e){return t._features[e]}))},W.prototype.select=function(t,e){var n=this;return void 0===e&&(e={}),$(t).forEach((function(t){n._selectedFeatureIds.has(t)||(n._selectedFeatureIds.add(t),n._changedFeatureIds.add(t),e.silent||(n._emitSelectionChange=!0))})),this},W.prototype.deselect=function(t,e){var n=this;return void 0===e&&(e={}),$(t).forEach((function(t){n._selectedFeatureIds.has(t)&&(n._selectedFeatureIds.delete(t),n._changedFeatureIds.add(t),e.silent||(n._emitSelectionChange=!0))})),K(this,e),this},W.prototype.clearSelected=function(t){return void 0===t&&(t={}),this.deselect(this._selectedFeatureIds.values(),{silent:t.silent}),this},W.prototype.setSelected=function(t,e){var n=this;return void 0===e&&(e={}),t=$(t),this.deselect(this._selectedFeatureIds.values().filter((function(e){return-1===t.indexOf(e)})),{silent:e.silent}),this.select(t.filter((function(t){return!n._selectedFeatureIds.has(t)})),{silent:e.silent}),this},W.prototype.setSelectedCoordinates=function(t){return this._selectedCoordinates=t,this._emitSelectionChange=!0,this},W.prototype.clearSelectedCoordinates=function(){return this._selectedCoordinates=[],this._emitSelectionChange=!0,this},W.prototype.getSelectedIds=function(){return this._selectedFeatureIds.values()},W.prototype.getSelected=function(){var t=this;return this._selectedFeatureIds.values().map((function(e){return t.get(e)}))},W.prototype.getSelectedCoordinates=function(){var t=this;return this._selectedCoordinates.map((function(e){return{coordinates:t.get(e.feature_id).getCoordinate(e.coord_path)}}))},W.prototype.isSelected=function(t){return this._selectedFeatureIds.has(t)},W.prototype.setFeatureProperty=function(t,e,n){this.get(t).setProperty(e,n),this.featureChanged(t)},W.prototype.storeMapConfig=function(){var t=this;b.forEach((function(e){t.ctx.map[e]&&(t._mapInitialConfig[e]=t.ctx.map[e].isEnabled())}))},W.prototype.restoreMapConfig=function(){var t=this;Object.keys(this._mapInitialConfig).forEach((function(e){t._mapInitialConfig[e]?t.ctx.map[e].enable():t.ctx.map[e].disable()}))},W.prototype.getInitialConfigValue=function(t){return void 0===this._mapInitialConfig[t]||this._mapInitialConfig[t]};var X=function(){for(var t=arguments,e={},n=0;n=48&&t<=57)};function c(r,o,i){void 0===i&&(i={}),s.stop();var u=n[r];if(void 0===u)throw new Error(r+" is not valid");a=r;var c=u(e,o);s=t(c,e),i.silent||e.map.fire(g.MODE_CHANGE,{mode:r}),e.store.setDirty(),e.store.render()}i.keydown=function(t){(t.srcElement||t.target).classList.contains("mapboxgl-canvas")&&(8!==t.keyCode&&46!==t.keyCode||!e.options.controls.trash?u(t.keyCode)?s.keydown(t):49===t.keyCode&&e.options.controls.point?c(h.DRAW_POINT):50===t.keyCode&&e.options.controls.line_string?c(h.DRAW_LINE_STRING):51===t.keyCode&&e.options.controls.polygon&&c(h.DRAW_POLYGON):(t.preventDefault(),s.trash()))},i.keyup=function(t){u(t.keyCode)&&s.keyup(t)},i.zoomend=function(){e.store.changeZoom()},i.data=function(t){if("style"===t.dataType){var n=e.setup,r=e.map,o=e.options,i=e.store;o.styles.some((function(t){return r.getLayer(t.id)}))||(n.addLayers(),i.setDirty(),i.render())}};var l={trash:!1,combineFeatures:!1,uncombineFeatures:!1};return{start:function(){a=e.options.defaultMode,s=t(n[a](e),e)},changeMode:c,actionable:function(t){var n=!1;Object.keys(t).forEach((function(e){if(void 0===l[e])throw new Error("Invalid action type");l[e]!==t[e]&&(n=!0),l[e]=t[e]})),n&&e.map.fire(g.ACTIONABLE,{actions:l})},currentModeName:function(){return a},currentModeRender:function(t,e){return s.render(t,e)},fire:function(t,e){i[t]&&i[t](e)},addEventListeners:function(){e.map.on("mousemove",i.mousemove),e.map.on("mousedown",i.mousedown),e.map.on("mouseup",i.mouseup),e.map.on("data",i.data),e.map.on("touchmove",i.touchmove),e.map.on("touchstart",i.touchstart),e.map.on("touchend",i.touchend),e.container.addEventListener("mouseout",i.mouseout),e.options.keybindings&&(e.container.addEventListener("keydown",i.keydown),e.container.addEventListener("keyup",i.keyup))},removeEventListeners:function(){e.map.off("mousemove",i.mousemove),e.map.off("mousedown",i.mousedown),e.map.off("mouseup",i.mouseup),e.map.off("data",i.data),e.map.off("touchmove",i.touchmove),e.map.off("touchstart",i.touchstart),e.map.off("touchend",i.touchend),e.container.removeEventListener("mouseout",i.mouseout),e.options.keybindings&&(e.container.removeEventListener("keydown",i.keydown),e.container.removeEventListener("keyup",i.keyup))},trash:function(t){s.trash(t)},combineFeatures:function(){s.combineFeatures()},uncombineFeatures:function(){s.uncombineFeatures()},getMode:function(){return a}}}(e),e.ui=function(t){var e={},n=null,r={mode:null,feature:null,mouse:null},o={mode:null,feature:null,mouse:null};function i(t){o=tt(o,t)}function a(){var e,n;if(t.container){var i=[],a=[];et.forEach((function(t){o[t]!==r[t]&&(i.push(t+"-"+r[t]),null!==o[t]&&a.push(t+"-"+o[t]))})),i.length>0&&(e=t.container.classList).remove.apply(e,i),a.length>0&&(n=t.container.classList).add.apply(n,a),r=tt(r,o)}}function s(t,e){void 0===e&&(e={});var r=document.createElement("button");return r.className=c.CONTROL_BUTTON+" "+e.className,r.setAttribute("title",e.title),e.container.appendChild(r),r.addEventListener("click",(function(r){if(r.preventDefault(),r.stopPropagation(),r.target===n)return u(),void e.onDeactivate();l(t),e.onActivate()}),!0),r}function u(){n&&(n.classList.remove(c.ACTIVE_BUTTON),n=null)}function l(t){u();var r=e[t];r&&r&&"trash"!==t&&(r.classList.add(c.ACTIVE_BUTTON),n=r)}return{setActiveButton:l,queueMapClasses:i,updateMapClasses:a,clearMapClasses:function(){i({mode:null,feature:null,mouse:null}),a()},addButtons:function(){var n=t.options.controls,r=document.createElement("div");return r.className=c.CONTROL_GROUP+" "+c.CONTROL_BASE,n?(n[d.LINE]&&(e[d.LINE]=s(d.LINE,{container:r,className:c.CONTROL_BUTTON_LINE,title:"LineString tool "+(t.options.keybindings?"(l)":""),onActivate:function(){return t.events.changeMode(h.DRAW_LINE_STRING)},onDeactivate:function(){return t.events.trash()}})),n[d.POLYGON]&&(e[d.POLYGON]=s(d.POLYGON,{container:r,className:c.CONTROL_BUTTON_POLYGON,title:"Polygon tool "+(t.options.keybindings?"(p)":""),onActivate:function(){return t.events.changeMode(h.DRAW_POLYGON)},onDeactivate:function(){return t.events.trash()}})),n[d.POINT]&&(e[d.POINT]=s(d.POINT,{container:r,className:c.CONTROL_BUTTON_POINT,title:"Marker tool "+(t.options.keybindings?"(m)":""),onActivate:function(){return t.events.changeMode(h.DRAW_POINT)},onDeactivate:function(){return t.events.trash()}})),n.trash&&(e.trash=s("trash",{container:r,className:c.CONTROL_BUTTON_TRASH,title:"Delete",onActivate:function(){t.events.trash()}})),n.combine_features&&(e.combine_features=s("combineFeatures",{container:r,className:c.CONTROL_BUTTON_COMBINE_FEATURES,title:"Combine",onActivate:function(){t.events.combineFeatures()}})),n.uncombine_features&&(e.uncombine_features=s("uncombineFeatures",{container:r,className:c.CONTROL_BUTTON_UNCOMBINE_FEATURES,title:"Uncombine",onActivate:function(){t.events.uncombineFeatures()}})),r):r},removeButtons:function(){Object.keys(e).forEach((function(t){var n=e[t];n.parentNode&&n.parentNode.removeChild(n),delete e[t]}))}}}(e),e.container=i.getContainer(),e.store=new W(e),n=e.ui.addButtons(),e.options.boxSelect&&(e.boxZoomInitial=i.boxZoom.isEnabled(),i.boxZoom.disable(),i.dragPan.disable(),i.dragPan.enable()),i.loaded()?o.connect():(i.on("load",o.connect),r=setInterval((function(){i.loaded()&&o.connect()}),16)),e.events.start(),n},addLayers:function(){e.map.addSource(l.COLD,{data:{type:f.FEATURE_COLLECTION,features:[]},type:"geojson"}),e.map.addSource(l.HOT,{data:{type:f.FEATURE_COLLECTION,features:[]},type:"geojson"}),e.options.styles.forEach((function(t){e.map.addLayer(t)})),e.store.setDirty(!0),e.store.render()},removeLayers:function(){e.options.styles.forEach((function(t){e.map.getLayer(t.id)&&e.map.removeLayer(t.id)})),e.map.getSource(l.COLD)&&e.map.removeSource(l.COLD),e.map.getSource(l.HOT)&&e.map.removeSource(l.HOT)}};return e.setup=o,o}var rt=[{id:"gl-draw-polygon-fill-inactive",type:"fill",filter:["all",["==","active","false"],["==","$type","Polygon"],["!=","mode","static"]],paint:{"fill-color":"#3bb2d0","fill-outline-color":"#3bb2d0","fill-opacity":.1}},{id:"gl-draw-polygon-fill-active",type:"fill",filter:["all",["==","active","true"],["==","$type","Polygon"]],paint:{"fill-color":"#fbb03b","fill-outline-color":"#fbb03b","fill-opacity":.1}},{id:"gl-draw-polygon-midpoint",type:"circle",filter:["all",["==","$type","Point"],["==","meta","midpoint"]],paint:{"circle-radius":3,"circle-color":"#fbb03b"}},{id:"gl-draw-polygon-stroke-inactive",type:"line",filter:["all",["==","active","false"],["==","$type","Polygon"],["!=","mode","static"]],layout:{"line-cap":"round","line-join":"round"},paint:{"line-color":"#3bb2d0","line-width":2}},{id:"gl-draw-polygon-stroke-active",type:"line",filter:["all",["==","active","true"],["==","$type","Polygon"]],layout:{"line-cap":"round","line-join":"round"},paint:{"line-color":"#fbb03b","line-dasharray":[.2,2],"line-width":2}},{id:"gl-draw-line-inactive",type:"line",filter:["all",["==","active","false"],["==","$type","LineString"],["!=","mode","static"]],layout:{"line-cap":"round","line-join":"round"},paint:{"line-color":"#3bb2d0","line-width":2}},{id:"gl-draw-line-active",type:"line",filter:["all",["==","$type","LineString"],["==","active","true"]],layout:{"line-cap":"round","line-join":"round"},paint:{"line-color":"#fbb03b","line-dasharray":[.2,2],"line-width":2}},{id:"gl-draw-polygon-and-line-vertex-stroke-inactive",type:"circle",filter:["all",["==","meta","vertex"],["==","$type","Point"],["!=","mode","static"]],paint:{"circle-radius":5,"circle-color":"#fff"}},{id:"gl-draw-polygon-and-line-vertex-inactive",type:"circle",filter:["all",["==","meta","vertex"],["==","$type","Point"],["!=","mode","static"]],paint:{"circle-radius":3,"circle-color":"#fbb03b"}},{id:"gl-draw-point-point-stroke-inactive",type:"circle",filter:["all",["==","active","false"],["==","$type","Point"],["==","meta","feature"],["!=","mode","static"]],paint:{"circle-radius":5,"circle-opacity":1,"circle-color":"#fff"}},{id:"gl-draw-point-inactive",type:"circle",filter:["all",["==","active","false"],["==","$type","Point"],["==","meta","feature"],["!=","mode","static"]],paint:{"circle-radius":3,"circle-color":"#3bb2d0"}},{id:"gl-draw-point-stroke-active",type:"circle",filter:["all",["==","$type","Point"],["==","active","true"],["!=","meta","midpoint"]],paint:{"circle-radius":7,"circle-color":"#fff"}},{id:"gl-draw-point-active",type:"circle",filter:["all",["==","$type","Point"],["!=","meta","midpoint"],["==","active","true"]],paint:{"circle-radius":5,"circle-color":"#fbb03b"}},{id:"gl-draw-polygon-fill-static",type:"fill",filter:["all",["==","mode","static"],["==","$type","Polygon"]],paint:{"fill-color":"#404040","fill-outline-color":"#404040","fill-opacity":.1}},{id:"gl-draw-polygon-stroke-static",type:"line",filter:["all",["==","mode","static"],["==","$type","Polygon"]],layout:{"line-cap":"round","line-join":"round"},paint:{"line-color":"#404040","line-width":2}},{id:"gl-draw-line-static",type:"line",filter:["all",["==","mode","static"],["==","$type","LineString"]],layout:{"line-cap":"round","line-join":"round"},paint:{"line-color":"#404040","line-width":2}},{id:"gl-draw-point-static",type:"circle",filter:["all",["==","mode","static"],["==","$type","Point"]],paint:{"circle-radius":5,"circle-color":"#404040"}}];function ot(t){return function(e){var n=e.featureTarget;return!!n&&!!n.properties&&n.properties.meta===t}}function it(t){return!!t.originalEvent&&!!t.originalEvent.shiftKey&&0===t.originalEvent.button}function at(t){return!!t.featureTarget&&!!t.featureTarget.properties&&t.featureTarget.properties.active===m.ACTIVE&&t.featureTarget.properties.meta===v.FEATURE}function st(t){return!!t.featureTarget&&!!t.featureTarget.properties&&t.featureTarget.properties.active===m.INACTIVE&&t.featureTarget.properties.meta===v.FEATURE}function ut(t){return void 0===t.featureTarget}function ct(t){return!!t.featureTarget&&!!t.featureTarget.properties&&t.featureTarget.properties.meta===v.FEATURE}function lt(t){var e=t.featureTarget;return!!e&&!!e.properties&&e.properties.meta===v.VERTEX}function pt(t){return!!t.originalEvent&&!0===t.originalEvent.shiftKey}function dt(t){return 27===t.keyCode}function ft(t){return 13===t.keyCode}var ht=Object.freeze({__proto__:null,isOfMetaType:ot,isShiftMousedown:it,isActiveFeature:at,isInactiveFeature:st,noTarget:ut,isFeature:ct,isVertex:lt,isShiftDown:pt,isEscapeKey:dt,isEnterKey:ft,isTrue:function(){return!0}}),gt=yt;function yt(t,e){this.x=t,this.y=e}yt.prototype={clone:function(){return new yt(this.x,this.y)},add:function(t){return this.clone()._add(t)},sub:function(t){return this.clone()._sub(t)},multByPoint:function(t){return this.clone()._multByPoint(t)},divByPoint:function(t){return this.clone()._divByPoint(t)},mult:function(t){return this.clone()._mult(t)},div:function(t){return this.clone()._div(t)},rotate:function(t){return this.clone()._rotate(t)},rotateAround:function(t,e){return this.clone()._rotateAround(t,e)},matMult:function(t){return this.clone()._matMult(t)},unit:function(){return this.clone()._unit()},perp:function(){return this.clone()._perp()},round:function(){return this.clone()._round()},mag:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},equals:function(t){return this.x===t.x&&this.y===t.y},dist:function(t){return Math.sqrt(this.distSqr(t))},distSqr:function(t){var e=t.x-this.x,n=t.y-this.y;return e*e+n*n},angle:function(){return Math.atan2(this.y,this.x)},angleTo:function(t){return Math.atan2(this.y-t.y,this.x-t.x)},angleWith:function(t){return this.angleWithSep(t.x,t.y)},angleWithSep:function(t,e){return Math.atan2(this.x*e-this.y*t,this.x*t+this.y*e)},_matMult:function(t){var e=t[0]*this.x+t[1]*this.y,n=t[2]*this.x+t[3]*this.y;return this.x=e,this.y=n,this},_add:function(t){return this.x+=t.x,this.y+=t.y,this},_sub:function(t){return this.x-=t.x,this.y-=t.y,this},_mult:function(t){return this.x*=t,this.y*=t,this},_div:function(t){return this.x/=t,this.y/=t,this},_multByPoint:function(t){return this.x*=t.x,this.y*=t.y,this},_divByPoint:function(t){return this.x/=t.x,this.y/=t.y,this},_unit:function(){return this._div(this.mag()),this},_perp:function(){var t=this.y;return this.y=this.x,this.x=-t,this},_rotate:function(t){var e=Math.cos(t),n=Math.sin(t),r=e*this.x-n*this.y,o=n*this.x+e*this.y;return this.x=r,this.y=o,this},_rotateAround:function(t,e){var n=Math.cos(t),r=Math.sin(t),o=e.x+n*(this.x-e.x)-r*(this.y-e.y),i=e.y+r*(this.x-e.x)+n*(this.y-e.y);return this.x=o,this.y=i,this},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}},yt.convert=function(t){return t instanceof yt?t:Array.isArray(t)?new yt(t[0],t[1]):t};var vt=e(gt);function mt(t,e){var n=e.getBoundingClientRect();return new vt(t.clientX-n.left-(e.clientLeft||0),t.clientY-n.top-(e.clientTop||0))}function bt(t,e,n,r){return{type:f.FEATURE,properties:{meta:v.VERTEX,parent:t,coord_path:n,active:r?m.ACTIVE:m.INACTIVE},geometry:{type:f.POINT,coordinates:e}}}function Et(t,e,n){var r=e.geometry.coordinates,o=n.geometry.coordinates;if(r[1]>85||r[1]85||o[1]=e&&this._bbox[3]>=n},zt.prototype.intersect=function(t){return this._valid?(e=t instanceof zt?t.bbox():t,!(this._bbox[0]>e[2]||this._bbox[2]e[3])):null;var e},zt.prototype._fastContains=function(){if(!this._valid)return new Function("return null;");var t="return "+this._bbox[0]+"<= ll[0] &&"+this._bbox[1]+"<= ll[1] &&"+this._bbox[2]+">= ll[0] &&"+this._bbox[3]+">= ll[1]";return new Function("ll",t)},zt.prototype.polygon=function(){return this._valid?{type:"Polygon",coordinates:[[[this._bbox[0],this._bbox[1]],[this._bbox[2],this._bbox[1]],[this._bbox[2],this._bbox[3]],[this._bbox[0],this._bbox[3]],[this._bbox[0],this._bbox[1]]]]}:null};var Jt=function(t){if(!t)return[];var e=Pt(xt(t)),n=[];return e.features.forEach((function(t){t.geometry&&(n=n.concat(Mt(t.geometry.coordinates)))})),n},Zt=Vt,Ht=Bt,qt={features:["FeatureCollection"],coordinates:["Point","MultiPoint","LineString","MultiLineString","Polygon","MultiPolygon"],geometry:["Feature"],geometries:["GeometryCollection"]},$t=Object.keys(qt);function Yt(t){for(var e=Ht(),n=Jt(t),r=0;rn&&(n=u),co&&(o=c),us&&(s=p)}));var u=e;return n+u.lat>85&&(u.lat=85-n),o+u.lat>90&&(u.lat=90-o),r+u.lat<-85&&(u.lat=-85-r),i+u.lat=270&&(u.lng-=360*Math.ceil(Math.abs(u.lng)/360)),u}function Qt(t,e){var n=Xt(t.map((function(t){return t.toGeoJSON()})),e);t.forEach((function(t){var e,r=t.getCoordinates(),o=function(t){var e={lng:t[0]+n.lng,lat:t[1]+n.lat};return[e.lng,e.lat]},i=function(t){return t.map((function(t){return o(t)}))};t.type===f.POINT?e=o(r):t.type===f.LINE_STRING||t.type===f.MULTI_POINT?e=r.map(o):t.type===f.POLYGON||t.type===f.MULTI_LINE_STRING?e=r.map(i):t.type===f.MULTI_POLYGON&&(e=r.map((function(t){return t.map((function(t){return i(t)}))}))),t.incomingCoords(e)}))}var te={onSetup:function(t){var e=this,n={dragMoveLocation:null,boxSelectStartLocation:null,boxSelectElement:void 0,boxSelecting:!1,canBoxSelect:!1,dragMoving:!1,canDragMove:!1,initiallySelectedFeatureIds:t.featureIds||[]};return this.setSelected(n.initiallySelectedFeatureIds.filter((function(t){return void 0!==e.getFeature(t)}))),this.fireActionable(),this.setActionableState({combineFeatures:!0,uncombineFeatures:!0,trash:!0}),n},fireUpdate:function(){this.map.fire(g.UPDATE,{action:y.MOVE,features:this.getSelected().map((function(t){return t.toGeoJSON()}))})},fireActionable:function(){var t=this,e=this.getSelected(),n=e.filter((function(e){return t.isInstanceOf("MultiFeature",e)})),r=!1;if(e.length>1){r=!0;var o=e[0].type.replace("Multi","");e.forEach((function(t){t.type.replace("Multi","")!==o&&(r=!1)}))}var i=n.length>0,a=e.length>0;this.setActionableState({combineFeatures:r,uncombineFeatures:i,trash:a})},getUniqueIds:function(t){return t.length?t.map((function(t){return t.properties.id})).filter((function(t){return void 0!==t})).reduce((function(t,e){return t.add(e),t}),new w).values():[]},stopExtendedInteractions:function(t){t.boxSelectElement&&(t.boxSelectElement.parentNode&&t.boxSelectElement.parentNode.removeChild(t.boxSelectElement),t.boxSelectElement=null),this.map.dragPan.enable(),t.boxSelecting=!1,t.canBoxSelect=!1,t.dragMoving=!1,t.canDragMove=!1},onStop:function(){Ot.enable(this)},onMouseMove:function(t,e){return ct(e)&&t.dragMoving&&this.fireUpdate(),this.stopExtendedInteractions(t),!0},onMouseOut:function(t){return!t.dragMoving||this.fireUpdate()}};te.onTap=te.onClick=function(t,e){return ut(e)?this.clickAnywhere(t,e):ot(v.VERTEX)(e)?this.clickOnVertex(t,e):ct(e)?this.clickOnFeature(t,e):void 0},te.clickAnywhere=function(t){var e=this,n=this.getSelectedIds();n.length&&(this.clearSelectedFeatures(),n.forEach((function(t){return e.doRender(t)}))),Ot.enable(this),this.stopExtendedInteractions(t)},te.clickOnVertex=function(t,e){this.changeMode(h.DIRECT_SELECT,{featureId:e.featureTarget.properties.parent,coordPath:e.featureTarget.properties.coord_path,startPos:e.lngLat}),this.updateUIClasses({mouse:p.MOVE})},te.startOnActiveFeature=function(t,e){this.stopExtendedInteractions(t),this.map.dragPan.disable(),this.doRender(e.featureTarget.properties.id),t.canDragMove=!0,t.dragMoveLocation=e.lngLat},te.clickOnFeature=function(t,e){var n=this;Ot.disable(this),this.stopExtendedInteractions(t);var r=pt(e),o=this.getSelectedIds(),i=e.featureTarget.properties.id,a=this.isSelected(i);if(!r&&a&&this.getFeature(i).type!==f.POINT)return this.changeMode(h.DIRECT_SELECT,{featureId:i});a&&r?(this.deselect(i),this.updateUIClasses({mouse:p.POINTER}),1===o.length&&Ot.enable(this)):!a&&r?(this.select(i),this.updateUIClasses({mouse:p.MOVE})):a||r||(o.forEach((function(t){return n.doRender(t)})),this.setSelected(i),this.updateUIClasses({mouse:p.MOVE})),this.doRender(i)},te.onMouseDown=function(t,e){return at(e)?this.startOnActiveFeature(t,e):this.drawConfig.boxSelect&&it(e)?this.startBoxSelect(t,e):void 0},te.startBoxSelect=function(t,e){this.stopExtendedInteractions(t),this.map.dragPan.disable(),t.boxSelectStartLocation=mt(e.originalEvent,this.map.getContainer()),t.canBoxSelect=!0},te.onTouchStart=function(t,e){if(at(e))return this.startOnActiveFeature(t,e)},te.onDrag=function(t,e){return t.canDragMove?this.dragMove(t,e):this.drawConfig.boxSelect&&t.canBoxSelect?this.whileBoxSelect(t,e):void 0},te.whileBoxSelect=function(t,e){t.boxSelecting=!0,this.updateUIClasses({mouse:p.ADD}),t.boxSelectElement||(t.boxSelectElement=document.createElement("div"),t.boxSelectElement.classList.add(c.BOX_SELECT),this.map.getContainer().appendChild(t.boxSelectElement));var n=mt(e.originalEvent,this.map.getContainer()),r=Math.min(t.boxSelectStartLocation.x,n.x),o=Math.max(t.boxSelectStartLocation.x,n.x),i=Math.min(t.boxSelectStartLocation.y,n.y),a=Math.max(t.boxSelectStartLocation.y,n.y),s="translate("+r+"px, "+i+"px)";t.boxSelectElement.style.transform=s,t.boxSelectElement.style.WebkitTransform=s,t.boxSelectElement.style.width=o-r+"px",t.boxSelectElement.style.height=a-i+"px"},te.dragMove=function(t,e){t.dragMoving=!0,e.originalEvent.stopPropagation();var n={lng:e.lngLat.lng-t.dragMoveLocation.lng,lat:e.lngLat.lat-t.dragMoveLocation.lat};Qt(this.getSelected(),n),t.dragMoveLocation=e.lngLat},te.onTouchEnd=te.onMouseUp=function(t,e){var n=this;if(t.dragMoving)this.fireUpdate();else if(t.boxSelecting){var r=[t.boxSelectStartLocation,mt(e.originalEvent,this.map.getContainer())],o=this.featuresAt(null,r,"click"),i=this.getUniqueIds(o).filter((function(t){return!n.isSelected(t)}));i.length&&(this.select(i),i.forEach((function(t){return n.doRender(t)})),this.updateUIClasses({mouse:p.MOVE}))}this.stopExtendedInteractions(t)},te.toDisplayFeatures=function(t,e,n){e.properties.active=this.isSelected(e.properties.id)?m.ACTIVE:m.INACTIVE,n(e),this.fireActionable(),e.properties.active===m.ACTIVE&&e.geometry.type!==f.POINT&&_t(e).forEach(n)},te.onTrash=function(){this.deleteFeature(this.getSelectedIds()),this.fireActionable()},te.onCombineFeatures=function(){var t=this.getSelected();if(!(0===t.length||t.length<2)){for(var e=[],n=[],r=t[0].type.replace("Multi",""),o=0;o1){var a=this.newFeature({type:f.FEATURE,properties:n[0].properties,geometry:{type:"Multi"+r,coordinates:e}});this.addFeature(a),this.deleteFeature(this.getSelectedIds(),{silent:!0}),this.setSelected([a.id]),this.map.fire(g.COMBINE_FEATURES,{createdFeatures:[a.toGeoJSON()],deletedFeatures:n})}this.fireActionable()}},te.onUncombineFeatures=function(){var t=this,e=this.getSelected();if(0!==e.length){for(var n=[],r=[],o=function(o){var i=e[o];t.isInstanceOf("MultiFeature",i)&&(i.getFeatures().forEach((function(e){t.addFeature(e),e.properties=i.properties,n.push(e.toGeoJSON()),t.select([e.id])})),t.deleteFeature(i.id,{silent:!0}),r.push(i.toGeoJSON()))},i=0;i1&&this.map.fire(g.UNCOMBINE_FEATURES,{createdFeatures:n,deletedFeatures:r}),this.fireActionable()}};var ee=ot(v.VERTEX),ne=ot(v.MIDPOINT),re={fireUpdate:function(){this.map.fire(g.UPDATE,{action:y.CHANGE_COORDINATES,features:this.getSelected().map((function(t){return t.toGeoJSON()}))})},fireActionable:function(t){this.setActionableState({combineFeatures:!1,uncombineFeatures:!1,trash:t.selectedCoordPaths.length>0})},startDragging:function(t,e){this.map.dragPan.disable(),t.canDragMove=!0,t.dragMoveLocation=e.lngLat},stopDragging:function(t){this.map.dragPan.enable(),t.dragMoving=!1,t.canDragMove=!1,t.dragMoveLocation=null},onVertex:function(t,e){this.startDragging(t,e);var n=e.featureTarget.properties,r=t.selectedCoordPaths.indexOf(n.coord_path);pt(e)||-1!==r?pt(e)&&-1===r&&t.selectedCoordPaths.push(n.coord_path):t.selectedCoordPaths=[n.coord_path];var o=this.pathsToCoordinates(t.featureId,t.selectedCoordPaths);this.setSelectedCoordinates(o)},onMidpoint:function(t,e){this.startDragging(t,e);var n=e.featureTarget.properties;t.feature.addCoordinate(n.coord_path,n.lng,n.lat),this.fireUpdate(),t.selectedCoordPaths=[n.coord_path]},pathsToCoordinates:function(t,e){return e.map((function(e){return{feature_id:t,coord_path:e}}))},onFeature:function(t,e){0===t.selectedCoordPaths.length?this.startDragging(t,e):this.stopDragging(t)},dragFeature:function(t,e,n){Qt(this.getSelected(),n),t.dragMoveLocation=e.lngLat},dragVertex:function(t,e,n){for(var r=t.selectedCoordPaths.map((function(e){return t.feature.getCoordinate(e)})),o=Xt(r.map((function(t){return{type:f.FEATURE,properties:{},geometry:{type:f.POINT,coordinates:t}}})),n),i=0;i0?this.dragVertex(t,e,n):this.dragFeature(t,e,n),t.dragMoveLocation=e.lngLat}},re.onClick=function(t,e){return ut(e)?this.clickNoTarget(t,e):at(e)?this.clickActiveFeature(t,e):st(e)?this.clickInactive(t,e):void this.stopDragging(t)},re.onTap=function(t,e){return ut(e)?this.clickNoTarget(t,e):at(e)?this.clickActiveFeature(t,e):st(e)?this.clickInactive(t,e):void 0},re.onTouchEnd=re.onMouseUp=function(t){t.dragMoving&&this.fireUpdate(),this.stopDragging(t)};var oe={};function ie(t,e){return!!t.lngLat&&t.lngLat.lng===e[0]&&t.lngLat.lat===e[1]}oe.onSetup=function(){var t=this.newFeature({type:f.FEATURE,properties:{},geometry:{type:f.POINT,coordinates:[]}});return this.addFeature(t),this.clearSelectedFeatures(),this.updateUIClasses({mouse:p.ADD}),this.activateUIButton(d.POINT),this.setActionableState({trash:!0}),{point:t}},oe.stopDrawingAndRemove=function(t){this.deleteFeature([t.point.id],{silent:!0}),this.changeMode(h.SIMPLE_SELECT)},oe.onTap=oe.onClick=function(t,e){this.updateUIClasses({mouse:p.MOVE}),t.point.updateCoordinate("",e.lngLat.lng,e.lngLat.lat),this.map.fire(g.CREATE,{features:[t.point.toGeoJSON()]}),this.changeMode(h.SIMPLE_SELECT,{featureIds:[t.point.id]})},oe.onStop=function(t){this.activateUIButton(),t.point.getCoordinate().length||this.deleteFeature([t.point.id],{silent:!0})},oe.toDisplayFeatures=function(t,e,n){var r=e.properties.id===t.point.id;if(e.properties.active=r?m.ACTIVE:m.INACTIVE,!r)return n(e)},oe.onTrash=oe.stopDrawingAndRemove,oe.onKeyUp=function(t,e){if(dt(e)||ft(e))return this.stopDrawingAndRemove(t,e)};var ae={onSetup:function(){var t=this.newFeature({type:f.FEATURE,properties:{},geometry:{type:f.POLYGON,coordinates:[[]]}});return this.addFeature(t),this.clearSelectedFeatures(),Ot.disable(this),this.updateUIClasses({mouse:p.ADD}),this.activateUIButton(d.POLYGON),this.setActionableState({trash:!0}),{polygon:t,currentVertexPosition:0}},clickAnywhere:function(t,e){if(t.currentVertexPosition>0&&ie(e,t.polygon.coordinates[0][t.currentVertexPosition-1]))return this.changeMode(h.SIMPLE_SELECT,{featureIds:[t.polygon.id]});this.updateUIClasses({mouse:p.ADD}),t.polygon.updateCoordinate("0."+t.currentVertexPosition,e.lngLat.lng,e.lngLat.lat),t.currentVertexPosition++,t.polygon.updateCoordinate("0."+t.currentVertexPosition,e.lngLat.lng,e.lngLat.lat)},clickOnVertex:function(t){return this.changeMode(h.SIMPLE_SELECT,{featureIds:[t.polygon.id]})},onMouseMove:function(t,e){t.polygon.updateCoordinate("0."+t.currentVertexPosition,e.lngLat.lng,e.lngLat.lat),lt(e)&&this.updateUIClasses({mouse:p.POINTER})}};ae.onTap=ae.onClick=function(t,e){return lt(e)?this.clickOnVertex(t,e):this.clickAnywhere(t,e)},ae.onKeyUp=function(t,e){dt(e)?(this.deleteFeature([t.polygon.id],{silent:!0}),this.changeMode(h.SIMPLE_SELECT)):ft(e)&&this.changeMode(h.SIMPLE_SELECT,{featureIds:[t.polygon.id]})},ae.onStop=function(t){this.updateUIClasses({mouse:p.NONE}),Ot.enable(this),this.activateUIButton(),void 0!==this.getFeature(t.polygon.id)&&(t.polygon.removeCoordinate("0."+t.currentVertexPosition),t.polygon.isValid()?this.map.fire(g.CREATE,{features:[t.polygon.toGeoJSON()]}):(this.deleteFeature([t.polygon.id],{silent:!0}),this.changeMode(h.SIMPLE_SELECT,{},{silent:!0})))},ae.toDisplayFeatures=function(t,e,n){var r=e.properties.id===t.polygon.id;if(e.properties.active=r?m.ACTIVE:m.INACTIVE,!r)return n(e);if(0!==e.geometry.coordinates.length){var o=e.geometry.coordinates[0].length;if(!(o<3)){if(e.properties.meta=v.FEATURE,n(bt(t.polygon.id,e.geometry.coordinates[0][0],"0.0",!1)),o>3){var i=e.geometry.coordinates[0].length-3;n(bt(t.polygon.id,e.geometry.coordinates[0][i],"0."+i,!1))}if(o<=4){var a=[[e.geometry.coordinates[0][0][0],e.geometry.coordinates[0][0][1]],[e.geometry.coordinates[0][1][0],e.geometry.coordinates[0][1][1]]];if(n({type:f.FEATURE,properties:e.properties,geometry:{coordinates:a,type:f.LINE_STRING}}),3===o)return}return n(e)}}},ae.onTrash=function(t){this.deleteFeature([t.polygon.id],{silent:!0}),this.changeMode(h.SIMPLE_SELECT)};var se={onSetup:function(t){var e,n,r=(t=t||{}).featureId,o="forward";if(r){if(!(e=this.getFeature(r)))throw new Error("Could not find a feature with the provided featureId");var i=t.from;if(i&&"Feature"===i.type&&i.geometry&&"Point"===i.geometry.type&&(i=i.geometry),i&&"Point"===i.type&&i.coordinates&&2===i.coordinates.length&&(i=i.coordinates),!i||!Array.isArray(i))throw new Error("Please use the `from` property to indicate which point to continue the line from");var a=e.coordinates.length-1;if(e.coordinates[a][0]===i[0]&&e.coordinates[a][1]===i[1])n=a+1,e.addCoordinate.apply(e,[n].concat(e.coordinates[a]));else{if(e.coordinates[0][0]!==i[0]||e.coordinates[0][1]!==i[1])throw new Error("`from` should match the point at either the start or the end of the provided LineString");o="backwards",n=0,e.addCoordinate.apply(e,[n].concat(e.coordinates[0]))}}else e=this.newFeature({type:f.FEATURE,properties:{},geometry:{type:f.LINE_STRING,coordinates:[]}}),n=0,this.addFeature(e);return this.clearSelectedFeatures(),Ot.disable(this),this.updateUIClasses({mouse:p.ADD}),this.activateUIButton(d.LINE),this.setActionableState({trash:!0}),{line:e,currentVertexPosition:n,direction:o}},clickAnywhere:function(t,e){if(t.currentVertexPosition>0&&ie(e,t.line.coordinates[t.currentVertexPosition-1])||"backwards"===t.direction&&ie(e,t.line.coordinates[t.currentVertexPosition+1]))return this.changeMode(h.SIMPLE_SELECT,{featureIds:[t.line.id]});this.updateUIClasses({mouse:p.ADD}),t.line.updateCoordinate(t.currentVertexPosition,e.lngLat.lng,e.lngLat.lat),"forward"===t.direction?(t.currentVertexPosition++,t.line.updateCoordinate(t.currentVertexPosition,e.lngLat.lng,e.lngLat.lat)):t.line.addCoordinate(0,e.lngLat.lng,e.lngLat.lat)},clickOnVertex:function(t){return this.changeMode(h.SIMPLE_SELECT,{featureIds:[t.line.id]})},onMouseMove:function(t,e){t.line.updateCoordinate(t.currentVertexPosition,e.lngLat.lng,e.lngLat.lat),lt(e)&&this.updateUIClasses({mouse:p.POINTER})}};se.onTap=se.onClick=function(t,e){if(lt(e))return this.clickOnVertex(t,e);this.clickAnywhere(t,e)},se.onKeyUp=function(t,e){ft(e)?this.changeMode(h.SIMPLE_SELECT,{featureIds:[t.line.id]}):dt(e)&&(this.deleteFeature([t.line.id],{silent:!0}),this.changeMode(h.SIMPLE_SELECT))},se.onStop=function(t){Ot.enable(this),this.activateUIButton(),void 0!==this.getFeature(t.line.id)&&(t.line.removeCoordinate(""+t.currentVertexPosition),t.line.isValid()?this.map.fire(g.CREATE,{features:[t.line.toGeoJSON()]}):(this.deleteFeature([t.line.id],{silent:!0}),this.changeMode(h.SIMPLE_SELECT,{},{silent:!0})))},se.onTrash=function(t){this.deleteFeature([t.line.id],{silent:!0}),this.changeMode(h.SIMPLE_SELECT)},se.toDisplayFeatures=function(t,e,n){var r=e.properties.id===t.line.id;if(e.properties.active=r?m.ACTIVE:m.INACTIVE,!r)return n(e);e.geometry.coordinates.length<2||(e.properties.meta=v.FEATURE,n(bt(t.line.id,e.geometry.coordinates["forward"===t.direction?e.geometry.coordinates.length-2:1],""+("forward"===t.direction?e.geometry.coordinates.length-2:1),!1)),n(e))};var ue={simple_select:te,direct_select:re,draw_point:oe,draw_polygon:ae,draw_line_string:se},ce={defaultMode:h.SIMPLE_SELECT,keybindings:!0,touchEnabled:!0,clickBuffer:2,touchBuffer:25,boxSelect:!0,displayControlsDefault:!0,styles:rt,modes:ue,controls:{},userProperties:!1},le={point:!0,line_string:!0,polygon:!0,trash:!0,combine_features:!0,uncombine_features:!0},pe={point:!1,line_string:!1,polygon:!1,trash:!1,combine_features:!1,uncombine_features:!1};function de(t,e){return t.map((function(t){return t.source?t:tt(t,{id:t.id+"."+e,source:"hot"===e?l.HOT:l.COLD})}))}var fe={exports:{}};!function(t,e){var r="__lodash_hash_undefined__",o=9007199254740991,i="[object Arguments]",a="[object Array]",s="[object Boolean]",u="[object Date]",c="[object Error]",l="[object Function]",p="[object Map]",d="[object Number]",f="[object Object]",h="[object Promise]",g="[object RegExp]",y="[object Set]",v="[object String]",m="[object Symbol]",b="[object WeakMap]",E="[object ArrayBuffer]",_="[object DataView]",O=/^\[object .+?Constructor\]$/,S=/^(?:0|[1-9]\d*)$/,I={};I["[object Float32Array]"]=I["[object Float64Array]"]=I["[object Int8Array]"]=I["[object Int16Array]"]=I["[object Int32Array]"]=I["[object Uint8Array]"]=I["[object Uint8ClampedArray]"]=I["[object Uint16Array]"]=I["[object Uint32Array]"]=!0,I[i]=I[a]=I[E]=I[s]=I[_]=I[u]=I[c]=I[l]=I[p]=I[d]=I[f]=I[g]=I[y]=I[v]=I[b]=!1;var C="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,w="object"==typeof self&&self&&self.Object===Object&&self,T=C||w||Function("return this")(),x=e&&!e.nodeType&&e,P=x&&t&&!t.nodeType&&t,M=P&&P.exports===x,L=M&&C.process,A=function(){try{return L&&L.binding&&L.binding("util")}catch(t){}}(),F=A&&A.isTypedArray;function N(t,e){for(var n=-1,r=null==t?0:t.length;++ns))return!1;var c=i.get(t);if(c&&i.get(e))return c==e;var l=-1,p=!0,d=2&n?new Et:void 0;for(i.set(t,e),i.set(e,t);++l-1},mt.prototype.set=function(t,e){var n=this.__data__,r=St(n,t);return r<0?(++this.size,n.push([t,e])):n[r][1]=e,this},bt.prototype.clear=function(){this.size=0,this.__data__={hash:new vt,map:new(it||mt),string:new vt}},bt.prototype.delete=function(t){var e=Lt(this,t).delete(t);return this.size-=e?1:0,e},bt.prototype.get=function(t){return Lt(this,t).get(t)},bt.prototype.has=function(t){return Lt(this,t).has(t)},bt.prototype.set=function(t,e){var n=Lt(this,t),r=n.size;return n.set(t,e),this.size+=n.size==r?0:1,this},Et.prototype.add=Et.prototype.push=function(t){return this.__data__.set(t,r),this},Et.prototype.has=function(t){return this.__data__.has(t)},_t.prototype.clear=function(){this.__data__=new mt,this.size=0},_t.prototype.delete=function(t){var e=this.__data__,n=e.delete(t);return this.size=e.size,n},_t.prototype.get=function(t){return this.__data__.get(t)},_t.prototype.has=function(t){return this.__data__.has(t)},_t.prototype.set=function(t,e){var n=this.__data__;if(n instanceof mt){var r=n.__data__;if(!it||r.length<199)return r.push([t,e]),this.size=++n.size,this;n=this.__data__=new bt(r)}return n.set(t,e),this.size=n.size,this};var Ft=et?function(t){return null==t?[]:(t=Object(t),function(t,e){for(var n=-1,r=null==t?0:t.length,o=0,i=[];++n-1&&t%1==0&&t-1&&t%1==0&&t<=o}function zt(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}function Jt(t){return null!=t&&"object"==typeof t}var Zt=F?function(t){return function(e){return t(e)}}(F):function(t){return Jt(t)&&Bt(t.length)&&!!I[It(t)]};function Ht(t){return null!=(e=t)&&Bt(e.length)&&!Vt(e)?Ot(t):xt(t);var e}t.exports=function(t,e){return wt(t,e)}}(fe,fe.exports);var he=e(fe.exports);function ge(t,e){return t.length===e.length&&JSON.stringify(t.map((function(t){return t})).sort())===JSON.stringify(e.map((function(t){return t})).sort())}var ye={Polygon:G,LineString:U,Point:k,MultiPolygon:z,MultiLineString:z,MultiPoint:z},ve=Object.freeze({__proto__:null,CommonSelectors:ht,constrainFeatureMovement:Xt,createMidPoint:Et,createSupplementaryPoints:_t,createVertex:bt,doubleClickZoom:Ot,euclideanDistance:L,featuresAt:x,getFeatureAtAndSetCursors:M,isClick:A,isEventAtCoordinates:ie,isTap:F,mapEventToBoundingBox:C,ModeHandler:t,moveFeatures:Qt,sortFeatures:I,stringSetsAreEqual:ge,StringSet:w,theme:rt,toDenseArray:$}),me=function(t,e){var n={options:t=function(t){void 0===t&&(t={});var e=tt(t);return t.controls||(e.controls={}),!1===t.displayControlsDefault?e.controls=tt(pe,t.controls):e.controls=tt(le,t.controls),(e=tt(ce,e)).styles=de(e.styles,"cold").concat(de(e.styles,"hot")),e}(t)};e=function(t,e){return e.modes=h,e.getFeatureIdsAt=function(e){return x.click({point:e},null,t).map((function(t){return t.properties.id}))},e.getSelectedIds=function(){return t.store.getSelectedIds()},e.getSelected=function(){return{type:f.FEATURE_COLLECTION,features:t.store.getSelectedIds().map((function(e){return t.store.get(e)})).map((function(t){return t.toGeoJSON()}))}},e.getSelectedPoints=function(){return{type:f.FEATURE_COLLECTION,features:t.store.getSelectedCoordinates().map((function(t){return{type:f.FEATURE,properties:{},geometry:{type:f.POINT,coordinates:t.coordinates}}}))}},e.set=function(n){if(void 0===n.type||n.type!==f.FEATURE_COLLECTION||!Array.isArray(n.features))throw new Error("Invalid FeatureCollection");var r=t.store.createRenderBatch(),o=t.store.getAllIds().slice(),i=e.add(n),a=new w(i);return(o=o.filter((function(t){return!a.has(t)}))).length&&e.delete(o),r(),i},e.add=function(e){var n=JSON.parse(JSON.stringify(wt(e))).features.map((function(e){if(e.id=e.id||D(),null===e.geometry)throw new Error("Invalid geometry: null");if(void 0===t.store.get(e.id)||t.store.get(e.id).type!==e.geometry.type){var n=ye[e.geometry.type];if(void 0===n)throw new Error("Invalid geometry type: "+e.geometry.type+".");var r=new n(t,e);t.store.add(r)}else{var o=t.store.get(e.id);o.properties=e.properties,he(o.getCoordinates(),e.geometry.coordinates)||o.incomingCoords(e.geometry.coordinates)}return e.id}));return t.store.render(),n},e.get=function(e){var n=t.store.get(e);if(n)return n.toGeoJSON()},e.getAll=function(){return{type:f.FEATURE_COLLECTION,features:t.store.getAll().map((function(t){return t.toGeoJSON()}))}},e.delete=function(n){return t.store.delete(n,{silent:!0}),e.getMode()!==h.DIRECT_SELECT||t.store.getSelectedIds().length?t.store.render():t.events.changeMode(h.SIMPLE_SELECT,void 0,{silent:!0}),e},e.deleteAll=function(){return t.store.delete(t.store.getAllIds(),{silent:!0}),e.getMode()===h.DIRECT_SELECT?t.events.changeMode(h.SIMPLE_SELECT,void 0,{silent:!0}):t.store.render(),e},e.changeMode=function(n,r){return void 0===r&&(r={}),n===h.SIMPLE_SELECT&&e.getMode()===h.SIMPLE_SELECT?(ge(r.featureIds||[],t.store.getSelectedIds())||(t.store.setSelected(r.featureIds,{silent:!0}),t.store.render()),e):(n===h.DIRECT_SELECT&&e.getMode()===h.DIRECT_SELECT&&r.featureId===t.store.getSelectedIds()[0]||t.events.changeMode(n,r,{silent:!0}),e)},e.getMode=function(){return t.events.getMode()},e.trash=function(){return t.events.trash({silent:!0}),e},e.combineFeatures=function(){return t.events.combineFeatures({silent:!0}),e},e.uncombineFeatures=function(){return t.events.uncombineFeatures({silent:!0}),e},e.setFeatureProperty=function(n,r,o){return t.store.setFeatureProperty(n,r,o),e},e}(n,e),n.api=e;var r=nt(n);return e.onAdd=r.onAdd,e.onRemove=r.onRemove,e.types=d,e.options=t,e};function be(t){me(t,this)}return be.modes=ue,be.constants=_,be.lib=ve,be}()}},e={};function n(r){var o=e[r];if(void 0!==o)return o.exports;var i=e[r]={exports:{}};return t[r].call(i.exports,i,i.exports,n),i.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var r in e)n.o(e,r)&&!n.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:e[r]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),n.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var r={};n.d(r,{AV:()=>Be,WK:()=>Ee,wU:()=>Ye,K9:()=>ue,aV:()=>Fe,Aq:()=>o});var o={};n.r(o),n.d(o,{IDS:()=>zt,addPointToVertices:()=>Jt,createSnapList:()=>Zt,getGuideFeature:()=>Wt,shouldHideGuide:()=>Kt,snap:()=>Yt});var i=n(188),a=n.n(i),s=6371008.8,u={centimeters:637100880,centimetres:637100880,degrees:360/(2*Math.PI),feet:20902260.511392,inches:39.37*s,kilometers:6371.0088,kilometres:6371.0088,meters:s,metres:s,miles:3958.761333810546,millimeters:6371008800,millimetres:6371008800,nauticalmiles:s/1852,radians:1,yards:6967335.223679999};function c(t,e,n={}){const r={type:"Feature"};return(0===n.id||n.id)&&(r.id=n.id),n.bbox&&(r.bbox=n.bbox),r.properties=e||{},r.geometry=t,r}function l(t,e,n={}){if(!t)throw new Error("coordinates is required");if(!Array.isArray(t))throw new Error("coordinates must be an Array");if(t.length<2)throw new Error("coordinates must be at least 2 numbers long");if(!b(t[0])||!b(t[1]))throw new Error("coordinates must contain numbers");return c({type:"Point",coordinates:t},e,n)}function p(t,e,n={}){for(const e of t){if(e.length<4)throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");if(e[e.length-1].length!==e[0].length)throw new Error("First and last Position are not equivalent.");for(let t=0;tc==l>-c?(i=c,c=e[++p]):(i=l,l=r[++d]);let f=0;if(pc==l>-c?(a=c+i,s=i-(a-c),c=e[++p]):(a=l+i,s=i-(a-l),l=r[++d]),i=a,0!==s&&(o[f++]=s);pc==l>-c?(a=i+c,u=a-i,s=i-(a-u)+(c-u),c=e[++p]):(a=i+l,u=a-i,s=i-(a-u)+(l-u),l=r[++d]),i=a,0!==s&&(o[f++]=s);for(;p=w*c?u:-function(t,e,n,r,o,i,a){let s,u,c,l,p,d,f,h,g,y,v,m,b,E,_,C,w,N;const j=t-o,D=n-o,R=e-i,k=r-i;E=j*k,d=O*j,f=d-(d-j),h=j-f,d=O*k,g=d-(d-k),y=k-g,_=h*y-(E-f*g-h*g-f*y),C=R*D,d=O*R,f=d-(d-R),h=R-f,d=O*D,g=d-(d-D),y=D-g,w=h*y-(C-f*g-h*g-f*y),v=_-w,p=_-v,P[0]=_-(v+p)+(p-w),m=E+v,p=m-E,b=E-(m-p)+(v-p),v=b-C,p=b-v,P[1]=b-(v+p)+(p-C),N=m+v,p=N-m,P[2]=m-(N-p)+(v-p),P[3]=N;let U=function(t,e){let n=e[0];for(let r=1;r=G||-U>=G)return U;if(p=t-j,s=t-(j+p)+(p-o),p=n-D,c=n-(D+p)+(p-o),p=e-R,u=e-(R+p)+(p-i),p=r-k,l=r-(k+p)+(p-i),0===s&&0===u&&0===c&&0===l)return U;if(G=x*a+S*Math.abs(U),U+=j*l+k*s-(R*c+D*u),U>=G||-U>=G)return U;E=s*k,d=O*s,f=d-(d-s),h=s-f,d=O*k,g=d-(d-k),y=k-g,_=h*y-(E-f*g-h*g-f*y),C=u*D,d=O*u,f=d-(d-u),h=u-f,d=O*D,g=d-(d-D),y=D-g,w=h*y-(C-f*g-h*g-f*y),v=_-w,p=_-v,F[0]=_-(v+p)+(p-w),m=E+v,p=m-E,b=E-(m-p)+(v-p),v=b-C,p=b-v,F[1]=b-(v+p)+(p-C),N=m+v,p=N-m,F[2]=m-(N-p)+(v-p),F[3]=N;const V=I(4,P,4,F,M);E=j*l,d=O*j,f=d-(d-j),h=j-f,d=O*l,g=d-(d-l),y=l-g,_=h*y-(E-f*g-h*g-f*y),C=R*c,d=O*R,f=d-(d-R),h=R-f,d=O*c,g=d-(d-c),y=c-g,w=h*y-(C-f*g-h*g-f*y),v=_-w,p=_-v,F[0]=_-(v+p)+(p-w),m=E+v,p=m-E,b=E-(m-p)+(v-p),v=b-C,p=b-v,F[1]=b-(v+p)+(p-C),N=m+v,p=N-m,F[2]=m-(N-p)+(v-p),F[3]=N;const B=I(V,M,4,F,L);E=s*l,d=O*s,f=d-(d-s),h=s-f,d=O*l,g=d-(d-l),y=l-g,_=h*y-(E-f*g-h*g-f*y),C=u*c,d=O*u,f=d-(d-u),h=u-f,d=O*c,g=d-(d-c),y=c-g,w=h*y-(C-f*g-h*g-f*y),v=_-w,p=_-v,F[0]=_-(v+p)+(p-w),m=E+v,p=m-E,b=E-(m-p)+(v-p),v=b-C,p=b-v,F[1]=b-(v+p)+(p-C),N=m+v,p=N-m,F[2]=m-(N-p)+(v-p),F[3]=N;const z=I(B,L,4,F,A);return A[z-1]}(t,e,n,r,o,i,c)}C(4),C(4),C(4),C(4),C(4),C(4),C(4),C(4),C(4),C(8),C(8),C(8),C(4),C(8),C(8),C(8),C(12);C(192),C(192);C(4),C(4),C(4),C(4),C(4),C(4),C(4),C(4),C(8),C(8),C(8),C(8),C(8),C(8),C(8),C(8),C(8),C(4),C(4),C(4),C(8),C(16),C(16),C(16),C(32),C(32),C(48),C(64);C(1152),C(1152);C(4),C(4),C(4),C(4),C(4),C(4),C(4),C(4),C(4),C(4),C(24),C(24),C(24),C(24),C(24),C(24),C(24),C(24),C(24),C(24),C(1152),C(1152),C(1152),C(1152),C(1152),C(2304),C(2304),C(3456),C(5760),C(8),C(8),C(8),C(16),C(24),C(48),C(48),C(96),C(192),C(384),C(384),C(384),C(768);C(96),C(96),C(96),C(1152);function j(t,e){var n,r,o,i,a,s,u,c,l,p=0,d=t[0],f=t[1],h=e.length;for(n=0;n=0||i<=0&&s>=0)return 0}else if(u>=0&&a<0||u<0&&a>=0){if(0===(o=N(i,s,a,u,0,0)))return 0;(o>0&&u>0&&a<=0||o<0&&u<=0&&a>0)&&p++}c=l,a=u,i=s}}return p%2!=0}function D(t){if(!t)throw new Error("coord is required");if(!Array.isArray(t)){if("Feature"===t.type&&null!==t.geometry&&"Point"===t.geometry.type)return[...t.geometry.coordinates];if("Point"===t.type)return[...t.coordinates]}if(Array.isArray(t)&&t.length>=2&&!Array.isArray(t[0])&&!Array.isArray(t[1]))return[...t];throw new Error("coord must be GeoJSON Point or an Array of numbers")}function R(t){if(Array.isArray(t))return t;if("Feature"===t.type){if(null!==t.geometry)return t.geometry.coordinates}else if(t.coordinates)return t.coordinates;throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array")}function k(t){return"Feature"===t.type?t.geometry:t}function U(t,e,n={}){if(!t)throw new Error("point is required");if(!e)throw new Error("polygon is required");const r=D(t),o=k(e),i=o.type,a=e.bbox;let s=o.coordinates;if(a&&!1===function(t,e){return e[0]<=t[0]&&e[1]<=t[1]&&e[2]>=t[0]&&e[3]>=t[1]}(r,a))return!1;"Polygon"===i&&(s=[s]);let u=!1;for(var c=0;c0)for(let t=(this.length>>1)-1;t>=0;t--)this._down(t)}push(t){this.data.push(t),this.length++,this._up(this.length-1)}pop(){if(0===this.length)return;const t=this.data[0],e=this.data.pop();return this.length--,this.length>0&&(this.data[0]=e,this._down(0)),t}peek(){return this.data[0]}_up(t){const{data:e,compare:n}=this,r=e[t];for(;t>0;){const o=t-1>>1,i=e[o];if(n(r,i)>=0)break;e[t]=i,t=o}e[t]=r}_down(t){const{data:e,compare:n}=this,r=this.length>>1,o=e[t];for(;t=0)break;e[t]=i,t=r}e[t]=o}}function V(t,e){return te?1:0}function B(t,e){return t.p.x>e.p.x?1:t.p.xe.p.y?1:-1:1}function z(t,e){return t.rightSweepEvent.p.x>e.rightSweepEvent.p.x?1:t.rightSweepEvent.p.x0?(u.isLeftEndpoint=!0,s.isLeftEndpoint=!1):(s.isLeftEndpoint=!0,u.isLeftEndpoint=!1),e.push(s),e.push(u),o=i,q+=1}}Z+=1}class Y{constructor(t){this.leftSweepEvent=t,this.rightSweepEvent=t.otherEvent}}function W(t,e){if(null===t||null===e)return!1;if(t.leftSweepEvent.ringId===e.leftSweepEvent.ringId&&(t.rightSweepEvent.isSamePoint(e.leftSweepEvent)||t.rightSweepEvent.isSamePoint(e.leftSweepEvent)||t.rightSweepEvent.isSamePoint(e.rightSweepEvent)||t.leftSweepEvent.isSamePoint(e.leftSweepEvent)||t.leftSweepEvent.isSamePoint(e.rightSweepEvent)))return!1;const n=t.leftSweepEvent.p.x,r=t.leftSweepEvent.p.y,o=t.rightSweepEvent.p.x,i=t.rightSweepEvent.p.y,a=e.leftSweepEvent.p.x,s=e.leftSweepEvent.p.y,u=e.rightSweepEvent.p.x,c=e.rightSweepEvent.p.y,l=(c-s)*(o-n)-(u-a)*(i-r),p=(u-a)*(r-s)-(c-s)*(n-a),d=(o-n)*(r-s)-(i-r)*(n-a);if(0===l)return!1;const f=p/l,h=d/l;if(f>=0&&f<=1&&h>=0&&h<=1){return[n+f*(o-n),r+f*(i-r)]}return!1}var K=function(t,e){const n=new G([],B);return function(t,e){if("FeatureCollection"===t.type){const n=t.features;for(let t=0;t{const n=e.join(",");t[n]||(t[n]=!0,s.push(e))}))}else s=a;return f(s.map((t=>l(t))))}function Q(t,e){if("Feature"===t.type)e(t,0);else if("FeatureCollection"===t.type)for(var n=0;n{i.push(rt(t,o))})),f(i)}(n,e);default:throw new Error("invalid poly")}}function rt(t,e){return t.length>1?h(t,e):d(t[0],e)}var ot=nt;function it(t,e,n={}){var r;const o=null!=(r=n.ignoreSelfIntersections)&&r;let i=!0;return et(t,(t=>{et(e,(e=>{if(!1===i)return!1;i=function(t,e,n){switch(t.type){case"Point":switch(e.type){case"Point":return r=t.coordinates,o=e.coordinates,!(r[0]===o[0]&&r[1]===o[1]);case"LineString":return!at(e,t);case"Polygon":return!U(t,e)}break;case"LineString":switch(e.type){case"Point":return!at(t,e);case"LineString":return!function(t,e,n){const r=X(t,e,{ignoreSelfIntersections:n});if(r.features.length>0)return!0;return!1}(t,e,n);case"Polygon":return!st(e,t,n)}break;case"Polygon":switch(e.type){case"Point":return!U(e,t);case"LineString":return!st(t,e,n);case"Polygon":return!function(t,e,n){for(const n of t.coordinates[0])if(U(n,e))return!0;for(const n of e.coordinates[0])if(U(n,t))return!0;const r=X(nt(t),nt(e),{ignoreSelfIntersections:n});if(r.features.length>0)return!0;return!1}(e,t,n)}}var r,o;return!1}(t.geometry,e.geometry,o)}))})),i}function at(t,e){for(let n=0;n0}function ut(t,e,n){const r=n[0]-t[0],o=n[1]-t[1],i=e[0]-t[0],a=e[1]-t[1];return 0==r*a-o*i&&(Math.abs(i)>=Math.abs(a)?i>0?t[0]<=n[0]&&n[0]<=e[0]:e[0]<=n[0]&&n[0]<=t[0]:a>0?t[1]<=n[1]&&n[1]<=e[1]:e[1]<=n[1]&&n[1]<=t[1])}var ct=it;function lt(t,e,n={}){var r=D(t),o=D(e),i=m(o[1]-r[1]),a=m(o[0]-r[0]),s=m(r[1]),u=m(o[1]),c=Math.pow(Math.sin(i/2),2)+Math.pow(Math.sin(a/2),2)*Math.cos(s)*Math.cos(u);return g(2*Math.atan2(Math.sqrt(c),Math.sqrt(1-c)),n.units)}var pt=lt;function dt(t,e,n={}){if(!0===n.final)return function(t,e){let n=dt(e,t);return n=(n+180)%360,n}(t,e);const r=D(t),o=D(e),i=m(r[0]),a=m(o[0]),s=m(r[1]),u=m(o[1]),c=Math.sin(a-i)*Math.cos(u),l=Math.cos(s)*Math.sin(u)-Math.sin(s)*Math.cos(u)*Math.cos(a-i);return v(Math.atan2(c,l))}function ft(t,e,n,r={}){const o=D(t),i=m(o[0]),a=m(o[1]),s=m(n),u=y(e,r.units),c=Math.asin(Math.sin(a)*Math.cos(u)+Math.cos(a)*Math.sin(u)*Math.cos(s));return l([v(i+Math.atan2(Math.sin(s)*Math.sin(u)*Math.cos(a),Math.cos(u)-Math.sin(a)*Math.sin(c))),v(c)],r.properties)}var ht=Object.defineProperty,gt=Object.defineProperties,yt=Object.getOwnPropertyDescriptors,vt=Object.getOwnPropertySymbols,mt=Object.prototype.hasOwnProperty,bt=Object.prototype.propertyIsEnumerable,Et=(t,e,n)=>e in t?ht(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,_t=(t,e)=>{for(var n in e||(e={}))mt.call(e,n)&&Et(t,n,e[n]);if(vt)for(var n of vt(e))bt.call(e,n)&&Et(t,n,e[n]);return t},Ot=(t,e)=>gt(t,yt(e));var St=function(t,e,n={}){if(!t||!e)throw new Error("lines and pt are required arguments");let r=l([1/0,1/0],{dist:1/0,index:-1,multiFeatureIndex:-1,location:-1}),o=0;return et(t,(function(t,i,a){const s=R(t);for(let t=0;t0&&y.features[0]&&(v=Ot(_t({},y.features[0]),{properties:{dist:lt(e,y.features[0],n),multiFeatureIndex:a,location:o+lt(i,y.features[0],n)}})),i.properties.dist{switch(n){case"type":case"features":return;default:e[n]=t[n]}})),e.features=t.features.map((t=>Ct(t))),e}(t);case"Point":case"LineString":case"Polygon":case"MultiPoint":case"MultiLineString":case"MultiPolygon":case"GeometryCollection":return Tt(t);default:throw new Error("unknown GeoJSON type")}}function Ct(t){const e={type:"Feature"};return Object.keys(t).forEach((n=>{switch(n){case"type":case"properties":case"geometry":return;default:e[n]=t[n]}})),e.properties=wt(t.properties),null==t.geometry?e.geometry=null:e.geometry=Tt(t.geometry),e}function wt(t){const e={};return t?(Object.keys(t).forEach((n=>{const r=t[n];"object"==typeof r?null===r?e[n]=null:Array.isArray(r)?e[n]=r.map((t=>t)):e[n]=wt(r):e[n]=r})),e):e}function Tt(t){const e={type:t.type};return t.bbox&&(e.bbox=t.bbox),"GeometryCollection"===t.type?(e.geometries=t.geometries.map((t=>Tt(t))),e):(e.coordinates=xt(t.coordinates),e)}function xt(t){const e=t;return"object"!=typeof e[0]?e.slice():e.map((t=>xt(t)))}var Pt=Object.defineProperty,Mt=Object.defineProperties,Lt=Object.getOwnPropertyDescriptors,At=Object.getOwnPropertySymbols,Ft=Object.prototype.hasOwnProperty,Nt=Object.prototype.propertyIsEnumerable,jt=(t,e,n)=>e in t?Pt(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Dt=(t,e)=>{for(var n in e||(e={}))Ft.call(e,n)&&jt(t,n,e[n]);if(At)for(var n of At(e))Nt.call(e,n)&&jt(t,n,e[n]);return t},Rt=(t,e)=>Mt(t,Lt(e));var kt=function(t,e,n={}){if(!t)throw new Error("targetPoint is required");if(!e)throw new Error("points is required");let r=1/0,o=0;Q(e,((e,i)=>{const a=lt(t,e,n);at.length)&&(e=t.length);for(var n=0,r=new Array(e);n0&&u0&&c1&&void 0!==arguments[1]&&arguments[1];if(!Array.isArray(n))throw Error("Your array is not an array");Array.isArray(n[0])?n.forEach((function(t){e(t)})):2===n.length&&Jt(t,d,n,r)};return o.forEach((function(t){t.id!==n.id?t.id!==zt.HORIZONTAL_GUIDE&&t.id!==zt.VERTICAL_GUIDE&&(f(t.geometry.coordinates),ct(p,t)||l.push(t)):n.type===Bt.POLYGON&&f(t.geometry.coordinates[0].slice(0,-2),!0)})),[l,d]};function Ht(t,e){var n=t.map((function(t){return{feature:t,point:St(t,e)}}));return n.sort((function(t,e){return t.point.properties.dist-e.point.properties.dist})),{feature:n[0].feature,point:n[0].point}}var qt=function(t,e){var n={};return e.forEach((function(e,r){var o=function(t,e){var n,r=[t.lng,t.lat],o="Point"===e.geometry.type,i="Polygon"===e.geometry.type,a="MultiPolygon"===e.geometry.type,s="MultiPoint"===e.geometry.type,u=void 0,c=R(e);if(o){var p=Gt(c,2);return{latlng:{lng:p[0],lat:p[1]},distance:pt(c,r)}}if(s){var h=kt(r,f(c.map((function(t){return l(t)})))),g=h.geometry.coordinates;return{latlng:{lng:g[0],lat:g[1]},distance:h.properties.distanceToPoint}}if(u=i||a?ot(e):e,i){var y=Ht("LineString"===u.geometry.type?[d(u.geometry.coordinates)]:u.geometry.coordinates.map((function(t){return d(t)})),r);u=y.feature,n=y.point}else if(a){var v=Ht(u.features.map((function(t){return"LineString"===t.geometry.type?[t.geometry.coordinates]:t.geometry.coordinates})).flatMap((function(t){return t})).map((function(t){return d(t)})),r);u=v.feature,n=v.point}else n=St(u,r);var m=Gt(n.geometry.coordinates,2),b=m[0],E=m[1],_=n.properties.index,O=u.geometry.coordinates;return"MultiLineString"===u.geometry.type&&(O=u.geometry.coordinates[n.properties.multiFeatureIndex]),_+1===O.length&&_--,{latlng:{lng:b,lat:E},segment:O.slice(_,_+2),distance:n.properties.dist,isMarker:o}}(t,e);(void 0===n.distance||o.distance2&&void 0!==arguments[2]?arguments[2]:1.25;return!Array.isArray(t.segment)?function(t){return t.latlng}(t):function(t,e,n){var r=t.segment[0],o=t.segment[1],i=[t.latlng.lng,t.latlng.lat],a=pt(r,i),s=pt(o,i),u=at.length)&&(e=t.length);for(var n=0,r=new Array(e);nt.length)&&(e=t.length);for(var n=0,r=new Array(e);n0){var r=t.line.coordinates[t.currentVertexPosition-1];if(t.lastVertex=r,r[0]===e&&r[1]===n)return this.changeMode(ye.SIMPLE_SELECT,{featureIds:[t.line.id]})}Jt(t.map,t.vertices,{lng:e,lat:n}),t.line.updateCoordinate(t.currentVertexPosition,e,n),t.currentVertexPosition++,t.line.updateCoordinate(t.currentVertexPosition,e,n)},be.onMouseMove=function(t,e){var n=Yt(t,e),r=n.lng,o=n.lat;t.line.updateCoordinate(t.currentVertexPosition,r,o),t.snappedLng=r,t.snappedLat=o,t.lastVertex&&t.lastVertex[0]===r&&t.lastVertex[1]===o?this.updateUIClasses({mouse:ve.POINTER}):this.updateUIClasses({mouse:ve.ADD})},be.toDisplayFeatures=function(t,e,n){Kt(t,e)||me.toDisplayFeatures(t,e,n)},be.onStop=function(t){this.deleteFeature(zt.VERTICAL_GUIDE,{silent:!0}),this.deleteFeature(zt.HORIZONTAL_GUIDE,{silent:!0}),this.map.off("moveend",t.moveendCallback),me.onStop.call(this,t)};const Ee=be;var _e=function(t,e,n={}){var r;const o=null!=(r=n.ignoreSelfIntersections)&&r;let i=!1;return et(t,(t=>{et(e,(e=>{if(!0===i)return!0;i=!it(t.geometry,e.geometry,{ignoreSelfIntersections:o})}))})),i};function Oe(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var n=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null==n)return;var r,o,i=[],a=!0,s=!1;try{for(n=n.call(t);!(a=(r=n.next()).done)&&(i.push(r.value),!e||i.length!==e);a=!0);}catch(t){s=!0,o=t}finally{try{a||null==n.return||n.return()}finally{if(s)throw o}}return i}(t,e)||function(t,e){if(!t)return;if("string"==typeof t)return Se(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);"Object"===n&&t.constructor&&(n=t.constructor.name);if("Map"===n||"Set"===n)return Array.from(t);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return Se(t,e)}(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function Se(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n0){var r=t.polygon.coordinates[0][t.currentVertexPosition-1];if(t.lastVertex=r,r[0]===e&&r[1]===n)return this.changeMode(Pe.SIMPLE_SELECT,{featureIds:[t.polygon.id]})}Jt(t.map,t.vertices,{lng:e,lat:n}),t.polygon.updateCoordinate("0.".concat(t.currentVertexPosition),e,n),t.currentVertexPosition++,t.polygon.updateCoordinate("0.".concat(t.currentVertexPosition),e,n)},Ae.onMouseMove=function(t,e){var n=Yt(t,e),r=n.lng,o=n.lat;t.polygon.updateCoordinate("0.".concat(t.currentVertexPosition),r,o),t.snappedLng=r,t.snappedLat=o,t.lastVertex&&t.lastVertex[0]===r&&t.lastVertex[1]===o?this.updateUIClasses({mouse:Me.POINTER}):this.updateUIClasses({mouse:Me.ADD})},Ae.toDisplayFeatures=function(t,e,n){Kt(t,e)||Le.toDisplayFeatures(t,e,n)},Ae.onStop=function(t){this.deleteFeature(zt.VERTICAL_GUIDE,{silent:!0}),this.deleteFeature(zt.HORIZONTAL_GUIDE,{silent:!0}),this.map.off("moveend",t.moveendCallback),this.map.off("draw.snap.options_changed",t.optionsChangedCallback);var e=t.polygon;if(t.options.overlap)Le.onStop.call(this,t);else{var n=this._ctx.store.getAll();try{var r=e;n.forEach((function(t){if(e.id===t.id)return!1;_e(t,r)&&(r=turf.difference(r,t))})),t.polygon.coordinates=r.coordinates||r.geometry.coordinates}catch(e){return Le.onStop.call(this,t),void this.deleteFeature([t.polygon.id],{silent:!0})}var o=t.polygon.removeCoordinate;t.polygon.removeCoordinate=function(){},Le.onStop.call(this,t),t.polygon.removeCoordinate=o.bind(t.polygon)}};const Fe=Ae;function Ne(t,e){return function(t){if(Array.isArray(t))return t}(t)||function(t,e){var n=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null==n)return;var r,o,i=[],a=!0,s=!1;try{for(n=n.call(t);!(a=(r=n.next()).done)&&(i.push(r.value),!e||i.length!==e);a=!0);}catch(t){s=!0,o=t}finally{try{a||null==n.return||n.return()}finally{if(s)throw o}}return i}(t,e)||function(t,e){if(!t)return;if("string"==typeof t)return je(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);"Object"===n&&t.constructor&&(n=t.constructor.name);if("Map"===n||"Set"===n)return Array.from(t);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return je(t,e)}(t,e)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function je(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);nt.length)&&(e=t.length);for(var n=0,r=new Array(e);n
2 |
3 |
4 |
5 |
6 | Mapbox Gl Draw Snap Mode (Demo)
7 |
8 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
97 |
98 |
292 |
293 |
294 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mapbox-gl-draw-snap-mode",
3 | "version": "0.4.0",
4 | "description": "Snapping mode for mapbox-gl-draw",
5 | "main": "dist/mapbox-gl-draw-snap-mode.cjs.js",
6 | "module": "dist/mapbox-gl-draw-snap-mode.esm.js",
7 | "exports": {
8 | ".": {
9 | "import": "./dist/mapbox-gl-draw-snap-mode.esm.js",
10 | "require": "./dist/mapbox-gl-draw-snap-mode.cjs.js"
11 | }
12 | },
13 | "scripts": {
14 | "build": "webpack",
15 | "start": "webpack serve --config ./webpack.development.js",
16 | "test": "echo \"Error: no test specified\" && exit 1"
17 | },
18 | "dependencies": {
19 | "@turf/turf": "^7.1.0"
20 | },
21 | "devDependencies": {
22 | "@babel/core": "^7.12.7",
23 | "@babel/preset-env": "^7.12.7",
24 | "@mapbox/mapbox-gl-draw": "^1.4.2",
25 | "babel-loader": "^8.2.1",
26 | "cross-env": "^7.0.2",
27 | "html-loader": "^1.3.2",
28 | "mapbox-gl": "^1.12.0",
29 | "path-browserify": "^1.0.1",
30 | "terser-webpack-plugin": "^5.0.3",
31 | "webpack": "^5.91.0",
32 | "webpack-bundle-analyzer": "^4.10.2",
33 | "webpack-cli": "^5.1.4",
34 | "webpack-dev-server": "^5.0.4"
35 | },
36 | "repository": {
37 | "type": "git",
38 | "url": "git+ssh://git@github.com/mhsattarian/mapbox-gl-draw-snap-mode.git"
39 | },
40 | "keywords": [
41 | "mapbox",
42 | "mapbox-gl",
43 | "mapbox-gl-draw",
44 | "draw",
45 | "mode",
46 | "snap",
47 | "snapping",
48 | "geojson"
49 | ],
50 | "author": "Mohammad H. Sattarian",
51 | "license": "MIT"
52 | }
53 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export { default as SnapPointMode } from "./modes/snap_point.js";
2 | export { default as SnapLineMode } from "./modes/snap_line.js";
3 | export { default as SnapPolygonMode } from "./modes/snap_polygon.js";
4 | export { default as SnapDirectSelect } from "./modes/snap_direct_select.js";
5 |
6 | export { default as SnapModeDrawStyles } from "./utils/customDrawStyles.js";
7 | export * as Utils from "./utils/index.js";
8 |
--------------------------------------------------------------------------------
/src/modes/snap_direct_select.js:
--------------------------------------------------------------------------------
1 | import MapboxDraw from "@mapbox/mapbox-gl-draw";
2 | import {
3 | createSnapList,
4 | getGuideFeature,
5 | IDS,
6 | snap,
7 | } from "./../utils/index.js";
8 |
9 | const { doubleClickZoom } = MapboxDraw.lib;
10 | const Constants = MapboxDraw.constants;
11 | const DirectSelect = MapboxDraw.modes.direct_select;
12 | const SnapDirectSelect = { ...DirectSelect };
13 |
14 | SnapDirectSelect.onSetup = function (opts) {
15 | const featureId = opts.featureId;
16 | const feature = this.getFeature(featureId);
17 |
18 | if (!feature) {
19 | throw new Error("You must provide a featureId to enter direct_select mode");
20 | }
21 |
22 | if (feature.type === Constants.geojsonTypes.POINT) {
23 | throw new TypeError("direct_select mode doesn't handle point features");
24 | }
25 |
26 | const [snapList, vertices] = createSnapList(
27 | this.map,
28 | this._ctx.api,
29 | feature,
30 | this._ctx.options.snapOptions?.snapGetFeatures
31 | );
32 |
33 | const verticalGuide = this.newFeature(getGuideFeature(IDS.VERTICAL_GUIDE));
34 | const horizontalGuide = this.newFeature(
35 | getGuideFeature(IDS.HORIZONTAL_GUIDE)
36 | );
37 |
38 | this.addFeature(verticalGuide);
39 | this.addFeature(horizontalGuide);
40 |
41 | const state = {
42 | map: this.map,
43 | featureId,
44 | feature,
45 | dragMoveLocation: opts.startPos || null,
46 | dragMoving: false,
47 | canDragMove: false,
48 | selectedCoordPaths: opts.coordPath ? [opts.coordPath] : [],
49 | vertices,
50 | snapList,
51 | verticalGuide,
52 | horizontalGuide,
53 | };
54 |
55 | state.options = this._ctx.options;
56 |
57 | this.setSelectedCoordinates(
58 | this.pathsToCoordinates(featureId, state.selectedCoordPaths)
59 | );
60 | this.setSelected(featureId);
61 | doubleClickZoom.disable(this);
62 |
63 | this.setActionableState({
64 | trash: true,
65 | });
66 |
67 | const optionsChangedCallback = (options) => {
68 | state.options = options;
69 | };
70 |
71 | // for removing listener later on close
72 | state["optionsChangedCallback"] = optionsChangedCallback;
73 | this.map.on("draw.snap.options_changed", optionsChangedCallback);
74 |
75 | return state;
76 | };
77 |
78 | SnapDirectSelect.dragVertex = function (state, e, delta) {
79 | const { lng, lat } = snap(state, e);
80 |
81 | state.feature.updateCoordinate(state.selectedCoordPaths[0], lng, lat);
82 | };
83 |
84 | SnapDirectSelect.onStop = function (state) {
85 | this.deleteFeature(IDS.VERTICAL_GUIDE, { silent: true });
86 | this.deleteFeature(IDS.HORIZONTAL_GUIDE, { silent: true });
87 |
88 | // remove moveend callback
89 | // this.map.off("moveend", state.moveendCallback);
90 | this.map.off("draw.snap.options_changed", state.optionsChangedCallback);
91 |
92 | // This relies on the the state of SnapPolygonMode being similar to DrawPolygon
93 | DirectSelect.onStop.call(this, state);
94 | };
95 |
96 | export default SnapDirectSelect;
97 |
--------------------------------------------------------------------------------
/src/modes/snap_line.js:
--------------------------------------------------------------------------------
1 | import MapboxDraw from "@mapbox/mapbox-gl-draw";
2 | import {
3 | addPointToVertices,
4 | createSnapList,
5 | getGuideFeature,
6 | IDS,
7 | shouldHideGuide,
8 | snap,
9 | } from "./../utils/index.js";
10 |
11 | const { doubleClickZoom } = MapboxDraw.lib;
12 | const { geojsonTypes, modes, cursors } = MapboxDraw.constants;
13 | const DrawLine = MapboxDraw.modes.draw_line_string;
14 | const SnapLineMode = { ...DrawLine };
15 |
16 | SnapLineMode.onSetup = function (options) {
17 | const line = this.newFeature({
18 | type: geojsonTypes.FEATURE,
19 | properties: {},
20 | geometry: {
21 | type: geojsonTypes.LINE_STRING,
22 | coordinates: [[]],
23 | },
24 | });
25 |
26 | const verticalGuide = this.newFeature(getGuideFeature(IDS.VERTICAL_GUIDE));
27 | const horizontalGuide = this.newFeature(
28 | getGuideFeature(IDS.HORIZONTAL_GUIDE)
29 | );
30 |
31 | this.addFeature(line);
32 | this.addFeature(verticalGuide);
33 | this.addFeature(horizontalGuide);
34 |
35 | const selectedFeatures = this.getSelected();
36 | this.clearSelectedFeatures();
37 | doubleClickZoom.disable(this);
38 |
39 | const [snapList, vertices] = createSnapList(
40 | this.map,
41 | this._ctx.api,
42 | line,
43 | this._ctx.options.snapOptions?.snapGetFeatures
44 | );
45 |
46 | const state = {
47 | map: this.map,
48 | line,
49 | currentVertexPosition: 0,
50 | vertices,
51 | snapList,
52 | selectedFeatures,
53 | verticalGuide,
54 | horizontalGuide,
55 | direction: "forward", // expected by DrawLineString
56 | };
57 |
58 | state.options = this._ctx.options;
59 |
60 | const moveendCallback = () => {
61 | const [snapList, vertices] = createSnapList(
62 | this.map,
63 | this._ctx.api,
64 | line,
65 | this._ctx.options.snapOptions?.snapGetFeatures
66 | );
67 | state.vertices = vertices;
68 | state.snapList = snapList;
69 | };
70 | // for removing listener later on close
71 | state["moveendCallback"] = moveendCallback;
72 |
73 | const optionsChangedCallback = (options) => {
74 | state.options = options;
75 | };
76 | // for removing listener later on close
77 | state["optionsChangedCallback"] = optionsChangedCallback;
78 |
79 | this.map.on("moveend", moveendCallback);
80 | this.map.on("draw.snap.options_changed", optionsChangedCallback);
81 |
82 | return state;
83 | };
84 |
85 | SnapLineMode.onClick = function (state) {
86 | // We save some processing by rounding on click, not mousemove
87 | const lng = state.snappedLng;
88 | const lat = state.snappedLat;
89 |
90 | // End the drawing if this click is on the previous position
91 | // Note: not bothering with 'direction'
92 | if (state.currentVertexPosition > 0) {
93 | const lastVertex = state.line.coordinates[state.currentVertexPosition - 1];
94 |
95 | state.lastVertex = lastVertex;
96 |
97 | if (lastVertex[0] === lng && lastVertex[1] === lat) {
98 | return this.changeMode(modes.SIMPLE_SELECT, {
99 | featureIds: [state.line.id],
100 | });
101 | }
102 | }
103 |
104 | // const point = state.map.project({ lng: lng, lat: lat });
105 |
106 | addPointToVertices(state.map, state.vertices, { lng, lat });
107 |
108 | state.line.updateCoordinate(state.currentVertexPosition, lng, lat);
109 |
110 | state.currentVertexPosition++;
111 |
112 | state.line.updateCoordinate(state.currentVertexPosition, lng, lat);
113 | };
114 |
115 | SnapLineMode.onMouseMove = function (state, e) {
116 | const { lng, lat } = snap(state, e);
117 |
118 | state.line.updateCoordinate(state.currentVertexPosition, lng, lat);
119 | state.snappedLng = lng;
120 | state.snappedLat = lat;
121 |
122 | if (
123 | state.lastVertex &&
124 | state.lastVertex[0] === lng &&
125 | state.lastVertex[1] === lat
126 | ) {
127 | this.updateUIClasses({ mouse: cursors.POINTER });
128 |
129 | // cursor options:
130 | // ADD: "add"
131 | // DRAG: "drag"
132 | // MOVE: "move"
133 | // NONE: "none"
134 | // POINTER: "pointer"
135 | } else {
136 | this.updateUIClasses({ mouse: cursors.ADD });
137 | }
138 | };
139 |
140 | // This is 'extending' DrawLine.toDisplayFeatures
141 | SnapLineMode.toDisplayFeatures = function (state, geojson, display) {
142 | if (shouldHideGuide(state, geojson)) return;
143 |
144 | // This relies on the the state of SnapLineMode being similar to DrawLine
145 | DrawLine.toDisplayFeatures(state, geojson, display);
146 | };
147 |
148 | // This is 'extending' DrawLine.onStop
149 | SnapLineMode.onStop = function (state) {
150 | this.deleteFeature(IDS.VERTICAL_GUIDE, { silent: true });
151 | this.deleteFeature(IDS.HORIZONTAL_GUIDE, { silent: true });
152 |
153 | // remove moveend callback
154 | this.map.off("moveend", state.moveendCallback);
155 |
156 | // This relies on the the state of SnapLineMode being similar to DrawLine
157 | DrawLine.onStop.call(this, state);
158 | };
159 |
160 | export default SnapLineMode;
161 |
--------------------------------------------------------------------------------
/src/modes/snap_point.js:
--------------------------------------------------------------------------------
1 | import MapboxDraw from "@mapbox/mapbox-gl-draw";
2 | import {
3 | createSnapList,
4 | getGuideFeature,
5 | IDS,
6 | shouldHideGuide,
7 | snap,
8 | } from "./../utils/index.js";
9 |
10 | const { doubleClickZoom } = MapboxDraw.lib;
11 | const { geojsonTypes, cursors } = MapboxDraw.constants;
12 | const DrawPoint = MapboxDraw.modes.draw_point;
13 | const SnapPointMode = { ...DrawPoint };
14 |
15 | SnapPointMode.onSetup = function (options) {
16 | const point = this.newFeature({
17 | type: geojsonTypes.FEATURE,
18 | properties: {},
19 | geometry: {
20 | type: geojsonTypes.POINT,
21 | coordinates: [[]],
22 | },
23 | });
24 |
25 | const verticalGuide = this.newFeature(getGuideFeature(IDS.VERTICAL_GUIDE));
26 | const horizontalGuide = this.newFeature(
27 | getGuideFeature(IDS.HORIZONTAL_GUIDE)
28 | );
29 |
30 | this.addFeature(point);
31 | this.addFeature(verticalGuide);
32 | this.addFeature(horizontalGuide);
33 |
34 | const selectedFeatures = this.getSelected();
35 | this.clearSelectedFeatures();
36 | doubleClickZoom.disable(this);
37 |
38 | const [snapList, vertices] = createSnapList(
39 | this.map,
40 | this._ctx.api,
41 | point,
42 | this._ctx.options.snapOptions?.snapGetFeatures
43 | );
44 |
45 | const state = {
46 | map: this.map,
47 | point,
48 | vertices,
49 | snapList,
50 | selectedFeatures,
51 | verticalGuide,
52 | horizontalGuide,
53 | };
54 |
55 | state.options = this._ctx.options;
56 |
57 | const moveendCallback = () => {
58 | const [snapList, vertices] = createSnapList(
59 | this.map,
60 | this._ctx.api,
61 | point,
62 | this._ctx.options.snapOptions?.snapGetFeatures
63 | );
64 | state.vertices = vertices;
65 | state.snapList = snapList;
66 | };
67 | // for removing listener later on close
68 | state["moveendCallback"] = moveendCallback;
69 |
70 | const optionsChangedCallback = (options) => {
71 | state.options = options;
72 | };
73 | // for removing listener later on close
74 | state["optionsChangedCallback"] = optionsChangedCallback;
75 |
76 | this.map.on("moveend", moveendCallback);
77 | this.map.on("draw.snap.options_changed", optionsChangedCallback);
78 |
79 | return state;
80 | };
81 |
82 | SnapPointMode.onClick = function (state) {
83 | // We mock out e with the rounded lng/lat then call DrawPoint with it
84 | DrawPoint.onClick.call(this, state, {
85 | lngLat: {
86 | lng: state.snappedLng,
87 | lat: state.snappedLat,
88 | },
89 | });
90 | };
91 |
92 | SnapPointMode.onMouseMove = function (state, e) {
93 | const { lng, lat } = snap(state, e);
94 |
95 | state.snappedLng = lng;
96 | state.snappedLat = lat;
97 |
98 | if (
99 | state.lastVertex &&
100 | state.lastVertex[0] === lng &&
101 | state.lastVertex[1] === lat
102 | ) {
103 | this.updateUIClasses({ mouse: cursors.POINTER });
104 |
105 | // cursor options:
106 | // ADD: "add"
107 | // DRAG: "drag"
108 | // MOVE: "move"
109 | // NONE: "none"
110 | // POINTER: "pointer"
111 | } else {
112 | this.updateUIClasses({ mouse: cursors.ADD });
113 | }
114 | };
115 |
116 | // This is 'extending' DrawPoint.toDisplayFeatures
117 | SnapPointMode.toDisplayFeatures = function (state, geojson, display) {
118 | if (shouldHideGuide(state, geojson)) return;
119 |
120 | // This relies on the the state of SnapPointMode having a 'point' prop
121 | DrawPoint.toDisplayFeatures(state, geojson, display);
122 | };
123 |
124 | // This is 'extending' DrawPoint.onStop
125 | SnapPointMode.onStop = function (state) {
126 | this.deleteFeature(IDS.VERTICAL_GUIDE, { silent: true });
127 | this.deleteFeature(IDS.HORIZONTAL_GUIDE, { silent: true });
128 |
129 | // remove moveend callback
130 | this.map.off("moveend", state.moveendCallback);
131 |
132 | // This relies on the the state of SnapPointMode having a 'point' prop
133 | DrawPoint.onStop.call(this, state);
134 | };
135 |
136 | export default SnapPointMode;
137 |
--------------------------------------------------------------------------------
/src/modes/snap_polygon.js:
--------------------------------------------------------------------------------
1 | import MapboxDraw from "@mapbox/mapbox-gl-draw";
2 | import {
3 | addPointToVertices,
4 | createSnapList,
5 | getGuideFeature,
6 | IDS,
7 | shouldHideGuide,
8 | snap,
9 | } from "./../utils/index.js";
10 | import booleanIntersects from "@turf/boolean-intersects";
11 |
12 | const { doubleClickZoom } = MapboxDraw.lib;
13 | const { geojsonTypes, modes, cursors } = MapboxDraw.constants;
14 | const DrawPolygon = MapboxDraw.modes.draw_polygon;
15 | const SnapPolygonMode = { ...DrawPolygon };
16 |
17 | SnapPolygonMode.onSetup = function (options) {
18 | const polygon = this.newFeature({
19 | type: geojsonTypes.FEATURE,
20 | properties: {},
21 | geometry: {
22 | type: geojsonTypes.POLYGON,
23 | coordinates: [[]],
24 | },
25 | });
26 |
27 | const verticalGuide = this.newFeature(getGuideFeature(IDS.VERTICAL_GUIDE));
28 | const horizontalGuide = this.newFeature(
29 | getGuideFeature(IDS.HORIZONTAL_GUIDE)
30 | );
31 |
32 | this.addFeature(polygon);
33 | this.addFeature(verticalGuide);
34 | this.addFeature(horizontalGuide);
35 |
36 | const selectedFeatures = this.getSelected();
37 | this.clearSelectedFeatures();
38 | doubleClickZoom.disable(this);
39 |
40 | const [snapList, vertices] = createSnapList(
41 | this.map,
42 | this._ctx.api,
43 | polygon,
44 | this._ctx.options.snapOptions?.snapGetFeatures
45 | );
46 |
47 | const state = {
48 | map: this.map,
49 | polygon,
50 | currentVertexPosition: 0,
51 | vertices,
52 | snapList,
53 | selectedFeatures,
54 | verticalGuide,
55 | horizontalGuide,
56 | };
57 |
58 | /// Adding default options
59 | state.options = Object.assign(this._ctx.options, {
60 | overlap: true,
61 | });
62 |
63 | const moveendCallback = () => {
64 | const [snapList, vertices] = createSnapList(
65 | this.map,
66 | this._ctx.api,
67 | polygon,
68 | this._ctx.options.snapOptions?.snapGetFeatures
69 | );
70 | state.vertices = vertices;
71 | state.snapList = snapList;
72 | };
73 | // for removing listener later on close
74 | state["moveendCallback"] = moveendCallback;
75 |
76 | const optionsChangedCallback = (options) => {
77 | state.options = options;
78 | };
79 |
80 | // for removing listener later on close
81 | state["optionsChangedCallback"] = optionsChangedCallback;
82 |
83 | this.map.on("moveend", moveendCallback);
84 | this.map.on("draw.snap.options_changed", optionsChangedCallback);
85 |
86 | return state;
87 | };
88 |
89 | SnapPolygonMode.onClick = function (state) {
90 | // We save some processing by rounding on click, not mousemove
91 | const lng = state.snappedLng;
92 | const lat = state.snappedLat;
93 |
94 | // End the drawing if this click is on the previous position
95 | if (state.currentVertexPosition > 0) {
96 | const lastVertex =
97 | state.polygon.coordinates[0][state.currentVertexPosition - 1];
98 |
99 | state.lastVertex = lastVertex;
100 |
101 | if (lastVertex[0] === lng && lastVertex[1] === lat) {
102 | return this.changeMode(modes.SIMPLE_SELECT, {
103 | featureIds: [state.polygon.id],
104 | });
105 | }
106 | }
107 |
108 | // const point = state.map.project();
109 |
110 | addPointToVertices(state.map, state.vertices, { lng, lat });
111 |
112 | state.polygon.updateCoordinate(`0.${state.currentVertexPosition}`, lng, lat);
113 |
114 | state.currentVertexPosition++;
115 |
116 | state.polygon.updateCoordinate(`0.${state.currentVertexPosition}`, lng, lat);
117 | };
118 |
119 | SnapPolygonMode.onMouseMove = function (state, e) {
120 | const { lng, lat } = snap(state, e);
121 |
122 | state.polygon.updateCoordinate(`0.${state.currentVertexPosition}`, lng, lat);
123 | state.snappedLng = lng;
124 | state.snappedLat = lat;
125 |
126 | if (
127 | state.lastVertex &&
128 | state.lastVertex[0] === lng &&
129 | state.lastVertex[1] === lat
130 | ) {
131 | this.updateUIClasses({ mouse: cursors.POINTER });
132 |
133 | // cursor options:
134 | // ADD: "add"
135 | // DRAG: "drag"
136 | // MOVE: "move"
137 | // NONE: "none"
138 | // POINTER: "pointer"
139 | } else {
140 | this.updateUIClasses({ mouse: cursors.ADD });
141 | }
142 | };
143 |
144 | // This is 'extending' DrawPolygon.toDisplayFeatures
145 | SnapPolygonMode.toDisplayFeatures = function (state, geojson, display) {
146 | if (shouldHideGuide(state, geojson)) return;
147 |
148 | // This relies on the the state of SnapPolygonMode being similar to DrawPolygon
149 | DrawPolygon.toDisplayFeatures(state, geojson, display);
150 | };
151 |
152 | // This is 'extending' DrawPolygon.onStop
153 | SnapPolygonMode.onStop = function (state) {
154 | this.deleteFeature(IDS.VERTICAL_GUIDE, { silent: true });
155 | this.deleteFeature(IDS.HORIZONTAL_GUIDE, { silent: true });
156 |
157 | // remove moveend callback
158 | this.map.off("moveend", state.moveendCallback);
159 | this.map.off("draw.snap.options_changed", state.optionsChangedCallback);
160 |
161 | var userPolygon = state.polygon;
162 | if (state.options.overlap) {
163 | DrawPolygon.onStop.call(this, state);
164 | return;
165 | }
166 | // if overlap is false, mutate polygon so it doesn't overlap with existing ones
167 | // get all editable features to check for intersections
168 | var features = this._ctx.store.getAll();
169 |
170 | try {
171 | var edited = userPolygon;
172 | features.forEach(function (feature) {
173 | if (userPolygon.id === feature.id) return false;
174 | if (!booleanIntersects(feature, edited)) return;
175 | edited = turf.difference(edited, feature);
176 | });
177 | state.polygon.coordinates =
178 | edited.coordinates || edited.geometry.coordinates;
179 | } catch (err) {
180 | // cancel this polygon if a difference cannot be calculated
181 | DrawPolygon.onStop.call(this, state);
182 | this.deleteFeature([state.polygon.id], { silent: true });
183 | return;
184 | }
185 |
186 | // monkeypatch so DrawPolygon.onStop doesn't error
187 | var rc = state.polygon.removeCoordinate;
188 | state.polygon.removeCoordinate = () => {};
189 | // This relies on the the state of SnapPolygonMode being similar to DrawPolygon
190 | DrawPolygon.onStop.call(this, state);
191 | state.polygon.removeCoordinate = rc.bind(state.polygon);
192 | };
193 |
194 | export default SnapPolygonMode;
195 |
--------------------------------------------------------------------------------
/src/utils/customDrawStyles.js:
--------------------------------------------------------------------------------
1 | import MapboxDraw from "@mapbox/mapbox-gl-draw";
2 | const theme = MapboxDraw.lib.theme;
3 |
4 | const modifiedDefaultStyles = theme.map(defaultStyle => {
5 | if (defaultStyle.id === 'gl-draw-line-inactive') {
6 | return {
7 | ...defaultStyle,
8 | filter: [
9 | ...defaultStyle.filter,
10 | ['!=', 'user_isSnapGuide', 'true'],
11 | ],
12 | };
13 | }
14 |
15 | return defaultStyle;
16 | });
17 |
18 | const customDrawStyles = [
19 | ...modifiedDefaultStyles,
20 | {
21 | id: "guide",
22 | type: "line",
23 | filter: [
24 | "all",
25 | ["==", "$type", "LineString"],
26 | ["==", "user_isSnapGuide", "true"],
27 | ],
28 | layout: {
29 | "line-cap": "round",
30 | "line-join": "round",
31 | },
32 | paint: {
33 | "line-color": "#c00c00",
34 | "line-width": 1,
35 | "line-dasharray": [5, 5],
36 | },
37 | },
38 | ];
39 |
40 | export default customDrawStyles;
41 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | // Heavily inspired from work of @davidgilbertson on Github and `leaflet-geoman` project.
2 | import MapboxDraw from "@mapbox/mapbox-gl-draw";
3 |
4 | const { geojsonTypes } = MapboxDraw.constants;
5 |
6 | import bboxPolygon from "@turf/bbox-polygon";
7 | import booleanDisjoint from "@turf/boolean-disjoint";
8 | import { getCoords } from "@turf/invariant";
9 | import distance from "@turf/distance";
10 | import polygonToLine from "@turf/polygon-to-line";
11 | import nearestPointOnLine from "@turf/nearest-point-on-line";
12 | import nearestPointInPointSet from "@turf/nearest-point";
13 | import midpoint from "@turf/midpoint";
14 | import {
15 | featureCollection,
16 | lineString as turfLineString,
17 | point as turfPoint,
18 | } from "@turf/helpers";
19 |
20 | export const IDS = {
21 | VERTICAL_GUIDE: "VERTICAL_GUIDE",
22 | HORIZONTAL_GUIDE: "HORIZONTAL_GUIDE",
23 | };
24 |
25 | export const addPointToVertices = (
26 | map,
27 | vertices,
28 | coordinates,
29 | forceInclusion
30 | ) => {
31 | const { width: w, height: h } = map.getCanvas();
32 | // Just add vertices of features currently visible in viewport
33 | const { x, y } = map.project(coordinates);
34 | const pointIsOnTheScreen = x > 0 && x < w && y > 0 && y < h;
35 |
36 | // But do add off-screen points if forced (e.g. for the current feature)
37 | // So features will always snap to their own points
38 | if (pointIsOnTheScreen || forceInclusion) {
39 | vertices.push(coordinates);
40 | }
41 | };
42 |
43 | export const createSnapList = (map, draw, currentFeature, getFeatures) => {
44 | // Get all features
45 | let features = [];
46 |
47 | if (typeof getFeatures === "function") {
48 | features = getFeatures(map, draw);
49 | }
50 |
51 | if (!Array.isArray(features) || features.length === 0) {
52 | features = draw.getAll().features;
53 | }
54 |
55 | const snapList = [];
56 |
57 | // Get current bbox as polygon
58 | const bboxAsPolygon = (() => {
59 | const canvas = map.getCanvas(),
60 | w = canvas.width,
61 | h = canvas.height,
62 | cUL = map.unproject([0, 0]).toArray(),
63 | cUR = map.unproject([w, 0]).toArray(),
64 | cLR = map.unproject([w, h]).toArray(),
65 | cLL = map.unproject([0, h]).toArray();
66 |
67 | return bboxPolygon([cLL, cUR].flat());
68 | })();
69 |
70 | const vertices = [];
71 |
72 | // Keeps vertices for drawing guides
73 | const addVerticesToVertices = (coordinates, isCurrentFeature = false) => {
74 | if (!Array.isArray(coordinates)) throw Error("Your array is not an array");
75 |
76 | if (Array.isArray(coordinates[0])) {
77 | // coordinates is an array of arrays, we must go deeper
78 | coordinates.forEach((coord) => {
79 | addVerticesToVertices(coord);
80 | });
81 | } else {
82 | // If not an array of arrays, only consider arrays with two items
83 | if (coordinates.length === 2) {
84 | addPointToVertices(map, vertices, coordinates, isCurrentFeature);
85 | }
86 | }
87 | };
88 |
89 | features.forEach((feature) => {
90 | // For current feature
91 | if (feature.id === currentFeature.id) {
92 | if (currentFeature.type === geojsonTypes.POLYGON) {
93 | // For the current polygon, the last two points are the mouse position and back home
94 | // so we chop those off (else we get vertices showing where the user clicked, even
95 | // if they were just panning the map)
96 | addVerticesToVertices(
97 | feature.geometry.coordinates[0].slice(0, -2),
98 | true
99 | );
100 | }
101 | return;
102 | }
103 |
104 | // If this is re-running because a user is moving the map, the features might include
105 | // vertices or the last leg of a polygon
106 | if (
107 | feature.id === IDS.HORIZONTAL_GUIDE ||
108 | feature.id === IDS.VERTICAL_GUIDE
109 | )
110 | return;
111 |
112 | addVerticesToVertices(feature.geometry.coordinates);
113 |
114 | // If feature is currently on viewport add to snap list
115 | if (!booleanDisjoint(bboxAsPolygon, feature)) {
116 | snapList.push(feature);
117 | }
118 | });
119 |
120 | return [snapList, vertices];
121 | };
122 |
123 | const getNearbyVertices = (vertices, coords) => {
124 | const verticals = [];
125 | const horizontals = [];
126 |
127 | vertices.forEach((vertex) => {
128 | verticals.push(vertex[0]);
129 | horizontals.push(vertex[1]);
130 | });
131 |
132 | const nearbyVerticalGuide = verticals.find(
133 | (px) => Math.abs(px - coords.lng) < 0.009
134 | );
135 |
136 | const nearbyHorizontalGuide = horizontals.find(
137 | (py) => Math.abs(py - coords.lat) < 0.009
138 | );
139 |
140 | return {
141 | verticalPx: nearbyVerticalGuide,
142 | horizontalPx: nearbyHorizontalGuide,
143 | };
144 | };
145 |
146 | const calcLayerDistances = (lngLat, layer) => {
147 | // the point P which we want to snap (probably the marker that is dragged)
148 | const P = [lngLat.lng, lngLat.lat];
149 |
150 | // is this a marker?
151 | const isMarker = layer.geometry.type === "Point";
152 | // is it a polygon?
153 | const isPolygon = layer.geometry.type === "Polygon";
154 | // is it a multiPolygon?
155 | const isMultiPolygon = layer.geometry.type === "MultiPolygon";
156 | // is it a multiPoint?
157 | const isMultiPoint = layer.geometry.type === "MultiPoint";
158 |
159 | let lines = undefined;
160 |
161 | // the coords of the layer
162 | const latlngs = getCoords(layer);
163 |
164 | if (isMarker) {
165 | const [lng, lat] = latlngs;
166 | // return the info for the marker, no more calculations needed
167 | return {
168 | latlng: { lng, lat },
169 | distance: distance(latlngs, P),
170 | };
171 | }
172 |
173 | if (isMultiPoint) {
174 | const np = nearestPointInPointSet(
175 | P,
176 | featureCollection(latlngs.map((x) => turfPoint(x)))
177 | );
178 | const c = np.geometry.coordinates;
179 | return {
180 | latlng: { lng: c[0], lat: c[1] },
181 | distance: np.properties.distanceToPoint,
182 | };
183 | }
184 |
185 | if (isPolygon || isMultiPolygon) {
186 | lines = polygonToLine(layer);
187 | } else {
188 | lines = layer;
189 | }
190 |
191 | let nearestPoint;
192 | if (isPolygon) {
193 | let lineStrings;
194 | if (lines.geometry.type === "LineString") {
195 | lineStrings = [turfLineString(lines.geometry.coordinates)];
196 | } else {
197 | lineStrings = lines.geometry.coordinates.map((coords) =>
198 | turfLineString(coords)
199 | );
200 | }
201 |
202 | const closestFeature = getFeatureWithNearestPoint(lineStrings, P);
203 | lines = closestFeature.feature;
204 | nearestPoint = closestFeature.point;
205 | } else if (isMultiPolygon) {
206 | const lineStrings = lines.features
207 | .map((feat) => {
208 | if (feat.geometry.type === "LineString") {
209 | return [feat.geometry.coordinates];
210 | } else {
211 | return feat.geometry.coordinates;
212 | }
213 | })
214 | .flatMap((coords) => coords)
215 | .map((coords) => turfLineString(coords));
216 |
217 | const closestFeature = getFeatureWithNearestPoint(lineStrings, P);
218 | lines = closestFeature.feature;
219 | nearestPoint = closestFeature.point;
220 | } else {
221 | nearestPoint = nearestPointOnLine(lines, P);
222 | }
223 |
224 | const [lng, lat] = nearestPoint.geometry.coordinates;
225 |
226 | let segmentIndex = nearestPoint.properties.index;
227 |
228 | let { coordinates } = lines.geometry;
229 |
230 | if (lines.geometry.type === "MultiLineString") {
231 | coordinates =
232 | lines.geometry.coordinates[nearestPoint.properties.multiFeatureIndex];
233 | }
234 |
235 | if (segmentIndex + 1 === coordinates.length) segmentIndex--;
236 |
237 | return {
238 | latlng: { lng, lat },
239 | segment: coordinates.slice(segmentIndex, segmentIndex + 2),
240 | distance: nearestPoint.properties.dist,
241 | isMarker,
242 | };
243 | };
244 |
245 | function getFeatureWithNearestPoint(lineStrings, P) {
246 | const nearestPointsOfEachFeature = lineStrings.map((feat) => ({
247 | feature: feat,
248 | point: nearestPointOnLine(feat, P),
249 | }));
250 |
251 | nearestPointsOfEachFeature.sort(
252 | (a, b) => a.point.properties.dist - b.point.properties.dist
253 | );
254 |
255 | return {
256 | feature: nearestPointsOfEachFeature[0].feature,
257 | point: nearestPointsOfEachFeature[0].point,
258 | };
259 | }
260 |
261 | const calcClosestLayer = (lngLat, layers) => {
262 | let closestLayer = {};
263 |
264 | // loop through the layers
265 | layers.forEach((layer, index) => {
266 | // find the closest latlng, segment and the distance of this layer to the dragged marker latlng
267 | const results = calcLayerDistances(lngLat, layer);
268 |
269 | // save the info if it doesn't exist or if the distance is smaller than the previous one
270 | if (
271 | closestLayer.distance === undefined ||
272 | results.distance < closestLayer.distance
273 | ) {
274 | closestLayer = results;
275 | closestLayer.layer = layer;
276 | }
277 | });
278 |
279 | // return the closest layer and it's data
280 | // if there is no closest layer, return undefined
281 | return closestLayer;
282 | };
283 |
284 | // minimal distance before marker snaps (in pixels)
285 | const metersPerPixel = function (latitude, zoomLevel) {
286 | const earthCircumference = 40075017;
287 | const latitudeRadians = latitude * (Math.PI / 180);
288 | return (
289 | (earthCircumference * Math.cos(latitudeRadians)) /
290 | Math.pow(2, zoomLevel + 8)
291 | );
292 | };
293 |
294 | // we got the point we want to snap to (C), but we need to check if a coord of the polygon
295 | function snapToLineOrPolygon(
296 | closestLayer,
297 | snapOptions,
298 | snapVertexPriorityDistance
299 | ) {
300 | // A and B are the points of the closest segment to P (the marker position we want to snap)
301 | const A = closestLayer.segment[0];
302 | const B = closestLayer.segment[1];
303 |
304 | // C is the point we would snap to on the segment.
305 | // The closest point on the closest segment of the closest polygon to P. That's right.
306 | const C = [closestLayer.latlng.lng, closestLayer.latlng.lat];
307 |
308 | // distances from A to C and B to C to check which one is closer to C
309 | const distanceAC = distance(A, C);
310 | const distanceBC = distance(B, C);
311 |
312 | // closest latlng of A and B to C
313 | let closestVertexLatLng = distanceAC < distanceBC ? A : B;
314 |
315 | // distance between closestVertexLatLng and C
316 | let shortestDistance = distanceAC < distanceBC ? distanceAC : distanceBC;
317 |
318 | // snap to middle (M) of segment if option is enabled
319 | if (snapOptions && snapOptions.snapToMidPoints) {
320 | const M = midpoint(A, B).geometry.coordinates;
321 | const distanceMC = distance(M, C);
322 |
323 | if (distanceMC < distanceAC && distanceMC < distanceBC) {
324 | // M is the nearest vertex
325 | closestVertexLatLng = M;
326 | shortestDistance = distanceMC;
327 | }
328 | }
329 |
330 | // the distance that needs to be undercut to trigger priority
331 | const priorityDistance = snapVertexPriorityDistance;
332 |
333 | // the latlng we ultimately want to snap to
334 | let snapLatlng;
335 |
336 | // if C is closer to the closestVertexLatLng (A, B or M) than the snapDistance,
337 | // the closestVertexLatLng has priority over C as the snapping point.
338 | if (shortestDistance < priorityDistance) {
339 | snapLatlng = closestVertexLatLng;
340 | } else {
341 | snapLatlng = C;
342 | }
343 |
344 | // return the copy of snapping point
345 | const [lng, lat] = snapLatlng;
346 | return { lng, lat };
347 | }
348 |
349 | function snapToPoint(closestLayer) {
350 | return closestLayer.latlng;
351 | }
352 |
353 | const checkPrioritySnapping = (
354 | closestLayer,
355 | snapOptions,
356 | snapVertexPriorityDistance = 1.25
357 | ) => {
358 | let snappingToPoint = !Array.isArray(closestLayer.segment);
359 | if (snappingToPoint) {
360 | return snapToPoint(closestLayer);
361 | } else {
362 | return snapToLineOrPolygon(
363 | closestLayer,
364 | snapOptions,
365 | snapVertexPriorityDistance
366 | );
367 | }
368 | };
369 |
370 | /**
371 | * Returns snap points if there are any, otherwise the original lng/lat of the event
372 | * Also, defines if vertices should show on the state object
373 | *
374 | * Mutates the state object
375 | *
376 | * @param state
377 | * @param e
378 | * @returns {{lng: number, lat: number}}
379 | */
380 | export const snap = (state, e) => {
381 | let lng = e.lngLat.lng;
382 | let lat = e.lngLat.lat;
383 |
384 | // Holding alt bypasses all snapping
385 | if (e.originalEvent.altKey) {
386 | state.showVerticalSnapLine = false;
387 | state.showHorizontalSnapLine = false;
388 |
389 | return { lng, lat };
390 | }
391 |
392 | if (state.snapList.length <= 0) {
393 | return { lng, lat };
394 | }
395 |
396 | // snapping is on
397 | let closestLayer, minDistance, snapLatLng;
398 | if (state.options.snap) {
399 | closestLayer = calcClosestLayer({ lng, lat }, state.snapList);
400 |
401 | // if no layers found. Can happen when circle is the only visible layer on the map and the hidden snapping-border circle layer is also on the map
402 | if (Object.keys(closestLayer).length === 0) {
403 | return false;
404 | }
405 |
406 | const isMarker = closestLayer.isMarker;
407 | const snapVertexPriorityDistance = state.options.snapOptions
408 | ? state.options.snapOptions.snapVertexPriorityDistance
409 | : undefined;
410 |
411 | if (!isMarker) {
412 | snapLatLng = checkPrioritySnapping(
413 | closestLayer,
414 | state.options.snapOptions,
415 | snapVertexPriorityDistance
416 | );
417 | // snapLatLng = closestLayer.latlng;
418 | } else {
419 | snapLatLng = closestLayer.latlng;
420 | }
421 |
422 | minDistance =
423 | ((state.options.snapOptions && state.options.snapOptions.snapPx) || 15) *
424 | metersPerPixel(snapLatLng.lat, state.map.getZoom());
425 | }
426 |
427 | let verticalPx, horizontalPx;
428 | if (state.options.guides) {
429 | const nearestGuideline = getNearbyVertices(state.vertices, e.lngLat);
430 |
431 | verticalPx = nearestGuideline.verticalPx;
432 | horizontalPx = nearestGuideline.horizontalPx;
433 |
434 | if (verticalPx) {
435 | // Draw a line from top to bottom
436 |
437 | const lngLatTop = { lng: verticalPx, lat: e.lngLat.lat + 10 };
438 | const lngLatBottom = { lng: verticalPx, lat: e.lngLat.lat - 10 };
439 |
440 | state.verticalGuide.updateCoordinate(0, lngLatTop.lng, lngLatTop.lat);
441 | state.verticalGuide.updateCoordinate(
442 | 1,
443 | lngLatBottom.lng,
444 | lngLatBottom.lat
445 | );
446 | }
447 |
448 | if (horizontalPx) {
449 | // Draw a line from left to right
450 |
451 | const lngLatTop = { lng: e.lngLat.lng + 10, lat: horizontalPx };
452 | const lngLatBottom = { lng: e.lngLat.lng - 10, lat: horizontalPx };
453 |
454 | state.horizontalGuide.updateCoordinate(0, lngLatTop.lng, lngLatTop.lat);
455 | state.horizontalGuide.updateCoordinate(
456 | 1,
457 | lngLatBottom.lng,
458 | lngLatBottom.lat
459 | );
460 | }
461 |
462 | state.showVerticalSnapLine = !!verticalPx;
463 | state.showHorizontalSnapLine = !!horizontalPx;
464 | }
465 |
466 | if (closestLayer && closestLayer.distance * 1000 < minDistance) {
467 | return snapLatLng;
468 | } else if (verticalPx || horizontalPx) {
469 | if (verticalPx) {
470 | lng = verticalPx;
471 | }
472 | if (horizontalPx) {
473 | lat = horizontalPx;
474 | }
475 | return { lng, lat };
476 | } else {
477 | return { lng, lat };
478 | }
479 | };
480 |
481 | export const getGuideFeature = (id) => ({
482 | id,
483 | type: geojsonTypes.FEATURE,
484 | properties: {
485 | isSnapGuide: "true", // for styling
486 | },
487 | geometry: {
488 | type: geojsonTypes.LINE_STRING,
489 | coordinates: [],
490 | },
491 | });
492 |
493 | export const shouldHideGuide = (state, geojson) => {
494 | if (
495 | geojson.properties.id === IDS.VERTICAL_GUIDE &&
496 | (!state.options.guides || !state.showVerticalSnapLine)
497 | ) {
498 | return true;
499 | }
500 |
501 | if (
502 | geojson.properties.id === IDS.HORIZONTAL_GUIDE &&
503 | (!state.options.guides || !state.showHorizontalSnapLine)
504 | ) {
505 | return true;
506 | }
507 |
508 | return false;
509 | };
510 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 | const TerserPlugin = require("terser-webpack-plugin");
3 | const BundleAnalyzerPlugin =
4 | require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
5 | const path = require("path");
6 |
7 | module.exports = [
8 | {
9 | mode: "production",
10 | entry: "./src/index.js",
11 | devtool: "source-map",
12 | output: {
13 | path: path.resolve(__dirname, "dist"),
14 | filename: "mapbox-gl-draw-snap-mode.cjs.js",
15 | library: "mapboxGlDrawSnapMode",
16 | libraryTarget: "umd",
17 | globalObject: "this",
18 | // libraryExport: 'default',
19 | },
20 | optimization: {
21 | minimize: true,
22 | minimizer: [new TerserPlugin({ parallel: true })],
23 | },
24 | // externals: [/^(@mapbox\/mapbox-gl-draw).*$/],
25 | // externals: [
26 | // function ({ context, request }, callback) {
27 | // if (/^(@mapbox\/mapbox-gl-draw).*$/.test(request)) {
28 | // // Externalize to a commonjs module using the request path
29 | // return callback(null, {
30 | // root: "MapboxDraw",
31 | // commonjs: request,
32 | // commonjs2: request,
33 | // });
34 | // }
35 |
36 | // // Continue without externalizing the import
37 | // callback();
38 | // },
39 | // ],
40 | module: {
41 | rules: [
42 | {
43 | test: /\.m?js$/,
44 | exclude: /node_modules/,
45 | use: {
46 | loader: "babel-loader",
47 | options: {
48 | presets: ["@babel/preset-env"],
49 | },
50 | },
51 | },
52 | ],
53 | },
54 | plugins: [
55 | // new BundleAnalyzerPlugin({
56 | // analyzerMode: "server",
57 | // generateStatsFile: true,
58 | // statsOptions: { source: false },
59 | // }),
60 | ],
61 | },
62 | {
63 | mode: "production",
64 | entry: "./src/index.js",
65 | devtool: "source-map",
66 | output: {
67 | path: path.resolve(__dirname, "dist"),
68 | filename: "mapbox-gl-draw-snap-mode.esm.js",
69 | library: {
70 | type: "module",
71 | },
72 | },
73 | optimization: {
74 | minimize: true,
75 | minimizer: [new TerserPlugin({ parallel: true })],
76 | },
77 | module: {
78 | rules: [
79 | {
80 | test: /\.m?js$/,
81 | exclude: /node_modules/,
82 | use: {
83 | loader: "babel-loader",
84 | options: {
85 | presets: ["@babel/preset-env"],
86 | },
87 | },
88 | },
89 | ],
90 | },
91 | experiments: {
92 | outputModule: true,
93 | },
94 | },
95 | ];
96 |
--------------------------------------------------------------------------------
/webpack.development.js:
--------------------------------------------------------------------------------
1 | /**
2 | * note:
3 | * Webpack dev Server had a problem with `umd` builds,
4 | * so v4 beta is used to fix that.
5 | *
6 | * it also possible to use the `3.11.0` version as well with `hot` and `injectClient` set to `false`.
7 | */
8 |
9 | const path = require("path");
10 |
11 | module.exports = [
12 | {
13 | mode: "development",
14 | devServer: {
15 | static: path.join(__dirname, "docs"),
16 | compress: true,
17 | port: 9000,
18 | hot: true,
19 | },
20 | entry: "./src/index.js",
21 | devtool: "source-map",
22 | output: {
23 | filename: "index.js",
24 | library: "mapboxGlDrawSnapMode",
25 | libraryTarget: "umd",
26 | globalObject: "this",
27 | },
28 | module: {
29 | rules: [
30 | {
31 | test: /\.m?js$/,
32 | exclude: /node_modules/,
33 | use: {
34 | loader: "babel-loader",
35 | options: {
36 | presets: ["@babel/preset-env"],
37 | },
38 | },
39 | },
40 | ],
41 | },
42 | },
43 | ];
44 |
--------------------------------------------------------------------------------