mapbox-gl
Advanced tools
Comparing version 0.10.0 to 0.11.0
@@ -5,2 +5,25 @@ ## dev | ||
## 0.11.0 (Sep 11 2015) | ||
#### API Improvements | ||
* Add `Map#featuresIn`: a bounding-box feature query | ||
* Emit stylesheet validation errors (#1436) | ||
#### UX Improvements | ||
* Handle v8 style `center`, `zoom`, `bearing`, `pitch` (#1452) | ||
* Improve circle type styling (#1446) | ||
* Improve dashed and patterned line antialiasing | ||
#### Bugfixes | ||
* Load images in a way that respects Cache-Control headers | ||
* Filter for rtree matches to those crossing bbox | ||
* Log errors by default (#1463) | ||
* Fixed modification of `text-size` via `setLayoutProperty` (#1451) | ||
* Throw on lat > 90 || < -90. (#1443) | ||
* Fix circle clipping bug (#1457) | ||
## 0.10.0 (Aug 21 2015) | ||
@@ -65,6 +88,6 @@ | ||
* Now you can directly mutate the minzoom and maxzoom layer properties with `map.setLayerZoomRange(layerId, minzoom, maxzoom)` | ||
* Exposed `mapboxgl.Control`, a base class used by all UI controls | ||
* Exposed `mapboxgl.Control`, a base class used by all UI controls | ||
* Refactored handlers to be individually included in Map options, or enable/disable them individually at runtime, e.g. `map.scrollZoom.disable()`. | ||
* New feature: Batch operations can now be done at once, improving performance for calling multiple style functions: (#1352) | ||
```js | ||
@@ -71,0 +94,0 @@ style.batch(function(s) { |
@@ -23,2 +23,3 @@ 'use strict'; | ||
CircleBucket.prototype.addFeatures = function() { | ||
var extent = 4096; | ||
for (var i = 0; i < this.features.length; i++) { | ||
@@ -31,2 +32,5 @@ var geometries = this.features[i].loadGeometry()[0]; | ||
// Do not include points that are outside the tile boundaries. | ||
if (x < 0 || x >= extent || y < 0 || y >= extent) continue; | ||
var idx = this.buffers.circleVertex.index - | ||
@@ -33,0 +37,0 @@ this.elementGroups.current.vertexStartIndex; |
@@ -35,3 +35,2 @@ 'use strict'; | ||
var params = args.params || {}, | ||
radius = (params.radius || 0) * 4096 / args.scale, | ||
x = args.x, | ||
@@ -41,3 +40,14 @@ y = args.y, | ||
var matching = this.rtree.search([ x - radius, y - radius, x + radius, y + radius ]); | ||
var radius, bounds; | ||
if (typeof x !== 'undefined' && typeof y !== 'undefined') { | ||
// a point (or point+radius) query | ||
radius = (params.radius || 0) * 4096 / args.scale; | ||
bounds = [x - radius, y - radius, x + radius, y + radius]; | ||
} | ||
else { | ||
// a rectangle query | ||
bounds = [ args.minX, args.minY, args.maxX, args.maxY ]; | ||
} | ||
var matching = this.rtree.search(bounds); | ||
for (var i = 0; i < matching.length; i++) { | ||
@@ -50,4 +60,6 @@ var feature = matching[i].feature, | ||
continue; | ||
if (!geometryContainsPoint(feature.loadGeometry(), type, new Point(x, y), radius)) | ||
if (radius && !geometryContainsPoint(feature.loadGeometry(), type, new Point(x, y), radius)) | ||
continue; | ||
else if (!geometryIntersectsBox(feature.loadGeometry(), type, bounds)) | ||
continue; | ||
@@ -69,6 +81,76 @@ var geoJSON = feature.toGeoJSON(this.x, this.y, this.z); | ||
} | ||
callback(null, result); | ||
}; | ||
function geometryIntersectsBox(rings, type, bounds) { | ||
return type === 'Point' ? pointIntersectsBox(rings, bounds) : | ||
type === 'LineString' ? lineIntersectsBox(rings, bounds) : | ||
type === 'Polygon' ? polyIntersectsBox(rings, bounds) || lineIntersectsBox(rings, bounds) : false; | ||
} | ||
// Tests whether any of the four corners of the bbox are contained in the | ||
// interior of the polygon. Otherwise, defers to lineIntersectsBox. | ||
function polyIntersectsBox(rings, bounds) { | ||
if (polyContainsPoint(rings, new Point(bounds[0], bounds[1])) | ||
|| polyContainsPoint(rings, new Point(bounds[0], bounds[3])) | ||
|| polyContainsPoint(rings, new Point(bounds[2], bounds[1])) | ||
|| polyContainsPoint(rings, new Point(bounds[2], bounds[3]))) | ||
return true; | ||
return lineIntersectsBox(rings, bounds); | ||
} | ||
// Only needs to cover the case where the line crosses the bbox boundary. | ||
// Otherwise, pointIntersectsBox should have us covered. | ||
function lineIntersectsBox(rings, bounds) { | ||
for (var k = 0; k < rings.length; k++) { | ||
var ring = rings[k]; | ||
for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) { | ||
var p0 = ring[i]; | ||
var p1 = ring[j]; | ||
// invert the segment so as to reuse segmentCrossesHorizontal for | ||
// checking whether it crosses the vertical sides of the bbox. | ||
var i0 = new Point(p0.y, p0.x); | ||
var i1 = new Point(p1.y, p1.x); | ||
if (segmentCrossesHorizontal(p0, p1, bounds[0], bounds[2], bounds[1]) | ||
|| segmentCrossesHorizontal(p0, p1, bounds[0], bounds[2], bounds[3]) | ||
|| segmentCrossesHorizontal(i0, i1, bounds[1], bounds[3], bounds[0]) | ||
|| segmentCrossesHorizontal(i0, i1, bounds[1], bounds[3], bounds[2])) | ||
return true; | ||
} | ||
} | ||
return pointIntersectsBox(rings, bounds); | ||
} | ||
/* | ||
* Answer whether segment p1-p2 intersects with (x1, y)-(x2, y) | ||
* Assumes x2 >= x1 | ||
*/ | ||
function segmentCrossesHorizontal(p0, p1, x1, x2, y) { | ||
if (p1.y === p0.y) | ||
return (p1.y === y | ||
&& Math.min(p0.x, p1.x) <= x2 | ||
&& Math.max(p0.x, p1.x) >= x1); | ||
var r = (y - p0.y) / (p1.y - p0.y); | ||
var x = p0.x + r * (p1.x - p0.x); | ||
return (x >= x1 && x <= x2 && r <= 1 && r >= 0); | ||
} | ||
function pointIntersectsBox(rings, bounds) { | ||
for (var i = 0; i < rings.length; i++) { | ||
var ring = rings[i]; | ||
for (var j = 0; j < ring.length; j++) { | ||
if (ring[j].x >= bounds[0] | ||
&& ring[j].y >= bounds[1] | ||
&& ring[j].x <= bounds[2] | ||
&& ring[j].y <= bounds[3]) return true; | ||
} | ||
} | ||
return false; | ||
} | ||
function geometryContainsPoint(rings, type, p, radius) { | ||
@@ -75,0 +157,0 @@ return type === 'Point' ? pointContainsPoint(rings, p, radius) : |
@@ -9,3 +9,11 @@ 'use strict'; | ||
* Create a longitude, latitude object from a given longitude and latitude pair in degrees. | ||
* Mapbox GL uses Longitude, Latitude coordinate order to match GeoJSON. | ||
* | ||
* Note that any Mapbox GL method that accepts a `LngLat` object can also accept an | ||
* `Array` and will perform an implicit conversion. The following lines are equivalent: | ||
``` | ||
map.setCenter([-79.469, 39.261]); | ||
map.setCenter( new mapboxgl.LngLat(-79.469, 39.261) ); | ||
``` | ||
* | ||
* @class LngLat | ||
@@ -24,2 +32,5 @@ * @classdesc A representation of a longitude, latitude point, in degrees. | ||
this.lat = +lat; | ||
if (this.lat > 90 || this.lat < -90) { | ||
throw new Error('Invalid LngLat latitude value: must be between -90 and 90'); | ||
} | ||
} | ||
@@ -26,0 +37,0 @@ |
@@ -204,6 +204,8 @@ 'use strict'; | ||
locationCoordinate: function(lnglat) { | ||
var k = this.zoomScale(this.tileZoom) / this.worldSize; | ||
var k = this.zoomScale(this.tileZoom) / this.worldSize, | ||
ll = LngLat.convert(lnglat); | ||
return new Coordinate( | ||
this.lngX(lnglat.lng) * k, | ||
this.latY(lnglat.lat) * k, | ||
this.lngX(ll.lng) * k, | ||
this.latY(ll.lat) * k, | ||
this.tileZoom); | ||
@@ -210,0 +212,0 @@ }, |
@@ -81,2 +81,3 @@ 'use strict'; | ||
gl.uniform1f(shader.u_blur, blur); | ||
gl.uniform4fv(shader.u_color, color); | ||
@@ -102,2 +103,5 @@ var posA = painter.lineAtlas.getDash(dasharray.from, layer.layout['line-cap'] === 'round'); | ||
gl.uniform1f(shader.u_extra, extra); | ||
gl.uniformMatrix2fv(shader.u_antialiasingmatrix, false, antialiasingMatrix); | ||
} else if (image) { | ||
@@ -127,2 +131,5 @@ var imagePosA = painter.spriteAtlas.getPosition(image.from, true); | ||
gl.uniform1f(shader.u_extra, extra); | ||
gl.uniformMatrix2fv(shader.u_antialiasingmatrix, false, antialiasingMatrix); | ||
} else { | ||
@@ -137,2 +144,4 @@ shader = painter.lineShader; | ||
gl.uniformMatrix2fv(shader.u_antialiasingmatrix, false, antialiasingMatrix); | ||
gl.uniform4fv(shader.u_color, color); | ||
} | ||
@@ -145,4 +154,2 @@ | ||
gl.disableVertexAttribArray(shader.a_color); | ||
for (var i = 0; i < elementGroups.groups.length; i++) { | ||
@@ -153,3 +160,2 @@ var group = elementGroups.groups[i]; | ||
gl.vertexAttribPointer(shader.a_data, 4, gl.BYTE, false, 8, vtxOffset + 4); | ||
gl.vertexAttrib4f(shader.a_color, color[0], color[1], color[2], color[3]); | ||
@@ -161,4 +167,2 @@ var count = group.elementLength * 3; | ||
gl.enableVertexAttribArray(shader.a_color); | ||
}; |
@@ -55,3 +55,3 @@ 'use strict'; | ||
['a_pos'], | ||
['u_matrix', 'u_pointsize', 'u_color']); | ||
['u_matrix', 'u_color']); | ||
@@ -67,12 +67,12 @@ this.rasterShader = gl.initializeShader('raster', | ||
this.lineShader = gl.initializeShader('line', | ||
['a_pos', 'a_data', 'a_color'], | ||
['u_matrix', 'u_linewidth', 'u_ratio', 'u_blur', 'u_extra', 'u_antialiasingmatrix']); | ||
['a_pos', 'a_data'], | ||
['u_matrix', 'u_linewidth', 'u_color', 'u_ratio', 'u_blur', 'u_extra', 'u_antialiasingmatrix']); | ||
this.linepatternShader = gl.initializeShader('linepattern', | ||
['a_pos', 'a_data', 'a_color'], | ||
['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']); | ||
['a_pos', 'a_data'], | ||
['u_matrix', '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', 'u_extra', 'u_antialiasingmatrix']); | ||
this.linesdfpatternShader = gl.initializeShader('linesdfpattern', | ||
['a_pos', 'a_data', 'a_color'], | ||
['u_matrix', 'u_exmatrix', 'u_linewidth', 'u_ratio', 'u_blur', 'u_patternscale_a', 'u_tex_y_a', 'u_patternscale_b', 'u_tex_y_b', 'u_image', 'u_sdfgamma', 'u_mix']); | ||
['a_pos', 'a_data'], | ||
['u_matrix', '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', 'u_extra', 'u_antialiasingmatrix']); | ||
@@ -79,0 +79,0 @@ this.dotShader = gl.initializeShader('dot', |
@@ -111,2 +111,3 @@ 'use strict'; | ||
featuresAt: Source._vectorFeaturesAt, | ||
featuresIn: Source._vectorFeaturesIn, | ||
@@ -113,0 +114,0 @@ _updateData: function() { |
@@ -99,3 +99,7 @@ 'use strict'; | ||
callback(null, []); | ||
}, | ||
featuresIn: function(bbox, params, callback) { | ||
callback(null, []); | ||
} | ||
}); |
@@ -89,2 +89,26 @@ 'use strict'; | ||
exports._vectorFeaturesIn = function(bounds, params, callback) { | ||
if (!this._pyramid) | ||
return callback(null, []); | ||
var results = this._pyramid.tilesIn(bounds); | ||
if (!results) | ||
return callback(null, []); | ||
util.asyncAll(results, function queryTile(result, cb) { | ||
this.dispatcher.send('query features', { | ||
uid: result.tile.uid, | ||
source: this.id, | ||
minX: result.minX, | ||
maxX: result.maxX, | ||
minY: result.minY, | ||
maxY: result.maxY, | ||
params: params | ||
}, cb, result.tile.workerID); | ||
}.bind(this), function done(err, features) { | ||
callback(err, Array.prototype.concat.apply([], features)); | ||
}); | ||
}; | ||
/* | ||
@@ -91,0 +115,0 @@ * Create a tiled data source instance given an options object |
@@ -343,3 +343,35 @@ 'use strict'; | ||
} | ||
}, | ||
/** | ||
* Search through our current tiles and attempt to find the tiles that | ||
* cover the given bounds. | ||
* @param {Array<Coordinate>} [minxminy, maxxmaxy] coordinates of the corners of bounding rectangle | ||
* @returns {Array<Object>} result items have {tile, minX, maxX, minY, maxY}, where min/max bounding values are the given bounds transformed in into the coordinate space of this tile. | ||
* @private | ||
*/ | ||
tilesIn: function(bounds) { | ||
var result = []; | ||
var ids = this.orderedIDs(); | ||
for (var i = 0; i < ids.length; i++) { | ||
var tile = this._tiles[ids[i]]; | ||
var tileSpaceBounds = [ | ||
tile.positionAt(bounds[0], this.maxzoom), | ||
tile.positionAt(bounds[1], this.maxzoom) | ||
]; | ||
if (tileSpaceBounds[0].x < 4096 && tileSpaceBounds[0].y < 4096 | ||
&& tileSpaceBounds[1].x >= 0 && tileSpaceBounds[1].y >= 0) { | ||
result.push({ | ||
tile: tile, | ||
minX: tileSpaceBounds[0].x, | ||
maxX: tileSpaceBounds[1].x, | ||
minY: tileSpaceBounds[0].y, | ||
maxY: tileSpaceBounds[1].y | ||
}); | ||
} | ||
} | ||
return result; | ||
} | ||
}; |
@@ -60,2 +60,3 @@ 'use strict'; | ||
featuresAt: Source._vectorFeaturesAt, | ||
featuresIn: Source._vectorFeaturesIn, | ||
@@ -62,0 +63,0 @@ _loadTile: function(tile) { |
@@ -165,3 +165,7 @@ 'use strict'; | ||
return callback(null, []); | ||
}, | ||
featuresIn: function(bbox, params, callback) { | ||
return callback(null, []); | ||
} | ||
}); |
@@ -38,4 +38,2 @@ 'use strict'; | ||
} | ||
this._resolvedLayout = new StyleDeclarationSet('layout', this.type, this.layout, this._constants || {}); | ||
} | ||
@@ -122,6 +120,7 @@ }, | ||
if (this.type === 'symbol') { | ||
this._cascaded['text-size'] = new StyleTransition(this._resolvedLayout.values()['text-size'], undefined, globalTrans); | ||
this._cascaded['icon-size'] = new StyleTransition(this._resolvedLayout.values()['icon-size'], undefined, globalTrans); | ||
var resolvedLayout = new StyleDeclarationSet('layout', this.type, this.layout); | ||
this._cascaded['text-size'] = new StyleTransition(resolvedLayout.values()['text-size'], undefined, globalTrans); | ||
this._cascaded['icon-size'] = new StyleTransition(resolvedLayout.values()['icon-size'], undefined, globalTrans); | ||
} | ||
}, | ||
}, | ||
@@ -175,3 +174,3 @@ recalculate: function(z, zoomHistory) { | ||
'minzoom', 'maxzoom', 'filter', | ||
'layout', '_resolvedLayout'])); | ||
'layout'])); | ||
}, | ||
@@ -178,0 +177,0 @@ |
@@ -50,5 +50,6 @@ 'use strict'; | ||
if (valid.length) { | ||
valid.forEach(function(e) { | ||
throw new Error(e.message); | ||
}); | ||
for (var i = 0; i < valid.length; i++) { | ||
this.fire('error', { error: new Error(valid[i].message) }); | ||
} | ||
return; | ||
} | ||
@@ -345,10 +346,2 @@ | ||
setLayoutProperty: function(layer, name, value) { | ||
this.batch(function(batch) { | ||
batch.setLayoutProperty(layer, name, value); | ||
}); | ||
return this; | ||
}, | ||
/** | ||
@@ -365,10 +358,2 @@ * Get a layout property's value from a given layer | ||
setPaintProperty: function(layer, name, value, klass) { | ||
this.batch(function(batch) { | ||
batch.setPaintProperty(layer, name, value, klass); | ||
}); | ||
return this; | ||
}, | ||
getPaintProperty: function(layer, name, klass) { | ||
@@ -407,2 +392,31 @@ return this.getLayer(layer).getPaintProperty(name, klass); | ||
featuresIn: function(bbox, params, callback) { | ||
var features = []; | ||
var error = null; | ||
if (params.layer) { | ||
params.layer = { id: params.layer }; | ||
} | ||
util.asyncEach(Object.keys(this.sources), function(id, callback) { | ||
var source = this.sources[id]; | ||
source.featuresIn(bbox, params, function(err, result) { | ||
if (result) features = features.concat(result); | ||
if (err) error = err; | ||
callback(); | ||
}); | ||
}.bind(this), function() { | ||
if (error) return callback(error); | ||
callback(null, features | ||
.filter(function(feature) { | ||
return this._layers[feature.layer] !== undefined; | ||
}.bind(this)) | ||
.map(function(feature) { | ||
feature.layer = this._layers[feature.layer].json(); | ||
return feature; | ||
}.bind(this))); | ||
}.bind(this)); | ||
}, | ||
_remove: function() { | ||
@@ -409,0 +423,0 @@ this.dispatcher.remove(); |
@@ -16,6 +16,7 @@ 'use strict'; | ||
* @typedef {Object} CameraOptions | ||
* @property {Array} center Longitude and latitude (passed as `[lng, lat]`) | ||
* @property {LngLat} center Map center | ||
* @property {number} zoom Map zoom level | ||
* @property {number} bearing Map rotation bearing in degrees counter-clockwise from north | ||
* @property {number} pitch The angle at which the camera is looking at the ground | ||
* @property {number} pitch Map angle in degrees at which the camera is looking at the ground | ||
* @property {LngLat} around If zooming, the zoom center (defaults to map center) | ||
*/ | ||
@@ -47,3 +48,3 @@ | ||
* | ||
* @param {Array} center Longitude and latitude (passed as `[lng, lat]`) | ||
* @param {LngLat} center Map center to jump to | ||
* @fires movestart | ||
@@ -63,3 +64,3 @@ * @fires moveend | ||
* | ||
* @param {Array} offset [x, y] | ||
* @param {Array<number>} offset [x, y] | ||
* @param {AnimationOptions} [options] | ||
@@ -78,3 +79,3 @@ * @fires movestart | ||
* | ||
* @param {LngLat|Array<number>} lnglat | ||
* @param {LngLat} lnglat Location to pan to | ||
* @param {AnimationOptions} [options] | ||
@@ -434,3 +435,3 @@ * @fires movestart | ||
* | ||
* @param {CameraOptions~AnimationOptions} options map view and animation options | ||
* @param {CameraOptions|AnimationOptions} options map view and animation options | ||
* @fires movestart | ||
@@ -437,0 +438,0 @@ * @fires moveend |
106
js/ui/map.js
@@ -39,7 +39,13 @@ 'use strict'; | ||
* @param {number} [options.maxZoom=20] Maximum zoom of the map | ||
* @param {Object} options.style Map style and data source definition (either a JSON object or a JSON URL), described in the [style reference](https://mapbox.com/mapbox-gl-style-spec/) | ||
* @param {Object|string} [options.style] Map style. This must be an an object conforming to the schema described in the [style reference](https://mapbox.com/mapbox-gl-style-spec/), or a URL to a JSON style. To load a style from the Mapbox API, you can use a URL of the form `mapbox://styles/:owner/:style`, where `:owner` is your Mapbox account name and `:style` is the style ID. Or you can use one of the predefined Mapbox styles: | ||
* * `mapbox://styles/mapbox/basic-v8` - Simple and flexible starting template. | ||
* * `mapbox://styles/mapbox/bright-v8` - Template for complex custom basemaps. | ||
* * `mapbox://styles/mapbox/streets-v8` - A ready-to-use basemap, perfect for minor customization or incorporating your own data. | ||
* * `mapbox://styles/mapbox/light-v8` - Subtle light backdrop for data vizualizations. | ||
* * `mapbox://styles/mapbox/dark-v8` - Subtle dark backdrop for data vizualizations. | ||
* @param {boolean} [options.hash=false] If `true`, the map will track and update the page URL according to map position | ||
* @param {boolean} [options.interactive=true] If `false`, no mouse, touch, or keyboard listeners are attached to the map, so it will not respond to input | ||
* @param {number} [options.bearingSnap=7] Snap to north threshold in degrees. | ||
* @param {Array} options.classes Style class names with which to initialize the map | ||
* @param {Array} [options.classes] Style class names with which to initialize the map | ||
* @param {boolean} [options.attributionControl=true] If `true`, an attribution control will be added to the map. | ||
* @param {boolean} [options.failIfMajorPerformanceCaveat=false] If `true`, map creation will fail if the implementation determines that the performance of the created WebGL context would be dramatically lower than expected. | ||
@@ -80,2 +86,3 @@ * @param {boolean} [options.preserveDrawingBuffer=false] If `true`, The maps canvas can be exported to a PNG using `map.getCanvas().toDataURL();`. This is false by default as a performance optimization. | ||
'_onWindowResize', | ||
'onError', | ||
'update', | ||
@@ -120,2 +127,6 @@ 'render' | ||
if (options.attributionControl) this.addControl(new Attribution()); | ||
this.on('style.error', this.onError); | ||
this.on('source.error', this.onError); | ||
this.on('tile.error', this.onError); | ||
}; | ||
@@ -284,3 +295,3 @@ | ||
/** | ||
* Get all features at a point ([x, y]) | ||
* Get all features at a point ([x, y]). Only works on layers where `interactive` is set to true. | ||
* | ||
@@ -314,2 +325,55 @@ * @param {Array<number>} point [x, y] pixel coordinates | ||
/** | ||
* Get all features in a rectangle. | ||
* | ||
* Note: because features come from vector tiles, the returned features will be: | ||
* | ||
* 1. Truncated at tile boundaries. | ||
* 2. Duplicated across tile boundaries. | ||
* | ||
* For example, suppose there is a highway running through your rectangle in a `featuresIn` query. `featuresIn` will only give you the parts of the highway feature that lie within the map tiles covering your rectangle, even if the road actually extends into other tiles. Also, the portion of the highway within each map tile will come back as a separate feature. | ||
* | ||
* @param {Array<Point>|Array<Array<number>>} [bounds] Coordinates of opposite corners of bounding rectangle, in pixel coordinates. Optional: use entire viewport if omitted. | ||
* @param {Object} params | ||
* @param {string} params.layer Optional. Only return features from a given layer | ||
* @param {string} params.type Optional. Either `raster` or `vector` | ||
* @param {featuresAtCallback} callback function that receives the response | ||
* | ||
* @callback featuresInCallback | ||
* @param {Object|null} err Error _If any_ | ||
* @param {Array} features A JSON array of features given the passed parameters of `featuresIn` | ||
* | ||
* @returns {Map} `this` | ||
* | ||
* @example | ||
* map.featuresIn([[10, 20], [30, 50], { layer: 'my-layer-name' }, | ||
* function(err, features) { | ||
* console.log(features); | ||
* }); | ||
*/ | ||
featuresIn: function(bounds, params, callback) { | ||
if (typeof callback === 'undefined') { | ||
callback = params; | ||
params = bounds; | ||
// bounds was omitted: use full viewport | ||
bounds = [ | ||
Point.convert([0, 0]), | ||
Point.convert([this.transform.width, this.transform.height]) | ||
]; | ||
} | ||
bounds = bounds.map(Point.convert.bind(Point)); | ||
bounds = [ | ||
new Point( | ||
Math.min(bounds[0].x, bounds[1].x), | ||
Math.min(bounds[0].y, bounds[1].y) | ||
), | ||
new Point( | ||
Math.max(bounds[0].x, bounds[1].x), | ||
Math.max(bounds[0].y, bounds[1].y) | ||
) | ||
].map(this.transform.pointCoordinate.bind(this.transform)); | ||
this.style.featuresIn(bounds, params, callback); | ||
return this; | ||
}, | ||
/** | ||
* Apply multiple style mutations in a batch | ||
@@ -502,5 +566,6 @@ * | ||
setPaintProperty: function(layer, name, value, klass) { | ||
this.style.setPaintProperty(layer, name, value, klass); | ||
this.style._cascade(this._classes); | ||
this.update(true); | ||
this.batch(function(batch) { | ||
batch.setPaintProperty(layer, name, value, klass); | ||
}); | ||
return this; | ||
@@ -530,3 +595,6 @@ }, | ||
setLayoutProperty: function(layer, name, value) { | ||
this.style.setLayoutProperty(layer, name, value); | ||
this.batch(function(batch) { | ||
batch.setLayoutProperty(layer, name, value); | ||
}); | ||
return this; | ||
@@ -716,2 +784,16 @@ }, | ||
/** | ||
* A default error handler for `style.error`, `source.error`, and `tile.error` events. | ||
* It logs the error via `console.error`. | ||
* | ||
* @example | ||
* // Disable the default error handler | ||
* map.off('style.error', map.onError); | ||
* map.off('source.error', map.onError); | ||
* map.off('tile.error', map.onError); | ||
*/ | ||
onError: function(e) { | ||
console.error(e.error); | ||
}, | ||
_rerender: function() { | ||
@@ -740,2 +822,12 @@ if (this.style && !this._frameId) { | ||
_onStyleLoad: function(e) { | ||
var unset = new Transform(), | ||
tr = this.transform; | ||
if (tr.center.lng === unset.center.lng && tr.center.lat === unset.center.lat && | ||
tr.zoom === unset.zoom && | ||
tr.bearing === unset.bearing && | ||
tr.pitch === unset.pitch) { | ||
this.jumpTo(this.style.stylesheet); | ||
} | ||
this.style._cascade(this._classes, {transition: false}); | ||
@@ -742,0 +834,0 @@ this._forwardStyleEvent(e); |
@@ -6,2 +6,3 @@ 'use strict'; | ||
xhr.open('GET', url, true); | ||
xhr.setRequestHeader('Accept', 'application/json'); | ||
xhr.onerror = function(e) { | ||
@@ -52,19 +53,21 @@ callback(e); | ||
exports.getImage = function(url, callback) { | ||
var img = new Image(); | ||
if (!sameOrigin(url)) { | ||
img.crossOrigin = 'Anonymous'; | ||
} | ||
img.onload = function() { | ||
callback(null, img); | ||
}; | ||
img.src = url; | ||
img.getData = function() { | ||
var canvas = document.createElement('canvas'); | ||
var context = canvas.getContext('2d'); | ||
canvas.width = img.width; | ||
canvas.height = img.height; | ||
context.drawImage(img, 0, 0); | ||
return context.getImageData(0, 0, img.width, img.height).data; | ||
}; | ||
return img; | ||
return exports.getArrayBuffer(url, function(err, imgData) { | ||
if (err) callback(err); | ||
var img = new Image(); | ||
img.onload = function() { | ||
callback(null, img); | ||
(window.URL || window.webkitURL).revokeObjectURL(img.src); | ||
}; | ||
var blob = new Blob([new Uint8Array(imgData)], { type: 'image/png' }); | ||
img.src = (window.URL || window.webkitURL).createObjectURL(blob); | ||
img.getData = function() { | ||
var canvas = document.createElement('canvas'); | ||
var context = canvas.getContext('2d'); | ||
canvas.width = img.width; | ||
canvas.height = img.height; | ||
context.drawImage(img, 0, 0); | ||
return context.getImageData(0, 0, img.width, img.height).data; | ||
}; | ||
return img; | ||
}); | ||
}; | ||
@@ -71,0 +74,0 @@ |
@@ -126,2 +126,26 @@ 'use strict'; | ||
/* | ||
* Call an asynchronous function on an array of arguments, | ||
* calling `callback` with the completed results of all calls. | ||
* | ||
* @param {Array<*>} array input to each call of the async function. | ||
* @param {Function} fn an async function with signature (data, callback) | ||
* @param {Function} callback a callback run after all async work is done. | ||
* called with an array, containing the results of each async call. | ||
* @returns {undefined} | ||
* @private | ||
*/ | ||
exports.asyncAll = function (array, fn, callback) { | ||
var remaining = array.length; | ||
var results = new Array(array.length); | ||
var error = null; | ||
array.forEach(function (item, i) { | ||
fn(item, function (err, result) { | ||
if (err) error = err; | ||
results[i] = result; | ||
if (--remaining === 0) callback(error, results); | ||
}); | ||
}); | ||
}; | ||
/* | ||
* Compute the difference between the keys in one object and the keys | ||
@@ -128,0 +152,0 @@ * in another object. |
{ | ||
"name": "mapbox-gl", | ||
"description": "A WebGL interactive maps library", | ||
"version": "0.10.0", | ||
"version": "0.11.0", | ||
"main": "js/mapbox-gl.js", | ||
@@ -20,3 +20,3 @@ "license": "BSD-3-Clause", | ||
"mapbox-gl-function": "^1.0.0", | ||
"mapbox-gl-style-spec": "^8.0.1", | ||
"mapbox-gl-style-spec": "^8.1.0", | ||
"minifyify": "^7.0.1", | ||
@@ -41,7 +41,5 @@ "pbf": "^1.3.2", | ||
"istanbul": "^0.3.15", | ||
"mapbox-gl-test-suite": "git+https://github.com/mapbox/mapbox-gl-test-suite.git#f21d42c120e7652252d39bf3d4eb4edd2f172dfb", | ||
"mkdirp": "^0.5.1", | ||
"mapbox-gl-test-suite": "git+https://github.com/mapbox/mapbox-gl-test-suite.git#2004426964b2ff74c231eae00c69c1e7fc73a329", | ||
"prova": "^2.1.2", | ||
"sinon": "^1.15.4", | ||
"st": "^0.5.4", | ||
"through": "^2.3.7", | ||
@@ -48,0 +46,0 @@ "watchify": "^3.2.2" |
@@ -10,4 +10,4 @@ [![Build Status](https://circleci.com/gh/mapbox/mapbox-gl-js.svg?style=svg)](https://circleci.com/gh/mapbox/mapbox-gl-js) | ||
```html | ||
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.10.0/mapbox-gl.js'></script> | ||
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.10.0/mapbox-gl.css' rel='stylesheet' /> | ||
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.11.0/mapbox-gl.js'></script> | ||
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.11.0/mapbox-gl.css' rel='stylesheet' /> | ||
``` | ||
@@ -67,3 +67,3 @@ | ||
The FPS benchmarking page compares the performance of your local copy of GL JS against `v0.7.0`. Benchmarking configuration is within `bench/fps/site.js`. | ||
The FPS benchmarking page compares the performance of your local copy of GL JS against previously released versions. Benchmarking configuration is within `bench/fps/site.js`. | ||
@@ -90,7 +90,10 @@ To serve the FPS benchmark page: | ||
* Update CHANGELOG.md | ||
* Update the version number in package.json, README.md, _config.yml, and _config.mb-pages.yml | ||
* Merge `mb-pages` into `master`: | ||
* `git checkout master && git merge origin/mb-pages` | ||
* Update `CHANGELOG.md` | ||
* Update the version number in `package.json`, `README.md`, `bench/fps/site.js`, `_config.yml`, and `_config.mb-pages.yml` | ||
* Publish the build to the CDN (see below) | ||
* Publish the build to npm (`npm publish`) | ||
* Merge the `mb-pages` branch to `master` | ||
* Merge `master` into `mb-pages` and publish documentation: | ||
* `git checkout mb-pages && git merge master && git push origin mb-pages` | ||
@@ -97,0 +100,0 @@ The CI server publishes builds to the Mapbox CDN automatically, but it does not currently support building tags. Therefore, |
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 too big to display
Sorry, the diff of this file is too big to display
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
3886150
12
37899
121
163
7
3
1
Updatedmapbox-gl-style-spec@^8.1.0