Permalink
Browse files

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

  • Loading branch information...
2 parents 266111e + 4dd7871 commit e30b0959e0318ea5801a59723b88f0d47762b981 @tschaub tschaub committed Feb 18, 2013
Showing with 2,738 additions and 78 deletions.
  1. +2 −0 .gitignore
  2. +26 −6 build.py
  3. +16 −3 examples/wms-custom-proj.js
  4. +40 −0 src/imageurlfunction.js
  5. +19 −0 src/objectliterals.exports
  6. +1 −0 src/ol/control/attributioncontrol.js
  7. +170 −0 src/ol/ellipsoid.js
  8. +10 −0 src/ol/ellipsoid/wgs84.js
  9. +16 −2 src/ol/extent.js
  10. +81 −15 src/ol/geolocation.js
  11. +187 −0 src/ol/image.js
  12. +2 −1 src/ol/interaction/dragpaninteraction.js
  13. +2 −4 src/ol/kinetic.js
  14. +1 −0 src/ol/layer/imagelayer.exports
  15. +24 −0 src/ol/layer/imagelayer.js
  16. +23 −0 src/ol/rectangle.js
  17. +115 −0 src/ol/renderer/canvas/canvasimagelayerrenderer.js
  18. +5 −1 src/ol/renderer/canvas/canvasmaprenderer.js
  19. +122 −0 src/ol/renderer/dom/domimagelayerrenderer.js
  20. +9 −6 src/ol/renderer/dom/dommaprenderer.js
  21. +15 −0 src/ol/renderer/layerrenderer.js
  22. +234 −0 src/ol/renderer/webgl/webglimagelayerrenderer.js
  23. +6 −0 src/ol/renderer/webgl/webgllayerrenderer.js
  24. +15 −4 src/ol/renderer/webgl/webglmaprenderer.js
  25. +14 −0 src/ol/renderer/webgl/webgltilelayerrenderer.js
  26. +113 −0 src/ol/source/imagesource.js
  27. +1 −0 src/ol/source/singleimagewms.exports
  28. +99 −0 src/ol/source/singleimagewmssource.js
  29. +1 −0 src/ol/source/staticimage.exports
  30. +61 −0 src/ol/source/staticimagesource.js
  31. +189 −0 src/ol/sphere.js
  32. +0 −10 src/ol/tilerange.js
  33. +1 −0 test/ol.html
  34. +391 −0 test/spec/ol/ellipsoid.test.js
  35. +51 −26 test/spec/ol/extent.test.js
  36. +27 −0 test/spec/ol/projection.test.js
  37. +11 −0 test/spec/ol/rectangle.test.js
  38. +87 −0 test/spec/ol/renderer/webgl/imagelayer.test.js
  39. +551 −0 test/spec/ol/sphere.test.js
View
2 .gitignore
@@ -11,6 +11,8 @@
/build/src
/build/phantomjs-*-windows
/build/phantomjs-*-windows.zip
+/build/proj4js-*.zip
+/build/proj4js
/examples/*.json
/examples/*.combined.js
/examples/example-list.js
View
32 build.py
@@ -22,14 +22,17 @@
variables.JSDOC = 'jsdoc' # FIXME
variables.PYTHON = os.path.join(Python27, 'python.exe')
PHANTOMJS_WINDOWS_ZIP = 'build/phantomjs-1.8.1-windows.zip'
- PHANTOMJS = 'build/phantomjs-1.8.1-windows/phantomjs.exe'
+ # FIXME we should not need both a pake variable and a Python constant here
+ # FIXME this requires pake to be modified to lazily evaluate variables in target names
+ variables.PHANTOMJS = 'build/phantomjs-1.8.1-windows/phantomjs.exe'
+ PHANTOMJS = variables.PHANTOMJS
else:
variables.GIT = 'git'
variables.GJSLINT = 'gjslint'
variables.JAVA = 'java'
variables.JSDOC = 'jsdoc'
variables.PYTHON = 'python'
- PHANTOMJS = 'phantomjs'
+ variables.PHANTOMJS = 'phantomjs'
variables.BRANCH = output('%(GIT)s', 'rev-parse', '--abbrev-ref', 'HEAD').strip()
@@ -74,6 +77,10 @@
PLOVR_JAR = 'bin/plovr-eba786b34df9.jar'
PLOVR_JAR_MD5 = '20eac8ccc4578676511cf7ccbfc65100'
+PROJ4JS = 'build/proj4js/lib/proj4js-combined.js'
+PROJ4JS_ZIP = 'build/proj4js-1.1.0.zip'
+PROJ4JS_ZIP_MD5 = '17caad64cf6ebc6e6fe62f292b134897'
+
def report_sizes(t):
t.info('uncompressed: %d bytes', os.stat(t.name).st_size)
@@ -335,12 +342,23 @@ def hostexamples(t):
t.cp('examples/example-list.js', 'examples/example-list.xml', 'examples/Jugl.js', 'build/gh-pages/%(BRANCH)s/examples/')
-@target('test', PHANTOMJS, INTERNAL_SRC, 'test/requireall.js', phony=True)
-def test(t):
- t.run(PHANTOMJS, 'test/phantom-jasmine/run_jasmine_test.coffee', 'test/ol.html')
+@target(PROJ4JS, PROJ4JS_ZIP)
+def proj4js(t):
+ from zipfile import ZipFile
+ ZipFile(PROJ4JS_ZIP).extractall('build')
+
+
+@target(PROJ4JS_ZIP, clean=False)
+def proj4js_zip(t):
+ t.download('http://download.osgeo.org/proj4js/' + os.path.basename(t.name), md5=PROJ4JS_ZIP_MD5)
if sys.platform == 'win32':
+ @target('test', '%(PHANTOMJS)s', INTERNAL_SRC, PROJ4JS, 'test/requireall.js', phony=True)
+ def test(t):
+ t.run(PHANTOMJS, 'test/phantom-jasmine/run_jasmine_test.coffee', 'test/ol.html')
+
+ # FIXME the PHANTOMJS should be a pake variable, not a constant
@target(PHANTOMJS, PHANTOMJS_WINDOWS_ZIP, clean=False)
def phantom_js(t):
from zipfile import ZipFile
@@ -351,7 +369,9 @@ def phantomjs_windows_zip(t):
t.download('http://phantomjs.googlecode.com/files/' + os.path.basename(t.name))
else:
- virtual(PHANTOMJS)
+ @target('test', INTERNAL_SRC, PROJ4JS, 'test/requireall.js', phony=True)
+ def test(t):
+ t.run('%(PHANTOMJS)s', 'test/phantom-jasmine/run_jasmine_test.coffee', 'test/ol.html')
@target('fixme', phony=True)
View
19 examples/wms-custom-proj.js
@@ -10,7 +10,9 @@ goog.require('ol.Projection');
goog.require('ol.ProjectionUnits');
goog.require('ol.RendererHints');
goog.require('ol.View2D');
+goog.require('ol.layer.ImageLayer');
goog.require('ol.layer.TileLayer');
+goog.require('ol.source.SingleImageWMS');
goog.require('ol.source.TiledWMS');
@@ -24,6 +26,17 @@ var epsg21781 = new ol.Projection('EPSG:21781', ol.ProjectionUnits.METERS,
new ol.Extent(485869.5728, 76443.1884, 837076.5648, 299941.7864));
ol.Projection.addProjection(epsg21781);
+// We give the single image source a set of resolutions. This prevents the
+// source from requesting images of arbitrary resolutions.
+var projectionExtent = epsg21781.getExtent();
+var maxResolution = Math.max(
+ projectionExtent.maxX - projectionExtent.minX,
+ projectionExtent.maxY - projectionExtent.minY) / 256;
+var resolutions = new Array(10);
+for (var i = 0; i < 10; ++i) {
+ resolutions[i] = maxResolution / Math.pow(2.0, i);
+}
+
var extent = new ol.Extent(420000, 30000, 900000, 350000);
var layers = new ol.Collection([
new ol.layer.TileLayer({
@@ -41,16 +54,16 @@ var layers = new ol.Collection([
extent: extent
})
}),
- new ol.layer.TileLayer({
- source: new ol.source.TiledWMS({
+ new ol.layer.ImageLayer({
+ source: new ol.source.SingleImageWMS({
url: 'http://wms.geo.admin.ch/',
attributions: [new ol.Attribution(
'&copy; ' +
'<a href="http://www.geo.admin.ch/internet/geoportal/en/home.html">' +
'National parks / geo.admin.ch</a>')],
params: {'LAYERS': 'ch.bafu.schutzgebiete-paerke_nationaler_bedeutung'},
projection: epsg21781,
- extent: extent
+ resolutions: resolutions
})
})
]);
View
40 src/imageurlfunction.js
@@ -0,0 +1,40 @@
+goog.provide('ol.ImageUrlFunction');
+goog.provide('ol.ImageUrlFunctionType');
+
+goog.require('ol.Extent');
+goog.require('ol.Size');
+
+
+/**
+ * @typedef {function(ol.Extent, ol.Size): (string|undefined)}
+ */
+ol.ImageUrlFunctionType;
+
+
+/**
+ * @param {string} baseUrl Base URL (may have query data).
+ * @return {ol.ImageUrlFunctionType} Image URL function.
+ */
+ol.ImageUrlFunction.createBboxParam = function(baseUrl) {
+ return function(extent, size) {
+ // FIXME Projection dependant axis order.
+ var bboxValue = [
+ extent.minX, extent.minY, extent.maxX, extent.maxY
+ ].join(',');
+ return goog.uri.utils.appendParams(baseUrl,
+ 'BBOX', bboxValue,
+ 'HEIGHT', size.height,
+ 'WIDTH', size.width);
+ };
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {ol.Size} size Size.
+ * @return {string|undefined} Image URL.
+ */
+ol.ImageUrlFunction.nullImageUrlFunction =
+ function(extent, size) {
+ return undefined;
+};
View
19 src/objectliterals.exports
@@ -92,10 +92,29 @@
@exportObjectLiteralProperty ol.source.DebugTileSourceOptions.projection ol.Projection|undefined
@exportObjectLiteralProperty ol.source.DebugTileSourceOptions.tileGrid ol.tilegrid.TileGrid|undefined
+@exportObjectLiteral ol.source.SingleImageWMSOptions
+@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.attributions Array.<ol.Attribution>|undefined
+@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.crossOrigin null|string|undefined
+@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.extent ol.Extent|undefined
+@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.params Object.<string,*>
+@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.projection ol.Projection|undefined
+@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.resolutions Array.<number>|undefined
+@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.url string|undefined
+@exportObjectLiteralProperty ol.source.SingleImageWMSOptions.version string|undefined
+
@exportObjectLiteral ol.source.StamenOptions
@exportObjectLiteralProperty ol.source.StamenOptions.flavor string|undefined
@exportObjectLiteralProperty ol.source.StamenOptions.provider string
+@exportObjectLiteral ol.source.StaticImageOptions
+@exportObjectLiteralProperty ol.source.StaticImageOptions.attributions Array.<ol.Attribution>|undefined
+@exportObjectLiteralProperty ol.source.StaticImageOptions.crossOrigin null|string|undefined
+@exportObjectLiteralProperty ol.source.StaticImageOptions.extent ol.Extent|undefined
+@exportObjectLiteralProperty ol.source.StaticImageOptions.imageExtent ol.Extent|undefined
+@exportObjectLiteralProperty ol.source.StaticImageOptions.imageSize ol.Size|undefined
+@exportObjectLiteralProperty ol.source.StaticImageOptions.projection ol.Projection|undefined
+@exportObjectLiteralProperty ol.source.StaticImageOptions.url string|undefined
+
@exportObjectLiteral ol.source.TiledWMSOptions
@exportObjectLiteralProperty ol.source.TiledWMSOptions.attributions Array.<ol.Attribution>|undefined
@exportObjectLiteralProperty ol.source.TiledWMSOptions.params Object
View
1 src/ol/control/attributioncontrol.js
@@ -1,4 +1,5 @@
// FIXME handle date line wrap
+// FIXME does not handle image sources
goog.provide('ol.control.Attribution');
View
170 src/ol/ellipsoid.js
@@ -0,0 +1,170 @@
+goog.provide('ol.Ellipsoid');
+
+goog.require('goog.math');
+goog.require('ol.Coordinate');
+
+
+
+/**
+ * @constructor
+ * @param {number} a Major radius.
+ * @param {number} flattening Flattening.
+ */
+ol.Ellipsoid = function(a, flattening) {
+
+ /**
+ * @type {number}
+ */
+ this.a = a;
+
+ /**
+ * @type {number}
+ */
+ this.flattening = flattening;
+
+ /**
+ * @type {number}
+ */
+ this.b = this.a * (1 - this.flattening);
+
+};
+
+
+/**
+ * @param {ol.Coordinate} c1 Coordinate 1.
+ * @param {ol.Coordinate} c2 Coordinate 1.
+ * @param {number=} opt_minDeltaLambda Minimum delta lambda for convergence.
+ * @param {number=} opt_maxIterations Maximum iterations.
+ * @return {{distance: number, initialBearing: number, finalBearing: number}}
+ * Vincenty.
+ */
+ol.Ellipsoid.prototype.vincenty =
+ function(c1, c2, opt_minDeltaLambda, opt_maxIterations) {
+ var minDeltaLambda = goog.isDef(opt_minDeltaLambda) ?
+ opt_minDeltaLambda : 1e-12;
+ var maxIterations = goog.isDef(opt_maxIterations) ?
+ opt_maxIterations : 100;
+ var f = this.flattening;
+ var lat1 = goog.math.toRadians(c1.y);
+ var lat2 = goog.math.toRadians(c2.y);
+ var deltaLon = goog.math.toRadians(c2.x - c1.x);
+ var U1 = Math.atan((1 - f) * Math.tan(lat1));
+ var cosU1 = Math.cos(U1);
+ var sinU1 = Math.sin(U1);
+ var U2 = Math.atan((1 - f) * Math.tan(lat2));
+ var cosU2 = Math.cos(U2);
+ var sinU2 = Math.sin(U2);
+ var lambda = deltaLon;
+ var cosSquaredAlpha, sinAlpha;
+ var cosLambda, deltaLambda = Infinity, sinLambda;
+ var cos2SigmaM, cosSigma, sigma, sinSigma;
+ var i;
+ for (i = maxIterations; i > 0; --i) {
+ cosLambda = Math.cos(lambda);
+ sinLambda = Math.sin(lambda);
+ var x = cosU2 * sinLambda;
+ var y = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
+ sinSigma = Math.sqrt(x * x + y * y);
+ if (sinSigma === 0) {
+ return {
+ distance: 0,
+ initialBearing: 0,
+ finalBearing: 0
+ };
+ }
+ cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
+ sigma = Math.atan2(sinSigma, cosSigma);
+ sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
+ cosSquaredAlpha = 1 - sinAlpha * sinAlpha;
+ cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSquaredAlpha;
+ if (isNaN(cos2SigmaM)) {
+ cos2SigmaM = 0;
+ }
+ var C = f / 16 * cosSquaredAlpha * (4 + f * (4 - 3 * cosSquaredAlpha));
+ var lambdaPrime = deltaLon + (1 - C) * f * sinAlpha * (sigma +
+ C * sinSigma * (cos2SigmaM +
+ C * cosSigma * (2 * cos2SigmaM * cos2SigmaM - 1)));
+ deltaLambda = Math.abs(lambdaPrime - lambda);
+ lambda = lambdaPrime;
+ if (deltaLambda < minDeltaLambda) {
+ break;
+ }
+ }
+ if (i === 0) {
+ return {
+ distance: NaN,
+ finalBearing: NaN,
+ initialBearing: NaN
+ };
+ }
+ var aSquared = this.a * this.a;
+ var bSquared = this.b * this.b;
+ var uSquared = cosSquaredAlpha * (aSquared - bSquared) / bSquared;
+ var A = 1 + uSquared / 16384 *
+ (4096 + uSquared * (uSquared * (320 - 175 * uSquared) - 768));
+ var B = uSquared / 1024 *
+ (256 + uSquared * (uSquared * (74 - 47 * uSquared) - 128));
+ var deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 *
+ (cosSigma * (2 * cos2SigmaM * cos2SigmaM - 1) -
+ B / 6 * cos2SigmaM * (4 * sinSigma * sinSigma - 3) *
+ (4 * cos2SigmaM * cos2SigmaM - 3)));
+ cosLambda = Math.cos(lambda);
+ sinLambda = Math.sin(lambda);
+ var alpha1 = Math.atan2(cosU2 * sinLambda,
+ cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
+ var alpha2 = Math.atan2(cosU1 * sinLambda,
+ cosU1 * sinU2 * cosLambda - sinU1 * cosU2);
+ return {
+ distance: this.b * A * (sigma - deltaSigma),
+ initialBearing: goog.math.toDegrees(alpha1),
+ finalBearing: goog.math.toDegrees(alpha2)
+ };
+};
+
+
+/**
+ * Returns the distance from c1 to c2 using Vincenty.
+ *
+ * @param {ol.Coordinate} c1 Coordinate 1.
+ * @param {ol.Coordinate} c2 Coordinate 1.
+ * @param {number=} opt_minDeltaLambda Minimum delta lambda for convergence.
+ * @param {number=} opt_maxIterations Maximum iterations.
+ * @return {number} Vincenty distance.
+ */
+ol.Ellipsoid.prototype.vincentyDistance =
+ function(c1, c2, opt_minDeltaLambda, opt_maxIterations) {
+ var vincenty = this.vincenty(c1, c2, opt_minDeltaLambda, opt_maxIterations);
+ return vincenty.distance;
+};
+
+
+/**
+ * Returns the final bearing from c1 to c2 using Vincenty.
+ *
+ * @param {ol.Coordinate} c1 Coordinate 1.
+ * @param {ol.Coordinate} c2 Coordinate 1.
+ * @param {number=} opt_minDeltaLambda Minimum delta lambda for convergence.
+ * @param {number=} opt_maxIterations Maximum iterations.
+ * @return {number} Initial bearing.
+ */
+ol.Ellipsoid.prototype.vincentyFinalBearing =
+ function(c1, c2, opt_minDeltaLambda, opt_maxIterations) {
+ var vincenty = this.vincenty(c1, c2, opt_minDeltaLambda, opt_maxIterations);
+ return vincenty.finalBearing;
+};
+
+
+/**
+ * Returns the initial bearing from c1 to c2 using Vincenty.
+ *
+ * @param {ol.Coordinate} c1 Coordinate 1.
+ * @param {ol.Coordinate} c2 Coordinate 1.
+ * @param {number=} opt_minDeltaLambda Minimum delta lambda for convergence.
+ * @param {number=} opt_maxIterations Maximum iterations.
+ * @return {number} Initial bearing.
+ */
+ol.Ellipsoid.prototype.vincentyInitialBearing =
+ function(c1, c2, opt_minDeltaLambda, opt_maxIterations) {
+ var vincenty = this.vincenty(c1, c2, opt_minDeltaLambda, opt_maxIterations);
+ return vincenty.initialBearing;
+};
View
10 src/ol/ellipsoid/wgs84.js
@@ -0,0 +1,10 @@
+goog.provide('ol.ellipsoid.WGS84');
+
+goog.require('ol.Ellipsoid');
+
+
+/**
+ * @const
+ * @type {ol.Ellipsoid}
+ */
+ol.ellipsoid.WGS84 = new ol.Ellipsoid(6378137, 1 / 298.257223563);
View
18 src/ol/extent.js
@@ -46,18 +46,32 @@ ol.Extent.boundingExtent = function(var_args) {
/**
- * Checks if the given coordinate is contained or on the edge of the extent.
+ * Checks if the passed coordinate is contained or on the edge
+ * of the extent.
*
* @param {ol.Coordinate} coordinate Coordinate.
* @return {boolean} Contains.
*/
-ol.Extent.prototype.contains = function(coordinate) {
+ol.Extent.prototype.containsCoordinate = function(coordinate) {
return this.minX <= coordinate.x && coordinate.x <= this.maxX &&
this.minY <= coordinate.y && coordinate.y <= this.maxY;
};
/**
+ * Checks if the passed extent is contained or on the edge of the
+ * extent.
+ *
+ * @param {ol.Extent} extent Extent.
+ * @return {boolean} Contains.
+ */
+ol.Extent.prototype.containsExtent = function(extent) {
+ return this.minX <= extent.minX && extent.maxX <= this.maxX &&
+ this.minY <= extent.minY && extent.maxY <= this.maxY;
+};
+
+
+/**
* @return {ol.Coordinate} Bottom left coordinate.
*/
ol.Extent.prototype.getBottomLeft = function() {
View
96 src/ol/geolocation.js
@@ -5,6 +5,7 @@ goog.provide('ol.Geolocation');
goog.provide('ol.GeolocationProperty');
goog.require('goog.functions');
+goog.require('goog.math');
goog.require('ol.Coordinate');
goog.require('ol.Object');
goog.require('ol.Projection');
@@ -15,8 +16,12 @@ goog.require('ol.Projection');
*/
ol.GeolocationProperty = {
ACCURACY: 'accuracy',
+ ALTITUDE: 'altitude',
+ ALTITUDE_ACCURACY: 'altitudeAccuracy',
+ HEADING: 'heading',
POSITION: 'position',
- PROJECTION: 'projection'
+ PROJECTION: 'projection',
+ SPEED: 'speed'
};
@@ -92,10 +97,19 @@ ol.Geolocation.isSupported = 'geolocation' in navigator;
*/
ol.Geolocation.prototype.positionChange_ = function(position) {
var coords = position.coords;
+ this.set(ol.GeolocationProperty.ACCURACY, coords.accuracy);
+ this.set(ol.GeolocationProperty.ALTITUDE,
+ goog.isNull(coords.altitude) ? undefined : coords.altitude);
+ this.set(ol.GeolocationProperty.ALTITUDE_ACCURACY,
+ goog.isNull(coords.altitudeAccuracy) ?
+ undefined : coords.altitudeAccuracy);
+ this.set(ol.GeolocationProperty.HEADING, goog.isNull(coords.heading) ?
+ undefined : goog.math.toRadians(coords.heading));
this.position_ = new ol.Coordinate(coords.longitude, coords.latitude);
this.set(ol.GeolocationProperty.POSITION,
this.transformCoords_(this.position_));
- this.set(ol.GeolocationProperty.ACCURACY, coords.accuracy);
+ this.set(ol.GeolocationProperty.SPEED,
+ goog.isNull(coords.speed) ? undefined : coords.speed);
};
@@ -108,6 +122,59 @@ ol.Geolocation.prototype.positionError_ = function(error) {
/**
+ * The accuracy of the position in meters.
+ * @return {number|undefined} accuracy.
+ */
+ol.Geolocation.prototype.getAccuracy = function() {
+ return /** @type {number} */ (
+ this.get(ol.GeolocationProperty.ACCURACY));
+};
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getAccuracy',
+ ol.Geolocation.prototype.getAccuracy);
+
+
+/**
+ * @return {number|undefined} Altitude.
+ */
+ol.Geolocation.prototype.getAltitude = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.GeolocationProperty.ALTITUDE));
+};
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getAltitude',
+ ol.Geolocation.prototype.getAltitude);
+
+
+/**
+ * @return {number|undefined} Altitude accuracy.
+ */
+ol.Geolocation.prototype.getAltitudeAccuracy = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.GeolocationProperty.ALTITUDE_ACCURACY));
+};
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getAltitudeAccuracy',
+ ol.Geolocation.prototype.getAltitudeAccuracy);
+
+
+/**
+ * @return {number|undefined} Heading.
+ */
+ol.Geolocation.prototype.getHeading = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.GeolocationProperty.HEADING));
+};
+goog.exportProperty(
+ ol.Geolocation.prototype,
+ 'getHeading',
+ ol.Geolocation.prototype.getHeading);
+
+
+/**
* The position of the device.
* @return {ol.Coordinate|undefined} position.
*/
@@ -122,30 +189,29 @@ goog.exportProperty(
/**
- * The accuracy of the position in meters.
- * @return {number|undefined} accuracy.
+ * @return {ol.Projection|undefined} projection.
*/
-ol.Geolocation.prototype.getAccuracy = function() {
- return /** @type {number} */ (
- this.get(ol.GeolocationProperty.ACCURACY));
+ol.Geolocation.prototype.getProjection = function() {
+ return /** @type {ol.Projection} */ (
+ this.get(ol.GeolocationProperty.PROJECTION));
};
goog.exportProperty(
ol.Geolocation.prototype,
- 'getAccuracy',
- ol.Geolocation.prototype.getAccuracy);
+ 'getProjection',
+ ol.Geolocation.prototype.getProjection);
/**
- * @return {ol.Projection|undefined} projection.
+ * @return {number|undefined} Speed.
*/
-ol.Geolocation.prototype.getProjection = function() {
- return /** @type {ol.Projection} */ (
- this.get(ol.GeolocationProperty.PROJECTION));
+ol.Geolocation.prototype.getSpeed = function() {
+ return /** @type {number|undefined} */ (
+ this.get(ol.GeolocationProperty.SPEED));
};
goog.exportProperty(
ol.Geolocation.prototype,
- 'getProjection',
- ol.Geolocation.prototype.getProjection);
+ 'getSpeed',
+ ol.Geolocation.prototype.getSpeed);
/**
View
187 src/ol/image.js
@@ -0,0 +1,187 @@
+goog.provide('ol.Image');
+goog.provide('ol.ImageState');
+
+goog.require('goog.array');
+goog.require('goog.events');
+goog.require('goog.events.EventTarget');
+goog.require('goog.events.EventType');
+goog.require('ol.Extent');
+
+
+/**
+ * @enum {number}
+ */
+ol.ImageState = {
+ IDLE: 0,
+ LOADING: 1,
+ LOADED: 2,
+ ERROR: 3
+};
+
+
+
+/**
+ * @constructor
+ * @extends {goog.events.EventTarget}
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @param {string} src Image source URI.
+ * @param {?string} crossOrigin Cross origin.
+ */
+ol.Image = function(extent, resolution, src, crossOrigin) {
+
+ /**
+ * @private
+ * @type {ol.Extent}
+ */
+ this.extent_ = extent;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.src_ = src;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.resolution_ = resolution;
+
+ /**
+ * @private
+ * @type {Image}
+ */
+ this.image_ = new Image();
+ if (!goog.isNull(crossOrigin)) {
+ this.image_.crossOrigin = crossOrigin;
+ }
+
+ /**
+ * @private
+ * @type {Object.<number, Image>}
+ */
+ this.imageByContext_ = {};
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.imageListenerKeys_ = null;
+
+ /**
+ * @protected
+ * @type {ol.ImageState}
+ */
+ this.state = ol.ImageState.IDLE;
+};
+goog.inherits(ol.Image, goog.events.EventTarget);
+
+
+/**
+ * @protected
+ */
+ol.Image.prototype.dispatchChangeEvent = function() {
+ this.dispatchEvent(goog.events.EventType.CHANGE);
+};
+
+
+/**
+ * @return {ol.Extent} Extent.
+ */
+ol.Image.prototype.getExtent = function() {
+ return this.extent_;
+};
+
+
+/**
+ * @param {Object=} opt_context Object.
+ * @return {HTMLCanvasElement|Image|HTMLVideoElement} Image.
+ */
+ol.Image.prototype.getImageElement = function(opt_context) {
+ if (goog.isDef(opt_context)) {
+ var image;
+ var key = goog.getUid(opt_context);
+ if (key in this.imageByContext_) {
+ return this.imageByContext_[key];
+ } else if (goog.object.isEmpty(this.imageByContext_)) {
+ image = this.image_;
+ } else {
+ image = /** @type {Image} */ (this.image_.cloneNode(false));
+ }
+ this.imageByContext_[key] = image;
+ return image;
+ } else {
+ return this.image_;
+ }
+};
+
+
+/**
+ * @return {number} Resolution.
+ */
+ol.Image.prototype.getResolution = function() {
+ return this.resolution_;
+};
+
+
+/**
+ * @return {ol.ImageState} State.
+ */
+ol.Image.prototype.getState = function() {
+ return this.state;
+};
+
+
+/**
+ * Tracks loading or read errors.
+ *
+ * @private
+ */
+ol.Image.prototype.handleImageError_ = function() {
+ this.state = ol.ImageState.ERROR;
+ this.unlistenImage_();
+ this.dispatchChangeEvent();
+};
+
+
+/**
+ * Tracks successful image load.
+ *
+ * @private
+ */
+ol.Image.prototype.handleImageLoad_ = function() {
+ this.state = ol.ImageState.LOADED;
+ this.unlistenImage_();
+ this.dispatchChangeEvent();
+};
+
+
+/**
+ * Load not yet loaded URI.
+ */
+ol.Image.prototype.load = function() {
+ if (this.state == ol.ImageState.IDLE) {
+ this.state = ol.ImageState.LOADING;
+ goog.asserts.assert(goog.isNull(this.imageListenerKeys_));
+ this.imageListenerKeys_ = [
+ goog.events.listenOnce(this.image_, goog.events.EventType.ERROR,
+ this.handleImageError_, false, this),
+ goog.events.listenOnce(this.image_, goog.events.EventType.LOAD,
+ this.handleImageLoad_, false, this)
+ ];
+ this.image_.src = this.src_;
+ }
+};
+
+
+/**
+ * Discards event handlers which listen for load completion or errors.
+ *
+ * @private
+ */
+ol.Image.prototype.unlistenImage_ = function() {
+ goog.asserts.assert(!goog.isNull(this.imageListenerKeys_));
+ goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey);
+ this.imageListenerKeys_ = null;
+};
View
3 src/ol/interaction/dragpaninteraction.js
@@ -106,7 +106,8 @@ ol.interaction.DragPan.prototype.handleDragStart = function(mapBrowserEvent) {
var browserEvent = mapBrowserEvent.browserEvent;
if (this.condition_(browserEvent)) {
if (this.kinetic_) {
- this.kinetic_.begin(browserEvent.clientX, browserEvent.clientY);
+ this.kinetic_.begin();
+ this.kinetic_.update(browserEvent.clientX, browserEvent.clientY);
}
var map = mapBrowserEvent.map;
map.requestRenderFrame();
View
6 src/ol/kinetic.js
@@ -63,14 +63,12 @@ ol.Kinetic = function(decay, minVelocity, delay) {
/**
- * @param {number} x X.
- * @param {number} y Y.
+ * FIXME empty description for jsdoc
*/
-ol.Kinetic.prototype.begin = function(x, y) {
+ol.Kinetic.prototype.begin = function() {
this.points_.length = 0;
this.angle_ = 0;
this.initialVelocity_ = 0;
- this.update(x, y);
};
View
1 src/ol/layer/imagelayer.exports
@@ -0,0 +1 @@
+@exportClass ol.layer.ImageLayer ol.layer.LayerOptions
View
24 src/ol/layer/imagelayer.js
@@ -0,0 +1,24 @@
+goog.provide('ol.layer.ImageLayer');
+
+goog.require('ol.layer.Layer');
+goog.require('ol.source.ImageSource');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.layer.Layer}
+ * @param {ol.layer.LayerOptions} layerOptions Layer options.
+ */
+ol.layer.ImageLayer = function(layerOptions) {
+ goog.base(this, layerOptions);
+};
+goog.inherits(ol.layer.ImageLayer, ol.layer.Layer);
+
+
+/**
+ * @return {ol.source.ImageSource} Single image source.
+ */
+ol.layer.ImageLayer.prototype.getImageSource = function() {
+ return /** @type {ol.source.ImageSource} */ (this.getSource());
+};
View
23 src/ol/rectangle.js
@@ -43,6 +43,16 @@ ol.Rectangle = function(minX, minY, maxX, maxY) {
/**
* @param {ol.Rectangle} rectangle Rectangle.
+ * @return {boolean} Equals.
+ */
+ol.Rectangle.prototype.equals = function(rectangle) {
+ return this.minX == rectangle.minX && this.minY == rectangle.minY &&
+ this.maxX == rectangle.maxX && this.maxY == rectangle.maxY;
+};
+
+
+/**
+ * @param {ol.Rectangle} rectangle Rectangle.
*/
ol.Rectangle.prototype.extend = function(rectangle) {
this.minX = Math.min(this.minX, rectangle.minX);
@@ -114,3 +124,16 @@ ol.Rectangle.prototype.normalize = function(coordinate) {
ol.Rectangle.prototype.toString = function() {
return '(' + [this.minX, this.minY, this.maxX, this.maxY].join(', ') + ')';
};
+
+
+/**
+ * @param {number} value Value.
+ */
+ol.Rectangle.prototype.scaleFromCenter = function(value) {
+ var deltaX = (this.getWidth() / 2.0) * (value - 1);
+ var deltaY = (this.getHeight() / 2.0) * (value - 1);
+ this.minX -= deltaX;
+ this.minY -= deltaY;
+ this.maxX += deltaX;
+ this.maxY += deltaY;
+};
View
115 src/ol/renderer/canvas/canvasimagelayerrenderer.js
@@ -0,0 +1,115 @@
+goog.provide('ol.renderer.canvas.ImageLayer');
+
+goog.require('goog.vec.Mat4');
+goog.require('ol.Image');
+goog.require('ol.ImageState');
+goog.require('ol.ViewHint');
+goog.require('ol.layer.ImageLayer');
+goog.require('ol.renderer.Map');
+goog.require('ol.renderer.canvas.Layer');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.canvas.Layer}
+ * @param {ol.renderer.Map} mapRenderer Map renderer.
+ * @param {ol.layer.ImageLayer} imageLayer Single image layer.
+ */
+ol.renderer.canvas.ImageLayer = function(mapRenderer, imageLayer) {
+
+ goog.base(this, mapRenderer, imageLayer);
+
+ /**
+ * @private
+ * @type {?ol.Image}
+ */
+ this.image_ = null;
+
+ /**
+ * @private
+ * @type {!goog.vec.Mat4.Number}
+ */
+ this.transform_ = goog.vec.Mat4.createNumber();
+
+};
+goog.inherits(ol.renderer.canvas.ImageLayer, ol.renderer.canvas.Layer);
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.ImageLayer.prototype.getImage = function() {
+ return goog.isNull(this.image_) ?
+ null : this.image_.getImageElement(this);
+};
+
+
+/**
+ * @return {ol.layer.ImageLayer} Single image layer.
+ */
+ol.renderer.canvas.ImageLayer.prototype.getImageLayer = function() {
+ return /** @type {ol.layer.ImageLayer} */ (this.getLayer());
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.ImageLayer.prototype.getTransform = function() {
+ return this.transform_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.canvas.ImageLayer.prototype.renderFrame =
+ function(frameState, layerState) {
+
+ var view2DState = frameState.view2DState;
+ var viewCenter = view2DState.center;
+ var viewResolution = view2DState.resolution;
+ var viewRotation = view2DState.rotation;
+
+ var image;
+ var imageLayer = this.getImageLayer();
+ var imageSource = imageLayer.getImageSource();
+
+ var hints = frameState.viewHints;
+
+ if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.PANNING]) {
+ image = imageSource.getImage(frameState.extent, viewResolution);
+ if (!goog.isNull(image)) {
+ var imageState = image.getState();
+ if (imageState == ol.ImageState.IDLE) {
+ goog.events.listenOnce(image, goog.events.EventType.CHANGE,
+ this.handleImageChange, false, this);
+ image.load();
+ } else if (imageState == ol.ImageState.LOADED) {
+ this.image_ = image;
+ }
+ }
+ }
+
+ if (!goog.isNull(this.image_)) {
+ image = this.image_;
+ var imageExtent = image.getExtent();
+ var imageResolution = image.getResolution();
+ var transform = this.transform_;
+ goog.vec.Mat4.makeIdentity(transform);
+ goog.vec.Mat4.translate(transform,
+ frameState.size.width / 2, frameState.size.height / 2, 0);
+ goog.vec.Mat4.rotateZ(transform, viewRotation);
+ goog.vec.Mat4.scale(
+ transform,
+ imageResolution / viewResolution,
+ imageResolution / viewResolution,
+ 1);
+ goog.vec.Mat4.translate(
+ transform,
+ (imageExtent.minX - viewCenter.x) / imageResolution,
+ (viewCenter.y - imageExtent.maxY) / imageResolution,
+ 0);
+ }
+};
View
6 src/ol/renderer/canvas/canvasmaprenderer.js
@@ -7,9 +7,11 @@ goog.require('goog.dom');
goog.require('goog.style');
goog.require('goog.vec.Mat4');
goog.require('ol.Size');
+goog.require('ol.layer.ImageLayer');
goog.require('ol.layer.TileLayer');
goog.require('ol.layer.Vector');
goog.require('ol.renderer.Map');
+goog.require('ol.renderer.canvas.ImageLayer');
goog.require('ol.renderer.canvas.TileLayer');
goog.require('ol.renderer.canvas.VectorLayer');
@@ -61,7 +63,9 @@ goog.inherits(ol.renderer.canvas.Map, ol.renderer.Map);
* @inheritDoc
*/
ol.renderer.canvas.Map.prototype.createLayerRenderer = function(layer) {
- if (layer instanceof ol.layer.TileLayer) {
+ if (layer instanceof ol.layer.ImageLayer) {
+ return new ol.renderer.canvas.ImageLayer(this, layer);
+ } else if (layer instanceof ol.layer.TileLayer) {
return new ol.renderer.canvas.TileLayer(this, layer);
} else if (layer instanceof ol.layer.Vector) {
return new ol.renderer.canvas.VectorLayer(this, layer);
View
122 src/ol/renderer/dom/domimagelayerrenderer.js
@@ -0,0 +1,122 @@
+goog.provide('ol.renderer.dom.ImageLayer');
+
+goog.require('goog.dom');
+goog.require('goog.vec.Mat4');
+goog.require('ol.Image');
+goog.require('ol.ImageState');
+goog.require('ol.ViewHint');
+goog.require('ol.dom');
+goog.require('ol.layer.ImageLayer');
+goog.require('ol.renderer.dom.Layer');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.dom.Layer}
+ * @param {ol.renderer.Map} mapRenderer Map renderer.
+ * @param {ol.layer.ImageLayer} imageLayer Image layer.
+ */
+ol.renderer.dom.ImageLayer = function(mapRenderer, imageLayer) {
+ var target = goog.dom.createElement(goog.dom.TagName.DIV);
+ target.className = 'ol-layer-image';
+ target.style.position = 'absolute';
+
+ goog.base(this, mapRenderer, imageLayer, target);
+
+ /**
+ * The last rendered image.
+ * @private
+ * @type {?ol.Image}
+ */
+ this.image_ = null;
+
+ /**
+ * @private
+ * @type {goog.vec.Mat4.AnyType}
+ */
+ this.transform_ = goog.vec.Mat4.createNumberIdentity();
+
+};
+goog.inherits(ol.renderer.dom.ImageLayer, ol.renderer.dom.Layer);
+
+
+/**
+ * @return {ol.layer.ImageLayer} Image layer.
+ */
+ol.renderer.dom.ImageLayer.prototype.getImageLayer = function() {
+ return /** @type {ol.layer.ImageLayer} */ (this.getLayer());
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.dom.ImageLayer.prototype.renderFrame =
+ function(frameState, layerState) {
+
+ var view2DState = frameState.view2DState;
+ var viewCenter = view2DState.center;
+ var viewResolution = view2DState.resolution;
+ var viewRotation = view2DState.rotation;
+
+ var image = this.image_;
+ var imageLayer = this.getImageLayer();
+ var imageSource = imageLayer.getImageSource();
+
+ var hints = frameState.viewHints;
+
+ if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.PANNING]) {
+ var image_ = imageSource.getImage(frameState.extent, viewResolution);
+ if (!goog.isNull(image_)) {
+ var imageState = image_.getState();
+ if (imageState == ol.ImageState.IDLE) {
+ goog.events.listenOnce(image_, goog.events.EventType.CHANGE,
+ this.handleImageChange, false, this);
+ image_.load();
+ } else if (imageState == ol.ImageState.LOADED) {
+ image = image_;
+ }
+ }
+ }
+
+ if (!goog.isNull(image)) {
+ var imageExtent = image.getExtent();
+ var imageResolution = image.getResolution();
+ var transform = goog.vec.Mat4.createNumber();
+ goog.vec.Mat4.makeIdentity(transform);
+ goog.vec.Mat4.translate(transform,
+ frameState.size.width / 2, frameState.size.height / 2, 0);
+ goog.vec.Mat4.rotateZ(transform, viewRotation);
+ goog.vec.Mat4.scale(
+ transform,
+ imageResolution / viewResolution,
+ imageResolution / viewResolution,
+ 1);
+ goog.vec.Mat4.translate(
+ transform,
+ (imageExtent.minX - viewCenter.x) / imageResolution,
+ (viewCenter.y - imageExtent.maxY) / imageResolution,
+ 0);
+ if (image != this.image_) {
+ var imageElement = image.getImageElement(this);
+ imageElement.style.position = 'absolute';
+ goog.dom.removeChildren(this.target);
+ goog.dom.appendChild(this.target, imageElement);
+ this.image_ = image;
+ }
+ this.setTransform(transform);
+ }
+
+};
+
+
+/**
+ * @param {goog.vec.Mat4.AnyType} transform Transform.
+ */
+ol.renderer.dom.ImageLayer.prototype.setTransform = function(transform) {
+ if (!goog.vec.Mat4.equals(transform, this.transform_)) {
+ ol.dom.transformElement2D(this.target, transform, 6);
+ goog.vec.Mat4.setFromArray(this.transform_, transform);
+ }
+};
View
15 src/ol/renderer/dom/dommaprenderer.js
@@ -5,8 +5,10 @@ goog.require('goog.asserts');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.style');
+goog.require('ol.layer.ImageLayer');
goog.require('ol.layer.TileLayer');
goog.require('ol.renderer.Map');
+goog.require('ol.renderer.dom.ImageLayer');
goog.require('ol.renderer.dom.TileLayer');
@@ -57,14 +59,15 @@ ol.renderer.dom.Map.prototype.addLayer = function(layer) {
* @inheritDoc
*/
ol.renderer.dom.Map.prototype.createLayerRenderer = function(layer) {
+ var layerRenderer;
if (layer instanceof ol.layer.TileLayer) {
- var layerRenderer = new ol.renderer.dom.TileLayer(this, layer);
- goog.dom.appendChild(this.layersPane_, layerRenderer.getTarget());
- return layerRenderer;
- } else {
- goog.asserts.assert(false);
- return null;
+ layerRenderer = new ol.renderer.dom.TileLayer(this, layer);
+ } else if (layer instanceof ol.layer.ImageLayer) {
+ layerRenderer = new ol.renderer.dom.ImageLayer(this, layer);
}
+ goog.asserts.assert(goog.isDef(layerRenderer));
+ goog.dom.appendChild(this.layersPane_, layerRenderer.getTarget());
+ return layerRenderer;
};
View
15 src/ol/renderer/layerrenderer.js
@@ -3,6 +3,8 @@ goog.provide('ol.renderer.Layer');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('ol.FrameState');
+goog.require('ol.Image');
+goog.require('ol.ImageState');
goog.require('ol.Object');
goog.require('ol.Tile');
goog.require('ol.TileCoord');
@@ -120,6 +122,19 @@ ol.renderer.Layer.prototype.handleLayerHueChange = goog.nullFunction;
/**
+ * Handle changes in image state.
+ * @param {goog.events.Event} event Image change event.
+ * @protected
+ */
+ol.renderer.Layer.prototype.handleImageChange = function(event) {
+ var image = /** @type {ol.Image} */ (event.target);
+ if (image.getState() === ol.ImageState.LOADED) {
+ this.getMap().requestRenderFrame();
+ }
+};
+
+
+/**
* @protected
*/
ol.renderer.Layer.prototype.handleLayerLoad = function() {
View
234 src/ol/renderer/webgl/webglimagelayerrenderer.js
@@ -0,0 +1,234 @@
+goog.provide('ol.renderer.webgl.ImageLayer');
+
+goog.require('goog.vec.Mat4');
+goog.require('ol.Coordinate');
+goog.require('ol.Extent');
+goog.require('ol.Image');
+goog.require('ol.ImageState');
+goog.require('ol.ViewHint');
+goog.require('ol.layer.ImageLayer');
+goog.require('ol.renderer.webgl.Layer');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.renderer.webgl.Layer}
+ * @param {ol.renderer.Map} mapRenderer Map renderer.
+ * @param {ol.layer.ImageLayer} imageLayer Tile layer.
+ */
+ol.renderer.webgl.ImageLayer = function(mapRenderer, imageLayer) {
+
+ goog.base(this, mapRenderer, imageLayer);
+
+ /**
+ * The last rendered image.
+ * @private
+ * @type {?ol.Image}
+ */
+ this.image_ = null;
+
+ /**
+ * The last rendered texture.
+ * @private
+ * @type {WebGLTexture}
+ */
+ this.texture_ = null;
+
+ /**
+ * @private
+ * @type {!goog.vec.Mat4.Number}
+ */
+ this.texCoordMatrix_ = goog.vec.Mat4.createNumberIdentity();
+
+ /**
+ * @private
+ * @type {!goog.vec.Mat4.Number}
+ */
+ this.vertexCoordMatrix_ = goog.vec.Mat4.createNumber();
+
+};
+goog.inherits(ol.renderer.webgl.ImageLayer, ol.renderer.webgl.Layer);
+
+
+/**
+ * @private
+ * @param {ol.Image} image Image.
+ * @return {WebGLTexture} Texture.
+ */
+ol.renderer.webgl.ImageLayer.prototype.createTexture_ = function(image) {
+
+ // We meet the conditions to work with non-power of two textures.
+ // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences#Non-Power_of_Two_Texture_Support
+ // http://learningwebgl.com/blog/?p=2101
+
+ var imageElement = image.getImageElement(this);
+ var gl = this.getMapRenderer().getGL();
+
+ var texture = gl.createTexture();
+
+ gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
+ gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA,
+ goog.webgl.RGBA, goog.webgl.UNSIGNED_BYTE, imageElement);
+
+ gl.texParameteri(
+ goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_S,
+ goog.webgl.CLAMP_TO_EDGE);
+ gl.texParameteri(
+ goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T,
+ goog.webgl.CLAMP_TO_EDGE);
+ gl.texParameteri(
+ goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER, goog.webgl.LINEAR);
+ gl.texParameteri(
+ goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, goog.webgl.LINEAR);
+
+ return texture;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.ImageLayer.prototype.disposeInternal = function() {
+ var mapRenderer = this.getMapRenderer();
+ var gl = mapRenderer.getGL();
+ if (!gl.isContextLost()) {
+ gl.deleteTexture(this.texture_);
+ }
+ goog.base(this, 'disposeInternal');
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.ImageLayer.prototype.getTexCoordMatrix = function() {
+ return this.texCoordMatrix_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.ImageLayer.prototype.getTexture = function() {
+ return this.texture_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.ImageLayer.prototype.getVertexCoordMatrix = function() {
+ return this.vertexCoordMatrix_;
+};
+
+
+/**
+ * @return {ol.layer.ImageLayer} Tile layer.
+ */
+ol.renderer.webgl.ImageLayer.prototype.getImageLayer = function() {
+ return /** @type {ol.layer.ImageLayer} */ (this.getLayer());
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.ImageLayer.prototype.handleWebGLContextLost = function() {
+ this.texture_ = null;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.ImageLayer.prototype.renderFrame =
+ function(frameState, layerState) {
+
+ var gl = this.getMapRenderer().getGL();
+
+ var view2DState = frameState.view2DState;
+ var viewCenter = view2DState.center;
+ var viewResolution = view2DState.resolution;
+ var viewRotation = view2DState.rotation;
+
+ var image = this.image_;
+ var texture = this.texture_;
+ var imageLayer = this.getImageLayer();
+ var imageSource = imageLayer.getImageSource();
+
+ var hints = frameState.viewHints;
+
+ if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.PANNING]) {
+ var image_ = imageSource.getImage(frameState.extent, viewResolution);
+ if (!goog.isNull(image_)) {
+ var imageState = image_.getState();
+ if (imageState == ol.ImageState.IDLE) {
+ goog.events.listenOnce(image_, goog.events.EventType.CHANGE,
+ this.handleImageChange, false, this);
+ image_.load();
+ } else if (imageState == ol.ImageState.LOADED) {
+ image = image_;
+ texture = this.createTexture_(image_);
+ if (!goog.isNull(this.texture_)) {
+ frameState.postRenderFunctions.push(
+ goog.partial(function(gl, texture) {
+ if (!gl.isContextLost()) {
+ gl.deleteTexture(texture);
+ }
+ }, gl, this.texture_));
+ }
+ }
+ }
+ }
+
+ if (!goog.isNull(image)) {
+ goog.asserts.assert(!goog.isNull(texture));
+
+ var canvas = this.getMapRenderer().getCanvas();
+
+ this.updateVertexCoordMatrix_(canvas.width, canvas.height,
+ viewCenter, viewResolution, viewRotation, image.getExtent());
+
+ // Translate and scale to flip the Y coord.
+ var texCoordMatrix = this.texCoordMatrix_;
+ goog.vec.Mat4.makeIdentity(texCoordMatrix);
+ goog.vec.Mat4.scale(texCoordMatrix, 1, -1, 1);
+ goog.vec.Mat4.translate(texCoordMatrix, 0, -1, 0);
+
+ this.image_ = image;
+ this.texture_ = texture;
+ }
+};
+
+
+/**
+ * @private
+ * @param {number} canvasWidth Canvas width.
+ * @param {number} canvasHeight Canvas height.
+ * @param {ol.Coordinate} viewCenter View center.
+ * @param {number} viewResolution View resolution.
+ * @param {number} viewRotation View rotation.
+ * @param {ol.Extent} imageExtent Image extent.
+ */
+ol.renderer.webgl.ImageLayer.prototype.updateVertexCoordMatrix_ =
+ function(canvasWidth, canvasHeight, viewCenter,
+ viewResolution, viewRotation, imageExtent) {
+
+ var canvasExtentWidth = canvasWidth * viewResolution;
+ var canvasExtentHeight = canvasHeight * viewResolution;
+
+ var vertexCoordMatrix = this.vertexCoordMatrix_;
+ goog.vec.Mat4.makeIdentity(vertexCoordMatrix);
+ goog.vec.Mat4.scale(vertexCoordMatrix,
+ 2 / canvasExtentWidth, 2 / canvasExtentHeight, 1);
+ goog.vec.Mat4.rotateZ(vertexCoordMatrix, -viewRotation);
+ goog.vec.Mat4.translate(vertexCoordMatrix,
+ imageExtent.minX - viewCenter.x,
+ imageExtent.minY - viewCenter.y,
+ 0);
+ goog.vec.Mat4.scale(vertexCoordMatrix,
+ imageExtent.getWidth() / 2, imageExtent.getHeight() / 2, 1);
+ goog.vec.Mat4.translate(vertexCoordMatrix, 1, 1, 0);
+
+};
View
6 src/ol/renderer/webgl/webgllayerrenderer.js
@@ -98,6 +98,12 @@ ol.renderer.webgl.Layer.prototype.getTexture = goog.abstractMethod;
/**
+ * @return {!goog.vec.Mat4.Number} Matrix.
+ */
+ol.renderer.webgl.Layer.prototype.getVertexCoordMatrix = goog.abstractMethod;
+
+
+/**
* @inheritDoc
*/
ol.renderer.webgl.Layer.prototype.handleLayerBrightnessChange = function() {
View
19 src/ol/renderer/webgl/webglmaprenderer.js
@@ -14,9 +14,11 @@ goog.require('goog.webgl');
goog.require('ol.FrameState');
goog.require('ol.Size');
goog.require('ol.Tile');
+goog.require('ol.layer.ImageLayer');
goog.require('ol.layer.TileLayer');
goog.require('ol.renderer.Map');
goog.require('ol.renderer.webgl.FragmentShader');
+goog.require('ol.renderer.webgl.ImageLayer');
goog.require('ol.renderer.webgl.TileLayer');
goog.require('ol.renderer.webgl.VertexShader');
goog.require('ol.structs.LRUCache');
@@ -79,11 +81,12 @@ ol.renderer.webgl.map.shader.Vertex = function() {
'attribute vec2 aTexCoord;',
'',
'uniform mat4 uTexCoordMatrix;',
+ 'uniform mat4 uVertexCoordMatrix;',
'',
'varying vec2 vTexCoord;',
'',
'void main(void) {',
- ' gl_Position = vec4(aPosition, 0., 1.);',
+ ' gl_Position = uVertexCoordMatrix * vec4(aPosition, 0., 1.);',
' vTexCoord = (uTexCoordMatrix * vec4(aTexCoord, 0., 1.)).st;',
'}'
].join('\n'));
@@ -159,7 +162,8 @@ ol.renderer.webgl.Map = function(container, map) {
* uColorMatrix: WebGLUniformLocation,
* uOpacity: WebGLUniformLocation,
* uTexture: WebGLUniformLocation,
- * uTexCoordMatrix: WebGLUniformLocation}|null}
+ * uTexCoordMatrix: WebGLUniformLocation,
+ * uVertexCoordMatrix: WebGLUniformLocation}|null}
*/
this.locations_ = null;
@@ -270,12 +274,15 @@ ol.renderer.webgl.Map.prototype.bindTileTexture =
* @inheritDoc
*/
ol.renderer.webgl.Map.prototype.createLayerRenderer = function(layer) {
+ var layerRenderer = null;
if (layer instanceof ol.layer.TileLayer) {
- return new ol.renderer.webgl.TileLayer(this, layer);
+ layerRenderer = new ol.renderer.webgl.TileLayer(this, layer);
+ } else if (layer instanceof ol.layer.ImageLayer) {
+ layerRenderer = new ol.renderer.webgl.ImageLayer(this, layer);
} else {
goog.asserts.assert(false);
- return null;
}
+ return layerRenderer;
};
@@ -519,6 +526,7 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
aTexCoord: gl.getAttribLocation(program, 'aTexCoord'),
uColorMatrix: gl.getUniformLocation(program, 'uColorMatrix'),
uTexCoordMatrix: gl.getUniformLocation(program, 'uTexCoordMatrix'),
+ uVertexCoordMatrix: gl.getUniformLocation(program, 'uVertexCoordMatrix'),
uOpacity: gl.getUniformLocation(program, 'uOpacity'),
uTexture: gl.getUniformLocation(program, 'uTexture')
};
@@ -556,6 +564,9 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
this.locations_.uTexCoordMatrix, false,
layerRenderer.getTexCoordMatrix());
gl.uniformMatrix4fv(
+ this.locations_.uVertexCoordMatrix, false,
+ layerRenderer.getVertexCoordMatrix());
+ gl.uniformMatrix4fv(
this.locations_.uColorMatrix, false, layerRenderer.getColorMatrix());
gl.uniform1f(this.locations_.uOpacity, layer.getOpacity());
gl.bindTexture(goog.webgl.TEXTURE_2D, layerRenderer.getTexture());
View
14 src/ol/renderer/webgl/webgltilelayerrenderer.js
@@ -143,6 +143,12 @@ ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) {
/**
* @private
+ * @type {!goog.vec.Mat4.Number}
+ */
+ this.vertexCoordMatrix_ = goog.vec.Mat4.createNumberIdentity();
+
+ /**
+ * @private
* @type {ol.TileRange}
*/
this.renderedTileRange_ = null;
@@ -238,6 +244,14 @@ ol.renderer.webgl.TileLayer.prototype.getTexture = function() {
/**
+ * @inheritDoc
+ */
+ol.renderer.webgl.TileLayer.prototype.getVertexCoordMatrix = function() {
+ return this.vertexCoordMatrix_;
+};
+
+
+/**
* @return {ol.layer.TileLayer} Tile layer.
*/
ol.renderer.webgl.TileLayer.prototype.getTileLayer = function() {
View
113 src/ol/source/imagesource.js
@@ -0,0 +1,113 @@
+goog.provide('ol.source.ImageSource');
+
+goog.require('goog.array');
+goog.require('ol.Attribution');
+goog.require('ol.Extent');
+goog.require('ol.Image');
+goog.require('ol.ImageUrlFunction');
+goog.require('ol.ImageUrlFunctionType');
+goog.require('ol.Projection');
+goog.require('ol.Size');
+goog.require('ol.array');
+goog.require('ol.source.Source');
+
+
+/**
+ * @typedef {{attributions: (Array.<ol.Attribution>|undefined),
+ * crossOrigin: (null|string|undefined),
+ * extent: (null|ol.Extent|undefined),
+ * projection: (ol.Projection|undefined),
+ * resolutions: (Array.<number>|undefined),
+ * imageUrlFunction: (ol.ImageUrlFunctionType|
+ * undefined)}}
+ */
+ol.source.ImageSourceOptions;
+
+
+
+/**
+ * @constructor
+ * @extends {ol.source.Source}
+ * @param {ol.source.ImageSourceOptions} options Single
+ * image source options.
+ */
+ol.source.ImageSource = function(options) {
+
+ goog.base(this, {
+ attributions: options.attributions,
+ extent: options.extent,
+ projection: options.projection
+ });
+
+ /**
+ * @protected
+ * @type {ol.ImageUrlFunctionType}
+ */
+ this.imageUrlFunction =
+ goog.isDef(options.imageUrlFunction) ?
+ options.imageUrlFunction :
+ ol.ImageUrlFunction.nullImageUrlFunction;
+
+ /**
+ * @private
+ * @type {?string}
+ */
+ this.crossOrigin_ =
+ goog.isDef(options.crossOrigin) ? options.crossOrigin : 'anonymous';
+
+ /**
+ * @private
+ * @type {Array.<number>}
+ */
+ this.resolutions_ = goog.isDef(options.resolutions) ?
+ options.resolutions : null;
+ goog.asserts.assert(goog.isNull(this.resolutions_) ||
+ goog.array.isSorted(this.resolutions_,
+ function(a, b) {
+ return b - a;
+ }, true));
+
+};
+goog.inherits(ol.source.ImageSource, ol.source.Source);
+
+
+/**
+ * @protected
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @param {ol.Size} size Size.
+ * @return {ol.Image} Single image.
+ */
+ol.source.ImageSource.prototype.createImage =
+ function(extent, resolution, size) {
+ var image = null;
+ var imageUrl = this.imageUrlFunction(extent, size);
+ if (goog.isDef(imageUrl)) {
+ image = new ol.Image(
+ extent, resolution, imageUrl, this.crossOrigin_);
+ }
+ return image;
+};
+
+
+/**
+ * @protected
+ * @param {number} resolution Resolution.
+ * @return {number} Resolution.
+ */
+ol.source.ImageSource.prototype.findNearestResolution =
+ function(resolution) {
+ if (!goog.isNull(this.resolutions_)) {
+ var idx = ol.array.linearFindNearest(this.resolutions_, resolution);
+ resolution = this.resolutions_[idx];
+ }
+ return resolution;
+};
+
+
+/**
+ * @param {ol.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @return {ol.Image} Single image.
+ */
+ol.source.ImageSource.prototype.getImage = goog.abstractMethod;
View
1 src/ol/source/singleimagewms.exports
@@ -0,0 +1 @@
+@exportSymbol ol.source.SingleImageWMS
View
99 src/ol/source/singleimagewmssource.js
@@ -0,0 +1,99 @@
+goog.provide('ol.source.SingleImageWMS');
+
+goog.require('ol.Extent');
+goog.require('ol.Image');
+goog.require('ol.ImageUrlFunction');
+goog.require('ol.Projection');
+goog.require('ol.Size');
+goog.require('ol.source.ImageSource');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.source.ImageSource}
+ * @param {ol.source.SingleImageWMSOptions} options Options.
+ */
+ol.source.SingleImageWMS = function(options) {
+
+ var projection = ol.Projection.createProjection(
+ options.projection, 'EPSG:3857');
+ var projectionExtent = projection.getExtent();
+
+ var extent = goog.isDef(options.extent) ?
+ options.extent : projectionExtent;
+
+ var version = goog.isDef(options.version) ?
+ options.version : '1.3';
+
+ var baseParams = {
+ 'SERVICE': 'WMS',
+ 'VERSION': version,
+ 'REQUEST': 'GetMap',
+ 'STYLES': '',
+ 'FORMAT': 'image/png',
+ 'TRANSPARENT': true
+ };
+ baseParams[version >= '1.3' ? 'CRS' : 'SRS'] = projection.getCode();
+ goog.object.extend(baseParams, options.params);
+
+ var imageUrlFunction;
+ if (options.url) {
+ var url = goog.uri.utils.appendParamsFromMap(
+ options.url, baseParams);
+ imageUrlFunction = ol.ImageUrlFunction.createBboxParam(url);
+ } else {
+ imageUrlFunction =
+ ol.ImageUrlFunction.nullImageUrlFunction;
+ }
+
+ goog.base(this, {
+ attributions: options.attributions,
+ crossOrigin: options.crossOrigin,
+ extent: extent,
+ projection: projection,
+ resolutions: options.resolutions,
+ imageUrlFunction: imageUrlFunction
+ });
+
+ /**
+ * @private
+ * @type {ol.Image}
+ */
+ this.image_ = null;
+
+ /**
+ * FIXME configurable?
+ * @private
+ * @type {number}
+ */
+ this.ratio_ = 1.5;
+
+};
+goog.inherits(ol.source.SingleImageWMS, ol.source.ImageSource);
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.SingleImageWMS.prototype.getImage =
+ function(extent, resolution) {
+ resolution = this.findNearestResolution(resolution);
+
+ var image = this.image_;
+ if (!goog.isNull(image) &&
+ image.getResolution() == resolution &&
+ image.getExtent().containsExtent(extent)) {
+ return image;
+ }
+
+ extent = new ol.Extent(extent.minX, extent.minY,
+ extent.maxX, extent.maxY);
+ extent.scaleFromCenter(this.ratio_);
+ var width = extent.getWidth() / resolution;
+ var height = extent.getHeight() / resolution;
+ var size = new ol.Size(width, height);
+
+ this.image_ = this.createImage(extent, resolution, size);
+ return this.image_;
+};
View
1 src/ol/source/staticimage.exports
@@ -0,0 +1 @@
+@exportSymbol ol.source.StaticImage
View
61 src/ol/source/staticimagesource.js
@@ -0,0 +1,61 @@
+goog.provide('ol.source.StaticImage');
+
+goog.require('ol.Image');
+goog.require('ol.ImageUrlFunctionType');
+goog.require('ol.source.ImageSource');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.source.ImageSource}
+ * @param {ol.source.StaticImageOptions} options Options.
+ */
+ol.source.StaticImage = function(options) {
+
+ var imageFunction = ol.source.StaticImage.createImageFunction(
+ options.url);
+
+ var imageExtent = options.imageExtent;
+ var imageSize = options.imageSize;
+ var imageResolution = imageExtent.getHeight() / imageSize.height;
+
+ goog.base(this, {
+ attributions: options.attributions,
+ crossOrigin: options.crossOrigin,
+ extent: options.extent,
+ projection: options.projection,
+ imageUrlFunction: imageFunction,
+ resolutions: [imageResolution]
+ });
+
+ /**
+ * @private
+ * @type {ol.Image}
+ */
+ this.image_ = this.createImage(imageExtent, imageResolution, imageSize);
+
+};
+goog.inherits(ol.source.StaticImage, ol.source.ImageSource);
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.StaticImage.prototype.getImage = function(extent, resolution) {
+ if (extent.intersects(this.image_.getExtent())) {
+ return this.image_;
+ }
+ return null;
+};
+
+
+/**
+ * @param {string|undefined} url URL.
+ * @return {ol.ImageUrlFunctionType} Function.
+ */
+ol.source.StaticImage.createImageFunction = function(url) {
+ return function(extent, size) {
+ return url;
+ };
+};
View
189 src/ol/sphere.js
@@ -0,0 +1,189 @@
+/**
+ * @license
+ * Latitude/longitude spherical geodesy formulae taken from
+ * http://www.movable-type.co.uk/scripts/latlong.html
+ * Licenced under CC-BY-3.0.
+ */
+
+// FIXME add intersection of two paths given start points and bearings
+// FIXME add rhumb lines
+
+goog.provide('ol.Sphere');
+
+goog.require('goog.math');
+goog.require('ol.Coordinate');
+
+
+
+/**
+ * @constructor
+ * @param {number} radius Radius.
+ */
+ol.Sphere = function(radius) {
+
+ /**
+ * @type {number}
+ */
+ this.radius = radius;
+
+};
+
+
+/**
+ * Returns the distance from c1 to c2 using the spherical law of cosines.
+ *
+ * @param {ol.Coordinate} c1 Coordinate 1.
+ * @param {ol.Coordinate} c2 Coordinate 2.
+ * @return {number} Spherical law of cosines distance.
+ */
+ol.Sphere.prototype.cosineDistance = function(c1, c2) {
+ var lat1 = goog.math.toRadians(c1.y);
+ var lat2 = goog.math.toRadians(c2.y);
+ var deltaLon = goog.math.toRadians(c2.x - c1.x);
+ return this.radius * Math.acos(
+ Math.sin(lat1) * Math.sin(lat2) +
+ Math.cos(lat1) * Math.cos(lat2) * Math.cos(deltaLon));
+};
+
+
+/**
+ * Returns the distance of c3 from the great circle path defined by c1 and c2.
+ *
+ * @param {ol.Coordinate} c1 Coordinate 1.
+ * @param {ol.Coordinate} c2 Coordinate 2.
+ * @param {ol.Coordinate} c3 Coordinate 3.
+ * @return {number} Cross-track distance.
+ */
+ol.Sphere.prototype.crossTrackDistance = function(c1, c2, c3) {
+ var d12 = this.cosineDistance(c1, c2);
+ var d13 = this.cosineDistance(c1, c2);
+ var theta12 = goog.math.toRadians(this.initialBearing(c1, c2));
+ var theta13 = goog.math.toRadians(this.initialBearing(c1, c3));
+ return this.radius *
+ Math.asin(Math.sin(d13 / this.radius) * Math.sin(theta13 - theta12));
+};
+
+
+/**
+ * Returns the distance from c1 to c2 using Pythagoras's theorem on an
+ * equirectangular projection.
+ *
+ * @param {ol.Coordinate} c1 Coordinate 1.
+ * @param {ol.Coordinate} c2 Coordinate 2.
+ * @return {number} Equirectangular distance.
+ */
+ol.Sphere.prototype.equirectangularDistance = function(c1, c2) {
+ var lat1 = goog.math.toRadians(c1.y);
+ var lat2 = goog.math.toRadians(c2.y);
+ var deltaLon = goog.math.toRadians(c2.x - c1.x);
+ var x = deltaLon * Math.cos((lat1 + lat2) / 2);
+ var y = lat2 - lat1;
+ return this.radius * Math.sqrt(x * x + y * y);
+};
+
+
+/**
+ * Returns the final bearing from c1 to c2.
+ *
+ * @param {ol.Coordinate} c1 Coordinate 1.
+ * @param {ol.Coordinate} c2 Coordinate 2.
+ * @return {number} Initial bearing.
+ */
+ol.Sphere.prototype.finalBearing = function(c1, c2) {
+ return (this.initialBearing(c2, c1) + 180) % 360;
+};
+
+
+/**
+ * Returns the distance from c1 to c2 using the haversine formula.
+ *
+ * @param {ol.Coordinate} c1 Coordinate 1.
+ * @param {ol.Coordinate} c2 Coordinate 2.
+ * @return {number} Haversine distance.
+ */
+ol.Sphere.prototype.haversineDistance = function(c1, c2) {
+ var lat1 = goog.math.toRadians(c1.y);
+ var lat2 = goog.math.toRadians(c2.y);
+ var deltaLatBy2 = (lat2 - lat1) / 2;
+ var deltaLonBy2 = goog.math.toRadians(c2.x - c1.x) / 2;
+ var a = Math.sin(deltaLatBy2) * Math.sin(deltaLatBy2) +
+ Math.sin(deltaLonBy2) * Math.sin(deltaLonBy2) *
+ Math.cos(lat1) * Math.cos(lat2);
+ return 2 * this.radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+};
+
+
+/**
+ * Returns the initial bearing from c1 to c2.
+ *
+ * @param {ol.Coordinate} c1 Coordinate 1.
+ * @param {ol.Coordinate} c2 Coordinate 2.
+ * @return {number} Initial bearing.
+ */
+ol.Sphere.prototype.initialBearing = function(c1, c2) {
+ var lat1 = goog.math.toRadians(c1.y);
+ var lat2 = goog.math.toRadians(c2.y);
+ var deltaLon = goog.math.toRadians(c2.x - c1.x);
+ var y = Math.sin(deltaLon) * Math.cos(lat2);
+ var x = Math.cos(lat1) * Math.sin(lat2) -
+ Math.sin(lat1) * Math.cos(lat2) * Math.cos(deltaLon);
+ return goog.math.toDegrees(Math.atan2(y, x));
+};
+
+
+/**
+ * Returns the maximum latitude of the great circle defined by bearing and
+ * latitude.
+ *
+ * @param {number} bearing Bearing.
+ * @param {number} latitude Latitude.
+ * @return {number} Maximum latitude.
+ */
+ol.Sphere.prototype.maximumLatitude = function(bearing, latitude) {
+ return Math.cos(Math.abs(Math.sin(goog.math.toRadians(bearing)) *
+ Math.cos(goog.math.toRadians(latitude))));
+};
+
+
+/**
+ * Returns the midpoint between c1 and c2.
+ *
+ * @param {ol.Coordinate} c1 Coordinate 1.
+ * @param {ol.Coordinate} c2 Coordinate 2.
+ * @return {ol.Coordinate} Midpoint.
+ */
+ol.Sphere.prototype.midpoint = function(c1, c2) {
+ var lat1 = goog.math.toRadians(c1.y);
+ var lat2 = goog.math.toRadians(c2.y);
+ var lon1 = goog.math.toRadians(c1.x);
+ var deltaLon = goog.math.toRadians(c2.x - c1.x);
+ var Bx = Math.cos(lat2) * Math.cos(deltaLon);
+ var By = Math.cos(lat2) * Math.sin(deltaLon);
+ var cosLat1PlusBx = Math.cos(lat1) + Bx;
+ var lat = Math.atan2(Math.sin(lat1) + Math.sin(lat2),
+ Math.sqrt(cosLat1PlusBx * cosLat1PlusBx + By * By));
+ var lon = lon1 + Math.atan2(By, cosLat1PlusBx);
+ return new ol.Coordinate(goog.math.toDegrees(lon), goog.math.toDegrees(lat));
+};
+
+
+/**
+ * Returns the coordinate at the given distance and bearing from c.
+ *
+ * @param {ol.Coordinate} c1 Coordinate.
+ * @param {number} distance Distance.
+ * @param {number} bearing Bearing.
+ * @return {ol.Coordinate} Coordinate.
+ */
+ol.Sphere.prototype.offset = function(c1, distance, bearing) {
+ var lat1 = goog.math.toRadians(c1.y);
+ var lon1 = goog.math.toRadians(c1.x);
+ var dByR = distance / this.radius;
+ var lat = Math.asin(
+ Math.sin(lat1) * Math.cos(dByR) +
+ Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing));
+ var lon = lon1 + Math.atan2(
+ Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
+ Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat));
+ return new ol.Coordinate(goog.math.toDegrees(lon), goog.math.toDegrees(lat));
+};
View
10 src/ol/tilerange.js
@@ -62,16 +62,6 @@ ol.TileRange.prototype.containsTileRange = function(tileRange) {
/**
- * @param {ol.TileRange} tileRange Tile range.
- * @return {boolean} Equals.
- */
-ol.TileRange.prototype.equals = function(tileRange) {
- return this.minX == tileRange.minX && tileRange.maxX == this.maxX &&
- this.minY == tileRange.minY && tileRange.minY == this.minY;
-};
-
-
-/**
* @inheritDoc
* @return {number} Height.
*/
View
1 test/ol.html
@@ -25,6 +25,7 @@
<script type="text/javascript" src="jasmine-1.2.0/jasmine-html.js"></script>
<script type="text/javascript" src="phantom-jasmine/console-runner.js"></script>
<script type="text/javascript" src="jasmine-extensions.js"></script>
+ <script type="text/javascript" src="../build/proj4js/lib/proj4js-combined.js"></script>
<script type="text/javascript">
View
391 test/spec/ol/ellipsoid.test.js