// node_modules/leaflet-boundary-canvas/src/BoundaryCanvas.js
(function() {
  var isRingBbox = function(ring, bbox) {
    if (ring.length !== 4) {
      return false;
    }
    var p, sumX = 0, sumY = 0;
    for (p = 0; p < 4; p++) {
      if (ring[p].x !== bbox.min.x && ring[p].x !== bbox.max.x || ring[p].y !== bbox.min.y && ring[p].y !== bbox.max.y) {
        return false;
      }
      sumX += ring[p].x;
      sumY += ring[p].y;
    }
    return sumX === 2 * (bbox.min.x + bbox.max.x) && sumY === 2 * (bbox.min.y + bbox.max.y);
  };
  var ExtendMethods = {
    _toMercGeometry: function(b, isGeoJSON) {
      var res = [];
      var c, r, p, mercComponent, mercRing, coords;
      if (!isGeoJSON) {
        if (!(b[0] instanceof Array)) {
          b = [[b]];
        } else if (!(b[0][0] instanceof Array)) {
          b = [b];
        }
      }
      for (c = 0; c < b.length; c++) {
        mercComponent = [];
        for (r = 0; r < b[c].length; r++) {
          mercRing = [];
          for (p = 0; p < b[c][r].length; p++) {
            coords = isGeoJSON ? L.latLng(b[c][r][p][1], b[c][r][p][0]) : b[c][r][p];
            mercRing.push(this._map.project(coords, 0));
          }
          mercComponent.push(mercRing);
        }
        res.push(mercComponent);
      }
      return res;
    },
    //lazy calculation of layer's boundary in map's projection. Bounding box is also calculated
    _getOriginalMercBoundary: function() {
      if (this._mercBoundary) {
        return this._mercBoundary;
      }
      var compomentBbox;
      if (L.Util.isArray(this.options.boundary)) {
        this._mercBoundary = this._toMercGeometry(this.options.boundary);
      } else {
        this._mercBoundary = [];
        var processGeoJSONObject = (function(obj) {
          if (obj.type === "GeometryCollection") {
            obj.geometries.forEach(processGeoJSONObject);
          } else if (obj.type === "Feature") {
            processGeoJSONObject(obj.geometry);
          } else if (obj.type === "FeatureCollection") {
            obj.features.forEach(processGeoJSONObject);
          } else if (obj.type === "Polygon") {
            this._mercBoundary = this._mercBoundary.concat(this._toMercGeometry([obj.coordinates], true));
          } else if (obj.type === "MultiPolygon") {
            this._mercBoundary = this._mercBoundary.concat(this._toMercGeometry(obj.coordinates, true));
          }
        }).bind(this);
        processGeoJSONObject(this.options.boundary);
      }
      this._mercBbox = new L.Bounds();
      for (var c = 0; c < this._mercBoundary.length; c++) {
        compomentBbox = new L.Bounds(this._mercBoundary[c][0]);
        this._mercBbox.extend(compomentBbox.min);
        this._mercBbox.extend(compomentBbox.max);
      }
      return this._mercBoundary;
    },
    _getClippedGeometry: function(geom, bounds) {
      var clippedGeom = [], clippedComponent, clippedExternalRing, clippedHoleRing, iC, iR;
      for (iC = 0; iC < geom.length; iC++) {
        clippedComponent = [];
        clippedExternalRing = L.PolyUtil.clipPolygon(geom[iC][0], bounds);
        if (clippedExternalRing.length === 0) {
          continue;
        }
        clippedComponent.push(clippedExternalRing);
        for (iR = 1; iR < geom[iC].length; iR++) {
          clippedHoleRing = L.PolyUtil.clipPolygon(geom[iC][iR], bounds);
          if (clippedHoleRing.length > 0) {
            clippedComponent.push(clippedHoleRing);
          }
        }
        clippedGeom.push(clippedComponent);
      }
      if (clippedGeom.length === 0) {
        return { isOut: true };
      }
      for (iC = 0; iC < clippedGeom.length; iC++) {
        if (isRingBbox(clippedGeom[iC][0], bounds)) {
          if (clippedGeom[iC].length === 1) {
            return { isIn: true };
          }
        } else {
          return { geometry: clippedGeom };
        }
        for (iR = 1; iR < clippedGeom[iC].length; iR++) {
          if (!isRingBbox(clippedGeom[iC][iR], bounds)) {
            return { geometry: clippedGeom };
          }
        }
      }
      return { isOut: true };
    },
    // Calculates intersection of original boundary geometry and tile boundary.
    // Uses quadtree as cache to speed-up intersection.
    // Return 
    //   {isOut: true} if no intersection,  
    //   {isIn: true} if tile is fully inside layer's boundary
    //   {geometry: <LatLng[][][]>} otherwise
    _getTileGeometry: function(x, y, z, skipIntersectionCheck) {
      if (!this.options.boundary) {
        return { isIn: true };
      }
      var cacheID = x + ":" + y + ":" + z, zCoeff = Math.pow(2, z), parentState, cache = this._boundaryCache;
      if (cache[cacheID]) {
        return cache[cacheID];
      }
      var mercBoundary = this._getOriginalMercBoundary(), ts = this.options.tileSize, tileBbox = new L.Bounds(new L.Point(x * ts / zCoeff, y * ts / zCoeff), new L.Point((x + 1) * ts / zCoeff, (y + 1) * ts / zCoeff));
      if (!skipIntersectionCheck && !tileBbox.intersects(this._mercBbox)) {
        return { isOut: true };
      }
      if (z === 0) {
        cache[cacheID] = { geometry: mercBoundary };
        return cache[cacheID];
      }
      parentState = this._getTileGeometry(Math.floor(x / 2), Math.floor(y / 2), z - 1, true);
      if (parentState.isOut || parentState.isIn) {
        return parentState;
      }
      cache[cacheID] = this._getClippedGeometry(parentState.geometry, tileBbox);
      return cache[cacheID];
    },
    _drawTileInternal: function(canvas, tilePoint, url, callback) {
      var zoom = this._getZoomForUrl(), state = this._getTileGeometry(tilePoint.x, tilePoint.y, zoom);
      if (state.isOut) {
        callback();
        return;
      }
      var ts = this.options.tileSize, tileX = ts * tilePoint.x, tileY = ts * tilePoint.y, zCoeff = Math.pow(2, zoom), ctx = canvas.getContext("2d"), imageObj = new Image(), _this = this;
      var setPattern = function() {
        var c, r, p, pattern, geom;
        if (!state.isIn) {
          geom = state.geometry;
          ctx.beginPath();
          for (c = 0; c < geom.length; c++) {
            for (r = 0; r < geom[c].length; r++) {
              if (geom[c][r].length === 0) {
                continue;
              }
              ctx.moveTo(geom[c][r][0].x * zCoeff - tileX, geom[c][r][0].y * zCoeff - tileY);
              for (p = 1; p < geom[c][r].length; p++) {
                ctx.lineTo(geom[c][r][p].x * zCoeff - tileX, geom[c][r][p].y * zCoeff - tileY);
              }
            }
          }
          ctx.clip();
        }
        pattern = ctx.createPattern(imageObj, "repeat");
        ctx.beginPath();
        ctx.rect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = pattern;
        ctx.fill();
        callback();
      };
      if (this.options.crossOrigin) {
        imageObj.crossOrigin = "";
      }
      imageObj.onload = function() {
        canvas.complete = true;
        setTimeout(setPattern, 0);
      };
      imageObj.src = url;
    },
    onAdd: function(map) {
      (L.TileLayer.Canvas || L.TileLayer).prototype.onAdd.call(this, map);
      if (this.options.trackAttribution) {
        map.on("moveend", this._updateAttribution, this);
        this._updateAttribution();
      }
    },
    onRemove: function(map) {
      (L.TileLayer.Canvas || L.TileLayer).prototype.onRemove.call(this, map);
      if (this.options.trackAttribution) {
        map.off("moveend", this._updateAttribution, this);
        if (!this._attributionRemoved) {
          var attribution = L.TileLayer.BoundaryCanvas.prototype.getAttribution.call(this);
          map.attributionControl.removeAttribution(attribution);
        }
      }
    },
    _updateAttribution: function() {
      var geom = this._getOriginalMercBoundary(), mapBounds = this._map.getBounds(), mercBounds = L.bounds(this._map.project(mapBounds.getSouthWest(), 0), this._map.project(mapBounds.getNorthEast(), 0)), state = this._getClippedGeometry(geom, mercBounds);
      if (this._attributionRemoved !== !!state.isOut) {
        var attribution = L.TileLayer.BoundaryCanvas.prototype.getAttribution.call(this);
        this._map.attributionControl[state.isOut ? "removeAttribution" : "addAttribution"](attribution);
        this._attributionRemoved = !!state.isOut;
      }
    }
  };
  if (L.version >= "0.8") {
    L.TileLayer.BoundaryCanvas = L.TileLayer.extend({
      options: {
        // all rings of boundary should be without self-intersections or intersections with other rings
        // zero-winding fill algorithm is used in canvas, so holes should have opposite direction to exterior ring
        // boundary can be
        // LatLng[] - simple polygon
        // LatLng[][] - polygon with holes
        // LatLng[][][] - multipolygon
        boundary: null
      },
      includes: ExtendMethods,
      initialize: function(url, options) {
        L.TileLayer.prototype.initialize.call(this, url, options);
        this._boundaryCache = {};
        this._mercBoundary = null;
        this._mercBbox = null;
        if (this.options.trackAttribution) {
          this._attributionRemoved = true;
          this.getAttribution = null;
        }
      },
      createTile: function(coords, done) {
        var tile = document.createElement("canvas"), url = this.getTileUrl(coords);
        tile.width = tile.height = this.options.tileSize;
        this._drawTileInternal(tile, coords, url, L.bind(done, null, null, tile));
        return tile;
      }
    });
  } else {
    L.TileLayer.BoundaryCanvas = L.TileLayer.Canvas.extend({
      options: {
        // all rings of boundary should be without self-intersections or intersections with other rings
        // zero-winding fill algorithm is used in canvas, so holes should have opposite direction to exterior ring
        // boundary can be
        // LatLng[] - simple polygon
        // LatLng[][] - polygon with holes
        // LatLng[][][] - multipolygon
        boundary: null
      },
      includes: ExtendMethods,
      initialize: function(url, options) {
        L.Util.setOptions(this, options);
        L.Util.setOptions(this, { async: true });
        this._url = url;
        this._boundaryCache = {};
        this._mercBoundary = null;
        this._mercBbox = null;
        if (this.options.trackAttribution) {
          this._attributionRemoved = true;
          this.getAttribution = null;
        }
      },
      drawTile: function(canvas, tilePoint) {
        var adjustedTilePoint = L.extend({}, tilePoint), url;
        this._adjustTilePoint(adjustedTilePoint);
        url = this.getTileUrl(adjustedTilePoint);
        this._drawTileInternal(canvas, tilePoint, url, L.bind(this.tileDrawn, this, canvas));
        if (this._getTileSize() !== this.options.tileSize) {
          canvas.style.width = canvas.style.height = this._getTileSize() + "px";
        }
      }
    });
  }
  L.TileLayer.boundaryCanvas = function(url, options) {
    return new L.TileLayer.BoundaryCanvas(url, options);
  };
  L.TileLayer.BoundaryCanvas.createFromLayer = function(layer, options) {
    if (layer instanceof L.TileLayer) {
      return new L.TileLayer.BoundaryCanvas(layer._url, L.extend({}, layer.options, options));
    }
    console.warn("Your tileLayer doesn' instanceof L.TileLayer");
    return layer;
  };
})();
//# sourceMappingURL=leaflet-boundary-canvas.js.map
