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

This commit is contained in:
Tim Schaub
2013-01-24 22:21:25 -07:00
49 changed files with 1694 additions and 571 deletions

View File

@@ -78,8 +78,8 @@
} }
} }
.ol-zoom-in { .ol-zoom-in {
border-radius: 4px 4px 0 0; border-radius: 2px 2px 0 0;
} }
.ol-zoom-out { .ol-zoom-out {
border-radius: 0 0 4px 4px; border-radius: 0 0 2px 2px;
} }

View File

@@ -80,7 +80,7 @@
margin-left: -13px; margin-left: -13px;
} }
</style> </style>
<title>Overlay example</title> <title>Anchored elements example</title>
</head> </head>
<body> <body>
<div id="map"> <div id="map">
@@ -89,16 +89,16 @@
<!-- Popup --> <!-- Popup -->
<div class="overlay arrow_box" id="popup"></div> <div class="overlay arrow_box" id="popup"></div>
<div id="text"> <div id="text">
<h1 id="title">Overlay example</h1> <h1 id="title">Anchored elements example</h1>
<div id="shortdesc">Demonstrates Overlays.</div> <div id="shortdesc">Demonstrates anchored elements.</div>
<div id="docs"> <div id="docs">
<p>See the <p>See the
<a href="overlay-and-popup.js" target="_blank">overlay-and-popup.js source</a> <a href="anchored-elements.js" target="_blank">anchored-elements.js source</a>
to see how this is done.</p> to see how this is done.</p>
</div> </div>
</div> </div>
</div> </div>
<div id="tags">overlay, popup, mapquest, openaerial</div> <div id="tags">anchored elements, overlay, popup, mapquest, openaerial</div>
<script src="loader.js?id=overlay-and-popup" type="text/javascript"></script> <script src="loader.js?id=anchored-elements" type="text/javascript"></script>
</body> </body>
</html> </html>

View File

@@ -1,12 +1,12 @@
goog.require('goog.debug.Console'); goog.require('goog.debug.Console');
goog.require('goog.debug.Logger'); goog.require('goog.debug.Logger');
goog.require('goog.debug.Logger.Level'); goog.require('goog.debug.Logger.Level');
goog.require('ol.AnchoredElement');
goog.require('ol.Collection'); goog.require('ol.Collection');
goog.require('ol.Coordinate'); goog.require('ol.Coordinate');
goog.require('ol.Map'); goog.require('ol.Map');
goog.require('ol.RendererHints'); goog.require('ol.RendererHints');
goog.require('ol.View2D'); goog.require('ol.View2D');
goog.require('ol.overlay.Overlay');
goog.require('ol.source.MapQuestOpenAerial'); goog.require('ol.source.MapQuestOpenAerial');
@@ -31,15 +31,15 @@ var map = new ol.Map({
}); });
// Vienna label // Vienna label
var vienna = new ol.overlay.Overlay({ var vienna = new ol.AnchoredElement({
map: map, map: map,
coordinate: ol.Projection.transformWithCodes( position: ol.Projection.transformWithCodes(
new ol.Coordinate(16.3725, 48.208889), 'EPSG:4326', 'EPSG:3857'), new ol.Coordinate(16.3725, 48.208889), 'EPSG:4326', 'EPSG:3857'),
element: document.getElementById('vienna') element: document.getElementById('vienna')
}); });
// Popup showing the position the user clicked // Popup showing the position the user clicked
var popup = new ol.overlay.Overlay({ var popup = new ol.AnchoredElement({
map: map, map: map,
element: document.getElementById('popup') element: document.getElementById('popup')
}); });
@@ -49,5 +49,5 @@ map.addEventListener('click', function(evt) {
'Welcome to ol3. The location you clicked was<br>' + 'Welcome to ol3. The location you clicked was<br>' +
ol.Coordinate.toStringHDMS(ol.Projection.transformWithCodes( ol.Coordinate.toStringHDMS(ol.Projection.transformWithCodes(
coordinate, 'EPSG:3857', 'EPSG:4326')); coordinate, 'EPSG:3857', 'EPSG:4326'));
popup.setCoordinate(coordinate); popup.setPosition(coordinate);
}); });

View File

@@ -45,7 +45,7 @@
</tr> </tr>
<tr> <tr>
<td>Rotate:</td> <td>Rotate:</td>
<td><code>Alt</code>+drag, <code>r</code> to reset</td> <td><code>Alt+Shift</code>+drag, <code>r</code> to reset</td>
</tr> </tr>
<tr> <tr>
<td>Brightness/contrast:</td> <td>Brightness/contrast:</td>

View File

@@ -107,25 +107,41 @@ keyboardInteraction.addCallback('H', function() {
layer.setHue(layer.getHue() + (Math.PI / 5)); layer.setHue(layer.getHue() + (Math.PI / 5));
}); });
keyboardInteraction.addCallback('j', function() { keyboardInteraction.addCallback('j', function() {
var bounce = ol.animation.createBounce(2 * view.getResolution()); var bounce = ol.animation.createBounce({
resolution: 2 * view.getResolution()
});
domMap.addPreRenderFunction(bounce); domMap.addPreRenderFunction(bounce);
webglMap.addPreRenderFunction(bounce); webglMap.addPreRenderFunction(bounce);
canvasMap.addPreRenderFunction(bounce); canvasMap.addPreRenderFunction(bounce);
}); });
keyboardInteraction.addCallback('l', function() { keyboardInteraction.addCallback('l', function() {
var panFrom = ol.animation.createPanFrom(view.getCenter()); var panFrom = ol.animation.createPanFrom({
source: view.getCenter(),
easing: ol.easing.elastic
});
domMap.addPreRenderFunction(panFrom); domMap.addPreRenderFunction(panFrom);
webglMap.addPreRenderFunction(panFrom); webglMap.addPreRenderFunction(panFrom);
canvasMap.addPreRenderFunction(panFrom); canvasMap.addPreRenderFunction(panFrom);
view.setCenter(LONDON); view.setCenter(LONDON);
}); });
keyboardInteraction.addCallback('L', function() { keyboardInteraction.addCallback('L', function() {
var start = Date.now(); var start = goog.now();
var duration = 5000; var duration = 5000;
var bounce = ol.animation.createBounce( var bounce = ol.animation.createBounce({
2 * view.getResolution(), duration, start); resolution: 2 * view.getResolution(),
var panFrom = ol.animation.createPanFrom(view.getCenter(), duration, start); start: start,
var spin = ol.animation.createSpin(duration, 2, start); duration: duration
});
var panFrom = ol.animation.createPanFrom({
source: view.getCenter(),
start: start,
duration: duration
});
var spin = ol.animation.createSpin({
turns: 2,
start: start,
duration: duration
});
var preRenderFunctions = [bounce, panFrom, spin]; var preRenderFunctions = [bounce, panFrom, spin];
domMap.addPreRenderFunctions(preRenderFunctions); domMap.addPreRenderFunctions(preRenderFunctions);
webglMap.addPreRenderFunctions(preRenderFunctions); webglMap.addPreRenderFunctions(preRenderFunctions);
@@ -133,19 +149,34 @@ keyboardInteraction.addCallback('L', function() {
view.setCenter(LONDON); view.setCenter(LONDON);
}); });
keyboardInteraction.addCallback('m', function() { keyboardInteraction.addCallback('m', function() {
var panFrom = ol.animation.createPanFrom(view.getCenter(), 1000); var panFrom = ol.animation.createPanFrom({
source: view.getCenter(),
duration: 1000,
easing: ol.easing.bounce
});
domMap.addPreRenderFunction(panFrom); domMap.addPreRenderFunction(panFrom);
webglMap.addPreRenderFunction(panFrom); webglMap.addPreRenderFunction(panFrom);
canvasMap.addPreRenderFunction(panFrom); canvasMap.addPreRenderFunction(panFrom);
view.setCenter(MOSCOW); view.setCenter(MOSCOW);
}); });
keyboardInteraction.addCallback('M', function() { keyboardInteraction.addCallback('M', function() {
var start = Date.now(); var start = goog.now();
var duration = 5000; var duration = 5000;
var bounce = ol.animation.createBounce( var bounce = ol.animation.createBounce({
2 * view.getResolution(), duration, start); resolution: 2 * view.getResolution(),
var panFrom = ol.animation.createPanFrom(view.getCenter(), duration, start); start: start,
var spin = ol.animation.createSpin(duration, -2, start); duration: duration
});
var panFrom = ol.animation.createPanFrom({
source: view.getCenter(),
start: start,
duration: duration
});
var spin = ol.animation.createSpin({
turns: -2,
start: start,
duration: duration
});
var preRenderFunctions = [bounce, panFrom, spin]; var preRenderFunctions = [bounce, panFrom, spin];
domMap.addPreRenderFunctions(preRenderFunctions); domMap.addPreRenderFunctions(preRenderFunctions);
webglMap.addPreRenderFunctions(preRenderFunctions); webglMap.addPreRenderFunctions(preRenderFunctions);
@@ -172,13 +203,19 @@ keyboardInteraction.addCallback('vV', function() {
layer.setVisible(!layer.getVisible()); layer.setVisible(!layer.getVisible());
}); });
keyboardInteraction.addCallback('x', function() { keyboardInteraction.addCallback('x', function() {
var spin = ol.animation.createSpin(2000, 2); var spin = ol.animation.createSpin({
turns: 2,
duration: 2000
});
domMap.addPreRenderFunction(spin); domMap.addPreRenderFunction(spin);
webglMap.addPreRenderFunction(spin); webglMap.addPreRenderFunction(spin);
canvasMap.addPreRenderFunction(spin); canvasMap.addPreRenderFunction(spin);
}); });
keyboardInteraction.addCallback('X', function() { keyboardInteraction.addCallback('X', function() {
var spin = ol.animation.createSpin(2000, -2); var spin = ol.animation.createSpin({
turns: -2,
duration: 2000
});
domMap.addPreRenderFunction(spin); domMap.addPreRenderFunction(spin);
webglMap.addPreRenderFunction(spin); webglMap.addPreRenderFunction(spin);
canvasMap.addPreRenderFunction(spin); canvasMap.addPreRenderFunction(spin);

View File

@@ -21,7 +21,7 @@
<tr> <tr>
<th>DOM</th> <th>DOM</th>
<th>WebGL</th> <th>WebGL</th>
<th>Canvas</th> <th>Canvas <a id="canvas-export" href="#" download="map.jpeg">export map as jpeg</a></th>
</tr> </tr>
<tr> <tr>
<td><div id="domMap" class="map"></div></td> <td><div id="domMap" class="map"></div></td>

View File

@@ -48,3 +48,7 @@ var canvasMap = new ol.Map({
}); });
canvasMap.bindTo('layers', webglMap); canvasMap.bindTo('layers', webglMap);
canvasMap.bindTo('view', webglMap); canvasMap.bindTo('view', webglMap);
goog.events.listen(goog.dom.getElement('canvas-export'), 'click', function(e) {
e.target.href = canvasMap.getRenderer().getCanvas().toDataURL('image/jpeg');
});

View File

@@ -52,9 +52,6 @@ var layers = new ol.Collection([
]); ]);
var map = new ol.Map({ var map = new ol.Map({
// By setting userProjection to the same as projection, we do not need
// proj4js because we do not need any transforms.
userProjection: epsg21781,
layers: layers, layers: layers,
renderers: ol.RendererHints.createFromQueryData(), renderers: ol.RendererHints.createFromQueryData(),
target: 'map', target: 'map',

View File

@@ -2,13 +2,18 @@
[![Travis CI Status](https://secure.travis-ci.org/openlayers/ol3.png)](http://travis-ci.org/#!/openlayers/ol3) [![Travis CI Status](https://secure.travis-ci.org/openlayers/ol3.png)](http://travis-ci.org/#!/openlayers/ol3)
## Build it
Run make: ## Hosted Examples
The examples are hosted on GitHub (as GitHub pages). See http://openlayers.github.com/ol3/master/examples/.
## Build OpenLayers 3
Run build.py:
$ ./build.py $ ./build.py
## Run the examples in debug mode ## Run examples locally
Run the [Plovr](http://plovr.com/) web server with: Run the [Plovr](http://plovr.com/) web server with:

View File

@@ -1,3 +1,55 @@
@exportObjectLiteral ol.MapOptions
@exportObjectLiteralProperty ol.MapOptions.controls ol.Collection|undefined
@exportObjectLiteralProperty ol.MapOptions.doubleClickZoom boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.dragPan boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.interactions ol.Collection|undefined
@exportObjectLiteralProperty ol.MapOptions.keyboard boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.keyboardPanOffset number|undefined
@exportObjectLiteralProperty ol.MapOptions.layers ol.Collection|undefined
@exportObjectLiteralProperty ol.MapOptions.mouseWheelZoom boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.mouseWheelZoomDelta number|undefined
@exportObjectLiteralProperty ol.MapOptions.renderer ol.RendererHint|undefined
@exportObjectLiteralProperty ol.MapOptions.renderers Array.<ol.RendererHint>|undefined
@exportObjectLiteralProperty ol.MapOptions.shiftDragZoom boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.target Element|string
@exportObjectLiteralProperty ol.MapOptions.view ol.IView|undefined
@exportObjectLiteralProperty ol.MapOptions.zoomDelta number|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
@exportObjectLiteral ol.animation.BounceOptions
@exportObjectLiteralProperty ol.animation.BounceOptions.resolution number
@exportObjectLiteralProperty ol.animation.BounceOptions.start number|undefined
@exportObjectLiteralProperty ol.animation.BounceOptions.duration number|undefined
@exportObjectLiteralProperty ol.animation.BounceOptions.easing function(number):number|undefined
@exportObjectLiteral ol.animation.PanFromOptions
@exportObjectLiteralProperty ol.animation.PanFromOptions.source ol.Coordinate
@exportObjectLiteralProperty ol.animation.PanFromOptions.start number|undefined
@exportObjectLiteralProperty ol.animation.PanFromOptions.duration number|undefined
@exportObjectLiteralProperty ol.animation.PanFromOptions.easing function(number):number|undefined
@exportObjectLiteral ol.animation.SpinOptions
@exportObjectLiteralProperty ol.animation.SpinOptions.turns number
@exportObjectLiteralProperty ol.animation.SpinOptions.start number|undefined
@exportObjectLiteralProperty ol.animation.SpinOptions.duration number|undefined
@exportObjectLiteralProperty ol.animation.SpinOptions.easing function(number):number|undefined
@exportObjectLiteral ol.animation.ZoomFromOptions
@exportObjectLiteralProperty ol.animation.ZoomFromOptions.resolution number
@exportObjectLiteralProperty ol.animation.ZoomFromOptions.start number|undefined
@exportObjectLiteralProperty ol.animation.ZoomFromOptions.duration number|undefined
@exportObjectLiteralProperty ol.animation.ZoomFromOptions.easing function(number):number|undefined
@exportObjectLiteral ol.control.AttributionOptions @exportObjectLiteral ol.control.AttributionOptions
@exportObjectLiteralProperty ol.control.AttributionOptions.map ol.Map|undefined @exportObjectLiteralProperty ol.control.AttributionOptions.map ol.Map|undefined
@exportObjectLiteralProperty ol.control.AttributionOptions.target Element|undefined @exportObjectLiteralProperty ol.control.AttributionOptions.target Element|undefined
@@ -23,34 +75,22 @@
@exportObjectLiteralProperty ol.layer.LayerOptions.source ol.source.Source @exportObjectLiteralProperty ol.layer.LayerOptions.source ol.source.Source
@exportObjectLiteralProperty ol.layer.LayerOptions.visible boolean|undefined @exportObjectLiteralProperty ol.layer.LayerOptions.visible boolean|undefined
@exportObjectLiteral ol.MapOptions @exportObjectLiteral ol.AnchoredElementOptions
@exportObjectLiteralProperty ol.MapOptions.controls ol.Collection|undefined @exportObjectLiteralProperty ol.AnchoredElementOptions.element Element|undefined
@exportObjectLiteralProperty ol.MapOptions.doubleClickZoom boolean|undefined @exportObjectLiteralProperty ol.AnchoredElementOptions.map ol.Map|undefined
@exportObjectLiteralProperty ol.MapOptions.dragPan boolean|undefined @exportObjectLiteralProperty ol.AnchoredElementOptions.position ol.Coordinate|undefined
@exportObjectLiteralProperty ol.MapOptions.interactions ol.Collection|undefined @exportObjectLiteralProperty ol.AnchoredElementOptions.positioning ol.AnchoredElementPositioning|undefined
@exportObjectLiteralProperty ol.MapOptions.keyboard boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.keyboardPanOffset number|undefined
@exportObjectLiteralProperty ol.MapOptions.layers ol.Collection|undefined
@exportObjectLiteralProperty ol.MapOptions.mouseWheelZoom boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.mouseWheelZoomDelta number|undefined
@exportObjectLiteralProperty ol.MapOptions.renderer ol.RendererHint|undefined
@exportObjectLiteralProperty ol.MapOptions.renderers Array.<ol.RendererHint>|undefined
@exportObjectLiteralProperty ol.MapOptions.shiftDragZoom boolean|undefined
@exportObjectLiteralProperty ol.MapOptions.target Element|string
@exportObjectLiteralProperty ol.MapOptions.view ol.IView|undefined
@exportObjectLiteralProperty ol.MapOptions.zoomDelta number|undefined
@exportObjectLiteral ol.overlay.OverlayOptions
@exportObjectLiteralProperty ol.overlay.OverlayOptions.coordinate ol.Coordinate|undefined
@exportObjectLiteralProperty ol.overlay.OverlayOptions.element Element|undefined
@exportObjectLiteralProperty ol.overlay.OverlayOptions.map ol.Map|undefined
@exportObjectLiteralProperty ol.overlay.OverlayOptions.positioning Array.<string>|undefined
@exportObjectLiteral ol.source.BingMapsOptions @exportObjectLiteral ol.source.BingMapsOptions
@exportObjectLiteralProperty ol.source.BingMapsOptions.culture string|undefined @exportObjectLiteralProperty ol.source.BingMapsOptions.culture string|undefined
@exportObjectLiteralProperty ol.source.BingMapsOptions.key string @exportObjectLiteralProperty ol.source.BingMapsOptions.key string
@exportObjectLiteralProperty ol.source.BingMapsOptions.style ol.BingMapsStyle @exportObjectLiteralProperty ol.source.BingMapsOptions.style ol.BingMapsStyle
@exportObjectLiteral ol.source.DebugTileSourceOptions
@exportObjectLiteralProperty ol.source.DebugTileSourceOptions.extent ol.Extent|undefined
@exportObjectLiteralProperty ol.source.DebugTileSourceOptions.projection ol.Projection|undefined
@exportObjectLiteralProperty ol.source.DebugTileSourceOptions.tileGrid ol.tilegrid.TileGrid|undefined
@exportObjectLiteral ol.source.StamenOptions @exportObjectLiteral ol.source.StamenOptions
@exportObjectLiteralProperty ol.source.StamenOptions.flavor string|undefined @exportObjectLiteralProperty ol.source.StamenOptions.flavor string|undefined
@exportObjectLiteralProperty ol.source.StamenOptions.provider string @exportObjectLiteralProperty ol.source.StamenOptions.provider string
@@ -76,14 +116,3 @@
@exportObjectLiteral ol.tilegrid.XYZOptions @exportObjectLiteral ol.tilegrid.XYZOptions
@exportObjectLiteralProperty ol.tilegrid.XYZOptions.maxZoom number @exportObjectLiteralProperty ol.tilegrid.XYZOptions.maxZoom number
@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

View File

@@ -0,0 +1,7 @@
@exportClass ol.AnchoredElement ol.AnchoredElementOptions
@exportSymbol ol.AnchoredElementPositioning
@exportProperty ol.AnchoredElementPositioning.BOTTOM_LEFT
@exportProperty ol.AnchoredElementPositioning.BOTTOM_RIGHT
@exportProperty ol.AnchoredElementPositioning.TOP_LEFT
@exportProperty ol.AnchoredElementPositioning.TOP_RIGHT

318
src/ol/anchoredelement.js Normal file
View File

@@ -0,0 +1,318 @@
goog.provide('ol.AnchoredElement');
goog.provide('ol.AnchoredElementPositioning');
goog.provide('ol.AnchoredElementProperty');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.style');
goog.require('ol.Coordinate');
goog.require('ol.Map');
goog.require('ol.MapEventType');
goog.require('ol.Object');
/**
* @enum {string}
*/
ol.AnchoredElementProperty = {
ELEMENT: 'element',
MAP: 'map',
POSITION: 'position',
POSITIONING: 'positioning'
};
/**
* @enum {string}
*/
ol.AnchoredElementPositioning = {
BOTTOM_LEFT: 'bottom-left',
BOTTOM_RIGHT: 'bottom-right',
TOP_LEFT: 'top-left',
TOP_RIGHT: 'top-right'
};
/**
* @constructor
* @extends {ol.Object}
* @param {ol.AnchoredElementOptions} anchoredElementOptions Anchored element
* options.
*/
ol.AnchoredElement = function(anchoredElementOptions) {
goog.base(this);
/**
* @private
* @type {Element}
*/
this.element_ = goog.dom.createElement(goog.dom.TagName.DIV);
this.element_.style.position = 'absolute';
/**
* @private
* @type {{bottom_: string,
* left_: string,
* right_: string,
* top_: string,
* visible: boolean}}
*/
this.rendered_ = {
bottom_: '',
left_: '',
right_: '',
top_: '',
visible: true
};
goog.events.listen(
this, ol.Object.getChangedEventType(ol.AnchoredElementProperty.ELEMENT),
this.handleElementChanged, false, this);
goog.events.listen(
this, ol.Object.getChangedEventType(ol.AnchoredElementProperty.MAP),
this.handleMapChanged, false, this);
goog.events.listen(
this, ol.Object.getChangedEventType(ol.AnchoredElementProperty.POSITION),
this.handlePositionChanged, false, this);
goog.events.listen(
this,
ol.Object.getChangedEventType(ol.AnchoredElementProperty.POSITIONING),
this.handlePositioningChanged, false, this);
if (goog.isDef(anchoredElementOptions.element)) {
this.setElement(anchoredElementOptions.element);
}
if (goog.isDef(anchoredElementOptions.position)) {
this.setPosition(anchoredElementOptions.position);
}
if (goog.isDef(anchoredElementOptions.positioning)) {
this.setPositioning(anchoredElementOptions.positioning);
}
if (goog.isDef(anchoredElementOptions.map)) {
this.setMap(anchoredElementOptions.map);
}
};
goog.inherits(ol.AnchoredElement, ol.Object);
/**
* @return {Element|undefined} Element.
*/
ol.AnchoredElement.prototype.getElement = function() {
return /** @type {Element|undefined} */ (
this.get(ol.AnchoredElementProperty.ELEMENT));
};
goog.exportProperty(
ol.AnchoredElement.prototype,
'getElement',
ol.AnchoredElement.prototype.getElement);
/**
* @return {ol.Map|undefined} Map.
*/
ol.AnchoredElement.prototype.getMap = function() {
return /** @type {ol.Map|undefined} */ (
this.get(ol.AnchoredElementProperty.MAP));
};
goog.exportProperty(
ol.AnchoredElement.prototype,
'getMap',
ol.AnchoredElement.prototype.getMap);
/**
* @return {ol.Coordinate|undefined} Position.
*/
ol.AnchoredElement.prototype.getPosition = function() {
return /** @type {ol.Coordinate|undefined} */ (
this.get(ol.AnchoredElementProperty.POSITION));
};
goog.exportProperty(
ol.AnchoredElement.prototype,
'getPosition',
ol.AnchoredElement.prototype.getPosition);
/**
* @return {ol.AnchoredElementPositioning|undefined} Positioning.
*/
ol.AnchoredElement.prototype.getPositioning = function() {
return /** @type {ol.AnchoredElementPositioning|undefined} */ (
this.get(ol.AnchoredElementProperty.POSITIONING));
};
goog.exportProperty(
ol.AnchoredElement.prototype,
'getPositioning',
ol.AnchoredElement.prototype.getPositioning);
/**
* @protected
*/
ol.AnchoredElement.prototype.handleElementChanged = function() {
goog.dom.removeChildren(this.element_);
var element = this.getElement();
if (goog.isDefAndNotNull(element)) {
goog.dom.append(/** @type {!Node} */ (this.element_), element);
}
};
/**
* @protected
*/
ol.AnchoredElement.prototype.handleMapChanged = function() {
if (!goog.isNull(this.mapPostrenderListenerKey_)) {
goog.dom.removeNode(this.element_);
goog.events.unlistenByKey(this.mapPostrenderListenerKey_);
this.mapPostrenderListenerKey_ = null;
}
var map = this.getMap();
if (goog.isDefAndNotNull(map)) {
this.mapPostrenderListenerKey_ = goog.events.listen(map,
ol.MapEventType.POSTRENDER, this.handleMapPostrender, false, this);
this.updatePixelPosition_();
goog.dom.append(
/** @type {!Node} */ (map.getOverlayContainer()), this.element_);
}
};
/**
* @protected
*/
ol.AnchoredElement.prototype.handleMapPostrender = function() {
this.updatePixelPosition_();
};
/**
* @protected
*/
ol.AnchoredElement.prototype.handlePositionChanged = function() {
this.updatePixelPosition_();
};
/**
* @protected
*/
ol.AnchoredElement.prototype.handlePositioningChanged = function() {
this.updatePixelPosition_();
};
/**
* @param {Element|undefined} element Element.
*/
ol.AnchoredElement.prototype.setElement = function(element) {
this.set(ol.AnchoredElementProperty.ELEMENT, element);
};
goog.exportProperty(
ol.AnchoredElement.prototype,
'setElement',
ol.AnchoredElement.prototype.setElement);
/**
* @param {ol.Map|undefined} map Map.
*/
ol.AnchoredElement.prototype.setMap = function(map) {
this.set(ol.AnchoredElementProperty.MAP, map);
};
goog.exportProperty(
ol.AnchoredElement.prototype,
'setMap',
ol.AnchoredElement.prototype.setMap);
/**
* @param {ol.Coordinate|undefined} position Position.
*/
ol.AnchoredElement.prototype.setPosition = function(position) {
this.set(ol.AnchoredElementProperty.POSITION, position);
};
goog.exportProperty(
ol.AnchoredElement.prototype,
'setPosition',
ol.AnchoredElement.prototype.setPosition);
/**
* @param {ol.AnchoredElementPositioning|undefined} positioning Positioning.
*/
ol.AnchoredElement.prototype.setPositioning = function(positioning) {
this.set(ol.AnchoredElementProperty.POSITIONING, positioning);
};
/**
* @private
*/
ol.AnchoredElement.prototype.updatePixelPosition_ = function() {
var map = this.getMap();
var position = this.getPosition();
if (!goog.isDef(map) || !map.isDef() || !goog.isDef(position)) {
if (this.rendered_.visible) {
goog.style.showElement(this.element_, false);
this.rendered_.visible = false;
}
return;
}
var pixel = map.getPixelFromCoordinate(position);
var mapSize = map.getSize();
goog.asserts.assert(goog.isDef(mapSize));
var style = this.element_.style;
var positioning = this.getPositioning();
if (positioning == ol.AnchoredElementPositioning.BOTTOM_RIGHT ||
positioning == ol.AnchoredElementPositioning.TOP_RIGHT) {
if (this.rendered_.left_ !== '') {
this.rendered_.left_ = style.left = '';
}
var right = Math.round(mapSize.width - pixel.x) + 'px';
if (this.rendered_.right_ != right) {
this.rendered_.right_ = style.right = right;
}
} else {
if (this.rendered_.right_ !== '') {
this.rendered_.right_ = style.right = '';
}
var left = Math.round(pixel.x) + 'px';
if (this.rendered_.left_ != left) {
this.rendered_.left_ = style.left = left;
}
}
if (positioning == ol.AnchoredElementPositioning.TOP_LEFT ||
positioning == ol.AnchoredElementPositioning.TOP_RIGHT) {
if (this.rendered_.bottom_ !== '') {
this.rendered_.bottom_ = style.bottom = '';
}
var top = Math.round(pixel.y) + 'px';
if (this.rendered_.top_ != top) {
this.rendered_.top_ = style.top = top;
}
} else {
if (this.rendered_.top_ !== '') {
this.rendered_.top_ = style.top = '';
}
var bottom = Math.round(mapSize.height - pixel.y) + 'px';
if (this.rendered_.bottom_ != bottom) {
this.rendered_.bottom_ = style.bottom = bottom;
}
}
if (!this.rendered_.visible) {
goog.style.showElement(this.element_, true);
this.rendered_.visible = true;
}
};

View File

@@ -9,25 +9,22 @@ goog.require('ol.easing');
/** /**
* @param {number} resolution Resolution. * @param {ol.animation.BounceOptions} options Options.
* @param {number=} opt_duration Duration.
* @param {number=} opt_start Start.
* @param {function(number): number=} opt_easingFunction Easing function.
* @return {ol.PreRenderFunction} Pre-render function. * @return {ol.PreRenderFunction} Pre-render function.
*/ */
ol.animation.createBounce = ol.animation.createBounce = function(options) {
function(resolution, opt_duration, opt_start, opt_easingFunction) { var resolution = options.resolution;
var start = goog.isDef(opt_start) ? opt_start : Date.now(); var start = goog.isDef(options.start) ? options.start : goog.now();
var duration = goog.isDef(opt_duration) ? opt_duration : 1000; var duration = goog.isDef(options.duration) ? options.duration : 1000;
var easingFunction = goog.isDef(opt_easingFunction) ? var easing = goog.isDef(options.easing) ?
opt_easingFunction : ol.easing.upAndDown; options.easing : ol.easing.upAndDown;
return function(map, frameState) { return function(map, frameState) {
if (frameState.time < start) { if (frameState.time < start) {
frameState.animate = true; frameState.animate = true;
frameState.viewHints[ol.ViewHint.ANIMATING] += 1; frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
return true; return true;
} else if (frameState.time < start + duration) { } else if (frameState.time < start + duration) {
var delta = easingFunction((frameState.time - start) / duration); var delta = easing((frameState.time - start) / duration);
var deltaResolution = resolution - frameState.view2DState.resolution; var deltaResolution = resolution - frameState.view2DState.resolution;
frameState.animate = true; frameState.animate = true;
frameState.view2DState.resolution += delta * deltaResolution; frameState.view2DState.resolution += delta * deltaResolution;
@@ -41,27 +38,24 @@ ol.animation.createBounce =
/** /**
* @param {ol.Coordinate} source Source. * @param {ol.animation.PanFromOptions} options Options.
* @param {number=} opt_duration Duration.
* @param {number=} opt_start Start.
* @param {function(number): number=} opt_easingFunction Easing function.
* @return {ol.PreRenderFunction} Pre-render function. * @return {ol.PreRenderFunction} Pre-render function.
*/ */
ol.animation.createPanFrom = ol.animation.createPanFrom = function(options) {
function(source, opt_duration, opt_start, opt_easingFunction) { var source = options.source;
var start = goog.isDef(opt_start) ? opt_start : Date.now(); var start = goog.isDef(options.start) ? options.start : goog.now();
var sourceX = source.x; var sourceX = source.x;
var sourceY = source.y; var sourceY = source.y;
var duration = goog.isDef(opt_duration) ? opt_duration : 1000; var duration = goog.isDef(options.duration) ? options.duration : 1000;
var easingFunction = goog.isDef(opt_easingFunction) ? var easing = goog.isDef(options.easing) ?
opt_easingFunction : goog.fx.easing.inAndOut; options.easing : goog.fx.easing.inAndOut;
return function(map, frameState) { return function(map, frameState) {
if (frameState.time < start) { if (frameState.time < start) {
frameState.animate = true; frameState.animate = true;
frameState.viewHints[ol.ViewHint.ANIMATING] += 1; frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
return true; return true;
} else if (frameState.time < start + duration) { } else if (frameState.time < start + duration) {
var delta = 1 - easingFunction((frameState.time - start) / duration); var delta = 1 - easing((frameState.time - start) / duration);
var deltaX = sourceX - frameState.view2DState.center.x; var deltaX = sourceX - frameState.view2DState.center.x;
var deltaY = sourceY - frameState.view2DState.center.y; var deltaY = sourceY - frameState.view2DState.center.y;
frameState.animate = true; frameState.animate = true;
@@ -77,27 +71,23 @@ ol.animation.createPanFrom =
/** /**
* @param {number=} opt_duration Duration. * @param {ol.animation.SpinOptions} options Options.
* @param {number=} opt_turns Turns.
* @param {number=} opt_start Start.
* @param {function(number): number=} opt_easingFunction Easing function.
* @return {ol.PreRenderFunction} Pre-render function. * @return {ol.PreRenderFunction} Pre-render function.
*/ */
ol.animation.createSpin = ol.animation.createSpin = function(options) {
function(opt_duration, opt_turns, opt_start, opt_easingFunction) { var start = goog.isDef(options.start) ? options.start : goog.now();
var start = goog.isDef(opt_start) ? opt_start : Date.now(); var duration = goog.isDef(options.duration) ? options.duration : 1000;
var duration = goog.isDef(opt_duration) ? opt_duration : 1000; var easing = goog.isDef(options.easing) ?
var turns = goog.isDef(opt_turns) ? opt_turns : 1; options.easing : goog.fx.easing.inAndOut;
var deltaTheta = 2 * turns * Math.PI; var deltaTheta = 2 * options.turns * Math.PI;
var easingFunction = goog.isDef(opt_easingFunction) ?
opt_easingFunction : goog.fx.easing.inAndOut;
return function(map, frameState) { return function(map, frameState) {
if (frameState.time < start) { if (frameState.time < start) {
frameState.animate = true; frameState.animate = true;
frameState.viewHints[ol.ViewHint.ANIMATING] += 1; frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
return true; return true;
} else if (frameState.time < start + duration) { } else if (frameState.time < start + duration) {
var delta = easingFunction((frameState.time - start) / duration); var delta = easing((frameState.time - start) / duration);
frameState.animate = true; frameState.animate = true;
frameState.view2DState.rotation += delta * deltaTheta; frameState.view2DState.rotation += delta * deltaTheta;
frameState.viewHints[ol.ViewHint.ANIMATING] += 1; frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
@@ -110,25 +100,22 @@ ol.animation.createSpin =
/** /**
* @param {number} sourceResolution Source resolution. * @param {ol.animation.ZoomFromOptions} options Options.
* @param {number=} opt_duration Duration.
* @param {number=} opt_start Start.
* @param {function(number): number=} opt_easingFunction Easing function.
* @return {ol.PreRenderFunction} Pre-render function. * @return {ol.PreRenderFunction} Pre-render function.
*/ */
ol.animation.createZoomFrom = ol.animation.createZoomFrom = function(options) {
function(sourceResolution, opt_duration, opt_start, opt_easingFunction) { var sourceResolution = options.resolution;
var start = goog.isDef(opt_start) ? opt_start : Date.now(); var start = goog.isDef(options.start) ? options.start : goog.now();
var duration = goog.isDef(opt_duration) ? opt_duration : 1000; var duration = goog.isDef(options.duration) ? options.duration : 1000;
var easingFunction = goog.isDef(opt_easingFunction) ? var easing = goog.isDef(options.easing) ?
opt_easingFunction : ol.easing.linear; options.easing : ol.easing.linear;
return function(map, frameState) { return function(map, frameState) {
if (frameState.time < start) { if (frameState.time < start) {
frameState.animate = true; frameState.animate = true;
frameState.viewHints[ol.ViewHint.ANIMATING] += 1; frameState.viewHints[ol.ViewHint.ANIMATING] += 1;
return true; return true;
} else if (frameState.time < start + duration) { } else if (frameState.time < start + duration) {
var delta = 1 - easingFunction((frameState.time - start) / duration); var delta = 1 - easing((frameState.time - start) / duration);
var deltaResolution = var deltaResolution =
sourceResolution - frameState.view2DState.resolution; sourceResolution - frameState.view2DState.resolution;
frameState.animate = true; frameState.animate = true;

View File

@@ -69,7 +69,7 @@ ol.control.Attribution.prototype.handleMapPostrender = function(mapEvent) {
if (goog.isNull(frameState)) { if (goog.isNull(frameState)) {
this.updateElement_(null); this.updateElement_(null);
} else { } else {
this.updateElement_(frameState.tileUsage); this.updateElement_(frameState.usedTiles);
} }
}; };
@@ -94,12 +94,12 @@ ol.control.Attribution.prototype.setMap = function(map) {
/** /**
* @private * @private
* @param {?Object.<string, Object.<string, ol.TileRange>>} tileUsage Tile * @param {?Object.<string, Object.<string, ol.TileRange>>} usedTiles Used
* usage. * tiles.
*/ */
ol.control.Attribution.prototype.updateElement_ = function(tileUsage) { ol.control.Attribution.prototype.updateElement_ = function(usedTiles) {
if (goog.isNull(tileUsage)) { if (goog.isNull(usedTiles)) {
if (this.renderedVisible_) { if (this.renderedVisible_) {
goog.style.showElement(this.element, false); goog.style.showElement(this.element, false);
this.renderedVisible_ = false; this.renderedVisible_ = false;
@@ -136,14 +136,14 @@ ol.control.Attribution.prototype.updateElement_ = function(tileUsage) {
var attributions = {}; var attributions = {};
var i, tileRanges, tileSource, tileSourceAttribution, var i, tileRanges, tileSource, tileSourceAttribution,
tileSourceAttributionKey, tileSourceAttributions, tileSourceKey, z; tileSourceAttributionKey, tileSourceAttributions, tileSourceKey, z;
for (tileSourceKey in tileUsage) { for (tileSourceKey in usedTiles) {
goog.asserts.assert(tileSourceKey in tileSources); goog.asserts.assert(tileSourceKey in tileSources);
tileSource = tileSources[tileSourceKey]; tileSource = tileSources[tileSourceKey];
tileSourceAttributions = tileSource.getAttributions(); tileSourceAttributions = tileSource.getAttributions();
if (goog.isNull(tileSourceAttributions)) { if (goog.isNull(tileSourceAttributions)) {
continue; continue;
} }
tileRanges = tileUsage[tileSourceKey]; tileRanges = usedTiles[tileSourceKey];
for (i = 0; i < tileSourceAttributions.length; ++i) { for (i = 0; i < tileSourceAttributions.length; ++i) {
tileSourceAttribution = tileSourceAttributions[i]; tileSourceAttribution = tileSourceAttributions[i];
tileSourceAttributionKey = goog.getUid(tileSourceAttribution).toString(); tileSourceAttributionKey = goog.getUid(tileSourceAttribution).toString();

View File

@@ -3,6 +3,7 @@
goog.provide('ol.control.MousePosition'); goog.provide('ol.control.MousePosition');
goog.require('goog.array');
goog.require('goog.dom'); goog.require('goog.dom');
goog.require('goog.events'); goog.require('goog.events');
goog.require('goog.events.EventType'); goog.require('goog.events.EventType');

View File

@@ -21,3 +21,40 @@ ol.easing.upAndDown = function(t) {
return 1 - goog.fx.easing.inAndOut(2 * (t - 0.5)); return 1 - goog.fx.easing.inAndOut(2 * (t - 0.5));
} }
}; };
/**
* from https://raw.github.com/DmitryBaranovskiy/raphael/master/raphael.js
* @param {number} t Input between 0 and 1.
* @return {number} Output between 0 and 1.
*/
ol.easing.elastic = function(t) {
return Math.pow(2, -10 * t) * Math.sin((t - 0.075) * (2 * Math.PI) / 0.3) + 1;
};
/**
* from https://raw.github.com/DmitryBaranovskiy/raphael/master/raphael.js
* @param {number} t Input between 0 and 1.
* @return {number} Output between 0 and 1.
*/
ol.easing.bounce = function(t) {
var s = 7.5625, p = 2.75, l;
if (t < (1 / p)) {
l = s * t * t;
} else {
if (t < (2 / p)) {
t -= (1.5 / p);
l = s * t * t + 0.75;
} else {
if (t < (2.5 / p)) {
t -= (2.25 / p);
l = s * t * t + 0.9375;
} else {
t -= (2.625 / p);
l = s * t * t + 0.984375;
}
}
}
return l;
};

View File

@@ -94,7 +94,8 @@ ol.Extent.prototype.getTopRight = function() {
* @return {ol.Extent} Extent. * @return {ol.Extent} Extent.
*/ */
ol.Extent.prototype.transform = function(transformFn) { ol.Extent.prototype.transform = function(transformFn) {
var min = transformFn(new ol.Coordinate(this.minX, this.minY)); var a = transformFn(new ol.Coordinate(this.minX, this.minY));
var max = transformFn(new ol.Coordinate(this.maxX, this.maxY)); var b = transformFn(new ol.Coordinate(this.maxX, this.maxY));
return new ol.Extent(min.x, min.y, max.x, max.y); return new ol.Extent(Math.min(a.x, b.x), Math.min(a.y, b.y),
Math.max(a.x, b.x), Math.max(a.y, b.y));
}; };

View File

@@ -1,4 +1,5 @@
// FIXME add view3DState // FIXME add view3DState
// FIXME factor out common code between usedTiles and wantedTiles
goog.provide('ol.FrameState'); goog.provide('ol.FrameState');
goog.provide('ol.PostRenderFunction'); goog.provide('ol.PostRenderFunction');
@@ -26,10 +27,11 @@ goog.require('ol.layer.LayerState');
* postRenderFunctions: Array.<ol.PostRenderFunction>, * postRenderFunctions: Array.<ol.PostRenderFunction>,
* size: ol.Size, * size: ol.Size,
* tileQueue: ol.TileQueue, * tileQueue: ol.TileQueue,
* tileUsage: Object.<string, Object.<string, ol.TileRange>>,
* time: number, * time: number,
* usedTiles: Object.<string, Object.<string, ol.TileRange>>,
* view2DState: ol.View2DState, * view2DState: ol.View2DState,
* viewHints: Array.<number>}} * viewHints: Array.<number>,
* wantedTiles: Object.<string, Object.<string, ol.TileRange>>}}
*/ */
ol.FrameState; ol.FrameState;

View File

@@ -20,6 +20,18 @@ ol.interaction.condition.altKeyOnly = function(browserEvent) {
}; };
/**
* @param {goog.events.BrowserEvent} browserEvent Browser event.
* @return {boolean} True if only the alt and shift keys are pressed.
*/
ol.interaction.condition.altShiftKeysOnly = function(browserEvent) {
return (
browserEvent.altKey &&
!browserEvent.platformModifierKey &&
browserEvent.shiftKey);
};
/** /**
* @param {goog.events.BrowserEvent} browserEvent Browser event. * @param {goog.events.BrowserEvent} browserEvent Browser event.
* @return {boolean} True if only the no modifier keys are pressed. * @return {boolean} True if only the no modifier keys are pressed.

View File

@@ -1,4 +1,6 @@
@exportClass ol.Map ol.MapOptions @exportClass ol.Map ol.MapOptions
@exportProperty ol.Map.prototype.addPreRenderFunction
@exportProperty ol.Map.prototype.addPreRenderFunctions
@exportProperty ol.Map.prototype.getControls @exportProperty ol.Map.prototype.getControls
@exportProperty ol.Map.prototype.getInteractions @exportProperty ol.Map.prototype.getInteractions

View File

@@ -8,7 +8,6 @@ goog.provide('ol.RendererHint');
goog.provide('ol.RendererHints'); goog.provide('ol.RendererHints');
goog.require('goog.Uri.QueryData'); goog.require('goog.Uri.QueryData');
goog.require('goog.array');
goog.require('goog.async.AnimationDelay'); goog.require('goog.async.AnimationDelay');
goog.require('goog.debug.Logger'); goog.require('goog.debug.Logger');
goog.require('goog.dispose'); goog.require('goog.dispose');
@@ -175,6 +174,12 @@ ol.Map = function(mapOptions) {
*/ */
this.target_ = mapOptionsInternal.target; this.target_ = mapOptionsInternal.target;
/**
* @private
* @type {?number}
*/
this.viewPropertyListenerKey_ = null;
/** /**
* @private * @private
* @type {Element} * @type {Element}
@@ -278,8 +283,16 @@ ol.Map = function(mapOptions) {
*/ */
this.tileQueue_ = new ol.TileQueue(goog.bind(this.getTilePriority, this)); this.tileQueue_ = new ol.TileQueue(goog.bind(this.getTilePriority, this));
goog.events.listen(this, ol.Object.getChangedEventType(ol.MapProperty.VIEW),
this.handleViewChanged_, false, this);
goog.events.listen(this, ol.Object.getChangedEventType(ol.MapProperty.SIZE),
this.handleSizeChanged_, false, this);
goog.events.listen(
this, ol.Object.getChangedEventType(ol.MapProperty.BACKGROUND_COLOR),
this.handleBackgroundColorChanged_, false, this),
this.setValues(mapOptionsInternal.values); this.setValues(mapOptionsInternal.values);
// this gives the map an initial size
this.handleBrowserWindowResize(); this.handleBrowserWindowResize();
this.controls_.forEach( this.controls_.forEach(
@@ -345,6 +358,14 @@ goog.exportProperty(
ol.Map.prototype.getBackgroundColor); ol.Map.prototype.getBackgroundColor);
/**
* @return {ol.renderer.Map} Renderer.
*/
ol.Map.prototype.getRenderer = function() {
return this.renderer_;
};
/** /**
* @return {Element} Container. * @return {Element} Container.
*/ */
@@ -391,6 +412,10 @@ ol.Map.prototype.getInteractions = function() {
ol.Map.prototype.getLayers = function() { ol.Map.prototype.getLayers = function() {
return /** @type {ol.Collection} */ (this.get(ol.MapProperty.LAYERS)); return /** @type {ol.Collection} */ (this.get(ol.MapProperty.LAYERS));
}; };
goog.exportProperty(
ol.Map.prototype,
'getLayers',
ol.Map.prototype.getLayers);
/** /**
@@ -453,19 +478,24 @@ ol.Map.prototype.getOverlayContainer = function() {
/** /**
* @param {ol.Tile} tile Tile. * @param {ol.Tile} tile Tile.
* @param {string} tileSourceKey Tile source key.
* @param {ol.Coordinate} tileCenter Tile center. * @param {ol.Coordinate} tileCenter Tile center.
* @param {number} tileResolution Tile resolution. * @return {number} Tile priority.
* @return {number|undefined} Tile priority.
*/ */
ol.Map.prototype.getTilePriority = function(tile, tileCenter, tileResolution) { ol.Map.prototype.getTilePriority = function(tile, tileSourceKey, tileCenter) {
if (goog.isNull(this.frameState_)) { var frameState = this.frameState_;
return undefined; if (goog.isNull(frameState) || !(tileSourceKey in frameState.wantedTiles)) {
} else { return ol.TileQueue.DROP;
var center = this.frameState_.view2DState.center; }
var zKey = tile.tileCoord.z.toString();
if (!(zKey in frameState.wantedTiles[tileSourceKey]) ||
!frameState.wantedTiles[tileSourceKey][zKey].contains(tile.tileCoord)) {
return ol.TileQueue.DROP;
}
var center = frameState.view2DState.center;
var deltaX = tileCenter.x - center.x; var deltaX = tileCenter.x - center.x;
var deltaY = tileCenter.y - center.y; var deltaY = tileCenter.y - center.y;
return Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution; return deltaX * deltaX + deltaY * deltaY;
}
}; };
@@ -525,13 +555,20 @@ ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) {
ol.Map.prototype.handlePostRender = function() { ol.Map.prototype.handlePostRender = function() {
this.tileQueue_.reprioritize(); // FIXME only call if needed this.tileQueue_.reprioritize(); // FIXME only call if needed
this.tileQueue_.loadMoreTiles(); this.tileQueue_.loadMoreTiles();
goog.array.forEach( var postRenderFunctions = this.postRenderFunctions_;
this.postRenderFunctions_, var i;
function(postRenderFunction) { for (i = 0; i < postRenderFunctions.length; ++i) {
postRenderFunction(this, this.frameState_); postRenderFunctions[i](this, this.frameState_);
}, }
this); postRenderFunctions.length = 0;
this.postRenderFunctions_.length = 0; };
/**
* @private
*/
ol.Map.prototype.handleBackgroundColorChanged_ = function() {
this.render();
}; };
@@ -544,6 +581,40 @@ ol.Map.prototype.handleBrowserWindowResize = function() {
}; };
/**
* @private
*/
ol.Map.prototype.handleSizeChanged_ = function() {
this.render();
};
/**
* @private
*/
ol.Map.prototype.handleViewPropertyChanged_ = function() {
this.render();
};
/**
* @private
*/
ol.Map.prototype.handleViewChanged_ = function() {
if (!goog.isNull(this.viewPropertyListenerKey_)) {
goog.events.unlistenByKey(this.viewPropertyListenerKey_);
this.viewPropertyListenerKey_ = null;
}
var view = this.getView();
if (goog.isDefAndNotNull(view)) {
this.viewPropertyListenerKey_ = goog.events.listen(
view, ol.ObjectEventType.CHANGED,
this.handleViewPropertyChanged_, false, this);
}
this.render();
};
/** /**
* @return {boolean} Is defined. * @return {boolean} Is defined.
*/ */
@@ -611,9 +682,11 @@ ol.Map.prototype.renderFrame_ = function(time) {
var backgroundColor = this.getBackgroundColor(); var backgroundColor = this.getBackgroundColor();
var viewHints = view.getHints(); var viewHints = view.getHints();
var layerStates = {}; var layerStates = {};
goog.array.forEach(layersArray, function(layer) { var layer;
for (i = 0; i < layersArray.length; ++i) {
layer = layersArray[i];
layerStates[goog.getUid(layer)] = layer.getLayerState(); layerStates[goog.getUid(layer)] = layer.getLayerState();
}); }
var view2DState = view2D.getView2DState(); var view2DState = view2D.getView2DState();
frameState = { frameState = {
animate: false, animate: false,
@@ -627,19 +700,23 @@ ol.Map.prototype.renderFrame_ = function(time) {
postRenderFunctions: [], postRenderFunctions: [],
size: size, size: size,
tileQueue: this.tileQueue_, tileQueue: this.tileQueue_,
tileUsage: {}, time: time,
usedTiles: {},
view2DState: view2DState, view2DState: view2DState,
viewHints: viewHints, viewHints: viewHints,
time: time wantedTiles: {}
}; };
} }
this.preRenderFunctions_ = goog.array.filter( var preRenderFunctions = this.preRenderFunctions_;
this.preRenderFunctions_, var n = 0, preRenderFunction;
function(preRenderFunction) { for (i = 0; i < preRenderFunctions.length; ++i) {
return preRenderFunction(this, frameState); preRenderFunction = preRenderFunctions[i];
}, if (preRenderFunction(this, frameState)) {
this); preRenderFunctions[n++] = preRenderFunction;
}
}
preRenderFunctions.length = n;
if (!goog.isNull(frameState)) { if (!goog.isNull(frameState)) {
// FIXME works for View2D only // FIXME works for View2D only
@@ -664,6 +741,8 @@ ol.Map.prototype.renderFrame_ = function(time) {
} }
this.renderer_.renderFrame(frameState); this.renderer_.renderFrame(frameState);
this.frameState_ = frameState;
this.dirty_ = false;
if (!goog.isNull(frameState)) { if (!goog.isNull(frameState)) {
if (frameState.animate) { if (frameState.animate) {
@@ -672,8 +751,6 @@ ol.Map.prototype.renderFrame_ = function(time) {
Array.prototype.push.apply( Array.prototype.push.apply(
this.postRenderFunctions_, frameState.postRenderFunctions); this.postRenderFunctions_, frameState.postRenderFunctions);
} }
this.frameState_ = frameState;
this.dirty_ = false;
this.dispatchEvent( this.dispatchEvent(
new ol.MapEvent(ol.MapEventType.POSTRENDER, this, frameState)); new ol.MapEvent(ol.MapEventType.POSTRENDER, this, frameState));
@@ -893,8 +970,8 @@ ol.Map.createInteractions_ = function(mapOptions) {
var rotate = goog.isDef(mapOptions.rotate) ? var rotate = goog.isDef(mapOptions.rotate) ?
mapOptions.rotate : true; mapOptions.rotate : true;
if (rotate) { if (rotate) {
interactions.push( interactions.push(new ol.interaction.DragRotate(
new ol.interaction.DragRotate(ol.interaction.condition.altKeyOnly)); ol.interaction.condition.altShiftKeysOnly));
} }
var doubleClickZoom = goog.isDef(mapOptions.doubleClickZoom) ? var doubleClickZoom = goog.isDef(mapOptions.doubleClickZoom) ?

View File

@@ -2,6 +2,7 @@ goog.provide('ol.MapBrowserEvent');
goog.provide('ol.MapBrowserEvent.EventType'); goog.provide('ol.MapBrowserEvent.EventType');
goog.provide('ol.MapBrowserEventHandler'); goog.provide('ol.MapBrowserEventHandler');
goog.require('goog.array');
goog.require('goog.asserts'); goog.require('goog.asserts');
goog.require('goog.events.BrowserEvent'); goog.require('goog.events.BrowserEvent');
goog.require('goog.events.EventTarget'); goog.require('goog.events.EventTarget');

View File

@@ -1,5 +0,0 @@
@exportClass ol.overlay.Overlay ol.overlay.OverlayOptions
@exportProperty ol.overlay.Overlay.prototype.getElement
@exportProperty ol.overlay.Overlay.prototype.setCoordinate
@exportProperty ol.overlay.Overlay.prototype.setMap

View File

@@ -1,210 +0,0 @@
goog.provide('ol.overlay.Overlay');
goog.provide('ol.overlay.OverlayPositioning');
goog.require('goog.events');
goog.require('goog.style');
/**
* @constructor
* @param {ol.overlay.OverlayOptions} overlayOptions Overlay options.
*/
ol.overlay.Overlay = function(overlayOptions) {
/**
* @type {ol.Coordinate}
* @private
*/
this.coordinate_ = null;
/**
* @type {Element}
* @private
*/
this.element_ = null;
/**
* @private
* @type {ol.Map}
*/
this.map_ = null;
/**
* @type {Array.<string>}
* @private
*/
this.positioning_ = [
ol.overlay.OverlayPositioning.LEFT,
ol.overlay.OverlayPositioning.BOTTOM
];
/**
* @private
* @type {Array.<number>}
*/
this.mapListenerKeys_ = null;
/**
* @private
* @type {Array.<number>}
*/
this.viewListenerKeys_ = null;
if (goog.isDef(overlayOptions.coordinate)) {
this.setCoordinate(overlayOptions.coordinate);
}
if (goog.isDef(overlayOptions.element)) {
this.setElement(overlayOptions.element);
}
if (goog.isDef(overlayOptions.map)) {
this.setMap(overlayOptions.map);
}
if (goog.isDef(overlayOptions.positioning)) {
this.setPositioning(overlayOptions.positioning);
}
};
/**
* @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.
*/
ol.overlay.Overlay.prototype.setCoordinate = function(coordinate) {
this.coordinate_ = coordinate;
this.updatePixelPosition_();
};
/**
* @param {Element} element The DOM element for the overlay.
*/
ol.overlay.Overlay.prototype.setElement = function(element) {
if (this.element_) {
goog.dom.removeNode(this.element_);
}
this.element_ = element;
if (this.map_) {
goog.style.setStyle(this.element_, 'position', 'absolute');
goog.dom.append(/** @type {!Node} */ (this.map_.getOverlayContainer()),
this.element_);
}
this.updatePixelPosition_();
};
/**
* @return {Element} The DOM element for the overlay.
*/
ol.overlay.Overlay.prototype.getElement = function() {
return this.element_;
};
/**
* @param {ol.Map} map Map.
*/
ol.overlay.Overlay.prototype.setMap = function(map) {
this.map_ = map;
if (!goog.isNull(this.mapListenerKeys_)) {
goog.array.forEach(this.mapListenerKeys_, goog.events.unlistenByKey);
this.mapListenerKeys_ = null;
}
if (this.element_) {
this.setElement(this.element_);
}
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_();
}
};
/**
* @return {ol.Map} Map.
*/
ol.overlay.Overlay.prototype.getMap = function() {
return this.map_;
};
/**
* Set the CSS properties to use for x- and y-positioning of the element. If
* not set, the default is {@code ['left', 'bottom']}.
* @param {Array.<string>} positioning The positioning.
*/
ol.overlay.Overlay.prototype.setPositioning = function(positioning) {
this.positioning_ = positioning;
this.updatePixelPosition_();
};
/**
* @private
*/
ol.overlay.Overlay.prototype.updatePixelPosition_ = function() {
if (!goog.isNull(this.map_) && !goog.isNull(this.coordinate_) &&
!goog.isNull(this.element_)) {
var pixel = this.map_.getPixelFromCoordinate(this.coordinate_);
var mapSize = this.map_.get(ol.MapProperty.SIZE);
var x = Math.round(pixel.x);
if (this.positioning_[0] === ol.overlay.OverlayPositioning.RIGHT) {
x = mapSize.width - x;
}
var y = Math.round(pixel.y);
if (this.positioning_[1] === ol.overlay.OverlayPositioning.BOTTOM) {
y = mapSize.height - y;
}
goog.style.setStyle(this.element_, this.positioning_[0], x + 'px');
goog.style.setStyle(this.element_, this.positioning_[1], y + 'px');
}
};
/**
* @enum {string}
*/
ol.overlay.OverlayPositioning = {
LEFT: 'left',
RIGHT: 'right',
TOP: 'top',
BOTTOM: 'bottom'
};

View File

@@ -2,6 +2,7 @@
goog.provide('ol.renderer.canvas.Map'); goog.provide('ol.renderer.canvas.Map');
goog.require('goog.array');
goog.require('goog.dom'); goog.require('goog.dom');
goog.require('goog.style'); goog.require('goog.style');
goog.require('goog.vec.Mat4'); goog.require('goog.vec.Mat4');
@@ -73,44 +74,8 @@ ol.renderer.canvas.Map.prototype.createLayerRenderer = function(layer) {
/** /**
* @inheritDoc * @inheritDoc
*/ */
ol.renderer.canvas.Map.prototype.handleBackgroundColorChanged = function() { ol.renderer.canvas.Map.prototype.getCanvas = function() {
this.getMap().render(); return this.canvas_;
};
/**
* @inheritDoc
*/
ol.renderer.canvas.Map.prototype.handleViewPropertyChanged = function() {
goog.base(this, 'handleViewPropertyChanged');
this.getMap().render();
};
/**
* @param {goog.events.Event} event Event.
* @protected
*/
ol.renderer.canvas.Map.prototype.handleLayerRendererChange = function(event) {
this.getMap().render();
};
/**
* @inheritDoc
*/
ol.renderer.canvas.Map.prototype.handleSizeChanged = function() {
goog.base(this, 'handleSizeChanged');
this.getMap().render();
};
/**
* @inheritDoc
*/
ol.renderer.canvas.Map.prototype.handleViewChanged = function() {
goog.base(this, 'handleViewChanged');
this.getMap().render();
}; };

View File

@@ -4,6 +4,7 @@
goog.provide('ol.renderer.canvas.TileLayer'); goog.provide('ol.renderer.canvas.TileLayer');
goog.require('goog.array');
goog.require('goog.dom'); goog.require('goog.dom');
goog.require('goog.style'); goog.require('goog.style');
goog.require('goog.vec.Mat4'); goog.require('goog.vec.Mat4');
@@ -87,6 +88,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame =
var tileLayer = this.getTileLayer(); var tileLayer = this.getTileLayer();
var tileSource = tileLayer.getTileSource(); var tileSource = tileLayer.getTileSource();
var tileSourceKey = goog.getUid(tileSource).toString();
var tileGrid = tileSource.getTileGrid(); var tileGrid = tileSource.getTileGrid();
var tileSize = tileGrid.getTileSize(); var tileSize = tileGrid.getTileSize();
var z = tileGrid.getZForResolution(view2DState.resolution); var z = tileGrid.getZForResolution(view2DState.resolution);
@@ -165,7 +167,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame =
tileState = tile.getState(); tileState = tile.getState();
if (tileState == ol.TileState.IDLE) { if (tileState == ol.TileState.IDLE) {
tileCenter = tileGrid.getTileCoordCenter(tileCoord); tileCenter = tileGrid.getTileCoordCenter(tileCoord);
frameState.tileQueue.enqueue(tile, tileCenter, tileResolution); frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter);
} else if (tileState == ol.TileState.LOADED) { } else if (tileState == ol.TileState.LOADED) {
tilesToDrawByZ[z][tileCoord.toString()] = tile; tilesToDrawByZ[z][tileCoord.toString()] = tile;
continue; continue;
@@ -213,9 +215,11 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame =
if (!allTilesLoaded) { if (!allTilesLoaded) {
frameState.animate = true; frameState.animate = true;
this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange);
} }
this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
this.scheduleExpireCache(frameState, tileSource);
var transform = this.transform_; var transform = this.transform_;
goog.vec.Mat4.makeIdentity(transform); goog.vec.Mat4.makeIdentity(transform);

View File

@@ -1,5 +1,6 @@
goog.provide('ol.renderer.dom.Map'); goog.provide('ol.renderer.dom.Map');
goog.require('goog.array');
goog.require('goog.asserts'); goog.require('goog.asserts');
goog.require('goog.dom'); goog.require('goog.dom');
goog.require('goog.dom.TagName'); goog.require('goog.dom.TagName');

View File

@@ -83,6 +83,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame =
var tileLayer = this.getTileLayer(); var tileLayer = this.getTileLayer();
var tileSource = tileLayer.getTileSource(); var tileSource = tileLayer.getTileSource();
var tileSourceKey = goog.getUid(tileSource).toString();
var tileGrid = tileSource.getTileGrid(); var tileGrid = tileSource.getTileGrid();
var z = tileGrid.getZForResolution(view2DState.resolution); var z = tileGrid.getZForResolution(view2DState.resolution);
var tileResolution = tileGrid.getResolution(z); var tileResolution = tileGrid.getResolution(z);
@@ -132,7 +133,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame =
tileState = tile.getState(); tileState = tile.getState();
if (tileState == ol.TileState.IDLE) { if (tileState == ol.TileState.IDLE) {
tileCenter = tileGrid.getTileCoordCenter(tileCoord); tileCenter = tileGrid.getTileCoordCenter(tileCoord);
frameState.tileQueue.enqueue(tile, tileCenter, tileResolution); frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter);
} else if (tileState == ol.TileState.LOADED) { } else if (tileState == ol.TileState.LOADED) {
tilesToDrawByZ[z][tileCoord.toString()] = tile; tilesToDrawByZ[z][tileCoord.toString()] = tile;
continue; continue;
@@ -234,9 +235,11 @@ ol.renderer.dom.TileLayer.prototype.renderFrame =
if (!allTilesLoaded) { if (!allTilesLoaded) {
frameState.animate = true; frameState.animate = true;
this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange);
} }
this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
this.scheduleExpireCache(frameState, tileSource);
}; };

View File

@@ -2,10 +2,12 @@ goog.provide('ol.renderer.Layer');
goog.require('goog.events'); goog.require('goog.events');
goog.require('goog.events.EventType'); goog.require('goog.events.EventType');
goog.require('ol.FrameState');
goog.require('ol.Object'); goog.require('ol.Object');
goog.require('ol.TileRange'); goog.require('ol.TileRange');
goog.require('ol.layer.Layer'); goog.require('ol.layer.Layer');
goog.require('ol.layer.LayerProperty'); goog.require('ol.layer.LayerProperty');
goog.require('ol.source.TileSource');
@@ -130,24 +132,66 @@ ol.renderer.Layer.prototype.handleLayerVisibleChange = goog.nullFunction;
/** /**
* @protected * @protected
* @param {Object.<string, Object.<string, ol.TileRange>>} tileUsage Tile usage. * @param {ol.FrameState} frameState Frame state.
* @param {ol.source.TileSource} tileSource Tile source.
*/
ol.renderer.Layer.prototype.scheduleExpireCache =
function(frameState, tileSource) {
if (tileSource.canExpireCache()) {
frameState.postRenderFunctions.push(
goog.partial(function(tileSource, map, frameState) {
var tileSourceKey = goog.getUid(tileSource).toString();
tileSource.expireCache(frameState.usedTiles[tileSourceKey]);
}, tileSource));
}
};
/**
* @protected
* @param {Object.<string, Object.<string, ol.TileRange>>} usedTiles Used tiles.
* @param {ol.source.Source} source Source. * @param {ol.source.Source} source Source.
* @param {number} z Z. * @param {number} z Z.
* @param {ol.TileRange} tileRange Tile range. * @param {ol.TileRange} tileRange Tile range.
*/ */
ol.renderer.Layer.prototype.updateTileUsage = ol.renderer.Layer.prototype.updateUsedTiles =
function(tileUsage, source, z, tileRange) { function(usedTiles, source, z, tileRange) {
// FIXME should we use tilesToDrawByZ instead? // FIXME should we use tilesToDrawByZ instead?
var sourceKey = goog.getUid(source).toString(); var sourceKey = goog.getUid(source).toString();
var zKey = z.toString(); var zKey = z.toString();
if (sourceKey in tileUsage) { if (sourceKey in usedTiles) {
if (z in tileUsage[sourceKey]) { if (zKey in usedTiles[sourceKey]) {
tileUsage[sourceKey][zKey].extend(tileRange); usedTiles[sourceKey][zKey].extend(tileRange);
} else { } else {
tileUsage[sourceKey][zKey] = tileRange; usedTiles[sourceKey][zKey] = tileRange;
} }
} else { } else {
tileUsage[sourceKey] = {}; usedTiles[sourceKey] = {};
tileUsage[sourceKey][zKey] = tileRange; usedTiles[sourceKey][zKey] = tileRange;
}
};
/**
* @protected
* @param {Object.<string, Object.<string, ol.TileRange>>} wantedTiles Wanted
* tile ranges.
* @param {ol.source.Source} source Source.
* @param {number} z Z.
* @param {ol.TileRange} tileRange Tile range.
*/
ol.renderer.Layer.prototype.updateWantedTiles =
function(wantedTiles, source, z, tileRange) {
var sourceKey = goog.getUid(source).toString();
var zKey = z.toString();
if (sourceKey in wantedTiles) {
if (zKey in wantedTiles[sourceKey]) {
wantedTiles[sourceKey][zKey].extend(tileRange);
} else {
wantedTiles[sourceKey][zKey] = tileRange;
}
} else {
wantedTiles[sourceKey] = {};
wantedTiles[sourceKey][zKey] = tileRange;
} }
}; };

View File

@@ -1,11 +1,10 @@
goog.provide('ol.renderer.Map'); goog.provide('ol.renderer.Map');
goog.require('goog.Disposable'); goog.require('goog.Disposable');
goog.require('goog.array');
goog.require('goog.asserts'); goog.require('goog.asserts');
goog.require('goog.events'); goog.require('goog.events');
goog.require('goog.functions'); goog.require('goog.functions');
goog.require('goog.fx.anim');
goog.require('goog.fx.anim.Animated');
goog.require('goog.vec.Mat4'); goog.require('goog.vec.Mat4');
goog.require('ol.FrameState'); goog.require('ol.FrameState');
goog.require('ol.View2D'); goog.require('ol.View2D');
@@ -41,40 +40,24 @@ ol.renderer.Map = function(container, map) {
*/ */
this.layerRenderers = {}; this.layerRenderers = {};
/** //
* @private // We listen to layer add/remove to add/remove layer renderers.
* @type {Array.<number>} //
*/
this.layersListenerKeys_ = null;
/** /**
* @private * @private
* @type {?number} * @type {?number}
*/ */
this.viewPropertyListenerKey_ = null; this.mapLayersChangedListenerKey_ =
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.LAYERS),
this.handleLayersChanged, false, this);
/** /**
* @private * @private
* @type {Array.<number>} * @type {Array.<number>}
*/ */
this.mapListenerKeys_ = [ this.layersListenerKeys_ = null;
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.BACKGROUND_COLOR),
this.handleBackgroundColorChanged, 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.SIZE),
this.handleSizeChanged, false, this),
goog.events.listen(
map, ol.Object.getChangedEventType(ol.MapProperty.VIEW),
this.handleViewChanged, false, this)
];
}; };
goog.inherits(ol.renderer.Map, goog.Disposable); goog.inherits(ol.renderer.Map, goog.Disposable);
@@ -137,10 +120,7 @@ ol.renderer.Map.prototype.disposeInternal = function() {
goog.object.forEach(this.layerRenderers, function(layerRenderer) { goog.object.forEach(this.layerRenderers, function(layerRenderer) {
goog.dispose(layerRenderer); goog.dispose(layerRenderer);
}); });
goog.array.forEach(this.mapListenerKeys_, goog.events.unlistenByKey); goog.events.unlistenByKey(this.mapLayersChangedListenerKey_);
if (!goog.isNull(this.viewPropertyListenerKey_)) {
goog.events.unlistenByKey(this.viewPropertyListenerKey_);
}
if (!goog.isNull(this.layersListenerKeys_)) { if (!goog.isNull(this.layersListenerKeys_)) {
goog.array.forEach(this.layersListenerKeys_, goog.events.unlistenByKey); goog.array.forEach(this.layersListenerKeys_, goog.events.unlistenByKey);
} }
@@ -148,6 +128,12 @@ ol.renderer.Map.prototype.disposeInternal = function() {
}; };
/**
* @return {Element} Canvas.
*/
ol.renderer.Map.prototype.getCanvas = goog.functions.NULL;
/** /**
* @param {ol.layer.Layer} layer Layer. * @param {ol.layer.Layer} layer Layer.
* @protected * @protected
@@ -169,12 +155,6 @@ ol.renderer.Map.prototype.getMap = function() {
}; };
/**
* Handle background color changed.
*/
ol.renderer.Map.prototype.handleBackgroundColorChanged = goog.nullFunction;
/** /**
* @param {ol.CollectionEvent} collectionEvent Collection event. * @param {ol.CollectionEvent} collectionEvent Collection event.
* @protected * @protected
@@ -218,40 +198,6 @@ ol.renderer.Map.prototype.handleLayersRemove = function(collectionEvent) {
}; };
/**
* @protected
*/
ol.renderer.Map.prototype.handleViewPropertyChanged = function() {
this.getMap().render();
};
/**
* @protected
*/
ol.renderer.Map.prototype.handleSizeChanged = function() {
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();
};
/** /**
* @param {ol.layer.Layer} layer Layer. * @param {ol.layer.Layer} layer Layer.
* @protected * @protected

View File

@@ -1,10 +1,9 @@
// FIXME clear textureCache
// FIXME generational tile texture garbage collector newFrame/get
// FIXME check against gl.getParameter(webgl.MAX_TEXTURE_SIZE) // FIXME check against gl.getParameter(webgl.MAX_TEXTURE_SIZE)
goog.provide('ol.renderer.webgl.Map'); goog.provide('ol.renderer.webgl.Map');
goog.provide('ol.renderer.webgl.map.shader'); goog.provide('ol.renderer.webgl.map.shader');
goog.require('goog.array');
goog.require('goog.debug.Logger'); goog.require('goog.debug.Logger');
goog.require('goog.dispose'); goog.require('goog.dispose');
goog.require('goog.dom'); goog.require('goog.dom');
@@ -17,13 +16,21 @@ goog.require('goog.webgl');
goog.require('ol.Tile'); goog.require('ol.Tile');
goog.require('ol.layer.Layer'); goog.require('ol.layer.Layer');
goog.require('ol.layer.TileLayer'); goog.require('ol.layer.TileLayer');
goog.require('ol.renderer.Map');
goog.require('ol.renderer.webgl.FragmentShader'); goog.require('ol.renderer.webgl.FragmentShader');
goog.require('ol.renderer.webgl.TileLayer'); goog.require('ol.renderer.webgl.TileLayer');
goog.require('ol.renderer.webgl.VertexShader'); goog.require('ol.renderer.webgl.VertexShader');
goog.require('ol.structs.LinkedMap');
goog.require('ol.webgl'); goog.require('ol.webgl');
goog.require('ol.webgl.WebGLContextEventType'); goog.require('ol.webgl.WebGLContextEventType');
/**
* @define {number} Texture cache high water mark.
*/
ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK = 1024;
/** /**
* @typedef {{magFilter: number, minFilter: number, texture: WebGLTexture}} * @typedef {{magFilter: number, minFilter: number, texture: WebGLTexture}}
*/ */
@@ -177,9 +184,15 @@ ol.renderer.webgl.Map = function(container, map) {
/** /**
* @private * @private
* @type {Object.<string, ol.renderer.webgl.TextureCacheEntry>} * @type {ol.structs.LinkedMap}
*/ */
this.textureCache_ = {}; this.textureCache_ = new ol.structs.LinkedMap(undefined, true);
/**
* @private
* @type {number}
*/
this.textureCacheFrameMarkerCount_ = 0;
/** /**
* @private * @private
@@ -225,8 +238,8 @@ ol.renderer.webgl.Map.prototype.bindTileTexture =
function(tile, magFilter, minFilter) { function(tile, magFilter, minFilter) {
var gl = this.getGL(); var gl = this.getGL();
var tileKey = tile.getKey(); var tileKey = tile.getKey();
var textureCacheEntry = this.textureCache_[tileKey]; if (this.textureCache_.containsKey(tileKey)) {
if (goog.isDef(textureCacheEntry)) { var textureCacheEntry = this.textureCache_.get(tileKey);
gl.bindTexture(goog.webgl.TEXTURE_2D, textureCacheEntry.texture); gl.bindTexture(goog.webgl.TEXTURE_2D, textureCacheEntry.texture);
if (textureCacheEntry.magFilter != magFilter) { if (textureCacheEntry.magFilter != magFilter) {
gl.texParameteri( gl.texParameteri(
@@ -251,11 +264,11 @@ ol.renderer.webgl.Map.prototype.bindTileTexture =
goog.webgl.CLAMP_TO_EDGE); goog.webgl.CLAMP_TO_EDGE);
gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T, gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T,
goog.webgl.CLAMP_TO_EDGE); goog.webgl.CLAMP_TO_EDGE);
this.textureCache_[tileKey] = { this.textureCache_.set(tileKey, {
texture: texture, texture: texture,
magFilter: magFilter, magFilter: magFilter,
minFilter: minFilter minFilter: minFilter
}; });
} }
}; };
@@ -285,14 +298,50 @@ ol.renderer.webgl.Map.prototype.disposeInternal = function() {
goog.object.forEach(this.shaderCache_, function(shader) { goog.object.forEach(this.shaderCache_, function(shader) {
gl.deleteShader(shader); gl.deleteShader(shader);
}); });
goog.object.forEach(this.textureCache_, function(textureCacheEntry) { this.textureCache_.forEach(function(textureCacheEntry) {
if (!goog.isNull(textureCacheEntry)) {
gl.deleteTexture(textureCacheEntry.texture); gl.deleteTexture(textureCacheEntry.texture);
}
}); });
} }
goog.base(this, 'disposeInternal'); goog.base(this, 'disposeInternal');
}; };
/**
* @param {ol.Map} map Map.
* @param {ol.FrameState} frameState Frame state.
* @private
*/
ol.renderer.webgl.Map.prototype.expireCache_ = function(map, frameState) {
var gl = this.getGL();
var key, textureCacheEntry;
while (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ >
ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) {
textureCacheEntry = /** @type {?ol.renderer.webgl.TextureCacheEntry} */
(this.textureCache_.peekLast());
if (goog.isNull(textureCacheEntry)) {
if (+this.textureCache_.peekLastKey() == frameState.time) {
break;
} else {
--this.textureCacheFrameMarkerCount_;
}
} else {
gl.deleteTexture(textureCacheEntry.texture);
}
this.textureCache_.pop();
}
};
/**
* @inheritDoc
*/
ol.renderer.webgl.Map.prototype.getCanvas = function() {
return this.canvas_;
};
/** /**
* @return {WebGLRenderingContext} GL. * @return {WebGLRenderingContext} GL.
*/ */
@@ -360,14 +409,6 @@ ol.renderer.webgl.Map.prototype.getShader = function(shaderObject) {
}; };
/**
* @inheritDoc
*/
ol.renderer.webgl.Map.prototype.handleBackgroundColorChanged = function() {
this.getMap().render();
};
/** /**
* @param {goog.events.Event} event Event. * @param {goog.events.Event} event Event.
* @protected * @protected
@@ -390,7 +431,8 @@ ol.renderer.webgl.Map.prototype.handleWebGLContextLost = function(event) {
this.arrayBuffer_ = null; this.arrayBuffer_ = null;
this.shaderCache_ = {}; this.shaderCache_ = {};
this.programCache_ = {}; this.programCache_ = {};
this.textureCache_ = {}; this.textureCache_.clear();
this.textureCacheFrameMarkerCount_ = 0;
goog.object.forEach(this.layerRenderers, function(layerRenderer) { goog.object.forEach(this.layerRenderers, function(layerRenderer) {
layerRenderer.handleWebGLContextLost(); layerRenderer.handleWebGLContextLost();
}); });
@@ -427,7 +469,7 @@ ol.renderer.webgl.Map.prototype.initializeGL_ = function() {
* @return {boolean} Is tile texture loaded. * @return {boolean} Is tile texture loaded.
*/ */
ol.renderer.webgl.Map.prototype.isTileTextureLoaded = function(tile) { ol.renderer.webgl.Map.prototype.isTileTextureLoaded = function(tile) {
return tile.getKey() in this.textureCache_; return this.textureCache_.containsKey(tile.getKey());
}; };
@@ -471,6 +513,9 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
return false; return false;
} }
this.textureCache_.set(frameState.time.toString(), null);
++this.textureCacheFrameMarkerCount_;
goog.array.forEach(frameState.layersArray, function(layer) { goog.array.forEach(frameState.layersArray, function(layer) {
var layerState = frameState.layerStates[goog.getUid(layer)]; var layerState = frameState.layerStates[goog.getUid(layer)];
if (!layerState.visible || !layerState.ready) { if (!layerState.visible || !layerState.ready) {
@@ -553,6 +598,11 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
this.calculateMatrices2D(frameState); this.calculateMatrices2D(frameState);
if (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ >
ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) {
frameState.postRenderFunctions.push(goog.bind(this.expireCache_, this));
}
}; };

View File

@@ -270,6 +270,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
var tileLayer = this.getTileLayer(); var tileLayer = this.getTileLayer();
var tileSource = tileLayer.getTileSource(); var tileSource = tileLayer.getTileSource();
var tileSourceKey = goog.getUid(tileSource).toString();
var tileGrid = tileSource.getTileGrid(); var tileGrid = tileSource.getTileGrid();
var z = tileGrid.getZForResolution(view2DState.resolution); var z = tileGrid.getZForResolution(view2DState.resolution);
var tileResolution = tileGrid.getResolution(z); var tileResolution = tileGrid.getResolution(z);
@@ -392,7 +393,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
tileState = tile.getState(); tileState = tile.getState();
if (tileState == ol.TileState.IDLE) { if (tileState == ol.TileState.IDLE) {
tileCenter = tileGrid.getTileCoordCenter(tileCoord); tileCenter = tileGrid.getTileCoordCenter(tileCoord);
frameState.tileQueue.enqueue(tile, tileCenter, tileResolution); frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter);
} else if (tileState == ol.TileState.LOADED) { } else if (tileState == ol.TileState.LOADED) {
if (mapRenderer.isTileTextureLoaded(tile)) { if (mapRenderer.isTileTextureLoaded(tile)) {
tilesToDrawByZ[z][tileCoord.toString()] = tile; tilesToDrawByZ[z][tileCoord.toString()] = tile;
@@ -455,11 +456,13 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
this.renderedTileRange_ = null; this.renderedTileRange_ = null;
this.renderedFramebufferExtent_ = null; this.renderedFramebufferExtent_ = null;
frameState.animate = true; frameState.animate = true;
this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange);
} }
} }
this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
this.scheduleExpireCache(frameState, tileSource);
goog.vec.Mat4.makeIdentity(this.matrix_); goog.vec.Mat4.makeIdentity(this.matrix_);
goog.vec.Mat4.translate(this.matrix_, goog.vec.Mat4.translate(this.matrix_,

View File

@@ -2,6 +2,7 @@ goog.provide('ol.BingMapsStyle');
goog.provide('ol.source.BingMaps'); goog.provide('ol.source.BingMaps');
goog.require('goog.Uri'); goog.require('goog.Uri');
goog.require('goog.array');
goog.require('goog.events'); goog.require('goog.events');
goog.require('goog.events.EventType'); goog.require('goog.events.EventType');
goog.require('goog.net.Jsonp'); goog.require('goog.net.Jsonp');

View File

@@ -0,0 +1 @@
@exportClass ol.source.DebugTileSource ol.source.DebugTileSourceOptions

View File

@@ -1,8 +1,8 @@
goog.provide('ol.source.DebugTileSource'); goog.provide('ol.source.DebugTileSource');
goog.provide('ol.source.DebugTileSourceOptions');
goog.require('ol.Size'); goog.require('ol.Size');
goog.require('ol.Tile'); goog.require('ol.Tile');
goog.require('ol.TileCache');
goog.require('ol.TileCoord'); goog.require('ol.TileCoord');
goog.require('ol.source.TileSource'); goog.require('ol.source.TileSource');
goog.require('ol.tilegrid.TileGrid'); goog.require('ol.tilegrid.TileGrid');
@@ -79,14 +79,6 @@ ol.DebugTile_.prototype.getImage = function(opt_context) {
}; };
/**
* @typedef {{extent: (ol.Extent|undefined),
* projection: (ol.Projection|undefined),
* tileGrid: (ol.tilegrid.TileGrid|undefined)}}
*/
ol.source.DebugTileSourceOptions;
/** /**
* @constructor * @constructor
@@ -103,26 +95,40 @@ ol.source.DebugTileSource = function(options) {
/** /**
* @private * @private
* @type {Object.<string, ol.DebugTile_>} * @type {ol.TileCache}
* FIXME will need to expire elements from this cache
* FIXME see elemoine's work with goog.structs.LinkedMap
*/ */
this.tileCache_ = {}; this.tileCache_ = new ol.TileCache();
}; };
goog.inherits(ol.source.DebugTileSource, ol.source.TileSource); goog.inherits(ol.source.DebugTileSource, ol.source.TileSource);
/**
* @inheritDoc
*/
ol.source.DebugTileSource.prototype.canExpireCache = function() {
return this.tileCache_.canExpireCache();
};
/**
* @inheritDoc
*/
ol.source.DebugTileSource.prototype.expireCache = function(usedTiles) {
this.tileCache_.expireCache(usedTiles);
};
/** /**
* @inheritDoc * @inheritDoc
*/ */
ol.source.DebugTileSource.prototype.getTile = function(tileCoord) { ol.source.DebugTileSource.prototype.getTile = function(tileCoord) {
var key = tileCoord.toString(); var key = tileCoord.toString();
if (goog.object.containsKey(this.tileCache_, key)) { if (this.tileCache_.containsKey(key)) {
return this.tileCache_[key]; return /** @type {ol.DebugTile_} */ (this.tileCache_.get(key));
} else { } else {
var tile = new ol.DebugTile_(tileCoord, this.tileGrid); var tile = new ol.DebugTile_(tileCoord, this.tileGrid);
this.tileCache_[key] = tile; this.tileCache_.set(key, tile);
return tile; return tile;
} }
}; };

View File

@@ -5,6 +5,7 @@ goog.require('ol.Attribution');
goog.require('ol.Extent'); goog.require('ol.Extent');
goog.require('ol.ImageTile'); goog.require('ol.ImageTile');
goog.require('ol.Projection'); goog.require('ol.Projection');
goog.require('ol.TileCache');
goog.require('ol.TileCoord'); goog.require('ol.TileCoord');
goog.require('ol.TileUrlFunction'); goog.require('ol.TileUrlFunction');
goog.require('ol.TileUrlFunctionType'); goog.require('ol.TileUrlFunctionType');
@@ -55,32 +56,46 @@ ol.source.ImageTileSource = function(options) {
/** /**
* @private * @private
* @type {Object.<string, ol.ImageTile>} * @type {ol.TileCache}
* FIXME will need to expire elements from this cache
* FIXME see elemoine's work with goog.structs.LinkedMap
*/ */
this.tileCache_ = {}; this.tileCache_ = new ol.TileCache();
}; };
goog.inherits(ol.source.ImageTileSource, ol.source.TileSource); goog.inherits(ol.source.ImageTileSource, ol.source.TileSource);
/**
* @inheritDoc
*/
ol.source.ImageTileSource.prototype.canExpireCache = function() {
return this.tileCache_.canExpireCache();
};
/**
* @inheritDoc
*/
ol.source.ImageTileSource.prototype.expireCache = function(usedTiles) {
this.tileCache_.expireCache(usedTiles);
};
/** /**
* @inheritDoc * @inheritDoc
*/ */
ol.source.ImageTileSource.prototype.getTile = function(tileCoord) { ol.source.ImageTileSource.prototype.getTile = function(tileCoord) {
var key = tileCoord.toString(); var key = tileCoord.toString();
if (goog.object.containsKey(this.tileCache_, key)) { if (this.tileCache_.containsKey(key)) {
return this.tileCache_[key]; return /** @type {ol.Tile} */ (this.tileCache_.get(key));
} else { } else {
var tileUrl = this.getTileCoordUrl(tileCoord); var tileUrl = this.getTileCoordUrl(tileCoord);
var tile; var tile;
if (goog.isDef(tileUrl)) { if (goog.isDef(tileUrl)) {
tile = new ol.ImageTile(tileCoord, tileUrl, this.crossOrigin_); tile = new ol.ImageTile(tileCoord, tileUrl, this.crossOrigin_);
this.tileCache_.set(key, tile);
} else { } else {
tile = null; tile = null;
} }
this.tileCache_[key] = tile;
return tile; return tile;
} }
}; };

View File

@@ -10,7 +10,7 @@ goog.require('ol.TileUrlFunction');
/** /**
* @typedef {{attribtions: (Array.<ol.Attribution>|undefined), * @typedef {{attributions: (Array.<ol.Attribution>|undefined),
* extent: (ol.Extent|undefined), * extent: (ol.Extent|undefined),
* projection: (ol.Projection|undefined)}} * projection: (ol.Projection|undefined)}}
*/ */

View File

@@ -3,6 +3,7 @@
goog.provide('ol.source.TiledWMS'); goog.provide('ol.source.TiledWMS');
goog.require('goog.array');
goog.require('goog.asserts'); goog.require('goog.asserts');
goog.require('goog.object'); goog.require('goog.object');
goog.require('goog.uri.utils'); goog.require('goog.uri.utils');

View File

@@ -1,6 +1,7 @@
goog.provide('ol.source.TileSource'); goog.provide('ol.source.TileSource');
goog.provide('ol.source.TileSourceOptions'); goog.provide('ol.source.TileSourceOptions');
goog.require('goog.functions');
goog.require('ol.Attribution'); goog.require('ol.Attribution');
goog.require('ol.Extent'); goog.require('ol.Extent');
goog.require('ol.Projection'); goog.require('ol.Projection');
@@ -46,6 +47,18 @@ ol.source.TileSource = function(tileSourceOptions) {
goog.inherits(ol.source.TileSource, ol.source.Source); goog.inherits(ol.source.TileSource, ol.source.Source);
/**
* @return {boolean} Can expire cache.
*/
ol.source.TileSource.prototype.canExpireCache = goog.functions.FALSE;
/**
* @param {Object.<string, ol.TileRange>} usedTiles Used tiles.
*/
ol.source.TileSource.prototype.expireCache = goog.abstractMethod;
/** /**
* @inheritDoc * @inheritDoc
*/ */

486
src/ol/structs/linkedmap.js Normal file
View File

@@ -0,0 +1,486 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// FIXME replace this with goog.structs.LinkedMap when goog.structs.LinkedMap
// adds peekLastKey()
/**
* @fileoverview A LinkedMap data structure that is accessed using key/value
* pairs like an ordinary Map, but which guarantees a consistent iteration
* order over its entries. The iteration order is either insertion order (the
* default) or ordered from most recent to least recent use. By setting a fixed
* size, the LRU version of the LinkedMap makes an effective object cache. This
* data structure is similar to Java's LinkedHashMap.
*
* @author brenneman@google.com (Shawn Brenneman)
*/
goog.provide('ol.structs.LinkedMap');
goog.require('goog.structs.Map');
/**
* Class for a LinkedMap datastructure, which combines O(1) map access for
* key/value pairs with a linked list for a consistent iteration order. Sample
* usage:
*
* <pre>
* var m = new LinkedMap();
* m.set('param1', 'A');
* m.set('param2', 'B');
* m.set('param3', 'C');
* alert(m.getKeys()); // param1, param2, param3
*
* var c = new LinkedMap(5, true);
* for (var i = 0; i < 10; i++) {
* c.set('entry' + i, false);
* }
* alert(c.getKeys()); // entry9, entry8, entry7, entry6, entry5
*
* c.set('entry5', true);
* c.set('entry1', false);
* alert(c.getKeys()); // entry1, entry5, entry9, entry8, entry7
* </pre>
*
* @param {number=} opt_maxCount The maximum number of objects to store in the
* LinkedMap. If unspecified or 0, there is no maximum.
* @param {boolean=} opt_cache When set, the LinkedMap stores items in order
* from most recently used to least recently used, instead of insertion
* order.
* @constructor
*/
ol.structs.LinkedMap = function(opt_maxCount, opt_cache) {
/**
* The maximum number of entries to allow, or null if there is no limit.
* @type {?number}
* @private
*/
this.maxCount_ = opt_maxCount || null;
/**
* @type {boolean}
* @private
*/
this.cache_ = !!opt_cache;
this.map_ = new goog.structs.Map();
this.head_ = new ol.structs.LinkedMap.Node_('', undefined);
this.head_.next = this.head_.prev = this.head_;
};
/**
* Finds a node and updates it to be the most recently used.
* @param {string} key The key of the node.
* @return {ol.structs.LinkedMap.Node_} The node or null if not found.
* @private
*/
ol.structs.LinkedMap.prototype.findAndMoveToTop_ = function(key) {
var node = /** @type {ol.structs.LinkedMap.Node_} */ (this.map_.get(key));
if (node) {
if (this.cache_) {
node.remove();
this.insert_(node);
}
}
return node;
};
/**
* Retrieves the value for a given key. If this is a caching LinkedMap, the
* entry will become the most recently used.
* @param {string} key The key to retrieve the value for.
* @param {*=} opt_val A default value that will be returned if the key is
* not found, defaults to undefined.
* @return {*} The retrieved value.
*/
ol.structs.LinkedMap.prototype.get = function(key, opt_val) {
var node = this.findAndMoveToTop_(key);
return node ? node.value : opt_val;
};
/**
* Retrieves the value for a given key without updating the entry to be the
* most recently used.
* @param {string} key The key to retrieve the value for.
* @param {*=} opt_val A default value that will be returned if the key is
* not found.
* @return {*} The retrieved value.
*/
ol.structs.LinkedMap.prototype.peekValue = function(key, opt_val) {
var node = this.map_.get(key);
return node ? node.value : opt_val;
};
/**
* Sets a value for a given key. If this is a caching LinkedMap, this entry
* will become the most recently used.
* @param {string} key The key to retrieve the value for.
* @param {*} value A default value that will be returned if the key is
* not found.
*/
ol.structs.LinkedMap.prototype.set = function(key, value) {
var node = this.findAndMoveToTop_(key);
if (node) {
node.value = value;
} else {
node = new ol.structs.LinkedMap.Node_(key, value);
this.map_.set(key, node);
this.insert_(node);
}
};
/**
* Returns the value of the first node without making any modifications.
* @return {*} The value of the first node or undefined if the map is empty.
*/
ol.structs.LinkedMap.prototype.peek = function() {
return this.head_.next.value;
};
/**
* Returns the value of the last node without making any modifications.
* @return {*} The value of the last node or undefined if the map is empty.
*/
ol.structs.LinkedMap.prototype.peekLast = function() {
return this.head_.prev.value;
};
/**
* Returns the key of the last node without making any modifications.
* @return {string|undefined} The key of the last node or undefined if the map
* is empty.
*/
ol.structs.LinkedMap.prototype.peekLastKey = function() {
return this.head_.prev.key;
};
/**
* Removes the first node from the list and returns its value.
* @return {*} The value of the popped node, or undefined if the map was empty.
*/
ol.structs.LinkedMap.prototype.shift = function() {
return this.popNode_(this.head_.next);
};
/**
* Removes the last node from the list and returns its value.
* @return {*} The value of the popped node, or undefined if the map was empty.
*/
ol.structs.LinkedMap.prototype.pop = function() {
return this.popNode_(this.head_.prev);
};
/**
* Removes a value from the LinkedMap based on its key.
* @param {string} key The key to remove.
* @return {boolean} True if the entry was removed, false if the key was not
* found.
*/
ol.structs.LinkedMap.prototype.remove = function(key) {
var node = /** @type {ol.structs.LinkedMap.Node_} */ (this.map_.get(key));
if (node) {
this.removeNode(node);
return true;
}
return false;
};
/**
* Removes a node from the {@code LinkedMap}. It can be overridden to do
* further cleanup such as disposing of the node value.
* @param {!ol.structs.LinkedMap.Node_} node The node to remove.
* @protected
*/
ol.structs.LinkedMap.prototype.removeNode = function(node) {
node.remove();
this.map_.remove(node.key);
};
/**
* @return {number} The number of items currently in the LinkedMap.
*/
ol.structs.LinkedMap.prototype.getCount = function() {
return this.map_.getCount();
};
/**
* @return {boolean} True if the cache is empty, false if it contains any items.
*/
ol.structs.LinkedMap.prototype.isEmpty = function() {
return this.map_.isEmpty();
};
/**
* Sets the maximum number of entries allowed in this object, truncating any
* excess objects if necessary.
* @param {number} maxCount The new maximum number of entries to allow.
*/
ol.structs.LinkedMap.prototype.setMaxCount = function(maxCount) {
this.maxCount_ = maxCount || null;
if (this.maxCount_ != null) {
this.truncate_(this.maxCount_);
}
};
/**
* @return {!Array.<string>} The list of the keys in the appropriate order for
* this LinkedMap.
*/
ol.structs.LinkedMap.prototype.getKeys = function() {
return this.map(function(val, key) {
return key;
});
};
/**
* @return {!Array} The list of the values in the appropriate order for
* this LinkedMap.
*/
ol.structs.LinkedMap.prototype.getValues = function() {
return this.map(function(val, key) {
return val;
});
};
/**
* Tests whether a provided value is currently in the LinkedMap. This does not
* affect item ordering in cache-style LinkedMaps.
* @param {Object} value The value to check for.
* @return {boolean} Whether the value is in the LinkedMap.
*/
ol.structs.LinkedMap.prototype.contains = function(value) {
return this.some(function(el) {
return el == value;
});
};
/**
* Tests whether a provided key is currently in the LinkedMap. This does not
* affect item ordering in cache-style LinkedMaps.
* @param {string} key The key to check for.
* @return {boolean} Whether the key is in the LinkedMap.
*/
ol.structs.LinkedMap.prototype.containsKey = function(key) {
return this.map_.containsKey(key);
};
/**
* Removes all entries in this object.
*/
ol.structs.LinkedMap.prototype.clear = function() {
this.truncate_(0);
};
/**
* Calls a function on each item in the LinkedMap.
*
* @see goog.structs.forEach
* @param {Function} f The function to call for each item. The function takes
* three arguments: the value, the key, and the LinkedMap.
* @param {Object=} opt_obj The object context to use as "this" for the
* function.
*/
ol.structs.LinkedMap.prototype.forEach = function(f, opt_obj) {
for (var n = this.head_.next; n != this.head_; n = n.next) {
f.call(opt_obj, n.value, n.key, this);
}
};
/**
* Calls a function on each item in the LinkedMap and returns the results of
* those calls in an array.
*
* @see goog.structs.map
* @param {!Function} f The function to call for each item. The function takes
* three arguments: the value, the key, and the LinkedMap.
* @param {Object=} opt_obj The object context to use as "this" for the
* function.
* @return {!Array} The results of the function calls for each item in the
* LinkedMap.
*/
ol.structs.LinkedMap.prototype.map = function(f, opt_obj) {
var rv = [];
for (var n = this.head_.next; n != this.head_; n = n.next) {
rv.push(f.call(opt_obj, n.value, n.key, this));
}
return rv;
};
/**
* Calls a function on each item in the LinkedMap and returns true if any of
* those function calls returns a true-like value.
*
* @see goog.structs.some
* @param {Function} f The function to call for each item. The function takes
* three arguments: the value, the key, and the LinkedMap, and returns a
* boolean.
* @param {Object=} opt_obj The object context to use as "this" for the
* function.
* @return {boolean} Whether f evaluates to true for at least one item in the
* LinkedMap.
*/
ol.structs.LinkedMap.prototype.some = function(f, opt_obj) {
for (var n = this.head_.next; n != this.head_; n = n.next) {
if (f.call(opt_obj, n.value, n.key, this)) {
return true;
}
}
return false;
};
/**
* Calls a function on each item in the LinkedMap and returns true only if every
* function call returns a true-like value.
*
* @see goog.structs.some
* @param {Function} f The function to call for each item. The function takes
* three arguments: the value, the key, and the Cache, and returns a
* boolean.
* @param {Object=} opt_obj The object context to use as "this" for the
* function.
* @return {boolean} Whether f evaluates to true for every item in the Cache.
*/
ol.structs.LinkedMap.prototype.every = function(f, opt_obj) {
for (var n = this.head_.next; n != this.head_; n = n.next) {
if (!f.call(opt_obj, n.value, n.key, this)) {
return false;
}
}
return true;
};
/**
* Appends a node to the list. LinkedMap in cache mode adds new nodes to
* the head of the list, otherwise they are appended to the tail. If there is a
* maximum size, the list will be truncated if necessary.
*
* @param {ol.structs.LinkedMap.Node_} node The item to insert.
* @private
*/
ol.structs.LinkedMap.prototype.insert_ = function(node) {
if (this.cache_) {
node.next = this.head_.next;
node.prev = this.head_;
this.head_.next = node;
node.next.prev = node;
} else {
node.prev = this.head_.prev;
node.next = this.head_;
this.head_.prev = node;
node.prev.next = node;
}
if (this.maxCount_ != null) {
this.truncate_(this.maxCount_);
}
};
/**
* Removes elements from the LinkedMap if the given count has been exceeded.
* In cache mode removes nodes from the tail of the list. Otherwise removes
* nodes from the head.
* @param {number} count Number of elements to keep.
* @private
*/
ol.structs.LinkedMap.prototype.truncate_ = function(count) {
for (var i = this.map_.getCount(); i > count; i--) {
this.removeNode(this.cache_ ? this.head_.prev : this.head_.next);
}
};
/**
* Removes the node from the LinkedMap if it is not the head, and returns
* the node's value.
* @param {!ol.structs.LinkedMap.Node_} node The item to remove.
* @return {*} The value of the popped node.
* @private
*/
ol.structs.LinkedMap.prototype.popNode_ = function(node) {
if (this.head_ != node) {
this.removeNode(node);
}
return node.value;
};
/**
* Internal class for a doubly-linked list node containing a key/value pair.
* @param {string} key The key.
* @param {*} value The value.
* @constructor
* @private
*/
ol.structs.LinkedMap.Node_ = function(key, value) {
this.key = key;
this.value = value;
};
/**
* The next node in the list.
* @type {!ol.structs.LinkedMap.Node_}
*/
ol.structs.LinkedMap.Node_.prototype.next;
/**
* The previous node in the list.
* @type {!ol.structs.LinkedMap.Node_}
*/
ol.structs.LinkedMap.Node_.prototype.prev;
/**
* Causes this node to remove itself from the list.
*/
ol.structs.LinkedMap.Node_.prototype.remove = function() {
this.prev.next = this.next;
this.next.prev = this.prev;
delete this.prev;
delete this.next;
};

58
src/ol/tilecache.js Normal file
View File

@@ -0,0 +1,58 @@
goog.provide('ol.TileCache');
goog.require('goog.dispose');
goog.require('ol.Tile');
goog.require('ol.TileRange');
goog.require('ol.structs.LinkedMap');
/**
* @define {number} Default high water mark.
*/
ol.DEFAULT_TILE_CACHE_HIGH_WATER_MARK = 512;
/**
* @constructor
* @extends {ol.structs.LinkedMap}
* @param {number=} opt_highWaterMark High water mark.
*/
ol.TileCache = function(opt_highWaterMark) {
goog.base(this, undefined, true);
/**
* @private
* @type {number}
*/
this.highWaterMark_ = goog.isDef(opt_highWaterMark) ?
opt_highWaterMark : ol.DEFAULT_TILE_CACHE_HIGH_WATER_MARK;
};
goog.inherits(ol.TileCache, ol.structs.LinkedMap);
/**
* @return {boolean} Can expire cache.
*/
ol.TileCache.prototype.canExpireCache = function() {
return this.getCount() > this.highWaterMark_;
};
/**
* @param {Object.<string, ol.TileRange>} usedTiles Used tiles.
*/
ol.TileCache.prototype.expireCache = function(usedTiles) {
var tile, zKey;
while (this.canExpireCache()) {
tile = /** @type {ol.Tile} */ (this.peekLast());
zKey = tile.tileCoord.z.toString();
if (zKey in usedTiles && usedTiles[zKey].contains(tile.tileCoord)) {
break;
} else {
this.pop();
}
}
};

View File

@@ -48,8 +48,8 @@ ol.tilegrid.XYZ.prototype.forEachTileCoordParentTileRange =
if (z < 0) { if (z < 0) {
break; break;
} }
x = Math.floor(x / 2); x >>= 1;
y = Math.floor(y / 2); y >>= 1;
tileRange = new ol.TileRange(x, y, x, y); tileRange = new ol.TileRange(x, y, x, y);
if (callback.call(opt_obj, z, tileRange)) { if (callback.call(opt_obj, z, tileRange)) {
break; break;

View File

@@ -3,14 +3,24 @@ goog.provide('ol.TileQueue');
goog.require('goog.events'); goog.require('goog.events');
goog.require('goog.events.EventType'); goog.require('goog.events.EventType');
goog.require('goog.structs.PriorityQueue');
goog.require('ol.Coordinate'); goog.require('ol.Coordinate');
goog.require('ol.Tile'); goog.require('ol.Tile');
goog.require('ol.TileState'); goog.require('ol.TileState');
/** /**
* @typedef {function(ol.Tile, ol.Coordinate, number): (number|undefined)} * Tile Queue.
*
* The implementation is inspired from the Closure Library's Heap
* class and Python's heapq module.
*
* http://closure-library.googlecode.com/svn/docs/closure_goog_structs_heap.js.source.html
* http://hg.python.org/cpython/file/2.7/Lib/heapq.py
*/
/**
* @typedef {function(ol.Tile, string, ol.Coordinate): number}
*/ */
ol.TilePriorityFunction; ol.TilePriorityFunction;
@@ -43,9 +53,9 @@ ol.TileQueue = function(tilePriorityFunction) {
/** /**
* @private * @private
* @type {goog.structs.PriorityQueue} * @type {Array.<Array.<*>>}
*/ */
this.queue_ = new goog.structs.PriorityQueue(); this.heap_ = [];
/** /**
* @private * @private
@@ -57,23 +67,49 @@ ol.TileQueue = function(tilePriorityFunction) {
/** /**
* @param {ol.Tile} tile Tile. * @const {number}
* @param {ol.Coordinate} tileCenter Tile center.
* @param {number} tileResolution Tile resolution.
*/ */
ol.TileQueue.prototype.enqueue = ol.TileQueue.DROP = Infinity;
function(tile, tileCenter, tileResolution) {
/**
* Remove and return the highest-priority tile. O(logn).
* @private
* @return {ol.Tile} Tile.
*/
ol.TileQueue.prototype.dequeue_ = function() {
var heap = this.heap_;
goog.asserts.assert(heap.length > 0);
var tile = /** @type {ol.Tile} */ (heap[0][1]);
if (heap.length == 1) {
heap.length = 0;
} else {
heap[0] = heap.pop();
this.siftUp_(0);
}
var tileKey = tile.getKey();
delete this.queuedTileKeys_[tileKey];
return tile;
};
/**
* Enqueue a tile. O(logn).
* @param {ol.Tile} tile Tile.
* @param {string} tileSourceKey Tile source key.
* @param {ol.Coordinate} tileCenter Tile center.
*/
ol.TileQueue.prototype.enqueue = function(tile, tileSourceKey, tileCenter) {
if (tile.getState() != ol.TileState.IDLE) { if (tile.getState() != ol.TileState.IDLE) {
return; return;
} }
var tileKey = tile.getKey(); var tileKey = tile.getKey();
if (!(tileKey in this.queuedTileKeys_)) { if (!(tileKey in this.queuedTileKeys_)) {
var priority = this.tilePriorityFunction_(tile, tileCenter, tileResolution); var priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter);
if (goog.isDef(priority)) { if (priority != ol.TileQueue.DROP) {
this.queue_.enqueue(priority, arguments); this.heap_.push([priority, tile, tileSourceKey, tileCenter]);
this.queuedTileKeys_[tileKey] = true; this.queuedTileKeys_[tileKey] = true;
} else { this.siftDown_(0, this.heap_.length - 1);
// FIXME fire drop event?
} }
} }
}; };
@@ -87,15 +123,57 @@ ol.TileQueue.prototype.handleTileChange = function() {
}; };
/**
* Gets the index of the left child of the node at the given index.
* @param {number} index The index of the node to get the left child for.
* @return {number} The index of the left child.
* @private
*/
ol.TileQueue.prototype.getLeftChildIndex_ = function(index) {
return index * 2 + 1;
};
/**
* Gets the index of the right child of the node at the given index.
* @param {number} index The index of the node to get the right child for.
* @return {number} The index of the right child.
* @private
*/
ol.TileQueue.prototype.getRightChildIndex_ = function(index) {
return index * 2 + 2;
};
/**
* Gets the index of the parent of the node at the given index.
* @param {number} index The index of the node to get the parent for.
* @return {number} The index of the parent.
* @private
*/
ol.TileQueue.prototype.getParentIndex_ = function(index) {
return (index - 1) >> 1;
};
/**
* Make _heap a heap. O(n).
* @private
*/
ol.TileQueue.prototype.heapify_ = function() {
for (var i = (this.heap_.length >> 1) - 1; i >= 0; i--) {
this.siftUp_(i);
}
};
/** /**
* FIXME empty description for jsdoc * FIXME empty description for jsdoc
*/ */
ol.TileQueue.prototype.loadMoreTiles = function() { ol.TileQueue.prototype.loadMoreTiles = function() {
var tile, tileKey; var tile;
while (!this.queue_.isEmpty() && this.tilesLoading_ < this.maxTilesLoading_) { while (this.heap_.length > 0 && this.tilesLoading_ < this.maxTilesLoading_) {
tile = (/** @type {Array} */ (this.queue_.dequeue()))[0]; tile = /** @type {ol.Tile} */ (this.dequeue_());
tileKey = tile.getKey();
delete this.queuedTileKeys_[tileKey];
goog.events.listen(tile, goog.events.EventType.CHANGE, goog.events.listen(tile, goog.events.EventType.CHANGE,
this.handleTileChange, false, this); this.handleTileChange, false, this);
tile.load(); tile.load();
@@ -104,17 +182,75 @@ ol.TileQueue.prototype.loadMoreTiles = function() {
}; };
/**
* @param {number} index The index of the node to move down.
* @private
*/
ol.TileQueue.prototype.siftUp_ = function(index) {
var heap = this.heap_;
var count = heap.length;
var node = heap[index];
var startIndex = index;
while (index < (count >> 1)) {
var lIndex = this.getLeftChildIndex_(index);
var rIndex = this.getRightChildIndex_(index);
var smallerChildIndex = rIndex < count &&
heap[rIndex][0] < heap[lIndex][0] ?
rIndex : lIndex;
heap[index] = heap[smallerChildIndex];
index = smallerChildIndex;
}
heap[index] = node;
this.siftDown_(startIndex, index);
};
/**
* @param {number} startIndex The index of the root.
* @param {number} index The index of the node to move up.
* @private
*/
ol.TileQueue.prototype.siftDown_ = function(startIndex, index) {
var heap = this.heap_;
var node = heap[index];
while (index > startIndex) {
var parentIndex = this.getParentIndex_(index);
if (heap[parentIndex][0] > node[0]) {
heap[index] = heap[parentIndex];
index = parentIndex;
} else {
break;
}
}
heap[index] = node;
};
/** /**
* FIXME empty description for jsdoc * FIXME empty description for jsdoc
*/ */
ol.TileQueue.prototype.reprioritize = function() { ol.TileQueue.prototype.reprioritize = function() {
if (!this.queue_.isEmpty()) { var heap = this.heap_;
var values = /** @type {Array.<Array>} */ (this.queue_.getValues()); var i, n = 0, node, priority, tile, tileCenter, tileKey, tileSourceKey;
this.queue_.clear(); for (i = 0; i < heap.length; ++i) {
this.queuedTileKeys_ = {}; node = heap[i];
var i; tile = /** @type {ol.Tile} */ (node[1]);
for (i = 0; i < values.length; ++i) { tileSourceKey = /** @type {string} */ (node[2]);
this.enqueue.apply(this, values[i]); tileCenter = /** @type {ol.Coordinate} */ (node[3]);
priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter);
if (priority == ol.TileQueue.DROP) {
tileKey = tile.getKey();
delete this.queuedTileKeys_[tileKey];
} else {
node[0] = priority;
heap[n++] = node;
} }
} }
heap.length = n;
this.heapify_();
}; };

View File

@@ -1,6 +1,7 @@
goog.provide('ol.TileUrlFunction'); goog.provide('ol.TileUrlFunction');
goog.provide('ol.TileUrlFunctionType'); goog.provide('ol.TileUrlFunctionType');
goog.require('goog.array');
goog.require('goog.math'); goog.require('goog.math');
goog.require('goog.uri.utils'); goog.require('goog.uri.utils');
goog.require('ol.TileCoord'); goog.require('ol.TileCoord');

View File

@@ -5,6 +5,7 @@ goog.require('goog.array');
goog.require('ol.IView'); goog.require('ol.IView');
goog.require('ol.IView2D'); goog.require('ol.IView2D');
goog.require('ol.IView3D'); goog.require('ol.IView3D');
goog.require('ol.Object');
/** /**

View File

@@ -296,8 +296,10 @@ ol.View2D.prototype.zoom = function(map, delta, opt_anchor, opt_duration) {
var currentResolution = this.getResolution(); var currentResolution = this.getResolution();
if (goog.isDef(currentResolution) && goog.isDef(opt_duration)) { if (goog.isDef(currentResolution) && goog.isDef(opt_duration)) {
map.requestRenderFrame(); map.requestRenderFrame();
map.addPreRenderFunction(ol.animation.createZoomFrom( map.addPreRenderFunction(ol.animation.createZoomFrom({
currentResolution, opt_duration)); resolution: currentResolution,
duration: opt_duration
}));
} }
var resolution = this.constraints_.resolution(currentResolution, delta); var resolution = this.constraints_.resolution(currentResolution, delta);
this.zoom_(map, resolution, opt_anchor); this.zoom_(map, resolution, opt_anchor);

View File

@@ -93,6 +93,7 @@
<script type="text/javascript" src="spec/ol/source/xyz.test.js"></script> <script type="text/javascript" src="spec/ol/source/xyz.test.js"></script>
<script type="text/javascript" src="spec/ol/tilecoord.test.js"></script> <script type="text/javascript" src="spec/ol/tilecoord.test.js"></script>
<script type="text/javascript" src="spec/ol/tilegrid.test.js"></script> <script type="text/javascript" src="spec/ol/tilegrid.test.js"></script>
<script type="text/javascript" src="spec/ol/tilequeue.test.js"></script>
<script type="text/javascript" src="spec/ol/tilerange.test.js"></script> <script type="text/javascript" src="spec/ol/tilerange.test.js"></script>
<script type="text/javascript" src="spec/ol/tileurlfunction.test.js"></script> <script type="text/javascript" src="spec/ol/tileurlfunction.test.js"></script>
<script type="text/javascript" src="spec/ol/control/control.test.js"></script> <script type="text/javascript" src="spec/ol/control/control.test.js"></script>

View File

@@ -55,6 +55,21 @@ describe('ol.Extent', function() {
expect(destinationExtent.maxX).toRoughlyEqual(5009377.085697311, 1e-9); expect(destinationExtent.maxX).toRoughlyEqual(5009377.085697311, 1e-9);
expect(destinationExtent.maxY).toRoughlyEqual(8399737.889818361, 1e-9); expect(destinationExtent.maxY).toRoughlyEqual(8399737.889818361, 1e-9);
}); });
it('takes arbitrary function', function() {
var transformFn = function(coordinate) {
return new ol.Coordinate(-coordinate.x, -coordinate.y);
};
var sourceExtent = new ol.Extent(-15, -30, 45, 60);
var destinationExtent = sourceExtent.transform(transformFn);
expect(destinationExtent).not.toBeUndefined();
expect(destinationExtent).not.toBeNull();
expect(destinationExtent.minX).toBe(-45);
expect(destinationExtent.minY).toBe(-60);
expect(destinationExtent.maxX).toBe(15);
expect(destinationExtent.maxY).toBe(30);
});
}); });
}); });

View File

@@ -0,0 +1,68 @@
describe('ol.TileQueue', function() {
// is the tile queue's array a heap?
function isHeap(tq) {
var heap = tq.heap_;
var i;
var key;
var leftKey;
var rightKey;
for (i = 0; i < (heap.length >> 1) - 1; i++) {
key = heap[i][0];
leftKey = heap[tq.getLeftChildIndex_(i)][0];
rightKey = heap[tq.getRightChildIndex_(i)][0];
if (leftKey < key || rightKey < key) {
return false;
}
}
return true;
}
function addRandomPriorityTiles(tq, num) {
var i, tile, priority;
for (i = 0; i < num; i++) {
tile = new ol.Tile();
priority = Math.floor(Math.random() * 100);
tq.heap_.push([priority, tile, '', new ol.Coordinate(0, 0)]);
tq.queuedTileKeys_[tile.getKey()] = true;
}
}
describe('heapify', function() {
it('does convert an arbitrary array into a heap', function() {
var tq = new ol.TileQueue(function() {});
addRandomPriorityTiles(tq, 100);
tq.heapify_();
expect(isHeap(tq)).toBeTruthy();
});
});
describe('reprioritize', function() {
it('does reprioritize the array', function() {
var tq = new ol.TileQueue(function() {});
addRandomPriorityTiles(tq, 100);
tq.heapify_();
// now reprioritize, changing the priority of 50 tiles and removing the
// rest
var i = 0;
tq.tilePriorityFunction_ = function() {
if ((i++) % 2 === 0) {
return ol.TileQueue.DROP;
}
return Math.floor(Math.random() * 100);
};
tq.reprioritize();
expect(tq.heap_.length).toEqual(50);
expect(isHeap(tq)).toBeTruthy();
});
});
});