forked from glenrobertson/leaflet-tilelayer-geojson
-
Notifications
You must be signed in to change notification settings - Fork 5
/
TileLayer.Vector.Unclipped.js
119 lines (107 loc) · 4.53 KB
/
TileLayer.Vector.Unclipped.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
* Tile layer for unclipped vector tiles where features spanning multiple tiles are contained with
* their full geometry in each tile (as opposed to clipping geometries at tile boundary).
*
* This layer loads such duplicated features only once by using a 'unique' function given in the options
* to identify identical features and to keep track of the tiles that are referencing the same feature.
*
* Uses a filter to remove duplicates, so a vector layer set with options.layerFactory must support
* feature filtering like in L.GeoJSON.
*/
L.TileLayer.Vector.Unclipped = L.TileLayer.Vector.extend({
// hash: unique featureKey -> number of tiles referencing the feature
featureRefCounts: {},
// hash: unique featureKey -> feature layer
commonFeatures: {},
initialize: function (url, options, vectorOptions) {
L.TileLayer.Vector.prototype.initialize.apply(this, arguments);
if (!options || !options.unique) {
console.warn('"unique" function missing in options, deduplicating disabled');
}
},
_createTileLayer: function() {
var tileLayer = L.TileLayer.Vector.prototype._createTileLayer.apply(this, arguments);
if (this.options.unique) {
if (tileLayer.options.filter) {
tileLayer.options.filter = this._andFilter(tileLayer.options.filter, L.bind(this._filterDuplicates, tileLayer));
} else {
tileLayer.options.filter = L.bind(this._filterDuplicates, tileLayer);
}
tileLayer._tilingLayer = this;
// common features this tile is referencing (array of unique feature keys)
tileLayer._featureRefs = [];
}
return tileLayer;
},
// filter out duplicate features that are contained in multiple tiles
// (true keeps, false discards feature)
_filterDuplicates: function(feature) {
var featureKey = this._tilingLayer.options.unique(feature);
var refs = this._tilingLayer.featureRefCounts[featureKey];
if (refs && refs > 0) {
refs++;
this._featureRefs.push(featureKey);
} else {
refs = 1;
}
this._tilingLayer.featureRefCounts[featureKey] = refs;
return refs <= 1;
},
_andFilter: function(filterA, filterB) {
return function(feature) {
return filterA(feature) && filterB(feature);
};
},
_unloadTile: function(evt) {
var tileLayer = evt.tile.layer;
if (tileLayer) {
if (this.options.unique) {
this._clearFeatureLayers(tileLayer);
this._clearCommonFeatureLayers(tileLayer);
}
}
L.TileLayer.Vector.prototype._unloadTile.apply(this, arguments);
},
// Remove feature layers from the given tile layer and
// decrease reference counter for all features of the tile.
_clearFeatureLayers: function(tileLayer) {
tileLayer.eachLayer(function (layer) {
if (layer.feature) {
var featureKey = this.options.unique(layer.feature);
var refs = this._decreaseFeatureRefCount(featureKey);
if (refs > 0) {
// referenced by other tiles, keep feature (move to root vector layer)
this.vectorLayer.addLayer(layer);
this.commonFeatures[featureKey] = layer;
// from removeLayer: remove layer from tileLayer but not from map (not sure if necessary)
var id = L.stamp(layer);
delete tileLayer._layers[id];
} else {
tileLayer.removeLayer(layer);
}
}
}, this);
},
// Remove common features that are only referenced by the given tile
_clearCommonFeatureLayers: function(tileLayer) {
var featureRefs = tileLayer._featureRefs;
for (i = 0, len = featureRefs.length; i < len; i++) {
var featureKey = featureRefs[i];
var refs = this._decreaseFeatureRefCount(featureKey);
if (refs <= 0) {
var layer = this.commonFeatures[featureKey];
if (layer) {
this.vectorLayer.removeLayer(layer);
}
delete this.commonFeatures[featureKey];
}
}
},
_decreaseFeatureRefCount: function(featureKey) {
var refs = --this.featureRefCounts[featureKey];
if (refs <= 0) {
delete this.featureRefCounts[featureKey];
}
return refs;
}
});