Merge branch 'master' of github.com:openlayers/ol3 into vector

This commit is contained in:
ahocevar
2013-01-16 16:34:59 +01:00
73 changed files with 3039 additions and 1842 deletions

View File

@@ -7,7 +7,7 @@
@exportObjectLiteralProperty ol.control.MousePositionOptions.map ol.Map|undefined
@exportObjectLiteralProperty ol.control.MousePositionOptions.projection ol.Projection|undefined
@exportObjectLiteralProperty ol.control.MousePositionOptions.target Element|undefined
@exportObjectLiteralProperty ol.control.MousePositionOptions.undefinedHtml string|undefined
@exportObjectLiteralProperty ol.control.MousePositionOptions.undefinedHTML string|undefined
@exportObjectLiteral ol.control.ZoomOptions
@exportObjectLiteralProperty ol.control.ZoomOptions.delta number|undefined
@@ -24,7 +24,6 @@
@exportObjectLiteralProperty ol.layer.LayerOptions.visible boolean|undefined
@exportObjectLiteral ol.MapOptions
@exportObjectLiteralProperty ol.MapOptions.center ol.Coordinate|undefined
@exportObjectLiteralProperty ol.MapOptions.controls ol.Collection|undefined
@exportObjectLiteralProperty ol.MapOptions.doubleClickZoom boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.dragPan boolean|undefined
@@ -32,22 +31,14 @@
@exportObjectLiteralProperty ol.MapOptions.keyboard boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.keyboardPanOffset number|undefined
@exportObjectLiteralProperty ol.MapOptions.layers ol.Collection|undefined
@exportObjectLiteralProperty ol.MapOptions.maxResolution number|undefined
@exportObjectLiteralProperty ol.MapOptions.mouseWheelZoom boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.mouseWheelZoomDelta number|undefined
@exportObjectLiteralProperty ol.MapOptions.numZoomLevels number|undefined
@exportObjectLiteralProperty ol.MapOptions.projection ol.Projection|string|undefined
@exportObjectLiteralProperty ol.MapOptions.renderer ol.RendererHint|undefined
@exportObjectLiteralProperty ol.MapOptions.renderers Array.<ol.RendererHint>|undefined
@exportObjectLiteralProperty ol.MapOptions.resolution number|undefined
@exportObjectLiteralProperty ol.MapOptions.resolutions Array.<number>|undefined
@exportObjectLiteralProperty ol.MapOptions.rotate boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.shiftDragZoom boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.target Element|string
@exportObjectLiteralProperty ol.MapOptions.userProjection ol.Projection|string|undefined
@exportObjectLiteralProperty ol.MapOptions.zoom number|undefined
@exportObjectLiteralProperty ol.MapOptions.view ol.IView|undefined
@exportObjectLiteralProperty ol.MapOptions.zoomDelta number|undefined
@exportObjectLiteralProperty ol.MapOptions.zoomFactor number|undefined
@exportObjectLiteral ol.overlay.OverlayOptions
@exportObjectLiteralProperty ol.overlay.OverlayOptions.coordinate ol.Coordinate|undefined
@@ -71,3 +62,14 @@
@exportObjectLiteralProperty ol.source.TiledWMSOptions.projection ol.Projection|undefined
@exportObjectLiteralProperty ol.source.TiledWMSOptions.url string|undefined
@exportObjectLiteralProperty ol.source.TiledWMSOptions.urls Array.<string>|undefined
@exportObjectLiteral ol.View2DOptions
@exportObjectLiteralProperty ol.View2DOptions.center ol.Coordinate|undefined
@exportObjectLiteralProperty ol.View2DOptions.maxResolution number|undefined
@exportObjectLiteralProperty ol.View2DOptions.numZoomLevels number|undefined
@exportObjectLiteralProperty ol.View2DOptions.projection ol.Projection|string|undefined
@exportObjectLiteralProperty ol.View2DOptions.resolution number|undefined
@exportObjectLiteralProperty ol.View2DOptions.resolutions Array.<number>|undefined
@exportObjectLiteralProperty ol.View2DOptions.rotation number|undefined
@exportObjectLiteralProperty ol.View2DOptions.zoom number|undefined
@exportObjectLiteralProperty ol.View2DOptions.zoomFactor number|undefined

109
src/ol/animation.js Normal file
View File

@@ -0,0 +1,109 @@
// FIXME works for View2D only
goog.provide('ol.animation');
goog.require('goog.fx.easing');
goog.require('ol.PreRenderFunction');
goog.require('ol.View2D');
goog.require('ol.easing');
/**
* @param {number} resolution Resolution.
* @param {number=} opt_duration Duration.
* @param {number=} opt_start Start.
* @param {function(number): number=} opt_easingFunction Easing function.
* @return {ol.PreRenderFunction} Pre-render function.
*/
ol.animation.createBounce =
function(resolution, opt_duration, opt_start, opt_easingFunction) {
var start = goog.isDef(opt_start) ? opt_start : Date.now();
var duration = goog.isDef(opt_duration) ? opt_duration : 1000;
var easingFunction = goog.isDef(opt_easingFunction) ?
opt_easingFunction : ol.easing.upAndDown;
return function(map, frameState) {
if (frameState.time < start) {
frameState.animate = true;
frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
return true;
} else if (frameState.time < start + duration) {
var delta = easingFunction((frameState.time - start) / duration);
var deltaResolution = resolution - frameState.view2DState.resolution;
frameState.animate = true;
frameState.view2DState.resolution += delta * deltaResolution;
frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
return true;
} else {
return false;
}
};
};
/**
* @param {ol.Coordinate} source Source.
* @param {number=} opt_duration Duration.
* @param {number=} opt_start Start.
* @param {function(number): number=} opt_easingFunction Easing function.
* @return {ol.PreRenderFunction} Pre-render function.
*/
ol.animation.createPanFrom =
function(source, opt_duration, opt_start, opt_easingFunction) {
var start = goog.isDef(opt_start) ? opt_start : Date.now();
var sourceX = source.x;
var sourceY = source.y;
var duration = goog.isDef(opt_duration) ? opt_duration : 1000;
var easingFunction = goog.isDef(opt_easingFunction) ?
opt_easingFunction : goog.fx.easing.inAndOut;
return function(map, frameState) {
if (frameState.time < start) {
frameState.animate = true;
frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
return true;
} else if (frameState.time < start + duration) {
var delta = 1 - easingFunction((frameState.time - start) / duration);
var deltaX = sourceX - frameState.view2DState.center.x;
var deltaY = sourceY - frameState.view2DState.center.y;
frameState.animate = true;
frameState.view2DState.center.x += delta * deltaX;
frameState.view2DState.center.y += delta * deltaY;
frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
return true;
} else {
return false;
}
};
};
/**
* @param {number=} opt_duration Duration.
* @param {number=} opt_turns Turns.
* @param {number=} opt_start Start.
* @param {function(number): number=} opt_easingFunction Easing function.
* @return {ol.PreRenderFunction} Pre-render function.
*/
ol.animation.createSpin =
function(opt_duration, opt_turns, opt_start, opt_easingFunction) {
var start = goog.isDef(opt_start) ? opt_start : Date.now();
var duration = goog.isDef(opt_duration) ? opt_duration : 1000;
var turns = goog.isDef(opt_turns) ? opt_turns : 1;
var deltaTheta = 2 * turns * Math.PI;
var easingFunction = goog.isDef(opt_easingFunction) ?
opt_easingFunction : goog.fx.easing.inAndOut;
return function(map, frameState) {
if (frameState.time < start) {
frameState.animate = true;
frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
return true;
} else if (frameState.time < start + duration) {
var delta = easingFunction((frameState.time - start) / duration);
frameState.animate = true;
frameState.view2DState.rotation += delta * deltaTheta;
frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
return true;
} else {
return false;
}
};
};

View File

@@ -130,7 +130,7 @@ ol.Collection.prototype.getAt = function(index) {
* @return {number} Length.
*/
ol.Collection.prototype.getLength = function() {
return /** @type {number} */ this.get(ol.CollectionProperty.LENGTH);
return /** @type {number} */ (this.get(ol.CollectionProperty.LENGTH));
};

View File

@@ -46,3 +46,16 @@ ol.Color.createFromString = function(str, opt_a) {
var a = opt_a || 255;
return new ol.Color(rgb[0], rgb[1], rgb[2], a);
};
/**
* @param {ol.Color} color1 Color 1.
* @param {ol.Color} color2 Color 2.
* @return {boolean} Equals.
*/
ol.Color.equals = function(color1, color2) {
return (color1.r == color2.r &&
color1.g == color2.g &&
color1.b == color2.b &&
color1.a == color2.a);
};

View File

@@ -2,6 +2,7 @@
// FIXME handle date line wrap
// FIXME handle layer order
// FIXME check clean-up code
// FIXME works for View2D only
goog.provide('ol.control.Attribution');
@@ -14,6 +15,8 @@ goog.require('goog.style');
goog.require('ol.Collection');
goog.require('ol.CoverageArea');
goog.require('ol.TileCoverageArea');
goog.require('ol.View2D');
goog.require('ol.View2DProperty');
goog.require('ol.control.Control');
goog.require('ol.layer.Layer');
@@ -33,12 +36,6 @@ ol.control.Attribution = function(attributionOptions) {
'class': 'ol-attribution'
}, this.ulElement_);
/**
* @private
* @type {Array.<number>}
*/
this.layersListenerKeys_ = null;
/**
* @private
* @type {Object.<number, ?number>}
@@ -63,6 +60,18 @@ ol.control.Attribution = function(attributionOptions) {
*/
this.mapListenerKeys_ = null;
/**
* @private
* @type {Array.<number>}
*/
this.layersListenerKeys_ = null;
/**
* @private
* @type {Array.<number>}
*/
this.viewListenerKeys_ = null;
goog.base(this, {
element: element,
map: attributionOptions.map,
@@ -110,14 +119,17 @@ ol.control.Attribution.prototype.createAttributionElementsForLayer_ =
var map = this.getMap();
var mapIsDef = map.isDef();
var mapExtent = /** @type {ol.Extent} */ map.getExtent();
var mapProjection = /** @type {ol.Projection} */ map.getProjection();
var mapResolution = /** @type {number} */ map.getResolution();
var layerVisible = layer.getVisible();
var attributionVisibilities;
if (mapIsDef && layerVisible) {
var mapSize = /** @type {ol.Size} */ (map.getSize());
// FIXME works for View2D only
var view = map.getView();
goog.asserts.assert(view instanceof ol.View2D);
var mapExtent = view.getExtent(mapSize);
var mapProjection = /** @type {ol.Projection} */ (view.getProjection());
var mapResolution = /** @type {number} */ (view.getResolution());
attributionVisibilities = this.getLayerAttributionVisiblities_(
layer, mapExtent, mapResolution, mapProjection);
} else {
@@ -131,7 +143,7 @@ ol.control.Attribution.prototype.createAttributionElementsForLayer_ =
var attributionElement = goog.dom.createElement(goog.dom.TagName.LI);
attributionElement.innerHTML = attribution.getHtml();
if (!map.isDef ||
if (!mapIsDef ||
!layerVisible ||
goog.isNull(attributionVisibilities) ||
!attributionVisibilities[attributionKey]) {
@@ -151,8 +163,8 @@ ol.control.Attribution.prototype.createAttributionElementsForLayer_ =
/**
* @param {ol.layer.Layer} layer Layer.
* @param {ol.Extent} mapExtent Map extent.
* @param {number} mapResolution Map resolution.
* @param {ol.Extent} mapExtent View extent.
* @param {number} mapResolution View resolution.
* @param {ol.Projection} mapProjection Map projection.
* @return {Object.<number, boolean>} Attribution visibilities.
* @private
@@ -169,7 +181,7 @@ ol.control.Attribution.prototype.getLayerAttributionVisiblities_ =
var mapZ;
if (source instanceof ol.source.TileSource) {
var tileSource = /** @type {ol.source.TileSource} */ source;
var tileSource = /** @type {ol.source.TileSource} */ (source);
var tileGrid = tileSource.getTileGrid();
mapZ = tileGrid.getZForResolution(mapResolution);
}
@@ -230,7 +242,7 @@ ol.control.Attribution.prototype.getLayerAttributionVisiblities_ =
* @param {goog.events.Event} event Event.
*/
ol.control.Attribution.prototype.handleLayerLoad = function(event) {
var layer = /** @type {ol.layer.Layer} */ event.target;
var layer = /** @type {ol.layer.Layer} */ (event.target);
this.createAttributionElementsForLayer_(layer);
};
@@ -240,17 +252,8 @@ ol.control.Attribution.prototype.handleLayerLoad = function(event) {
* @protected
*/
ol.control.Attribution.prototype.handleLayerVisibleChanged = function(event) {
var map = this.getMap();
var mapIsDef = map.isDef();
var mapExtent = /** @type {ol.Extent} */ map.getExtent();
var mapProjection = /** @type {ol.Projection} */ map.getProjection();
var mapResolution = /** @type {number} */ map.getResolution();
var layer = /** @type {ol.layer.Layer} */ event.target;
this.updateLayerAttributionsVisibility_(
layer, mapIsDef, mapExtent, mapResolution, mapProjection);
var layer = /** @type {ol.layer.Layer} */ (event.target);
this.updateLayerAttributionsVisibility_(layer);
};
@@ -260,7 +263,7 @@ ol.control.Attribution.prototype.handleLayerVisibleChanged = function(event) {
* @protected
*/
ol.control.Attribution.prototype.handleLayersAdd = function(collectionEvent) {
var layer = /** @type {ol.layer.Layer} */ collectionEvent.elem;
var layer = /** @type {ol.layer.Layer} */ (collectionEvent.elem);
this.addLayer(layer);
};
@@ -271,7 +274,7 @@ ol.control.Attribution.prototype.handleLayersAdd = function(collectionEvent) {
*/
ol.control.Attribution.prototype.handleLayersRemove =
function(collectionEvent) {
var layer = /** @type {ol.layer.Layer} */ collectionEvent.elem;
var layer = /** @type {ol.layer.Layer} */ (collectionEvent.elem);
this.removeLayer(layer);
};
@@ -279,20 +282,26 @@ ol.control.Attribution.prototype.handleLayersRemove =
/**
* @protected
*/
ol.control.Attribution.prototype.handleMapChanged = function() {
ol.control.Attribution.prototype.handleMapViewChanged = function() {
if (!goog.isNull(this.viewListenerKeys_)) {
goog.array.forEach(this.viewListenerKeys_, goog.events.unlistenByKey);
this.viewListenerKeys_ = null;
}
var map = this.getMap();
var mapIsDef = map.isDef();
var mapExtent = /** @type {ol.Extent} */ map.getExtent();
var mapProjection = /** @type {ol.Projection} */ map.getProjection();
var mapResolution = map.getResolution();
var layers = map.getLayers();
layers.forEach(function(layer) {
this.updateLayerAttributionsVisibility_(
layer, mapIsDef, mapExtent, mapResolution, mapProjection);
}, this);
goog.asserts.assert(!goog.isNull(map));
var view = map.getView();
if (!goog.isNull(view)) {
// FIXME works for View2D only
goog.asserts.assert(view instanceof ol.View2D);
this.viewListenerKeys_ = [
goog.events.listen(
view, ol.Object.getChangedEventType(ol.View2DProperty.CENTER),
this.updateAttributions, false, this),
goog.events.listen(
view, ol.Object.getChangedEventType(ol.View2DProperty.RESOLUTION),
this.updateAttributions, false, this)
];
}
};
@@ -359,35 +368,51 @@ ol.control.Attribution.prototype.setMap = function(map) {
goog.base(this, 'setMap', map);
if (!goog.isNull(map)) {
this.mapListenerKeys_ = [
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.CENTER),
this.handleMapChanged, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.LAYERS),
this.handleMapLayersChanged, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.RESOLUTION),
this.handleMapChanged, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.SIZE),
this.handleMapChanged, false, this)
this.updateAttributions, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.VIEW),
this.updateAttributions, false, this)
];
this.handleMapViewChanged();
this.handleMapLayersChanged();
}
};
/**
* @protected
*/
ol.control.Attribution.prototype.updateAttributions = function() {
var map = this.getMap();
var layers = map.getLayers();
layers.forEach(function(layer) {
this.updateLayerAttributionsVisibility_(layer);
}, this);
};
/**
* @param {ol.layer.Layer} layer Layer.
* @param {boolean} mapIsDef Map is defined.
* @param {ol.Extent} mapExtent Map extent.
* @param {number} mapResolution Map resolution.
* @param {ol.Projection} mapProjection Map projection.
* @private
*/
ol.control.Attribution.prototype.updateLayerAttributionsVisibility_ =
function(layer, mapIsDef, mapExtent, mapResolution, mapProjection) {
if (mapIsDef && layer.getVisible()) {
function(layer) {
var map = this.getMap();
if (map.isDef() && layer.getVisible()) {
var mapSize = /** @type {ol.Size} */ (map.getSize());
var view = map.getView();
// FIXME works for View2D only
goog.asserts.assert(view instanceof ol.View2D);
var mapExtent = view.getExtent(mapSize);
var mapProjection = /** @type {ol.Projection} */ (view.getProjection());
var mapResolution = /** @type {number} */ (view.getResolution());
var attributionVisibilities = this.getLayerAttributionVisiblities_(
layer, mapExtent, mapResolution, mapProjection);
goog.object.forEach(

View File

@@ -1,7 +1,9 @@
// FIXME should listen on appropriate pane, once it is defined
// FIXME works for View2D only
goog.provide('ol.control.MousePosition');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.style');
@@ -47,8 +49,20 @@ ol.control.MousePosition = function(mousePositionOptions) {
* @private
* @type {string}
*/
this.undefinedHtml_ = goog.isDef(mousePositionOptions.undefinedHtml) ?
mousePositionOptions.undefinedHtml : '';
this.undefinedHTML_ = goog.isDef(mousePositionOptions.undefinedHTML) ?
mousePositionOptions.undefinedHTML : '';
/**
* @private
* @type {string}
*/
this.renderedHTML_ = element.innerHTML;
/**
* @private
* @type {ol.Projection}
*/
this.mapProjection_ = null;
/**
* @private
@@ -58,23 +72,38 @@ ol.control.MousePosition = function(mousePositionOptions) {
/**
* @private
* @type {Array.<number>}
* @type {ol.Projection}
*/
this.listenerKeys_ = [];
this.renderedProjection_ = null;
this.handleMapProjectionChanged();
/**
* @private
* @type {ol.Pixel}
*/
this.lastMouseMovePixel_ = null;
/**
* @private
* @type {Array.<?number>}
*/
this.listenerKeys_ = null;
};
goog.inherits(ol.control.MousePosition, ol.control.Control);
/**
* @param {ol.MapEvent} mapEvent Map event.
* @protected
*/
ol.control.MousePosition.prototype.handleMapProjectionChanged = function() {
this.updateTransform_();
// FIXME should we instead re-calculate using the last known mouse position?
this.element.innerHTML = this.undefinedHtml_;
ol.control.MousePosition.prototype.handleMapPostrender = function(mapEvent) {
var frameState = mapEvent.frameState;
if (goog.isNull(frameState)) {
this.mapProjection_ = null;
} else {
this.mapProjection_ = frameState.view2DState.projection;
}
this.updateHTML_(this.lastMouseMovePixel_);
};
@@ -87,19 +116,8 @@ ol.control.MousePosition.prototype.handleMouseMove = function(browserEvent) {
var eventPosition = goog.style.getRelativePosition(
browserEvent, map.getViewport());
var pixel = new ol.Pixel(eventPosition.x, eventPosition.y);
var coordinate = map.getCoordinateFromPixel(pixel);
var html;
if (!goog.isNull(coordinate)) {
coordinate = this.transform_(coordinate);
if (goog.isDef(this.coordinateFormat_)) {
html = this.coordinateFormat_(coordinate);
} else {
html = coordinate.toString();
}
} else {
html = this.undefinedHtml_;
}
this.element.innerHTML = html;
this.updateHTML_(pixel);
this.lastMouseMovePixel_ = pixel;
};
@@ -108,7 +126,8 @@ ol.control.MousePosition.prototype.handleMouseMove = function(browserEvent) {
* @protected
*/
ol.control.MousePosition.prototype.handleMouseOut = function(browserEvent) {
this.element.innerHTML = this.undefinedHtml_;
this.updateHTML_(null);
this.lastMouseMovePixel_ = null;
};
@@ -123,34 +142,47 @@ ol.control.MousePosition.prototype.setMap = function(map) {
goog.base(this, 'setMap', map);
if (!goog.isNull(map)) {
var viewport = map.getViewport();
this.listenerKeys = [
goog.events.listen(map,
ol.Object.getChangedEventType(ol.MapProperty.PROJECTION),
this.handleMapProjectionChanged, false, this),
this.listenerKeys_ = [
goog.events.listen(viewport, goog.events.EventType.MOUSEMOVE,
this.handleMouseMove, false, this),
goog.events.listen(viewport, goog.events.EventType.MOUSEOUT,
this.handleMouseOut, false, this)
this.handleMouseOut, false, this),
goog.events.listen(map, ol.MapEventType.POSTRENDER,
this.handleMapPostrender, false, this)
];
this.updateTransform_();
}
};
/**
* @param {?ol.Pixel} pixel Pixel.
* @private
*/
ol.control.MousePosition.prototype.updateTransform_ = function() {
var map = this.getMap();
if (goog.isNull(map)) {
this.transform_ = ol.Projection.identityTransform;
} else {
var mapProjection = map.getProjection();
if (!goog.isDef(mapProjection) || !goog.isDef(this.projection_)) {
this.transform_ = ol.Projection.identityTransform;
} else {
this.transform_ =
ol.Projection.getTransform(mapProjection, this.projection_);
ol.control.MousePosition.prototype.updateHTML_ = function(pixel) {
var html = this.undefinedHTML_;
if (!goog.isNull(pixel)) {
if (this.renderedProjection_ != this.mapProjection_) {
if (goog.isDef(this.projection_)) {
this.transform_ = ol.Projection.getTransform(
this.mapProjection_, this.projection_);
} else {
this.transform_ = ol.Projection.identityTransform;
}
this.renderedProjection_ = this.mapProjection_;
}
var map = this.getMap();
var coordinate = map.getCoordinateFromPixel(pixel);
if (!goog.isNull(coordinate)) {
coordinate = this.transform_(coordinate);
if (goog.isDef(this.coordinateFormat_)) {
html = this.coordinateFormat_(coordinate);
} else {
html = coordinate.toString();
}
}
}
if (!goog.isDef(this.renderedHTML_) || html != this.renderedHTML_) {
this.element.innerHTML = html;
this.renderedHTML_ = html;
}
};

View File

@@ -1,3 +1,5 @@
// FIXME works for View2D only
goog.provide('ol.control.Zoom');
goog.require('goog.dom');
@@ -58,7 +60,9 @@ goog.inherits(ol.control.Zoom, ol.control.Control);
ol.control.Zoom.prototype.handleIn_ = function(browserEvent) {
// prevent #zoomIn anchor from getting appended to the url
browserEvent.preventDefault();
this.getMap().zoom(this.delta_);
var map = this.getMap();
// FIXME works for View2D only
map.getView().zoom(map, this.delta_);
};
@@ -69,5 +73,7 @@ ol.control.Zoom.prototype.handleIn_ = function(browserEvent) {
ol.control.Zoom.prototype.handleOut_ = function(browserEvent) {
// prevent #zoomOut anchor from getting appended to the url
browserEvent.preventDefault();
this.getMap().zoom(-this.delta_);
var map = this.getMap();
// FIXME works for View2D only
map.getView().zoom(map, -this.delta_);
};

86
src/ol/dom/dom.js Normal file
View File

@@ -0,0 +1,86 @@
// FIXME add tests for browser features (Modernizr?)
// FIXME implement Matrix Filter for IE < 9
goog.provide('ol.dom');
goog.provide('ol.dom.BrowserFeature');
goog.require('goog.vec.Mat4');
/**
* @enum {boolean}
*/
ol.dom.BrowserFeature = {
CAN_USE_CSS_TRANSFORM: false,
CAN_USE_CSS_TRANSFORM3D: true,
CAN_USE_MATRIX_FILTER: false
};
/**
* @param {Element} element Element.
* @param {string} value Value.
*/
ol.dom.setTransform = function(element, value) {
var style = element.style;
style.WebkitTransform = value;
style.MozTransform = value;
style.OTransform = value;
style.transform = value;
};
/**
* @param {Element} element Element.
* @param {goog.vec.Mat4.AnyType} transform Matrix.
* @param {number=} opt_precision Precision.
*/
ol.dom.transformElement2D = function(element, transform, opt_precision) {
// using matrix() causes gaps in Chrome and Firefox on Mac OS X, so prefer
// matrix3d()
var i;
if (ol.dom.BrowserFeature.CAN_USE_CSS_TRANSFORM3D) {
var value3D;
if (goog.isDef(opt_precision)) {
/** @type {Array.<string>} */
var strings3D = new Array(16);
for (i = 0; i < 16; ++i) {
strings3D[i] = transform[i].toFixed(opt_precision);
}
value3D = strings3D.join(',');
} else {
value3D = transform.join(',');
}
ol.dom.setTransform(element, 'matrix3d(' + value3D + ')');
} else if (ol.dom.BrowserFeature.CAN_USE_CSS_TRANSFORM) {
/** @type {Array.<number>} */
var transform2D = [
goog.vec.Mat4.getElement(transform, 0, 0),
goog.vec.Mat4.getElement(transform, 1, 0),
goog.vec.Mat4.getElement(transform, 0, 1),
goog.vec.Mat4.getElement(transform, 1, 1),
goog.vec.Mat4.getElement(transform, 0, 3),
goog.vec.Mat4.getElement(transform, 1, 3)
];
var value2D;
if (goog.isDef(opt_precision)) {
/** @type {Array.<string>} */
var strings2D = new Array(6);
for (i = 0; i < 6; ++i) {
strings2D[i] = transform2D[i].toFixed(opt_precision);
}
value2D = strings2D.join(',');
} else {
value2D = transform2D.join(',');
}
ol.dom.setTransform(element, 'matrix(' + value2D + ')');
} else if (ol.dom.BrowserFeature.CAN_USE_MATRIX_FILTER) {
// http://msdn.microsoft.com/en-us/library/ms533014%28VS.85,loband%29.aspx
goog.asserts.assert(false); // FIXME
} else {
// FIXME check this code!
var style = element.style;
style.left = Math.round(goog.vec.Mat4.getElement(transform, 0, 3)) + 'px';
style.top = Math.round(goog.vec.Mat4.getElement(transform, 1, 3)) + 'px';
}
};

14
src/ol/easing.js Normal file
View File

@@ -0,0 +1,14 @@
goog.provide('ol.easing');
/**
* @param {number} t Input between 0 and 1.
* @return {number} Output between 0 and 1.
*/
ol.easing.upAndDown = function(t) {
if (t < 0.5) {
return goog.fx.easing.inAndOut(2 * t);
} else {
return 1 - goog.fx.easing.inAndOut(2 * (t - 0.5));
}
};

44
src/ol/framestate.js Normal file
View File

@@ -0,0 +1,44 @@
// FIXME add view3DState
goog.provide('ol.FrameState');
goog.provide('ol.PostRenderFunction');
goog.provide('ol.PreRenderFunction');
goog.require('goog.vec.Mat4');
goog.require('ol.Color');
goog.require('ol.Coordinate');
goog.require('ol.Extent');
goog.require('ol.Size');
goog.require('ol.TileQueue');
goog.require('ol.View2DState');
goog.require('ol.layer.LayerState');
/**
* @typedef {{animate: boolean,
* backgroundColor: ol.Color,
* coordinateToPixelMatrix: goog.vec.Mat4.Number,
* extent: (null|ol.Extent),
* layersArray: Array.<ol.layer.Layer>,
* layerStates: Object.<number, ol.layer.LayerState>,
* pixelToCoordinateMatrix: goog.vec.Mat4.Number,
* postRenderFunctions: Array.<ol.PostRenderFunction>,
* size: ol.Size,
* tileQueue: ol.TileQueue,
* time: number,
* view2DState: ol.View2DState,
* viewHints: Array.<number>}}
*/
ol.FrameState;
/**
* @typedef {function(ol.Map, ?ol.FrameState): boolean}
*/
ol.PostRenderFunction;
/**
* @typedef {function(ol.Map, ?ol.FrameState): boolean}
*/
ol.PreRenderFunction;

138
src/ol/imagetile.js Normal file
View File

@@ -0,0 +1,138 @@
goog.provide('ol.ImageTile');
goog.require('goog.array');
goog.require('goog.events');
goog.require('goog.events.EventTarget');
goog.require('goog.events.EventType');
goog.require('ol.Tile');
goog.require('ol.TileCoord');
goog.require('ol.TileState');
/**
* @constructor
* @extends {ol.Tile}
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @param {string} src Image source URI.
* @param {?string} crossOrigin Cross origin.
*/
ol.ImageTile = function(tileCoord, src, crossOrigin) {
goog.base(this, tileCoord);
/**
* Image URI
*
* @private
* @type {string}
*/
this.src_ = src;
/**
* @private
* @type {Image}
*/
this.image_ = new Image();
if (!goog.isNull(crossOrigin)) {
this.image_.crossOrigin = crossOrigin;
}
/**
* @private
* @type {Object.<number, Image>}
*/
this.imageByContext_ = {};
/**
* @private
* @type {Array.<number>}
*/
this.imageListenerKeys_ = null;
};
goog.inherits(ol.ImageTile, ol.Tile);
/**
* @inheritDoc
*/
ol.ImageTile.prototype.getImage = function(opt_context) {
if (goog.isDef(opt_context)) {
var image;
var key = goog.getUid(opt_context);
if (key in this.imageByContext_) {
return this.imageByContext_[key];
} else if (goog.object.isEmpty(this.imageByContext_)) {
image = this.image_;
} else {
image = /** @type {Image} */ (this.image_.cloneNode(false));
}
this.imageByContext_[key] = image;
return image;
} else {
return this.image_;
}
};
/**
* @inheritDoc
*/
ol.ImageTile.prototype.getKey = function() {
return this.src_;
};
/**
* Tracks loading or read errors.
*
* @private
*/
ol.ImageTile.prototype.handleImageError_ = function() {
this.state = ol.TileState.ERROR;
this.unlistenImage_();
this.dispatchChangeEvent();
};
/**
* Tracks successful image load.
*
* @private
*/
ol.ImageTile.prototype.handleImageLoad_ = function() {
this.state = ol.TileState.LOADED;
this.unlistenImage_();
this.dispatchChangeEvent();
};
/**
* Load not yet loaded URI.
*/
ol.ImageTile.prototype.load = function() {
if (this.state == ol.TileState.IDLE) {
this.state = ol.TileState.LOADING;
goog.asserts.assert(goog.isNull(this.imageListenerKeys_));
this.imageListenerKeys_ = [
goog.events.listenOnce(this.image_, goog.events.EventType.ERROR,
this.handleImageError_, false, this),
goog.events.listenOnce(this.image_, goog.events.EventType.LOAD,
this.handleImageLoad_, false, this)
];
this.image_.src = this.src_;
}
};
/**
* Discards event handlers which listen for load completion or errors.
*
* @private
*/
ol.ImageTile.prototype.unlistenImage_ = function() {
goog.asserts.assert(!goog.isNull(this.imageListenerKeys_));
goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey);
this.imageListenerKeys_ = null;
};

View File

@@ -1,7 +1,10 @@
// FIXME works for View2D only
goog.provide('ol.interaction.DblClickZoom');
goog.require('ol.MapBrowserEvent');
goog.require('ol.MapBrowserEvent.EventType');
goog.require('ol.View2D');
goog.require('ol.interaction.Interaction');
@@ -35,7 +38,10 @@ ol.interaction.DblClickZoom.prototype.handleMapBrowserEvent =
var anchor = mapBrowserEvent.getCoordinate();
var delta = mapBrowserEvent.browserEvent.shiftKey ?
-this.delta_ : this.delta_;
map.zoom(delta, anchor);
// FIXME works for View2D only
var view = map.getView();
goog.asserts.assert(view instanceof ol.View2D);
view.zoom(map, delta, anchor);
mapBrowserEvent.preventDefault();
browserEvent.preventDefault();
}

View File

@@ -89,6 +89,7 @@ ol.interaction.Drag.prototype.handleMapBrowserEvent =
if (!map.isDef()) {
return;
}
var view = map.getView();
var browserEvent = mapBrowserEvent.browserEvent;
if (this.dragging_) {
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DRAG) {
@@ -109,9 +110,9 @@ ol.interaction.Drag.prototype.handleMapBrowserEvent =
this.startY = browserEvent.clientY;
this.deltaX = 0;
this.deltaY = 0;
this.startCenter = /** @type {!ol.Coordinate} */ map.getCenter();
this.startCenter = /** @type {!ol.Coordinate} */ (view.getCenter());
this.startCoordinate = /** @type {ol.Coordinate} */
mapBrowserEvent.getCoordinate();
(mapBrowserEvent.getCoordinate());
var handled = this.handleDragStart(mapBrowserEvent);
if (handled) {
this.dragging_ = true;

View File

@@ -1,7 +1,12 @@
// FIXME works for View2D only
goog.provide('ol.interaction.DragPan');
goog.require('goog.asserts');
goog.require('ol.Coordinate');
goog.require('ol.MapBrowserEvent');
goog.require('ol.View2D');
goog.require('ol.ViewHint');
goog.require('ol.interaction.ConditionType');
goog.require('ol.interaction.Drag');
@@ -31,17 +36,28 @@ goog.inherits(ol.interaction.DragPan, ol.interaction.Drag);
*/
ol.interaction.DragPan.prototype.handleDrag = function(mapBrowserEvent) {
var map = mapBrowserEvent.map;
var resolution = map.getResolution();
var rotation = map.getRotation();
// FIXME works for View2D only
var view = map.getView();
goog.asserts.assert(view instanceof ol.View2D);
var resolution = view.getResolution();
var rotation = view.getRotation();
var delta =
new ol.Coordinate(-resolution * this.deltaX, resolution * this.deltaY);
if (map.canRotate() && goog.isDef(rotation)) {
delta.rotate(rotation);
}
delta.rotate(rotation);
var newCenter = new ol.Coordinate(
this.startCenter.x + delta.x, this.startCenter.y + delta.y);
map.requestRenderFrame();
map.setCenter(newCenter);
view.setCenter(newCenter);
};
/**
* @inheritDoc
*/
ol.interaction.DragPan.prototype.handleDragEnd = function(mapBrowserEvent) {
var map = mapBrowserEvent.map;
map.requestRenderFrame();
map.getView().setHint(ol.ViewHint.PANNING, -1);
};
@@ -51,7 +67,9 @@ ol.interaction.DragPan.prototype.handleDrag = function(mapBrowserEvent) {
ol.interaction.DragPan.prototype.handleDragStart = function(mapBrowserEvent) {
var browserEvent = mapBrowserEvent.browserEvent;
if (this.condition_(browserEvent)) {
mapBrowserEvent.map.requestRenderFrame();
var map = mapBrowserEvent.map;
map.requestRenderFrame();
map.getView().setHint(ol.ViewHint.PANNING, 1);
return true;
} else {
return false;

View File

@@ -1,7 +1,10 @@
// FIXME works for View2D only
goog.provide('ol.interaction.DragRotateAndZoom');
goog.require('goog.math.Vec2');
goog.require('ol.MapBrowserEvent');
goog.require('ol.View2D');
goog.require('ol.interaction.ConditionType');
goog.require('ol.interaction.Drag');
@@ -50,12 +53,15 @@ ol.interaction.DragRotateAndZoom.prototype.handleDrag =
browserEvent.offsetX - size.width / 2,
size.height / 2 - browserEvent.offsetY);
var theta = Math.atan2(delta.y, delta.x);
var resolution = this.startRatio_ * delta.magnitude();
// FIXME works for View2D only
var view = map.getView();
goog.asserts.assert(view instanceof ol.View2D);
map.requestRenderFrame();
// FIXME the calls to map.rotate and map.zoomToResolution should use
// map.withFrozenRendering but an assertion fails :-(
map.rotate(this.startRotation_, -theta);
var resolution = this.startRatio_ * delta.magnitude();
map.zoomToResolution(resolution);
view.rotate(map, this.startRotation_, -theta);
view.zoomToResolution(map, resolution);
};
@@ -66,14 +72,15 @@ ol.interaction.DragRotateAndZoom.prototype.handleDragStart =
function(mapBrowserEvent) {
var browserEvent = mapBrowserEvent.browserEvent;
var map = mapBrowserEvent.map;
if (map.canRotate() && this.condition_(browserEvent)) {
var resolution = map.getResolution();
var view = map.getView().getView2D();
if (this.condition_(browserEvent)) {
var resolution = view.getResolution();
var size = map.getSize();
var delta = new goog.math.Vec2(
browserEvent.offsetX - size.width / 2,
size.height / 2 - browserEvent.offsetY);
var theta = Math.atan2(delta.y, delta.x);
this.startRotation_ = (map.getRotation() || 0) + theta;
this.startRotation_ = (view.getRotation() || 0) + theta;
this.startRatio_ = resolution / delta.magnitude();
map.requestRenderFrame();
return true;

View File

@@ -1,6 +1,7 @@
goog.provide('ol.interaction.DragRotate');
goog.require('ol.MapBrowserEvent');
goog.require('ol.View2D');
goog.require('ol.interaction.ConditionType');
goog.require('ol.interaction.Drag');
@@ -42,8 +43,11 @@ ol.interaction.DragRotate.prototype.handleDrag = function(mapBrowserEvent) {
var theta = Math.atan2(
size.height / 2 - offset.y,
offset.x - size.width / 2);
// FIXME supports View2D only
var view = map.getView();
goog.asserts.assert(view instanceof ol.View2D);
map.requestRenderFrame();
map.rotate(this.startRotation_, -theta);
view.rotate(map, this.startRotation_, -theta);
};
@@ -54,15 +58,17 @@ ol.interaction.DragRotate.prototype.handleDragStart =
function(mapBrowserEvent) {
var browserEvent = mapBrowserEvent.browserEvent;
var map = mapBrowserEvent.map;
if (browserEvent.isMouseActionButton() && this.condition_(browserEvent) &&
map.canRotate()) {
// FIXME supports View2D only
var view = map.getView();
goog.asserts.assert(view instanceof ol.View2D);
if (browserEvent.isMouseActionButton() && this.condition_(browserEvent)) {
map.requestRenderFrame();
var size = map.getSize();
var offset = mapBrowserEvent.getPixel();
var theta = Math.atan2(
size.height / 2 - offset.y,
offset.x - size.width / 2);
this.startRotation_ = (map.getRotation() || 0) + theta;
this.startRotation_ = (view.getRotation() || 0) + theta;
return true;
} else {
return false;

View File

@@ -1,4 +1,5 @@
// FIXME draw drag box
// FIXME works for View2D only
goog.provide('ol.interaction.DragZoom');
@@ -63,7 +64,15 @@ ol.interaction.DragZoom.prototype.handleDragEnd =
var extent = ol.Extent.boundingExtent(
this.startCoordinate,
mapBrowserEvent.getCoordinate());
map.fitExtent(extent);
map.withFrozenRendering(function() {
// FIXME works for View2D only
var view = map.getView();
goog.asserts.assert(view instanceof ol.View2D);
var mapSize = /** @type {ol.Size} */ (map.getSize());
view.fitExtent(extent, mapSize);
// FIXME we should preserve rotation
view.setRotation(0);
});
}
};

View File

@@ -43,7 +43,7 @@ ol.interaction.Keyboard.prototype.handleMapBrowserEvent =
function(mapBrowserEvent) {
if (mapBrowserEvent.type == goog.events.KeyHandler.EventType.KEY) {
var keyEvent = /** @type {goog.events.KeyEvent} */
mapBrowserEvent.browserEvent;
(mapBrowserEvent.browserEvent);
var callback = this.charCodeCallbacks_[keyEvent.charCode];
if (callback) {
callback();

View File

@@ -1,7 +1,10 @@
// FIXME works for View2D only
goog.provide('ol.interaction.KeyboardPan');
goog.require('goog.events.KeyCodes');
goog.require('goog.events.KeyHandler.EventType');
goog.require('ol.View2D');
goog.require('ol.interaction.Interaction');
@@ -32,14 +35,17 @@ ol.interaction.KeyboardPan.prototype.handleMapBrowserEvent =
function(mapBrowserEvent) {
if (mapBrowserEvent.type == goog.events.KeyHandler.EventType.KEY) {
var keyEvent = /** @type {goog.events.KeyEvent} */
mapBrowserEvent.browserEvent;
(mapBrowserEvent.browserEvent);
var keyCode = keyEvent.keyCode;
if (keyCode == goog.events.KeyCodes.DOWN ||
keyCode == goog.events.KeyCodes.LEFT ||
keyCode == goog.events.KeyCodes.RIGHT ||
keyCode == goog.events.KeyCodes.UP) {
var map = mapBrowserEvent.map;
var resolution = map.getResolution();
// FIXME works for View2D only
var view = map.getView();
goog.asserts.assert(view instanceof ol.View2D);
var resolution = view.getResolution();
var delta;
var mapUnitsDelta = resolution * this.pixelDelta_;
if (keyCode == goog.events.KeyCodes.DOWN) {
@@ -52,10 +58,10 @@ ol.interaction.KeyboardPan.prototype.handleMapBrowserEvent =
goog.asserts.assert(keyCode == goog.events.KeyCodes.UP);
delta = new ol.Coordinate(0, mapUnitsDelta);
}
var oldCenter = map.getCenter();
var oldCenter = view.getCenter();
var newCenter = new ol.Coordinate(
oldCenter.x + delta.x, oldCenter.y + delta.y);
map.setCenter(newCenter);
view.setCenter(newCenter);
keyEvent.preventDefault();
mapBrowserEvent.preventDefault();
}

View File

@@ -1,7 +1,10 @@
// FIXME works for View2D only
goog.provide('ol.interaction.KeyboardZoom');
goog.require('goog.events.KeyCodes');
goog.require('goog.events.KeyHandler.EventType');
goog.require('ol.View2D');
goog.require('ol.interaction.Interaction');
@@ -23,12 +26,15 @@ ol.interaction.KeyboardZoom.prototype.handleMapBrowserEvent =
function(mapBrowserEvent) {
if (mapBrowserEvent.type == goog.events.KeyHandler.EventType.KEY) {
var keyEvent = /** @type {goog.events.KeyEvent} */
mapBrowserEvent.browserEvent;
(mapBrowserEvent.browserEvent);
var charCode = keyEvent.charCode;
if (charCode == '+'.charCodeAt(0) || charCode == '-'.charCodeAt(0)) {
var map = mapBrowserEvent.map;
var delta = (charCode == '+'.charCodeAt(0)) ? 4 : -4;
map.zoom(delta);
// FIXME works for View2D only
var view = map.getView();
goog.asserts.assert(view instanceof ol.View2D);
view.zoom(map, delta);
keyEvent.preventDefault();
mapBrowserEvent.preventDefault();
}

View File

@@ -1,8 +1,11 @@
// FIXME works for View2D only
goog.provide('ol.interaction.MouseWheelZoom');
goog.require('goog.events.MouseWheelEvent');
goog.require('goog.events.MouseWheelHandler.EventType');
goog.require('ol.MapBrowserEvent');
goog.require('ol.View2D');
@@ -32,12 +35,15 @@ ol.interaction.MouseWheelZoom.prototype.handleMapBrowserEvent =
goog.events.MouseWheelHandler.EventType.MOUSEWHEEL) {
var map = mapBrowserEvent.map;
var mouseWheelEvent = /** @type {goog.events.MouseWheelEvent} */
mapBrowserEvent.browserEvent;
(mapBrowserEvent.browserEvent);
goog.asserts.assert(mouseWheelEvent instanceof goog.events.MouseWheelEvent);
var anchor = mapBrowserEvent.getCoordinate();
var delta = mouseWheelEvent.deltaY < 0 ? this.delta_ : -this.delta_;
// FIXME works for View2D only
var view = map.getView();
goog.asserts.assert(view instanceof ol.View2D);
map.requestRenderFrame();
map.zoom(delta, anchor);
view.zoom(map, delta, anchor);
mapBrowserEvent.preventDefault();
mouseWheelEvent.preventDefault();
}

27
src/ol/iview.js Normal file
View File

@@ -0,0 +1,27 @@
goog.provide('ol.IView');
goog.require('ol.IView2D');
goog.require('ol.IView3D');
/**
* Interface for views.
* @interface
*/
ol.IView = function() {
};
/**
* @return {ol.IView2D} View2D.
*/
ol.IView.prototype.getView2D = function() {
};
/**
* @return {ol.IView3D} View3D.
*/
ol.IView.prototype.getView3D = function() {
};

59
src/ol/iview2d.js Normal file
View File

@@ -0,0 +1,59 @@
goog.provide('ol.IView2D');
goog.provide('ol.View2DState');
goog.require('ol.Coordinate');
goog.require('ol.Extent');
goog.require('ol.Projection');
/**
* @typedef {{center: ol.Coordinate,
* projection: ol.Projection,
* resolution: number,
* rotation: number}}
*/
ol.View2DState;
/**
* Interface for views.
* @interface
*/
ol.IView2D = function() {
};
/**
* @return {ol.Coordinate|undefined} Map center.
*/
ol.IView2D.prototype.getCenter = function() {
};
/**
* @return {ol.Projection|undefined} Map projection.
*/
ol.IView2D.prototype.getProjection = function() {
};
/**
* @return {number|undefined} Map resolution.
*/
ol.IView2D.prototype.getResolution = function() {
};
/**
* @return {number|undefined} Map rotation.
*/
ol.IView2D.prototype.getRotation = function() {
};
/**
* @return {ol.View2DState} View2D state.
*/
ol.IView2D.prototype.getView2DState = function() {
};

12
src/ol/iview3d.js Normal file
View File

@@ -0,0 +1,12 @@
goog.provide('ol.IView3D');
/**
* Interface for views.
* @interface
*/
ol.IView3D = function() {
};

View File

@@ -1,5 +1,6 @@
goog.provide('ol.layer.Layer');
goog.provide('ol.layer.LayerProperty');
goog.provide('ol.layer.LayerState');
goog.require('goog.events');
goog.require('goog.events.EventType');
@@ -21,6 +22,18 @@ ol.layer.LayerProperty = {
};
/**
* @typedef {{brightness: number,
* contrast: number,
* hue: number,
* opacity: number,
* ready: boolean,
* saturation: number,
* visible: boolean}}
*/
ol.layer.LayerState;
/**
* @constructor
@@ -71,7 +84,7 @@ ol.layer.Layer.prototype.dispatchLoadEvent_ = function() {
* @return {number} Brightness.
*/
ol.layer.Layer.prototype.getBrightness = function() {
return /** @type {number} */ this.get(ol.layer.LayerProperty.BRIGHTNESS);
return /** @type {number} */ (this.get(ol.layer.LayerProperty.BRIGHTNESS));
};
goog.exportProperty(
ol.layer.Layer.prototype,
@@ -83,7 +96,7 @@ goog.exportProperty(
* @return {number} Contrast.
*/
ol.layer.Layer.prototype.getContrast = function() {
return /** @type {number} */ this.get(ol.layer.LayerProperty.CONTRAST);
return /** @type {number} */ (this.get(ol.layer.LayerProperty.CONTRAST));
};
goog.exportProperty(
ol.layer.Layer.prototype,
@@ -95,7 +108,7 @@ goog.exportProperty(
* @return {number} Hue.
*/
ol.layer.Layer.prototype.getHue = function() {
return /** @type {number} */ this.get(ol.layer.LayerProperty.HUE);
return /** @type {number} */ (this.get(ol.layer.LayerProperty.HUE));
};
goog.exportProperty(
ol.layer.Layer.prototype,
@@ -103,11 +116,34 @@ goog.exportProperty(
ol.layer.Layer.prototype.getHue);
/**
* @return {ol.layer.LayerState} Layer state.
*/
ol.layer.Layer.prototype.getLayerState = function() {
var brightness = this.getBrightness();
var contrast = this.getContrast();
var hue = this.getHue();
var opacity = this.getOpacity();
var ready = this.isReady();
var saturation = this.getSaturation();
var visible = this.getVisible();
return {
brightness: goog.isDef(brightness) ? brightness : 0,
contrast: goog.isDef(contrast) ? contrast : 1,
hue: goog.isDef(hue) ? hue : 0,
opacity: goog.isDef(opacity) ? opacity : 1,
ready: ready,
saturation: goog.isDef(saturation) ? saturation : 1,
visible: goog.isDef(visible) ? visible : true
};
};
/**
* @return {number} Opacity.
*/
ol.layer.Layer.prototype.getOpacity = function() {
return /** @type {number} */ this.get(ol.layer.LayerProperty.OPACITY);
return /** @type {number} */ (this.get(ol.layer.LayerProperty.OPACITY));
};
goog.exportProperty(
ol.layer.Layer.prototype,
@@ -119,7 +155,7 @@ goog.exportProperty(
* @return {number} Saturation.
*/
ol.layer.Layer.prototype.getSaturation = function() {
return /** @type {number} */ this.get(ol.layer.LayerProperty.SATURATION);
return /** @type {number} */ (this.get(ol.layer.LayerProperty.SATURATION));
};
goog.exportProperty(
ol.layer.Layer.prototype,
@@ -139,7 +175,7 @@ ol.layer.Layer.prototype.getSource = function() {
* @return {boolean} Visible.
*/
ol.layer.Layer.prototype.getVisible = function() {
return /** @type {boolean} */ this.get(ol.layer.LayerProperty.VISIBLE);
return /** @type {boolean} */ (this.get(ol.layer.LayerProperty.VISIBLE));
};
goog.exportProperty(
ol.layer.Layer.prototype,

View File

@@ -20,5 +20,5 @@ goog.inherits(ol.layer.TileLayer, ol.layer.Layer);
* @return {ol.source.TileSource} Source.
*/
ol.layer.TileLayer.prototype.getTileSource = function() {
return /** @type {ol.source.TileSource} */ this.getSource();
return /** @type {ol.source.TileSource} */ (this.getSource());
};

View File

@@ -3,10 +3,11 @@
// FIXME add tilt and height?
goog.provide('ol.Map');
goog.provide('ol.MapEventType');
goog.provide('ol.MapProperty');
goog.provide('ol.RendererHint');
goog.provide('ol.RendererHints');
goog.require('goog.Uri.QueryData');
goog.require('goog.array');
goog.require('goog.async.AnimationDelay');
goog.require('goog.debug.Logger');
@@ -22,22 +23,24 @@ goog.require('goog.events.KeyHandler.EventType');
goog.require('goog.events.MouseWheelEvent');
goog.require('goog.events.MouseWheelHandler');
goog.require('goog.events.MouseWheelHandler.EventType');
goog.require('goog.functions');
goog.require('goog.object');
goog.require('ol.BrowserFeature');
goog.require('ol.Collection');
goog.require('ol.Color');
goog.require('ol.Constraints');
goog.require('ol.Coordinate');
goog.require('ol.Extent');
goog.require('ol.FrameState');
goog.require('ol.MapBrowserEvent');
goog.require('ol.Object');
goog.require('ol.Pixel');
goog.require('ol.Projection');
goog.require('ol.ResolutionConstraint');
goog.require('ol.RotationConstraint');
goog.require('ol.Size');
goog.require('ol.TileQueue');
goog.require('ol.TransformFunction');
goog.require('ol.View');
goog.require('ol.View2D');
goog.require('ol.View2DState');
goog.require('ol.control.Attribution');
goog.require('ol.control.Zoom');
goog.require('ol.interaction.DblClickZoom');
@@ -87,26 +90,14 @@ ol.DEFAULT_RENDERER_HINTS = [
];
/**
* @enum {string}
*/
ol.MapEventType = {
POSTRENDER: 'postrender'
};
/**
* @enum {string}
*/
ol.MapProperty = {
BACKGROUND_COLOR: 'backgroundColor',
CENTER: 'center',
LAYERS: 'layers',
PROJECTION: 'projection',
RESOLUTION: 'resolution',
ROTATION: 'rotation',
SIZE: 'size',
USER_PROJECTION: 'userProjection'
VIEW: 'view'
};
@@ -130,18 +121,6 @@ ol.Map = function(mapOptions) {
var mapOptionsInternal = ol.Map.createOptionsInternal(mapOptions);
/**
* @type {ol.TransformFunction}
* @private
*/
this.userToMapTransform_ = ol.Projection.identityTransform;
/**
* @type {ol.TransformFunction}
* @private
*/
this.mapToUserTransform_ = ol.Projection.cloneTransform;
/**
* @private
* @type {goog.async.AnimationDelay}
@@ -150,6 +129,24 @@ ol.Map = function(mapOptions) {
new goog.async.AnimationDelay(this.renderFrame_, undefined, this);
this.registerDisposable(this.animationDelay_);
/**
* @private
* @type {goog.vec.Mat4.Number}
*/
this.coordinateToPixelMatrix_ = goog.vec.Mat4.createNumber();
/**
* @private
* @type {goog.vec.Mat4.Number}
*/
this.pixelToCoordinateMatrix_ = goog.vec.Mat4.createNumber();
/**
* @private
* @type {?ol.FrameState}
*/
this.frameState_ = null;
/**
* @private
* @type {number}
@@ -168,12 +165,6 @@ ol.Map = function(mapOptions) {
*/
this.target_ = mapOptionsInternal.target;
/**
* @private
* @type {ol.Constraints}
*/
this.constraints_ = mapOptionsInternal.constraints;
/**
* @private
* @type {Element}
@@ -253,13 +244,29 @@ ol.Map = function(mapOptions) {
goog.events.listen(this.viewportSizeMonitor_, goog.events.EventType.RESIZE,
this.handleBrowserWindowResize, false, this);
goog.events.listen(
this, ol.Object.getChangedEventType(ol.MapProperty.PROJECTION),
this.handleProjectionChanged, false, this);
/**
* @private
* @type {Array.<ol.PreRenderFunction>}
*/
this.preRenderFunctions_ = [];
goog.events.listen(
this, ol.Object.getChangedEventType(ol.MapProperty.USER_PROJECTION),
this.handleUserProjectionChanged, false, this);
/**
* @private
* @type {Array.<ol.PostRenderFunction>}
*/
this.postRenderFunctions_ = [];
/**
* @private
* @type {function(this: ol.Map)}
*/
this.handlePostRender_ = goog.bind(this.handlePostRender, this);
/**
* @private
* @type {ol.TileQueue}
*/
this.tileQueue_ = new ol.TileQueue(goog.bind(this.getTilePriority, this));
this.setValues(mapOptionsInternal.values);
@@ -278,10 +285,22 @@ goog.inherits(ol.Map, ol.Object);
/**
* @return {boolean} Can rotate.
* @param {ol.PreRenderFunction} preRenderFunction Pre-render function.
*/
ol.Map.prototype.canRotate = function() {
return this.renderer_.canRotate();
ol.Map.prototype.addPreRenderFunction = function(preRenderFunction) {
this.requestRenderFrame();
this.preRenderFunctions_.push(preRenderFunction);
};
/**
* @param {Array.<ol.PreRenderFunction>} preRenderFunctions
* Pre-render functions.
*/
ol.Map.prototype.addPreRenderFunctions = function(preRenderFunctions) {
this.requestRenderFrame();
Array.prototype.push.apply(
this.preRenderFunctions_, preRenderFunctions);
};
@@ -295,30 +314,6 @@ ol.Map.prototype.disposeInternal = function() {
};
/**
* @param {ol.Extent} extent Extent.
*/
ol.Map.prototype.fitExtent = function(extent) {
this.withFrozenRendering(function() {
this.setCenter(extent.getCenter());
var resolution = this.getResolutionForExtent(extent);
resolution = this.constraints_.resolution(resolution, 0);
this.setResolution(resolution);
if (this.canRotate()) {
this.setRotation(0);
}
}, this);
};
/**
* @param {ol.Extent} userExtent Extent in user projection.
*/
ol.Map.prototype.fitUserExtent = function(userExtent) {
this.fitExtent(userExtent.transform(this.userToMapTransform_));
};
/**
* Freeze rendering.
*/
@@ -340,18 +335,6 @@ goog.exportProperty(
ol.Map.prototype.getBackgroundColor);
/**
* @return {ol.Coordinate|undefined} Center.
*/
ol.Map.prototype.getCenter = function() {
return /** @type {ol.Coordinate} */ this.get(ol.MapProperty.CENTER);
};
goog.exportProperty(
ol.Map.prototype,
'getCenter',
ol.Map.prototype.getCenter);
/**
* @return {Element} Container.
*/
@@ -373,25 +356,13 @@ ol.Map.prototype.getControls = function() {
* @return {ol.Coordinate} Coordinate.
*/
ol.Map.prototype.getCoordinateFromPixel = function(pixel) {
return this.isDef() ? this.renderer_.getCoordinateFromPixel(pixel) : null;
};
/**
* @return {ol.Extent|undefined} Extent.
*/
ol.Map.prototype.getExtent = function() {
if (this.isDef()) {
var center = this.getCenter();
var resolution = this.getResolution();
var size = this.getSize();
var minX = center.x - resolution * size.width / 2;
var minY = center.y - resolution * size.height / 2;
var maxX = center.x + resolution * size.width / 2;
var maxY = center.y + resolution * size.height / 2;
return new ol.Extent(minX, minY, maxX, maxY);
var frameState = this.frameState_;
if (goog.isNull(frameState)) {
return null;
} else {
return undefined;
var vec3 = [pixel.x, pixel.y, 0];
goog.vec.Mat4.multVec3(frameState.pixelToCoordinateMatrix, vec3, vec3);
return new ol.Coordinate(vec3[0], vec3[1]);
}
};
@@ -414,99 +385,25 @@ ol.Map.prototype.getLayers = function() {
/**
* @param {ol.Coordinate} coordinate Coordinate.
* @return {ol.Pixel|undefined} Pixel.
* @return {ol.Pixel} Pixel.
*/
ol.Map.prototype.getPixelFromCoordinate = function(coordinate) {
if (this.isDef()) {
return this.renderer_.getPixelFromCoordinate(coordinate);
var frameState = this.frameState_;
if (goog.isNull(frameState)) {
return null;
} else {
return undefined;
var vec3 = [coordinate.x, coordinate.y, 0];
goog.vec.Mat4.multVec3(frameState.coordinateToPixelMatrix, vec3, vec3);
return new ol.Pixel(vec3[0], vec3[1]);
}
};
/**
* @return {ol.Projection|undefined} Projection.
*/
ol.Map.prototype.getProjection = function() {
return /** @type {ol.Projection} */ this.get(ol.MapProperty.PROJECTION);
};
goog.exportProperty(
ol.Map.prototype,
'getProjection',
ol.Map.prototype.getProjection);
/**
* @return {number|undefined} Resolution.
*/
ol.Map.prototype.getResolution = function() {
return /** @type {number} */ this.get(ol.MapProperty.RESOLUTION);
};
goog.exportProperty(
ol.Map.prototype,
'getResolution',
ol.Map.prototype.getResolution);
/**
* @param {ol.Extent} extent Extent.
* @return {number|undefined} Resolution.
*/
ol.Map.prototype.getResolutionForExtent = function(extent) {
var size = this.getSize();
if (goog.isDef(size)) {
var xResolution = (extent.maxX - extent.minX) / size.width;
var yResolution = (extent.maxY - extent.minY) / size.height;
return Math.max(xResolution, yResolution);
} else {
return undefined;
}
};
/**
* @return {ol.Extent} Rotated extent.
*/
ol.Map.prototype.getRotatedExtent = function() {
goog.asserts.assert(this.isDef());
var center = /** @type {!ol.Coordinate} */ this.getCenter();
var resolution = this.getResolution();
var rotation = this.getRotation() || 0;
var size = this.getSize();
var xScale = resolution * size.width / 2;
var yScale = resolution * size.height / 2;
var corners = [
new ol.Coordinate(-xScale, -yScale),
new ol.Coordinate(-xScale, yScale),
new ol.Coordinate(xScale, -yScale),
new ol.Coordinate(xScale, yScale)
];
goog.array.forEach(corners, function(corner) {
corner.rotate(rotation);
corner.add(center);
});
return ol.Extent.boundingExtent.apply(null, corners);
};
/**
* @return {number} Rotation.
*/
ol.Map.prototype.getRotation = function() {
return /** @type {number} */ this.get(ol.MapProperty.ROTATION) || 0;
};
goog.exportProperty(
ol.Map.prototype,
'getRotation',
ol.Map.prototype.getRotation);
/**
* @return {ol.Size|undefined} Size.
*/
ol.Map.prototype.getSize = function() {
return /** @type {ol.Size|undefined} */ this.get(ol.MapProperty.SIZE);
return /** @type {ol.Size|undefined} */ (this.get(ol.MapProperty.SIZE));
};
goog.exportProperty(
ol.Map.prototype,
@@ -515,42 +412,15 @@ goog.exportProperty(
/**
* @return {ol.Coordinate|undefined} Center in user projection.
* @return {ol.View} View.
*/
ol.Map.prototype.getUserCenter = function() {
var center = this.getCenter();
if (goog.isDef(center)) {
return this.mapToUserTransform_(center);
} else {
return undefined;
}
};
/**
* @return {ol.Extent|undefined} Extent in user projection.
*/
ol.Map.prototype.getUserExtent = function() {
var extent = this.getExtent();
if (goog.isDef(extent)) {
return extent.transform(this.mapToUserTransform_);
} else {
return undefined;
}
};
/**
* @return {ol.Projection|undefined} Projection.
*/
ol.Map.prototype.getUserProjection = function() {
return /** @type {ol.Projection} */ this.get(
ol.MapProperty.USER_PROJECTION);
ol.Map.prototype.getView = function() {
return /** @type {ol.View} */ (this.get(ol.MapProperty.VIEW));
};
goog.exportProperty(
ol.Map.prototype,
'getUserProjection',
ol.Map.prototype.getUserProjection);
'getView',
ol.Map.prototype.getView);
/**
@@ -571,6 +441,24 @@ ol.Map.prototype.getOverlayContainer = function() {
};
/**
* @param {ol.Tile} tile Tile.
* @param {ol.Coordinate} tileCenter Tile center.
* @param {number} tileResolution Tile resolution.
* @return {number|undefined} Tile priority.
*/
ol.Map.prototype.getTilePriority = function(tile, tileCenter, tileResolution) {
if (goog.isNull(this.frameState_)) {
return undefined;
} else {
var center = this.frameState_.view2DState.center;
var deltaX = tileCenter.x - center.x;
var deltaY = tileCenter.y - center.y;
return Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;
}
};
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @param {string=} opt_type Type.
@@ -587,7 +475,7 @@ ol.Map.prototype.handleBrowserEvent = function(browserEvent, opt_type) {
* @private
*/
ol.Map.prototype.handleControlsAdd_ = function(collectionEvent) {
var control = /** @type {ol.control.Control} */ collectionEvent.elem;
var control = /** @type {ol.control.Control} */ (collectionEvent.elem);
control.setMap(this);
};
@@ -597,7 +485,7 @@ ol.Map.prototype.handleControlsAdd_ = function(collectionEvent) {
* @private
*/
ol.Map.prototype.handleControlsRemove_ = function(collectionEvent) {
var control = /** @type {ol.control.Control} */ collectionEvent.elem;
var control = /** @type {ol.control.Control} */ (collectionEvent.elem);
control.setMap(null);
};
@@ -608,7 +496,7 @@ ol.Map.prototype.handleControlsRemove_ = function(collectionEvent) {
ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) {
var interactions = this.getInteractions();
var interactionsArray = /** @type {Array.<ol.interaction.Interaction>} */
interactions.getArray();
(interactions.getArray());
if (this.dispatchEvent(mapBrowserEvent) !== false) {
for (var i = interactionsArray.length - 1; i >= 0; i--) {
var interaction = interactionsArray[i];
@@ -624,16 +512,16 @@ ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) {
/**
* @protected
*/
ol.Map.prototype.handleProjectionChanged = function() {
this.recalculateTransforms_();
};
/**
* @protected
*/
ol.Map.prototype.handleUserProjectionChanged = function() {
this.recalculateTransforms_();
ol.Map.prototype.handlePostRender = function() {
this.tileQueue_.reprioritize(); // FIXME only call if needed
this.tileQueue_.loadMoreTiles();
goog.array.forEach(
this.postRenderFunctions_,
function(postRenderFunction) {
postRenderFunction(this, this.frameState_);
},
this);
this.postRenderFunctions_.length = 0;
};
@@ -650,31 +538,12 @@ ol.Map.prototype.handleBrowserWindowResize = function() {
* @return {boolean} Is defined.
*/
ol.Map.prototype.isDef = function() {
return goog.isDefAndNotNull(this.getCenter()) &&
goog.isDef(this.getResolution()) &&
var view = this.getView();
return goog.isDef(view) && view.isDef() &&
goog.isDefAndNotNull(this.getSize());
};
/**
* @private
*/
ol.Map.prototype.recalculateTransforms_ = function() {
var projection = this.getProjection();
var userProjection = this.getUserProjection();
if (goog.isDefAndNotNull(projection) &&
goog.isDefAndNotNull(userProjection)) {
this.mapToUserTransform_ = ol.Projection.getTransform(
projection, userProjection);
this.userToMapTransform_ = ol.Projection.getTransform(
userProjection, projection);
} else {
this.mapToUserTransform_ = ol.Projection.cloneTransform;
this.userToMapTransform_ = ol.Projection.identityTransform;
}
};
/**
* Render.
*/
@@ -708,28 +577,98 @@ ol.Map.prototype.requestRenderFrame = function() {
* @private
*/
ol.Map.prototype.renderFrame_ = function(time) {
var i;
if (this.freezeRenderingCount_ != 0) {
return;
}
if (goog.DEBUG) {
this.logger.info('renderFrame_');
}
this.renderer_.renderFrame(time);
this.dirty_ = false;
if (goog.DEBUG) {
this.logger.info('postrender');
var size = this.getSize();
var layers = this.getLayers();
var layersArray = goog.isDef(layers) ?
/** @type {Array.<ol.layer.Layer>} */ (layers.getArray()) : undefined;
var view = this.getView();
var view2D = goog.isDef(view) ? this.getView().getView2D() : undefined;
/** @type {?ol.FrameState} */
var frameState = null;
if (goog.isDef(layersArray) && goog.isDef(size) && goog.isDef(view2D) &&
view2D.isDef()) {
var backgroundColor = this.getBackgroundColor();
var viewHints = view.getHints();
var layerStates = {};
goog.array.forEach(layersArray, function(layer) {
layerStates[goog.getUid(layer)] = layer.getLayerState();
});
var view2DState = view2D.getView2DState();
frameState = {
animate: false,
backgroundColor: goog.isDef(backgroundColor) ?
backgroundColor : new ol.Color(1, 1, 1, 1),
coordinateToPixelMatrix: this.coordinateToPixelMatrix_,
extent: null,
layersArray: layersArray,
layerStates: layerStates,
pixelToCoordinateMatrix: this.pixelToCoordinateMatrix_,
postRenderFunctions: [],
size: size,
tileQueue: this.tileQueue_,
view2DState: view2DState,
viewHints: viewHints,
time: time
};
}
this.dispatchEvent(ol.MapEventType.POSTRENDER);
};
this.preRenderFunctions_ = goog.array.filter(
this.preRenderFunctions_,
function(preRenderFunction) {
return preRenderFunction(this, frameState);
},
this);
if (!goog.isNull(frameState)) {
// FIXME works for View2D only
var center = view2DState.center;
var resolution = view2DState.resolution;
var rotation = view2DState.rotation;
var x = resolution * size.width / 2;
var y = resolution * size.height / 2;
var corners = [
new ol.Coordinate(-x, -y),
new ol.Coordinate(-x, y),
new ol.Coordinate(x, -y),
new ol.Coordinate(x, y)
];
var corner;
for (i = 0; i < 4; ++i) {
corner = corners[i];
corner.rotate(rotation);
corner.add(center);
}
frameState.extent = ol.Extent.boundingExtent.apply(null, corners);
}
this.renderer_.renderFrame(frameState);
if (!goog.isNull(frameState)) {
if (frameState.animate) {
this.requestRenderFrame();
}
Array.prototype.push.apply(
this.postRenderFunctions_, frameState.postRenderFunctions);
}
this.frameState_ = frameState;
this.dirty_ = false;
this.dispatchEvent(
new ol.MapEvent(ol.MapEventType.POSTRENDER, this, frameState));
goog.global.setTimeout(this.handlePostRender_, 0);
/**
* @param {number|undefined} rotation Rotation.
* @param {number} delta Delta.
*/
ol.Map.prototype.rotate = function(rotation, delta) {
rotation = this.constraints_.rotation(rotation, delta);
this.setRotation(rotation);
};
@@ -745,18 +684,6 @@ goog.exportProperty(
ol.Map.prototype.setBackgroundColor);
/**
* @param {ol.Coordinate|undefined} center Center.
*/
ol.Map.prototype.setCenter = function(center) {
this.set(ol.MapProperty.CENTER, center);
};
goog.exportProperty(
ol.Map.prototype,
'setCenter',
ol.Map.prototype.setCenter);
/**
* @param {ol.Collection} layers Layers.
*/
@@ -769,42 +696,6 @@ goog.exportProperty(
ol.Map.prototype.setLayers);
/**
* @param {ol.Projection} projection Projection.
*/
ol.Map.prototype.setProjection = function(projection) {
this.set(ol.MapProperty.PROJECTION, projection);
};
goog.exportProperty(
ol.Map.prototype,
'setProjection',
ol.Map.prototype.setProjection);
/**
* @param {number|undefined} resolution Resolution.
*/
ol.Map.prototype.setResolution = function(resolution) {
this.set(ol.MapProperty.RESOLUTION, resolution);
};
goog.exportProperty(
ol.Map.prototype,
'setResolution',
ol.Map.prototype.setResolution);
/**
* @param {number|undefined} rotation Rotation.
*/
ol.Map.prototype.setRotation = function(rotation) {
this.set(ol.MapProperty.ROTATION, rotation);
};
goog.exportProperty(
ol.Map.prototype,
'setRotation',
ol.Map.prototype.setRotation);
/**
* @param {ol.Size} size Size.
*/
@@ -818,23 +709,15 @@ goog.exportProperty(
/**
* @param {ol.Coordinate} userCenter Center in user projection.
* @param {ol.IView} view View.
*/
ol.Map.prototype.setUserCenter = function(userCenter) {
this.setCenter(this.userToMapTransform_(userCenter));
};
/**
* @param {ol.Projection} userProjection User projection.
*/
ol.Map.prototype.setUserProjection = function(userProjection) {
this.set(ol.MapProperty.USER_PROJECTION, userProjection);
ol.Map.prototype.setView = function(view) {
this.set(ol.MapProperty.VIEW, view);
};
goog.exportProperty(
ol.Map.prototype,
'setUserProjection',
ol.Map.prototype.setUserProjection);
'setView',
ol.Map.prototype.setView);
/**
@@ -863,53 +746,9 @@ ol.Map.prototype.withFrozenRendering = function(f, opt_obj) {
};
/**
* @private
* @param {number|undefined} resolution Resolution to go to.
* @param {ol.Coordinate=} opt_anchor Anchor coordinate.
*/
ol.Map.prototype.zoom_ = function(resolution, opt_anchor) {
if (goog.isDefAndNotNull(resolution) && goog.isDefAndNotNull(opt_anchor)) {
var anchor = opt_anchor;
var oldCenter = /** @type {!ol.Coordinate} */ this.getCenter();
var oldResolution = this.getResolution();
var x = anchor.x - resolution * (anchor.x - oldCenter.x) / oldResolution;
var y = anchor.y - resolution * (anchor.y - oldCenter.y) / oldResolution;
var center = new ol.Coordinate(x, y);
this.withFrozenRendering(function() {
this.setCenter(center);
this.setResolution(resolution);
}, this);
} else {
this.setResolution(resolution);
}
};
/**
* @param {number} delta Delta from previous zoom level.
* @param {ol.Coordinate=} opt_anchor Anchor coordinate.
*/
ol.Map.prototype.zoom = function(delta, opt_anchor) {
var resolution = this.constraints_.resolution(this.getResolution(), delta);
this.zoom_(resolution, opt_anchor);
};
/**
* @param {number|undefined} resolution Resolution to go to.
* @param {ol.Coordinate=} opt_anchor Anchor coordinate.
*/
ol.Map.prototype.zoomToResolution = function(resolution, opt_anchor) {
resolution = this.constraints_.resolution(resolution, 0);
this.zoom_(resolution, opt_anchor);
};
/**
* @typedef {{controls: ol.Collection,
* interactions: ol.Collection,
* constraints: ol.Constraints,
* rendererConstructor:
* function(new: ol.renderer.Map, Element, ol.Map),
* target: Element,
@@ -929,25 +768,11 @@ ol.Map.createOptionsInternal = function(mapOptions) {
*/
var values = {};
if (goog.isDef(mapOptions.center)) {
values[ol.MapProperty.CENTER] = mapOptions.center;
}
values[ol.MapProperty.LAYERS] = goog.isDef(mapOptions.layers) ?
mapOptions.layers : new ol.Collection();
values[ol.MapProperty.PROJECTION] = ol.Projection.createProjection(
mapOptions.projection, 'EPSG:3857');
if (goog.isDef(mapOptions.resolution)) {
values[ol.MapProperty.RESOLUTION] = mapOptions.resolution;
} else if (goog.isDef(mapOptions.zoom)) {
values[ol.MapProperty.RESOLUTION] =
ol.Projection.EPSG_3857_HALF_SIZE / (128 << mapOptions.zoom);
}
values[ol.MapProperty.USER_PROJECTION] = ol.Projection.createProjection(
mapOptions.userProjection, 'EPSG:4326');
values[ol.MapProperty.VIEW] = goog.isDef(mapOptions.view) ?
mapOptions.view : new ol.View2D();
/**
* @type {function(new: ol.renderer.Map, Element, ol.Map)}
@@ -982,11 +807,6 @@ ol.Map.createOptionsInternal = function(mapOptions) {
}
}
/**
* @type {ol.Constraints}
*/
var constraints = ol.Map.createConstraints_(mapOptions);
/**
* @type {ol.Collection}
*/
@@ -1013,7 +833,6 @@ ol.Map.createOptionsInternal = function(mapOptions) {
var target = goog.dom.getElement(mapOptions.target);
return {
constraints: constraints,
controls: controls,
interactions: interactions,
rendererConstructor: rendererConstructor,
@@ -1024,40 +843,6 @@ ol.Map.createOptionsInternal = function(mapOptions) {
};
/**
* @private
* @param {ol.MapOptions} mapOptions Map options.
* @return {ol.Constraints} Map constraints.
*/
ol.Map.createConstraints_ = function(mapOptions) {
var resolutionConstraint;
if (goog.isDef(mapOptions.resolutions)) {
resolutionConstraint = ol.ResolutionConstraint.createSnapToResolutions(
mapOptions.resolutions);
} else {
var maxResolution, numZoomLevels, zoomFactor;
if (goog.isDef(mapOptions.maxResolution) &&
goog.isDef(mapOptions.numZoomLevels) &&
goog.isDef(mapOptions.zoomFactor)) {
maxResolution = mapOptions.maxResolution;
numZoomLevels = mapOptions.numZoomLevels;
zoomFactor = mapOptions.zoomFactor;
} else {
maxResolution = ol.Projection.EPSG_3857_HALF_SIZE / 128;
// number of steps we want between two data resolutions
var numSteps = 4;
numZoomLevels = 29 * numSteps;
zoomFactor = Math.exp(Math.log(2) / numSteps);
}
resolutionConstraint = ol.ResolutionConstraint.createSnapToPower(
zoomFactor, maxResolution, numZoomLevels - 1);
}
// FIXME rotation constraint is not configurable at the moment
var rotationConstraint = ol.RotationConstraint.none;
return new ol.Constraints(resolutionConstraint, rotationConstraint);
};
/**
* @private
* @param {ol.MapOptions} mapOptions Map options.
@@ -1139,3 +924,20 @@ ol.Map.createInteractions_ = function(mapOptions) {
return interactions;
};
/**
* @param {goog.Uri.QueryData=} opt_queryData Query data.
* @return {Array.<ol.RendererHint>} Renderer hints.
*/
ol.RendererHints.createFromQueryData = function(opt_queryData) {
var queryData = goog.isDef(opt_queryData) ?
opt_queryData : new goog.Uri.QueryData(goog.global.location.search);
if (queryData.containsKey('renderers')) {
return queryData.get('renderers').split(',');
} else if (queryData.containsKey('renderer')) {
return [queryData.get('renderer')];
} else {
return ol.DEFAULT_RENDERER_HINTS;
}
};

View File

@@ -270,7 +270,9 @@ ol.MapBrowserEventHandler.prototype.drag_ = function(browserEvent) {
};
/** @override */
/**
* FIXME empty description for jsdoc
*/
ol.MapBrowserEventHandler.prototype.disposeInternal = function() {
var element = this.map_.getViewport();
goog.events.unlisten(element,

View File

@@ -1,6 +1,16 @@
goog.provide('ol.MapEvent');
goog.provide('ol.MapEventType');
goog.require('goog.events.Event');
goog.require('ol.FrameState');
/**
* @enum {string}
*/
ol.MapEventType = {
POSTRENDER: 'postrender'
};
@@ -9,8 +19,9 @@ goog.require('goog.events.Event');
* @extends {goog.events.Event}
* @param {string} type Event type.
* @param {ol.Map} map Map.
* @param {?ol.FrameState=} opt_frameState Frame state.
*/
ol.MapEvent = function(type, map) {
ol.MapEvent = function(type, map, opt_frameState) {
goog.base(this, type);
@@ -24,6 +35,11 @@ ol.MapEvent = function(type, map) {
*/
this.defaultPrevented = false;
/**
* @type {?ol.FrameState}
*/
this.frameState = goog.isDef(opt_frameState) ? opt_frameState : null;
};
goog.inherits(ol.MapEvent, goog.events.Event);

View File

@@ -6,6 +6,7 @@
*/
goog.provide('ol.Object');
goog.provide('ol.ObjectEventType');
goog.require('goog.array');
goog.require('goog.events');
@@ -13,6 +14,14 @@ goog.require('goog.events.EventTarget');
goog.require('goog.object');
/**
* @enum {string}
*/
ol.ObjectEventType = {
CHANGED: 'changed'
};
/**
* @enum {string}
*/
@@ -192,6 +201,7 @@ ol.Object.prototype.notify = function(key) {
ol.Object.prototype.notifyInternal_ = function(key) {
var eventType = ol.Object.getChangedEventType(key);
this.dispatchEvent(eventType);
this.dispatchEvent(ol.ObjectEventType.CHANGED);
};

View File

@@ -43,7 +43,13 @@ ol.overlay.Overlay = function(overlayOptions) {
* @private
* @type {Array.<number>}
*/
this.mapListenerKeys_ = [];
this.mapListenerKeys_ = null;
/**
* @private
* @type {Array.<number>}
*/
this.viewListenerKeys_ = null;
if (goog.isDef(overlayOptions.coordinate)) {
this.setCoordinate(overlayOptions.coordinate);
@@ -60,6 +66,37 @@ ol.overlay.Overlay = function(overlayOptions) {
};
/**
* @private
*/
ol.overlay.Overlay.prototype.handleViewChanged_ = function() {
goog.asserts.assert(!goog.isNull(this.map_));
if (!goog.isNull(this.viewListenerKeys_)) {
goog.array.forEach(this.viewListenerKeys_, goog.events.unlistenByKey);
this.viewListenerKeys_ = null;
}
var view = this.map_.getView();
if (goog.isDefAndNotNull(view)) {
// FIXME works for View2D only
goog.asserts.assert(view instanceof ol.View2D);
this.viewListenerKeys_ = [
goog.events.listen(
view, ol.Object.getChangedEventType(ol.View2DProperty.CENTER),
this.updatePixelPosition_, false, this),
goog.events.listen(
view, ol.Object.getChangedEventType(ol.View2DProperty.RESOLUTION),
this.updatePixelPosition_, false, this),
goog.events.listen(
view, ol.Object.getChangedEventType(ol.View2DProperty.ROTATION),
this.updatePixelPosition_, false, this)
];
this.updatePixelPosition_();
}
};
/**
* @param {ol.Coordinate} coordinate Coordinate for the overlay's position on
* the map.
@@ -100,25 +137,24 @@ ol.overlay.Overlay.prototype.getElement = function() {
*/
ol.overlay.Overlay.prototype.setMap = function(map) {
this.map_ = map;
goog.array.forEach(this.mapListenerKeys_, goog.events.unlistenByKey);
if (!goog.isNull(this.mapListenerKeys_)) {
goog.array.forEach(this.mapListenerKeys_, goog.events.unlistenByKey);
this.mapListenerKeys_ = null;
}
if (this.element_) {
this.setElement(this.element_);
}
this.mapListenerKeys_ = map ? [
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.CENTER),
this.updatePixelPosition_, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.RESOLUTION),
this.updatePixelPosition_, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.ROTATION),
this.updatePixelPosition_, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.SIZE),
this.updatePixelPosition_, false, this)
] : [];
this.updatePixelPosition_();
if (goog.isDefAndNotNull(map)) {
this.mapListenerKeys_ = [
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.SIZE),
this.updatePixelPosition_, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.VIEW),
this.handleViewChanged_, false, this)
];
this.handleViewChanged_();
}
};

View File

@@ -87,7 +87,7 @@ ol.Projection.prototype.getUnits = function() {
*/
ol.Proj4jsProjection = function(code, proj4jsProj) {
var units = /** @type {ol.ProjectionUnits} */ proj4jsProj.units;
var units = /** @type {ol.ProjectionUnits} */ (proj4jsProj.units);
goog.base(this, code, units, null);

View File

@@ -1,6 +1,9 @@
goog.provide('ol.renderer.dom.Layer');
goog.require('ol.Coordinate');
goog.require('ol.FrameState');
goog.require('ol.layer.Layer');
goog.require('ol.layer.LayerState');
goog.require('ol.renderer.Layer');
@@ -13,6 +16,7 @@ goog.require('ol.renderer.Layer');
* @param {!Element} target Target.
*/
ol.renderer.dom.Layer = function(mapRenderer, layer, target) {
goog.base(this, mapRenderer, layer);
/**
@@ -21,27 +25,15 @@ ol.renderer.dom.Layer = function(mapRenderer, layer, target) {
*/
this.target = target;
/**
* Top left corner of the target in map coords.
*
* @type {ol.Coordinate}
* @protected
*/
this.origin = null;
this.handleLayerOpacityChange();
this.handleLayerVisibleChange();
};
goog.inherits(ol.renderer.dom.Layer, ol.renderer.Layer);
/**
* @inheritDoc
* @return {ol.renderer.Map} Map renderer.
* @return {!Element} Target.
*/
ol.renderer.dom.Layer.prototype.getMapRenderer = function() {
return /** @type {ol.renderer.dom.Map} */ goog.base(this, 'getMapRenderer');
ol.renderer.dom.Layer.prototype.getTarget = function() {
return this.target;
};
@@ -57,7 +49,7 @@ ol.renderer.dom.Layer.prototype.handleLayerLoad = function() {
* @inheritDoc
*/
ol.renderer.dom.Layer.prototype.handleLayerOpacityChange = function() {
goog.style.setOpacity(this.target, this.getLayer().getOpacity());
this.getMap().render();
};
@@ -65,22 +57,12 @@ ol.renderer.dom.Layer.prototype.handleLayerOpacityChange = function() {
* @inheritDoc
*/
ol.renderer.dom.Layer.prototype.handleLayerVisibleChange = function() {
goog.style.showElement(this.target, this.getLayer().getVisible());
this.getMap().render();
};
/**
* Render.
* @param {number} time Time.
* @param {ol.FrameState} frameState Frame state.
* @param {ol.layer.LayerState} layerState Layer state.
*/
ol.renderer.dom.Layer.prototype.renderFrame = goog.abstractMethod;
/**
* Set the location of the top left corner of the target.
*
* @param {ol.Coordinate} origin Origin.
*/
ol.renderer.dom.Layer.prototype.setOrigin = function(origin) {
this.origin = origin;
};

View File

@@ -5,8 +5,9 @@ goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.functions');
goog.require('goog.style');
goog.require('ol.Coordinate');
goog.require('ol.FrameState');
goog.require('ol.layer.TileLayer');
goog.require('ol.renderer.Map');
goog.require('ol.renderer.dom.TileLayer');
@@ -37,92 +38,32 @@ ol.renderer.dom.Map = function(container, map) {
goog.dom.insertChildAt(container, this.layersPane_, 0);
/**
* @type {Object}
* @private
* @type {boolean}
*/
this.layerPanes_ = {};
this.renderedVisible_ = true;
/**
* @type {ol.Coordinate}
* @private
*/
this.renderedCenter_ = null;
/**
* @type {number | undefined}
* @private
*/
this.renderedResolution_ = undefined;
/**
* @type {ol.Size}
* @private
*/
this.renderedSize_ = null;
/**
* @type {number | undefined}
* @private
*/
this.renderedRotation_ = undefined;
/**
* The origin (top left) of the layers pane in map coordinates.
*
* @type {ol.Coordinate}
* @private
*/
this.layersPaneOrigin_ = null;
};
goog.inherits(ol.renderer.dom.Map, ol.renderer.Map);
/**
* Apply the given transform to the layers pane.
* @param {number} dx Translation along the x-axis.
* @param {number} dy Translation along the y-axis.
* @param {number} rotation Rotation angle.
* @private
*/
ol.renderer.dom.Map.prototype.applyTransform_ = function(dx, dy, rotation) {
var transform =
'translate(' + Math.round(dx) + 'px, ' + Math.round(dy) + 'px) ' +
'rotate(' + rotation.toFixed(6) + 'rad) ' +
'scale3d(1, 1, 1)';
var style = this.layersPane_.style;
style.WebkitTransform = transform;
style.MozTransform = transform;
style.OTransform = transform;
style.msTransform = transform;
style.transform = transform;
};
/**
* @inheritDoc
*/
ol.renderer.dom.Map.prototype.canRotate = goog.functions.TRUE;
ol.renderer.dom.Map.prototype.addLayer = function(layer) {
goog.base(this, 'addLayer', layer);
this.getMap().render();
};
/**
* @inheritDoc
*/
ol.renderer.dom.Map.prototype.createLayerRenderer = function(layer) {
if (layer instanceof ol.layer.TileLayer) {
var layerPane = goog.dom.createElement(goog.dom.TagName.DIV);
layerPane.className = 'ol-layer';
layerPane.style.position = 'absolute';
goog.dom.appendChild(this.layersPane_, layerPane);
var layerRenderer = new ol.renderer.dom.TileLayer(this, layer, layerPane);
this.layerPanes_[goog.getUid(layerRenderer)] = layerPane;
var layerRenderer = new ol.renderer.dom.TileLayer(this, layer);
goog.dom.appendChild(this.layersPane_, layerRenderer.getTarget());
return layerRenderer;
} else {
goog.asserts.assert(false);
return null;
@@ -133,8 +74,8 @@ ol.renderer.dom.Map.prototype.createLayerRenderer = function(layer) {
/**
* @inheritDoc
*/
ol.renderer.dom.Map.prototype.handleCenterChanged = function() {
goog.base(this, 'handleCenterChanged');
ol.renderer.dom.Map.prototype.removeLayer = function(layer) {
goog.base(this, 'removeLayer', layer);
this.getMap().render();
};
@@ -142,145 +83,30 @@ ol.renderer.dom.Map.prototype.handleCenterChanged = function() {
/**
* @inheritDoc
*/
ol.renderer.dom.Map.prototype.handleResolutionChanged = function() {
goog.base(this, 'handleResolutionChanged');
this.getMap().render();
};
ol.renderer.dom.Map.prototype.renderFrame = function(frameState) {
/**
* @inheritDoc
*/
ol.renderer.dom.Map.prototype.handleRotationChanged = function() {
goog.base(this, 'handleRotationChanged');
this.getMap().render();
};
/**
* @inheritDoc
*/
ol.renderer.dom.Map.prototype.handleSizeChanged = function() {
goog.base(this, 'handleSizeChanged');
this.getMap().render();
};
/**
* Render the map. Sets up the layers pane on first render and adjusts its
* position as needed on subsequent calls.
* @inheritDoc
*/
ol.renderer.dom.Map.prototype.renderFrame = function(time) {
var map = this.getMap();
if (!map.isDef()) {
if (goog.isNull(frameState)) {
if (this.renderedVisible_) {
goog.style.showElement(this.layersPane_, false);
this.renderedVisible_ = false;
}
return;
}
var mapCenter = map.getCenter();
var mapSize = map.getSize();
var mapResolution = map.getResolution();
var mapRotation = map.getRotation();
goog.asserts.assert(goog.isDefAndNotNull(mapCenter));
goog.asserts.assert(goog.isDef(mapResolution));
goog.asserts.assert(goog.isDef(mapRotation));
goog.asserts.assert(goog.isDefAndNotNull(mapSize));
if (goog.isNull(this.renderedCenter_)) {
// first rendering
goog.asserts.assert(!goog.isDef(this.renderedResolution_));
goog.asserts.assert(!goog.isDef(this.renderedRotation_));
goog.asserts.assert(goog.isNull(this.renderedSize_));
this.resetLayersPane_();
} else {
goog.asserts.assert(goog.isDef(this.renderedResolution_));
goog.asserts.assert(!goog.isNull(this.renderedSize_));
if (mapResolution !== this.renderedResolution_ ||
!mapSize.equals(this.renderedSize_)) {
// resolution or size changed, adjust layers pane
this.resetLayersPane_();
} else if (!mapCenter.equals(this.renderedCenter_) ||
mapRotation !== this.renderedRotation_) {
// same resolution and size, new center or rotation
this.transformLayersPane_();
goog.array.forEach(frameState.layersArray, function(layer) {
var layerState = frameState.layerStates[goog.getUid(layer)];
if (!layerState.ready) {
return;
}
var layerRenderer = this.getLayerRenderer(layer);
layerRenderer.renderFrame(frameState, layerState);
}, this);
if (!this.renderedVisible_) {
goog.style.showElement(this.layersPane_, true);
this.renderedVisible_ = true;
}
this.renderedCenter_ = mapCenter;
this.renderedResolution_ = mapResolution;
this.renderedRotation_ = mapRotation;
this.renderedSize_ = mapSize;
var requestRenderFrame = false;
this.forEachReadyVisibleLayer(function(layer, layerRenderer) {
if (layerRenderer.renderFrame(time)) {
requestRenderFrame = true;
}
});
if (requestRenderFrame) {
map.requestRenderFrame();
}
this.calculateMatrices2D(frameState);
};
/**
* Reset the layers pane to its initial position.
* @private
*/
ol.renderer.dom.Map.prototype.resetLayersPane_ = function() {
var map = this.map;
var mapSize = map.getSize();
var halfWidth = mapSize.width / 2;
var halfHeight = mapSize.height / 2;
var center = map.getCenter();
var resolution = map.getResolution();
var origin = new ol.Coordinate(
center.x - resolution * halfWidth,
center.y + resolution * halfHeight);
this.layersPaneOrigin_ = origin;
this.setTransformOrigin_(halfWidth, halfHeight);
this.applyTransform_(0, 0, map.getRotation());
goog.object.forEach(this.layerRenderers, function(layerRenderer) {
layerRenderer.setOrigin(origin);
});
};
/**
* Set the transform-origin CSS property of the layers pane.
* @param {number} x The x-axis origin.
* @param {number} y The y-axis origin.
* @private
*/
ol.renderer.dom.Map.prototype.setTransformOrigin_ = function(x, y) {
var origin = Math.round(x) + 'px ' + Math.round(y) + 'px';
var style = this.layersPane_.style;
style.WebkitTransformOrigin = origin;
style.MozTransformOrigin = origin;
style.OTransformOrigin = origin;
style.msTransformOrigin = origin;
style.transformOrigin = origin;
};
/**
* Apply the appropriate transform to the layers pane.
* @private
*/
ol.renderer.dom.Map.prototype.transformLayersPane_ = function() {
var map = this.map;
var resolution = map.getResolution();
var center = map.getCenter();
var size = map.getSize();
var origin = this.layersPaneOrigin_;
var ox = (center.x - origin.x) / resolution;
var oy = (origin.y - center.y) / resolution;
this.setTransformOrigin_(ox, oy);
var dx = ox - (size.width / 2);
var dy = oy - (size.height / 2);
this.applyTransform_(-dx, -dy, map.getRotation());
};

View File

@@ -1,13 +1,23 @@
// FIXME probably need to reset TileLayerZ if offsets get too large
// FIXME when zooming out, preserve higher Z divs to avoid white flash
goog.provide('ol.renderer.dom.TileLayer');
goog.require('goog.asserts');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.events.EventType');
goog.require('goog.math.Vec2');
goog.require('goog.style');
goog.require('goog.vec.Mat4');
goog.require('ol.Coordinate');
goog.require('ol.Extent');
goog.require('ol.Size');
goog.require('ol.TileCoord');
goog.require('ol.TileRange');
goog.require('ol.TileState');
goog.require('ol.ViewHint');
goog.require('ol.dom');
goog.require('ol.renderer.dom.Layer');
goog.require('ol.tilegrid.TileGrid');
@@ -16,212 +26,363 @@ goog.require('ol.renderer.dom.Layer');
* @extends {ol.renderer.dom.Layer}
* @param {ol.renderer.Map} mapRenderer Map renderer.
* @param {ol.layer.TileLayer} tileLayer Tile layer.
* @param {!Element} target Target.
*/
ol.renderer.dom.TileLayer = function(mapRenderer, tileLayer, target) {
ol.renderer.dom.TileLayer = function(mapRenderer, tileLayer) {
var target = goog.dom.createElement(goog.dom.TagName.DIV);
target.className = 'ol-layer';
target.style.position = 'absolute';
goog.base(this, mapRenderer, tileLayer, target);
/**
* @type {Object}
* @private
* @type {boolean}
*/
this.renderedTiles_ = {};
this.renderedVisible_ = true;
/**
* @type {number|undefined}
* @private
* @type {number}
*/
this.renderedMapResolution_ = undefined;
this.renderedOpacity_ = 1;
/**
* @private
* @type {Object.<number, ol.renderer.dom.TileLayerZ_>}
*/
this.tileLayerZs_ = {};
};
goog.inherits(ol.renderer.dom.TileLayer, ol.renderer.dom.Layer);
/**
* @return {ol.layer.TileLayer} Tile layer.
*/
ol.renderer.dom.TileLayer.prototype.getTileLayer = function() {
return /** @type {ol.layer.TileLayer} */ (this.getLayer());
};
/**
* @inheritDoc
* @return {ol.layer.TileLayer} Layer.
*/
ol.renderer.dom.TileLayer.prototype.getLayer = function() {
return /** @type {ol.layer.TileLayer} */ goog.base(this, 'getLayer');
};
ol.renderer.dom.TileLayer.prototype.renderFrame =
function(frameState, layerState) {
/**
* Get the pixel offset between the tile origin and the container origin.
* @private
* @param {number} z Z.
* @param {number} resolution Resolution.
* @return {ol.Coordinate} Offset.
*/
ol.renderer.dom.TileLayer.prototype.getTileOffset_ = function(z, resolution) {
var tileLayer = this.getLayer();
var tileSource = tileLayer.getTileSource();
var tileGrid = tileSource.getTileGrid();
var tileOrigin = tileGrid.getOrigin(z);
var offset = new ol.Coordinate(
Math.round((this.origin.x - tileOrigin.x) / resolution),
Math.round((tileOrigin.y - this.origin.y) / resolution));
return offset;
};
/**
* Get rid of all tiles that weren't drawn in the most recent rendering.
* @param {Object.<number, Object.<string, ol.Tile>>} tilesDrawnByZ Tiles just
* rendered.
* @private
*/
ol.renderer.dom.TileLayer.prototype.removeExtraTiles_ =
function(tilesDrawnByZ) {
var key, tileCoord, tilesDrawn, tile;
for (key in this.renderedTiles_) {
tileCoord = ol.TileCoord.createFromString(key);
tilesDrawn = tilesDrawnByZ[tileCoord.z];
if (!(tilesDrawn && key in tilesDrawn)) {
tile = this.renderedTiles_[key];
delete this.renderedTiles_[key];
goog.dom.removeNode(tile.getImage(this));
if (!layerState.visible) {
if (this.renderedVisible_) {
goog.style.showElement(this.target, false);
this.renderedVisible_ = false;
}
}
};
/**
* @inheritDoc
*/
ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) {
var map = this.getMap();
if (!map.isDef()) {
return;
}
var mapExtent = /** @type {!ol.Extent} */ map.getRotatedExtent();
var mapResolution = /** @type {number} */ map.getResolution();
var resolutionChanged = (mapResolution !== this.renderedMapResolution_);
var tileLayer = this.getLayer();
var view2DState = frameState.view2DState;
var tileLayer = this.getTileLayer();
var tileSource = tileLayer.getTileSource();
var tileGrid = tileSource.getTileGrid();
var z = tileGrid.getZForResolution(view2DState.resolution);
var tileResolution = tileGrid.getResolution(z);
var tileRange = tileGrid.getTileRangeForExtentAndResolution(
frameState.extent, tileResolution);
// z represents the "best" resolution
var z = tileGrid.getZForResolution(mapResolution);
/**
* @type {Object.<number, Object.<string, ol.Tile>>}
*/
/** @type {Object.<number, Object.<string, ol.Tile>>} */
var tilesToDrawByZ = {};
tilesToDrawByZ[z] = {};
var tileRange =
tileGrid.getTileRangeForExtentAndResolution(mapExtent, mapResolution);
var findInterimTiles = function(z, tileRange) {
// FIXME this could be more efficient about filling partial holes
var fullyCovered = true;
var tile, tileCoord, tileCoordKey, x, y;
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
tileCoord = new ol.TileCoord(z, x, y);
tileCoordKey = tileCoord.toString();
if (tilesToDrawByZ[z] && tilesToDrawByZ[z][tileCoordKey]) {
return;
}
tile = tileSource.getTile(tileCoord);
if (!goog.isNull(tile) && tile.getState() == ol.TileState.LOADED) {
if (!tilesToDrawByZ[z]) {
tilesToDrawByZ[z] = {};
}
tilesToDrawByZ[z][tileCoordKey] = tile;
} else {
fullyCovered = false;
}
}
}
return fullyCovered;
};
var allTilesLoaded = true;
var tile, tileCenter, tileCoord, tileState, x, y;
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
tileCoord = new ol.TileCoord(z, x, y);
tile = tileSource.getTile(tileCoord);
if (goog.isNull(tile)) {
continue;
}
tileState = tile.getState();
if (tileState == ol.TileState.IDLE) {
tileCenter = tileGrid.getTileCoordCenter(tileCoord);
frameState.tileQueue.enqueue(tile, tileCenter, tileResolution);
} else if (tileState == ol.TileState.LOADED) {
tilesToDrawByZ[z][tileCoord.toString()] = tile;
continue;
} else if (tileState == ol.TileState.ERROR) {
continue;
}
allTilesLoaded = false;
tileGrid.forEachTileCoordParentTileRange(tileCoord, findInterimTiles);
// first pass through the tile range to determine all the tiles needed
tileRange.forEachTileCoord(z, function(tileCoord) {
var tile = tileSource.getTile(tileCoord);
if (goog.isNull(tile)) {
// we're outside the source's extent, continue
return;
}
var key = tile.tileCoord.toString();
var state = tile.getState();
if (state == ol.TileState.IDLE) {
tile.load();
} else if (state == ol.TileState.LOADED) {
tilesToDrawByZ[z][key] = tile;
return;
}
allTilesLoaded = false;
/**
* Look for already loaded tiles at alternate z that can serve as
* placeholders until tiles at the current z have loaded.
*
* TODO: make this more efficent for filling partial holes
*/
tileGrid.forEachTileCoordParentTileRange(
tileCoord,
function(altZ, altTileRange) {
var fullyCovered = true;
altTileRange.forEachTileCoord(altZ, function(altTileCoord) {
var tileKey = altTileCoord.toString();
if (tilesToDrawByZ[altZ] && tilesToDrawByZ[altZ][tileKey]) {
return;
}
var altTile = tileSource.getTile(altTileCoord);
if (!goog.isNull(altTile) &&
altTile.getState() == ol.TileState.LOADED) {
if (!(altZ in tilesToDrawByZ)) {
tilesToDrawByZ[altZ] = {};
}
tilesToDrawByZ[altZ][tileKey] = altTile;
} else {
fullyCovered = false;
}
});
return fullyCovered;
});
}, this);
}
/** @type {Array.<number>} */
var zs = goog.array.map(goog.object.getKeys(tilesToDrawByZ), Number);
goog.array.sort(zs);
var fragment = document.createDocumentFragment();
var altFragment = document.createDocumentFragment();
var newTiles = false;
var newAltTiles = false;
for (var i = 0, ii = zs.length; i < ii; ++i) {
var tileZ = zs[i];
var tilesToDraw = tilesToDrawByZ[tileZ];
var tileOffset = this.getTileOffset_(tileZ, mapResolution);
for (var key in tilesToDraw) {
var tile = tilesToDraw[key];
var tileCoord = tile.tileCoord;
var pixelBounds = tileGrid.getPixelBoundsForTileCoordAndResolution(
tileCoord, mapResolution);
var img = tile.getImage(this);
var style = img.style;
var append = !(key in this.renderedTiles_);
if (append || resolutionChanged) {
style.left = (pixelBounds.minX - tileOffset.x) + 'px';
style.top = (-pixelBounds.maxY - tileOffset.y) + 'px';
style.width = pixelBounds.getWidth() + 'px';
style.height = pixelBounds.getHeight() + 'px';
}
if (append) {
this.renderedTiles_[key] = tile;
style.position = 'absolute';
if (tileZ === z) {
goog.dom.appendChild(fragment, img);
newTiles = true;
} else {
goog.dom.appendChild(altFragment, img);
newAltTiles = true;
/** @type {Object.<number, boolean>} */
var newTileLayerZKeys = {};
var tileSize = tileGrid.getTileSize();
var iz, tileCoordKey, tileCoordOrigin, tileLayerZ, tileLayerZKey, tilesToDraw;
for (iz = 0; iz < zs.length; ++iz) {
tileLayerZKey = zs[iz];
if (tileLayerZKey in this.tileLayerZs_) {
tileLayerZ = this.tileLayerZs_[tileLayerZKey];
} else {
tileCoordOrigin =
tileGrid.getTileCoordForCoordAndZ(view2DState.center, tileLayerZKey);
tileLayerZ = new ol.renderer.dom.TileLayerZ_(tileGrid, tileCoordOrigin);
newTileLayerZKeys[tileLayerZKey] = true;
this.tileLayerZs_[tileLayerZKey] = tileLayerZ;
}
tilesToDraw = tilesToDrawByZ[tileLayerZKey];
for (tileCoordKey in tilesToDraw) {
tileLayerZ.addTile(tilesToDraw[tileCoordKey]);
}
tileLayerZ.finalizeAddTiles();
}
/** @type {Array.<number>} */
var tileLayerZKeys =
goog.array.map(goog.object.getKeys(this.tileLayerZs_), Number);
goog.array.sort(tileLayerZKeys);
var i, j, origin, resolution;
var transform = goog.vec.Mat4.createNumber();
for (i = 0; i < tileLayerZKeys.length; ++i) {
tileLayerZKey = tileLayerZKeys[i];
tileLayerZ = this.tileLayerZs_[tileLayerZKey];
if (!(tileLayerZKey in tilesToDrawByZ)) {
goog.dom.removeNode(tileLayerZ.target);
delete this.tileLayerZs_[tileLayerZKey];
continue;
}
resolution = tileLayerZ.getResolution();
origin = tileLayerZ.getOrigin();
goog.vec.Mat4.makeIdentity(transform);
goog.vec.Mat4.translate(
transform, frameState.size.width / 2, frameState.size.height / 2, 0);
goog.vec.Mat4.rotateZ(transform, view2DState.rotation);
goog.vec.Mat4.scale(transform, resolution / view2DState.resolution,
resolution / view2DState.resolution, 1);
goog.vec.Mat4.translate(
transform,
(origin.x - view2DState.center.x) / resolution,
(view2DState.center.y - origin.y) / resolution,
0);
tileLayerZ.setTransform(transform);
if (tileLayerZKey in newTileLayerZKeys) {
for (j = tileLayerZKey - 1; j >= 0; --j) {
if (j in this.tileLayerZs_) {
goog.dom.insertSiblingAfter(
tileLayerZ.target, this.tileLayerZs_[j].target);
break;
}
}
}
}
if (newAltTiles) {
var child = this.target.firstChild;
if (child) {
goog.dom.insertSiblingBefore(altFragment, child);
if (j < 0) {
goog.dom.insertChildAt(this.target, tileLayerZ.target, 0);
}
} else {
goog.dom.appendChild(this.target, altFragment);
if (!frameState.viewHints[ol.ViewHint.ANIMATING] &&
!frameState.viewHints[ol.ViewHint.PANNING]) {
tileLayerZ.removeTilesOutsideExtent(frameState.extent);
}
}
}
if (newTiles) {
goog.dom.appendChild(this.target, fragment);
if (layerState.opacity != this.renderedOpacity_) {
goog.style.setOpacity(this.target, layerState.opacity);
this.renderedOpacity_ = layerState.opacity;
}
this.renderedMapResolution_ = mapResolution;
if (layerState.visible && !this.renderedVisible_) {
goog.style.showElement(this.target, true);
this.renderedVisible_ = true;
}
this.removeExtraTiles_(tilesToDrawByZ);
if (!allTilesLoaded) {
frameState.animate = true;
}
return !allTilesLoaded;
};
/**
* @constructor
* @private
* @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
* @param {ol.TileCoord} tileCoordOrigin Tile coord origin.
*/
ol.renderer.dom.TileLayerZ_ = function(tileGrid, tileCoordOrigin) {
/**
* @type {!Element}
*/
this.target = goog.dom.createElement(goog.dom.TagName.DIV);
this.target.style.position = 'absolute';
/**
* @private
* @type {ol.tilegrid.TileGrid}
*/
this.tileGrid_ = tileGrid;
/**
* @private
* @type {ol.TileCoord}
*/
this.tileCoordOrigin_ = tileCoordOrigin;
/**
* @private
* @type {ol.Coordinate}
*/
this.origin_ = tileGrid.getTileCoordExtent(tileCoordOrigin).getTopLeft();
/**
* @private
* @type {number}
*/
this.resolution_ = tileGrid.getResolution(tileCoordOrigin.z);
/**
* @private
* @type {Object.<string, ol.Tile>}
*/
this.tiles_ = {};
/**
* @private
* @type {DocumentFragment}
*/
this.documentFragment_ = null;
/**
* @private
* @type {goog.vec.Mat4.AnyType}
*/
this.transform_ = goog.vec.Mat4.createNumberIdentity();
};
/**
* @param {ol.Tile} tile Tile.
*/
ol.renderer.dom.TileLayerZ_.prototype.addTile = function(tile) {
var tileCoord = tile.tileCoord;
goog.asserts.assert(tileCoord.z == this.tileCoordOrigin_.z);
var tileCoordKey = tileCoord.toString();
if (tileCoordKey in this.tiles_) {
return;
}
var tileSize = this.tileGrid_.getTileSize();
var image = tile.getImage(this);
var style = image.style;
style.position = 'absolute';
style.left =
((tileCoord.x - this.tileCoordOrigin_.x) * tileSize.width) + 'px';
style.top =
((this.tileCoordOrigin_.y - tileCoord.y) * tileSize.height) + 'px';
if (goog.isNull(this.documentFragment_)) {
this.documentFragment_ = document.createDocumentFragment();
}
goog.dom.appendChild(this.documentFragment_, image);
this.tiles_[tileCoordKey] = tile;
};
/**
* FIXME empty description for jsdoc
*/
ol.renderer.dom.TileLayerZ_.prototype.finalizeAddTiles = function() {
if (!goog.isNull(this.documentFragment_)) {
goog.dom.appendChild(this.target, this.documentFragment_);
this.documentFragment_ = null;
}
};
/**
* @return {ol.Coordinate} Origin.
*/
ol.renderer.dom.TileLayerZ_.prototype.getOrigin = function() {
return this.origin_;
};
/**
* @return {number} Resolution.
*/
ol.renderer.dom.TileLayerZ_.prototype.getResolution = function() {
return this.resolution_;
};
/**
* @param {ol.Extent} extent Extent.
*/
ol.renderer.dom.TileLayerZ_.prototype.removeTilesOutsideExtent =
function(extent) {
var tileRange =
this.tileGrid_.getTileRangeForExtentAndZ(extent, this.tileCoordOrigin_.z);
var tilesToRemove = [];
var tile, tileCoordKey;
for (tileCoordKey in this.tiles_) {
tile = this.tiles_[tileCoordKey];
if (!tileRange.contains(tile.tileCoord)) {
tilesToRemove.push(tile);
}
}
var i;
for (i = 0; i < tilesToRemove.length; ++i) {
tile = tilesToRemove[i];
tileCoordKey = tile.tileCoord.toString();
goog.dom.removeNode(tile.getImage(this));
delete this.tiles_[tileCoordKey];
}
};
/**
* @param {goog.vec.Mat4.AnyType} transform Transform.
*/
ol.renderer.dom.TileLayerZ_.prototype.setTransform = function(transform) {
if (!goog.vec.Mat4.equals(transform, this.transform_)) {
ol.dom.transformElement2D(this.target, transform, 6);
goog.vec.Mat4.setFromArray(this.transform_, transform);
}
};

View File

@@ -1,11 +1,15 @@
goog.provide('ol.renderer.Map');
goog.require('goog.Disposable');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.functions');
goog.require('goog.fx.anim');
goog.require('goog.fx.anim.Animated');
goog.require('goog.vec.Mat4');
goog.require('ol.FrameState');
goog.require('ol.View2D');
goog.require('ol.View2DProperty');
@@ -43,23 +47,12 @@ ol.renderer.Map = function(container, map) {
*/
this.layersListenerKeys_ = null;
/**
* @private
* @type {goog.vec.Mat4.Number}
*/
this.coordinateToPixelMatrix_ = goog.vec.Mat4.createNumber();
/**
* @private
* @type {goog.vec.Mat4.Number}
* @type {?number}
*/
this.pixelToCoordinateMatrix_ = goog.vec.Mat4.createNumber();
/**
* @private
* @type {boolean}
*/
this.matricesDirty_ = true;
this.viewPropertyListenerKey_ = null;
/**
* @private
@@ -70,25 +63,17 @@ ol.renderer.Map = function(container, map) {
map, ol.Object.getChangedEventType(ol.MapProperty.BACKGROUND_COLOR),
this.handleBackgroundColorChanged, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.CENTER),
this.handleCenterChanged, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.LAYERS),
this.handleLayersChanged, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.RESOLUTION),
this.handleResolutionChanged, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.ROTATION),
this.handleRotationChanged, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.SIZE),
this.handleSizeChanged, false, this)
this.handleSizeChanged, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.VIEW),
this.handleViewChanged, false, this)
];
};
@@ -106,9 +91,35 @@ ol.renderer.Map.prototype.addLayer = function(layer) {
/**
* @return {boolean} Can rotate.
* @param {ol.FrameState} frameState FrameState.
* @protected
*/
ol.renderer.Map.prototype.canRotate = goog.functions.FALSE;
ol.renderer.Map.prototype.calculateMatrices2D = function(frameState) {
var view2DState = frameState.view2DState;
var coordinateToPixelMatrix = frameState.coordinateToPixelMatrix;
goog.vec.Mat4.makeIdentity(coordinateToPixelMatrix);
goog.vec.Mat4.translate(coordinateToPixelMatrix,
frameState.size.width / 2,
frameState.size.height / 2,
0);
goog.vec.Mat4.scale(coordinateToPixelMatrix,
1 / view2DState.resolution,
-1 / view2DState.resolution,
1);
goog.vec.Mat4.rotateZ(coordinateToPixelMatrix,
-view2DState.rotation);
goog.vec.Mat4.translate(coordinateToPixelMatrix,
-view2DState.center.x,
-view2DState.center.y,
0);
var inverted = goog.vec.Mat4.invert(
coordinateToPixelMatrix, frameState.pixelToCoordinateMatrix);
goog.asserts.assert(inverted);
};
/**
@@ -127,6 +138,9 @@ ol.renderer.Map.prototype.disposeInternal = function() {
goog.dispose(layerRenderer);
});
goog.array.forEach(this.mapListenerKeys_, goog.events.unlistenByKey);
if (!goog.isNull(this.viewPropertyListenerKey_)) {
goog.events.unlistenByKey(this.viewPropertyListenerKey_);
}
if (!goog.isNull(this.layersListenerKeys_)) {
goog.array.forEach(this.layersListenerKeys_, goog.events.unlistenByKey);
}
@@ -134,37 +148,6 @@ ol.renderer.Map.prototype.disposeInternal = function() {
};
/**
* @param {function(this: T, ol.layer.Layer, ol.renderer.Layer, number)} f
* Function.
* @param {T=} opt_obj Object.
* @template T
*/
ol.renderer.Map.prototype.forEachReadyVisibleLayer = function(f, opt_obj) {
var layers = this.map.getLayers();
if (goog.isDef(layers)) {
layers.forEach(function(layer, index) {
if (layer.isReady() && layer.getVisible()) {
var layerRenderer = this.getLayerRenderer(layer);
f.call(opt_obj, layer, layerRenderer, index);
}
}, this);
}
};
/**
* @param {ol.Pixel} pixel Pixel.
* @return {ol.Coordinate} Coordinate.
*/
ol.renderer.Map.prototype.getCoordinateFromPixel = function(pixel) {
this.updateMatrices_();
var vec3 = [pixel.x, pixel.y, 0];
goog.vec.Mat4.multVec3(this.pixelToCoordinateMatrix_, vec3, vec3);
return new ol.Coordinate(vec3[0], vec3[1]);
};
/**
* @param {ol.layer.Layer} layer Layer.
* @protected
@@ -186,38 +169,18 @@ ol.renderer.Map.prototype.getMap = function() {
};
/**
* @param {ol.Coordinate} coordinate Coordinate.
* @return {ol.Pixel} Pixel.
*/
ol.renderer.Map.prototype.getPixelFromCoordinate = function(coordinate) {
this.updateMatrices_();
var vec3 = [coordinate.x, coordinate.y, 0];
goog.vec.Mat4.multVec3(this.coordinateToPixelMatrix_, vec3, vec3);
return new ol.Pixel(vec3[0], vec3[1]);
};
/**
* Handle background color changed.
*/
ol.renderer.Map.prototype.handleBackgroundColorChanged = goog.nullFunction;
/**
* @protected
*/
ol.renderer.Map.prototype.handleCenterChanged = function() {
this.matricesDirty_ = true;
};
/**
* @param {ol.CollectionEvent} collectionEvent Collection event.
* @protected
*/
ol.renderer.Map.prototype.handleLayersAdd = function(collectionEvent) {
var layer = /** @type {ol.layer.Layer} */ collectionEvent.elem;
var layer = /** @type {ol.layer.Layer} */ (collectionEvent.elem);
this.addLayer(layer);
};
@@ -226,10 +189,7 @@ ol.renderer.Map.prototype.handleLayersAdd = function(collectionEvent) {
* @protected
*/
ol.renderer.Map.prototype.handleLayersChanged = function() {
var layerRenderers = goog.object.getValues(this.layerRenderers);
goog.array.forEach(layerRenderers, function(layerRenderer) {
this.removeLayerRenderer(layerRenderer);
}, this);
goog.disposeAll(goog.object.getValues(this.layerRenderers));
this.layerRenderers = {};
if (!goog.isNull(this.layersListenerKeys_)) {
goog.array.forEach(this.layersListenerKeys_, goog.events.unlistenByKey);
@@ -253,7 +213,7 @@ ol.renderer.Map.prototype.handleLayersChanged = function() {
* @protected
*/
ol.renderer.Map.prototype.handleLayersRemove = function(collectionEvent) {
var layer = /** @type {ol.layer.Layer} */ collectionEvent.elem;
var layer = /** @type {ol.layer.Layer} */ (collectionEvent.elem);
this.removeLayer(layer);
};
@@ -261,16 +221,8 @@ ol.renderer.Map.prototype.handleLayersRemove = function(collectionEvent) {
/**
* @protected
*/
ol.renderer.Map.prototype.handleResolutionChanged = function() {
this.matricesDirty_ = true;
};
/**
* @protected
*/
ol.renderer.Map.prototype.handleRotationChanged = function() {
this.matricesDirty_ = true;
ol.renderer.Map.prototype.handleViewPropertyChanged = function() {
this.getMap().render();
};
@@ -278,7 +230,25 @@ ol.renderer.Map.prototype.handleRotationChanged = function() {
* @protected
*/
ol.renderer.Map.prototype.handleSizeChanged = function() {
this.matricesDirty_ = true;
this.getMap().render();
};
/**
* @protected
*/
ol.renderer.Map.prototype.handleViewChanged = function() {
if (!goog.isNull(this.viewPropertyListenerKey_)) {
goog.events.unlistenByKey(this.viewPropertyListenerKey_);
this.viewPropertyListenerKey_ = null;
}
var view = this.getMap().getView();
if (goog.isDefAndNotNull(view)) {
this.viewPropertyListenerKey_ = goog.events.listen(
view, ol.ObjectEventType.CHANGED,
this.handleViewPropertyChanged, false, this);
}
this.getMap().render();
};
@@ -310,9 +280,9 @@ ol.renderer.Map.prototype.removeLayerRenderer = function(layer) {
/**
* Render.
* @param {number} time Time.
* @param {?ol.FrameState} frameState Frame state.
*/
ol.renderer.Map.prototype.renderFrame = goog.functions.FALSE;
ol.renderer.Map.prototype.renderFrame = goog.nullFunction;
/**
@@ -325,44 +295,3 @@ ol.renderer.Map.prototype.setLayerRenderer = function(layer, layerRenderer) {
goog.asserts.assert(!(key in this.layerRenderers));
this.layerRenderers[key] = layerRenderer;
};
/**
* @private
*/
ol.renderer.Map.prototype.updateMatrices_ = function() {
if (this.matricesDirty_) {
var map = this.map;
var center = /** @type {!ol.Coordinate} */ map.getCenter();
var resolution = /** @type {number} */ map.getResolution();
var rotation = map.getRotation();
var size = /** @type {!ol.Size} */ map.getSize();
goog.vec.Mat4.makeIdentity(this.coordinateToPixelMatrix_);
goog.vec.Mat4.translate(this.coordinateToPixelMatrix_,
size.width / 2,
size.height / 2,
0);
goog.vec.Mat4.scale(this.coordinateToPixelMatrix_,
1 / resolution,
-1 / resolution,
1);
if (this.canRotate() && goog.isDef(rotation)) {
goog.vec.Mat4.rotateZ(this.coordinateToPixelMatrix_, -rotation);
}
goog.vec.Mat4.translate(this.coordinateToPixelMatrix_,
-center.x,
-center.y,
0);
var inverted = goog.vec.Mat4.invert(
this.coordinateToPixelMatrix_, this.pixelToCoordinateMatrix_);
goog.asserts.assert(inverted);
this.matricesDirty_ = false;
}
};

View File

@@ -1,8 +1,13 @@
// FIXME move colorMatrix_ elsewhere?
goog.provide('ol.renderer.webgl.Layer');
goog.require('goog.vec.Mat4');
goog.require('ol.FrameState');
goog.require('ol.layer.Layer');
goog.require('ol.layer.LayerState');
goog.require('ol.renderer.Layer');
goog.require('ol.vec.Mat4');
@@ -13,7 +18,50 @@ goog.require('ol.renderer.Layer');
* @param {ol.layer.Layer} layer Layer.
*/
ol.renderer.webgl.Layer = function(mapRenderer, layer) {
goog.base(this, mapRenderer, layer);
/**
* @private
* @type {!goog.vec.Mat4.Float32}
*/
this.brightnessMatrix_ = goog.vec.Mat4.createFloat32();
/**
* @private
* @type {!goog.vec.Mat4.Float32}
*/
this.contrastMatrix_ = goog.vec.Mat4.createFloat32();
/**
* @private
* @type {!goog.vec.Mat4.Float32}
*/
this.hueMatrix_ = goog.vec.Mat4.createFloat32();
/**
* @private
* @type {!goog.vec.Mat4.Float32}
*/
this.saturationMatrix_ = goog.vec.Mat4.createFloat32();
/**
* @private
* @type {!goog.vec.Mat4.Float32}
*/
this.colorMatrix_ = goog.vec.Mat4.createFloat32();
/**
* @private
* @type {boolean}
*/
this.colorMatrixDirty_ = true;
this.handleLayerBrightnessChange();
this.handleLayerContrastChange();
this.handleLayerHueChange();
this.handleLayerSaturationChange();
};
goog.inherits(ol.renderer.webgl.Layer, ol.renderer.Layer);
@@ -27,17 +75,28 @@ ol.renderer.webgl.Layer.prototype.dispatchChangeEvent = function() {
/**
* @inheritDoc
* @return {ol.renderer.Map} MapRenderer.
* @return {!goog.vec.Mat4.Float32} Color matrix.
*/
ol.renderer.webgl.Layer.prototype.getMapRenderer = function() {
return /** @type {ol.renderer.webgl.Map} */ goog.base(
this, 'getMapRenderer');
ol.renderer.webgl.Layer.prototype.getColorMatrix = function() {
if (this.colorMatrixDirty_) {
this.updateColorMatrix_();
}
return this.colorMatrix_;
};
/**
* @return {goog.vec.Mat4.AnyType} Matrix.
* @inheritDoc
* @return {ol.renderer.Map} MapRenderer.
*/
ol.renderer.webgl.Layer.prototype.getMapRenderer = function() {
return /** @type {ol.renderer.webgl.Map} */ (goog.base(
this, 'getMapRenderer'));
};
/**
* @return {!goog.vec.Mat4.Number} Matrix.
*/
ol.renderer.webgl.Layer.prototype.getMatrix = goog.abstractMethod;
@@ -52,6 +111,9 @@ ol.renderer.webgl.Layer.prototype.getTexture = goog.abstractMethod;
* @inheritDoc
*/
ol.renderer.webgl.Layer.prototype.handleLayerBrightnessChange = function() {
var value = this.getLayer().getBrightness();
ol.vec.Mat4.makeBrightness(this.brightnessMatrix_, value);
this.colorMatrixDirty_ = true;
this.dispatchChangeEvent();
};
@@ -60,6 +122,9 @@ ol.renderer.webgl.Layer.prototype.handleLayerBrightnessChange = function() {
* @inheritDoc
*/
ol.renderer.webgl.Layer.prototype.handleLayerContrastChange = function() {
var value = this.getLayer().getContrast();
ol.vec.Mat4.makeContrast(this.contrastMatrix_, value);
this.colorMatrixDirty_ = true;
this.dispatchChangeEvent();
};
@@ -68,6 +133,9 @@ ol.renderer.webgl.Layer.prototype.handleLayerContrastChange = function() {
* @inheritDoc
*/
ol.renderer.webgl.Layer.prototype.handleLayerHueChange = function() {
var value = this.getLayer().getHue();
ol.vec.Mat4.makeHue(this.hueMatrix_, value);
this.colorMatrixDirty_ = true;
this.dispatchChangeEvent();
};
@@ -92,6 +160,9 @@ ol.renderer.webgl.Layer.prototype.handleLayerOpacityChange = function() {
* @inheritDoc
*/
ol.renderer.webgl.Layer.prototype.handleLayerSaturationChange = function() {
var saturation = this.getLayer().getSaturation();
ol.vec.Mat4.makeSaturation(this.saturationMatrix_, saturation);
this.colorMatrixDirty_ = true;
this.dispatchChangeEvent();
};
@@ -112,7 +183,21 @@ ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = goog.nullFunction;
/**
* Render.
* @param {number} time Time.
* @return {boolean} Request render frame.
* @param {ol.FrameState} frameState Frame state.
* @param {ol.layer.LayerState} layerState Layer state.
*/
ol.renderer.webgl.Layer.prototype.renderFrame = goog.abstractMethod;
/**
* @private
*/
ol.renderer.webgl.Layer.prototype.updateColorMatrix_ = function() {
var colorMatrix = this.colorMatrix_;
goog.vec.Mat4.makeIdentity(colorMatrix);
goog.vec.Mat4.multMat(colorMatrix, this.contrastMatrix_, colorMatrix);
goog.vec.Mat4.multMat(colorMatrix, this.brightnessMatrix_, colorMatrix);
goog.vec.Mat4.multMat(colorMatrix, this.saturationMatrix_, colorMatrix);
goog.vec.Mat4.multMat(colorMatrix, this.hueMatrix_, colorMatrix);
this.colorMatrixDirty_ = false;
};

View File

@@ -1,7 +1,5 @@
// FIXME clear textureCache
// FIXME defer texture loads until after render when animating
// FIXME generational tile texture garbage collector newFrame/get
// FIXME defer cleanup until post-render
// FIXME check against gl.getParameter(webgl.MAX_TEXTURE_SIZE)
goog.provide('ol.renderer.webgl.Map');
@@ -14,10 +12,9 @@ goog.require('goog.dom.TagName');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.events.EventType');
goog.require('goog.functions');
goog.require('goog.style');
goog.require('goog.vec.Mat4');
goog.require('goog.webgl');
goog.require('ol.Tile');
goog.require('ol.layer.Layer');
goog.require('ol.layer.TileLayer');
goog.require('ol.renderer.webgl.FragmentShader');
@@ -119,6 +116,12 @@ ol.renderer.webgl.Map = function(container, map) {
this.canvas_.className = 'ol-unselectable';
goog.dom.insertChildAt(container, this.canvas_, 0);
/**
* @private
* @type {boolean}
*/
this.renderedVisible_ = true;
/**
* @private
* @type {ol.Size}
@@ -143,12 +146,6 @@ ol.renderer.webgl.Map = function(container, map) {
goog.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED,
this.handleWebGLContextResourced, false, this);
/**
* @private
* @type {ol.Color}
*/
this.clearColor_ = new ol.Color(1, 1, 1, 1);
/**
* @private
* @type {{aPosition: number,
@@ -220,15 +217,15 @@ ol.renderer.webgl.Map.prototype.addLayer = function(layer) {
/**
* @param {Image} image Image.
* @param {ol.Tile} tile Tile.
* @param {number} magFilter Mag filter.
* @param {number} minFilter Min filter.
*/
ol.renderer.webgl.Map.prototype.bindImageTexture =
function(image, magFilter, minFilter) {
ol.renderer.webgl.Map.prototype.bindTileTexture =
function(tile, magFilter, minFilter) {
var gl = this.getGL();
var imageKey = image.src;
var textureCacheEntry = this.textureCache_[imageKey];
var tileKey = tile.getKey();
var textureCacheEntry = this.textureCache_[tileKey];
if (goog.isDef(textureCacheEntry)) {
gl.bindTexture(goog.webgl.TEXTURE_2D, textureCacheEntry.texture);
if (textureCacheEntry.magFilter != magFilter) {
@@ -245,7 +242,7 @@ ol.renderer.webgl.Map.prototype.bindImageTexture =
var texture = gl.createTexture();
gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA, goog.webgl.RGBA,
goog.webgl.UNSIGNED_BYTE, image);
goog.webgl.UNSIGNED_BYTE, tile.getImage());
gl.texParameteri(
goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, magFilter);
gl.texParameteri(
@@ -254,7 +251,7 @@ ol.renderer.webgl.Map.prototype.bindImageTexture =
goog.webgl.CLAMP_TO_EDGE);
gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T,
goog.webgl.CLAMP_TO_EDGE);
this.textureCache_[imageKey] = {
this.textureCache_[tileKey] = {
texture: texture,
magFilter: magFilter,
minFilter: minFilter
@@ -263,75 +260,10 @@ ol.renderer.webgl.Map.prototype.bindImageTexture =
};
/**
* @inheritDoc
*/
ol.renderer.webgl.Map.prototype.canRotate = goog.functions.TRUE;
/**
* @param {number} value Hue value.
* @return {!goog.vec.Mat4.Float32} Matrix.
*/
ol.renderer.webgl.Map.prototype.createHueRotateMatrix = function(value) {
var cosHue = Math.cos(value);
var sinHue = Math.sin(value);
var v00 = 0.213 + cosHue * 0.787 - sinHue * 0.213;
var v01 = 0.715 - cosHue * 0.715 - sinHue * 0.715;
var v02 = 0.072 - cosHue * 0.072 + sinHue * 0.928;
var v03 = 0;
var v10 = 0.213 - cosHue * 0.213 + sinHue * 0.143;
var v11 = 0.715 + cosHue * 0.285 + sinHue * 0.140;
var v12 = 0.072 - cosHue * 0.072 - sinHue * 0.283;
var v13 = 0;
var v20 = 0.213 - cosHue * 0.213 - sinHue * 0.787;
var v21 = 0.715 - cosHue * 0.715 + sinHue * 0.715;
var v22 = 0.072 + cosHue * 0.928 + sinHue * 0.072;
var v23 = 0;
var v30 = 0;
var v31 = 0;
var v32 = 0;
var v33 = 1;
var matrix = goog.vec.Mat4.createFloat32();
goog.vec.Mat4.setFromValues(matrix,
v00, v10, v20, v30,
v01, v11, v21, v31,
v02, v12, v22, v32,
v03, v13, v23, v33);
return matrix;
};
/**
* @param {number} value Brightness value.
* @return {!goog.vec.Mat4.Float32} Matrix.
*/
ol.renderer.webgl.Map.prototype.createBrightnessMatrix = function(value) {
var matrix = goog.vec.Mat4.createFloat32Identity();
goog.vec.Mat4.setColumnValues(matrix, 3, value, value, value, 1);
return matrix;
};
/**
* @param {number} value Contrast value.
* @return {!goog.vec.Mat4.Float32} Matrix.
*/
ol.renderer.webgl.Map.prototype.createContrastMatrix = function(value) {
var matrix = goog.vec.Mat4.createFloat32();
goog.vec.Mat4.setDiagonalValues(matrix, value, value, value, 1);
var translateValue = (-0.5 * value + 0.5);
goog.vec.Mat4.setColumnValues(matrix, 3,
translateValue, translateValue, translateValue, 1);
return matrix;
};
/**
* @inheritDoc
*/
ol.renderer.webgl.Map.prototype.createLayerRenderer = function(layer) {
var gl = this.getGL();
if (layer instanceof ol.layer.TileLayer) {
return new ol.renderer.webgl.TileLayer(this, layer);
} else {
@@ -341,38 +273,6 @@ ol.renderer.webgl.Map.prototype.createLayerRenderer = function(layer) {
};
/**
* @param {number} value Saturation value.
* @return {!goog.vec.Mat4.Float32} Matrix.
*/
ol.renderer.webgl.Map.prototype.createSaturateMatrix = function(value) {
var v00 = 0.213 + 0.787 * value;
var v01 = 0.715 - 0.715 * value;
var v02 = 0.072 - 0.072 * value;
var v03 = 0;
var v10 = 0.213 - 0.213 * value;
var v11 = 0.715 + 0.285 * value;
var v12 = 0.072 - 0.072 * value;
var v13 = 0;
var v20 = 0.213 - 0.213 * value;
var v21 = 0.715 - 0.715 * value;
var v22 = 0.072 + 0.928 * value;
var v23 = 0;
var v30 = 0;
var v31 = 0;
var v32 = 0;
var v33 = 1;
var matrix = goog.vec.Mat4.createFloat32();
goog.vec.Mat4.setFromValues(matrix,
v00, v10, v20, v30,
v01, v11, v21, v31,
v02, v12, v22, v32,
v03, v13, v23, v33);
return matrix;
};
/**
* @inheritDoc
*/
@@ -464,21 +364,6 @@ ol.renderer.webgl.Map.prototype.getShader = function(shaderObject) {
* @inheritDoc
*/
ol.renderer.webgl.Map.prototype.handleBackgroundColorChanged = function() {
var backgroundColor = this.getMap().getBackgroundColor();
this.clearColor_ = new ol.Color(
backgroundColor.r / 255,
backgroundColor.g / 255,
backgroundColor.b / 255,
backgroundColor.a / 255);
this.getMap().render();
};
/**
* @inheritDoc
*/
ol.renderer.webgl.Map.prototype.handleCenterChanged = function() {
goog.base(this, 'handleCenterChanged');
this.getMap().render();
};
@@ -492,33 +377,6 @@ ol.renderer.webgl.Map.prototype.handleLayerRendererChange = function(event) {
};
/**
* @inheritDoc
*/
ol.renderer.webgl.Map.prototype.handleResolutionChanged = function() {
goog.base(this, 'handleResolutionChanged');
this.getMap().render();
};
/**
* @inheritDoc
*/
ol.renderer.webgl.Map.prototype.handleRotationChanged = function() {
goog.base(this, 'handleRotationChanged');
this.getMap().render();
};
/**
* @inheritDoc
*/
ol.renderer.webgl.Map.prototype.handleSizeChanged = function() {
goog.base(this, 'handleSizeChanged');
this.getMap().render();
};
/**
* @param {goog.events.Event} event Event.
* @protected
@@ -565,11 +423,11 @@ ol.renderer.webgl.Map.prototype.initializeGL_ = function() {
/**
* @param {Image} image Image.
* @return {boolean} Is image texture loaded.
* @param {ol.Tile} tile Tile.
* @return {boolean} Is tile texture loaded.
*/
ol.renderer.webgl.Map.prototype.isImageTextureLoaded = function(image) {
return image.src in this.textureCache_;
ol.renderer.webgl.Map.prototype.isTileTextureLoaded = function(tile) {
return tile.getKey() in this.textureCache_;
};
@@ -601,40 +459,38 @@ ol.renderer.webgl.Map.prototype.removeLayerRenderer = function(layer) {
/**
* @inheritDoc
*/
ol.renderer.webgl.Map.prototype.renderFrame = function(time) {
ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
if (!this.getMap().isDef()) {
return;
var gl = this.getGL();
if (goog.isNull(frameState)) {
if (this.renderedVisible_) {
goog.style.showElement(this.canvas_, false);
this.renderedVisible_ = false;
}
return false;
}
var requestRenderFrame = false;
this.forEachReadyVisibleLayer(function(layer, layerRenderer) {
if (layerRenderer.renderFrame(time)) {
requestRenderFrame = true;
goog.array.forEach(frameState.layersArray, function(layer) {
var layerState = frameState.layerStates[goog.getUid(layer)];
if (!layerState.visible || !layerState.ready) {
return;
}
});
var layerRenderer = this.getLayerRenderer(layer);
layerRenderer.renderFrame(frameState, layerState);
}, this);
var size = /** @type {ol.Size} */ this.getMap().getSize();
var size = frameState.size;
if (!this.canvasSize_.equals(size)) {
this.canvas_.width = size.width;
this.canvas_.height = size.height;
this.canvasSize_ = size;
}
var animate = false;
this.forEachReadyVisibleLayer(function(layer, layerRenderer) {
if (layerRenderer.renderFrame(time)) {
animate = true;
}
});
var gl = this.getGL();
gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, null);
gl.clearColor(this.clearColor_.r, this.clearColor_.g, this.clearColor_.b,
this.clearColor_.a);
var clearColor = frameState.backgroundColor;
gl.clearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
gl.clear(goog.webgl.COLOR_BUFFER_BIT);
gl.enable(goog.webgl.BLEND);
gl.viewport(0, 0, size.width, size.height);
@@ -674,28 +530,28 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(time) {
this.locations_.aTexCoord, 2, goog.webgl.FLOAT, false, 16, 8);
gl.uniform1i(this.locations_.uTexture, 0);
this.forEachReadyVisibleLayer(function(layer, layerRenderer) {
goog.array.forEach(frameState.layersArray, function(layer) {
var layerState = frameState.layerStates[goog.getUid(layer)];
if (!layerState.visible || !layerState.ready) {
return;
}
var layerRenderer = this.getLayerRenderer(layer);
gl.uniformMatrix4fv(
this.locations_.uMatrix, false, layerRenderer.getMatrix());
var hueRotateMatrix = this.createHueRotateMatrix(layer.getHue());
var saturateMatrix = this.createSaturateMatrix(layer.getSaturation());
var brightnessMatrix = this.createBrightnessMatrix(layer.getBrightness());
var contrastMatrix = this.createContrastMatrix(layer.getContrast());
var colorMatrix = goog.vec.Mat4.createFloat32Identity();
goog.vec.Mat4.multMat(colorMatrix, contrastMatrix, colorMatrix);
goog.vec.Mat4.multMat(colorMatrix, brightnessMatrix, colorMatrix);
goog.vec.Mat4.multMat(colorMatrix, saturateMatrix, colorMatrix);
goog.vec.Mat4.multMat(colorMatrix, hueRotateMatrix, colorMatrix);
gl.uniformMatrix4fv(this.locations_.uColorMatrix, false, colorMatrix);
gl.uniformMatrix4fv(
this.locations_.uColorMatrix, false, layerRenderer.getColorMatrix());
gl.uniform1f(this.locations_.uOpacity, layer.getOpacity());
gl.bindTexture(goog.webgl.TEXTURE_2D, layerRenderer.getTexture());
gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4);
}, this);
if (requestRenderFrame) {
this.getMap().requestRenderFrame();
if (!this.renderedVisible_) {
goog.style.showElement(this.canvas_, true);
this.renderedVisible_ = true;
}
this.calculateMatrices2D(frameState);
};

View File

@@ -3,12 +3,6 @@ goog.provide('ol.renderer.webgl');
goog.require('ol.webgl');
/**
* @define {boolean} Free resources immediately.
*/
ol.renderer.webgl.FREE_RESOURCES_IMMEDIATELY = false;
/**
* @return {boolean} Is supported.
*/

View File

@@ -1,7 +1,5 @@
// FIXME large resolutions lead to too large framebuffers :-(
// FIXME animated shaders! check in redraw
// FIXME throttle texture uploads
// FIXME prioritize texture uploads
goog.provide('ol.renderer.webgl.TileLayer');
goog.provide('ol.renderer.webgl.tilelayerrenderer');
@@ -10,13 +8,14 @@ goog.provide('ol.renderer.webgl.tilelayerrenderer.shader.Vertex');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.debug.Logger');
goog.require('goog.events.EventType');
goog.require('goog.object');
goog.require('goog.structs.PriorityQueue');
goog.require('goog.vec.Mat4');
goog.require('goog.vec.Vec4');
goog.require('goog.webgl');
goog.require('ol.Coordinate');
goog.require('ol.FrameState');
goog.require('ol.Size');
goog.require('ol.TileState');
goog.require('ol.layer.TileLayer');
@@ -88,14 +87,6 @@ ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) {
goog.base(this, mapRenderer, tileLayer);
if (goog.DEBUG) {
/**
* @inheritDoc
*/
this.logger = goog.debug.Logger.getLogger(
'ol.renderer.webgl.tilelayerrenderer.' + goog.getUid(this));
}
/**
* @private
* @type {ol.renderer.webgl.FragmentShader}
@@ -145,7 +136,7 @@ ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) {
/**
* @private
* @type {goog.vec.Mat4.AnyType}
* @type {!goog.vec.Mat4.Number}
*/
this.matrix_ = goog.vec.Mat4.createNumber();
@@ -166,11 +157,12 @@ goog.inherits(ol.renderer.webgl.TileLayer, ol.renderer.webgl.Layer);
/**
* @param {ol.FrameState} frameState Frame state.
* @param {number} framebufferDimension Framebuffer dimension.
* @private
*/
ol.renderer.webgl.TileLayer.prototype.bindFramebuffer_ =
function(framebufferDimension) {
function(frameState, framebufferDimension) {
var mapRenderer = this.getMapRenderer();
var gl = mapRenderer.getGL();
@@ -178,33 +170,14 @@ ol.renderer.webgl.TileLayer.prototype.bindFramebuffer_ =
if (!goog.isDef(this.framebufferDimension_) ||
this.framebufferDimension_ != framebufferDimension) {
if (goog.DEBUG) {
this.logger.info('re-sizing framebuffer');
}
if (ol.renderer.webgl.FREE_RESOURCES_IMMEDIATELY) {
if (goog.DEBUG) {
this.logger.info('freeing WebGL resources');
}
if (!gl.isContextLost()) {
gl.deleteFramebuffer(this.framebuffer_);
gl.deleteTexture(this.texture_);
}
} else {
var map = this.getMap();
goog.events.listenOnce(
map,
ol.MapEventType.POSTRENDER,
goog.partial(function(gl, framebuffer, texture) {
if (goog.DEBUG) {
this.logger.info('freeing WebGL resources on postrender');
}
if (!gl.isContextLost()) {
gl.deleteFramebuffer(framebuffer);
gl.deleteTexture(texture);
}
}, gl, this.framebuffer_, this.texture_));
}
var map = this.getMap();
frameState.postRenderFunctions.push(
goog.partial(function(gl, framebuffer, texture) {
if (!gl.isContextLost()) {
gl.deleteFramebuffer(framebuffer);
gl.deleteTexture(texture);
}
}, gl, this.framebuffer_, this.texture_));
var texture = gl.createTexture();
gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
@@ -247,15 +220,6 @@ ol.renderer.webgl.TileLayer.prototype.disposeInternal = function() {
};
/**
* @return {ol.layer.TileLayer} Layer.
* @inheritDoc
*/
ol.renderer.webgl.TileLayer.prototype.getLayer = function() {
return /** @type {ol.layer.TileLayer} */ goog.base(this, 'getLayer');
};
/**
* @inheritDoc
*/
@@ -272,6 +236,14 @@ ol.renderer.webgl.TileLayer.prototype.getTexture = function() {
};
/**
* @return {ol.layer.TileLayer} Tile layer.
*/
ol.renderer.webgl.TileLayer.prototype.getTileLayer = function() {
return /** @type {ol.layer.TileLayer} */ (this.getLayer());
};
/**
* @inheritDoc
*/
@@ -287,28 +259,22 @@ ol.renderer.webgl.TileLayer.prototype.handleWebGLContextLost = function() {
/**
* @inheritDoc
*/
ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
var requestRenderFrame = false;
ol.renderer.webgl.TileLayer.prototype.renderFrame =
function(frameState, layerState) {
var mapRenderer = this.getMapRenderer();
var map = this.getMap();
var gl = mapRenderer.getGL();
goog.asserts.assert(map.isDef());
var mapCenter = map.getCenter();
var mapExtent = map.getExtent();
var mapResolution = /** @type {number} */ map.getResolution();
var mapRotatedExtent = map.getRotatedExtent();
var mapRotation = map.getRotation();
var view2DState = frameState.view2DState;
var center = view2DState.center;
var tileLayer = this.getLayer();
var tileLayer = this.getTileLayer();
var tileSource = tileLayer.getTileSource();
var tileGrid = tileSource.getTileGrid();
var z = tileGrid.getZForResolution(mapResolution);
var z = tileGrid.getZForResolution(view2DState.resolution);
var tileResolution = tileGrid.getResolution(z);
var tileRange = tileGrid.getTileRangeForExtentAndResolution(
mapRotatedExtent, tileResolution);
frameState.extent, tileResolution);
var framebufferExtent;
@@ -337,7 +303,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
minX + framebufferExtentSize.width,
minY + framebufferExtentSize.height);
this.bindFramebuffer_(framebufferDimension);
this.bindFramebuffer_(frameState, framebufferDimension);
gl.viewport(0, 0, framebufferDimension, framebufferDimension);
gl.clearColor(0, 0, 0, 0);
@@ -382,65 +348,73 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
* @type {Object.<number, Object.<string, ol.Tile>>}
*/
var tilesToDrawByZ = {};
tilesToDrawByZ[z] = {};
/**
* @type {Array.<Image>}
*/
var imagesToLoad = [];
var findInterimTiles = function(z, tileRange) {
// FIXME this could be more efficient about filling partial holes
var fullyCovered = true;
var tile, tileCoord, tileCoordKey, x, y;
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
tileCoord = new ol.TileCoord(z, x, y);
tileCoordKey = tileCoord.toString();
if (tilesToDrawByZ[z] && tilesToDrawByZ[z][tileCoordKey]) {
return;
}
tile = tileSource.getTile(tileCoord);
if (!goog.isNull(tile) &&
tile.getState() == ol.TileState.LOADED &&
mapRenderer.isTileTextureLoaded(tile)) {
if (!tilesToDrawByZ[z]) {
tilesToDrawByZ[z] = {};
}
tilesToDrawByZ[z][tileCoordKey] = tile;
} else {
fullyCovered = false;
}
}
}
return fullyCovered;
};
var tilesToLoad = new goog.structs.PriorityQueue();
var allTilesLoaded = true;
var deltaX, deltaY, priority, tile, tileCenter, tileCoord, tileState, x, y;
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
tilesToDrawByZ[z] = {};
tileRange.forEachTileCoord(z, function(tileCoord) {
var tile = tileSource.getTile(tileCoord);
if (goog.isNull(tile)) {
return;
}
var tileState = tile.getState();
if (tileState == ol.TileState.IDLE) {
tile.load();
} else if (tileState == ol.TileState.LOADED) {
var image = tile.getImage();
if (mapRenderer.isImageTextureLoaded(image)) {
tilesToDrawByZ[z][tileCoord.toString()] = tile;
return;
} else {
imagesToLoad.push(image);
tileCoord = new ol.TileCoord(z, x, y);
tile = tileSource.getTile(tileCoord);
if (goog.isNull(tile)) {
continue;
}
} else if (tileState == ol.TileState.ERROR) {
return;
tileState = tile.getState();
if (tileState == ol.TileState.IDLE) {
tileCenter = tileGrid.getTileCoordCenter(tileCoord);
frameState.tileQueue.enqueue(tile, tileCenter, tileResolution);
} else if (tileState == ol.TileState.LOADED) {
if (mapRenderer.isTileTextureLoaded(tile)) {
tilesToDrawByZ[z][tileCoord.toString()] = tile;
continue;
} else {
tileCenter = tileGrid.getTileCoordCenter(tileCoord);
deltaX = tileCenter.x - center.x;
deltaY = tileCenter.y - center.y;
priority = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
tilesToLoad.enqueue(priority, tile);
}
} else if (tileState == ol.TileState.ERROR) {
continue;
}
allTilesLoaded = false;
tileGrid.forEachTileCoordParentTileRange(tileCoord, findInterimTiles);
}
allTilesLoaded = false;
// FIXME this could be more efficient about filling partial holes
tileGrid.forEachTileCoordParentTileRange(
tileCoord,
function(z, tileRange) {
var fullyCovered = true;
tileRange.forEachTileCoord(z, function(tileCoord) {
var tileCoordKey = tileCoord.toString();
if (tilesToDrawByZ[z] && tilesToDrawByZ[z][tileCoordKey]) {
return;
}
var tile = tileSource.getTile(tileCoord);
if (!goog.isNull(tile) &&
tile.getState() == ol.TileState.LOADED) {
if (!tilesToDrawByZ[z]) {
tilesToDrawByZ[z] = {};
}
tilesToDrawByZ[z][tileCoordKey] = tile;
} else {
fullyCovered = false;
}
});
return fullyCovered;
});
}, this);
}
/** @type {Array.<number>} */
var zs = goog.array.map(goog.object.getKeys(tilesToDrawByZ), Number);
@@ -457,29 +431,22 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
framebufferExtentSize.height - 1;
goog.vec.Vec4.setFromValues(uTileOffset, sx, sy, tx, ty);
gl.uniform4fv(this.locations_.uTileOffset, uTileOffset);
mapRenderer.bindImageTexture(
tile.getImage(), goog.webgl.LINEAR, goog.webgl.LINEAR);
mapRenderer.bindTileTexture(tile, goog.webgl.LINEAR, goog.webgl.LINEAR);
gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4);
}, this);
}, this);
if (!goog.array.isEmpty(imagesToLoad)) {
goog.events.listenOnce(
map,
ol.MapEventType.POSTRENDER,
goog.partial(function(mapRenderer, imagesToLoad) {
if (goog.DEBUG) {
this.logger.info(
'uploading ' + imagesToLoad.length + ' textures');
if (!tilesToLoad.isEmpty()) {
frameState.postRenderFunctions.push(
goog.partial(function(mapRenderer, tilesToLoad) {
var i, tile;
// FIXME determine a suitable number of textures to upload per frame
for (i = 0; !tilesToLoad.isEmpty() && i < 4; ++i) {
tile = /** @type {ol.Tile} */ (tilesToLoad.remove());
mapRenderer.bindTileTexture(
tile, goog.webgl.LINEAR, goog.webgl.LINEAR);
}
goog.array.forEach(imagesToLoad, function(image) {
mapRenderer.bindImageTexture(
image, goog.webgl.LINEAR, goog.webgl.LINEAR);
});
if (goog.DEBUG) {
this.logger.info('uploaded textures');
}
}, mapRenderer, imagesToLoad));
}, mapRenderer, tilesToLoad));
}
if (allTilesLoaded) {
@@ -488,25 +455,23 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
} else {
this.renderedTileRange_ = null;
this.renderedFramebufferExtent_ = null;
requestRenderFrame = true;
frameState.animate = true;
}
}
goog.vec.Mat4.makeIdentity(this.matrix_);
goog.vec.Mat4.translate(this.matrix_,
(mapCenter.x - framebufferExtent.minX) /
(view2DState.center.x - framebufferExtent.minX) /
(framebufferExtent.maxX - framebufferExtent.minX),
(mapCenter.y - framebufferExtent.minY) /
(view2DState.center.y - framebufferExtent.minY) /
(framebufferExtent.maxY - framebufferExtent.minY),
0);
if (goog.isDef(mapRotation)) {
goog.vec.Mat4.rotateZ(this.matrix_, mapRotation);
}
goog.vec.Mat4.rotateZ(this.matrix_, view2DState.rotation);
goog.vec.Mat4.scale(this.matrix_,
(mapExtent.maxX - mapExtent.minX) /
frameState.size.width * view2DState.resolution /
(framebufferExtent.maxX - framebufferExtent.minX),
(mapExtent.maxY - mapExtent.minY) /
frameState.size.height * view2DState.resolution /
(framebufferExtent.maxY - framebufferExtent.minY),
1);
goog.vec.Mat4.translate(this.matrix_,
@@ -514,6 +479,4 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
-0.5,
0);
return requestRenderFrame;
};

View File

@@ -6,7 +6,7 @@ goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.net.Jsonp');
goog.require('ol.TileCoverageArea');
goog.require('ol.source.TileSource');
goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.XYZ');
@@ -25,7 +25,7 @@ ol.BingMapsStyle = {
/**
* @constructor
* @extends {ol.source.TileSource}
* @extends {ol.source.ImageTileSource}
* @param {ol.source.BingMapsOptions} bingMapsOptions Bing Maps options.
*/
ol.source.BingMaps = function(bingMapsOptions) {
@@ -57,7 +57,7 @@ ol.source.BingMaps = function(bingMapsOptions) {
}, goog.bind(this.handleImageryMetadataResponse, this));
};
goog.inherits(ol.source.BingMaps, ol.source.TileSource);
goog.inherits(ol.source.BingMaps, ol.source.ImageTileSource);
/**

View File

@@ -0,0 +1,128 @@
goog.provide('ol.source.DebugTileSource');
goog.provide('ol.source.DebugTileSourceOptions');
goog.require('ol.Size');
goog.require('ol.Tile');
goog.require('ol.TileCoord');
goog.require('ol.source.TileSource');
goog.require('ol.tilegrid.TileGrid');
/**
* @constructor
* @extends {ol.Tile}
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
* @private
*/
ol.DebugTile_ = function(tileCoord, tileGrid) {
goog.base(this, tileCoord);
this.state = ol.TileState.LOADED;
/**
* @private
* @type {ol.TileCoord}
*/
this.tileCoord_ = tileCoord;
/**
* @private
* @type {ol.Size}
*/
this.tileSize_ = tileGrid.getTileSize();
/**
* @private
* @type {Object.<number, HTMLCanvasElement>}
*/
this.canvasByContext_ = {};
};
goog.inherits(ol.DebugTile_, ol.Tile);
/**
* @inheritDoc
*/
ol.DebugTile_.prototype.getImage = function(opt_context) {
var key = goog.isDef(opt_context) ? goog.getUid(opt_context) : -1;
if (key in this.canvasByContext_) {
return this.canvasByContext_[key];
} else {
var tileSize = this.tileSize_;
var canvas = /** @type {HTMLCanvasElement} */
(goog.dom.createElement(goog.dom.TagName.CANVAS));
canvas.width = tileSize.width;
canvas.height = tileSize.height;
var context = canvas.getContext('2d');
context.strokeStyle = 'black';
context.strokeRect(0.5, 0.5, tileSize.width + 0.5, tileSize.height + 0.5);
context.fillStyle = 'black';
context.textAlign = 'center';
context.textBaseline = 'middle';
context.font = '24px sans-serif';
context.fillText(
this.tileCoord_.toString(), tileSize.width / 2, tileSize.height / 2);
this.canvasByContext_[key] = canvas;
return canvas;
}
};
/**
* @typedef {{extent: (ol.Extent|undefined),
* projection: (ol.Projection|undefined),
* tileGrid: (ol.tilegrid.TileGrid|undefined)}}
*/
ol.source.DebugTileSourceOptions;
/**
* @constructor
* @extends {ol.source.TileSource}
* @param {ol.source.DebugTileSourceOptions} options Options.
*/
ol.source.DebugTileSource = function(options) {
goog.base(this, {
extent: options.extent,
projection: options.projection,
tileGrid: options.tileGrid
});
/**
* @private
* @type {Object.<string, ol.DebugTile_>}
* FIXME will need to expire elements from this cache
* FIXME see elemoine's work with goog.structs.LinkedMap
*/
this.tileCache_ = {};
};
goog.inherits(ol.source.DebugTileSource, ol.source.TileSource);
/**
* @inheritDoc
*/
ol.source.DebugTileSource.prototype.getTile = function(tileCoord) {
var key = tileCoord.toString();
if (goog.object.containsKey(this.tileCache_, key)) {
return this.tileCache_[key];
} else {
var tile = new ol.DebugTile_(tileCoord, this.tileGrid);
this.tileCache_[key] = tile;
return tile;
}
};

View File

@@ -0,0 +1,95 @@
goog.provide('ol.source.ImageTileSource');
goog.provide('ol.source.ImageTileSourceOptions');
goog.require('ol.Attribution');
goog.require('ol.Extent');
goog.require('ol.ImageTile');
goog.require('ol.Projection');
goog.require('ol.TileCoord');
goog.require('ol.TileUrlFunction');
goog.require('ol.TileUrlFunctionType');
goog.require('ol.source.TileSource');
goog.require('ol.tilegrid.TileGrid');
/**
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
* crossOrigin: (null|string|undefined),
* extent: (ol.Extent|undefined),
* projection: (ol.Projection|undefined),
* tileGrid: (ol.tilegrid.TileGrid|undefined),
* tileUrlFunction: (ol.TileUrlFunctionType|undefined)}}
*/
ol.source.ImageTileSourceOptions;
/**
* @constructor
* @extends {ol.source.TileSource}
* @param {ol.source.ImageTileSourceOptions} options Options.
*/
ol.source.ImageTileSource = function(options) {
goog.base(this, {
attributions: options.attributions,
extent: options.extent,
projection: options.projection,
tileGrid: options.tileGrid
});
/**
* @protected
* @type {ol.TileUrlFunctionType}
*/
this.tileUrlFunction = goog.isDef(options.tileUrlFunction) ?
options.tileUrlFunction :
ol.TileUrlFunction.nullTileUrlFunction;
/**
* @private
* @type {?string}
*/
this.crossOrigin_ =
goog.isDef(options.crossOrigin) ? options.crossOrigin : 'anonymous';
/**
* @private
* @type {Object.<string, ol.ImageTile>}
* FIXME will need to expire elements from this cache
* FIXME see elemoine's work with goog.structs.LinkedMap
*/
this.tileCache_ = {};
};
goog.inherits(ol.source.ImageTileSource, ol.source.TileSource);
/**
* @inheritDoc
*/
ol.source.ImageTileSource.prototype.getTile = function(tileCoord) {
var key = tileCoord.toString();
if (goog.object.containsKey(this.tileCache_, key)) {
return this.tileCache_[key];
} else {
var tileUrl = this.getTileCoordUrl(tileCoord);
var tile;
if (goog.isDef(tileUrl)) {
tile = new ol.ImageTile(tileCoord, tileUrl, this.crossOrigin_);
} else {
tile = null;
}
this.tileCache_[key] = tile;
return tile;
}
};
/**
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @return {string|undefined} Tile URL.
*/
ol.source.ImageTileSource.prototype.getTileCoordUrl = function(tileCoord) {
return this.tileUrlFunction(tileCoord);
};

View File

@@ -26,7 +26,7 @@ ol.source.StamenFlavor = {
/**
* @typedef {{flavor: ol.source.StamenFlavor,
* @typedef {{flavor: (ol.source.StamenFlavor|undefined),
* provider: ol.source.StamenProvider}}
*/
ol.source.StamenOptions;

View File

@@ -10,14 +10,14 @@ goog.require('ol.Attribution');
goog.require('ol.Projection');
goog.require('ol.TileCoord');
goog.require('ol.TileUrlFunction');
goog.require('ol.source.TileSource');
goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.TileGrid');
/**
* @constructor
* @extends {ol.source.TileSource}
* @extends {ol.source.ImageTileSource}
* @param {ol.source.TiledWMSOptions} tiledWMSOptions options.
*/
ol.source.TiledWMS = function(tiledWMSOptions) {
@@ -35,20 +35,8 @@ ol.source.TiledWMS = function(tiledWMSOptions) {
if (goog.isDef(tiledWMSOptions.tileGrid)) {
tileGrid = tiledWMSOptions.tileGrid;
} else {
// FIXME Factor this out to a more central/generic place.
var size = Math.max(
projectionExtent.maxX - projectionExtent.minX,
projectionExtent.maxY - projectionExtent.minY);
var maxZoom = goog.isDef(tiledWMSOptions.maxZoom) ?
tiledWMSOptions.maxZoom : 18;
var resolutions = new Array(maxZoom + 1);
for (var z = 0, zz = resolutions.length; z < zz; ++z) {
resolutions[z] = ol.Projection.EPSG_3857_HALF_SIZE / (128 << z);
}
tileGrid = new ol.tilegrid.TileGrid({
origin: projectionExtent.getTopLeft(),
resolutions: resolutions
});
tileGrid = ol.tilegrid.createForProjection(projection,
tiledWMSOptions.maxZoom);
}
var baseParams = {
@@ -116,4 +104,4 @@ ol.source.TiledWMS = function(tiledWMSOptions) {
});
};
goog.inherits(ol.source.TiledWMS, ol.source.TileSource);
goog.inherits(ol.source.TiledWMS, ol.source.ImageTileSource);

View File

@@ -16,7 +16,7 @@ goog.require('goog.string');
goog.require('ol.Projection');
goog.require('ol.TileCoverageArea');
goog.require('ol.TileUrlFunction');
goog.require('ol.source.TileSource');
goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.XYZ');
@@ -45,7 +45,7 @@ goog.exportSymbol('grid', grid);
/**
* @constructor
* @extends {ol.source.TileSource}
* @extends {ol.source.ImageTileSource}
* @param {ol.source.TileJSONOptions} tileJsonOptions TileJSON optios.
*/
ol.source.TileJSON = function(tileJsonOptions) {
@@ -69,7 +69,7 @@ ol.source.TileJSON = function(tileJsonOptions) {
this.deferred_.addCallback(this.handleTileJSONResponse, this);
};
goog.inherits(ol.source.TileJSON, ol.source.TileSource);
goog.inherits(ol.source.TileJSON, ol.source.ImageTileSource);
/**

View File

@@ -14,11 +14,9 @@ goog.require('ol.tilegrid.TileGrid');
/**
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
* crossOrigin: (null|string|undefined),
* extent: (ol.Extent|undefined),
* projection: (ol.Projection|undefined),
* tileGrid: (ol.tilegrid.TileGrid|undefined),
* tileUrlFunction: (ol.TileUrlFunctionType|undefined)}}
* tileGrid: (ol.tilegrid.TileGrid|undefined)}}
*/
ol.source.TileSourceOptions;
@@ -44,29 +42,6 @@ ol.source.TileSource = function(tileSourceOptions) {
this.tileGrid = goog.isDef(tileSourceOptions.tileGrid) ?
tileSourceOptions.tileGrid : null;
/**
* @protected
* @type {ol.TileUrlFunctionType}
*/
this.tileUrlFunction = goog.isDef(tileSourceOptions.tileUrlFunction) ?
tileSourceOptions.tileUrlFunction :
ol.TileUrlFunction.nullTileUrlFunction;
/**
* @private
* @type {?string}
*/
this.crossOrigin_ = goog.isDef(tileSourceOptions.crossOrigin) ?
tileSourceOptions.crossOrigin : 'anonymous';
/**
* @private
* @type {Object.<string, ol.Tile>}
* FIXME will need to expire elements from this cache
* FIXME see elemoine's work with goog.structs.LinkedMap
*/
this.tileCache_ = {};
};
goog.inherits(ol.source.TileSource, ol.source.Source);
@@ -83,31 +58,7 @@ ol.source.TileSource.prototype.getResolutions = function() {
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @return {ol.Tile} Tile.
*/
ol.source.TileSource.prototype.getTile = function(tileCoord) {
var key = tileCoord.toString();
if (goog.object.containsKey(this.tileCache_, key)) {
return this.tileCache_[key];
} else {
var tileUrl = this.getTileCoordUrl(tileCoord);
var tile;
if (goog.isDef(tileUrl)) {
tile = new ol.Tile(tileCoord, tileUrl, this.crossOrigin_);
} else {
tile = null;
}
this.tileCache_[key] = tile;
return tile;
}
};
/**
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @return {string|undefined} Tile URL.
*/
ol.source.TileSource.prototype.getTileCoordUrl = function(tileCoord) {
return this.tileUrlFunction(tileCoord);
};
ol.source.TileSource.prototype.getTile = goog.abstractMethod;
/**

View File

@@ -11,7 +11,7 @@ goog.require('ol.Size');
goog.require('ol.TileCoord');
goog.require('ol.TileUrlFunction');
goog.require('ol.TileUrlFunctionType');
goog.require('ol.source.TileSource');
goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.XYZ');
@@ -31,7 +31,7 @@ ol.source.XYZOptions;
/**
* @constructor
* @extends {ol.source.TileSource}
* @extends {ol.source.ImageTileSource}
* @param {ol.source.XYZOptions} xyzOptions XYZ options.
*/
ol.source.XYZ = function(xyzOptions) {
@@ -110,4 +110,4 @@ ol.source.XYZ = function(xyzOptions) {
});
};
goog.inherits(ol.source.XYZ, ol.source.TileSource);
goog.inherits(ol.source.XYZ, ol.source.ImageTileSource);

View File

@@ -24,10 +24,8 @@ ol.TileState = {
* @constructor
* @extends {goog.events.EventTarget}
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @param {string} src Image source URI.
* @param {?string} crossOrigin Cross origin.
*/
ol.Tile = function(tileCoord, src, crossOrigin) {
ol.Tile = function(tileCoord) {
goog.base(this);
@@ -37,39 +35,10 @@ ol.Tile = function(tileCoord, src, crossOrigin) {
this.tileCoord = tileCoord;
/**
* Image URI
*
* @private
* @type {string}
*/
this.src_ = src;
/**
* @private
* @protected
* @type {ol.TileState}
*/
this.state_ = ol.TileState.IDLE;
/**
* @private
* @type {Image}
*/
this.image_ = new Image();
if (!goog.isNull(crossOrigin)) {
this.image_.crossOrigin = crossOrigin;
}
/**
* @private
* @type {Object.<number, Image>}
*/
this.imageByContext_ = {};
/**
* @private
* @type {Array.<number>}
*/
this.imageListenerKeys_ = null;
this.state = ol.TileState.IDLE;
};
goog.inherits(ol.Tile, goog.events.EventTarget);
@@ -85,24 +54,16 @@ ol.Tile.prototype.dispatchChangeEvent = function() {
/**
* @param {Object=} opt_context Object.
* @return {Image} Image.
* @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
*/
ol.Tile.prototype.getImage = function(opt_context) {
if (goog.isDef(opt_context)) {
var image;
var key = goog.getUid(opt_context);
if (key in this.imageByContext_) {
return this.imageByContext_[key];
} else if (goog.object.isEmpty(this.imageByContext_)) {
image = this.image_;
} else {
image = /** @type {Image} */ this.image_.cloneNode(false);
}
this.imageByContext_[key] = image;
return image;
} else {
return this.image_;
}
ol.Tile.prototype.getImage = goog.abstractMethod;
/**
* @return {string} Key.
*/
ol.Tile.prototype.getKey = function() {
return goog.getUid(this).toString();
};
@@ -110,59 +71,11 @@ ol.Tile.prototype.getImage = function(opt_context) {
* @return {ol.TileState} State.
*/
ol.Tile.prototype.getState = function() {
return this.state_;
return this.state;
};
/**
* Tracks loading or read errors.
*
* @private
* FIXME empty description for jsdoc
*/
ol.Tile.prototype.handleImageError_ = function() {
this.state_ = ol.TileState.ERROR;
this.unlistenImage_();
};
/**
* Tracks successful image load.
*
* @private
*/
ol.Tile.prototype.handleImageLoad_ = function() {
this.state_ = ol.TileState.LOADED;
this.unlistenImage_();
this.dispatchChangeEvent();
};
/**
* Load not yet loaded URI.
*/
ol.Tile.prototype.load = function() {
if (this.state_ == ol.TileState.IDLE) {
this.state_ = ol.TileState.LOADING;
goog.asserts.assert(goog.isNull(this.imageListenerKeys_));
this.imageListenerKeys_ = [
goog.events.listenOnce(this.image_, goog.events.EventType.ERROR,
this.handleImageError_, false, this),
goog.events.listenOnce(this.image_, goog.events.EventType.LOAD,
this.handleImageLoad_, false, this)
];
this.image_.src = this.src_;
}
};
/**
* Discards event handlers which listen for load completion or errors.
*
* @private
*/
ol.Tile.prototype.unlistenImage_ = function() {
goog.asserts.assert(!goog.isNull(this.imageListenerKeys_));
goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey);
this.imageListenerKeys_ = null;
};
ol.Tile.prototype.load = goog.abstractMethod;

View File

@@ -316,3 +316,26 @@ ol.tilegrid.TileGrid.prototype.getTileSize = function() {
ol.tilegrid.TileGrid.prototype.getZForResolution = function(resolution) {
return ol.array.linearFindNearest(this.resolutions_, resolution);
};
/**
* @param {ol.Projection} projection Projection.
* @param {number=} opt_maxZoom Maximum zoom level (optional). Default is 18.
* @return {ol.tilegrid.TileGrid} TileGrid instance.
*/
ol.tilegrid.createForProjection = function(projection, opt_maxZoom) {
var projectionExtent = projection.getExtent();
var size = Math.max(
projectionExtent.maxX - projectionExtent.minX,
projectionExtent.maxY - projectionExtent.minY);
var maxZoom = goog.isDef(opt_maxZoom) ?
opt_maxZoom : 18;
var resolutions = new Array(maxZoom + 1);
for (var z = 0, zz = resolutions.length; z < zz; ++z) {
resolutions[z] = size / (256 << z);
}
return new ol.tilegrid.TileGrid({
origin: projectionExtent.getTopLeft(),
resolutions: resolutions
});
};

120
src/ol/tilequeue.js Normal file
View File

@@ -0,0 +1,120 @@
goog.provide('ol.TilePriorityFunction');
goog.provide('ol.TileQueue');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.structs.PriorityQueue');
goog.require('ol.Coordinate');
goog.require('ol.Tile');
goog.require('ol.TileState');
/**
* @typedef {function(ol.Tile, ol.Coordinate, number): (number|undefined)}
*/
ol.TilePriorityFunction;
/**
* @constructor
* @param {ol.TilePriorityFunction} tilePriorityFunction
* Tile priority function.
*/
ol.TileQueue = function(tilePriorityFunction) {
/**
* @private
* @type {ol.TilePriorityFunction}
*/
this.tilePriorityFunction_ = tilePriorityFunction;
/**
* @private
* @type {number}
*/
this.maxTilesLoading_ = 8;
/**
* @private
* @type {number}
*/
this.tilesLoading_ = 0;
/**
* @private
* @type {goog.structs.PriorityQueue}
*/
this.queue_ = new goog.structs.PriorityQueue();
/**
* @private
* @type {Object.<string, boolean>}
*/
this.queuedTileKeys_ = {};
};
/**
* @param {ol.Tile} tile Tile.
* @param {ol.Coordinate} tileCenter Tile center.
* @param {number} tileResolution Tile resolution.
*/
ol.TileQueue.prototype.enqueue =
function(tile, tileCenter, tileResolution) {
if (tile.getState() != ol.TileState.IDLE) {
return;
}
var tileKey = tile.getKey();
if (!(tileKey in this.queuedTileKeys_)) {
var priority = this.tilePriorityFunction_(tile, tileCenter, tileResolution);
if (goog.isDef(priority)) {
this.queue_.enqueue(priority, arguments);
this.queuedTileKeys_[tileKey] = true;
} else {
// FIXME fire drop event?
}
}
};
/**
* @protected
*/
ol.TileQueue.prototype.handleTileChange = function() {
--this.tilesLoading_;
};
/**
* FIXME empty description for jsdoc
*/
ol.TileQueue.prototype.loadMoreTiles = function() {
var tile, tileKey;
while (!this.queue_.isEmpty() && this.tilesLoading_ < this.maxTilesLoading_) {
tile = (/** @type {Array} */ (this.queue_.dequeue()))[0];
tileKey = tile.getKey();
delete this.queuedTileKeys_[tileKey];
goog.events.listen(tile, goog.events.EventType.CHANGE,
this.handleTileChange, false, this);
tile.load();
++this.tilesLoading_;
}
};
/**
* FIXME empty description for jsdoc
*/
ol.TileQueue.prototype.reprioritize = function() {
if (!this.queue_.isEmpty()) {
var values = /** @type {Array.<Array>} */ (this.queue_.getValues());
this.queue_.clear();
this.queuedTileKeys_ = {};
var i;
for (i = 0; i < values.length; ++i) {
this.enqueue.apply(this, values[i]);
}
}
};

View File

@@ -28,9 +28,9 @@ ol.TileRange.boundingTileRange = function(var_args) {
var tileCoord0 = arguments[0];
var tileRange = new ol.TileRange(tileCoord0.x, tileCoord0.y,
tileCoord0.x, tileCoord0.y);
var i;
var i, tileCoord;
for (i = 1; i < arguments.length; ++i) {
var tileCoord = arguments[i];
tileCoord = arguments[i];
goog.asserts.assert(tileCoord.z == tileCoord0.z);
tileRange.minX = Math.min(tileRange.minX, tileCoord.x);
tileRange.minY = Math.min(tileRange.minY, tileCoord.y);
@@ -71,22 +71,6 @@ ol.TileRange.prototype.equals = function(tileRange) {
};
/**
* @param {number} z Z.
* @param {function(this: T, ol.TileCoord)} f Callback.
* @param {T=} opt_obj The object to be used for the value of 'this' within f.
* @template T
*/
ol.TileRange.prototype.forEachTileCoord = function(z, f, opt_obj) {
var x, y;
for (x = this.minX; x <= this.maxX; ++x) {
for (y = this.minY; y <= this.maxY; ++y) {
f.call(opt_obj, new ol.TileCoord(z, x, y));
}
}
};
/**
* @inheritDoc
* @return {number} Height.

92
src/ol/vec/mat4.js Normal file
View File

@@ -0,0 +1,92 @@
goog.provide('ol.vec.Mat4');
goog.require('goog.vec.Mat4');
/**
* @param {!goog.vec.Mat4.Float32} matrix Matrix.
* @param {number} value Brightness value.
* @return {!goog.vec.Mat4.Float32} Matrix.
*/
ol.vec.Mat4.makeBrightness = function(matrix, value) {
goog.vec.Mat4.makeTranslate(matrix, value, value, value);
return matrix;
};
/**
* @param {!goog.vec.Mat4.Float32} matrix Matrix.
* @param {number} value Contrast value.
* @return {!goog.vec.Mat4.Float32} Matrix.
*/
ol.vec.Mat4.makeContrast = function(matrix, value) {
goog.vec.Mat4.makeScale(matrix, value, value, value);
var translateValue = (-0.5 * value + 0.5);
goog.vec.Mat4.setColumnValues(matrix, 3,
translateValue, translateValue, translateValue, 1);
return matrix;
};
/**
* @param {!goog.vec.Mat4.Float32} matrix Matrix.
* @param {number} value Hue value.
* @return {!goog.vec.Mat4.Float32} Matrix.
*/
ol.vec.Mat4.makeHue = function(matrix, value) {
var cosHue = Math.cos(value);
var sinHue = Math.sin(value);
var v00 = 0.213 + cosHue * 0.787 - sinHue * 0.213;
var v01 = 0.715 - cosHue * 0.715 - sinHue * 0.715;
var v02 = 0.072 - cosHue * 0.072 + sinHue * 0.928;
var v03 = 0;
var v10 = 0.213 - cosHue * 0.213 + sinHue * 0.143;
var v11 = 0.715 + cosHue * 0.285 + sinHue * 0.140;
var v12 = 0.072 - cosHue * 0.072 - sinHue * 0.283;
var v13 = 0;
var v20 = 0.213 - cosHue * 0.213 - sinHue * 0.787;
var v21 = 0.715 - cosHue * 0.715 + sinHue * 0.715;
var v22 = 0.072 + cosHue * 0.928 + sinHue * 0.072;
var v23 = 0;
var v30 = 0;
var v31 = 0;
var v32 = 0;
var v33 = 1;
goog.vec.Mat4.setFromValues(matrix,
v00, v10, v20, v30,
v01, v11, v21, v31,
v02, v12, v22, v32,
v03, v13, v23, v33);
return matrix;
};
/**
* @param {!goog.vec.Mat4.Float32} matrix Matrix.
* @param {number} value Saturation value.
* @return {!goog.vec.Mat4.Float32} Matrix.
*/
ol.vec.Mat4.makeSaturation = function(matrix, value) {
var v00 = 0.213 + 0.787 * value;
var v01 = 0.715 - 0.715 * value;
var v02 = 0.072 - 0.072 * value;
var v03 = 0;
var v10 = 0.213 - 0.213 * value;
var v11 = 0.715 + 0.285 * value;
var v12 = 0.072 - 0.072 * value;
var v13 = 0;
var v20 = 0.213 - 0.213 * value;
var v21 = 0.715 - 0.715 * value;
var v22 = 0.072 + 0.928 * value;
var v23 = 0;
var v30 = 0;
var v31 = 0;
var v32 = 0;
var v33 = 1;
goog.vec.Mat4.setFromValues(matrix,
v00, v10, v20, v30,
v01, v11, v21, v31,
v02, v12, v22, v32,
v03, v13, v23, v33);
return matrix;
};

65
src/ol/view.js Normal file
View File

@@ -0,0 +1,65 @@
goog.provide('ol.View');
goog.provide('ol.ViewHint');
goog.require('goog.array');
goog.require('ol.IView');
goog.require('ol.IView2D');
goog.require('ol.IView3D');
/**
* @enum {number}
*/
ol.ViewHint = {
ANIMATING: 0,
PANNING: 1
};
/**
* @constructor
* @implements {ol.IView}
* @extends {ol.Object}
*/
ol.View = function() {
/**
* @private
* @type {Array.<number>}
*/
this.hints_ = [0, 0];
};
goog.inherits(ol.View, ol.Object);
/**
* @return {Array.<number>} Hint.
*/
ol.View.prototype.getHints = function() {
return goog.array.clone(this.hints_);
};
/**
* @inheritDoc
*/
ol.View.prototype.getView2D = goog.abstractMethod;
/**
* @inheritDoc
*/
ol.View.prototype.getView3D = goog.abstractMethod;
/**
* @param {ol.ViewHint} hint Hint.
* @param {number} delta Delta.
*/
ol.View.prototype.setHint = function(hint, delta) {
goog.asserts.assert(0 <= hint && hint < this.hints_.length);
this.hints_[hint] += delta;
goog.asserts.assert(this.hints_[hint] >= 0);
};

344
src/ol/view2d.js Normal file
View File

@@ -0,0 +1,344 @@
// FIXME getView3D has not return type
// FIXME remove getExtent?
goog.provide('ol.View2D');
goog.provide('ol.View2DProperty');
goog.require('ol.Constraints');
goog.require('ol.Extent');
goog.require('ol.IView2D');
goog.require('ol.IView3D');
goog.require('ol.Projection');
goog.require('ol.ResolutionConstraint');
goog.require('ol.RotationConstraint');
goog.require('ol.View');
/**
* @enum {string}
*/
ol.View2DProperty = {
CENTER: 'center',
PROJECTION: 'projection',
RESOLUTION: 'resolution',
ROTATION: 'rotation'
};
/**
* @constructor
* @implements {ol.IView2D}
* @implements {ol.IView3D}
* @extends {ol.View}
* @param {ol.View2DOptions=} opt_view2DOptions View2D options.
*/
ol.View2D = function(opt_view2DOptions) {
goog.base(this);
var view2DOptions = opt_view2DOptions || {};
/**
* @type {Object.<string, *>}
*/
var values = {};
values[ol.View2DProperty.CENTER] = goog.isDef(view2DOptions.center) ?
view2DOptions.center : null;
values[ol.View2DProperty.PROJECTION] = ol.Projection.createProjection(
view2DOptions.projection, 'EPSG:3857');
if (goog.isDef(view2DOptions.resolution)) {
values[ol.View2DProperty.RESOLUTION] = view2DOptions.resolution;
} else if (goog.isDef(view2DOptions.zoom)) {
var projectionExtent = values[ol.View2DProperty.PROJECTION].getExtent();
var size = Math.max(
projectionExtent.maxX - projectionExtent.minX,
projectionExtent.maxY - projectionExtent.minY);
values[ol.View2DProperty.RESOLUTION] = size / (256 << view2DOptions.zoom);
}
values[ol.View2DProperty.ROTATION] = view2DOptions.rotation;
this.setValues(values);
/**
* @private
* @type {ol.Constraints}
*/
this.constraints_ = ol.View2D.createConstraints_(view2DOptions);
};
goog.inherits(ol.View2D, ol.View);
/**
* @inheritDoc
*/
ol.View2D.prototype.getCenter = function() {
return /** @type {ol.Coordinate|undefined} */ (
this.get(ol.View2DProperty.CENTER));
};
goog.exportProperty(
ol.View2D.prototype,
'getCenter',
ol.View2D.prototype.getCenter);
/**
* @param {ol.Size} size Box pixel size.
* @return {ol.Extent} Extent.
*/
ol.View2D.prototype.getExtent = function(size) {
goog.asserts.assert(this.isDef());
var center = this.getCenter();
var resolution = this.getResolution();
var minX = center.x - resolution * size.width / 2;
var minY = center.y - resolution * size.height / 2;
var maxX = center.x + resolution * size.width / 2;
var maxY = center.y + resolution * size.height / 2;
return new ol.Extent(minX, minY, maxX, maxY);
};
/**
* @inheritDoc
*/
ol.View2D.prototype.getProjection = function() {
return /** @type {ol.Projection|undefined} */ (
this.get(ol.View2DProperty.PROJECTION));
};
goog.exportProperty(
ol.View2D.prototype,
'getProjection',
ol.View2D.prototype.getProjection);
/**
* @inheritDoc
*/
ol.View2D.prototype.getResolution = function() {
return /** @type {number|undefined} */ (
this.get(ol.View2DProperty.RESOLUTION));
};
goog.exportProperty(
ol.View2D.prototype,
'getResolution',
ol.View2D.prototype.getResolution);
/**
* @param {ol.Extent} extent Extent.
* @param {ol.Size} size Box pixel size.
* @return {number} Resolution.
*/
ol.View2D.prototype.getResolutionForExtent = function(extent, size) {
var xResolution = (extent.maxX - extent.minX) / size.width;
var yResolution = (extent.maxY - extent.minY) / size.height;
return Math.max(xResolution, yResolution);
};
/**
* @return {number} Map rotation.
*/
ol.View2D.prototype.getRotation = function() {
return /** @type {number|undefined} */ (
this.get(ol.View2DProperty.ROTATION)) || 0;
};
goog.exportProperty(
ol.View2D.prototype,
'getRotation',
ol.View2D.prototype.getRotation);
/**
* @inheritDoc
*/
ol.View2D.prototype.getView2D = function() {
return this;
};
/**
* @inheritDoc
*/
ol.View2D.prototype.getView2DState = function() {
goog.asserts.assert(this.isDef());
var center = /** @type {ol.Coordinate} */ (this.getCenter());
var projection = /** @type {ol.Projection} */ (this.getProjection());
var resolution = /** @type {number} */ (this.getResolution());
var rotation = /** @type {number} */ (this.getRotation());
return {
center: new ol.Coordinate(center.x, center.y),
projection: projection,
resolution: resolution,
rotation: rotation
};
};
/**
* FIXME return type
*/
ol.View2D.prototype.getView3D = function() {
};
/**
* @param {ol.Extent} extent Extent.
* @param {ol.Size} size Box pixel size.
*/
ol.View2D.prototype.fitExtent = function(extent, size) {
this.setCenter(extent.getCenter());
var resolution = this.getResolutionForExtent(extent, size);
resolution = this.constraints_.resolution(resolution, 0);
this.setResolution(resolution);
};
/**
* @return {boolean} Is defined.
*/
ol.View2D.prototype.isDef = function() {
return goog.isDefAndNotNull(this.getCenter()) &&
goog.isDef(this.getResolution());
};
/**
* @param {ol.Coordinate|undefined} center Center.
*/
ol.View2D.prototype.setCenter = function(center) {
this.set(ol.View2DProperty.CENTER, center);
};
goog.exportProperty(
ol.View2D.prototype,
'setCenter',
ol.View2D.prototype.setCenter);
/**
* @param {ol.Projection|undefined} projection Projection.
*/
ol.View2D.prototype.setProjection = function(projection) {
this.set(ol.View2DProperty.PROJECTION, projection);
};
goog.exportProperty(
ol.View2D.prototype,
'setProjection',
ol.View2D.prototype.setProjection);
/**
* @param {number|undefined} resolution Resolution.
*/
ol.View2D.prototype.setResolution = function(resolution) {
this.set(ol.View2DProperty.RESOLUTION, resolution);
};
goog.exportProperty(
ol.View2D.prototype,
'setResolution',
ol.View2D.prototype.setResolution);
/**
* @param {number|undefined} rotation Rotation.
*/
ol.View2D.prototype.setRotation = function(rotation) {
this.set(ol.View2DProperty.ROTATION, rotation);
};
goog.exportProperty(
ol.View2D.prototype,
'setRotation',
ol.View2D.prototype.setRotation);
/**
* @param {ol.Map} map Map.
* @param {number|undefined} rotation Rotation.
* @param {number} delta Delta.
*/
ol.View2D.prototype.rotate = function(map, rotation, delta) {
rotation = this.constraints_.rotation(rotation, delta);
this.setRotation(rotation);
};
/**
* @private
* @param {ol.Map} map Map.
* @param {number|undefined} resolution Resolution to go to.
* @param {ol.Coordinate=} opt_anchor Anchor coordinate.
*/
ol.View2D.prototype.zoom_ = function(map, resolution, opt_anchor) {
if (goog.isDefAndNotNull(resolution) && goog.isDefAndNotNull(opt_anchor)) {
var anchor = opt_anchor;
var oldCenter = /** @type {!ol.Coordinate} */ (this.getCenter());
var oldResolution = this.getResolution();
var x = anchor.x - resolution * (anchor.x - oldCenter.x) / oldResolution;
var y = anchor.y - resolution * (anchor.y - oldCenter.y) / oldResolution;
var center = new ol.Coordinate(x, y);
map.withFrozenRendering(function() {
this.setCenter(center);
this.setResolution(resolution);
}, this);
} else {
this.setResolution(resolution);
}
};
/**
* @param {ol.Map} map Map.
* @param {number} delta Delta from previous zoom level.
* @param {ol.Coordinate=} opt_anchor Anchor coordinate.
*/
ol.View2D.prototype.zoom = function(map, delta, opt_anchor) {
var resolution = this.constraints_.resolution(this.getResolution(), delta);
this.zoom_(map, resolution, opt_anchor);
};
/**
* @param {ol.Map} map Map.
* @param {number|undefined} resolution Resolution to go to.
* @param {ol.Coordinate=} opt_anchor Anchor coordinate.
*/
ol.View2D.prototype.zoomToResolution = function(map, resolution, opt_anchor) {
resolution = this.constraints_.resolution(resolution, 0);
this.zoom_(map, resolution, opt_anchor);
};
/**
* @private
* @param {ol.View2DOptions} view2DOptions View2D options.
* @return {ol.Constraints} Constraints.
*/
ol.View2D.createConstraints_ = function(view2DOptions) {
var resolutionConstraint;
if (goog.isDef(view2DOptions.resolutions)) {
resolutionConstraint = ol.ResolutionConstraint.createSnapToResolutions(
view2DOptions.resolutions);
} else {
var maxResolution, numZoomLevels, zoomFactor;
if (goog.isDef(view2DOptions.maxResolution) &&
goog.isDef(view2DOptions.numZoomLevels) &&
goog.isDef(view2DOptions.zoomFactor)) {
maxResolution = view2DOptions.maxResolution;
numZoomLevels = view2DOptions.numZoomLevels;
zoomFactor = view2DOptions.zoomFactor;
} else {
var projectionExtent = ol.Projection.createProjection(
view2DOptions.projection, 'EPSG:3857').getExtent();
maxResolution = Math.max(
projectionExtent.maxX - projectionExtent.minX,
projectionExtent.maxY - projectionExtent.minY) / 256;
// number of steps we want between two data resolutions
var numSteps = 4;
numZoomLevels = 29 * numSteps;
zoomFactor = Math.exp(Math.log(2) / numSteps);
}
resolutionConstraint = ol.ResolutionConstraint.createSnapToPower(
zoomFactor, maxResolution, numZoomLevels - 1);
}
// FIXME rotation constraint is not configurable at the moment
var rotationConstraint = ol.RotationConstraint.none;
return new ol.Constraints(resolutionConstraint, rotationConstraint);
};