Upgrade data
This commit is contained in:
372
iFrameGPX/leaflet-boundary/Leaflet.BoundaryCanvas.js
Normal file
372
iFrameGPX/leaflet-boundary/Leaflet.BoundaryCanvas.js
Normal file
@@ -0,0 +1,372 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
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;
|
||||
|
||||
//bins[Number(ring[p].x === bbox.min.x) + 2 * Number(ring[p].y === bbox.min.y)] = 1;
|
||||
}
|
||||
|
||||
//check that we have all 4 vertex of bbox in our geometry
|
||||
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, c;
|
||||
|
||||
if (L.Util.isArray(this.options.boundary)) { //Depricated: just array of coordinates
|
||||
this._mercBoundary = this._toMercGeometry(this.options.boundary);
|
||||
} else { //GeoJSON
|
||||
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 (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) { //we are outside of all multipolygon components
|
||||
return {isOut: true};
|
||||
}
|
||||
|
||||
for (iC = 0; iC < clippedGeom.length; iC++) {
|
||||
if (isRingBbox(clippedGeom[iC][0], bounds)) {
|
||||
//inside exterior rings and no holes
|
||||
if (clippedGeom[iC].length === 1) {
|
||||
return {isIn: true};
|
||||
}
|
||||
} else { //intersects exterior ring
|
||||
return {geometry: clippedGeom};
|
||||
}
|
||||
|
||||
for (iR = 1; iR < clippedGeom[iC].length; iR++) {
|
||||
//inside exterior ring, but have intersection with a hole
|
||||
if (!isRingBbox(clippedGeom[iC][iR], bounds)) {
|
||||
return {geometry: clippedGeom};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//we are inside all holes in geometry
|
||||
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));
|
||||
|
||||
//fast check intersection
|
||||
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 () {
|
||||
//TODO: implement correct image loading cancelation
|
||||
canvas.complete = true; //HACK: emulate HTMLImageElement property to make happy L.TileLayer
|
||||
setTimeout(setPattern, 0); //IE9 bug - black tiles appear randomly if call setPattern() without timeout
|
||||
}
|
||||
|
||||
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 = {}; //cache index "x:y:z"
|
||||
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}); //image loading is always async
|
||||
this._url = url;
|
||||
this._boundaryCache = {}; //cache index "x:y:z"
|
||||
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));
|
||||
|
||||
//Leaflet v0.7.x bugfix (L.Tile.Canvas doesn't support maxNativeZoom option)
|
||||
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) {
|
||||
return new L.TileLayer.BoundaryCanvas(layer._url, L.extend({}, layer.options, options));
|
||||
};
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user