mapbox-gl
Advanced tools
Comparing version 0.5.2 to 0.6.0
23
API.md
@@ -25,2 +25,3 @@ # Mapbox GL JS API Documentation | ||
`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 | ||
@@ -112,12 +113,11 @@ 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): | ||
### 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 properties 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 | ||
@@ -128,2 +128,4 @@ ### Events | ||
----- | ----- | ||
`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.) | ||
@@ -242,3 +244,3 @@ `movestart` | Fired on start of any movement of the map | ||
## new mapboxgl.Navigation() | ||
## new mapboxgl.Navigation(options) | ||
@@ -248,5 +250,8 @@ 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`) | ||
@@ -253,0 +258,0 @@ ## mapboxgl.Evented |
@@ -5,2 +5,34 @@ ## dev | ||
## 0.6.0 (Feb 9 2015) | ||
#### Bugfixes | ||
* Add wrapped padding to sprite for repeating images (#972) | ||
* Clear color buffers before rendering (#966) | ||
* Make line-opacity work with line-image (#970) | ||
* event.toElement fallback for Firefox (#932) | ||
* skip duplicate vertices at ends of lines (#776) | ||
* allow characters outside \w to be used in token | ||
* Clear old tiles when new GeoJSON is loaded (#905) | ||
#### Improvements | ||
* Added `map.setPaintProperty()`, `map.getPaintProperty()`, `map.setLayoutProperty()`, and `map.getLayoutProperty()`. | ||
* Switch to ESLint and more strict code rules (#957) | ||
* Grab 2x raster tiles if retina (#754) | ||
* Support for mapbox:// style URLs (#875) | ||
#### Breaking | ||
* Updated to mapbox-gl-style-spec v7.0.0 ([Changelog](https://github.com/mapbox/mapbox-gl-style-spec/blob/a2b0b561ce16015a1ef400dc870326b1b5255091/CHANGELOG.md)). Styles are | ||
now expected to be version 7. You can use the [gl-style-migrate](https://github.com/mapbox/mapbox-gl-style-lint#migrations) | ||
utility to update existing styles. | ||
* HTTP_URL and HTTPS_URL config options must no longer include a `/v4` path prefix. | ||
* `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) | ||
@@ -7,0 +39,0 @@ |
@@ -47,9 +47,3 @@ { | ||
"@point_translate": [0, -30], | ||
"@tunnel_line_dasharray": [{ | ||
"base": 1.5, | ||
"stops": [[12, 6], [20, 9]] | ||
}, { | ||
"base": 1.5, | ||
"stops": [[12, 2], [20, 3]] | ||
}], | ||
"@tunnel_line_dasharray": [0.1, 0.8], | ||
"@motorway_width": { | ||
@@ -102,9 +96,3 @@ "base": 1.2, | ||
}, | ||
"@path_line_dasharray": [{ | ||
"base": 1.2, | ||
"stops": [[15, 2], [20, 4]] | ||
}, { | ||
"base": 1.2, | ||
"stops": [[15, 1], [20, 2]] | ||
}], | ||
"@path_line_dasharray": [1, 0.5], | ||
"@rail_width": { | ||
@@ -118,3 +106,3 @@ "base": 1.4, | ||
}, | ||
"@rail_hatch_line_dasharray": [2, 30], | ||
"@rail_hatch_line_dasharray": [0.3, 4], | ||
"@admin_level_3_width": { | ||
@@ -740,3 +728,3 @@ "base": 1, | ||
"line-opacity": "@admin-opacity", | ||
"line-dasharray": [10, 3], | ||
"line-dasharray": [5, 1.5], | ||
"line-width": "@admin_level_3_width" | ||
@@ -770,3 +758,3 @@ } | ||
"line-opacity": "@admin-opacity", | ||
"line-dasharray": [4, 4], | ||
"line-dasharray": [2, 2], | ||
"line-width": "@admin_level_2_width" | ||
@@ -784,3 +772,3 @@ } | ||
"line-opacity": "@admin-opacity", | ||
"line-dasharray": [10, 3], | ||
"line-dasharray": [5, 1.5], | ||
"line-width": "@admin_level_3_width" | ||
@@ -787,0 +775,0 @@ } |
@@ -31,3 +31,3 @@ 'use strict'; | ||
setupViews() { | ||
setupViews: function() { | ||
// set up views for each type to add data of different types to the same buffer | ||
@@ -41,3 +41,3 @@ this.ubytes = new Uint8Array(this.array); | ||
// binds the buffer to a webgl context | ||
bind(gl) { | ||
bind: function(gl) { | ||
var type = gl[this.arrayType]; | ||
@@ -56,3 +56,3 @@ if (!this.buffer) { | ||
destroy(gl) { | ||
destroy: function(gl) { | ||
if (this.buffer) { | ||
@@ -64,3 +64,3 @@ 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) { | ||
@@ -67,0 +67,0 @@ |
@@ -16,3 +16,3 @@ 'use strict'; | ||
add(a, b, c) { | ||
add: function(a, b, c) { | ||
var pos2 = this.pos / 2; | ||
@@ -19,0 +19,0 @@ |
@@ -15,3 +15,3 @@ 'use strict'; | ||
add(x, y) { | ||
add: function(x, y) { | ||
var pos2 = this.pos / 2; | ||
@@ -18,0 +18,0 @@ |
@@ -19,3 +19,3 @@ 'use strict'; | ||
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, | ||
@@ -47,3 +47,3 @@ pos2 = pos / 2, | ||
bind(gl, shader) { | ||
bind: function(gl, shader) { | ||
Buffer.prototype.bind.call(this, gl); | ||
@@ -50,0 +50,0 @@ |
@@ -19,3 +19,3 @@ 'use strict'; | ||
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, | ||
@@ -47,3 +47,3 @@ pos2 = pos / 2, | ||
bind(gl, shader) { | ||
bind: function(gl, shader) { | ||
Buffer.prototype.bind.call(this, gl); | ||
@@ -50,0 +50,0 @@ |
@@ -16,3 +16,3 @@ 'use strict'; | ||
add(a, b, c) { | ||
add: function(a, b, c) { | ||
var pos2 = this.pos / 2; | ||
@@ -19,0 +19,0 @@ |
@@ -27,3 +27,3 @@ 'use strict'; | ||
// tx, ty - texture normal | ||
add(point, extrude, tx, ty, linesofar) { | ||
add: function(point, extrude, tx, ty, linesofar) { | ||
var pos = this.pos, | ||
@@ -30,0 +30,0 @@ pos2 = pos / 2, |
@@ -16,3 +16,3 @@ 'use strict'; | ||
add(a, b) { | ||
add: function(a, b) { | ||
var pos2 = this.pos / 2; | ||
@@ -19,0 +19,0 @@ |
@@ -8,27 +8,32 @@ 'use strict'; | ||
var SymbolBucket = require('./symbol_bucket'); | ||
var RasterBucket = require('./raster_bucket'); | ||
var LayoutProperties = require('../style/layout_properties'); | ||
var featureFilter = require('feature-filter'); | ||
var StyleDeclarationSet = require('../style/style_declaration_set'); | ||
function createBucket(layer, buffers, collision, indices) { | ||
function createBucket(layer, buffers, collision, z) { | ||
var values = new StyleDeclarationSet('layout', layer.type, layer.layout, {}).values(), | ||
fakeZoomHistory = { lastIntegerZoom: Infinity, lastIntegerZoomTime: 0, lastZoom: 0 }, | ||
layout = {}; | ||
if (!LayoutProperties[layer.type]) { | ||
//console.warn('unknown bucket type'); | ||
return; | ||
for (var k in values) { | ||
layout[k] = values[k].calculate(z, fakeZoomHistory); | ||
} | ||
var layoutProperties = new LayoutProperties[layer.type](layer.layout); | ||
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); | ||
var bucket = new BucketClass(buffers, new LayoutProperties[layer.type](layout), collision); | ||
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; | ||
} |
@@ -17,4 +17,4 @@ 'use strict'; | ||
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; | ||
@@ -46,3 +46,3 @@ this.toBeInserted.push(bbox); | ||
if (params.bucket && matching[i].info.id !== params.bucket.id) | ||
if (params.layer && matching[i].layers.indexOf(params.layer.id) < 0) | ||
continue; | ||
@@ -54,5 +54,7 @@ if (params.$type && type !== params.$type) | ||
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 | ||
}; | ||
@@ -69,27 +71,6 @@ if (params.geometry) { | ||
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); | ||
} else if (type === 'LineString') { | ||
return lineContainsPoint(rings, p, radius); | ||
} else if (type === 'Polygon') { | ||
return polyContainsPoint(rings, p) ? true : lineContainsPoint(rings, p, radius); | ||
} else { | ||
return false; | ||
} | ||
return type === 'Point' ? pointContainsPoint(rings, p, radius) : | ||
type === 'LineString' ? pointContainsPoint(rings, p, radius) : | ||
type === 'Polygon' ? polyContainsPoint(rings, p) || lineContainsPoint(rings, p, radius) : false; | ||
} | ||
@@ -115,3 +96,3 @@ | ||
// In that case, we treat the line as "containing point p". | ||
var v = ring[j-1], w = ring[j]; | ||
var v = ring[j - 1], w = ring[j]; | ||
if (distToSegmentSquared(p, v, w) < r) return true; | ||
@@ -133,3 +114,3 @@ } | ||
p2 = ring[j]; | ||
if (((p1.y > p.y) != (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) { | ||
if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) { | ||
c = !c; | ||
@@ -136,0 +117,0 @@ } |
@@ -7,6 +7,5 @@ 'use strict'; | ||
function FillBucket(layoutProperties, buffers, placement, elementGroups) { | ||
this.layoutProperties = layoutProperties; | ||
function FillBucket(buffers) { | ||
this.buffers = buffers; | ||
this.elementGroups = elementGroups || new ElementGroups(buffers.fillVertex, buffers.fillElement, buffers.outlineElement); | ||
this.elementGroups = new ElementGroups(buffers.fillVertex, buffers.fillElement, buffers.outlineElement); | ||
} | ||
@@ -79,5 +78,1 @@ | ||
}; | ||
FillBucket.prototype.hasData = function() { | ||
return !!this.elementGroups.current; | ||
}; |
@@ -7,6 +7,6 @@ 'use strict'; | ||
function LineBucket(layoutProperties, buffers, placement, elementGroups) { | ||
function LineBucket(buffers, layoutProperties) { | ||
this.buffers = buffers; | ||
this.elementGroups = new ElementGroups(buffers.lineVertex, buffers.lineElement); | ||
this.layoutProperties = layoutProperties; | ||
this.buffers = buffers; | ||
this.elementGroups = elementGroups || new ElementGroups(buffers.lineVertex, buffers.lineElement); | ||
} | ||
@@ -34,2 +34,9 @@ | ||
LineBucket.prototype.addLine = function(vertices, join, cap, miterLimit, roundLimit) { | ||
var len = vertices.length; | ||
// If the line has duplicate vertices at the end, adjust length to remove them. | ||
while (len > 2 && vertices[len - 1].equals(vertices[len - 2])) { | ||
len--; | ||
} | ||
if (vertices.length < 2) { | ||
@@ -42,4 +49,3 @@ //console.warn('a line must have at least two vertices'); | ||
var len = vertices.length, | ||
firstVertex = vertices[0], | ||
var firstVertex = vertices[0], | ||
lastVertex = vertices[len - 1], | ||
@@ -51,3 +57,3 @@ closed = firstVertex.equals(lastVertex); | ||
// we could be more precies, but it would only save a negligible amount of space | ||
// we could be more precise, but it would only save a negligible amount of space | ||
this.elementGroups.makeRoomFor(len * 4); | ||
@@ -57,3 +63,3 @@ var elementGroup = this.elementGroups.current; | ||
if (len == 2 && closed) { | ||
if (len === 2 && closed) { | ||
// console.warn('a line may not have coincident points'); | ||
@@ -259,5 +265,1 @@ return; | ||
}; | ||
LineBucket.prototype.hasData = function() { | ||
return !!this.elementGroups.current; | ||
}; |
@@ -12,31 +12,16 @@ 'use strict'; | ||
var resolveIcons = require('../symbol/resolve_icons'); | ||
var mergeLines = require('../symbol/mergelines'); | ||
module.exports = SymbolBucket; | ||
var fullRange = [2 * Math.PI , 0]; | ||
var fullRange = [2 * Math.PI, 0]; | ||
function SymbolBucket(layoutProperties, buffers, collision, elementGroups) { | ||
function SymbolBucket(buffers, layoutProperties, collision) { | ||
this.buffers = buffers; | ||
this.elementGroups = { | ||
text: new ElementGroups(buffers.glyphVertex), | ||
icon: new ElementGroups(buffers.iconVertex) | ||
}; | ||
this.layoutProperties = layoutProperties; | ||
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 { | ||
this.elementGroups = { | ||
text: new ElementGroups(buffers.glyphVertex), | ||
icon: new ElementGroups(buffers.iconVertex) | ||
}; | ||
} | ||
} | ||
@@ -89,11 +74,23 @@ | ||
for (var k = 0; k < features.length; k++) { | ||
var geometries = [], | ||
k; | ||
var feature = features[k]; | ||
var text = textFeatures[k]; | ||
var lines = feature.loadGeometry(); | ||
for (k = 0; k < features.length; k++) { | ||
geometries.push(features[k].loadGeometry()); | ||
} | ||
if (layoutProperties['symbol-placement'] === 'line') { | ||
var merged = mergeLines(features, textFeatures, geometries); | ||
geometries = merged.geometries; | ||
features = merged.features; | ||
textFeatures = merged.textFeatures; | ||
} | ||
for (k = 0; k < features.length; k++) { | ||
if (!geometries[k]) continue; | ||
var shaping = false; | ||
if (text) { | ||
shaping = Shaping.shape(text, fontstack, this.stacks, maxWidth, | ||
if (textFeatures[k]) { | ||
shaping = Shaping.shape(textFeatures[k], fontstack, this.stacks, maxWidth, | ||
lineHeight, horizontalAlign, verticalAlign, justify, spacing, textOffset); | ||
@@ -104,7 +101,8 @@ } | ||
if (this.icons && layoutProperties['icon-image']) { | ||
image = this.icons[resolveTokens(feature.properties, layoutProperties['icon-image'])]; | ||
image = this.icons[resolveTokens(features[k].properties, layoutProperties['icon-image'])]; | ||
if (image) { | ||
if (typeof this.elementGroups.sdfIcons === 'undefined') { | ||
this.elementGroups.sdfIcons = image.sdf; | ||
} else if (this.elementGroups.sdfIcons != image.sdf) { | ||
} else if (this.elementGroups.sdfIcons !== image.sdf) { | ||
console.warn('Style sheet warning: Cannot mix SDF and non-SDF icons in one bucket'); | ||
@@ -116,3 +114,3 @@ } | ||
if (!shaping && !image) continue; | ||
this.addFeature(lines, this.stacks, shaping, image); | ||
this.addFeature(geometries[k], this.stacks, shaping, image); | ||
} | ||
@@ -280,3 +278,2 @@ }; | ||
var firstdone = false; | ||
var firsterr; | ||
this.getTextDependencies(tile, actor, done); | ||
@@ -287,3 +284,2 @@ this.getIconDependencies(tile, actor, done); | ||
firstdone = true; | ||
firsterr = err; | ||
} | ||
@@ -302,7 +298,7 @@ }; | ||
icons: icons | ||
}, (err, newicons) => { | ||
}, function(err, newicons) { | ||
if (err) return callback(err); | ||
this.icons = newicons; | ||
callback(); | ||
}); | ||
}.bind(this)); | ||
} else { | ||
@@ -351,5 +347,1 @@ callback(); | ||
}; | ||
SymbolBucket.prototype.hasData = function() { | ||
return !!this.elementGroups.text.current || !!this.elementGroups.icon.current; | ||
}; |
@@ -20,3 +20,3 @@ 'use strict'; | ||
// extend the bounds to contain the given point or bounds | ||
extend(obj) { | ||
extend: function(obj) { | ||
var sw = this._sw, | ||
@@ -54,15 +54,15 @@ ne = this._ne, | ||
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; } | ||
}; | ||
@@ -69,0 +69,0 @@ |
@@ -68,6 +68,6 @@ 'use strict'; | ||
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( | ||
@@ -78,3 +78,3 @@ this.lngX(latlng.lng, worldSize), | ||
unproject(point, worldSize) { | ||
unproject: function(point, worldSize) { | ||
return new LatLng( | ||
@@ -91,7 +91,7 @@ this.yLat(point.y, worldSize), | ||
// 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)); | ||
@@ -101,6 +101,6 @@ 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); | ||
@@ -110,3 +110,3 @@ return 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90; | ||
panBy(offset) { | ||
panBy: function(offset) { | ||
var point = this.centerPoint._add(offset); | ||
@@ -117,3 +117,3 @@ this.center = this.pointLocation(point); | ||
setZoomAround(zoom, center) { | ||
setZoomAround: function(zoom, center) { | ||
var p = this.locationPoint(center), | ||
@@ -126,3 +126,3 @@ p1 = this.size._sub(p), | ||
setBearingAround(bearing, center) { | ||
setBearingAround: function(bearing, center) { | ||
var offset = this.locationPoint(center).sub(this.centerPoint); | ||
@@ -134,3 +134,3 @@ this.panBy(offset); | ||
locationPoint(latlng) { | ||
locationPoint: function(latlng) { | ||
var p = this.project(latlng); | ||
@@ -140,3 +140,3 @@ 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); | ||
@@ -146,3 +146,3 @@ return this.unproject(this.point.sub(p2)); | ||
locationCoordinate(latlng) { | ||
locationCoordinate: function(latlng) { | ||
var k = this.zoomScale(this.tileZoom) / this.worldSize; | ||
@@ -156,3 +156,3 @@ return { | ||
pointCoordinate(tileCenter, p) { | ||
pointCoordinate: function(tileCenter, p) { | ||
var zoomFactor = this.zoomScale(this.zoomFraction), | ||
@@ -169,3 +169,3 @@ kt = this.zoomScale(this.tileZoom - tileCenter.zoom), | ||
_constrain() { | ||
_constrain: function() { | ||
if (!this.center) return; | ||
@@ -172,0 +172,0 @@ |
@@ -0,4 +1,6 @@ | ||
'use strict'; | ||
// Font data From Hershey Simplex Font | ||
// http://paulbourke.net/dataformats/hershey/ | ||
var simplex_font = { | ||
var simplexFont = { | ||
" ": [16, []], | ||
@@ -98,3 +100,3 @@ "!": [10, [5, 21, 5, 7, -1, -1, 5, 2, 4, 1, 5, 0, 6, 1, 5, 2]], | ||
"}": [14, [5, 25, 7, 24, 8, 23, 9, 21, 9, 19, 8, 17, 7, 16, 6, 14, 6, 12, 8, 10, -1, -1, 7, 24, 8, 22, 8, 20, 7, 18, 6, 17, 5, 15, 5, 13, 6, 11, 10, 9, 6, 7, 5, 5, 5, 3, 6, 1, 7, 0, 8, -2, 8, -4, 7, -6, -1, -1, 8, 8, 6, 6, 6, 4, 7, 2, 8, 1, 9, -1, 9, -3, 8, -5, 7, -6, 5, -7]], | ||
"~": [24, [3, 6, 3, 8, 4, 11, 6, 12, 8, 12, 10, 11, 14, 8, 16, 7, 18, 7, 20, 8, 21, 10, -1, -1, 3, 8, 4, 10, 6, 11, 8, 11, 10, 10, 14, 7, 16, 6, 18, 6, 20, 7, 21, 10, 21, 12]], | ||
"~": [24, [3, 6, 3, 8, 4, 11, 6, 12, 8, 12, 10, 11, 14, 8, 16, 7, 18, 7, 20, 8, 21, 10, -1, -1, 3, 8, 4, 10, 6, 11, 8, 11, 10, 10, 14, 7, 16, 6, 18, 6, 20, 7, 21, 10, 21, 12]] | ||
}; | ||
@@ -106,6 +108,6 @@ | ||
var strokes = [], | ||
i, len, j, len2, glyph, data, x, y, prev; | ||
i, len, j, len2, glyph, x, y, prev; | ||
for (i = 0, len = text.length; i < len; i++) { | ||
glyph = simplex_font[text[i]]; | ||
glyph = simplexFont[text[i]]; | ||
if (!glyph) continue; | ||
@@ -112,0 +114,0 @@ prev = null; |
'use strict'; | ||
if (typeof window === 'undefined') { | ||
new (require('./source/worker'))(self); | ||
new (require('./source/worker'))(self); /*eslint no-new: 0*/ | ||
} else { | ||
@@ -6,0 +6,0 @@ // jshint -W079 |
@@ -7,15 +7,13 @@ 'use strict'; | ||
function drawBackground(gl, painter, bucket, layerStyle, posMatrix, params, imageSprite) { | ||
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; | ||
if (image) { | ||
painter.spriteAtlas.setSprite(imageSprite); | ||
} | ||
var imagePosA = image ? painter.spriteAtlas.getPosition(image.from, true) : null; | ||
var imagePosB = image ? painter.spriteAtlas.getPosition(image.to, true) : null; | ||
var imagePos = image ? painter.spriteAtlas.getPosition(image, true) : null; | ||
if (imagePos) { | ||
if (imagePosA && imagePosB) { | ||
// Draw texture fill | ||
@@ -25,23 +23,27 @@ shader = painter.patternShader; | ||
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.uniform2fv(shader.u_pattern_tl_a, imagePosA.tl); | ||
gl.uniform2fv(shader.u_pattern_br_a, imagePosA.br); | ||
gl.uniform2fv(shader.u_pattern_tl_b, imagePosB.tl); | ||
gl.uniform2fv(shader.u_pattern_br_b, imagePosB.br); | ||
gl.uniform1f(shader.u_opacity, opacity); | ||
var transform = painter.transform; | ||
var size = imagePos.size; | ||
var sizeA = imagePosA.size; | ||
var sizeB = imagePosB.size; | ||
var center = transform.locationCoordinate(transform.center); | ||
var scale = 1 / Math.pow(2, transform.zoomFraction); | ||
var matrix = mat3.create(); | ||
mat3.scale(matrix, matrix, [ | ||
1 / size[0], | ||
1 / size[1] | ||
gl.uniform1f(shader.u_mix, image.t); | ||
var matrixA = mat3.create(); | ||
mat3.scale(matrixA, matrixA, [ | ||
1 / (sizeA[0] * image.fromScale), | ||
1 / (sizeA[1] * image.fromScale) | ||
]); | ||
mat3.translate(matrix, matrix, [ | ||
(center.column * transform.tileSize) % size[0], | ||
(center.row * transform.tileSize) % size[1] | ||
mat3.translate(matrixA, matrixA, [ | ||
(center.column * transform.tileSize) % (sizeA[0] * image.fromScale), | ||
(center.row * transform.tileSize) % (sizeA[1] * image.fromScale) | ||
]); | ||
mat3.rotate(matrix, matrix, -transform.angle); | ||
mat3.scale(matrix, matrix, [ | ||
mat3.rotate(matrixA, matrixA, -transform.angle); | ||
mat3.scale(matrixA, matrixA, [ | ||
scale * transform.width / 2, | ||
@@ -51,4 +53,20 @@ -scale * transform.height / 2 | ||
gl.uniformMatrix3fv(shader.u_patternmatrix, false, matrix); | ||
var matrixB = mat3.create(); | ||
mat3.scale(matrixB, matrixB, [ | ||
1 / (sizeB[0] * image.toScale), | ||
1 / (sizeB[1] * image.toScale) | ||
]); | ||
mat3.translate(matrixB, matrixB, [ | ||
(center.column * transform.tileSize) % (sizeB[0] * image.toScale), | ||
(center.row * transform.tileSize) % (sizeB[1] * image.toScale) | ||
]); | ||
mat3.rotate(matrixB, matrixB, -transform.angle); | ||
mat3.scale(matrixB, matrixB, [ | ||
scale * transform.width / 2, | ||
-scale * transform.height / 2 | ||
]); | ||
gl.uniformMatrix3fv(shader.u_patternmatrix_a, false, matrixA); | ||
gl.uniformMatrix3fv(shader.u_patternmatrix_b, false, matrixB); | ||
painter.spriteAtlas.bind(gl, true); | ||
@@ -59,3 +77,3 @@ | ||
shader = painter.fillShader; | ||
gl.switchShader(shader, params.padded || posMatrix); | ||
gl.switchShader(shader, posMatrix); | ||
gl.uniform4fv(shader.u_color, color); | ||
@@ -62,0 +80,0 @@ } |
@@ -5,10 +5,13 @@ 'use strict'; | ||
var browser = require('../util/browser'); | ||
var TileCoord = require('../source/tile_coord'); | ||
module.exports = drawDebug; | ||
function drawDebug(gl, painter, tile, params) { | ||
function drawDebug(gl, painter, tile) { | ||
var pos = TileCoord.fromID(tile.id); | ||
// Blend to the front, not the back. | ||
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); | ||
gl.switchShader(painter.debugShader, painter.tile.posMatrix, painter.tile.exMatrix); | ||
gl.switchShader(painter.debugShader, tile.posMatrix); | ||
@@ -23,3 +26,3 @@ // draw bounding rectangle | ||
// draw tile coordinate | ||
var coord = params.z + '/' + params.x + '/' + params.y; | ||
var coord = pos.z + '/' + pos.x + '/' + pos.y; | ||
@@ -26,0 +29,0 @@ var vertices = textVertices(coord, 50, 200, 5); |
@@ -8,7 +8,12 @@ 'use strict'; | ||
function drawFill(gl, painter, bucket, layerStyle, posMatrix, params, imageSprite) { | ||
function drawFill(painter, layer, posMatrix, tile) { | ||
// No data | ||
if (!tile.buffers) return; | ||
var elementGroups = tile.elementGroups[layer.ref || layer.id]; | ||
if (!elementGroups) return; | ||
var translatedPosMatrix = painter.translateMatrix(posMatrix, params.z, layerStyle['fill-translate'], layerStyle['fill-translate-anchor']); | ||
var gl = painter.gl; | ||
var translatedPosMatrix = painter.translateMatrix(posMatrix, tile.zoom, layer.paint['fill-translate'], layer.paint['fill-translate-anchor']); | ||
var color = layerStyle['fill-color']; | ||
var color = layer.paint['fill-color']; | ||
@@ -41,13 +46,14 @@ var vertex, elements, group, count; | ||
// Draw the actual triangle fan into the stencil buffer. | ||
gl.switchShader(painter.fillShader, translatedPosMatrix, painter.tile.exMatrix); | ||
gl.switchShader(painter.fillShader, translatedPosMatrix); | ||
// 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; | ||
@@ -69,8 +75,8 @@ gl.vertexAttribPointer(painter.fillShader.a_pos, 2, gl.SHORT, false, 4, offset + 0); | ||
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 && params.antialiasing && !(layerStyle['fill-image'] && !strokeColor)) { | ||
gl.switchShader(painter.outlineShader, translatedPosMatrix, painter.tile.exMatrix); | ||
if (layer.paint['fill-antialias'] === true && !(layer.paint['fill-image'] && !strokeColor)) { | ||
gl.switchShader(painter.outlineShader, translatedPosMatrix); | ||
gl.lineWidth(2 * browser.devicePixelRatio); | ||
@@ -96,8 +102,8 @@ | ||
// 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; | ||
@@ -112,12 +118,11 @@ gl.vertexAttribPointer(painter.outlineShader.a_pos, 2, gl.SHORT, false, 4, offset + 0); | ||
var image = layerStyle['fill-image']; | ||
var opacity = layerStyle['fill-opacity'] || 1; | ||
var image = layer.paint['fill-image']; | ||
var opacity = layer.paint['fill-opacity'] || 1; | ||
var shader; | ||
if (image) { | ||
painter.spriteAtlas.setSprite(imageSprite); | ||
// Draw texture fill | ||
var imagePos = painter.spriteAtlas.getPosition(image, true); | ||
if (!imagePos) return; | ||
var imagePosA = painter.spriteAtlas.getPosition(image.from, true); | ||
var imagePosB = painter.spriteAtlas.getPosition(image.to, true); | ||
if (!imagePosA || !imagePosB) return; | ||
@@ -127,17 +132,26 @@ shader = painter.patternShader; | ||
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.uniform2fv(shader.u_pattern_tl_a, imagePosA.tl); | ||
gl.uniform2fv(shader.u_pattern_br_a, imagePosA.br); | ||
gl.uniform2fv(shader.u_pattern_tl_b, imagePosB.tl); | ||
gl.uniform2fv(shader.u_pattern_br_b, imagePosB.br); | ||
gl.uniform1f(shader.u_opacity, opacity); | ||
gl.uniform1f(shader.u_mix, image.t); | ||
var factor = 8 / Math.pow(2, painter.transform.tileZoom - params.z); | ||
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) | ||
var matrixA = mat3.create(); | ||
mat3.scale(matrixA, matrixA, [ | ||
1 / (imagePosA.size[0] * factor * image.fromScale), | ||
1 / (imagePosA.size[1] * factor * image.fromScale) | ||
]); | ||
gl.uniformMatrix3fv(shader.u_patternmatrix, false, matrix); | ||
var matrixB = mat3.create(); | ||
mat3.scale(matrixB, matrixB, [ | ||
1 / (imagePosB.size[0] * factor * image.toScale), | ||
1 / (imagePosB.size[1] * factor * image.toScale) | ||
]); | ||
gl.uniformMatrix3fv(shader.u_patternmatrix_a, false, matrixA); | ||
gl.uniformMatrix3fv(shader.u_patternmatrix_b, false, matrixB); | ||
painter.spriteAtlas.bind(gl, true); | ||
@@ -148,3 +162,3 @@ | ||
shader = painter.fillShader; | ||
gl.switchShader(shader, params.padded || posMatrix); | ||
gl.switchShader(shader, posMatrix); | ||
gl.uniform4fv(shader.u_color, color); | ||
@@ -151,0 +165,0 @@ } |
@@ -5,32 +5,82 @@ 'use strict'; | ||
module.exports = function drawLine(gl, painter, bucket, layerStyle, posMatrix, params, imageSprite) { | ||
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 width = layerStyle['line-width']; | ||
var offset = layerStyle['line-gap-width'] > 0 ? layerStyle['line-gap-width'] / 2 + width / 2 : 0; | ||
var blur = layerStyle['line-blur'] + antialiasing; | ||
var inset = Math.max(-1, offset - width / 2 - antialiasing / 2) + 1; | ||
var outset = offset + width / 2 + antialiasing / 2; | ||
var blur = layer.paint['line-blur'] + antialiasing; | ||
var edgeWidth = layer.paint['line-width'] / 2; | ||
var inset = -1; | ||
var offset = 0; | ||
var shift = 0; | ||
var color = layerStyle['line-color']; | ||
var ratio = painter.transform.scale / (1 << params.z) / 8; | ||
var vtxMatrix = painter.translateMatrix(posMatrix, params.z, layerStyle['line-translate'], layerStyle['line-translate-anchor']); | ||
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; | ||
} | ||
var outset = offset + edgeWidth + antialiasing / 2 + shift; | ||
var color = layer.paint['line-color']; | ||
var ratio = painter.transform.scale / (1 << tile.zoom) / 8; | ||
var vtxMatrix = painter.translateMatrix(posMatrix, tile.zoom, layer.paint['line-translate'], layer.paint['line-translate-anchor']); | ||
var shader; | ||
var image = layerStyle['line-image']; | ||
if (image) { | ||
painter.spriteAtlas.setSprite(imageSprite); | ||
} | ||
var imagePos = image && painter.spriteAtlas.getPosition(image, true); | ||
if (imagePos) { | ||
var factor = 8 / Math.pow(2, painter.transform.tileZoom - params.z); | ||
var dasharray = layer.paint['line-dasharray']; | ||
var image = layer.paint['line-image']; | ||
if (dasharray) { | ||
shader = painter.linesdfpatternShader; | ||
gl.switchShader(shader, vtxMatrix, tile.exMatrix); | ||
gl.uniform2fv(shader.u_linewidth, [ outset, inset ]); | ||
gl.uniform1f(shader.u_ratio, ratio); | ||
gl.uniform1f(shader.u_blur, blur); | ||
gl.uniform4fv(shader.u_color, color); | ||
var posA = painter.lineAtlas.getDash(dasharray.from, layer.layout['line-cap'] === 'round'); | ||
var posB = painter.lineAtlas.getDash(dasharray.to, layer.layout['line-cap'] === 'round'); | ||
painter.lineAtlas.bind(gl); | ||
var patternratio = Math.pow(2, Math.floor(Math.log(painter.transform.scale) / Math.LN2) - tile.zoom) / 8; | ||
var scaleA = [patternratio / posA.width / dasharray.fromScale, -posA.height / 2]; | ||
var gammaA = painter.lineAtlas.width / (dasharray.fromScale * posA.width * 256 * browser.devicePixelRatio) / 2; | ||
var scaleB = [patternratio / posB.width / dasharray.toScale, -posB.height / 2]; | ||
var gammaB = painter.lineAtlas.width / (dasharray.toScale * posB.width * 256 * browser.devicePixelRatio) / 2; | ||
gl.uniform2fv(shader.u_patternscale_a, scaleA); | ||
gl.uniform1f(shader.u_tex_y_a, posA.y); | ||
gl.uniform2fv(shader.u_patternscale_b, scaleB); | ||
gl.uniform1f(shader.u_tex_y_b, posB.y); | ||
gl.uniform1i(shader.u_image, 0); | ||
gl.uniform1f(shader.u_sdfgamma, Math.max(gammaA, gammaB)); | ||
gl.uniform1f(shader.u_mix, dasharray.t); | ||
} else if (image) { | ||
var imagePosA = painter.spriteAtlas.getPosition(image.from, true); | ||
var imagePosB = painter.spriteAtlas.getPosition(image.to, true); | ||
if (!imagePosA || !imagePosB) return; | ||
var factor = 8 / Math.pow(2, painter.transform.tileZoom - tile.zoom); | ||
painter.spriteAtlas.bind(gl, true); | ||
shader = painter.linepatternShader; | ||
gl.switchShader(shader, vtxMatrix, painter.tile.exMatrix); | ||
gl.switchShader(shader, vtxMatrix, tile.exMatrix); | ||
@@ -41,10 +91,14 @@ gl.uniform2fv(shader.u_linewidth, [ outset, inset ]); | ||
gl.uniform2fv(shader.u_pattern_size, [imagePos.size[0] * factor, imagePos.size[1] ]); | ||
gl.uniform2fv(shader.u_pattern_tl, imagePos.tl); | ||
gl.uniform2fv(shader.u_pattern_br, imagePos.br); | ||
gl.uniform1f(shader.u_fade, painter.transform.zoomFraction); | ||
gl.uniform2fv(shader.u_pattern_size_a, [imagePosA.size[0] * factor * image.fromScale, imagePosB.size[1] ]); | ||
gl.uniform2fv(shader.u_pattern_size_b, [imagePosB.size[0] * factor * image.toScale, imagePosB.size[1] ]); | ||
gl.uniform2fv(shader.u_pattern_tl_a, imagePosA.tl); | ||
gl.uniform2fv(shader.u_pattern_br_a, imagePosA.br); | ||
gl.uniform2fv(shader.u_pattern_tl_b, imagePosB.tl); | ||
gl.uniform2fv(shader.u_pattern_br_b, imagePosB.br); | ||
gl.uniform1f(shader.u_fade, image.t); | ||
gl.uniform1f(shader.u_opacity, layer.paint['line-opacity']); | ||
} else { | ||
shader = painter.lineShader; | ||
gl.switchShader(shader, vtxMatrix, painter.tile.exMatrix); | ||
gl.switchShader(shader, vtxMatrix, tile.exMatrix); | ||
@@ -54,15 +108,12 @@ gl.uniform2fv(shader.u_linewidth, [ outset, inset ]); | ||
gl.uniform1f(shader.u_blur, blur); | ||
gl.uniform4fv(shader.u_color, color); | ||
gl.uniform2fv(shader.u_dasharray, layerStyle['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; | ||
@@ -69,0 +120,0 @@ gl.vertexAttribPointer(shader.a_pos, 2, gl.SHORT, false, 8, vtxOffset + 0); |
'use strict'; | ||
var TileCoord = require('../source/tile_coord'); | ||
var PrerenderedTexture = require('./prerendered'); | ||
var mat4 = require('gl-matrix').mat4; | ||
var util = require('../util/util'); | ||
@@ -10,74 +8,30 @@ | ||
function drawRaster(gl, painter, bucket, layerStyle, params, style, layer, tile) { | ||
var texture; | ||
function drawRaster(painter, layer, posMatrix, tile) { | ||
var gl = painter.gl; | ||
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 = bucket.tile; | ||
} | ||
gl.disable(gl.STENCIL_TEST); | ||
var shader = painter.rasterShader; | ||
gl.switchShader(shader, painter.tile.posMatrix, painter.tile.exMatrix); | ||
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'])); | ||
gl.uniform1f(shader.u_brightness_low, layer.paint['raster-brightness-min']); | ||
gl.uniform1f(shader.u_brightness_high, layer.paint['raster-brightness-max']); | ||
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, opacities; | ||
if (layer && layer.layers) { | ||
parentTile = null; | ||
opacities = [layerStyle['raster-opacity'], 0]; | ||
} else { | ||
parentTile = texture.source && texture.source._findLoadedParent(texture.id, 0, {}); | ||
opacities = getOpacities(texture, parentTile, layerStyle, painter.transform); | ||
} | ||
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); | ||
texture.bind(gl); | ||
gl.bindTexture(gl.TEXTURE_2D, tile.texture); | ||
if (parentTile) { | ||
gl.activeTexture(gl.TEXTURE1); | ||
parentTile.bind(gl); | ||
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); | ||
@@ -90,8 +44,6 @@ parentScaleBy = Math.pow(2, parentPos.z - tilePos.z); | ||
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]); | ||
@@ -102,3 +54,3 @@ gl.uniform1f(shader.u_opacity1, opacities[1]); | ||
gl.bindBuffer(gl.ARRAY_BUFFER, texture.boundsBuffer || painter.tileExtentBuffer); | ||
gl.bindBuffer(gl.ARRAY_BUFFER, tile.boundsBuffer || painter.tileExtentBuffer); | ||
@@ -135,3 +87,3 @@ gl.vertexAttribPointer(shader.a_pos, 2, gl.SHORT, false, 8, 0); | ||
function getOpacities(tile, parentTile, layerStyle, transform) { | ||
function getOpacities(tile, parentTile, layer, transform) { | ||
if (!tile.source) return [1, 0]; | ||
@@ -141,3 +93,3 @@ | ||
var fadeDuration = layerStyle['raster-fade-duration']; | ||
var fadeDuration = layer.paint['raster-fade-duration']; | ||
var sinceTile = (now - tile.timeAdded) / fadeDuration; | ||
@@ -149,3 +101,3 @@ var sinceParent = parentTile ? (now - parentTile.timeAdded) / fadeDuration : -1; | ||
var idealZ = tile.source._coveringZoomLevel(transform); | ||
var idealZ = tile.source._pyramid.coveringZoomLevel(transform); | ||
var parentFurther = parentTile ? Math.abs(parentPos.z - idealZ) > Math.abs(tilePos.z - idealZ) : false; | ||
@@ -164,3 +116,3 @@ | ||
var op = layerStyle['raster-opacity']; | ||
var op = layer.paint['raster-opacity']; | ||
opacity[0] *= op; | ||
@@ -167,0 +119,0 @@ opacity[1] *= op; |
@@ -8,9 +8,15 @@ 'use strict'; | ||
function drawSymbols(gl, painter, bucket, layerStyle, posMatrix, params, imageSprite) { | ||
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, posMatrix, params, imageSprite, '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, posMatrix, params, imageSprite, 'icon'); | ||
if (elementGroups.icon.groups.length) { | ||
drawSymbol(painter, layer, posMatrix, tile, elementGroups.icon, 'icon', elementGroups.sdfIcons); | ||
} | ||
@@ -25,10 +31,9 @@ gl.enable(gl.STENCIL_TEST); | ||
function drawSymbol(gl, painter, bucket, layerStyle, posMatrix, params, imageSprite, prefix) { | ||
function drawSymbol(painter, layer, posMatrix, tile, elementGroups, prefix, sdf) { | ||
var gl = painter.gl; | ||
posMatrix = painter.translateMatrix(posMatrix, params.z, layerStyle[prefix + '-translate'], layerStyle[prefix + '-translate-anchor']); | ||
posMatrix = painter.translateMatrix(posMatrix, tile.zoom, layer.paint[prefix + '-translate'], layer.paint[prefix + '-translate-anchor']); | ||
var layoutProperties = bucket.layoutProperties; | ||
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); | ||
@@ -40,4 +45,4 @@ | ||
// 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]; | ||
@@ -47,6 +52,5 @@ mat4.scale(exMatrix, exMatrix, [ fontScale, fontScale, 1 ]); | ||
var text = prefix === 'text'; | ||
var sdf = text || bucket.elementGroups.sdfIcons; | ||
var shader, buffer, texsize; | ||
if (!text && !imageSprite.loaded()) | ||
if (!text && !painter.style.sprite.loaded()) | ||
return; | ||
@@ -64,8 +68,8 @@ | ||
painter.glyphAtlas.updateTexture(gl); | ||
buffer = bucket.buffers.glyphVertex; | ||
buffer = tile.buffers.glyphVertex; | ||
texsize = [painter.glyphAtlas.width / 4, painter.glyphAtlas.height / 4]; | ||
} else { | ||
painter.spriteAtlas.setSprite(imageSprite); | ||
painter.spriteAtlas.bind(gl, alignedWithMap || params.rotating || params.zooming || fontScale != 1 || sdf); | ||
buffer = bucket.buffers.iconVertex; | ||
painter.spriteAtlas.bind(gl, alignedWithMap || painter.options.rotating || | ||
painter.options.zooming || fontScale !== 1 || sdf); | ||
buffer = tile.buffers.iconVertex; | ||
texsize = [painter.spriteAtlas.width / 4, painter.spriteAtlas.height / 4]; | ||
@@ -84,5 +88,5 @@ } | ||
// 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); | ||
@@ -98,4 +102,4 @@ gl.uniform1f(shader.u_angle, (angle + 256) % 256); | ||
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; | ||
@@ -109,17 +113,17 @@ if (sdf) { | ||
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); | ||
} | ||
} |
@@ -8,7 +8,7 @@ 'use strict'; | ||
function drawVertices(gl, painter, bucket) { | ||
function drawVertices(gl, painter, bucket, tile) { | ||
// Blend to the front, not the back. | ||
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); | ||
gl.switchShader(painter.dotShader, painter.tile.posMatrix, painter.tile.exMatrix); | ||
gl.switchShader(painter.dotShader, tile.posMatrix); | ||
@@ -38,6 +38,6 @@ // // Draw debug points. | ||
var newPosMatrix = mat4.clone(painter.tile.posMatrix); | ||
var newPosMatrix = mat4.clone(tile.posMatrix); | ||
mat4.scale(newPosMatrix, newPosMatrix, [0.5, 0.5, 1]); | ||
gl.switchShader(painter.dotShader, newPosMatrix, painter.tile.exMatrix); | ||
gl.switchShader(painter.dotShader, newPosMatrix); | ||
@@ -44,0 +44,0 @@ // Draw all line buffers |
@@ -15,3 +15,3 @@ 'use strict'; | ||
context.getShader = function(name, type) { | ||
var kind = type == this.FRAGMENT_SHADER ? 'fragment' : 'vertex'; | ||
var kind = type === this.FRAGMENT_SHADER ? 'fragment' : 'vertex'; | ||
if (!shaders[name] || !shaders[name][kind]) { | ||
@@ -18,0 +18,0 @@ throw new Error("Could not find shader " + name); |
@@ -8,10 +8,2 @@ 'use strict'; | ||
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'); | ||
/* | ||
@@ -80,8 +72,12 @@ * Initialize a new painter object. | ||
['a_pos', 'a_data'], | ||
['u_matrix', 'u_exmatrix', 'u_linewidth', 'u_color', 'u_ratio', 'u_dasharray', 'u_blur']); | ||
['u_matrix', 'u_exmatrix', 'u_linewidth', 'u_color', 'u_ratio', 'u_blur']); | ||
this.linepatternShader = gl.initializeShader('linepattern', | ||
['a_pos', 'a_data'], | ||
['u_matrix', 'u_exmatrix', 'u_linewidth', 'u_ratio', 'u_pattern_size', 'u_pattern_tl', 'u_pattern_br', 'u_point', 'u_blur', 'u_fade']); | ||
['u_matrix', 'u_exmatrix', 'u_linewidth', 'u_ratio', 'u_pattern_size_a', 'u_pattern_size_b', 'u_pattern_tl_a', 'u_pattern_br_a', 'u_pattern_tl_b', 'u_pattern_br_b', 'u_blur', 'u_fade', 'u_opacity']); | ||
this.linesdfpatternShader = gl.initializeShader('linesdfpattern', | ||
['a_pos', 'a_data'], | ||
['u_matrix', 'u_exmatrix', 'u_linewidth', 'u_color', 'u_ratio', 'u_blur', 'u_patternscale_a', 'u_tex_y_a', 'u_patternscale_b', 'u_tex_y_b', 'u_image', 'u_sdfgamma', 'u_mix']); | ||
this.dotShader = gl.initializeShader('dot', | ||
@@ -106,3 +102,3 @@ ['a_pos'], | ||
['a_pos'], | ||
['u_matrix', 'u_pattern_tl', 'u_pattern_br', 'u_mix', 'u_patternmatrix', 'u_opacity', 'u_image'] | ||
['u_matrix', 'u_pattern_tl_a', 'u_pattern_br_a', 'u_pattern_tl_b', 'u_pattern_br_b', 'u_mix', 'u_patternmatrix_a', 'u_patternmatrix_b', 'u_opacity', 'u_image'] | ||
); | ||
@@ -169,5 +165,5 @@ | ||
GLPainter.prototype.drawClippingMask = function() { | ||
GLPainter.prototype.drawClippingMask = function(tile) { | ||
var gl = this.gl; | ||
gl.switchShader(this.fillShader, this.tile.posMatrix, this.tile.exMatrix); | ||
gl.switchShader(this.fillShader, tile.posMatrix); | ||
gl.colorMask(false, false, false, false); | ||
@@ -208,19 +204,33 @@ | ||
GLPainter.prototype.render = function(style) { | ||
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; | ||
this.lineAtlas = style.lineAtlas; | ||
this.spriteAtlas = style.spriteAtlas; | ||
this.spriteAtlas.setSprite(style.sprite); | ||
this.glyphAtlas = style.glyphAtlas; | ||
this.glyphAtlas.bind(this.gl); | ||
this.frameHistory.record(this.transform.zoom); | ||
this.prepareBuffers(); | ||
this.clearColor(); | ||
var i, len, group, source; | ||
for (var i = style._groups.length - 1; i >= 0; i--) { | ||
var group = style._groups[i]; | ||
var source = style.sources[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]; | ||
if (source) { | ||
@@ -231,3 +241,3 @@ this.clearStencil(); | ||
} else if (group.source === undefined) { | ||
this.draw(undefined, style, group, { background: true }); | ||
this.drawLayers(group, this.identityMatrix); | ||
} | ||
@@ -237,61 +247,23 @@ } | ||
GLPainter.prototype.draw = function glPainterDraw(tile, style, layers, params) { | ||
this.tile = tile; | ||
GLPainter.prototype.drawTile = function(tile, layers) { | ||
this.drawClippingMask(tile); | ||
this.drawLayers(layers, tile.posMatrix, tile); | ||
if (tile) { | ||
this.drawClippingMask(); | ||
if (this.options.debug) { | ||
draw.debug(this, tile); | ||
} | ||
this.frameHistory.record(this.transform.zoom); | ||
this.drawLayers(tile, style, layers, params); | ||
if (params.debug) { | ||
drawDebug(this.gl, this, tile, params); | ||
} | ||
}; | ||
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.drawLayers = function(layers, matrix, tile) { | ||
for (var i = layers.length - 1; i >= 0; i--) { | ||
var layer = layers[i]; | ||
GLPainter.prototype.drawLayer = function(tile, style, layer, params, matrix, buckets) { | ||
var gl = this.gl; | ||
if (layer.hidden) | ||
continue; | ||
var layerStyle = style.computed[layer.id]; | ||
if (!layerStyle || layerStyle.hidden) return; | ||
draw[layer.type](this, layer, matrix, tile); | ||
if (layer.layers && layer.type === 'raster') { | ||
drawRaster(gl, this, buckets[layer.bucket], layerStyle, params, style, layer, tile); | ||
} else if (params.background) { | ||
drawBackground(gl, this, undefined, layerStyle, this.identityMatrix, params, style.sprite); | ||
} 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) { | ||
var useMatrix = matrix || this.tile.posMatrix; | ||
draw(gl, this, bucket, layerStyle, useMatrix, params, style.sprite); | ||
} else { | ||
console.warn('No bucket type specified'); | ||
if (this.options.vertices) { | ||
draw.vertices(this, layer, matrix, tile); | ||
} | ||
if (params.vertices && !layer.layers) { | ||
drawVertices(gl, this, bucket); | ||
} | ||
} | ||
@@ -324,7 +296,7 @@ }; | ||
if (anchor === 'viewport') { | ||
var sin_a = Math.sin(-this.transform.angle); | ||
var cos_a = Math.cos(-this.transform.angle); | ||
var sinA = Math.sin(-this.transform.angle); | ||
var cosA = Math.cos(-this.transform.angle); | ||
translate = [ | ||
translate[0] * cos_a - translate[1] * sin_a, | ||
translate[0] * sin_a + translate[1] * cos_a | ||
translate[0] * cosA - translate[1] * sinA, | ||
translate[0] * sinA + translate[1] * cosA | ||
]; | ||
@@ -331,0 +303,0 @@ } |
@@ -12,2 +12,3 @@ 'use strict'; | ||
"linepattern": glify('../../shaders/linepattern.*.glsl'), | ||
"linesdfpattern": glify('../../shaders/linesdfpattern.*.glsl'), | ||
"outline": glify('../../shaders/outline.*.glsl'), | ||
@@ -14,0 +15,0 @@ "pattern": glify('../../shaders/pattern.*.glsl'), |
'use strict'; | ||
var util = require('../util/util'), | ||
Source = require('./source'), | ||
Cache = require('../util/mru_cache'); | ||
var util = require('../util/util'); | ||
var Evented = require('../util/evented'); | ||
var TilePyramid = require('./tile_pyramid'); | ||
var Source = require('./source'); | ||
@@ -12,3 +13,2 @@ module.exports = GeoJSONSource; | ||
this._isGeoJSON = true; | ||
this._data = options.data; | ||
@@ -18,16 +18,21 @@ | ||
// TODO deduplicate with Source | ||
this._tiles = {}; | ||
this._cache = new Cache(this.cacheSize, function(tile) { | ||
tile.remove(); | ||
this._pyramid = new TilePyramid({ | ||
tileSize: 512, | ||
minzoom: this.minzoom, | ||
maxzoom: this.maxzoom, | ||
cacheSize: 20, | ||
load: this._loadTile.bind(this), | ||
abort: this._abortTile.bind(this), | ||
unload: this._unloadTile.bind(this), | ||
add: this._addTile.bind(this), | ||
remove: this._removeTile.bind(this) | ||
}); | ||
} | ||
GeoJSONSource.prototype = util.inherit(Source, { | ||
GeoJSONSource.prototype = util.inherit(Evented, { | ||
minzoom: 0, | ||
maxzoom: 14, | ||
type: 'vector', | ||
_dirty: true, | ||
setData(data) { | ||
setData: function(data) { | ||
this._data = data; | ||
@@ -39,8 +44,28 @@ this._dirty = true; | ||
update() { | ||
if (this._dirty) this._updateData(); | ||
if (this._loaded) this._updateTiles(); | ||
onAdd: function(map) { | ||
this.map = map; | ||
}, | ||
_updateData() { | ||
loaded: function() { | ||
return this._loaded && this._pyramid.loaded(); | ||
}, | ||
update: function(transform) { | ||
if (this._dirty) { | ||
this._updateData(); | ||
} | ||
if (this._loaded) { | ||
this._pyramid.update(this.used, transform); | ||
} | ||
}, | ||
reload: function() { | ||
this._updateData(); | ||
}, | ||
render: Source._renderTiles, | ||
featuresAt: Source._vectorFeaturesAt, | ||
_updateData: function() { | ||
this._dirty = false; | ||
@@ -52,3 +77,3 @@ this.workerID = this.dispatcher.send('parse geojson', { | ||
maxZoom: this.maxzoom | ||
}, (err) => { | ||
}, function(err) { | ||
if (err) { | ||
@@ -59,5 +84,49 @@ this.fire('error', {error: err}); | ||
this._loaded = true; | ||
this._pyramid.clearTiles(); | ||
this.fire('change'); | ||
}); | ||
}.bind(this)); | ||
}, | ||
_loadTile: function(tile) { | ||
var params = { | ||
id: tile.uid, | ||
tileId: tile.id, | ||
zoom: tile.zoom, | ||
maxZoom: this.maxzoom, | ||
tileSize: this.tileSize, | ||
source: this.id, | ||
depth: tile.zoom >= this.maxzoom ? this.map.options.maxZoom - tile.zoom : 1 | ||
}; | ||
tile.workerID = this.dispatcher.send('load geojson tile', params, function(err, data) { | ||
if (tile.aborted) | ||
return; | ||
if (err) { | ||
this.fire('tile.error', {tile: tile}); | ||
return; | ||
} | ||
tile.loadVectorData(data); | ||
this.fire('tile.load', {tile: tile}); | ||
}.bind(this), this.workerID); | ||
}, | ||
_abortTile: function(tile) { | ||
tile.aborted = true; | ||
}, | ||
_addTile: function(tile) { | ||
this.fire('tile.add', {tile: tile}); | ||
}, | ||
_removeTile: function(tile) { | ||
this.fire('tile.remove', {tile: 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); | ||
} | ||
}); |
'use strict'; | ||
var util = require('../util/util'), | ||
ajax = require('../util/ajax'), | ||
browser = require('../util/browser'), | ||
normalizeURL = require('../util/mapbox').normalizeSourceURL, | ||
Evented = require('../util/evented'), | ||
Cache = require('../util/mru_cache'), | ||
TileCoord = require('./tile_coord'), | ||
Tile = require('./tile'), | ||
Point = require('point-geometry'); | ||
var util = require('../util/util'); | ||
var ajax = require('../util/ajax'); | ||
var browser = require('../util/browser'); | ||
var TileCoord = require('./tile_coord'); | ||
var TilePyramid = require('./tile_pyramid'); | ||
var normalizeURL = require('../util/mapbox').normalizeSourceURL; | ||
module.exports = Source; | ||
function Source(options) { | ||
util.extend(this, util.pick(options, | ||
'type', 'url', 'tileSize')); | ||
if (this.type === 'vector' && this.tileSize !== 512) { | ||
throw new Error('vector tile sources must have a tileSize of 512'); | ||
} | ||
this._tiles = {}; | ||
this._cache = new Cache(this.cacheSize, function(tile) { | ||
tile.remove(); | ||
}); | ||
var loaded = (err, tileJSON) => { | ||
exports._loadTileJSON = function(options) { | ||
var loaded = function(err, tileJSON) { | ||
if (err) { | ||
@@ -37,311 +20,71 @@ this.fire('error', {error: err}); | ||
this._loaded = true; | ||
this._pyramid = new TilePyramid({ | ||
tileSize: this.tileSize, | ||
cacheSize: 20, | ||
minzoom: this.minzoom, | ||
maxzoom: this.maxzoom, | ||
load: this._loadTile.bind(this), | ||
abort: this._abortTile.bind(this), | ||
unload: this._unloadTile.bind(this), | ||
add: this._addTile.bind(this), | ||
remove: this._removeTile.bind(this) | ||
}); | ||
this.fire('load'); | ||
}; | ||
}.bind(this); | ||
if (this.url) { | ||
ajax.getJSON(normalizeURL(this.url), loaded); | ||
if (options.url) { | ||
ajax.getJSON(normalizeURL(options.url), loaded); | ||
} else { | ||
browser.frame(loaded.bind(this, null, options)); | ||
} | ||
} | ||
}; | ||
Source.prototype = util.inherit(Evented, { | ||
minzoom: 0, | ||
maxzoom: 22, | ||
tileSize: 512, | ||
cacheSize: 20, | ||
_loaded: false, | ||
exports._renderTiles = function(layers, painter) { | ||
if (!this._pyramid) | ||
return; | ||
onAdd(map) { | ||
this.map = map; | ||
}, | ||
var ids = this._pyramid.renderedIDs(); | ||
for (var i = 0; i < ids.length; i++) { | ||
var pos = TileCoord.fromID(ids[i]), | ||
tile = this._pyramid.getTile(ids[i]), | ||
z = pos.z, | ||
x = pos.x, | ||
y = pos.y, | ||
w = pos.w; | ||
loaded() { | ||
if (!this._loaded) { | ||
return false; | ||
} | ||
for (var t in this._tiles) { | ||
if (!this._tiles[t].loaded) | ||
return false; | ||
} | ||
return true; | ||
}, | ||
render(layers, painter) { | ||
// Iteratively paint every tile. | ||
if (!this._loaded) return; | ||
var order = Object.keys(this._tiles); | ||
order.sort(zOrder); | ||
for (var i = 0; i < order.length; i++) { | ||
var id = order[i]; | ||
var tile = this._tiles[id]; | ||
if (tile.loaded && !this.coveredTiles[id]) { | ||
this._renderTile(tile, id, layers, painter); | ||
} | ||
} | ||
}, | ||
// Given a tile of data, its id, and a style layers, render the tile to the canvas | ||
_renderTile(tile, id, layers, painter) { | ||
var pos = TileCoord.fromID(id); | ||
var z = pos.z, x = pos.x, y = pos.y, w = pos.w; | ||
x += w * (1 << z); | ||
tile.calculateMatrices(z, x, y, painter.transform, painter); | ||
tile.calculateMatrices(z, x, y, this.map.transform, painter); | ||
painter.drawTile(tile, layers); | ||
} | ||
}; | ||
painter.draw(tile, this.map.style, layers, { | ||
z: z, x: x, y: y, | ||
debug: this.map.debug, | ||
antialiasing: this.map.antialiasing, | ||
vertices: this.map.vertices, | ||
rotating: this.map.rotating, | ||
zooming: this.map.zooming | ||
}); | ||
}, | ||
exports._vectorFeaturesAt = function(point, params, callback) { | ||
if (!this._pyramid) | ||
return callback(null, []); | ||
featuresAt(point, params, callback) { | ||
var order = Object.keys(this._tiles); | ||
order.sort(zOrder); | ||
for (var i = 0; i < order.length; i++) { | ||
var id = order[i]; | ||
var tile = this._tiles[id]; | ||
var pos = tile.positionAt(id, point); | ||
var result = this._pyramid.tileAt(point); | ||
if (!result) | ||
return callback(null, []); | ||
if (pos && pos.x >= 0 && pos.x < 4096 && pos.y >= 0 && pos.y < 4096) { | ||
// The click is within the viewport. There is only ever one tile in | ||
// a layer that has this property. | ||
return tile.featuresAt(pos, params, callback); | ||
} | ||
} | ||
callback(null, []); | ||
}, | ||
// get the zoom level adjusted for the difference in map and source tilesizes | ||
_getZoom(transform) { | ||
return transform.zoom + Math.log(transform.tileSize / this.tileSize) / Math.LN2; | ||
}, | ||
_coveringZoomLevel(transform) { | ||
return Math.floor(this._getZoom(transform)); | ||
}, | ||
_getCoveringTiles(transform) { | ||
if (!this.used) return []; | ||
var z = this._coveringZoomLevel(transform); | ||
if (z < this.minzoom) return []; | ||
if (z > this.maxzoom) z = this.maxzoom; | ||
var tr = transform, | ||
tileCenter = TileCoord.zoomTo(tr.locationCoordinate(tr.center), z), | ||
centerPoint = new Point(tileCenter.column - 0.5, tileCenter.row - 0.5); | ||
return TileCoord.cover(z, [ | ||
TileCoord.zoomTo(tr.pointCoordinate(tileCenter, {x: 0, y: 0}), z), | ||
TileCoord.zoomTo(tr.pointCoordinate(tileCenter, {x: tr.width, y: 0}), z), | ||
TileCoord.zoomTo(tr.pointCoordinate(tileCenter, {x: tr.width, y: tr.height}), z), | ||
TileCoord.zoomTo(tr.pointCoordinate(tileCenter, {x: 0, y: tr.height}), z) | ||
]).sort(function(a, b) { | ||
return centerPoint.dist(TileCoord.fromID(a)) - | ||
centerPoint.dist(TileCoord.fromID(b)); | ||
}); | ||
}, | ||
// 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) { | ||
var complete = true; | ||
var z = TileCoord.fromID(id).z; | ||
var ids = TileCoord.children(id); | ||
for (var i = 0; i < ids.length; i++) { | ||
if (this._tiles[ids[i]] && this._tiles[ids[i]].loaded) { | ||
retain[ids[i]] = true; | ||
} else { | ||
complete = false; | ||
if (z < maxCoveringZoom) { | ||
// Go further down the hierarchy to find more unloaded children. | ||
this._findLoadedChildren(ids[i], maxCoveringZoom, retain); | ||
} | ||
} | ||
} | ||
return complete; | ||
}, | ||
// 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) { | ||
for (var z = TileCoord.fromID(id).z; z >= minCoveringZoom; z--) { | ||
id = TileCoord.parent(id); | ||
var tile = this._tiles[id]; | ||
if (tile && tile.loaded) { | ||
retain[id] = true; | ||
return tile; | ||
} | ||
} | ||
}, | ||
update() { | ||
if (this._loaded) this._updateTiles(); | ||
}, | ||
// Removes tiles that are outside the viewport and adds new tiles that are inside the viewport. | ||
_updateTiles() { | ||
var i; | ||
var id; | ||
var complete; | ||
var tile; | ||
var transform = this.map.transform; | ||
// Determine the overzooming/underzooming amounts. | ||
var zoom = Math.floor(this._getZoom(transform)); | ||
var minCoveringZoom = util.clamp(zoom - 10, this.minzoom, this.maxzoom); | ||
var maxCoveringZoom = util.clamp(zoom + 1, this.minzoom, this.maxzoom); | ||
// Retain is a list of tiles that we shouldn't delete, even if they are not | ||
// the most ideal tile for the current viewport. This may include tiles like | ||
// parent or child tiles that are *already* loaded. | ||
var retain = {}; | ||
// Covered is a list of retained tiles who's areas are full covered by other, | ||
// better, retained tiles. They are not drawn separately. | ||
this.coveredTiles = {}; | ||
var fullyComplete = true; | ||
// Add existing child/parent tiles if the actual tile is not yet loaded | ||
var required = this._getCoveringTiles(transform); | ||
for (i = 0; i < required.length; i++) { | ||
id = +required[i]; | ||
retain[id] = true; | ||
tile = this._addTile(id); | ||
if (!tile.loaded) { | ||
// The tile we require is not yet loaded. Try to find a parent or | ||
// child tile that we already have. | ||
// First, try to find existing child tiles that completely cover the | ||
// missing tile. | ||
complete = this._findLoadedChildren(id, maxCoveringZoom, retain); | ||
// Then, if there are no complete child tiles, try to find existing | ||
// parent tiles that completely cover the missing tile. | ||
if (!complete) { | ||
complete = this._findLoadedParent(id, minCoveringZoom, retain); | ||
} | ||
// The unloaded tile's area is not completely covered loaded tiles | ||
if (!complete) { | ||
fullyComplete = false; | ||
} | ||
} | ||
} | ||
var now = new Date().getTime(); | ||
var fadeDuration = this.type === 'raster' ? this.map.style.rasterFadeDuration : 0; | ||
for (id in retain) { | ||
tile = this._tiles[id]; | ||
if (tile && tile.timeAdded > now - fadeDuration) { | ||
// This tile is still fading in. Find tiles to cross-fade with it. | ||
complete = this._findLoadedChildren(id, maxCoveringZoom, retain); | ||
if (complete) { | ||
this.coveredTiles[id] = true; | ||
} else { | ||
this._findLoadedParent(id, minCoveringZoom, retain); | ||
} | ||
} | ||
} | ||
for (id in this.coveredTiles) retain[id] = true; | ||
// Remove the tiles we don't need anymore. | ||
var remove = util.keysDifference(this._tiles, retain); | ||
for (i = 0; i < remove.length; i++) { | ||
id = +remove[i]; | ||
this._removeTile(id); | ||
} | ||
}, | ||
_loadTile(id) { | ||
var pos = TileCoord.fromID(id), | ||
tile; | ||
if (pos.w === 0) { | ||
var url = this._isGeoJSON ? null : TileCoord.url(id, this.tiles); | ||
tile = this._tiles[id] = Tile.create(this.type, id, this, url, (err) => { | ||
if (err) { | ||
this.fire('tile.error', {tile: tile}); | ||
} else { | ||
this.fire('tile.load', {tile: tile}); | ||
} | ||
}); | ||
} else { | ||
var wrapped = TileCoord.toID(pos.z, pos.x, pos.y, 0); | ||
tile = this._tiles[id] = this._tiles[wrapped] || this._addTile(wrapped); | ||
tile.uses++; | ||
} | ||
return tile; | ||
}, | ||
// Adds a vector tile to the map. It will trigger a rerender of the map and will | ||
// be part in all future renders of the map. The map object will handle copying | ||
// the tile data to the GPU if it is required to paint the current viewport. | ||
_addTile(id) { | ||
var tile = this._tiles[id]; | ||
if (!tile) { | ||
tile = this._cache.get(id); | ||
if (tile) { | ||
tile.uses = 1; | ||
this._tiles[id] = tile; | ||
} | ||
} | ||
if (!tile) { | ||
tile = this._loadTile(id); | ||
this.fire('tile.add', {tile: tile}); | ||
} | ||
return tile; | ||
}, | ||
_removeTile(id) { | ||
var tile = this._tiles[id]; | ||
if (tile) { | ||
tile.uses--; | ||
delete this._tiles[id]; | ||
if (tile.uses <= 0) { | ||
if (!tile.loaded) { | ||
tile.abort(); | ||
tile.remove(); | ||
} else { | ||
this._cache.add(id, tile); | ||
} | ||
this.fire('tile.remove', {tile: tile}); | ||
} | ||
} | ||
} | ||
}); | ||
function zOrder(a, b) { | ||
return (b % 32) - (a % 32); | ||
} | ||
var sources = { | ||
vector: Source, | ||
raster: Source, | ||
geojson: require('./geojson_source'), | ||
video: require('./video_source') | ||
this.dispatcher.send('query features', { | ||
id: result.tile.uid, | ||
x: result.x, | ||
y: result.y, | ||
scale: result.scale, | ||
source: this.id, | ||
params: params | ||
}, callback, result.tile.workerID); | ||
}; | ||
Source.create = function(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); | ||
}; |
@@ -13,3 +13,3 @@ 'use strict'; | ||
w *= 2; | ||
if (w < 0) w = w * -1 -1; | ||
if (w < 0) w = w * -1 - 1; | ||
var dim = 1 << z; | ||
@@ -32,3 +32,3 @@ return ((dim * dim * w + dim * y + x) * 32) + z; | ||
var w = Math.floor(xy / (dim * dim)); | ||
if (w % 2 !== 0) w = w * -1 -1; | ||
if (w % 2 !== 0) w = w * -1 - 1; | ||
w /= 2; | ||
@@ -61,4 +61,4 @@ return { z: z, x: x, y: y, w: w }; | ||
var pos = TileCoord.fromID(id); | ||
if (pos.z === 0) return; | ||
else return TileCoord.toID(pos.z - 1, Math.floor(pos.x / 2), Math.floor(pos.y / 2)); | ||
if (pos.z === 0) return null; | ||
return TileCoord.toID(pos.z - 1, Math.floor(pos.x / 2), Math.floor(pos.y / 2), pos.w); | ||
}; | ||
@@ -73,3 +73,3 @@ | ||
} | ||
return TileCoord.toID(pos.z, pos.x, pos.y); | ||
return TileCoord.toID(pos.z, pos.x, pos.y, pos.w); | ||
}; | ||
@@ -121,3 +121,3 @@ | ||
// sort edges by x-coordinate | ||
if ((e0.x0 == e1.x0 && e0.y0 == e1.y0) ? | ||
if ((e0.x0 === e1.x0 && e0.y0 === e1.y0) ? | ||
(e0.x0 + e1.dy / e0.dy * e0.dx < e1.x1) : | ||
@@ -166,3 +166,3 @@ (e0.x1 - e1.dy / e0.dy * e0.dx < e1.x0)) { | ||
wx = (x + tiles) % tiles; | ||
t[TileCoord.toID(z, wx, y, Math.floor(x/tiles))] = {x: wx, y: y}; | ||
t[TileCoord.toID(z, wx, y, Math.floor(x / tiles))] = {x: wx, y: y}; | ||
} | ||
@@ -169,0 +169,0 @@ } |
'use strict'; | ||
var glmatrix = require('gl-matrix'), | ||
mat2 = glmatrix.mat2, | ||
mat4 = glmatrix.mat4, | ||
vec2 = glmatrix.vec2; | ||
var glmatrix = require('gl-matrix'); | ||
var mat2 = glmatrix.mat2; | ||
var mat4 = glmatrix.mat4; | ||
var vec2 = glmatrix.vec2; | ||
var TileCoord = require('./tile_coord'); | ||
var util = require('../util/util'); | ||
var BufferSet = require('../data/buffer/buffer_set'); | ||
module.exports = Tile; | ||
function Tile() {} | ||
function Tile(id) { | ||
this.id = id; | ||
this.uid = util.uniqueId(); | ||
this.loaded = false; | ||
this.zoom = TileCoord.fromID(id).z; | ||
this.uses = 0; | ||
} | ||
@@ -16,3 +25,3 @@ Tile.prototype = { | ||
calculateMatrices(z, x, y, transform, painter) { | ||
calculateMatrices: function(z, x, y, transform, painter) { | ||
@@ -54,3 +63,3 @@ // Initialize model-view matrix that converts from the tile coordinates | ||
positionAt(id, point) { | ||
positionAt: function(point) { | ||
// tile hasn't finished loading | ||
@@ -68,21 +77,17 @@ if (!this.invPosMatrix) return null; | ||
featuresAt(pos, params, callback) { | ||
this.source.dispatcher.send('query features', { | ||
id: this.id, | ||
x: pos.x, | ||
y: pos.y, | ||
scale: pos.scale, | ||
source: this.source.id, | ||
params: params | ||
}, callback, this.workerID); | ||
} | ||
}; | ||
loadVectorData: function(data) { | ||
this.loaded = true; | ||
var tiles = { | ||
vector: require('./vector_tile'), | ||
raster: require('./raster_tile') | ||
}; | ||
// empty GeoJSON tile | ||
if (!data) return; | ||
Tile.create = function(type, id, source, url, callback) { | ||
return new tiles[type](id, source, url, callback); | ||
this.buffers = new BufferSet(data.buffers); | ||
this.elementGroups = data.elementGroups; | ||
}, | ||
unloadVectorData: function(painter) { | ||
for (var b in this.buffers) { | ||
this.buffers[b].destroy(painter.gl); | ||
} | ||
} | ||
}; |
@@ -8,3 +8,3 @@ 'use strict'; | ||
var Point = require('point-geometry'); | ||
var Source = require('./source'); | ||
var Evented = require('../util/evented'); | ||
var ajax = require('../util/ajax'); | ||
@@ -27,11 +27,11 @@ | ||
// 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)); | ||
@@ -48,4 +48,4 @@ this._loaded = true; | ||
VideoSource.prototype = util.inherit(Source, { | ||
onAdd(map) { | ||
VideoSource.prototype = util.inherit(Evented, { | ||
onAdd: function(map) { | ||
this.map = map; | ||
@@ -58,3 +58,3 @@ if (this.video) { | ||
createTile() { | ||
createTile: function() { | ||
/* | ||
@@ -108,44 +108,32 @@ * Calculate which mercator tile is suitable for rendering the video in | ||
]); | ||
this.boundsBuffer = gl.createBuffer(); | ||
gl.bindBuffer(gl.ARRAY_BUFFER, this.boundsBuffer); | ||
this.tile = new Tile(); | ||
this.tile.buckets = {}; | ||
this.tile.boundsBuffer = gl.createBuffer(); | ||
gl.bindBuffer(gl.ARRAY_BUFFER, this.tile.boundsBuffer); | ||
gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW); | ||
this.tile = new Tile(); | ||
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 bucket = { | ||
type: 'raster', | ||
tile: this, | ||
boundsBuffer: this.boundsBuffer, | ||
bind: this.bind.bind(this) | ||
}; | ||
var buckets = {}; | ||
buckets[layer.bucket] = bucket; | ||
var c = this.center; | ||
this.tile.calculateMatrices(c.zoom, c.column, c.row, this.map.transform, painter); | ||
painter.tile = this.tile; | ||
painter.drawLayer(undefined, this.map.style, layer, {}, undefined, buckets); | ||
}, | ||
bind(gl) { | ||
if (!this.texture) { | ||
this.texture = gl.createTexture(); | ||
gl.bindTexture(gl.TEXTURE_2D, this.texture); | ||
var gl = painter.gl; | ||
if (!this.tile.texture) { | ||
this.tile.texture = gl.createTexture(); | ||
gl.bindTexture(gl.TEXTURE_2D, this.tile.texture); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); | ||
@@ -156,12 +144,13 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); | ||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.video); | ||
} else { | ||
gl.bindTexture(gl.TEXTURE_2D, this.texture); | ||
gl.bindTexture(gl.TEXTURE_2D, this.tile.texture); | ||
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.video); | ||
} | ||
painter.drawLayers(layers, this.tile.posMatrix, this.tile); | ||
}, | ||
featuresAt(point, params, callback) { | ||
featuresAt: function(point, params, callback) { | ||
return callback(null, []); | ||
} | ||
}); |
@@ -6,6 +6,13 @@ 'use strict'; | ||
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; | ||
@@ -20,29 +27,102 @@ | ||
this.depth = depth; | ||
this.buffers = new BufferSet(); | ||
} | ||
/* | ||
* 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; | ||
WorkerTile.prototype.parse = function(data, layers, actor, callback) { | ||
this.featureTree = new FeatureTree(getGeometry, getType); | ||
this.data = data; | ||
this.callback = 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 tileExtent = 4096; | ||
this.collision = new Collision(this.zoom, tileExtent, this.tileSize, this.depth); | ||
this.featureTree = new FeatureTree(getGeometry, getType); | ||
// Map non-ref layers to buckets. | ||
for (i = 0; i < layers.length; i++) { | ||
layer = layers[i]; | ||
var buckets = this.buckets = sortTileIntoBuckets(this, data, bucketInfo); | ||
if (layer.source !== this.source) | ||
continue; | ||
var key, bucket; | ||
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; | ||
var visibility = layer.layout.visibility; | ||
if (visibility === 'none') | ||
continue; | ||
bucket = createBucket(layer, buffers, collision, this.zoom); | ||
bucket.layers = [layer.id]; | ||
buckets[bucket.id] = bucket; | ||
bucketsInOrder.push(bucket); | ||
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; | ||
var remaining = bucketInfo.length; | ||
/* | ||
@@ -57,11 +137,5 @@ * The async parsing here is a bit tricky. | ||
for (var i = 0; i < bucketInfo.length; i++) { | ||
bucket = buckets[bucketInfo[i].id]; | ||
if (bucket) bucket.info = bucketInfo[i]; | ||
for (i = 0; i < bucketsInOrder.length; i++) { | ||
bucket = bucketsInOrder[i]; | ||
if (bucketInfo[i].source !== this.source || !bucket) { | ||
remaining--; | ||
continue; // raster bucket, etc | ||
} | ||
// Link buckets that need to be parsed in order | ||
@@ -71,3 +145,2 @@ if (bucket.collision) { | ||
prevPlacementBucket.next = bucket; | ||
prevPlacementBucket.next.bucketInfo = bucketInfo[i]; | ||
} else { | ||
@@ -82,8 +155,5 @@ bucket.previousPlaced = true; | ||
} | ||
} | ||
// 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); | ||
@@ -106,3 +176,3 @@ } | ||
var now = Date.now(); | ||
if (bucket.type !== 'raster') bucket.addFeatures(); | ||
if (bucket.features.length) bucket.addFeatures(); | ||
var time = Date.now() - now; | ||
@@ -112,3 +182,3 @@ if (bucket.interactive) { | ||
var feature = bucket.features[i]; | ||
tile.featureTree.insert(feature.bbox(), bucket.info, feature); | ||
tile.featureTree.insert(feature.bbox(), bucket.layers, feature); | ||
} | ||
@@ -119,3 +189,3 @@ } | ||
self.bucketStats._total += time; | ||
self.bucketStats[bucket.name] = (self.bucketStats[bucket.name] || 0) + time; | ||
self.bucketStats[bucket.id] = (self.bucketStats[bucket.id] || 0) + time; | ||
} | ||
@@ -125,4 +195,8 @@ } | ||
remaining--; | ||
if (!remaining) return tile.done(); | ||
if (!remaining) { | ||
done(); | ||
return; | ||
} | ||
// try parsing the next bucket, if it is ready | ||
@@ -134,132 +208,20 @@ if (bucket.next) { | ||
} | ||
}; | ||
WorkerTile.prototype.done = function() { | ||
// Collect all buffers to mark them as transferable object. | ||
var buffers = []; | ||
function done() { | ||
var transferables = [], | ||
elementGroups = {}; | ||
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 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]; | ||
} | ||
}; |
'use strict'; | ||
var Actor = require('../util/actor'); | ||
var featureFilter = require('feature-filter'); | ||
var WorkerTile = require('./worker_tile'); | ||
@@ -22,3 +21,3 @@ var util = require('../util/util'); | ||
this.loaded = {}; | ||
this.buckets = []; | ||
this.layers = []; | ||
this.geoJSONIndexes = {}; | ||
@@ -28,8 +27,4 @@ } | ||
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; | ||
}, | ||
@@ -44,3 +39,3 @@ | ||
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]; | ||
@@ -54,9 +49,15 @@ | ||
tile.parse(new vt.VectorTile(new Protobuf(new Uint8Array(data))), this.buckets, this.actor, callback); | ||
tile.data = new vt.VectorTile(new Protobuf(new Uint8Array(data))); | ||
tile.parse(tile.data, this.layers, this.actor, callback); | ||
this.loaded[source] = this.loaded[source] || {}; | ||
this.loaded[source][id] = tile; | ||
}); | ||
}.bind(this)); | ||
}, | ||
'reload tile': function(params, callback) { | ||
var tile = this.loaded[params.source][params.id]; | ||
tile.parse(tile.data, this.layers, this.actor, callback); | ||
}, | ||
'abort tile': function(params) { | ||
@@ -79,6 +80,7 @@ var source = this.loading[params.source]; | ||
'parse geojson': function(params, callback) { | ||
var indexData = (err, data) => { | ||
var indexData = function(err, data) { | ||
if (err) return callback(err); | ||
this.geoJSONIndexes[params.source] = geojsonvt(data, {baseZoom: params.maxZoom}); | ||
callback(null); | ||
}; | ||
}.bind(this); | ||
@@ -107,3 +109,3 @@ // TODO accept params.url for urls instead | ||
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); | ||
@@ -110,0 +112,0 @@ this.loaded[source] = this.loaded[source] || {}; |
@@ -28,4 +28,4 @@ 'use strict'; | ||
this.times = this.times.filter(function(t) { | ||
return t.id != n; | ||
return t.id !== n; | ||
}); | ||
}; |
@@ -15,3 +15,3 @@ 'use strict'; | ||
ajax.getJSON(base + '.json', (err, data) => { | ||
ajax.getJSON(base + '.json', function(err, data) { | ||
if (err) { | ||
@@ -24,5 +24,5 @@ this.fire('error', {error: err}); | ||
if (this.img) this.fire('load'); | ||
}); | ||
}.bind(this)); | ||
ajax.getImage(base + '.png', (err, img) => { | ||
ajax.getImage(base + '.png', function(err, img) { | ||
if (err) { | ||
@@ -36,3 +36,3 @@ this.fire('error', {error: err}); | ||
var newdata = img.data = new Uint8Array(data.length); | ||
for (var i = 0; i < data.length; i+=4) { | ||
for (var i = 0; i < data.length; i += 4) { | ||
var alpha = data[i + 3] / 255; | ||
@@ -47,3 +47,3 @@ newdata[i + 0] = data[i + 0] * alpha; | ||
if (this.data) this.fire('load'); | ||
}); | ||
}.bind(this)); | ||
} | ||
@@ -79,7 +79,5 @@ | ||
var pos = this.data && this.data[name]; | ||
if (pos && this.img) { | ||
return pos; | ||
} else { | ||
return new SpritePosition(); | ||
} | ||
if (pos && this.img) return pos; | ||
return new SpritePosition(); | ||
}; |
@@ -16,6 +16,6 @@ 'use strict'; | ||
for (var prop in properties) { | ||
if (properties[prop]['default'] === undefined) continue; | ||
Properties.prototype[prop] = properties[prop]['default']; | ||
if (properties[prop].default === undefined) continue; | ||
Properties.prototype[prop] = properties[prop].default; | ||
} | ||
module.exports[className.replace('layout_','')] = Properties; | ||
module.exports[className.replace('layout_', '')] = Properties; | ||
}); |
@@ -14,3 +14,3 @@ 'use strict'; | ||
var prop = properties[p], | ||
value = prop['default']; | ||
value = prop.default; | ||
@@ -24,3 +24,3 @@ if (value === undefined) continue; | ||
Calculated.prototype.hidden = false; | ||
module.exports[className.replace('paint_','')] = Calculated; | ||
module.exports[className.replace('paint_', '')] = Calculated; | ||
}); |
@@ -1,1 +0,1 @@ | ||
module.exports = require('mapbox-gl-style-spec/reference/v6'); | ||
module.exports = require('mapbox-gl-style-spec/reference/v7'); |
@@ -5,8 +5,3 @@ 'use strict'; | ||
module.exports.resolve = function (properties, constants) { | ||
if (!constants) | ||
return properties; | ||
var result = {}, i; | ||
exports.resolve = function(value, constants) { | ||
function resolve(value) { | ||
@@ -16,30 +11,41 @@ return typeof value === 'string' && value[0] === '@' ? constants[value] : value; | ||
for (var key in properties) { | ||
var value = resolve(properties[key]); | ||
var i; | ||
if (Array.isArray(value)) { | ||
value = value.slice(); | ||
value = resolve(value); | ||
for (i = 0; i < value.length; i++) { | ||
if (value[i] in constants) { | ||
value[i] = resolve(value[i]); | ||
} | ||
if (Array.isArray(value)) { | ||
value = value.slice(); | ||
for (i = 0; i < value.length; i++) { | ||
if (value[i] in constants) { | ||
value[i] = resolve(value[i]); | ||
} | ||
} | ||
} | ||
if (value.stops) { | ||
value = util.extend({}, value); | ||
value.stops = value.stops.slice(); | ||
if (value.stops) { | ||
value = util.extend({}, value); | ||
value.stops = value.stops.slice(); | ||
for (i = 0; i < value.stops.length; i++) { | ||
if (value.stops[i][1] in constants) { | ||
value.stops[i] = [ | ||
value.stops[i][0], | ||
resolve(value.stops[i][1]) | ||
]; | ||
} | ||
for (i = 0; i < value.stops.length; i++) { | ||
if (value.stops[i][1] in constants) { | ||
value.stops[i] = [ | ||
value.stops[i][0], | ||
resolve(value.stops[i][1]) | ||
]; | ||
} | ||
} | ||
} | ||
result[key] = value; | ||
return value; | ||
}; | ||
exports.resolveAll = function (properties, constants) { | ||
if (!constants) | ||
return properties; | ||
var result = {}; | ||
for (var key in properties) { | ||
result[key] = exports.resolve(properties[key], constants); | ||
} | ||
@@ -46,0 +52,0 @@ |
'use strict'; | ||
var util = require('../util/util'); | ||
var interpolate = require('../util/interpolate'); | ||
var reference = require('./reference'); | ||
@@ -9,72 +9,87 @@ var parseCSSColor = require('csscolorparser').parseCSSColor; | ||
/* | ||
* A parsed representation of a property:value pair | ||
*/ | ||
function StyleDeclaration(renderType, prop, value) { | ||
var className = 'paint_' + renderType; | ||
var propReference = reference[className] && reference[className][prop]; | ||
if (!propReference) return; | ||
function StyleDeclaration(reference, value) { | ||
this.value = this.parseValue(value, reference); | ||
this.type = reference.type; | ||
this.transitionable = reference.transition; | ||
this.value = this.parseValue(value, propReference.type, propReference.values); | ||
this.prop = prop; | ||
this.type = propReference.type; | ||
// immuatable representation of value. used for comparison | ||
this.json = JSON.stringify(value); | ||
} | ||
StyleDeclaration.prototype.calculate = function(z) { | ||
return typeof this.value === 'function' ? this.value(z) : this.value; | ||
StyleDeclaration.prototype.calculate = function(z, zoomHistory, transitionDuration) { | ||
return typeof this.value === 'function' ? this.value(z, zoomHistory, transitionDuration) : this.value; | ||
}; | ||
StyleDeclaration.prototype.parseValue = function(value, type, values) { | ||
if (type === 'color') { | ||
return parseColor(value); | ||
} else if (type === 'number') { | ||
return parseNumber(value); | ||
} else if (type === 'boolean') { | ||
return Boolean(value); | ||
} else if (type === 'image') { | ||
return String(value); | ||
} else if (type === 'string') { | ||
return String(value); | ||
} else if (type === 'array') { | ||
return parseNumberArray(value); | ||
} else if (type === 'enum' && Array.isArray(values)) { | ||
return values.indexOf(value) >= 0 ? value : undefined; | ||
} else { | ||
console.warn(type + ' is not a supported property type'); | ||
StyleDeclaration.prototype.parseValue = function(value, propReference) { | ||
if (!value) return value; | ||
if (propReference.function === 'interpolated') { | ||
if (value.stops) { | ||
return interpolatedFunction(value, propReference.type === 'color'); | ||
} else if (propReference.type === 'color') { | ||
return parseColor(value); | ||
} | ||
return value; | ||
} else if (propReference.transition) { | ||
return transitionedPiecewiseConstantFunction(value.stops ? value.stops : [[0, value]]); | ||
} | ||
if (value.stops) { | ||
return piecewiseConstantFunction(value); | ||
} | ||
return value; | ||
}; | ||
function parseNumber(num) { | ||
if (num.stops) num = stopsFn(num); | ||
var value = +num; | ||
return !isNaN(value) ? value : num; | ||
function getBiggestStopLessThan(stops, z) { | ||
for (var i = 0; i < stops.length; i++) { | ||
if (stops[i][0] > z) { | ||
return stops[i === 0 ? 0 : i - 1]; | ||
} | ||
} | ||
return stops[stops.length - 1]; | ||
} | ||
function parseNumberArray(array) { | ||
var widths = array.map(parseNumber); | ||
function piecewiseConstantFunction(params) { | ||
return function(z) { | ||
var result = []; | ||
for (var i = 0; i < widths.length; i++) { | ||
result.push(typeof widths[i] === 'function' ? widths[i](z) : widths[i]); | ||
} | ||
return result; | ||
return getBiggestStopLessThan(params.stops, z)[1]; | ||
}; | ||
} | ||
var colorCache = {}; | ||
function transitionedPiecewiseConstantFunction(stops) { | ||
function parseColor(value) { | ||
if (value.stops) return stopsFn(value, true); | ||
if (colorCache[value]) return colorCache[value]; | ||
return function(z, zh, duration) { | ||
var color = colorCache[value] = prepareColor(parseCSSColor(value)); | ||
return color; | ||
var fraction = z % 1; | ||
var t = Math.min((Date.now() - zh.lastIntegerZoomTime) / duration, 1); | ||
var fromScale = 1; | ||
var toScale = 1; | ||
var mix, from, to; | ||
if (z > zh.lastIntegerZoom) { | ||
mix = fraction + (1 - fraction) * t; | ||
fromScale *= 2; | ||
from = getBiggestStopLessThan(stops, z - 1); | ||
to = getBiggestStopLessThan(stops, z); | ||
} else { | ||
mix = 1 - (1 - t) * fraction; | ||
to = getBiggestStopLessThan(stops, z); | ||
from = getBiggestStopLessThan(stops, z + 1); | ||
fromScale /= 2; | ||
} | ||
return { | ||
from: from[1], | ||
fromScale: fromScale, | ||
to: to[1], | ||
toScale: toScale, | ||
t: mix | ||
}; | ||
}; | ||
} | ||
function stopsFn(params, color) { | ||
function interpolatedFunction(params, color) { | ||
var stops = params.stops; | ||
@@ -105,31 +120,32 @@ var base = params.base || reference.function.base.default; | ||
if (color) return interpColor(parseColor(low[1]), parseColor(high[1]), t); | ||
else return util.interp(low[1], high[1], t); | ||
if (color) return interpolate.color(parseColor(low[1]), parseColor(high[1]), t); | ||
else if (low[1].length === 2) return interpolate.vec2(low[1], high[1], t); | ||
return interpolate.number(low[1], high[1], t); | ||
} else if (low) { | ||
if (color) return parseColor(low[1]); | ||
else return low[1]; | ||
return low[1]; | ||
} else if (high) { | ||
if (color) return parseColor(high[1]); | ||
else return high[1]; | ||
return high[1]; | ||
} else { | ||
if (color) return [0, 0, 0, 1]; | ||
else return 1; | ||
} | ||
if (color) return [0, 0, 0, 1]; | ||
return 1; | ||
}; | ||
} | ||
var colorCache = {}; | ||
function parseColor(value) { | ||
if (colorCache[value]) return colorCache[value]; | ||
var color = colorCache[value] = prepareColor(parseCSSColor(value)); | ||
return color; | ||
} | ||
function prepareColor(c) { | ||
return [c[0] / 255, c[1] / 255, c[2] / 255, c[3] / 1]; | ||
} | ||
function interpColor(from, to, t) { | ||
return [ | ||
util.interp(from[0], to[0], t), | ||
util.interp(from[1], to[1], t), | ||
util.interp(from[2], to[2], t), | ||
util.interp(from[3], to[3], t) | ||
]; | ||
} |
'use strict'; | ||
var util = require('../util/util'); | ||
var interpolate = require('../util/interpolate'); | ||
@@ -16,8 +17,6 @@ module.exports = StyleTransition; | ||
var type = declaration.type; | ||
if (type === 'number') { | ||
this.interp = util.interp; | ||
} else if (type === 'color') { | ||
this.interp = interpColor; | ||
} else if (type === 'array') { | ||
this.interp = interpNumberArray; | ||
if ((type === 'string' || type === 'array') && declaration.transitionable) { | ||
this.interp = interpZoomTransitioned; | ||
} else { | ||
this.interp = interpolate[type]; | ||
} | ||
@@ -49,5 +48,5 @@ | ||
*/ | ||
StyleTransition.prototype.at = function(z, t) { | ||
StyleTransition.prototype.at = function(z, zoomHistory, t) { | ||
var value = this.declaration.calculate(z); | ||
var value = this.declaration.calculate(z, zoomHistory, this.duration); | ||
@@ -59,3 +58,3 @@ if (this.instant()) return value; | ||
if (t < this.endTime) { | ||
var oldValue = this.oldTransition.at(z, this.startTime); | ||
var oldValue = this.oldTransition.at(z, zoomHistory, this.startTime); | ||
var eased = this.ease((t - this.startTime - this.delay) / this.duration); | ||
@@ -69,15 +68,10 @@ value = this.interp(oldValue, value, eased); | ||
function interpNumberArray(from, to, t) { | ||
return from.map(function(d, i) { | ||
return util.interp(d, to[i], t); | ||
}); | ||
function interpZoomTransitioned(from, to, t) { | ||
return { | ||
from: from.to, | ||
fromScale: from.toScale, | ||
to: to.to, | ||
toScale: to.toScale, | ||
t: t | ||
}; | ||
} | ||
function interpColor(from, to, t) { | ||
return [ | ||
util.interp(from[0], to[0], t), | ||
util.interp(from[1], to[1], t), | ||
util.interp(from[2], to[2], t), | ||
util.interp(from[3], to[3], t) | ||
]; | ||
} |
@@ -5,7 +5,3 @@ 'use strict'; | ||
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'); | ||
@@ -15,19 +11,15 @@ var GlyphSource = require('../symbol/glyph_source'); | ||
var SpriteAtlas = require('../symbol/sprite_atlas'); | ||
var LineAtlas = require('../render/line_atlas'); | ||
var util = require('../util/util'); | ||
var ajax = require('../util/ajax'); | ||
var normalizeURL = require('../util/mapbox').normalizeStyleURL; | ||
var browser = require('../util/browser'); | ||
var Dispatcher = require('../util/dispatcher'); | ||
var Point = require('point-geometry'); | ||
var AnimationLoop = require('./animation_loop'); | ||
module.exports = Style; | ||
/* | ||
* The map style's current state | ||
* | ||
* The stylesheet object is not modified. To change the style, just change | ||
* the the stylesheet object and trigger a cascade. | ||
*/ | ||
function Style(stylesheet, animationLoop) { | ||
this.classes = {}; | ||
this.animationLoop = animationLoop; | ||
this.animationLoop = animationLoop || new AnimationLoop(); | ||
this.dispatcher = new Dispatcher(Math.max(browser.hardwareConcurrency - 1, 1), this); | ||
@@ -37,13 +29,10 @@ this.glyphAtlas = new GlyphAtlas(1024, 1024); | ||
this.spriteAtlas.resize(browser.devicePixelRatio); | ||
this.lineAtlas = new LineAtlas(256, 512); | ||
this.buckets = {}; | ||
this.orderedBuckets = []; | ||
this.flattened = []; | ||
this.layerMap = {}; | ||
this.layerGroups = []; | ||
this.processedPaintProps = {}; | ||
this.transitions = {}; | ||
this.computed = {}; | ||
this._layers = {}; | ||
this._groups = []; | ||
this.sources = {}; | ||
this.zoomHistory = {}; | ||
util.bindAll([ | ||
@@ -54,3 +43,3 @@ '_forwardSourceEvent', | ||
var loaded = (err, stylesheet) => { | ||
var loaded = function(err, stylesheet) { | ||
if (err) { | ||
@@ -64,3 +53,3 @@ this.fire('error', {error: err}); | ||
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'); | ||
@@ -79,9 +68,8 @@ | ||
this.glyphSource = new GlyphSource(stylesheet.glyphs, this.glyphAtlas); | ||
this.cascade({transition: false}); | ||
this._resolve(); | ||
this.fire('load'); | ||
}; | ||
}.bind(this); | ||
if (typeof stylesheet === 'string') { | ||
ajax.getJSON(stylesheet, loaded); | ||
ajax.getJSON(normalizeURL(stylesheet), loaded); | ||
} else { | ||
@@ -92,30 +80,6 @@ browser.frame(loaded.bind(this, null, stylesheet)); | ||
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) | ||
@@ -134,297 +98,116 @@ return false; | ||
recalculate(z) { | ||
if (typeof z !== 'number') console.warn('recalculate expects zoom level'); | ||
_resolve: function() { | ||
var id, layer, group; | ||
var transitions = this.transitions; | ||
var layerValues = {}; | ||
this._layers = {}; | ||
this._groups = []; | ||
for (var id in this.sources) | ||
this.sources[id].used = false; | ||
for (var i = 0; i < this.stylesheet.layers.length; i++) { | ||
layer = new StyleLayer(this.stylesheet.layers[i], this.stylesheet.constants || {}); | ||
this._layers[layer.id] = layer; | ||
} | ||
this.rasterFadeDuration = 300; | ||
for (var name in transitions) { | ||
var layer = transitions[name], | ||
bucket = this.buckets[layer.ref || name], | ||
layerType = this.layerMap[name].type; | ||
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']); | ||
} | ||
// Resolve layout properties. | ||
for (id in this._layers) { | ||
this._layers[id].resolveLayout(); | ||
} | ||
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 reference and paint properties. | ||
for (id in this._layers) { | ||
this._layers[id].resolveReference(this._layers); | ||
this._layers[id].resolvePaint(); | ||
} | ||
return simple; | ||
}, | ||
// Split the layers into groups of consecutive layers with the same datasource | ||
_groupLayers(layers) { | ||
var g = 0; | ||
var groups = []; | ||
var group; | ||
// Split into groups of consecutive top-level layers with the same source. | ||
for (id in this._layers) { | ||
layer = this._layers[id]; | ||
// loop over layers top down | ||
for (var i = layers.length - 1; i >= 0; i--) { | ||
var layer = layers[i]; | ||
var bucket = this.buckets[layer.ref || layer.id]; | ||
var source = bucket && bucket.source; | ||
// if the current layer is in a different source | ||
if (group && source !== group.source) g++; | ||
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._broadcastLayers(); | ||
}, | ||
/* | ||
* Take all the rules and declarations from the stylesheet, | ||
* and figure out which apply currently | ||
*/ | ||
cascade(options) { | ||
var i, | ||
layer, | ||
id, | ||
prop, | ||
paintProp; | ||
_broadcastLayers: function() { | ||
var ordered = []; | ||
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; | ||
for (var id in this._layers) { | ||
ordered.push(this._layers[id].json()); | ||
} | ||
this.dispatcher.broadcast('set buckets', this.orderedBuckets); | ||
// apply layer group inheritance resulting in a flattened array | ||
var flattened = this.flattened = flattenLayers(this.stylesheet.layers); | ||
this.dispatcher.broadcast('set layers', ordered); | ||
}, | ||
// map layer ids to layer definitions for resolving refs | ||
var layerMap = this.layerMap = {}; | ||
for (i = 0; i < flattened.length; i++) { | ||
layer = flattened[i]; | ||
_cascade: function(classes, options) { | ||
if (!this._loaded) return; | ||
var newLayer = {}; | ||
for (var k in layer) { | ||
if (k === 'layers') continue; | ||
newLayer[k] = layer[k]; | ||
} | ||
options = options || { | ||
transition: true | ||
}; | ||
layerMap[layer.id] = newLayer; | ||
flattened[i] = newLayer; | ||
for (var id in this._layers) { | ||
this._layers[id].cascade(classes, options, | ||
this.stylesheet.transition || {}, | ||
this.animationLoop); | ||
} | ||
for (i = 0; i < flattened.length; i++) { | ||
flattened[i] = resolveLayer(layerMap, flattened[i]); | ||
} | ||
this.fire('change'); | ||
}, | ||
// pre-calculate style declarations and transition properties for all layers x all classes | ||
var processedPaintProps = this.processedPaintProps = {}; | ||
for (i = 0; i < flattened.length; i++) { | ||
layer = flattened[i]; | ||
id = layer.id; | ||
var renderType = layer.type; | ||
_recalculate: function(z) { | ||
for (var id in this.sources) | ||
this.sources[id].used = false; | ||
processedPaintProps[id] = {}; | ||
for (prop in layer) { | ||
if (!(/^paint/).test(prop)) continue; | ||
var paint = StyleConstant.resolve(layer[prop], constants); | ||
this._updateZoomHistory(z); | ||
// 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]); | ||
} | ||
} | ||
this.rasterFadeDuration = 300; | ||
for (id in this._layers) { | ||
var layer = this._layers[id]; | ||
// 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; | ||
} | ||
} | ||
if (layer.recalculate(z, this.zoomHistory) && layer.source) { | ||
this.sources[layer.source].used = true; | ||
} | ||
} | ||
this.layerGroups = this._groupLayers(this.stylesheet.layers); | ||
this.cascadeClasses(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; | ||
return layer; | ||
var maxZoomTransitionDuration = 300; | ||
if (Math.floor(this.z) !== Math.floor(z)) { | ||
this.animationLoop.set(maxZoomTransitionDuration); | ||
} | ||
// Flatten composite layer structures. | ||
function flattenLayers(layers) { | ||
var flat = []; | ||
for (var i = 0; i < layers.length; i++) { | ||
flat.push(layers[i]); | ||
if (layers[i].layers) { | ||
flat.push.apply(flat, flattenLayers(layers[i].layers)); | ||
} | ||
} | ||
return flat; | ||
} | ||
this.z = z; | ||
this.fire('zoom'); | ||
}, | ||
cascadeClasses(options) { | ||
if (!this._loaded) return; | ||
_updateZoomHistory: function(z) { | ||
options = options || { | ||
transition: true | ||
}; | ||
var zh = this.zoomHistory; | ||
var transitions = {}; | ||
var processedPaintProps = this.processedPaintProps; | ||
var flattened = this.flattened; | ||
var classes = this.classes; | ||
if (zh.lastIntegerZoom === undefined) { | ||
// first time | ||
zh.lastIntegerZoom = Math.floor(z); | ||
zh.lastIntegerZoomTime = 0; | ||
zh.lastZoom = z; | ||
} | ||
for (var i = 0; i < flattened.length; i++) { | ||
var layer = flattened[i]; | ||
var id = layer.id; | ||
transitions[id] = {}; | ||
// check whether an integer zoom level as passed since the last frame | ||
// and if yes, record it with the time. Used for transitioning patterns. | ||
if (Math.floor(zh.lastZoom) < Math.floor(z)) { | ||
zh.lastIntegerZoom = Math.floor(z); | ||
zh.lastIntegerZoomTime = Date.now(); | ||
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; | ||
} | ||
} | ||
} | ||
} else if (Math.floor(zh.lastZoom) > Math.floor(z)) { | ||
zh.lastIntegerZoom = Math.floor(z + 1); | ||
zh.lastIntegerZoomTime = Date.now(); | ||
} | ||
this.transitions = transitions; | ||
this.fire('change'); | ||
}, | ||
zh.lastZoom = z; | ||
}, | ||
addSource(id, source) { | ||
addSource: function(id, source) { | ||
if (this.sources[id] !== undefined) { | ||
@@ -450,3 +233,3 @@ throw new Error('There is already a source with this ID'); | ||
removeSource(id) { | ||
removeSource: function(id) { | ||
if (this.sources[id] === undefined) { | ||
@@ -469,39 +252,30 @@ throw new Error('There is no source with this ID'); | ||
getSource(id) { | ||
getSource: function(id) { | ||
return this.sources[id]; | ||
}, | ||
addClass(n, options) { | ||
if (this.classes[n]) return; // prevent unnecessary recalculation | ||
this.classes[n] = true; | ||
this.cascadeClasses(options); | ||
getLayer: function(id) { | ||
return this._layers[id]; | ||
}, | ||
removeClass(n, options) { | ||
if (!this.classes[n]) return; // prevent unnecessary recalculation | ||
delete this.classes[n]; | ||
this.cascadeClasses(options); | ||
setPaintProperty: function(layer, name, value, klass) { | ||
this.getLayer(layer).setPaintProperty(name, value, klass); | ||
}, | ||
hasClass(n) { | ||
return !!this.classes[n]; | ||
getPaintProperty: function(layer, name, klass) { | ||
return this.getLayer(layer).getPaintProperty(name, klass); | ||
}, | ||
setClassList(l, options) { | ||
this.classes = {}; | ||
for (var i = 0; i < l.length; i++) { | ||
this.classes[l[i]] = true; | ||
} | ||
this.cascadeClasses(options); | ||
setLayoutProperty: function(layer, name, value) { | ||
layer = this.getLayer(layer); | ||
layer.setLayoutProperty(name, value); | ||
this._broadcastLayers(); | ||
this.sources[layer.source].reload(); | ||
}, | ||
getClassList() { | ||
return Object.keys(this.classes); | ||
getLayoutProperty: function(layer, name) { | ||
return this.getLayer(layer).getLayoutProperty(name); | ||
}, | ||
getLayer(id) { | ||
return this.layerMap[id]; | ||
}, | ||
featuresAt(point, params, callback) { | ||
featuresAt: function(point, params, callback) { | ||
var features = []; | ||
@@ -513,7 +287,6 @@ var error = null; | ||
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]; | ||
@@ -525,34 +298,30 @@ source.featuresAt(point, params, function(err, result) { | ||
}); | ||
}, () => { | ||
}.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() { | ||
_updateSources: function(transform) { | ||
for (var id in this.sources) { | ||
this.sources[id].update(); | ||
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)); | ||
@@ -559,0 +328,0 @@ }, |
@@ -16,13 +16,13 @@ 'use strict'; | ||
var free = this.free[i]; | ||
if (free.y == rect.y && free.h == rect.h && free.x + free.w == rect.x) { | ||
if (free.y === rect.y && free.h === rect.h && free.x + free.w === rect.x) { | ||
free.w += rect.w; | ||
} | ||
else if (free.x == rect.x && free.w == rect.w && free.y + free.h == rect.y) { | ||
else if (free.x === rect.x && free.w === rect.w && free.y + free.h === rect.y) { | ||
free.h += rect.h; | ||
} | ||
else if (rect.y == free.y && rect.h == free.h && rect.x + rect.w == free.x) { | ||
else if (rect.y === free.y && rect.h === free.h && rect.x + rect.w === free.x) { | ||
free.x = rect.x; | ||
free.w += rect.w; | ||
} | ||
else if (rect.x == free.x && rect.w == free.w && rect.y + rect.h == free.y) { | ||
else if (rect.x === free.x && rect.w === free.w && rect.y + rect.h === free.y) { | ||
free.y = rect.y; | ||
@@ -57,27 +57,27 @@ free.h += rect.h; | ||
return { x: -1, y: -1 }; | ||
} else { | ||
this.free.splice(smallest, 1); | ||
} | ||
// Shorter/Longer Axis Split Rule (SAS) | ||
// http://clb.demon.fi/files/RectangleBinPack.pdf p. 15 | ||
// Ignore the dimension of R and just split long the shorter dimension | ||
// See Also: http://www.cs.princeton.edu/~chazelle/pubs/blbinpacking.pdf | ||
if (rect.w < rect.h) { | ||
// split horizontally | ||
// +--+---+ | ||
// |__|___| <-- b1 | ||
// +------+ <-- b2 | ||
if (rect.w > width) this.free.push({ x: rect.x + width, y: rect.y, w: rect.w - width, h: height }); | ||
if (rect.h > height) this.free.push({ x: rect.x, y: rect.y + height, w: rect.w, h: rect.h - height }); | ||
} else { | ||
// split vertically | ||
// +--+---+ | ||
// |__| | <-- b1 | ||
// +--|---+ <-- b2 | ||
if (rect.w > width) this.free.push({ x: rect.x + width, y: rect.y, w: rect.w - width, h: rect.h }); | ||
if (rect.h > height) this.free.push({ x: rect.x, y: rect.y + height, w: width, h: rect.h - height }); | ||
} | ||
this.free.splice(smallest, 1); | ||
return { x: rect.x, y: rect.y, w: width, h: height }; | ||
// Shorter/Longer Axis Split Rule (SAS) | ||
// http://clb.demon.fi/files/RectangleBinPack.pdf p. 15 | ||
// Ignore the dimension of R and just split long the shorter dimension | ||
// See Also: http://www.cs.princeton.edu/~chazelle/pubs/blbinpacking.pdf | ||
if (rect.w < rect.h) { | ||
// split horizontally | ||
// +--+---+ | ||
// |__|___| <-- b1 | ||
// +------+ <-- b2 | ||
if (rect.w > width) this.free.push({ x: rect.x + width, y: rect.y, w: rect.w - width, h: height }); | ||
if (rect.h > height) this.free.push({ x: rect.x, y: rect.y + height, w: rect.w, h: rect.h - height }); | ||
} else { | ||
// split vertically | ||
// +--+---+ | ||
// |__| | <-- b1 | ||
// +--|---+ <-- b2 | ||
if (rect.w > width) this.free.push({ x: rect.x + width, y: rect.y, w: rect.w - width, h: rect.h }); | ||
if (rect.h > height) this.free.push({ x: rect.x, y: rect.y + height, w: width, h: rect.h - height }); | ||
} | ||
return { x: rect.x, y: rect.y, w: width, h: height }; | ||
}; |
@@ -155,3 +155,3 @@ 'use strict'; | ||
var placementRange = [2*Math.PI, 0]; | ||
var placementRange = [2 * Math.PI, 0]; | ||
@@ -158,0 +158,0 @@ for (var k = 0; k < glyphs.length; k++) { |
@@ -124,8 +124,8 @@ 'use strict'; | ||
var buffered_width = glyph.width + buffer * 2; | ||
var buffered_height = glyph.height + buffer * 2; | ||
var bufferedWidth = glyph.width + buffer * 2; | ||
var bufferedHeight = glyph.height + buffer * 2; | ||
// Add a 1px border around every image. | ||
var pack_width = buffered_width; | ||
var pack_height = buffered_height; | ||
var packWidth = bufferedWidth; | ||
var packHeight = bufferedHeight; | ||
@@ -135,6 +135,6 @@ // Increase to next number divisible by 4, but at least 1. | ||
// into 2 bytes rather than 4 bytes. | ||
pack_width += (4 - pack_width % 4); | ||
pack_height += (4 - pack_height % 4); | ||
packWidth += (4 - packWidth % 4); | ||
packHeight += (4 - packHeight % 4); | ||
var rect = this.bin.allocate(pack_width, pack_height); | ||
var rect = this.bin.allocate(packWidth, packHeight); | ||
if (rect.x < 0) { | ||
@@ -154,6 +154,6 @@ console.warn('glyph bitmap overflow'); | ||
var source = glyph.bitmap; | ||
for (var y = 0; y < buffered_height; y++) { | ||
for (var y = 0; y < bufferedHeight; y++) { | ||
var y1 = this.width * (rect.y + y) + rect.x; | ||
var y2 = buffered_width * y; | ||
for (var x = 0; x < buffered_width; x++) { | ||
var y2 = bufferedWidth * y; | ||
for (var x = 0; x < bufferedWidth; x++) { | ||
target[y1 + x] = source[y2 + x]; | ||
@@ -195,5 +195,5 @@ } | ||
data.data[j] = this.data[i]; | ||
data.data[j+1] = this.data[i]; | ||
data.data[j+2] = this.data[i]; | ||
data.data[j+3] = 255; | ||
data.data[j + 1] = this.data[i]; | ||
data.data[j + 2] = this.data[i]; | ||
data.data[j + 3] = 255; | ||
} | ||
@@ -200,0 +200,0 @@ this.ctx.putImageData(data, 0, 0); |
@@ -52,3 +52,3 @@ 'use strict'; | ||
var onRangeLoaded = (err, range, data) => { | ||
var onRangeLoaded = function(err, range, data) { | ||
// TODO not be silent about errors | ||
@@ -67,3 +67,3 @@ if (!err) { | ||
if (!remaining) callback(undefined, result); | ||
}; | ||
}.bind(this); | ||
@@ -70,0 +70,0 @@ for (var r in missing) { |
'use strict'; | ||
var util = require('../util/util'); | ||
var interp = require('../util/interpolate'); | ||
var Anchor = require('../symbol/anchor'); | ||
@@ -44,4 +44,4 @@ | ||
var t = (markedDistance - distance) / segmentDist, | ||
x = util.interp(a.x, b.x, t), | ||
y = util.interp(a.y, b.y, t), | ||
x = interp(a.x, b.x, t), | ||
y = interp(a.y, b.y, t), | ||
s = minScales[added % len]; | ||
@@ -48,0 +48,0 @@ |
@@ -14,11 +14,8 @@ 'use strict'; | ||
var x = image.w / 2; | ||
var y = image.h / 2; | ||
var dx = props['icon-offset'][0]; | ||
var dy = props['icon-offset'][1]; | ||
var x1 = (dx - x); | ||
var x2 = (dx + x); | ||
var y1 = (dy - y); | ||
var y2 = (dy + y); | ||
var x1 = dx - image.originalWidth / 2; | ||
var x2 = x1 + image.w; | ||
var y1 = dy - image.originalHeight / 2; | ||
var y2 = y1 + image.h; | ||
@@ -238,6 +235,5 @@ var tl = new Point(x1, y1); | ||
segment_loop: | ||
while (true) { | ||
var dist = newAnchor.dist(end); | ||
var scale = offset/dist; | ||
var scale = offset / dist; | ||
var angle = -Math.atan2(end.x - newAnchor.x, end.y - newAnchor.y) + direction * Math.PI / 2; | ||
@@ -272,3 +268,3 @@ if (upsideDown) angle += Math.PI; | ||
anchor.scale = scale; | ||
break segment_loop; | ||
return; | ||
} | ||
@@ -275,0 +271,0 @@ } |
@@ -18,3 +18,6 @@ 'use strict'; | ||
var hastext = false; | ||
if (!text) continue; | ||
if (!text) { | ||
textFeatures[i] = null; | ||
continue; | ||
} | ||
text = text.toString(); | ||
@@ -66,2 +69,1 @@ | ||
} | ||
'use strict'; | ||
var util = require('../util/util'); | ||
var interpolate = require('../util/interpolate'); | ||
var Point = require('point-geometry'); | ||
@@ -16,3 +16,3 @@ | ||
getCorners: getCorners, | ||
getCorners: getCorners | ||
}; | ||
@@ -83,2 +83,3 @@ | ||
if (entryOutside && exitOutside) { | ||
/*eslint no-empty: 0*/ | ||
// no collision, since blocker is out of range | ||
@@ -161,4 +162,4 @@ } else if (entryOutside) { | ||
c.sort(); | ||
for (k = 0; k < c.length; k+=2) { | ||
collisions.push([c[k], c[k+1]]); | ||
for (k = 0; k < c.length; k += 2) { | ||
collisions.push([c[k], c[k + 1]]); | ||
} | ||
@@ -183,3 +184,3 @@ | ||
for (var i = 0; i < 4; i++ ) { | ||
for (var i = 0; i < 4; i++) { | ||
cornerBoxCollisions(collisions, cornersR[i], cornersF); | ||
@@ -210,3 +211,3 @@ cornerBoxCollisions(collisions, cornersF[i], cornersR, true); | ||
// and floating point issues cause only one of the entry or exit to be counted | ||
throw('expecting an even number of intersections'); | ||
throw new Error('expecting an even number of intersections'); | ||
} | ||
@@ -217,6 +218,6 @@ | ||
// Group by pairs, where each represents a range where a collision occurs | ||
for (var k = 0; k < angles.length; k+=2) { | ||
collisions[k/2] = flip ? | ||
[2 * Math.PI - angles[k+1], 2 * Math.PI - angles[k]] : // reflect an angle around 0 degrees | ||
[angles[k], angles[k+1]]; | ||
for (var k = 0; k < angles.length; k += 2) { | ||
collisions[k / 2] = flip ? | ||
[2 * Math.PI - angles[k + 1], 2 * Math.PI - angles[k]] : // reflect an angle around 0 degrees | ||
[angles[k], angles[k + 1]]; | ||
} | ||
@@ -239,3 +240,3 @@ | ||
var discriminant = b*b - 4*a*c; | ||
var discriminant = b * b - 4 * a * c; | ||
@@ -249,7 +250,7 @@ // a collision exists only if line intersects circle at two points | ||
// hack to handle floating point representations of 0 and 1 | ||
if (0 < x1 && x1 < 1) { | ||
if (x1 > 0 && x1 < 1) { | ||
angles.push(getAngle(p1, p2, x1, corner)); | ||
} | ||
if (0 < x2 && x2 < 1) { | ||
if (x2 > 0 && x2 < 1) { | ||
angles.push(getAngle(p1, p2, x2, corner)); | ||
@@ -264,4 +265,4 @@ } | ||
return (-corner.angleWithSep( | ||
util.interp(p1.x, p2.x, d), | ||
util.interp(p1.y, p2.y, d)) + 2 * Math.PI) % (2 * Math.PI); | ||
interpolate(p1.x, p2.x, d), | ||
interpolate(p1.y, p2.y, d)) + 2 * Math.PI) % (2 * Math.PI); | ||
} | ||
@@ -268,0 +269,0 @@ |
@@ -15,3 +15,2 @@ 'use strict'; | ||
this.filter = 0; // WebGL ID | ||
this.buffer = 1; // const | ||
this.pixelRatio = 1; | ||
@@ -43,3 +42,3 @@ this.dirty = true; | ||
SpriteAtlas.prototype.resize = function(newRatio) { | ||
if (this.pixelRatio == newRatio) return false; | ||
if (this.pixelRatio === newRatio) return false; | ||
@@ -55,3 +54,3 @@ var oldRatio = this.pixelRatio; | ||
if (this.data) { | ||
var old_data = this.data; | ||
var oldData = this.data; | ||
@@ -69,14 +68,14 @@ this.data = false; | ||
var newImage = this.data; | ||
var oldImage = old_data; | ||
var oldImage = oldData; | ||
for (var y = 0; y < newHeight; y++) { | ||
var old_yoffset = Math.floor((y * oldHeight) / newHeight) * oldWidth; | ||
var new_yoffset = y * newWidth; | ||
var oldYOffset = Math.floor((y * oldHeight) / newHeight) * oldWidth; | ||
var newYOffset = y * newWidth; | ||
for (var x = 0; x < newWidth; x++) { | ||
var old_x = Math.floor((x * oldWidth) / newWidth); | ||
newImage[new_yoffset + x] = oldImage[old_yoffset + old_x]; | ||
var oldX = Math.floor((x * oldWidth) / newWidth); | ||
newImage[newYOffset + x] = oldImage[oldYOffset + oldX]; | ||
} | ||
} | ||
old_data = null; | ||
oldData = null; | ||
this.dirty = true; | ||
@@ -88,22 +87,38 @@ } | ||
function copy_bitmap(src, src_stride, src_x, src_y, dst, dst_stride, dst_x, dst_y, width, height) { | ||
var src_i = src_y * src_stride + src_x; | ||
var dst_i = dst_y * dst_stride + dst_x; | ||
for (var y = 0; y < height; y++, src_i += src_stride, dst_i += dst_stride) { | ||
for (var x = 0; x < width; x++) { | ||
dst[dst_i + x] = src[src_i + x]; | ||
function copyBitmap(src, srcStride, srcX, srcY, dst, dstStride, dstX, dstY, width, height, wrap) { | ||
var srcI = srcY * srcStride + srcX; | ||
var dstI = dstY * dstStride + dstX; | ||
var x, y; | ||
if (wrap) { | ||
// add 1 pixel wrapped padding on each side of the image | ||
dstI -= dstStride; | ||
for (y = -1; y <= height; y++, srcI = ((y + height) % height + srcY) * srcStride + srcX, dstI += dstStride) { | ||
for (x = -1; x <= width; x++) { | ||
dst[dstI + x] = src[srcI + ((x + width) % width)]; | ||
} | ||
} | ||
} else { | ||
for (y = 0; y < height; y++, srcI += srcStride, dstI += dstStride) { | ||
for (x = 0; x < width; x++) { | ||
dst[dstI + x] = src[srcI + x]; | ||
} | ||
} | ||
} | ||
} | ||
SpriteAtlas.prototype.allocateImage = function(pixel_width, pixel_height) { | ||
SpriteAtlas.prototype.allocateImage = function(pixelWidth, pixelHeight) { | ||
// Increase to next number divisible by 4, but at least 1. | ||
// This is so we can scale down the texture coordinates and pack them | ||
// into 2 bytes rather than 4 bytes. | ||
var pack_width = pixel_width + (4 - pixel_width % 4); | ||
var pack_height = pixel_height + (4 - pixel_width % 4); | ||
// Pad icons to prevent them from polluting neighbours during linear interpolation | ||
var padding = 2; | ||
var packWidth = pixelWidth + padding + (4 - (pixelWidth + padding) % 4); | ||
var packHeight = pixelHeight + padding + (4 - (pixelHeight + padding) % 4);// + 4; | ||
// We have to allocate a new area in the bin, and store an empty image in it. | ||
// Add a 1px border around every image. | ||
var rect = this.bin.allocate(pack_width, pack_height); | ||
var rect = this.bin.allocate(packWidth, packHeight); | ||
if (rect.w === 0) { | ||
@@ -113,4 +128,4 @@ return rect; | ||
rect.x += this.buffer; | ||
rect.y += this.buffer; | ||
rect.originalWidth = pixelWidth; | ||
rect.originalHeight = pixelHeight; | ||
@@ -120,3 +135,3 @@ return rect; | ||
SpriteAtlas.prototype.getImage = function(name) { | ||
SpriteAtlas.prototype.getImage = function(name, wrap) { | ||
if (this.images[name]) { | ||
@@ -143,3 +158,3 @@ return this.images[name]; | ||
this.copy(rect, pos); | ||
this.copy(rect, pos, wrap); | ||
@@ -151,3 +166,3 @@ return rect; | ||
SpriteAtlas.prototype.getPosition = function(name, repeating) { | ||
var rect = this.getImage(name); | ||
var rect = this.getImage(name, repeating); | ||
if (!rect) { | ||
@@ -170,5 +185,5 @@ return null; | ||
return { | ||
size: [ rect.w, rect.h ], | ||
tl: [ (rect.x) / this.width, (rect.y) / this.height ], | ||
br: [ (rect.x + rect.w) / this.width, (rect.y + rect.h) / this.height ] | ||
size: [rect.w, rect.h], | ||
tl: [(rect.x) / this.width, (rect.y) / this.height], | ||
br: [(rect.x + rect.w) / this.width, (rect.y + rect.h) / this.height] | ||
}; | ||
@@ -190,16 +205,16 @@ }; | ||
SpriteAtlas.prototype.copy = function(dst, src) { | ||
SpriteAtlas.prototype.copy = function(dst, src, wrap) { | ||
// if (!sprite->raster) return; | ||
if (!this.sprite.img.data) return; | ||
var src_img = new Uint32Array(this.sprite.img.data.buffer); | ||
var srcImg = new Uint32Array(this.sprite.img.data.buffer); | ||
this.allocate(); | ||
var dst_img = this.data; | ||
var dstImg = this.data; | ||
copy_bitmap( | ||
/* source buffer */ src_img, | ||
copyBitmap( | ||
/* source buffer */ srcImg, | ||
/* source stride */ this.sprite.img.width, | ||
/* source x */ src.x, | ||
/* source y */ src.y, | ||
/* dest buffer */ dst_img, | ||
/* dest buffer */ dstImg, | ||
/* dest stride */ this.width * this.pixelRatio, | ||
@@ -209,3 +224,4 @@ /* dest x */ dst.x * this.pixelRatio, | ||
/* icon dimension */ src.width, | ||
/* icon dimension */ src.height | ||
/* icon dimension */ src.height, | ||
/* wrap */ wrap | ||
); | ||
@@ -240,7 +256,7 @@ | ||
var filter_val = linear ? gl.LINEAR : gl.NEAREST; | ||
if (filter_val != this.filter) { | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter_val); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter_val); | ||
this.filter = filter_val; | ||
var filterVal = linear ? gl.LINEAR : gl.NEAREST; | ||
if (filterVal !== this.filter) { | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filterVal); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filterVal); | ||
this.filter = filterVal; | ||
} | ||
@@ -247,0 +263,0 @@ |
@@ -12,3 +12,3 @@ 'use strict'; | ||
Attribution.prototype = util.inherit(Control, { | ||
onAdd(map) { | ||
onAdd: function(map) { | ||
var className = 'mapboxgl-ctrl-attrib', | ||
@@ -18,6 +18,5 @@ container = this._container = DOM.create('div', className, map.container); | ||
this._update(); | ||
map.on('source.add', this._update.bind(this)); | ||
map.on('source.load', this._update.bind(this)); | ||
map.on('source.change', this._update.bind(this)); | ||
map.on('source.remove', this._update.bind(this)); | ||
map.on('moveend', this._updateEditLink.bind(this)); | ||
@@ -28,14 +27,14 @@ | ||
_update() { | ||
var attrObj = {}; | ||
for (var id in this._map.sources) { | ||
var source = this._map.sources[id]; | ||
if (source.attribution) { | ||
attrObj[source.attribution] = true; | ||
_update: function() { | ||
var attributions = []; | ||
if (this._map.style) { | ||
for (var id in this._map.style.sources) { | ||
var source = this._map.style.sources[id]; | ||
if (source.attribution && attributions.indexOf(source.attribution) < 0) { | ||
attributions.push(source.attribution); | ||
} | ||
} | ||
} | ||
var attributions = []; | ||
for (var i in attrObj) { | ||
attributions.push(i); | ||
} | ||
this._container.innerHTML = attributions.join(' | '); | ||
@@ -46,3 +45,3 @@ this._editLink = this._container.getElementsByClassName('mapbox-improve-map')[0]; | ||
_updateEditLink() { | ||
_updateEditLink: function() { | ||
if (this._editLink) { | ||
@@ -49,0 +48,0 @@ var center = this._map.getCenter(); |
@@ -8,9 +8,10 @@ 'use strict'; | ||
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); | ||
@@ -17,0 +18,0 @@ if (this.onRemove) this.onRemove(this._map); |
@@ -9,6 +9,8 @@ 'use strict'; | ||
function 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'; | ||
@@ -39,3 +41,3 @@ | ||
_onCompassDown(e) { | ||
_onCompassDown: function(e) { | ||
DOM.disableDrag(); | ||
@@ -50,3 +52,3 @@ | ||
_onCompassMove(e) { | ||
_onCompassMove: function(e) { | ||
var x = e.screenX, | ||
@@ -63,3 +65,3 @@ d = x < 2 ? -5 : // left edge of the screen, continue rotating | ||
_onCompassUp() { | ||
_onCompassUp: function() { | ||
document.removeEventListener('mousemove', this._onCompassMove); | ||
@@ -70,3 +72,3 @@ document.removeEventListener('mouseup', this._onCompassUp); | ||
_createButton(className, fn) { | ||
_createButton: function(className, fn) { | ||
var a = DOM.create('a', className, this._container); | ||
@@ -86,3 +88,3 @@ a.href = '#'; | ||
_drawNorth() { | ||
_drawNorth: function() { | ||
var rad = 20, | ||
@@ -89,0 +91,0 @@ width = 8, |
'use strict'; | ||
var util = require('../util/util'); | ||
var interpolate = require('../util/interpolate'); | ||
var browser = require('../util/browser'); | ||
@@ -10,7 +11,7 @@ var LatLng = require('../geo/lat_lng'); | ||
util.extend(exports, { | ||
isEasing() { | ||
isEasing: function() { | ||
return !!this._abortFn; | ||
}, | ||
stop() { | ||
stop: function() { | ||
if (this._abortFn) { | ||
@@ -26,3 +27,3 @@ this._abortFn.call(this); | ||
_ease(frame, finish, options) { | ||
_ease: function(frame, finish, options) { | ||
this._finishFn = finish; | ||
@@ -39,3 +40,3 @@ this._abortFn = browser.timed(function (t) { | ||
panBy(offset, options) { | ||
panBy: function(offset, options) { | ||
this.panTo(this.transform.center, util.extend({offset: Point.convert(offset).mult(-1)}, options)); | ||
@@ -45,3 +46,3 @@ return this; | ||
panTo(latlng, options) { | ||
panTo: function(latlng, options) { | ||
this.stop(); | ||
@@ -77,3 +78,3 @@ | ||
// Zooms to a certain zoom level with easing. | ||
zoomTo(zoom, options) { | ||
zoomTo: function(zoom, options) { | ||
this.stop(); | ||
@@ -105,3 +106,3 @@ | ||
this._ease(function(k) { | ||
tr.setZoomAround(util.interp(startZoom, zoom, k), around); | ||
tr.setZoomAround(interpolate(startZoom, zoom, k), around); | ||
this.animationLoop.set(300); // text fading | ||
@@ -119,7 +120,7 @@ this._move(true); | ||
clearTimeout(this._onZoomEnd); | ||
this._onZoomEnd = setTimeout(() => { | ||
this._onZoomEnd = setTimeout(function() { | ||
this.zooming = false; | ||
this._rerender(); | ||
this.fire('moveend'); | ||
}, 200); | ||
}.bind(this), 200); | ||
} | ||
@@ -130,11 +131,11 @@ | ||
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(); | ||
@@ -163,3 +164,3 @@ | ||
this._ease(function(k) { | ||
tr.setBearingAround(util.interp(start, bearing, k), around); | ||
tr.setBearingAround(interpolate(start, bearing, k), around); | ||
this._move(false, true); | ||
@@ -174,7 +175,7 @@ }, function() { | ||
resetNorth(options) { | ||
resetNorth: function(options) { | ||
return this.rotateTo(0, util.extend({duration: 1000}, options)); | ||
}, | ||
fitBounds(bounds, options) { | ||
fitBounds: function(bounds, options) { | ||
@@ -206,3 +207,3 @@ options = util.extend({ | ||
easeTo(latlng, zoom, bearing, options) { | ||
easeTo: function(latlng, zoom, bearing, options) { | ||
this.stop(); | ||
@@ -240,3 +241,3 @@ | ||
if (zoom !== startZoom) { | ||
tr.setZoomAround(util.interp(startZoom, zoom, k), around); | ||
tr.setZoomAround(interpolate(startZoom, zoom, k), around); | ||
} else { | ||
@@ -247,3 +248,3 @@ tr.center = tr.unproject(from.add(to.sub(from).mult(k))); | ||
if (bearing !== startBearing) { | ||
tr.bearing = util.interp(startBearing, bearing, k); | ||
tr.bearing = interpolate(startBearing, bearing, k); | ||
} | ||
@@ -262,3 +263,3 @@ | ||
flyTo(latlng, zoom, bearing, options) { | ||
flyTo: function(latlng, zoom, bearing, options) { | ||
this.stop(); | ||
@@ -327,3 +328,3 @@ | ||
this.zooming = true; | ||
if (startBearing != bearing) this.rotating = true; | ||
if (startBearing !== bearing) this.rotating = true; | ||
@@ -340,3 +341,3 @@ this.fire('movestart'); | ||
if (bearing !== startBearing) { | ||
tr.bearing = util.interp(startBearing, bearing, k); | ||
tr.bearing = interpolate(startBearing, bearing, k); | ||
} | ||
@@ -357,3 +358,3 @@ | ||
// 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); | ||
@@ -366,3 +367,3 @@ var diff = Math.abs(bearing - currentBearing); | ||
_updateEasing(duration, zoom, bezier) { | ||
_updateEasing: function(duration, zoom, bezier) { | ||
var easing; | ||
@@ -369,0 +370,0 @@ |
@@ -67,3 +67,3 @@ 'use strict'; | ||
var fromScale = map.ease && isFinite(e.delta) ? map.ease.to : map.transform.scale, | ||
duration = !isFinite(e.delta) ? 800 : e.source == 'trackpad' ? 0 : 300; | ||
duration = !isFinite(e.delta) ? 800 : e.source === 'trackpad' ? 0 : 300; | ||
@@ -70,0 +70,0 @@ map.zoomTo(map.transform.scaleZoom(fromScale * scale), { |
@@ -7,13 +7,28 @@ 'use strict'; | ||
function Hash(map) { | ||
this.map = map; | ||
window.addEventListener('hashchange', this.onhash.bind(this), false); | ||
map.on('move', util.debounce(this.updateHash.bind(this), 100)); | ||
function Hash() { | ||
util.bindAll([ | ||
'_onHashChange', | ||
'_updateHash' | ||
], this); | ||
} | ||
Hash.prototype = { | ||
onhash() { | ||
addTo: function(map) { | ||
this._map = map; | ||
window.addEventListener('hashchange', this._onHashChange, false); | ||
this._map.on('moveend', this._updateHash); | ||
return this; | ||
}, | ||
remove: function() { | ||
window.removeEventListener('hashchange', this._onHashChange, false); | ||
this._map.off('moveend', this._updateHash); | ||
delete this._map; | ||
return this; | ||
}, | ||
_onHashChange: function() { | ||
var loc = location.hash.replace('#', '').split('/'); | ||
if (loc.length >= 3) { | ||
this.map.setView([+loc[1], +loc[2]], +loc[0], +(loc[3] || 0)); | ||
this._map.setView([+loc[1], +loc[2]], +loc[0], +(loc[3] || 0)); | ||
return true; | ||
@@ -24,6 +39,6 @@ } | ||
updateHash() { | ||
var center = this.map.getCenter(), | ||
zoom = this.map.getZoom(), | ||
bearing = this.map.getBearing(), | ||
_updateHash: function() { | ||
var center = this._map.getCenter(), | ||
zoom = this._map.getZoom(), | ||
bearing = this._map.getBearing(), | ||
precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)), | ||
@@ -30,0 +45,0 @@ |
@@ -106,3 +106,3 @@ 'use strict'; | ||
function onmouseup() { | ||
panned = pos && firstPos && (pos.x != firstPos.x || pos.y != firstPos.y); | ||
panned = pos && firstPos && (pos.x !== firstPos.x || pos.y !== firstPos.y); | ||
@@ -130,5 +130,5 @@ rotating = false; | ||
else { | ||
var target = ev.toElement; | ||
while (target && target != el && target.parentNode) target = target.parentNode; | ||
if (target == el) { | ||
var target = ev.toElement || ev.target; | ||
while (target && target !== el && target.parentNode) target = target.parentNode; | ||
if (target === el) { | ||
hover(point); | ||
@@ -202,2 +202,5 @@ } | ||
// Slow down zoom if shift key is held for more precise zooming | ||
if (ev.shiftKey && value) value = value / 4; | ||
// Only fire the callback if we actually know what type of scrolling | ||
@@ -213,4 +216,4 @@ // device the user uses. | ||
// Firefox doubles the values on retina screens... | ||
if (firefox && e.deltaMode == window.WheelEvent.DOM_DELTA_PIXEL) deltaY /= browser.devicePixelRatio; | ||
if (e.deltaMode == window.WheelEvent.DOM_DELTA_LINE) deltaY *= 40; | ||
if (firefox && e.deltaMode === window.WheelEvent.DOM_DELTA_PIXEL) deltaY /= browser.devicePixelRatio; | ||
if (e.deltaMode === window.WheelEvent.DOM_DELTA_LINE) deltaY *= 40; | ||
scroll(deltaY, e); | ||
@@ -217,0 +220,0 @@ e.preventDefault(); |
161
js/ui/map.js
@@ -27,3 +27,2 @@ 'use strict'; | ||
this.transform = new Transform(options.minZoom, options.maxZoom); | ||
this.hash = options.hash && new Hash(this); | ||
@@ -40,2 +39,3 @@ if (options.maxBounds) { | ||
'_forwardTileEvent', | ||
'_onStyleLoad', | ||
'_onStyleChange', | ||
@@ -54,4 +54,5 @@ '_onSourceAdd', | ||
// don't set position from options if set through hash | ||
if (!this.hash || !this.hash.onhash()) { | ||
this._hash = options.hash && (new Hash()).addTo(this); | ||
// don't set position from options if set through hash | ||
if (!this._hash || !this._hash._onHashChange()) { | ||
this.setView(options.center, options.zoom, options.bearing); | ||
@@ -62,5 +63,7 @@ } | ||
this.stacks = {}; | ||
this._classes = {}; | ||
this.resize(); | ||
if (options.classes) this.setClasses(options.classes); | ||
if (options.style) this.setStyle(options.style); | ||
@@ -88,3 +91,3 @@ if (options.attributionControl) this.addControl(new Attribution()); | ||
addSource(id, source) { | ||
addSource: function(id, source) { | ||
this.style.addSource(id, source); | ||
@@ -94,3 +97,3 @@ return this; | ||
removeSource(id) { | ||
removeSource: function(id) { | ||
this.style.removeSource(id); | ||
@@ -100,3 +103,3 @@ return this; | ||
addControl(control) { | ||
addControl: function(control) { | ||
control.addTo(this); | ||
@@ -107,3 +110,3 @@ return this; | ||
// Set the map's center, zoom, and bearing | ||
setView(center, zoom, bearing) { | ||
setView: function(center, zoom, bearing) { | ||
this.stop(); | ||
@@ -125,20 +128,48 @@ | ||
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: function(klass, options) { | ||
if (this._classes[klass]) return; | ||
this._classes[klass] = true; | ||
if (this.style) this.style._cascade(this._classes, options); | ||
}, | ||
removeClass: function(klass, options) { | ||
if (!this._classes[klass]) return; | ||
delete this._classes[klass]; | ||
if (this.style) this.style._cascade(this._classes, options); | ||
}, | ||
setClasses: function(klasses, options) { | ||
this._classes = {}; | ||
for (var i = 0; i < klasses.length; i++) { | ||
this._classes[klasses[i]] = true; | ||
} | ||
if (this.style) this.style._cascade(this._classes, options); | ||
}, | ||
hasClass: function(klass) { | ||
return !!this._classes[klass]; | ||
}, | ||
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; | ||
@@ -166,3 +197,3 @@ | ||
getBounds() { | ||
getBounds: function() { | ||
return new LatLngBounds( | ||
@@ -173,10 +204,10 @@ this.transform.pointLocation(new Point(0, 0)), | ||
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); | ||
@@ -186,6 +217,6 @@ return this; | ||
setStyle(style) { | ||
setStyle: function(style) { | ||
if (this.style) { | ||
this.style | ||
.off('load', this._forwardStyleEvent) | ||
.off('load', this._onStyleLoad) | ||
.off('error', this._forwardStyleEvent) | ||
@@ -207,3 +238,3 @@ .off('change', this._onStyleChange) | ||
this.style = null; | ||
return; | ||
return this; | ||
} else if (style instanceof Style) { | ||
@@ -216,3 +247,3 @@ this.style = style; | ||
this.style | ||
.on('load', this._forwardStyleEvent) | ||
.on('load', this._onStyleLoad) | ||
.on('error', this._forwardStyleEvent) | ||
@@ -233,4 +264,24 @@ .on('change', this._onStyleChange) | ||
_move (zoom, rotate) { | ||
setPaintProperty: function(layer, name, value, klass) { | ||
this.style.setPaintProperty(layer, name, value, klass); | ||
this.style._cascade(this._classes); | ||
this.update(true); | ||
return this; | ||
}, | ||
getPaintProperty: function(layer, name, klass) { | ||
return this.style.getPaintProperty(layer, name, klass); | ||
}, | ||
setLayoutProperty: function(layer, name, value) { | ||
this.style.setLayoutProperty(layer, name, value); | ||
return this; | ||
}, | ||
getLayoutProperty: function(layer, name) { | ||
return this.style.getLayoutProperty(layer, name); | ||
}, | ||
_move: function(zoom, rotate) { | ||
this.update(zoom).fire('move'); | ||
@@ -246,3 +297,3 @@ | ||
_setupContainer() { | ||
_setupContainer: function() { | ||
var id = this.options.container; | ||
@@ -254,3 +305,3 @@ var container = this.container = typeof id === 'string' ? document.getElementById(id) : id; | ||
_setupPainter() { | ||
_setupPainter: function() { | ||
var gl = this.canvas.getWebGLContext(); | ||
@@ -266,3 +317,3 @@ | ||
_contextLost(event) { | ||
_contextLost: function(event) { | ||
event.preventDefault(); | ||
@@ -274,3 +325,3 @@ if (this._frameId) { | ||
_contextRestored() { | ||
_contextRestored: function() { | ||
this._setupPainter(); | ||
@@ -283,3 +334,3 @@ this.resize(); | ||
loaded() { | ||
loaded: function() { | ||
if (this._styleDirty || this._sourcesDirty) | ||
@@ -292,3 +343,3 @@ return false; | ||
update(updateStyle) { | ||
update: function(updateStyle) { | ||
if (!this.style) return this; | ||
@@ -305,6 +356,6 @@ | ||
// 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); | ||
} | ||
@@ -314,11 +365,22 @@ | ||
this._sourcesDirty = false; | ||
this._sourcesDirtyTimeout = setTimeout(() => { | ||
this._sourcesDirtyTimeout = setTimeout(function() { | ||
this._sourcesDirtyTimeout = null; | ||
}, 50); | ||
this.style._updateSources(); | ||
}.bind(this), 50); | ||
this.style._updateSources(this.transform); | ||
} | ||
this.painter.render(this.style); | ||
this.painter.render(this.style, { | ||
debug: this.debug, | ||
vertices: this.vertices, | ||
rotating: this.rotating, | ||
zooming: this.zooming | ||
}); | ||
this.fire('render'); | ||
if (this.loaded() && !this._loaded) { | ||
this._loaded = true; | ||
this.fire('load'); | ||
} | ||
this._frameId = null; | ||
@@ -337,3 +399,4 @@ | ||
remove() { | ||
remove: function() { | ||
if (this._hash) this._hash.remove(); | ||
browser.cancelFrame(this._frameId); | ||
@@ -345,3 +408,3 @@ clearTimeout(this._sourcesDirtyTimeout); | ||
_rerender() { | ||
_rerender: function() { | ||
if (this.style && !this._frameId) { | ||
@@ -352,15 +415,20 @@ 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)); | ||
}, | ||
_onStyleChange(e) { | ||
_onStyleLoad: function(e) { | ||
this.style._cascade(this._classes, {transition: false}); | ||
this._forwardStyleEvent(e); | ||
}, | ||
_onStyleChange: function(e) { | ||
this.update(true); | ||
@@ -370,3 +438,3 @@ this._forwardStyleEvent(e); | ||
_onSourceAdd(e) { | ||
_onSourceAdd: function(e) { | ||
var source = e.source; | ||
@@ -378,3 +446,3 @@ if (source.onAdd) | ||
_onSourceRemove(e) { | ||
_onSourceRemove: function(e) { | ||
var source = e.source; | ||
@@ -386,3 +454,3 @@ if (source.onRemove) | ||
_onSourceUpdate(e) { | ||
_onSourceUpdate: function(e) { | ||
this.update(); | ||
@@ -405,7 +473,2 @@ this._forwardSourceEvent(e); | ||
// polygon antialiasing | ||
_antialiasing: true, | ||
get antialiasing() { return this._antialiasing; }, | ||
set antialiasing(value) { this._antialiasing = value; this.update(); }, | ||
// show vertices | ||
@@ -412,0 +475,0 @@ _vertices: false, |
@@ -18,3 +18,3 @@ 'use strict'; | ||
if (data.type == '<response>') { | ||
if (data.type === '<response>') { | ||
callback = this.callbacks[data.id]; | ||
@@ -25,3 +25,3 @@ delete this.callbacks[data.id]; | ||
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({ | ||
@@ -33,3 +33,3 @@ type: '<response>', | ||
}, buffers); | ||
}); | ||
}.bind(this)); | ||
} else { | ||
@@ -36,0 +36,0 @@ this.parent[data.type](data.data); |
@@ -14,3 +14,3 @@ 'use strict'; | ||
fn.call(ctx, 1); | ||
return; | ||
return null; | ||
} | ||
@@ -17,0 +17,0 @@ |
'use strict'; | ||
var frameName = (function() { | ||
if (window.requestAnimationFrame) return 'requestAnimationFrame'; | ||
if (window.mozRequestAnimationFrame) return 'mozRequestAnimationFrame'; | ||
if (window.webkitRequestAnimationFrame) return 'webkitRequestAnimationFrame'; | ||
if (window.msRequestAnimationFrame) return 'msRequestAnimationFrame'; | ||
})(); | ||
var frame = window.requestAnimationFrame || | ||
window.mozRequestAnimationFrame || | ||
window.webkitRequestAnimationFrame || | ||
window.msRequestAnimationFrame; | ||
exports.frame = function(fn) { | ||
return window[frameName](fn); | ||
return frame(fn); | ||
}; | ||
var cancel = window.cancelAnimationFrame || | ||
window.mozCancelAnimationFrame || | ||
window.webkitCancelAnimationFrame || | ||
window.msCancelAnimationFrame; | ||
exports.cancelFrame = function(id) { | ||
(window.cancelRequestAnimationFrame || | ||
window.mozCancelRequestAnimationFrame || | ||
window.webkitCancelRequestAnimationFrame || | ||
window.msCancelRequestAnimationFrame)(id); | ||
cancel(id); | ||
}; | ||
@@ -24,3 +24,3 @@ | ||
fn.call(ctx, 1); | ||
return; | ||
return null; | ||
} | ||
@@ -70,3 +70,3 @@ | ||
function() { | ||
return !!(Function.prototype && Function.prototype.bind), | ||
return !!(Function.prototype && Function.prototype.bind) && | ||
!!(Object.keys && | ||
@@ -73,0 +73,0 @@ Object.create && |
@@ -21,3 +21,3 @@ 'use strict'; | ||
if (absolute) { | ||
blob = new Blob(['importScripts("' + workerFile + '");'], {type : 'application/javascript'}); | ||
blob = new Blob(['importScripts("' + workerFile + '");'], {type: 'application/javascript'}); | ||
url = window.URL.createObjectURL(blob); | ||
@@ -36,3 +36,3 @@ } else { | ||
Dispatcher.prototype = { | ||
broadcast(type, data) { | ||
broadcast: function(type, data) { | ||
for (var i = 0; i < this.actors.length; i++) { | ||
@@ -43,3 +43,3 @@ 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)) { | ||
@@ -54,3 +54,3 @@ // Use round robin to send requests to web workers. | ||
remove() { | ||
remove: function() { | ||
for (var i = 0; i < this.actors.length; i++) { | ||
@@ -57,0 +57,0 @@ this.actors[i].target.terminate(); |
'use strict'; | ||
module.exports = { | ||
HTTP_URL: 'http://a.tiles.mapbox.com/v4', | ||
HTTPS_URL: 'https://a.tiles.mapbox.com/v4', | ||
HTTP_URL: 'http://a.tiles.mapbox.com', | ||
HTTPS_URL: 'https://a.tiles.mapbox.com', | ||
FORCE_HTTPS: false, | ||
REQUIRE_ACCESS_TOKEN: true | ||
}; |
@@ -16,7 +16,7 @@ 'use strict'; | ||
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); | ||
} | ||
@@ -40,13 +40,13 @@ }; | ||
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 | ||
} | ||
}; |
@@ -6,3 +6,3 @@ 'use strict'; | ||
module.exports = { | ||
on(type, fn) { | ||
on: function(type, fn) { | ||
this._events = this._events || {}; | ||
@@ -15,3 +15,3 @@ this._events[type] = this._events[type] || []; | ||
off(type, fn) { | ||
off: function(type, fn) { | ||
if (!type) { | ||
@@ -40,3 +40,3 @@ // clear all listeners if no arguments specified | ||
once(type, fn) { | ||
once: function(type, fn) { | ||
var wrapper = function(data) { | ||
@@ -50,3 +50,3 @@ this.off(type, wrapper); | ||
fire(type, data) { | ||
fire: function(type, data) { | ||
if (!this.listens(type)) return this; | ||
@@ -67,5 +67,5 @@ | ||
listens(type) { | ||
listens: function(type) { | ||
return !!(this._events && this._events[type]); | ||
} | ||
}; |
@@ -10,3 +10,3 @@ 'use strict'; | ||
function readFontstacks(tag, stacks, pbf) { | ||
if (tag == 1) { | ||
if (tag === 1) { | ||
var fontstack = pbf.readMessage(readFontstack, {glyphs: {}}); | ||
@@ -18,5 +18,5 @@ stacks[fontstack.name] = fontstack; | ||
function readFontstack(tag, fontstack, pbf) { | ||
if (tag == 1) fontstack.name = pbf.readString(); | ||
else if (tag == 2) fontstack.range = pbf.readString(); | ||
else if (tag == 3) { | ||
if (tag === 1) fontstack.name = pbf.readString(); | ||
else if (tag === 2) fontstack.range = pbf.readString(); | ||
else if (tag === 3) { | ||
var glyph = pbf.readMessage(readGlyph, {}); | ||
@@ -28,9 +28,9 @@ fontstack.glyphs[glyph.id] = glyph; | ||
function readGlyph(tag, glyph, pbf) { | ||
if (tag == 1) glyph.id = pbf.readVarint(); | ||
else if (tag == 2) glyph.bitmap = pbf.readBytes(); | ||
else if (tag == 3) glyph.width = pbf.readVarint(); | ||
else if (tag == 4) glyph.height = pbf.readVarint(); | ||
else if (tag == 5) glyph.left = pbf.readSVarint(); | ||
else if (tag == 6) glyph.top = pbf.readSVarint(); | ||
else if (tag == 7) glyph.advance = pbf.readVarint(); | ||
if (tag === 1) glyph.id = pbf.readVarint(); | ||
else if (tag === 2) glyph.bitmap = pbf.readBytes(); | ||
else if (tag === 3) glyph.width = pbf.readVarint(); | ||
else if (tag === 4) glyph.height = pbf.readVarint(); | ||
else if (tag === 5) glyph.left = pbf.readSVarint(); | ||
else if (tag === 6) glyph.top = pbf.readSVarint(); | ||
else if (tag === 7) glyph.advance = pbf.readVarint(); | ||
} |
'use strict'; | ||
var config = require('./config'); | ||
var browser = require('./browser'); | ||
function normalizeURL(url, accessToken) { | ||
function normalizeURL(url, pathPrefix, accessToken) { | ||
accessToken = accessToken || config.ACCESS_TOKEN; | ||
@@ -14,5 +15,5 @@ | ||
var https = config.FORCE_HTTPS || | ||
(typeof document !== 'undefined' && 'https:' === document.location.protocol); | ||
(typeof document !== 'undefined' && document.location.protocol === 'https:'); | ||
url = url.replace(/^mapbox:\/\//, (https ? config.HTTPS_URL : config.HTTP_URL) + '/'); | ||
url = url.replace(/^mapbox:\/\//, (https ? config.HTTPS_URL : config.HTTP_URL) + pathPrefix); | ||
url += url.indexOf('?') !== -1 ? '&access_token=' : '?access_token='; | ||
@@ -32,2 +33,10 @@ | ||
module.exports.normalizeStyleURL = function(url, accessToken) { | ||
var match = url.match(/^mapbox:\/\/([^.]+)/); | ||
if (!match) | ||
return url; | ||
return normalizeURL(url, '/styles/v1/' + match[1] + '/', accessToken); | ||
}; | ||
module.exports.normalizeSourceURL = function(url, accessToken) { | ||
@@ -37,3 +46,3 @@ if (!url.match(/^mapbox:\/\//)) | ||
url = normalizeURL(url + '.json', accessToken); | ||
url = normalizeURL(url + '.json', '/v4/', accessToken); | ||
@@ -52,3 +61,9 @@ // TileJSON requests need a secure flag appended to their URLs so | ||
return normalizeURL(url, accessToken); | ||
return normalizeURL(url, '/v4/', accessToken); | ||
}; | ||
module.exports.normalizeTileURL = function(url, sourceUrl) { | ||
if (!sourceUrl || !sourceUrl.match(/^mapbox:\/\//)) | ||
return url; | ||
return url.replace(/\.png(?=$|\?)/, browser.devicePixelRatio >= 2 ? '@2x.png' : '.png'); | ||
}; |
@@ -5,3 +5,3 @@ 'use strict'; | ||
var tokenPattern = /{([\w-]+)}/; | ||
var tokenPattern = /{([^{}()\[\]<>$=:;.,^]+)}/; | ||
@@ -8,0 +8,0 @@ function resolveTokens(properties, expression) { |
@@ -22,6 +22,2 @@ 'use strict'; | ||
exports.interp = function (a, b, t) { | ||
return (a * (1 - t)) + (b * t); | ||
}; | ||
exports.premultiply = function (c) { | ||
@@ -45,2 +41,10 @@ c[0] *= c[3]; | ||
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) { | ||
@@ -63,5 +67,8 @@ var remaining = array.length; | ||
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]; | ||
} | ||
} | ||
@@ -144,3 +151,5 @@ return dest; | ||
exports.bindAll = function(fns, context) { | ||
fns.forEach((fn) => context[fn] = context[fn].bind(context)); | ||
fns.forEach(function(fn) { | ||
context[fn] = context[fn].bind(context); | ||
}); | ||
}; |
{ | ||
"name": "mapbox-gl", | ||
"description": "A WebGL interactive maps library", | ||
"version": "0.5.2", | ||
"version": "0.6.0", | ||
"main": "js/mapbox-gl.js", | ||
@@ -15,3 +15,2 @@ "license": "BSD", | ||
"envify": "2.0.1", | ||
"es6ify": "^1.5.1", | ||
"feature-filter": "1.0.0", | ||
@@ -22,4 +21,4 @@ "geojson-rewind": "~0.1.0", | ||
"glify": "0.4.2", | ||
"mapbox-gl-style-spec": "6.0.0", | ||
"minifyify": "^4.4.0", | ||
"mapbox-gl-style-spec": "7.0.0", | ||
"minifyify": "^6.1.0", | ||
"pbf": "^1.2.0", | ||
@@ -36,5 +35,5 @@ "pngjs": "^0.4.0", | ||
"browserify": "~5.9.1", | ||
"eslint": "^0.13.0", | ||
"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#master", | ||
"mkdirp": "^0.5.0", | ||
@@ -46,4 +45,2 @@ "mocha": "~1.21.3", | ||
"tape": "2.14.0", | ||
"traceur": "0.0.74", | ||
"traceur-source-maps": "^1.0.6", | ||
"watchify": "1.0.1", | ||
@@ -57,3 +54,2 @@ "zuul": "1.10.0" | ||
"transform": [ | ||
"es6ify", | ||
"envify", | ||
@@ -72,3 +68,3 @@ "glify", | ||
"start": "npm run watch & serve", | ||
"lint": "jshint js test --exclude=js/lib/*", | ||
"lint": "eslint js test", | ||
"test": "npm run lint && tape test/js/*/*.js", | ||
@@ -81,28 +77,42 @@ "test-suite": "node test/render.test.js || true", | ||
"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": { | ||
"eslintConfig": { | ||
"rules": { | ||
"no-use-before-define": [ | ||
2, | ||
"nofunc" | ||
], | ||
"camelcase": 2, | ||
"space-after-function-name": 2, | ||
"space-in-parens": 2, | ||
"space-before-blocks": 2, | ||
"space-after-keywords": 2, | ||
"comma-style": 2, | ||
"no-lonely-if": 2, | ||
"no-else-return": 2, | ||
"new-cap": 2, | ||
"no-empty": 2, | ||
"no-new": 2, | ||
"no-multi-spaces": 0, | ||
"space-in-brackets": 0, | ||
"quotes": 0, | ||
"no-underscore-dangle": 0, | ||
"curly": 0, | ||
"no-constant-condition": 0, | ||
"no-native-reassign": 0, | ||
"no-shadow": 0, | ||
"key-spacing": 0 | ||
}, | ||
"env": { | ||
"node": true, | ||
"browser": true | ||
}, | ||
"globals": { | ||
"require": false, | ||
"module": false, | ||
"exports": false, | ||
"console": false, | ||
"self": false, | ||
"__dirname": false | ||
}, | ||
"predef": [ | ||
"-Map", | ||
"-Worker" | ||
], | ||
"newcap": false, | ||
"browser": true, | ||
"node": true, | ||
"globalstrict": true, | ||
"trailing": true, | ||
"undef": true, | ||
"unused": true, | ||
"debug": true, | ||
"esnext": true | ||
"Map": true, | ||
"Buffer": true, | ||
"Worker": true | ||
} | ||
} | ||
} |
{ | ||
"version": 6, | ||
"version": 7, | ||
"constants": { | ||
@@ -4,0 +4,0 @@ "@land": "#eee", |
@@ -12,5 +12,4 @@ 'use strict'; | ||
}); | ||
} else { | ||
return require('gl').createContext(512, 512); | ||
} | ||
return require('gl').createContext(512, 512); | ||
}; |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var Buffer = require('../../../js/data/buffer/buffer'); | ||
@@ -8,0 +5,0 @@ |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var Buffer = require('../../../js/data/buffer/buffer'); | ||
@@ -8,0 +5,0 @@ var FillElementsBuffer = require('../../../js/data/buffer/fill_elements_buffer'); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var Buffer = require('../../../js/data/buffer/buffer'); | ||
@@ -8,0 +5,0 @@ var FillVertexBuffer = require('../../../js/data/buffer/fill_vertex_buffer'); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var Buffer = require('../../../js/data/buffer/buffer'); | ||
@@ -8,0 +5,0 @@ var IconVertexBuffer = require('../../../js/data/buffer/icon_vertex_buffer'); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var Buffer = require('../../../js/data/buffer/buffer'); | ||
@@ -8,0 +5,0 @@ var LineElementBuffer = require('../../../js/data/buffer/line_element_buffer'); |
@@ -5,5 +5,2 @@ 'use strict'; | ||
var Point = require('point-geometry'); | ||
require('../../bootstrap'); | ||
var Buffer = require('../../../js/data/buffer/buffer'); | ||
@@ -10,0 +7,0 @@ var LineVertexBuffer = require('../../../js/data/buffer/line_vertex_buffer'); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var createBucket = require('../../../js/data/create_bucket'); | ||
@@ -8,0 +5,0 @@ var BufferSet = require('../../../js/data/buffer/buffer_set'); |
@@ -7,9 +7,7 @@ 'use strict'; | ||
var Protobuf = require('pbf'); | ||
require('../../bootstrap'); | ||
var FeatureTree = require('../../../js/data/feature_tree'); | ||
var path = require('path'); | ||
test('featuretree', function(t) { | ||
var tile = new vt.VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(__dirname + '/../../fixtures/mbsv5-6-18-23.vector.pbf')))); | ||
var tile = new vt.VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(path.join(__dirname, '/../../fixtures/mbsv5-6-18-23.vector.pbf'))))); | ||
function getType(feature) { | ||
@@ -29,3 +27,3 @@ return vt.VectorTileFeature.types[feature.type]; | ||
x: 0, | ||
y: 0, | ||
y: 0 | ||
}, function(err, features) { | ||
@@ -39,3 +37,3 @@ t.deepEqual(features, []); | ||
test('featuretree with args', function(t) { | ||
var tile = new vt.VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(__dirname + '/../../fixtures/mbsv5-6-18-23.vector.pbf')))); | ||
var tile = new vt.VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(path.join(__dirname, '/../../fixtures/mbsv5-6-18-23.vector.pbf'))))); | ||
function getType(feature) { | ||
@@ -57,3 +55,3 @@ return vt.VectorTileFeature.types[feature.type]; | ||
x: 0, | ||
y: 0, | ||
y: 0 | ||
}, function(err, features) { | ||
@@ -67,3 +65,3 @@ t.deepEqual(features, []); | ||
test('featuretree query', function(t) { | ||
var tile = new vt.VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(__dirname + '/../../fixtures/mbsv5-6-18-23.vector.pbf')))); | ||
var tile = new vt.VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(path.join(__dirname, '/../../fixtures/mbsv5-6-18-23.vector.pbf'))))); | ||
function getType(feature) { | ||
@@ -76,16 +74,6 @@ return vt.VectorTileFeature.types[feature.type]; | ||
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<tile.layers.water._features.length; i++) { | ||
for (var i = 0; i < tile.layers.water._features.length; i++) { | ||
var feature = tile.layers.water.feature(i); | ||
ft.insert(feature.bbox(), bucketInfo, feature); | ||
ft.insert(feature.bbox(), ['water'], feature); | ||
} | ||
@@ -100,3 +88,3 @@ | ||
x: 1842, | ||
y: 2014, | ||
y: 2014 | ||
}, function(err, features) { | ||
@@ -106,5 +94,3 @@ t.notEqual(features.length, 0, 'non-empty results for queryFeatures'); | ||
t.ok(f.$type, 'result has $type'); | ||
t.ok(f.layer, 'result has layer'); | ||
t.equal(f.layer.id, 'water'); | ||
t.equal(f.layer.type, 'fill'); | ||
t.deepEqual(f.layers, ['water']); | ||
t.ok(f.properties, 'result has properties'); | ||
@@ -111,0 +97,0 @@ t.notEqual(f.properties.osm_id, undefined, 'properties has osm_id by default'); |
@@ -8,10 +8,8 @@ 'use strict'; | ||
var Point = require('point-geometry'); | ||
require('../../bootstrap'); | ||
var FillBucket = require('../../../js/data/fill_bucket'); | ||
var BufferSet = require('../../../js/data/buffer/buffer_set'); | ||
var path = require('path'); | ||
// Load a fill feature from fixture tile. | ||
var vt = new VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(__dirname + '/../../fixtures/mbsv5-6-18-23.vector.pbf')))); | ||
var vt = new VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(path.join(__dirname, '/../../fixtures/mbsv5-6-18-23.vector.pbf'))))); | ||
var feature = vt.layers.water.feature(0); | ||
@@ -24,5 +22,4 @@ | ||
var info = {}; | ||
var buffers = new BufferSet(); | ||
var bucket = new FillBucket(info, buffers); | ||
var bucket = new FillBucket(buffers); | ||
t.ok(bucket); | ||
@@ -48,2 +45,1 @@ | ||
}); | ||
@@ -5,8 +5,6 @@ 'use strict'; | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var Protobuf = require('pbf'); | ||
var VectorTile = require('vector-tile').VectorTile; | ||
var Point = require('point-geometry'); | ||
require('../../bootstrap'); | ||
var LineBucket = require('../../../js/data/line_bucket'); | ||
@@ -16,9 +14,8 @@ var BufferSet = require('../../../js/data/buffer/buffer_set'); | ||
// Load a line feature from fixture tile. | ||
var vt = new VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(__dirname + '/../../fixtures/mbsv5-6-18-23.vector.pbf')))); | ||
var vt = new VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(path.join(__dirname, '/../../fixtures/mbsv5-6-18-23.vector.pbf'))))); | ||
var feature = vt.layers.road.feature(0); | ||
test('LineBucket', function(t) { | ||
var info = {}; | ||
var buffers = new BufferSet(); | ||
var bucket = new LineBucket(info, buffers); | ||
var bucket = new LineBucket(buffers, {}); | ||
t.ok(bucket); | ||
@@ -58,2 +55,1 @@ | ||
}); | ||
@@ -5,7 +5,5 @@ 'use strict'; | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var Protobuf = require('pbf'); | ||
var VectorTile = require('vector-tile').VectorTile; | ||
require('../../bootstrap'); | ||
var SymbolBucket = require('../../../js/data/symbol_bucket'); | ||
@@ -18,11 +16,12 @@ var BufferSet = require('../../../js/data/buffer/buffer_set'); | ||
// Load a point feature from fixture tile. | ||
var vt = new VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(__dirname + '/../../fixtures/mbsv5-6-18-23.vector.pbf')))); | ||
var vt = new VectorTile(new Protobuf(new Uint8Array(fs.readFileSync(path.join(__dirname, '/../../fixtures/mbsv5-6-18-23.vector.pbf'))))); | ||
var feature = vt.layers.place_label.feature(10); | ||
var glyphs = JSON.parse(fs.readFileSync(__dirname + '/../../fixtures/fontstack-glyphs.json')); | ||
var glyphs = JSON.parse(fs.readFileSync(path.join(__dirname, '/../../fixtures/fontstack-glyphs.json'))); | ||
test('SymbolBucket', function(t) { | ||
/*eslint new-cap: 0*/ | ||
var info = new LayoutProperties.symbol({ type: 'symbol', 'text-font': 'Test' }); | ||
var buffers = new BufferSet(); | ||
var collision = new Collision(6, 4096, 512); | ||
var atlas = new GlyphAtlas(1024,1024); | ||
var atlas = new GlyphAtlas(1024, 1024); | ||
var rects = {}; | ||
@@ -35,3 +34,3 @@ for (var id in glyphs) { | ||
function bucketSetup() { | ||
var bucket = new SymbolBucket(info, buffers, collision); | ||
var bucket = new SymbolBucket(buffers, info, collision); | ||
bucket.textFeatures = ['abcde']; | ||
@@ -64,2 +63,1 @@ bucket.stacks = { 'Test': { | ||
}); | ||
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var LatLng = require('../../../js/geo/lat_lng'); | ||
@@ -8,0 +5,0 @@ var LatLngBounds = require('../../../js/geo/lat_lng_bounds'); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var LatLng = require('../../../js/geo/lat_lng'); | ||
@@ -13,2 +10,3 @@ | ||
t.throws(function() { | ||
/*eslint no-new: 0*/ | ||
new LatLng('foo', 0); | ||
@@ -15,0 +13,0 @@ }, "Invalid LatLng object: (foo, 0)", 'detects and throws on invalid input'); |
@@ -5,5 +5,2 @@ 'use strict'; | ||
var Point = require('point-geometry'); | ||
require('../../bootstrap'); | ||
var Transform = require('../../../js/geo/transform'); | ||
@@ -10,0 +7,0 @@ var LatLng = require('../../../js/geo/lat_lng'); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var createContext = require('./../../gl'); | ||
@@ -8,0 +5,0 @@ var glutil = require('../../../js/render/gl_util'); |
'use strict'; | ||
var test = require('tape'); | ||
var GeoJSONSource = require('../../../js/source/geojson_source'); | ||
var Transform = require('../../../js/geo/transform'); | ||
var LatLng = require('../../../js/geo/lat_lng'); | ||
require('../../bootstrap'); | ||
var hawkHill = { | ||
"type": "FeatureCollection", | ||
"features": [{ | ||
"type": "Feature", | ||
"properties": {}, | ||
"geometry": { | ||
"type": "LineString", | ||
"coordinates": [ | ||
[-122.48369693756104, 37.83381888486939], | ||
[-122.48348236083984, 37.83317489144141], | ||
[-122.48339653015138, 37.83270036637107], | ||
[-122.48356819152832, 37.832056363179625], | ||
[-122.48404026031496, 37.83114119107971], | ||
[-122.48404026031496, 37.83049717427869], | ||
[-122.48348236083984, 37.829920943955045], | ||
[-122.48356819152832, 37.82954808664175], | ||
[-122.48507022857666, 37.82944639795659], | ||
[-122.48610019683838, 37.82880236636284], | ||
[-122.48695850372314, 37.82931081282506], | ||
[-122.48700141906738, 37.83080223556934], | ||
[-122.48751640319824, 37.83168351665737], | ||
[-122.48803138732912, 37.832158048267786], | ||
[-122.48888969421387, 37.83297152392784], | ||
[-122.48987674713133, 37.83263257682617], | ||
[-122.49043464660643, 37.832937629287755], | ||
[-122.49125003814696, 37.832429207817725], | ||
[-122.49163627624512, 37.832564787218985], | ||
[-122.49223709106445, 37.83337825839438], | ||
[-122.49378204345702, 37.83368330777276] | ||
] | ||
} | ||
}] | ||
}; | ||
var GeoJSONSource = require('../../../js/source/geojson_source'); | ||
test('GeoJSONSource#setData', function(t) { | ||
@@ -24,1 +57,92 @@ t.test('returns self', function(t) { | ||
}); | ||
test('GeoJSONSource#update', function(t) { | ||
var transform = new Transform(); | ||
transform.width = 200; | ||
transform.height = 200; | ||
transform.setZoomAround(15, LatLng.convert([37.830348, -122.486052])); | ||
t.test('sends parse request to dispatcher', function(t) { | ||
var source = new GeoJSONSource({data: {}}); | ||
source.dispatcher = { | ||
send: function(message) { | ||
t.equal(message, 'parse geojson'); | ||
t.end(); | ||
} | ||
}; | ||
source.update(transform); | ||
}); | ||
t.test('emits change on success', function(t) { | ||
var source = new GeoJSONSource({data: {}}); | ||
source.dispatcher = { | ||
send: function(message, args, callback) { | ||
setTimeout(callback, 0); | ||
} | ||
}; | ||
source.update(transform); | ||
source.on('change', function() { | ||
t.end(); | ||
}); | ||
}); | ||
t.test('emits error on failure', function(t) { | ||
var source = new GeoJSONSource({data: {}}); | ||
source.dispatcher = { | ||
send: function(message, args, callback) { | ||
setTimeout(callback.bind(null, 'error'), 0); | ||
} | ||
}; | ||
source.update(transform); | ||
source.on('error', function(err) { | ||
t.equal(err.error, 'error'); | ||
t.end(); | ||
}); | ||
}); | ||
t.test('clears previous tiles', function(t) { | ||
var source = new GeoJSONSource({data: hawkHill}); | ||
// TODO: decouple | ||
source.used = true; | ||
source.dispatcher = { | ||
send: function(message, args, callback) { | ||
setTimeout(callback, 0); | ||
} | ||
}; | ||
source.map = { | ||
options: { | ||
maxZoom: 20 | ||
} | ||
}; | ||
source.style = { | ||
}; | ||
source.glyphAtlas = { | ||
removeGlyphs: function() {} | ||
}; | ||
source.update(transform); | ||
source.once('change', function() { | ||
source.update(transform); // Load tiles | ||
source.setData({}); | ||
source.update(transform); | ||
source.once('change', function() { | ||
t.deepEqual(source._pyramid.renderedIDs(), []); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
}); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var Wrapper = require('../../../js/source/geojson_wrapper'); | ||
@@ -8,0 +5,0 @@ |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var TileCoord = require('../../../js/source/tile_coord'); | ||
@@ -60,5 +57,5 @@ | ||
t.test('returns undefined for z0', function(t) { | ||
t.equal(TileCoord.parent(0), undefined); | ||
t.equal(TileCoord.parent(32), undefined); | ||
t.test('returns null for z0', function(t) { | ||
t.equal(TileCoord.parent(0), null); | ||
t.equal(TileCoord.parent(32), null); | ||
t.end(); | ||
@@ -65,0 +62,0 @@ }); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var WorkerTile = require('../../../js/source/worker_tile'); | ||
@@ -15,2 +12,3 @@ var Wrapper = require('../../../js/source/geojson_wrapper'); | ||
type: 'fill', | ||
layout: {}, | ||
compare: function () { return true; } | ||
@@ -26,7 +24,26 @@ }]; | ||
var tile = new WorkerTile('', 0, 20, 512, 'source', 1); | ||
tile.parse(new Wrapper(features), buckets, {}, function(err, result) { | ||
t.ok(result.buffers, 'buffers'); | ||
t.ok(result.elementGroups, 'element groups'); | ||
t.end(); | ||
t.test('basic worker tile', function(t) { | ||
tile.parse(new Wrapper(features), buckets, {}, function(err, result) { | ||
t.equal(err, null); | ||
t.ok(result.buffers, 'buffers'); | ||
t.ok(result.elementGroups, 'element groups'); | ||
t.end(); | ||
}); | ||
}); | ||
t.test('hidden layers', function(t) { | ||
buckets.push({ | ||
id: 'test-hidden', | ||
source: 'source', | ||
type: 'fill', | ||
layout: { visibility: 'none' }, | ||
compare: function () { return true; } | ||
}); | ||
tile.parse(new Wrapper(features), buckets, {}, function(err, result) { | ||
t.equal(err, null); | ||
t.equal(Object.keys(result.elementGroups).length, 1, 'element groups exclude hidden layer'); | ||
t.end(); | ||
}); | ||
}); | ||
}); |
@@ -7,5 +7,2 @@ 'use strict'; | ||
var http = require('http'); | ||
require('../../bootstrap'); | ||
var Worker = require('../../../js/source/worker'); | ||
@@ -30,11 +27,2 @@ | ||
test('set buckets', function(t) { | ||
t.test('creates filter functions', function(t) { | ||
var worker = new Worker(_self); | ||
worker['set buckets']([{}]); | ||
t.ok(worker.buckets[0].compare()); | ||
t.end(); | ||
}); | ||
}); | ||
test('load tile', function(t) { | ||
@@ -41,0 +29,0 @@ t.test('calls callback on error', function(t) { |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var AnimationLoop = require('../../../js/style/animation_loop'); | ||
@@ -8,0 +5,0 @@ |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var PaintProperties = require('../../../js/style/paint_properties'); | ||
test('PaintProperties', function(t) { | ||
/*eslint new-cap: 0*/ | ||
test('resolves default values', function(t) { | ||
@@ -11,0 +10,0 @@ var f = new PaintProperties.fill(); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var StyleConstant = require('../../../js/style/style_constant'); | ||
test('StyleConstant.resolve', function(t) { | ||
test('resolves simple types', function(t) { | ||
t.deepEqual(StyleConstant.resolve({"a": "a"}, {}), {"a": "a"}); | ||
t.deepEqual(StyleConstant.resolve({"a": "a", "b": "b"}, {}), {"a": "a", "b": "b"}); | ||
t.deepEqual(StyleConstant.resolve({"a": "b"}, {"a": "a"}), {"a": "b"}); | ||
t.deepEqual(StyleConstant.resolve({"a": "@a"}, {"@a": "a"}), {"a": "a"}); | ||
t.deepEqual(StyleConstant.resolve({"a": "@a"}, {"@a": "a"}), {"a": "a"}); | ||
t.deepEqual(StyleConstant.resolve({"a": "@a", "b": "b"}, {"@a": "a"}), {"a": "a", "b": "b"}); | ||
t.test('ignores non-constants', function(t) { | ||
t.deepEqual(StyleConstant.resolve("a", {}), "a"); | ||
t.end(); | ||
}); | ||
test('does not modify in place', function(t) { | ||
var p = {}; | ||
t.notEqual(StyleConstant.resolve(p, {}), p); | ||
t.test('resolves scalars', function(t) { | ||
t.deepEqual(StyleConstant.resolve("@a", {"@a": "a"}), "a"); | ||
t.end(); | ||
}); | ||
test('resolves array values', function(t) { | ||
var properties = { | ||
"array": ["@a", "b"] | ||
}; | ||
var constants = { | ||
"@a": "a" | ||
}; | ||
t.deepEqual(StyleConstant.resolve(properties, constants), { | ||
"array": ["a", "b"] | ||
}); | ||
t.equal(properties.array[0], "@a"); | ||
t.test('resolves array values', function(t) { | ||
t.deepEqual(StyleConstant.resolve(["@a", "b"], {"@a": "a"}), ["a", "b"]); | ||
t.end(); | ||
}); | ||
test('resolves function values', function(t) { | ||
var properties = { | ||
"function": { | ||
"stops": [[0, "@a"], [1, "@b"]] | ||
} | ||
t.test('resolves function values', function(t) { | ||
var fun = { | ||
"stops": [[0, "@a"], [1, "@b"]] | ||
}; | ||
@@ -56,21 +32,30 @@ | ||
t.deepEqual(StyleConstant.resolve(properties, constants), { | ||
"function": { | ||
"stops": [[0, "a"], [1, "b"]] | ||
} | ||
t.deepEqual(StyleConstant.resolve(fun, constants), { | ||
"stops": [[0, "a"], [1, "b"]] | ||
}); | ||
t.end(); | ||
}); | ||
}); | ||
t.equal(properties.function.stops[0][1], "@a"); | ||
test('StyleConstant.resolveAll', function(t) { | ||
t.test('resolves all constants', function(t) { | ||
t.deepEqual(StyleConstant.resolveAll( | ||
{"a": "@a", "b": "@b"}, | ||
{"@a": "a", "@b": "b"}), | ||
{"a": "a", "b": "b"}); | ||
t.end(); | ||
}); | ||
t.test('does not modify in place', function(t) { | ||
var p = {}; | ||
t.notEqual(StyleConstant.resolveAll(p, {}), p); | ||
t.end(); | ||
}); | ||
test('no constants', function(t) { | ||
t.deepEqual(StyleConstant.resolve({"a": "a"}), {"a": "a"}); | ||
t.deepEqual(StyleConstant.resolve({"a": [1, 2]}), {"a": [1, 2]}); | ||
t.deepEqual(StyleConstant.resolve({"a": {"stops": [[1, 2]]}}), {"a": {"stops": [[1, 2]]}}); | ||
t.test('no constants', function(t) { | ||
t.deepEqual(StyleConstant.resolveAll({"a": "a"}), {"a": "a"}); | ||
t.deepEqual(StyleConstant.resolveAll({"a": [1, 2]}), {"a": [1, 2]}); | ||
t.deepEqual(StyleConstant.resolveAll({"a": {"stops": [[1, 2]]}}), {"a": {"stops": [[1, 2]]}}); | ||
t.end(); | ||
}); | ||
t.end(); | ||
}); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var StyleDeclaration = require('../../../js/style/style_declaration'); | ||
test('styledeclaration', function(t) { | ||
test('StyleDeclaration', function(t) { | ||
t.test('boolean', function(t) { | ||
t.equal((new StyleDeclaration('fill', 'fill-antialias', false)).calculate(0), false); | ||
var decl = new StyleDeclaration({type: "boolean"}, false); | ||
t.equal(decl.calculate(0), false); | ||
t.end(); | ||
@@ -17,17 +14,18 @@ }); | ||
t.test('image', function(t) { | ||
t.equal((new StyleDeclaration('fill', 'fill-image', 'smilingclownstaringatyou.png')).calculate(0), | ||
'smilingclownstaringatyou.png'); | ||
var decl = new StyleDeclaration({type: "image", transition: true}, 'smilingclownstaringatyou.png'); | ||
t.deepEqual(decl.calculate(0, { lastIntegerZoomTime: 0, lastIntegerZoom: 0 }, 300), | ||
{ to: 'smilingclownstaringatyou.png', toScale: 1, from: 'smilingclownstaringatyou.png', fromScale: 0.5, t: 1 }); | ||
t.end(); | ||
}); | ||
t.test('keywords', function(t) { | ||
t.equal((new StyleDeclaration('fill', 'fill-translate-anchor', 'viewport')).calculate(0), | ||
'viewport'); | ||
t.test('enum', function(t) { | ||
var decl = new StyleDeclaration({type: "enum"}, 'viewport'); | ||
t.equal(decl.calculate(0), 'viewport'); | ||
t.end(); | ||
}); | ||
t.test('parseWidthArray', function(t) { | ||
var dashFn = new StyleDeclaration('line', 'line-dasharray', [0, 10, 5]); | ||
t.ok(dashFn instanceof StyleDeclaration); | ||
t.deepEqual(dashFn.calculate(0), [0, 10, 5]); | ||
t.test('array', function(t) { | ||
var decl = new StyleDeclaration({type: "array", transition: true}, [0, 10, 5]); | ||
t.deepEqual(decl.calculate(0, { lastIntegerZoomTime: 0, lastIntegerZoom: 0 }, 300), | ||
{ to: [ 0, 10, 5 ], toScale: 1, from: [ 0, 10, 5 ], fromScale: 0.5, t: 1 }); | ||
t.end(); | ||
@@ -37,4 +35,4 @@ }); | ||
t.test('constant', function(t) { | ||
t.equal((new StyleDeclaration('line', 'line-width', 5)).calculate(0), 5); | ||
t.equal((new StyleDeclaration('line', 'line-width', 5)).calculate(100), 5); | ||
t.equal((new StyleDeclaration({type: "number"}, 5)).calculate(0), 5); | ||
t.equal((new StyleDeclaration({type: "number"}, 5)).calculate(100), 5); | ||
t.end(); | ||
@@ -44,11 +42,11 @@ }); | ||
t.test('functions', function(t) { | ||
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [] })).calculate(0), 1); | ||
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [[2, 2], [5, 10]] })).calculate(0), 2); | ||
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [[0, 0], [5, 10]] })).calculate(12), 10); | ||
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [[0, 0], [5, 10]] })).calculate(6), 10); | ||
t.equal(Math.round((new StyleDeclaration('fill', 'fill-opacity', { stops: [[0, 0], [5, 10]], base: 1.01 })).calculate(2.5)), 5); | ||
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [[0, 0], [1, 10], [2, 20]] })).calculate(2), 20); | ||
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [[0, 0], [1, 10], [2, 20]] })).calculate(1), 10); | ||
t.equal((new StyleDeclaration('fill', 'fill-opacity', { stops: [[0, 0]] })).calculate(6), 0); | ||
var reference = {type: "number", function: "interpolated"}; | ||
t.equal((new StyleDeclaration(reference, { stops: [] })).calculate(0), 1); | ||
t.equal((new StyleDeclaration(reference, { stops: [[2, 2], [5, 10]] })).calculate(0), 2); | ||
t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [5, 10]] })).calculate(12), 10); | ||
t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [5, 10]] })).calculate(6), 10); | ||
t.equal(Math.round((new StyleDeclaration(reference, { stops: [[0, 0], [5, 10]], base: 1.01 })).calculate(2.5)), 5); | ||
t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [1, 10], [2, 20]] })).calculate(2), 20); | ||
t.equal((new StyleDeclaration(reference, { stops: [[0, 0], [1, 10], [2, 20]] })).calculate(1), 10); | ||
t.equal((new StyleDeclaration(reference, { stops: [[0, 0]] })).calculate(6), 0); | ||
t.end(); | ||
@@ -58,19 +56,11 @@ }); | ||
t.test('color parsing', function(t) { | ||
t.deepEqual(new StyleDeclaration('line', 'line-color', 'red').calculate(0), [ 1, 0, 0, 1 ]); | ||
t.deepEqual(new StyleDeclaration('line', 'line-color', '#ff00ff').calculate(0), [ 1, 0, 1, 1 ]); | ||
t.deepEqual(new StyleDeclaration('line', 'line-color', { stops: [[0, '#f00'], [1, '#0f0']] }).calculate(0), [1, 0, 0, 1]); | ||
var reference = {type: "color", function: "interpolated"}; | ||
t.deepEqual(new StyleDeclaration(reference, 'red').calculate(0), [ 1, 0, 0, 1 ]); | ||
t.deepEqual(new StyleDeclaration(reference, '#ff00ff').calculate(0), [ 1, 0, 1, 1 ]); | ||
t.deepEqual(new StyleDeclaration(reference, { stops: [[0, '#f00'], [1, '#0f0']] }).calculate(0), [1, 0, 0, 1]); | ||
// cached | ||
t.deepEqual(new StyleDeclaration('line', 'line-color', '#ff00ff').calculate(0), [ 1, 0, 1, 1 ]); | ||
t.deepEqual(new StyleDeclaration('line', 'line-color', 'rgba(255, 51, 0, 1)').calculate(0), [ 1, 0.2, 0, 1 ]); | ||
t.deepEqual(new StyleDeclaration(reference, '#ff00ff').calculate(0), [ 1, 0, 1, 1 ]); | ||
t.deepEqual(new StyleDeclaration(reference, 'rgba(255, 51, 0, 1)').calculate(0), [ 1, 0.2, 0, 1 ]); | ||
t.end(); | ||
}); | ||
t.equal((new StyleDeclaration('', 'unknown-prop')).prop, undefined, 'unknown prop'); | ||
var widthfn = new StyleDeclaration('line', 'line-width', function(z) { | ||
return Math.pow(z, 2); | ||
}); | ||
t.equal(widthfn.calculate(10), 100); | ||
t.end(); | ||
}); |
'use strict'; | ||
var test = require('tape'); | ||
var fs = require('fs'); | ||
var st = require('st'); | ||
var http = require('http'); | ||
require('../../bootstrap'); | ||
var AnimationLoop = require('../../../js/style/animation_loop'); | ||
var path = require('path'); | ||
var Style = require('../../../js/style/style'); | ||
var Source = require('../../../js/source/source'); | ||
var VectorTileSource = require('../../../js/source/vector_tile_source'); | ||
var LayoutProperties = require('../../../js/style/layout_properties'); | ||
var PaintProperties = require('../../../js/style/paint_properties'); | ||
var StyleLayer = require('../../../js/style/style_layer'); | ||
var util = require('../../../js/util/util'); | ||
var UPDATE = process.env.UPDATE; | ||
function createStyleJSON() { | ||
return { | ||
"version": 6, | ||
"version": 7, | ||
"sources": {}, | ||
@@ -27,3 +23,3 @@ "layers": [] | ||
function createSource() { | ||
return new Source({ | ||
return new VectorTileSource({ | ||
type: 'vector', | ||
@@ -38,3 +34,3 @@ minzoom: 1, | ||
test('Style', function(t) { | ||
var server = http.createServer(st({path: __dirname + '/../../fixtures'})); | ||
var server = http.createServer(st({path: path.join(__dirname, '/../../fixtures')})); | ||
@@ -68,3 +64,3 @@ t.test('before', function(t) { | ||
style.on('load', function() { | ||
t.ok(style.getSource('mapbox') instanceof Source); | ||
t.ok(style.getSource('mapbox') instanceof VectorTileSource); | ||
t.end(); | ||
@@ -79,2 +75,42 @@ }); | ||
test('Style#_resolve', function(t) { | ||
t.test('creates StyleLayers', function(t) { | ||
var style = new Style({ | ||
"version": 7, | ||
"sources": {}, | ||
"layers": [{ | ||
id: 'fill', | ||
type: 'fill' | ||
}] | ||
}); | ||
style.on('load', function() { | ||
t.ok(style.getLayer('fill') instanceof StyleLayer); | ||
t.end(); | ||
}); | ||
}); | ||
t.test('handles ref layer preceding referent', function(t) { | ||
var style = new Style({ | ||
"version": 7, | ||
"sources": {}, | ||
"layers": [{ | ||
id: 'ref', | ||
ref: 'referent' | ||
}, { | ||
id: 'referent', | ||
type: 'fill' | ||
}] | ||
}); | ||
style.on('load', function() { | ||
var ref = style.getLayer('ref'), | ||
referent = style.getLayer('referent'); | ||
t.equal(ref.type, 'fill'); | ||
t.equal(ref.layout, referent.layout); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
test('Style#addSource', function(t) { | ||
@@ -193,5 +229,54 @@ t.test('returns self', function(t) { | ||
test('Style#setPaintProperty', function(t) { | ||
t.test('sets property', function(t) { | ||
var style = new Style({ | ||
"version": 7, | ||
"layers": [{ | ||
"id": "background", | ||
"type": "background" | ||
}] | ||
}); | ||
style.on('load', function() { | ||
style.setPaintProperty('background', 'background-color', 'red'); | ||
t.deepEqual(style.getPaintProperty('background', 'background-color'), [1, 0, 0, 1]); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
test('Style#setLayoutProperty', function(t) { | ||
t.test('sets property', function(t) { | ||
var style = new Style({ | ||
"version": 7, | ||
"sources": { | ||
"geojson": { | ||
"type": "geojson", | ||
"data": { | ||
"type": "FeatureCollection", | ||
"features": [] | ||
} | ||
} | ||
}, | ||
"layers": [{ | ||
"id": "symbol", | ||
"type": "symbol", | ||
"source": "geojson", | ||
"layout": { | ||
"text-transform": "uppercase" | ||
} | ||
}] | ||
}); | ||
style.on('load', function() { | ||
style.setLayoutProperty('symbol', 'text-transform', 'lowercase'); | ||
t.deepEqual(style.getLayoutProperty('symbol', 'text-transform'), 'lowercase'); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
test('Style#featuresAt', function(t) { | ||
var style = new Style({ | ||
"version": 6, | ||
"version": 7, | ||
"sources": { | ||
@@ -208,2 +293,5 @@ "mapbox": { | ||
"source-layer": "water", | ||
"layout": { | ||
'line-cap': 'round' | ||
}, | ||
"paint": { | ||
@@ -223,3 +311,4 @@ "line-color": "red" | ||
style.on('load', function() { | ||
style.recalculate(0); | ||
style._cascade([]); | ||
style._recalculate(0); | ||
@@ -229,22 +318,6 @@ style.sources.mapbox.featuresAt = function(position, params, callback) { | ||
$type: 'Polygon', | ||
layer: { | ||
id: 'land', | ||
type: 'line', | ||
layout: { | ||
'line-cap': 'round' | ||
} | ||
} | ||
layers: ['land'] | ||
}, { | ||
$type: 'Polygon', | ||
layer: { | ||
id: 'landref', | ||
ref: 'land', | ||
type: 'line', | ||
layout: { | ||
'line-cap': 'round' | ||
}, | ||
paint: { | ||
'line-color': 'blue' | ||
} | ||
} | ||
layers: ['land', 'landref'] | ||
}]); | ||
@@ -265,3 +338,3 @@ }; | ||
var layout = results[0].layer.layout; | ||
var layout = results[0].layers[0].layout; | ||
t.deepEqual(layout, {'line-cap': 'round'}); | ||
@@ -280,3 +353,3 @@ t.deepEqual( | ||
var paint = results[0].layer.paint; | ||
var paint = results[0].layers[0].paint; | ||
t.deepEqual(paint, {'line-color': [ 1, 0, 0, 1 ]}); | ||
@@ -295,4 +368,4 @@ t.deepEqual( | ||
var layer = results[0].layer; | ||
var refLayer = results[1].layer; | ||
var layer = results[1].layers[0]; | ||
var refLayer = results[1].layers[1]; | ||
t.deepEqual(layer.layout, refLayer.layout); | ||
@@ -311,3 +384,3 @@ t.deepEqual(layer.type, refLayer.type); | ||
var layer = results[0].layer; | ||
var layer = results[0].layers[0]; | ||
t.equal(layer.something, 'else'); | ||
@@ -322,56 +395,1 @@ | ||
}); | ||
test('style', function(t) { | ||
var style = new Style(require('../../fixtures/style-basic.json'), new AnimationLoop()); | ||
style.on('load', function() { | ||
// Replace changing startTime/endTime values with singe stable value | ||
// for fixture comparison. | ||
var style_transitions = JSON.parse(JSON.stringify(style.transitions, function(key, val) { | ||
if (key === 'startTime' || key === 'endTime') { | ||
return +new Date('Tue, 17 Jun 2014 0:00:00 UTC'); | ||
} else { | ||
return val; | ||
} | ||
})); | ||
if (UPDATE) fs.writeFileSync(__dirname + '/../../expected/style-basic-transitions.json', JSON.stringify(style_transitions, null, 2)); | ||
var style_transitions_expected = JSON.parse(fs.readFileSync(__dirname + '/../../expected/style-basic-transitions.json')); | ||
t.deepEqual(style_transitions, style_transitions_expected); | ||
style.recalculate(10); | ||
t.equal(style.hasClass('foo'), false, 'non-existent class'); | ||
t.deepEqual(style.getClassList(), [], 'getClassList'); | ||
t.deepEqual(style.removeClass('foo'), undefined, 'remove non-existent class'); | ||
// layerGroups | ||
var style_layergroups = JSON.parse(JSON.stringify(style.layerGroups)); | ||
if (UPDATE) fs.writeFileSync(__dirname + '/../../expected/style-basic-layergroups.json', JSON.stringify(style_layergroups, null, 2)); | ||
var style_layergroups_expected = JSON.parse(fs.readFileSync(__dirname + '/../../expected/style-basic-layergroups.json')); | ||
t.deepEqual(style_layergroups, style_layergroups_expected); | ||
// Check non JSON-stringified properites of layerGroups arrays. | ||
t.deepEqual(style.layerGroups[0].source, 'mapbox.mapbox-streets-v5'); | ||
t.deepEqual(style.layerGroups[1].source, undefined); | ||
// computed | ||
var style_computed = JSON.parse(JSON.stringify(style.computed)); | ||
if (UPDATE) fs.writeFileSync(__dirname + '/../../expected/style-basic-computed.json', JSON.stringify(style_computed, null, 2)); | ||
var style_computed_expected = JSON.parse(fs.readFileSync(__dirname + '/../../expected/style-basic-computed.json')); | ||
t.deepEqual(style_computed, style_computed_expected); | ||
// addClass and removeClass | ||
style.addClass('night'); | ||
t.ok(style.hasClass('night')); | ||
style.removeClass('night'); | ||
t.ok(!style.hasClass('night')); | ||
// getLayer | ||
var style_getlayer = JSON.parse(JSON.stringify(style.getLayer('park'))); | ||
if (UPDATE) fs.writeFileSync(__dirname + '/../../expected/style-basic-getlayer.json', JSON.stringify(style_getlayer, null, 2)); | ||
var style_getlayer_expected = JSON.parse(fs.readFileSync(__dirname + '/../../expected/style-basic-getlayer.json')); | ||
t.deepEqual(style_getlayer, style_getlayer_expected); | ||
t.end(); | ||
}); | ||
}); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var Anchor = require('../../../js/symbol/anchor'); | ||
@@ -8,0 +5,0 @@ |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var BinPack = require('../../../js/symbol/bin_pack'); | ||
@@ -8,0 +5,0 @@ |
@@ -5,5 +5,2 @@ 'use strict'; | ||
var Point = require('point-geometry'); | ||
require('../../bootstrap'); | ||
var interpolate = require('../../../js/symbol/interpolate'); | ||
@@ -23,5 +20,5 @@ | ||
t.deepEqual(interpolate(points, 0.5, 0.5, 8, 8), [ | ||
{ angle: 1.5707963267948966, scale: 0.5, segment: 1, x: 0, y: 1 }, | ||
{ angle: 1.5707963267948966, scale: 0.5, segment: 1, x: 0, y: 1 } | ||
]); | ||
t.end(); | ||
}); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var resolveText = require('../../../js/symbol/resolve_text'); | ||
@@ -23,3 +20,3 @@ | ||
'Baker St', | ||
, | ||
null, | ||
'14 St NW' | ||
@@ -58,3 +55,3 @@ ], | ||
textFeatures: [ | ||
'서울특별시', | ||
'서울특별시' | ||
], | ||
@@ -61,0 +58,0 @@ codepoints: [ 48324, 49436, 49884, 50872, 53945 ] |
@@ -5,15 +5,12 @@ 'use strict'; | ||
var Point = require('point-geometry'); | ||
require('../../bootstrap'); | ||
var rc = require('../../../js/symbol/rotation_range'); | ||
var PI = Math.PI; | ||
function deg(x) { return x/PI * 180; } | ||
function deg(x) { return x / PI * 180; } | ||
test('#mergeCollisions', function(t) { | ||
t.deepEqual(rc.mergeCollisions([[3/8*PI, 5/8*PI], [4/8*PI, 6/8*PI], [1/8*PI, 2/8*PI]], [2*PI, 0]), [1/8*PI, 6/8*PI], 'merges overlapping ranges'); | ||
t.deepEqual(rc.mergeCollisions([[PI/2, PI], [5/4*PI, 6/4*PI]], [0, PI]), [5/4*PI, 6/4*PI], 'ignore collision within ignore range'); | ||
t.deepEqual(rc.mergeCollisions([[1/2*PI, PI]], [3/4*PI, 3/2*PI]), [1/2*PI, 3/4*PI], 'crop collision that ends within ignore range'); | ||
t.deepEqual(rc.mergeCollisions([[1/2*PI, PI]], [1/4*PI, 3/4*PI]), [3/4*PI, PI], 'crop collision that starts within ignore range'); | ||
t.deepEqual(rc.mergeCollisions([[PI * 3 / 8, PI * 5 / 8], [PI * 4 / 8, PI * 6 / 8], [PI / 8, PI * 2 / 8]], [2 * PI, 0]), [PI / 8, PI * 6 / 8], 'merges overlapping ranges'); | ||
t.deepEqual(rc.mergeCollisions([[PI / 2, PI], [PI * 5 / 4, PI * 6 / 4]], [0, PI]), [PI * 5 / 4, PI * 6 / 4], 'ignore collision within ignore range'); | ||
t.deepEqual(rc.mergeCollisions([[PI / 2, PI]], [PI * 3 / 4, PI * 3 / 2]), [PI / 2, PI * 3 / 4], 'crop collision that ends within ignore range'); | ||
t.deepEqual(rc.mergeCollisions([[PI / 2, PI]], [PI / 4, PI * 3 / 4]), [PI * 3 / 4, PI], 'crop collision that starts within ignore range'); | ||
t.end(); | ||
@@ -38,3 +35,3 @@ }); | ||
new Point(1, 1), | ||
[new Point(0, 0), new Point(0, 10), new Point(10, 10), new Point(10, 0)]), [[PI/4, PI * 7/4]], | ||
[new Point(0, 0), new Point(0, 10), new Point(10, 10), new Point(10, 0)]), [[PI / 4, PI * 7 / 4]], | ||
'returns intersections in sorted order as angles 0..2PI'); | ||
@@ -56,3 +53,3 @@ | ||
c.sort(); | ||
t.deepEqual(c, [Math.PI/2, Math.PI*3/2], 'handles two intersection points'); | ||
t.deepEqual(c, [Math.PI / 2, Math.PI * 3 / 2], 'handles two intersection points'); | ||
@@ -63,3 +60,3 @@ t.deepEqual(rc.circleEdgeCollisions([], | ||
new Point(0, 0), new Point(10, 0)), | ||
[Math.PI/2], 'handles one intersection point'); | ||
[Math.PI / 2], 'handles one intersection point'); | ||
@@ -66,0 +63,0 @@ t.deepEqual(rc.circleEdgeCollisions([], |
@@ -5,5 +5,3 @@ 'use strict'; | ||
var fs = require('fs'); | ||
require('../../bootstrap'); | ||
var path = require('path'); | ||
var shaping = require('../../../js/symbol/shaping'); | ||
@@ -13,3 +11,3 @@ | ||
if (typeof process !== 'undefined' && process.env !== undefined) { | ||
var UPDATE = !!process.env.UPDATE; | ||
UPDATE = !!process.env.UPDATE; | ||
} | ||
@@ -22,3 +20,3 @@ | ||
'Test': { | ||
glyphs: JSON.parse(fs.readFileSync(__dirname + '/../../fixtures/fontstack-glyphs.json')) | ||
glyphs: JSON.parse(fs.readFileSync(path.join(__dirname, '/../../fixtures/fontstack-glyphs.json'))) | ||
} | ||
@@ -32,18 +30,18 @@ }; | ||
shaped = shaping.shape('hi' + String.fromCharCode(0), name, stacks, 15 * oneEm, oneEm, 0.5, 0.5, 0.5, 0 * oneEm, [0, 0]); | ||
if (UPDATE) fs.writeFileSync(__dirname + '/../../expected/text-shaping-null.json', JSON.stringify(shaped, null, 2)); | ||
t.deepEqual(JSON.parse(fs.readFileSync(__dirname + '/../../expected/text-shaping-null.json')), shaped); | ||
if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-null.json'), JSON.stringify(shaped, null, 2)); | ||
t.deepEqual(JSON.parse(fs.readFileSync(path.join(__dirname, '/../../expected/text-shaping-null.json'))), shaped); | ||
// Default shaping. | ||
shaped = shaping.shape('abcde', name, stacks, 15 * oneEm, oneEm, 0.5, 0.5, 0.5, 0 * oneEm, [0, 0]); | ||
if (UPDATE) fs.writeFileSync(__dirname + '/../../expected/text-shaping-default.json', JSON.stringify(shaped, null, 2)); | ||
t.deepEqual(JSON.parse(fs.readFileSync(__dirname + '/../../expected/text-shaping-default.json')), shaped); | ||
if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-default.json'), JSON.stringify(shaped, null, 2)); | ||
t.deepEqual(JSON.parse(fs.readFileSync(path.join(__dirname, '/../../expected/text-shaping-default.json'))), shaped); | ||
// Letter spacing. | ||
shaped = shaping.shape('abcde', name, stacks, 15 * oneEm, oneEm, 0.5, 0.5, 0.5, 0.125 * oneEm, [0, 0]); | ||
if (UPDATE) fs.writeFileSync(__dirname + '/../../expected/text-shaping-spacing.json', JSON.stringify(shaped, null, 2)); | ||
t.deepEqual(JSON.parse(fs.readFileSync(__dirname + '/../../expected/text-shaping-spacing.json')), shaped); | ||
if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-spacing.json'), JSON.stringify(shaped, null, 2)); | ||
t.deepEqual(JSON.parse(fs.readFileSync(path.join(__dirname, '/../../expected/text-shaping-spacing.json'))), shaped); | ||
// Line break. | ||
shaped = shaping.shape('abcde abcde', name, stacks, 4 * oneEm, oneEm, 0.5, 0.5, 0.5, 0 * oneEm, [0, 0]); | ||
if (UPDATE) fs.writeFileSync(__dirname + '/../../expected/text-shaping-linebreak.json', JSON.stringify(shaped, null, 2)); | ||
if (UPDATE) fs.writeFileSync(path.join(__dirname, '/../../expected/text-shaping-linebreak.json'), JSON.stringify(shaped, null, 2)); | ||
t.deepEqual(require('../../expected/text-shaping-linebreak.json'), shaped); | ||
@@ -50,0 +48,0 @@ |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var Map = require('../../../js/ui/map'); | ||
@@ -8,0 +5,0 @@ var util = require('../../../js/util/util'); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var Map = require('../../../js/ui/map'); | ||
@@ -35,3 +32,3 @@ var Style = require('../../../js/style/style'); | ||
style = { | ||
version: 6, | ||
version: 7, | ||
layers: [] | ||
@@ -46,3 +43,3 @@ }; | ||
style = new Style({ | ||
version: 6, | ||
version: 7, | ||
layers: [] | ||
@@ -90,4 +87,4 @@ }); | ||
map.setStyle({version: 6, layers: []}); | ||
map.setStyle({version: 6, layers: []}); | ||
map.setStyle({version: 7, layers: []}); | ||
map.setStyle({version: 7, layers: []}); | ||
@@ -248,2 +245,35 @@ t.end(); | ||
t.test('#addClass', function(t) { | ||
var map = createMap(); | ||
map.addClass('night'); | ||
t.ok(map.hasClass('night')); | ||
t.end(); | ||
}); | ||
t.test('#removeClass', function(t) { | ||
var map = createMap(); | ||
map.addClass('night'); | ||
map.removeClass('night'); | ||
t.ok(!map.hasClass('night')); | ||
t.end(); | ||
}); | ||
t.test('#setClasses', function(t) { | ||
var map = createMap(); | ||
map.addClass('night'); | ||
map.setClasses([]); | ||
t.ok(!map.hasClass('night')); | ||
map.setClasses(['night']); | ||
t.ok(map.hasClass('night')); | ||
t.end(); | ||
}); | ||
t.test('#getClasses', function(t) { | ||
var map = createMap(); | ||
map.addClass('night'); | ||
t.deepEqual(map.getClasses(), ['night']); | ||
t.end(); | ||
}); | ||
t.test('#project', function(t) { | ||
@@ -250,0 +280,0 @@ var map = createMap(); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var util = require('../../../js/util/browser'); | ||
@@ -8,0 +5,0 @@ |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var Evented = require('../../../js/util/evented'); | ||
@@ -8,0 +5,0 @@ |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var mapbox = require('../../../js/util/mapbox'); | ||
var config = require('../../../js/util/config'); | ||
var browser = require('../../../js/util/browser'); | ||
@@ -72,3 +70,44 @@ test("mapbox", function(t) { | ||
t.test('.normalizeStyleURL', function(t) { | ||
t.test('returns an API URL with access_token parameter', function(t) { | ||
t.equal(mapbox.normalizeStyleURL('mapbox://user.style'), 'http://a.tiles.mapbox.com/styles/v1/user/user.style?access_token=key'); | ||
t.end(); | ||
}); | ||
t.test('ignores non-mapbox:// scheme', function(t) { | ||
t.equal(mapbox.normalizeStyleURL('http://path'), 'http://path'); | ||
t.end(); | ||
}); | ||
t.end(); | ||
}); | ||
t.test('.normalizeTileURL', function(t) { | ||
t.test('does nothing on 1x devices', function(t) { | ||
t.equal(mapbox.normalizeTileURL('http://path.png/tile.png', 'mapbox://user.map'), 'http://path.png/tile.png'); | ||
t.end(); | ||
}); | ||
t.test('inserts @2x on 2x devices', function(t) { | ||
browser.devicePixelRatio = 2; | ||
t.equal(mapbox.normalizeTileURL('http://path.png/tile.png', 'mapbox://user.map'), 'http://path.png/tile@2x.png'); | ||
t.equal(mapbox.normalizeTileURL('http://path.png/tile.png?access_token=foo', 'mapbox://user.map'), 'http://path.png/tile@2x.png?access_token=foo'); | ||
browser.devicePixelRatio = 1; | ||
t.end(); | ||
}); | ||
t.test('ignores non-mapbox:// sources', function(t) { | ||
t.equal(mapbox.normalizeTileURL('http://path.png', 'http://path'), 'http://path.png'); | ||
t.end(); | ||
}); | ||
t.test('ignores undefined sources', function(t) { | ||
t.equal(mapbox.normalizeTileURL('http://path.png'), 'http://path.png'); | ||
t.end(); | ||
}); | ||
t.end(); | ||
}); | ||
t.end(); | ||
}); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var MRUCache = require('../../../js/util/mru_cache'); | ||
@@ -8,0 +5,0 @@ |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var resolveTokens = require('../../../js/util/token'); | ||
@@ -18,4 +15,6 @@ | ||
t.equal('dashed', resolveTokens({'dashed-property': 'dashed'}, '{dashed-property}')); | ||
t.equal('150 m', resolveTokens({'HØYDE': 150}, '{HØYDE} m')); | ||
t.equal('reserved {for:future} use', resolveTokens({'for:future': 'unknown'}, 'reserved {for:future} use')); | ||
t.end(); | ||
}); |
'use strict'; | ||
var test = require('tape'); | ||
require('../../bootstrap'); | ||
var util = require('../../../js/util/util'); | ||
@@ -14,7 +11,6 @@ | ||
t.equal(util.easeCubicInOut(1), 1, 'easeCubicInOut=1'); | ||
t.equal(util.interp(0, 1, 0.5), 0.5, 'interp=0.5'); | ||
t.deepEqual(util.premultiply([0, 1, 2, 2]), [0, 2, 4, 2], 'premultiply'); | ||
t.deepEqual(util.keysDifference({a:1}, {}), ['a'], 'keysDifference'); | ||
t.deepEqual(util.keysDifference({a:1}, {a:1}), [], 'keysDifference'); | ||
t.deepEqual(util.extend({a:1}, {b:2}), {a:1,b:2}, 'extend'); | ||
t.deepEqual(util.extend({a:1}, {b:2}), {a:1, b:2}, 'extend'); | ||
@@ -21,0 +17,0 @@ t.test('bezier', function(t) { |
@@ -13,5 +13,2 @@ 'use strict'; | ||
var spawn = require('child_process').spawn; | ||
require('./bootstrap'); | ||
var Map = require('../js/ui/map'); | ||
@@ -62,2 +59,3 @@ var browser = require('../js/util/browser'); | ||
style: style, | ||
classes: info.classes || [], | ||
interactive: false, | ||
@@ -67,4 +65,2 @@ attributionControl: false | ||
map.style.setClassList(info.classes || [], {transition: false}); | ||
var gl = map.painter.gl; | ||
@@ -106,4 +102,2 @@ | ||
map.on('render', rendered); | ||
var watchdog = setTimeout(function() { | ||
@@ -117,8 +111,3 @@ t.fail('timed out after 20 seconds'); | ||
function rendered() { | ||
if (!map.loaded()) | ||
return; | ||
map.off('render', rendered); | ||
map.once('load', function() { | ||
var w = width * browser.devicePixelRatio, | ||
@@ -168,3 +157,3 @@ h = height * browser.devicePixelRatio; | ||
// The compare program returns 2 on error otherwise 0 if the images are similar or 1 if they are dissimilar. | ||
if (code == 2) { | ||
if (code === 2) { | ||
writeResult(error.trim(), Infinity); | ||
@@ -202,11 +191,15 @@ } else { | ||
} | ||
} | ||
}); | ||
}; | ||
} | ||
var tests = process.argv.slice(2); | ||
var tests; | ||
if (process.argv[1] === __filename) { | ||
tests = process.argv.slice(2); | ||
} | ||
fs.readdirSync(path.join(suitePath, 'tests')).forEach(function(dir) { | ||
if (dir === 'index.html' || dir[0] === '.') return; | ||
if (tests.length && tests.indexOf(dir) < 0) return; | ||
if (tests && tests.length && tests.indexOf(dir) < 0) return; | ||
@@ -213,0 +206,0 @@ var style = require(path.join(suitePath, 'tests', dir, 'style.json')), |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
1261706
18
13
256
20968
21
+ Addedalign-text@0.1.4(transitive)
+ Addedarray-differ@1.0.0(transitive)
+ Addedarray-union@1.0.2(transitive)
+ Addedarray-uniq@1.0.3(transitive)
+ Addedarrify@1.0.1(transitive)
+ Addedcenter-align@0.1.3(transitive)
+ Addedcliui@2.1.0(transitive)
+ Addedconvert-source-map@1.9.0(transitive)
+ Addedis-buffer@1.1.6(transitive)
+ Addedkind-of@3.2.2(transitive)
+ Addedlazy-cache@1.0.4(transitive)
+ Addedlodash._arrayeach@3.0.0(transitive)
+ Addedlodash._baseassign@3.2.0(transitive)
+ Addedlodash._basecopy@3.0.1(transitive)
+ Addedlodash._baseeach@3.0.4(transitive)
+ Addedlodash._bindcallback@3.0.1(transitive)
+ Addedlodash._createassigner@3.1.1(transitive)
+ Addedlodash._createwrapper@3.2.0(transitive)
+ Addedlodash._getnative@3.9.1(transitive)
+ Addedlodash._isiterateecall@3.0.9(transitive)
+ Addedlodash._replaceholders@3.0.0(transitive)
+ Addedlodash._root@3.0.1(transitive)
+ Addedlodash.assign@3.2.0(transitive)
+ Addedlodash.bind@3.1.0(transitive)
+ Addedlodash.defaults@3.1.2(transitive)
+ Addedlodash.foreach@3.0.3(transitive)
+ Addedlodash.isarguments@3.1.0(transitive)
+ Addedlodash.isarray@3.0.4(transitive)
+ Addedlodash.keys@3.1.2(transitive)
+ Addedlodash.restparam@3.6.1(transitive)
+ Addedlongest@1.0.1(transitive)
+ Addedmapbox-gl-style-spec@7.0.0(transitive)
+ Addedminifyify@6.4.0(transitive)
+ Addedminimatch@3.1.2(transitive)
+ Addedmultimatch@2.1.0(transitive)
+ Addedrepeat-string@1.6.1(transitive)
+ Addedright-align@0.1.3(transitive)
+ Addedsource-map@0.4.40.5.7(transitive)
+ Addedtmp@0.0.25(transitive)
+ Addedtransform-filter@0.1.1(transitive)
+ Addeduglify-js@2.8.29(transitive)
+ Addedyargs@3.10.0(transitive)
- Removedes6ify@^1.5.1
- Removedasync@0.2.10(transitive)
- Removedcommander@2.20.3(transitive)
- Removedconvert-source-map@0.4.1(transitive)
- Removedes6ify@1.6.0(transitive)
- Removedglob@4.3.5(transitive)
- Removedinflight@1.0.6(transitive)
- Removedlodash._basebind@2.4.1(transitive)
- Removedlodash._basecreate@2.4.1(transitive)
- Removedlodash._basecreatecallback@2.4.1(transitive)
- Removedlodash._basecreatewrapper@2.4.1(transitive)
- Removedlodash._createwrapper@2.4.1(transitive)
- Removedlodash._isnative@2.4.1(transitive)
- Removedlodash._objecttypes@2.4.1(transitive)
- Removedlodash._setbinddata@2.4.1(transitive)
- Removedlodash._shimkeys@2.4.1(transitive)
- Removedlodash._slice@2.4.1(transitive)
- Removedlodash.bind@2.4.1(transitive)
- Removedlodash.defaults@2.4.1(transitive)
- Removedlodash.foreach@2.4.1(transitive)
- Removedlodash.forown@2.4.1(transitive)
- Removedlodash.identity@2.4.1(transitive)
- Removedlodash.isfunction@2.4.1(transitive)
- Removedlodash.isobject@2.4.1(transitive)
- Removedlodash.keys@2.4.1(transitive)
- Removedlodash.noop@2.4.1(transitive)
- Removedlodash.support@2.4.1(transitive)
- Removedmapbox-gl-style-spec@6.0.0(transitive)
- Removedminifyify@4.4.0(transitive)
- Removedminimatch@2.0.10(transitive)
- Removedonce@1.4.0(transitive)
- Removedrsvp@3.6.2(transitive)
- Removedsafe-buffer@5.2.1(transitive)
- Removedsemver@2.3.2(transitive)
- Removedsource-map@0.1.320.1.34(transitive)
- Removedsource-map-support@0.2.10(transitive)
- Removedthrough@2.2.7(transitive)
- Removedtmp@0.0.23(transitive)
- Removedtraceur@0.0.79(transitive)
- Removeduglify-js@2.4.24(transitive)
- Removedwrappy@1.0.2(transitive)
- Removedxtend@2.2.0(transitive)
- Removedyargs@3.5.4(transitive)
Updatedmapbox-gl-style-spec@7.0.0
Updatedminifyify@^6.1.0