diff --git a/.gitignore b/.gitignore index a691d821e3..24c0e4318d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ /build/ol.js /build/ol-all.js /build/src +/build/phantomjs-*-windows +/build/phantomjs-*-windows.zip /examples/*.json /examples/*.combined.js /examples/example-list.js diff --git a/build.cmd b/build.cmd new file mode 100755 index 0000000000..75c1454306 --- /dev/null +++ b/build.cmd @@ -0,0 +1,2 @@ +@echo off +%SystemDrive%\Python27\python.exe build.py %* diff --git a/build.py b/build.py index 3349b31c0c..eb1dd3d6ba 100755 --- a/build.py +++ b/build.py @@ -1,10 +1,10 @@ #!/usr/bin/env python from cStringIO import StringIO -import glob import gzip import json import os +import os.path import re import shutil import sys @@ -13,19 +13,23 @@ from pake import Target, ifind, main, output, rule, target, variables, virtual if sys.platform == 'win32': - variables.GIT = 'C:/Program Files/Git/bin/git.exe' - variables.GJSLINT = 'gjslint' # FIXME - variables.JAVA = 'C:/Program Files/Java/jre7/bin/java.exe' + ProgramFiles = os.environ.get('ProgramFiles', 'C:\\Program Files') + ProgramFiles_X86 = os.environ.get('ProgramFiles(X86)', 'C:\\Program Files') + Python27 = os.environ.get('SystemDrive', 'C:') + '\\Python27' + variables.GIT = os.path.join(ProgramFiles_X86, 'Git', 'bin', 'git.exe') + variables.GJSLINT = os.path.join(Python27, 'Scripts', 'gjslint.exe') + variables.JAVA = os.path.join(ProgramFiles, 'Java', 'jre7', 'bin', 'java.exe') variables.JSDOC = 'jsdoc' # FIXME - variables.PHANTOMJS = 'phantomjs' # FIXME - variables.PYTHON = 'C:/Python27/python.exe' + 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' else: variables.GIT = 'git' variables.GJSLINT = 'gjslint' variables.JAVA = 'java' variables.JSDOC = 'jsdoc' - variables.PHANTOMJS = 'phantomjs' variables.PYTHON = 'python' + PHANTOMJS = 'phantomjs' variables.BRANCH = output('%(GIT)s', 'rev-parse', '--abbrev-ref', 'HEAD').strip() @@ -40,9 +44,14 @@ EXTERNAL_SRC = [ 'build/src/external/src/types.js'] EXAMPLES = [path - for path in glob.glob('examples/*.html') + for path in ifind('examples') + if not path.startswith('examples/standalone/') + if path.endswith('.html') if path != 'examples/example-list.html'] +EXAMPLES_JSON = [example.replace('.html', '.json') + for example in EXAMPLES] + EXAMPLES_SRC = [path for path in ifind('examples') if path.endswith('.js') @@ -76,7 +85,7 @@ def report_sizes(t): t.info(' compressed: %d bytes', len(stringio.getvalue())) -virtual('all', 'build-all', 'build', 'examples') +virtual('all', 'build-all', 'build', 'examples', 'precommit') virtual('precommit', 'lint', 'build-all', 'test', 'build', 'build-examples', 'doc') @@ -152,7 +161,12 @@ def build_src_internal_types_js(t): virtual('build-examples', 'examples', (path.replace('.html', '.combined.js') for path in EXAMPLES)) -virtual('examples', 'examples/example-list.js', (path.replace('.html', '.json') for path in EXAMPLES)) +virtual('examples', 'examples/example-list.xml', (path.replace('.html', '.json') for path in EXAMPLES)) + + +@target('examples/example-list.xml', 'examples/example-list.js') +def examples_examples_list_xml(t): + t.touch() # already generated by bin/exampleparser.py @target('examples/example-list.js', 'bin/exampleparser.py', EXAMPLES) @@ -188,12 +202,12 @@ def examples_star_combined_js(name, match): @target('serve', PLOVR_JAR, INTERNAL_SRC, 'test/requireall.js', 'examples') def serve(t): - t.run('%(JAVA)s', '-jar', PLOVR_JAR, 'serve', glob.glob('build/*.json'), glob.glob('examples/*.json'), glob.glob('test/*.json')) + t.run('%(JAVA)s', '-jar', PLOVR_JAR, 'serve', 'build/ol.json', 'build/ol-all.json', EXAMPLES_JSON, 'test/test.json') @target('serve-precommit', PLOVR_JAR, INTERNAL_SRC) def serve_precommit(t): - t.run('%(JAVA)s', '-jar', PLOVR_JAR, 'serve', 'build/ol-all.json') + t.run('%(JAVA)s', '-jar', PLOVR_JAR, 'serve', 'build/ol-all.json', 'test/test.json') virtual('lint', 'build/lint-src-timestamp', 'build/lint-spec-timestamp', 'build/check-requires-timestamp') @@ -321,9 +335,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', INTERNAL_SRC, 'test/requireall.js', phony=True) +@target('test', PHANTOMJS, INTERNAL_SRC, 'test/requireall.js', phony=True) def test(t): - t.run('%(PHANTOMJS)s', 'test/phantom-jasmine/run_jasmine_test.coffee', 'test/ol.html') + t.run(PHANTOMJS, 'test/phantom-jasmine/run_jasmine_test.coffee', 'test/ol.html') + + +if sys.platform == 'win32': + @target(PHANTOMJS, PHANTOMJS_WINDOWS_ZIP, clean=False) + def phantom_js(t): + from zipfile import ZipFile + ZipFile(PHANTOMJS_WINDOWS_ZIP).extractall('build') + + @target(PHANTOMJS_WINDOWS_ZIP, clean=False) + def phantomjs_windows_zip(t): + t.download('http://phantomjs.googlecode.com/files/' + os.path.basename(t.name)) + +else: + virtual(PHANTOMJS) @target('fixme', phony=True) @@ -348,5 +376,17 @@ def find_fixme(t): print print "A total number of", totalcount, "TODO/FIXME was found" + +@target('reallyclean') +def reallyclean(t): + """Removes untracked files and folders from previous builds.""" + # -X => only clean up files that are usually ignored e.g. + # through .gitignore + # -d => also consider directories for deletion + # -f => if git configuration variable clean.requireForce != false, + # git clean will refuse to run unless given -f or -n. + t.run('%(GIT)s', 'clean', '-X', '-d', '-f', '.') + + if __name__ == '__main__': main() diff --git a/examples/full-screen.html b/examples/full-screen.html index 53db08f8af..11a3d5d560 100644 --- a/examples/full-screen.html +++ b/examples/full-screen.html @@ -21,6 +21,12 @@ padding: 0 0.5em 0.5em 0.5em; border-radius: 4px; } + #geolocation { + background-color: white; + border: 1px solid #222; + border-radius: 10px 10px 10px 0; + padding: 3px; + } @media only screen and (max-width: 600px) { #text { display: none; @@ -31,6 +37,7 @@
+

Full-screen example

Example of a full-screen map.
diff --git a/examples/full-screen.js b/examples/full-screen.js index 3e9cce6f89..eb13bda2f2 100644 --- a/examples/full-screen.js +++ b/examples/full-screen.js @@ -1,8 +1,11 @@ goog.require('goog.debug.Console'); goog.require('goog.debug.Logger'); goog.require('goog.debug.Logger.Level'); +goog.require('goog.style'); +goog.require('ol.AnchoredElement'); goog.require('ol.Collection'); goog.require('ol.Coordinate'); +goog.require('ol.Geolocation'); goog.require('ol.Map'); goog.require('ol.RendererHints'); goog.require('ol.View2D'); @@ -28,3 +31,14 @@ var map = new ol.Map({ zoom: 0 }) }); + +var geolocation = new ol.Geolocation(); +geolocation.bindTo('projection', map.getView()); + +var element = document.getElementById('geolocation'); +var marker = new ol.AnchoredElement({ + map: map, + element: element +}); +marker.bindTo('position', geolocation); +goog.style.showElement(element, true); diff --git a/examples/side-by-side.js b/examples/side-by-side.js index 4c5bb354b7..b5d108b0df 100644 --- a/examples/side-by-side.js +++ b/examples/side-by-side.js @@ -42,12 +42,13 @@ var domMap = new ol.Map({ view: view }); -domMap.getControls().push(new ol.control.MousePosition({ +var domMousePosition = new ol.control.MousePosition({ coordinateFormat: ol.Coordinate.toStringHDMS, projection: ol.Projection.getFromCode('EPSG:4326'), target: document.getElementById('domMousePosition'), undefinedHTML: ' ' -})); +}); +domMousePosition.setMap(domMap); var webglMap = new ol.Map({ renderer: ol.RendererHint.WEBGL, @@ -58,12 +59,13 @@ if (webglMap !== null) { webglMap.bindTo('view', domMap); } -webglMap.getControls().push(new ol.control.MousePosition({ +var webglMousePosition = new ol.control.MousePosition({ coordinateFormat: ol.Coordinate.toStringHDMS, projection: ol.Projection.getFromCode('EPSG:4326'), target: document.getElementById('webglMousePosition'), undefinedHTML: ' ' -})); +}); +webglMousePosition.setMap(webglMap); var canvasMap = new ol.Map({ renderer: ol.RendererHint.CANVAS, @@ -74,12 +76,13 @@ if (canvasMap !== null) { canvasMap.bindTo('view', domMap); } -canvasMap.getControls().push(new ol.control.MousePosition({ +var canvasMousePosition = new ol.control.MousePosition({ coordinateFormat: ol.Coordinate.toStringHDMS, projection: ol.Projection.getFromCode('EPSG:4326'), target: document.getElementById('canvasMousePosition'), undefinedHtml: ' ' -})); +}); +canvasMousePosition.setMap(canvasMap); var keyboardInteraction = new ol.interaction.Keyboard(); keyboardInteraction.addCallback('0', function() { @@ -110,7 +113,7 @@ keyboardInteraction.addCallback('H', function() { layer.setHue(layer.getHue() + (Math.PI / 5)); }); keyboardInteraction.addCallback('j', function() { - var bounce = ol.animation.createBounce({ + var bounce = ol.animation.bounce({ resolution: 2 * view.getResolution() }); domMap.addPreRenderFunction(bounce); @@ -118,69 +121,69 @@ keyboardInteraction.addCallback('j', function() { canvasMap.addPreRenderFunction(bounce); }); keyboardInteraction.addCallback('l', function() { - var panFrom = ol.animation.createPanFrom({ + var pan = ol.animation.pan({ source: view.getCenter(), easing: ol.easing.elastic }); - domMap.addPreRenderFunction(panFrom); - webglMap.addPreRenderFunction(panFrom); - canvasMap.addPreRenderFunction(panFrom); + domMap.addPreRenderFunction(pan); + webglMap.addPreRenderFunction(pan); + canvasMap.addPreRenderFunction(pan); view.setCenter(LONDON); }); keyboardInteraction.addCallback('L', function() { var start = goog.now(); var duration = 5000; - var bounce = ol.animation.createBounce({ + var bounce = ol.animation.bounce({ resolution: 2 * view.getResolution(), start: start, duration: duration }); - var panFrom = ol.animation.createPanFrom({ + var pan = ol.animation.pan({ source: view.getCenter(), start: start, duration: duration }); - var spin = ol.animation.createSpin({ - turns: 2, + var rotate = ol.animation.rotate({ + rotation: 4 * Math.PI, start: start, duration: duration }); - var preRenderFunctions = [bounce, panFrom, spin]; + var preRenderFunctions = [bounce, pan, rotate]; domMap.addPreRenderFunctions(preRenderFunctions); webglMap.addPreRenderFunctions(preRenderFunctions); canvasMap.addPreRenderFunctions(preRenderFunctions); view.setCenter(LONDON); }); keyboardInteraction.addCallback('m', function() { - var panFrom = ol.animation.createPanFrom({ + var pan = ol.animation.pan({ source: view.getCenter(), duration: 1000, easing: ol.easing.bounce }); - domMap.addPreRenderFunction(panFrom); - webglMap.addPreRenderFunction(panFrom); - canvasMap.addPreRenderFunction(panFrom); + domMap.addPreRenderFunction(pan); + webglMap.addPreRenderFunction(pan); + canvasMap.addPreRenderFunction(pan); view.setCenter(MOSCOW); }); keyboardInteraction.addCallback('M', function() { var start = goog.now(); var duration = 5000; - var bounce = ol.animation.createBounce({ + var bounce = ol.animation.bounce({ resolution: 2 * view.getResolution(), start: start, duration: duration }); - var panFrom = ol.animation.createPanFrom({ + var pan = ol.animation.pan({ source: view.getCenter(), start: start, duration: duration }); - var spin = ol.animation.createSpin({ - turns: -2, + var rotate = ol.animation.rotate({ + rotation: -4 * Math.PI, start: start, duration: duration }); - var preRenderFunctions = [bounce, panFrom, spin]; + var preRenderFunctions = [bounce, pan, rotate]; domMap.addPreRenderFunctions(preRenderFunctions); webglMap.addPreRenderFunctions(preRenderFunctions); canvasMap.addPreRenderFunctions(preRenderFunctions); @@ -206,21 +209,21 @@ keyboardInteraction.addCallback('vV', function() { layer.setVisible(!layer.getVisible()); }); keyboardInteraction.addCallback('x', function() { - var spin = ol.animation.createSpin({ - turns: 2, + var rotate = ol.animation.rotate({ + rotation: 4 * Math.PI, duration: 2000 }); - domMap.addPreRenderFunction(spin); - webglMap.addPreRenderFunction(spin); - canvasMap.addPreRenderFunction(spin); + domMap.addPreRenderFunction(rotate); + webglMap.addPreRenderFunction(rotate); + canvasMap.addPreRenderFunction(rotate); }); keyboardInteraction.addCallback('X', function() { - var spin = ol.animation.createSpin({ - turns: -2, + var rotate = ol.animation.rotate({ + rotation: -4 * Math.PI, duration: 2000 }); - domMap.addPreRenderFunction(spin); - webglMap.addPreRenderFunction(spin); - canvasMap.addPreRenderFunction(spin); + domMap.addPreRenderFunction(rotate); + webglMap.addPreRenderFunction(rotate); + canvasMap.addPreRenderFunction(rotate); }); domMap.getInteractions().push(keyboardInteraction); diff --git a/pake.py b/pake.py index dec8b71a7a..27b3648580 100644 --- a/pake.py +++ b/pake.py @@ -288,7 +288,10 @@ def ifind(*paths): for path in paths: for dirpath, dirnames, names in os.walk(path): for name in names: - yield os.path.join(dirpath, name) + if sys.platform == 'win32': + yield '/'.join(dirpath.split('\\') + [name]) + else: + yield os.path.join(dirpath, name) def main(argv=sys.argv): diff --git a/readme.md b/readme.md index 62b9f9ed5e..723a41881d 100644 --- a/readme.md +++ b/readme.md @@ -13,12 +13,16 @@ Run build.py: $ ./build.py +Windows users should run `build` instead. + ## Run examples locally Run the [Plovr](http://plovr.com/) web server with: $ ./build.py serve +Windows users should run `build serve` instead. + Then, either open one of the example html files from the `examples` directory directly in your browser, or start a simple webserver, for example: $ python -mSimpleHTTPServer diff --git a/src/objectliterals.exports b/src/objectliterals.exports index bbd66ec223..cf5e830f3f 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -1,5 +1,5 @@ @exportObjectLiteral ol.MapOptions -@exportObjectLiteralProperty ol.MapOptions.controls ol.Collection|undefined +@exportObjectLiteralProperty ol.MapOptions.attributionControl boolean|undefined @exportObjectLiteralProperty ol.MapOptions.doubleClickZoom boolean|undefined @exportObjectLiteralProperty ol.MapOptions.dragPan boolean|undefined @exportObjectLiteralProperty ol.MapOptions.interactions ol.Collection|undefined @@ -13,6 +13,7 @@ @exportObjectLiteralProperty ol.MapOptions.shiftDragZoom boolean|undefined @exportObjectLiteralProperty ol.MapOptions.target Element|string @exportObjectLiteralProperty ol.MapOptions.view ol.IView|undefined +@exportObjectLiteralProperty ol.MapOptions.zoomControl boolean|undefined @exportObjectLiteralProperty ol.MapOptions.zoomDelta number|undefined @exportObjectLiteral ol.View2DOptions @@ -32,23 +33,23 @@ @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.PanOptions +@exportObjectLiteralProperty ol.animation.PanOptions.source ol.Coordinate +@exportObjectLiteralProperty ol.animation.PanOptions.start number|undefined +@exportObjectLiteralProperty ol.animation.PanOptions.duration number|undefined +@exportObjectLiteralProperty ol.animation.PanOptions.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.RotateOptions +@exportObjectLiteralProperty ol.animation.RotateOptions.rotation number +@exportObjectLiteralProperty ol.animation.RotateOptions.start number|undefined +@exportObjectLiteralProperty ol.animation.RotateOptions.duration number|undefined +@exportObjectLiteralProperty ol.animation.RotateOptions.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.animation.ZoomOptions +@exportObjectLiteralProperty ol.animation.ZoomOptions.resolution number +@exportObjectLiteralProperty ol.animation.ZoomOptions.start number|undefined +@exportObjectLiteralProperty ol.animation.ZoomOptions.duration number|undefined +@exportObjectLiteralProperty ol.animation.ZoomOptions.easing function(number):number|undefined @exportObjectLiteral ol.control.AttributionOptions @exportObjectLiteralProperty ol.control.AttributionOptions.map ol.Map|undefined diff --git a/src/ol/animation.exports b/src/ol/animation.exports index 7286bf8cc3..98207b2e1a 100644 --- a/src/ol/animation.exports +++ b/src/ol/animation.exports @@ -1,4 +1,4 @@ @exportSymbol ol.animation -@exportProperty ol.animation.createBounce -@exportProperty ol.animation.createPanFrom -@exportProperty ol.animation.createSpin +@exportProperty ol.animation.bounce +@exportProperty ol.animation.pan +@exportProperty ol.animation.rotate diff --git a/src/ol/animation.js b/src/ol/animation.js index feccd87f06..74a7120de2 100644 --- a/src/ol/animation.js +++ b/src/ol/animation.js @@ -12,7 +12,7 @@ goog.require('ol.easing'); * @param {ol.animation.BounceOptions} options Options. * @return {ol.PreRenderFunction} Pre-render function. */ -ol.animation.createBounce = function(options) { +ol.animation.bounce = function(options) { var resolution = options.resolution; var start = goog.isDef(options.start) ? options.start : goog.now(); var duration = goog.isDef(options.duration) ? options.duration : 1000; @@ -38,10 +38,10 @@ ol.animation.createBounce = function(options) { /** - * @param {ol.animation.PanFromOptions} options Options. + * @param {ol.animation.PanOptions} options Options. * @return {ol.PreRenderFunction} Pre-render function. */ -ol.animation.createPanFrom = function(options) { +ol.animation.pan = function(options) { var source = options.source; var start = goog.isDef(options.start) ? options.start : goog.now(); var sourceX = source.x; @@ -71,15 +71,15 @@ ol.animation.createPanFrom = function(options) { /** - * @param {ol.animation.SpinOptions} options Options. + * @param {ol.animation.RotateOptions} options Options. * @return {ol.PreRenderFunction} Pre-render function. */ -ol.animation.createSpin = function(options) { +ol.animation.rotate = function(options) { + var sourceRotation = options.rotation; var start = goog.isDef(options.start) ? options.start : goog.now(); var duration = goog.isDef(options.duration) ? options.duration : 1000; var easing = goog.isDef(options.easing) ? options.easing : goog.fx.easing.inAndOut; - var deltaTheta = 2 * options.turns * Math.PI; return function(map, frameState) { if (frameState.time < start) { @@ -87,9 +87,11 @@ ol.animation.createSpin = function(options) { frameState.viewHints[ol.ViewHint.ANIMATING] += 1; return true; } else if (frameState.time < start + duration) { - var delta = easing((frameState.time - start) / duration); + var delta = 1 - easing((frameState.time - start) / duration); + var deltaRotation = + sourceRotation - frameState.view2DState.rotation; frameState.animate = true; - frameState.view2DState.rotation += delta * deltaTheta; + frameState.view2DState.rotation += delta * deltaRotation; frameState.viewHints[ol.ViewHint.ANIMATING] += 1; return true; } else { @@ -100,10 +102,10 @@ ol.animation.createSpin = function(options) { /** - * @param {ol.animation.ZoomFromOptions} options Options. + * @param {ol.animation.ZoomOptions} options Options. * @return {ol.PreRenderFunction} Pre-render function. */ -ol.animation.createZoomFrom = function(options) { +ol.animation.zoom = function(options) { var sourceResolution = options.resolution; var start = goog.isDef(options.start) ? options.start : goog.now(); var duration = goog.isDef(options.duration) ? options.duration : 1000; diff --git a/src/ol/control/attribution.exports b/src/ol/control/attribution.exports index 961219fc9d..57e5bdb9d8 100644 --- a/src/ol/control/attribution.exports +++ b/src/ol/control/attribution.exports @@ -1,2 +1,3 @@ @exportClass ol.control.Attribution ol.control.AttributionOptions +@exportProperty ol.control.Attribution.prototype.setMap diff --git a/src/ol/control/mouseposition.exports b/src/ol/control/mouseposition.exports index 7a4856fcb5..e29f9caa13 100644 --- a/src/ol/control/mouseposition.exports +++ b/src/ol/control/mouseposition.exports @@ -1,2 +1,3 @@ @exportClass ol.control.MousePosition ol.control.MousePositionOptions +@exportProperty ol.control.MousePosition.prototype.setMap diff --git a/src/ol/control/zoom.exports b/src/ol/control/zoom.exports index 1a1835cffb..91cb9a7fbe 100644 --- a/src/ol/control/zoom.exports +++ b/src/ol/control/zoom.exports @@ -1,2 +1,2 @@ @exportClass ol.control.Zoom ol.control.ZoomOptions - +@exportProperty ol.control.Zoom.prototype.setMap diff --git a/src/ol/framestate.js b/src/ol/framestate.js index bbe89d10a0..9e45ce99b6 100644 --- a/src/ol/framestate.js +++ b/src/ol/framestate.js @@ -31,7 +31,7 @@ goog.require('ol.layer.LayerState'); * usedTiles: Object.>, * view2DState: ol.View2DState, * viewHints: Array., - * wantedTiles: Object.>}} + * wantedTiles: Object.>}} */ ol.FrameState; diff --git a/src/ol/geolocation.exports b/src/ol/geolocation.exports new file mode 100644 index 0000000000..adc26edc01 --- /dev/null +++ b/src/ol/geolocation.exports @@ -0,0 +1 @@ +@exportSymbol ol.Geolocation diff --git a/src/ol/geolocation.js b/src/ol/geolocation.js new file mode 100644 index 0000000000..b75aaaf38c --- /dev/null +++ b/src/ol/geolocation.js @@ -0,0 +1,168 @@ +// FIXME handle geolocation not supported +// FIXME handle geolocation errors + +goog.provide('ol.Geolocation'); +goog.provide('ol.GeolocationProperty'); + +goog.require('goog.functions'); +goog.require('ol.Coordinate'); +goog.require('ol.Object'); +goog.require('ol.Projection'); + + +/** + * @enum {string} + */ +ol.GeolocationProperty = { + ACCURACY: 'accuracy', + POSITION: 'position', + PROJECTION: 'projection' +}; + + + +/** + * @constructor + * @extends {ol.Object} + * @param {GeolocationPositionOptions=} opt_positionOptions PositionOptions. + */ +ol.Geolocation = function(opt_positionOptions) { + + goog.base(this); + + /** + * The unprojected (EPSG:4326) device position. + * @private + * @type {ol.Coordinate} + */ + this.position_ = null; + + if (ol.Geolocation.isSupported) { + goog.events.listen( + this, ol.Object.getChangedEventType(ol.GeolocationProperty.PROJECTION), + this.handleProjectionChanged_, false, this); + + /** + * @private + * @type {number} + */ + this.watchId_ = navigator.geolocation.watchPosition( + goog.bind(this.positionChange_, this), + goog.bind(this.positionError_, this), + opt_positionOptions); + } +}; +goog.inherits(ol.Geolocation, ol.Object); + + +/** + * @inheritDoc + */ +ol.Geolocation.prototype.disposeInternal = function() { + navigator.geolocation.clearWatch(this.watchId_); + goog.base(this, 'disposeInternal'); +}; + + +/** + * @private + */ +ol.Geolocation.prototype.handleProjectionChanged_ = function() { + var projection = this.getProjection(); + if (goog.isDefAndNotNull(projection)) { + this.transformCoords_ = ol.Projection.getTransform( + ol.Projection.getFromCode('EPSG:4326'), projection); + if (!goog.isNull(this.position_)) { + this.set(ol.GeolocationProperty.POSITION, + this.transformCoords_(this.position_)); + } + } +}; + + +/** + * @type {boolean} Is supported. + */ +ol.Geolocation.isSupported = 'geolocation' in navigator; + + +/** + * @private + * @param {GeolocationPosition} position position event. + */ +ol.Geolocation.prototype.positionChange_ = function(position) { + var coords = position.coords; + 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); +}; + + +/** + * @private + * @param {GeolocationPositionError} error error object. + */ +ol.Geolocation.prototype.positionError_ = function(error) { +}; + + +/** + * The position of the device. + * @return {ol.Coordinate|undefined} position. + */ +ol.Geolocation.prototype.getPosition = function() { + return /** @type {ol.Coordinate} */ ( + this.get(ol.GeolocationProperty.POSITION)); +}; +goog.exportProperty( + ol.Geolocation.prototype, + 'getPosition', + ol.Geolocation.prototype.getPosition); + + +/** + * 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 {ol.Projection|undefined} projection. + */ +ol.Geolocation.prototype.getProjection = function() { + return /** @type {ol.Projection} */ ( + this.get(ol.GeolocationProperty.PROJECTION)); +}; +goog.exportProperty( + ol.Geolocation.prototype, + 'getProjection', + ol.Geolocation.prototype.getProjection); + + +/** + * @param {ol.Projection} projection Projection. + */ +ol.Geolocation.prototype.setProjection = function(projection) { + this.set(ol.GeolocationProperty.PROJECTION, projection); +}; +goog.exportProperty( + ol.Geolocation.prototype, + 'setProjection', + ol.Geolocation.prototype.setProjection); + + +/** + * @private + * @param {ol.Coordinate} coordinate Coordinate. + * @return {ol.Coordinate} Coordinate. + */ +ol.Geolocation.prototype.transformCoords_ = goog.functions.identity; diff --git a/src/ol/interaction/dragpaninteraction.js b/src/ol/interaction/dragpaninteraction.js index 6cca9a096d..e7228f8cf6 100644 --- a/src/ol/interaction/dragpaninteraction.js +++ b/src/ol/interaction/dragpaninteraction.js @@ -51,7 +51,9 @@ goog.inherits(ol.interaction.DragPan, ol.interaction.Drag); */ ol.interaction.DragPan.prototype.handleDrag = function(mapBrowserEvent) { if (this.kinetic_) { - this.kinetic_.update(mapBrowserEvent.browserEvent); + this.kinetic_.update( + mapBrowserEvent.browserEvent.clientX, + mapBrowserEvent.browserEvent.clientY); } var map = mapBrowserEvent.map; // FIXME works for View2D only @@ -84,7 +86,7 @@ ol.interaction.DragPan.prototype.handleDragEnd = function(mapBrowserEvent) { var distance = this.kinetic_.getDistance(); var angle = this.kinetic_.getAngle(); var center = view.getCenter(); - this.kineticPreRenderFn_ = this.kinetic_.createPanFrom(center); + this.kineticPreRenderFn_ = this.kinetic_.pan(center); map.addPreRenderFunction(this.kineticPreRenderFn_); var centerpx = map.getPixelFromCoordinate(center); @@ -104,7 +106,7 @@ ol.interaction.DragPan.prototype.handleDragStart = function(mapBrowserEvent) { var browserEvent = mapBrowserEvent.browserEvent; if (this.condition_(browserEvent)) { if (this.kinetic_) { - this.kinetic_.begin(browserEvent); + this.kinetic_.begin(browserEvent.clientX, browserEvent.clientY); } var map = mapBrowserEvent.map; map.requestRenderFrame(); diff --git a/src/ol/interaction/dragrotateandzoominteraction.js b/src/ol/interaction/dragrotateandzoominteraction.js index 95df6f5320..9cc3ab89ca 100644 --- a/src/ol/interaction/dragrotateandzoominteraction.js +++ b/src/ol/interaction/dragrotateandzoominteraction.js @@ -79,7 +79,7 @@ ol.interaction.DragRotateAndZoom.prototype.handleDragStart = browserEvent.offsetX - size.width / 2, size.height / 2 - browserEvent.offsetY); var theta = Math.atan2(delta.y, delta.x); - this.startRotation_ = (view.getRotation() || 0) + theta; + this.startRotation_ = view.getRotation() + theta; this.startRatio_ = resolution / delta.magnitude(); map.requestRenderFrame(); return true; diff --git a/src/ol/interaction/dragrotateinteraction.js b/src/ol/interaction/dragrotateinteraction.js index f951f6fd2c..048a847957 100644 --- a/src/ol/interaction/dragrotateinteraction.js +++ b/src/ol/interaction/dragrotateinteraction.js @@ -67,7 +67,7 @@ ol.interaction.DragRotate.prototype.handleDragStart = var theta = Math.atan2( size.height / 2 - offset.y, offset.x - size.width / 2); - this.startRotation_ = (view.getRotation() || 0) + theta; + this.startRotation_ = view.getRotation() + theta; return true; } else { return false; diff --git a/src/ol/kinetic.js b/src/ol/kinetic.js index 5be0754ead..31fcb8b9aa 100644 --- a/src/ol/kinetic.js +++ b/src/ol/kinetic.js @@ -63,23 +63,25 @@ ol.Kinetic = function(decay, minVelocity, delay) { /** - * @param {goog.events.BrowserEvent} browserEvent Browser event. + * @param {number} x X. + * @param {number} y Y. */ -ol.Kinetic.prototype.begin = function(browserEvent) { +ol.Kinetic.prototype.begin = function(x, y) { this.points_.length = 0; this.angle_ = 0; this.initialVelocity_ = 0; - this.update(browserEvent); + this.update(x, y); }; /** - * @param {goog.events.BrowserEvent} browserEvent Browser event. + * @param {number} x X. + * @param {number} y Y. */ -ol.Kinetic.prototype.update = function(browserEvent) { +ol.Kinetic.prototype.update = function(x, y) { this.points_.push({ - x: browserEvent.clientX, - y: browserEvent.clientY, + x: x, + y: y, t: goog.now() }); }; @@ -112,7 +114,7 @@ ol.Kinetic.prototype.end = function() { * @param {ol.Coordinate} source Source coordinate for the animation. * @return {ol.PreRenderFunction} Pre-render function for kinetic animation. */ -ol.Kinetic.prototype.createPanFrom = function(source) { +ol.Kinetic.prototype.pan = function(source) { var decay = this.decay_; var initialVelocity = this.initialVelocity_; var minVelocity = this.minVelocity_; @@ -121,7 +123,7 @@ ol.Kinetic.prototype.createPanFrom = function(source) { return initialVelocity * (Math.exp((decay * t) * duration) - 1) / (minVelocity - initialVelocity); }; - return ol.animation.createPanFrom({ + return ol.animation.pan({ source: source, duration: duration, easing: easingFunction diff --git a/src/ol/map.exports b/src/ol/map.exports index 8c4285f1c3..96ee6e3e29 100644 --- a/src/ol/map.exports +++ b/src/ol/map.exports @@ -1,7 +1,6 @@ @exportClass ol.Map ol.MapOptions @exportProperty ol.Map.prototype.addPreRenderFunction @exportProperty ol.Map.prototype.addPreRenderFunctions -@exportProperty ol.Map.prototype.getControls @exportProperty ol.Map.prototype.getInteractions @exportSymbol ol.RendererHint diff --git a/src/ol/map.js b/src/ol/map.js index 58925d8443..9d391c90d5 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -22,8 +22,6 @@ goog.require('goog.events.MouseWheelHandler'); goog.require('goog.events.MouseWheelHandler.EventType'); goog.require('ol.BrowserFeature'); goog.require('ol.Collection'); -goog.require('ol.CollectionEvent'); -goog.require('ol.CollectionEventType'); goog.require('ol.Color'); goog.require('ol.Coordinate'); goog.require('ol.Extent'); @@ -230,17 +228,6 @@ ol.Map = function(mapOptions) { this.handleBrowserEvent, false, this); this.registerDisposable(mouseWheelHandler); - /** - * @type {ol.Collection} - * @private - */ - this.controls_ = mapOptionsInternal.controls; - - goog.events.listen(this.controls_, ol.CollectionEventType.ADD, - this.handleControlsAdd_, false, this); - goog.events.listen(this.controls_, ol.CollectionEventType.REMOVE, - this.handleControlsRemove_, false, this); - /** * @type {ol.Collection} * @private @@ -299,7 +286,9 @@ ol.Map = function(mapOptions) { // this gives the map an initial size this.handleBrowserWindowResize(); - this.controls_.forEach( + /** @type {Array.} */ + var controls = mapOptionsInternal.controls; + goog.array.forEach(controls, /** * @param {ol.control.Control} control Control. */ @@ -387,14 +376,6 @@ ol.Map.prototype.getTarget = function() { }; -/** - * @return {ol.Collection} Controls. - */ -ol.Map.prototype.getControls = function() { - return this.controls_; -}; - - /** * @param {ol.Pixel} pixel Pixel. * @return {ol.Coordinate} Coordinate. @@ -500,9 +481,8 @@ ol.Map.prototype.getTilePriority = function(tile, tileSourceKey, tileCenter) { if (goog.isNull(frameState) || !(tileSourceKey in frameState.wantedTiles)) { return ol.TileQueue.DROP; } - var zKey = tile.tileCoord.z.toString(); - if (!(zKey in frameState.wantedTiles[tileSourceKey]) || - !frameState.wantedTiles[tileSourceKey][zKey].contains(tile.tileCoord)) { + var coordKey = tile.tileCoord.toString(); + if (!frameState.wantedTiles[tileSourceKey][coordKey]) { return ol.TileQueue.DROP; } var center = frameState.view2DState.center; @@ -523,26 +503,6 @@ ol.Map.prototype.handleBrowserEvent = function(browserEvent, opt_type) { }; -/** - * @param {ol.CollectionEvent} collectionEvent Collection event. - * @private - */ -ol.Map.prototype.handleControlsAdd_ = function(collectionEvent) { - var control = /** @type {ol.control.Control} */ (collectionEvent.elem); - control.setMap(this); -}; - - -/** - * @param {ol.CollectionEvent} collectionEvent Collection event. - * @private - */ -ol.Map.prototype.handleControlsRemove_ = function(collectionEvent) { - var control = /** @type {ol.control.Control} */ (collectionEvent.elem); - control.setMap(null); -}; - - /** * @param {ol.MapBrowserEvent} mapBrowserEvent The event to handle. */ @@ -754,8 +714,8 @@ ol.Map.prototype.renderFrame_ = function(time) { frameState.extent = ol.Extent.boundingExtent.apply(null, corners); } - this.renderer_.renderFrame(frameState); this.frameState_ = frameState; + this.renderer_.renderFrame(frameState); this.dirty_ = false; if (!goog.isNull(frameState)) { @@ -849,7 +809,7 @@ ol.Map.prototype.withFrozenRendering = function(f, opt_obj) { /** - * @typedef {{controls: ol.Collection, + * @typedef {{controls: Array., * interactions: ol.Collection, * rendererConstructor: * function(new: ol.renderer.Map, Element, ol.Map), @@ -915,14 +875,9 @@ ol.Map.createOptionsInternal = function(mapOptions) { } /** - * @type {ol.Collection} + * @type {Array.} */ - var controls; - if (goog.isDef(mapOptions.controls)) { - controls = mapOptions.controls; - } else { - controls = ol.Map.createControls_(mapOptions); - } + var controls = ol.Map.createControls_(mapOptions); /** * @type {ol.Collection} @@ -953,22 +908,29 @@ ol.Map.createOptionsInternal = function(mapOptions) { /** * @private * @param {ol.MapOptions} mapOptions Map options. - * @return {ol.Collection} Controls. + * @return {Array.} Controls. */ ol.Map.createControls_ = function(mapOptions) { + /** @type {Array.} */ + var controls = []; - var controls = new ol.Collection(); + var attributionControl = goog.isDef(mapOptions.attributionControl) ? + mapOptions.attributionControl : true; + if (attributionControl) { + controls.push(new ol.control.Attribution({})); + } - controls.push(new ol.control.Attribution({})); - - var zoomDelta = goog.isDef(mapOptions.zoomDelta) ? - mapOptions.zoomDelta : 4; - controls.push(new ol.control.Zoom({ - delta: zoomDelta - })); + var zoomControl = goog.isDef(mapOptions.zoomControl) ? + mapOptions.zoomControl : true; + if (zoomControl) { + var zoomDelta = goog.isDef(mapOptions.zoomDelta) ? + mapOptions.zoomDelta : 4; + controls.push(new ol.control.Zoom({ + delta: zoomDelta + })); + } return controls; - }; diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index 59b0c6051c..02e5e904ff 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -167,6 +167,9 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = tileState = tile.getState(); if (tileState == ol.TileState.IDLE) { + goog.events.listenOnce(tile, goog.events.EventType.CHANGE, + this.handleTileChange, false, this); + this.updateWantedTiles(frameState.wantedTiles, tileSource, tileCoord); tileCenter = tileGrid.getTileCoordCenter(tileCoord); frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter); } else if (tileState == ol.TileState.LOADED) { @@ -214,11 +217,6 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = } } - if (!allTilesLoaded) { - frameState.animate = true; - this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); - } - this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); this.scheduleExpireCache(frameState, tileSource); diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index a92d34a0d2..094d6a3009 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -131,6 +131,9 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = tileState = tile.getState(); if (tileState == ol.TileState.IDLE) { + goog.events.listenOnce(tile, goog.events.EventType.CHANGE, + this.handleTileChange, false, this); + this.updateWantedTiles(frameState.wantedTiles, tileSource, tileCoord); tileCenter = tileGrid.getTileCoordCenter(tileCoord); frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter); } else if (tileState == ol.TileState.LOADED) { @@ -232,11 +235,6 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = this.renderedVisible_ = true; } - if (!allTilesLoaded) { - frameState.animate = true; - this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); - } - this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); this.scheduleExpireCache(frameState, tileSource); diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index 4ff6f66cab..fab64889fc 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -4,7 +4,10 @@ goog.require('goog.events'); goog.require('goog.events.EventType'); goog.require('ol.FrameState'); goog.require('ol.Object'); +goog.require('ol.Tile'); +goog.require('ol.TileCoord'); goog.require('ol.TileRange'); +goog.require('ol.TileState'); goog.require('ol.layer.Layer'); goog.require('ol.layer.LayerProperty'); goog.require('ol.layer.LayerState'); @@ -146,6 +149,19 @@ ol.renderer.Layer.prototype.handleLayerVisibleChange = function() { }; +/** + * Handle changes in tile state. + * @param {goog.events.Event} event Tile change event. + * @protected + */ +ol.renderer.Layer.prototype.handleTileChange = function(event) { + var tile = /** @type {ol.Tile} */ (event.target); + if (tile.getState() === ol.TileState.LOADED) { + this.getMap().requestRenderFrame(); + } +}; + + /** * @param {ol.FrameState} frameState Frame state. * @param {ol.layer.LayerState} layerState Layer state. @@ -197,24 +213,16 @@ ol.renderer.Layer.prototype.updateUsedTiles = /** * @protected - * @param {Object.>} wantedTiles Wanted - * tile ranges. + * @param {Object.>} wantedTiles Wanted tiles. * @param {ol.source.Source} source Source. - * @param {number} z Z. - * @param {ol.TileRange} tileRange Tile range. + * @param {ol.TileCoord} tileCoord Tile coordinate. */ ol.renderer.Layer.prototype.updateWantedTiles = - function(wantedTiles, source, z, tileRange) { + function(wantedTiles, source, tileCoord) { 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 { + var coordKey = tileCoord.toString(); + if (!(sourceKey in wantedTiles)) { wantedTiles[sourceKey] = {}; - wantedTiles[sourceKey][zKey] = tileRange; } + wantedTiles[sourceKey][coordKey] = true; }; diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 3663948de8..27c0cbadab 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -393,6 +393,9 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = tileState = tile.getState(); if (tileState == ol.TileState.IDLE) { + goog.events.listenOnce(tile, goog.events.EventType.CHANGE, + this.handleTileChange, false, this); + this.updateWantedTiles(frameState.wantedTiles, tileSource, tileCoord); tileCenter = tileGrid.getTileCoordCenter(tileCoord); frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter); } else if (tileState == ol.TileState.LOADED) { @@ -457,7 +460,6 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = this.renderedTileRange_ = null; this.renderedFramebufferExtent_ = null; frameState.animate = true; - this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); } } diff --git a/src/ol/structs/lrucache.js b/src/ol/structs/lrucache.js index ecf53521a6..7792dc2f61 100644 --- a/src/ol/structs/lrucache.js +++ b/src/ol/structs/lrucache.js @@ -91,7 +91,7 @@ ol.structs.LRUCache.prototype.clear = function() { * @return {boolean} Contains key. */ ol.structs.LRUCache.prototype.containsKey = function(key) { - return key in this.entries_; + return this.entries_.hasOwnProperty(key); }; @@ -155,15 +155,6 @@ ol.structs.LRUCache.prototype.getKeys = function() { }; -/** - * @return {string} Last key. - */ -ol.structs.LRUCache.prototype.getLastKey = function() { - goog.asserts.assert(!goog.isNull(this.newest_)); - return this.newest_.key_; -}; - - /** * @return {Array} Values. */ diff --git a/src/ol/view2d.js b/src/ol/view2d.js index c1da3c23bc..ca726b99f4 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -298,7 +298,7 @@ ol.View2D.prototype.zoom = function(map, delta, opt_anchor, opt_duration) { var currentResolution = this.getResolution(); if (goog.isDef(currentResolution) && goog.isDef(opt_duration)) { map.requestRenderFrame(); - map.addPreRenderFunction(ol.animation.createZoomFrom({ + map.addPreRenderFunction(ol.animation.zoom({ resolution: currentResolution, duration: opt_duration })); diff --git a/test/spec/ol/lrucache.test.js b/test/spec/ol/lrucache.test.js index 7ceb8a0115..587724e36a 100644 --- a/test/spec/ol/lrucache.test.js +++ b/test/spec/ol/lrucache.test.js @@ -99,11 +99,38 @@ describe('ol.structs.LRUCache', function() { }); }); - describe('setting a disallowed key', function() { - it('raises an exception', function() { + describe('disallowed keys', function() { + it('setting raises an exception', function() { + expect(function() { + lruCache.set('constructor', 0); + }).toThrow(); expect(function() { lruCache.set('hasOwnProperty', 0); }).toThrow(); + expect(function() { + lruCache.set('isPrototypeOf', 0); + }).toThrow(); + expect(function() { + lruCache.set('propertyIsEnumerable', 0); + }).toThrow(); + expect(function() { + lruCache.set('toLocaleString', 0); + }).toThrow(); + expect(function() { + lruCache.set('toString', 0); + }).toThrow(); + expect(function() { + lruCache.set('valueOf', 0); + }).toThrow(); + }); + it('getting returns false', function() { + expect(lruCache.containsKey('constructor')).toBeFalsy(); + expect(lruCache.containsKey('hasOwnProperty')).toBeFalsy(); + expect(lruCache.containsKey('isPrototypeOf')).toBeFalsy(); + expect(lruCache.containsKey('propertyIsEnumerable')).toBeFalsy(); + expect(lruCache.containsKey('toLocaleString')).toBeFalsy(); + expect(lruCache.containsKey('toString')).toBeFalsy(); + expect(lruCache.containsKey('valueOf')).toBeFalsy(); }); });