| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- /*global L:true, ClipperLib:true */
- import ClipperLib from 'clipper-lib/clipper.js'
- L.Clipper = L.Evented.extend({
- options: {
- //featureGroup: new L.FeatureGroup(),
- selectedPathOptions: {
- color: '#FF3399'
- }
- },
- initialize: function (map, options) {
- L.Handler.prototype.initialize.call(this, map);
- L.Util.setOptions(this, options);
- if (!(this.options.featureGroup instanceof L.FeatureGroup)) {
- throw new Error('options.featureGroup must be a L.FeatureGroup');
- }
- },
- enable: function () {
- if (this._enabled || !this._hasAvailableLayers()) {
- return;
- }
- this.fire('enabled', {handler: this.type});
- // this disable other handlers
- this._map.fire('draw:joinstart', { handler: this.type });
- // allow drawLayer to be updated before beginning edition.
- L.Handler.prototype.enable.call(this);
- this.options.featureGroup
- .on('layeradd', this._enableLayerEdit, this)
- .on('layerremove', this._disableLayerEdit, this);
- },
- disable: function () {
- if (!this._enabled) { return; }
- this.options.featureGroup
- .off('layeradd', this._enableLayerJoin, this)
- .off('layerremove', this._disableLayerJoin, this);
- L.Handler.prototype.disable.call(this);
- this._map.fire('draw:editstop', { handler: this.type });
- this.fire('disabled', {handler: this.type});
- },
- addHooks: function () {
- var map = this._map;
- if (map) {
- map.getContainer().focus();
- this.options.featureGroup.eachLayer(this._enableLayerEdit, this);
- // Leaflet.draw specific
- //this._tooltip = new L.tooltip(this._map);
- //this._tooltip.updateContent(this._getTooltipText());
- // this._map.on('mousemove', this._onMouseMove, this);
- }
- },
- removeHooks: function () {
- if (this._map) {
- // Clean up selected layers.
- this.options.featureGroup.eachLayer(this._disableLayerEdit, this);
- // Clear the backups of the original layers
- this._uneditedLayerProps = {};
- this._tooltip.dispose();
- this._tooltip = null;
- this._map.off('mousemove', this._onMouseMove, this);
- }
- },
- revertLayers: function () {
- this.options.featureGroup.eachLayer(function (layer) {
- this._revertLayer(layer);
- }, this);
- },
- save: function () {
- var editedLayers = new L.LayerGroup();
- this.options.featureGroup.eachLayer(function (layer) {
- if (layer.edited) {
- editedLayers.addLayer(layer);
- layer.edited = false;
- }
- });
- this._map.fire('draw:edited', {layers: editedLayers});
- },
- _revertLayer: function (layer) {
- var id = L.Util.stamp(layer);
- layer.edited = false;
- if (this._uneditedLayerProps.hasOwnProperty(id)) {
- // Polyline, Polygon or Rectangle
- if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
- layer.setLatLngs(this._uneditedLayerProps[id].latlngs);
- } else if (layer instanceof L.Circle) {
- layer.setLatLng(this._uneditedLayerProps[id].latlng);
- layer.setRadius(this._uneditedLayerProps[id].radius);
- } else if (layer instanceof L.Marker) { // Marker
- layer.setLatLng(this._uneditedLayerProps[id].latlng);
- }
- }
- },
- _enableLayerEdit: function (e) {
- var layer = e.layer || e.target || e;
- layer.on('click', this._subjectLayer, this);
- },
- _disableLayerEdit: function (e) {
- var layer = e.layer || e.target || e;
- layer.off('click');
- },
- // Only supported in Leaflet.draw
- /*_getTooltipText: function () {
- var labelText;
- if (!this._subject) {
- labelText = {
- text: 'Choose a subject layer.'
- };
- } else {
- labelText = {
- text: 'Choose a clipper layer.'
- };
- }
- return labelText;
- },*/
- _onMouseMove: function (e) {
- this._tooltip.updatePosition(e.latlng);
- },
- _hasAvailableLayers: function () {
- return this.options.featureGroup.getLayers().length !== 0;
- },
- _subjectLayer: function(e) {
- var layer = e.layer || e.target || e;
- if (!this._subject) {
- this._subject = layer;
- } else {
- this._clipperLayer(e);
- }
- layer.setStyle(this.options.selectedPathOptions);
- },
- _clipperLayer: function(e) {
- var layer = e.layer || e.target || e;
- this._clipper = layer;
- layer.setStyle(this.options.selectedPathOptions);
- this._layerClip();
- },
-
- _layerClip: function() {
-
- var subjCoords = this._subject.getLatLngs();
- var clipCoords = this._clipper.getLatLngs();
- var subj = this._coordsToPoints(subjCoords);
- var clip = this._coordsToPoints(clipCoords);
- var solution = ClipperLib.Paths();
-
- var cpr = new ClipperLib.Clipper();
- for(var s = 0, slen = subj.length; s<slen; s++) {
- cpr.AddPaths(subj[s], ClipperLib.PolyType.ptSubject, true);
- }
- for(var c = 0, clen = clip.length; c < clen; c++) {
- cpr.AddPaths(clip[c], ClipperLib.PolyType.ptClip, true);
- }
- cpr.Execute(this._cliptype, solution);
-
- this._solution = L.polygon( this._pointsToCoords([solution], true) );
-
- this.options.featureGroup.addLayer(this._solution);
- this.options.featureGroup.removeLayer(this._subject);
- this.options.featureGroup.removeLayer(this._clipper);
-
- this._subject = null;
- this._clipper = null;
- },
-
- /*
- --- ClipperJS ---
- Add in ability to convert coords or latlngs to x,y points
- to be used by clipperjs
-
- 4503599627370495 min/max
- 180.1234567890123 min/max
-
- */
- _pointAmplifier: function() {
- return Math.pow(10,13);
- },
-
- // coord: [lng, lat] / [x, y]
- // latlng: [lat, lng] / { lat:lat, lng:lng }
- // point: { X:x, Y:y }
- // polygons: [
- // [ // polygon
- // coords,
- // holes
- // ]
- // ]
- _coordsToPoints: function (polygons, latlng) {
- var points = [], amp = this._pointAmplifier();
- for (var i = 0, ilen = polygons.length; i < ilen; i++) {
- points.push([]);
- for (var j = 0, jlen = polygons[i].length; j < jlen; j++) {
- var coords = polygons[i][j];
- points[i].push([]);
- for (var k = 0, klen = coords.length; k < klen; k++) {
- var coord = coords[k];
- if (Array.isArray(coord)) {
- if (latlng) {
- points[i][j].push({X: Math.round(coord[1] * amp), Y: Math.round(coord[0] * amp)});
- } else {
- points[i][j].push({X: Math.round(coord[0] * amp), Y: Math.round(coord[1] * amp)});
- }
- } else {
- points[i][j].push({X: Math.round(coord.lng * amp), Y: Math.round(coord.lat * amp)});
- }
- }
- }
- }
- return points;
- },
- _pointsToCoords: function (polygons, latlng) {
- var coords = [], amp = this._pointAmplifier();
- for (var i = 0, ilen = polygons.length; i < ilen; i++) {
- coords.push([]);
- for (var j = 0, jlen = polygons[i].length; j < jlen; j++) {
- var points = polygons[i][j];
- coords[i].push([]);
- for (var k = 0, klen = points.length; k < klen; k++) {
- var point = points[k];
- if (latlng) {
- coords[i][j].push([point.Y / amp, point.X / amp]);
- } else {
- coords[i][j].push([point.X / amp, point.Y / amp]);
- }
- }
- }
- }
- return coords;
- }
- // --- End ClipperJS --- //
- });
- L.Clipper.AND = L.Clipper.extend({
- // Save the type so super can fire, need to do this as cannot do this.TYPE :(
- _cliptype: ClipperLib.ClipType.ctIntersection
- });
- L.Clipper.Intersection = L.Clipper.AND;
- L.Clipper.OR = L.Clipper.extend({
- _cliptype: ClipperLib.ClipType.ctUnion
- });
- L.Clipper.Union = L.Clipper.OR;
- L.Clipper.NOT = L.Clipper.extend({
- _cliptype: ClipperLib.ClipType.ctDifference
- });
- L.Clipper.Difference = L.Clipper.NOT;
- L.Clipper.XOR = L.Clipper.extend({
- _cliptype: ClipperLib.ClipType.ctXor
- });
- L.Clipper.Xor = L.Clipper.XOR;
|