├── .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 | [![npm](https://img.shields.io/npm/v/mapbox-gl-draw-snap-mode?color=green)](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 | ![a GIF showing usage demo](docs/demo.gif) 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 | --------------------------------------------------------------------------------