├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── demo ├── demo.js └── index.html ├── dist ├── maptalks.collisionLayer.es.js ├── maptalks.collisionLayer.js └── maptalks.collisionLayer.min.js ├── gulpfile.js ├── index.js └── package.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "loose" : true, "modules" : false }] 4 | ], 5 | "plugins" : [ 6 | "transform-proto-to-assign" 7 | ], 8 | "ignore": [ 9 | "dist/*.js" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /.idea 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 likecao 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 | # maptalks-collisionLayer 2 | 3 | A marker collision plugin for [maptalks](http://maptalks.org/) and uses [rbush](https://github.com/mourner/rbush) library. 4 | 5 | [demo](https://ageeye-cn.github.io/maptalks.collisionLayer/demo/index.html) 6 | 7 | # Usage 8 | 9 | ```javascript 10 | var collisionLayer = new maptalks.CollisionLayer('layerName').addTo(map) 11 | collisionLayer.updateCollision() 12 | ``` 13 | 14 | # Options 15 | 16 | Option | Type | Default 17 | ------ | ---- | ------- 18 | `activeId` | String | ```null``` 19 | `isCollision` | Boolean | ```true``` 20 | `isShowCollisionPoints` | Boolean | ```true``` 21 | `hidePointsId` | String | ```hidePoints``` 22 | `hidePointsSymbol` | Object | ```{'markerType': 'ellipse','markerFillOpacity': 0.3,'markerLineOpacity': 0.3,'markerWidth': 3,'markerHeight': 3,}``` 23 | 24 | # Methods 25 | 26 | Method | Return | Description 27 | ------ | ---- | ------- 28 | `updateCollision()` | | 29 | `setActiveId( id)` | | 30 | `isShowCollisionPoints()` | Boolean | 31 | `showCollisionPoints()` | | 32 | `hideCollisionPoints()` | | 33 | `enableCollision()` | | 34 | `disableCollision()` | | 35 | `isCollision()` | Boolean | 36 | 37 | # License 38 | 39 | MIT License. 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | const map = new maptalks.Map('map', { 2 | center: [104,31], 3 | zoom: 5, 4 | attribution: { 5 | content: '© OpenStreetMap contributors, © CARTO' 6 | }, 7 | baseLayer: new maptalks.TileLayer('base', { 8 | urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', 9 | subdomains: ['a','b','c','d'] 10 | }) 11 | }) 12 | 13 | const collisionLayer = new maptalks.CollisionLayer('layerName').addTo(map) 14 | 15 | collisionLayer.addGeometry(new maptalks.LineString([[104,31],[104,32]])) 16 | 17 | var extent = map.getExtent(), 18 | min = extent.getMin(), 19 | w = extent.getWidth(), 20 | h = extent.getHeight(), 21 | markers = []; 22 | for (var i = 0; i < 1000; i++) { 23 | markers.push(new maptalks.Marker([min.x + Math.random() * w, min.y + Math.random() * h])); 24 | } 25 | 26 | collisionLayer.addGeometry(markers) 27 | collisionLayer.updateCollision() 28 | 29 | function enableCollision() { 30 | collisionLayer.enableCollision() 31 | } 32 | 33 | function disableCollision() { 34 | collisionLayer.disableCollision() 35 | } 36 | 37 | function showCollisionPoints() { 38 | collisionLayer.showCollisionPoints() 39 | } 40 | 41 | function hideCollisionPoints() { 42 | collisionLayer.hideCollisionPoints() 43 | } 44 | 45 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 18 | 19 | 20 |
21 |
22 | Enable Collision 23 | disable Collision 24 | show collision points 25 | hide collision points 26 |
27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /dist/maptalks.collisionLayer.es.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * maptalks.collisionLayer v0.2.2 3 | * LICENSE : MIT 4 | * (c) 2016-2018 maptalks.org 5 | */ 6 | /*! 7 | * requires maptalks@>=0.36.0 8 | */ 9 | import { MultiPoint, VectorLayer } from 'maptalks'; 10 | import rbush from 'rbush'; 11 | 12 | function _defaults(obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; } 13 | 14 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 15 | 16 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 17 | 18 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : _defaults(subClass, superClass); } 19 | 20 | var options = { 21 | activeId: null, 22 | isCollision: true, 23 | isShowCollisionPoints: true, 24 | hidePointsId: 'hidePoints', 25 | hidePointsSymbol: { 26 | 'markerType': 'ellipse', 27 | 'markerFillOpacity': 0.3, 28 | 'markerLineOpacity': 0.3, 29 | 'markerWidth': 3, 30 | 'markerHeight': 3 31 | } 32 | }; 33 | 34 | var CollisionLayer = function (_maptalks$VectorLayer) { 35 | _inherits(CollisionLayer, _maptalks$VectorLayer); 36 | 37 | function CollisionLayer() { 38 | _classCallCheck(this, CollisionLayer); 39 | 40 | return _possibleConstructorReturn(this, _maptalks$VectorLayer.apply(this, arguments)); 41 | } 42 | 43 | CollisionLayer.prototype.onAdd = function onAdd() { 44 | this.map.on('viewchange', this.onViewChange, this); 45 | this._rbush = rbush(); 46 | this._hidePoints = new MultiPoint([], { 47 | id: this.options.hidePointsId, 48 | symbol: this.options.hidePointsSymbol 49 | }); 50 | }; 51 | 52 | CollisionLayer.prototype.onViewChange = function onViewChange() { 53 | var _this2 = this; 54 | 55 | setTimeout(function () { 56 | return _this2.updateCollision(); 57 | }, 0); 58 | }; 59 | 60 | CollisionLayer.prototype.updateCollision = function updateCollision() { 61 | var _this3 = this; 62 | 63 | if (!this.options.isCollision) { 64 | return; 65 | } 66 | 67 | if (!this.getGeometryById(this.options.hidePointsId)) { 68 | this.addGeometry(this._hidePoints); 69 | } 70 | 71 | this._rbush.clear(); 72 | 73 | var hidePoints = [], 74 | _options = this.options, 75 | activeId = _options.activeId, 76 | isShowCollisionPoints = _options.isShowCollisionPoints, 77 | activeGeometry = this.getGeometryById(activeId), 78 | markers = this.getMarkers(); 79 | 80 | 81 | if (activeGeometry && activeGeometry.type === 'Point') { 82 | var box = this.getMarkerBox(activeGeometry); 83 | if (box) { 84 | this._rbush.insert(box); 85 | } 86 | activeGeometry.show(); 87 | } 88 | 89 | markers.forEach(function (marker) { 90 | if (activeGeometry === marker) { 91 | return; 92 | } 93 | 94 | var box = _this3.getMarkerBox(marker); 95 | 96 | if (!box) { 97 | marker.show(); 98 | return; 99 | } 100 | 101 | var result = _this3._rbush.search(box); 102 | 103 | if (result.length === 0) { 104 | _this3._rbush.insert(box); 105 | marker.show(); 106 | } else { 107 | marker.hide(); 108 | hidePoints.push(marker.getCoordinates()); 109 | } 110 | }); 111 | 112 | if (isShowCollisionPoints) { 113 | this._hidePoints.setCoordinates(hidePoints); 114 | this._hidePoints.bringToBack(); 115 | } else { 116 | this._hidePoints.setCoordinates([]); 117 | } 118 | }; 119 | 120 | CollisionLayer.prototype.getMarkerBox = function getMarkerBox(marker) { 121 | var size = marker.getSize(); 122 | 123 | if (!size) { 124 | return; 125 | } 126 | 127 | var width = size.width, 128 | height = size.height, 129 | coordinates = marker.getCoordinates(); 130 | 131 | 132 | if (!coordinates) { 133 | return; 134 | } 135 | 136 | var _map$coordinateToCont = this.map.coordinateToContainerPoint(marker.getCoordinates()), 137 | x = _map$coordinateToCont.x, 138 | y = _map$coordinateToCont.y, 139 | minX = Math.round(x - width / 2), 140 | maxX = Math.round(x + width / 2), 141 | minY = Math.round(y - height / 2), 142 | maxY = Math.round(y + height / 2); 143 | 144 | return { minX: minX, maxX: maxX, minY: minY, maxY: maxY }; 145 | }; 146 | 147 | CollisionLayer.prototype.getMarkers = function getMarkers() { 148 | return this.getGeometries(function (geometry) { 149 | return geometry.type === 'Point' && geometry; 150 | }); 151 | }; 152 | 153 | CollisionLayer.prototype.isShowCollisionPoints = function isShowCollisionPoints() { 154 | return this.options.isShowCollisionPoints; 155 | }; 156 | 157 | CollisionLayer.prototype.showCollisionPoints = function showCollisionPoints() { 158 | this.options.isShowCollisionPoints = true; 159 | this.updateCollision(); 160 | }; 161 | 162 | CollisionLayer.prototype.hideCollisionPoints = function hideCollisionPoints() { 163 | this.options.isShowCollisionPoints = false; 164 | this.updateCollision(); 165 | }; 166 | 167 | CollisionLayer.prototype.setActiveId = function setActiveId(id) { 168 | this.options.activeId = id; 169 | this.updateCollision(); 170 | }; 171 | 172 | CollisionLayer.prototype.enableCollision = function enableCollision() { 173 | this.options.isCollision = true; 174 | this.updateCollision(); 175 | }; 176 | 177 | CollisionLayer.prototype.disableCollision = function disableCollision() { 178 | this.options.isCollision = false; 179 | 180 | this._hidePoints.setCoordinates([]); 181 | var markers = this.getMarkers(); 182 | markers.forEach(function (marker) { 183 | marker.show(); 184 | }); 185 | }; 186 | 187 | CollisionLayer.prototype.isCollision = function isCollision() { 188 | return this.options.isCollision; 189 | }; 190 | 191 | return CollisionLayer; 192 | }(VectorLayer); 193 | 194 | CollisionLayer.mergeOptions(options); 195 | 196 | export { CollisionLayer }; 197 | 198 | typeof console !== 'undefined' && console.log('maptalks.collisionLayer v0.2.2, requires maptalks@>=0.36.0.'); 199 | -------------------------------------------------------------------------------- /dist/maptalks.collisionLayer.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * maptalks.collisionLayer v0.2.2 3 | * LICENSE : MIT 4 | * (c) 2016-2018 maptalks.org 5 | */ 6 | /*! 7 | * requires maptalks@>=0.36.0 8 | */ 9 | (function (global, factory) { 10 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('maptalks'), require('rbush')) : 11 | typeof define === 'function' && define.amd ? define(['exports', 'maptalks', 'rbush'], factory) : 12 | (factory((global.maptalks = global.maptalks || {}),global.maptalks,global.rbush)); 13 | }(this, (function (exports,maptalks,rbush) { 'use strict'; 14 | 15 | rbush = rbush && rbush.hasOwnProperty('default') ? rbush['default'] : rbush; 16 | 17 | function _defaults(obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; } 18 | 19 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 20 | 21 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 22 | 23 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : _defaults(subClass, superClass); } 24 | 25 | var options = { 26 | activeId: null, 27 | isCollision: true, 28 | isShowCollisionPoints: true, 29 | hidePointsId: 'hidePoints', 30 | hidePointsSymbol: { 31 | 'markerType': 'ellipse', 32 | 'markerFillOpacity': 0.3, 33 | 'markerLineOpacity': 0.3, 34 | 'markerWidth': 3, 35 | 'markerHeight': 3 36 | } 37 | }; 38 | 39 | var CollisionLayer = function (_maptalks$VectorLayer) { 40 | _inherits(CollisionLayer, _maptalks$VectorLayer); 41 | 42 | function CollisionLayer() { 43 | _classCallCheck(this, CollisionLayer); 44 | 45 | return _possibleConstructorReturn(this, _maptalks$VectorLayer.apply(this, arguments)); 46 | } 47 | 48 | CollisionLayer.prototype.onAdd = function onAdd() { 49 | this.map.on('viewchange', this.onViewChange, this); 50 | this._rbush = rbush(); 51 | this._hidePoints = new maptalks.MultiPoint([], { 52 | id: this.options.hidePointsId, 53 | symbol: this.options.hidePointsSymbol 54 | }); 55 | }; 56 | 57 | CollisionLayer.prototype.onViewChange = function onViewChange() { 58 | var _this2 = this; 59 | 60 | setTimeout(function () { 61 | return _this2.updateCollision(); 62 | }, 0); 63 | }; 64 | 65 | CollisionLayer.prototype.updateCollision = function updateCollision() { 66 | var _this3 = this; 67 | 68 | if (!this.options.isCollision) { 69 | return; 70 | } 71 | 72 | if (!this.getGeometryById(this.options.hidePointsId)) { 73 | this.addGeometry(this._hidePoints); 74 | } 75 | 76 | this._rbush.clear(); 77 | 78 | var hidePoints = [], 79 | _options = this.options, 80 | activeId = _options.activeId, 81 | isShowCollisionPoints = _options.isShowCollisionPoints, 82 | activeGeometry = this.getGeometryById(activeId), 83 | markers = this.getMarkers(); 84 | 85 | 86 | if (activeGeometry && activeGeometry.type === 'Point') { 87 | var box = this.getMarkerBox(activeGeometry); 88 | if (box) { 89 | this._rbush.insert(box); 90 | } 91 | activeGeometry.show(); 92 | } 93 | 94 | markers.forEach(function (marker) { 95 | if (activeGeometry === marker) { 96 | return; 97 | } 98 | 99 | var box = _this3.getMarkerBox(marker); 100 | 101 | if (!box) { 102 | marker.show(); 103 | return; 104 | } 105 | 106 | var result = _this3._rbush.search(box); 107 | 108 | if (result.length === 0) { 109 | _this3._rbush.insert(box); 110 | marker.show(); 111 | } else { 112 | marker.hide(); 113 | hidePoints.push(marker.getCoordinates()); 114 | } 115 | }); 116 | 117 | if (isShowCollisionPoints) { 118 | this._hidePoints.setCoordinates(hidePoints); 119 | this._hidePoints.bringToBack(); 120 | } else { 121 | this._hidePoints.setCoordinates([]); 122 | } 123 | }; 124 | 125 | CollisionLayer.prototype.getMarkerBox = function getMarkerBox(marker) { 126 | var size = marker.getSize(); 127 | 128 | if (!size) { 129 | return; 130 | } 131 | 132 | var width = size.width, 133 | height = size.height, 134 | coordinates = marker.getCoordinates(); 135 | 136 | 137 | if (!coordinates) { 138 | return; 139 | } 140 | 141 | var _map$coordinateToCont = this.map.coordinateToContainerPoint(marker.getCoordinates()), 142 | x = _map$coordinateToCont.x, 143 | y = _map$coordinateToCont.y, 144 | minX = Math.round(x - width / 2), 145 | maxX = Math.round(x + width / 2), 146 | minY = Math.round(y - height / 2), 147 | maxY = Math.round(y + height / 2); 148 | 149 | return { minX: minX, maxX: maxX, minY: minY, maxY: maxY }; 150 | }; 151 | 152 | CollisionLayer.prototype.getMarkers = function getMarkers() { 153 | return this.getGeometries(function (geometry) { 154 | return geometry.type === 'Point' && geometry; 155 | }); 156 | }; 157 | 158 | CollisionLayer.prototype.isShowCollisionPoints = function isShowCollisionPoints() { 159 | return this.options.isShowCollisionPoints; 160 | }; 161 | 162 | CollisionLayer.prototype.showCollisionPoints = function showCollisionPoints() { 163 | this.options.isShowCollisionPoints = true; 164 | this.updateCollision(); 165 | }; 166 | 167 | CollisionLayer.prototype.hideCollisionPoints = function hideCollisionPoints() { 168 | this.options.isShowCollisionPoints = false; 169 | this.updateCollision(); 170 | }; 171 | 172 | CollisionLayer.prototype.setActiveId = function setActiveId(id) { 173 | this.options.activeId = id; 174 | this.updateCollision(); 175 | }; 176 | 177 | CollisionLayer.prototype.enableCollision = function enableCollision() { 178 | this.options.isCollision = true; 179 | this.updateCollision(); 180 | }; 181 | 182 | CollisionLayer.prototype.disableCollision = function disableCollision() { 183 | this.options.isCollision = false; 184 | 185 | this._hidePoints.setCoordinates([]); 186 | var markers = this.getMarkers(); 187 | markers.forEach(function (marker) { 188 | marker.show(); 189 | }); 190 | }; 191 | 192 | CollisionLayer.prototype.isCollision = function isCollision() { 193 | return this.options.isCollision; 194 | }; 195 | 196 | return CollisionLayer; 197 | }(maptalks.VectorLayer); 198 | 199 | CollisionLayer.mergeOptions(options); 200 | 201 | exports.CollisionLayer = CollisionLayer; 202 | 203 | Object.defineProperty(exports, '__esModule', { value: true }); 204 | 205 | typeof console !== 'undefined' && console.log('maptalks.collisionLayer v0.2.2, requires maptalks@>=0.36.0.'); 206 | 207 | }))); 208 | -------------------------------------------------------------------------------- /dist/maptalks.collisionLayer.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * maptalks.collisionLayer v0.2.2 3 | * LICENSE : MIT 4 | * (c) 2016-2018 maptalks.org 5 | */ 6 | /*! 7 | * requires maptalks@>=0.36.0 8 | */ 9 | !function(t,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports,require("maptalks"),require("rbush")):"function"==typeof define&&define.amd?define(["exports","maptalks","rbush"],o):o(t.maptalks=t.maptalks||{},t.maptalks,t.rbush)}(this,function(t,o,i){"use strict";function e(t,o){if("function"!=typeof o&&null!==o)throw new TypeError("Super expression must either be null or a function, not "+typeof o);t.prototype=Object.create(o&&o.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),o&&(Object.setPrototypeOf?Object.setPrototypeOf(t,o):function(t,o){for(var i=Object.getOwnPropertyNames(o),e=0;e=0.36.0.")}); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'), 2 | pkg = require('./package.json'), 3 | BundleHelper = require('maptalks-build-helpers').BundleHelper, 4 | babel = require('maptalks-rollup-plugin-babel'), 5 | commonjs = require('rollup-plugin-commonjs'), 6 | localResolve = require('rollup-plugin-local-resolve'), 7 | nodeResolve = require('rollup-plugin-node-resolve'); 8 | 9 | const bundleHelper = new BundleHelper(pkg); 10 | 11 | console.log(bundleHelper) 12 | 13 | gulp.task('build', () => { 14 | return bundleHelper.bundle('index.js', { 15 | 'external': [ 16 | 'maptalks', 17 | 'rbush' 18 | ], 19 | 'plugins': [ 20 | localResolve(), 21 | nodeResolve({ 22 | module: true, 23 | jsnext: true, 24 | main: true 25 | }), 26 | commonjs(), 27 | babel({ 28 | plugins: ['transform-proto-to-assign'] 29 | }) 30 | ] 31 | }); 32 | }); 33 | 34 | gulp.task('minify', ['build'], () => { 35 | bundleHelper.minify(); 36 | }); 37 | 38 | gulp.task('watch', ['build'], () => { 39 | gulp.watch(['index.js', './gulpfile.js'], ['build']); 40 | }); 41 | 42 | /* 43 | const TestHelper = require('maptalks-build-helpers').TestHelper; 44 | const testHelper = new TestHelper(); 45 | const karmaConfig = require('./karma.config.js'); 46 | 47 | gulp.task('test', ['build'], () => { 48 | testHelper.test(karmaConfig); 49 | }); 50 | 51 | gulp.task('tdd', ['build'], () => { 52 | karmaConfig.singleRun = false; 53 | gulp.watch(['index.js'], ['test']); 54 | testHelper.test(karmaConfig); 55 | }); */ 56 | 57 | gulp.task('default', ['watch']); 58 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import * as maptalks from 'maptalks' 2 | import rbush from 'rbush' 3 | 4 | const options = { 5 | activeId: null, 6 | isCollision: true, 7 | isShowCollisionPoints: true, 8 | hidePointsId: 'hidePoints', 9 | hidePointsSymbol: { 10 | 'markerType': 'ellipse', 11 | 'markerFillOpacity': 0.3, 12 | 'markerLineOpacity': 0.3, 13 | 'markerWidth': 3, 14 | 'markerHeight': 3, 15 | } 16 | } 17 | 18 | export class CollisionLayer extends maptalks.VectorLayer { 19 | onAdd() { 20 | this.map.on('viewchange', this.onViewChange, this) 21 | this._rbush = rbush() 22 | this._hidePoints = new maptalks.MultiPoint([], { 23 | id: this.options.hidePointsId, 24 | symbol: this.options.hidePointsSymbol 25 | }) 26 | } 27 | 28 | onViewChange() { 29 | setTimeout(() => this.updateCollision(), 0) 30 | } 31 | 32 | updateCollision() { 33 | if (!this.options.isCollision) { 34 | return 35 | } 36 | 37 | if (!this.getGeometryById(this.options.hidePointsId)) { 38 | this.addGeometry(this._hidePoints) 39 | } 40 | 41 | this._rbush.clear() 42 | 43 | const hidePoints = [], 44 | {activeId, isShowCollisionPoints} = this.options, 45 | activeGeometry = this.getGeometryById(activeId), 46 | markers = this.getMarkers() 47 | 48 | if (activeGeometry && activeGeometry.type === 'Point') { 49 | const box = this.getMarkerBox(activeGeometry) 50 | if (box){ 51 | this._rbush.insert(box) 52 | } 53 | activeGeometry.show() 54 | } 55 | 56 | markers.forEach(marker => { 57 | if (activeGeometry === marker) { 58 | return 59 | } 60 | 61 | const box = this.getMarkerBox(marker) 62 | 63 | if (!box){ 64 | marker.show() 65 | return 66 | } 67 | 68 | const result = this._rbush.search(box) 69 | 70 | if (result.length === 0) { 71 | this._rbush.insert(box) 72 | marker.show() 73 | } else { 74 | marker.hide() 75 | hidePoints.push(marker.getCoordinates()) 76 | } 77 | }) 78 | 79 | if (isShowCollisionPoints) { 80 | this._hidePoints.setCoordinates(hidePoints) 81 | this._hidePoints.bringToBack() 82 | } else { 83 | this._hidePoints.setCoordinates([]) 84 | } 85 | } 86 | 87 | getMarkerBox(marker) { 88 | const size = marker.getSize() 89 | 90 | if (!size){ 91 | return 92 | } 93 | 94 | const {width, height} = size, 95 | coordinates = marker.getCoordinates() 96 | 97 | if (!coordinates){ 98 | return 99 | } 100 | 101 | const {x, y} = this.map.coordinateToContainerPoint(marker.getCoordinates()), 102 | minX = Math.round(x - width / 2), 103 | maxX = Math.round(x + width / 2), 104 | minY = Math.round(y - height / 2), 105 | maxY = Math.round(y + height / 2) 106 | 107 | return {minX, maxX, minY, maxY} 108 | } 109 | 110 | getMarkers() { 111 | return this.getGeometries(geometry => { 112 | return geometry.type === 'Point' && geometry 113 | }) 114 | } 115 | 116 | isShowCollisionPoints() { 117 | return this.options.isShowCollisionPoints 118 | } 119 | 120 | showCollisionPoints() { 121 | this.options.isShowCollisionPoints = true 122 | this.updateCollision() 123 | } 124 | 125 | hideCollisionPoints() { 126 | this.options.isShowCollisionPoints = false 127 | this.updateCollision() 128 | } 129 | 130 | setActiveId(id) { 131 | this.options.activeId = id 132 | this.updateCollision() 133 | } 134 | 135 | enableCollision() { 136 | this.options.isCollision = true 137 | this.updateCollision() 138 | } 139 | 140 | disableCollision() { 141 | this.options.isCollision = false 142 | 143 | this._hidePoints.setCoordinates([]) 144 | const markers = this.getMarkers() 145 | markers.forEach(marker => { 146 | marker.show() 147 | }) 148 | } 149 | 150 | isCollision() { 151 | return this.options.isCollision 152 | } 153 | } 154 | 155 | CollisionLayer.mergeOptions(options); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "maptalks.collisionLayer", 3 | "version": "0.2.2", 4 | "description": "A mouse coordinate plugin for maptalks.js", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "" 9 | }, 10 | "main": "dist/maptalks.collisionLayer.js", 11 | "module": "dist/maptalks.collisionLayer.es.js", 12 | "jsnext:main": "dist/maptalks.collisionLayer.es.js", 13 | "files": [ 14 | "dist/maptalks.collisionLayer.js", 15 | "dist/maptalks.collisionLayer.min.js", 16 | "dist/maptalks.collisionLayer.es.js" 17 | ], 18 | "scripts": { 19 | "version": "gulp minify && git add -A dist", 20 | "prepublish": "gulp minify" 21 | }, 22 | "devDependencies": { 23 | "babel-eslint": "^7.1.1", 24 | "babel-preset-es2015": "^6.18.0", 25 | "gulp": "^3.9.0", 26 | "maptalks": ">=0.36.0", 27 | "maptalks-build-helpers": "^0.4.2", 28 | "rbush": "^2.0.1" 29 | }, 30 | "peerDependencies": { 31 | "maptalks": ">=0.36.0", 32 | "rbush": "^2.0.1" 33 | }, 34 | "dependencies": { 35 | } 36 | } 37 | --------------------------------------------------------------------------------