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 %} + + + + + + + + + + + + + +
OptionDescription
positionA 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