diff --git a/API.md b/API.md
index b8cc4696d3c..2492bb45c4a 100644
--- a/API.md
+++ b/API.md
@@ -22,6 +22,7 @@ Option | Value | Description
`style` | object | Map style and data source definition (either a JSON object or a JSON URL), described in the [style reference](https://mapbox.com/mapbox-gl-style-spec)
`hash` | boolean | If `true`, the map will track and update the page URL according to map position (default: `false`)
`interactive` | boolean | If `false`, no mouse, touch, or keyboard listeners are attached to the map, so it will not respond to input (default: `true`)
+`classes` | array | Style class names with which to initialize the map
Options that define the initial position of the map (if `hash` is set to `true`, the position will be set according to the URL and options will be used by default):
@@ -108,21 +109,22 @@ Method | Description
------ | ------
`addControl(control)` | Adds a control to the map
-### Working with styles
+### Working with style classes
Method | Description
------ | ------
-`style.addClass(className)` | Adds a style class to the map
-`style.removeClass(className)` | Removes a style class from the map
-`style.hasClass(className)` | Returns boolean indicating whether a style class is active
-`style.setClassList([className])` | Sets active style classes to a specified array
-`style.getClassList()` | Returns an array of active style classes
-`style.cascade()` | Applies map style, allowing for smooth transitions in modified paint properties
+`addClass(className)` | Adds a style class to the map
+`removeClass(className)` | Removes a style class from the map
+`hasClass(className)` | Returns boolean indicating whether a style class is active
+`setClasses([className])` | Sets active style classes to a specified array
+`getClasses()` | Returns an array of active style classes
### Events
Event | Description
----- | -----
+`render` | Fired whenever a frame is rendered to the WebGL context
+`load` | Fired on the first complete render, when all dependencies have been loaded
`move` | Fired during any movement of the map (panning, zooming, rotation, etc.)
`movestart` | Fired on start of any movement of the map
`moveend` | Fired after movement of the map, when it becomes idle
@@ -236,14 +238,17 @@ Option | Description
`url` | A string or array of URL(s) to video files
`coordinates` | lat,lng coordinates in order clockwise starting at the top left: tl, tr, br, bl
-## new mapboxgl.Navigation()
+## new mapboxgl.Navigation(options)
Creates a navigation control with zoom buttons and a compass.
```js
-map.addControl(new mapboxgl.Navigation());
+map.addControl(new mapboxgl.Navigation({position: 'topleft'})); // position is optional
```
+Option | Description
+------ | ------
+`position` | A string indicating the control's position on the map. Options are `topright`, `topleft`, `bottomright`, `bottomleft` (defaults to `topright`)
## mapboxgl.Evented
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9fd27f64a9d..3e203855c3f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,9 @@ An in-progress version being developed in the `mb-pages` branch.
* `addClass`, `removeClass`, `setClasses`, `hasClass`, and `getClasses` are now methods
on Map.
* `Style#cascade` is now private, pending a public style mutation API (#755).
+* The format for `featuresAt` results changed. Instead of result-per-geometry-cross-layer,
+ each result has a `layers` array with all layers that contain the feature. This avoids
+ duplication of geometry and properties in the result set.
## 0.5.2 (Jan 07 2015)
diff --git a/debug/style.json b/debug/style.json
index 3b505ca43a2..33c4bec37be 100644
--- a/debug/style.json
+++ b/debug/style.json
@@ -241,9 +241,7 @@
}
}, {
"id": "water_offset",
- "type": "fill",
- "source": "mapbox",
- "source-layer": "water",
+ "ref": "water",
"paint": {
"fill-color": "white",
"fill-opacity": 0.3,
diff --git a/docs/_posts/3400-01-01-api.html b/docs/_posts/3400-01-01-api.html
index 48ffaf35cbb..ef2132a0ea9 100644
--- a/docs/_posts/3400-01-01-api.html
+++ b/docs/_posts/3400-01-01-api.html
@@ -56,7 +56,7 @@
subnav:
- title: mapboxgl.Navigation
url: /api
- id: new-mapboxgl-navigation-
+ id: new-mapboxgl-navigation-options-
subnav:
- title: mapboxgl.Evented
url: /api
@@ -137,6 +137,11 @@
Mapbox GL JS API Documentation
boolean
If false , no mouse, touch, or keyboard listeners are attached to the map, so it will not respond to input (default: true ) |
+
+classes |
+array |
+Style class names with which to initialize the map |
+
Options that define the initial position of the map (if hash
is set to true
, the position will be set according to the URL and options will be used by default):
@@ -628,9 +633,23 @@ new mapboxgl.VideoSource(options)
-new mapboxgl.Navigation()
Creates a navigation control with zoom buttons and a compass.
-{% highlight js %}map.addControl(new mapboxgl.Navigation());
+new mapboxgl.Navigation(options)
Creates a navigation control with zoom buttons and a compass.
+{% highlight js %}map.addControl(new mapboxgl.Navigation({position: 'topleft'})); // position is optional
{% endhighlight %}
+
+
+
+Option |
+Description |
+
+
+
+
+position |
+A string indicating the control's position on the map. Options are topright , topleft , bottomright , bottomleft (defaults to topright ) |
+
+
+
mapboxgl.Evented
A class inherited by most other classes (Map
, Source
etc.) to get event capabilities.
Methods
diff --git a/docs/_posts/examples/3400-01-03-toggle-styles.html b/docs/_posts/examples/3400-01-03-toggle-styles.html
index 755596f3fc2..9cf03c94acd 100644
--- a/docs/_posts/examples/3400-01-03-toggle-styles.html
+++ b/docs/_posts/examples/3400-01-03-toggle-styles.html
@@ -24,10 +24,10 @@
});
window.setInterval(function() {
- if (map.style.classes.night) {
- map.style.removeClass('night');
+ if (map.hasClass('night')) {
+ map.removeClass('night');
} else {
- map.style.addClass('night');
+ map.addClass('night');
}
}, 2000);
});
diff --git a/js/data/buffer/buffer.js b/js/data/buffer/buffer.js
index f66c11ba912..99200fa15d0 100644
--- a/js/data/buffer/buffer.js
+++ b/js/data/buffer/buffer.js
@@ -28,7 +28,7 @@ Buffer.prototype = {
return this.pos / this.itemSize;
},
- setupViews() {
+ setupViews: function() {
// set up views for each type to add data of different types to the same buffer
this.ubytes = new Uint8Array(this.array);
this.bytes = new Int8Array(this.array);
@@ -37,7 +37,7 @@ Buffer.prototype = {
},
// binds the buffer to a webgl context
- bind(gl) {
+ bind: function(gl) {
var type = gl[this.arrayType];
if (!this.buffer) {
this.buffer = gl.createBuffer();
@@ -51,14 +51,14 @@ Buffer.prototype = {
}
},
- destroy(gl) {
+ destroy: function(gl) {
if (this.buffer) {
gl.deleteBuffer(this.buffer);
}
},
// increase the buffer size by 50% if a new item doesn't fit
- resize() {
+ resize: function() {
if (this.length < this.pos + this.itemSize) {
while (this.length < this.pos + this.itemSize) {
diff --git a/js/data/buffer/fill_elements_buffer.js b/js/data/buffer/fill_elements_buffer.js
index 05817fd9d08..b377036a0e6 100644
--- a/js/data/buffer/fill_elements_buffer.js
+++ b/js/data/buffer/fill_elements_buffer.js
@@ -10,17 +10,15 @@ function FillElementsBuffer(buffer) {
}
FillElementsBuffer.prototype = util.inherit(Buffer, {
- itemSize: 6, // bytes per triangle (3 * unsigned short == 6 bytes)
+ itemSize: 2, // bytes per triangle (3 * unsigned short == 6 bytes)
arrayType: 'ELEMENT_ARRAY_BUFFER',
- add(a, b, c) {
+ add: function(a) {
var pos2 = this.pos / 2;
this.resize();
this.ushorts[pos2 + 0] = a;
- this.ushorts[pos2 + 1] = b;
- this.ushorts[pos2 + 2] = c;
this.pos += this.itemSize;
}
diff --git a/js/data/buffer/fill_vertex_buffer.js b/js/data/buffer/fill_vertex_buffer.js
index f4bda69ff0a..47486ecb2b2 100644
--- a/js/data/buffer/fill_vertex_buffer.js
+++ b/js/data/buffer/fill_vertex_buffer.js
@@ -12,7 +12,7 @@ function FillVertexBuffer(buffer) {
FillVertexBuffer.prototype = util.inherit(Buffer, {
itemSize: 4, // bytes per vertex (2 * short == 4 bytes)
- add(x, y) {
+ add: function(x, y) {
var pos2 = this.pos / 2;
this.resize();
diff --git a/js/data/buffer/glyph_vertex_buffer.js b/js/data/buffer/glyph_vertex_buffer.js
index 9334cee3136..70e27b8bd10 100644
--- a/js/data/buffer/glyph_vertex_buffer.js
+++ b/js/data/buffer/glyph_vertex_buffer.js
@@ -16,7 +16,7 @@ GlyphVertexBuffer.prototype = util.inherit(Buffer, {
defaultLength: 2048 * 16,
itemSize: 16,
- add(x, y, ox, oy, tx, ty, angle, minzoom, range, maxzoom, labelminzoom) {
+ add: function(x, y, ox, oy, tx, ty, angle, minzoom, range, maxzoom, labelminzoom) {
var pos = this.pos,
pos2 = pos / 2,
angleFactor = GlyphVertexBuffer.angleFactor;
@@ -43,7 +43,7 @@ GlyphVertexBuffer.prototype = util.inherit(Buffer, {
this.pos += this.itemSize;
},
- bind(gl, shader) {
+ bind: function(gl, shader) {
Buffer.prototype.bind.call(this, gl);
var stride = this.itemSize;
diff --git a/js/data/buffer/icon_vertex_buffer.js b/js/data/buffer/icon_vertex_buffer.js
index 7f126511af2..ce71ee44b52 100644
--- a/js/data/buffer/icon_vertex_buffer.js
+++ b/js/data/buffer/icon_vertex_buffer.js
@@ -16,7 +16,7 @@ GlyphVertexBuffer.prototype = util.inherit(Buffer, {
defaultLength: 2048 * 16,
itemSize: 16,
- add(x, y, ox, oy, tx, ty, angle, minzoom, range, maxzoom, labelminzoom) {
+ add: function(x, y, ox, oy, tx, ty, angle, minzoom, range, maxzoom, labelminzoom) {
var pos = this.pos,
pos2 = pos / 2,
angleFactor = GlyphVertexBuffer.angleFactor;
@@ -43,7 +43,7 @@ GlyphVertexBuffer.prototype = util.inherit(Buffer, {
this.pos += this.itemSize;
},
- bind(gl, shader) {
+ bind: function(gl, shader) {
Buffer.prototype.bind.call(this, gl);
var stride = this.itemSize;
diff --git a/js/data/buffer/line_element_buffer.js b/js/data/buffer/line_element_buffer.js
index 5df50b0e3ec..ed11860df03 100644
--- a/js/data/buffer/line_element_buffer.js
+++ b/js/data/buffer/line_element_buffer.js
@@ -13,7 +13,7 @@ LineElementBuffer.prototype = util.inherit(Buffer, {
itemSize: 6, // bytes per triangle (3 * unsigned short == 6 bytes)
arrayType: 'ELEMENT_ARRAY_BUFFER',
- add(a, b, c) {
+ add: function(a, b, c) {
var pos2 = this.pos / 2;
this.resize();
diff --git a/js/data/buffer/line_vertex_buffer.js b/js/data/buffer/line_vertex_buffer.js
index 70e3c69d1fd..d93335c853e 100644
--- a/js/data/buffer/line_vertex_buffer.js
+++ b/js/data/buffer/line_vertex_buffer.js
@@ -24,7 +24,7 @@ LineVertexBuffer.prototype = util.inherit(Buffer, {
// x, y - vertex position
// ex, ey - extrude normal
// tx, ty - texture normal
- add(point, extrude, tx, ty, linesofar) {
+ add: function(point, extrude, tx, ty, linesofar) {
var pos = this.pos,
pos2 = pos / 2,
index = this.index,
diff --git a/js/data/buffer/outline_elements_buffer.js b/js/data/buffer/outline_elements_buffer.js
index 1344974a0fe..fcab1c88b4d 100644
--- a/js/data/buffer/outline_elements_buffer.js
+++ b/js/data/buffer/outline_elements_buffer.js
@@ -13,7 +13,7 @@ OutlineElementsBuffer.prototype = util.inherit(Buffer, {
itemSize: 4, // bytes per line (2 * unsigned short == 4 bytes)
arrayType: 'ELEMENT_ARRAY_BUFFER',
- add(a, b) {
+ add: function(a, b) {
var pos2 = this.pos / 2;
this.resize();
diff --git a/js/data/create_bucket.js b/js/data/create_bucket.js
index 3aeff1ba558..badef297b4b 100644
--- a/js/data/create_bucket.js
+++ b/js/data/create_bucket.js
@@ -5,8 +5,8 @@ module.exports = createBucket;
var LineBucket = require('./line_bucket');
var FillBucket = require('./fill_bucket');
var SymbolBucket = require('./symbol_bucket');
-var RasterBucket = require('./raster_bucket');
var LayoutProperties = require('../style/layout_properties');
+var featureFilter = require('feature-filter');
function createBucket(layer, buffers, collision, indices) {
@@ -20,14 +20,18 @@ function createBucket(layer, buffers, collision, indices) {
var BucketClass =
layer.type === 'line' ? LineBucket :
layer.type === 'fill' ? FillBucket :
- layer.type === 'symbol' ? SymbolBucket :
- layer.type === 'raster' ? RasterBucket : null;
+ layer.type === 'symbol' ? SymbolBucket : null;
var bucket = new BucketClass(layoutProperties, buffers, collision, indices);
+
+ bucket.id = layer.id;
bucket.type = layer.type;
+ bucket['source-layer'] = layer['source-layer'];
bucket.interactive = layer.interactive;
bucket.minZoom = layer.minzoom;
bucket.maxZoom = layer.maxzoom;
+ bucket.filter = featureFilter(layer.filter);
+ bucket.features = [];
return bucket;
}
diff --git a/js/data/element_groups.js b/js/data/element_groups.js
index 433cd50cdff..e035c961c7a 100644
--- a/js/data/element_groups.js
+++ b/js/data/element_groups.js
@@ -17,6 +17,7 @@ ElementGroups.prototype.makeRoomFor = function(numVertices) {
this.secondElementBuffer && this.secondElementBuffer.index);
this.groups.push(this.current);
}
+ return this.current;
};
function ElementGroup(vertexStartIndex, elementStartIndex, secondElementStartIndex) {
diff --git a/js/data/feature_tree.js b/js/data/feature_tree.js
index 9a2052f2513..4e80ec96175 100644
--- a/js/data/feature_tree.js
+++ b/js/data/feature_tree.js
@@ -14,8 +14,8 @@ function FeatureTree(getGeometry, getType) {
this.toBeInserted = [];
}
-FeatureTree.prototype.insert = function(bbox, bucket_info, feature) {
- bbox.info = bucket_info;
+FeatureTree.prototype.insert = function(bbox, layers, feature) {
+ bbox.layers = layers;
bbox.feature = feature;
this.toBeInserted.push(bbox);
};
@@ -42,16 +42,18 @@ FeatureTree.prototype.query = function(args, callback) {
var type = this.getType(feature);
var geometry = this.getGeometry(feature);
- if (params.bucket && matching[i].info.id !== params.bucket.id)
+ if (params.layer && matching[i].layers.indexOf(params.layer.id) < 0)
continue;
if (params.$type && type !== params.$type)
continue;
if (!geometryContainsPoint(geometry, type, new Point(x, y), radius))
continue;
- var props = this.formatResults(matching[i].info);
- props.properties = matching[i].feature.properties;
- props.$type = type;
+ var props = {
+ $type: type,
+ properties: matching[i].feature.properties,
+ layers: matching[i].layers
+ };
if (params.geometry) {
props._geometry = geometry;
@@ -63,21 +65,6 @@ FeatureTree.prototype.query = function(args, callback) {
callback(null, result);
};
-FeatureTree.prototype.formatResults = function(bucketInfo) {
- var results = {
- $type: bucketInfo.$type,
- layer: {
- id: bucketInfo.id,
- type: bucketInfo.type,
- source: bucketInfo.source,
- 'source-layer': bucketInfo['source-layer'],
- layout: bucketInfo.layout
- }
- };
- if (bucketInfo.ref) results.layer.ref = bucketInfo.ref;
- return results;
-};
-
function geometryContainsPoint(rings, type, p, radius) {
if (type === 'Point') {
return pointContainsPoint(rings, p, radius);
diff --git a/js/data/fill_bucket.js b/js/data/fill_bucket.js
index 275505feae4..0f71b4794cf 100644
--- a/js/data/fill_bucket.js
+++ b/js/data/fill_bucket.js
@@ -1,6 +1,8 @@
'use strict';
var ElementGroups = require('./element_groups');
+var earcut = require('earcut');
+var classifyRings = require('../util/classify_rings');
module.exports = FillBucket;
@@ -11,71 +13,77 @@ function FillBucket(layoutProperties, buffers, placement, elementGroups) {
}
FillBucket.prototype.addFeatures = function() {
- var features = this.features;
- for (var i = 0; i < features.length; i++) {
- var feature = features[i];
- this.addFeature(feature.loadGeometry());
- }
-};
-
-FillBucket.prototype.addFeature = function(lines) {
- for (var i = 0; i < lines.length; i++) {
- this.addFill(lines[i]);
- }
-};
-
-FillBucket.prototype.addFill = function(vertices) {
- if (vertices.length < 3) {
- //console.warn('a fill must have at least three vertices');
- return;
- }
-
- // Calculate the total number of vertices we're going to produce so that we
- // can resize the buffer beforehand, or detect whether the current line
- // won't fit into the buffer anymore.
- // In order to be able to use the vertex buffer for drawing the antialiased
- // outlines, we separate all polygon vertices with a degenerate (out-of-
- // viewplane) vertex.
-
- var len = vertices.length;
-
- // Check whether this geometry buffer can hold all the required vertices.
- this.elementGroups.makeRoomFor(len + 1);
- var elementGroup = this.elementGroups.current;
-
- var fillVertex = this.buffers.fillVertex;
- var fillElement = this.buffers.fillElement;
- var outlineElement = this.buffers.outlineElement;
-
- // Start all lines with a degenerate vertex
- elementGroup.vertexLength++;
-
- // We're generating triangle fans, so we always start with the first coordinate in this polygon.
- var firstIndex = fillVertex.index - elementGroup.vertexStartIndex,
- prevIndex, currentIndex, currentVertex;
-
- for (var i = 0; i < vertices.length; i++) {
- currentIndex = fillVertex.index - elementGroup.vertexStartIndex;
- currentVertex = vertices[i];
-
- fillVertex.add(currentVertex.x, currentVertex.y);
- elementGroup.vertexLength++;
-
- // Only add triangles that have distinct vertices.
- if (i >= 2 && (currentVertex.x !== vertices[0].x || currentVertex.y !== vertices[0].y)) {
- fillElement.add(firstIndex, prevIndex, currentIndex);
- elementGroup.elementLength++;
+ var fillVertex = this.buffers.fillVertex,
+ fillElement = this.buffers.fillElement,
+ outlineElement = this.buffers.outlineElement;
+
+ var start = self.performance.now();
+ self.tesselateTime = self.tesselateTime || 0;
+
+ var geometries = [];
+ var currentIndex;
+ var prevIndex;
+ var elementGroup;
+
+ // add outlines
+ for (var k = this.features.length - 1; k >= 0; k--) {
+ var lines = geometries[k] = this.features[k].loadGeometry();
+ for (var l = 0; l < lines.length; l++) {
+ var line = lines[l];
+ elementGroup = this.elementGroups.makeRoomFor(line.length);
+
+ for (var v = 0; v < line.length; v++) {
+ var vertex = line[v];
+
+ currentIndex = fillVertex.index - elementGroup.vertexStartIndex;
+ fillVertex.add(vertex.x, vertex.y);
+ elementGroup.vertexLength++;
+
+ if (v >= 1) {
+ outlineElement.add(prevIndex, currentIndex);
+ elementGroup.secondElementLength++;
+ }
+
+ prevIndex = currentIndex;
+ }
}
+ }
- if (i >= 1) {
- outlineElement.add(prevIndex, currentIndex);
- elementGroup.secondElementLength++;
+ // add fills
+ for (var i = this.features.length - 1; i >= 0; i--) {
+ var rings = geometries[i];
+ var polygons = classifyRings(convertCoords(rings));
+
+ for (var j = 0; j < polygons.length; j++) {
+ var triangles = earcut(polygons[j]);
+ elementGroup = this.elementGroups.makeRoomFor(triangles.length);
+
+ for (var m = 0; m < triangles.length; m++) {
+ var index = fillVertex.index - elementGroup.vertexStartIndex;
+ fillVertex.add(triangles[m][0], triangles[m][1]);
+ fillElement.add(index);
+ elementGroup.elementLength++;
+ elementGroup.vertexLength++;
+ }
}
-
- prevIndex = currentIndex;
}
+
+ self.tesselateTime += self.performance.now() - start;
};
FillBucket.prototype.hasData = function() {
return !!this.elementGroups.current;
};
+
+function convertCoords(rings) {
+ var result = [];
+ for (var i = 0; i < rings.length; i++) {
+ var ring = [];
+ for (var j = 0; j < rings[i].length; j++) {
+ var p = rings[i][j];
+ ring.push([p.x, p.y]);
+ }
+ result.push(ring);
+ }
+ return result;
+}
diff --git a/js/data/raster_bucket.js b/js/data/raster_bucket.js
deleted file mode 100644
index 723ac52cdbd..00000000000
--- a/js/data/raster_bucket.js
+++ /dev/null
@@ -1,7 +0,0 @@
-'use strict';
-
-module.exports = RasterBucket;
-
-function RasterBucket(layoutProperties) {
- this.layoutProperties = layoutProperties;
-}
diff --git a/js/data/symbol_bucket.js b/js/data/symbol_bucket.js
index e0c5200a7ed..815cc216ccb 100644
--- a/js/data/symbol_bucket.js
+++ b/js/data/symbol_bucket.js
@@ -20,17 +20,6 @@ function SymbolBucket(layoutProperties, buffers, collision, elementGroups) {
this.buffers = buffers;
this.collision = collision;
- if (layoutProperties['symbol-placement'] === 'line') {
- if (!layoutProperties.hasOwnProperty('text-rotation-alignment')) {
- layoutProperties['text-rotation-alignment'] = 'map';
- }
- if (!layoutProperties.hasOwnProperty('icon-rotation-alignment')) {
- layoutProperties['icon-rotation-alignment'] = 'map';
- }
-
- layoutProperties['symbol-avoid-edges'] = true;
- }
-
if (elementGroups) {
this.elementGroups = elementGroups;
} else {
@@ -307,11 +296,11 @@ SymbolBucket.prototype.getIconDependencies = function(tile, actor, callback) {
actor.send('get icons', {
id: tile.id,
icons: icons
- }, (err, newicons) => {
+ }, function(err, newicons) {
if (err) return callback(err);
this.icons = newicons;
callback();
- });
+ }.bind(this));
} else {
callback();
}
diff --git a/js/geo/lat_lng_bounds.js b/js/geo/lat_lng_bounds.js
index b3a47f7ea7d..01ebb3a0d72 100644
--- a/js/geo/lat_lng_bounds.js
+++ b/js/geo/lat_lng_bounds.js
@@ -17,7 +17,7 @@ function LatLngBounds(sw, ne) {
LatLngBounds.prototype = {
// extend the bounds to contain the given point or bounds
- extend(obj) {
+ extend: function(obj) {
var sw = this._sw,
ne = this._ne,
sw2, ne2;
@@ -50,19 +50,19 @@ LatLngBounds.prototype = {
return this;
},
- getCenter() {
+ getCenter: function() {
return new LatLng((this._sw.lat + this._ne.lat) / 2, (this._sw.lng + this._ne.lng) / 2);
},
- getSouthWest() { return this._sw; },
- getNorthEast() { return this._ne; },
- getNorthWest() { return new LatLng(this.getNorth(), this.getWest()); },
- getSouthEast() { return new LatLng(this.getSouth(), this.getEast()); },
+ getSouthWest: function() { return this._sw; },
+ getNorthEast: function() { return this._ne; },
+ getNorthWest: function() { return new LatLng(this.getNorth(), this.getWest()); },
+ getSouthEast: function() { return new LatLng(this.getSouth(), this.getEast()); },
- getWest() { return this._sw.lng; },
- getSouth() { return this._sw.lat; },
- getEast() { return this._ne.lng; },
- getNorth() { return this._ne.lat; }
+ getWest: function() { return this._sw.lng; },
+ getSouth: function() { return this._sw.lat; },
+ getEast: function() { return this._ne.lng; },
+ getNorth: function() { return this._ne.lat; }
};
// constructs LatLngBounds from an array if necessary
diff --git a/js/geo/transform.js b/js/geo/transform.js
index 3a34d94ed1a..7081c4b5e54 100644
--- a/js/geo/transform.js
+++ b/js/geo/transform.js
@@ -65,16 +65,16 @@ Transform.prototype = {
this._constrain();
},
- zoomScale(zoom) { return Math.pow(2, zoom); },
- scaleZoom(scale) { return Math.log(scale) / Math.LN2; },
+ zoomScale: function(zoom) { return Math.pow(2, zoom); },
+ scaleZoom: function(scale) { return Math.log(scale) / Math.LN2; },
- project(latlng, worldSize) {
+ project: function(latlng, worldSize) {
return new Point(
this.lngX(latlng.lng, worldSize),
this.latY(latlng.lat, worldSize));
},
- unproject(point, worldSize) {
+ unproject: function(point, worldSize) {
return new LatLng(
this.yLat(point.y, worldSize),
this.xLng(point.x, worldSize));
@@ -86,30 +86,30 @@ Transform.prototype = {
get point() { return new Point(this.x, this.y); },
// lat/lon <-> absolute pixel coords convertion
- lngX(lon, worldSize) {
+ lngX: function(lon, worldSize) {
return (180 + lon) * (worldSize || this.worldSize) / 360;
},
// latitude to absolute y coord
- latY(lat, worldSize) {
+ latY: function(lat, worldSize) {
var y = 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360));
return (180 - y) * (worldSize || this.worldSize) / 360;
},
- xLng(x, worldSize) {
+ xLng: function(x, worldSize) {
return x * 360 / (worldSize || this.worldSize) - 180;
},
- yLat(y, worldSize) {
+ yLat: function(y, worldSize) {
var y2 = 180 - y * 360 / (worldSize || this.worldSize);
return 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90;
},
- panBy(offset) {
+ panBy: function(offset) {
var point = this.centerPoint._add(offset);
this.center = this.pointLocation(point);
this._constrain();
},
- setZoomAround(zoom, center) {
+ setZoomAround: function(zoom, center) {
var p = this.locationPoint(center),
p1 = this.size._sub(p),
latlng = this.pointLocation(p1);
@@ -117,24 +117,24 @@ Transform.prototype = {
this.panBy(p1.sub(this.locationPoint(latlng)));
},
- setBearingAround(bearing, center) {
+ setBearingAround: function(bearing, center) {
var offset = this.locationPoint(center).sub(this.centerPoint);
this.panBy(offset);
this.bearing = bearing;
this.panBy(offset.mult(-1));
},
- locationPoint(latlng) {
+ locationPoint: function(latlng) {
var p = this.project(latlng);
return this.centerPoint._sub(this.point._sub(p)._rotate(this.angle));
},
- pointLocation(p) {
+ pointLocation: function(p) {
var p2 = this.centerPoint._sub(p)._rotate(-this.angle);
return this.unproject(this.point.sub(p2));
},
- locationCoordinate(latlng) {
+ locationCoordinate: function(latlng) {
var k = this.zoomScale(this.tileZoom) / this.worldSize;
return {
column: this.lngX(latlng.lng) * k,
@@ -143,7 +143,7 @@ Transform.prototype = {
};
},
- pointCoordinate(tileCenter, p) {
+ pointCoordinate: function(tileCenter, p) {
var zoomFactor = this.zoomScale(this.zoomFraction),
kt = this.zoomScale(this.tileZoom - tileCenter.zoom),
p2 = this.centerPoint._sub(p)._rotate(-this.angle)._div(this.tileSize * zoomFactor);
@@ -155,7 +155,7 @@ Transform.prototype = {
};
},
- _constrain() {
+ _constrain: function() {
if (!this.center) return;
var minY, maxY, minX, maxX, sy, sx, x2, y2,
diff --git a/js/render/draw_background.js b/js/render/draw_background.js
index 1e54423989b..c0eec6abeef 100644
--- a/js/render/draw_background.js
+++ b/js/render/draw_background.js
@@ -4,10 +4,11 @@ var mat3 = require('gl-matrix').mat3;
module.exports = drawBackground;
-function drawBackground(gl, painter, bucket, layerStyle, posMatrix, params) {
- var color = layerStyle['background-color'];
- var image = layerStyle['background-image'];
- var opacity = layerStyle['background-opacity'];
+function drawBackground(painter, layer, posMatrix) {
+ var gl = painter.gl;
+ var color = layer.paint['background-color'];
+ var image = layer.paint['background-image'];
+ var opacity = layer.paint['background-opacity'];
var shader;
var imagePos = image ? painter.spriteAtlas.getPosition(image, true) : null;
@@ -49,7 +50,7 @@ function drawBackground(gl, painter, bucket, layerStyle, posMatrix, params) {
} else {
// Draw filling rectangle.
shader = painter.fillShader;
- gl.switchShader(shader, params.padded || posMatrix);
+ gl.switchShader(shader, posMatrix);
gl.uniform4fv(shader.u_color, color);
}
diff --git a/js/render/draw_fill.js b/js/render/draw_fill.js
index 5f1aac2e6bd..6e06f2896a2 100644
--- a/js/render/draw_fill.js
+++ b/js/render/draw_fill.js
@@ -5,73 +5,86 @@ var mat3 = require('gl-matrix').mat3;
module.exports = drawFill;
-function drawFill(gl, painter, bucket, layerStyle, tile, posMatrix, params) {
+function drawFill(painter, layer, posMatrix, tile) {
+ // No data
+ if (!tile.buffers) return;
+ var elementGroups = tile.elementGroups[layer.ref || layer.id];
+ if (!elementGroups) return;
+
+ var gl = painter.gl;
+ var translatedPosMatrix = painter.translateMatrix(posMatrix, tile.zoom, layer.paint['fill-translate'], layer.paint['fill-translate-anchor']);
+
+ var color = layer.paint['fill-color'];
+ var image = layer.paint['fill-image'];
+ var opacity = layer.paint['fill-opacity'];
+ var shader;
- var translatedPosMatrix = painter.translateMatrix(posMatrix, tile.zoom, layerStyle['fill-translate'], layerStyle['fill-translate-anchor']);
+ if (image) {
+ // Draw texture fill
+ var imagePos = painter.spriteAtlas.getPosition(image, true);
+ if (!imagePos) return;
- var color = layerStyle['fill-color'];
+ shader = painter.patternShader;
+ gl.switchShader(shader, translatedPosMatrix);
+ gl.uniform1i(shader.u_image, 0);
+ gl.uniform2fv(shader.u_pattern_tl, imagePos.tl);
+ gl.uniform2fv(shader.u_pattern_br, imagePos.br);
+ gl.uniform1f(shader.u_mix, painter.transform.zoomFraction);
+ gl.uniform1f(shader.u_opacity, opacity);
- var vertex, elements, group, count;
+ var factor = 8 / Math.pow(2, painter.transform.tileZoom - tile.zoom);
- // Draw the stencil mask.
+ var matrix = mat3.create();
+ mat3.scale(matrix, matrix, [
+ 1 / (imagePos.size[0] * factor),
+ 1 / (imagePos.size[1] * factor),
+ 1, 1
+ ]);
- // We're only drawing to the first seven bits (== support a maximum of
- // 127 overlapping polygons in one place before we get rendering errors).
- gl.stencilMask(0x3F);
- gl.clear(gl.STENCIL_BUFFER_BIT);
+ gl.uniformMatrix3fv(shader.u_patternmatrix, false, matrix);
- // Draw front facing triangles. Wherever the 0x80 bit is 1, we are
- // increasing the lower 7 bits by one if the triangle is a front-facing
- // triangle. This means that all visible polygons should be in CCW
- // orientation, while all holes (see below) are in CW orientation.
- gl.stencilFunc(gl.NOTEQUAL, 0x80, 0x80);
+ painter.spriteAtlas.bind(gl, true);
- // When we do a nonzero fill, we count the number of times a pixel is
- // covered by a counterclockwise polygon, and subtract the number of
- // times it is "uncovered" by a clockwise polygon.
- gl.stencilOpSeparate(gl.FRONT, gl.INCR_WRAP, gl.KEEP, gl.KEEP);
- gl.stencilOpSeparate(gl.BACK, gl.DECR_WRAP, gl.KEEP, gl.KEEP);
+ } else {
+ // Draw filling rectangle.
+ shader = painter.fillShader;
+ gl.switchShader(shader, translatedPosMatrix);
+ gl.uniform4fv(shader.u_color, color);
+ }
- // When drawing a shape, we first draw all shapes to the stencil buffer
- // and incrementing all areas where polygons are
- gl.colorMask(false, false, false, false);
+ var vertex, elements, group, count;
- // Draw the actual triangle fan into the stencil buffer.
- gl.switchShader(painter.fillShader, translatedPosMatrix);
+ //gl.switchShader(painter.fillShader, translatedPosMatrix, painter.tile.exMatrix);
+ //gl.uniform4fv(painter.fillShader.u_color, color);
// Draw all buffers
- vertex = bucket.buffers.fillVertex;
+ vertex = tile.buffers.fillVertex;
vertex.bind(gl);
- elements = bucket.buffers.fillElement;
+ elements = tile.buffers.fillElement;
elements.bind(gl);
var offset, elementOffset;
- for (var i = 0; i < bucket.elementGroups.groups.length; i++) {
- group = bucket.elementGroups.groups[i];
+
+ for (var i = 0; i < elementGroups.groups.length; i++) {
+ group = elementGroups.groups[i];
offset = group.vertexStartIndex * vertex.itemSize;
- gl.vertexAttribPointer(painter.fillShader.a_pos, 2, gl.SHORT, false, 4, offset + 0);
+ gl.vertexAttribPointer(shader.a_pos, 2, gl.SHORT, false, 4, offset + 0);
- count = group.elementLength * 3;
+ count = group.elementLength;
elementOffset = group.elementStartIndex * elements.itemSize;
gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_SHORT, elementOffset);
+ if (i > 0) console.log(i);
}
- // Now that we have the stencil mask in the stencil buffer, we can start
- // writing to the color buffer.
- gl.colorMask(true, true, true, true);
-
- // From now on, we don't want to update the stencil buffer anymore.
- gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
- gl.stencilMask(0x0);
-
- var strokeColor = layerStyle['fill-outline-color'];
+ var strokeColor = layer.paint['fill-outline-color'];
// Because we're drawing top-to-bottom, and we update the stencil mask
// below, we have to draw the outline first (!)
- if (layerStyle['fill-antialias'] === true && !(layerStyle['fill-image'] && !strokeColor)) {
+ if (layer.paint['fill-antialias'] === true && !(layer.paint['fill-image'] && !strokeColor)) {
gl.switchShader(painter.outlineShader, translatedPosMatrix);
gl.lineWidth(2 * browser.devicePixelRatio);
+ /*
if (strokeColor) {
// If we defined a different color for the fill outline, we are
// going to ignore the bits in 0x3F and just care about the global
@@ -85,17 +98,18 @@ function drawFill(gl, painter, bucket, layerStyle, tile, posMatrix, params) {
// the (non-antialiased) fill.
gl.stencilFunc(gl.EQUAL, 0x80, 0xBF);
}
+ */
gl.uniform2f(painter.outlineShader.u_world, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.uniform4fv(painter.outlineShader.u_color, strokeColor ? strokeColor : color);
// Draw all buffers
- vertex = bucket.buffers.fillVertex;
- elements = bucket.buffers.outlineElement;
+ vertex = tile.buffers.fillVertex;
+ elements = tile.buffers.outlineElement;
elements.bind(gl);
- for (var k = 0; k < bucket.elementGroups.groups.length; k++) {
- group = bucket.elementGroups.groups[k];
+ for (var k = 0; k < elementGroups.groups.length; k++) {
+ group = elementGroups.groups[k];
offset = group.vertexStartIndex * vertex.itemSize;
gl.vertexAttribPointer(painter.outlineShader.a_pos, 2, gl.SHORT, false, 4, offset + 0);
@@ -104,49 +118,4 @@ function drawFill(gl, painter, bucket, layerStyle, tile, posMatrix, params) {
gl.drawElements(gl.LINES, count, gl.UNSIGNED_SHORT, elementOffset);
}
}
-
- var image = layerStyle['fill-image'];
- var opacity = layerStyle['fill-opacity'] || 1;
- var shader;
-
- if (image) {
- // Draw texture fill
- var imagePos = painter.spriteAtlas.getPosition(image, true);
- if (!imagePos) return;
-
- shader = painter.patternShader;
- gl.switchShader(shader, posMatrix);
- gl.uniform1i(shader.u_image, 0);
- gl.uniform2fv(shader.u_pattern_tl, imagePos.tl);
- gl.uniform2fv(shader.u_pattern_br, imagePos.br);
- gl.uniform1f(shader.u_mix, painter.transform.zoomFraction);
- gl.uniform1f(shader.u_opacity, opacity);
-
- var factor = 8 / Math.pow(2, painter.transform.tileZoom - tile.zoom);
-
- var matrix = mat3.create();
- mat3.scale(matrix, matrix, [
- 1 / (imagePos.size[0] * factor),
- 1 / (imagePos.size[1] * factor)
- ]);
-
- gl.uniformMatrix3fv(shader.u_patternmatrix, false, matrix);
-
- painter.spriteAtlas.bind(gl, true);
-
- } else {
- // Draw filling rectangle.
- shader = painter.fillShader;
- gl.switchShader(shader, params.padded || posMatrix);
- gl.uniform4fv(shader.u_color, color);
- }
-
- // Only draw regions that we marked
- gl.stencilFunc(gl.NOTEQUAL, 0x0, 0x3F);
- gl.bindBuffer(gl.ARRAY_BUFFER, painter.tileExtentBuffer);
- gl.vertexAttribPointer(shader.a_pos, painter.tileExtentBuffer.itemSize, gl.SHORT, false, 0, 0);
- gl.drawArrays(gl.TRIANGLE_STRIP, 0, painter.tileExtentBuffer.itemCount);
-
- gl.stencilMask(0x00);
- gl.stencilFunc(gl.EQUAL, 0x80, 0x80);
}
diff --git a/js/render/draw_line.js b/js/render/draw_line.js
index a6ceeaf4ff5..9addcc93b25 100644
--- a/js/render/draw_line.js
+++ b/js/render/draw_line.js
@@ -2,23 +2,30 @@
var browser = require('../util/browser');
-module.exports = function drawLine(gl, painter, bucket, layerStyle, tile, posMatrix) {
+module.exports = function drawLine(painter, layer, posMatrix, tile) {
+ // No data
+ if (!tile.buffers) return;
+ var elementGroups = tile.elementGroups[layer.ref || layer.id];
+ if (!elementGroups) return;
+
+ var gl = painter.gl;
+
// don't draw zero-width lines
- if (layerStyle['line-width'] <= 0) return;
+ if (layer.paint['line-width'] <= 0) return;
// the distance over which the line edge fades out.
// Retina devices need a smaller distance to avoid aliasing.
var antialiasing = 1 / browser.devicePixelRatio;
- var blur = layerStyle['line-blur'] + antialiasing;
- var edgeWidth = layerStyle['line-width'] / 2;
+ var blur = layer.paint['line-blur'] + antialiasing;
+ var edgeWidth = layer.paint['line-width'] / 2;
var inset = -1;
var offset = 0;
var shift = 0;
- if (layerStyle['line-gap-width'] > 0) {
- inset = layerStyle['line-gap-width'] / 2 + antialiasing * 0.5;
- edgeWidth = layerStyle['line-width'];
+ if (layer.paint['line-gap-width'] > 0) {
+ inset = layer.paint['line-gap-width'] / 2 + antialiasing * 0.5;
+ edgeWidth = layer.paint['line-width'];
// shift outer lines half a pixel towards the middle to eliminate the crack
offset = inset - antialiasing / 2;
@@ -26,13 +33,13 @@ module.exports = function drawLine(gl, painter, bucket, layerStyle, tile, posMat
var outset = offset + edgeWidth + antialiasing / 2 + shift;
- var color = layerStyle['line-color'];
+ var color = layer.paint['line-color'];
var ratio = painter.transform.scale / (1 << tile.zoom) / 8;
- var vtxMatrix = painter.translateMatrix(posMatrix, tile.zoom, layerStyle['line-translate'], layerStyle['line-translate-anchor']);
+ var vtxMatrix = painter.translateMatrix(posMatrix, tile.zoom, layer.paint['line-translate'], layer.paint['line-translate-anchor']);
var shader;
- var image = layerStyle['line-image'];
+ var image = layer.paint['line-image'];
var imagePos = image && painter.spriteAtlas.getPosition(image, true);
if (imagePos) {
var factor = 8 / Math.pow(2, painter.transform.tileZoom - tile.zoom);
@@ -60,17 +67,16 @@ module.exports = function drawLine(gl, painter, bucket, layerStyle, tile, posMat
gl.uniform1f(shader.u_blur, blur);
gl.uniform4fv(shader.u_color, color);
- gl.uniform2fv(shader.u_dasharray, layerStyle['line-dasharray']);
+ gl.uniform2fv(shader.u_dasharray, layer.paint['line-dasharray']);
}
- var vertex = bucket.buffers.lineVertex;
+ var vertex = tile.buffers.lineVertex;
vertex.bind(gl);
- var element = bucket.buffers.lineElement;
+ var element = tile.buffers.lineElement;
element.bind(gl);
- var groups = bucket.elementGroups.groups;
- for (var i = 0; i < groups.length; i++) {
- var group = groups[i];
+ for (var i = 0; i < elementGroups.groups.length; i++) {
+ var group = elementGroups.groups[i];
var vtxOffset = group.vertexStartIndex * vertex.itemSize;
gl.vertexAttribPointer(shader.a_pos, 2, gl.SHORT, false, 8, vtxOffset + 0);
gl.vertexAttribPointer(shader.a_data, 4, gl.BYTE, false, 8, vtxOffset + 4);
diff --git a/js/render/draw_raster.js b/js/render/draw_raster.js
index cbe1cddb343..4790a171980 100644
--- a/js/render/draw_raster.js
+++ b/js/render/draw_raster.js
@@ -1,53 +1,12 @@
'use strict';
var TileCoord = require('../source/tile_coord');
-var PrerenderedTexture = require('./prerendered');
-var mat4 = require('gl-matrix').mat4;
var util = require('../util/util');
module.exports = drawRaster;
-function drawRaster(gl, painter, bucket, layerStyle, tile, posMatrix, params, style, layer) {
- var texture;
-
- if (layer && layer.layers) {
- if (!bucket.prerendered) {
- bucket.prerendered = new PrerenderedTexture(gl, bucket.layoutProperties, painter);
- bucket.prerendered.bindFramebuffer();
-
- gl.clearStencil(0x80);
- gl.stencilMask(0xFF);
- gl.clear(gl.STENCIL_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
- gl.stencilMask(0x00);
-
- gl.viewport(0, 0, bucket.prerendered.size, bucket.prerendered.size);
-
- var buffer = bucket.prerendered.buffer * 4096;
-
- var matrix = mat4.create();
- mat4.ortho(matrix, -buffer, 4096 + buffer, -4096 - buffer, buffer, 0, 1);
- mat4.translate(matrix, matrix, [0, -4096, 0]);
-
- params.padded = mat4.create();
- mat4.ortho(params.padded, 0, 4096, -4096, 0, 0, 1);
- mat4.translate(params.padded, params.padded, [0, -4096, 0]);
-
- painter.drawLayers(tile, style, layer.layers, params, matrix);
-
- delete params.padded;
-
- if (bucket.layoutProperties['raster-blur'] > 0) {
- bucket.prerendered.blur(painter, bucket.layoutProperties['raster-blur']);
- }
-
- bucket.prerendered.unbindFramebuffer();
- gl.viewport(0, 0, painter.width, painter.height);
- }
-
- texture = bucket.prerendered;
- } else {
- texture = tile;
- }
+function drawRaster(painter, layer, posMatrix, tile) {
+ var gl = painter.gl;
gl.disable(gl.STENCIL_TEST);
@@ -55,30 +14,25 @@ function drawRaster(gl, painter, bucket, layerStyle, tile, posMatrix, params, st
gl.switchShader(shader, posMatrix);
// color parameters
- gl.uniform1f(shader.u_brightness_low, layerStyle['raster-brightness'][0]);
- gl.uniform1f(shader.u_brightness_high, layerStyle['raster-brightness'][1]);
- gl.uniform1f(shader.u_saturation_factor, saturationFactor(layerStyle['raster-saturation']));
- gl.uniform1f(shader.u_contrast_factor, contrastFactor(layerStyle['raster-contrast']));
- gl.uniform3fv(shader.u_spin_weights, spinWeights(layerStyle['raster-hue-rotate']));
-
- var parentTile, opacities;
- if (layer && layer.layers) {
- parentTile = null;
- opacities = [layerStyle['raster-opacity'], 0];
- } else {
- parentTile = texture.source && texture.source._pyramid.findLoadedParent(texture.id, 0, {});
- opacities = getOpacities(texture, parentTile, layerStyle, painter.transform);
- }
+ gl.uniform1f(shader.u_brightness_low, layer.paint['raster-brightness'][0]);
+ gl.uniform1f(shader.u_brightness_high, layer.paint['raster-brightness'][1]);
+ gl.uniform1f(shader.u_saturation_factor, saturationFactor(layer.paint['raster-saturation']));
+ gl.uniform1f(shader.u_contrast_factor, contrastFactor(layer.paint['raster-contrast']));
+ gl.uniform3fv(shader.u_spin_weights, spinWeights(layer.paint['raster-hue-rotate']));
+
+ var parentTile = tile.source && tile.source._pyramid.findLoadedParent(tile.id, 0, {}),
+ opacities = getOpacities(tile, parentTile, layer, painter.transform);
+
var parentScaleBy, parentTL;
gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, texture.texture);
+ gl.bindTexture(gl.TEXTURE_2D, tile.texture);
if (parentTile) {
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, parentTile.texture);
- var tilePos = TileCoord.fromID(texture.id);
+ var tilePos = TileCoord.fromID(tile.id);
var parentPos = parentTile && TileCoord.fromID(parentTile.id);
parentScaleBy = Math.pow(2, parentPos.z - tilePos.z);
parentTL = [tilePos.x * parentScaleBy % 1, tilePos.y * parentScaleBy % 1];
@@ -86,18 +40,16 @@ function drawRaster(gl, painter, bucket, layerStyle, tile, posMatrix, params, st
opacities[1] = 0;
}
- var bufferScale = bucket.prerendered ? (4096 * (1 + 2 * bucket.prerendered.buffer)) / 4096 : 1;
-
// cross-fade parameters
gl.uniform2fv(shader.u_tl_parent, parentTL || [0, 0]);
gl.uniform1f(shader.u_scale_parent, parentScaleBy || 1);
- gl.uniform1f(shader.u_buffer_scale, bufferScale);
+ gl.uniform1f(shader.u_buffer_scale, 1);
gl.uniform1f(shader.u_opacity0, opacities[0]);
gl.uniform1f(shader.u_opacity1, opacities[1]);
gl.uniform1i(shader.u_image0, 0);
gl.uniform1i(shader.u_image1, 1);
- gl.bindBuffer(gl.ARRAY_BUFFER, texture.boundsBuffer || painter.tileExtentBuffer);
+ gl.bindBuffer(gl.ARRAY_BUFFER, tile.boundsBuffer || painter.tileExtentBuffer);
gl.vertexAttribPointer(shader.a_pos, 2, gl.SHORT, false, 8, 0);
gl.vertexAttribPointer(shader.a_texture_pos, 2, gl.SHORT, false, 8, 4);
@@ -129,12 +81,12 @@ function saturationFactor(saturation) {
-saturation;
}
-function getOpacities(tile, parentTile, layerStyle, transform) {
+function getOpacities(tile, parentTile, layer, transform) {
if (!tile.source) return [1, 0];
var now = new Date().getTime();
- var fadeDuration = layerStyle['raster-fade-duration'];
+ var fadeDuration = layer.paint['raster-fade-duration'];
var sinceTile = (now - tile.timeAdded) / fadeDuration;
var sinceParent = parentTile ? (now - parentTile.timeAdded) / fadeDuration : -1;
@@ -155,7 +107,7 @@ function getOpacities(tile, parentTile, layerStyle, transform) {
opacity[1] = 1 - opacity[0];
}
- var op = layerStyle['raster-opacity'];
+ var op = layer.paint['raster-opacity'];
opacity[0] *= op;
opacity[1] *= op;
diff --git a/js/render/draw_symbol.js b/js/render/draw_symbol.js
index 9877851acb0..eda8ec315d4 100644
--- a/js/render/draw_symbol.js
+++ b/js/render/draw_symbol.js
@@ -5,13 +5,19 @@ var mat4 = require('gl-matrix').mat4;
module.exports = drawSymbols;
-function drawSymbols(gl, painter, bucket, layerStyle, tile, posMatrix) {
+function drawSymbols(painter, layer, posMatrix, tile) {
+ // No data
+ if (!tile.buffers) return;
+ var elementGroups = tile.elementGroups[layer.ref || layer.id];
+ if (!elementGroups) return;
+
+ var gl = painter.gl;
gl.disable(gl.STENCIL_TEST);
- if (bucket.elementGroups.text.groups.length) {
- drawSymbol(gl, painter, bucket, layerStyle, tile, posMatrix, 'text');
+ if (elementGroups.text.groups.length) {
+ drawSymbol(painter, layer, posMatrix, tile, elementGroups.text, 'text', true);
}
- if (bucket.elementGroups.icon.groups.length) {
- drawSymbol(gl, painter, bucket, layerStyle, tile, posMatrix, 'icon');
+ if (elementGroups.icon.groups.length) {
+ drawSymbol(painter, layer, posMatrix, tile, elementGroups.icon, 'icon', elementGroups.sdfIcons);
}
gl.enable(gl.STENCIL_TEST);
}
@@ -21,27 +27,25 @@ var defaultSizes = {
text: 24
};
-function drawSymbol(gl, painter, bucket, layerStyle, tile, posMatrix, prefix) {
-
- posMatrix = painter.translateMatrix(posMatrix, tile.zoom, layerStyle[prefix + '-translate'], layerStyle[prefix + '-translate-anchor']);
+function drawSymbol(painter, layer, posMatrix, tile, elementGroups, prefix, sdf) {
+ var gl = painter.gl;
- var layoutProperties = bucket.layoutProperties;
+ posMatrix = painter.translateMatrix(posMatrix, tile.zoom, layer.paint[prefix + '-translate'], layer.paint[prefix + '-translate-anchor']);
var exMatrix = mat4.clone(painter.projectionMatrix);
- var alignedWithMap = layoutProperties[prefix + '-rotation-alignment'] === 'map';
+ var alignedWithMap = layer.layout[prefix + '-rotation-alignment'] === 'map';
var angleOffset = (alignedWithMap ? painter.transform.angle : 0);
if (angleOffset) {
mat4.rotateZ(exMatrix, exMatrix, angleOffset);
}
- // If layerStyle.size > layoutProperties[prefix + '-max-size'] then labels may collide
- var fontSize = layerStyle[prefix + '-size'] || layoutProperties[prefix + '-max-size'];
+ // If layer.paint.size > layer.layout[prefix + '-max-size'] then labels may collide
+ var fontSize = layer.paint[prefix + '-size'] || layer.layout[prefix + '-max-size'];
var fontScale = fontSize / defaultSizes[prefix];
mat4.scale(exMatrix, exMatrix, [ fontScale, fontScale, 1 ]);
var text = prefix === 'text';
- var sdf = text || bucket.elementGroups.sdfIcons;
var shader, buffer, texsize;
if (!text && !painter.style.sprite.loaded())
@@ -57,11 +61,11 @@ function drawSymbol(gl, painter, bucket, layerStyle, tile, posMatrix, prefix) {
if (text) {
painter.glyphAtlas.updateTexture(gl);
- buffer = bucket.buffers.glyphVertex;
+ buffer = tile.buffers.glyphVertex;
texsize = [painter.glyphAtlas.width / 4, painter.glyphAtlas.height / 4];
} else {
painter.spriteAtlas.bind(gl, alignedWithMap || painter.options.rotating || painter.options.zooming || fontScale != 1 || sdf);
- buffer = bucket.buffers.iconVertex;
+ buffer = tile.buffers.iconVertex;
texsize = [painter.spriteAtlas.width / 4, painter.spriteAtlas.height / 4];
}
@@ -75,9 +79,9 @@ function drawSymbol(gl, painter, bucket, layerStyle, tile, posMatrix, prefix) {
var angle = Math.round(painter.transform.angle / Math.PI * 128);
// adjust min/max zooms for variable font sies
- var zoomAdjust = Math.log(fontSize / layoutProperties[prefix + '-max-size']) / Math.LN2 || 0;
+ var zoomAdjust = Math.log(fontSize / layer.layout[prefix + '-max-size']) / Math.LN2 || 0;
- var flip = alignedWithMap && layoutProperties[prefix + '-keep-upright'];
+ var flip = alignedWithMap && layer.layout[prefix + '-keep-upright'];
gl.uniform1f(shader.u_flip, flip ? 1 : 0);
gl.uniform1f(shader.u_angle, (angle + 256) % 256);
gl.uniform1f(shader.u_zoom, (painter.transform.zoom - zoomAdjust) * 10); // current zoom level
@@ -88,8 +92,8 @@ function drawSymbol(gl, painter, bucket, layerStyle, tile, posMatrix, prefix) {
gl.uniform1f(shader.u_maxfadezoom, Math.floor(f.maxfadezoom * 10));
gl.uniform1f(shader.u_fadezoom, (painter.transform.zoom + f.bump) * 10);
- var begin = bucket.elementGroups[prefix].groups[0].vertexStartIndex,
- len = bucket.elementGroups[prefix].groups[0].vertexLength;
+ var begin = elementGroups.groups[0].vertexStartIndex,
+ len = elementGroups.groups[0].vertexLength;
if (sdf) {
var sdfPx = 8;
@@ -98,19 +102,19 @@ function drawSymbol(gl, painter, bucket, layerStyle, tile, posMatrix, prefix) {
var gamma = 0.105 * defaultSizes[prefix] / fontSize / browser.devicePixelRatio;
gl.uniform1f(shader.u_gamma, gamma);
- gl.uniform4fv(shader.u_color, layerStyle[prefix + '-color']);
+ gl.uniform4fv(shader.u_color, layer.paint[prefix + '-color']);
gl.uniform1f(shader.u_buffer, (256 - 64) / 256);
gl.drawArrays(gl.TRIANGLES, begin, len);
- if (layerStyle[prefix + '-halo-color']) {
+ if (layer.paint[prefix + '-halo-color']) {
// Draw halo underneath the text.
- gl.uniform1f(shader.u_gamma, layerStyle[prefix + '-halo-blur'] * blurOffset / fontScale / sdfPx + gamma);
- gl.uniform4fv(shader.u_color, layerStyle[prefix + '-halo-color']);
- gl.uniform1f(shader.u_buffer, (haloOffset - layerStyle[prefix + '-halo-width'] / fontScale) / sdfPx);
+ gl.uniform1f(shader.u_gamma, layer.paint[prefix + '-halo-blur'] * blurOffset / fontScale / sdfPx + gamma);
+ gl.uniform4fv(shader.u_color, layer.paint[prefix + '-halo-color']);
+ gl.uniform1f(shader.u_buffer, (haloOffset - layer.paint[prefix + '-halo-width'] / fontScale) / sdfPx);
gl.drawArrays(gl.TRIANGLES, begin, len);
}
} else {
- gl.uniform1f(shader.u_opacity, layerStyle['icon-opacity']);
+ gl.uniform1f(shader.u_opacity, layer.paint['icon-opacity']);
gl.drawArrays(gl.TRIANGLES, begin, len);
}
}
diff --git a/js/render/painter.js b/js/render/painter.js
index cbc3e84438d..d030e9fbfec 100644
--- a/js/render/painter.js
+++ b/js/render/painter.js
@@ -5,14 +5,6 @@ var browser = require('../util/browser');
var mat4 = require('gl-matrix').mat4;
var FrameHistory = require('./frame_history');
-var drawSymbol = require('./draw_symbol');
-var drawLine = require('./draw_line');
-var drawFill = require('./draw_fill');
-var drawRaster = require('./draw_raster');
-var drawDebug = require('./draw_debug');
-var drawBackground = require('./draw_background');
-var drawVertices = require('./draw_vertices');
-
/*
* Initialize a new painter object.
*
@@ -201,6 +193,16 @@ GLPainter.prototype.bindDefaultFramebuffer = function() {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
};
+var draw = {
+ symbol: require('./draw_symbol'),
+ line: require('./draw_line'),
+ fill: require('./draw_fill'),
+ raster: require('./draw_raster'),
+ background: require('./draw_background'),
+ debug: require('./draw_debug'),
+ vertices: require('./draw_vertices')
+};
+
GLPainter.prototype.render = function(style, options) {
this.style = style;
this.options = options;
@@ -214,75 +216,40 @@ GLPainter.prototype.render = function(style, options) {
this.frameHistory.record(this.transform.zoom);
this.prepareBuffers();
- var i, len, group, source;
-
- // Render the groups
- var groups = style.layerGroups;
- for (i = 0, len = groups.length; i < len; i++) {
- group = groups[i];
- source = style.sources[group.source];
+ for (var i = style._groups.length - 1; i >= 0; i--) {
+ var group = style._groups[i];
+ var source = style.sources[group.source];
if (source) {
this.clearStencil();
source.render(group, this);
} else if (group.source === undefined) {
- this.drawLayers(undefined, style, group, { background: true });
+ this.drawLayers(group, this.identityMatrix);
}
}
};
-GLPainter.prototype.drawTile = function(tile, style, layers) {
+GLPainter.prototype.drawTile = function(tile, layers) {
this.drawClippingMask(tile);
- this.drawLayers(tile, style, layers, {});
+ this.drawLayers(layers, tile.posMatrix, tile);
if (this.options.debug) {
- drawDebug(this.gl, this, tile);
+ draw.debug(this, tile);
}
};
-GLPainter.prototype.drawLayers = function(tile, style, layers, params, matrix) {
- // Draw layers front-to-back.
- // Layers are already in reverse order from style.restructure()
- for (var i = 0; i < layers.length; i++) {
- this.drawLayer(tile, style, layers[i], params, matrix, tile && tile.buckets);
- }
-};
-
-GLPainter.prototype.drawLayer = function(tile, style, layer, params, matrix, buckets) {
- var gl = this.gl;
+GLPainter.prototype.drawLayers = function(layers, matrix, tile) {
+ for (var i = layers.length - 1; i >= 0; i--) {
+ var layer = layers[i];
- var layerStyle = style.computed[layer.id];
- if (!layerStyle || layerStyle.hidden) return;
+ if (layer.hidden)
+ continue;
- if (layer.layers && layer.type === 'raster') {
- drawRaster(gl, this, buckets[layer.bucket], layerStyle, tile, matrix || tile.posMatrix, params, style, layer);
- } else if (params.background) {
- drawBackground(gl, this, undefined, layerStyle, this.identityMatrix, params);
- } else {
-
- var bucket = buckets[layer.bucket];
- // There are no vertices yet for this layer.
- if (!bucket || (bucket.hasData && !bucket.hasData())) return;
-
- var type = bucket.type;
-
- if (bucket.minZoom && this.transform.zoom < bucket.minZoom) return;
- if (bucket.maxZoom && this.transform.zoom >= bucket.maxZoom) return;
-
- var draw = type === 'symbol' ? drawSymbol :
- type === 'fill' ? drawFill :
- type === 'line' ? drawLine :
- type === 'raster' ? drawRaster : null;
-
- if (draw) {
- draw(gl, this, bucket, layerStyle, tile, matrix || tile.posMatrix, params);
- } else {
- console.warn('No bucket type specified');
- }
+ draw[layer.type](this, layer, matrix, tile);
- if (this.options.vertices && !layer.layers) {
- drawVertices(gl, this, bucket);
+ if (this.options.vertices) {
+ draw.vertices(this, layer, matrix, tile);
}
}
};
diff --git a/js/render/prerendered.js b/js/render/prerendered.js
deleted file mode 100644
index 2c8f09ba946..00000000000
--- a/js/render/prerendered.js
+++ /dev/null
@@ -1,110 +0,0 @@
-'use strict';
-
-var mat4 = require('gl-matrix').mat4;
-
-module.exports = PrerenderedTexture;
-
-function PrerenderedTexture(gl, layoutProperties, painter) {
- this.gl = gl;
- this.buffer = layoutProperties['raster-buffer'] || (1/32);
- this.size = (layoutProperties['raster-size'] || 512) * (1 + 2 * this.buffer);
- this.painter = painter;
-
- this.texture = null;
- this.fbo = null;
- this.fbos = this.painter.preFbos[this.size];
-}
-
-PrerenderedTexture.prototype.bindFramebuffer = function() {
- var gl = this.gl;
-
- // try to reuse available raster textures
- this.texture = this.painter.getTexture(this.size);
-
- if (!this.texture) {
- this.texture = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, this.texture);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.size, this.size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
- this.texture.size = this.size;
- } else {
- gl.bindTexture(gl.TEXTURE_2D, this.texture);
- }
-
- if (!this.fbos) {
- this.fbo = gl.createFramebuffer();
- var stencil = gl.createRenderbuffer();
- gl.bindRenderbuffer(gl.RENDERBUFFER, stencil);
- gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, this.size, this.size);
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
- gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, stencil);
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
- } else {
- this.fbo = this.fbos.pop();
- gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0);
- }
-};
-
-PrerenderedTexture.prototype.unbindFramebuffer = function() {
- this.painter.bindDefaultFramebuffer();
- if (this.fbos) {
- this.fbos.push(this.fbo);
- } else {
- this.painter.preFbos[this.size] = [this.fbo];
- }
-};
-
-PrerenderedTexture.prototype.blur = function(painter, passes) {
- var gl = this.gl;
- var originalTexture = this.texture;
- var secondaryTexture = this.painter.getTexture(this.size);
- if (!secondaryTexture) {
- secondaryTexture = gl.createTexture();
- gl.bindTexture(gl.TEXTURE_2D, secondaryTexture);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.size, this.size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
- secondaryTexture.size = this.size;
- } else {
- gl.bindTexture(gl.TEXTURE_2D, secondaryTexture);
- }
- gl.bindTexture(gl.TEXTURE_2D, null);
-
- var matrix = mat4.create();
- mat4.ortho(matrix, 0, 4096, -4096, 0, 0, 1);
- mat4.translate(matrix, matrix, [0, -4096, 0]);
-
- gl.switchShader(painter.gaussianShader, matrix);
- gl.activeTexture(gl.TEXTURE0);
- gl.uniform1i(painter.gaussianShader.u_image, 0);
-
- for (var i = 0; i < passes; i++) {
-
- // Render horizontal
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, secondaryTexture, 0);
- gl.clear(gl.COLOR_BUFFER_BIT);
- gl.uniform2fv(painter.gaussianShader.u_offset, [1 / this.size, 0]);
- gl.bindTexture(gl.TEXTURE_2D, originalTexture);
- gl.bindBuffer(gl.ARRAY_BUFFER, painter.tileExtentBuffer);
- gl.vertexAttribPointer(painter.gaussianShader.a_pos, 2, gl.SHORT, false, 8, 0);
- gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
-
-
- // Render vertical
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, originalTexture, 0);
- gl.clear(gl.COLOR_BUFFER_BIT);
- gl.uniform2fv(painter.gaussianShader.u_offset, [0, 1 / this.size]);
- gl.bindTexture(gl.TEXTURE_2D, secondaryTexture);
- gl.bindBuffer(gl.ARRAY_BUFFER, painter.tileExtentBuffer);
- gl.vertexAttribPointer(painter.gaussianShader.a_pos, 2, gl.SHORT, false, 8, 0);
- gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
- }
-
- this.painter.saveTexture(secondaryTexture);
-};
diff --git a/js/source/geojson_source.js b/js/source/geojson_source.js
index a7f200e53fb..8243e88d4b4 100644
--- a/js/source/geojson_source.js
+++ b/js/source/geojson_source.js
@@ -32,22 +32,22 @@ GeoJSONSource.prototype = util.inherit(Evented, {
maxzoom: 14,
_dirty: true,
- setData(data) {
+ setData: function(data) {
this._data = data;
this._dirty = true;
this.fire('change');
return this;
},
- onAdd(map) {
+ onAdd: function(map) {
this.map = map;
},
- loaded() {
+ loaded: function() {
return this._loaded && this._pyramid.loaded();
},
- update(transform) {
+ update: function(transform) {
if (this._dirty) {
this._updateData();
}
@@ -60,14 +60,14 @@ GeoJSONSource.prototype = util.inherit(Evented, {
render: Source._renderTiles,
featuresAt: Source._vectorFeaturesAt,
- _updateData() {
+ _updateData: function() {
this._dirty = false;
this.workerID = this.dispatcher.send('parse geojson', {
data: this._data,
tileSize: 512,
source: this.id,
maxZoom: this.maxzoom
- }, (err) => {
+ }, function(err) {
if (err) {
this.fire('error', {error: err});
return;
@@ -75,10 +75,10 @@ GeoJSONSource.prototype = util.inherit(Evented, {
this._loaded = true;
this._pyramid.clearTiles();
this.fire('change');
- });
+ }.bind(this));
},
- _loadTile(tile) {
+ _loadTile: function(tile) {
var params = {
id: tile.uid,
tileId: tile.id,
@@ -89,31 +89,31 @@ GeoJSONSource.prototype = util.inherit(Evented, {
depth: tile.zoom >= this.maxzoom ? this.map.options.maxZoom - tile.zoom : 1
};
- tile.workerID = this.dispatcher.send('load geojson tile', params, (err, data) => {
+ tile.workerID = this.dispatcher.send('load geojson tile', params, function(err, data) {
if (tile.aborted)
return;
if (err)
return this.fire('tile.error', {tile: tile});
- tile.loadVectorData(data, this.style.buckets);
+ tile.loadVectorData(data);
this.fire('tile.load', {tile: tile});
- }, this.workerID);
+ }.bind(this), this.workerID);
},
- _abortTile(tile) {
+ _abortTile: function(tile) {
tile.aborted = true;
},
- _addTile(tile) {
+ _addTile: function(tile) {
this.fire('tile.add', {tile: tile});
},
- _removeTile(tile) {
+ _removeTile: function(tile) {
this.fire('tile.remove', {tile: tile});
},
- _unloadTile(tile) {
+ _unloadTile: function(tile) {
tile.unloadVectorData(this.map.painter);
this.glyphAtlas.removeGlyphs(tile.uid);
this.dispatcher.send('remove tile', { id: tile.uid, source: this.id }, null, tile.workerID);
diff --git a/js/source/raster_tile_source.js b/js/source/raster_tile_source.js
index 427b302d711..905ae420e71 100644
--- a/js/source/raster_tile_source.js
+++ b/js/source/raster_tile_source.js
@@ -20,15 +20,15 @@ RasterTileSource.prototype = util.inherit(Evented, {
tileSize: 512,
_loaded: false,
- onAdd(map) {
+ onAdd: function(map) {
this.map = map;
},
- loaded() {
+ loaded: function() {
return this._pyramid && this._pyramid.loaded();
},
- update(transform) {
+ update: function(transform) {
if (this._pyramid) {
this._pyramid.update(this.used, transform, this.map.style.rasterFadeDuration);
}
@@ -36,8 +36,8 @@ RasterTileSource.prototype = util.inherit(Evented, {
render: Source._renderTiles,
- _loadTile(tile) {
- ajax.getImage(TileCoord.url(tile.id, this.tiles), (err, img) => {
+ _loadTile: function(tile) {
+ ajax.getImage(TileCoord.url(tile.id, this.tiles), function(err, img) {
if (tile.aborted)
return;
@@ -65,41 +65,29 @@ RasterTileSource.prototype = util.inherit(Evented, {
this.map.animationLoop.set(this.style.rasterFadeDuration);
tile.source = this;
- tile.buckets = {};
- var buckets = this.style.buckets;
- for (var b in buckets) {
- var bucket = buckets[b];
- var sourceid = bucket && bucket.source;
- if (this.id === sourceid) {
- tile.buckets[b] = {
- layoutProperties: bucket.layout,
- type: 'raster'
- };
- }
- }
-
tile.loaded = true;
+
this.fire('tile.load', {tile: tile});
- });
+ }.bind(this));
},
- _abortTile(tile) {
+ _abortTile: function(tile) {
tile.aborted = true;
},
- _addTile(tile) {
+ _addTile: function(tile) {
this.fire('tile.add', {tile: tile});
},
- _removeTile(tile) {
+ _removeTile: function(tile) {
this.fire('tile.remove', {tile: tile});
},
- _unloadTile(tile) {
+ _unloadTile: function(tile) {
if (tile.texture) this.map.painter.saveTexture(tile.texture);
},
- featuresAt(point, params, callback) {
+ featuresAt: function(point, params, callback) {
callback(null, []);
}
});
diff --git a/js/source/source.js b/js/source/source.js
index 235057116eb..e9313505948 100644
--- a/js/source/source.js
+++ b/js/source/source.js
@@ -8,7 +8,7 @@ var TilePyramid = require('./tile_pyramid');
var normalizeURL = require('../util/mapbox').normalizeSourceURL;
exports._loadTileJSON = function(options) {
- var loaded = (err, tileJSON) => {
+ var loaded = function(err, tileJSON) {
if (err) {
this.fire('error', {error: err});
return;
@@ -30,7 +30,7 @@ exports._loadTileJSON = function(options) {
});
this.fire('load');
- };
+ }.bind(this);
if (options.url) {
ajax.getJSON(normalizeURL(options.url), loaded);
@@ -55,7 +55,7 @@ exports._renderTiles = function(layers, painter) {
x += w * (1 << z);
tile.calculateMatrices(z, x, y, painter.transform, painter);
- painter.drawTile(tile, painter.style, layers);
+ painter.drawTile(tile, layers);
}
};
@@ -77,13 +77,13 @@ exports._vectorFeaturesAt = function(point, params, callback) {
}, callback, result.tile.workerID);
};
-var sources = {
- vector: require('./vector_tile_source'),
- raster: require('./raster_tile_source'),
- geojson: require('./geojson_source'),
- video: require('./video_source')
-};
-
exports.create = function(source) {
+ // This is not at file scope in order to avoid a circular require.
+ var sources = {
+ vector: require('./vector_tile_source'),
+ raster: require('./raster_tile_source'),
+ geojson: require('./geojson_source'),
+ video: require('./video_source')
+ };
return new sources[source.type](source);
};
diff --git a/js/source/tile.js b/js/source/tile.js
index 812eb0994de..1ea6998c986 100644
--- a/js/source/tile.js
+++ b/js/source/tile.js
@@ -7,7 +7,6 @@ var vec2 = glmatrix.vec2;
var TileCoord = require('./tile_coord');
var util = require('../util/util');
var BufferSet = require('../data/buffer/buffer_set');
-var createBucket = require('../data/create_bucket');
module.exports = Tile;
@@ -23,7 +22,7 @@ Tile.prototype = {
// todo unhardcode
tileExtent: 4096,
- calculateMatrices(z, x, y, transform, painter) {
+ calculateMatrices: function(z, x, y, transform, painter) {
// Initialize model-view matrix that converts from the tile coordinates
// to screen coordinates.
@@ -60,7 +59,7 @@ Tile.prototype = {
mat2.rotate(this.rotationMatrix, this.rotationMatrix, transform.angle);
},
- positionAt(point) {
+ positionAt: function(point) {
// tile hasn't finished loading
if (!this.invPosMatrix) return null;
@@ -73,30 +72,19 @@ Tile.prototype = {
};
},
- loadVectorData(data, styleBuckets) {
+ loadVectorData: function(data) {
this.loaded = true;
- this.buckets = {};
// empty GeoJSON tile
if (!data) return;
this.buffers = new BufferSet(data.buffers);
- for (var b in data.elementGroups) {
- this.buckets[b] = createBucket(styleBuckets[b], this.buffers, undefined, data.elementGroups[b]);
- }
+ this.elementGroups = data.elementGroups;
},
- unloadVectorData(painter) {
- for (var bucket in this.buckets) {
- if (this.buckets[bucket] && this.buckets[bucket].prerendered) {
- painter.saveTexture(this.buckets[bucket].prerendered.texture);
- }
- }
-
- if (this.buffers) {
- for (var b in this.buffers) {
- this.buffers[b].destroy(painter.gl);
- }
+ unloadVectorData: function(painter) {
+ for (var b in this.buffers) {
+ this.buffers[b].destroy(painter.gl);
}
}
};
diff --git a/js/source/tile_pyramid.js b/js/source/tile_pyramid.js
index d1079914639..53a354aca6b 100644
--- a/js/source/tile_pyramid.js
+++ b/js/source/tile_pyramid.js
@@ -20,11 +20,11 @@ function TilePyramid(options) {
this._remove = options.remove;
this._tiles = {};
- this._cache = new Cache(options.cacheSize, tile => this._unload(tile));
+ this._cache = new Cache(options.cacheSize, function(tile) { return this._unload(tile); }.bind(this));
}
TilePyramid.prototype = {
- loaded() {
+ loaded: function() {
for (var t in this._tiles) {
if (!this._tiles[t].loaded)
return false;
@@ -32,32 +32,32 @@ TilePyramid.prototype = {
return true;
},
- orderedIDs() {
+ orderedIDs: function() {
return Object.keys(this._tiles)
- .sort((a, b) => (b % 32) - (a % 32)) // z-order
- .map((id) => +id);
+ .sort(function(a, b) { return (b % 32) - (a % 32); }) // z-order
+ .map(function(id) { return +id; });
},
- renderedIDs() {
- return this.orderedIDs().filter((id) => {
+ renderedIDs: function() {
+ return this.orderedIDs().filter(function(id) {
return this._tiles[id].loaded && !this._coveredTiles[id];
- });
+ }.bind(this));
},
- getTile(id) {
+ getTile: function(id) {
return this._tiles[id];
},
// get the zoom level adjusted for the difference in map and source tilesizes
- getZoom(transform) {
+ getZoom: function(transform) {
return transform.zoom + Math.log(transform.tileSize / this.tileSize) / Math.LN2;
},
- coveringZoomLevel(transform) {
+ coveringZoomLevel: function(transform) {
return Math.floor(this.getZoom(transform));
},
- coveringTiles(transform) {
+ coveringTiles: function(transform) {
var z = this.coveringZoomLevel(transform);
if (z < this.minzoom) return [];
@@ -80,7 +80,7 @@ TilePyramid.prototype = {
// Recursively find children of the given tile (up to maxCoveringZoom) that are already loaded;
// adds found tiles to retain object; returns true if children completely cover the tile
- findLoadedChildren(id, maxCoveringZoom, retain) {
+ findLoadedChildren: function(id, maxCoveringZoom, retain) {
var complete = true;
var z = TileCoord.fromID(id).z;
var ids = TileCoord.children(id);
@@ -100,7 +100,7 @@ TilePyramid.prototype = {
// Find a loaded parent of the given tile (up to minCoveringZoom);
// adds the found tile to retain object and returns the tile if found
- findLoadedParent(id, minCoveringZoom, retain) {
+ findLoadedParent: function(id, minCoveringZoom, retain) {
for (var z = TileCoord.fromID(id).z; z >= minCoveringZoom; z--) {
id = TileCoord.parent(id);
var tile = this._tiles[id];
@@ -112,7 +112,7 @@ TilePyramid.prototype = {
},
// Removes tiles that are outside the viewport and adds new tiles that are inside the viewport.
- update(used, transform, fadeDuration) {
+ update: function(used, transform, fadeDuration) {
var i;
var id;
var tile;
@@ -169,7 +169,7 @@ TilePyramid.prototype = {
}
},
- addTile(id) {
+ addTile: function(id) {
var tile = this._tiles[id];
if (tile)
return tile;
@@ -189,7 +189,7 @@ TilePyramid.prototype = {
return tile;
},
- removeTile(id) {
+ removeTile: function(id) {
var tile = this._tiles[id];
if (!tile)
return;
@@ -209,13 +209,13 @@ TilePyramid.prototype = {
}
},
- clearTiles() {
+ clearTiles: function() {
for (var id in this._tiles)
this.removeTile(id);
this._cache.reset();
},
- tileAt(point) {
+ tileAt: function(point) {
var ids = this.orderedIDs();
for (var i = 0; i < ids.length; i++) {
var tile = this._tiles[ids[i]];
@@ -233,7 +233,7 @@ TilePyramid.prototype = {
}
},
- _wrappedID(id) {
+ _wrappedID: function(id) {
var pos = TileCoord.fromID(id);
return pos.w === 0 ? id : TileCoord.toID(pos.z, pos.x, pos.y, 0);
}
diff --git a/js/source/vector_tile_source.js b/js/source/vector_tile_source.js
index 61deba7f801..58d02787482 100644
--- a/js/source/vector_tile_source.js
+++ b/js/source/vector_tile_source.js
@@ -23,15 +23,15 @@ VectorTileSource.prototype = util.inherit(Evented, {
tileSize: 512,
_loaded: false,
- onAdd(map) {
+ onAdd: function(map) {
this.map = map;
},
- loaded() {
+ loaded: function() {
return this._pyramid && this._pyramid.loaded();
},
- update(transform) {
+ update: function(transform) {
if (this._pyramid) {
this._pyramid.update(this.used, transform);
}
@@ -40,7 +40,7 @@ VectorTileSource.prototype = util.inherit(Evented, {
render: Source._renderTiles,
featuresAt: Source._vectorFeaturesAt,
- _loadTile(tile) {
+ _loadTile: function(tile) {
var params = {
url: TileCoord.url(tile.id, this.tiles),
id: tile.uid,
@@ -52,32 +52,32 @@ VectorTileSource.prototype = util.inherit(Evented, {
depth: tile.zoom >= this.maxzoom ? this.map.options.maxZoom - tile.zoom : 1
};
- tile.workerID = this.dispatcher.send('load tile', params, (err, data) => {
+ tile.workerID = this.dispatcher.send('load tile', params, function(err, data) {
if (tile.aborted)
return;
if (err)
return this.fire('tile.error', {tile: tile});
- tile.loadVectorData(data, this.style.buckets);
+ tile.loadVectorData(data);
this.fire('tile.load', {tile: tile});
- });
+ }.bind(this));
},
- _abortTile(tile) {
+ _abortTile: function(tile) {
tile.aborted = true;
this.dispatcher.send('abort tile', { id: tile.uid, source: this.id }, null, tile.workerID);
},
- _addTile(tile) {
+ _addTile: function(tile) {
this.fire('tile.add', {tile: tile});
},
- _removeTile(tile) {
+ _removeTile: function(tile) {
this.fire('tile.remove', {tile: tile});
},
- _unloadTile(tile) {
+ _unloadTile: function(tile) {
tile.unloadVectorData(this.map.painter);
this.glyphAtlas.removeGlyphs(tile.uid);
this.dispatcher.send('remove tile', { id: tile.uid, source: this.id }, null, tile.workerID);
diff --git a/js/source/video_source.js b/js/source/video_source.js
index 4e524101374..f49d8676b88 100644
--- a/js/source/video_source.js
+++ b/js/source/video_source.js
@@ -23,15 +23,15 @@ function VideoSource(options) {
var loopID;
// start repainting when video starts playing
- this.video.addEventListener('playing', () => {
+ this.video.addEventListener('playing', function() {
loopID = this.map.style.animationLoop.set(Infinity);
this.map._rerender();
- });
+ }.bind(this));
// stop repainting when video stops
- this.video.addEventListener('pause', () => {
+ this.video.addEventListener('pause', function() {
this.map.style.animationLoop.cancel(loopID);
- });
+ }.bind(this));
this._loaded = true;
@@ -44,7 +44,7 @@ function VideoSource(options) {
}
VideoSource.prototype = util.inherit(Evented, {
- onAdd(map) {
+ onAdd: function(map) {
this.map = map;
if (this.video) {
this.video.play();
@@ -52,7 +52,7 @@ VideoSource.prototype = util.inherit(Evented, {
}
},
- createTile() {
+ createTile: function() {
/*
* Calculate which mercator tile is suitable for rendering the video in
* and create a buffer with the corner coordinates. These coordinates
@@ -112,22 +112,18 @@ VideoSource.prototype = util.inherit(Evented, {
this.center = center;
},
- loaded() {
+ loaded: function() {
return this.video && this.video.readyState >= 2;
},
- update() {
+ update: function() {
// noop
},
- render(layers, painter) {
+ render: function(layers, painter) {
if (!this._loaded) return;
if (this.video.readyState < 2) return; // not enough data for current position
- var layer = layers[0];
- var buckets = {};
- buckets[layer.bucket] = { type: 'raster' };
-
var c = this.center;
this.tile.calculateMatrices(c.zoom, c.column, c.row, this.map.transform, painter);
@@ -145,10 +141,10 @@ VideoSource.prototype = util.inherit(Evented, {
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.video);
}
- painter.drawLayer(this.tile, this.map.style, layer, {}, undefined, buckets);
+ painter.drawLayers(layers, this.tile.posMatrix, this.tile);
},
- featuresAt(point, params, callback) {
+ featuresAt: function(point, params, callback) {
return callback(null, []);
}
});
diff --git a/js/source/worker.js b/js/source/worker.js
index fcab230990e..eaf5a9f8dd0 100644
--- a/js/source/worker.js
+++ b/js/source/worker.js
@@ -1,7 +1,6 @@
'use strict';
var Actor = require('../util/actor');
-var featureFilter = require('feature-filter');
var WorkerTile = require('./worker_tile');
var util = require('../util/util');
var ajax = require('../util/ajax');
@@ -19,17 +18,13 @@ function Worker(self) {
this.actor = new Actor(self, this);
this.loading = {};
this.loaded = {};
- this.buckets = [];
+ this.layers = [];
this.geoJSONIndexes = {};
}
util.extend(Worker.prototype, {
- 'set buckets': function(buckets) {
- this.buckets = buckets;
- for (var i = 0; i < this.buckets.length; i++) {
- var bucket = this.buckets[i];
- bucket.compare = featureFilter(bucket.filter);
- }
+ 'set layers': function(layers) {
+ this.layers = layers;
},
'load tile': function(params, callback) {
@@ -39,7 +34,7 @@ util.extend(Worker.prototype, {
if (!this.loading[source])
this.loading[source] = {};
- this.loading[source][id] = ajax.getArrayBuffer(params.url, (err, data) => {
+ this.loading[source][id] = ajax.getArrayBuffer(params.url, function(err, data) {
delete this.loading[source][id];
if (err) return callback(err);
@@ -48,11 +43,12 @@ util.extend(Worker.prototype, {
params.id, params.zoom, params.maxZoom,
params.tileSize, params.source, params.depth);
- tile.parse(new vt.VectorTile(new Protobuf(new Uint8Array(data))), this.buckets, this.actor, callback);
+ tile.parse(new vt.VectorTile(new Protobuf(new Uint8Array(data))), this.layers, this.actor, callback);
+ console.log(Math.round(self.tesselateTime) + ' ms');
this.loaded[source] = this.loaded[source] || {};
this.loaded[source][id] = tile;
- });
+ }.bind(this));
},
'abort tile': function(params) {
@@ -72,10 +68,10 @@ util.extend(Worker.prototype, {
},
'parse geojson': function(params, callback) {
- var indexData = (err, data) => {
+ var indexData = function(err, data) {
this.geoJSONIndexes[params.source] = geojsonvt(data, {baseZoom: params.maxZoom});
callback(null);
- };
+ }.bind(this);
// TODO accept params.url for urls instead
if (typeof params.data === 'string') ajax.getJSON(params.data, indexData);
@@ -99,7 +95,7 @@ util.extend(Worker.prototype, {
if (!geoJSONTile) return callback(null, null); // nothing in the given tile
var tile = new WorkerTile(id, params.zoom, params.maxZoom, params.tileSize, source, params.depth);
- tile.parse(new GeoJSONWrapper(geoJSONTile.features), this.buckets, this.actor, callback);
+ tile.parse(new GeoJSONWrapper(geoJSONTile.features), this.layers, this.actor, callback);
this.loaded[source] = this.loaded[source] || {};
this.loaded[source][id] = tile;
diff --git a/js/source/worker_tile.js b/js/source/worker_tile.js
index 22d4bccdc54..341eca44156 100644
--- a/js/source/worker_tile.js
+++ b/js/source/worker_tile.js
@@ -3,10 +3,17 @@
var FeatureTree = require('../data/feature_tree');
var vt = require('vector-tile');
var Collision = require('../symbol/collision');
-
var BufferSet = require('../data/buffer/buffer_set');
var createBucket = require('../data/create_bucket');
+function getGeometry(feature) {
+ return feature.loadGeometry();
+}
+
+function getType(feature) {
+ return vt.VectorTileFeature.types[feature.type];
+}
+
module.exports = WorkerTile;
function WorkerTile(id, zoom, maxZoom, tileSize, source, depth) {
@@ -16,32 +23,100 @@ function WorkerTile(id, zoom, maxZoom, tileSize, source, depth) {
this.tileSize = tileSize;
this.source = source;
this.depth = depth;
- this.buffers = new BufferSet();
+ this.featureTree = new FeatureTree(getGeometry, getType);
}
-/*
- * Given tile data, parse raw vertices and data, create a vector
- * tile and parse it into ready-to-render vertices.
- *
- * @param {object} data
- * @param {function} respond
- */
-WorkerTile.prototype.parse = function(data, bucketInfo, actor, callback) {
- var tile = this;
-
- this.data = data;
- this.callback = callback;
-
- var tileExtent = 4096;
- this.collision = new Collision(this.zoom, tileExtent, this.tileSize, this.depth);
- this.featureTree = new FeatureTree(getGeometry, getType);
+WorkerTile.prototype.parse = function(data, layers, actor, callback) {
+ var i, k,
+ tile = this,
+ layer,
+ bucket,
+ buffers = new BufferSet(),
+ collision = new Collision(this.zoom, 4096, this.tileSize, this.depth),
+ buckets = {},
+ bucketsInOrder = [],
+ bucketsBySourceLayer = {};
- var buckets = this.buckets = sortTileIntoBuckets(this, data, bucketInfo);
+ // Map non-ref layers to buckets.
+ for (i = 0; i < layers.length; i++) {
+ layer = layers[i];
- var key, bucket;
- var prevPlacementBucket;
+ if (layer.source !== this.source)
+ continue;
+
+ if (layer.ref)
+ continue;
+
+ var minzoom = layer.minzoom;
+ if (minzoom && this.zoom < minzoom && minzoom < this.maxZoom)
+ continue;
+
+ var maxzoom = layer.maxzoom;
+ if (maxzoom && this.zoom >= maxzoom)
+ continue;
+
+ bucket = createBucket(layer, buffers, collision);
+ bucket.layers = [layer.id];
+
+ buckets[bucket.id] = bucket;
+ bucketsInOrder.push(bucket);
- var remaining = bucketInfo.length;
+ if (data.layers) {
+ // vectortile
+ var sourceLayer = layer['source-layer'];
+ if (!bucketsBySourceLayer[sourceLayer])
+ bucketsBySourceLayer[sourceLayer] = {};
+ bucketsBySourceLayer[sourceLayer][bucket.id] = bucket;
+ } else {
+ // geojson tile
+ bucketsBySourceLayer[bucket.id] = bucket;
+ }
+ }
+
+ // Index ref layers.
+ for (i = 0; i < layers.length; i++) {
+ layer = layers[i];
+
+ if (layer.source !== this.source)
+ continue;
+
+ if (!layer.ref)
+ continue;
+
+ bucket = buckets[layer.ref];
+ if (!bucket)
+ continue;
+
+ bucket.layers.push(layer.id);
+ }
+
+ // read each layer, and sort its features into buckets
+ if (data.layers) {
+ // vectortile
+ for (k in bucketsBySourceLayer) {
+ layer = data.layers[k];
+ if (!layer) continue;
+ sortLayerIntoBuckets(layer, bucketsBySourceLayer[k]);
+ }
+ } else {
+ // geojson
+ sortLayerIntoBuckets(data, bucketsBySourceLayer);
+ }
+
+ function sortLayerIntoBuckets(layer, buckets) {
+ for (var i = 0; i < layer.length; i++) {
+ var feature = layer.feature(i);
+ for (var key in buckets) {
+ var bucket = buckets[key];
+ if (bucket.filter(feature)) {
+ bucket.features.push(feature);
+ }
+ }
+ }
+ }
+
+ var prevPlacementBucket;
+ var remaining = bucketsInOrder.length;
/*
* The async parsing here is a bit tricky.
@@ -52,20 +127,13 @@ WorkerTile.prototype.parse = function(data, bucketInfo, actor, callback) {
* Buckets that don't need to be parsed in order, aren't to save time.
*/
- for (var i = 0; i < bucketInfo.length; i++) {
- bucket = buckets[bucketInfo[i].id];
- if (bucket) bucket.info = bucketInfo[i];
-
- if (bucketInfo[i].source !== this.source || !bucket) {
- remaining--;
- continue; // raster bucket, etc
- }
+ for (i = 0; i < bucketsInOrder.length; i++) {
+ bucket = bucketsInOrder[i];
// Link buckets that need to be parsed in order
if (bucket.collision) {
if (prevPlacementBucket) {
prevPlacementBucket.next = bucket;
- prevPlacementBucket.next.bucketInfo = bucketInfo[i];
} else {
bucket.previousPlaced = true;
}
@@ -75,12 +143,9 @@ WorkerTile.prototype.parse = function(data, bucketInfo, actor, callback) {
if (bucket.getDependencies) {
bucket.getDependencies(this, actor, dependenciesDone(bucket));
}
- }
- // parse buckets where order doesn't matter and no dependencies
- for (key in buckets) {
- bucket = buckets[key];
- if (!bucket.getDependencies && !bucket.collision) {
+ // immediately parse buckets where order doesn't matter and no dependencies
+ if (!bucket.collision && !bucket.getDependencies) {
parseBucket(tile, bucket);
}
}
@@ -98,23 +163,23 @@ WorkerTile.prototype.parse = function(data, bucketInfo, actor, callback) {
if (!skip) {
var now = Date.now();
- if (bucket.type !== 'raster' && bucket.features.length) bucket.addFeatures();
+ if (bucket.features.length) bucket.addFeatures();
var time = Date.now() - now;
if (bucket.interactive) {
for (var i = 0; i < bucket.features.length; i++) {
var feature = bucket.features[i];
- tile.featureTree.insert(feature.bbox(), bucket.info, feature);
+ tile.featureTree.insert(feature.bbox(), bucket.layers, feature);
}
}
if (typeof self !== 'undefined') {
self.bucketStats = self.bucketStats || {_total: 0};
self.bucketStats._total += time;
- self.bucketStats[bucket.name] = (self.bucketStats[bucket.name] || 0) + time;
+ self.bucketStats[bucket.id] = (self.bucketStats[bucket.id] || 0) + time;
}
}
remaining--;
- if (!remaining) return tile.done();
+ if (!remaining) return done();
// try parsing the next bucket, if it is ready
if (bucket.next) {
@@ -122,134 +187,22 @@ WorkerTile.prototype.parse = function(data, bucketInfo, actor, callback) {
parseBucket(tile, bucket.next);
}
}
-};
-
-WorkerTile.prototype.done = function() {
- // Collect all buffers to mark them as transferable object.
- var buffers = [];
-
- for (var type in this.buffers) {
- buffers.push(this.buffers[type].array);
- }
- // Convert buckets to a transferable format
- var buckets = this.buckets;
- var elementGroups = {};
- for (var b in buckets) elementGroups[b] = buckets[b].elementGroups;
-
- this.callback(null, {
- elementGroups: elementGroups,
- buffers: this.buffers
- }, buffers);
-
- // we don't need anything except featureTree at this point, so we mark it for GC
- this.buffers = null;
- this.collision = null;
- this.buckets = null;
-};
-
-function sortTileIntoBuckets(tile, data, bucketInfo) {
-
- var sourceLayers = {},
- buckets = {},
- layerName,
- refs = [];
+ function done() {
+ var transferables = [],
+ elementGroups = {};
- function matchTileToBucket(info) {
- var bucketName = info.id;
-
- var minZoom = info.minzoom;
- var maxZoom = info.maxzoom;
-
- if (info.ref) refs.push(info);
-
- if (info.source !== tile.source) return;
- if (minZoom && tile.zoom < minZoom && minZoom < tile.maxZoom) return;
- if (maxZoom && tile.zoom >= maxZoom) return;
-
- var bucket = createBucket(info, tile.buffers, tile.collision);
- if (!bucket) return;
- bucket.features = [];
- bucket.name = bucketName;
- bucket['source-layer'] = info['source-layer'];
- buckets[bucketName] = bucket;
-
- if (data.layers) {
- // vectortile
- layerName = info['source-layer'];
- if (!sourceLayers[layerName]) sourceLayers[layerName] = {};
- sourceLayers[layerName][bucketName] = info;
- } else {
- // geojson tile
- sourceLayers[bucketName] = info;
+ for (k in buffers) {
+ transferables.push(buffers[k].array);
}
- }
- // For each source layer, find a list of buckets that use data from it
- for (var i = 0; i < bucketInfo.length; i++) {
- var info = bucketInfo[i];
- matchTileToBucket(info);
- }
- while (refs.length) {
- var l = refs.shift();
- // bucket is from a different source
- if (!buckets[l.ref]) continue;
- var refSource = buckets[l.ref]['source-layer'];
- var refLayer = sourceLayers[refSource][l.ref];
-
- Object.keys(refLayer).forEach(key => {
- if (key !== 'paint' && !l[key]) l[key] = refLayer[key];
- });
- sourceLayers[refSource][l.id] = l;
-
- var bucket = createBucket(l, tile.buffers, tile.collision);
- bucket.features = [];
- bucket.name = l.id;
- buckets[l.id] = bucket;
- }
-
- // read each layer, and sort its features into buckets
- if (data.layers) {
- // vectortile
- for (layerName in sourceLayers) {
- var layer = data.layers[layerName];
- if (!layer) continue;
- sortLayerIntoBuckets(layer, sourceLayers[layerName], buckets);
+ for (k in buckets) {
+ elementGroups[k] = buckets[k].elementGroups;
}
- } else {
- // geojson
- sortLayerIntoBuckets(data, sourceLayers, buckets);
- }
- return buckets;
-}
-
-/*
- * Sorts features in a layer into different buckets, according to the maping
- *
- * Layers in vector tiles contain many different features, and feature types,
- * e.g. the landuse layer has parks, industrial buildings, forests, playgrounds
- * etc. However, when styling, we need to separate these features so that we can
- * render them separately with different styles.
- *
- * @param {VectorTileLayer} layer
- * @param {Mapping} mapping
- */
-function sortLayerIntoBuckets(layer, mapping, buckets) {
- for (var i = 0; i < layer.length; i++) {
- var feature = layer.feature(i);
- for (var key in mapping) {
- if (mapping[key].compare(feature)) {
- buckets[key].features.push(feature);
- }
- }
+ callback(null, {
+ elementGroups: elementGroups,
+ buffers: buffers
+ }, transferables);
}
-}
-
-function getGeometry(feature) {
- return feature.loadGeometry();
-}
-
-function getType(feature) {
- return vt.VectorTileFeature.types[feature.type];
-}
+};
diff --git a/js/style/image_sprite.js b/js/style/image_sprite.js
index 5054c4f2324..8007a920722 100644
--- a/js/style/image_sprite.js
+++ b/js/style/image_sprite.js
@@ -12,7 +12,7 @@ function ImageSprite(base) {
base = this.base + (this.retina ? '@2x' : '');
- ajax.getJSON(base + '.json', (err, data) => {
+ ajax.getJSON(base + '.json', function(err, data) {
if (err) {
this.fire('error', {error: err});
return;
@@ -20,9 +20,9 @@ function ImageSprite(base) {
this.data = data;
if (this.img) this.fire('load');
- });
+ }.bind(this));
- ajax.getImage(base + '.png', (err, img) => {
+ ajax.getImage(base + '.png', function(err, img) {
if (err) {
this.fire('error', {error: err});
return;
@@ -41,7 +41,7 @@ function ImageSprite(base) {
this.img = img;
if (this.data) this.fire('load');
- });
+ }.bind(this));
}
ImageSprite.prototype = Object.create(Evented);
diff --git a/js/style/style.js b/js/style/style.js
index c3a7d30d2d8..332724493f4 100644
--- a/js/style/style.js
+++ b/js/style/style.js
@@ -2,11 +2,7 @@
var Evented = require('../util/evented');
var Source = require('../source/source');
-var StyleTransition = require('./style_transition');
-var StyleDeclaration = require('./style_declaration');
-var StyleConstant = require('./style_constant');
-var LayoutProperties = require('./layout_properties');
-var PaintProperties = require('./paint_properties');
+var StyleLayer = require('./style_layer');
var ImageSprite = require('./image_sprite');
var GlyphSource = require('../symbol/glyph_source');
var GlyphAtlas = require('../symbol/glyph_atlas');
@@ -27,13 +23,8 @@ function Style(stylesheet, animationLoop) {
this.spriteAtlas = new SpriteAtlas(512, 512);
this.spriteAtlas.resize(browser.devicePixelRatio);
- this.buckets = {};
- this.orderedBuckets = [];
- this.layerMap = {};
- this.layerGroups = [];
- this.processedPaintProps = {};
- this.transitions = {};
- this.computed = {};
+ this._layers = {};
+ this._groups = [];
this.sources = {};
util.bindAll([
@@ -41,7 +32,7 @@ function Style(stylesheet, animationLoop) {
'_forwardTileEvent'
], this);
- var loaded = (err, stylesheet) => {
+ var loaded = function(err, stylesheet) {
if (err) {
this.fire('error', {error: err});
return;
@@ -50,7 +41,7 @@ function Style(stylesheet, animationLoop) {
this._loaded = true;
this.stylesheet = stylesheet;
- if (stylesheet.version !== 6) console.warn('Stylesheet version must be 6');
+ if (stylesheet.version !== 7) console.warn('Stylesheet version must be 7');
if (!Array.isArray(stylesheet.layers)) console.warn('Stylesheet must have layers');
var sources = stylesheet.sources;
@@ -64,8 +55,9 @@ function Style(stylesheet, animationLoop) {
}
this.glyphSource = new GlyphSource(stylesheet.glyphs, this.glyphAtlas);
+ this._resolve();
this.fire('load');
- };
+ }.bind(this);
if (typeof stylesheet === 'string') {
ajax.getJSON(normalizeURL(stylesheet), loaded);
@@ -74,34 +66,10 @@ function Style(stylesheet, animationLoop) {
}
}
-function premultiplyLayer(layer, type) {
- var colorProp = type + '-color',
- haloProp = type + '-halo-color',
- outlineProp = type + '-outline-color',
- color = layer[colorProp],
- haloColor = layer[haloProp],
- outlineColor = layer[outlineProp],
- opacity = layer[type + '-opacity'];
-
- var colorOpacity = color && (opacity * color[3]);
- var haloOpacity = haloColor && (opacity * haloColor[3]);
- var outlineOpacity = outlineColor && (opacity * outlineColor[3]);
-
- if (colorOpacity !== undefined && colorOpacity < 1) {
- layer[colorProp] = util.premultiply([color[0], color[1], color[2], colorOpacity]);
- }
- if (haloOpacity !== undefined && haloOpacity < 1) {
- layer[haloProp] = util.premultiply([haloColor[0], haloColor[1], haloColor[2], haloOpacity]);
- }
- if (outlineOpacity !== undefined && outlineOpacity < 1) {
- layer[outlineProp] = util.premultiply([outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity]);
- }
-}
-
Style.prototype = util.inherit(Evented, {
_loaded: false,
- loaded() {
+ loaded: function() {
if (!this._loaded)
return false;
@@ -115,277 +83,84 @@ Style.prototype = util.inherit(Evented, {
return true;
},
- recalculate(z) {
- if (typeof z !== 'number') console.warn('recalculate expects zoom level');
-
- var transitions = this.transitions;
- var layerValues = {};
-
- for (var id in this.sources)
- this.sources[id].used = false;
-
- this.rasterFadeDuration = 300;
+ _resolve: function() {
+ var id, layer, group, ordered = [];
- for (var name in transitions) {
- var layer = transitions[name],
- bucket = this.buckets[layer.ref || name],
- layerType = this.layerMap[name].type;
+ this._layers = {};
+ this._groups = [];
- if (!PaintProperties[layerType]) {
- console.warn('unknown layer type ' + layerType);
- continue;
- }
- var appliedLayer = layerValues[name] = new PaintProperties[layerType]();
- for (var rule in layer) {
- var transition = layer[rule];
- appliedLayer[rule] = transition.at(z);
- }
-
- if (layerType === 'symbol') {
- if ((appliedLayer['text-opacity'] === 0 || !bucket.layout['text-field']) &&
- (appliedLayer['icon-opacity'] === 0 || !bucket.layout['icon-image'])) {
- appliedLayer.hidden = true;
- } else {
- premultiplyLayer(appliedLayer, 'text');
- premultiplyLayer(appliedLayer, 'icon');
- }
- } else {
- if (appliedLayer[layerType + '-opacity'] === 0) {
- appliedLayer.hidden = true;
- } else {
- premultiplyLayer(appliedLayer, layerType);
- }
- }
-
- // Find all the sources that are currently being used
- // so that we can automatically enable/disable them as needed
- if (!appliedLayer.hidden) {
- var source = bucket && bucket.source;
-
- // mark source as used so that tiles are downloaded
- if (source) this.sources[source].used = true;
- }
-
- if (appliedLayer['raster-fade-duration']) {
- this.rasterFadeDuration = Math.max(this.rasterFadeDuration, appliedLayer['raster-fade-duration']);
- }
+ for (var i = 0; i < this.stylesheet.layers.length; i++) {
+ layer = new StyleLayer(this.stylesheet.layers[i]);
+ this._layers[layer.id] = layer;
}
- this.computed = layerValues;
-
- this.z = z;
- this.fire('zoom');
- },
-
- _simpleLayer(layer) {
- var simple = {};
- simple.id = layer.id;
-
- var bucket = this.buckets[layer.ref || layer.id];
- if (bucket) simple.bucket = bucket.id;
- if (layer.type) simple.type = layer.type;
-
- if (layer.layers) {
- simple.layers = [];
- for (var i = 0; i < layer.layers.length; i++) {
- simple.layers.push(this._simpleLayer(layer.layers[i]));
- }
+ // Resolve layout properties.
+ for (id in this._layers) {
+ this._layers[id].resolveLayout(this._layers,
+ this.stylesheet.constants,
+ this.stylesheet.transition);
}
- return simple;
- },
-
- // Split the layers into groups of consecutive layers with the same datasource
- _groupLayers(layers) {
- var g = 0;
- var groups = [];
- var group;
- // loop over layers top down
- for (var i = layers.length - 1; i >= 0; i--) {
- var layer = layers[i];
+ // Resolve paint properties.
+ for (id in this._layers) {
+ this._layers[id].resolvePaint(this._layers,
+ this.stylesheet.constants,
+ this.stylesheet.transition);
+ }
- var bucket = this.buckets[layer.ref || layer.id];
- var source = bucket && bucket.source;
+ // Split into groups of consecutive top-level layers with the same source.
+ for (id in this._layers) {
+ layer = this._layers[id];
- // if the current layer is in a different source
- if (group && source !== group.source) g++;
+ ordered.push(layer.json());
- if (!groups[g]) {
+ if (!group || layer.source !== group.source) {
group = [];
- group.source = source;
- groups[g] = group;
+ group.source = layer.source;
+ this._groups.push(group);
}
- group.push(this._simpleLayer(layer));
+ group.push(layer);
}
- return groups;
+ this.dispatcher.broadcast('set layers', ordered);
},
- /*
- * Take all the rules and declarations from the stylesheet,
- * and figure out which apply currently
- */
- _cascade(classes, options) {
- var layer,
- id,
- prop,
- paintProp;
-
- var constants = this.stylesheet.constants;
- var globalTrans = this.stylesheet.transition;
-
- // derive buckets from layers
- this.orderedBuckets = [];
- this.buckets = getBuckets({}, this.orderedBuckets, this.stylesheet.layers);
- function getBuckets(buckets, ordered, layers) {
- for (var a = 0; a < layers.length; a++) {
- var layer = layers[a];
- if (layer.layers) {
- buckets = getBuckets(buckets, ordered, layer.layers);
- }
- if (!layer.ref && (!layer.source || !layer.type)) {
- continue;
- }
- var bucket = {id: layer.id};
- for (prop in layer) {
- if ((/^paint/).test(prop)) continue;
- bucket[prop] = layer[prop];
- }
- bucket.layout = StyleConstant.resolve(bucket.layout, constants);
- buckets[layer.id] = bucket;
- ordered.push(bucket);
- }
- return buckets;
- }
- this.dispatcher.broadcast('set buckets', this.orderedBuckets);
-
- var layerMap = this.layerMap = mapLayers(this.stylesheet.layers, {});
+ _cascade: function(classes, options) {
+ if (!this._loaded) return;
- for (id in layerMap) {
- layerMap[id] = resolveLayer(layerMap, layerMap[id]);
- }
+ options = options || {
+ transition: true
+ };
- // pre-calculate style declarations and transition properties for all layers x all classes
- var processedPaintProps = this.processedPaintProps = {};
- for (id in layerMap) {
- layer = layerMap[id];
- var renderType = layer.type;
-
- processedPaintProps[id] = {};
- for (prop in layer) {
- if (!(/^paint/).test(prop)) continue;
- var paint = StyleConstant.resolve(layer[prop], constants);
-
- // makes "" the key for the default paint property, which is a bit
- // unusual, but is valid JS and should work in all browsers
- var className = (prop === "paint") ? "" : prop.slice(6);
- var classProps = processedPaintProps[id][className] = {};
- for (paintProp in paint) {
- var match = paintProp.match(/^(.*)-transition$/);
- if (match) {
- if (!classProps[match[1]]) classProps[match[1]] = {};
- classProps[match[1]].transition = paint[paintProp];
- } else {
- if (!classProps[paintProp]) classProps[paintProp] = {};
- classProps[paintProp].styleDeclaration = new StyleDeclaration(renderType, paintProp, paint[paintProp]);
- }
- }
-
- // do a second pass to fill in missing transition properties & remove
- // transition properties without matching style declaration
- for (paintProp in classProps) {
- if (!classProps[paintProp].styleDeclaration) {
- delete classProps[paintProp];
- } else {
- var trans = classProps[paintProp].transition;
- var newTrans = {};
- newTrans.duration = trans && trans.duration >= 0 ? trans.duration :
- globalTrans && globalTrans.duration >= 0 ? globalTrans.duration : 300;
- newTrans.delay = trans && trans.delay >= 0 ? trans.delay :
- globalTrans && globalTrans.delay >= 0 ? globalTrans.delay : 0;
- classProps[paintProp].transition = newTrans;
- }
- }
- }
+ for (var id in this._layers) {
+ this._layers[id].cascade(classes, options, this.animationLoop);
}
- this.layerGroups = this._groupLayers(this.stylesheet.layers);
- this._cascadeClasses(classes, options);
-
- // Resolve layer references.
- function resolveLayer(layerMap, layer) {
- if (!layer.ref || !layerMap[layer.ref]) return layer;
-
- var parent = resolveLayer(layerMap, layerMap[layer.ref]);
- layer.layout = parent.layout;
- layer.type = parent.type;
- layer.filter = parent.filter;
- layer.source = parent.source;
- layer['source-layer'] = parent['source-layer'];
- layer.minzoom = parent.minzoom;
- layer.maxzoom = parent.maxzoom;
+ this.fire('change');
+ },
- return layer;
- }
+ _recalculate: function(z) {
+ if (typeof z !== 'number') console.warn('recalculate expects zoom level');
- function mapLayers(layers, map) {
- for (var i = 0; i < layers.length; i++) {
- var layer = layers[i];
- map[layer.id] = layer;
- if (layer.layers) {
- mapLayers(layer.layers, map);
- }
- }
- return map;
- }
- },
+ for (var id in this.sources)
+ this.sources[id].used = false;
- _cascadeClasses(classes, options) {
- if (!this._loaded) return;
+ this.rasterFadeDuration = 300;
- options = options || {
- transition: true
- };
+ for (id in this._layers) {
+ var layer = this._layers[id];
- var transitions = {};
- var processedPaintProps = this.processedPaintProps;
-
- for (var id in this.layerMap) {
- transitions[id] = {};
- for (var className in processedPaintProps[id]) {
- if (!(className === "" || classes[className])) continue;
- var paintProps = processedPaintProps[id][className];
- for (var prop in paintProps) {
- var newDeclaration = paintProps[prop].styleDeclaration;
- var newStyleTrans = (options.transition) ? paintProps[prop].transition : {duration: 0, delay: 0};
- var oldTransition = this.transitions[id] && this.transitions[id][prop];
-
- // Only create a new transition if the declaration changed
- if (!oldTransition || oldTransition.declaration.json !== newDeclaration.json) {
- var newTransition = new StyleTransition(newDeclaration, oldTransition, newStyleTrans);
- transitions[id][prop] = newTransition;
-
- // Run the animation loop until the end of the transition
- if (!newTransition.instant()) {
- newTransition.loopID = this.animationLoop.set(newTransition.endTime - (new Date()).getTime());
- }
-
- if (oldTransition) {
- this.animationLoop.cancel(oldTransition.loopID);
- }
- } else {
- transitions[id][prop] = oldTransition;
- }
- }
+ if (layer.recalculate(z) && layer.source) {
+ this.sources[layer.source].used = true;
}
}
- this.transitions = transitions;
- this.fire('change');
+ this.z = z;
+ this.fire('zoom');
},
- addSource(id, source) {
+ addSource: function(id, source) {
if (this.sources[id] !== undefined) {
throw new Error('There is already a source with this ID');
}
@@ -406,7 +181,7 @@ Style.prototype = util.inherit(Evented, {
return this;
},
- removeSource(id) {
+ removeSource: function(id) {
if (this.sources[id] === undefined) {
throw new Error('There is no source with this ID');
}
@@ -424,64 +199,59 @@ Style.prototype = util.inherit(Evented, {
return this;
},
- getSource(id) {
+ getSource: function(id) {
return this.sources[id];
},
- getLayer(id) {
- return this.layerMap[id];
+ getLayer: function(id) {
+ return this._layers[id];
},
- featuresAt(point, params, callback) {
+ featuresAt: function(point, params, callback) {
var features = [];
var error = null;
point = Point.convert(point);
if (params.layer) {
- var layer = this.getLayer(params.layer);
- params.bucket = this.buckets[layer.ref || layer.id];
+ params.layer = { id: params.layer.id };
}
- util.asyncEach(Object.keys(this.sources), (id, callback) => {
+ util.asyncEach(Object.keys(this.sources), function(id, callback) {
var source = this.sources[id];
source.featuresAt(point, params, function(err, result) {
if (result) features = features.concat(result);
if (err) error = err;
callback();
});
- }, () => {
+ }.bind(this), function() {
if (error) return callback(error);
- features.forEach((feature) => {
- var layer = feature.layer;
- layer.paint = this.computed[layer.id];
- layer.layout = new LayoutProperties[layer.type](layer.layout);
- var rawLayer = this.layerMap[layer.id];
- Object.keys(rawLayer).forEach(key => {
- if (!layer[key]) layer[key] = rawLayer[key];
- });
- });
+ features.forEach(function(feature) {
+ feature.layers = feature.layers.map(function(id) {
+ return this._layers[id].json();
+ }.bind(this));
+ }.bind(this));
callback(null, features);
- });
+ }.bind(this));
},
- _remove() {
+ _remove: function() {
this.dispatcher.remove();
},
- _updateSources(transform) {
+ _updateSources: function(transform) {
for (var id in this.sources) {
this.sources[id].update(transform);
}
},
- _forwardSourceEvent(e) {
+ _forwardSourceEvent: function(e) {
this.fire('source.' + e.type, util.extend({source: e.target}, e));
},
- _forwardTileEvent(e) {
+ _forwardTileEvent: function(e) {
this.fire(e.type, util.extend({source: e.target}, e));
},
diff --git a/js/style/style_layer.js b/js/style/style_layer.js
new file mode 100644
index 00000000000..719eb190940
--- /dev/null
+++ b/js/style/style_layer.js
@@ -0,0 +1,172 @@
+'use strict';
+
+var util = require('../util/util');
+var StyleConstant = require('./style_constant');
+var StyleTransition = require('./style_transition');
+var StyleDeclaration = require('./style_declaration');
+var LayoutProperties = require('./layout_properties');
+var PaintProperties = require('./paint_properties');
+
+module.exports = StyleLayer;
+
+function StyleLayer(layer) {
+ this._layer = layer;
+
+ this.id = layer.id;
+ this.ref = layer.ref;
+
+ this.assign(layer);
+}
+
+StyleLayer.prototype = {
+ resolveLayout: function(layers, constants) {
+ if (!this.ref) {
+ this.layout = new LayoutProperties[this.type](
+ StyleConstant.resolve(this._layer.layout, constants));
+
+ if (this.layout['symbol-placement'] === 'line') {
+ if (!this.layout.hasOwnProperty('text-rotation-alignment')) {
+ this.layout['text-rotation-alignment'] = 'map';
+ }
+ if (!this.layout.hasOwnProperty('icon-rotation-alignment')) {
+ this.layout['icon-rotation-alignment'] = 'map';
+ }
+ this.layout['symbol-avoid-edges'] = true;
+ }
+ }
+ },
+
+ resolvePaint: function(layers, constants, globalTrans) {
+ globalTrans = globalTrans || {};
+
+ if (this.ref) {
+ this.assign(layers[this.ref]);
+ }
+
+ // Resolved and cascaded paint properties.
+ this._resolved = {}; // class name -> (property name -> StyleDeclaration)
+ this._cascaded = {}; // property name -> StyleTransition
+
+ for (var p in this._layer) {
+ var match = p.match(/^paint(?:\.(.*))?$/);
+ if (!match)
+ continue;
+
+ var paint = StyleConstant.resolve(this._layer[p], constants),
+ declarations = this._resolved[match[1] || ''] = {};
+
+ for (var k in paint) {
+ if (/-transition$/.test(k))
+ continue;
+
+ var declaration = declarations[k] =
+ new StyleDeclaration(this.type, k, paint[k]);
+
+ var t = paint[k + '-transition'] || {};
+ declaration.transition = {
+ duration: util.coalesce(t.duration, globalTrans.duration, 300),
+ delay: util.coalesce(t.delay, globalTrans.delay, 0)
+ };
+ }
+ }
+ },
+
+ cascade: function(classes, options, animationLoop) {
+ for (var klass in this._resolved) {
+ if (klass !== "" && !classes[klass])
+ continue;
+
+ var declarations = this._resolved[klass];
+ for (var k in declarations) {
+ var newDeclaration = declarations[k];
+ var newStyleTrans = options.transition ? declarations[k].transition : {duration: 0, delay: 0};
+ var oldTransition = this._cascaded[k];
+
+ // Only create a new transition if the declaration changed
+ if (!oldTransition || oldTransition.declaration.json !== newDeclaration.json) {
+ var newTransition = this._cascaded[k] =
+ new StyleTransition(newDeclaration, oldTransition, newStyleTrans);
+
+ // Run the animation loop until the end of the transition
+ if (!newTransition.instant()) {
+ newTransition.loopID = animationLoop.set(newTransition.endTime - (new Date()).getTime());
+ }
+
+ if (oldTransition) {
+ animationLoop.cancel(oldTransition.loopID);
+ }
+ }
+ }
+ }
+ },
+
+ recalculate: function(z) {
+ var type = this.type,
+ calculated = this.paint = new PaintProperties[type]();
+
+ for (var k in this._cascaded) {
+ calculated[k] = this._cascaded[k].at(z);
+ }
+
+ this.hidden = (this.minzoom && z < this.minzoom) ||
+ (this.maxzoom && z >= this.maxzoom);
+
+ if (type === 'symbol') {
+ if ((calculated['text-opacity'] === 0 || !this.layout['text-field']) &&
+ (calculated['icon-opacity'] === 0 || !this.layout['icon-image'])) {
+ this.hidden = true;
+ } else {
+ premultiplyLayer(calculated, 'text');
+ premultiplyLayer(calculated, 'icon');
+ }
+ } else {
+ if (calculated[type + '-opacity'] === 0) {
+ this.hidden = true;
+ } else {
+ premultiplyLayer(calculated, type);
+ }
+ }
+
+ return !this.hidden;
+ },
+
+ assign: function(layer) {
+ util.extend(this, util.pick(layer,
+ 'type', 'source', 'source-layer',
+ 'minzoom', 'maxzoom', 'filter',
+ 'layout'));
+ },
+
+ json: function() {
+ return util.extend({},
+ this._layer,
+ util.pick(this,
+ 'type', 'source', 'source-layer',
+ 'minzoom', 'maxzoom', 'filter',
+ 'layout', 'paint'));
+ }
+};
+
+function premultiplyLayer(layer, type) {
+ var colorProp = type + '-color',
+ haloProp = type + '-halo-color',
+ outlineProp = type + '-outline-color',
+ color = layer[colorProp],
+ haloColor = layer[haloProp],
+ outlineColor = layer[outlineProp],
+ opacity = layer[type + '-opacity'];
+
+ var colorOpacity = color && (opacity * color[3]);
+ var haloOpacity = haloColor && (opacity * haloColor[3]);
+ var outlineOpacity = outlineColor && (opacity * outlineColor[3]);
+
+ if (colorOpacity !== undefined && colorOpacity < 1) {
+ layer[colorProp] = util.premultiply([color[0], color[1], color[2], colorOpacity]);
+ }
+ if (haloOpacity !== undefined && haloOpacity < 1) {
+ layer[haloProp] = util.premultiply([haloColor[0], haloColor[1], haloColor[2], haloOpacity]);
+ }
+ if (outlineOpacity !== undefined && outlineOpacity < 1) {
+ layer[outlineProp] = util.premultiply([outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity]);
+ }
+}
diff --git a/js/symbol/glyph_source.js b/js/symbol/glyph_source.js
index aa3946349c1..0608661e7be 100644
--- a/js/symbol/glyph_source.js
+++ b/js/symbol/glyph_source.js
@@ -49,7 +49,7 @@ GlyphSource.prototype.getRects = function(fontstack, glyphIDs, tileID, callback)
if (!remaining) callback(undefined, result);
- var onRangeLoaded = (err, range, data) => {
+ var onRangeLoaded = function(err, range, data) {
// TODO not be silent about errors
if (!err) {
var stack = this.stacks[fontstack][range] = data.stacks[fontstack];
@@ -63,7 +63,7 @@ GlyphSource.prototype.getRects = function(fontstack, glyphIDs, tileID, callback)
}
remaining--;
if (!remaining) callback(undefined, result);
- };
+ }.bind(this);
for (var r in missing) {
this.loadRange(fontstack, r, onRangeLoaded);
diff --git a/js/ui/control/attribution.js b/js/ui/control/attribution.js
index bc6b5a7dd51..75744cb7bc5 100644
--- a/js/ui/control/attribution.js
+++ b/js/ui/control/attribution.js
@@ -9,7 +9,7 @@ module.exports = Attribution;
function Attribution() {}
Attribution.prototype = util.inherit(Control, {
- onAdd(map) {
+ onAdd: function(map) {
var className = 'mapboxgl-ctrl-attrib',
container = this._container = DOM.create('div', className, map.container);
@@ -23,7 +23,7 @@ Attribution.prototype = util.inherit(Control, {
return container;
},
- _update() {
+ _update: function() {
var attrObj = {};
for (var id in this._map.sources) {
var source = this._map.sources[id];
@@ -40,7 +40,7 @@ Attribution.prototype = util.inherit(Control, {
this._updateEditLink();
},
- _updateEditLink() {
+ _updateEditLink: function() {
if (this._editLink) {
var center = this._map.getCenter();
this._editLink.href = 'https://www.mapbox.com/map-feedback/#/' +
diff --git a/js/ui/control/control.js b/js/ui/control/control.js
index 8f96b670ebc..0562fb95f11 100644
--- a/js/ui/control/control.js
+++ b/js/ui/control/control.js
@@ -5,14 +5,14 @@ module.exports = Control;
function Control() {}
Control.prototype = {
- addTo(map) {
+ addTo: function(map) {
this._map = map;
this._container = this.onAdd(map);
if (this.opts && this.opts.position) this._container.className += ' mapboxgl-ctrl-' + this.opts.position;
return this;
},
- remove() {
+ remove: function() {
this._container.parentNode.removeChild(this._container);
if (this.onRemove) this.onRemove(this._map);
this._map = null;
diff --git a/js/ui/control/navigation.js b/js/ui/control/navigation.js
index e06a8a27fcb..f7d8648564a 100644
--- a/js/ui/control/navigation.js
+++ b/js/ui/control/navigation.js
@@ -9,7 +9,7 @@ module.exports = Navigation;
function Navigation(opts) { this.opts = opts || {}; }
Navigation.prototype = util.inherit(Control, {
- onAdd(map) {
+ onAdd: function(map) {
if (!this.opts.position) this.opts.position = 'topright';
var className = 'mapboxgl-ctrl-nav';
@@ -37,7 +37,7 @@ Navigation.prototype = util.inherit(Control, {
return container;
},
- _onCompassDown(e) {
+ _onCompassDown: function(e) {
DOM.disableDrag();
document.addEventListener('mousemove', this._onCompassMove);
@@ -47,7 +47,7 @@ Navigation.prototype = util.inherit(Control, {
e.stopPropagation();
},
- _onCompassMove(e) {
+ _onCompassMove: function(e) {
var x = e.screenX,
d = x < 2 ? -5 : // left edge of the screen, continue rotating
x > window.screen.width - 2 ? 5 : // right edge
@@ -59,13 +59,13 @@ Navigation.prototype = util.inherit(Control, {
e.preventDefault();
},
- _onCompassUp() {
+ _onCompassUp: function() {
document.removeEventListener('mousemove', this._onCompassMove);
document.removeEventListener('mouseup', this._onCompassUp);
DOM.enableDrag();
},
- _createButton(className, fn) {
+ _createButton: function(className, fn) {
var a = DOM.create('a', className, this._container);
a.href = '#';
a.addEventListener('click', function(e) {
@@ -80,7 +80,7 @@ Navigation.prototype = util.inherit(Control, {
return a;
},
- _drawNorth() {
+ _drawNorth: function() {
var rad = 20,
width = 8,
center = 26,
diff --git a/js/ui/easings.js b/js/ui/easings.js
index 759466bd8e8..c39ca5d00c5 100644
--- a/js/ui/easings.js
+++ b/js/ui/easings.js
@@ -7,11 +7,11 @@ var LatLngBounds = require('../geo/lat_lng_bounds');
var Point = require('point-geometry');
util.extend(exports, {
- isEasing() {
+ isEasing: function() {
return !!this._abortFn;
},
- stop() {
+ stop: function() {
if (this._abortFn) {
this._abortFn.call(this);
delete this._abortFn;
@@ -22,7 +22,7 @@ util.extend(exports, {
return this;
},
- _ease(frame, finish, options) {
+ _ease: function(frame, finish, options) {
this._finishFn = finish;
this._abortFn = browser.timed(function (t) {
frame.call(this, options.easing(t));
@@ -34,12 +34,12 @@ util.extend(exports, {
}, options.animate === false ? 0 : options.duration, this);
},
- panBy(offset, options) {
+ panBy: function(offset, options) {
this.panTo(this.transform.center, util.extend({offset: Point.convert(offset).mult(-1)}, options));
return this;
},
- panTo(latlng, options) {
+ panTo: function(latlng, options) {
this.stop();
latlng = LatLng.convert(latlng);
@@ -70,7 +70,7 @@ util.extend(exports, {
},
// Zooms to a certain zoom level with easing.
- zoomTo(zoom, options) {
+ zoomTo: function(zoom, options) {
this.stop();
options = util.extend({
@@ -110,25 +110,25 @@ util.extend(exports, {
if (options.duration < 200) {
clearTimeout(this._onZoomEnd);
- this._onZoomEnd = setTimeout(() => {
+ this._onZoomEnd = setTimeout(function() {
this.zooming = false;
this._rerender();
this.fire('moveend');
- }, 200);
+ }.bind(this), 200);
}
return this;
},
- zoomIn(options) {
+ zoomIn: function(options) {
this.zoomTo(this.getZoom() + 1, options);
},
- zoomOut(options) {
+ zoomOut: function(options) {
this.zoomTo(this.getZoom() - 1, options);
},
- rotateTo(bearing, options) {
+ rotateTo: function(bearing, options) {
this.stop();
options = util.extend({
@@ -162,11 +162,11 @@ util.extend(exports, {
return this;
},
- resetNorth(options) {
+ resetNorth: function(options) {
return this.rotateTo(0, util.extend({duration: 1000}, options));
},
- fitBounds(bounds, options) {
+ fitBounds: function(bounds, options) {
options = util.extend({
padding: 0,
@@ -193,7 +193,7 @@ util.extend(exports, {
this.flyTo(center, zoom, 0, options);
},
- easeTo(latlng, zoom, bearing, options) {
+ easeTo: function(latlng, zoom, bearing, options) {
this.stop();
options = util.extend({
@@ -246,7 +246,7 @@ util.extend(exports, {
return this;
},
- flyTo(latlng, zoom, bearing, options) {
+ flyTo: function(latlng, zoom, bearing, options) {
this.stop();
options = util.extend({
@@ -338,7 +338,7 @@ util.extend(exports, {
},
// convert bearing so that it's numerically close to the current one so that it interpolates properly
- _normalizeBearing(bearing, currentBearing) {
+ _normalizeBearing: function(bearing, currentBearing) {
bearing = util.wrap(bearing, -180, 180);
var diff = Math.abs(bearing - currentBearing);
if (Math.abs(bearing - 360 - currentBearing) < diff) bearing -= 360;
@@ -346,7 +346,7 @@ util.extend(exports, {
return bearing;
},
- _updateEasing(duration, zoom, bezier) {
+ _updateEasing: function(duration, zoom, bezier) {
var easing;
if (this.ease) {
diff --git a/js/ui/hash.js b/js/ui/hash.js
index e0652aab875..28e537fbd78 100644
--- a/js/ui/hash.js
+++ b/js/ui/hash.js
@@ -12,21 +12,21 @@ function Hash() {
}
Hash.prototype = {
- addTo(map) {
+ addTo: function(map) {
this._map = map;
window.addEventListener('hashchange', this._onHashChange, false);
this._map.on('moveend', this._updateHash);
return this;
},
- remove() {
+ remove: function() {
window.removeEventListener('hashchange', this._onHashChange, false);
this._map.off('moveend', this._updateHash);
delete this._map;
return this;
},
- _onHashChange() {
+ _onHashChange: function() {
var loc = location.hash.replace('#', '').split('/');
if (loc.length >= 3) {
this._map.setView([+loc[1], +loc[2]], +loc[0], +(loc[3] || 0));
@@ -35,7 +35,7 @@ Hash.prototype = {
return false;
},
- _updateHash() {
+ _updateHash: function() {
var center = this._map.getCenter(),
zoom = this._map.getZoom(),
bearing = this._map.getBearing(),
diff --git a/js/ui/map.js b/js/ui/map.js
index 14067160001..5933651a69f 100644
--- a/js/ui/map.js
+++ b/js/ui/map.js
@@ -84,23 +84,23 @@ util.extend(Map.prototype, {
attributionControl: true
},
- addSource(id, source) {
+ addSource: function(id, source) {
this.style.addSource(id, source);
return this;
},
- removeSource(id) {
+ removeSource: function(id) {
this.style.removeSource(id);
return this;
},
- addControl(control) {
+ addControl: function(control) {
control.addTo(this);
return this;
},
// Set the map's center, zoom, and bearing
- setView(center, zoom, bearing) {
+ setView: function(center, zoom, bearing) {
this.stop();
var tr = this.transform,
@@ -117,35 +117,35 @@ util.extend(Map.prototype, {
.fire('moveend');
},
- setCenter(center) {
+ setCenter: function(center) {
this.setView(center, this.getZoom(), this.getBearing());
},
- setZoom(zoom) {
+ setZoom: function(zoom) {
this.setView(this.getCenter(), zoom, this.getBearing());
},
- setBearing(bearing) {
+ setBearing: function(bearing) {
this.setView(this.getCenter(), this.getZoom(), bearing);
},
- getCenter() { return this.transform.center; },
- getZoom() { return this.transform.zoom; },
- getBearing() { return this.transform.bearing; },
+ getCenter: function() { return this.transform.center; },
+ getZoom: function() { return this.transform.zoom; },
+ getBearing: function() { return this.transform.bearing; },
- addClass(klass, options) {
+ addClass: function(klass, options) {
if (this._classes[klass]) return;
this._classes[klass] = true;
if (this.style) this.style._cascadeClasses(this._classes, options);
},
- removeClass(klass, options) {
+ removeClass: function(klass, options) {
if (!this._classes[klass]) return;
delete this._classes[klass];
if (this.style) this.style._cascadeClasses(this._classes, options);
},
- setClasses(klasses, options) {
+ setClasses: function(klasses, options) {
this._classes = {};
for (var i = 0; i < klasses.length; i++) {
this._classes[klasses[i]] = true;
@@ -153,16 +153,16 @@ util.extend(Map.prototype, {
if (this.style) this.style._cascadeClasses(this._classes, options);
},
- hasClass(klass) {
+ hasClass: function(klass) {
return !!this._classes[klass];
},
- getClasses() {
+ getClasses: function() {
return Object.keys(this._classes);
},
// Detect the map's new width and height and resize it.
- resize() {
+ resize: function() {
var width = 0, height = 0;
if (this.container) {
@@ -185,25 +185,25 @@ util.extend(Map.prototype, {
.fire('moveend');
},
- getBounds() {
+ getBounds: function() {
return new LatLngBounds(
this.transform.pointLocation(new Point(0, 0)),
this.transform.pointLocation(this.transform.size));
},
- project(latlng) {
+ project: function(latlng) {
return this.transform.locationPoint(LatLng.convert(latlng));
},
- unproject(point) {
+ unproject: function(point) {
return this.transform.pointLocation(Point.convert(point));
},
- featuresAt(point, params, callback) {
+ featuresAt: function(point, params, callback) {
this.style.featuresAt(point, params, callback);
return this;
},
- setStyle(style) {
+ setStyle: function(style) {
if (this.style) {
this.style
.off('load', this._onStyleLoad)
@@ -247,7 +247,7 @@ util.extend(Map.prototype, {
return this;
},
- _move (zoom, rotate) {
+ _move: function(zoom, rotate) {
this.update(zoom).fire('move');
@@ -259,14 +259,14 @@ util.extend(Map.prototype, {
// map setup code
- _setupContainer() {
+ _setupContainer: function() {
var id = this.options.container;
var container = this.container = typeof id === 'string' ? document.getElementById(id) : id;
if (container) container.classList.add('mapboxgl-map');
this.canvas = new Canvas(this, container);
},
- _setupPainter() {
+ _setupPainter: function() {
var gl = this.canvas.getWebGLContext();
if (!gl) {
@@ -277,14 +277,14 @@ util.extend(Map.prototype, {
this.painter = new GLPainter(gl, this.transform);
},
- _contextLost(event) {
+ _contextLost: function(event) {
event.preventDefault();
if (this._frameId) {
browser.cancelFrame(this._frameId);
}
},
- _contextRestored() {
+ _contextRestored: function() {
this._setupPainter();
this.resize();
this.update();
@@ -292,7 +292,7 @@ util.extend(Map.prototype, {
// Rendering
- loaded() {
+ loaded: function() {
if (this._styleDirty || this._sourcesDirty)
return false;
if (this.style && !this.style.loaded())
@@ -300,7 +300,7 @@ util.extend(Map.prototype, {
return true;
},
- update(updateStyle) {
+ update: function(updateStyle) {
if (!this.style) return this;
this._styleDirty = this._styleDirty || updateStyle;
@@ -312,17 +312,17 @@ util.extend(Map.prototype, {
},
// Call when a (re-)render of the map is required, e.g. when the user panned or zoomed,f or new data is available.
- render() {
+ render: function() {
if (this.style && this._styleDirty) {
this._styleDirty = false;
- this.style.recalculate(this.transform.zoom);
+ this.style._recalculate(this.transform.zoom);
}
if (this.style && this._sourcesDirty && !this._sourcesDirtyTimeout) {
this._sourcesDirty = false;
- this._sourcesDirtyTimeout = setTimeout(() => {
+ this._sourcesDirtyTimeout = setTimeout(function() {
this._sourcesDirtyTimeout = null;
- }, 50);
+ }.bind(this), 50);
this.style._updateSources(this.transform);
}
@@ -353,7 +353,7 @@ util.extend(Map.prototype, {
return this;
},
- remove() {
+ remove: function() {
if (this._hash) this._hash.remove();
browser.cancelFrame(this._frameId);
clearTimeout(this._sourcesDirtyTimeout);
@@ -361,49 +361,49 @@ util.extend(Map.prototype, {
return this;
},
- _rerender() {
+ _rerender: function() {
if (this.style && !this._frameId) {
this._frameId = browser.frame(this.render);
}
},
- _forwardStyleEvent(e) {
+ _forwardStyleEvent: function(e) {
this.fire('style.' + e.type, util.extend({style: e.target}, e));
},
- _forwardSourceEvent(e) {
+ _forwardSourceEvent: function(e) {
this.fire(e.type, util.extend({style: e.target}, e));
},
- _forwardTileEvent(e) {
+ _forwardTileEvent: function(e) {
this.fire(e.type, util.extend({style: e.target}, e));
},
- _onStyleLoad(e) {
+ _onStyleLoad: function(e) {
this.style._cascade(this._classes, {transition: false});
this._forwardStyleEvent(e);
},
- _onStyleChange(e) {
+ _onStyleChange: function(e) {
this.update(true);
this._forwardStyleEvent(e);
},
- _onSourceAdd(e) {
+ _onSourceAdd: function(e) {
var source = e.source;
if (source.onAdd)
source.onAdd(this);
this._forwardSourceEvent(e);
},
- _onSourceRemove(e) {
+ _onSourceRemove: function(e) {
var source = e.source;
if (source.onRemove)
source.onRemove(this);
this._forwardSourceEvent(e);
},
- _onSourceUpdate(e) {
+ _onSourceUpdate: function(e) {
this.update();
this._forwardSourceEvent(e);
}
diff --git a/js/util/actor.js b/js/util/actor.js
index 81e694d439e..75679dba622 100644
--- a/js/util/actor.js
+++ b/js/util/actor.js
@@ -21,14 +21,14 @@ Actor.prototype.receive = function(message) {
callback(data.error || null, data.data);
} else if (typeof data.id !== 'undefined') {
var id = data.id;
- this.parent[data.type](data.data, (err, data, buffers) => {
+ this.parent[data.type](data.data, function(err, data, buffers) {
this.postMessage({
type: '',
id: String(id),
error: err ? String(err) : null,
data: data
}, buffers);
- });
+ }.bind(this));
} else {
this.parent[data.type](data.data);
}
diff --git a/js/util/browser/dispatcher.js b/js/util/browser/dispatcher.js
index a3644b98ee1..f9a4525a10f 100644
--- a/js/util/browser/dispatcher.js
+++ b/js/util/browser/dispatcher.js
@@ -32,13 +32,13 @@ function Dispatcher(length, parent) {
}
Dispatcher.prototype = {
- broadcast(type, data) {
+ broadcast: function(type, data) {
for (var i = 0; i < this.actors.length; i++) {
this.actors[i].send(type, data);
}
},
- send(type, data, callback, targetID, buffers) {
+ send: function(type, data, callback, targetID, buffers) {
if (typeof targetID !== 'number' || isNaN(targetID)) {
// Use round robin to send requests to web workers.
targetID = this.currentActor = (this.currentActor + 1) % this.actors.length;
@@ -48,7 +48,7 @@ Dispatcher.prototype = {
return targetID;
},
- remove() {
+ remove: function() {
for (var i = 0; i < this.actors.length; i++) {
this.actors[i].target.terminate();
}
diff --git a/js/util/classify_rings.js b/js/util/classify_rings.js
new file mode 100644
index 00000000000..2e5a6e0fbd7
--- /dev/null
+++ b/js/util/classify_rings.js
@@ -0,0 +1,68 @@
+'use strict';
+
+module.exports = classifyRings;
+
+// classifies an array of rings into polygons with outer rings and holes
+
+function classifyRings(rings) {
+ var len = rings.length;
+
+ if (len <= 1) return [rings];
+
+ var i, j,
+ config = new Array(len);
+
+ for (i = 0; i < len; i++) {
+ if (config[i]) continue;
+ config[i] = false;
+ for (j = 0; j < len; j++) {
+ if (i === j || config[j]) continue;
+
+ if (ringPartiallyContains(rings[i], rings[j])) {
+
+ // mark i as outer ring; add j as inner ring
+ config[i] = config[i] || [rings[i]];
+ config[i].push(rings[j]);
+ config[j] = true; // mark j as inner ring
+ }
+ }
+ }
+
+ var polygons = [];
+ for (i = 0; i < len; i++) {
+ if (config[i] === false) polygons.push([rings[i]]);
+ else if (config[i].length) polygons.push(config[i]);
+ }
+
+ return polygons;
+}
+
+function ringPartiallyContains(outer, inner) {
+ var len = inner.length,
+ num = 0,
+ counted = 0;
+
+ for (var i = 0; i < len; i++) {
+ var p = inner[i];
+ if (p[0] === -128 || p[1] === -128 || p[0] === 4224 || p[1] === 4224) continue;
+ counted++;
+ if (ringContains(outer, p)) num++;
+ if (counted >= 10) break;
+ }
+ if (counted === 0) return false;
+ return num / counted >= 0.8;
+}
+
+function ringContains(points, p) {
+ var len = points.length,
+ inside = false,
+ i, j, p1, p2;
+
+ for (i = 0, j = len - 1; i < len; j = i++) {
+ p1 = points[i];
+ p2 = points[j];
+ if (((p1[1] > p[1]) !== (p2[1] > p[1])) &&
+ (p[0] < (p2[0] - p1[0]) * (p[1] - p1[1]) / (p2[1] - p1[1]) + p1[0])) inside = !inside;
+ }
+ return inside;
+}
diff --git a/js/util/dispatcher.js b/js/util/dispatcher.js
index 2660f0cbc8d..a6ebc6da51d 100644
--- a/js/util/dispatcher.js
+++ b/js/util/dispatcher.js
@@ -13,11 +13,11 @@ function MessageBus(addListeners, postListeners) {
}
},
postMessage: function(data) {
- setTimeout(() => {
+ setTimeout(function() {
for (var i = 0; i < postListeners.length; i++) {
postListeners[i]({data: data, target: this.target});
}
- }, 0);
+ }.bind(this), 0);
}
};
}
@@ -36,15 +36,15 @@ function Dispatcher(length, parent) {
}
Dispatcher.prototype = {
- broadcast(type, data) {
+ broadcast: function(type, data) {
this.actor.send(type, data);
},
- send(type, data, callback, targetID, buffers) {
+ send: function(type, data, callback, targetID, buffers) {
this.actor.send(type, data, callback, buffers);
},
- remove() {
+ remove: function() {
// noop
}
};
diff --git a/js/util/evented.js b/js/util/evented.js
index b6acce5092e..44cb15f6341 100644
--- a/js/util/evented.js
+++ b/js/util/evented.js
@@ -3,7 +3,7 @@
var util = require('./util');
module.exports = {
- on(type, fn) {
+ on: function(type, fn) {
this._events = this._events || {};
this._events[type] = this._events[type] || [];
this._events[type].push(fn);
@@ -11,7 +11,7 @@ module.exports = {
return this;
},
- off(type, fn) {
+ off: function(type, fn) {
if (!type) {
// clear all listeners if no arguments specified
delete this._events;
@@ -35,7 +35,7 @@ module.exports = {
return this;
},
- once(type, fn) {
+ once: function(type, fn) {
var wrapper = function(data) {
this.off(type, wrapper);
fn.call(this, data);
@@ -44,7 +44,7 @@ module.exports = {
return this;
},
- fire(type, data) {
+ fire: function(type, data) {
if (!this.listens(type)) return this;
data = util.extend({}, data);
@@ -60,7 +60,7 @@ module.exports = {
return this;
},
- listens(type) {
+ listens: function(type) {
return !!(this._events && this._events[type]);
}
};
diff --git a/js/util/util.js b/js/util/util.js
index 0bcb0480420..df569bab5db 100644
--- a/js/util/util.js
+++ b/js/util/util.js
@@ -41,6 +41,14 @@ exports.wrap = function (n, min, max) {
return n === max ? n : ((n - min) % d + d) % d + min;
};
+exports.coalesce = function() {
+ for (var i = 0; i < arguments.length; i++) {
+ var arg = arguments[i];
+ if (arg !== null && arg !== undefined)
+ return arg;
+ }
+};
+
exports.asyncEach = function (array, fn, callback) {
var remaining = array.length;
if (remaining === 0) return callback();
@@ -58,9 +66,12 @@ exports.keysDifference = function (obj, other) {
return difference;
};
-exports.extend = function (dest, src) {
- for (var i in src) {
- dest[i] = src[i];
+exports.extend = function (dest) {
+ for (var i = 1; i < arguments.length; i++) {
+ var src = arguments[i];
+ for (var k in src) {
+ dest[k] = src[k];
+ }
}
return dest;
};
@@ -138,5 +149,7 @@ exports.debounce = function(fn, time) {
};
exports.bindAll = function(fns, context) {
- fns.forEach((fn) => context[fn] = context[fn].bind(context));
+ fns.forEach(function(fn) {
+ context[fn] = context[fn].bind(context);
+ });
};
diff --git a/package.json b/package.json
index ee5367cf9e9..de1c93f2e10 100644
--- a/package.json
+++ b/package.json
@@ -11,14 +11,14 @@
"dependencies": {
"brfs": "1.2.0",
"csscolorparser": "~1.0.2",
+ "earcut": "^1.1.0",
"envify": "2.0.1",
- "es6ify": "^1.5.1",
"feature-filter": "1.0.0",
"geojson-rewind": "~0.1.0",
"geojson-vt": "^1.0.1",
"gl-matrix": "https://github.com/toji/gl-matrix/archive/v2.2.1.tar.gz",
"glify": "0.4.2",
- "mapbox-gl-style-spec": "6.0.0",
+ "mapbox-gl-style-spec": "git+https://github.com/mapbox/mapbox-gl-style-spec#v7",
"minifyify": "^4.4.0",
"pbf": "^1.2.0",
"pngjs": "^0.4.0",
@@ -33,15 +33,13 @@
"browserify": "~5.9.1",
"istanbul": "^0.3.0",
"jshint": "2.5.10",
- "mapbox-gl-test-suite": "git+https://github.com/mapbox/mapbox-gl-test-suite.git",
+ "mapbox-gl-test-suite": "git+https://github.com/mapbox/mapbox-gl-test-suite.git#v7",
"mkdirp": "^0.5.0",
"mocha": "~1.21.3",
"serve": "^1.4.0",
"spritesmith": "0.19.2",
"st": "^0.5.1",
"tape": "2.14.0",
- "traceur": "0.0.74",
- "traceur-source-maps": "^1.0.6",
"watchify": "1.0.1",
"zuul": "1.10.0"
},
@@ -50,7 +48,6 @@
},
"browserify": {
"transform": [
- "es6ify",
"envify",
"glify",
"brfs"
@@ -72,7 +69,7 @@
"build": "browserify -d js/mapbox-gl.js > dist/mapbox-gl-dev.js",
"production": "browserify js/mapbox-gl.js -d -p [minifyify --map mapbox-gl.js.map --output dist/mapbox-gl.js.map] > dist/mapbox-gl.js",
"prepublish": "npm run build && npm run production",
- "cov": "istanbul cover tape test/js/*/*.js test/render.test.js -x js/lib/glmatrix.js -x js/lib/debugtext.js",
+ "cov": "istanbul cover tape test/js/*/*.js test/render.test.js -x js/lib/debugtext.js",
"docs": "cd docs/_generate && npm install && node generate.js"
},
"jshintConfig": {
diff --git a/test/bootstrap.js b/test/bootstrap.js
deleted file mode 100644
index 1d0ec6ed5af..00000000000
--- a/test/bootstrap.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict';
-
-var traceur = require('traceur');
-
-require('traceur-source-maps').install(traceur);
-
-traceur.require.makeDefault(function(filename) {
- return !(/node_modules/.test(filename));
-}, {
- arrowFunctions: true,
- propertyMethods: true,
- modules: 'commonjs',
- propertyNameShorthand: true,
- asyncFunctions: true
-});
diff --git a/test/expected/style-basic-computed.json b/test/expected/style-basic-computed.json
deleted file mode 100644
index ab4e148534e..00000000000
--- a/test/expected/style-basic-computed.json
+++ /dev/null
@@ -1,97 +0,0 @@
-{
- "background": {
- "background-color": [
- 0.9333333333333333,
- 0.9333333333333333,
- 0.9333333333333333,
- 1
- ]
- },
- "park": {
- "fill-color": [
- 0.7333333333333333,
- 0.8666666666666667,
- 0.6666666666666666,
- 1
- ]
- },
- "water": {
- "fill-color": [
- 0.6,
- 0.6,
- 0.6,
- 1
- ]
- },
- "building": {
- "fill-color": [
- 0.8666666666666667,
- 0.8666666666666667,
- 0.8666666666666667,
- 1
- ],
- "fill-opacity": 0,
- "fill-outline-color": [
- 0.8,
- 0.8,
- 0.8,
- 1
- ],
- "hidden": true
- },
- "borders": {
- "line-color": [
- 0,
- 0,
- 0,
- 0.3
- ],
- "line-width": 1
- },
- "poi": {
- "hidden": true
- },
- "country_label": {
- "text-halo-color": [
- 0.7,
- 0.7,
- 0.7,
- 0.7
- ],
- "text-halo-width": 0.25,
- "text-color": [
- 0,
- 0,
- 0,
- 1
- ],
- "icon-halo-color": [
- 0,
- 0,
- 0,
- 0
- ]
- },
- "road_label": {
- "text-color": [
- 0,
- 0,
- 0,
- 1
- ],
- "text-halo-color": [
- 0.7,
- 0.7,
- 0.7,
- 0.7
- ],
- "text-halo-width": 0.25,
- "text-size": 9.95877695907278,
- "icon-halo-color": [
- 0,
- 0,
- 0,
- 0
- ]
- }
-}
diff --git a/test/expected/style-basic-getlayer.json b/test/expected/style-basic-getlayer.json
deleted file mode 100644
index f0950eca500..00000000000
--- a/test/expected/style-basic-getlayer.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "id": "park",
- "type": "fill",
- "source": "mapbox.mapbox-streets-v5",
- "source-layer": "landuse",
- "filter": [
- "==",
- "class",
- "park"
- ],
- "paint": {
- "fill-color": "@park"
- }
-}
\ No newline at end of file
diff --git a/test/expected/style-basic-layergroups.json b/test/expected/style-basic-layergroups.json
deleted file mode 100644
index dfbbb67f386..00000000000
--- a/test/expected/style-basic-layergroups.json
+++ /dev/null
@@ -1,45 +0,0 @@
-[
- [
- {
- "id": "road_label",
- "bucket": "road_label",
- "type": "symbol"
- },
- {
- "id": "country_label",
- "bucket": "country_label",
- "type": "symbol"
- },
- {
- "id": "poi",
- "bucket": "poi",
- "type": "symbol"
- },
- {
- "id": "borders",
- "bucket": "borders",
- "type": "line"
- },
- {
- "id": "building",
- "bucket": "building",
- "type": "fill"
- },
- {
- "id": "water",
- "bucket": "water",
- "type": "fill"
- },
- {
- "id": "park",
- "bucket": "park",
- "type": "fill"
- }
- ],
- [
- {
- "id": "background",
- "type": "background"
- }
- ]
-]
\ No newline at end of file
diff --git a/test/expected/style-basic-transitions.json b/test/expected/style-basic-transitions.json
deleted file mode 100644
index ce7484619c6..00000000000
--- a/test/expected/style-basic-transitions.json
+++ /dev/null
@@ -1,245 +0,0 @@
-{
- "background": {
- "background-color": {
- "declaration": {
- "value": [
- 0.9333333333333333,
- 0.9333333333333333,
- 0.9333333333333333,
- 1
- ],
- "prop": "background-color",
- "type": "color",
- "json": "\"#eee\""
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- }
- },
- "park": {
- "fill-color": {
- "declaration": {
- "value": [
- 0.7333333333333333,
- 0.8666666666666667,
- 0.6666666666666666,
- 1
- ],
- "prop": "fill-color",
- "type": "color",
- "json": "\"#bda\""
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- }
- },
- "water": {
- "fill-color": {
- "declaration": {
- "value": [
- 0.6,
- 0.6,
- 0.6,
- 1
- ],
- "prop": "fill-color",
- "type": "color",
- "json": "\"#999\""
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- }
- },
- "building": {
- "fill-color": {
- "declaration": {
- "value": [
- 0.8666666666666667,
- 0.8666666666666667,
- 0.8666666666666667,
- 1
- ],
- "prop": "fill-color",
- "type": "color",
- "json": "\"#ddd\""
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- },
- "fill-opacity": {
- "declaration": {
- "prop": "fill-opacity",
- "type": "number",
- "json": "{\"base\":1.01,\"stops\":[[13,0],[14,1]]}"
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- },
- "fill-outline-color": {
- "declaration": {
- "value": [
- 0.8,
- 0.8,
- 0.8,
- 1
- ],
- "prop": "fill-outline-color",
- "type": "color",
- "json": "\"#ccc\""
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- }
- },
- "borders": {
- "line-color": {
- "declaration": {
- "value": [
- 0,
- 0,
- 0,
- 0.3
- ],
- "prop": "line-color",
- "type": "color",
- "json": "\"rgba(0,0,0,0.3)\""
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- },
- "line-width": {
- "declaration": {
- "value": 1,
- "prop": "line-width",
- "type": "number",
- "json": "1"
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- }
- },
- "poi": {},
- "country_label": {
- "text-halo-color": {
- "declaration": {
- "value": [
- 1,
- 1,
- 1,
- 0.7
- ],
- "prop": "text-halo-color",
- "type": "color",
- "json": "\"rgba(255,255,255,0.7)\""
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- },
- "text-halo-width": {
- "declaration": {
- "value": 0.25,
- "prop": "text-halo-width",
- "type": "number",
- "json": "0.25"
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- },
- "text-color": {
- "declaration": {
- "value": [
- 0,
- 0,
- 0,
- 1
- ],
- "prop": "text-color",
- "type": "color",
- "json": "\"#000000\""
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- }
- },
- "road_label": {
- "text-color": {
- "declaration": {
- "value": [
- 0,
- 0,
- 0,
- 1
- ],
- "prop": "text-color",
- "type": "color",
- "json": "\"#000000\""
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- },
- "text-halo-color": {
- "declaration": {
- "value": [
- 1,
- 1,
- 1,
- 0.7
- ],
- "prop": "text-halo-color",
- "type": "color",
- "json": "\"rgba(255,255,255,0.7)\""
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- },
- "text-halo-width": {
- "declaration": {
- "value": 0.25,
- "prop": "text-halo-width",
- "type": "number",
- "json": "0.25"
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- },
- "text-size": {
- "declaration": {
- "prop": "text-size",
- "type": "number",
- "json": "{\"stops\":[[4.770835839035499,8.01],[15.477225251693334,12]]}"
- },
- "endTime": 1402963200000,
- "startTime": 1402963200000,
- "duration": 0,
- "delay": 0
- }
- }
-}
\ No newline at end of file
diff --git a/test/fixtures/style-basic.json b/test/fixtures/style-basic.json
index 04da25269f1..d844b3f8a07 100644
--- a/test/fixtures/style-basic.json
+++ b/test/fixtures/style-basic.json
@@ -1,5 +1,5 @@
{
- "version": 6,
+ "version": 7,
"constants": {
"@land": "#eee",
"@water": "#999",
diff --git a/test/js/buffer/buffer.test.js b/test/js/buffer/buffer.test.js
index e8e9c8b034f..d2f63c7ac7d 100644
--- a/test/js/buffer/buffer.test.js
+++ b/test/js/buffer/buffer.test.js
@@ -1,9 +1,6 @@
'use strict';
var test = require('tape');
-
-require('../../bootstrap');
-
var Buffer = require('../../../js/data/buffer/buffer');
test('Buffer', function(t) {
diff --git a/test/js/buffer/fill_element_buffer.test.js b/test/js/buffer/fill_element_buffer.test.js
index 55f98addafc..a5fee36e322 100644
--- a/test/js/buffer/fill_element_buffer.test.js
+++ b/test/js/buffer/fill_element_buffer.test.js
@@ -1,9 +1,6 @@
'use strict';
var test = require('tape');
-
-require('../../bootstrap');
-
var Buffer = require('../../../js/data/buffer/buffer');
var FillElementsBuffer = require('../../../js/data/buffer/fill_elements_buffer');
diff --git a/test/js/buffer/fill_vertex_buffer.test.js b/test/js/buffer/fill_vertex_buffer.test.js
index 9f64d3b79ec..7d02b605ccc 100644
--- a/test/js/buffer/fill_vertex_buffer.test.js
+++ b/test/js/buffer/fill_vertex_buffer.test.js
@@ -1,9 +1,6 @@
'use strict';
var test = require('tape');
-
-require('../../bootstrap');
-
var Buffer = require('../../../js/data/buffer/buffer');
var FillVertexBuffer = require('../../../js/data/buffer/fill_vertex_buffer');
diff --git a/test/js/buffer/icon_vertex_buffer.test.js b/test/js/buffer/icon_vertex_buffer.test.js
index 2315fa3029c..294ca09a23f 100644
--- a/test/js/buffer/icon_vertex_buffer.test.js
+++ b/test/js/buffer/icon_vertex_buffer.test.js
@@ -1,9 +1,6 @@
'use strict';
var test = require('tape');
-
-require('../../bootstrap');
-
var Buffer = require('../../../js/data/buffer/buffer');
var IconVertexBuffer = require('../../../js/data/buffer/icon_vertex_buffer');
diff --git a/test/js/buffer/line_element_buffer.test.js b/test/js/buffer/line_element_buffer.test.js
index 04da95649cf..b42ebe87421 100644
--- a/test/js/buffer/line_element_buffer.test.js
+++ b/test/js/buffer/line_element_buffer.test.js
@@ -1,9 +1,6 @@
'use strict';
var test = require('tape');
-
-require('../../bootstrap');
-
var Buffer = require('../../../js/data/buffer/buffer');
var LineElementBuffer = require('../../../js/data/buffer/line_element_buffer');
diff --git a/test/js/buffer/line_vertex_buffer.test.js b/test/js/buffer/line_vertex_buffer.test.js
index c895ccf8bfc..447eb1ec9bb 100644
--- a/test/js/buffer/line_vertex_buffer.test.js
+++ b/test/js/buffer/line_vertex_buffer.test.js
@@ -2,9 +2,6 @@
var test = require('tape');
var Point = require('point-geometry');
-
-require('../../bootstrap');
-
var Buffer = require('../../../js/data/buffer/buffer');
var LineVertexBuffer = require('../../../js/data/buffer/line_vertex_buffer');
diff --git a/test/js/data/create_bucket.test.js b/test/js/data/create_bucket.test.js
index 3cade856d6c..322a2cee9c0 100644
--- a/test/js/data/create_bucket.test.js
+++ b/test/js/data/create_bucket.test.js
@@ -1,9 +1,6 @@
'use strict';
var test = require('tape');
-
-require('../../bootstrap');
-
var createBucket = require('../../../js/data/create_bucket');
var BufferSet = require('../../../js/data/buffer/buffer_set');
var LineBucket = require('../../../js/data/line_bucket');
diff --git a/test/js/data/feature_tree.test.js b/test/js/data/feature_tree.test.js
index f245b44180a..805c876f020 100644
--- a/test/js/data/feature_tree.test.js
+++ b/test/js/data/feature_tree.test.js
@@ -4,9 +4,6 @@ var test = require('tape');
var vt = require('vector-tile');
var fs = require('fs');
var Protobuf = require('pbf');
-
-require('../../bootstrap');
-
var FeatureTree = require('../../../js/data/feature_tree');
test('featuretree', function(t) {
@@ -68,20 +65,10 @@ test('featuretree query', function(t) {
return feature.loadGeometry();
}
var ft = new FeatureTree(getGeometry, getType);
- var bucketInfo = {
- 'id': 'water',
- 'interactive': true,
- 'layout': {},
- 'maxzoom': 22,
- 'minzoom': 0,
- 'source': 'mapbox.mapbox-streets-v5',
- 'source-layer': 'water',
- 'type': 'fill'
- };
for (var i=0; i