diff --git a/.gitignore b/.gitignore
index 66d8c7908f..7ec33e4db6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,18 @@
-/build/OpenLayers.js
-/tools/closure-compiler.jar
-/tools/*.pyc
/apidoc_config/Data/
+/bin/plovr*.jar
+/build/OpenLayers.js
+/build/ol.js
+/build/ol3.js
+/build/ol3-compiled.js
+/build/webgl-debug.js
+/demos/*/advanced-optimizations.*
+/demos/*/build.html
+/demos/*/debug.html
+/demos/*/simple-optimizations.*
/doc/apidocs/
/examples/example-list.js
/examples/example-list.xml
-/bin/plovr.jar
-/jsdoc/
\ No newline at end of file
+/jsdoc/
+/proj4js-combined.js
+/tools/*.pyc
+/tools/closure-compiler.jar
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000..c335685327
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,142 @@
+PLOVR_JAR=bin/plovr-4b3caf2b7d84.jar
+SRC_OL = $(shell find src/ol -name \*.js)
+SRC_OL3 = $(shell find externs src/ol3 -name \*.js)
+TARGETS = $(shell find demos -name advanced-optimizations.js -o -name simple-optimizations.js)
+comma := ,
+empty :=
+space := $(empty) $(empty)
+
+.PHONY: all
+all: build demos
+
+.PHONY: build
+build: build/ol.js build/ol3-compiled.js build/webgl-debug.js
+
+build/ol.js: $(PLOVR_JAR) $(SRC_OL3) base.json \
+ build/ol.json src/ol/ol.js
+ java -jar $(PLOVR_JAR) build build/ol.json >$@
+ @echo $@ "uncompressed:" $$(wc -c <$@) bytes
+ @echo $@ " compressed:" $$(gzip -9 -c <$@ | wc -c) bytes
+
+build/ol3-compiled.js: $(PLOVR_JAR) $(SRC_OL3) base.json \
+ build/ol3.json build/ol3.js
+ java -jar $(PLOVR_JAR) build build/ol3.json >$@
+ @echo $@ "uncompressed:" $$(wc -c <$@) bytes
+ @echo $@ " compressed:" $$(gzip -9 -c <$@ | wc -c) bytes
+
+build/ol3.js: $(SRC_OL3)
+ ( echo "goog.require('goog.dom');" ; find src/ol3 -name \*.js | xargs grep -rh ^goog.provide | sort | uniq | sed -e 's/provide/require/g' ) > $@
+
+.PHONY: demos
+demos: demos/api1 demos/proj4js demos/side-by-side demos/two-layers
+
+.PHONY: demos/api1
+demos/api1: \
+ build/ol.js \
+ demos/api1/build.html \
+ demos/api1/debug.html
+
+demos/api1/build.html: demos/api1/index.html.in
+ sed -e 's|@SRC@|../../build/ol.js|' $< > $@
+
+demos/api1/debug.html: demos/api1/index.html.in
+ sed -e 's|@SRC@|http://localhost:9810/compile?id=ol|' $< > $@
+
+.PHONY: demos/proj4js
+demos/proj4js: \
+ build/ol.js \
+ demos/proj4js/build.html \
+ demos/proj4js/debug.html
+
+demos/proj4js/build.html: demos/proj4js/index.html.in
+ sed -e 's|@SRC@|../../build/ol.js|' $< > $@
+
+demos/proj4js/debug.html: demos/proj4js/index.html.in
+ sed -e 's|@SRC@|http://localhost:9810/compile?id=ol|' $< > $@
+
+.PHONY: demos/side-by-side
+demos/side-by-side: \
+ demos/side-by-side/advanced-optimizations.html \
+ demos/side-by-side/advanced-optimizations.js \
+ demos/side-by-side/debug.html \
+ demos/side-by-side/simple-optimizations.html \
+ demos/side-by-side/simple-optimizations.js
+
+demos/side-by-side/advanced-optimizations.html: demos/side-by-side/index.html.in
+ sed -e 's|@SRC@|advanced-optimizations.js|' $< > $@
+
+demos/side-by-side/advanced-optimizations.js: $(PLOVR_JAR) $(SRC_OL3) base.json \
+ demos/side-by-side/side-by-side.json demos/side-by-side/side-by-side.js
+ java -jar $(PLOVR_JAR) build demos/side-by-side/side-by-side.json >$@
+ @echo $@ "uncompressed:" $$(wc -c <$@) bytes
+ @echo $@ " compressed:" $$(gzip -9 -c <$@ | wc -c) bytes
+
+demos/side-by-side/debug.html: demos/side-by-side/index.html.in
+ sed -e 's|@SRC@|http://localhost:9810/compile?id=demo-side-by-side|' $< > $@
+
+demos/side-by-side/simple-optimizations.html: demos/side-by-side/index.html.in
+ sed -e 's|@SRC@|simple-optimizations.js|' $< > $@
+
+# FIXME invoke plovr directly, rather than assuming that the server is running
+demos/side-by-side/simple-optimizations.js: $(PLOVR_JAR) $(SRC_OL3) base.json \
+ demos/side-by-side/side-by-side.json demos/side-by-side/side-by-side.js
+ curl 'http://localhost:9810/compile?id=demo-side-by-side&mode=SIMPLE' > $@
+ @echo $@ "uncompressed:" $$(wc -c <$@) bytes
+ @echo $@ " compressed:" $$(gzip -9 -c <$@ | wc -c) bytes
+
+.PHONY: demos/two-layers
+demos/two-layers: \
+ demos/two-layers/advanced-optimizations.html \
+ demos/two-layers/advanced-optimizations.js \
+ demos/two-layers/debug.html \
+ demos/two-layers/simple-optimizations.html \
+ demos/two-layers/simple-optimizations.js
+
+demos/two-layers/advanced-optimizations.html: demos/two-layers/index.html.in
+ sed -e 's|@SRC@|advanced-optimizations.js|' $< > $@
+
+demos/two-layers/advanced-optimizations.js: $(PLOVR_JAR) $(SRC_OL3) base.json \
+ demos/two-layers/two-layers.json demos/two-layers/two-layers.js
+ java -jar $(PLOVR_JAR) build demos/two-layers/two-layers.json >$@
+ @echo $@ "uncompressed:" $$(wc -c <$@) bytes
+ @echo $@ " compressed:" $$(gzip -9 -c <$@ | wc -c) bytes
+
+demos/two-layers/debug.html: demos/two-layers/index.html.in
+ sed -e 's|@SRC@|http://localhost:9810/compile?id=demo-two-layers|' $< > $@
+
+demos/two-layers/simple-optimizations.html: demos/two-layers/index.html.in
+ sed -e 's|@SRC@|simple-optimizations.js|' $< > $@
+
+# FIXME invoke plovr directly, rather than assuming that the server is running
+demos/two-layers/simple-optimizations.js: $(PLOVR_JAR) $(SRC_OL3) base.json \
+ demos/two-layers/two-layers.json demos/two-layers/two-layers.js
+ curl 'http://localhost:9810/compile?id=demo-two-layers&mode=SIMPLE' > $@
+ @echo $@ "uncompressed:" $$(wc -c <$@) bytes
+ @echo $@ " compressed:" $$(gzip -9 -c <$@ | wc -c) bytes
+
+.PHONY: serve
+serve: $(PLOVR_JAR)
+ java -jar $(PLOVR_JAR) serve build/ol.json build/ol3.json demos/*/*.json
+
+.PHONY: lint
+lint:
+ gjslint --strict --limited_doc_files=$(subst $(space),$(comma),$(shell find externs -name \*.js)) $(SRC_OL3) $(SRC_OL) $(filter-out $(TARGETS),$(shell find demos -name \*.js))
+
+build/webgl-debug.js:
+ curl https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/sdk/debug/webgl-debug.js > $@
+
+$(PLOVR_JAR):
+ curl http://plovr.googlecode.com/files/$(notdir $@) > $@
+
+clean:
+ rm -f build/all.js
+ rm -f build/ol.js
+ rm -f build/ol3.js
+ rm -f build/ol3-compiled.js
+ rm -f demos/*/*.html
+ rm -f demos/*/advanced-optimizations.*
+ rm -f demos/*/simple-optimizations.*
+
+reallyclean: clean
+ rm -f $(PLOVR_JAR)
+ rm -f build/webgl-debug.js
diff --git a/api.json b/api.json
deleted file mode 100644
index 7bd07e492e..0000000000
--- a/api.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- "id": "api",
-
- "output-file": "api.js",
-
- "output-wrapper": [
- "// Copyright 2012 ...\n",
- "(function(){%output%})();"
- ],
-
- "inputs": "src/ol.export.js",
- "paths": [
- "src"
- ],
-
- "define": {
- // "goog.dom.ASSUME_STANDARDS_MODE": true,
- // "goog.userAgent.ASSUME_MOBILE_WEBKIT": true,
- "goog.DEBUG": false,
- "ol.API" : true
- },
-
- "mode": "ADVANCED",
- "level": "VERBOSE",
- // "pretty-print": true,
- // "debug": true,
-
- // "experimental-compiler-options": {
- // "generateExports": true
- // },
-
-
- "checks": {
- // acceptable values are "ERROR", "WARNING", and "OFF"
- "accessControls": "WARNING",
- "visibility": "WARNING",
- "checkTypes": "WARNING",
- "checkRegExp": "WARNING",
- "checkVars": "WARNING",
- "deprecated": "WARNING",
- "fileoverviewTags": "WARNING",
- "invalidCasts": "WARNING",
- "missingProperties": "WARNING",
- "nonStandardJsDocs": "WARNING",
- "undefinedVars": "WARNING"
- },
-
- "jsdoc-html-output-path": "jsdoc"
-}
diff --git a/base.json b/base.json
new file mode 100644
index 0000000000..e4d98e1efa
--- /dev/null
+++ b/base.json
@@ -0,0 +1,37 @@
+{
+
+ "checks": {
+ "accessControls": "ERROR",
+ "visibility": "ERROR",
+ "checkTypes": "ERROR",
+ "checkRegExp": "ERROR",
+ "checkVars": "ERROR",
+ "deprecated": "ERROR",
+ "fileoverviewTags": "ERROR",
+ "invalidCasts": "ERROR",
+ "missingProperties": "ERROR",
+ "nonStandardJsDocs": "ERROR",
+ "undefinedVars": "ERROR"
+ },
+
+ "define": {
+ "goog.dom.ASSUME_STANDARDS_MODE": true,
+ "goog.DEBUG": false
+ },
+
+ "externs": [
+ "externs/bingmaps.js",
+ "externs/proj4js.js",
+ "externs/tilejson.js",
+ "externs/webgl-debug.js"
+ ],
+
+ "level": "VERBOSE",
+
+ "mode": "ADVANCED",
+
+ "paths": "src",
+
+ "treat-warnings-as-errors": true
+
+}
diff --git a/bin/build.xml b/bin/build.xml
index 5c51c4a784..c70e948a9a 100644
--- a/bin/build.xml
+++ b/bin/build.xml
@@ -6,7 +6,8 @@
-
+
+
@@ -25,7 +26,7 @@
-
diff --git a/build/ol.json b/build/ol.json
new file mode 100644
index 0000000000..935a6aac49
--- /dev/null
+++ b/build/ol.json
@@ -0,0 +1,9 @@
+{
+
+ "id": "ol",
+
+ "inherits": "../base.json",
+
+ "inputs": "src/ol/ol.js"
+
+}
diff --git a/build/ol3.json b/build/ol3.json
new file mode 100644
index 0000000000..1a392513a3
--- /dev/null
+++ b/build/ol3.json
@@ -0,0 +1,9 @@
+{
+
+ "id": "ol3",
+
+ "inherits": "../base.json",
+
+ "inputs": "build/ol3.js"
+
+}
diff --git a/demo/hello-api.html b/demo/hello-api.html
deleted file mode 100644
index e0da39a20e..0000000000
--- a/demo/hello-api.html
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
- OL3 Hello World
-
-
-
-
-
-
-
-
-
-
diff --git a/demo/hello-compiled.html b/demo/hello-compiled.html
deleted file mode 100644
index b50bd6b8f5..0000000000
--- a/demo/hello-compiled.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
- OL3 Hello World
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/demo/hello-compiled.json b/demo/hello-compiled.json
deleted file mode 100644
index 5cab96f3ea..0000000000
--- a/demo/hello-compiled.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "id": "hello",
-
- "output-file": "hello-compiled.js",
-
- "inputs": [
- "hello.js",
- "../src/ol.js"
- ],
- "paths": [
- "../src"
- ],
-
- "define": {
- // "goog.dom.ASSUME_STANDARDS_MODE": true,
- "goog.DEBUG": false
- },
-
- "mode": "ADVANCED",
- "level": "VERBOSE",
- // "pretty-print": true,
- // "debug": true,
-
- // "experimental-compiler-options": {
- // "generateExports": true
- // },
-
- "checks": {
- // acceptable values are "ERROR", "WARNING", and "OFF"
- "accessControls": "WARNING",
- "visibility": "WARNING",
- "checkTypes": "WARNING",
- "checkRegExp": "WARNING",
- "checkVars": "WARNING",
- "deprecated": "WARNING",
- "fileoverviewTags": "WARNING",
- "invalidCasts": "WARNING",
- "missingProperties": "WARNING",
- "nonStandardJsDocs": "WARNING",
- "undefinedVars": "WARNING"
- }
-}
diff --git a/demo/hello-epi-compiled.json b/demo/hello-epi-compiled.json
deleted file mode 100644
index ac42ce4866..0000000000
--- a/demo/hello-epi-compiled.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
- "id": "hello-epi",
-
- "output-file": "hello-epi-compiled.js",
-
- "inputs": [
- "hello-epi.js"
- ],
- "paths": [
- "../src"
- ],
-
- "define": {
- // "goog.dom.ASSUME_STANDARDS_MODE": true,
- "goog.DEBUG": false
- },
-
- "mode": "ADVANCED",
- "level": "VERBOSE",
- // "pretty-print": true,
- // "debug": true,
-
- // "experimental-compiler-options": {
- // "generateExports": true
- // },
-
-
- "checks": {
- // acceptable values are "ERROR", "WARNING", and "OFF"
- "accessControls": "WARNING",
- "visibility": "WARNING",
- "checkTypes": "WARNING",
- "checkRegExp": "WARNING",
- "checkVars": "WARNING",
- "deprecated": "WARNING",
- "fileoverviewTags": "WARNING",
- "invalidCasts": "WARNING",
- "missingProperties": "WARNING",
- "nonStandardJsDocs": "WARNING",
- "undefinedVars": "WARNING"
- }
-}
diff --git a/demo/hello-epi.js b/demo/hello-epi.js
deleted file mode 100644
index 2b6ba9467f..0000000000
--- a/demo/hello-epi.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/* This is a code which is going to be compiled together with the library */
-
-goog.require('ol.Map');
-goog.require('ol.layer.OSM');
-goog.require('ol.Loc');
-
-goog.require('goog.dom');
-
-function init() {
- var map = new ol.Map();
- map.setContainer(goog.dom.getElement('map'));
- map.setLayers( [ new ol.layer.OSM() ] );
- map.setCenter( new ol.Loc(45, 5));
- map.setZoom(10);
-}
-window['init'] = init;
diff --git a/demo/hello.js b/demo/hello.js
deleted file mode 100644
index 90cfcae4e1..0000000000
--- a/demo/hello.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/* This is a code which is going to be compiled together with the library */
-
-function init() {
- var map = ol.map()
- .renderTo('map')
- .layers([ol.layer.osm()])
- .center([45, 5])
- .zoom(10);
-}
-window['init'] = init;
diff --git a/demo/loader.js b/demo/loader.js
deleted file mode 100644
index 8b01bc2322..0000000000
--- a/demo/loader.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/**
- Adds the plovr generated script to the document.
- */
-(function() {
- var url = "http://" + window.location.hostname + ":9810/compile?id=ol";
- document.write("");
-})();
diff --git a/demo/map.html b/demo/map.html
deleted file mode 100644
index 0374e68fa1..0000000000
--- a/demo/map.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- OL3 Map
-
-
-
-
-
-
-
-
-
diff --git a/demo/map.js b/demo/map.js
deleted file mode 100644
index c5ea52311c..0000000000
--- a/demo/map.js
+++ /dev/null
@@ -1,6 +0,0 @@
-var map = ol.map({
- renderTo: 'map',
- layers: [ol.layer.osm()],
- center: [0, 0],
- zoom: 1
-});
diff --git a/demo/map.json b/demo/map.json
deleted file mode 100644
index 7725caf2bf..0000000000
--- a/demo/map.json
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Build configuration for the map.js example. Use following syntax:
- *
- * ol build map.json
- *
- * The output will be named map-compiled.js.
- */
-
-{
- "id": "hello",
-
- "output-file": "map-compiled.js",
-
- "inputs": ["../src/ol.js", "map.js"],
-
- "paths": ["../src"],
-
- "define": {
- "goog.DEBUG": false
- },
-
- "mode": "ADVANCED",
- "level": "VERBOSE",
-
- // acceptable values are "ERROR", "WARNING", and "OFF"
- "checks": {
- "accessControls": "WARNING",
- "visibility": "WARNING",
- "checkTypes": "WARNING",
- "checkRegExp": "WARNING",
- "checkVars": "WARNING",
- "deprecated": "WARNING",
- "fileoverviewTags": "WARNING",
- "invalidCasts": "WARNING",
- "missingProperties": "WARNING",
- "nonStandardJsDocs": "WARNING",
- "undefinedVars": "WARNING"
- }
-}
diff --git a/demos/api1/api1.js b/demos/api1/api1.js
new file mode 100644
index 0000000000..c6d7e92ca2
--- /dev/null
+++ b/demos/api1/api1.js
@@ -0,0 +1,6 @@
+var map = ol.map({
+ renderTo: 'map',
+ layers: [ol.layer.osm()],
+ center: [45, 5],
+ zoom: 10
+});
diff --git a/demos/api1/index.html.in b/demos/api1/index.html.in
new file mode 100644
index 0000000000..c2b0b0fa81
--- /dev/null
+++ b/demos/api1/index.html.in
@@ -0,0 +1,19 @@
+
+
+
+
+
+ ol3 api1 demo
+
+
+ ol3 api1 demo
+
+
+
+
diff --git a/demos/proj4js/index.html.in b/demos/proj4js/index.html.in
new file mode 100644
index 0000000000..5239fee524
--- /dev/null
+++ b/demos/proj4js/index.html.in
@@ -0,0 +1,13 @@
+
+
+
+
+
+ ol3 proj4js demo
+
+
+ ol3 proj4js demo
+
+
+
+
diff --git a/demos/proj4js/proj4js.js b/demos/proj4js/proj4js.js
new file mode 100644
index 0000000000..70f3adfb62
--- /dev/null
+++ b/demos/proj4js/proj4js.js
@@ -0,0 +1,23 @@
+goog.require('goog.dom');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Projection');
+
+
+var outputElement = document.getElementById('output');
+
+var point, transformedPoint;
+
+point = new ol3.Coordinate(-626172.13571216376, 6887893.4928337997);
+transformedPoint = ol3.Projection.transformWithCodes(
+ point, 'GOOGLE', 'WGS84');
+outputElement.appendChild(goog.dom.createTextNode(transformedPoint.toString()));
+
+Proj4js.defs['EPSG:21781'] =
+ '+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 ' +
+ '+x_0=600000 +y_0=200000 +ellps=bessel ' +
+ '+towgs84=674.374,15.056,405.346,0,0,0,0 +units=m +no_defs';
+
+point = new ol3.Coordinate(7.439583333333333, 46.95240555555556);
+transformedPoint = ol3.Projection.transformWithCodes(
+ point, 'EPSG:4326', 'EPSG:21781');
+outputElement.appendChild(goog.dom.createTextNode(transformedPoint.toString()));
diff --git a/demos/side-by-side/index.html.in b/demos/side-by-side/index.html.in
new file mode 100644
index 0000000000..0e9ced36a5
--- /dev/null
+++ b/demos/side-by-side/index.html.in
@@ -0,0 +1,72 @@
+
+
+
+
+ ol3 side-by-side demo
+
+
+ ol3 side-by-side demo
+
+
+ DOM
+ WebGL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Pan:
+ drag, arrow keys
+
+
+ Zoom:
+ double-click, Shift+double-click, mouse wheel, +/- keys; Shift+drag
+
+
+ Rotate:
+ Alt+drag, r to reset (WebGL only)
+
+
+ Brightness/contrast:
+ b/B/c/C keys (WebGL only)
+
+
+ Hue/saturation:
+ h/H/s/S keys (WebGL only)
+
+
+ Opacity:
+ o/O keys
+
+
+ Visibility:
+ v/V keys
+
+
+ Reset
+ 0 key
+
+
+ Notes: The two maps share the same center, resolution, rotation and layers. Currently the DOM map does not support rotation.
+ Console tips: If you want to play around with the API in the console, first make sure that you're using the simple optimizations version of this demo, then open a console and experiment with the domMap, webglMap and layer variables in the console. The source code is in side-by-side.js .
+
+
+
diff --git a/demos/side-by-side/side-by-side.js b/demos/side-by-side/side-by-side.js
new file mode 100644
index 0000000000..987d052a27
--- /dev/null
+++ b/demos/side-by-side/side-by-side.js
@@ -0,0 +1,109 @@
+goog.require('goog.debug.Console');
+goog.require('goog.debug.Logger');
+goog.require('goog.debug.Logger.Level');
+goog.require('ol3.CoordinateFormat');
+goog.require('ol3.RendererHint');
+goog.require('ol3.control.Attribution');
+goog.require('ol3.control.MousePosition');
+goog.require('ol3.createMap');
+goog.require('ol3.interaction.Keyboard');
+goog.require('ol3.layer.MapQuestOpenAerial');
+
+
+if (goog.DEBUG) {
+ goog.debug.Console.autoInstall();
+ goog.debug.Logger.getLogger('ol').setLevel(goog.debug.Logger.Level.INFO);
+}
+
+
+
+var layer = new ol3.layer.MapQuestOpenAerial();
+
+var layers = new ol3.Collection();
+layers.push(layer);
+
+var domMap = ol3.createMap(
+ document.getElementById('domMap'),
+ {'layers': layers},
+ ol3.RendererHint.DOM);
+domMap.setCenter(new ol3.Coordinate(0, 0));
+domMap.setResolution(layer.getStore().getResolutions()[0]);
+
+var webglMap = ol3.createMap(
+ document.getElementById('webglMap'),
+ {},
+ ol3.RendererHint.WEBGL);
+if (!goog.isNull(webglMap)) {
+ webglMap.bindTo('center', domMap);
+ webglMap.bindTo('layers', domMap);
+ webglMap.bindTo('resolution', domMap);
+ webglMap.bindTo('rotation', domMap);
+}
+
+var attributionControl = new ol3.control.Attribution(domMap);
+document.getElementById('attribution').appendChild(
+ attributionControl.getElement());
+
+var domMousePositionControl = new ol3.control.MousePosition(domMap,
+ ol3.Projection.getFromCode('EPSG:4326'), ol3.CoordinateFormat.hdms,
+ ' ');
+document.getElementById('domMousePosition').appendChild(
+ domMousePositionControl.getElement());
+
+var webglMousePositionControl = new ol3.control.MousePosition(webglMap,
+ ol3.Projection.getFromCode('EPSG:4326'), ol3.CoordinateFormat.hdms,
+ ' ');
+document.getElementById('webglMousePosition').appendChild(
+ webglMousePositionControl.getElement());
+
+var keyboardInteraction = new ol3.interaction.Keyboard();
+keyboardInteraction.addCallback('0', function() {
+ layer.setBrightness(0);
+ layer.setContrast(0);
+ layer.setHue(0);
+ layer.setSaturation(0);
+ layer.setOpacity(1);
+ layer.setVisible(true);
+});
+keyboardInteraction.addCallback('b', function() {
+ layer.setBrightness(layer.getBrightness() - 0.1);
+});
+keyboardInteraction.addCallback('B', function() {
+ layer.setBrightness(layer.getBrightness() + 0.1);
+});
+keyboardInteraction.addCallback('c', function() {
+ layer.setContrast(layer.getContrast() - 0.1);
+});
+keyboardInteraction.addCallback('C', function() {
+ layer.setContrast(layer.getContrast() + 0.1);
+});
+keyboardInteraction.addCallback('h', function() {
+ layer.setHue(layer.getHue() - 0.1);
+});
+keyboardInteraction.addCallback('H', function() {
+ layer.setHue(layer.getHue() + 0.1);
+});
+keyboardInteraction.addCallback('o', function() {
+ layer.setOpacity(layer.getOpacity() - 0.1);
+});
+keyboardInteraction.addCallback('O', function() {
+ layer.setOpacity(layer.getOpacity() + 0.1);
+});
+keyboardInteraction.addCallback('r', function() {
+ webglMap.setRotation(0);
+});
+keyboardInteraction.addCallback('s', function() {
+ layer.setSaturation(layer.getSaturation() - 0.1);
+});
+keyboardInteraction.addCallback('S', function() {
+ layer.setSaturation(layer.getSaturation() + 0.1);
+});
+keyboardInteraction.addCallback('vV', function() {
+ layer.setVisible(!layer.getVisible());
+});
+domMap.getInteractions().push(keyboardInteraction);
+
+goog.exportSymbol('layer', layer);
+goog.exportSymbol('layers', layers);
+goog.exportSymbol('domMap', domMap);
+goog.exportSymbol('webglMap', webglMap);
diff --git a/demos/side-by-side/side-by-side.json b/demos/side-by-side/side-by-side.json
new file mode 100644
index 0000000000..9b14a6766a
--- /dev/null
+++ b/demos/side-by-side/side-by-side.json
@@ -0,0 +1,9 @@
+{
+
+ "id": "demo-side-by-side",
+
+ "inherits": "../../base.json",
+
+ "inputs": "demos/side-by-side/side-by-side.js"
+
+}
diff --git a/demos/two-layers/index.html.in b/demos/two-layers/index.html.in
new file mode 100644
index 0000000000..0e3abdb732
--- /dev/null
+++ b/demos/two-layers/index.html.in
@@ -0,0 +1,32 @@
+
+
+
+
+ ol3 two-layers demo
+
+
+ ol3 two-layers demo
+
+
+ DOM
+ WebGL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demos/two-layers/two-layers.js b/demos/two-layers/two-layers.js
new file mode 100644
index 0000000000..1428894886
--- /dev/null
+++ b/demos/two-layers/two-layers.js
@@ -0,0 +1,44 @@
+goog.require('ol3.Coordinate');
+goog.require('ol3.RendererHint');
+goog.require('ol3.control.Attribution');
+goog.require('ol3.createMap');
+goog.require('ol3.layer.BingMaps');
+goog.require('ol3.layer.TileJSON');
+
+
+var layer1 = new ol3.layer.BingMaps(
+ ol3.BingMapsStyle.AERIAL,
+ 'AheP841R-MsLErKQChaTba_xDoOCl40-EeTubD9uNhNAyQTePwFY9iVD1_pyqqlE');
+var layer2 = new ol3.layer.TileJSON(
+ 'http://api.tiles.mapbox.com/v3/mapbox.va-quake-aug.jsonp');
+
+var layers = new ol3.Collection([layer1, layer2]);
+
+var webglMap = ol3.createMap(
+ document.getElementById('webglMap'),
+ {'layers': new ol3.Collection([layer1, layer2])},
+ ol3.RendererHint.WEBGL);
+
+goog.events.listen(layer2, goog.events.EventType.LOAD, function() {
+ webglMap.setUserCenter(new ol3.Coordinate(-77.93254999999999, 37.9555));
+ webglMap.setResolution(layer2.getStore().getResolutions()[5]);
+});
+
+var domMap = ol3.createMap(
+ document.getElementById('domMap'),
+ {},
+ ol3.RendererHint.DOM);
+domMap.bindTo('center', webglMap);
+domMap.bindTo('layers', webglMap);
+domMap.bindTo('resolution', webglMap);
+domMap.bindTo('rotation', webglMap);
+
+var attributionControl = new ol3.control.Attribution(webglMap);
+document.getElementById('attribution').appendChild(
+ attributionControl.getElement());
+
+goog.exportSymbol('layer1', layer1);
+goog.exportSymbol('layer2', layer2);
+goog.exportSymbol('layers', layers);
+goog.exportSymbol('domMap', domMap);
+goog.exportSymbol('webglMap', webglMap);
diff --git a/demos/two-layers/two-layers.json b/demos/two-layers/two-layers.json
new file mode 100644
index 0000000000..722701e841
--- /dev/null
+++ b/demos/two-layers/two-layers.json
@@ -0,0 +1,9 @@
+{
+
+ "id": "demo-two-layers",
+
+ "inherits": "../../base.json",
+
+ "inputs": "demos/two-layers/two-layers.js"
+
+}
diff --git a/doc/ol3.md b/doc/ol3.md
new file mode 100644
index 0000000000..3b78e33c8c
--- /dev/null
+++ b/doc/ol3.md
@@ -0,0 +1,274 @@
+CLASS HIERARCHY
+===============
+
+```
+goog.math.Coordinate // Simple 2D point
+|
++- TileCoord
+
+goog.math.Box
+|
++- Extent // The extent of a single object in two dimensions, projection not stored
+|
++- TileBounds // A range of tiles in two dimensions, integer coordinates, z not stored
+
+
+Projection
+
+
+goog.events.EventTarget
+|
++- MVCObject
+| |
+| +- Camera
+| |
+| +- Control
+| | |
+| | +- ?
+| |
+| +- Layer
+| | |
+| | +- TileLayer
+| | | |
+| | | +- TMSTileLayer
+| | | |
+| | | +- WMTSTileLayer
+| | | |
+| | | +- XYZTileLayer / OSMTileLayer
+| | |
+| | +- VectorLayer
+| | |
+| | +- ImageLayer
+| |
+| +- LayerRenderer
+| |
+| +- LayerRendererOptions
+| |
+| +- Map
+| |
+| +- MapRenderer
+| | |
+| | +- HTMLMapRenderer
+| | |
+| | +- WebGLMapRenderer
+| |
+| +- MVCArray
+| | |
+| | +- ControlArray
+| | |
+| | +- LayerViewArray
+|
+| +- TileQueue
+|
++- Tile
+```
+
+
+Layer renderer hierarchy
+------------------------
+
+```
+goog.events.EventTarget
+|
++- MVCObject
+ |
+ +- LayerRenderer
+ |
+ +- SingleTileLayerRenderer
+ | |
+ | +- HTMLSingleTileLayerRenderer
+ | |
+ | +- WebGLSingleTileLayerRenderer
+ |
+ +- TileLayerRenderer
+ | |
+ | +- HTMLTileLayerRenderer
+ | |
+ | +- WebGLTileLayerRenderer
+ |
+ +- VectorLayerRenderer
+ | |
+ | +- HTMLVectorLayerRenderer
+ | | |
+ | | +- SVGHTMLVectorLayerRenderer
+ | | |
+ | | +- Canvas2DHTMLVectorLayerRenderer
+ | | |
+ | | +- VMLHTMLVectorLayerRenderer
+ | |
+ | +- WebGLVectorLayerRenderer
+```
+
+
+OBJECT PROPERTIES AND METHODS
+=============================
+
+Notation:
+
+- `property type` property with type, trailing ? indicates unsure, getters and setters are assumed to exist.
+- `f(args) -> type` function taking args returning type.
+- `f(args) -> type = something` f is a trivial wrapper around something.
+- `fires 'x'` fires events of type 'x'.
+
+Principles:
+
+- All non-trivial objects inherit from `MVCObject`.
+- All non-trivial collections are either `MVCArrays` or a child class thereof.
+- Resolutions are `Array.`, infinitely scalable resources (e.g. vectore layers) have resolutions == null.
+
+```
+MVCObject
+ as Google Maps MVCObject
+ freeze()
+ unfreeze()
+
+TileCoord
+ clone() -> TileCoord
+ getHash() -> number
+
+TileBounds
+ forEachTileCoord(z, function(tileCoord))
+
+Tile
+ tileCoord TileCoord
+ url string
+ state UNLOADED | LOADING | LOADED
+ fires 'loaded' // when loaded
+ fires 'aborted' // when loading is aborted
+
+Camera
+ position goog.math.Coordinate
+ resolution number
+ rotation number
+
+Layer
+ projections Array.
+ extent Extent
+ getResolutions() -> Array.|null
+ fires 'change' // when data changes
+
+LayerArray
+ getResolutions() -> Array.|null
+ getMaxResolution() = this.getResolutions()[0] | null
+
+LayerRendererOptions
+ layer Layer
+ visible boolean
+ opacity number
+ brightness number
+ color number
+ hue number
+ saturation number
+
+Map
+ projection Projection
+ renderer Renderer
+ layers LayerArray
+ addLayer(layer) = layers.push(layer)
+ getExtent() -> Extent
+ getMaxResolution() = layers.getMaxResolution()
+
+TileGrid
+ resolutions Array.
+ extent ol.Extent
+ xEast boolean
+ ySouth boolean
+ origin(s) Coord|Array.
+ tileSize goog.math.Size
+ forEachTileCoordChild(tileCoord, function(z, TileBounds))
+ forEachTileCoordParent(tileCoord, function(z, TileBounds))
+ getExtentTileBounds(z, extent) -> TileBounds
+ getTileCoord(coordinate) -> TileCoord
+ getTileCoordCenter(tileCoord) -> goog.math.Coordinate
+ getTileCoordExtent(tileCoord) -> ol.Extent
+ getTileCoordResolution(tileCoord) -> number
+ getZForResolution(resolution) -> number
+
+TileLayer
+ tileGrid TileGrid
+ tileUrl function(tileCoord) -> string
+ getTileCoordUrl(tileCoord) -> string = this.tileUrl(tileCoord)
+
+TileQueue
+ camera Camera // or maybe MVCArray. ?
+ getTileCoordPriority(tileCoord) -> number // private
+ enqueueTile(Tile)
+
+VectorLayer
+ forEachFeature(resolution, extent, projection, function(Feature))
+
+Renderer
+ target HTMLDivElement
+ map Map
+ camera Camera
+ getCapabilities() -> Array. // maybe ?
+```
+
+Questions:
+
+- Store tile layer extent in TileLayer or in TileGrid? (not clear)
+
+Two concepts: tile coordinate system range and and available data extent.
+TileGrid extent is range (or validity extent) of the tile coordinate system.
+TileLayer extent is the available data extent. A particular TileGrid may range
+from 0,0 to 10,10. My cache may conform to that grid but I may only have tiles
+ranging from 2,2 to 8,8. When you need to wrap multiple worlds, you pay
+attention to the TileGrid extent. When you need to decide whether or not to
+bother requesting a tile, you pay attention to the TileLayer extent.
+
+- Who determines "best" resolution? (static function?)
+
+
+Todo: if tile layer extent stored in TileLayer rather than TileGrid then extent
+will occasionally need to be passed to TileGrid functions for cropping.
+
+DESIGN ASSERTIONS
+=================
+
+Map
+
+- A map has a renderer (the map renderer).
+- A map has a camera.
+- Multiple maps can share the same camera.
+- A map has a layer list.
+
+Layer
+
+- A layer can have multiple projections (the supported projections).
+- A layer advertizes the projections it supports.
+- A layer returns no data if asked data for an unsupported projection.
+
+LayerRendererOptions
+
+- A layer renderer options object stores view-related states for a layer.
+- Options include visibility, opacity, saturation, hue, etc.
+- A layer renderer options object has a layer.
+- Multiple layer renderer options can share the same layer.
+- In other words a layer can be viewed in different manners.
+
+Renderer
+
+- The map renderer responds to events.
+- The map renderer receives events from the camera.
+- The map renderer creates layer renderers.
+
+Control
+
+- A control may listen to map events.
+- A control may listen to camera events.
+- A map navigation control acts on the camera.
+
+MVC
+
+- Types can be described in MVC terms.
+- Models don't know what rendering means.
+- Maps are models.
+- Layers are models.
+- Layer views are models (sorry!).
+- Cameras are models.
+- Layer lists are collections.
+- Renderers are views.
+- Controls are views or controllers or both.
+- An attribution control is a view.
+- A map navigation control is a controller.
+- A zoom slider control is both a view and a controller.
diff --git a/externs/bingmaps.js b/externs/bingmaps.js
new file mode 100644
index 0000000000..d38bb4e42e
--- /dev/null
+++ b/externs/bingmaps.js
@@ -0,0 +1,176 @@
+/**
+ * @externs
+ */
+
+
+
+/**
+ * @constructor
+ */
+var BingMapsCoverageArea = function() {};
+
+
+/**
+ * @type {Array.}
+ */
+BingMapsCoverageArea.prototype.bbox;
+
+
+/**
+ * @type {number}
+ */
+BingMapsCoverageArea.prototype.zoomMax;
+
+
+/**
+ * @type {number}
+ */
+BingMapsCoverageArea.prototype.zoomMin;
+
+
+
+/**
+ * @constructor
+ */
+var BingMapsImageryProvider = function() {};
+
+
+/**
+ * @type {string}
+ */
+BingMapsImageryProvider.prototype.attribution;
+
+
+/**
+ * @type {Array.}
+ */
+BingMapsImageryProvider.prototype.coverageAreas;
+
+
+
+/**
+ * @constructor
+ */
+var BingMapsImageryMetadataResponse = function() {};
+
+
+/**
+ * @type {string}
+ */
+BingMapsImageryMetadataResponse.prototype.authenticationResultCode;
+
+
+/**
+ * @type {string}
+ */
+BingMapsImageryMetadataResponse.prototype.brandLogoUri;
+
+
+/**
+ * @type {string}
+ */
+BingMapsImageryMetadataResponse.prototype.copyright;
+
+
+/**
+ * @type {Array.}
+ */
+BingMapsImageryMetadataResponse.prototype.resourceSets;
+
+
+/**
+ * @type {number}
+ */
+BingMapsImageryMetadataResponse.prototype.statusCode;
+
+
+/**
+ * @type {string}
+ */
+BingMapsImageryMetadataResponse.prototype.statusDescription;
+
+
+/**
+ * @type {string}
+ */
+BingMapsImageryMetadataResponse.prototype.traceId;
+
+
+
+/**
+ * @constructor
+ */
+var BingMapsResource = function() {};
+
+
+/**
+ * @type {number}
+ */
+BingMapsResource.prototype.imageHeight;
+
+
+/**
+ * @type {string}
+ */
+BingMapsResource.prototype.imageUrl;
+
+
+/**
+ * @type {Array.}
+ */
+BingMapsResource.prototype.imageUrlSubdomains;
+
+
+/**
+ * @type {number}
+ */
+BingMapsResource.prototype.imageWidth;
+
+
+/**
+ * @type {Array.}
+ */
+BingMapsResource.prototype.imageryProviders;
+
+
+/**
+ * @type {Object}
+ */
+BingMapsResource.prototype.vintageEnd;
+
+
+/**
+ * @type {Object}
+ */
+BingMapsResource.prototype.vintageStart;
+
+
+/**
+ * @type {number}
+ */
+BingMapsResource.prototype.zoomMax;
+
+
+/**
+ * @type {number}
+ */
+BingMapsResource.prototype.zoomMin;
+
+
+
+/**
+ * @constructor
+ */
+var BingMapsResourceSet = function() {};
+
+
+/**
+ * @type {number}
+ */
+BingMapsResourceSet.prototype.estimatedTotal;
+
+
+/**
+ * @type {Array.}
+ */
+BingMapsResourceSet.prototype.resources;
diff --git a/externs/geojson.js b/externs/geojson.js
index 68a6bc0178..687eb4cecb 100644
--- a/externs/geojson.js
+++ b/externs/geojson.js
@@ -1,3 +1,4 @@
+
/**
* @fileoverview Externs for GeoJSON.
* @see http://geojson.org/geojson-spec.html
diff --git a/externs/proj4js.js b/externs/proj4js.js
new file mode 100644
index 0000000000..1eab482820
--- /dev/null
+++ b/externs/proj4js.js
@@ -0,0 +1,74 @@
+/**
+ * @externs
+ * @see http://trac.osgeo.org/proj4js/
+ */
+
+
+/**
+ * @type {Object}
+ */
+var Proj4js = {};
+
+
+/**
+ * @type {Object.}
+ */
+Proj4js.defs;
+
+
+/**
+ * @type {function(string)}
+ */
+Proj4js.reportError;
+
+
+
+/**
+ * @constructor
+ * @param {number} x
+ * @param {number} y
+ */
+Proj4js.Point = function(x, y) {};
+
+
+/**
+ * @type {number}
+ */
+Proj4js.Point.prototype.x;
+
+
+/**
+ * @type {number}
+ */
+Proj4js.Point.prototype.y;
+
+
+
+/**
+ * @constructor
+ * @param {string} srsCode
+ * @param {Function=} opt_callback
+ */
+Proj4js.Proj = function(srsCode, opt_callback) {};
+
+
+/**
+ * @type {string}
+ */
+Proj4js.Proj.prototype.title;
+
+
+/**
+ * @type {string}
+ */
+Proj4js.Proj.prototype.units;
+
+
+/**
+ * @nosideeffects
+ * @param {Proj4js.Proj} source
+ * @param {Proj4js.Proj} dest
+ * @param {Proj4js.Point|{x:number, y:number}} point
+ * @return {Proj4js.Point}
+ */
+Proj4js.transform = function(source, dest, point) {};
diff --git a/externs/tilejson.js b/externs/tilejson.js
new file mode 100644
index 0000000000..5927c479ee
--- /dev/null
+++ b/externs/tilejson.js
@@ -0,0 +1,95 @@
+/**
+ * @externs
+ * @see https://github.com/mapbox/tilejson-spec
+ */
+
+
+
+/**
+ * @constructor
+ */
+var TileJSON = function() {};
+
+
+/**
+ * @type {string}
+ */
+TileJSON.prototype.tilejson;
+
+
+/**
+ * @type {string|undefined}
+ */
+TileJSON.prototype.name;
+
+
+/**
+ * @type {string|undefined}
+ */
+TileJSON.prototype.description;
+
+
+/**
+ * @type {string|undefined}
+ */
+TileJSON.prototype.version;
+
+
+/**
+ * @type {string|undefined}
+ */
+TileJSON.prototype.attribution;
+
+
+/**
+ * @type {string|undefined}
+ */
+TileJSON.prototype.template;
+
+
+/**
+ * @type {string|undefined}
+ */
+TileJSON.prototype.legend;
+
+
+/**
+ * @type {string|undefined}
+ */
+TileJSON.prototype.scheme;
+
+
+/**
+ * @type {!Array.}
+ */
+TileJSON.prototype.tiles;
+
+
+/**
+ * @type {!Array.}
+ */
+TileJSON.prototype.grids;
+
+
+/**
+ * @type {number|undefined}
+ */
+TileJSON.prototype.minzoom;
+
+
+/**
+ * @type {number|undefined}
+ */
+TileJSON.prototype.maxzoom;
+
+
+/**
+ * @type {!Array.|undefined}
+ */
+TileJSON.prototype.bounds;
+
+
+/**
+ * @type {!Array.|undefined}
+ */
+TileJSON.prototype.center;
diff --git a/externs/webgl-debug.js b/externs/webgl-debug.js
new file mode 100644
index 0000000000..9f5803831c
--- /dev/null
+++ b/externs/webgl-debug.js
@@ -0,0 +1,98 @@
+/**
+ * @externs
+ * @see http://www.khronos.org/webgl/wiki/Debugging
+ * @see http://www.khronos.org/webgl/wiki/HandlingContextLost
+ */
+
+
+
+/**
+ * @constructor
+ * @extends {WebGLRenderingContext}
+ */
+var WebGLDebugRenderingContext = function() {};
+
+
+
+/**
+ * @constructor
+ * @extends {HTMLCanvasElement}
+ */
+var WebGLDebugLostContextSimulatingCanvas = function() {};
+
+
+/**
+ * @nosideeffects
+ * @return {number}
+ */
+WebGLDebugLostContextSimulatingCanvas.prototype.getNumCalls = function() {};
+
+
+/**
+ */
+WebGLDebugLostContextSimulatingCanvas.prototype.loseContext = function() {};
+
+
+/**
+ * @param {number} numCalls
+ */
+WebGLDebugLostContextSimulatingCanvas.prototype.loseContextInNCalls =
+ function(numCalls) {};
+
+
+/**
+ */
+WebGLDebugLostContextSimulatingCanvas.prototype.restoreContext = function() {};
+
+
+/**
+ * @param {number} timeout
+ */
+WebGLDebugLostContextSimulatingCanvas.prototype.setRestoreTimeout =
+ function(timeout) {};
+
+
+/**
+ * @type {Object}
+ */
+var WebGLDebugUtils = {};
+
+
+/**
+ * @nosideeffects
+ * @param {number} value
+ * @return {string}
+ */
+WebGLDebugUtils.glEnumToString = function(value) {};
+
+
+/**
+ * @nosideeffects
+ * @param {string} functionName
+ * @param {Array} args Args.
+ * @return {string} String.
+ */
+WebGLDebugUtils.glFunctionArgsToString = function(functionName, args) {};
+
+
+/**
+ * @param {WebGLRenderingContext} ctx
+ */
+WebGLDebugUtils.init = function(ctx) {};
+
+
+/**
+ * @param {HTMLCanvasElement} canvas
+ * @return {WebGLDebugLostContextSimulatingCanvas}
+ */
+WebGLDebugUtils.makeLostContextSimulatingCanvas = function(canvas) {};
+
+
+/**
+ * @param {WebGLRenderingContext} context
+ * @param {Function=} opt_onErrorFunc
+ * @param {Function=} opt_onFunc
+ * @return {WebGLDebugRenderingContext}
+ */
+WebGLDebugUtils.makeDebugContext =
+ function(context, opt_onErrorFunc, opt_onFunc) {};
diff --git a/main.json b/main.json
deleted file mode 100644
index 5e5a6efe1f..0000000000
--- a/main.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "id": "ol",
-
- "inputs": [
- "src/ol.js",
- "src/ol.export.js"
- ],
- "paths": [
- "src"
- ],
-
- "define": {
- // "goog.dom.ASSUME_STANDARDS_MODE": true,
- "goog.DEBUG": false
- },
-
- "externs": [
- "externs/geojson.js"
- ],
-
- "mode": "ADVANCED",
- "level": "VERBOSE",
- "pretty-print": true,
- "debug": true,
-
- // "experimental-compiler-options": {
- // "generateExports": true
- // },
-
- "checks": {
- // acceptable values are "ERROR", "WARNING", and "OFF"
- "accessControls": "WARNING",
- "visibility": "WARNING",
- "checkTypes": "WARNING",
- "checkRegExp": "WARNING",
- "checkVars": "WARNING",
- "deprecated": "WARNING",
- "fileoverviewTags": "WARNING",
- "invalidCasts": "WARNING",
- "missingProperties": "WARNING",
- "nonStandardJsDocs": "WARNING",
- "undefinedVars": "WARNING"
- },
-
- "jsdoc-html-output-path": "jsdoc"
-}
diff --git a/readme.md b/readme.md
index f41fcc8bb8..1895d719de 100644
--- a/readme.md
+++ b/readme.md
@@ -1,79 +1,35 @@
-# OpenLayers
+# OpenLayers 3
-Copyright (c) 2005-2012 OpenLayers Contributors. See authors.txt for
-more details.
+## Build it
-OpenLayers is a JavaScript library for building map applications
-on the web. OpenLayers is made available under a BSD-license.
-Please see license.txt in this distribution for more details.
+Run make:
-## Getting OpenLayers
+ $ make
-OpenLayers lives at http://www.openlayers.org/. Find details on downloading stable releases or the development version the [development site](http://trac.osgeo.org/openlayers/wiki/HowToDownload).
+## Run the examples
-## Installing OpenLayers
+Run make (as above), then explore the `demos/` directory with your web browser.
-You can use OpenLayers as-is by copying build/OpenLayers.js and the
-entire theme/ and img/ directories up to your webserver and putting them
-in the same directory. The files can be in subdirectories on your website,
-or right in the root of the site, as in these examples.
-To include the OpenLayers library in your web page from the root of the site, use:
+## Run the examples in debug mode
-
+Run the [Plovr](http://plovr.com/) web server with:
-As an example, using bash (with the release files in ~/openlayers):
+ $ make serve
- $ cd /var/www/html
- $ cp ~/openlayers/OpenLayers.js ./
- $ cp -R ~/openlayers/theme ./
- $ cp -R ~/openlayers/img ./
+Then, start a simple webserver, for example:
-If you want to use the multiple-file version of OpenLayers (for, say,
-debugging or development purposes), copy the lib/ directory up to your
-webserver in the same directory you put the img/ folder. Then add
-the following to your web page instead:
+ $ python -mSimpleHTTPServer
-
+Explore the `demos/` directory through this server, for example . You can turn off compilation by appending `?mode=RAW` to the URL, for example .
-As an example, using bash (with the release files in ~/openlayers):
+Note that appending `?mode=RAW` doesn't work with `file://` URLs, which is why you need to access the `demos/` directory though a web server.
- $ cd /var/www/html
- $ cp -R ~/openlayers/lib ./
- $ cp -R ~/openlayers/theme ./
- $ cp -R ~/openlayers/img ./
+## Run tests
-## Alternate OpenLayers Versions in this Release
+Run the plovr web server (see above), then open in your browser and select *List of tests* or *Test runner*.
-The following versions of OpenLayers single file builds are included in this release
-and can be used in place of OpenLayers.js in any of the above instructions:
+## Run the linter
-1. OpenLayers.js - full build --> Includes everything except the alternate language
- translations and deprecated classes.
-2. OpenLayers.mobile.js - a mobile focused build --> Includes a subset of the OpenLayers
- library to serve common mobile web app use cases. This build provides access to
- OpenStreetMap, Bing, WMS, WFS and vector layers; touch optimized controls; geolocation;
- vector editing and interaction tools. The examples tagged ``mobile`` can use this build.
-3. OpenLayers.light.js - a simple use case focused build --> Includes a subset of the
- OpenLayers library to serve the basic use case of displaying points and polygons
- on a map. This build provides access to OpenStreetMap, Bing, Google, WMS, and
- vector layers; basic map controls; and vector interaction tools. The examples
- tagged ``light`` can use this build.
-
-## Using OpenLayers in Your Own Website
-
-The [examples directory](http://openlayers.org/dev/examples/) is full of useful examples.
-
-Documentation is available at http://trac.osgeo.org/openlayers/wiki/Documentation.
-You can generate the API documentation with http://www.naturaldocs.org/
-As an example, using bash (with the release files in ~/openlayers):
-
- $ cd ~/openlayers/
- $ /path/to/NaturalDocs -i lib/ -o HTML doc/ -p doc_config/ -s Default OL
-
-Information on changes in the API is available in release notes found in the notes folder.
-
-## Contributing to OpenLayers
-
-Please join the email lists at http://openlayers.org/mailman/listinfo
-Patches are welcome!
+First, install the [Closure Linter](https://developers.google.com/closure/utilities/docs/linter_howto). Then:
+ $ make lint
diff --git a/src/api/bounds.js b/src/api/bounds.js
deleted file mode 100644
index 9575ba0c7b..0000000000
--- a/src/api/bounds.js
+++ /dev/null
@@ -1,149 +0,0 @@
-goog.provide('ol.bounds');
-
-goog.require('ol.Bounds');
-goog.require('ol.projection');
-
-
-/**
- * @typedef {ol.Bounds|Array.|Object} bounds Location.
- */
-ol.LocLike;
-
-
-
-/**
- * @export
- * @param {ol.LocLike} opt_arg Location.
- * @return {ol.Bounds} Location.
- */
-ol.bounds = function(opt_arg){
-
- if (opt_arg instanceof ol.Bounds) {
- return opt_arg;
- }
-
- var minX = 0;
- var minY = 0;
- var maxX = 0;
- var maxY = 0;
- var projection;
-
- var x = 0;
- var y = 0;
- var z;
-
- if (goog.isArray(opt_arg)) {
- minX = opt_arg[0];
- minY = opt_arg[1];
- maxX = opt_arg[2];
- maxY = opt_arg[3];
- } else if (goog.isObject(opt_arg)) {
- ol.base.checkKeys(opt_arg, ['minX', 'minY', 'maxX', 'maxY', 'projection']);
- minX = ol.API ? opt_arg['minX'] : opt_arg.minX;
- minY = ol.API ? opt_arg['minY'] : opt_arg.minY;
- maxX = ol.API ? opt_arg['maxX'] : opt_arg.maxX;
- maxY = ol.API ? opt_arg['maxY'] : opt_arg.maxY;
- projection = ol.projection(ol.API ? opt_arg['projection'] : opt_arg.projection);
- }
- else {
- throw new Error('ol.bounds');
- }
-
- var bounds = new ol.Bounds(minX, minY, maxX, maxY, projection);
- return bounds;
-
-};
-
-
-/**
- * @export
- * @param {ol.Projection=} opt_arg Projection.
- * @return {ol.Bounds|ol.Projection|undefined} Result.
- */
-ol.Bounds.prototype.projection = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setProjection(opt_arg);
- return this;
- }
- else {
- return this.getProjection();
- }
-};
-
-
-/**
- * @export
- * @param {number=} opt_arg Minimum X.
- * @return {!ol.Bounds|number} Result.
- */
-ol.Bounds.prototype.minX = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setMinX(opt_arg);
- return this;
- }
- else {
- return this.getMinX();
- }
-};
-
-
-/**
- * @export
- * @param {number=} opt_arg Minimum Y.
- * @return {ol.Bounds|number} Result.
- */
-ol.Bounds.prototype.minY = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setMinY(opt_arg);
- return this;
- }
- else {
- return this.getMinY();
- }
-};
-
-
-/**
- * @export
- * @param {number=} opt_arg Maximum X.
- * @return {ol.Bounds|number} Result.
- */
-ol.Bounds.prototype.maxX = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setMaxX(opt_arg);
- return this;
- }
- else {
- return this.getMaxX();
- }
-};
-
-
-/**
- * @export
- * @param {number=} opt_arg Maximum Y.
- * @return {ol.Bounds|number} Result.
- */
-ol.Bounds.prototype.maxY = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setMaxY(opt_arg);
- return this;
- }
- else {
- return this.getMaxY();
- }
-};
-
-/**
- * Transform this node into another coordinate reference system. Returns a new
- * bounds instead of modifying this bounds.
- *
- * @param {ol.Projection|string} proj Target projection (or string identifier).
- * @return {ol.Bounds} A new bounds in the target projection.
- */
-ol.Bounds.prototype.transform = function(proj) {
- if (goog.isString(proj)) {
- proj = new ol.Projection(proj);
- }
- return this.doTransform(proj);
-};
diff --git a/src/api/feature.js b/src/api/feature.js
deleted file mode 100644
index 62b3b8ce26..0000000000
--- a/src/api/feature.js
+++ /dev/null
@@ -1,92 +0,0 @@
-goog.provide('ol.feature');
-
-goog.require('ol.base');
-goog.require('ol.Feature');
-goog.require('ol.geom.Geometry');
-
-
-/**
- * @typedef {ol.Feature|Object|string}
- */
-ol.FeatureLike;
-
-
-/**
- * @export
- * @param {ol.FeatureLike=} opt_arg Argument.
- * @return {ol.Feature} Feature.
- */
-ol.feature = function(opt_arg){
-
- /** @type {Object|undefined} */
- var properties;
- /** @type {ol.geom.Geometry|undefined} */
- var geometry;
- /** @type {string|undefined} */
- var type;
-
- if (arguments.length == 1) {
- if (opt_arg instanceof ol.Feature) {
- return opt_arg;
- }
- else if (goog.isObject(opt_arg)) {
- ol.base.checkKeys(opt_arg, ['geometry', 'properties', 'type']);
- properties = ol.API ? opt_arg['properties'] : opt_arg.properties;
- geometry = ol.API ? opt_arg['geometry'] : opt_arg.geometry;
- type = ol.API ? opt_arg['type'] : opt_arg.type;
- }
- else {
- throw new Error('ol.feature');
- }
- }
-
- var feature = new ol.Feature();
- if (goog.isDef(type) && type == 'Feature') {
- //this means it is a GeoJSON object
- //format.read(opt_arg);
-
- } else {
- if (goog.isDef(properties)) {
- feature.setAttributes(properties);
- }
- if (goog.isDef(geometry)) {
- feature.setGeometry(geometry);
- }
- }
- return feature;
-
-};
-
-/**
- * @export
- * @param {!string} attr The name of the attribute to be set.
- * @param {string|number|boolean} value The value of the attribute to be set.
- * @returns {ol.Feature} The feature so calls can be chained
- */
-ol.Feature.prototype.set = function(attr, value) {
- this.setAttribute(attr, value);
- return this;
-};
-
-/**
- * @export
- * @param {!string} attr The name of the attribute to be set.
- * @returns {string|number|boolean|undefined} The attribute value requested.
- */
-ol.Feature.prototype.get = function(attr) {
- return this.getAttribute(attr);
-};
-
-/**
- * @export
- * @param {ol.geom.Geometry=} opt_arg
- * @returns {ol.Feature|ol.geom.Geometry|undefined} get or set the geometry on a feature
- */
-ol.Feature.prototype.geometry = function(opt_arg) {
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setGeometry(opt_arg);
- return this;
- } else {
- return this.getGeometry();
- }
-};
diff --git a/src/api/geom/collection.js b/src/api/geom/collection.js
deleted file mode 100644
index 9a93ead0c4..0000000000
--- a/src/api/geom/collection.js
+++ /dev/null
@@ -1,133 +0,0 @@
-goog.provide('ol.geom.collection');
-
-goog.require('ol.geom.Collection');
-goog.require('ol.geom.point');
-goog.require('ol.projection');
-
-/**
- * @export
- * @param {Array.} opt_arg Components.
- * @return {ol.geom.Collection} Collection.
- */
-ol.geom.collection = function(opt_arg){
-
- if (opt_arg instanceof ol.geom.Collection) {
- return opt_arg;
- }
-
- var components = [];
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- if (goog.isArray(opt_arg)) {
- var allValid = goog.array.every(opt_arg, function(geom){
- if (geom instanceof ol.geom.Geometry) {
- components.push(geom);
- return true;
- } else {
- return false;
- }
- });
- if (!allValid) {
- var msg = 'ol.geom.collection: at least one component '
- + 'definition was no geometry.';
- throw new Error(msg);
- }
- } else {
- throw new Error('ol.geom.collection');
- }
- }
-
- var c = new ol.geom.Collection(components);
- return c;
-};
-goog.inherits(ol.geom.collection, ol.geom.geometry);
-
-/**
- * @export
- * @param {Array.=} opt_arg An array of point specifications.
- * @return {Array.|ol.geom.Collection|undefined} Result.
- */
-ol.geom.Collection.prototype.components = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- var components = [],
- allValid = false;
-
- allValid = goog.array.every(opt_arg, function(geom){
- if (geom instanceof ol.geom.Geometry) {
- components.push(geom);
- return true;
- } else {
- return false;
- }
- });
- if (!allValid) {
- components = [];
- }
- this.setComponents(components);
- return this;
- }
- else {
- return this.getComponents();
- }
-};
-
-/**
- * @export
- * @param {ol.geom.Geometry} geom A geometry.
- * @param {number=} opt_index An optional index to add the point(s) at. If not
- * provided, the point(s) will be added to the end of the list of components.
- * @return {ol.geom.Collection} The Collection instance.
- */
-ol.geom.Collection.prototype.add = function(geom, opt_index){
- var index = this.components_.length;
- if (arguments.length == 2 && goog.isDef(opt_index)) {
- index = opt_index;
- }
- this.addComponent(geom, index);
- return this;
-};
-
-/**
- * @export
- * @param {Array.} components Some point specifications.
- * @param {number=} opt_index An optional index to add the components at. If not
- * provided, the components will be added to the end of the list of
- * components.
- * @return {ol.geom.Collection} The Collection instance.
- */
-ol.geom.Collection.prototype.addAll = function(components, opt_index){
- var index = this.components_.length;
-
- if (arguments.length == 2 && goog.isDef(opt_index)) {
- index = opt_index;
- }
-
- goog.array.every(components, function(c){
- this.addComponent(c, index);
- index++;
- return true;
- }, this);
-
- return this;
-};
-
-/**
- * @export
- * @param {(ol.geom.Geometry|Array.)} components A point specification or
- * an array of point specifications.
- * @return {ol.geom.Collection} The Collection instance.
- */
-ol.geom.Collection.prototype.remove = function(components){
- var compArr = [];
- if (!goog.isArray(components)) {
- compArr.push(components);
- } else {
- compArr = components;
- }
-
- goog.array.every(compArr, function(c){
- this.removeComponent(c);
- return true;
- }, this);
-
- return this;
-};
diff --git a/src/api/geom/geometry.js b/src/api/geom/geometry.js
deleted file mode 100644
index 5d8b9d09b9..0000000000
--- a/src/api/geom/geometry.js
+++ /dev/null
@@ -1,35 +0,0 @@
-goog.provide('ol.geom.geometry');
-
-goog.require('ol.geom.Geometry');
-
-/**
- * @export
- * @return {ol.geom.Geometry} Geometry..
- */
-ol.geom.geometry = function(){
- var g = new ol.geom.Geometry();
- return g;
-};
-
-/**
- * @export
- * @param {ol.Bounds=} opt_arg new Bounds.
- * @return {ol.geom.Geometry|ol.Bounds|undefined} either a Geometry (when used as
- * setter) or a Bounds/undefined (if used as getter).
- */
-ol.geom.Geometry.prototype.bounds = function(opt_arg) {
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- return this.setBounds(opt_arg);
- } else {
- return this.getBounds();
- }
-};
-
-/**
- * Returns the centroid of the geometry.
- *
- * @returns {ol.geom.Point} The centroid of the geometry.
- */
-ol.geom.Geometry.prototype.centroid = function() {
- return this.getCentroid();
-};
\ No newline at end of file
diff --git a/src/api/geom/linestring.js b/src/api/geom/linestring.js
deleted file mode 100644
index 4debb2a62c..0000000000
--- a/src/api/geom/linestring.js
+++ /dev/null
@@ -1,142 +0,0 @@
-goog.provide('ol.geom.linestring');
-
-goog.require('ol.geom.LineString');
-goog.require('ol.geom.point');
-goog.require('ol.projection');
-
-/**
- * @typedef {Array.} linestring LineString.
- */
-ol.LineStringLike;
-
-/**
- * @export
- * @param {ol.LineStringLike} opt_arg Points.
- * @return {ol.geom.LineString} LineString.
- */
-ol.geom.linestring = function(opt_arg){
-
- if (opt_arg instanceof ol.geom.LineString) {
- return opt_arg;
- }
-
- var vertices = [];
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- if (goog.isArray(opt_arg)) {
- var allValid = goog.array.every(opt_arg, function(spec){
- var v = ol.geom.point(spec);
- if (v instanceof ol.geom.Point) {
- vertices.push(v);
- return true;
- } else {
- return false;
- }
- });
- if (!allValid) {
- var msg = 'ol.geom.linestring: at least one point '
- + 'definition was erroneous.';
- throw new Error(msg);
- }
- } else {
- throw new Error('ol.geom.linestring');
- }
- }
-
- var ls = new ol.geom.LineString(vertices);
- return ls;
-};
-goog.inherits(ol.geom.linestring, ol.geom.geometry);
-
-/**
- * @export
- * @param {Array.=} opt_arg An array of vertex specifications.
- * @return {Array.|ol.geom.LineString|undefined} Result.
- */
-ol.geom.LineString.prototype.vertices = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- var vertices = [],
- allValid = false;
- goog.array.every(opt_arg, function(spec){
- var v = ol.geom.point(spec);
- if (v instanceof ol.geom.Point) {
- vertices.push(v);
- return true;
- } else {
- return false;
- }
- });
- if (!allValid) {
- vertices = [];
- }
- this.setVertices(vertices);
- return this;
- }
- else {
- return this.getVertices();
- }
-};
-
-/**
- * @export
- * @param {ol.PointLike} vertex A point specification.
- * @param {number=} opt_index An optional index to add the vertices at. If not
- * provided, the vertex will be added to the end of the list of vertices.
- * @return {ol.geom.LineString} The LineString instance.
- */
-ol.geom.LineString.prototype.add = function(vertex, opt_index){
- var index = this.vertices_.length,
- allValid = false,
- v = ol.geom.point(vertex);
- if (arguments.length == 2 && goog.isDef(opt_index)) {
- index = opt_index;
- }
- this.addVertex(v, index);
- return this;
-};
-
-/**
- * @export
- * @param {Array.} vertices Some vertex specifications.
- * @param {number=} opt_index An optional index to add the vertices at. If not
- * provided, the points will be added to the end of the list of vertices.
- * @return {ol.geom.LineString} The LineString instance.
- */
-ol.geom.LineString.prototype.addAll = function(vertices, opt_index){
- var index = this.vertices_.length,
- v;
-
- if (arguments.length == 2 && goog.isDef(opt_index)) {
- index = opt_index;
- }
-
- goog.array.every(vertices, function(vertexSpec){
- v = ol.geom.point(vertexSpec);
- this.addVertex(v, index);
- index++;
- return true;
- }, this);
-
- return this;
-};
-
-/**
- * @export
- * @param {(ol.geom.Point|Array.)} vertices A point specification or
- * an array of point specifications.
- * @return {ol.geom.LineString} The MultiPoint instance.
- */
-ol.geom.LineString.prototype.remove = function(vertices){
- var vertexArr = [];
- if (!goog.isArray(vertices)) {
- vertexArr.push(vertices);
- } else {
- vertexArr = vertices;
- }
-
- goog.array.every(vertexArr, function(v){
- this.removeVertex(v);
- return true;
- }, this);
-
- return this;
-};
diff --git a/src/api/geom/multilinestring.js b/src/api/geom/multilinestring.js
deleted file mode 100644
index 6349fb3be4..0000000000
--- a/src/api/geom/multilinestring.js
+++ /dev/null
@@ -1,138 +0,0 @@
-goog.provide('ol.geom.multilinestring');
-
-goog.require('ol.geom.MultiLineString');
-goog.require('ol.geom.point');
-goog.require('ol.geom.collection');
-goog.require('ol.projection');
-
-/**
- * @export
- * @param {Array.} opt_arg Point.
- * @return {ol.geom.MultiLineString} MultiLineString.
- */
-ol.geom.multilinestring = function(opt_arg){
-
- if (opt_arg instanceof ol.geom.MultiLineString) {
- return opt_arg;
- }
-
- var ls = [];
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- if (goog.isArray(opt_arg)) {
- var allValid = goog.array.every(opt_arg, function(spec){
- var l = ol.geom.linestring(spec);
- if (l instanceof ol.geom.LineString) {
- ls.push(l);
- return true;
- } else {
- return false;
- }
- });
- if (!allValid) {
- var msg = 'ol.geom.linestring: at least one linestring '
- + 'definition was erroneous.';
- throw new Error(msg);
- }
- } else {
- throw new Error('ol.geom.multilinestring');
- }
- }
-
- var mls = new ol.geom.MultiLineString(ls);
- return mls;
-};
-goog.inherits(ol.geom.multilinestring, ol.geom.collection);
-
-/**
- * @export
- * @param {Array.=} opt_arg An array of point specifications.
- * @return {Array.|ol.geom.MultiLineString|undefined} Result.
- */
-ol.geom.MultiLineString.prototype.linestrings = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- var ls = [],
- allValid = false;
- allValid = goog.array.every(opt_arg, function(spec){
- var l = ol.geom.linestring(spec);
- if (l instanceof ol.geom.LineString) {
- ls.push(l);
- return true;
- } else {
- return false;
- }
- });
- if (!allValid) {
- ls = [];
- }
- this.setComponents(ls);
- return this;
- }
- else {
- return this.getComponents();
- }
-};
-
-/**
- * @export
- * @param {ol.LineStringLike} line A linestring specification.
- * @param {number=} opt_index An optional index to add the point(s) at. If not
- * provided, the point(s) will be added to the end of the list of points.
- * @return {ol.geom.MultiLineString} The MultiPoint instance.
- */
-ol.geom.MultiLineString.prototype.add = function(line, opt_index){
- var index = this.getLineStrings().length,
- l = ol.geom.linestring(line);
- if (arguments.length == 2 && goog.isDef(opt_index)) {
- index = opt_index;
- }
- this.addLineString(l, index);
- return this;
-};
-
-/**
- * @export
- * @param {Array.} lines Some linestring specifications.
- * @param {number=} opt_index An optional index to add the points at. If not
- * provided, the linestrings will be added to the end of the list of
- * linestrings.
- * @return {ol.geom.MultiLineString} The MultiLineString instance.
- */
-ol.geom.MultiLineString.prototype.addAll = function(lines, opt_index){
- var index = this.getLineStrings().length,
- l;
-
- if (arguments.length == 2 && goog.isDef(opt_index)) {
- index = opt_index;
- }
-
- goog.array.every(lines, function(pointSpec){
- l = ol.geom.linestring(pointSpec);
- this.addLineString(l, index);
- index++;
- return true;
- }, this);
-
- return this;
-};
-
-/**
- * @export
- * @param {(ol.geom.LineString|Array.)} lines A linestring
- * specification or an array of linestring specifications.
- * @return {ol.geom.MultiLineString} The MultiLineString instance.
- */
-ol.geom.MultiLineString.prototype.remove = function(lines){
- var lineArr = [];
- if (!goog.isArray(lines)) {
- lineArr.push(lines);
- } else {
- lineArr = lines;
- }
-
- goog.array.every(lineArr, function(l){
- this.removeLineString(l);
- return true;
- }, this);
-
- return this;
-};
diff --git a/src/api/geom/multipoint.js b/src/api/geom/multipoint.js
deleted file mode 100644
index 5ca6ec317e..0000000000
--- a/src/api/geom/multipoint.js
+++ /dev/null
@@ -1,137 +0,0 @@
-goog.provide('ol.geom.multipoint');
-
-goog.require('ol.geom.MultiPoint');
-goog.require('ol.geom.point');
-goog.require('ol.geom.collection');
-goog.require('ol.projection');
-
-/**
- * @export
- * @param {Array.} opt_arg Point.
- * @return {ol.geom.MultiPoint} MultiPoint.
- */
-ol.geom.multipoint = function(opt_arg){
-
- if (opt_arg instanceof ol.geom.MultiPoint) {
- return opt_arg;
- }
-
- var points = [];
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- if (goog.isArray(opt_arg)) {
- var allValid = goog.array.every(opt_arg, function(spec){
- var p = ol.geom.point(spec);
- if (p instanceof ol.geom.Point) {
- points.push(p);
- return true;
- } else {
- return false;
- }
- });
- if (!allValid) {
- var msg = 'ol.geom.multipoint: at least one point '
- + 'definition was erroneous.';
- throw new Error(msg);
- }
- } else {
- throw new Error('ol.geom.multipoint');
- }
- }
-
- var mp = new ol.geom.MultiPoint(points);
- return mp;
-};
-goog.inherits(ol.geom.multipoint, ol.geom.collection);
-
-/**
- * @export
- * @param {Array.=} opt_arg An array of point specifications.
- * @return {Array.|ol.geom.MultiPoint|undefined} Result.
- */
-ol.geom.MultiPoint.prototype.points = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- var points = [],
- allValid = false;
- allValid = goog.array.every(opt_arg, function(spec){
- var p = ol.geom.point(spec);
- if (p instanceof ol.geom.Point) {
- points.push(p);
- return true;
- } else {
- return false;
- }
- });
- if (!allValid) {
- points = [];
- }
- this.setComponents(points);
- return this;
- }
- else {
- return this.getComponents();
- }
-};
-
-/**
- * @export
- * @param {ol.PointLike} point A point specification.
- * @param {number=} opt_index An optional index to add the point(s) at. If not
- * provided, the point(s) will be added to the end of the list of points.
- * @return {ol.geom.MultiPoint} The MultiPoint instance.
- */
-ol.geom.MultiPoint.prototype.add = function(point, opt_index){
- var index = this.getPoints().length,
- p = ol.geom.point(point);
- if (arguments.length == 2 && goog.isDef(opt_index)) {
- index = opt_index;
- }
- this.addPoint(p, index);
- return this;
-};
-
-/**
- * @export
- * @param {Array.} points Some point specifications.
- * @param {number=} opt_index An optional index to add the points at. If not
- * provided, the points will be added to the end of the list of points.
- * @return {ol.geom.MultiPoint} The MultiPoint instance.
- */
-ol.geom.MultiPoint.prototype.addAll = function(points, opt_index){
- var index = this.getPoints().length,
- p;
-
- if (arguments.length == 2 && goog.isDef(opt_index)) {
- index = opt_index;
- }
-
- goog.array.every(points, function(pointSpec){
- p = ol.geom.point(pointSpec);
- this.addPoint(p, index);
- index++;
- return true;
- }, this);
-
- return this;
-};
-
-/**
- * @export
- * @param {(ol.geom.Point|Array.)} points A point specification or
- * an array of point specifications.
- * @return {ol.geom.MultiPoint} The MultiPoint instance.
- */
-ol.geom.MultiPoint.prototype.remove = function(points){
- var pointArr = [];
- if (!goog.isArray(points)) {
- pointArr.push(points);
- } else {
- pointArr = points;
- }
-
- goog.array.every(pointArr, function(p){
- this.removePoint(p);
- return true;
- }, this);
-
- return this;
-};
diff --git a/src/api/geom/point.js b/src/api/geom/point.js
deleted file mode 100644
index e9dab7648c..0000000000
--- a/src/api/geom/point.js
+++ /dev/null
@@ -1,122 +0,0 @@
-goog.provide('ol.geom.point');
-
-goog.require('ol.geom.Point');
-goog.require('ol.projection');
-
-/**
- * @typedef {Array.|Object} point Point.
- */
-ol.PointLike;
-
-/**
- * @export
- * @param {ol.PointLike} opt_arg Point.
- * @return {ol.geom.Point} Point.
- */
-ol.geom.point = function(opt_arg){
-
- if (opt_arg instanceof ol.geom.Point) {
- return opt_arg;
- }
-
- var x = 0;
- var y = 0;
- var z;
- var projection;
-
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- if (goog.isArray(opt_arg)) {
- x = opt_arg[0];
- y = opt_arg[1];
- z = opt_arg[2];
- projection = opt_arg[3];
-
- } else if (goog.isObject(opt_arg)) {
- x = ol.API ? opt_arg['x'] : opt_arg.x;
- y = ol.API ? opt_arg['y'] : opt_arg.y;
- z = ol.API ? opt_arg['z'] : opt_arg.z;
- projection = ol.API ? opt_arg['projection'] : opt_arg.projection;
- } else {
- throw new Error('ol.geom.point');
- }
- }
- if (goog.isDef(projection)) {
- projection = ol.projection(projection);
- }
-
- var p = new ol.geom.Point(x,y,z,projection);
- return p;
-};
-goog.inherits(ol.geom.point, ol.geom.geometry);
-
-
-/**
- * @export
- * @param {number=} opt_arg X.
- * @return {ol.geom.Point|number} Result.
- */
-ol.geom.Point.prototype.x = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setX(opt_arg);
- return this;
- }
- else {
- return this.getX();
- }
-};
-
-
-/**
- * @export
- * @param {number=} opt_arg Y.
- * @return {ol.geom.Point|number} Result.
- */
-ol.geom.Point.prototype.y = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setY(opt_arg);
- return this;
- }
- else {
- return this.getY();
- }
-};
-
-
-/**
- * @export
- * @param {number=} opt_arg Z.
- * @return {ol.geom.Point|number|undefined} Result.
- */
-ol.geom.Point.prototype.z = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setZ(opt_arg);
- return this;
- }
- else {
- return this.getZ();
- }
-};
-
-/**
- * @export
- * @param {ol.Projection=} opt_arg Projection.
- * @return {ol.geom.Point|ol.Projection|undefined} Result.
- */
-ol.geom.Point.prototype.projection = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setProjection(ol.projection(opt_arg));
- return this;
- }
- else {
- return this.getProjection();
- }
-};
-
-/**
- * Returns the centroid of this point; which is a clone of the point itself.
- *
- * @return {ol.geom.Point} The centroid.
- */
-ol.geom.Point.prototype.centroid = function() {
- return this.getCentroid();
-};
diff --git a/src/api/layer/osm.js b/src/api/layer/osm.js
deleted file mode 100644
index 7d8d31b95d..0000000000
--- a/src/api/layer/osm.js
+++ /dev/null
@@ -1,11 +0,0 @@
-goog.provide('ol.layer.osm');
-
-goog.require('ol.layer.OSM');
-
-/**
- * @export
- * @return {ol.layer.OSM}
- */
-ol.layer.osm = function() {
- return new ol.layer.OSM();
-};
diff --git a/src/api/layer/wms.js b/src/api/layer/wms.js
deleted file mode 100644
index dc4cfe22a4..0000000000
--- a/src/api/layer/wms.js
+++ /dev/null
@@ -1,41 +0,0 @@
-goog.provide('ol.layer.wms');
-
-goog.require('ol.layer.WMS');
-
-/**
- * @export
- * @param {Object} opt_arg Config object.
- * @return {ol.layer.WMS}
- */
-ol.layer.wms = function(opt_arg) {
- if (opt_arg instanceof ol.layer.WMS) {
- return opt_arg;
- }
-
- /** @type {string} */
- var url;
- /** @type {Array.} */
- var layers;
- /** @type {string} */
- var format;
-
- if (goog.isObject(opt_arg)) {
- ol.base.checkKeys(opt_arg, ['url', 'layers', 'format']);
- url = ol.API ? opt_arg['url'] : opt_arg.url;
- layers = ol.API ? opt_arg['layers'] : opt_arg.layers;
- format = ol.API ? opt_arg['format'] : opt_arg.format;
- }
-
- var msg;
- if (!goog.isDef(url)) {
- msg = 'Cannot create WMS layer; option "url" is missing';
- ol.error(msg);
- }
- if (!goog.isArray(layers)) {
- msg = 'Cannot create WMS layer; option "layers" is missing, ' +
- 'or is not an array';
- ol.error(msg);
- }
-
- return new ol.layer.WMS(url, layers, format);
-};
diff --git a/src/api/layer/xyz.js b/src/api/layer/xyz.js
deleted file mode 100644
index ce69d2dc6d..0000000000
--- a/src/api/layer/xyz.js
+++ /dev/null
@@ -1,31 +0,0 @@
-goog.provide('ol.layer.xyz');
-
-goog.require('ol.layer.XYZ');
-
-/**
- * @export
- * @param {Object} opt_arg Config object.
- * @return {ol.layer.XYZ}
- */
-ol.layer.xyz = function(opt_arg) {
- if (opt_arg instanceof ol.layer.XYZ) {
- return opt_arg;
- }
-
- /** @type {string} */
- var url;
-
- var usage = 'ol.layer.xyz accepts an object with a "url" property';
-
- if (goog.isObject(opt_arg)) {
- url = ol.API ? opt_arg['url'] : opt_arg.url;
- } else {
- throw new Error(usage);
- }
-
- if (!goog.isDef(url)) {
- throw new Error(usage);
- }
-
- return new ol.layer.XYZ(url);
-};
diff --git a/src/api/loc.js b/src/api/loc.js
deleted file mode 100644
index d0cc3888e0..0000000000
--- a/src/api/loc.js
+++ /dev/null
@@ -1,146 +0,0 @@
-goog.provide('ol.loc');
-
-goog.require('ol.Loc');
-goog.require('ol.projection');
-
-
-/**
- * @typedef {ol.Loc|Array.|Object} loc Location.
- */
-ol.LocLike;
-
-
-
-/**
- * @export
- * @param {ol.LocLike} opt_arg Location.
- * @return {ol.Loc} Location.
- */
-ol.loc = function(opt_arg){
-
- if (opt_arg instanceof ol.Loc) {
- return opt_arg;
- }
-
- /** @type {number|undefined} */
- var x;
-
- /** @type {number|undefined} */
- var y;
-
- /** @type {number|undefined} */
- var z;
-
- /** @type {Object|undefined} */
- var projection;
-
- var usage = 'ol.loc accepts a coordinate array or an object with x, y, and (optional) z properties';
-
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- if (goog.isArray(opt_arg)) {
- x = opt_arg[0];
- y = opt_arg[1];
- z = opt_arg[2];
- projection = opt_arg[3];
- } else if (goog.isObject(opt_arg)) {
- ol.base.checkKeys(opt_arg, ['projection', 'x', 'y', 'z']);
- x = ol.API ? opt_arg['x'] : opt_arg.x;
- y = ol.API ? opt_arg['y'] : opt_arg.y;
- z = ol.API ? opt_arg['z'] : opt_arg.z;
- projection = ol.API ? opt_arg['projection'] : opt_arg.projection;
- } else {
- throw new Error(usage);
- }
- }
-
- if (!goog.isNumber(x) || !goog.isNumber(y)) {
- throw new Error(usage);
- }
-
- if (goog.isDef(projection)) {
- projection = ol.projection(projection);
- }
-
- var loc = new ol.Loc(x, y, z, projection);
- return loc;
-
-};
-
-/**
- * Transform this location to another coordinate reference system. This
- * requires that this location has a projection set already (if not, an error
- * will be thrown). Returns a new location object and does not modify this
- * location.
- *
- * @export
- * @param {string|ol.Projection} proj The destination projection. Can be
- * supplied as a projection instance of a string identifier.
- * @returns {ol.Loc} A new location.
- */
-ol.Loc.prototype.transform = function(proj) {
- if (goog.isString(proj)) {
- proj = new ol.Projection(proj);
- }
- return this.doTransform(proj);
-};
-
-/**
- * @export
- * @param {ol.Projection=} opt_arg Projection.
- * @return {ol.Loc|ol.Projection|undefined} Result.
- */
-ol.Loc.prototype.projection = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- return this.setProjection(ol.projection(opt_arg));
- }
- else {
- return this.getProjection();
- }
-};
-
-
-/**
- * @export
- * @param {number=} opt_arg X.
- * @return {ol.Loc|number} Result.
- */
-ol.Loc.prototype.x = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setX(opt_arg);
- return this;
- }
- else {
- return this.getX();
- }
-};
-
-
-/**
- * @export
- * @param {number=} opt_arg Y.
- * @return {ol.Loc|number} Result.
- */
-ol.Loc.prototype.y = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setY(opt_arg);
- return this;
- }
- else {
- return this.getY();
- }
-};
-
-
-/**
- * @export
- * @param {number=} opt_arg Z.
- * @return {ol.Loc|number|undefined} Result.
- */
-ol.Loc.prototype.z = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- return this.setZ(opt_arg);
- }
- else {
- return this.getZ();
- }
-};
diff --git a/src/api/map.js b/src/api/map.js
deleted file mode 100644
index 3c2e1b50bd..0000000000
--- a/src/api/map.js
+++ /dev/null
@@ -1,256 +0,0 @@
-goog.provide('ol.map');
-
-goog.require('ol.Loc');
-goog.require('ol.Map');
-goog.require('ol.Projection');
-goog.require('ol.loc');
-goog.require('ol.projection');
-goog.require('ol.error');
-
-goog.require('goog.dispose');
-
-/**
- * @typedef {ol.Map|{center, zoom, numZoomLevels, projection, userProjection, maxExtent, maxResolution, resolutions, renderTo, layers, controls}|string}
- */
-ol.MapLike;
-
-
-/**
- * @export
- * @param {ol.MapLike=} opt_arg Argument.
- * @return {ol.Map} Map.
- */
-ol.map = function(opt_arg) {
-
- /** @type {ol.Loc|undefined} */
- var center;
- /** @type {number|undefined} */
- var zoom;
- /** @type {number|undefined} */
- var numZoomLevels;
- /** @type {ol.Projection|undefined} */
- var projection;
- /** @type {ol.Projection|undefined} */
- var userProjection;
- /** @type {ol.Bounds|undefined} */
- var maxExtent;
- /** @type {number|undefined} */
- var maxResolution;
- /** @type {Array.|undefined} */
- var resolutions;
- /** @type {Element|string|undefined} */
- var renderTo;
- /** @type {Array|undefined} */
- var layers;
- /** @type {Array|undefined} */
- var controls;
-
- if (arguments.length == 1) {
- if (opt_arg instanceof ol.Map) {
- return opt_arg;
- }
- else if (goog.isObject(opt_arg)) {
- ol.base.checkKeys(opt_arg, ['center', 'zoom', 'numZoomLevels', 'projection', 'userProjection', 'maxExtent', 'maxResolution', 'resolutions', 'renderTo', 'layers', 'controls']);
- center = ol.API ? opt_arg['center'] : opt_arg.center;
- zoom = ol.API ? opt_arg['zoom'] : opt_arg.zoom;
- numZoomLevels = ol.API ? opt_arg['numZoomLevels'] : opt_arg.numZoomLevels;
- projection = ol.API ? opt_arg['projection'] : opt_arg.projection;
- userProjection = ol.API ? opt_arg['userProjection'] : opt_arg.userProjection;
- maxExtent = ol.API ? opt_arg['maxExtent'] : opt_arg.maxExtent;
- maxResolution = ol.API ? opt_arg['maxResolution'] : opt_arg.maxResolution;
- resolutions = ol.API ? opt_arg['resolutions'] : opt_arg.resolutions;
- renderTo = ol.API ? opt_arg['renderTo'] : opt_arg.renderTo;
- layers = ol.API ? opt_arg['layers'] : opt_arg.layers;
- controls = ol.API ? opt_arg['controls'] : opt_arg.controls;
- }
- else {
- throw new Error('ol.map');
- }
- }
-
-
- var map = new ol.Map();
- if (goog.isDef(center)) {
- map.center(center);
- }
- if (goog.isDef(zoom)) {
- map.setZoom(zoom);
- }
- if (goog.isDef(numZoomLevels)) {
- map.setNumZoomLevels(numZoomLevels);
- }
- if (goog.isDef(projection)) {
- map.setProjection(ol.projection(projection));
- }
- if (goog.isDef(userProjection)) {
- map.setUserProjection(ol.projection(userProjection));
- }
- if (goog.isDef(maxExtent)) {
- map.setMaxExtent(ol.bounds(maxExtent));
- }
- if (goog.isDef(maxResolution)) {
- map.setMaxResolution(maxResolution);
- }
- if (goog.isDef(resolutions)) {
- map.setResolutions(resolutions);
- }
- if (goog.isDef(layers)) {
- map.setLayers(layers);
- }
- if (goog.isDef(controls)) {
- map.setControls(controls);
- }
- if (goog.isDef(renderTo)) {
- map.renderTo(renderTo);
- }
- return map;
-
-};
-
-/**
- * @export
- * @param {ol.LocLike=} opt_arg
- * @returns {ol.Map|ol.Loc|undefined} Map center.
- */
-ol.Map.prototype.center = function(opt_arg) {
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- var loc = ol.loc(opt_arg);
- var proj = loc.getProjection();
- if (goog.isNull(proj)) {
- proj = this.getUserProjection();
- loc.setProjection(proj);
- }
- this.setCenter(loc);
- return this;
- } else {
- var proj = this.getUserProjection();
- return this.getCenter().doTransform(proj);
- }
-};
-
-/**
- * @export
- * @param {ol.ProjectionLike=} opt_arg
- * @returns {ol.Map|ol.Projection|undefined}
- */
-ol.Map.prototype.projection = function(opt_arg) {
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setProjection(ol.projection(opt_arg));
- return this;
- } else {
- return this.getProjection();
- }
-};
-
-/**
- * @export
- * @param {ol.ProjectionLike=} opt_arg
- * @returns {ol.Map|ol.Projection|undefined}
- */
-ol.Map.prototype.userProjection = function(opt_arg) {
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setUserProjection(ol.projection(opt_arg));
- return this;
- } else {
- return this.getUserProjection();
- }
-};
-
-/**
- * @export
- * @param {number=} opt_arg
- * @returns {ol.Map|number|undefined} Map center.
- */
-ol.Map.prototype.zoom = function(opt_arg) {
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setZoom(opt_arg);
- return this;
- } else {
- return this.getZoom();
- }
-};
-
-/**
- * @export
- * @param {number=} opt_arg
- * @returns {ol.Map|number|undefined} Map center.
- */
-ol.Map.prototype.numZoomLevels = function(opt_arg) {
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setNumZoomLevels(opt_arg);
- return this;
- } else {
- return this.getNumZoomLevels();
- }
-};
-
-/**
- * @export
- * @param {Array=} opt_arg
- * @returns {ol.Map|Array|undefined} Map center.
- */
-ol.Map.prototype.resolutions = function(opt_arg) {
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setResolutions(opt_arg);
- return this;
- } else {
- return this.getResolutions();
- }
-};
-
-/**
- * @export
- * @param {Array=} opt_arg
- * @returns {ol.Map|Array|undefined} Map center.
- */
-ol.Map.prototype.layers = function(opt_arg) {
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setLayers(opt_arg);
- return this;
- } else {
- return this.getLayers();
- }
-};
-
-/**
- * @export
- * @param {Array=} opt_arg
- * @returns {ol.Map|Array|undefined} Map center.
- */
-ol.Map.prototype.controls = function(opt_arg) {
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setControls(opt_arg);
- return this;
- } else {
- return this.getControls();
- }
-};
-
-/**
- * @export
- * @param {Array=} opt_arg
- * @returns {ol.Map|ol.Bounds|undefined} Map max extent.
- */
-ol.Map.prototype.maxExtent = function(opt_arg) {
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setMaxExtent(ol.bounds(opt_arg));
- return this;
- } else {
- return this.getMaxExtent();
- }
-};
-
-/**
- * @param {string|Element} arg Render the map to a container
- * @returns {ol.Map}
- */
-ol.Map.prototype.renderTo = function(arg) {
- this.setContainer(goog.dom.getElement(arg));
- return this;
-};
-
-/**
- */
-ol.Map.prototype.destroy = function() {
- goog.dispose(this);
-};
diff --git a/src/api/popup.js b/src/api/popup.js
deleted file mode 100644
index dd51db9a73..0000000000
--- a/src/api/popup.js
+++ /dev/null
@@ -1,139 +0,0 @@
-goog.provide('ol.popup');
-
-goog.require('ol.Popup');
-goog.require('ol.map');
-
-
-/**
- * @typedef {ol.Popup|{map, anchor, placement, content, template}} popup
- */
-ol.PopupLike;
-
-
-
-/**
- * @export
- * @param {ol.PopupLike} opt_arg popup object literal.
- * @return {ol.Popup} the popup.
- */
-ol.popup = function(opt_arg){
-
- if (opt_arg instanceof ol.Popup) {
- return opt_arg;
- }
-
- /** @type {ol.Map} */
- var map;
-
- /** @type {ol.Loc|ol.Feature|undefined} */
- var anchor;
-
- /** @type {string|undefined} */
- var placement;
-
- /** @type {string|undefined} */
- var content;
-
- /** @type {string|undefined} */
- var template;
-
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- if (goog.isObject(opt_arg)) {
- ol.base.checkKeys(opt_arg, ['map', 'anchor', 'placement', 'content', 'template']);
- map = ol.API ? opt_arg['map'] : opt_arg.map;
- anchor = ol.API ? opt_arg['anchor'] : opt_arg.anchor;
- placement = ol.API ? opt_arg['placement'] : opt_arg.placement;
- content = ol.API ? opt_arg['content'] : opt_arg.content;
- template = ol.API ? opt_arg['template'] : opt_arg.template;
- }
- }
-
- var popup = new ol.Popup(map, anchor);
-
- if (goog.isDef(anchor)) {
- popup.setAnchor(anchor);
- }
- if (goog.isDef(placement)) {
- popup.setPlacement(placement);
- }
- if (goog.isDef(content)) {
- popup.setContent(content);
- }
- if (goog.isDef(template)) {
- popup.setTemplate(template);
- }
-
- return popup;
-
-};
-
-
-/**
- * @export
- * @param {ol.Loc|ol.Feature=} opt_arg a feature or a location.
- * @return {ol.Popup|ol.Feature|ol.Loc|undefined} Result.
- */
-ol.Popup.prototype.anchor = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setAnchor(opt_arg);
- return this;
- }
- else {
- return this.getAnchor();
- }
-};
-
-
-/**
- * @export
- * @param {ol.Map=} opt_arg the map .
- * @return {ol.Popup|ol.Map|undefined} the map or the popup.
- */
-ol.Popup.prototype.map = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setMap(opt_arg);
- return this;
- }
- else {
- return this.getMap();
- }
-};
-
-/**
- * @export
- * @param {string=} opt_arg the content for the map (HTML makrkup)
- * @return {ol.Popup|string|undefined} the content or the popup.
- */
-ol.Popup.prototype.content = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setContent(opt_arg);
- return this;
- }
- else {
- return this.getContent();
- }
-};
-
-/**
- * @export
- * @param {string=} opt_arg the template to be used to generate the content
- * @return {ol.Popup|string|undefined} the template or the popup.
- */
-ol.Popup.prototype.template = function(opt_arg){
- if (arguments.length == 1 && goog.isDef(opt_arg)) {
- this.setTemplate(opt_arg);
- return this;
- }
- else {
- return this.getTemplate();
- }
-};
-
-/**
- * Open the popup.
- * @export
- * @param {ol.Feature|ol.Loc} opt_arg feature or location for the anchor
- */
-ol.Popup.prototype.open = function(opt_arg) {
- this.doOpen(opt_arg);
-};
diff --git a/src/api/projection.js b/src/api/projection.js
deleted file mode 100644
index 8e067a1efd..0000000000
--- a/src/api/projection.js
+++ /dev/null
@@ -1,88 +0,0 @@
-goog.provide('ol.projection');
-
-goog.require('ol.base');
-goog.require('ol.Projection');
-
-
-/**
- * @typedef {ol.Projection|Object|string}
- */
-ol.ProjectionLike;
-
-
-/**
- * @export
- * @param {ol.ProjectionLike=} opt_arg Argument.
- * @return {ol.Projection} Projection.
- */
-ol.projection = function(opt_arg){
- /** @type {string} */
- var code;
-
- /** @type {undefined|number} */
- var units;
-
- /** @type {undefined|Array|ol.UnreferencedBounds} */
- var extent;
-
- if (arguments.length == 1 && goog.isDefAndNotNull(opt_arg)) {
- if (opt_arg instanceof ol.Projection) {
- return opt_arg;
- }
- else if (goog.isString(opt_arg)) {
- code = opt_arg;
- }
- else if (goog.isObject(opt_arg)) {
- ol.base.checkKeys(opt_arg, ['code', 'maxExtent', 'units']);
- if (goog.isString(ol.API ? opt_arg['code'] : opt_arg.code)) {
- code = ol.API ? opt_arg['code'] : opt_arg.code;
- } else {
- throw new Error('Projection requires a string code.');
- }
- units = ol.API ? opt_arg['units'] : opt_arg.units;
- extent = ol.API ? opt_arg['maxExtent'] : opt_arg.maxExtent;
- }
- else {
- throw new Error('ol.projection');
- }
- }
- var proj = new ol.Projection(code);
- if (goog.isDef(units)) {
- proj.setUnits(units);
- }
- if (goog.isDef(extent)) {
- proj.setExtent(
- new ol.UnreferencedBounds(extent[0],extent[1],extent[2],extent[3])
- );
- }
- return proj;
-};
-
-/**
- * @export
- * @param {string=} opt_code Code.
- * @return {!ol.Projection|string} Result.
- */
-ol.Projection.prototype.code = function(opt_code){
- if (arguments.length == 1 && goog.isDef(opt_code)) {
- this.setCode(opt_code);
- return this;
- }
- else {
- return this.getCode();
- }
-};
-
-/**
- * @export
- * @param {string=} opt_units Units abbreviation.
- * @return {undefined|!ol.Projection|string} Result.
- */
-ol.Projection.prototype.units = function(opt_units){
- if (goog.isDef(opt_units)) {
- return this.setUnits(opt_units);
- }
- else {
- return this.getUnits();
- }
-};
diff --git a/src/ol.export.js b/src/ol.export.js
deleted file mode 100644
index f243f1386d..0000000000
--- a/src/ol.export.js
+++ /dev/null
@@ -1,132 +0,0 @@
-goog.require("ol");
-
-// ol.map
-goog.exportSymbol('ol.map', ol.map );
-goog.exportSymbol('ol.Map', ol.Map );
-goog.exportProperty( ol.Map.prototype, 'renderTo', ol.Map.prototype.renderTo );
-goog.exportProperty( ol.Map.prototype, 'center', ol.Map.prototype.center );
-goog.exportProperty( ol.Map.prototype, 'projection', ol.Map.prototype.projection );
-goog.exportProperty( ol.Map.prototype, 'userProjection', ol.Map.prototype.userProjection );
-goog.exportProperty( ol.Map.prototype, 'zoom', ol.Map.prototype.zoom );
-goog.exportProperty( ol.Map.prototype, 'numZoomLevels', ol.Map.prototype.numZoomLevels );
-goog.exportProperty( ol.Map.prototype, 'resolutions', ol.Map.prototype.resolutions );
-goog.exportProperty( ol.Map.prototype, 'layers', ol.Map.prototype.layers );
-goog.exportProperty( ol.Map.prototype, 'controls', ol.Map.prototype.controls );
-goog.exportProperty( ol.Map.prototype, 'maxExtent', ol.Map.prototype.maxExtent );
-goog.exportProperty( ol.Map.prototype, 'destroy', ol.Map.prototype.destroy );
-
-// ol.loc
-goog.exportSymbol('ol.loc', ol.loc );
-goog.exportSymbol('ol.Loc', ol.Loc ); // This is not required only for the tests with isInstanceOf
-goog.exportProperty( ol.Loc.prototype, 'projection', ol.Loc.prototype.projection );
-goog.exportProperty( ol.Loc.prototype, 'x', ol.Loc.prototype.x );
-goog.exportProperty( ol.Loc.prototype, 'y', ol.Loc.prototype.y );
-goog.exportProperty( ol.Loc.prototype, 'z', ol.Loc.prototype.z );
-goog.exportProperty( ol.Loc.prototype, 'transform', ol.Loc.prototype.transform );
-goog.exportProperty( ol.Loc.prototype, 'destroy', ol.Loc.prototype.destroy );
-
-// ol.projection
-goog.exportSymbol('ol.projection', ol.projection );
-goog.exportSymbol('ol.Projection', ol.Projection );
-goog.exportProperty( ol.Projection.prototype, 'code', ol.Projection.prototype.code );
-goog.exportProperty( ol.Projection.prototype, 'units', ol.Projection.prototype.units );
-
-// ol.bounds
-goog.exportSymbol('ol.bounds', ol.bounds );
-goog.exportSymbol('ol.Bounds', ol.Bounds );
-goog.exportProperty( ol.Bounds.prototype, 'projection', ol.Bounds.prototype.projection );
-goog.exportProperty( ol.Bounds.prototype, 'minX', ol.Bounds.prototype.minX );
-goog.exportProperty( ol.Bounds.prototype, 'minY', ol.Bounds.prototype.minY );
-goog.exportProperty( ol.Bounds.prototype, 'maxX', ol.Bounds.prototype.maxX );
-goog.exportProperty( ol.Bounds.prototype, 'maxY', ol.Bounds.prototype.maxY );
-
-// ol.layer.xyz
-goog.exportSymbol('ol.layer.xyz', ol.layer.xyz);
-goog.exportSymbol('ol.layer.XYZ', ol.layer.XYZ);
-
-// ol.layer.osm
-goog.exportSymbol('ol.layer.osm', ol.layer.osm);
-goog.exportSymbol('ol.layer.OSM', ol.layer.OSM);
-
-// ol.layer.wms
-goog.exportSymbol('ol.layer.wms', ol.layer.wms);
-goog.exportSymbol('ol.layer.WMS', ol.layer.WMS);
-
-// ol.feature
-goog.exportSymbol('ol.feature', ol.feature);
-goog.exportSymbol('ol.Feature', ol.Feature);
-goog.exportProperty(ol.Feature.prototype, 'set', ol.Feature.prototype.set);
-goog.exportProperty(ol.Feature.prototype, 'get', ol.Feature.prototype.get);
-goog.exportProperty(ol.Feature.prototype, 'geometry', ol.Feature.prototype.geometry);
-
-// ol.geometry
-goog.exportSymbol('ol.geom.geometry', ol.geom.geometry);
-goog.exportSymbol('ol.geom.Geometry', ol.geom.Geometry);
-goog.exportProperty(ol.geom.Geometry.prototype, 'bounds', ol.geom.Geometry.prototype.bounds);
-goog.exportProperty(ol.geom.Geometry.prototype, 'centroid', ol.geom.Geometry.prototype.centroid);
-
-// ol.geom.collection
-goog.exportSymbol('ol.geom.collection', ol.geom.collection);
-goog.exportSymbol('ol.geom.Collection', ol.geom.Collection);
-goog.exportProperty(ol.geom.Collection.prototype, 'components', ol.geom.Collection.prototype.components);
-goog.exportProperty(ol.geom.Collection.prototype, 'add', ol.geom.Collection.prototype.add);
-goog.exportProperty(ol.geom.Collection.prototype, 'addAll', ol.geom.Collection.prototype.addAll);
-goog.exportProperty(ol.geom.Collection.prototype, 'remove', ol.geom.Collection.prototype.remove);
-goog.exportProperty(ol.geom.Collection.prototype, 'centroid', ol.geom.Collection.prototype.centroid);
-
-// ol.geom.point
-goog.exportSymbol('ol.geom.point', ol.geom.point);
-goog.exportSymbol('ol.geom.Point', ol.geom.Point);
-goog.exportProperty(ol.geom.Point.prototype, 'x', ol.geom.Point.prototype.x);
-goog.exportProperty(ol.geom.Point.prototype, 'y', ol.geom.Point.prototype.y);
-goog.exportProperty(ol.geom.Point.prototype, 'z', ol.geom.Point.prototype.z);
-goog.exportProperty(ol.geom.Point.prototype, 'projection', ol.geom.Point.prototype.projection);
-goog.exportProperty(ol.geom.Point.prototype, 'centroid', ol.geom.Point.prototype.centroid);
-
-// ol.geom.linestring
-goog.exportSymbol('ol.geom.linestring', ol.geom.linestring);
-goog.exportSymbol('ol.geom.LineString', ol.geom.LineString);
-goog.exportProperty(ol.geom.LineString.prototype, 'vertices', ol.geom.LineString.prototype.vertices);
-goog.exportProperty(ol.geom.LineString.prototype, 'add', ol.geom.LineString.prototype.add);
-goog.exportProperty(ol.geom.LineString.prototype, 'addAll', ol.geom.LineString.prototype.addAll);
-goog.exportProperty(ol.geom.LineString.prototype, 'remove', ol.geom.LineString.prototype.remove);
-goog.exportProperty(ol.geom.LineString.prototype, 'centroid', ol.geom.LineString.prototype.centroid);
-
-// ol.geom.multipoint
-goog.exportSymbol('ol.geom.multipoint', ol.geom.multipoint);
-goog.exportSymbol('ol.geom.MultiPoint', ol.geom.MultiPoint);
-goog.exportProperty(ol.geom.MultiPoint.prototype, 'points', ol.geom.MultiPoint.prototype.points);
-goog.exportProperty(ol.geom.MultiPoint.prototype, 'add', ol.geom.MultiPoint.prototype.add);
-goog.exportProperty(ol.geom.MultiPoint.prototype, 'addAll', ol.geom.MultiPoint.prototype.addAll);
-goog.exportProperty(ol.geom.MultiPoint.prototype, 'remove', ol.geom.MultiPoint.prototype.remove);
-goog.exportProperty(ol.geom.MultiPoint.prototype, 'centroid', ol.geom.MultiPoint.prototype.centroid);
-
-// ol.geom.multilinestring
-goog.exportSymbol('ol.geom.multilinestring', ol.geom.multilinestring);
-goog.exportSymbol('ol.geom.MultiLineString', ol.geom.MultiLineString);
-goog.exportProperty(ol.geom.MultiLineString.prototype, 'linestrings', ol.geom.MultiLineString.prototype.linestrings);
-goog.exportProperty(ol.geom.MultiLineString.prototype, 'add', ol.geom.MultiLineString.prototype.add);
-goog.exportProperty(ol.geom.MultiLineString.prototype, 'addAll', ol.geom.MultiLineString.prototype.addAll);
-goog.exportProperty(ol.geom.MultiLineString.prototype, 'remove', ol.geom.MultiLineString.prototype.remove);
-goog.exportProperty(ol.geom.MultiLineString.prototype, 'centroid', ol.geom.MultiLineString.prototype.centroid);
-
-
-// ol.popup
-goog.exportSymbol('ol.popup', ol.popup);
-goog.exportSymbol('ol.Popup', ol.Popup);
-goog.exportProperty(ol.Popup.prototype, 'anchor', ol.Popup.prototype.anchor);
-goog.exportProperty(ol.Popup.prototype, 'map', ol.Popup.prototype.map);
-goog.exportProperty(ol.Popup.prototype, 'content', ol.Popup.prototype.content);
-goog.exportProperty(ol.Popup.prototype, 'template', ol.Popup.prototype.template);
-goog.exportProperty(ol.Popup.prototype, 'open', ol.Popup.prototype.open);
-
-/**
- * Lookup for dynamically registered controls and renderers does not work in
- * advanced compiled mode. For now, we duplicate the registration calls here.
- *
- * TODO: determine a good way for plugins to register
- */
-goog.exportProperty(ol.renderer.Composite, 'isSupported', ol.renderer.Composite.isSupported);
-goog.exportProperty(ol.renderer.TileLayerRenderer, 'isSupported', ol.renderer.TileLayerRenderer.isSupported);
-goog.exportProperty(ol.renderer.TileLayerRenderer, 'canRender', ol.renderer.TileLayerRenderer.canRender);
-goog.exportProperty(ol.renderer.TileLayerRenderer, 'getType', ol.renderer.TileLayerRenderer.getType);
diff --git a/src/ol.js b/src/ol.js
deleted file mode 100644
index 8c936f0329..0000000000
--- a/src/ol.js
+++ /dev/null
@@ -1,32 +0,0 @@
-goog.provide("ol");
-
-goog.require('ol.base');
-goog.require('ol.bounds');
-goog.require('ol.control.Attribution');
-goog.require('ol.control.Zoom');
-goog.require('ol.handler.Drag');
-goog.require('ol.handler.MouseWheel');
-goog.require('ol.handler.Click');
-goog.require("ol.map");
-goog.require("ol.loc");
-goog.require("ol.feature");
-goog.require("ol.projection");
-goog.require("ol.layer.xyz");
-goog.require("ol.layer.osm");
-goog.require("ol.layer.wms");
-goog.require("ol.popup");
-goog.require("ol.Tile");
-goog.require("ol.TileSet");
-goog.require("ol.TileCache");
-goog.require("ol.geom.geometry");
-goog.require("ol.geom.point");
-goog.require("ol.geom.multipoint");
-goog.require("ol.geom.linestring");
-goog.require("ol.geom.multilinestring");
-goog.require("ol.geom.collection");
-goog.require('ol.layer.XYZ');
-goog.require('ol.layer.OSM');
-goog.require('ol.layer.WMS');
-goog.require('ol.renderer.Composite');
-goog.require('ol.renderer.TileLayerRenderer');
-goog.require('ol.renderer.WebGL');
diff --git a/src/ol/Bounds.js b/src/ol/Bounds.js
deleted file mode 100644
index d0676d7c65..0000000000
--- a/src/ol/Bounds.js
+++ /dev/null
@@ -1,105 +0,0 @@
-goog.provide('ol.Bounds');
-
-goog.require('ol.UnreferencedBounds');
-goog.require('ol.Loc');
-goog.require('ol.Projection');
-
-goog.require('goog.string.format');
-
-/**
- * @export
- * @constructor
- * @param {number} minX Minimum X.
- * @param {number} minY Minimum Y.
- * @param {number} maxX Maximum X.
- * @param {number} maxY Maximum Y.
- * @param {ol.Projection=} opt_projection Projection.
- * @extends {ol.UnreferencedBounds}
- */
-ol.Bounds = function(minX, minY, maxX, maxY, opt_projection) {
-
- goog.base(this, minX, minY, maxX, maxY);
-
- /**
- * @protected
- * @type {ol.Projection}
- */
- this.projection_ = goog.isDef(opt_projection) ? opt_projection : null;
-
-};
-goog.inherits(ol.Bounds, ol.UnreferencedBounds);
-
-/**
- * @return {ol.Projection} Projection.
- */
-ol.Bounds.prototype.getProjection = function() {
- return this.projection_;
-};
-
-/**
- * @param {ol.Projection} projection Projection.
- */
-ol.Bounds.prototype.setProjection = function(projection) {
- this.projection_ = projection;
-};
-
-/**
- * Determine if this bounds intersects the target bounds (bounds that only
- * touch are considered intersecting).
- *
- * @param {ol.Bounds} bounds Target bounds.
- * @return {boolean} The provided bounds intersects this bounds.
- */
-ol.Bounds.prototype.intersects = function(bounds) {
- var otherProj = bounds.getProjection();
- if (!goog.isNull(otherProj) && !goog.isNull(this.projection_)) {
- bounds = bounds.doTransform(this.projection_);
- }
- return goog.base(this, "intersects", bounds.toUnreferencedBounds());
-};
-
-/**
- * Transform this node into another coordinate reference system. Returns a new
- * bounds instead of modifying this bounds.
- *
- * @param {ol.Projection} proj Target projection.
- * @return {ol.Bounds} A new bounds in the target projection.
- */
-ol.Bounds.prototype.doTransform = function(proj) {
- if (goog.isNull(this.projection_)) {
- throw new Error("Bounds must have a projection before transforming.");
- }
- var tl = new ol.Loc(
- this.minX_, this.maxY_, undefined, this.projection_).doTransform(proj);
- var tr = new ol.Loc(
- this.maxX_, this.maxY_, undefined, this.projection_).doTransform(proj);
- var bl = new ol.Loc(
- this.minX_, this.minY_, undefined, this.projection_).doTransform(proj);
- var br = new ol.Loc(
- this.maxX_, this.minY_, undefined, this.projection_).doTransform(proj);
-
- var x = [tl.getX(), tr.getX(), bl.getX(), br.getX()].sort();
- var y = [tl.getY(), tr.getY(), bl.getY(), br.getY()].sort();
-
- return new ol.Bounds(x[0], y[0], x[3], y[3], proj);
-};
-
-/**
- * Return a bbox string for this bounds.
- *
- * @return {string} The "minx,miny,maxx,maxy" representation of this bounds.
- */
-ol.Bounds.prototype.toBBOX = function() {
- return goog.string.format(
- '%f,%f,%f,%f', this.minX_, this.minY_, this.maxX_, this.maxY_);
-};
-
-/**
- * Cast this bounds into an unreferenced bounds.
- *
- * @returns {ol.UnreferencedBounds}
- */
-ol.Bounds.prototype.toUnreferencedBounds = function() {
- return new ol.UnreferencedBounds(
- this.getMinX(), this.getMinY(), this.getMaxX(), this.getMaxY());
-};
diff --git a/src/ol/Feature.js b/src/ol/Feature.js
deleted file mode 100644
index 7fb7d4fab4..0000000000
--- a/src/ol/Feature.js
+++ /dev/null
@@ -1,77 +0,0 @@
-goog.provide('ol.Feature');
-
-goog.require('ol.geom.Geometry');
-
-
-
-/**
- * @export
- * @constructor
- */
-ol.Feature = function() {
-
- /**
- * @private
- * @type {ol.geom.Geometry}
- */
- this.geometry_ = null;
-
- /**
- * @private
- * @type {Object}
- */
- this.attributes_ = {};
-
-};
-
-/**
- * @return {ol.geom.Geometry} The geometry associated with the feature.
- */
-ol.Feature.prototype.getGeometry = function() {
- return this.geometry_;
-};
-
-/**
- * @param {ol.geom.Geometry} geom the geometry for the feature.
- */
-ol.Feature.prototype.setGeometry = function(geom) {
- this.geometry_ = geom;
-};
-
-
-/**
- * @param {!string} name the attribute value to retrieve.
- @return {string|number|boolean} the attribute value.
- */
-ol.Feature.prototype.getAttribute = function(name) {
- return this.attributes_[name];
-};
-
-
-/**
- * @param {!string} name of the attribute to set.
- * @param {string|number|boolean} value the attribute value to set.
- */
-ol.Feature.prototype.setAttribute = function(name, value) {
- this.attributes_[name] = value;
-};
-
-
-/**
- * @param {Object} attrs An json structure containing key/value pairs.
- */
-ol.Feature.prototype.setAttributes = function(attrs) {
- for (var key in attrs) {
- this.setAttribute(key, attrs[key]);
- }
-};
-
-
-/**
-*/
-ol.Feature.prototype.destroy = function() {
- //remove attributes and geometry, etc.
- for (var key in this) {
- delete this[key];
- }
-};
diff --git a/src/ol/Loc.js b/src/ol/Loc.js
deleted file mode 100644
index 035ffa933a..0000000000
--- a/src/ol/Loc.js
+++ /dev/null
@@ -1,147 +0,0 @@
-goog.provide('ol.Loc');
-
-goog.require('ol.Projection');
-
-
-
-/**
- * @export
- * @constructor
- * @param {number} x X.
- * @param {number} y Y.
- * @param {number=} opt_z Z.
- * @param {ol.Projection=} opt_projection Projection.
- */
-ol.Loc = function(x, y, opt_z, opt_projection) {
-
- /**
- * @private
- * @type {number}
- */
- this.x_ = x;
-
- /**
- * @private
- * @type {number}
- */
- this.y_ = y;
-
- /**
- * @private
- * @type {number|undefined}
- */
- this.z_ = opt_z;
-
- /**
- * @private
- * @type {ol.Projection}
- */
- this.projection_ = goog.isDef(opt_projection) ? opt_projection : null;
-
-};
-
-
-/**
- * @return {ol.Projection|undefined} Projection.
- */
-ol.Loc.prototype.getProjection = function() {
- return this.projection_;
-};
-
-
-/**
- * @return {number} X.
- */
-ol.Loc.prototype.getX = function() {
- return this.x_;
-};
-
-
-/**
- * @return {number} Y.
- */
-ol.Loc.prototype.getY = function() {
- return this.y_;
-};
-
-
-/**
- * @return {number|undefined} Z.
- */
-ol.Loc.prototype.getZ = function() {
- return this.z_;
-};
-
-
-/**
- * @param {ol.Projection} projection Projection.
- */
-ol.Loc.prototype.setProjection = function(projection) {
- this.projection_ = projection;
-};
-
-
-/**
- * @param {number} x X.
- */
-ol.Loc.prototype.setX = function(x) {
- this.x_ = x;
-};
-
-
-/**
- * @param {number} y Y.
- */
-ol.Loc.prototype.setY = function(y) {
- this.y_ = y;
-};
-
-
-/**
- * @param {number|undefined} z Z.
- */
-ol.Loc.prototype.setZ = function(z) {
- this.z_ = z;
-};
-
-/**
- * Transform this location to a new location given a projection object.
- *
- * @param {ol.Projection} proj The destination projection.
- * @returns {ol.Loc}
- */
-ol.Loc.prototype.doTransform = function(proj) {
- var point = {'x': this.x_, 'y': this.y_};
- var sourceProj = this.projection_;
- if (!goog.isDefAndNotNull(sourceProj)) {
- throw new Error("Cannot transform a location without a source projection.");
- }
- ol.Projection.transform(point, sourceProj, proj);
- return new ol.Loc(point['x'], point['y'], this.z_, proj);
-};
-
-/**
- * Adds the passed x, y(, z) delta to a new location.
- *
- * @param {number} x
- * @param {number} y
- * @param {number=} opt_z
- * @returns {ol.Loc}
- */
-ol.Loc.prototype.add = function(x, y, opt_z) {
- var newZ;
- if (goog.isDef(this.z_)) {
- newZ = (opt_z || 0) + this.z_;
- }
- return new ol.Loc(this.x_ + x, this.y_ + y, newZ, this.projection_);
-};
-
-/**
- * Clean up.
- * @export
- */
-ol.Loc.prototype.destroy = function() {
- for (var key in this) {
- delete this[key];
- }
-};
diff --git a/src/ol/Map.js b/src/ol/Map.js
deleted file mode 100644
index cadf228e04..0000000000
--- a/src/ol/Map.js
+++ /dev/null
@@ -1,625 +0,0 @@
-goog.provide('ol.Map');
-
-goog.require('ol.Loc');
-goog.require('ol.Bounds');
-goog.require('ol.Projection');
-goog.require('ol.control.Control');
-goog.require('ol.renderer.MapRenderer');
-goog.require('ol.handler.Drag');
-goog.require('ol.handler.MouseWheel');
-goog.require('ol.handler.Click');
-
-goog.require('goog.dom');
-goog.require('goog.math');
-goog.require('goog.asserts');
-goog.require('goog.events.EventTarget');
-
-/**
- * @define {boolean} Whether to enable the drag handler.
- */
-ol.ENABLE_DRAG_HANDLER = true;
-
-/**
- * @define {boolean} Whether to enable the mousewheel handler.
- */
-ol.ENABLE_MOUSEWHEEL_HANDLER = true;
-
-/**
- * @define {boolean} Whether to enable the click handler.
- */
-ol.ENABLE_CLICK_HANDLER = true;
-
-/**
- * @export
- * @constructor
- * @extends {goog.events.EventTarget}
- *
- * @event layeradd Fires when a layer is added to the map. The event object
- * contains a 'layer' property referencing the added layer.
- */
-ol.Map = function() {
-
- goog.base(this);
-
- /**
- * @private
- * @type {ol.Projection}
- */
- this.projection_ = null;
-
- /**
- * @private
- * @type {ol.Projection}
- */
- this.userProjection_ = null;
-
- /**
- * @private
- * @type {ol.Loc}
- */
- this.center_ = null;
-
- /**
- * @private
- * @type {number|undefined}
- */
- this.zoom_ = undefined;
-
- /**
- * @private
- * @type {number}
- */
- this.numZoomLevels_ = 22;
-
- /**
- * @private
- * @type {Array}
- */
- this.resolutions_ = null;
-
- /**
- * @private
- * @type {Array}
- */
- this.layers_ = null;
-
- /**
- * @private
- * @type {Array}
- */
- this.controls_ = null;
-
- /**
- * @private
- * @type {ol.Bounds}
- */
- this.maxExtent_ = null;
-
- /**
- * @private
- * @type {number|undefined}
- */
- this.maxResolution_ = undefined;
-
- /**
- * @private
- * @type {Element}
- */
- this.viewport_ = null;
-
- /**
- * @private
- * @type {goog.math.Size}
- */
- this.viewportSize_ = null;
-
- /**
- * @private
- * @type {Node}
- */
- this.mapOverlay_ = null;
-
- /**
- * @private
- * @type {Node}
- */
- this.staticOverlay_ = null;
-
- /**
- * @private
- * @type {Element}
- */
- this.container_ = null;
-};
-goog.inherits(ol.Map, goog.events.EventTarget);
-
-/**
- @const
- @type {string}
-*/
-ol.Map.prototype.DEFAULT_PROJECTION = "EPSG:3857";
-/**
- @const
- @type {string}
-*/
-ol.Map.prototype.DEFAULT_USER_PROJECTION = "EPSG:4326";
-/**
- @const
- @type {number}
-*/
-ol.Map.ZOOM_FACTOR = 2;
-/**
- @const
- @type {number}
-*/
-ol.Map.DEFAULT_TILE_SIZE = 256;
-/**
- @const
- @type {Array.}
- */
-ol.Map.DEFAULT_CONTROLS = ["attribution", "zoom"];
-
-/**
- * @return {ol.Loc} Map center in map projection.
- */
-ol.Map.prototype.getCenter = function() {
- return this.center_;
-};
-
-
-/**
- * @return {!ol.Projection} Projection.
- */
-ol.Map.prototype.getProjection = function() {
- if (goog.isNull(this.projection_)) {
- this.projection_ = new ol.Projection(this.DEFAULT_PROJECTION);
- }
- return this.projection_;
-};
-
-
-/**
- * @return {!ol.Projection} User projection.
- */
-ol.Map.prototype.getUserProjection = function() {
- if (goog.isNull(this.userProjection_)) {
- this.userProjection_ = new ol.Projection(this.DEFAULT_USER_PROJECTION);
- }
- return this.userProjection_;
-};
-
-
-/**
- * @return {number|undefined} Zoom.
- */
-ol.Map.prototype.getZoom = function() {
- return this.zoom_;
-};
-
-
-/**
- * @return {number} number of zoom levels.
- */
-ol.Map.prototype.getNumZoomLevels = function() {
- return this.numZoomLevels_;
-};
-
-
-/**
- * @return {Array|undefined} array of resolutions available for this map
- */
-ol.Map.prototype.getResolutions = function() {
- return this.resolutions_;
-};
-
-
-/**
- * @return {Array|undefined} array of layers available for this map
- */
-ol.Map.prototype.getLayers = function() {
- return this.layers_;
-};
-
-
-/**
- * @return {Array.}
- */
-ol.Map.prototype.getControls = function() {
- return this.controls_;
-};
-
-
-/**
- * @return {ol.Bounds} the maxExtent for the map
- */
-ol.Map.prototype.getMaxExtent = function() {
- if (goog.isDefAndNotNull(this.maxExtent_)) {
- return this.maxExtent_;
- } else {
- var projection = this.getProjection();
- var extent = projection.getExtent();
- if (goog.isDefAndNotNull(extent)) {
- extent = new ol.Bounds(
- extent.getMinX(), extent.getMinY(),
- extent.getMaxX(), extent.getMaxY());
- extent.setProjection(projection);
- return extent;
- } else {
- throw('maxExtent must be defined either in the map or the projection');
- }
- }
-};
-
-
-/**
- * @return {number} the max resolution for the map
- */
-ol.Map.prototype.getMaxResolution = function() {
- if (goog.isDefAndNotNull(this.maxResolution_)) {
- return this.maxResolution_;
- } else {
- var extent = this.getMaxExtent();
- var dim = Math.max(
- (extent.getMaxX()-extent.getMinX()),
- (extent.getMaxY()-extent.getMinY())
- );
- return dim/ol.Map.DEFAULT_TILE_SIZE;
- }
-};
-
-
-/**
- * @param {number} zoom the zoom level being requested
- * @return {number} the resolution for the map at the given zoom level
- */
-ol.Map.prototype.getResolutionForZoom = function(zoom) {
- if (goog.isDefAndNotNull(this.resolutions_)) {
- return this.resolutions_[zoom];
- } else {
- var maxResolution = this.getMaxResolution();
- return maxResolution/Math.pow(ol.Map.ZOOM_FACTOR, zoom);
- }
-};
-
-/**
- * @return {number} the resolution for the map at the given zoom level
- */
-ol.Map.prototype.getResolution = function() {
- goog.asserts.assert(goog.isDef(this.renderer_));
- return this.renderer_.getResolution();
-};
-
-
-/**
- * TODO: We'll have to ask the map overlay renderer for this. This method will
- * not work once map space is not aligned with pixel space.
- *
- * @param {goog.math.Coordinate|{x: number, y: number}} pixel
- * @return {ol.Loc}
- */
-ol.Map.prototype.getLocForViewportPixel = function(pixel) {
- var size = this.getViewportSize();
- var center = this.center_;
- var resolution = this.getResolution();
- var x = center.getX() + (resolution * (pixel.x - (size.width / 2)));
- var y = center.getY() - (resolution * (pixel.y - (size.height / 2)));
- return new ol.Loc(x, y, undefined, this.getProjection());
-};
-
-/**
- * TODO: We'll have to ask the map overlay renderer for this. This method will
- * not work once map space is not aligned with pixel space.
- *
- * @param {ol.Loc} loc
- * @return {{x: number, y: number}}
- */
-ol.Map.prototype.getViewportPixelForLoc = function(loc) {
- var size = this.getViewportSize();
- var center = this.center_;
- var resolution = this.getResolution();
- return {
- x: ((loc.getX() - center.getX()) / resolution) + (size.width / 2),
- y: ((center.getY() - loc.getY()) / resolution) + (size.height / 2)
- };
-};
-
-/**
- * @return {goog.math.Size}
- */
-ol.Map.prototype.getViewportSize = function() {
- // TODO: listen for resize and set this.viewportSize_ null
- // https://github.com/openlayers/ol3/issues/2
- if (goog.isNull(this.viewportSize_)) {
- this.viewportSize_ = goog.style.getSize(this.viewport_);
- }
- return this.viewportSize_;
-};
-
-/**
- * @param {ol.Loc} center Center in map projection.
- */
-ol.Map.prototype.setCenter = function(center) {
- goog.asserts.assert(!goog.isNull(center.getProjection()));
- this.center_ = center.doTransform(this.getProjection());
- this.conditionallyRender();
-};
-
-
-/**
- * @param {ol.Loc} center
- * @param {number} zoom
- */
-ol.Map.prototype.setCenterAndZoom = function(center, zoom) {
- goog.asserts.assert(!goog.isNull(center.getProjection()));
- this.center_ = center.doTransform(this.getProjection());
- this.zoom_ = this.limitZoom(zoom);
- this.conditionallyRender();
-};
-
-
-/**
- * @param {number} zoom The zoom level to zoom to
- * @param {goog.math.Coordinate|{x: number, y: number}=} opt_anchor
- * Optional anchor pixel for the zoom origin.
- */
-ol.Map.prototype.setZoom = function(zoom, opt_anchor) {
- var currentZoom = this.zoom_,
- newZoom = this.limitZoom(zoom),
- newCenter;
- if (newZoom === currentZoom) {
- return;
- }
- if (goog.isDef(opt_anchor)) {
- var size = this.getViewportSize(),
- anchorLoc = this.getLocForViewportPixel(opt_anchor),
- newRes = this.getResolutionForZoom(newZoom);
- newCenter = anchorLoc.add(
- (size.width/2 - opt_anchor.x) * newRes,
- (opt_anchor.y - size.height/2) * newRes
- );
- } else {
- newCenter = this.center_;
- }
- this.setCenterAndZoom(newCenter, newZoom);
-};
-
-
-/**
- * @param {ol.Projection} projection Projection.
- */
-ol.Map.prototype.setProjection = function(projection) {
- this.projection_ = projection;
-};
-
-
-/**
- * @param {ol.Projection} userProjection set the user projection.
- */
-ol.Map.prototype.setUserProjection = function(userProjection) {
- this.userProjection_ = userProjection;
-};
-
-
-/**
- * @param {number} zoom
- * @return {number} zoom clamped to the range of available zoom levels.
- */
-ol.Map.prototype.limitZoom = function(zoom) {
- return goog.math.clamp(zoom, 0, this.getNumZoomLevels()-1);
-};
-
-
-/**
- * @param {number} nZoom Zoom.
- */
-ol.Map.prototype.setNumZoomLevels = function(nZoom) {
- this.numZoomLevels_ = nZoom;
-};
-
-/**
- * @param {Array} resolutions the map resolutions if set on the map
- */
-ol.Map.prototype.setResolutions = function(resolutions) {
- this.resolutions_ = resolutions;
-};
-
-/**
- * @param {Array} layers the layers set on the map
- */
-ol.Map.prototype.setLayers = function(layers) {
- //TODO remove layers properly if there are layers already
- this.layers_ = [];
- this.addLayers(layers);
-};
-
-ol.Map.prototype.addLayers = function(layers) {
- var layer;
- for (var i=0, ii=layers.length; i|undefined} opt_controls
- */
-ol.Map.prototype.setControls = function(opt_controls) {
- if (!this.controls_) {
- var control;
- for (var i=0, ii=opt_controls.length; i}
- */
-ol.Map.preferredRenderers = ["webgl", "canvas"];
-
diff --git a/src/ol/Popup.js b/src/ol/Popup.js
deleted file mode 100644
index bb932685c1..0000000000
--- a/src/ol/Popup.js
+++ /dev/null
@@ -1,343 +0,0 @@
-goog.provide('ol.Popup');
-
-goog.require('ol.Map');
-goog.require('ol.Loc');
-goog.require('ol.Feature');
-
-
-/**
- * @export
- * @constructor
- * @param {ol.Map} map the map on which the popup is placed.
- * @param {ol.Loc|ol.Feature=} opt_anchor the anchor object for the popup.
- * @param {string=} opt_placement the placement of the arrow on the popup.
- * @param {boolean=} opt_close include a close button on the popup
- */
-ol.Popup = function(map, opt_anchor, opt_placement, opt_close) {
-
- /**
- * @private
- * @type {ol.Map}
- */
- this.map_ = map;
-
- /**
- * @private
- * @type {ol.Loc|ol.Feature|undefined}
- */
- this.anchor_ = opt_anchor;
-
- /**
- * can be 'top','bottom','right','left','auto'
- * TODO: 'auto' not yet implemented
- * @private
- * @type {!string}
- */
- this.placement_ = goog.isDefAndNotNull(opt_placement)?opt_placement:'top';
-
- /**
- * include a close button on the popup - defaults to true.
- * @private
- * @type {boolean|undefined}
- */
- this.closeButton_ = goog.isDefAndNotNull(opt_close) ? opt_close : true;
-
- /**
- * @private
- * @type {string|undefined}
- */
- this.content_ = undefined;
-
- /**
- * @private
- * @type {string|undefined}
- */
- this.template_ = undefined;
-
- /**
- * @private
- * @type {Element}
- */
- this.container_ = null;
-
- /**
- * @private
- * @type {number}
- */
- this.arrowOffset_ = 30; //FIXME: set this from CSS dynamically somehow?
-
- /**
- * if the CSS sets either width or height assume the app is specifying the
- * size of the popup, if not auto size the popup.
- * @private
- * @type {boolean}
- */
- this.autoSize_ = true;
-
-};
-
-/**
- * @const
- */
-ol.Popup.CLASS_NAME = 'ol-popup';
-
-/**
- * @return {ol.Map} Projection.
- */
-ol.Popup.prototype.getMap = function() {
- return this.map_;
-};
-
-/**
- * @param {ol.Map} map the map object to hold this popup.
- */
-ol.Popup.prototype.setMap = function(map) {
- this.map_ = map;
-};
-
-/**
- * @return {ol.Feature|ol.Loc|undefined} the anchor .
- */
-ol.Popup.prototype.getAnchor = function() {
- return this.anchor_;
-};
-
-/**
- * @param {ol.Feature|ol.Loc} anchor the anchor location to place this popup.
- */
-ol.Popup.prototype.setAnchor = function(anchor) {
- this.anchor_ = anchor;
-};
-
-
-/**
- * @return {string|undefined} the placement value relative to the anchor.
- */
-ol.Popup.prototype.getPlacement = function() {
- return this.placement_;
-};
-
-/**
- * @param {string} placement where to place this popup relative to the anchor.
- */
-ol.Popup.prototype.setPlacement = function(placement) {
- if (!goog.isNull(this.container_)) {
- goog.dom.classes.remove(this.container_,
- ol.Popup.CLASS_NAME+'-'+this.placement_);
- goog.dom.classes.add(this.container_,ol.Popup.CLASS_NAME+'-'+placement);
- }
- this.placement_ = placement;
-};
-
-
-/**
- * @return {string|undefined} static content to be displayed in the popup (HTML)
- */
-ol.Popup.prototype.getContent = function() {
- return this.content_;
-};
-
-/**
- * @param {string} content the content to be displayed this popup.
- */
-ol.Popup.prototype.setContent = function(content) {
- this.content_ = content;
-};
-
-
-/**
- * @private
- * @returns {string} generates the content
- */
-ol.Popup.prototype.generateContent_ = function() {
- //set the content
- if ( goog.isDefAndNotNull(this.content_) ) {
- return this.content_;
- } else {
- if ( goog.isDefAndNotNull(this.template_) &&
- goog.isDefAndNotNull(this.anchor_) &&
- (this.anchor_ instanceof ol.Feature)) {
- //set content from feature attributes on the template
- //TODO: this.setContent(template.apply(this.anchor_.getAttributes()));
- return this.template_; //stub to return something
- } else {
- ol.error('ol.Popup unabale to generate any content');
- return 'no content
';
- }
- }
-};
-
-
-/**
- * @return {string|undefined} the anchor .
- */
-ol.Popup.prototype.getTemplate = function() {
- return this.template_;
-};
-
-/**
- * @param {string} template the map object to hold this popup.
- */
-ol.Popup.prototype.setTemplate = function(template) {
- this.template_ = template;
-};
-
-/**
- * Open the popup.
- * @param {ol.Feature|ol.Loc} opt_arg feature or location for the anchor
- */
-ol.Popup.prototype.doOpen = function(opt_arg) {
- if (goog.isDef(opt_arg)) {
- this.setAnchor(opt_arg);
- }
-
- //create popup container if it's not created already
- if (goog.isNull(this.container_)) {
- this.container_ = goog.dom.createElement('div');
- goog.dom.classes.add(this.container_,
- ol.Popup.CLASS_NAME, ol.Popup.CLASS_NAME+'-'+this.placement_);
-
- //see if the style class sets width or height
- if (goog.style.getStyle(this.container_, 'width').length>0 ||
- goog.style.getStyle(this.container_, 'height').length>0 ) {
- this.autoSize_ = false;
- }
-
- if (this.closeButton_) {
- var closeButton = goog.dom.createElement('div');
- goog.dom.appendChild(this.container_, closeButton);
- goog.dom.classes.add(closeButton, ol.Popup.CLASS_NAME+'-close');
- }
- goog.events.listen(this.map_, 'click', this.clickHandler,
- undefined, this);
- goog.dom.appendChild(this.map_.getMapOverlay(), this.container_);
- }
-
- this.childContent_=goog.dom.htmlToDocumentFragment(this.generateContent_());
- goog.dom.appendChild(this.container_, this.childContent_);
-
- if (this.autoSize_) {
- this.registerImageListeners();
- }
-
- this.setAnchorOffset_();
-};
-
-ol.Popup.prototype.setAnchorOffset_ = function() {
-
- if (goog.isNull(this.container_.parentNode)) {
- //this means the popup has already been closed, nothing to do here
- //which might happen while waiting for images to load
- return;
- }
-
- if (!goog.isDefAndNotNull(this.anchor_)) {
- //must have an anchor when trying to set the position
- ol.error("ol.Popup must have an anchor to set the position");
- return;
- }
-
- //position the element
- if (this.anchor_ instanceof ol.Feature) {
- this.pos_ = this.anchor_.getGeometry().getCentroid();
- } else {
- this.pos_ = new ol.geom.Point(this.anchor_.getX(), this.anchor_.getY());
- }
- var pos = /** @type {ol.Loc} */ (this.pos_);
- var popupPosPx = this.map_.getViewportPixelForLoc(pos);
- var popupSize = goog.style.getSize(this.container_);
-
- switch(this.placement_) {
- default:
- case 'auto':
- //TODO: switch based on map quadrant
- break;
- case 'top':
- case 'bottom':
- popupPosPx[0] -= popupSize.width / 2.0;
-
- if (this.placement_ == "bottom") {
- popupPosPx[1] -= popupSize.height + this.arrowOffset_;
- } else {
- popupPosPx[1] += this.arrowOffset_;
- }
- break;
- case 'left':
- case 'right':
- popupPosPx[1] -= popupSize.height / 2.0;
-
- if (this.placement_ == "right") {
- popupPosPx[0] -= popupSize.width + this.arrowOffset_;
- } else {
- popupPosPx[0] += this.arrowOffset_;
- }
- break;
- }
- this.moveTo_(popupPosPx);
-
-};
-
-/**
- * registerImageListeners
- * Called when an image contained by the popup loaded. this function
- * updates the popup size, then unregisters the image load listener.
- */
-ol.Popup.prototype.registerImageListeners = function() {
-
- // As the images load, this function will call setAnchorOffset_() to
- // resize the popup to fit the content div (which presumably is now
- // bigger than when the image was not loaded).
- //
- //cycle through the images and if their size is 0x0, that means that
- // they haven't been loaded yet, so we attach the listener, which
- // will fire when the images finish loading and will resize the
- // popup accordingly to its new size.
- var images = this.container_.getElementsByTagName("img");
- for (var i = 0, len = images.length; i < len; i++) {
- var img = images[i];
- if (img.width == 0 || img.height == 0) {
- goog.events.listenOnce(img, 'load',
- goog.bind(this.setAnchorOffset_, this));
- }
- }
-};
-
-
-/**
- * @param px - {goog.} the top and left position of the popup div.
- */
-ol.Popup.prototype.moveTo_ = function(px) {
- if (goog.isDefAndNotNull(px)) {
- goog.style.setPosition(this.container_, px[0], px[1]);
- }
-};
-
-/**
- * Click handler
- * @param {Event} evt the event generated by a click
- */
-ol.Popup.prototype.clickHandler = function(evt) {
- var target = /** @type {Node} */ evt.target;
- if (goog.dom.classes.has(target,ol.Popup.CLASS_NAME+'-close')) {
- this.close();
- }
-};
-
-/**
- * Clean up.
- * @export
- */
-ol.Popup.prototype.close = function() {
- goog.dom.removeChildren(this.container_);
- goog.dom.removeNode(this.container_);
-};
-
-/**
- * Clean up.
- * @export
- */
-ol.Popup.prototype.destroy = function() {
- for (var key in this) {
- delete this[key];
- }
-};
diff --git a/src/ol/Projection.js b/src/ol/Projection.js
deleted file mode 100644
index 78e38d7d09..0000000000
--- a/src/ol/Projection.js
+++ /dev/null
@@ -1,257 +0,0 @@
-goog.provide('ol.Projection');
-goog.require('ol.UnreferencedBounds');
-
-/**
- * @export
- * @constructor
- * @param {string} code Projection identifier.
- */
-ol.Projection = function(code) {
-
- /**
- * @private
- * @type {string}
- */
- this.code_ = code;
-
- /**
- * @private
- * @type {string|undefined}
- */
- this.units_ = undefined;
-
- /**
- * @private
- * @type {Object}
- */
- this.proj_ = null;
-
- /**
- * @private
- * @type {ol.UnreferencedBounds}
- */
- this.extent_ = null;
-
-};
-
-
-/**
- * @return {string} Code.
- */
-ol.Projection.prototype.getCode = function() {
- return this.code_;
-};
-
-/**
- * @param {string} code Code.
- */
-ol.Projection.prototype.setCode = function(code) {
- this.code_ = code;
-};
-
-/**
- * @return {string|undefined} Units abbreviation.
- */
-ol.Projection.prototype.getUnits = function() {
- return this.units_;
-};
-
-/**
- * @param {string} units Units abbreviation.
- */
-ol.Projection.prototype.setUnits = function(units) {
- this.units_ = units;
-};
-
-/**
- * Get the validity extent of the coordinate reference system.
- *
- * @return {ol.UnreferencedBounds} The valididty extent.
- */
-ol.Projection.prototype.getExtent = function() {
- if (goog.isNull(this.extent_)) {
- var defs = ol.Projection.defaults[this.code_];
- if (goog.isDef(defs)) {
- var ext = defs.maxExtent;
- if (goog.isDef(ext)) {
- this.setExtent(new ol.UnreferencedBounds(ext[0],ext[1],ext[2],ext[3]));
- }
- }
- }
- return this.extent_;
-};
-
-/**
- * @param {!ol.UnreferencedBounds} extent Validity extent.
- */
-ol.Projection.prototype.setExtent = function(extent) {
- this.extent_ = extent;
-};
-
-/**
- * Transforms is an object, with from properties, each of which may
- * have a to property. This allows you to define projections without
- * requiring support for proj4js to be included.
- *
- * This object has keys which correspond to a 'source' projection object. The
- * keys should be strings, corresponding to the projection.getCode() value.
- * Each source projection object should have a set of destination projection
- * keys included in the object.
- *
- * Each value in the destination object should be a transformation function,
- * where the function is expected to be passed an object with a .x and a .y
- * property. The function should return the object, with the .x and .y
- * transformed according to the transformation function.
- *
- * Note - Properties on this object should not be set directly. To add a
- * transform method to this object, use the method. For an
- * example of usage, see the OpenLayers.Layer.SphericalMercator file.
- *
- * @type {Object}
- */
-ol.Projection.transforms = {};
-
-/**
- * Defaults for the SRS codes known to OpenLayers (currently EPSG:4326, CRS:84,
- * urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857, EPSG:102113 and
- * EPSG:102100). Keys are the SRS code, values are units, maxExtent (the
- * validity extent for the SRS) and yx (true if this SRS is known to have a
- * reverse axis order).
- *
- * @type {Object}
- */
-ol.Projection.defaults = {
- "EPSG:4326": {
- units: "degrees",
- maxExtent: [-180, -90, 180, 90],
- yx: true
- },
- "CRS:84": {
- units: "degrees",
- maxExtent: [-180, -90, 180, 90]
- },
- "EPSG:900913": {
- units: "m",
- maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]
- }
-};
-
-/**
- * Set a custom transform method between two projections. Use this method in
- * cases where the proj4js lib is not available or where custom projections
- * need to be handled.
- *
- * @param {string} from The code for the source projection.
- * @param {string} to The code for the destination projection.
- * @param {function(Object)} method A function that takes an object with x and
- * y properties as an argument and transforms that point from the source to
- * the destination projection in place. The original point should be
- * modified.
- */
-ol.Projection.addTransform = function(from, to, method) {
- if (method === ol.Projection.nullTransform) {
- var defaults = ol.Projection.defaults[from];
- if (defaults && !ol.Projection.defaults[to]) {
- ol.Projection.defaults[to] = defaults;
- }
- }
- if(!ol.Projection.transforms[from]) {
- ol.Projection.transforms[from] = {};
- }
- ol.Projection.transforms[from][to] = method;
-};
-
-/**
- * Transform a point coordinate from one projection to another.
- *
- * @param {Object} point Object with x and y properties.
- * @param {ol.Projection} source Source projection.
- * @param {ol.Projection} dest Destination projection.
- */
-ol.Projection.transform = function(point, source, dest) {
- goog.asserts.assertObject(point);
- goog.asserts.assertObject(source);
- goog.asserts.assertObject(dest);
- if (source.proj_ && dest.proj_) {
- // TODO: implement Proj4js handling
- // point = Proj4js.transform(source.proj_, dest.proj_, point);
- } else {
- var sourceCode = source.getCode();
- var destCode = dest.getCode();
- var transforms = ol.Projection.transforms;
- if (transforms[sourceCode] && transforms[sourceCode][destCode]) {
- transforms[sourceCode][destCode](point);
- }
- }
-};
-
-/**
- * A null transformation - useful for defining projection aliases when
- * proj4js is not available:
- *
- * ol.Projection.addTransform("EPSG:3857", "EPSG:900913",
- * ol.Projection.nullTransform);
- * ol.Projection.addTransform("EPSG:900913", "EPSG:3857",
- * ol.Projection.nullTransform);
- *
- * @type {function(Object)}
- */
-ol.Projection.nullTransform = function(point) {
- return point;
-};
-
-/**
- * Note: Transforms for web mercator <-> geographic
- * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100.
- * OpenLayers originally started referring to EPSG:900913 as web mercator.
- * The EPSG has declared EPSG:3857 to be web mercator.
- * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as
- * equivalent. See http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2009/11/20/ArcGIS-Online-moving-to-Google-_2F00_-Bing-tiling-scheme_3A00_-What-does-this-mean-for-you_3F00_.aspx#12084.
- * For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and
- * urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis
- * order for EPSG:4326.
- */
-(function() {
-
- var pole = 20037508.34;
-
- function inverseMercator(xy) {
- xy.x = 180 * xy.x / pole;
- xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2);
- return xy;
- }
-
- function forwardMercator(xy) {
- xy.x = xy.x * pole / 180;
- xy.y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;
- return xy;
- }
-
- function map(base, codes) {
- var add = ol.Projection.addTransform;
- var same = ol.Projection.nullTransform;
- var i, len, code, other, j;
- for (i=0, len=codes.length; i=0; --i) {
- map(mercator[i], geographic);
- }
- for (i=geographic.length-1; i>=0; --i) {
- map(geographic[i], mercator);
- }
-
-})();
diff --git a/src/ol/Tile.js b/src/ol/Tile.js
deleted file mode 100644
index fb84b117a2..0000000000
--- a/src/ol/Tile.js
+++ /dev/null
@@ -1,169 +0,0 @@
-goog.provide('ol.Tile');
-
-goog.require('ol.Bounds');
-
-goog.require('goog.events.EventTarget');
-goog.require('goog.events');
-goog.require('goog.asserts');
-
-/**
- * The Tile class.
- * @constructor
- * @extends {goog.events.EventTarget}
- * @param {string} url
- * @param {ol.Bounds|undefined} opt_bounds
- */
-ol.Tile = function(url, opt_bounds) {
-
- /**
- * @private
- * @type {string}
- */
- this.url_ = url;
-
- /**
- * @private
- * @type {ol.Bounds|undefined}
- */
- this.bounds_ = opt_bounds;
-
- /**
- * @private
- * @type {boolean}
- */
- this.loaded_ = false;
-
- /**
- * @private
- * @type {boolean}
- */
- this.loading_ = false;
-
- /**
- * @private
- * @type {HTMLImageElement}
- */
- this.img_ = this.createImage();
- goog.events.listenOnce(this.img_, goog.events.EventType.LOAD,
- this.handleImageLoad, false, this);
- goog.events.listenOnce(this.img_, goog.events.EventType.ERROR,
- this.handleImageError, false, this);
-};
-goog.inherits(ol.Tile, goog.events.EventTarget);
-
-/**
- * @protected
- * @return {HTMLImageElement}
- */
-ol.Tile.prototype.createImage = function() {
- // overriden by subclasses
-};
-
-/**
- * Load the tile. A tile should loaded only once.
- */
-ol.Tile.prototype.load = function() {
- goog.asserts.assert(!this.loaded_ && !this.loading_);
- this.loading_ = true;
- this.img_.src = this.url_;
-};
-
-/**
- * Get the tile url.
- * @return {string}
- */
-ol.Tile.prototype.getUrl = function() {
- return this.url_;
-};
-
-/**
- * Get the tile bounds.
- * @return {ol.Bounds|undefined}
- */
-ol.Tile.prototype.getBounds = function() {
- return this.bounds_;
-};
-
-/**
- * Get the tile image.
- * @return {HTMLImageElement}
- */
-ol.Tile.prototype.getImg = function() {
- return this.img_;
-};
-
-/**
- * Handle load event on the image.
- * @param {goog.events.BrowserEvent} evt Event.
- */
-ol.Tile.prototype.handleImageLoad = function(evt) {
- this.loading_ = false;
- this.loaded_ = true;
- this.img_.style.visibility = "inherit";
- this.img_.style.opacity = 1; // TODO: allow for layer opacity
- goog.events.dispatchEvent(this, 'load');
-};
-
-/**
- * Handle load error event on the image.
- * @param {goog.events.BrowserEvent} evt Event.
- */
-ol.Tile.prototype.handleImageError = function(evt) {
- this.loading_ = false;
- goog.events.dispatchEvent(this, 'error');
-};
-
-/**
- * Is the tile loaded already?
- * @return {boolean}
- */
-ol.Tile.prototype.isLoaded = function() {
- return this.loaded_;
-};
-
-/**
- * Is the tile being loaded?
- * @return {boolean}
- */
-ol.Tile.prototype.isLoading = function() {
- return this.loading_;
-};
-
-/**
- *
- */
-ol.Tile.prototype.destroy = function() {
- goog.events.dispatchEvent(this, 'destroy');
-};
-
-/**
- * Create a tile constructor, for specific width and height values
- * for the tiles.
- * @param {number} width
- * @param {number} height
- * @return {function(new:ol.Tile, string, ol.Bounds=)}
- */
-ol.Tile.createConstructor = function(width, height) {
- /**
- * @constructor
- * @extends {ol.Tile}
- */
- var Tile = function(url, opt_bounds) {
- goog.base(this, url, opt_bounds);
- };
- goog.inherits(Tile, ol.Tile);
- /** @inheritDoc */
- Tile.prototype.createImage = (function() {
- var img = document.createElement("img");
- img.className = "olTile";
- img.style.position = "absolute";
- img.style.width = width + "px";
- img.style.height = height + "px";
- img.style.opacity = 0;
- img.src = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
- return function() {
- return img.cloneNode(false);
- };
- })();
- return Tile;
-};
diff --git a/src/ol/TileCache.js b/src/ol/TileCache.js
deleted file mode 100644
index 0772adf06b..0000000000
--- a/src/ol/TileCache.js
+++ /dev/null
@@ -1,23 +0,0 @@
-goog.provide('ol.TileCache');
-
-goog.require('goog.structs.LinkedMap');
-
-/**
- * A cache of ol.Tile objects.
- * @constructor
- * @extends {goog.structs.LinkedMap}
- * @param {number=} opt_size
- */
-ol.TileCache = function(opt_size) {
- goog.base(this, opt_size || 100, true /* cache mode */);
-};
-
-goog.inherits(ol.TileCache, goog.structs.LinkedMap);
-
-/**
- * @inheritDoc
- */
-ol.TileCache.prototype.removeNode = function(node) {
- goog.base(this, 'removeNode', node);
- node.value.destroy();
-};
diff --git a/src/ol/TileSet.js b/src/ol/TileSet.js
deleted file mode 100644
index 56c8f3885b..0000000000
--- a/src/ol/TileSet.js
+++ /dev/null
@@ -1,66 +0,0 @@
-goog.provide('ol.TileSet');
-
-/**
- * The TileSet class. A TileSet instance represents a collection of
- * tiles. Tiles of a TileSet have the same resolution, width and
- * height.
- * @constructor
- * @param {Array.>} tiles
- * @param {number} tileWidth
- * @param {number} tileHeight
- * @param {number} resolution
- */
-ol.TileSet = function(tiles, tileWidth, tileHeight, resolution) {
-
- /**
- * @private
- * @type {Array.>}
- */
- this.tiles_ = tiles;
-
- /**
- * @private
- * @type {number}
- */
- this.tileWidth_ = tileWidth;
-
- /**
- * @private
- * @type {number}
- */
- this.tileHeight_ = tileHeight;
-
- /**
- * @private
- * @type {number}
- */
- this.resolution_ = resolution;
-};
-
-/**
- * @return {Array.>}
- */
-ol.TileSet.prototype.getTiles = function() {
- return this.tiles_;
-};
-
-/**
- * @return {number}
- */
-ol.TileSet.prototype.getResolution = function() {
- return this.resolution_;
-};
-
-/**
- * @return {number}
- */
-ol.TileSet.prototype.getTileHeight = function() {
- return this.tileHeight_;
-};
-
-/**
- * @return {number}
- */
-ol.TileSet.prototype.getTileWidth = function() {
- return this.tileWidth_;
-};
diff --git a/src/ol/UnreferencedBounds.js b/src/ol/UnreferencedBounds.js
deleted file mode 100644
index f07a775254..0000000000
--- a/src/ol/UnreferencedBounds.js
+++ /dev/null
@@ -1,130 +0,0 @@
-goog.provide('ol.UnreferencedBounds');
-
-/**
- * @constructor
- * @param {number} minX Minimum X.
- * @param {number} minY Minimum Y.
- * @param {number} maxX Maximum X.
- * @param {number} maxY Maximum Y.
- */
-ol.UnreferencedBounds = function(minX, minY, maxX, maxY) {
-
- /**
- * @protected
- * @type {number}
- */
- this.minX_ = minX;
-
- /**
- * @protected
- * @type {number}
- */
- this.minY_ = minY;
-
- /**
- * @protected
- * @type {number}
- */
- this.maxX_ = maxX;
-
- /**
- * @protected
- * @type {number}
- */
- this.maxY_ = maxY;
-
-};
-
-
-/**
- * @return {number} Minimun X.
- */
-ol.UnreferencedBounds.prototype.getMinX = function() {
- return this.minX_;
-};
-
-/**
- * @param {number} minX Minimum X.
- */
-ol.UnreferencedBounds.prototype.setMinX = function(minX) {
- this.minX_ = minX;
-};
-
-/**
- * @return {number} Minimun Y.
- */
-ol.UnreferencedBounds.prototype.getMinY = function() {
- return this.minY_;
-};
-
-/**
- * @param {number} minY Minimum Y.
- */
-ol.UnreferencedBounds.prototype.setMinY = function(minY) {
- this.minY_ = minY;
-};
-
-/**
- * @return {number} Maximun X.
- */
-ol.UnreferencedBounds.prototype.getMaxX = function() {
- return this.maxX_;
-};
-
-/**
- * @param {number} maxX Maximum X.
- */
-ol.UnreferencedBounds.prototype.setMaxX = function(maxX) {
- this.maxX_ = maxX;
-};
-
-/**
- * @return {number} Maximun Y.
- */
-ol.UnreferencedBounds.prototype.getMaxY = function() {
- return this.maxY_;
-};
-
-/**
- * @param {number} maxY Maximum Y.
- */
-ol.UnreferencedBounds.prototype.setMaxY = function(maxY) {
- this.maxY_ = maxY;
-};
-
-/**
- * @return {number} Bounds width.
- */
-ol.UnreferencedBounds.prototype.getWidth = function() {
- return this.maxX_ - this.minX_;
-};
-
-/**
- * @return {number} Bounds height.
- */
-ol.UnreferencedBounds.prototype.getHeight = function() {
- return this.maxY_ - this.minY_;
-};
-
-/**
- * Determine if this bounds intersects the target bounds (bounds that only
- * touch are considered intersecting).
- *
- * @param {ol.UnreferencedBounds} bounds Target bounds.
- * @return {boolean} The provided bounds intersects this bounds.
- */
-ol.UnreferencedBounds.prototype.intersects = function(bounds) {
- return !(
- // this is left
- (this.minX_ > bounds.getMaxX()) ||
-
- // this is right
- (this.maxX_ < bounds.getMinX()) ||
-
- // this is above
- (this.minY_ > bounds.getMaxY()) ||
-
- // this is below
- (this.maxY_ < bounds.getMinY())
- );
-};
diff --git a/src/ol/base.js b/src/ol/base.js
deleted file mode 100644
index bdf3e64037..0000000000
--- a/src/ol/base.js
+++ /dev/null
@@ -1,46 +0,0 @@
-goog.provide('ol.base');
-goog.provide('ol.error');
-
-/**
- * @param {string} message Message.
- */
-ol.error = function(message) {
- if (ol.error.VERBOSE_ERRORS) {
- throw new Error(message);
- } else {
- throw null;
- }
-};
-
-/**
- * Compilation with public API, let's accept options from external world
- * @define {boolean}
- */
-ol.API = true;
-
-/**
- * @define {boolean}
- */
-ol.error.VERBOSE_ERRORS = true;
-
-/**
- * Options passed in the API from external world are checked for wrong keys
- * @define {boolean}
- */
-ol.CHECK_KEYS = true;
-
-/**
- * @param {Object} obj Object.
- * @param {!Array.} allowedKeys Allowed keys.
- */
-ol.base.checkKeys = function(obj, allowedKeys) {
- if (ol.CHECK_KEYS) {
- var keys = goog.object.getKeys(obj);
- goog.array.forEach(allowedKeys, function(allowedKey) {
- goog.array.remove(keys, allowedKey);
- });
- if (!goog.array.isEmpty(keys)) {
- ol.error('object contains invalid keys: ' + keys.join(', '));
- }
- }
-};
diff --git a/src/ol/collection_test.js b/src/ol/collection_test.js
new file mode 100644
index 0000000000..2e52fcdd91
--- /dev/null
+++ b/src/ol/collection_test.js
@@ -0,0 +1,22 @@
+goog.require('goog.testing.jsunit');
+goog.require('ol');
+goog.require('ol3.Collection');
+
+
+function testCreateFromArray() {
+ var array = [0, 1, 2];
+ var collection = ol.collection(array);
+ assertTrue(collection instanceof ol3.Collection);
+ assertEquals(3, collection.getLength());
+ assertEquals(0, collection.getAt(0));
+ assertEquals(1, collection.getAt(1));
+ assertEquals(2, collection.getAt(2));
+}
+
+
+function testCreateFromCollection() {
+ var collection1 = new ol3.Collection();
+ var collection2 = ol.collection(collection1);
+ assertTrue(collection1 === collection2);
+}
+
diff --git a/src/ol/control/Attribution.js b/src/ol/control/Attribution.js
deleted file mode 100644
index b681293e04..0000000000
--- a/src/ol/control/Attribution.js
+++ /dev/null
@@ -1,95 +0,0 @@
-goog.provide('ol.control.Attribution');
-
-goog.require('ol.control.Control');
-
-goog.require('goog.dom');
-goog.require('goog.events');
-goog.require('goog.events.Event');
-
-
-/**
- * @constructor
- * @extends {ol.control.Control}
- * @param {boolean|undefined} opt_autoActivate
- */
-ol.control.Attribution = function(opt_autoActivate) {
-
- goog.base(this, opt_autoActivate);
-
- /**
- * @type {Node}
- */
- this.container_ = null;
-
- /**
- * Activate this control when it is added to a map. Default is true.
- *
- * @type {boolean} autoActivate
- */
- this.autoActivate_ =
- goog.isDef(opt_autoActivate) ? opt_autoActivate : true;
-
-};
-goog.inherits(ol.control.Attribution, ol.control.Control);
-
-/**
- * @const {string}
- */
-ol.control.Attribution.prototype.CLS = 'ol-control-attribution';
-
-/**
- * @param {ol.Map} map
- */
-ol.control.Attribution.prototype.setMap = function(map) {
- var staticOverlay = map.getStaticOverlay();
- if (goog.isNull(this.container_)) {
- this.container_ = goog.dom.createDom('div', this.CLS);
- goog.events.listen(this.container_, 'click',
- goog.events.Event.stopPropagation);
- }
- if (!goog.isNull(staticOverlay)) {
- goog.dom.append(staticOverlay, this.container_);
- }
- goog.base(this, 'setMap', map);
-};
-
-/** @inheritDoc */
-ol.control.Attribution.prototype.activate = function() {
- var active = goog.base(this, 'activate');
- if (active) {
- goog.events.listen(this.map_, 'layeradd', this.update, false, this);
- this.update();
- }
- return active;
-};
-
-/** @inheritDoc */
-ol.control.Attribution.prototype.deactivate = function() {
- var inactive = goog.base(this, 'deactivate');
- if (inactive) {
- goog.events.unlisten(this.map_, 'layeradd', this.update, false, this);
- }
- return inactive;
-};
-
-ol.control.Attribution.prototype.update = function() {
- var attribution = [],
- layers = this.map_.getLayers(), layerAttribution;
- for (var i=0, ii=layers.length; i} components An array of components.
- *
- * @constructor
- */
-ol.geom.Collection = function(components) {
-
- /**
- * @private
- * @type {Array.}
- */
- this.typeBlacklist_ = [
- ol.geom.Collection
- ];
-
- /**
- * @private
- * @type {Array.}
- */
- this.typeWhitelist_ = [
- ol.geom.MultiPoint,
- ol.geom.MultiLineString
- // TODO uncomment when implemented
- // ,ol.geom.MultiPolygon
- ];
-
- /**
- * @private
- * @type {Array.}
- */
- this.components_ = [];
-
- if (arguments.length === 1 && goog.isDef(components)) {
- this.setComponents(components);
- }
-};
-
-goog.inherits(ol.geom.Collection, ol.geom.Geometry);
-
-/**
- * Sets the list of disallowed types for the collection.
- * @param {Array.} typeBlacklist Array of constructors to disallow.
- */
-ol.geom.Collection.prototype.setTypeBlacklist = function(typeBlacklist){
- this.typeBlacklist_ = typeBlacklist;
-};
-/**
- * Gets the list of disallowed types for the collection.
- * @return {Array.} Array of constructors to disallow.
- */
-ol.geom.Collection.prototype.getTypeBlacklist = function(){
- return this.typeBlacklist_;
-};
-
-/**
- * Sets the list of always allowed types for the collection.
- * @param {Array.} typeWhitelist Array of constructors to allow.
- */
-ol.geom.Collection.prototype.setTypeWhitelist = function(typeWhitelist){
- this.typeWhitelist_ = typeWhitelist;
-};
-/**
- * Gets the list of always allowed types for the collection.
- * @return {Array.} Array of constructors to allow.
- */
-ol.geom.Collection.prototype.getTypeWhitelist = function(){
- return this.typeWhitelist_;
-};
-
-
-/**
- * Sets the Collection's components.
- *
- * @return {Array.} An array of components.
- */
-ol.geom.Collection.prototype.getComponents = function() {
- return this.components_;
-};
-
-/**
- * Gets the Collection's components.
- *
- * @param {Array.} components An array of components.
- */
-ol.geom.Collection.prototype.setComponents = function(components) {
- var allValidTypes = goog.array.every(
- components,
- this.isAllowedComponent,
- this
- );
- if (allValidTypes) {
- this.components_ = components;
- } else {
- var msg = 'ol.geom.Collection: at least one component passed to '
- + 'setComponents is not allowed.';
- ol.error(msg);
- }
-};
-
-/**
- * Adds the given component to the list of components at the specified index.
- *
- * @param {ol.geom.Geometry} component A component to be added.
- * @param {number} index The index where to add.
- */
-ol.geom.Collection.prototype.addComponent = function(component, index) {
- if (this.isAllowedComponent(component)) {
- goog.array.insertAt(this.components_, component, index);
- } else {
- var msg = 'ol.geom.Collection: component is not allowed to be added.';
- ol.error(msg);
- }
-};
-
-/**
- * Checks whether the passed component is an instance of any of the constructors
- * listed in the passed list.
- *
- * @param {ol.geom.Geometry} component The component to check.
- * @param {Array.} list The List of constructors to check the
- * component against.
- *
- * @return {boolean} Whether the passed component is an instance of any of the
- * constructors listed in the passed list.
- *
- * @private
- */
-ol.geom.Collection.prototype.isOnList = function(component, list) {
- var isOnList = !goog.array.every(list, function(listedConstr){
- if (component instanceof listedConstr) {
- return false;
- } else {
- return true;
- }
- });
- return isOnList;
-};
-
-/**
- * Checks whether the passed component is allowed according to the black and
- * whitelists.
- *
- * @param {ol.geom.Geometry} component The component to check.
- * @return {boolean} Whether the passed component is allowed as part of this
- * collection according to black- and whitelist.
- */
-ol.geom.Collection.prototype.isAllowedComponent = function(component){
- var whitelist = this.getTypeWhitelist(),
- blacklist = this.getTypeBlacklist(),
- isOnWhitelist = this.isOnList(component, whitelist),
- isOnBlacklist = this.isOnList(component, blacklist);
- return (isOnWhitelist || !isOnBlacklist);
-};
-
-/**
- * Removes the given component from the list of components.
- *
- * @param {ol.geom.Geometry} component A component to be removed.
- */
-ol.geom.Collection.prototype.removeComponent = function(component) {
- goog.array.remove(this.components_, component);
-};
-
-/**
- * Compute the centroid for this geometry collection.
- *
- * @returns {ol.geom.Point} The centroid of the collection.
- */
-ol.geom.Collection.prototype.getCentroid = function() {
- var components = this.getComponents(),
- len = components.length,
- sum_x = 0, sum_y = 0,
- centroid = null;
- if (len > 0) {
- goog.array.forEach(components, function(component){
- var singleCentroid = component.getCentroid();
- if (goog.isDefAndNotNull(singleCentroid)) {
- sum_x += singleCentroid.getX();
- sum_y += singleCentroid.getX();
- } else {
- len--;
- }
- });
- centroid = new ol.geom.Point(sum_x / len, sum_y / len);
- }
- return centroid;
-};
\ No newline at end of file
diff --git a/src/ol/geom/Geometry.js b/src/ol/geom/Geometry.js
deleted file mode 100644
index 2ed4420624..0000000000
--- a/src/ol/geom/Geometry.js
+++ /dev/null
@@ -1,58 +0,0 @@
-goog.provide('ol.geom.Geometry');
-
-goog.require('ol.geom.IGeometry');
-goog.require('ol.Bounds');
-
-/**
- * Creates ol.Geometry objects.
- *
- * @export
- * @implements {ol.geom.IGeometry}
- * @constructor
- */
-ol.geom.Geometry = function() {
-
- /**
- * @private
- * @type {ol.Bounds|undefined}
- */
- this.bounds_ = undefined;
-};
-
-/**
- * @return {ol.Bounds|undefined} The ol.Bounds.
- */
-ol.geom.Geometry.prototype.getBounds = function() {
- return this.bounds_;
-};
-
-/**
- * @param {ol.Bounds} bounds The new ol.Bounds.
- * @return {ol.geom.Geometry} This.
- */
-ol.geom.Geometry.prototype.setBounds = function(bounds) {
- this.bounds_ = bounds;
- return this;
-};
-
-/**
- * Returns the centroid of the geometry.
- *
- * @returns {ol.geom.Point} The centroid of the geometry.
- */
-ol.geom.Geometry.prototype.getCentroid = function() {
- // throw an error to enforce subclasses to implement it properly
- ol.error('ol.geom.Geometry: getCentroid must be implemented by subclasses');
- return null;
-};
-
-/**
- * Returns the area of the geometry.
- *
- * @returns {number} The area of the geometry.
- */
-ol.geom.Geometry.prototype.getArea = function() {
- // throw an error to enforce subclasses to implement it properly
- ol.error('ol.geom.Geometry: getArea must be implemented by subclasses');
- return 0;
-};
diff --git a/src/ol/geom/IGeometry.js b/src/ol/geom/IGeometry.js
deleted file mode 100644
index 2245be339c..0000000000
--- a/src/ol/geom/IGeometry.js
+++ /dev/null
@@ -1,27 +0,0 @@
-goog.provide('ol.geom.IGeometry');
-
-//goog.require('ol.geom.Point');
-//goog.require('ol.Bounds');
-
-/**
- * Interface for geometry classes forcing ol.geom.* classes to implement
- * expected functionality.
- *
- * @interface
- */
-ol.geom.IGeometry = function(){};
-
-/**
- * @return {ol.geom.Point} The centroid of the geometry.
- */
-ol.geom.IGeometry.prototype.getCentroid = function(){};
-
-/**
- * @return {ol.Bounds|undefined} The centroid of the geometry.
- */
-ol.geom.IGeometry.prototype.getBounds = function(){};
-
-/**
- * @return {number} The area of the geometry.
- */
-ol.geom.IGeometry.prototype.getArea = function(){};
\ No newline at end of file
diff --git a/src/ol/geom/LineString.js b/src/ol/geom/LineString.js
deleted file mode 100644
index 23f6e8eb0c..0000000000
--- a/src/ol/geom/LineString.js
+++ /dev/null
@@ -1,76 +0,0 @@
-goog.provide('ol.geom.LineString');
-
-goog.require('goog.array');
-goog.require('ol.geom.Geometry');
-goog.require('ol.geom.Collection');
-goog.require('ol.geom.Point');
-goog.require('ol.Projection');
-
-/**
- * Creates ol.geom.LineString objects.
- *
- * @export
- * @extends {ol.geom.Geometry}
- * @param {Array.} vertices An array of points building the
- * linestrings vertices.
- *
- * @constructor
- */
-ol.geom.LineString = function(vertices) {
- /**
- * @private
- * @type {Array.}
- */
- this.vertices_ = vertices;
-
-};
-
-goog.inherits(ol.geom.LineString, ol.geom.Geometry);
-
-/**
- * Sets the LineString's points.
- *
- * @return {Array.} An array of points.
- */
-ol.geom.LineString.prototype.getVertices = function() {
- return this.vertices_;
-};
-
-/**
- * Gets the LineString's points.
- *
- * @param {Array.} vertices An array of points.
- */
-ol.geom.LineString.prototype.setVertices = function(vertices) {
- this.vertices_ = vertices;
-};
-
-/**
- * Adds the given vertex to the list of vertices at the specified index.
- *
- * @param {ol.geom.Point} vertex A point to be added.
- * @param {number} index The index where to add.
- */
-ol.geom.LineString.prototype.addVertex = function(vertex, index) {
- goog.array.insertAt(this.vertices_,vertex,index);
-};
-
-/**
- * Removes the given vertex from the list of vertices.
- *
- * @param {ol.geom.Point} vertex A point to be removed.
- */
-ol.geom.LineString.prototype.removeVertex = function(vertex) {
- goog.array.remove(this.vertices_, vertex);
-};
-
-/**
- * Compute the centroid for this linestring.
- *
- * @returns {ol.geom.Point} The centroid of the linestring.
- */
-ol.geom.LineString.prototype.getCentroid = function() {
- var vertices = this.getVertices(),
- collection = new ol.geom.Collection(vertices);
- return collection.getCentroid();
-};
\ No newline at end of file
diff --git a/src/ol/geom/MultiLineString.js b/src/ol/geom/MultiLineString.js
deleted file mode 100644
index 4541a1a176..0000000000
--- a/src/ol/geom/MultiLineString.js
+++ /dev/null
@@ -1,61 +0,0 @@
-goog.provide('ol.geom.MultiLineString');
-
-goog.require('goog.array');
-goog.require('ol.geom.Collection');
-
-/**
- * Creates ol.geom.MultiLineString objects.
- *
- * @export
- * @extends {ol.geom.Collection}
- * @param {Array.} linestrings An array of linestrings.
- *
- * @constructor
- */
-ol.geom.MultiLineString = function(linestrings) {
- this.setTypeWhitelist([ol.geom.LineString]);
- this.setTypeBlacklist([ol.geom.Geometry]);
- if (arguments.length === 1 && goog.isDef(linestrings)) {
- this.setLineStrings(linestrings);
- }
-
-};
-
-goog.inherits(ol.geom.MultiLineString, ol.geom.Collection);
-
-/**
- * Gets the MultiLineString's linestrings.
- *
- * @return {Array.} An array of linestrings.
- */
-ol.geom.MultiLineString.prototype.getLineStrings = function() {
- return this.getComponents();
-};
-
-/**
- * Sets the MultiLineString's linestrings.
- *
- * @param {Array.} linestrings An array of linestrings.
- */
-ol.geom.MultiLineString.prototype.setLineStrings = function(linestrings) {
- this.setComponents(linestrings);
-};
-
-/**
- * Adds the given linestring to the list of linestrings at the specified index.
- *
- * @param {ol.geom.LineString} linestring A linestring to be added.
- * @param {number} index The index where to add.
- */
-ol.geom.MultiLineString.prototype.addLineString = function(linestring, index) {
- this.addComponent(linestring, index);
-};
-
-/**
- * Removes the given linestring from the list of linestrings.
- *
- * @param {ol.geom.LineString} linestring A linestring to be removed.
- */
-ol.geom.MultiLineString.prototype.removeLineString = function(linestring) {
- this.removeComponent(linestring);
-};
diff --git a/src/ol/geom/MultiPoint.js b/src/ol/geom/MultiPoint.js
deleted file mode 100644
index 31db595017..0000000000
--- a/src/ol/geom/MultiPoint.js
+++ /dev/null
@@ -1,61 +0,0 @@
-goog.provide('ol.geom.MultiPoint');
-
-goog.require('goog.array');
-goog.require('ol.geom.Collection');
-
-/**
- * Creates ol.geom.MultiPoint objects.
- *
- * @export
- * @extends {ol.geom.Collection}
- * @param {Array.} points An array of points.
- *
- * @constructor
- */
-ol.geom.MultiPoint = function(points) {
- this.setTypeWhitelist([ol.geom.Point]);
- this.setTypeBlacklist([ol.geom.Geometry]);
- if (arguments.length === 1 && goog.isDef(points)) {
- this.setPoints(points);
- }
-
-};
-
-goog.inherits(ol.geom.MultiPoint, ol.geom.Collection);
-
-/**
- * Sets the MultiPoint's points.
- *
- * @return {Array.} An array of points.
- */
-ol.geom.MultiPoint.prototype.getPoints = function() {
- return this.getComponents();
-};
-
-/**
- * Gets the MultiPoint's points.
- *
- * @param {Array.} points An array of points.
- */
-ol.geom.MultiPoint.prototype.setPoints = function(points) {
- this.setComponents(points);
-};
-
-/**
- * Adds the given point to the list of points at the specified index.
- *
- * @param {ol.geom.Point} point A point to be added.
- * @param {number} index The index where to add.
- */
-ol.geom.MultiPoint.prototype.addPoint = function(point, index) {
- this.addComponent(point, index);
-};
-
-/**
- * Removes the given point from the list of points.
- *
- * @param {ol.geom.Point} point A point to be removed.
- */
-ol.geom.MultiPoint.prototype.removePoint = function(point) {
- this.removeComponent(point);
-};
diff --git a/src/ol/geom/Point.js b/src/ol/geom/Point.js
deleted file mode 100644
index c1471b3809..0000000000
--- a/src/ol/geom/Point.js
+++ /dev/null
@@ -1,163 +0,0 @@
-goog.provide('ol.geom.Point');
-
-goog.require('ol.geom.Geometry');
-
-goog.require('ol.Projection');
-goog.require('ol.coord.AccessorInterface');
-goog.require('ol.base');
-
-/**
- * Creates ol.geom.Point objects.
- *
- * @export
- * @extends {ol.geom.Geometry}
- * @param {number} x X.
- * @param {number} y Y.
- * @param {number=} opt_z Z.
- * @param {ol.Projection=} opt_projection Projection.
- *
- * @implements {ol.coord.AccessorInterface}
- *
- * @constructor
- */
-ol.geom.Point = function(x, y, opt_z, opt_projection) {
- /**
- * @private
- * @type {number}
- */
- this.x_ = x;
-
- /**
- * @private
- * @type {number}
- */
- this.y_ = y;
-
- /**
- * @private
- * @type {number|undefined}
- */
- this.z_ = opt_z;
-
- /**
- * @private
- * @type {ol.Projection}
- */
- this.projection_ = goog.isDef(opt_projection) ? opt_projection : null;
-};
-
-goog.inherits(ol.geom.Point, ol.geom.Geometry);
-
-/**
- * @return {number} X.
- */
-ol.geom.Point.prototype.getX = function() {
- return this.x_;
-};
-
-
-/**
- * @return {number} Y.
- */
-ol.geom.Point.prototype.getY = function() {
- return this.y_;
-};
-
-
-/**
- * @return {number|undefined} Z.
- */
-ol.geom.Point.prototype.getZ = function() {
- return this.z_;
-};
-
-/**
- * @return {ol.Projection|undefined} Projection.
- */
-ol.geom.Point.prototype.getProjection = function() {
- return this.projection_;
-};
-
-/**
- * @param {ol.Projection} projection Projection.
- */
-ol.geom.Point.prototype.setProjection = function(projection) {
- this.projection_ = projection;
-};
-
-/**
- * @param {number} x X.
- */
-ol.geom.Point.prototype.setX = function(x) {
- this.x_ = x;
-};
-
-
-/**
- * @param {number} y Y.
- */
-ol.geom.Point.prototype.setY = function(y) {
- this.y_ = y;
-};
-
-
-/**
- * @param {number|undefined} z Z.
- */
-ol.geom.Point.prototype.setZ = function(z) {
- this.z_ = z;
-};
-
-/**
- * Transform this point to another coordinate reference system. This
- * requires that this point has a projection set already (if not, an error
- * will be thrown). Returns a new point object and does not modify this
- * point.
- *
- * @param {string|!ol.Projection} proj The destination projection. Can be
- * supplied as a projection instance of a string identifier.
- * @returns {!ol.geom.Point} A new location.
- */
-ol.geom.Point.prototype.transform = function(proj) {
- if (goog.isString(proj)) {
- proj = new ol.Projection(proj);
- }
- return this._transform(proj);
-};
-
-/**
- * Transform this point to a new location given a projection object.
- *
- * @param {!ol.Projection} proj The destination projection.
- * @returns {!ol.geom.Point}
- * @private
- */
-ol.geom.Point.prototype._transform = function(proj) {
- var point = {'x': this.x_, 'y': this.y_};
- var sourceProj = this.projection_;
- if (!goog.isDefAndNotNull(sourceProj)) {
- var msg = 'Cannot transform a point without a source projection.';
- ol.error(msg);
- }
- ol.Projection.transform(point, sourceProj, proj);
-
- return new ol.geom.Point(point['x'], point['y'], this.z_, proj);
-};
-
-/**
- * Returns the centroid of the point.
- *
- * @returns {ol.geom.Point} The centroid of the point.
- */
-ol.geom.Point.prototype.getCentroid = function() {
- return new ol.geom.Point(this.x_, this.y_, this.z_, this.projection_);
-};
-
-/**
- * Returns the area of the geometry whcih is always 0.
- *
- * @returns {number} The area of the point (always 0).
- */
-ol.geom.Point.prototype.getArea = function() {
- return 0;
-};
diff --git a/src/ol/layer/Layer.js b/src/ol/layer/Layer.js
deleted file mode 100644
index 8f588e5684..0000000000
--- a/src/ol/layer/Layer.js
+++ /dev/null
@@ -1,22 +0,0 @@
-goog.provide('ol.layer.Layer');
-
-/**
- * @constructor
- * @export
- */
-ol.layer.Layer = function() {
-
- /**
- * @type {string}
- * @protected
- */
- this.attribution_;
-
-};
-
-/**
- * @return {string}
- */
-ol.layer.Layer.prototype.getAttribution = function() {
- return this.attribution_;
-};
diff --git a/src/ol/layer/OSM.js b/src/ol/layer/OSM.js
deleted file mode 100644
index b628e3119d..0000000000
--- a/src/ol/layer/OSM.js
+++ /dev/null
@@ -1,21 +0,0 @@
-goog.provide('ol.layer.OSM');
-
-goog.require('ol.layer.XYZ');
-
-/**
- * Class for OSM layers.
- *
- * @export
- * @constructor
- * @extends {ol.layer.XYZ}
- */
-ol.layer.OSM = function() {
-
- //TODO Is this attribution still correct?
- /** @inheritDoc */
- this.attribution_ = "Data CC-By-SA by OpenStreetMap ";
-
- goog.base(this, 'http://a.tile.openstreetmap.org/{z}/{x}/{y}.png');
-};
-
-goog.inherits(ol.layer.OSM, ol.layer.XYZ);
diff --git a/src/ol/layer/TileLayer.js b/src/ol/layer/TileLayer.js
deleted file mode 100644
index 0b2bc7cc1e..0000000000
--- a/src/ol/layer/TileLayer.js
+++ /dev/null
@@ -1,490 +0,0 @@
-goog.provide('ol.layer.TileLayer');
-
-goog.require('ol.error');
-goog.require('ol.layer.Layer');
-goog.require('ol.Tile');
-goog.require('ol.TileCache');
-
-/**
- * @constructor
- * @extends {ol.layer.Layer}
- */
-ol.layer.TileLayer = function() {
-
- /**
- * @private
- * @type {string|undefined}
- */
- this.url_ = undefined;
-
- /**
- * @protected
- * @type {ol.Projection}
- */
- this.projection_ = null;
-
- /**
- * @private
- * @type {ol.Bounds}
- */
- this.extent_ = null;
-
- /**
- * @protected
- * @type {number}
- */
- this.tileWidth_ = 256;
-
- /**
- * @protected
- * @type {number}
- */
- this.tileHeight_ = 256;
-
- /**
- * @protected
- * @type {function(new:ol.Tile, string, ol.Bounds=)}
- */
- this.Tile = ol.Tile.createConstructor(this.tileWidth_, this.tileHeight_);
-
- /**
- * @protected
- * @type {number|undefined}
- */
- this.tileOriginX_ = undefined;
-
- /**
- * @protected
- * @type {number|undefined}
- */
- this.tileOriginY_ = undefined;
-
- /**
- * @private
- * @type {string}
- */
- this.tileOriginCorner_ = 'tl';
-
- /**
- * @private
- * @type {number|undefined}
- */
- this.maxResolution_ = undefined;
-
- /**
- * @private
- * @type {boolean}
- */
- this.xRight_ = true;
-
- /**
- * @private
- * @type {boolean}
- */
- this.yDown_ = true;
-
- /**
- * @private
- * @type {number|undefined}
- */
- this.numZoomLevels_ = undefined;
-
- /**
- * @protected
- * @type {Array.}
- */
- this.resolutions_ = null;
-
- /**
- * @private
- * @type {ol.TileCache}
- */
- this.cache_ = new ol.TileCache();
-
-};
-
-goog.inherits(ol.layer.TileLayer, ol.layer.Layer);
-
-/**
- * @protected
- * @param {number} x
- * @param {number} y
- * @param {number} z
- * @return {string}
- */
-ol.layer.TileLayer.prototype.getTileUrl = function(x, y, z) {
- // overridden by subclasses
-};
-
-/**
- * @return {string|undefined} The layer URL.
- */
-ol.layer.TileLayer.prototype.getUrl = function() {
- return this.url_;
-};
-
-/**
- * @return {boolean} The tile index increases from left to right.
- */
-ol.layer.TileLayer.prototype.getXRight = function() {
- return this.xRight_;
-};
-
-/**
- * @return {boolean} The tile index increases from top to bottom.
- */
-ol.layer.TileLayer.prototype.getYDown = function() {
- return this.yDown_;
-};
-
-/**
- * @param {boolean} right The tile index increases from left to right.
- */
-ol.layer.TileLayer.prototype.setXRight = function(right) {
- this.xRight_ = right;
-};
-
-/**
- * @param {boolean} down The tile index increases from top to bottom.
- */
-ol.layer.TileLayer.prototype.setYDown = function(down) {
- this.yDown_ = down;
-};
-
-/**
- * Get layer extent. Return null if the layer has no extent
- * and no projection.
- * @return {ol.UnreferencedBounds}
- */
-ol.layer.TileLayer.prototype.getExtent = function() {
- if (!goog.isNull(this.extent_)) {
- return this.extent_;
- }
- if (!goog.isNull(this.projection_)) {
- return this.projection_.getExtent();
- }
- return null;
-};
-
-/**
- * Get tile size.
- * @return {Array.}
- */
-ol.layer.TileLayer.prototype.getTileSize = function() {
- return [this.tileWidth_, this.tileHeight_];
-};
-
-/**
- * Get tile origin.
- * @return {Array.}
- */
-ol.layer.TileLayer.prototype.getTileOrigin = function() {
- if (goog.isDef(this.tileOriginX_) &&
- goog.isDef(this.tileOriginY_)) {
- return [this.tileOriginX_, this.tileOriginY_];
- }
- var errmsg = 'Cannot calculate tile origin; ';
- if (goog.isDef(this.tileOriginCorner_)) {
- var extent = this.getExtent();
- if (!goog.isNull(extent)) {
- var tileOriginX, tileOriginY;
- switch (this.tileOriginCorner_) {
- case "tl":
- tileOriginX = extent.getMinX();
- tileOriginY = extent.getMaxY();
- break;
- case "tr":
- tileOriginX = extent.getMaxX();
- tileOriginY = extent.getMaxY();
- break;
- case "bl":
- tileOriginX = extent.getMinX();
- tileOriginY = extent.getMinY();
- break;
- case "br":
- tileOriginX = extent.getMaxX();
- tileOriginY = extent.getMinY();
- break;
- default:
- errmsg += 'tileOriginCorner value is incorrect.';
- ol.error(errmsg);
- }
- return [tileOriginX, tileOriginY];
- }
- errmsg += 'layer has no extent.';
- ol.error(errmsg);
- }
- errmsg += 'layer has no tileOriginCorner.';
- ol.error(errmsg);
- return null;
-};
-
-/**
- * Get max resolution. Return undefined if the layer has no maxResolution,
- * and no extent from which maxResolution could be derived.
- * @return {number|undefined}
- */
-ol.layer.TileLayer.prototype.getMaxResolution = function() {
- if (!goog.isDef(this.maxResolution_)) {
- var extent = this.getExtent();
- if (!goog.isNull(extent)) {
- this.maxResolution_ = Math.max(
- (extent.getMaxX() - extent.getMinX()) / this.tileWidth_,
- (extent.getMaxY() - extent.getMinY()) / this.tileHeight_);
- }
- }
- return this.maxResolution_;
-};
-
-/**
- * Get the number of the zoom levels.
- * @return {number|undefined}
- */
-ol.layer.TileLayer.prototype.getNumZoomLevels = function() {
- return this.numZoomLevels_;
-};
-
-/**
- * Get layer resolutions. Return null if the layer has no resolutions.
- * @return {Array.}
- */
-ol.layer.TileLayer.prototype.getResolutions = function() {
- if (goog.isNull(this.resolutions_)) {
- var maxResolution = this.getMaxResolution(),
- numZoomLevels = this.getNumZoomLevels();
- if (goog.isDef(maxResolution) && goog.isDef(numZoomLevels)) {
- this.resolutions_ = [];
- for (var i = 0; i < numZoomLevels; i++) {
- this.resolutions_[i] = maxResolution / Math.pow(2, i);
- }
- }
- }
- return this.resolutions_;
-};
-
-/**
- * Set the layer URL.
- * @param {string} url
- */
-ol.layer.TileLayer.prototype.setUrl = function(url) {
- this.url_ = url;
-};
-
-/**
- * Set layer projection.
- * @param {ol.Projection} projection
- */
-ol.layer.TileLayer.prototype.setProjection = function(projection) {
- this.projection_ = projection;
-};
-
-/**
- * Set layer extent.
- * @param {ol.Bounds} extent
- */
-ol.layer.TileLayer.prototype.setExtent = function(extent) {
- this.extent_ = extent;
-};
-
-/**
- * Set tile origin.
- * @param {number} tileOriginX
- * @param {number} tileOriginY
- */
-ol.layer.TileLayer.prototype.setTileOrigin = function(tileOriginX, tileOriginY) {
- this.tileOriginX_ = tileOriginX;
- this.tileOriginY_ = tileOriginY;
-};
-
-/**
- * Set tile origin corner.
- * @param {string} tileOriginCorner
- */
-ol.layer.TileLayer.prototype.setTileOriginCorner = function(tileOriginCorner) {
- this.tileOriginCorner_ = tileOriginCorner;
-};
-
-/**
- * Set maximum resolution.
- * @param {number} maxResolution
- */
-ol.layer.TileLayer.prototype.setMaxResolution = function(maxResolution) {
- this.maxResolution_ = maxResolution;
-};
-
-/**
- * Set the number of zoom levels.
- * @param {number} numZoomLevels
- */
-ol.layer.TileLayer.prototype.setNumZoomLevels = function(numZoomLevels) {
- this.numZoomLevels_ = numZoomLevels;
-};
-
-/**
- * Set resolutions for the layer.
- * @param {Array.} resolutions
- */
-ol.layer.TileLayer.prototype.setResolutions = function(resolutions) {
- this.resolutions_ = resolutions;
-};
-
-/**
- * Get a tile from the cache, or create a tile and add to
- * the cache.
- * @param url {string}
- * @param bounds {ol.Bounds}
- */
-ol.layer.TileLayer.prototype.getTile = function(url, bounds) {
- var tile = this.cache_.get(url);
- if (!goog.isDef(tile)) {
- tile = new this.Tile(url, bounds);
- this.cache_.set(tile.getUrl(), tile);
- }
- return tile;
-};
-
-/**
- * Get a tile from the cache, or create a tile and add to
- * the cache.
- * @param {number} x
- * @param {number} y
- * @param {number} z
- */
-ol.layer.TileLayer.prototype.getTileForXYZ = function(x, y, z) {
- if (!this.validXY(x, y, z)) {
- return null;
- }
- var tileUrl = this.getTileUrl(x, y, z);
- var tile = this.cache_.get(tileUrl);
- if (!goog.isDef(tile)) {
- tile = new this.Tile(tileUrl);
- this.cache_.set(tileUrl, tile);
- }
- return tile;
-};
-
-/**
- * Determine if the tile x/y/z intersects the layer extent. Always
- * return true if the layer has no extent.
- * @param {number} x
- * @param {number} y
- * @param {number} z
- * @return {boolean}
- */
-ol.layer.TileLayer.prototype.validXY = function(x, y, z) {
- var extent = this.getExtent();
- if (goog.isNull(extent)) {
- return true;
- }
- var extentMinX = extent.getMinX(),
- extentMinY = extent.getMinY(),
- extentMaxX = extent.getMaxX(),
- extentMaxY = extent.getMaxY();
- var tileOrigin = this.getTileOrigin(),
- tileOriginX = tileOrigin[0],
- tileOriginY = tileOrigin[1];
- var resolution = this.getResolutions()[z];
- var tileWidth = this.tileWidth_ * resolution,
- tileHeight = this.tileHeight_ * resolution;
- var minX, maxX;
- if (this.xRight_) {
- minX = Math.floor((extentMinX - tileOriginX) / tileWidth);
- maxX = Math.ceil((extentMaxX - tileOriginX) / tileWidth) - 1;
- } else {
- minX = Math.floor((tileOriginX - extentMaxX) / tileWidth);
- maxX = Math.ceil((tileOriginX - extentMinX) / tileWidth) - 1;
- }
- var minY, maxY;
- if (this.yDown_) {
- minY = Math.floor((tileOriginY - extentMaxY) / tileHeight);
- maxY = Math.ceil((tileOriginY - extentMinY) / tileHeight) - 1;
- } else {
- minY = Math.floor((extentMinY - tileOriginY) / tileHeight);
- maxY = Math.ceil((extentMaxY - tileOriginY) / tileHeight) - 1;
- }
- return x >= minX && x <= maxX && y >= minY && y <= maxY;
-};
-
-/**
- * Get data from the layer. This is the layer's main API function.
- * @param {ol.Bounds} bounds
- * @param {number} resolution
- */
-ol.layer.TileLayer.prototype.getData = function(bounds, resolution) {
- var me = this,
- zoomAndRes = me.getZoomAndRes(resolution),
- zoom = zoomAndRes[0];
- resolution = zoomAndRes[1];
-
- // define some values used for the actual tiling
- var boundsMinX = bounds.getMinX(),
- boundsMaxX = bounds.getMaxX(),
- boundsMinY = bounds.getMinY(),
- boundsMaxY = bounds.getMaxY(),
- tileWidth = me.tileWidth_,
- tileHeight = me.tileHeight_,
- tileOrigin = me.getTileOrigin(),
- tileOriginX = tileOrigin[0],
- tileOriginY = tileOrigin[1],
- tileWidthGeo = tileWidth * resolution,
- tileHeightGeo = tileHeight * resolution;
-
- // make sure we don't create tiles outside the layer extent
- var extent = this.getExtent();
- if (extent) {
- boundsMinX = Math.max(boundsMinX, extent.getMinX());
- boundsMaxX = Math.min(boundsMaxX, extent.getMaxX());
- boundsMinY = Math.max(boundsMinY, extent.getMinY());
- boundsMaxY = Math.min(boundsMaxY, extent.getMaxY());
- }
-
- var offsetX = Math.floor(
- (boundsMinX - tileOriginX) / tileWidthGeo),
- offsetY = Math.floor(
- (tileOriginY - boundsMaxY) / tileHeightGeo),
- gridLeft = tileOriginX + tileWidthGeo * offsetX,
- gridTop = tileOriginY - tileHeightGeo * offsetY;
-
- // now tile
- var tiles = [],
- tile,
- url,
- tileBottom, tileRight, tileBounds;
- for (var y=0, tileTop=gridTop; tileTop > boundsMinY;
- ++y, tileTop-=tileHeightGeo) {
- tiles[y] = [];
- tileBottom = tileTop - tileHeightGeo;
- for (var x=0, tileLeft=gridLeft; tileLeft < boundsMaxX;
- ++x, tileLeft+=tileWidthGeo) {
- tileRight = tileLeft + tileWidthGeo;
- tileBounds = new ol.Bounds(tileLeft, tileBottom,
- tileRight, tileTop, this.projection_);
- url = this.getTileUrl(offsetX + x, offsetY + y, zoom);
- tile = this.getTile(url, tileBounds);
- tiles[y][x] = tile;
- }
- }
-
- return new ol.TileSet(tiles, tileWidth, tileHeight, resolution);
-};
-
-/**
- * Get the zoom level (z) and layer resolution for the given resolution.
- * @param {number} resolution
- * @return {Array.}
- */
-ol.layer.TileLayer.prototype.getZoomAndRes = function(resolution) {
- var delta = Number.POSITIVE_INFINITY,
- currentDelta,
- resolutions = this.getResolutions(),
- zoom;
- for (var i=resolutions.length-1; i>=0; --i) {
- currentDelta = Math.abs(resolutions[i] - resolution);
- if (currentDelta > delta) {
- break;
- }
- delta = currentDelta;
- }
- zoom = i + 1;
- return [zoom, resolutions[zoom]];
-};
diff --git a/src/ol/layer/WMS.js b/src/ol/layer/WMS.js
deleted file mode 100644
index d3187183f9..0000000000
--- a/src/ol/layer/WMS.js
+++ /dev/null
@@ -1,74 +0,0 @@
-goog.provide('ol.layer.WMS');
-
-goog.require('goog.Uri');
-goog.require('ol.layer.TileLayer');
-
-/**
- * Class for WMS layers.
- *
- * @export
- * @constructor
- * @extends {ol.layer.TileLayer}
- * @param {string} url The WMS URL.
- * @param {Array.} layers List of layers.
- * @param {string|undefined} format Image format (e.g. "image/jpeg")
- */
-ol.layer.WMS = function(url, layers, format) {
- goog.base(this);
- this.setUrl(url);
-
- /**
- * @private
- * @type {Array.}
- */
- this.layers_ = layers;
-
- /**
- * @private
- * @type {string|undefined}
- */
- this.format_ = format;
-};
-
-goog.inherits(ol.layer.WMS, ol.layer.TileLayer);
-
-/**
- * @const
- * @type {Object}
- */
-ol.layer.WMS.prototype.DEFAULT_PARAMS = {
- "SERVICE": "WMS",
- "VERSION": "1.1.1",
- "REQUEST": "GetMap",
- "STYLES": "",
- "FORMAT": "image/png"
-};
-
-/**
- * @inheritDoc
- */
-ol.layer.WMS.prototype.getTileUrl = function(x, y, z) {
- var tileOrigin = this.getTileOrigin(),
- tileOriginX = tileOrigin[0],
- tileOriginY = tileOrigin[1];
- var resolution = this.getResolutions()[z];
- var tileWidth = this.tileWidth_ * resolution,
- tileHeight = this.tileHeight_ * resolution;
- var minX = tileOriginX + (x * tileWidth),
- maxY = tileOriginY - (y * tileHeight),
- maxX = minX + tileWidth,
- minY = maxY - tileHeight;
-
- var qd = new goog.Uri.QueryData();
- qd.extend(this.DEFAULT_PARAMS);
- qd.set('WIDTH', this.tileWidth_);
- qd.set('HEIGHT', this.tileHeight_);
- qd.set('BBOX', [minX, minY, maxX, maxY].join(','));
- qd.set('LAYERS', [this.layers_].join(','));
- // FIXME this requires a projection in the layer, which should
- // not be required
- qd.set('SRS', this.projection_.getCode());
- var uri = new goog.Uri(this.getUrl());
- uri.setQueryData(qd);
- return uri.toString();
-};
diff --git a/src/ol/layer/XYZ.js b/src/ol/layer/XYZ.js
deleted file mode 100644
index 4a1c16fe86..0000000000
--- a/src/ol/layer/XYZ.js
+++ /dev/null
@@ -1,35 +0,0 @@
-goog.provide('ol.layer.XYZ');
-
-goog.require('ol.layer.TileLayer');
-goog.require('ol.Projection');
-goog.require('ol.TileSet');
-
-/**
- * Class for XYZ layers.
- *
- * @export
- * @constructor
- * @extends {ol.layer.TileLayer}
- * @param {string} url URL template. E.g.
- * http://a.tile.openstreetmap.org/{z}/{x}/{y}.png.
- */
-ol.layer.XYZ = function(url) {
-
- goog.base(this);
-
- this.setUrl(url);
- this.setProjection(new ol.Projection("EPSG:3857"));
- this.setNumZoomLevels(22);
-};
-
-goog.inherits(ol.layer.XYZ, ol.layer.TileLayer);
-
-/**
- * @inheritDoc
- */
-ol.layer.XYZ.prototype.getTileUrl = function(x, y, z) {
- var base = this.getUrl();
- return base.replace('{x}', x + '')
- .replace('{y}', y + '')
- .replace('{z}', z + '');
-};
diff --git a/src/ol/object_test.js b/src/ol/object_test.js
new file mode 100644
index 0000000000..d823851261
--- /dev/null
+++ b/src/ol/object_test.js
@@ -0,0 +1,19 @@
+goog.require('goog.testing.jsunit');
+goog.require('ol');
+goog.require('ol3.Object');
+
+
+function testObject1() {
+ var obj = {k: 1};
+ obj = ol.object(obj);
+ assertTrue(obj instanceof ol3.Object);
+ assertEquals(1, obj.get('k'));
+}
+
+
+function testObject2() {
+ var obj1 = new ol3.Object();
+ var obj2 = ol.object(obj1);
+ assertTrue(obj2 === obj1);
+}
+
diff --git a/src/ol/ol.js b/src/ol/ol.js
new file mode 100644
index 0000000000..e5f55b026d
--- /dev/null
+++ b/src/ol/ol.js
@@ -0,0 +1,168 @@
+goog.provide('ol');
+goog.provide('ol.layer');
+
+goog.require('goog.dom');
+goog.require('ol3.Collection');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Layer');
+goog.require('ol3.Map');
+goog.require('ol3.Object');
+goog.require('ol3.Projection');
+goog.require('ol3.createMap');
+goog.require('ol3.layer.OpenStreetMap');
+
+
+goog.exportSymbol('ol', ol);
+
+
+/**
+ * @typedef {Array|ol3.Collection}
+ */
+ol.Collection;
+
+
+/**
+ * @typedef {Array.|ol3.Coordinate|{x: number, y: number}}
+ */
+ol.Coordinate;
+
+
+/**
+ * @typedef {{center: (ol.Coordinate|undefined),
+ * layers: (ol.Collection|undefined),
+ * renderTo: (Element|string|undefined),
+ * resolution: (number|undefined),
+ * zoom: (number|undefined)}}
+ */
+ol.MapOptions;
+
+
+/**
+ * @typedef {Object|ol3.Object}
+ */
+ol.Object;
+
+
+/**
+ * @typedef {ol3.Projection|string}
+ */
+ol.Projection;
+
+
+/**
+ * @param {ol.Collection} collection Collection.
+ * @return {ol3.Collection} Collection.
+ */
+ol.collection = function(collection) {
+ if (collection instanceof ol3.Collection) {
+ return collection;
+ } else if (goog.isArray(collection)) {
+ var array = /** @type {Array} */ collection;
+ return new ol3.Collection(collection);
+ } else {
+ return null;
+ }
+};
+goog.exportProperty(ol, 'collection', ol.collection);
+
+
+/**
+ * @param {ol.Coordinate} coordinate Coordinate.
+ * @return {ol3.Coordinate} Coordinate.
+ */
+ol.coordinate = function(coordinate) {
+ if (coordinate instanceof ol3.Coordinate) {
+ return coordinate;
+ } else if (goog.isArray(coordinate)) {
+ var array = /** @type {Array.} */ coordinate;
+ return new ol3.Coordinate(array[1], array[0]);
+ } else if (goog.isObject(coordinate)) {
+ var object = /** @type {{x: number, y: number}} */ coordinate;
+ return new ol3.Coordinate(object.x, object.y);
+ } else {
+ return null;
+ }
+};
+goog.exportProperty(ol, 'coordinate', ol.coordinate);
+
+
+goog.exportProperty(ol, 'layer', ol.layer);
+
+
+/**
+ * @return {ol3.Layer} Layer.
+ */
+ol.layer.osm = function() {
+ return new ol3.layer.OpenStreetMap();
+};
+goog.exportProperty(ol.layer, 'osm', ol.layer.osm);
+
+
+/**
+ * @param {ol.MapOptions=} opt_mapOptions Options.
+ * @return {ol3.Map} Map.
+ */
+ol.map = function(opt_mapOptions) {
+ var options = opt_mapOptions || {};
+ var center = ol.coordinate(/** @type {ol.Coordinate} */
+ (goog.object.get(options, 'center', null)));
+ var layers = ol.collection(/** @type {ol.Collection} */
+ (goog.object.get(options, 'layers', null)));
+ var projection = ol.projection(/** @type {ol.Projection} */
+ (goog.object.get(options, 'projection', 'EPSG:3857')));
+ var resolution = /** @type {number|undefined} */
+ goog.object.get(options, 'resolution');
+ if (!goog.isDef(resolution) && goog.object.containsKey(options, 'zoom')) {
+ var zoom = /** @type {number} */ goog.object.get(options, 'zoom');
+ resolution = ol3.Projection.EPSG_3857_HALF_SIZE / (128 << zoom);
+ }
+ var target = goog.dom.getElement(/** @type {Element|string} */
+ (goog.object.get(options, 'renderTo', 'map')));
+ var userProjection = ol.projection(/** @type {ol.Projection} */
+ (goog.object.get(options, 'userProjection', 'EPSG:4326')));
+ var map = ol3.createMap(target, {
+ 'layers': layers,
+ 'projection': projection,
+ 'resolution': resolution,
+ 'userProjection': userProjection
+ });
+ if (!goog.isNull(center)) {
+ map.setUserCenter(center);
+ }
+ return map;
+};
+goog.exportProperty(ol, 'map', ol.map);
+
+
+/**
+ * @param {ol.Object} object Object.
+ * @return {ol3.Object} Object.
+ */
+ol.object = function(object) {
+ if (object instanceof ol3.Object) {
+ return object;
+ } else if (goog.isObject(object)) {
+ var values = /** @type {Object} */ object;
+ return new ol3.Object(values);
+ } else {
+ return null;
+ }
+};
+goog.exportProperty(ol, 'object', ol.object);
+
+
+/**
+ * @param {ol.Projection} projection Projection.
+ * @return {ol3.Projection} Projection.
+ */
+ol.projection = function(projection) {
+ if (projection instanceof ol3.Projection) {
+ return projection;
+ } else if (goog.isString(projection)) {
+ var code = /** @type {string} */ projection;
+ return ol3.Projection.getFromCode(code);
+ } else {
+ return null;
+ }
+};
+goog.exportProperty(ol, 'projection', ol.projection);
diff --git a/src/ol/renderer/Composite.js b/src/ol/renderer/Composite.js
deleted file mode 100644
index 62105865bc..0000000000
--- a/src/ol/renderer/Composite.js
+++ /dev/null
@@ -1,314 +0,0 @@
-goog.provide('ol.renderer.Composite');
-
-goog.require('ol.renderer.MapRenderer');
-goog.require('ol.renderer.LayerRenderer');
-goog.require('ol.layer.Layer');
-goog.require('ol.Loc');
-
-goog.require('goog.array');
-goog.require('goog.dom');
-goog.require('goog.style');
-goog.require('goog.math.Coordinate');
-
-/**
- * @constructor
- * @param {!Element} container
- * @extends {ol.renderer.MapRenderer}
- */
-ol.renderer.Composite = function(container) {
-
- goog.base(this, container);
-
- /**
- * @type {Array.}
- * @private
- */
- this.renderers_ = [];
-
- /**
- * Pixel buffer for renderer container.
- *
- * @type {number}
- * @private
- */
- this.buffer_ = 128;
-
- /**
- * @type {Element}
- * @private
- */
- this.target_ = null;
-
- /**
- * The current top left corner location of the target element (map coords).
- *
- * @type {ol.Loc}
- * @private
- */
- this.targetOrigin_ = null;
-
- /**
- * The pixel offset of the target element with respect to its container.
- *
- * @type {goog.math.Coordinate}
- * @private
- */
- this.targetOffset_ = null;
-
- /**
- * @type {Object}
- * @private
- */
- this.layerContainers_ = {};
-
-};
-goog.inherits(ol.renderer.Composite, ol.renderer.MapRenderer);
-
-/**
- * @param {Array.} layers
- * @param {ol.Loc} center
- * @param {number} resolution
- * @param {boolean} animate
- */
-ol.renderer.Composite.prototype.draw = function(layers, center, resolution, animate) {
- if (goog.isNull(this.target_)) {
- // first rendering
- this.createTarget_(center, resolution);
- }
-
- // TODO: deal with layer order and removal
-
- if (this.renderedResolution_) {
- if (resolution !== this.renderedResolution_) {
- // TODO: apply transition to old target
- this.resetTarget_(center, resolution);
- }
- }
- this.renderedResolution_ = resolution;
-
- // shift target element to account for center change
- if (this.renderedCenter_) {
- this.shiftTarget_(center, resolution);
- }
- this.renderedCenter_ = center;
-
- // update each layer renderer
- var renderer;
- for (var i=0, ii=layers.length; i}
- */
-ol.renderer.Composite.preferredRenderers = ["svg", "canvas", "vml"];
-
-/**
- * @param {ol.layer.Layer} layer
- * @returns {Function}
- */
-ol.renderer.Composite.prototype.pickRendererType = function(layer) {
- // maps candidate renderer types to candidate renderers
- var types = {};
-
- function picker(Candidate) {
- var supports = Candidate['isSupported']() && Candidate['canRender'](layer);
- if (supports) {
- types[Candidate['getType']()] = Candidate;
- }
- return supports;
- }
- var Candidates = goog.array.filter(ol.renderer.Composite.registry_, picker);
-
- // check to see if any preferred renderers are available
- var preferences = ol.renderer.Composite.preferredRenderers;
-
- var Renderer;
- for (var i=0, ii=preferences.length; i}
- * @private
- */
-ol.renderer.Composite.registry_ = [];
-
-/**
- * @param {Function} Renderer
- */
-ol.renderer.Composite.register = function(Renderer) {
- ol.renderer.Composite.registry_.push(Renderer);
-};
-
-/**
- * return {string}
- */
-ol.renderer.Composite.getType = function() {
- // TODO: revisit
- return "composite";
-};
-
-/**
- * TODO: determine if there is a better way to register these renderers
- *
- * @export
- * @return {boolean}
- */
-ol.renderer.Composite.isSupported = function() {
- return true;
-};
-
-ol.renderer.MapRenderer.register(ol.renderer.Composite);
diff --git a/src/ol/renderer/LayerRenderer.js b/src/ol/renderer/LayerRenderer.js
deleted file mode 100644
index 1cf55f0832..0000000000
--- a/src/ol/renderer/LayerRenderer.js
+++ /dev/null
@@ -1,98 +0,0 @@
-goog.provide('ol.renderer.LayerRenderer');
-
-goog.require('goog.math.Coordinate');
-goog.require('goog.math.Size');
-
-/**
- * A single layer renderer that will be created by the composite map renderer.
- *
- * @constructor
- * @param {!Element} container
- * @param {!ol.layer.Layer} layer
- */
-ol.renderer.LayerRenderer = function(container, layer) {
-
- /**
- * @type {!Element}
- * @protected
- */
- this.container_ = container;
-
- /**
- * @type {goog.math.Size}
- * @private
- */
- this.containerSize_ = null;
-
- /**
- * Location of the top-left corner of the renderer container in map coords.
- *
- * @type {ol.Loc}
- * @protected
- */
- this.containerOrigin_ = null;
-
- /**
- * @type {!ol.layer.Layer}
- * @protected
- */
- this.layer_ = layer;
-
-};
-
-/**
- * @return {goog.math.Size}
- * @protected
- */
-ol.renderer.LayerRenderer.prototype.getContainerSize = function() {
- // TODO: listen for resize and set this.constainerSize_ null
- // https://github.com/openlayers/ol3/issues/2
- if (goog.isNull(this.containerSize_)) {
- this.containerSize_ = goog.style.getSize(this.container_);
- }
- return this.containerSize_;
-};
-
-/**
- * Set the location of the top-left corner of the renderer container.
- *
- * @param {ol.Loc} origin The container origin.
- */
-ol.renderer.LayerRenderer.prototype.setContainerOrigin = function(origin) {
- this.containerOrigin_ = origin;
-};
-
-/**
- * Get layer being rendered.
- *
- * @returns {!ol.layer.Layer}
- */
-ol.renderer.LayerRenderer.prototype.getLayer = function() {
- return this.layer_;
-};
-
-/**
- * Get an identifying string for this renderer.
- *
- * @returns {string|undefined}
- */
-ol.renderer.LayerRenderer.prototype.getType = function() {};
-
-/**
- * Determine if this renderer is supported in the given environment.
- *
- * @returns {boolean}
- */
-ol.renderer.LayerRenderer.isSupported = function() {
- return false;
-};
-
-/**
- * Determine if this renderer is capable of renderering the given layer.
- *
- * @param {ol.layer.Layer} layer
- * @returns {boolean}
- */
-ol.renderer.LayerRenderer.canRender = function(layer) {
- return false;
-};
diff --git a/src/ol/renderer/MapRenderer.js b/src/ol/renderer/MapRenderer.js
deleted file mode 100644
index 5fc37f62b9..0000000000
--- a/src/ol/renderer/MapRenderer.js
+++ /dev/null
@@ -1,110 +0,0 @@
-goog.provide('ol.renderer.MapRenderer');
-
-goog.require('goog.style');
-goog.require('goog.math.Size');
-
-/**
- * @constructor
- * @param {!Element} container
- */
-ol.renderer.MapRenderer = function(container) {
-
- /**
- * @type !Element
- * @protected
- */
- this.container_ = container;
-
- /**
- * @type {goog.math.Size}
- * @private
- */
- this.containerSize_ = null;
-
- /**
- * @type {ol.Loc}
- * @protected
- */
- this.renderedCenter_;
-
- /**
- * @type {number}
- * @protected
- */
- this.renderedResolution_;
-
-};
-
-/**
- * @return {goog.math.Size}
- * @protected
- */
-ol.renderer.MapRenderer.prototype.getContainerSize = function() {
- // TODO: listen for resize and set this.constainerSize_ null
- // https://github.com/openlayers/ol3/issues/2
- if (goog.isNull(this.containerSize_)) {
- this.containerSize_ = goog.style.getSize(this.container_);
- }
- return this.containerSize_;
-};
-
-
-/**
- * @param {Array.} layers
- * @param {ol.Loc} center
- * @param {number} resolution
- * @param {boolean} animate
- */
-ol.renderer.MapRenderer.prototype.draw = function(layers, center, resolution, animate) {
-};
-
-/**
- * @return {number} The rendered resolution.
- */
-ol.renderer.MapRenderer.prototype.getResolution = function() {
- return this.renderedResolution_;
-};
-
-/**
- * TODO: determine a closure friendly way to register map renderers.
- * @type {Array}
- * @private
- */
-ol.renderer.MapRenderer.registry_ = [];
-
-/**
- * @param {Function} Renderer
- */
-ol.renderer.MapRenderer.register = function(Renderer) {
- ol.renderer.MapRenderer.registry_.push(Renderer);
-};
-
-/**
- * @param {Array.} preferences List of preferred renderer types.
- * @returns {Function} A renderer constructor.
- */
-ol.renderer.MapRenderer.pickRendererType = function(preferences) {
- // map of candidate renderer types to candidate renderers
- var types = {};
-
- function picker(Candidate) {
- var supports = Candidate.isSupported();
- if (supports) {
- types[Candidate.getType()] = Candidate;
- }
- return supports;
- }
- var Candidates = goog.array.filter(ol.renderer.MapRenderer.registry_, picker);
-
- // check to see if any preferred renderers are available
- var Renderer;
- for (var i=0, ii=preferences.length; i}
- */
- this.layerResolutions_ = layer.getResolutions();
-
- /**
- * @type {Array.}
- */
- this.tileOrigin_ = layer.getTileOrigin();
-
- /**
- * @type {Array.}
- */
- this.tileSize_ = layer.getTileSize();
-
- /**
- * @type {boolean}
- */
- this.xRight_ = layer.getXRight();
-
- /**
- * @type {boolean}
- */
- this.yDown_ = layer.getYDown();
-
- /**
- * @type {number|undefined}
- * @private
- */
- this.renderedResolution_ = undefined;
-
- /**
- * @type {number|undefined}
- * @private
- */
- this.renderedZ_ = undefined;
-
-};
-goog.inherits(ol.renderer.TileLayerRenderer, ol.renderer.LayerRenderer);
-
-/**
- * Render the layer.
- *
- * @param {!ol.Loc} center
- * @param {number} resolution
- */
-ol.renderer.TileLayerRenderer.prototype.draw = function(center, resolution) {
- if (resolution !== this.renderedResolution_) {
- this.changeResolution_(resolution);
- }
- var z = this.renderedZ_;
- var tileOrigin = this.tileOrigin_;
-
- var offset = this.getTileOffset_();
- var tileBox = this.getTileBox_(center, resolution);
-
- var fragment = document.createDocumentFragment();
- var ijz, key, tile, xyz, box, img, newTiles = false;
- for (var i=tileBox.left; i} ijz
- * @param {number} resolution
- * @return {goog.math.Box}
- */
-ol.renderer.TileLayerRenderer.prototype.getTilePixelBox_ = function(ijz, resolution) {
- var tileResolution = this.layerResolutions_[ijz[2]];
- var scale = resolution / tileResolution;
- var tileSize = this.tileSize_;
-
- // desired tile size (in fractional pixels)
- var fpxTileWidth = tileSize[0] / scale;
- var fpxTileHeight = tileSize[1] / scale;
-
- var col = ijz[0];
- var left = Math.round(col * fpxTileWidth); // inclusive
- var right = Math.round((col + 1) * fpxTileWidth); // exclusive
-
- var row = ijz[1];
- var top = Math.round(row * fpxTileHeight); // inclusive
- var bottom = Math.round((row + 1) * fpxTileWidth); // exclusive
-
- return new goog.math.Box(top, right, bottom, left);
-};
-
-/**
- * @param {ol.Loc} loc
- * @param {number} resolution
- * @return {goog.math.Coordinate}
- */
-ol.renderer.TileLayerRenderer.prototype.getNormalizedTileCoord_ = function(loc, resolution) {
- var tileOrigin = this.tileOrigin_;
- var tileSize = this.tileSize_;
- var pair = this.getPreferredResAndZ_(resolution);
- var tileResolution = pair[0];
- var z = pair[1];
- var scale = resolution / tileResolution;
-
- // offset from tile origin in pixel space
- var dx = Math.floor((loc.getX() - tileOrigin[0]) / resolution);
- var dy = Math.floor((tileOrigin[1] - loc.getY()) / resolution);
-
- // desired tile size (in fractional pixels)
- var fpxTileWidth = tileSize[0] / scale;
- var fpxTileHeight = tileSize[1] / scale;
-
- // determine normalized col number (0 based, ascending right)
- var col = Math.floor(dx / fpxTileWidth);
- // determine normalized row number (0 based, ascending down)
- var row = Math.floor(dy / fpxTileHeight);
-
- var box = this.getTilePixelBox_([col, row, z], resolution);
-
- // adjust col to allow for stretched tiles
- if (dx < box.left) {
- col -= 1;
- } else if (dx >= box.right) {
- col += 1;
- }
-
- // adjust row to allow for stretched tiles
- if (dy < box.top) {
- row -= 1;
- } else if (dy >= box.bottom) {
- row += 1;
- }
-
- return new goog.math.Coordinate(col, row);
-};
-
-/**
- * @param {number} resolution
- * @return {Array.}
- */
-ol.renderer.TileLayerRenderer.prototype.getPreferredResAndZ_ = (function() {
- var cache = {};
- return function(resolution) {
- if (resolution in cache) {
- return cache[resolution];
- }
- var minDiff = Number.POSITIVE_INFINITY;
- var candidate, diff, z, r;
- for (var i=0, ii=this.layerResolutions_.length; i} ijz
- * @return {Array.}
- */
-ol.renderer.TileLayerRenderer.prototype.getTileCoordsFromNormalizedCoords_ = function(ijz) {
- return [
- this.xRight_ ? ijz[0] : -ijz[0] - 1,
- this.yDown_ ? ijz[1] : -ijz[1] - 1,
- ijz[2]
- ];
-};
-
-/**
- * @param {ol.Loc} center
- * @param {number} resolution
- * @return {goog.math.Box}
- */
-ol.renderer.TileLayerRenderer.prototype.getTileBox_ = function(center, resolution) {
- var size = this.getContainerSize();
- var halfWidth = size.width / 2;
- var halfHeight = size.height / 2;
-
- var leftTop = new ol.Loc(
- center.getX() - (resolution * halfWidth),
- center.getY() + (resolution * halfHeight));
-
- var rightBottom = new ol.Loc(
- center.getX() + (resolution * halfWidth),
- center.getY() - (resolution * halfHeight));
-
- var ltCoord = this.getNormalizedTileCoord_(leftTop, resolution);
- var rbCoord = this.getNormalizedTileCoord_(rightBottom, resolution);
-
- // right and bottom are treated as excluded, so we increment for the box
- rbCoord.x += 1;
- rbCoord.y += 1;
-
- return goog.math.Box.boundingBox(ltCoord, rbCoord);
-};
-
-/**
- * Get rid of tiles outside the rendered extent.
- */
-ol.renderer.TileLayerRenderer.prototype.removeInvisibleTiles_ = function() {
- var index, prune, i, j, z, tile;
- var box = this.renderedTileBox_;
- for (var ijz in this.renderedTiles_) {
- index = ijz.split(",");
- i = +index[0];
- j = +index[1];
- z = +index[2];
- prune = this.renderedZ_ !== z ||
- i < box.left || // beyond on the left side
- i >= box.right || // beyond on the right side
- j < box.top || // above
- j >= box.bottom; // below
- if (prune) {
- tile = this.renderedTiles_[ijz];
- delete this.renderedTiles_[ijz];
- this.container_.removeChild(tile.getImg());
- }
- }
-};
-
-/**
- * Deal with changes in resolution.
- * TODO: implement the animation
- *
- * @param {number} resolution New resolution.
- */
-ol.renderer.TileLayerRenderer.prototype.changeResolution_ = function(resolution) {
- var pair = this.getPreferredResAndZ_(resolution);
- this.renderedZ_ = pair[1];
- this.renderedResolution_ = resolution;
- this.renderedTiles_ = {};
- goog.dom.removeChildren(this.container_);
-};
-
-
-/**
- * Get an identifying string for this renderer.
- *
- * @export
- * @returns {string}
- */
-ol.renderer.TileLayerRenderer.getType = function() {
- // TODO: revisit
- return "tile";
-};
-
-/**
- * Determine if this renderer type is supported in this environment.
- *
- * @export
- * @return {boolean} This renderer is supported.
- */
-ol.renderer.TileLayerRenderer.isSupported = function() {
- return true;
-};
-
-/**
- * Determine if this renderer can render the given layer.
- *
- * @export
- * @param {ol.layer.Layer} layer The candidate layer.
- * @return {boolean} This renderer is capable of rendering the layer.
- */
-ol.renderer.TileLayerRenderer.canRender = function(layer) {
- return layer instanceof ol.layer.TileLayer;
-};
-
-ol.renderer.Composite.register(ol.renderer.TileLayerRenderer);
diff --git a/src/ol/renderer/WebGL.js b/src/ol/renderer/WebGL.js
deleted file mode 100644
index 53b3ba04aa..0000000000
--- a/src/ol/renderer/WebGL.js
+++ /dev/null
@@ -1,276 +0,0 @@
-/**
- * @fileoverview WebGL based MapRenderer drawing all the supplied layers in OpenGL
- */
-
-goog.provide('ol.renderer.WebGL');
-
-goog.require('ol.renderer.MapRenderer');
-goog.require('ol.layer.Layer');
-goog.require('ol.Loc');
-
-goog.require('goog.events');
-goog.require('goog.array');
-goog.require('goog.asserts');
-goog.require('goog.vec.Mat4');
-goog.require('goog.webgl');
-
-/**
- * Initialization of the native WebGL renderer (canvas, context, layers)
- * @constructor
- * @param {!Element} container
- * @extends {ol.renderer.MapRenderer}
- */
-ol.renderer.WebGL = function(container) {
-
- /**
- * @private
- * @type {!Element}
- */
- this.canvas_ = goog.dom.createDom('canvas', 'ol-renderer-webgl-canvas'); // Suppose to have: style: 'width:100%;height:100%;'
-
- /**
- * @private
- * @type {WebGLRenderingContext}
- */
- this.gl_ = (this.canvas_.getContext('experimental-webgl', {
- 'alpha': false,
- 'depth': false,
- 'antialias': true,
- 'stencil': false,
- 'preserveDrawingBuffer': false
- }));
- goog.asserts.assert(!goog.isNull(this.gl_), "The WebGL is not supported on your browser. Check http://get.webgl.org/");
-
- goog.dom.append(container, this.canvas_);
-
- goog.base(this, container);
-
- /**
- * @private
- * @type {Object.}
- */
- this.textureCache_ = {};
-
- var gl = this.gl_;
-
- var clearColor = [0, 0, 0]; // hardcoded background color
- gl.clearColor(clearColor[0], clearColor[1], clearColor[2], 1);
- gl.disable(goog.webgl.DEPTH_TEST);
- gl.disable(goog.webgl.SCISSOR_TEST);
- gl.disable(goog.webgl.CULL_FACE);
-
- var fragmentShader = gl.createShader(goog.webgl.FRAGMENT_SHADER);
- gl.shaderSource(fragmentShader, [
- 'precision mediump float;',
- '',
- 'uniform sampler2D uTexture;',
- '',
- 'varying vec2 vTexCoord;',
- '',
- 'void main(void) {',
- ' gl_FragColor = vec4(vec3(texture2D(uTexture, vTexCoord)), 1.);',
- '}'
- ].join('\n'));
- gl.compileShader(fragmentShader);
- if (!gl.getShaderParameter(fragmentShader, goog.webgl.COMPILE_STATUS)) {
- window.console.log(gl.getShaderInfoLog(fragmentShader));
- goog.asserts.assert(gl.getShaderParameter(fragmentShader, goog.webgl.COMPILE_STATUS));
- }
-
- var vertexShader = gl.createShader(goog.webgl.VERTEX_SHADER);
- gl.shaderSource(vertexShader, [
- 'attribute vec2 aPosition;',
- 'attribute vec2 aTexCoord;',
- '',
- 'uniform mat4 uMVPMatrix;',
- '',
- 'varying vec2 vTexCoord;',
- '',
- 'void main(void) {',
- ' gl_Position = uMVPMatrix * vec4(aPosition, 0.0, 1.0);',
- ' vTexCoord = aTexCoord;',
- '}'
- ].join('\n'));
- if (!gl.getShaderParameter(vertexShader, goog.webgl.COMPILE_STATUS)) {
- window.console.log(gl.getShaderInfoLog(vertexShader));
- goog.asserts.assert(gl.getShaderParameter(vertexShader, goog.webgl.COMPILE_STATUS));
- }
-
- var program = gl.createProgram();
- gl.attachShader(program, fragmentShader);
- gl.attachShader(program, vertexShader);
- gl.linkProgram(program);
- if (!gl.getProgramParameter(program, goog.webgl.LINK_STATUS)) {
- window.console.log(gl.getProgramInfoLog(program));
- goog.asserts.assert(gl.getProgramParameter(program, goog.webgl.LINK_STATUS));
- }
-
- this.mvpMatrixLocation_ = gl.getUniformLocation(program, 'uMVPMatrix');
- this.textureLocation_ = gl.getUniformLocation(program, 'uTexture');
-
- var texCoordBuffer = gl.createBuffer();
- gl.bindBuffer(goog.webgl.ARRAY_BUFFER, texCoordBuffer);
- gl.bufferData(goog.webgl.ARRAY_BUFFER, new Float32Array([0, 1, 1, 1, 0, 0, 1, 0]), goog.webgl.STATIC_DRAW);
- var texCoordLocation = gl.getAttributeLocation(program, 'aTexCoord');
- gl.enableVertexAttribArray(texCoordLocation);
- gl.vertexAttribPointer(texCoordLocation, 2, goog.webgl.FLOAT, false, 0, 0);
- gl.bindBuffer(goog.webgl.ARRAY_BUFFER, null);
-
- this.positionLocation_ = gl.getAttributeLocation(program, 'aPosition');
- gl.enableVertexAttribArray(this.positionLocation_);
- this.positionBuffer_ = gl.createBuffer();
-
-};
-
-goog.inherits(ol.renderer.WebGL, ol.renderer.MapRenderer);
-
-
-/**
- * Determine if this renderer type is supported in this environment.
- * A static method.
- * @returns {boolean} This renderer is supported.
- */
-ol.renderer.WebGL.isSupported = function() {
- return !goog.isNull( goog.dom.createDom('canvas').getContext('experimental-webgl') );
-};
-
-
-/**
- * @param {ol.Tile} tile Tile.
- * @protected
- */
-ol.renderer.WebGL.prototype.bindTexture = function(tile) {
- var gl = this.gl_;
- var url = tile.getUrl();
- if (url in this.textureCache_) {
- gl.bindTexture(gl.TEXTURE_2D, this.textureCache_[url]);
- } else {
- 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, tile.getImg());
- gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, goog.webgl.LINEAR);
- gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER, goog.webgl.LINEAR);
- this.textureCache_[url] = texture;
- }
-};
-
-
-/**
- * @param {ol.Tile} tile Tile.
- * @protected
- */
-ol.renderer.WebGL.prototype.handleTileLoad = function(tile) {
- this.redraw();
-};
-
-
-/**
- * @param {ol.Tile} tile Tile.
- * @protected
- */
-ol.renderer.WebGL.prototype.handleTileDestroy = function(tile) {
- var gl = this.gl_;
- var url = tile.getUrl();
- if (url in this.textureCache_) {
- gl.deleteTexture(this.textureCache_[url]);
- delete this.textureCache_[url];
- }
-};
-
-
-/**
- * @inheritDoc
- */
-ol.renderer.WebGL.prototype.draw = function(layers, center, resolution, animate) {
-
- var gl = this.gl_;
-
- var width = this.canvas_.width;
- var height = this.canvas_.height;
-
- var bounds = new ol.Bounds(
- center.getX() - width * resolution / 2,
- center.getY() - height * resolution / 2,
- center.getX() + width * resolution / 2,
- center.getY() + height * resolution / 2,
- center.getProjection());
-
- /** @type {goog.vec.Mat4.Type} */
- var cameraMatrix;
- goog.vec.Mat4.makeIdentity(cameraMatrix);
- goog.vec.Mat4.scale(cameraMatrix, resolution, resolution, 1);
- goog.vec.Mat4.translate(cameraMatrix, -center.getX(), -center.getY(), 0);
-
- /** @type {goog.vec.Mat4.Type} */
- var positionToViewportMatrix;
- goog.vec.Mat4.makeIdentity(positionToViewportMatrix);
- goog.vec.Mat4.scale(positionToViewportMatrix, 1 / width, 1 / height, 1);
- goog.vec.Mat4.multMat(positionToViewportMatrix, cameraMatrix, positionToViewportMatrix);
-
- /** @type {goog.vec.Mat4.Type} */
- var viewportToPositionMatrix;
- var inverted = goog.vec.Mat4.invert(positionToViewportMatrix, viewportToPositionMatrix);
- goog.asserts.assert(inverted);
-
- /** @type {goog.vec.Mat4.Type} */
- var targetPixelToPositionMatrix;
- goog.vec.Mat4.makeIdentity(targetPixelToPositionMatrix);
- goog.vec.Mat4.translate(targetPixelToPositionMatrix, -1, 1, 0);
- goog.vec.Mat4.scale(targetPixelToPositionMatrix, 2 / width, -2 / height, 1);
- goog.vec.Mat4.multMat(viewportToPositionMatrix, targetPixelToPositionMatrix, targetPixelToPositionMatrix);
-
- gl.clear(goog.webgl.COLOR_BUFFER_BIT);
- gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.positionBuffer_);
- gl.uniform1i(this.textureLocation_, 0);
- gl.uniformMatrix4fv(this.positionLocation_, false, positionToViewportMatrix);
-
- goog.array.forEach(layers, function(layer) {
- if (!(layer instanceof ol.layer.TileLayer)) {
- return;
- }
- var tileLayer = /** @type {ol.layer.TileLayer} */ (layer);
- var tileSet = layer.getData(bounds, resolution);
- var tiles = tileSet.getTiles();
- var i, j, row, tile, tileBounds, positions, texture;
- for (i = 0; i < tiles.length; ++i) {
- row = tiles[i];
- for (j = 0; j < row.length; ++j) {
- tile = row[j];
- if (!tile.isLoaded()) {
- if (!tile.isLoading()) {
- goog.events.listen(tile, 'load', this.handleTileLoad,
- undefined, this);
- goog.events.listen(tile, 'destroy', this.handleTileDestroy,
- undefined, this);
- tile.load();
- }
- continue;
- }
- tileBounds = tile.getBounds();
- positions = [
- tileBounds.getMinX(), tileBounds.getMinY(),
- tileBounds.getMaxX(), tileBounds.getMinY(),
- tileBounds.getMinX(), tileBounds.getMaxY(),
- tileBounds.getMaxX(), tileBounds.getMaxY()
- ];
- gl.bufferData(goog.webgl.ARRAY_BUFFER, new Float32Array(positions), goog.webgl.DYNAMIC_DRAW);
- gl.vertexAttribPointer(this.positionLocation_, 2, goog.webgl.FLOAT, false, 0, 0);
- this.bindTexture(tile);
- gl.drawArrays(goog.webgl.TRIANGLES, 0, 4);
- }
- }
- }, this);
-
- this.renderedLayers_ = layers;
- this.renderedCenter_ = center;
- this.renderedResolution_ = resolution;
- this.renderedAnimate_ = animate;
-
-};
-
-
-/**
- */
-ol.renderer.WebGL.prototype.redraw = function() {
- this.draw(this.renderedLayers_, this.renderedCenter_, this.renderedResolution_, this.renderedAnimate_);
-};
diff --git a/src/ol3/base/array.js b/src/ol3/base/array.js
new file mode 100644
index 0000000000..a7259991e7
--- /dev/null
+++ b/src/ol3/base/array.js
@@ -0,0 +1,59 @@
+goog.provide('ol3.array');
+
+goog.require('goog.array');
+
+
+/**
+ * @param {Array.} arr Array.
+ * @param {number} target Target.
+ * @return {number} Index.
+ */
+ol3.array.binaryFindNearest = function(arr, target) {
+ var index = goog.array.binarySearch(arr, target, function(a, b) {
+ return b - a;
+ });
+ if (index >= 0) {
+ return index;
+ } else if (index == -1) {
+ return 0;
+ } else if (index == -arr.length - 1) {
+ return arr.length - 1;
+ } else {
+ var left = -index - 2;
+ var right = -index - 1;
+ if (arr[left] - target < target - arr[right]) {
+ return left;
+ } else {
+ return right;
+ }
+ }
+};
+
+
+/**
+ * @param {Array.} arr Array.
+ * @param {number} target Target.
+ * @return {number} Index.
+ */
+ol3.array.linearFindNearest = function(arr, target) {
+ var n = arr.length;
+ if (arr[0] <= target) {
+ return 0;
+ } else if (target <= arr[n - 1]) {
+ return n - 1;
+ } else {
+ var i;
+ for (i = 1; i < n; ++i) {
+ if (arr[i] == target) {
+ return i;
+ } else if (arr[i] < target) {
+ if (arr[i - 1] - target < target - arr[i]) {
+ return i - 1;
+ } else {
+ return i;
+ }
+ }
+ }
+ return n - 1;
+ }
+};
diff --git a/src/ol3/base/array_test.js b/src/ol3/base/array_test.js
new file mode 100644
index 0000000000..2b700aecae
--- /dev/null
+++ b/src/ol3/base/array_test.js
@@ -0,0 +1,46 @@
+goog.require('goog.testing.jsunit');
+goog.require('ol3.array');
+
+
+function testBinaryFindNearest() {
+ var arr = [1000, 500, 100];
+
+ assertEquals(0, ol3.array.binaryFindNearest(arr, 10000));
+ assertEquals(0, ol3.array.binaryFindNearest(arr, 1000));
+ assertEquals(0, ol3.array.binaryFindNearest(arr, 900));
+
+ assertEquals(1, ol3.array.binaryFindNearest(arr, 750));
+
+ assertEquals(1, ol3.array.binaryFindNearest(arr, 550));
+ assertEquals(1, ol3.array.binaryFindNearest(arr, 500));
+ assertEquals(1, ol3.array.binaryFindNearest(arr, 450));
+
+ assertEquals(2, ol3.array.binaryFindNearest(arr, 300));
+
+ assertEquals(2, ol3.array.binaryFindNearest(arr, 200));
+ assertEquals(2, ol3.array.binaryFindNearest(arr, 100));
+ assertEquals(2, ol3.array.binaryFindNearest(arr, 50));
+
+}
+
+
+function testLinearFindNearest() {
+ var arr = [1000, 500, 100];
+
+ assertEquals(0, ol3.array.linearFindNearest(arr, 10000));
+ assertEquals(0, ol3.array.linearFindNearest(arr, 1000));
+ assertEquals(0, ol3.array.linearFindNearest(arr, 900));
+
+ assertEquals(1, ol3.array.linearFindNearest(arr, 750));
+
+ assertEquals(1, ol3.array.linearFindNearest(arr, 550));
+ assertEquals(1, ol3.array.linearFindNearest(arr, 500));
+ assertEquals(1, ol3.array.linearFindNearest(arr, 450));
+
+ assertEquals(2, ol3.array.linearFindNearest(arr, 300));
+
+ assertEquals(2, ol3.array.linearFindNearest(arr, 200));
+ assertEquals(2, ol3.array.linearFindNearest(arr, 100));
+ assertEquals(2, ol3.array.linearFindNearest(arr, 50));
+
+}
diff --git a/src/ol3/base/attribution.js b/src/ol3/base/attribution.js
new file mode 100644
index 0000000000..a4723c076b
--- /dev/null
+++ b/src/ol3/base/attribution.js
@@ -0,0 +1,58 @@
+goog.provide('ol3.Attribution');
+
+goog.require('ol3.CoverageArea');
+goog.require('ol3.Projection');
+
+
+
+/**
+ * @constructor
+ * @param {string} html HTML.
+ * @param {Array.=} opt_coverageAreas Coverage areas.
+ * @param {ol3.Projection=} opt_projection Projection.
+ */
+ol3.Attribution = function(html, opt_coverageAreas, opt_projection) {
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.html_ = html;
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.coverageAreas_ = opt_coverageAreas || null;
+
+ /**
+ * @private
+ * @type {ol3.Projection}
+ */
+ this.projection_ = opt_projection || null;
+
+};
+
+
+/**
+ * @return {Array.} Coverage areas.
+ */
+ol3.Attribution.prototype.getCoverageAreas = function() {
+ return this.coverageAreas_;
+};
+
+
+/**
+ * @return {string} HTML.
+ */
+ol3.Attribution.prototype.getHtml = function() {
+ return this.html_;
+};
+
+
+/**
+ * @return {ol3.Projection} Projection.
+ */
+ol3.Attribution.prototype.getProjection = function() {
+ return this.projection_;
+};
diff --git a/src/ol3/base/collection.js b/src/ol3/base/collection.js
new file mode 100644
index 0000000000..4ee3b4c408
--- /dev/null
+++ b/src/ol3/base/collection.js
@@ -0,0 +1,215 @@
+
+/**
+ * @fileoverview An implementation of Google Maps' MVCArray.
+ * @see https://developers.google.com/maps/documentation/javascript/reference
+ */
+
+goog.provide('ol3.Collection');
+goog.provide('ol3.CollectionEvent');
+goog.provide('ol3.CollectionEventType');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.events.Event');
+goog.require('ol3.Object');
+
+
+/**
+ * @enum {string}
+ */
+ol3.CollectionEventType = {
+ ADD: 'add',
+ INSERT_AT: 'insert_at',
+ REMOVE: 'remove',
+ REMOVE_AT: 'remove_at',
+ SET_AT: 'set_at'
+};
+
+
+
+/**
+ * @constructor
+ * @extends {goog.events.Event}
+ * @param {ol3.CollectionEventType} type Type.
+ * @param {*=} opt_elem Element.
+ * @param {number=} opt_index Index.
+ * @param {*=} opt_prev Value.
+ * @param {Object=} opt_target Target.
+ */
+ol3.CollectionEvent =
+ function(type, opt_elem, opt_index, opt_prev, opt_target) {
+
+ goog.base(this, type, opt_target);
+
+ /**
+ * @type {*}
+ */
+ this.elem = opt_elem;
+
+ /**
+ * @type {number|undefined}
+ */
+ this.index = opt_index;
+
+ /**
+ * @type {*}
+ */
+ this.prev = opt_prev;
+
+};
+goog.inherits(ol3.CollectionEvent, goog.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol3.CollectionProperty = {
+ LENGTH: 'length'
+};
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Object}
+ * @param {Array=} opt_array Array.
+ */
+ol3.Collection = function(opt_array) {
+
+ goog.base(this);
+
+ /**
+ * @private
+ * @type {Array}
+ */
+ this.array_ = opt_array || [];
+
+ this.updateLength_();
+
+};
+goog.inherits(ol3.Collection, ol3.Object);
+
+
+/**
+ */
+ol3.Collection.prototype.clear = function() {
+ while (this[ol3.CollectionProperty.LENGTH]) {
+ this.pop();
+ }
+};
+
+
+/**
+ * @param {Function} f Function.
+ * @param {Object=} opt_obj Object.
+ */
+ol3.Collection.prototype.forEach = function(f, opt_obj) {
+ goog.array.forEach(this.array_, f, opt_obj);
+};
+
+
+/**
+ * @return {Array} Array.
+ */
+ol3.Collection.prototype.getArray = function() {
+ return this.array_;
+};
+
+
+/**
+ * @param {number} index Index.
+ * @return {*} Element.
+ */
+ol3.Collection.prototype.getAt = function(index) {
+ return this.array_[index];
+};
+
+
+/**
+ * @return {number} Length.
+ */
+ol3.Collection.prototype.getLength = function() {
+ return /** @type {number} */ this.get(ol3.CollectionProperty.LENGTH);
+};
+
+
+/**
+ * @param {number} index Index.
+ * @param {*} elem Element.
+ */
+ol3.Collection.prototype.insertAt = function(index, elem) {
+ goog.array.insertAt(this.array_, elem, index);
+ this.updateLength_();
+ this.dispatchEvent(new ol3.CollectionEvent(
+ ol3.CollectionEventType.ADD, elem, undefined, undefined, this));
+ this.dispatchEvent(new ol3.CollectionEvent(
+ ol3.CollectionEventType.INSERT_AT, elem, index, undefined, this));
+};
+
+
+/**
+ * @return {*} Element.
+ */
+ol3.Collection.prototype.pop = function() {
+ return this.removeAt(this.getLength() - 1);
+};
+
+
+/**
+ * @param {*} elem Element.
+ * @return {number} Length.
+ */
+ol3.Collection.prototype.push = function(elem) {
+ var n = this.array_.length;
+ this.insertAt(n, elem);
+ return n;
+};
+
+
+/**
+ * @param {number} index Index.
+ * @return {*} Value.
+ */
+ol3.Collection.prototype.removeAt = function(index) {
+ var prev = this.array_[index];
+ goog.array.removeAt(this.array_, index);
+ this.updateLength_();
+ this.dispatchEvent(new ol3.CollectionEvent(
+ ol3.CollectionEventType.REMOVE, prev, undefined, undefined, this));
+ this.dispatchEvent(new ol3.CollectionEvent(ol3.CollectionEventType.REMOVE_AT,
+ undefined, index, prev, this));
+ return prev;
+};
+
+
+/**
+ * @param {number} index Index.
+ * @param {*} elem Element.
+ */
+ol3.Collection.prototype.setAt = function(index, elem) {
+ var n = this[ol3.CollectionProperty.LENGTH];
+ if (index < n) {
+ var prev = this.array_[index];
+ this.array_[index] = elem;
+ this.dispatchEvent(new ol3.CollectionEvent(ol3.CollectionEventType.SET_AT,
+ elem, index, prev, this));
+ this.dispatchEvent(new ol3.CollectionEvent(ol3.CollectionEventType.REMOVE,
+ prev, undefined, undefined, this));
+ this.dispatchEvent(new ol3.CollectionEvent(ol3.CollectionEventType.ADD,
+ elem, undefined, undefined, this));
+ } else {
+ var j;
+ for (j = n; j < index; ++j) {
+ this.insertAt(j, undefined);
+ }
+ this.insertAt(index, elem);
+ }
+};
+
+
+/**
+ * @private
+ */
+ol3.Collection.prototype.updateLength_ = function() {
+ this.set(ol3.CollectionProperty.LENGTH, this.array_.length);
+};
diff --git a/src/ol3/base/collection_test.js b/src/ol3/base/collection_test.js
new file mode 100644
index 0000000000..0b0feb840c
--- /dev/null
+++ b/src/ol3/base/collection_test.js
@@ -0,0 +1,238 @@
+goog.require('goog.array');
+goog.require('goog.testing.jsunit');
+goog.require('ol3.Collection');
+goog.require('ol3.CollectionEventType');
+
+
+function testEmpty() {
+ var collection = new ol3.Collection();
+ assertEquals(0, collection.getLength());
+ assertTrue(goog.array.equals(collection.getArray(), []));
+ assertUndefined(collection.getAt(0));
+}
+
+
+function testConstruct() {
+ var array = [0, 1, 2];
+ var collection = new ol3.Collection(array);
+ assertEquals(0, collection.getAt(0));
+ assertEquals(1, collection.getAt(1));
+ assertEquals(2, collection.getAt(2));
+}
+
+
+function testPush() {
+ var collection = new ol3.Collection();
+ collection.push(1);
+ assertEquals(1, collection.getLength());
+ assertTrue(goog.array.equals(collection.getArray(), [1]));
+ assertEquals(1, collection.getAt(0));
+}
+
+
+function testPushPop() {
+ var collection = new ol3.Collection();
+ collection.push(1);
+ collection.pop();
+ assertEquals(0, collection.getLength());
+ assertTrue(goog.array.equals(collection.getArray(), []));
+ assertUndefined(collection.getAt(0));
+}
+
+
+function testInsertAt() {
+ var collection = new ol3.Collection([0, 2]);
+ collection.insertAt(1, 1);
+ assertEquals(0, collection.getAt(0));
+ assertEquals(1, collection.getAt(1));
+ assertEquals(2, collection.getAt(2));
+}
+
+
+function testSetAt() {
+ var collection = new ol3.Collection();
+ collection.setAt(1, 1);
+ assertEquals(2, collection.getLength());
+ assertUndefined(collection.getAt(0));
+ assertEquals(1, collection.getAt(1));
+}
+
+
+function testRemoveAt() {
+ var collection = new ol3.Collection([0, 1, 2]);
+ collection.removeAt(1);
+ assertEquals(0, collection.getAt(0));
+ assertEquals(2, collection.getAt(1));
+}
+
+
+function testForEachEmpty() {
+ var collection = new ol3.Collection();
+ var forEachCalled = false;
+ collection.forEach(function() {
+ forEachCalled = true;
+ });
+ assertFalse(forEachCalled);
+}
+
+
+function testForEachPopulated() {
+ var collection = new ol3.Collection();
+ collection.push(1);
+ collection.push(2);
+ var forEachCount = 0;
+ collection.forEach(function() {
+ ++forEachCount;
+ });
+ assertEquals(2, forEachCount);
+}
+
+
+function testSetAtEvent() {
+ var collection = new ol3.Collection(['a', 'b']);
+ var index, prev;
+ goog.events.listen(collection, ol3.CollectionEventType.SET_AT, function(e) {
+ index = e.index;
+ prev = e.prev;
+ });
+ collection.setAt(1, 1);
+ assertEquals(1, index);
+ assertEquals('b', prev);
+}
+
+
+function testRemoveAtEvent() {
+ var collection = new ol3.Collection(['a']);
+ var index, prev;
+ goog.events.listen(
+ collection, ol3.CollectionEventType.REMOVE_AT, function(e) {
+ index = e.index;
+ prev = e.prev;
+ });
+ collection.pop();
+ assertEquals(0, index);
+ assertEquals('a', prev);
+}
+
+
+function testInsertAtEvent() {
+ var collection = new ol3.Collection([0, 2]);
+ var index;
+ goog.events.listen(
+ collection, ol3.CollectionEventType.INSERT_AT, function(e) {
+ index = e.index;
+ });
+ collection.insertAt(1, 1);
+ assertEquals(1, index);
+}
+
+
+function testSetAtBeyondEnd() {
+ var collection = new ol3.Collection();
+ var inserts = [];
+ goog.events.listen(
+ collection, ol3.CollectionEventType.INSERT_AT, function(e) {
+ inserts.push(e.index);
+ });
+ collection.setAt(2, 0);
+ assertEquals(3, collection.getLength());
+ assertUndefined(collection.getAt(0));
+ assertUndefined(collection.getAt(1));
+ assertEquals(0, collection.getAt(2));
+ assertEquals(3, inserts.length);
+ assertEquals(0, inserts[0]);
+ assertEquals(1, inserts[1]);
+ assertEquals(2, inserts[2]);
+}
+
+
+function testLengthChangeInsertAt() {
+ var collection = new ol3.Collection([0, 1, 2]);
+ var lengthEventDispatched;
+ goog.events.listen(collection, 'length_changed', function() {
+ lengthEventDispatched = true;
+ });
+ collection.insertAt(2, 3);
+ assertTrue(lengthEventDispatched);
+}
+
+
+function testLengthChangeRemoveAt() {
+ var collection = new ol3.Collection([0, 1, 2]);
+ var lengthEventDispatched;
+ goog.events.listen(collection, 'length_changed', function() {
+ lengthEventDispatched = true;
+ });
+ collection.removeAt(0);
+ assertTrue(lengthEventDispatched);
+}
+
+
+function testLengthChangeSetAt() {
+ var collection = new ol3.Collection([0, 1, 2]);
+ var lengthEventDispatched;
+ goog.events.listen(collection, 'length_changed', function() {
+ lengthEventDispatched = true;
+ });
+ collection.setAt(1, 1);
+ assertUndefined(lengthEventDispatched);
+}
+
+
+function testForEach() {
+ var collection = new ol3.Collection([1, 2, 4]);
+ var sum = 0;
+ collection.forEach(function(elem) {
+ sum += elem;
+ });
+ assertEquals(7, sum);
+}
+
+
+function testForEachScope() {
+ var collection = new ol3.Collection([0]);
+ var that;
+ var uniqueObj = {};
+ collection.forEach(function(elem) {
+ that = this;
+ }, uniqueObj);
+ assertTrue(that === uniqueObj);
+}
+
+
+function testAddEvent() {
+ var collection = new ol3.Collection();
+ var elem;
+ goog.events.listen(collection, ol3.CollectionEventType.ADD, function(e) {
+ elem = e.elem;
+ });
+ collection.push(1);
+ assertEquals(1, elem);
+}
+
+
+function testAddRemoveEvent() {
+ var collection = new ol3.Collection([1]);
+ var addedElem;
+ goog.events.listen(collection, ol3.CollectionEventType.ADD, function(e) {
+ addedElem = e.elem;
+ });
+ var removedElem;
+ goog.events.listen(collection, ol3.CollectionEventType.REMOVE, function(e) {
+ removedElem = e.elem;
+ });
+ collection.setAt(0, 2);
+ assertEquals(1, removedElem);
+ assertEquals(2, addedElem);
+}
+
+
+function testRemove() {
+ var collection = new ol3.Collection([1]);
+ var elem;
+ goog.events.listen(collection, ol3.CollectionEventType.REMOVE, function(e) {
+ elem = e.elem;
+ });
+ collection.pop();
+ assertEquals(1, elem);
+}
diff --git a/src/ol3/base/color.js b/src/ol3/base/color.js
new file mode 100644
index 0000000000..9fdc81daa2
--- /dev/null
+++ b/src/ol3/base/color.js
@@ -0,0 +1,56 @@
+goog.provide('ol3.Color');
+
+goog.require('goog.color');
+
+
+
+/**
+ * @constructor
+ * @param {number} r Red.
+ * @param {number} g Green.
+ * @param {number} b Blue.
+ * @param {number} a Alpha.
+ */
+ol3.Color = function(r, g, b, a) {
+
+ /**
+ * @type {number}
+ */
+ this.r = r;
+
+ /**
+ * @type {number}
+ */
+ this.g = g;
+
+ /**
+ * @type {number}
+ */
+ this.b = b;
+
+ /**
+ * @type {number}
+ */
+ this.a = a;
+
+};
+
+
+/**
+ * @param {string} str String.
+ * @param {number=} opt_a Alpha.
+ * @return {ol3.Color} Color.
+ */
+ol3.Color.createFromString = function(str, opt_a) {
+ var rgb = goog.color.hexToRgb(goog.color.parse(str).hex);
+ var a = opt_a || 255;
+ return new ol3.Color(rgb[0], rgb[1], rgb[2], a);
+};
+
+
+/**
+ * @return {ol3.Color} Clone.
+ */
+ol3.Color.prototype.clone = function() {
+ return new ol3.Color(this.r, this.g, this.b, this.a);
+};
diff --git a/src/ol3/base/coordinate.js b/src/ol3/base/coordinate.js
new file mode 100644
index 0000000000..7fc72d0968
--- /dev/null
+++ b/src/ol3/base/coordinate.js
@@ -0,0 +1,31 @@
+goog.provide('ol3.Coordinate');
+
+goog.require('goog.math.Vec2');
+
+
+
+/**
+ * @constructor
+ * @extends {goog.math.Vec2}
+ * @param {number} x X.
+ * @param {number} y Y.
+ */
+ol3.Coordinate = function(x, y) {
+ goog.base(this, x, y);
+};
+goog.inherits(ol3.Coordinate, goog.math.Vec2);
+
+
+/**
+ * @const
+ * @type {ol3.Coordinate}
+ */
+ol3.Coordinate.ZERO = new ol3.Coordinate(0, 0);
+
+
+/**
+ * @return {ol3.Coordinate} Clone.
+ */
+ol3.Coordinate.prototype.clone = function() {
+ return new ol3.Coordinate(this.x, this.y);
+};
diff --git a/src/ol3/base/coordinateformat.js b/src/ol3/base/coordinateformat.js
new file mode 100644
index 0000000000..7a7796cb58
--- /dev/null
+++ b/src/ol3/base/coordinateformat.js
@@ -0,0 +1,57 @@
+goog.provide('ol3.CoordinateFormat');
+goog.provide('ol3.CoordinateFormatType');
+
+goog.require('goog.math');
+goog.require('ol3.Coordinate');
+
+
+/**
+ * @typedef {function((ol3.Coordinate|undefined)): string}
+ */
+ol3.CoordinateFormatType;
+
+
+/**
+ * @param {number} precision Precision.
+ * @return {ol3.CoordinateFormatType} Coordinate format.
+ */
+ol3.CoordinateFormat.createXY = function(precision) {
+ return function(coordinate) {
+ if (goog.isDef(coordinate)) {
+ return coordinate.x.toFixed(precision) + ', ' +
+ coordinate.y.toFixed(precision);
+ } else {
+ return '';
+ }
+ };
+};
+
+
+/**
+ * @private
+ * @param {number} degrees Degrees.
+ * @param {string} hemispheres Hemispheres.
+ * @return {string} String.
+ */
+ol3.CoordinateFormat.degreesToHDMS_ = function(degrees, hemispheres) {
+ var normalizedDegrees = goog.math.modulo(degrees + 180, 360) - 180;
+ var x = Math.abs(Math.round(3600 * normalizedDegrees));
+ return Math.floor(x / 3600) + '\u00b0 ' +
+ Math.floor((x / 60) % 60) + '\u2032 ' +
+ Math.floor(x % 60) + '\u2033 ' +
+ hemispheres.charAt(normalizedDegrees < 0 ? 1 : 0);
+};
+
+
+/**
+ * @param {ol3.Coordinate|undefined} coordinate Coordinate.
+ * @return {string} Coordinate format.
+ */
+ol3.CoordinateFormat.hdms = function(coordinate) {
+ if (goog.isDef(coordinate)) {
+ return ol3.CoordinateFormat.degreesToHDMS_(coordinate.y, 'NS') + ' ' +
+ ol3.CoordinateFormat.degreesToHDMS_(coordinate.x, 'EW');
+ } else {
+ return '';
+ }
+};
diff --git a/src/ol3/base/coveragearea.js b/src/ol3/base/coveragearea.js
new file mode 100644
index 0000000000..a7c505a980
--- /dev/null
+++ b/src/ol3/base/coveragearea.js
@@ -0,0 +1,45 @@
+goog.provide('ol3.CoverageArea');
+
+goog.require('ol3.Extent');
+
+
+
+/**
+ * @constructor
+ * @param {ol3.Extent} extent Extent.
+ */
+ol3.CoverageArea = function(extent) {
+
+ /**
+ * @type {ol3.Extent}
+ */
+ this.extent = extent;
+
+};
+
+
+/**
+ * @param {ol3.Extent} extent Extent.
+ * @return {boolean} Intersects.
+ */
+ol3.CoverageArea.prototype.intersectsExtent = function(extent) {
+ return this.extent.intersects(extent);
+};
+
+
+/**
+ * @param {ol3.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @return {boolean} Intersects.
+ */
+ol3.CoverageArea.prototype.intersectsExtentAndResolution = goog.abstractMethod;
+
+
+/**
+ * @param {ol3.TransformFunction} transformFn Transform.
+ * @return {ol3.CoverageArea} Transformed coverage area.
+ */
+ol3.CoverageArea.prototype.transform = function(transformFn) {
+ var extent = this.extent.transform(transformFn);
+ return new ol3.CoverageArea(extent);
+};
diff --git a/src/ol3/base/createmap.js b/src/ol3/base/createmap.js
new file mode 100644
index 0000000000..079ffa4dd6
--- /dev/null
+++ b/src/ol3/base/createmap.js
@@ -0,0 +1,153 @@
+goog.provide('ol3.RendererHint');
+goog.provide('ol3.createMap');
+
+goog.require('goog.object');
+goog.require('ol3.Collection');
+goog.require('ol3.Map');
+goog.require('ol3.MapProperty');
+goog.require('ol3.Projection');
+goog.require('ol3.dom');
+goog.require('ol3.dom.MapRenderer');
+goog.require('ol3.interaction.AltDragRotate');
+goog.require('ol3.interaction.CenterConstraint');
+goog.require('ol3.interaction.Constraints');
+goog.require('ol3.interaction.DblClickZoom');
+goog.require('ol3.interaction.DragPan');
+goog.require('ol3.interaction.KeyboardPan');
+goog.require('ol3.interaction.KeyboardZoom');
+goog.require('ol3.interaction.MouseWheelZoom');
+goog.require('ol3.interaction.ResolutionConstraint');
+goog.require('ol3.interaction.RotationConstraint');
+goog.require('ol3.interaction.ShiftDragZoom');
+goog.require('ol3.webgl');
+goog.require('ol3.webgl.MapRenderer');
+
+
+/**
+ * @define {string} Default projection code.
+ */
+ol3.DEFAULT_PROJECTION_CODE = 'EPSG:3857';
+
+
+/**
+ * @define {string} Default user projection code.
+ */
+ol3.DEFAULT_USER_PROJECTION_CODE = 'EPSG:4326';
+
+
+/**
+ * @define {boolean} Whether to enable DOM.
+ */
+ol3.ENABLE_DOM = true;
+
+
+/**
+ * @define {boolean} Whether to enable WebGL.
+ */
+ol3.ENABLE_WEBGL = true;
+
+
+/**
+ * @enum {string}
+ */
+ol3.RendererHint = {
+ DOM: 'dom',
+ WEBGL: 'webgl'
+};
+
+
+/**
+ * @type {Array.}
+ */
+ol3.DEFAULT_RENDERER_HINT = [
+ ol3.RendererHint.WEBGL,
+ ol3.RendererHint.DOM
+];
+
+
+/**
+ * @param {Element} target Target.
+ * @param {Object.=} opt_values Values.
+ * @param {ol3.RendererHint|Array.=} opt_rendererHints
+ * Renderer hints.
+ * @return {ol3.Map} Map.
+ */
+ol3.createMap = function(target, opt_values, opt_rendererHints) {
+
+ var values = {};
+ if (goog.isDef(opt_values)) {
+ goog.object.extend(values, opt_values);
+ }
+
+ // FIXME this should be a configuration option
+ var centerConstraint = ol3.interaction.CenterConstraint.snapToPixel;
+ var resolutionConstraint =
+ ol3.interaction.ResolutionConstraint.createSnapToPower(
+ Math.exp(Math.log(2) / 8), ol3.Projection.EPSG_3857_HALF_SIZE / 128);
+ var rotationConstraint = ol3.interaction.RotationConstraint.none;
+ var constraints = new ol3.interaction.Constraints(
+ centerConstraint, resolutionConstraint, rotationConstraint);
+
+ if (!goog.object.containsKey(values, ol3.MapProperty.INTERACTIONS)) {
+ var interactions = new ol3.Collection();
+ interactions.push(new ol3.interaction.AltDragRotate(constraints));
+ interactions.push(new ol3.interaction.DblClickZoom(constraints));
+ interactions.push(new ol3.interaction.DragPan(constraints));
+ interactions.push(new ol3.interaction.KeyboardPan(constraints, 16));
+ interactions.push(new ol3.interaction.KeyboardZoom(constraints));
+ interactions.push(new ol3.interaction.MouseWheelZoom(constraints));
+ interactions.push(new ol3.interaction.ShiftDragZoom(constraints));
+ values[ol3.MapProperty.INTERACTIONS] = interactions;
+ }
+
+ if (!goog.object.containsKey(values, ol3.MapProperty.LAYERS)) {
+ values[ol3.MapProperty.LAYERS] = new ol3.Collection();
+ }
+
+ if (!goog.object.containsKey(values, ol3.MapProperty.PROJECTION)) {
+ values[ol3.MapProperty.PROJECTION] =
+ ol3.Projection.getFromCode(ol3.DEFAULT_PROJECTION_CODE);
+ }
+
+ if (!goog.object.containsKey(values, ol3.MapProperty.USER_PROJECTION)) {
+ values[ol3.MapProperty.USER_PROJECTION] =
+ ol3.Projection.getFromCode(ol3.DEFAULT_USER_PROJECTION_CODE);
+ }
+
+ /**
+ * @type {Array.}
+ */
+ var rendererHints;
+ if (goog.isDef(opt_rendererHints)) {
+ if (goog.isArray(opt_rendererHints)) {
+ rendererHints = opt_rendererHints;
+ } else {
+ rendererHints = [opt_rendererHints];
+ }
+ } else {
+ rendererHints = ol3.DEFAULT_RENDERER_HINT;
+ }
+
+ var i, rendererHint, rendererConstructor;
+ for (i = 0; i < rendererHints.length; ++i) {
+ rendererHint = rendererHints[i];
+ if (rendererHint == ol3.RendererHint.DOM) {
+ if (ol3.ENABLE_DOM && ol3.dom.isSupported()) {
+ rendererConstructor = ol3.dom.MapRenderer;
+ break;
+ }
+ } else if (rendererHint == ol3.RendererHint.WEBGL) {
+ if (ol3.ENABLE_WEBGL && ol3.webgl.isSupported()) {
+ rendererConstructor = ol3.webgl.MapRenderer;
+ break;
+ }
+ }
+ }
+
+ if (goog.isDef(rendererConstructor)) {
+ return new ol3.Map(target, rendererConstructor, values);
+ } else {
+ return null;
+ }
+
+};
diff --git a/src/ol3/base/extent.js b/src/ol3/base/extent.js
new file mode 100644
index 0000000000..cd39773171
--- /dev/null
+++ b/src/ol3/base/extent.js
@@ -0,0 +1,59 @@
+goog.provide('ol3.Extent');
+
+goog.require('ol3.Coordinate');
+goog.require('ol3.Rectangle');
+goog.require('ol3.TransformFunction');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Rectangle}
+ * @param {number} minX Minimum X.
+ * @param {number} minY Minimum Y.
+ * @param {number} maxX Maximum X.
+ * @param {number} maxY Maximum Y.
+ */
+ol3.Extent = function(minX, minY, maxX, maxY) {
+ goog.base(this, minX, minY, maxX, maxY);
+};
+goog.inherits(ol3.Extent, ol3.Rectangle);
+
+
+/**
+ * @param {...ol3.Coordinate} var_args Coordinates.
+ * @return {!ol3.Extent} Bounding extent.
+ */
+ol3.Extent.boundingExtent = function(var_args) {
+ var coordinate0 = arguments[0];
+ var extent = new ol3.Extent(coordinate0.x, coordinate0.y,
+ coordinate0.x, coordinate0.y);
+ var i;
+ for (i = 1; i < arguments.length; ++i) {
+ var coordinate = arguments[i];
+ extent.minX = Math.min(extent.minX, coordinate.x);
+ extent.minY = Math.min(extent.minY, coordinate.y);
+ extent.maxX = Math.max(extent.maxX, coordinate.x);
+ extent.maxY = Math.max(extent.maxY, coordinate.y);
+ }
+ return extent;
+};
+
+
+/**
+ * @return {ol3.Extent} Extent.
+ */
+ol3.Extent.prototype.clone = function() {
+ return new ol3.Extent(this.minX, this.minY, this.maxX, this.maxY);
+};
+
+
+/**
+ * @param {ol3.TransformFunction} transformFn Transform function.
+ * @return {ol3.Extent} Extent.
+ */
+ol3.Extent.prototype.transform = function(transformFn) {
+ var min = transformFn(new ol3.Coordinate(this.minX, this.minY));
+ var max = transformFn(new ol3.Coordinate(this.maxX, this.maxY));
+ return new ol3.Extent(min.x, min.y, max.x, max.y);
+};
diff --git a/src/ol3/base/extent_test.js b/src/ol3/base/extent_test.js
new file mode 100644
index 0000000000..d618847a48
--- /dev/null
+++ b/src/ol3/base/extent_test.js
@@ -0,0 +1,29 @@
+goog.require('goog.testing.jsunit');
+goog.require('ol3.Extent');
+goog.require('ol3.Projection');
+
+
+function testClone() {
+ var extent = new ol3.Extent(1, 2, 3, 4);
+ var clonedExtent = extent.clone();
+ assertTrue(clonedExtent instanceof ol3.Extent);
+ assertFalse(clonedExtent === extent);
+ assertEquals(extent.minX, clonedExtent.minX);
+ assertEquals(extent.minY, clonedExtent.minY);
+ assertEquals(extent.maxX, clonedExtent.maxX);
+ assertEquals(extent.maxY, clonedExtent.maxY);
+}
+
+
+function testTransform() {
+ var transformFn =
+ ol3.Projection.getTransformFromCodes('EPSG:4326', 'EPSG:3857');
+ var sourceExtent = new ol3.Extent(-15, -30, 45, 60);
+ var destinationExtent = sourceExtent.transform(transformFn);
+ assertNotNullNorUndefined(destinationExtent);
+ // FIXME check values with third-party tool
+ assertRoughlyEquals(-1669792.3618991037, destinationExtent.minX, 1e-9);
+ assertRoughlyEquals(-3503549.843504376, destinationExtent.minY, 1e-9);
+ assertRoughlyEquals(5009377.085697311, destinationExtent.maxX, 1e-9);
+ assertRoughlyEquals(8399737.889818361, destinationExtent.maxY, 1e-9);
+}
diff --git a/src/ol3/base/layer.js b/src/ol3/base/layer.js
new file mode 100644
index 0000000000..f6819e4373
--- /dev/null
+++ b/src/ol3/base/layer.js
@@ -0,0 +1,228 @@
+goog.provide('ol3.Layer');
+goog.provide('ol3.LayerProperty');
+
+goog.require('goog.math');
+goog.require('ol3.Object');
+goog.require('ol3.Store');
+
+
+/**
+ * @enum {string}
+ */
+ol3.LayerProperty = {
+ BRIGHTNESS: 'brightness',
+ CONTRAST: 'contrast',
+ HUE: 'hue',
+ OPACITY: 'opacity',
+ SATURATION: 'saturation',
+ VISIBLE: 'visible'
+};
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Object}
+ * @param {ol3.Store} store Store.
+ * @param {Object.=} opt_values Values.
+ */
+ol3.Layer = function(store, opt_values) {
+
+ goog.base(this);
+
+ /**
+ * @private
+ * @type {ol3.Store}
+ */
+ this.store_ = store;
+
+ this.setBrightness(0);
+ this.setContrast(0);
+ this.setHue(0);
+ this.setOpacity(1);
+ this.setSaturation(0);
+ this.setVisible(true);
+
+ if (goog.isDef(opt_values)) {
+ this.setValues(opt_values);
+ }
+
+};
+goog.inherits(ol3.Layer, ol3.Object);
+
+
+/**
+ * @return {number} Brightness.
+ */
+ol3.Layer.prototype.getBrightness = function() {
+ return /** @type {number} */ this.get(ol3.LayerProperty.BRIGHTNESS);
+};
+goog.exportProperty(
+ ol3.Layer.prototype,
+ 'getBrightness',
+ ol3.Layer.prototype.getBrightness);
+
+
+/**
+ * @return {number} Contrast.
+ */
+ol3.Layer.prototype.getContrast = function() {
+ return /** @type {number} */ this.get(ol3.LayerProperty.CONTRAST);
+};
+goog.exportProperty(
+ ol3.Layer.prototype,
+ 'getContrast',
+ ol3.Layer.prototype.getContrast);
+
+
+/**
+ * @return {number} Hue.
+ */
+ol3.Layer.prototype.getHue = function() {
+ return /** @type {number} */ this.get(ol3.LayerProperty.HUE);
+};
+goog.exportProperty(
+ ol3.Layer.prototype,
+ 'getHue',
+ ol3.Layer.prototype.getHue);
+
+
+/**
+ * @return {number} Opacity.
+ */
+ol3.Layer.prototype.getOpacity = function() {
+ return /** @type {number} */ this.get(ol3.LayerProperty.OPACITY);
+};
+goog.exportProperty(
+ ol3.Layer.prototype,
+ 'getOpacity',
+ ol3.Layer.prototype.getOpacity);
+
+
+/**
+ * @return {number} Saturation.
+ */
+ol3.Layer.prototype.getSaturation = function() {
+ return /** @type {number} */ this.get(ol3.LayerProperty.SATURATION);
+};
+goog.exportProperty(
+ ol3.Layer.prototype,
+ 'getSaturation',
+ ol3.Layer.prototype.getSaturation);
+
+
+/**
+ * @return {ol3.Store} Store.
+ */
+ol3.Layer.prototype.getStore = function() {
+ return this.store_;
+};
+
+
+/**
+ * @return {boolean} Visible.
+ */
+ol3.Layer.prototype.getVisible = function() {
+ return /** @type {boolean} */ this.get(ol3.LayerProperty.VISIBLE);
+};
+goog.exportProperty(
+ ol3.Layer.prototype,
+ 'getVisible',
+ ol3.Layer.prototype.getVisible);
+
+
+/**
+ * @return {boolean} Is ready.
+ */
+ol3.Layer.prototype.isReady = function() {
+ return this.getStore().isReady();
+};
+
+
+/**
+ * @param {number} brightness Brightness.
+ */
+ol3.Layer.prototype.setBrightness = function(brightness) {
+ brightness = goog.math.clamp(brightness, -1, 1);
+ if (brightness != this.getBrightness()) {
+ this.set(ol3.LayerProperty.BRIGHTNESS, brightness);
+ }
+};
+goog.exportProperty(
+ ol3.Layer.prototype,
+ 'setBrightness',
+ ol3.Layer.prototype.setBrightness);
+
+
+/**
+ * @param {number} contrast Contrast.
+ */
+ol3.Layer.prototype.setContrast = function(contrast) {
+ contrast = goog.math.clamp(contrast, -1, 1);
+ if (contrast != this.getContrast()) {
+ this.set(ol3.LayerProperty.CONTRAST, contrast);
+ }
+};
+goog.exportProperty(
+ ol3.Layer.prototype,
+ 'setContrast',
+ ol3.Layer.prototype.setContrast);
+
+
+/**
+ * @param {number} hue Hue.
+ */
+ol3.Layer.prototype.setHue = function(hue) {
+ if (hue != this.getHue()) {
+ this.set(ol3.LayerProperty.HUE, hue);
+ }
+};
+goog.exportProperty(
+ ol3.Layer.prototype,
+ 'setHue',
+ ol3.Layer.prototype.setHue);
+
+
+/**
+ * @param {number} opacity Opacity.
+ */
+ol3.Layer.prototype.setOpacity = function(opacity) {
+ opacity = goog.math.clamp(opacity, 0, 1);
+ if (opacity != this.getOpacity()) {
+ this.set(ol3.LayerProperty.OPACITY, opacity);
+ }
+};
+goog.exportProperty(
+ ol3.Layer.prototype,
+ 'setOpacity',
+ ol3.Layer.prototype.setOpacity);
+
+
+/**
+ * @param {number} saturation Saturation.
+ */
+ol3.Layer.prototype.setSaturation = function(saturation) {
+ saturation = goog.math.clamp(saturation, -1, 1);
+ if (saturation != this.getSaturation()) {
+ this.set(ol3.LayerProperty.SATURATION, saturation);
+ }
+};
+goog.exportProperty(
+ ol3.Layer.prototype,
+ 'setSaturation',
+ ol3.Layer.prototype.setSaturation);
+
+
+/**
+ * @param {boolean} visible Visible.
+ */
+ol3.Layer.prototype.setVisible = function(visible) {
+ visible = !!visible;
+ if (visible != this.getVisible()) {
+ this.set(ol3.LayerProperty.VISIBLE, visible);
+ }
+};
+goog.exportProperty(
+ ol3.Layer.prototype,
+ 'setVisible',
+ ol3.Layer.prototype.setVisible);
diff --git a/src/ol3/base/layerrenderer.js b/src/ol3/base/layerrenderer.js
new file mode 100644
index 0000000000..e724c679aa
--- /dev/null
+++ b/src/ol3/base/layerrenderer.js
@@ -0,0 +1,127 @@
+goog.provide('ol3.LayerRenderer');
+
+goog.require('goog.events');
+goog.require('goog.events.EventType');
+goog.require('ol3.Layer');
+goog.require('ol3.LayerProperty');
+goog.require('ol3.Object');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Object}
+ * @param {ol3.MapRenderer} mapRenderer Map renderer.
+ * @param {ol3.Layer} layer Layer.
+ */
+ol3.LayerRenderer = function(mapRenderer, layer) {
+
+ goog.base(this);
+
+ /**
+ * @private
+ * @type {ol3.MapRenderer}
+ */
+ this.mapRenderer_ = mapRenderer;
+
+ /**
+ * @private
+ * @type {ol3.Layer}
+ */
+ this.layer_ = layer;
+
+ goog.events.listen(this.layer_,
+ ol3.Object.getChangedEventType(ol3.LayerProperty.BRIGHTNESS),
+ this.handleLayerBrightnessChange, false, this);
+
+ goog.events.listen(this.layer_,
+ ol3.Object.getChangedEventType(ol3.LayerProperty.CONTRAST),
+ this.handleLayerContrastChange, false, this);
+
+ goog.events.listen(this.layer_,
+ ol3.Object.getChangedEventType(ol3.LayerProperty.HUE),
+ this.handleLayerHueChange, false, this);
+
+ goog.events.listen(this.layer_, goog.events.EventType.LOAD,
+ this.handleLayerLoad, false, this);
+
+ goog.events.listen(this.layer_,
+ ol3.Object.getChangedEventType(ol3.LayerProperty.OPACITY),
+ this.handleLayerOpacityChange, false, this);
+
+ goog.events.listen(this.layer_,
+ ol3.Object.getChangedEventType(ol3.LayerProperty.SATURATION),
+ this.handleLayerSaturationChange, false, this);
+
+ goog.events.listen(this.layer_,
+ ol3.Object.getChangedEventType(ol3.LayerProperty.VISIBLE),
+ this.handleLayerVisibleChange, false, this);
+
+};
+goog.inherits(ol3.LayerRenderer, ol3.Object);
+
+
+/**
+ * @return {ol3.Layer} Layer.
+ */
+ol3.LayerRenderer.prototype.getLayer = function() {
+ return this.layer_;
+};
+
+
+/**
+ * @return {ol3.Map} Map.
+ */
+ol3.LayerRenderer.prototype.getMap = function() {
+ return this.mapRenderer_.getMap();
+};
+
+
+/**
+ * @return {ol3.MapRenderer} Map renderer.
+ */
+ol3.LayerRenderer.prototype.getMapRenderer = function() {
+ return this.mapRenderer_;
+};
+
+
+/**
+ * @protected
+ */
+ol3.LayerRenderer.prototype.handleLayerBrightnessChange = goog.nullFunction;
+
+
+/**
+ * @protected
+ */
+ol3.LayerRenderer.prototype.handleLayerContrastChange = goog.nullFunction;
+
+
+/**
+ * @protected
+ */
+ol3.LayerRenderer.prototype.handleLayerHueChange = goog.nullFunction;
+
+
+/**
+ * @protected
+ */
+ol3.LayerRenderer.prototype.handleLayerLoad = goog.nullFunction;
+
+
+/**
+ * @protected
+ */
+ol3.LayerRenderer.prototype.handleLayerOpacityChange = goog.nullFunction;
+
+
+/**
+ * @protected
+ */
+ol3.LayerRenderer.prototype.handleLayerSaturationChange = goog.nullFunction;
+
+
+/**
+ * @protected
+ */
+ol3.LayerRenderer.prototype.handleLayerVisibleChange = goog.nullFunction;
diff --git a/src/ol3/base/map.js b/src/ol3/base/map.js
new file mode 100644
index 0000000000..1f1ecb57ff
--- /dev/null
+++ b/src/ol3/base/map.js
@@ -0,0 +1,803 @@
+// FIXME better map browser event types
+// FIXME recheck layer/map projection compatability when projection changes
+// FIXME layer renderers should skip when they can't reproject
+// FIXME add tilt and height?
+
+goog.provide('ol3.Map');
+goog.provide('ol3.MapEventType');
+goog.provide('ol3.MapProperty');
+
+goog.require('goog.array');
+goog.require('goog.debug.Logger');
+goog.require('goog.dispose');
+goog.require('goog.dom');
+goog.require('goog.dom.ViewportSizeMonitor');
+goog.require('goog.events');
+goog.require('goog.events.BrowserEvent');
+goog.require('goog.events.Event');
+goog.require('goog.events.EventType');
+goog.require('goog.events.KeyHandler');
+goog.require('goog.events.KeyHandler.EventType');
+goog.require('goog.events.MouseWheelEvent');
+goog.require('goog.events.MouseWheelHandler');
+goog.require('goog.events.MouseWheelHandler.EventType');
+goog.require('goog.functions');
+goog.require('goog.fx.DragEvent');
+goog.require('goog.fx.Dragger');
+goog.require('goog.fx.anim');
+goog.require('goog.fx.anim.Animated');
+goog.require('goog.object');
+goog.require('ol3.Collection');
+goog.require('ol3.Color');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Extent');
+goog.require('ol3.Interaction');
+goog.require('ol3.LayerRenderer');
+goog.require('ol3.MapBrowserEvent');
+goog.require('ol3.Object');
+goog.require('ol3.Pixel');
+goog.require('ol3.Projection');
+goog.require('ol3.Size');
+goog.require('ol3.TransformFunction');
+
+
+/**
+ * @enum {string}
+ */
+ol3.MapEventType = {
+ POST_RENDER: 'postrender'
+};
+
+
+/**
+ * @enum {string}
+ */
+ol3.MapProperty = {
+ BACKGROUND_COLOR: 'backgroundColor',
+ CENTER: 'center',
+ INTERACTIONS: 'interactions',
+ LAYERS: 'layers',
+ PROJECTION: 'projection',
+ RESOLUTION: 'resolution',
+ ROTATION: 'rotation',
+ SIZE: 'size',
+ USER_PROJECTION: 'userProjection'
+};
+
+
+/**
+ * @enum {number}
+ */
+ol3.MapPaneZIndex = {
+ VIEWPORT: 1000
+};
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Object}
+ * @implements {goog.fx.anim.Animated}
+ * @param {Element} container Container.
+ * @param {function(new: ol3.MapRenderer, Element, ol3.Map)} rendererConstructor
+ * Renderer constructor.
+ * @param {Object=} opt_values Values.
+ * @param {goog.dom.ViewportSizeMonitor=} opt_viewportSizeMonitor
+ * Viewport size monitor.
+ */
+ol3.Map = function(
+ container, rendererConstructor, opt_values, opt_viewportSizeMonitor) {
+
+ goog.base(this);
+
+ if (goog.DEBUG) {
+ /**
+ * @protected
+ * @type {goog.debug.Logger}
+ */
+ this.logger = goog.debug.Logger.getLogger('ol3.map.' + goog.getUid(this));
+ }
+
+ /**
+ * @type {ol3.TransformFunction}
+ * @private
+ */
+ this.userToMapTransform_ = ol3.Projection.identityTransform;
+
+ /**
+ * @type {ol3.TransformFunction}
+ * @private
+ */
+ this.mapToUserTransform_ = ol3.Projection.cloneTransform;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.animatedRenderer_ = false;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.animatingCount_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.freezeRenderingCount_ = 0;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.dirty_ = false;
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.container_ = container;
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.viewport_ = goog.dom.createElement(goog.dom.TagName.DIV);
+ this.viewport_.className = 'ol-viewport';
+ this.viewport_.style.position = 'relative';
+ this.viewport_.style.overflow = 'hidden';
+ this.viewport_.style.width = '100%';
+ this.viewport_.style.height = '100%';
+ this.viewport_.style.zIndex = ol3.MapPaneZIndex.VIEWPORT;
+ goog.dom.appendChild(container, this.viewport_);
+
+ goog.events.listen(this.viewport_, [
+ goog.events.EventType.DBLCLICK
+ ], this.handleBrowserEvent, false, this);
+
+ // FIXME we probably shouldn't listen on document...
+ var keyHandler = new goog.events.KeyHandler(document);
+ goog.events.listen(keyHandler, goog.events.KeyHandler.EventType.KEY,
+ this.handleBrowserEvent, false, this);
+ this.registerDisposable(keyHandler);
+
+ var mouseWheelHandler = new goog.events.MouseWheelHandler(this.viewport_);
+ goog.events.listen(mouseWheelHandler,
+ goog.events.MouseWheelHandler.EventType.MOUSEWHEEL,
+ this.handleBrowserEvent, false, this);
+ this.registerDisposable(mouseWheelHandler);
+
+ var dragger = new goog.fx.Dragger(this.viewport_);
+ dragger.defaultAction = function() {};
+ goog.events.listen(dragger, [
+ goog.fx.Dragger.EventType.START,
+ goog.fx.Dragger.EventType.DRAG,
+ goog.fx.Dragger.EventType.END
+ ], this.handleDraggerEvent, false, this);
+ this.registerDisposable(dragger);
+
+ /**
+ * @type {ol3.MapRenderer}
+ * @private
+ */
+ this.renderer_ = new rendererConstructor(this.viewport_, this);
+ this.registerDisposable(this.renderer_);
+
+ /**
+ * @private
+ * @type {goog.dom.ViewportSizeMonitor}
+ */
+ this.viewportSizeMonitor_ =
+ opt_viewportSizeMonitor || new goog.dom.ViewportSizeMonitor();
+
+ goog.events.listen(this.viewportSizeMonitor_, goog.events.EventType.RESIZE,
+ this.handleBrowserWindowResize, false, this);
+
+ goog.events.listen(
+ this, ol3.Object.getChangedEventType(ol3.MapProperty.PROJECTION),
+ this.handleProjectionChanged, false, this);
+
+ goog.events.listen(
+ this, ol3.Object.getChangedEventType(ol3.MapProperty.USER_PROJECTION),
+ this.handleUserProjectionChanged, false, this);
+
+ if (goog.isDef(opt_values)) {
+ this.setValues(opt_values);
+ }
+
+ this.handleBrowserWindowResize();
+
+};
+goog.inherits(ol3.Map, ol3.Object);
+
+
+/**
+ * @return {boolean} Can rotate.
+ */
+ol3.Map.prototype.canRotate = function() {
+ return this.renderer_.canRotate();
+};
+
+
+/**
+ * @param {ol3.Extent} extent Extent.
+ */
+ol3.Map.prototype.fitExtent = function(extent) {
+ this.withFrozenRendering(function() {
+ this.setCenter(extent.getCenter());
+ this.setResolution(this.getResolutionForExtent(extent));
+ if (this.canRotate()) {
+ this.setRotation(0);
+ }
+ }, this);
+};
+
+
+/**
+ * @param {ol3.Extent} userExtent Extent in user projection.
+ */
+ol3.Map.prototype.fitUserExtent = function(userExtent) {
+ this.fitExtent(userExtent.transform(this.userToMapTransform_));
+};
+
+
+/**
+ */
+ol3.Map.prototype.freezeRendering = function() {
+ ++this.freezeRenderingCount_;
+};
+
+
+/**
+ * @return {ol3.Color|undefined} Background color.
+ */
+ol3.Map.prototype.getBackgroundColor = function() {
+ return /** @type {ol3.Color|undefined} */ (
+ this.get(ol3.MapProperty.BACKGROUND_COLOR));
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'getBackgroundColor',
+ ol3.Map.prototype.getBackgroundColor);
+
+
+/**
+ * @return {ol3.Coordinate|undefined} Center.
+ */
+ol3.Map.prototype.getCenter = function() {
+ return /** @type {ol3.Coordinate} */ this.get(ol3.MapProperty.CENTER);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'getCenter',
+ ol3.Map.prototype.getCenter);
+
+
+/**
+ * @return {Element} Container.
+ */
+ol3.Map.prototype.getContainer = function() {
+ return this.container_;
+};
+
+
+/**
+ * @param {ol3.Pixel} pixel Pixel.
+ * @return {ol3.Coordinate|undefined} Coordinate.
+ */
+ol3.Map.prototype.getCoordinateFromPixel = function(pixel) {
+ if (this.isDef()) {
+ return this.renderer_.getCoordinateFromPixel(pixel);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @return {ol3.Extent|undefined} Extent.
+ */
+ol3.Map.prototype.getExtent = function() {
+ if (this.isDef()) {
+ var center = this.getCenter();
+ var resolution = this.getResolution();
+ var size = this.getSize();
+ var minX = center.x - resolution * size.width / 2;
+ var minY = center.y - resolution * size.height / 2;
+ var maxX = center.x + resolution * size.width / 2;
+ var maxY = center.y + resolution * size.height / 2;
+ return new ol3.Extent(minX, minY, maxX, maxY);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @return {ol3.Collection} Interactions.
+ */
+ol3.Map.prototype.getInteractions = function() {
+ return /** @type {ol3.Collection} */ this.get(ol3.MapProperty.INTERACTIONS);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'getInteractions',
+ ol3.Map.prototype.getInteractions);
+
+
+/**
+ * @return {ol3.Collection} Layers.
+ */
+ol3.Map.prototype.getLayers = function() {
+ return /** @type {ol3.Collection} */ (this.get(ol3.MapProperty.LAYERS));
+};
+
+
+/**
+ * @param {ol3.Coordinate} coordinate Coordinate.
+ * @return {ol3.Pixel|undefined} Pixel.
+ */
+ol3.Map.prototype.getPixelFromCoordinate = function(coordinate) {
+ if (this.isDef()) {
+ return this.renderer_.getPixelFromCoordinate(coordinate);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @return {ol3.Projection|undefined} Projection.
+ */
+ol3.Map.prototype.getProjection = function() {
+ return /** @type {ol3.Projection} */ this.get(ol3.MapProperty.PROJECTION);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'getProjection',
+ ol3.Map.prototype.getProjection);
+
+
+/**
+ * @return {number|undefined} Resolution.
+ */
+ol3.Map.prototype.getResolution = function() {
+ return /** @type {number} */ this.get(ol3.MapProperty.RESOLUTION);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'getResolution',
+ ol3.Map.prototype.getResolution);
+
+
+/**
+ * @param {ol3.Extent} extent Extent.
+ * @return {number|undefined} Resolution.
+ */
+ol3.Map.prototype.getResolutionForExtent = function(extent) {
+ var size = this.getSize();
+ if (goog.isDef(size)) {
+ var xResolution = (extent.maxX - extent.minX) / size.width;
+ var yResolution = (extent.maxY - extent.minY) / size.height;
+ return Math.max(xResolution, yResolution);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @return {ol3.Extent} Rotated extent.
+ */
+ol3.Map.prototype.getRotatedExtent = function() {
+ goog.asserts.assert(this.isDef());
+ var center = /** @type {!ol3.Coordinate} */ this.getCenter();
+ var resolution = this.getResolution();
+ var rotation = this.getRotation() || 0;
+ var size = this.getSize();
+ var xScale = resolution * size.width / 2;
+ var yScale = resolution * size.height / 2;
+ var corners = [
+ new ol3.Coordinate(-xScale, -yScale),
+ new ol3.Coordinate(-xScale, yScale),
+ new ol3.Coordinate(xScale, -yScale),
+ new ol3.Coordinate(xScale, yScale)
+ ];
+ goog.array.forEach(corners, function(corner) {
+ corner.rotate(rotation);
+ corner.add(center);
+ });
+ return ol3.Extent.boundingExtent.apply(null, corners);
+};
+
+
+/**
+ * @return {number|undefined} Rotation.
+ */
+ol3.Map.prototype.getRotation = function() {
+ return /** @type {number|undefined} */ this.get(ol3.MapProperty.ROTATION);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'getRotation',
+ ol3.Map.prototype.getRotation);
+
+
+/**
+ * @return {ol3.Size|undefined} Size.
+ */
+ol3.Map.prototype.getSize = function() {
+ return /** @type {ol3.Size|undefined} */ this.get(ol3.MapProperty.SIZE);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'getSize',
+ ol3.Map.prototype.getSize);
+
+
+/**
+ * @return {ol3.Coordinate|undefined} Center in user projection.
+ */
+ol3.Map.prototype.getUserCenter = function() {
+ var center = this.getCenter();
+ if (goog.isDef(center)) {
+ return this.mapToUserTransform_(center);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @return {ol3.Extent|undefined} Extent in user projection.
+ */
+ol3.Map.prototype.getUserExtent = function() {
+ var extent = this.getExtent();
+ if (goog.isDef(extent)) {
+ return extent.transform(this.mapToUserTransform_);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @export
+ * @return {ol3.Projection|undefined} Projection.
+ */
+ol3.Map.prototype.getUserProjection = function() {
+ return /** @type {ol3.Projection} */ this.get(
+ ol3.MapProperty.USER_PROJECTION);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'getUserProjection',
+ ol3.Map.prototype.getUserProjection);
+
+
+/**
+ * @return {Element} Viewport.
+ */
+ol3.Map.prototype.getViewport = function() {
+ return this.viewport_;
+};
+
+
+/**
+ * @param {goog.events.BrowserEvent} browserEvent Browser event.
+ * @param {string=} opt_type Type.
+ */
+ol3.Map.prototype.handleBrowserEvent = function(browserEvent, opt_type) {
+ var type = opt_type || browserEvent.type;
+ var mapBrowserEvent = new ol3.MapBrowserEvent(type, this, browserEvent);
+ var interactions = this.getInteractions();
+ var interactionsArray = /** @type {Array.} */
+ interactions.getArray();
+ goog.array.every(interactionsArray, function(interaction) {
+ interaction.handleMapBrowserEvent(mapBrowserEvent);
+ return !mapBrowserEvent.defaultPrevented;
+ });
+};
+
+
+/**
+ * @param {goog.fx.DragEvent} dragEvent Drag event.
+ */
+ol3.Map.prototype.handleDraggerEvent = function(dragEvent) {
+ var browserEvent = dragEvent.browserEvent;
+ this.handleBrowserEvent(browserEvent, dragEvent.type);
+};
+
+
+/**
+ * @protected
+ */
+ol3.Map.prototype.handleProjectionChanged = function() {
+ this.recalculateTransforms_();
+};
+
+
+/**
+ * @protected
+ */
+ol3.Map.prototype.handleUserProjectionChanged = function() {
+ this.recalculateTransforms_();
+};
+
+
+/**
+ * @protected
+ */
+ol3.Map.prototype.handleBrowserWindowResize = function() {
+ var size = new ol3.Size(this.container_.clientWidth,
+ this.container_.clientHeight);
+ this.setSize(size);
+};
+
+
+/**
+ * @return {boolean} Is animating.
+ */
+ol3.Map.prototype.isAnimating = function() {
+ return this.animatingCount_ > 0;
+};
+
+
+/**
+ * @return {boolean} Is defined.
+ */
+ol3.Map.prototype.isDef = function() {
+ return goog.isDefAndNotNull(this.getCenter()) &&
+ goog.isDef(this.getResolution()) &&
+ goog.isDefAndNotNull(this.getSize());
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.Map.prototype.onAnimationFrame = function() {
+ if (goog.DEBUG) {
+ this.logger.info('onAnimationFrame');
+ }
+ this.renderFrame_();
+};
+
+
+/**
+ * @private
+ */
+ol3.Map.prototype.recalculateTransforms_ = function() {
+ var projection = this.getProjection();
+ var userProjection = this.getUserProjection();
+ if (goog.isDefAndNotNull(projection) &&
+ goog.isDefAndNotNull(userProjection)) {
+ this.mapToUserTransform_ = ol3.Projection.getTransform(
+ projection, userProjection);
+ this.userToMapTransform_ = ol3.Projection.getTransform(
+ userProjection, projection);
+ } else {
+ this.mapToUserTransform_ = ol3.Projection.cloneTransform;
+ this.userToMapTransform_ = ol3.Projection.identityTransform;
+ }
+};
+
+
+/**
+ */
+ol3.Map.prototype.render = function() {
+ if (this.animatingCount_ < 1) {
+ if (this.freezeRenderingCount_ === 0) {
+ this.renderFrame_();
+ } else {
+ this.dirty_ = true;
+ }
+ }
+};
+
+
+/**
+ * @private
+ */
+ol3.Map.prototype.renderFrame_ = function() {
+ if (goog.DEBUG) {
+ this.logger.info('renderFrame_');
+ }
+ var animatedRenderer = this.renderer_.render();
+ this.dirty_ = false;
+ if (animatedRenderer != this.animatedRenderer_) {
+ if (animatedRenderer) {
+ this.startAnimating();
+ } else {
+ this.stopAnimating();
+ }
+ this.animatedRenderer_ = animatedRenderer;
+ }
+ if (goog.DEBUG) {
+ this.logger.info('postrender');
+ }
+ this.dispatchEvent(ol3.MapEventType.POST_RENDER);
+};
+
+
+/**
+ * @param {ol3.Color} backgroundColor Background color.
+ */
+ol3.Map.prototype.setBackgroundColor = function(backgroundColor) {
+ this.set(ol3.MapProperty.BACKGROUND_COLOR, backgroundColor);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'setBackgroundColor',
+ ol3.Map.prototype.setBackgroundColor);
+
+
+/**
+ * @param {ol3.Coordinate|undefined} center Center.
+ */
+ol3.Map.prototype.setCenter = function(center) {
+ this.set(ol3.MapProperty.CENTER, center);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'setCenter',
+ ol3.Map.prototype.setCenter);
+
+
+/**
+ * @param {ol3.Collection} interactions Interactions.
+ */
+ol3.Map.prototype.setInteractions = function(interactions) {
+ this.set(ol3.MapProperty.INTERACTIONS, interactions);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'setInteractions',
+ ol3.Map.prototype.setInteractions);
+
+
+/**
+ * @export
+ * @param {ol3.Collection} layers Layers.
+ */
+ol3.Map.prototype.setLayers = function(layers) {
+ this.set(ol3.MapProperty.LAYERS, layers);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'setLayers',
+ ol3.Map.prototype.setLayers);
+
+
+/**
+ * @export
+ * @param {ol3.Projection} projection Projection.
+ */
+ol3.Map.prototype.setProjection = function(projection) {
+ this.set(ol3.MapProperty.PROJECTION, projection);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'setProjection',
+ ol3.Map.prototype.setProjection);
+
+
+/**
+ * @export
+ * @param {number|undefined} resolution Resolution.
+ */
+ol3.Map.prototype.setResolution = function(resolution) {
+ this.set(ol3.MapProperty.RESOLUTION, resolution);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'setResolution',
+ ol3.Map.prototype.setResolution);
+
+
+/**
+ * @export
+ * @param {number|undefined} rotation Rotation.
+ */
+ol3.Map.prototype.setRotation = function(rotation) {
+ this.set(ol3.MapProperty.ROTATION, rotation);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'setRotation',
+ ol3.Map.prototype.setRotation);
+
+
+/**
+ * @param {ol3.Size} size Size.
+ */
+ol3.Map.prototype.setSize = function(size) {
+ var currentSize = this.getSize();
+ if (!goog.isDef(currentSize) || !currentSize.equals(size)) {
+ this.set(ol3.MapProperty.SIZE, size);
+ }
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'setSize',
+ ol3.Map.prototype.setSize);
+
+
+/**
+ * @export
+ * @param {ol3.Coordinate} userCenter Center in user projection.
+ */
+ol3.Map.prototype.setUserCenter = function(userCenter) {
+ this.setCenter(this.userToMapTransform_(userCenter));
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'setUserCenter',
+ ol3.Map.prototype.setUserCenter);
+
+
+/**
+ * @export
+ * @param {ol3.Projection} userProjection User projection.
+ */
+ol3.Map.prototype.setUserProjection = function(userProjection) {
+ this.set(ol3.MapProperty.USER_PROJECTION, userProjection);
+};
+goog.exportProperty(
+ ol3.Map.prototype,
+ 'setUserProjection',
+ ol3.Map.prototype.setUserProjection);
+
+
+/**
+ */
+ol3.Map.prototype.startAnimating = function() {
+ if (++this.animatingCount_ == 1) {
+ if (goog.DEBUG) {
+ this.logger.info('startAnimating');
+ }
+ goog.fx.anim.registerAnimation(this);
+ }
+};
+
+
+/**
+ */
+ol3.Map.prototype.stopAnimating = function() {
+ goog.asserts.assert(this.animatingCount_ > 0);
+ if (--this.animatingCount_ === 0) {
+ if (goog.DEBUG) {
+ this.logger.info('stopAnimating');
+ }
+ goog.fx.anim.unregisterAnimation(this);
+ }
+};
+
+
+/**
+ */
+ol3.Map.prototype.unfreezeRendering = function() {
+ goog.asserts.assert(this.freezeRenderingCount_ > 0);
+ if (--this.freezeRenderingCount_ === 0 &&
+ this.animatingCount_ < 1 &&
+ this.dirty_) {
+ this.renderFrame_();
+ }
+};
+
+
+/**
+ * @param {function(this: T)} f Function.
+ * @param {T=} opt_obj Object.
+ * @template T
+ */
+ol3.Map.prototype.withFrozenRendering = function(f, opt_obj) {
+ this.freezeRendering();
+ try {
+ f.call(opt_obj);
+ } finally {
+ this.unfreezeRendering();
+ }
+};
diff --git a/src/ol3/base/mapbrowserevent.js b/src/ol3/base/mapbrowserevent.js
new file mode 100644
index 0000000000..25b8081b04
--- /dev/null
+++ b/src/ol3/base/mapbrowserevent.js
@@ -0,0 +1,53 @@
+goog.provide('ol3.MapBrowserEvent');
+
+goog.require('goog.events.BrowserEvent');
+goog.require('goog.style');
+goog.require('ol3.Coordinate');
+goog.require('ol3.MapEvent');
+goog.require('ol3.Pixel');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.MapEvent}
+ * @param {string} type Event type.
+ * @param {ol3.Map} map Map.
+ * @param {goog.events.BrowserEvent} browserEvent Browser event.
+ */
+ol3.MapBrowserEvent = function(type, map, browserEvent) {
+
+ goog.base(this, type, map);
+
+ /**
+ * @type {goog.events.BrowserEvent}
+ */
+ this.browserEvent = browserEvent;
+
+ /**
+ * @private
+ * @type {ol3.Coordinate|undefined}
+ */
+ this.coordinate_ = undefined;
+
+};
+goog.inherits(ol3.MapBrowserEvent, ol3.MapEvent);
+
+
+/**
+ * @return {ol3.Coordinate|undefined} Coordinate.
+ */
+ol3.MapBrowserEvent.prototype.getCoordinate = function() {
+ if (goog.isDef(this.coordinate_)) {
+ return this.coordinate_;
+ } else {
+ var map = this.map;
+ var browserEvent = this.browserEvent;
+ var eventPosition = goog.style.getRelativePosition(
+ browserEvent, map.getViewport());
+ var pixel = new ol3.Pixel(eventPosition.x, eventPosition.y);
+ var coordinate = map.getCoordinateFromPixel(pixel);
+ this.coordinate_ = coordinate;
+ return coordinate;
+ }
+};
diff --git a/src/ol3/base/mapevent.js b/src/ol3/base/mapevent.js
new file mode 100644
index 0000000000..52bd32a23a
--- /dev/null
+++ b/src/ol3/base/mapevent.js
@@ -0,0 +1,36 @@
+goog.provide('ol3.MapEvent');
+
+goog.require('goog.events.Event');
+
+
+
+/**
+ * @constructor
+ * @extends {goog.events.Event}
+ * @param {string} type Event type.
+ * @param {ol3.Map} map Map.
+ */
+ol3.MapEvent = function(type, map) {
+
+ goog.base(this, type);
+
+ /**
+ * @type {ol3.Map}
+ */
+ this.map = map;
+
+ /**
+ * @type {boolean}
+ */
+ this.defaultPrevented = false;
+
+};
+goog.inherits(ol3.MapEvent, goog.events.Event);
+
+
+/**
+ */
+ol3.MapEvent.prototype.preventDefault = function() {
+ goog.base(this, 'preventDefault');
+ this.defaultPrevented = true;
+};
diff --git a/src/ol3/base/maprenderer.js b/src/ol3/base/maprenderer.js
new file mode 100644
index 0000000000..537b44113b
--- /dev/null
+++ b/src/ol3/base/maprenderer.js
@@ -0,0 +1,376 @@
+goog.provide('ol3.MapRenderer');
+
+goog.require('goog.Disposable');
+goog.require('goog.events');
+goog.require('goog.fx.anim');
+goog.require('goog.fx.anim.Animated');
+goog.require('goog.vec.Mat4');
+goog.require('ol3.Map');
+goog.require('ol3.MapProperty');
+
+
+
+/**
+ * @constructor
+ * @extends {goog.Disposable}
+ * @param {Element} container Container.
+ * @param {ol3.Map} map Map.
+ */
+ol3.MapRenderer = function(container, map) {
+
+ goog.base(this);
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.container_ = container;
+
+ /**
+ * @protected
+ * @type {ol3.Map}
+ */
+ this.map = map;
+
+ /**
+ * @protected
+ * @type {Object.}
+ */
+ this.layerRenderers = {};
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.layersListenerKeys_ = null;
+
+ /**
+ * @private
+ * @type {goog.vec.Mat4.Number}
+ */
+ this.coordinateToPixelMatrix_ = goog.vec.Mat4.createNumber();
+
+ /**
+ * @private
+ * @type {goog.vec.Mat4.Number}
+ */
+ this.pixelToCoordinateMatrix_ = goog.vec.Mat4.createNumber();
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.matricesDirty_ = true;
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.mapListenerKeys_ = [
+ goog.events.listen(
+ map, ol3.Object.getChangedEventType(ol3.MapProperty.BACKGROUND_COLOR),
+ this.handleBackgroundColorChanged, false, this),
+
+ goog.events.listen(
+ map, ol3.Object.getChangedEventType(ol3.MapProperty.CENTER),
+ this.handleCenterChanged, false, this),
+
+ goog.events.listen(
+ map, ol3.Object.getChangedEventType(ol3.MapProperty.LAYERS),
+ this.handleLayersChanged, false, this),
+
+ goog.events.listen(
+ map, ol3.Object.getChangedEventType(ol3.MapProperty.RESOLUTION),
+ this.handleResolutionChanged, false, this),
+
+ goog.events.listen(
+ map, ol3.Object.getChangedEventType(ol3.MapProperty.ROTATION),
+ this.handleRotationChanged, false, this),
+
+ goog.events.listen(
+ map, ol3.Object.getChangedEventType(ol3.MapProperty.SIZE),
+ this.handleSizeChanged, false, this)
+ ];
+
+};
+goog.inherits(ol3.MapRenderer, goog.Disposable);
+
+
+/**
+ * @param {ol3.Layer} layer Layer.
+ * @protected
+ */
+ol3.MapRenderer.prototype.addLayer = function(layer) {
+ var layerRenderer = this.createLayerRenderer(layer);
+ this.setLayerRenderer(layer, layerRenderer);
+};
+
+
+/**
+ * @return {boolean} Can rotate.
+ */
+ol3.MapRenderer.prototype.canRotate = goog.functions.FALSE;
+
+
+/**
+ * @param {ol3.Layer} layer Layer.
+ * @protected
+ * @return {ol3.LayerRenderer} layerRenderer Layer renderer.
+ */
+ol3.MapRenderer.prototype.createLayerRenderer = goog.abstractMethod;
+
+
+/**
+ * @inheritDoc
+ */
+ol3.MapRenderer.prototype.disposeInternal = function() {
+ goog.object.forEach(this.layerRenderers, function(layerRenderer) {
+ goog.dispose(layerRenderer);
+ });
+ goog.array.forEach(this.mapListenerKeys_, goog.events.unlistenByKey);
+ if (!goog.isNull(this.layersListenerKeys_)) {
+ goog.array.forEach(this.layersListenerKeys_, goog.events.unlistenByKey);
+ }
+ goog.base(this, 'disposeInternal');
+};
+
+
+/**
+ * @param {function(this: T, ol3.Layer, ol3.LayerRenderer, number)} f Function.
+ * @param {T=} opt_obj Object.
+ * @template T
+ */
+ol3.MapRenderer.prototype.forEachReadyVisibleLayer = function(f, opt_obj) {
+ var layers = this.map.getLayers();
+ layers.forEach(function(layer, index) {
+ if (layer.isReady() && layer.getVisible()) {
+ var layerRenderer = this.getLayerRenderer(layer);
+ f.call(opt_obj, layer, layerRenderer, index);
+ }
+ }, this);
+};
+
+
+/**
+ * @param {ol3.Pixel} pixel Pixel.
+ * @return {ol3.Coordinate} Coordinate.
+ */
+ol3.MapRenderer.prototype.getCoordinateFromPixel = function(pixel) {
+ this.updateMatrices_();
+ var vec3 = [pixel.x, pixel.y, 0];
+ goog.vec.Mat4.multVec3(this.pixelToCoordinateMatrix_, vec3, vec3);
+ return new ol3.Coordinate(vec3[0], vec3[1]);
+};
+
+
+/**
+ * @param {ol3.Layer} layer Layer.
+ * @protected
+ * @return {ol3.LayerRenderer} Layer renderer.
+ */
+ol3.MapRenderer.prototype.getLayerRenderer = function(layer) {
+ var key = goog.getUid(layer);
+ var layerRenderer = this.layerRenderers[key];
+ goog.asserts.assert(goog.isDef(layerRenderer));
+ return layerRenderer;
+};
+
+
+/**
+ * @return {ol3.Map} Map.
+ */
+ol3.MapRenderer.prototype.getMap = function() {
+ return this.map;
+};
+
+
+/**
+ * @param {ol3.Coordinate} coordinate Coordinate.
+ * @return {ol3.Pixel} Pixel.
+ */
+ol3.MapRenderer.prototype.getPixelFromCoordinate = function(coordinate) {
+ this.updateMatrices_();
+ var vec3 = [coordinate.x, coordinate.y, 0];
+ goog.vec.Mat4.multVec3(this.coordinateToPixelMatrix_, vec3, vec3);
+ return new ol3.Pixel(vec3[0], vec3[1]);
+};
+
+
+/**
+ */
+ol3.MapRenderer.prototype.handleBackgroundColorChanged = goog.nullFunction;
+
+
+/**
+ * @protected
+ */
+ol3.MapRenderer.prototype.handleCenterChanged = function() {
+ this.matricesDirty_ = true;
+};
+
+
+/**
+ * @param {ol3.CollectionEvent} collectionEvent Collection event.
+ * @protected
+ */
+ol3.MapRenderer.prototype.handleLayersAdd = function(collectionEvent) {
+ var layer = /** @type {ol3.Layer} */ collectionEvent.elem;
+ this.addLayer(layer);
+};
+
+
+/**
+ * @protected
+ */
+ol3.MapRenderer.prototype.handleLayersChanged = function() {
+ var layerRenderers = goog.object.getValues(this.layerRenderers);
+ goog.array.forEach(layerRenderers, function(layerRenderer) {
+ this.removeLayerRenderer(layerRenderer);
+ }, this);
+ this.layerRenderers = {};
+ if (!goog.isNull(this.layersListenerKeys_)) {
+ goog.array.forEach(this.layersListenerKeys_, goog.events.unlistenByKey);
+ this.layersListenerKeys_ = null;
+ }
+ var layers = this.map.getLayers();
+ if (goog.isDefAndNotNull(layers)) {
+ layers.forEach(this.addLayer, this);
+ this.layersListenerKeys_ = [
+ goog.events.listen(layers, ol3.CollectionEventType.ADD,
+ this.handleLayersAdd, false, this),
+ goog.events.listen(layers, ol3.CollectionEventType.REMOVE,
+ this.handleLayersRemove, false, this)
+ ];
+ }
+};
+
+
+/**
+ * @param {ol3.CollectionEvent} collectionEvent Collection event.
+ * @protected
+ */
+ol3.MapRenderer.prototype.handleLayersRemove = function(collectionEvent) {
+ var layer = /** @type {ol3.Layer} */ collectionEvent.elem;
+ this.removeLayer(layer);
+};
+
+
+/**
+ * @protected
+ */
+ol3.MapRenderer.prototype.handleResolutionChanged = function() {
+ this.matricesDirty_ = true;
+};
+
+
+/**
+ * @protected
+ */
+ol3.MapRenderer.prototype.handleRotationChanged = function() {
+ this.matricesDirty_ = true;
+};
+
+
+/**
+ * @protected
+ */
+ol3.MapRenderer.prototype.handleSizeChanged = function() {
+ this.matricesDirty_ = true;
+};
+
+
+/**
+ * @param {ol3.Layer} layer Layer.
+ * @protected
+ */
+ol3.MapRenderer.prototype.removeLayer = function(layer) {
+ goog.dispose(this.removeLayerRenderer(layer));
+};
+
+
+/**
+ * @param {ol3.Layer} layer Layer.
+ * @return {ol3.LayerRenderer} Layer renderer.
+ * @protected
+ */
+ol3.MapRenderer.prototype.removeLayerRenderer = function(layer) {
+ var key = goog.getUid(layer);
+ if (key in this.layerRenderers) {
+ var layerRenderer = this.layerRenderers[key];
+ delete this.layerRenderers[key];
+ return layerRenderer;
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * @return {boolean} Animating.
+ */
+ol3.MapRenderer.prototype.render = function() {
+ var animate = false;
+ this.forEachReadyVisibleLayer(function(layer, layerRenderer) {
+ if (layerRenderer.render()) {
+ animate = true;
+ }
+ });
+ return animate;
+};
+
+
+/**
+ * @param {ol3.Layer} layer Layer.
+ * @param {ol3.LayerRenderer} layerRenderer Layer renderer.
+ * @protected
+ */
+ol3.MapRenderer.prototype.setLayerRenderer = function(layer, layerRenderer) {
+ var key = goog.getUid(layer);
+ goog.asserts.assert(!(key in this.layerRenderers));
+ this.layerRenderers[key] = layerRenderer;
+};
+
+
+/**
+ * @private
+ */
+ol3.MapRenderer.prototype.updateMatrices_ = function() {
+
+ if (this.matricesDirty_) {
+
+ var map = this.map;
+ var center = /** @type {!ol3.Coordinate} */ map.getCenter();
+ var resolution = /** @type {number} */ map.getResolution();
+ var rotation = map.getRotation();
+ var size = /** @type {!ol3.Size} */ map.getSize();
+
+ goog.vec.Mat4.makeIdentity(this.coordinateToPixelMatrix_);
+ goog.vec.Mat4.translate(this.coordinateToPixelMatrix_,
+ size.width / 2,
+ size.height / 2,
+ 0);
+ goog.vec.Mat4.scale(this.coordinateToPixelMatrix_,
+ 1 / resolution,
+ -1 / resolution,
+ 1);
+ if (this.canRotate() && goog.isDef(rotation)) {
+ goog.vec.Mat4.rotate(this.coordinateToPixelMatrix_,
+ -rotation,
+ 0,
+ 0,
+ 1);
+ }
+ goog.vec.Mat4.translate(this.coordinateToPixelMatrix_,
+ -center.x,
+ -center.y,
+ 0);
+
+ var inverted = goog.vec.Mat4.invert(
+ this.coordinateToPixelMatrix_, this.pixelToCoordinateMatrix_);
+ goog.asserts.assert(inverted);
+
+ this.matricesDirty_ = false;
+
+ }
+
+};
diff --git a/src/ol3/base/object.js b/src/ol3/base/object.js
new file mode 100644
index 0000000000..70f0d17591
--- /dev/null
+++ b/src/ol3/base/object.js
@@ -0,0 +1,267 @@
+
+/**
+ * @fileoverview An implementation of Google Maps' MVCObject.
+ * @see https://developers.google.com/maps/articles/mvcfun
+ * @see https://developers.google.com/maps/documentation/javascript/reference
+ */
+
+goog.provide('ol3.Object');
+
+goog.require('goog.array');
+goog.require('goog.events');
+goog.require('goog.events.EventTarget');
+goog.require('goog.object');
+
+
+/**
+ * @enum {string}
+ */
+ol3.ObjectProperty = {
+ ACCESSORS: 'ol_accessors_',
+ BINDINGS: 'ol_bindings_'
+};
+
+
+
+/**
+ * @constructor
+ * @extends {goog.events.EventTarget}
+ * @param {Object.=} opt_values Values.
+ */
+ol3.Object = function(opt_values) {
+ goog.base(this);
+ if (goog.isDef(opt_values)) {
+ this.setValues(opt_values);
+ }
+};
+goog.inherits(ol3.Object, goog.events.EventTarget);
+
+
+/**
+ * @private
+ * @type {Object.}
+ */
+ol3.Object.changedEventTypeCache_ = {};
+
+
+/**
+ * @private
+ * @type {Object.}
+ */
+ol3.Object.getterNameCache_ = {};
+
+
+/**
+ * @private
+ * @type {Object.}
+ */
+ol3.Object.setterNameCache_ = {};
+
+
+/**
+ * @param {string} str String.
+ * @return {string} Capitalized string.
+ */
+ol3.Object.capitalize = function(str) {
+ return str.substr(0, 1).toUpperCase() + str.substr(1);
+};
+
+
+/**
+ * @param {ol3.Object} obj Object.
+ * @return {Object.} Accessors.
+ */
+ol3.Object.getAccessors = function(obj) {
+ return obj[ol3.ObjectProperty.ACCESSORS] ||
+ (obj[ol3.ObjectProperty.ACCESSORS] = {});
+};
+
+
+/**
+ * @param {string} key Key.
+ * @return {string} Changed name.
+ */
+ol3.Object.getChangedEventType = function(key) {
+ return ol3.Object.changedEventTypeCache_[key] ||
+ (ol3.Object.changedEventTypeCache_[key] = key.toLowerCase() + '_changed');
+};
+
+
+/**
+ * @param {string} key String.
+ * @return {string} Getter name.
+ */
+ol3.Object.getGetterName = function(key) {
+ return ol3.Object.getterNameCache_[key] ||
+ (ol3.Object.getterNameCache_[key] = 'get' + ol3.Object.capitalize(key));
+};
+
+
+/**
+ * @param {ol3.Object} obj Object.
+ * @return {Object.} Listeners.
+ */
+ol3.Object.getListeners = function(obj) {
+ return obj[ol3.ObjectProperty.BINDINGS] ||
+ (obj[ol3.ObjectProperty.BINDINGS] = {});
+};
+
+
+/**
+ * @param {string} key String.
+ * @return {string} Setter name.
+ */
+ol3.Object.getSetterName = function(key) {
+ return ol3.Object.setterNameCache_[key] ||
+ (ol3.Object.setterNameCache_[key] = 'set' + ol3.Object.capitalize(key));
+};
+
+
+/**
+ * @param {string} key Key.
+ * @param {ol3.Object} target Target.
+ * @param {string=} opt_targetKey Target key.
+ * @param {boolean=} opt_noNotify No notify.
+ */
+ol3.Object.prototype.bindTo =
+ function(key, target, opt_targetKey, opt_noNotify) {
+ var targetKey = opt_targetKey || key;
+ this.unbind(key);
+ var eventType = ol3.Object.getChangedEventType(targetKey);
+ var listeners = ol3.Object.getListeners(this);
+ listeners[key] = goog.events.listen(target, eventType, function() {
+ this.notifyInternal_(key);
+ }, undefined, this);
+ var accessors = ol3.Object.getAccessors(this);
+ accessors[key] = {target: target, key: targetKey};
+ var noNotify = opt_noNotify || false;
+ if (!noNotify) {
+ this.notifyInternal_(key);
+ }
+};
+
+
+/**
+ * @param {string} key Key.
+ */
+ol3.Object.prototype.changed = goog.nullFunction;
+
+
+/**
+ * @param {string} key Key.
+ * @return {*} Value.
+ */
+ol3.Object.prototype.get = function(key) {
+ var accessors = ol3.Object.getAccessors(this);
+ if (goog.object.containsKey(accessors, key)) {
+ var accessor = accessors[key];
+ var target = accessor.target;
+ var targetKey = accessor.key;
+ var getterName = ol3.Object.getGetterName(targetKey);
+ if (target[getterName]) {
+ return target[getterName]();
+ } else {
+ return target.get(targetKey);
+ }
+ } else {
+ return this[key];
+ }
+};
+
+
+/**
+ * @param {string} key Key.
+ */
+ol3.Object.prototype.notify = function(key) {
+ var accessors = ol3.Object.getAccessors(this);
+ if (goog.object.containsKey(accessors, key)) {
+ var accessor = accessors[key];
+ var target = accessor.target;
+ var targetKey = accessor.key;
+ target.notify(targetKey);
+ } else {
+ this.notifyInternal_(key);
+ }
+};
+
+
+/**
+ * @param {string} key Key.
+ * @private
+ */
+ol3.Object.prototype.notifyInternal_ = function(key) {
+ var eventType = ol3.Object.getChangedEventType(key);
+ this.dispatchEvent(eventType);
+};
+
+
+/**
+ * @param {string} key Key.
+ * @param {*} value Value.
+ */
+ol3.Object.prototype.set = function(key, value) {
+ var accessors = ol3.Object.getAccessors(this);
+ if (goog.object.containsKey(accessors, key)) {
+ var accessor = accessors[key];
+ var target = accessor.target;
+ var targetKey = accessor.key;
+ var setterName = ol3.Object.getSetterName(targetKey);
+ if (target[setterName]) {
+ target[setterName](value);
+ } else {
+ target.set(targetKey, value);
+ }
+ } else {
+ this[key] = value;
+ this.notifyInternal_(key);
+ }
+};
+
+
+/**
+ * @param {Object.} options Options.
+ */
+ol3.Object.prototype.setOptions = function(options) {
+ goog.object.forEach(options, function(value, key) {
+ var setterName = ol3.Object.getSetterName(key);
+ if (this[setterName]) {
+ this[setterName](value);
+ } else {
+ this.set(key, value);
+ }
+ }, this);
+};
+
+
+/**
+ * @param {Object.} values Values.
+ */
+ol3.Object.prototype.setValues = ol3.Object.prototype.setOptions;
+
+
+/**
+ * @param {string} key Key.
+ */
+ol3.Object.prototype.unbind = function(key) {
+ var listeners = ol3.Object.getListeners(this);
+ var listener = listeners[key];
+ if (listener) {
+ delete listeners[key];
+ goog.events.unlistenByKey(listener);
+ var value = this.get(key);
+ var accessors = ol3.Object.getAccessors(this);
+ delete accessors[key];
+ this[key] = value;
+ }
+};
+
+
+/**
+ */
+ol3.Object.prototype.unbindAll = function() {
+ var listeners = ol3.Object.getListeners(this);
+ var keys = goog.object.getKeys(listeners);
+ goog.array.forEach(keys, function(key) {
+ this.unbind(key);
+ }, this);
+};
diff --git a/src/ol3/base/object_test.js b/src/ol3/base/object_test.js
new file mode 100644
index 0000000000..fc209929b1
--- /dev/null
+++ b/src/ol3/base/object_test.js
@@ -0,0 +1,389 @@
+goog.require('goog.testing.jsunit');
+goog.require('ol3.Object');
+
+
+function testModel() {
+ var m = new ol3.Object();
+ assertNotNullNorUndefined(m);
+}
+
+
+function testGetUndefined() {
+ var m = new ol3.Object();
+ assertUndefined(m.get('k'));
+}
+
+
+function testGetSetGet() {
+ var m = new ol3.Object();
+ assertUndefined(m.get('k'));
+ m.set('k', 1);
+ assertEquals(1, m.get('k'));
+}
+
+
+function testSetValues() {
+ var m = new ol3.Object();
+ m.setValues({
+ k1: 1,
+ k2: 2
+ });
+ assertEquals(1, m.get('k1'));
+ assertEquals(2, m.get('k2'));
+}
+
+
+function testNotifyKeyEvent() {
+ var m = new ol3.Object();
+ var eventDispatched = false;
+ goog.events.listen(m, 'k_changed', function() {
+ eventDispatched = true;
+ });
+ m.notify('k');
+ assertTrue(eventDispatched);
+}
+
+
+function testBindSetNotifyKeyEvent() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ var callbackCalled = false;
+ goog.events.listen(n, 'k_changed', function() {
+ eventDispatched = true;
+ });
+ n.bindTo('k', m);
+ m.set('k', 1);
+ assertTrue(eventDispatched);
+}
+
+
+function testSetNotifyKeyEvent() {
+ var m = new ol3.Object();
+ var eventDispatched = false;
+ goog.events.listen(m, 'k_changed', function() {
+ eventDispatched = true;
+ });
+ m.set('k', 1);
+ assertTrue(eventDispatched);
+}
+
+
+function testSetBind() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ m.set('k', 1);
+ assertEquals(1, m.get('k'));
+ assertUndefined(n.get('k'));
+ n.bindTo('k', m);
+ assertEquals(1, m.get('k'));
+ assertEquals(1, n.get('k'));
+}
+
+
+function testBindSet() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ n.bindTo('k', m);
+ m.set('k', 1);
+ assertEquals(1, m.get('k'));
+ assertEquals(1, n.get('k'));
+}
+
+
+function testBindSetBackwards() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ n.bindTo('k', m);
+ n.set('k', 1);
+ assertEquals(1, m.get('k'));
+ assertEquals(1, n.get('k'));
+}
+
+
+function testSetBindBackwards() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ n.set('k', 1);
+ n.bindTo('k', m);
+ assertUndefined(m.get('k'));
+ assertUndefined(n.get('k'));
+}
+
+
+function testBindSetUnbind() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ n.bindTo('k', m);
+ n.set('k', 1);
+ assertEquals(1, m.get('k'));
+ assertEquals(1, n.get('k'));
+ n.unbind('k');
+ assertEquals(1, m.get('k'));
+ assertEquals(1, n.get('k'));
+ n.set('k', 2);
+ assertEquals(1, m.get('k'));
+ assertEquals(2, n.get('k'));
+}
+
+
+function testUnbindAll() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ n.bindTo('k', m);
+ n.set('k', 1);
+ assertEquals(m.get('k'), 1);
+ assertEquals(n.get('k'), 1);
+ n.unbindAll();
+ assertEquals(m.get('k'), 1);
+ assertEquals(n.get('k'), 1);
+ n.set('k', 2);
+ assertEquals(m.get('k'), 1);
+ assertEquals(n.get('k'), 2);
+}
+
+
+function testBindNotify() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ m.bindTo('k', n);
+ mEventDispatched = false;
+ goog.events.listen(m, 'k_changed', function() {
+ mEventDispatched = true;
+ });
+ nEventDispatched = false;
+ goog.events.listen(n, 'k_changed', function() {
+ nEventDispatched = true;
+ });
+ n.set('k', 1);
+ assertTrue(mEventDispatched);
+ assertTrue(nEventDispatched);
+}
+
+
+function testBindBackwardsNotify() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ n.bindTo('k', m);
+ mEventDispatched = false;
+ goog.events.listen(m, 'k_changed', function() {
+ mEventDispatched = true;
+ });
+ nEventDispatched = false;
+ goog.events.listen(n, 'k_changed', function() {
+ nEventDispatched = true;
+ });
+ n.set('k', 1);
+ assertTrue(mEventDispatched);
+ assertTrue(nEventDispatched);
+}
+
+
+function testBindRename() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ n.bindTo('kn', m, 'km');
+ m.set('km', 1);
+ assertEquals(m.get('km'), 1);
+ assertEquals(n.get('kn'), 1);
+}
+
+
+function testBindRenameEvents() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ kmEventDispatched = false;
+ goog.events.listen(m, 'km_changed', function() {
+ kmEventDispatched = true;
+ });
+ knEventDispatched = false;
+ goog.events.listen(n, 'kn_changed', function() {
+ knEventDispatched = true;
+ });
+ n.bindTo('kn', m, 'km');
+ m.set('km', 1);
+ assertEquals(m.get('km'), 1);
+ assertEquals(n.get('kn'), 1);
+ assertTrue(kmEventDispatched);
+ assertTrue(knEventDispatched);
+}
+
+
+function testTransitiveBindForwards() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ var o = new ol3.Object();
+ n.bindTo('kn', m, 'km');
+ o.bindTo('ko', n, 'kn');
+ m.set('km', 1);
+ assertEquals(1, m.get('km'));
+ assertEquals(1, n.get('kn'));
+ assertEquals(1, o.get('ko'));
+}
+
+
+function testTransitiveBindBackwards() {
+ var m = new ol3.Object();
+ var n = new ol3.Object();
+ var o = new ol3.Object();
+ n.bindTo('kn', m, 'km');
+ o.bindTo('ko', n, 'kn');
+ o.set('ko', 1);
+ assertEquals(1, m.get('km'));
+ assertEquals(1, n.get('kn'));
+ assertEquals(1, o.get('ko'));
+}
+
+
+function testMrideyAccessors() {
+ // http://blog.mridey.com/2010/03/maps-javascript-api-v3-more-about.html
+ var a = new ol3.Object();
+ a.set('level', 2);
+ assertEquals(2, a.get('level'));
+ var b = new ol3.Object();
+ b.setValues({
+ level: 2,
+ index: 3,
+ description: 'Hello world.'
+ });
+ assertEquals(3, b.get('index'));
+}
+
+
+function testMrideyBinding() {
+ // http://blog.mridey.com/2010/03/maps-javascript-api-v3-more-about.html
+ var a = new ol3.Object();
+ a.set('level', 2);
+ var b = new ol3.Object();
+ b.bindTo('index', a, 'level');
+ assertEquals(2, b.get('index'));
+ a.set('level', 3);
+ assertEquals(3, b.get('index'));
+ b.set('index', 4);
+ assertEquals(4, a.get('level'));
+ var c = new ol3.Object();
+ c.bindTo('zoom', a, 'level');
+ assertEquals(4, c.get('zoom'));
+ b.unbind('index');
+ assertEquals(4, b.get('index'));
+ c.set('zoom', 5);
+ assertEquals(5, a.get('level'));
+ assertEquals(4, b.get('index'));
+}
+
+
+function testCircularBind() {
+ var a = new ol3.Object();
+ var b = new ol3.Object();
+ a.bindTo('k', b);
+ assertThrows(function() {
+ b.bindTo('k', a);
+ });
+}
+
+
+function testPriority() {
+ var a = new ol3.Object();
+ var b = new ol3.Object();
+ a.set('k', 1);
+ b.set('k', 2);
+ a.bindTo('k', b);
+ assertEquals(2, a.get('k'));
+ assertEquals(2, b.get('k'));
+}
+
+
+function testPriorityUndefined() {
+ var a = new ol3.Object();
+ var b = new ol3.Object();
+ a.set('k', 1);
+ a.bindTo('k', b);
+ assertUndefined(a.get('k'));
+ assertUndefined(b.get('k'));
+}
+
+
+function testSetter() {
+ var a = new ol3.Object();
+ var x;
+ var setterCalled;
+ a.setX = function(value) {
+ this.x = value;
+ setterCalled = true;
+ };
+ a.set('x', 1);
+ assertEquals(1, a.get('x'));
+ assertUndefined(setterCalled);
+}
+
+
+function testSetterBind() {
+ var a = new ol3.Object();
+ var x;
+ var setterCalled;
+ a.setX = function(value) {
+ this.x = value;
+ setterCalled = true;
+ };
+ var b = new ol3.Object();
+ b.bindTo('x', a);
+ b.set('x', 1);
+ assertEquals(1, a.get('x'));
+ assertEquals(1, b.get('x'));
+ assertTrue(setterCalled);
+}
+
+
+function testGetter() {
+ var a = new ol3.Object();
+ var getterCalled;
+ a.getX = function() {
+ getterCalled = true;
+ return 1;
+ };
+ assertUndefined(a.get('x'));
+ assertUndefined(getterCalled);
+}
+
+
+function testGetterBind() {
+ var a = new ol3.Object();
+ var getterCalled;
+ a.getX = function() {
+ getterCalled = true;
+ return 1;
+ };
+ var b = new ol3.Object();
+ b.bindTo('x', a);
+ assertEquals(1, b.get('x'));
+ assertTrue(getterCalled);
+}
+
+
+function testBindSelf() {
+ var a = new ol3.Object();
+ assertThrows(function() {
+ a.bindTo('k', a);
+ });
+}
+
+
+function testCreateWithOptions() {
+ var obj = new ol3.Object({k: 1});
+ assertEquals(1, obj.get('k'));
+}
+
+
+function testEventTypeCaseSensitivity() {
+ var obj = new ol3.Object();
+ var lowercaseEventDispatched = false;
+ goog.events.listen(obj, 'k_changed', function() {
+ lowercaseEventDispatched = true;
+ });
+ var uppercaseEventDispatched = false;
+ goog.events.listen(obj, 'K_changed', function() {
+ uppercaseEventDispatched = true;
+ });
+ obj.set('K', 1);
+ assertTrue(lowercaseEventDispatched);
+ assertFalse(uppercaseEventDispatched);
+}
diff --git a/src/ol3/base/ol.js b/src/ol3/base/ol.js
new file mode 100644
index 0000000000..d94603a335
--- /dev/null
+++ b/src/ol3/base/ol.js
@@ -0,0 +1,9 @@
+goog.provide('ol3');
+
+goog.require('goog.debug.Logger');
+
+
+if (goog.DEBUG) {
+ var logger = goog.debug.Logger.getLogger('ol3');
+ logger.setLevel(goog.debug.Logger.Level.FINEST);
+}
diff --git a/src/ol3/base/pixel.js b/src/ol3/base/pixel.js
new file mode 100644
index 0000000000..314766d64c
--- /dev/null
+++ b/src/ol3/base/pixel.js
@@ -0,0 +1,24 @@
+goog.provide('ol3.Pixel');
+
+goog.require('goog.math.Coordinate');
+
+
+
+/**
+ * @constructor
+ * @extends {goog.math.Coordinate}
+ * @param {number} x X.
+ * @param {number} y Y.
+ */
+ol3.Pixel = function(x, y) {
+ goog.base(this, x, y);
+};
+goog.inherits(ol3.Pixel, goog.math.Coordinate);
+
+
+/**
+ * @return {ol3.Pixel} Clone.
+ */
+ol3.Pixel.prototype.clone = function() {
+ return new ol3.Pixel(this.x, this.y);
+};
diff --git a/src/ol3/base/pixelbounds.js b/src/ol3/base/pixelbounds.js
new file mode 100644
index 0000000000..7b95314e30
--- /dev/null
+++ b/src/ol3/base/pixelbounds.js
@@ -0,0 +1,18 @@
+goog.provide('ol3.PixelBounds');
+
+goog.require('ol3.Rectangle');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Rectangle}
+ * @param {number} minX Minimum X.
+ * @param {number} minY Minimum Y.
+ * @param {number} maxX Maximum X.
+ * @param {number} maxY Maximum Y.
+ */
+ol3.PixelBounds = function(minX, minY, maxX, maxY) {
+ goog.base(this, minX, minY, maxX, maxY);
+};
+goog.inherits(ol3.PixelBounds, ol3.Rectangle);
diff --git a/src/ol3/base/projection.js b/src/ol3/base/projection.js
new file mode 100644
index 0000000000..779b5de8aa
--- /dev/null
+++ b/src/ol3/base/projection.js
@@ -0,0 +1,500 @@
+goog.provide('ol3.Projection');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.object');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Extent');
+goog.require('ol3.TransformFunction');
+
+
+/**
+ * @define {boolean} Enable Proj4js.
+ */
+ol3.ENABLE_PROJ4JS = true;
+
+
+/**
+ * @enum {string}
+ */
+ol3.ProjectionUnits = {
+ DEGREES: 'degrees',
+ METERS: 'm'
+};
+
+
+
+/**
+ * @constructor
+ * @param {string} code Code.
+ * @param {ol3.ProjectionUnits} units Units.
+ * @param {ol3.Extent} extent Extent.
+ */
+ol3.Projection = function(code, units, extent) {
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.code_ = code;
+
+ /**
+ * @private
+ * @type {ol3.ProjectionUnits}
+ */
+ this.units_ = units;
+
+ /**
+ * @private
+ * @type {ol3.Extent}
+ */
+ this.extent_ = extent;
+
+};
+
+
+/**
+ * @return {string} Code.
+ */
+ol3.Projection.prototype.getCode = function() {
+ return this.code_;
+};
+
+
+/**
+ * @return {ol3.Extent} Extent.
+ */
+ol3.Projection.prototype.getExtent = function() {
+ return this.extent_;
+};
+
+
+/**
+ * @return {ol3.ProjectionUnits} Units.
+ */
+ol3.Projection.prototype.getUnits = function() {
+ return this.units_;
+};
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Projection}
+ * @param {string} code Code.
+ * @param {Proj4js.Proj} proj4jsProj Proj4js projection.
+ */
+ol3.Proj4jsProjection = function(code, proj4jsProj) {
+
+ var units = /** @type {ol3.ProjectionUnits} */ proj4jsProj.units;
+
+ goog.base(this, code, units, null);
+
+ /**
+ * @private
+ * @type {Proj4js.Proj}
+ */
+ this.proj4jsProj_ = proj4jsProj;
+
+};
+goog.inherits(ol3.Proj4jsProjection, ol3.Projection);
+
+
+/**
+ * @return {Proj4js.Proj} Proj4js projection.
+ */
+ol3.Proj4jsProjection.prototype.getProj4jsProj = function() {
+ return this.proj4jsProj_;
+};
+
+
+/**
+ * @private
+ * @type {Object.}
+ */
+ol3.Projection.proj4jsProjections_ = {};
+
+
+/**
+ * @private
+ * @type {Object.}
+ */
+ol3.Projection.projections_ = {};
+
+
+/**
+ * @private
+ * @type {Object.>}
+ */
+ol3.Projection.transforms_ = {};
+
+
+/**
+ * @param {Array.} projections Projections.
+ * @private
+ */
+ol3.Projection.addEquivalentProjections_ = function(projections) {
+ ol3.Projection.addProjections(projections);
+ goog.array.forEach(projections, function(source) {
+ goog.array.forEach(projections, function(destination) {
+ ol3.Projection.addTransform(
+ source, destination, ol3.Projection.cloneTransform);
+ });
+ });
+};
+
+
+/**
+ * @param {Array.} projections1 Projections.
+ * @param {Array.} projections2 Projections.
+ * @param {ol3.TransformFunction} forwardTransform Forward transform.
+ * @param {ol3.TransformFunction} inverseTransform Inverse transform.
+ * @private
+ */
+ol3.Projection.addEquivalentTransforms_ =
+ function(projections1, projections2, forwardTransform, inverseTransform) {
+ goog.array.forEach(projections1, function(projection1) {
+ goog.array.forEach(projections2, function(projection2) {
+ ol3.Projection.addTransform(projection1, projection2, forwardTransform);
+ ol3.Projection.addTransform(projection2, projection1, inverseTransform);
+ });
+ });
+};
+
+
+/**
+ * @param {ol3.Proj4jsProjection} proj4jsProjection Proj4js projection.
+ */
+ol3.Projection.addProj4jsProjection = function(proj4jsProjection) {
+ var proj4jsProjections = ol3.Projection.proj4jsProjections_;
+ var code = proj4jsProjection.getCode();
+ goog.asserts.assert(!goog.object.containsKey(proj4jsProjections, code));
+ proj4jsProjections[code] = proj4jsProjection;
+};
+
+
+/**
+ * @param {ol3.Projection} projection Projection.
+ */
+ol3.Projection.addProjection = function(projection) {
+ var projections = ol3.Projection.projections_;
+ var code = projection.getCode();
+ goog.asserts.assert(!goog.object.containsKey(projections, code));
+ projections[code] = projection;
+};
+
+
+/**
+ * @param {Array.} projections Projections.
+ */
+ol3.Projection.addProjections = function(projections) {
+ goog.array.forEach(projections, function(projection) {
+ ol3.Projection.addProjection(projection);
+ });
+};
+
+
+/**
+ * @param {ol3.Projection} source Source.
+ * @param {ol3.Projection} destination Destination.
+ * @param {ol3.TransformFunction} transformFn Transform.
+ */
+ol3.Projection.addTransform = function(source, destination, transformFn) {
+ var projections = ol3.Projection.projections_;
+ var sourceCode = source.getCode();
+ var destinationCode = destination.getCode();
+ var transforms = ol3.Projection.transforms_;
+ if (!goog.object.containsKey(transforms, sourceCode)) {
+ transforms[sourceCode] = {};
+ }
+ goog.asserts.assert(
+ !goog.object.containsKey(transforms[sourceCode], destinationCode));
+ transforms[sourceCode][destinationCode] = transformFn;
+};
+
+
+/**
+ * @param {string} code Code.
+ * @return {ol3.Projection} Projection.
+ */
+ol3.Projection.getFromCode = function(code) {
+ var projection = ol3.Projection.projections_[code];
+ if (ol3.Projection.isProj4jsSupported() && !goog.isDef(projection)) {
+ projection = ol3.Projection.getProj4jsProjectionFromCode_(code);
+ }
+ if (!goog.isDef(projection)) {
+ goog.asserts.assert(goog.isDef(projection));
+ projection = null;
+ }
+ return projection;
+};
+
+
+/**
+ * @param {string} code Code.
+ * @private
+ * @return {ol3.Proj4jsProjection} Proj4js projection.
+ */
+ol3.Projection.getProj4jsProjectionFromCode_ = function(code) {
+ var proj4jsProjections = ol3.Projection.proj4jsProjections_;
+ var proj4jsProjection = proj4jsProjections[code];
+ if (!goog.isDef(proj4jsProjection)) {
+ var proj4jsProj = new Proj4js.Proj(code);
+ proj4jsProjection = new ol3.Proj4jsProjection(code, proj4jsProj);
+ proj4jsProjections[code] = proj4jsProjection;
+ }
+ return proj4jsProjection;
+};
+
+
+/**
+ * @param {ol3.Projection} projection1 Projection 1.
+ * @param {ol3.Projection} projection2 Projection 2.
+ * @return {boolean} Equivalent.
+ */
+ol3.Projection.equivalent = function(projection1, projection2) {
+ if (projection1 === projection2) {
+ return true;
+ } else if (projection1.getUnits() != projection2.getUnits()) {
+ return false;
+ } else {
+ var transformFn = ol3.Projection.getTransform(projection1, projection2);
+ return transformFn === ol3.Projection.cloneTransform;
+ }
+};
+
+
+/**
+ * @param {ol3.Projection} source Source.
+ * @param {ol3.Projection} destination Destination.
+ * @return {ol3.TransformFunction} Transform.
+ */
+ol3.Projection.getTransform = function(source, destination) {
+ var transforms = ol3.Projection.transforms_;
+ var sourceCode = source.getCode();
+ var destinationCode = destination.getCode();
+ var transform;
+ if (goog.object.containsKey(transforms, sourceCode) &&
+ goog.object.containsKey(transforms[sourceCode], destinationCode)) {
+ transform = transforms[sourceCode][destinationCode];
+ }
+ if (ol3.Projection.isProj4jsSupported() && !goog.isDef(transform)) {
+ var proj4jsSource;
+ if (source instanceof ol3.Proj4jsProjection) {
+ proj4jsSource = source;
+ } else {
+ proj4jsSource =
+ ol3.Projection.getProj4jsProjectionFromCode_(source.getCode());
+ }
+ var sourceProj4jsProj = proj4jsSource.getProj4jsProj();
+ var proj4jsDestination;
+ if (destination instanceof ol3.Proj4jsProjection) {
+ proj4jsDestination = destination;
+ } else {
+ proj4jsDestination =
+ ol3.Projection.getProj4jsProjectionFromCode_(source.getCode());
+ }
+ var destinationProj4jsProj = proj4jsDestination.getProj4jsProj();
+ transform =
+ /**
+ * @param {ol3.Coordinate} coordinate Coordinate.
+ * @return {ol3.Coordinate} Coordinate.
+ */
+ function(coordinate) {
+ var proj4jsPoint = new Proj4js.Point(coordinate.x, coordinate.y);
+ proj4jsPoint = Proj4js.transform(
+ sourceProj4jsProj, destinationProj4jsProj, proj4jsPoint);
+ return new ol3.Coordinate(proj4jsPoint.x, proj4jsPoint.y);
+ };
+ ol3.Projection.addTransform(source, destination, transform);
+ }
+ if (!goog.isDef(transform)) {
+ goog.asserts.assert(goog.isDef(transform));
+ transform = ol3.Projection.identityTransform;
+ }
+ return transform;
+};
+
+
+/**
+ * @param {string} sourceCode Source code.
+ * @param {string} destinationCode Destination code.
+ * @return {ol3.TransformFunction} Transform.
+ */
+ol3.Projection.getTransformFromCodes = function(sourceCode, destinationCode) {
+ var source = ol3.Projection.getFromCode(sourceCode);
+ var destination = ol3.Projection.getFromCode(destinationCode);
+ return ol3.Projection.getTransform(source, destination);
+};
+
+
+/**
+ * @return {boolean} Has Proj4js.
+ */
+ol3.Projection.isProj4jsSupported = function() {
+ return ol3.ENABLE_PROJ4JS && 'Proj4js' in goog.global;
+};
+
+
+/**
+ * @param {ol3.Coordinate} point Point.
+ * @return {ol3.Coordinate} Point.
+ */
+ol3.Projection.identityTransform = function(point) {
+ return point;
+};
+
+
+/**
+ * @param {ol3.Coordinate} point Point.
+ * @return {ol3.Coordinate} Point.
+ */
+ol3.Projection.cloneTransform = function(point) {
+ return point.clone();
+};
+
+
+/**
+ * @param {ol3.Coordinate} point Point.
+ * @param {ol3.Projection} source Source.
+ * @param {ol3.Projection} destination Destination.
+ * @return {ol3.Coordinate} Point.
+ */
+ol3.Projection.transform = function(point, source, destination) {
+ var transformFn = ol3.Projection.getTransform(source, destination);
+ return transformFn(point);
+};
+
+
+/**
+ * @param {ol3.Coordinate} point Point.
+ * @param {string} sourceCode Source code.
+ * @param {string} destinationCode Destination code.
+ * @return {ol3.Coordinate} Point.
+ */
+ol3.Projection.transformWithCodes =
+ function(point, sourceCode, destinationCode) {
+ var transformFn = ol3.Projection.getTransformFromCodes(
+ sourceCode, destinationCode);
+ return transformFn(point);
+};
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol3.Projection.EPSG_3857_RADIUS = 6378137;
+
+
+/**
+ * @param {ol3.Coordinate} point Point.
+ * @return {ol3.Coordinate} Point.
+ */
+ol3.Projection.forwardSphericalMercator = function(point) {
+ var x = ol3.Projection.EPSG_3857_RADIUS * Math.PI * point.x / 180;
+ var y = ol3.Projection.EPSG_3857_RADIUS *
+ Math.log(Math.tan(Math.PI * (point.y + 90) / 360));
+ return new ol3.Coordinate(x, y);
+};
+
+
+/**
+ * @param {ol3.Coordinate} point Point.
+ * @return {ol3.Coordinate} Point.
+ */
+ol3.Projection.inverseSphericalMercator = function(point) {
+ var x = 180 * point.x / (ol3.Projection.EPSG_3857_RADIUS * Math.PI);
+ var y = 360 * Math.atan(
+ Math.exp(point.y / ol3.Projection.EPSG_3857_RADIUS)) / Math.PI - 90;
+ return new ol3.Coordinate(x, y);
+};
+
+
+/**
+ * @const
+ * @type {number}
+ */
+ol3.Projection.EPSG_3857_HALF_SIZE = Math.PI * ol3.Projection.EPSG_3857_RADIUS;
+
+
+/**
+ * @const
+ * @type {ol3.Extent}
+ */
+ol3.Projection.EPSG_3857_EXTENT = new ol3.Extent(
+ -ol3.Projection.EPSG_3857_HALF_SIZE,
+ -ol3.Projection.EPSG_3857_HALF_SIZE,
+ ol3.Projection.EPSG_3857_HALF_SIZE,
+ ol3.Projection.EPSG_3857_HALF_SIZE);
+
+
+/**
+ * @private
+ * @type {Array.}
+ */
+ol3.Projection.EPSG_3857_LIKE_CODES_ = [
+ 'EPSG:3857',
+ 'EPSG:102100',
+ 'EPSG:102113',
+ 'EPSG:900913'
+];
+
+
+/**
+ * @const
+ * @private
+ * @type {Array.}
+ */
+ol3.Projection.EPSG_3857_LIKE_PROJECTIONS_ = goog.array.map(
+ ol3.Projection.EPSG_3857_LIKE_CODES_,
+ function(code) {
+ return new ol3.Projection(
+ code,
+ ol3.ProjectionUnits.METERS,
+ ol3.Projection.EPSG_3857_EXTENT);
+ });
+
+
+/**
+ * @const
+ * @private
+ * @type {ol3.Extent}
+ */
+ol3.Projection.EPSG_4326_EXTENT_ = new ol3.Extent(-180, -90, 180, 90);
+
+
+/**
+ * @private
+ * @type {Array.}
+ */
+ol3.Projection.EPSG_4326_LIKE_CODES_ = [
+ 'CRS:84',
+ 'EPSG:4326',
+ 'urn:ogc:def:crs:EPSG:6.6:4326'
+];
+
+
+/**
+ * @const
+ * @type {Array.}
+ */
+ol3.Projection.EPSG_4326_LIKE_PROJECTIONS = goog.array.map(
+ ol3.Projection.EPSG_4326_LIKE_CODES_,
+ function(code) {
+ return new ol3.Projection(
+ code,
+ ol3.ProjectionUnits.DEGREES,
+ ol3.Projection.EPSG_4326_EXTENT_);
+ });
+
+
+ol3.Projection.addEquivalentProjections_(
+ ol3.Projection.EPSG_3857_LIKE_PROJECTIONS_);
+ol3.Projection.addEquivalentProjections_(
+ ol3.Projection.EPSG_4326_LIKE_PROJECTIONS);
+ol3.Projection.addEquivalentTransforms_(
+ ol3.Projection.EPSG_4326_LIKE_PROJECTIONS,
+ ol3.Projection.EPSG_3857_LIKE_PROJECTIONS_,
+ ol3.Projection.forwardSphericalMercator,
+ ol3.Projection.inverseSphericalMercator);
diff --git a/src/ol3/base/projection_test.js b/src/ol3/base/projection_test.js
new file mode 100644
index 0000000000..3e4d8568b7
--- /dev/null
+++ b/src/ol3/base/projection_test.js
@@ -0,0 +1,87 @@
+goog.require('goog.array');
+goog.require('goog.testing.jsunit');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Projection');
+
+
+function _testAllEquivalent(codes) {
+ var projections = goog.array.map(codes, ol3.Projection.getFromCode);
+ goog.array.forEach(projections, function(source) {
+ goog.array.forEach(projections, function(destination) {
+ assertTrue(ol3.Projection.equivalent(source, destination));
+ });
+ });
+}
+
+
+function testEpsg3857Equivalence() {
+ _testAllEquivalent([
+ 'EPSG:3857',
+ 'EPSG:102100',
+ 'EPSG:102113',
+ 'EPSG:900913'
+ ]);
+}
+
+
+function testEpsg4326Equivalence() {
+ _testAllEquivalent([
+ 'CRS:84',
+ 'urn:ogc:def:crs:EPSG:6.6:4326',
+ 'EPSG:4326'
+ ]);
+}
+
+
+function testIdentityTransform() {
+ var epsg4326 = ol3.Projection.getFromCode('EPSG:4326');
+ var uniqueObject = {};
+ var sourcePoint = new ol3.Coordinate(uniqueObject, uniqueObject);
+ var destinationPoint = ol3.Projection.transform(
+ sourcePoint, epsg4326, epsg4326);
+ assertFalse(sourcePoint === destinationPoint);
+ assertTrue(destinationPoint.x === sourcePoint.x);
+ assertTrue(destinationPoint.y === sourcePoint.y);
+}
+
+
+function testForwardSphericalMercatorOrigin() {
+ var point = ol3.Projection.transformWithCodes(
+ new ol3.Coordinate(0, 0), 'EPSG:4326', 'EPSG:3857');
+ assertNotNullNorUndefined(point);
+ assertEquals(0, point.x);
+ assertRoughlyEquals(0, point.y, 1e-9);
+}
+
+
+function testInverseSphericalMercatorOrigin() {
+ var point = ol3.Projection.transformWithCodes(
+ new ol3.Coordinate(0, 0), 'EPSG:3857', 'EPSG:4326');
+ assertNotNullNorUndefined(point);
+ assertEquals(0, point.x);
+ assertEquals(0, point.y);
+}
+
+
+function testForwardSphericalMercatorAlastaira() {
+ // http://alastaira.wordpress.com/2011/01/23/the-google-maps-bing-maps-spherical-mercator-projection/
+ var point = ol3.Projection.transformWithCodes(
+ new ol3.Coordinate(-5.625, 52.4827802220782),
+ 'EPSG:4326',
+ 'EPSG:900913');
+ assertNotNullNorUndefined(point);
+ assertRoughlyEquals(-626172.13571216376, point.x, 1e-9);
+ assertRoughlyEquals(6887893.4928337997, point.y, 1e-9);
+}
+
+
+function testInverseSphericalMercatorAlastaira() {
+ // http://alastaira.wordpress.com/2011/01/23/the-google-maps-bing-maps-spherical-mercator-projection/
+ var point = ol3.Projection.transformWithCodes(
+ new ol3.Coordinate(-626172.13571216376, 6887893.4928337997),
+ 'EPSG:900913',
+ 'EPSG:4326');
+ assertNotNullNorUndefined(point);
+ assertRoughlyEquals(-5.625, point.x, 1e-9);
+ assertRoughlyEquals(52.4827802220782, point.y, 1e-9);
+}
diff --git a/src/ol3/base/rectangle.js b/src/ol3/base/rectangle.js
new file mode 100644
index 0000000000..45a79a1e26
--- /dev/null
+++ b/src/ol3/base/rectangle.js
@@ -0,0 +1,123 @@
+goog.provide('ol3.Rectangle');
+
+goog.require('goog.asserts');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Size');
+
+
+
+/**
+ * @constructor
+ * @param {number} minX Minimum X.
+ * @param {number} minY Minimum Y.
+ * @param {number} maxX Maximum X.
+ * @param {number} maxY Maximum Y.
+ */
+ol3.Rectangle = function(minX, minY, maxX, maxY) {
+
+ goog.asserts.assert(minX <= maxX);
+ goog.asserts.assert(minY <= maxY);
+
+ /**
+ * @type {number}
+ */
+ this.minX = minX;
+
+ /**
+ * @type {number}
+ */
+ this.minY = minY;
+
+ /**
+ * @type {number}
+ */
+ this.maxX = maxX;
+
+ /**
+ * @type {number}
+ */
+ this.maxY = maxY;
+
+};
+
+
+/**
+ * @return {ol3.Rectangle} Clone.
+ */
+ol3.Rectangle.prototype.clone = function() {
+ return new ol3.Rectangle(this.minX, this.minY, this.maxX, this.maxY);
+};
+
+
+/**
+ * @param {ol3.Coordinate} coordinate Coordinate.
+ * @return {boolean} Contains.
+ */
+ol3.Rectangle.prototype.contains = function(coordinate) {
+ return this.minX <= coordinate.x && coordinate.x <= this.maxX &&
+ this.minY <= coordinate.y && coordinate.y <= this.maxY;
+};
+
+
+/**
+ * @return {ol3.Coordinate} Center.
+ */
+ol3.Rectangle.prototype.getCenter = function() {
+ return new ol3.Coordinate(
+ (this.minX + this.maxX) / 2, (this.minY + this.maxY) / 2);
+};
+
+
+/**
+ * @return {number} Height.
+ */
+ol3.Rectangle.prototype.getHeight = function() {
+ return this.maxY - this.minY;
+};
+
+
+/**
+ * @return {ol3.Size} Size.
+ */
+ol3.Rectangle.prototype.getSize = function() {
+ return new ol3.Size(this.getWidth(), this.getHeight());
+};
+
+
+/**
+ * @return {number} Width.
+ */
+ol3.Rectangle.prototype.getWidth = function() {
+ return this.maxX - this.minX;
+};
+
+
+/**
+ * @param {ol3.Rectangle} rectangle Rectangle.
+ * @return {boolean} Intersects.
+ */
+ol3.Rectangle.prototype.intersects = function(rectangle) {
+ return this.minX <= rectangle.maxX &&
+ this.maxX >= rectangle.minX &&
+ this.minY <= rectangle.maxY &&
+ this.maxY >= rectangle.minY;
+};
+
+
+/**
+ * @param {ol3.Coordinate} coordinate Coordinate.
+ * @return {ol3.Coordinate} Coordinate.
+ */
+ol3.Rectangle.prototype.normalize = function(coordinate) {
+ return new ol3.Coordinate(
+ (coordinate.x - this.minX) / this.getWidth(),
+ (coordinate.y - this.minY) / this.getHeight());
+};
+
+
+/**
+ * @return {string} String.
+ */
+ol3.Rectangle.prototype.toString = function() {
+ return '(' + [this.minX, this.minY, this.maxX, this.maxY].join(', ') + ')';
+};
diff --git a/src/ol3/base/rectangle_test.js b/src/ol3/base/rectangle_test.js
new file mode 100644
index 0000000000..dc990e8f00
--- /dev/null
+++ b/src/ol3/base/rectangle_test.js
@@ -0,0 +1,142 @@
+goog.require('goog.testing.jsunit');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Rectangle');
+
+
+function testCenter() {
+ var rectangle = new ol3.Rectangle(1, 2, 3, 4);
+ var center = rectangle.getCenter();
+ assertEquals(2, center.x);
+ assertEquals(3, center.y);
+}
+
+
+function testClone() {
+ var rectangle = new ol3.Rectangle(1, 2, 3, 4);
+ var clonedRectangle = rectangle.clone();
+ assertTrue(clonedRectangle instanceof ol3.Rectangle);
+ assertFalse(clonedRectangle === rectangle);
+ assertEquals(rectangle.minX, clonedRectangle.minX);
+ assertEquals(rectangle.minY, clonedRectangle.minY);
+ assertEquals(rectangle.maxX, clonedRectangle.maxX);
+ assertEquals(rectangle.maxY, clonedRectangle.maxY);
+}
+
+
+function testContainsPositive() {
+ var rectangle = new ol3.Rectangle(1, 2, 3, 4);
+ assertTrue(rectangle.contains(new ol3.Coordinate(1, 2)));
+ assertTrue(rectangle.contains(new ol3.Coordinate(1, 3)));
+ assertTrue(rectangle.contains(new ol3.Coordinate(1, 4)));
+ assertTrue(rectangle.contains(new ol3.Coordinate(2, 2)));
+ assertTrue(rectangle.contains(new ol3.Coordinate(2, 3)));
+ assertTrue(rectangle.contains(new ol3.Coordinate(2, 4)));
+ assertTrue(rectangle.contains(new ol3.Coordinate(3, 2)));
+ assertTrue(rectangle.contains(new ol3.Coordinate(3, 3)));
+ assertTrue(rectangle.contains(new ol3.Coordinate(3, 4)));
+}
+
+
+function testContainsNegative() {
+ var rectangle = new ol3.Rectangle(1, 2, 3, 4);
+ assertFalse(rectangle.contains(new ol3.Coordinate(0, 1)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(0, 2)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(0, 3)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(0, 4)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(0, 5)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(1, 1)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(1, 5)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(2, 1)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(2, 5)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(3, 1)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(3, 5)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(4, 1)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(4, 2)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(4, 3)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(4, 4)));
+ assertFalse(rectangle.contains(new ol3.Coordinate(4, 5)));
+}
+
+
+function testIntersects() {
+
+ var rectangle1 = new ol3.Rectangle(50, 50, 100, 100);
+
+ function assertIntersects(rectangle2) {
+ assertTrue(rectangle1 + ' expected to intersect ' + rectangle2,
+ rectangle1.intersects(rectangle2));
+ }
+ function assertNotIntersects(rectangle2) {
+ assertFalse(rectangle1 + ' expected to not intersect ' + rectangle2,
+ rectangle1.intersects(rectangle2));
+ }
+
+ assertIntersects(rectangle1);
+ assertIntersects(new ol3.Rectangle(20, 20, 80, 80));
+ assertIntersects(new ol3.Rectangle(20, 50, 80, 100));
+ assertIntersects(new ol3.Rectangle(20, 80, 80, 120));
+ assertIntersects(new ol3.Rectangle(50, 20, 100, 80));
+ assertIntersects(new ol3.Rectangle(50, 80, 100, 120));
+ assertIntersects(new ol3.Rectangle(80, 20, 120, 80));
+ assertIntersects(new ol3.Rectangle(80, 50, 120, 100));
+ assertIntersects(new ol3.Rectangle(80, 80, 120, 120));
+ assertIntersects(new ol3.Rectangle(20, 20, 120, 120));
+ assertIntersects(new ol3.Rectangle(70, 70, 80, 80));
+ assertNotIntersects(new ol3.Rectangle(10, 10, 30, 30));
+ assertNotIntersects(new ol3.Rectangle(30, 10, 70, 30));
+ assertNotIntersects(new ol3.Rectangle(50, 10, 100, 30));
+ assertNotIntersects(new ol3.Rectangle(80, 10, 120, 30));
+ assertNotIntersects(new ol3.Rectangle(120, 10, 140, 30));
+ assertNotIntersects(new ol3.Rectangle(10, 30, 30, 70));
+ assertNotIntersects(new ol3.Rectangle(120, 30, 140, 70));
+ assertNotIntersects(new ol3.Rectangle(10, 50, 30, 100));
+ assertNotIntersects(new ol3.Rectangle(120, 50, 140, 100));
+ assertNotIntersects(new ol3.Rectangle(10, 80, 30, 120));
+ assertNotIntersects(new ol3.Rectangle(120, 80, 140, 120));
+ assertNotIntersects(new ol3.Rectangle(10, 120, 30, 140));
+ assertNotIntersects(new ol3.Rectangle(30, 120, 70, 140));
+ assertNotIntersects(new ol3.Rectangle(50, 120, 100, 140));
+ assertNotIntersects(new ol3.Rectangle(80, 120, 120, 140));
+ assertNotIntersects(new ol3.Rectangle(120, 120, 140, 140));
+}
+
+
+function testSize() {
+ var rectangle = new ol3.Rectangle(0, 1, 2, 4);
+ var size = rectangle.getSize();
+ assertEquals(2, size.width);
+ assertEquals(3, size.height);
+}
+
+
+function testNormalize() {
+ var rectangle = new ol3.Rectangle(0, 1, 2, 3);
+ var coordinate;
+
+ coordinate = rectangle.normalize(new ol3.Coordinate(1, 2));
+ assertEquals(0.5, coordinate.x);
+ assertEquals(0.5, coordinate.y);
+
+ coordinate = rectangle.normalize(new ol3.Coordinate(0, 3));
+ assertEquals(0, coordinate.x);
+ assertEquals(1, coordinate.y);
+
+ coordinate = rectangle.normalize(new ol3.Coordinate(2, 1));
+ assertEquals(1, coordinate.x);
+ assertEquals(0, coordinate.y);
+
+ coordinate = rectangle.normalize(new ol3.Coordinate(0, 0));
+ assertEquals(0, coordinate.x);
+ assertEquals(-0.5, coordinate.y);
+
+ coordinate = rectangle.normalize(new ol3.Coordinate(-1, 1));
+ assertEquals(-0.5, coordinate.x);
+ assertEquals(0, coordinate.y);
+
+}
+
+
+function testToString() {
+ var rectangle = new ol3.Rectangle(0, 1, 2, 3);
+ assertEquals('(0, 1, 2, 3)', rectangle.toString());
+}
diff --git a/src/ol3/base/size.js b/src/ol3/base/size.js
new file mode 100644
index 0000000000..7f09aa964c
--- /dev/null
+++ b/src/ol3/base/size.js
@@ -0,0 +1,25 @@
+goog.provide('ol3.Size');
+
+goog.require('goog.math.Size');
+
+
+
+/**
+ * @constructor
+ * @extends {goog.math.Size}
+ * @param {number} width Width.
+ * @param {number} height Height.
+ */
+ol3.Size = function(width, height) {
+ goog.base(this, width, height);
+};
+goog.inherits(ol3.Size, goog.math.Size);
+
+
+/**
+ * @param {ol3.Size} size Size.
+ * @return {boolean} Equals.
+ */
+ol3.Size.prototype.equals = function(size) {
+ return this.width == size.width && this.height == size.height;
+};
diff --git a/src/ol3/base/store.js b/src/ol3/base/store.js
new file mode 100644
index 0000000000..597710b938
--- /dev/null
+++ b/src/ol3/base/store.js
@@ -0,0 +1,96 @@
+goog.provide('ol3.Store');
+
+goog.require('goog.functions');
+goog.require('ol3.Attribution');
+goog.require('ol3.Extent');
+goog.require('ol3.Projection');
+
+
+
+/**
+ * @constructor
+ * @param {ol3.Projection} projection Projection.
+ * @param {ol3.Extent=} opt_extent Extent.
+ * @param {Array.=} opt_attributions Attributions.
+ */
+ol3.Store = function(projection, opt_extent, opt_attributions) {
+
+ /**
+ * @private
+ * @type {ol3.Projection}
+ */
+ this.projection_ = projection;
+
+ /**
+ * @private
+ * @type {ol3.Extent}
+ */
+ this.extent_ = opt_extent || projection.getExtent();
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.attributions_ = opt_attributions || null;
+
+};
+
+
+/**
+ * @return {Array.} Attributions.
+ */
+ol3.Store.prototype.getAttributions = function() {
+ return this.attributions_;
+};
+
+
+/**
+ * @return {ol3.Extent} Extent.
+ */
+ol3.Store.prototype.getExtent = function() {
+ return this.extent_;
+};
+
+
+/**
+ * @return {ol3.Projection} Projection.
+ */
+ol3.Store.prototype.getProjection = function() {
+ return this.projection_;
+};
+
+
+/**
+ * @return {Array.|undefined} Resolutions.
+ */
+ol3.Store.prototype.getResolutions = goog.abstractMethod;
+
+
+/**
+ * @return {boolean} Is ready.
+ */
+ol3.Store.prototype.isReady = goog.functions.TRUE;
+
+
+/**
+ * @param {Array.} attributions Attributions.
+ */
+ol3.Store.prototype.setAttributions = function(attributions) {
+ this.attributions_ = attributions;
+};
+
+
+/**
+ * @param {ol3.Extent} extent Extent.
+ */
+ol3.Store.prototype.setExtent = function(extent) {
+ this.extent_ = extent;
+};
+
+
+/**
+ * @param {ol3.Projection} projection Projetion.
+ */
+ol3.Store.prototype.setProjection = function(projection) {
+ this.projection_ = projection;
+};
diff --git a/src/ol3/base/transformfunction.js b/src/ol3/base/transformfunction.js
new file mode 100644
index 0000000000..a25739f797
--- /dev/null
+++ b/src/ol3/base/transformfunction.js
@@ -0,0 +1,9 @@
+goog.provide('ol3.TransformFunction');
+
+goog.require('ol3.Coordinate');
+
+
+/**
+ * @typedef {function(ol3.Coordinate): ol3.Coordinate}
+ */
+ol3.TransformFunction;
diff --git a/src/ol3/control/attribution.js b/src/ol3/control/attribution.js
new file mode 100644
index 0000000000..54dca48475
--- /dev/null
+++ b/src/ol3/control/attribution.js
@@ -0,0 +1,398 @@
+// FIXME handle rotation
+// FIXME handle date line wrap
+// FIXME handle layer order
+// FIXME check clean-up code
+
+goog.provide('ol3.control.Attribution');
+
+goog.require('goog.dom');
+goog.require('goog.dom.TagName');
+goog.require('goog.events');
+goog.require('goog.events.EventType');
+goog.require('goog.object');
+goog.require('goog.style');
+goog.require('ol3.Collection');
+goog.require('ol3.Control');
+goog.require('ol3.CoverageArea');
+goog.require('ol3.Layer');
+goog.require('ol3.MapProperty');
+goog.require('ol3.TileCoverageArea');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Control}
+ * @param {ol3.Map} map Map.
+ */
+ol3.control.Attribution = function(map) {
+
+ goog.base(this, map);
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.ulElement_ = goog.dom.createElement(goog.dom.TagName.UL);
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.layersListenerKeys_ = null;
+
+ /**
+ * @private
+ * @type {Object.}
+ */
+ this.layerVisibleChangeListenerKeys_ = {};
+
+ /**
+ * @private
+ * @type {Object.}
+ */
+ this.attributionElements_ = {};
+
+ /**
+ * @private
+ * @type {Object.>}
+ */
+ this.coverageAreass_ = {};
+
+ goog.events.listen(
+ map, ol3.Object.getChangedEventType(ol3.MapProperty.CENTER),
+ this.handleMapChanged, false, this);
+
+ goog.events.listen(
+ map, ol3.Object.getChangedEventType(ol3.MapProperty.LAYERS),
+ this.handleMapLayersChanged, false, this);
+
+ goog.events.listen(map,
+ ol3.Object.getChangedEventType(ol3.MapProperty.RESOLUTION),
+ this.handleMapChanged, false, this);
+
+ goog.events.listen(map, ol3.Object.getChangedEventType(ol3.MapProperty.SIZE),
+ this.handleMapChanged, false, this);
+
+ this.handleMapLayersChanged();
+
+};
+goog.inherits(ol3.control.Attribution, ol3.Control);
+
+
+/**
+ * @param {ol3.Layer} layer Layer.
+ * @protected
+ */
+ol3.control.Attribution.prototype.addLayer = function(layer) {
+
+ var layerKey = goog.getUid(layer);
+
+ this.layerVisibleChangeListenerKeys_[layerKey] = goog.events.listen(
+ layer, ol3.Object.getChangedEventType(ol3.LayerProperty.VISIBLE),
+ this.handleLayerVisibleChanged, false, this);
+
+ if (layer.getStore().isReady()) {
+ this.createAttributionElementsForLayer_(layer);
+ } else {
+ goog.events.listenOnce(layer, goog.events.EventType.LOAD,
+ this.handleLayerLoad, false, this);
+ }
+
+};
+
+
+/**
+ * @param {ol3.Layer} layer Layer.
+ * @private
+ */
+ol3.control.Attribution.prototype.createAttributionElementsForLayer_ =
+ function(layer) {
+
+ var store = layer.getStore();
+ var attributions = store.getAttributions();
+ if (goog.isNull(attributions)) {
+ return;
+ }
+
+ var map = this.getMap();
+ var mapIsDef = map.isDef();
+ var mapExtent = /** @type {ol3.Extent} */ map.getExtent();
+ var mapProjection = /** @type {ol3.Projection} */ map.getProjection();
+ var mapResolution = /** @type {number} */ map.getResolution();
+
+ var layerVisible = layer.getVisible();
+
+ var attributionVisibilities;
+ if (mapIsDef && layerVisible) {
+ attributionVisibilities = this.getLayerAttributionVisiblities_(
+ layer, mapExtent, mapResolution, mapProjection);
+ } else {
+ attributionVisibilities = null;
+ }
+
+ goog.array.forEach(attributions, function(attribution) {
+
+ var attributionKey = goog.getUid(attribution);
+
+ var attributionElement = goog.dom.createElement(goog.dom.TagName.LI);
+ attributionElement.innerHTML = attribution.getHtml();
+
+ if (!map.isDef ||
+ !layerVisible ||
+ goog.isNull(attributionVisibilities) ||
+ !attributionVisibilities[attributionKey]) {
+ goog.style.showElement(attributionElement, false);
+ }
+
+ goog.dom.appendChild(this.ulElement_, attributionElement);
+
+ this.attributionElements_[attributionKey] = attributionElement;
+
+ }, this);
+
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.control.Attribution.prototype.getElement = function() {
+ return this.ulElement_;
+};
+
+
+/**
+ * @param {ol3.Layer} layer Layer.
+ * @param {ol3.Extent} mapExtent Map extent.
+ * @param {number} mapResolution Map resolution.
+ * @param {ol3.Projection} mapProjection Map projection.
+ * @return {Object.} Attribution visibilities.
+ * @private
+ */
+ol3.control.Attribution.prototype.getLayerAttributionVisiblities_ =
+ function(layer, mapExtent, mapResolution, mapProjection) {
+
+ var store = layer.getStore();
+ var attributions = store.getAttributions();
+
+ if (goog.isNull(attributions)) {
+ return null;
+ }
+
+ var mapZ;
+ if (store instanceof ol3.TileStore) {
+ var tileStore = /** @type {ol3.TileStore} */ store;
+ var tileGrid = tileStore.getTileGrid();
+ mapZ = tileGrid.getZForResolution(mapResolution);
+ }
+
+ var attributionVisibilities = {};
+ goog.array.forEach(attributions, function(attribution) {
+
+ var attributionKey = goog.getUid(attribution);
+
+ var attributionVisible = true;
+
+ var coverageAreas;
+ if (attributionKey in this.coverageAreass_) {
+ coverageAreas = this.coverageAreass_[attributionKey];
+ } else {
+ var attributionProjection = attribution.getProjection();
+ coverageAreas = attribution.getCoverageAreas();
+ if (!goog.isNull(coverageAreas) &&
+ !ol3.Projection.equivalent(attributionProjection, mapProjection)) {
+ var transformFn = ol3.Projection.getTransform(
+ attributionProjection, mapProjection);
+ if (transformFn !== ol3.Projection.cloneTransform) {
+ coverageAreas = goog.array.map(coverageAreas, function(coverageArea) {
+ return coverageArea.transform(transformFn);
+ });
+ }
+ }
+ this.coverageAreass_[attributionKey] = coverageAreas;
+ }
+
+ if (!goog.isNull(coverageAreas)) {
+ if (store instanceof ol3.TileStore) {
+ attributionVisible = goog.array.some(
+ coverageAreas,
+ /**
+ * @param {ol3.TileCoverageArea} tileCoverageArea
+ * Tile coverage area.
+ */
+ function(tileCoverageArea) {
+ goog.asserts.assert(
+ tileCoverageArea instanceof ol3.TileCoverageArea);
+ return tileCoverageArea.intersectsExtentAndZ(mapExtent, mapZ);
+ });
+ } else {
+ attributionVisible = goog.array.some(
+ coverageAreas,
+ function(coverageArea) {
+ return coverageArea.intersectsExtentAndResolution(
+ mapExtent, mapResolution);
+ });
+ }
+ }
+
+ attributionVisibilities[attributionKey] = attributionVisible;
+
+ }, this);
+
+ return attributionVisibilities;
+
+};
+
+
+/**
+ * @param {goog.events.Event} event Event.
+ */
+ol3.control.Attribution.prototype.handleLayerLoad = function(event) {
+ var layer = /** @type {ol3.Layer} */ event.target;
+ this.createAttributionElementsForLayer_(layer);
+};
+
+
+/**
+ * @param {goog.events.Event} event Event.
+ * @protected
+ */
+ol3.control.Attribution.prototype.handleLayerVisibleChanged = function(event) {
+
+ var map = this.getMap();
+ var mapIsDef = map.isDef();
+ var mapExtent = /** @type {ol3.Extent} */ map.getExtent();
+ var mapProjection = /** @type {ol3.Projection} */ map.getProjection();
+ var mapResolution = /** @type {number} */ map.getResolution();
+
+ var layer = /** @type {ol3.Layer} */ event.target;
+
+ this.updateLayerAttributionsVisibility_(
+ layer, mapIsDef, mapExtent, mapResolution, mapProjection);
+
+};
+
+
+/**
+ * @param {ol3.CollectionEvent} collectionEvent Collection event.
+ * @protected
+ */
+ol3.control.Attribution.prototype.handleLayersAdd = function(collectionEvent) {
+ var layer = /** @type {ol3.Layer} */ collectionEvent.elem;
+ this.addLayer(layer);
+};
+
+
+/**
+ * @param {ol3.CollectionEvent} collectionEvent Collection event.
+ * @protected
+ */
+ol3.control.Attribution.prototype.handleLayersRemove =
+ function(collectionEvent) {
+ var layer = /** @type {ol3.Layer} */ collectionEvent.elem;
+ this.removeLayer(layer);
+};
+
+
+/**
+ * @protected
+ */
+ol3.control.Attribution.prototype.handleMapChanged = function() {
+
+ var map = this.getMap();
+ var mapIsDef = map.isDef();
+ var mapExtent = /** @type {ol3.Extent} */ map.getExtent();
+ var mapProjection = /** @type {ol3.Projection} */ map.getProjection();
+ var mapResolution = map.getResolution();
+
+ var layers = map.getLayers();
+ layers.forEach(function(layer) {
+ this.updateLayerAttributionsVisibility_(
+ layer, mapIsDef, mapExtent, mapResolution, mapProjection);
+ }, this);
+
+};
+
+
+/**
+ * @protected
+ */
+ol3.control.Attribution.prototype.handleMapLayersChanged = function() {
+ if (!goog.isNull(this.layersListenerKeys_)) {
+ goog.array.forEach(this.layersListenerKeys_, goog.events.unlistenByKey);
+ this.layersListenerKeys_ = null;
+ }
+ goog.object.forEach(this.attributionElements_, function(attributionElement) {
+ goog.dom.removeNode(attributionElement);
+ }, this);
+ this.attributionElements_ = {};
+ this.coverageAreass_ = {};
+ var map = this.getMap();
+ var layers = map.getLayers();
+ if (goog.isDefAndNotNull(layers)) {
+ layers.forEach(this.addLayer, this);
+ this.layersListenerKeys_ = [
+ goog.events.listen(layers, ol3.CollectionEventType.ADD,
+ this.handleLayersAdd, false, this),
+ goog.events.listen(layers, ol3.CollectionEventType.REMOVE,
+ this.handleLayersRemove, false, this)
+ ];
+ }
+};
+
+
+/**
+ * @param {ol3.Layer} layer Layer.
+ * @protected
+ */
+ol3.control.Attribution.prototype.removeLayer = function(layer) {
+
+ var layerKey = goog.getUid(layer);
+
+ goog.events.unlistenByKey(this.layerVisibleChangeListenerKeys_[layerKey]);
+ delete this.layerVisibleChangeListenerKeys_[layerKey];
+
+ goog.array.forEach(layer.getStore().getAttributions(), function(attribution) {
+ var attributionKey = goog.getUid(attribution);
+ delete this.coverageAreass_[attributionKey];
+ var attributionElement = this.attributionElements_[attributionKey];
+ goog.dom.removeNode(attributionElement);
+ delete this.attributionElements_[attributionKey];
+ }, this);
+
+};
+
+
+/**
+ * @param {ol3.Layer} layer Layer.
+ * @param {boolean} mapIsDef Map is defined.
+ * @param {ol3.Extent} mapExtent Map extent.
+ * @param {number} mapResolution Map resolution.
+ * @param {ol3.Projection} mapProjection Map projection.
+ * @private
+ */
+ol3.control.Attribution.prototype.updateLayerAttributionsVisibility_ =
+ function(layer, mapIsDef, mapExtent, mapResolution, mapProjection) {
+ if (mapIsDef && layer.getVisible()) {
+ var attributionVisibilities = this.getLayerAttributionVisiblities_(
+ layer, mapExtent, mapResolution, mapProjection);
+ goog.object.forEach(
+ attributionVisibilities,
+ function(attributionVisible, attributionKey) {
+ var attributionElement = this.attributionElements_[attributionKey];
+ goog.style.showElement(attributionElement, attributionVisible);
+ },
+ this);
+ } else {
+ var store = layer.getStore();
+ var attributions = store.getAttributions();
+ if (!goog.isNull(attributions)) {
+ goog.array.forEach(attributions, function(attribution) {
+ var attributionKey = goog.getUid(attribution);
+ var attributionElement = this.attributionElements_[attributionKey];
+ goog.style.showElement(attributionElement, false);
+ }, this);
+ }
+ }
+};
diff --git a/src/ol3/control/control.js b/src/ol3/control/control.js
new file mode 100644
index 0000000000..9b019e5224
--- /dev/null
+++ b/src/ol3/control/control.js
@@ -0,0 +1,33 @@
+goog.provide('ol3.Control');
+
+goog.require('ol3.Map');
+
+
+
+/**
+ * @constructor
+ * @param {ol3.Map} map Map.
+ */
+ol3.Control = function(map) {
+
+ /**
+ * @private
+ * @type {ol3.Map}
+ */
+ this.map_ = map;
+
+};
+
+
+/**
+ * @return {Element} Element.
+ */
+ol3.Control.prototype.getElement = goog.abstractMethod;
+
+
+/**
+ * @return {ol3.Map} Map.
+ */
+ol3.Control.prototype.getMap = function() {
+ return this.map_;
+};
diff --git a/src/ol3/control/mouseposition.js b/src/ol3/control/mouseposition.js
new file mode 100644
index 0000000000..fdf9bee13d
--- /dev/null
+++ b/src/ol3/control/mouseposition.js
@@ -0,0 +1,128 @@
+// FIXME should listen on appropriate pane, once it is defined
+
+goog.provide('ol3.control.MousePosition');
+
+goog.require('goog.events');
+goog.require('goog.events.EventType');
+goog.require('ol3.Control');
+goog.require('ol3.MapProperty');
+goog.require('ol3.Object');
+goog.require('ol3.Projection');
+goog.require('ol3.TransformFunction');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Control}
+ * @param {ol3.Map} map Map.
+ * @param {ol3.Projection=} opt_projection Projection.
+ * @param {ol3.CoordinateFormatType=} opt_coordinateFormat Coordinate format.
+ * @param {string=} opt_undefinedHTML Undefined HTML.
+ */
+ol3.control.MousePosition =
+ function(map, opt_projection, opt_coordinateFormat, opt_undefinedHTML) {
+
+ goog.base(this, map);
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.divElement_ = goog.dom.createElement(goog.dom.TagName.DIV);
+
+ /**
+ * @private
+ * @type {ol3.Projection}
+ */
+ this.projection_ = opt_projection || null;
+
+ /**
+ * @private
+ * @type {ol3.CoordinateFormatType|undefined}
+ */
+ this.coordinateFormat_ = opt_coordinateFormat;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.undefinedHTML_ = opt_undefinedHTML || '';
+
+ /**
+ * @private
+ * @type {ol3.TransformFunction}
+ */
+ this.transform_ = ol3.Projection.identityTransform;
+
+ goog.events.listen(map,
+ ol3.Object.getChangedEventType(ol3.MapProperty.PROJECTION),
+ this.handleMapProjectionChanged, false, this);
+
+ goog.events.listen(map.getViewport(), goog.events.EventType.MOUSEMOVE,
+ this.handleMouseMove, false, this);
+
+ goog.events.listen(map.getViewport(), goog.events.EventType.MOUSEOUT,
+ this.handleMouseOut, false, this);
+
+ this.handleMapProjectionChanged();
+
+};
+goog.inherits(ol3.control.MousePosition, ol3.Control);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.control.MousePosition.prototype.getElement = function() {
+ return this.divElement_;
+};
+
+
+/**
+ * @protected
+ */
+ol3.control.MousePosition.prototype.handleMapProjectionChanged = function() {
+ var map = this.getMap();
+ var mapProjection = map.getProjection();
+ if (!goog.isDef(mapProjection) || goog.isNull(this.projection_)) {
+ this.transform_ = ol3.Projection.identityTransform;
+ } else {
+ this.transform_ =
+ ol3.Projection.getTransform(mapProjection, this.projection_);
+ }
+ // FIXME should we instead re-calculate using the last known mouse position?
+ this.divElement_.innerHTML = this.undefinedHTML_;
+};
+
+
+/**
+ * @param {goog.events.BrowserEvent} browserEvent Browser event.
+ * @protected
+ */
+ol3.control.MousePosition.prototype.handleMouseMove = function(browserEvent) {
+ var map = this.getMap();
+ var pixel = new ol3.Pixel(browserEvent.offsetX, browserEvent.offsetY);
+ var coordinate = map.getCoordinateFromPixel(pixel);
+ var html;
+ if (goog.isDef(coordinate)) {
+ coordinate = this.transform_(coordinate);
+ if (goog.isDef(this.coordinateFormat_)) {
+ html = this.coordinateFormat_(coordinate);
+ } else {
+ html = coordinate.toString();
+ }
+ } else {
+ html = this.undefinedHTML_;
+ }
+ this.divElement_.innerHTML = html;
+};
+
+
+/**
+ * @param {goog.events.BrowserEvent} browserEvent Browser event.
+ * @protected
+ */
+ol3.control.MousePosition.prototype.handleMouseOut = function(browserEvent) {
+ this.divElement_.innerHTML = this.undefinedHTML_;
+};
diff --git a/src/ol3/dom/dom.js b/src/ol3/dom/dom.js
new file mode 100644
index 0000000000..31b6c9dc19
--- /dev/null
+++ b/src/ol3/dom/dom.js
@@ -0,0 +1,9 @@
+goog.provide('ol3.dom');
+
+goog.require('goog.functions');
+
+
+/**
+ * @return {boolean} Is supported.
+ */
+ol3.dom.isSupported = goog.functions.TRUE;
diff --git a/src/ol3/dom/layerrenderer.js b/src/ol3/dom/layerrenderer.js
new file mode 100644
index 0000000000..1b51c3ac40
--- /dev/null
+++ b/src/ol3/dom/layerrenderer.js
@@ -0,0 +1,84 @@
+goog.provide('ol3.dom.LayerRenderer');
+
+goog.require('ol3.Coordinate');
+goog.require('ol3.LayerRenderer');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.LayerRenderer}
+ * @param {ol3.MapRenderer} mapRenderer Map renderer.
+ * @param {ol3.Layer} layer Layer.
+ * @param {!Element} target Target.
+ */
+ol3.dom.LayerRenderer = function(mapRenderer, layer, target) {
+ goog.base(this, mapRenderer, layer);
+
+ /**
+ * @type {!Element}
+ * @protected
+ */
+ this.target = target;
+
+ /**
+ * Top left corner of the target in map coords.
+ *
+ * @type {ol3.Coordinate}
+ * @protected
+ */
+ this.origin = null;
+
+ this.handleLayerOpacityChange();
+ this.handleLayerVisibleChange();
+
+};
+goog.inherits(ol3.dom.LayerRenderer, ol3.LayerRenderer);
+
+
+/**
+ * @override
+ * @return {ol3.MapRenderer} Map renderer.
+ */
+ol3.dom.LayerRenderer.prototype.getMapRenderer = function() {
+ return /** @type {ol3.dom.MapRenderer} */ goog.base(this, 'getMapRenderer');
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.dom.LayerRenderer.prototype.handleLayerLoad = function() {
+ this.getMap().render();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.dom.LayerRenderer.prototype.handleLayerOpacityChange = function() {
+ goog.style.setOpacity(this.target, this.getLayer().getOpacity());
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.dom.LayerRenderer.prototype.handleLayerVisibleChange = function() {
+ goog.style.showElement(this.target, this.getLayer().getVisible());
+};
+
+
+/**
+ */
+ol3.dom.LayerRenderer.prototype.render = goog.abstractMethod;
+
+
+/**
+ * Set the location of the top left corner of the target.
+ *
+ * @param {ol3.Coordinate} origin Origin.
+ */
+ol3.dom.LayerRenderer.prototype.setOrigin = function(origin) {
+ this.origin = origin;
+};
diff --git a/src/ol3/dom/maprenderer.js b/src/ol3/dom/maprenderer.js
new file mode 100644
index 0000000000..4f793799ee
--- /dev/null
+++ b/src/ol3/dom/maprenderer.js
@@ -0,0 +1,169 @@
+goog.provide('ol3.dom.MapRenderer');
+
+goog.require('goog.asserts');
+goog.require('goog.dom');
+goog.require('goog.dom.TagName');
+goog.require('goog.style');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Map');
+goog.require('ol3.MapRenderer');
+goog.require('ol3.TileLayer');
+goog.require('ol3.dom.TileLayerRenderer');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.MapRenderer}
+ * @param {Element} container Container.
+ * @param {ol3.Map} map Map.
+ */
+ol3.dom.MapRenderer = function(container, map) {
+
+ goog.base(this, container, map);
+
+ /**
+ * @type {!Element}
+ * @private
+ */
+ this.layersPane_ = goog.dom.createElement(goog.dom.TagName.DIV);
+ this.layersPane_.className = 'ol-layers-pane';
+ this.layersPane_.style.position = 'absolute';
+ goog.dom.appendChild(container, this.layersPane_);
+
+ /**
+ * @type {Object}
+ * @private
+ */
+ this.layerPanes_ = {};
+
+ /**
+ * @type {ol3.Coordinate|undefined}
+ * @private
+ */
+ this.renderedCenter_ = undefined;
+
+ /**
+ * The pixel offset of the layers pane with respect to its container.
+ *
+ * @type {ol3.Coordinate}
+ * @private
+ */
+ this.layersPaneOffset_ = null;
+};
+goog.inherits(ol3.dom.MapRenderer, ol3.MapRenderer);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.dom.MapRenderer.prototype.createLayerRenderer = function(layer) {
+
+ if (layer instanceof ol3.TileLayer) {
+
+ var layerPane = goog.dom.createElement(goog.dom.TagName.DIV);
+ layerPane.className = 'ol-layer';
+ layerPane.style.position = 'absolute';
+ goog.dom.appendChild(this.layersPane_, layerPane);
+
+ var layerRenderer = new ol3.dom.TileLayerRenderer(this, layer, layerPane);
+
+ this.layerPanes_[goog.getUid(layerRenderer)] = layerPane;
+
+ return layerRenderer;
+
+ } else {
+ goog.asserts.assert(false);
+ return null;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.dom.MapRenderer.prototype.handleCenterChanged = function() {
+ goog.base(this, 'handleCenterChanged');
+ var map = this.getMap();
+ if (!map.isDef()) {
+ return;
+ }
+ // FIXME: shiftLayersPane_ and resetLayersPane_ should be called
+ // elsewhere as we may be frozen here
+ if (goog.isDef(this.renderedCenter_)) {
+ this.shiftLayersPane_();
+ } else {
+ this.resetLayersPane_();
+ }
+ map.render();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.dom.MapRenderer.prototype.handleResolutionChanged = function() {
+ goog.base(this, 'handleResolutionChanged');
+ var map = this.getMap();
+ if (!map.isDef()) {
+ return;
+ }
+ // FIXME: resetLayersPane_ should be called
+ // elsewhere as we may be frozen here
+ this.resetLayersPane_();
+ map.render();
+};
+
+
+/**
+ * Reset the layers pane to its initial position.
+ * @private
+ */
+ol3.dom.MapRenderer.prototype.resetLayersPane_ = function() {
+ var offset = new ol3.Coordinate(0, 0);
+ goog.style.setPosition(this.layersPane_, offset);
+
+ this.layersPaneOffset_ = offset;
+ this.renderedCenter_ = this.map.getCenter();
+
+ this.setOrigin_();
+};
+
+
+/**
+ * Set the origin for each layer renderer.
+ * @private
+ */
+ol3.dom.MapRenderer.prototype.setOrigin_ = function() {
+ var center = this.map.getCenter();
+ var resolution = this.map.getResolution();
+ var targetSize = this.map.getSize();
+ var targetWidth = targetSize.width;
+ var targetHeight = targetSize.height;
+ var origin = new ol3.Coordinate(
+ center.x - resolution * targetWidth / 2,
+ center.y + resolution * targetHeight / 2);
+ goog.object.forEach(this.layerRenderers, function(layerRenderer) {
+ layerRenderer.setOrigin(origin);
+ });
+};
+
+
+/**
+ * Move the layers pane.
+ * @private
+ */
+ol3.dom.MapRenderer.prototype.shiftLayersPane_ = function() {
+ var center = this.map.getCenter();
+ var oldCenter = this.renderedCenter_;
+ var resolution = this.map.getResolution();
+ var dx = Math.round((oldCenter.x - center.x) / resolution);
+ var dy = Math.round((center.y - oldCenter.y) / resolution);
+ if (!(dx === 0 && dy === 0)) {
+ var offset = this.layersPaneOffset_;
+ offset.x += Math.round((oldCenter.x - center.x) / resolution);
+ offset.y += Math.round((center.y - oldCenter.y) / resolution);
+ goog.style.setPosition(this.layersPane_, offset);
+ this.renderedCenter_ = center;
+ }
+};
diff --git a/src/ol3/dom/tilelayerrenderer.js b/src/ol3/dom/tilelayerrenderer.js
new file mode 100644
index 0000000000..50152687e7
--- /dev/null
+++ b/src/ol3/dom/tilelayerrenderer.js
@@ -0,0 +1,148 @@
+goog.provide('ol3.dom.TileLayerRenderer');
+
+goog.require('goog.dom');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Extent');
+goog.require('ol3.dom.LayerRenderer');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.dom.LayerRenderer}
+ * @param {ol3.MapRenderer} mapRenderer Map renderer.
+ * @param {ol3.TileLayer} tileLayer Tile layer.
+ * @param {!Element} target Target.
+ */
+ol3.dom.TileLayerRenderer = function(mapRenderer, tileLayer, target) {
+ goog.base(this, mapRenderer, tileLayer, target);
+
+ /**
+ * @type {Object}
+ * @private
+ */
+ this.renderedTiles_ = {};
+
+ /**
+ * @type {number|undefined}
+ * @private
+ */
+ this.renderedMapResolution_ = undefined;
+};
+goog.inherits(ol3.dom.TileLayerRenderer, ol3.dom.LayerRenderer);
+
+
+/**
+ * @override
+ * @return {ol3.TileLayer} Layer.
+ */
+ol3.dom.TileLayerRenderer.prototype.getLayer = function() {
+ return /** @type {ol3.TileLayer} */ goog.base(this, 'getLayer');
+};
+
+
+/**
+ * Get the pixel offset between the tile origin and the container origin.
+ * @private
+ * @param {number} z Z.
+ * @param {number} resolution Resolution.
+ * @return {ol3.Coordinate} Offset.
+ */
+ol3.dom.TileLayerRenderer.prototype.getTileOffset_ = function(z, resolution) {
+ var tileLayer = this.getLayer();
+ var tileStore = tileLayer.getStore();
+ var tileGrid = tileStore.getTileGrid();
+ var tileOrigin = tileGrid.getOrigin(z);
+ var offset = new ol3.Coordinate(
+ Math.round((this.origin.x - tileOrigin.x) / resolution),
+ Math.round((tileOrigin.y - this.origin.y) / resolution));
+ return offset;
+};
+
+
+/**
+ * Get rid of tiles outside the rendered extent.
+ * @private
+ * @param {ol3.TileBounds} tileBounds Tile bounds.
+ * @param {number} z Z.
+ */
+ol3.dom.TileLayerRenderer.prototype.removeInvisibleTiles_ = function(
+ tileBounds, z) {
+ var key, tileCoord, prune, tile;
+ for (key in this.renderedTiles_) {
+ tileCoord = ol3.TileCoord.createFromString(key);
+ prune = z !== tileCoord.z ||
+ tileCoord.x < tileBounds.minX ||
+ tileCoord.x > tileBounds.maxX ||
+ tileCoord.y < tileBounds.minY ||
+ tileCoord.y > tileBounds.maxY;
+ if (prune) {
+ tile = this.renderedTiles_[key];
+ delete this.renderedTiles_[key];
+ goog.dom.removeNode(tile.getImage(this));
+ }
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.dom.TileLayerRenderer.prototype.render = function() {
+
+ var map = this.getMap();
+ if (!map.isDef()) {
+ return;
+ }
+ var mapExtent = /** @type {!ol3.Extent} */ map.getExtent();
+ var mapResolution = /** @type {number} */ map.getResolution();
+
+ var tileLayer = this.getLayer();
+ var tileStore = tileLayer.getStore();
+ var tileGrid = tileStore.getTileGrid();
+
+ if (mapResolution != this.renderedMapResolution_) {
+ this.renderedTiles_ = {};
+ goog.dom.removeChildren(this.target);
+ }
+
+ // z represents the "best" resolution
+ var z = tileGrid.getZForResolution(mapResolution);
+
+ var tileBounds =
+ tileGrid.getTileBoundsForExtentAndResolution(mapExtent, mapResolution);
+ var tileOffset = this.getTileOffset_(z, mapResolution);
+
+ var fragment = document.createDocumentFragment();
+
+ var key, tile, pixelBounds, img, newTiles = false;
+ tileBounds.forEachTileCoord(z, function(tileCoord) {
+ key = tileCoord.toString();
+ tile = this.renderedTiles_[key];
+ if (!goog.isDef(tile)) {
+ tile = tileStore.getTile(tileCoord);
+ if (goog.isNull(tile)) {
+ } else {
+ tile.load();
+ this.renderedTiles_[key] = tile;
+ pixelBounds = tileGrid.getPixelBoundsForTileCoordAndResolution(
+ tileCoord, mapResolution);
+ img = tile.getImage(this);
+ img.style.position = 'absolute';
+ img.style.left = (pixelBounds.minX - tileOffset.x) + 'px';
+ img.style.top = (-pixelBounds.maxY - tileOffset.y) + 'px';
+ img.style.width = pixelBounds.getWidth() + 'px';
+ img.style.height = pixelBounds.getHeight() + 'px';
+ goog.dom.appendChild(fragment, img);
+ newTiles = true;
+ }
+ }
+ }, this);
+
+ if (newTiles) {
+ goog.dom.appendChild(this.target, fragment);
+ }
+
+ this.removeInvisibleTiles_(tileBounds, z);
+ this.renderedMapResolution_ = mapResolution;
+};
diff --git a/src/ol3/interaction/altdragrotate.js b/src/ol3/interaction/altdragrotate.js
new file mode 100644
index 0000000000..c38ad19e06
--- /dev/null
+++ b/src/ol3/interaction/altdragrotate.js
@@ -0,0 +1,60 @@
+goog.provide('ol3.interaction.AltDragRotate');
+
+goog.require('ol3.MapBrowserEvent');
+goog.require('ol3.interaction.Drag');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.interaction.Drag}
+ * @param {ol3.interaction.Constraints} constraints Constraints.
+ */
+ol3.interaction.AltDragRotate = function(constraints) {
+
+ goog.base(this, constraints);
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.startRotation_ = 0;
+
+};
+goog.inherits(ol3.interaction.AltDragRotate, ol3.interaction.Drag);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.AltDragRotate.prototype.handleDrag = function(mapBrowserEvent) {
+ var browserEvent = mapBrowserEvent.browserEvent;
+ var map = mapBrowserEvent.map;
+ var size = map.getSize();
+ var theta = Math.atan2(
+ size.height / 2 - browserEvent.offsetY,
+ browserEvent.offsetX - size.width / 2);
+ this.rotate(map, this.startRotation_, -theta);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.AltDragRotate.prototype.handleDragStart =
+ function(mapBrowserEvent) {
+ var browserEvent = mapBrowserEvent.browserEvent;
+ var map = mapBrowserEvent.map;
+ if (browserEvent.isMouseActionButton() && browserEvent.altKey &&
+ map.canRotate()) {
+ var size = map.getSize();
+ var theta = Math.atan2(
+ size.height / 2 - browserEvent.offsetY,
+ browserEvent.offsetX - size.width / 2);
+ this.startRotation_ = (map.getRotation() || 0) + theta;
+ browserEvent.preventDefault();
+ return true;
+ } else {
+ return false;
+ }
+};
diff --git a/src/ol3/interaction/centerconstraint.js b/src/ol3/interaction/centerconstraint.js
new file mode 100644
index 0000000000..46ee5f4bda
--- /dev/null
+++ b/src/ol3/interaction/centerconstraint.js
@@ -0,0 +1,47 @@
+goog.provide('ol3.interaction.CenterConstraint');
+goog.provide('ol3.interaction.CenterConstraintType');
+
+goog.require('ol3.Coordinate');
+
+
+/**
+ * @typedef {function((ol3.Coordinate|undefined),
+ * (number|undefined),
+ * ol3.Coordinate): (ol3.Coordinate|undefined)}
+ */
+ol3.interaction.CenterConstraintType;
+
+
+/**
+ * @param {ol3.Coordinate|undefined} center Center.
+ * @param {number|undefined} resolution Resolution.
+ * @param {ol3.Coordinate} delta Delta.
+ * @return {ol3.Coordinate|undefined} Center.
+ */
+ol3.interaction.CenterConstraint.none = function(center, resolution, delta) {
+ if (goog.isDefAndNotNull(center) && goog.isDef(resolution)) {
+ var x = center.x + delta.x;
+ var y = center.y + delta.y;
+ return new ol3.Coordinate(x, y);
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {ol3.Coordinate|undefined} center Center.
+ * @param {number|undefined} resolution Resolution.
+ * @param {ol3.Coordinate} delta Delta.
+ * @return {ol3.Coordinate|undefined} Center.
+ */
+ol3.interaction.CenterConstraint.snapToPixel =
+ function(center, resolution, delta) {
+ if (goog.isDefAndNotNull(center) && goog.isDef(resolution)) {
+ var x = Math.floor((center.x + delta.x) / resolution + 0.5) * resolution;
+ var y = Math.floor((center.y + delta.y) / resolution + 0.5) * resolution;
+ return new ol3.Coordinate(x, y);
+ } else {
+ return undefined;
+ }
+};
diff --git a/src/ol3/interaction/constraints.js b/src/ol3/interaction/constraints.js
new file mode 100644
index 0000000000..c5e5855cc6
--- /dev/null
+++ b/src/ol3/interaction/constraints.js
@@ -0,0 +1,36 @@
+goog.provide('ol3.interaction.Constraints');
+
+goog.require('ol3.interaction.CenterConstraintType');
+goog.require('ol3.interaction.ResolutionConstraintType');
+goog.require('ol3.interaction.RotationConstraintType');
+
+
+
+/**
+ * @constructor
+ * @param {ol3.interaction.CenterConstraintType} centerConstraint
+ * Center constraint.
+ * @param {ol3.interaction.ResolutionConstraintType} resolutionConstraint
+ * Resolution constraint.
+ * @param {ol3.interaction.RotationConstraintType} rotationConstraint
+ * Rotation constraint.
+ */
+ol3.interaction.Constraints =
+ function(centerConstraint, resolutionConstraint, rotationConstraint) {
+
+ /**
+ * @type {ol3.interaction.CenterConstraintType}
+ */
+ this.center = centerConstraint;
+
+ /**
+ * @type {ol3.interaction.ResolutionConstraintType}
+ */
+ this.resolution = resolutionConstraint;
+
+ /**
+ * @type {ol3.interaction.RotationConstraintType}
+ */
+ this.rotation = rotationConstraint;
+
+};
diff --git a/src/ol3/interaction/dblclickzoom.js b/src/ol3/interaction/dblclickzoom.js
new file mode 100644
index 0000000000..15055f5000
--- /dev/null
+++ b/src/ol3/interaction/dblclickzoom.js
@@ -0,0 +1,36 @@
+goog.provide('ol3.interaction.DblClickZoom');
+
+goog.require('goog.events.EventType');
+goog.require('ol3.Interaction');
+goog.require('ol3.MapBrowserEvent');
+goog.require('ol3.interaction.Constraints');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Interaction}
+ * @param {ol3.interaction.Constraints} constraints Constraints.
+ */
+ol3.interaction.DblClickZoom = function(constraints) {
+ goog.base(this, constraints);
+};
+goog.inherits(ol3.interaction.DblClickZoom, ol3.Interaction);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.DblClickZoom.prototype.handleMapBrowserEvent =
+ function(mapBrowserEvent) {
+ var browserEvent = mapBrowserEvent.browserEvent;
+ if (browserEvent.type == goog.events.EventType.DBLCLICK &&
+ browserEvent.isMouseActionButton()) {
+ var map = mapBrowserEvent.map;
+ var resolution = map.getResolution();
+ var delta = mapBrowserEvent.browserEvent.shiftKey ? -1 : 1;
+ var anchor = mapBrowserEvent.getCoordinate();
+ this.zoom(map, resolution, delta, anchor);
+ mapBrowserEvent.preventDefault();
+ }
+};
diff --git a/src/ol3/interaction/drag.js b/src/ol3/interaction/drag.js
new file mode 100644
index 0000000000..adf3ca4b72
--- /dev/null
+++ b/src/ol3/interaction/drag.js
@@ -0,0 +1,122 @@
+
+goog.provide('ol3.interaction.Drag');
+
+goog.require('goog.asserts');
+goog.require('goog.events.EventType');
+goog.require('goog.functions');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Interaction');
+goog.require('ol3.MapBrowserEvent');
+goog.require('ol3.interaction.Constraints');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Interaction}
+ * @param {ol3.interaction.Constraints} constraints Constraints.
+ */
+ol3.interaction.Drag = function(constraints) {
+
+ goog.base(this, constraints);
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.dragging_ = false;
+
+ /**
+ * @type {number}
+ */
+ this.startX = 0;
+
+ /**
+ * @type {number}
+ */
+ this.startY = 0;
+
+ /**
+ * @type {number}
+ */
+ this.offsetX = 0;
+
+ /**
+ * @type {number}
+ */
+ this.offsetY = 0;
+
+ /**
+ * @type {ol3.Coordinate}
+ */
+ this.startCenter = null;
+
+ /**
+ * @type {ol3.Coordinate}
+ */
+ this.startCoordinate = null;
+
+};
+goog.inherits(ol3.interaction.Drag, ol3.Interaction);
+
+
+/**
+ * @param {ol3.MapBrowserEvent} mapBrowserEvent Event.
+ * @protected
+ */
+ol3.interaction.Drag.prototype.handleDrag = goog.nullFunction;
+
+
+/**
+ * @param {ol3.MapBrowserEvent} mapBrowserEvent Event.
+ * @protected
+ */
+ol3.interaction.Drag.prototype.handleDragEnd = goog.nullFunction;
+
+
+/**
+ * @param {ol3.MapBrowserEvent} mapBrowserEvent Event.
+ * @protected
+ * @return {boolean} Capture dragging.
+ */
+ol3.interaction.Drag.prototype.handleDragStart = goog.functions.FALSE;
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.Drag.prototype.handleMapBrowserEvent =
+ function(mapBrowserEvent) {
+ var map = mapBrowserEvent.map;
+ if (!map.isDef()) {
+ return;
+ }
+ var browserEvent = mapBrowserEvent.browserEvent;
+ if (this.dragging_) {
+ if (mapBrowserEvent.type == goog.fx.Dragger.EventType.DRAG) {
+ goog.asserts.assert(browserEvent instanceof goog.events.BrowserEvent);
+ this.deltaX = browserEvent.clientX - this.startX;
+ this.deltaY = browserEvent.clientY - this.startY;
+ this.handleDrag(mapBrowserEvent);
+ } else if (mapBrowserEvent.type == goog.fx.Dragger.EventType.END) {
+ goog.asserts.assert(browserEvent instanceof goog.events.BrowserEvent);
+ this.deltaX = browserEvent.clientX - this.startX;
+ this.deltaY = browserEvent.clientY - this.startY;
+ this.handleDragEnd(mapBrowserEvent);
+ this.dragging_ = false;
+ }
+ } else if (mapBrowserEvent.type == goog.fx.Dragger.EventType.START) {
+ goog.asserts.assert(browserEvent instanceof goog.events.BrowserEvent);
+ this.startX = browserEvent.clientX;
+ this.startY = browserEvent.clientY;
+ this.deltaX = 0;
+ this.deltaY = 0;
+ this.startCenter = /** @type {!ol3.Coordinate} */ map.getCenter();
+ this.startCoordinate = /** @type {ol3.Coordinate} */
+ mapBrowserEvent.getCoordinate();
+ if (this.handleDragStart(mapBrowserEvent)) {
+ this.dragging_ = true;
+ mapBrowserEvent.preventDefault();
+ }
+ }
+};
diff --git a/src/ol3/interaction/dragpan.js b/src/ol3/interaction/dragpan.js
new file mode 100644
index 0000000000..6594e6fbd9
--- /dev/null
+++ b/src/ol3/interaction/dragpan.js
@@ -0,0 +1,48 @@
+goog.provide('ol3.interaction.DragPan');
+
+goog.require('ol3.Coordinate');
+goog.require('ol3.MapBrowserEvent');
+goog.require('ol3.interaction.Constraints');
+goog.require('ol3.interaction.Drag');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.interaction.Drag}
+ * @param {ol3.interaction.Constraints} constraints Constraints.
+ */
+ol3.interaction.DragPan = function(constraints) {
+ goog.base(this, constraints);
+};
+goog.inherits(ol3.interaction.DragPan, ol3.interaction.Drag);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.DragPan.prototype.handleDrag = function(mapBrowserEvent) {
+ var map = mapBrowserEvent.map;
+ var resolution = map.getResolution();
+ var rotation = map.getRotation();
+ var delta =
+ new ol3.Coordinate(-resolution * this.deltaX, resolution * this.deltaY);
+ if (map.canRotate() && goog.isDef(rotation)) {
+ delta.rotate(rotation);
+ }
+ this.pan(map, delta, this.startCenter);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.DragPan.prototype.handleDragStart = function(mapBrowserEvent) {
+ var browserEvent = mapBrowserEvent.browserEvent;
+ if (!browserEvent.shiftKey) {
+ browserEvent.preventDefault();
+ return true;
+ } else {
+ return false;
+ }
+};
diff --git a/src/ol3/interaction/interaction.js b/src/ol3/interaction/interaction.js
new file mode 100644
index 0000000000..e5e07ddb95
--- /dev/null
+++ b/src/ol3/interaction/interaction.js
@@ -0,0 +1,107 @@
+// FIXME factor out key precondition (shift et. al)
+
+goog.provide('ol3.Interaction');
+
+goog.require('ol3.MapBrowserEvent');
+goog.require('ol3.interaction.Constraints');
+
+
+
+/**
+ * @constructor
+ * @param {ol3.interaction.Constraints} constraints Constraints.
+ */
+ol3.Interaction = function(constraints) {
+
+ /**
+ * @protected
+ * @type {ol3.interaction.Constraints}
+ */
+ this.constraints = constraints;
+
+};
+
+
+/**
+ * @param {ol3.Map} map Map.
+ * @param {ol3.Extent} extent Extent.
+ */
+ol3.Interaction.prototype.fitExtent = function(map, extent) {
+ var resolution = map.getResolutionForExtent(extent);
+ resolution = this.constraints.resolution(resolution, 0);
+ var center = extent.getCenter();
+ center = this.constraints.center(center, resolution, ol3.Coordinate.ZERO);
+ map.withFrozenRendering(function() {
+ map.setCenter(center);
+ map.setResolution(resolution);
+ });
+};
+
+
+/**
+ * @param {ol3.MapBrowserEvent} mapBrowserEvent Map browser event.
+ */
+ol3.Interaction.prototype.handleMapBrowserEvent = goog.abstractMethod;
+
+
+/**
+ * @param {ol3.Map} map Map.
+ * @param {ol3.Coordinate} delta Delta.
+ * @param {ol3.Coordinate=} opt_anchor Anchor.
+ */
+ol3.Interaction.prototype.pan = function(map, delta, opt_anchor) {
+ var center = opt_anchor ? opt_anchor : map.getCenter();
+ var resolution = map.getResolution();
+ center = this.constraints.center(center, resolution, delta);
+ map.setCenter(center);
+};
+
+
+/**
+ * @param {ol3.Map} map Map.
+ * @param {number|undefined} rotation Rotation.
+ * @param {number} delta Delta.
+ * @param {ol3.Coordinate=} opt_anchor Anchor.
+ */
+ol3.Interaction.prototype.rotate = function(map, rotation, delta, opt_anchor) {
+ // FIXME handle rotation about anchor
+ rotation = this.constraints.rotation(rotation, delta);
+ map.setRotation(rotation);
+};
+
+
+/**
+ * @param {ol3.Map} map Map.
+ * @param {number|undefined} resolution Resolution.
+ */
+ol3.Interaction.prototype.setResolution = function(map, resolution) {
+ resolution = this.constraints.resolution(resolution, 0);
+ map.setResolution(resolution);
+};
+
+
+/**
+ * @param {ol3.Map} map Map.
+ * @param {number|undefined} resolution Resolution.
+ * @param {number} delta Delta.
+ * @param {ol3.Coordinate=} opt_anchor Anchor.
+ */
+ol3.Interaction.prototype.zoom = function(map, resolution, delta, opt_anchor) {
+ if (goog.isDefAndNotNull(opt_anchor)) {
+ var anchor = opt_anchor;
+ var mapCenter = /** @type {!ol3.Coordinate} */ map.getCenter();
+ var mapResolution = map.getResolution();
+ resolution = this.constraints.resolution(resolution, delta);
+ var x = anchor.x - resolution * (anchor.x - mapCenter.x) / mapResolution;
+ var y = anchor.y - resolution * (anchor.y - mapCenter.y) / mapResolution;
+ var center = new ol3.Coordinate(x, y);
+ center = this.constraints.center(center, resolution, ol3.Coordinate.ZERO);
+ map.withFrozenRendering(function() {
+ map.setCenter(center);
+ map.setResolution(resolution);
+ });
+ } else {
+ resolution = this.constraints.resolution(resolution, delta);
+ map.setResolution(resolution);
+ }
+};
diff --git a/src/ol3/interaction/keyboard.js b/src/ol3/interaction/keyboard.js
new file mode 100644
index 0000000000..a81bafc163
--- /dev/null
+++ b/src/ol3/interaction/keyboard.js
@@ -0,0 +1,53 @@
+// FIXME this class is ugly and should be removed
+
+goog.provide('ol3.interaction.Keyboard');
+
+goog.require('ol3.Interaction');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Interaction}
+ */
+ol3.interaction.Keyboard = function() {
+
+ goog.base(this, null);
+
+ /**
+ * @private
+ * @type {Object.}
+ */
+ this.charCodeCallbacks_ = {};
+
+};
+goog.inherits(ol3.interaction.Keyboard, ol3.Interaction);
+
+
+/**
+ * @param {string} s String.
+ * @param {Function} callback Callback.
+ */
+ol3.interaction.Keyboard.prototype.addCallback = function(s, callback) {
+ var i;
+ for (i = 0; i < s.length; ++i) {
+ this.charCodeCallbacks_[s.charCodeAt(i)] = callback;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.Keyboard.prototype.handleMapBrowserEvent =
+ function(mapBrowserEvent) {
+ if (mapBrowserEvent.type == goog.events.KeyHandler.EventType.KEY) {
+ var keyEvent = /** @type {goog.events.KeyEvent} */
+ mapBrowserEvent.browserEvent;
+ var callback = this.charCodeCallbacks_[keyEvent.charCode];
+ if (callback) {
+ callback();
+ mapBrowserEvent.preventDefault();
+ }
+ }
+};
diff --git a/src/ol3/interaction/keyboardpan.js b/src/ol3/interaction/keyboardpan.js
new file mode 100644
index 0000000000..24c5166d98
--- /dev/null
+++ b/src/ol3/interaction/keyboardpan.js
@@ -0,0 +1,62 @@
+goog.provide('ol3.interaction.KeyboardPan');
+
+goog.require('goog.events.KeyCodes');
+goog.require('goog.events.KeyHandler.EventType');
+goog.require('ol3.Interaction');
+goog.require('ol3.interaction.Constraints');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Interaction}
+ * @param {ol3.interaction.Constraints} constraints Constraints.
+ * @param {number} pixelDelta Pixel delta.
+ */
+ol3.interaction.KeyboardPan = function(constraints, pixelDelta) {
+
+ goog.base(this, constraints);
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.pixelDelta_ = pixelDelta;
+
+};
+goog.inherits(ol3.interaction.KeyboardPan, ol3.Interaction);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.KeyboardPan.prototype.handleMapBrowserEvent =
+ function(mapBrowserEvent) {
+ if (mapBrowserEvent.type == goog.events.KeyHandler.EventType.KEY) {
+ var keyEvent = /** @type {goog.events.KeyEvent} */
+ mapBrowserEvent.browserEvent;
+ var keyCode = keyEvent.keyCode;
+ if (keyCode == goog.events.KeyCodes.DOWN ||
+ keyCode == goog.events.KeyCodes.LEFT ||
+ keyCode == goog.events.KeyCodes.RIGHT ||
+ keyCode == goog.events.KeyCodes.UP) {
+ var map = mapBrowserEvent.map;
+ var resolution = map.getResolution();
+ var delta;
+ var mapUnitsDelta = resolution * this.pixelDelta_;
+ if (keyCode == goog.events.KeyCodes.DOWN) {
+ delta = new ol3.Coordinate(0, -mapUnitsDelta);
+ } else if (keyCode == goog.events.KeyCodes.LEFT) {
+ delta = new ol3.Coordinate(-mapUnitsDelta, 0);
+ } else if (keyCode == goog.events.KeyCodes.RIGHT) {
+ delta = new ol3.Coordinate(mapUnitsDelta, 0);
+ } else {
+ goog.asserts.assert(keyCode == goog.events.KeyCodes.UP);
+ delta = new ol3.Coordinate(0, mapUnitsDelta);
+ }
+ this.pan(map, delta);
+ keyEvent.preventDefault();
+ mapBrowserEvent.preventDefault();
+ }
+ }
+};
diff --git a/src/ol3/interaction/keyboardzoom.js b/src/ol3/interaction/keyboardzoom.js
new file mode 100644
index 0000000000..23cafba2bf
--- /dev/null
+++ b/src/ol3/interaction/keyboardzoom.js
@@ -0,0 +1,39 @@
+goog.provide('ol3.interaction.KeyboardZoom');
+
+goog.require('goog.events.KeyCodes');
+goog.require('goog.events.KeyHandler.EventType');
+goog.require('ol3.Interaction');
+goog.require('ol3.interaction.ResolutionConstraintType');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Interaction}
+ * @param {ol3.interaction.Constraints} constraints Constraints.
+ */
+ol3.interaction.KeyboardZoom = function(constraints) {
+ goog.base(this, constraints);
+};
+goog.inherits(ol3.interaction.KeyboardZoom, ol3.Interaction);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.KeyboardZoom.prototype.handleMapBrowserEvent =
+ function(mapBrowserEvent) {
+ if (mapBrowserEvent.type == goog.events.KeyHandler.EventType.KEY) {
+ var keyEvent = /** @type {goog.events.KeyEvent} */
+ mapBrowserEvent.browserEvent;
+ var charCode = keyEvent.charCode;
+ if (charCode == '+'.charCodeAt(0) || charCode == '-'.charCodeAt(0)) {
+ var map = mapBrowserEvent.map;
+ var resolution = map.getResolution();
+ var delta = charCode == '+'.charCodeAt(0) ? 1 : -1;
+ this.zoom(map, resolution, delta);
+ keyEvent.preventDefault();
+ mapBrowserEvent.preventDefault();
+ }
+ }
+};
diff --git a/src/ol3/interaction/mousewheelzoom.js b/src/ol3/interaction/mousewheelzoom.js
new file mode 100644
index 0000000000..40f41837a0
--- /dev/null
+++ b/src/ol3/interaction/mousewheelzoom.js
@@ -0,0 +1,41 @@
+goog.provide('ol3.interaction.MouseWheelZoom');
+
+goog.require('goog.events.MouseWheelEvent');
+goog.require('goog.events.MouseWheelHandler.EventType');
+goog.require('ol3.MapBrowserEvent');
+goog.require('ol3.interaction.Constraints');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Interaction}
+ * @param {ol3.interaction.Constraints} constraints Constraints.
+ */
+ol3.interaction.MouseWheelZoom = function(constraints) {
+ goog.base(this, constraints);
+};
+goog.inherits(ol3.interaction.MouseWheelZoom, ol3.Interaction);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.MouseWheelZoom.prototype.handleMapBrowserEvent =
+ function(mapBrowserEvent) {
+ if (mapBrowserEvent.type ==
+ goog.events.MouseWheelHandler.EventType.MOUSEWHEEL) {
+ var map = mapBrowserEvent.map;
+ var mouseWheelEvent = /** @type {goog.events.MouseWheelEvent} */
+ mapBrowserEvent.browserEvent;
+ goog.asserts.assert(mouseWheelEvent instanceof goog.events.MouseWheelEvent);
+ if (mouseWheelEvent.deltaY !== 0) {
+ var delta = mouseWheelEvent.deltaY < 0 ? 1 : -1;
+ var resolution = map.getResolution();
+ var anchor = mapBrowserEvent.getCoordinate();
+ this.zoom(map, resolution, delta, anchor);
+ mapBrowserEvent.preventDefault();
+ mouseWheelEvent.preventDefault();
+ }
+ }
+};
diff --git a/src/ol3/interaction/resolutionconstraint.js b/src/ol3/interaction/resolutionconstraint.js
new file mode 100644
index 0000000000..35391158b9
--- /dev/null
+++ b/src/ol3/interaction/resolutionconstraint.js
@@ -0,0 +1,73 @@
+goog.provide('ol3.interaction.ResolutionConstraint');
+goog.provide('ol3.interaction.ResolutionConstraintType');
+
+goog.require('goog.math');
+goog.require('ol3.array');
+
+
+/**
+ * @typedef {function((number|undefined), number): (number|undefined)}
+ */
+ol3.interaction.ResolutionConstraintType;
+
+
+/**
+ * @param {number} power Power.
+ * @param {number} maxResolution Maximum resolution.
+ * @param {number=} opt_minResolution Minimum resolution.
+ * @return {ol3.interaction.ResolutionConstraintType} Zoom function.
+ */
+ol3.interaction.ResolutionConstraint.createContinuous =
+ function(power, maxResolution, opt_minResolution) {
+ var minResolution = opt_minResolution || 0;
+ return function(resolution, delta) {
+ if (goog.isDef(resolution)) {
+ resolution /= Math.pow(power, delta);
+ return goog.math.clamp(resolution, minResolution, maxResolution);
+ } else {
+ return undefined;
+ }
+ };
+};
+
+
+/**
+ * @param {Array.} resolutions Resolutions.
+ * @return {ol3.interaction.ResolutionConstraintType} Zoom function.
+ */
+ol3.interaction.ResolutionConstraint.createSnapToResolutions =
+ function(resolutions) {
+ return function(resolution, delta) {
+ if (goog.isDef(resolution)) {
+ var z = ol3.array.linearFindNearest(resolutions, resolution);
+ z = goog.math.clamp(z + delta, 0, resolutions.length - 1);
+ return resolutions[z];
+ } else {
+ return undefined;
+ }
+ };
+};
+
+
+/**
+ * @param {number} power Power.
+ * @param {number} maxResolution Maximum resolution.
+ * @param {number=} opt_maxLevel Maximum level.
+ * @return {ol3.interaction.ResolutionConstraintType} Zoom function.
+ */
+ol3.interaction.ResolutionConstraint.createSnapToPower =
+ function(power, maxResolution, opt_maxLevel) {
+ return function(resolution, delta) {
+ if (goog.isDef(resolution)) {
+ var oldLevel = Math.floor(
+ Math.log(maxResolution / resolution) / Math.log(power) + 0.5);
+ var newLevel = Math.max(oldLevel + delta, 0);
+ if (goog.isDef(opt_maxLevel)) {
+ newLevel = Math.min(newLevel, opt_maxLevel);
+ }
+ return maxResolution / Math.pow(power, newLevel);
+ } else {
+ return undefined;
+ }
+ };
+};
diff --git a/src/ol3/interaction/resolutionconstraint_test.js b/src/ol3/interaction/resolutionconstraint_test.js
new file mode 100644
index 0000000000..2ce4192a2f
--- /dev/null
+++ b/src/ol3/interaction/resolutionconstraint_test.js
@@ -0,0 +1,159 @@
+goog.require('goog.testing.jsunit');
+goog.require('ol3.interaction.ResolutionConstraint');
+
+
+function testSnapToResolutionsZero() {
+ var resolutionConstraint =
+ ol3.interaction.ResolutionConstraint.createSnapToResolutions(
+ [1000, 500, 250, 100]);
+ assertEquals(1000, resolutionConstraint(1000, 0));
+ assertEquals(500, resolutionConstraint(500, 0));
+ assertEquals(250, resolutionConstraint(250, 0));
+ assertEquals(100, resolutionConstraint(100, 0));
+}
+
+
+function testSnapToResolutionsZoomIn() {
+ var resolutionConstraint =
+ ol3.interaction.ResolutionConstraint.createSnapToResolutions(
+ [1000, 500, 250, 100]);
+ assertEquals(500, resolutionConstraint(1000, 1));
+ assertEquals(250, resolutionConstraint(500, 1));
+ assertEquals(100, resolutionConstraint(250, 1));
+ assertEquals(100, resolutionConstraint(100, 1));
+}
+
+
+function testSnapToResolutionsZoomOut() {
+ var resolutionConstraint =
+ ol3.interaction.ResolutionConstraint.createSnapToResolutions(
+ [1000, 500, 250, 100]);
+ assertEquals(1000, resolutionConstraint(1000, -1));
+ assertEquals(1000, resolutionConstraint(500, -1));
+ assertEquals(500, resolutionConstraint(250, -1));
+ assertEquals(250, resolutionConstraint(100, -1));
+}
+
+
+function testSnapToResolutionsNearestZero() {
+ var resolutionConstraint =
+ ol3.interaction.ResolutionConstraint.createSnapToResolutions(
+ [1000, 500, 250, 100]);
+ assertEquals(1000, resolutionConstraint(1050, 0));
+ assertEquals(1000, resolutionConstraint(950, 0));
+ assertEquals(500, resolutionConstraint(550, 0));
+ assertEquals(500, resolutionConstraint(400, 0));
+ assertEquals(250, resolutionConstraint(300, 0));
+ assertEquals(250, resolutionConstraint(200, 0));
+ assertEquals(100, resolutionConstraint(150, 0));
+ assertEquals(100, resolutionConstraint(50, 0));
+}
+
+
+function testSnapToResolutionsNearestZoomIn() {
+ var resolutionConstraint =
+ ol3.interaction.ResolutionConstraint.createSnapToResolutions(
+ [1000, 500, 250, 100]);
+ assertEquals(500, resolutionConstraint(1050, 1));
+ assertEquals(500, resolutionConstraint(950, 1));
+ assertEquals(250, resolutionConstraint(550, 1));
+ assertEquals(250, resolutionConstraint(450, 1));
+ assertEquals(100, resolutionConstraint(300, 1));
+ assertEquals(100, resolutionConstraint(200, 1));
+ assertEquals(100, resolutionConstraint(150, 1));
+ assertEquals(100, resolutionConstraint(50, 1));
+}
+
+
+function testSnapToResolutionsNearestZoomOut() {
+ var resolutionConstraint =
+ ol3.interaction.ResolutionConstraint.createSnapToResolutions(
+ [1000, 500, 250, 100]);
+ assertEquals(1000, resolutionConstraint(1050, -1));
+ assertEquals(1000, resolutionConstraint(950, -1));
+ assertEquals(1000, resolutionConstraint(550, -1));
+ assertEquals(1000, resolutionConstraint(450, -1));
+ assertEquals(500, resolutionConstraint(300, -1));
+ assertEquals(500, resolutionConstraint(200, -1));
+ assertEquals(250, resolutionConstraint(150, -1));
+ assertEquals(250, resolutionConstraint(50, -1));
+}
+
+
+function testSnapToPowerZero() {
+ var resolutionConstraint =
+ ol3.interaction.ResolutionConstraint.createSnapToPower(2, 1024, 10);
+ assertEquals(1024, resolutionConstraint(1024, 0));
+ assertEquals(512, resolutionConstraint(512, 0));
+ assertEquals(256, resolutionConstraint(256, 0));
+ assertEquals(128, resolutionConstraint(128, 0));
+ assertEquals(64, resolutionConstraint(64, 0));
+ assertEquals(32, resolutionConstraint(32, 0));
+ assertEquals(16, resolutionConstraint(16, 0));
+ assertEquals(8, resolutionConstraint(8, 0));
+ assertEquals(4, resolutionConstraint(4, 0));
+ assertEquals(2, resolutionConstraint(2, 0));
+ assertEquals(1, resolutionConstraint(1, 0));
+}
+
+
+function testSnapToPowerZoomIn() {
+ var resolutionConstraint =
+ ol3.interaction.ResolutionConstraint.createSnapToPower(2, 1024, 10);
+ assertEquals(512, resolutionConstraint(1024, 1));
+ assertEquals(256, resolutionConstraint(512, 1));
+ assertEquals(128, resolutionConstraint(256, 1));
+ assertEquals(64, resolutionConstraint(128, 1));
+ assertEquals(32, resolutionConstraint(64, 1));
+ assertEquals(16, resolutionConstraint(32, 1));
+ assertEquals(8, resolutionConstraint(16, 1));
+ assertEquals(4, resolutionConstraint(8, 1));
+ assertEquals(2, resolutionConstraint(4, 1));
+ assertEquals(1, resolutionConstraint(2, 1));
+ assertEquals(1, resolutionConstraint(1, 1));
+}
+
+
+function testSnapToPowerZoomOut() {
+ var resolutionConstraint =
+ ol3.interaction.ResolutionConstraint.createSnapToPower(2, 1024, 10);
+ assertEquals(1024, resolutionConstraint(1024, -1));
+ assertEquals(1024, resolutionConstraint(512, -1));
+ assertEquals(512, resolutionConstraint(256, -1));
+ assertEquals(256, resolutionConstraint(128, -1));
+ assertEquals(128, resolutionConstraint(64, -1));
+ assertEquals(64, resolutionConstraint(32, -1));
+ assertEquals(32, resolutionConstraint(16, -1));
+ assertEquals(16, resolutionConstraint(8, -1));
+ assertEquals(8, resolutionConstraint(4, -1));
+ assertEquals(4, resolutionConstraint(2, -1));
+ assertEquals(2, resolutionConstraint(1, -1));
+}
+
+
+function testSnapToPowerNearestZero() {
+ var resolutionConstraint =
+ ol3.interaction.ResolutionConstraint.createSnapToPower(2, 1024, 10);
+ assertEquals(1024, resolutionConstraint(1050, 0));
+ assertEquals(1024, resolutionConstraint(9050, 0));
+ assertEquals(512, resolutionConstraint(550, 0));
+ assertEquals(512, resolutionConstraint(450, 0));
+ assertEquals(256, resolutionConstraint(300, 0));
+ assertEquals(256, resolutionConstraint(250, 0));
+ assertEquals(128, resolutionConstraint(150, 0));
+ assertEquals(128, resolutionConstraint(100, 0));
+ assertEquals(64, resolutionConstraint(75, 0));
+ assertEquals(64, resolutionConstraint(50, 0));
+ assertEquals(32, resolutionConstraint(40, 0));
+ assertEquals(32, resolutionConstraint(30, 0));
+ assertEquals(16, resolutionConstraint(20, 0));
+ assertEquals(16, resolutionConstraint(12, 0));
+ assertEquals(8, resolutionConstraint(9, 0));
+ assertEquals(8, resolutionConstraint(7, 0));
+ assertEquals(4, resolutionConstraint(5, 0));
+ assertEquals(4, resolutionConstraint(3.5, 0));
+ assertEquals(2, resolutionConstraint(2.1, 0));
+ assertEquals(2, resolutionConstraint(1.9, 0));
+ assertEquals(1, resolutionConstraint(1.1, 0));
+ assertEquals(1, resolutionConstraint(0.9, 0));
+}
diff --git a/src/ol3/interaction/rotationconstraint.js b/src/ol3/interaction/rotationconstraint.js
new file mode 100644
index 0000000000..bd2da44fb8
--- /dev/null
+++ b/src/ol3/interaction/rotationconstraint.js
@@ -0,0 +1,39 @@
+goog.provide('ol3.interaction.RotationConstraint');
+goog.provide('ol3.interaction.RotationConstraintType');
+
+
+/**
+ * @typedef {function((number|undefined), number): (number|undefined)}
+ */
+ol3.interaction.RotationConstraintType;
+
+
+/**
+ * @param {number|undefined} rotation Rotation.
+ * @param {number} delta Delta.
+ * @return {number|undefined} Rotation.
+ */
+ol3.interaction.RotationConstraint.none = function(rotation, delta) {
+ if (goog.isDef(rotation)) {
+ return rotation + delta;
+ } else {
+ return undefined;
+ }
+};
+
+
+/**
+ * @param {number} n N.
+ * @return {ol3.interaction.RotationConstraintType} Rotation constraint.
+ */
+ol3.interaction.RotationConstraint.createSnapToN = function(n) {
+ var theta = 2 * Math.PI / n;
+ return function(rotation, delta) {
+ if (goog.isDef(rotation)) {
+ rotation = Math.floor((rotation + delta) / theta + 0.5) * theta;
+ return rotation;
+ } else {
+ return undefined;
+ }
+ };
+};
diff --git a/src/ol3/interaction/shiftdragrotateandzoom.js b/src/ol3/interaction/shiftdragrotateandzoom.js
new file mode 100644
index 0000000000..821fc5530e
--- /dev/null
+++ b/src/ol3/interaction/shiftdragrotateandzoom.js
@@ -0,0 +1,75 @@
+goog.provide('ol3.interaction.ShiftDragRotateAndZoom');
+
+goog.require('goog.math.Vec2');
+goog.require('ol3.MapBrowserEvent');
+goog.require('ol3.interaction.Constraints');
+goog.require('ol3.interaction.Drag');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.interaction.Drag}
+ * @param {ol3.interaction.Constraints} constraints Constraints.
+ */
+ol3.interaction.ShiftDragRotateAndZoom = function(constraints) {
+
+ goog.base(this, constraints);
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.startRatio_ = 0;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.startRotation_ = 0;
+
+};
+goog.inherits(ol3.interaction.ShiftDragRotateAndZoom, ol3.interaction.Drag);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.ShiftDragRotateAndZoom.prototype.handleDrag =
+ function(mapBrowserEvent) {
+ var browserEvent = mapBrowserEvent.browserEvent;
+ var map = mapBrowserEvent.map;
+ var size = map.getSize();
+ var delta = new goog.math.Vec2(
+ browserEvent.offsetX - size.width / 2,
+ size.height / 2 - browserEvent.offsetY);
+ var theta = Math.atan2(delta.y, delta.x);
+ // FIXME this should use map.withFrozenRendering but an assertion fails :-(
+ this.rotate(map, this.startRotation_, -theta);
+ var resolution = this.startRatio_ * delta.magnitude();
+ this.setResolution(map, resolution);
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.ShiftDragRotateAndZoom.prototype.handleDragStart =
+ function(mapBrowserEvent) {
+ var browserEvent = mapBrowserEvent.browserEvent;
+ var map = mapBrowserEvent.map;
+ if (map.canRotate() && browserEvent.shiftKey) {
+ var resolution = map.getResolution();
+ var size = map.getSize();
+ var delta = new goog.math.Vec2(
+ browserEvent.offsetX - size.width / 2,
+ size.height / 2 - browserEvent.offsetY);
+ var theta = Math.atan2(delta.y, delta.x);
+ this.startRotation_ = (map.getRotation() || 0) + theta;
+ this.startRatio_ = resolution / delta.magnitude();
+ browserEvent.preventDefault();
+ return true;
+ } else {
+ return false;
+ }
+};
diff --git a/src/ol3/interaction/shiftdragzoom.js b/src/ol3/interaction/shiftdragzoom.js
new file mode 100644
index 0000000000..2f1ffa0d9a
--- /dev/null
+++ b/src/ol3/interaction/shiftdragzoom.js
@@ -0,0 +1,65 @@
+// FIXME draw drag box
+
+goog.provide('ol3.interaction.ShiftDragZoom');
+
+goog.require('ol3.Extent');
+goog.require('ol3.MapBrowserEvent');
+goog.require('ol3.interaction.Constraints');
+goog.require('ol3.interaction.Drag');
+
+
+/**
+ * @define {number} Hysterisis pixels.
+ */
+ol3.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS = 8;
+
+
+/**
+ * @const {number}
+ */
+ol3.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS_SQUARED =
+ ol3.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS *
+ ol3.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS;
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.interaction.Drag}
+ * @param {ol3.interaction.Constraints} constraints Constraints.
+ */
+ol3.interaction.ShiftDragZoom = function(constraints) {
+ goog.base(this, constraints);
+};
+goog.inherits(ol3.interaction.ShiftDragZoom, ol3.interaction.Drag);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.ShiftDragZoom.prototype.handleDragEnd =
+ function(mapBrowserEvent) {
+ if (this.deltaX * this.deltaX + this.deltaY * this.deltaY >=
+ ol3.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS_SQUARED) {
+ var map = mapBrowserEvent.map;
+ var extent = ol3.Extent.boundingExtent(
+ this.startCoordinate,
+ mapBrowserEvent.getCoordinate());
+ this.fitExtent(map, extent);
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.interaction.ShiftDragZoom.prototype.handleDragStart =
+ function(mapBrowserEvent) {
+ var browserEvent = mapBrowserEvent.browserEvent;
+ if (browserEvent.isMouseActionButton() && browserEvent.shiftKey) {
+ browserEvent.preventDefault();
+ return true;
+ } else {
+ return false;
+ }
+};
diff --git a/src/ol3/tile/bingmaps.js b/src/ol3/tile/bingmaps.js
new file mode 100644
index 0000000000..22bd612cfb
--- /dev/null
+++ b/src/ol3/tile/bingmaps.js
@@ -0,0 +1,189 @@
+goog.provide('ol3.layer.BingMaps');
+goog.provide('ol3.tilestore.BingMaps');
+
+goog.require('goog.Uri');
+goog.require('goog.events');
+goog.require('goog.events.EventType');
+goog.require('goog.net.Jsonp');
+goog.require('ol3.TileCoverageArea');
+goog.require('ol3.TileLayer');
+goog.require('ol3.TileStore');
+goog.require('ol3.tilegrid.XYZ');
+
+
+/**
+ * @enum {string}
+ */
+ol3.BingMapsStyle = {
+ AERIAL: 'Aerial',
+ AERIAL_WITH_LABELS: 'AerialWithLabels',
+ ROAD: 'Road',
+ ORDNANCE_SURVEY: 'OrdnanceSurvey',
+ COLLINS_BART: 'CollinsBart'
+};
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.TileLayer}
+ * @param {ol3.BingMapsStyle} style Bing Maps style.
+ * @param {string} key Key.
+ * @param {string=} opt_culture Culture.
+ * @param {Object.=} opt_values Values.
+ */
+ol3.layer.BingMaps = function(style, key, opt_culture, opt_values) {
+ var tileStore = new ol3.tilestore.BingMaps(style, key, opt_culture,
+ function(tileStore) {
+ this.dispatchEvent(goog.events.EventType.LOAD);
+ }, this);
+ goog.base(this, tileStore, opt_values);
+};
+goog.inherits(ol3.layer.BingMaps, ol3.TileLayer);
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.TileStore}
+ * @param {ol3.BingMapsStyle} style Bing Maps style.
+ * @param {string} key Key.
+ * @param {string=} opt_culture Culture.
+ * @param {?function(ol3.tilestore.BingMaps)=} opt_callback Callback.
+ * @param {*=} opt_obj Object.
+ */
+ol3.tilestore.BingMaps =
+ function(style, key, opt_culture, opt_callback, opt_obj) {
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.culture_ = opt_culture || 'en-us';
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.ready_ = false;
+
+ /**
+ * @private
+ * @type {?function(ol3.tilestore.BingMaps)}
+ */
+ this.callback_ = opt_callback || null;
+
+ /**
+ * @private
+ * @type {*}
+ */
+ this.object_ = opt_obj;
+
+ var uri = new goog.Uri(
+ 'http://dev.virtualearth.net/REST/v1/Imagery/Metadata/' + style);
+ var jsonp = new goog.net.Jsonp(uri, 'jsonp');
+ jsonp.send({
+ 'include': 'ImageryProviders',
+ 'key': key
+ }, goog.bind(this.handleImageryMetadataResponse, this));
+
+ var projection = ol3.Projection.getFromCode('EPSG:3857');
+ var extent = projection.getExtent();
+
+ goog.base(
+ this, projection, null, ol3.TileUrlFunction.nullTileUrlFunction, extent);
+
+};
+goog.inherits(ol3.tilestore.BingMaps, ol3.TileStore);
+
+
+/**
+ * @param {BingMapsImageryMetadataResponse} response Response.
+ */
+ol3.tilestore.BingMaps.prototype.handleImageryMetadataResponse =
+ function(response) {
+
+ goog.asserts.assert(
+ response.authenticationResultCode == 'ValidCredentials');
+ goog.asserts.assert(response.statusCode == 200);
+ goog.asserts.assert(response.statusDescription == 'OK');
+
+ var brandLogoUri = response.brandLogoUri;
+ var copyright = response.copyright;
+ goog.asserts.assert(response.resourceSets.length == 1);
+ var resourceSet = response.resourceSets[0];
+ goog.asserts.assert(resourceSet.resources.length == 1);
+ var resource = resourceSet.resources[0];
+
+ var zoomMin = resource.zoomMin;
+ var zoomMax = resource.zoomMax;
+ var tileSize = new ol3.Size(resource.imageWidth, resource.imageHeight);
+ var tileGrid = new ol3.tilegrid.XYZ(zoomMax, tileSize);
+ this.tileGrid = tileGrid;
+
+ this.tileUrlFunction = ol3.TileUrlFunction.withTileCoordTransform(
+ function(tileCoord) {
+ if (tileCoord.z < zoomMin || zoomMax < tileCoord.z) {
+ return null;
+ }
+ var n = 1 << tileCoord.z;
+ var y = -tileCoord.y - 1;
+ if (y < 0 || n <= y) {
+ return null;
+ } else {
+ var x = goog.math.modulo(tileCoord.x, n);
+ return new ol3.TileCoord(tileCoord.z, x, y);
+ }
+ },
+ ol3.TileUrlFunction.createFromTileUrlFunctions(
+ goog.array.map(
+ resource.imageUrlSubdomains,
+ function(subdomain) {
+ var imageUrl = resource.imageUrl
+ .replace('{subdomain}', subdomain)
+ .replace('{culture}', this.culture_);
+ return function(tileCoord) {
+ if (goog.isNull(tileCoord)) {
+ return undefined;
+ } else {
+ return imageUrl.replace(
+ '{quadkey}', tileCoord.quadKey());
+ }
+ };
+ })));
+
+ var projection = ol3.Projection.getFromCode('EPSG:4326');
+ var attributions = goog.array.map(
+ resource.imageryProviders,
+ function(imageryProvider) {
+ var html = imageryProvider.attribution;
+ var coverageAreas = goog.array.map(
+ imageryProvider.coverageAreas,
+ function(coverageArea) {
+ var bbox = coverageArea.bbox;
+ var extent = new ol3.Extent(bbox[1], bbox[0], bbox[3], bbox[2]);
+ var minZ = coverageArea.zoomMin;
+ var maxZ = coverageArea.zoomMax;
+ return new ol3.TileCoverageArea(tileGrid, extent, minZ, maxZ);
+ });
+ return new ol3.Attribution(html, coverageAreas, projection);
+ });
+ this.setAttributions(attributions);
+
+ this.ready_ = true;
+
+ if (!goog.isNull(this.callback_)) {
+ this.callback_.call(this.object_, this);
+ this.callback_ = null;
+ this.object_ = null;
+ }
+
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.tilestore.BingMaps.prototype.isReady = function() {
+ return this.ready_;
+};
diff --git a/src/ol3/tile/mapquest.js b/src/ol3/tile/mapquest.js
new file mode 100644
index 0000000000..324788b175
--- /dev/null
+++ b/src/ol3/tile/mapquest.js
@@ -0,0 +1,62 @@
+goog.provide('ol3.layer.MapQuestOSM');
+goog.provide('ol3.layer.MapQuestOpenAerial');
+
+goog.require('ol3.Attribution');
+goog.require('ol3.TileUrlFunction');
+goog.require('ol3.layer.XYZ');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.layer.XYZ}
+ * @param {Object.=} opt_values Values.
+ */
+ol3.layer.MapQuestOSM = function(opt_values) {
+
+ var tileUrlFunction = ol3.TileUrlFunction.createFromTemplate(
+ 'http://otile{1-4}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.jpg');
+
+ var attributions = [
+ new ol3.Attribution(
+ 'Tiles Courtesy of ' +
+ 'MapQuest ' +
+ ' '),
+ new ol3.Attribution(
+ 'Data © ' +
+ 'OpenStreetMap ' +
+ 'contributors, ' +
+ 'CC BY-SA ')
+ ];
+
+ goog.base(this, 18, tileUrlFunction, attributions);
+
+};
+goog.inherits(ol3.layer.MapQuestOSM, ol3.layer.XYZ);
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.layer.XYZ}
+ * @param {Object.=} opt_values Values.
+ */
+ol3.layer.MapQuestOpenAerial = function(opt_values) {
+
+ var tileUrlFunction = ol3.TileUrlFunction.createFromTemplate(
+ 'http://oatile{1-4}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg');
+
+ var attributions = [
+ new ol3.Attribution(
+ 'Tiles Courtesy of ' +
+ 'MapQuest ' +
+ ' '),
+ new ol3.Attribution(
+ 'Portions Courtesy NASA/JPL-Caltech and ' +
+ 'U.S. Depart. of Agriculture, Farm Service Agency')
+ ];
+
+ goog.base(this, 18, tileUrlFunction, attributions);
+
+};
+goog.inherits(ol3.layer.MapQuestOpenAerial, ol3.layer.XYZ);
diff --git a/src/ol3/tile/openstreetmap.js b/src/ol3/tile/openstreetmap.js
new file mode 100644
index 0000000000..a619403fc3
--- /dev/null
+++ b/src/ol3/tile/openstreetmap.js
@@ -0,0 +1,40 @@
+goog.provide('ol3.layer.OpenStreetMap');
+goog.provide('ol3.store.OpenStreetMap');
+
+goog.require('ol3.TileLayer');
+goog.require('ol3.TileUrlFunction');
+goog.require('ol3.tilestore.XYZ');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.TileLayer}
+ * @param {Object.=} opt_values Values.
+ */
+ol3.layer.OpenStreetMap = function(opt_values) {
+ var tileStore = new ol3.store.OpenStreetMap();
+ goog.base(this, tileStore, opt_values);
+};
+goog.inherits(ol3.layer.OpenStreetMap, ol3.TileLayer);
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.tilestore.XYZ}
+ */
+ol3.store.OpenStreetMap = function() {
+
+ var tileUrlFunction = ol3.TileUrlFunction.createFromTemplate(
+ 'http://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png');
+
+ var attribution = new ol3.Attribution(
+ '© OpenStreetMap ' +
+ 'contributors, ' +
+ 'CC BY-SA ');
+
+ goog.base(this, 18, tileUrlFunction, [attribution]);
+
+};
+goog.inherits(ol3.store.OpenStreetMap, ol3.tilestore.XYZ);
diff --git a/src/ol3/tile/stamen.js b/src/ol3/tile/stamen.js
new file mode 100644
index 0000000000..178e64571b
--- /dev/null
+++ b/src/ol3/tile/stamen.js
@@ -0,0 +1,92 @@
+// FIXME Configure minZoom when supported by TileGrid
+
+goog.provide('ol3.layer.Stamen');
+goog.provide('ol3.store.Stamen');
+
+goog.require('ol3.TileUrlFunction');
+goog.require('ol3.layer.XYZ');
+
+
+/**
+ * @enum {string}
+ */
+ol3.StamenProvider = {
+ TERRAIN: 'terrain',
+ TONER: 'toner',
+ WATERCOLOR: 'watercolor'
+};
+
+
+/**
+ * @enum {string}
+ */
+ol3.StamenFlavor = {
+ TERRAIN_BACKGROUND: 'background',
+ TERRAIN_LABELS: 'labels',
+ TERRAIN_LINES: 'lines',
+ TONER_2010: '2010',
+ TONER_2011: '2011',
+ TONER_2011_LABELS: '2011-labels',
+ TONER_2011_LINES: '2011-lines',
+ TONER_2011_LITE: '2011-lite',
+ TONER_BACKGROUND: 'background',
+ TONER_HYBRID: 'hybrid',
+ TONER_LABELS: 'labels',
+ TONER_LINES: 'lines',
+ TONER_LITE: 'lite'
+};
+
+
+/**
+ * @type {Object.}
+ */
+ol3.StamenProviderConfig = {};
+ol3.StamenProviderConfig[ol3.StamenProvider.TERRAIN] = {
+ type: 'jpg',
+ minZoom: 4,
+ maxZoom: 18
+};
+ol3.StamenProviderConfig[ol3.StamenProvider.TONER] = {
+ type: 'png',
+ minZoom: 0,
+ maxZoom: 20
+};
+ol3.StamenProviderConfig[ol3.StamenProvider.WATERCOLOR] = {
+ type: 'jpg',
+ minZoom: 3,
+ maxZoom: 16
+};
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.layer.XYZ}
+ * @param {ol3.StamenProvider} provider Provider.
+ * @param {ol3.StamenFlavor=} opt_flavor Flavor.
+ * @param {Object.=} opt_values Values.
+ */
+ol3.layer.Stamen = function(provider, opt_flavor, opt_values) {
+
+ var config = ol3.StamenProviderConfig[provider];
+
+ var layer = provider;
+ if (goog.isDef(opt_flavor)) {
+ layer += '-' + opt_flavor;
+ }
+ var tileUrlFunction = ol3.TileUrlFunction.createFromTemplate(
+ 'http://{a-d}.tile.stamen.com/' + layer + '/{z}/{x}/{y}.' + config.type);
+
+ var attribution = new ol3.Attribution(
+ 'Map tiles by Stamen Design , ' +
+ 'under ' +
+ 'CC BY 3.0 . ' +
+ 'Data by OpenStreetMap , ' +
+ 'under ' +
+ 'CC BY SA .');
+
+ goog.base(this, config.maxZoom, tileUrlFunction, [attribution]);
+
+};
+goog.inherits(ol3.layer.Stamen, ol3.layer.XYZ);
diff --git a/src/ol3/tile/tile.js b/src/ol3/tile/tile.js
new file mode 100644
index 0000000000..d4b372d4ac
--- /dev/null
+++ b/src/ol3/tile/tile.js
@@ -0,0 +1,159 @@
+goog.provide('ol3.Tile');
+goog.provide('ol3.TileState');
+
+goog.require('goog.array');
+goog.require('goog.events');
+goog.require('goog.events.EventTarget');
+goog.require('goog.events.EventType');
+goog.require('ol3.TileCoord');
+
+
+/**
+ * @enum {number}
+ */
+ol3.TileState = {
+ IDLE: 0,
+ LOADING: 1,
+ LOADED: 2,
+ ERROR: 3
+};
+
+
+
+/**
+ * @constructor
+ * @extends {goog.events.EventTarget}
+ * @param {ol3.TileCoord} tileCoord Tile coordinate.
+ * @param {string} src Source.
+ * @param {?string} crossOrigin Cross origin.
+ */
+ol3.Tile = function(tileCoord, src, crossOrigin) {
+
+ goog.base(this);
+
+ /**
+ * @type {ol3.TileCoord}
+ */
+ this.tileCoord = tileCoord;
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.src_ = src;
+
+ /**
+ * @private
+ * @type {ol3.TileState}
+ */
+ this.state_ = ol3.TileState.IDLE;
+
+ /**
+ * @private
+ * @type {Image}
+ */
+ this.image_ = new Image();
+ if (!goog.isNull(crossOrigin)) {
+ this.image_.crossOrigin = crossOrigin;
+ }
+
+ /**
+ * @private
+ * @type {Object.}
+ */
+ this.imageByContext_ = {};
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.imageListenerKeys_ = null;
+
+};
+goog.inherits(ol3.Tile, goog.events.EventTarget);
+
+
+/**
+ * @protected
+ */
+ol3.Tile.prototype.dispatchChangeEvent = function() {
+ this.dispatchEvent(goog.events.EventType.CHANGE);
+};
+
+
+/**
+ * @param {Object=} opt_context Object.
+ * @return {Image} Image.
+ */
+ol3.Tile.prototype.getImage = function(opt_context) {
+ if (goog.isDef(opt_context)) {
+ var image;
+ var key = goog.getUid(opt_context);
+ if (key in this.imageByContext_) {
+ return this.imageByContext_[key];
+ } else if (goog.object.isEmpty(this.imageByContext_)) {
+ image = this.image_;
+ } else {
+ image = /** @type {Image} */ this.image_.cloneNode(false);
+ }
+ this.imageByContext_[key] = image;
+ return image;
+ } else {
+ return this.image_;
+ }
+};
+
+
+/**
+ * @return {ol3.TileState} State.
+ */
+ol3.Tile.prototype.getState = function() {
+ return this.state_;
+};
+
+
+/**
+ * @private
+ */
+ol3.Tile.prototype.handleImageError_ = function() {
+ this.state_ = ol3.TileState.ERROR;
+ this.unlistenImage_();
+};
+
+
+/**
+ * @private
+ */
+ol3.Tile.prototype.handleImageLoad_ = function() {
+ this.state_ = ol3.TileState.LOADED;
+ this.unlistenImage_();
+ this.dispatchChangeEvent();
+};
+
+
+/**
+ */
+ol3.Tile.prototype.load = function() {
+ if (this.state_ == ol3.TileState.IDLE) {
+ this.state_ = ol3.TileState.LOADING;
+ goog.asserts.assert(goog.isNull(this.imageListenerKeys_));
+ this.imageListenerKeys_ = [
+ goog.events.listenOnce(this.image_, goog.events.EventType.ERROR,
+ this.handleImageError_, false, this),
+ goog.events.listenOnce(this.image_, goog.events.EventType.LOAD,
+ this.handleImageLoad_, false, this)
+ ];
+ this.image_.src = this.src_;
+ }
+};
+
+
+/**
+ * @private
+ */
+ol3.Tile.prototype.unlistenImage_ = function() {
+ goog.asserts.assert(!goog.isNull(this.imageListenerKeys_));
+ goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey);
+ this.imageListenerKeys_ = null;
+};
+
diff --git a/src/ol3/tile/tilebounds.js b/src/ol3/tile/tilebounds.js
new file mode 100644
index 0000000000..59e9eb8097
--- /dev/null
+++ b/src/ol3/tile/tilebounds.js
@@ -0,0 +1,113 @@
+goog.provide('ol3.TileBounds');
+
+goog.require('goog.asserts');
+goog.require('ol3.Rectangle');
+goog.require('ol3.TileCoord');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Rectangle}
+ * @param {number} minX Minimum X.
+ * @param {number} minY Minimum Y.
+ * @param {number} maxX Maximum X.
+ * @param {number} maxY Maximum Y.
+ */
+ol3.TileBounds = function(minX, minY, maxX, maxY) {
+ goog.base(this, minX, minY, maxX, maxY);
+};
+goog.inherits(ol3.TileBounds, ol3.Rectangle);
+
+
+/**
+ * @param {...ol3.TileCoord} var_args Tile coordinates.
+ * @return {!ol3.TileBounds} Bounding tile box.
+ */
+ol3.TileBounds.boundingTileBounds = function(var_args) {
+ var tileCoord0 = arguments[0];
+ var tileBounds = new ol3.TileBounds(tileCoord0.x, tileCoord0.y,
+ tileCoord0.x, tileCoord0.y);
+ var i;
+ for (i = 1; i < arguments.length; ++i) {
+ var tileCoord = arguments[i];
+ goog.asserts.assert(tileCoord.z == tileCoord0.z);
+ tileBounds.minX = Math.min(tileBounds.minX, tileCoord.x);
+ tileBounds.minY = Math.min(tileBounds.minY, tileCoord.y);
+ tileBounds.maxX = Math.max(tileBounds.maxX, tileCoord.x);
+ tileBounds.maxY = Math.max(tileBounds.maxY, tileCoord.y);
+ }
+ return tileBounds;
+};
+
+
+/**
+ * @return {ol3.TileBounds} Clone.
+ */
+ol3.TileBounds.prototype.clone = function() {
+ return new ol3.TileBounds(this.minX, this.minY, this.maxX, this.maxY);
+};
+
+
+/**
+ * @param {ol3.TileCoord} tileCoord Tile coordinate.
+ * @return {boolean} Contains tile coordinate.
+ */
+ol3.TileBounds.prototype.contains = function(tileCoord) {
+ return this.minX <= tileCoord.x && tileCoord.x <= this.maxX &&
+ this.minY <= tileCoord.y && tileCoord.y <= this.maxY;
+};
+
+
+/**
+ * @param {ol3.TileBounds} tileBounds Tile bounds.
+ * @return {boolean} Contains.
+ */
+ol3.TileBounds.prototype.containsTileBounds = function(tileBounds) {
+ return this.minX <= tileBounds.minX && tileBounds.maxX <= this.maxX &&
+ this.minY <= tileBounds.minY && tileBounds.minY <= this.maxY;
+};
+
+
+/**
+ * @param {ol3.TileBounds} tileBounds Tile bounds.
+ * @return {boolean} Equals.
+ */
+ol3.TileBounds.prototype.equals = function(tileBounds) {
+ return this.minX == tileBounds.minX && tileBounds.maxX == this.maxX &&
+ this.minY == tileBounds.minY && tileBounds.minY == this.minY;
+};
+
+
+/**
+ * @param {number} z Z.
+ * @param {function(this: T, ol3.TileCoord)} f Callback.
+ * @param {T=} opt_obj The object to be used for the value of 'this' within f.
+ * @template T
+ */
+ol3.TileBounds.prototype.forEachTileCoord = function(z, f, opt_obj) {
+ var x, y;
+ for (x = this.minX; x <= this.maxX; ++x) {
+ for (y = this.minY; y <= this.maxY; ++y) {
+ f.call(opt_obj, new ol3.TileCoord(z, x, y));
+ }
+ }
+};
+
+
+/**
+ * @override
+ * @return {number} Height.
+ */
+ol3.TileBounds.prototype.getHeight = function() {
+ return this.maxY - this.minY + 1;
+};
+
+
+/**
+ * @override
+ * @return {number} Width.
+ */
+ol3.TileBounds.prototype.getWidth = function() {
+ return this.maxX - this.minX + 1;
+};
diff --git a/src/ol3/tile/tilebounds_test.js b/src/ol3/tile/tilebounds_test.js
new file mode 100644
index 0000000000..07bd9b4241
--- /dev/null
+++ b/src/ol3/tile/tilebounds_test.js
@@ -0,0 +1,102 @@
+goog.require('goog.testing.jsunit');
+goog.require('ol3.TileBounds');
+
+
+function testClone() {
+ var tileBounds = new ol3.TileBounds(1, 2, 3, 4);
+ var clonedTileBounds = tileBounds.clone();
+ assertTrue(clonedTileBounds instanceof ol3.TileBounds);
+ assertFalse(clonedTileBounds === tileBounds);
+ assertEquals(tileBounds.minX, clonedTileBounds.minX);
+ assertEquals(tileBounds.minY, clonedTileBounds.minY);
+ assertEquals(tileBounds.maxX, clonedTileBounds.maxX);
+ assertEquals(tileBounds.maxY, clonedTileBounds.maxY);
+}
+
+
+function testContains() {
+ var tileBounds = new ol3.TileBounds(1, 1, 3, 3);
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 0, 0)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 0, 1)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 0, 2)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 0, 3)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 0, 4)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 1, 0)));
+ assertTrue(tileBounds.contains(new ol3.TileCoord(0, 1, 1)));
+ assertTrue(tileBounds.contains(new ol3.TileCoord(0, 1, 2)));
+ assertTrue(tileBounds.contains(new ol3.TileCoord(0, 1, 3)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 1, 4)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 2, 0)));
+ assertTrue(tileBounds.contains(new ol3.TileCoord(0, 2, 1)));
+ assertTrue(tileBounds.contains(new ol3.TileCoord(0, 2, 2)));
+ assertTrue(tileBounds.contains(new ol3.TileCoord(0, 2, 3)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 2, 4)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 3, 0)));
+ assertTrue(tileBounds.contains(new ol3.TileCoord(0, 3, 1)));
+ assertTrue(tileBounds.contains(new ol3.TileCoord(0, 3, 2)));
+ assertTrue(tileBounds.contains(new ol3.TileCoord(0, 3, 3)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 3, 4)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 4, 0)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 4, 1)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 4, 2)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 4, 3)));
+ assertFalse(tileBounds.contains(new ol3.TileCoord(0, 4, 4)));
+}
+
+
+function testBoundingTileBounds() {
+ var tileBounds = new ol3.TileBounds.boundingTileBounds(
+ new ol3.TileCoord(3, 1, 3),
+ new ol3.TileCoord(3, 2, 0));
+ assertEquals(1, tileBounds.minX);
+ assertEquals(0, tileBounds.minY);
+ assertEquals(2, tileBounds.maxX);
+ assertEquals(3, tileBounds.maxY);
+}
+
+
+function testBoundingTileBoundsMixedZ() {
+ assertThrows(function() {
+ var tileBounds = new ol3.TileBounds.boundingTileBounds(
+ new ol3.TileCoord(3, 1, 3),
+ new ol3.TileCoord(4, 2, 0));
+ });
+}
+
+
+function testForEachTileCoord() {
+
+ var tileBounds = new ol3.TileBounds(0, 2, 1, 3);
+
+ var tileCoords = [];
+ tileBounds.forEachTileCoord(5, function(tileCoord) {
+ tileCoords.push(tileCoord.clone());
+ });
+
+ assertEquals(4, tileCoords.length);
+
+ assertEquals(5, tileCoords[0].z);
+ assertEquals(0, tileCoords[0].x);
+ assertEquals(2, tileCoords[0].y);
+
+ assertEquals(5, tileCoords[1].z);
+ assertEquals(0, tileCoords[1].x);
+ assertEquals(3, tileCoords[1].y);
+
+ assertEquals(5, tileCoords[2].z);
+ assertEquals(1, tileCoords[2].x);
+ assertEquals(2, tileCoords[2].y);
+
+ assertEquals(5, tileCoords[3].z);
+ assertEquals(1, tileCoords[3].x);
+ assertEquals(3, tileCoords[3].y);
+
+}
+
+
+function testSize() {
+ var tileBounds = new ol3.TileBounds(0, 1, 2, 4);
+ var size = tileBounds.getSize();
+ assertEquals(3, size.width);
+ assertEquals(4, size.height);
+}
diff --git a/src/ol3/tile/tilecoord.js b/src/ol3/tile/tilecoord.js
new file mode 100644
index 0000000000..25d4993371
--- /dev/null
+++ b/src/ol3/tile/tilecoord.js
@@ -0,0 +1,122 @@
+goog.provide('ol3.TileCoord');
+
+goog.require('goog.array');
+goog.require('ol3.Coordinate');
+
+
+/**
+ * @enum {number}
+ */
+ol3.QuadKeyCharCode = {
+ ZERO: '0'.charCodeAt(0),
+ ONE: '1'.charCodeAt(0),
+ TWO: '2'.charCodeAt(0),
+ THREE: '3'.charCodeAt(0)
+};
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Coordinate}
+ * @param {number} z Z.
+ * @param {number} x X.
+ * @param {number} y Y.
+ */
+ol3.TileCoord = function(z, x, y) {
+
+ goog.base(this, x, y);
+
+ /**
+ * @type {number}
+ */
+ this.z = z;
+
+};
+goog.inherits(ol3.TileCoord, ol3.Coordinate);
+
+
+/**
+ * @param {string} quadKey Quad key.
+ * @return {ol3.TileCoord} Tile coordinate.
+ */
+ol3.TileCoord.createFromQuadKey = function(quadKey) {
+ var z = quadKey.length, x = 0, y = 0;
+ var mask = 1 << (z - 1);
+ var i;
+ for (i = 0; i < z; ++i) {
+ switch (quadKey.charCodeAt(i)) {
+ case ol3.QuadKeyCharCode.ONE:
+ x += mask;
+ break;
+ case ol3.QuadKeyCharCode.TWO:
+ y += mask;
+ break;
+ case ol3.QuadKeyCharCode.THREE:
+ x += mask;
+ y += mask;
+ break;
+ }
+ mask >>= 1;
+ }
+ return new ol3.TileCoord(z, x, y);
+};
+
+
+/**
+ * @param {string} str String.
+ * @return {ol3.TileCoord} Tile coord.
+ */
+ol3.TileCoord.createFromString = function(str) {
+ var v = str.split('/');
+ v = goog.array.map(v, function(e, i, a) {
+ return parseInt(e, 10);
+ });
+ return new ol3.TileCoord(v[0], v[1], v[2]);
+};
+
+
+/**
+ * @return {ol3.TileCoord} Clone.
+ */
+ol3.TileCoord.prototype.clone = function() {
+ return new ol3.TileCoord(this.z, this.x, this.y);
+};
+
+
+/**
+ * @return {number} Hash.
+ */
+ol3.TileCoord.prototype.hash = function() {
+ return (this.x << this.z) + this.y;
+};
+
+
+/**
+ * @return {string} Quad key.
+ */
+ol3.TileCoord.prototype.quadKey = function() {
+ var digits = new Array(this.z);
+ var mask = 1 << (this.z - 1);
+ var i, charCode;
+ for (i = 0; i < this.z; ++i) {
+ charCode = ol3.QuadKeyCharCode.ZERO;
+ if (this.x & mask) {
+ charCode += 1;
+ }
+ if (this.y & mask) {
+ charCode += 2;
+ }
+ digits[i] = String.fromCharCode(charCode);
+ mask >>= 1;
+ }
+ return digits.join('');
+};
+
+
+/**
+ * @return {string} String.
+ */
+ol3.TileCoord.prototype.toString = function() {
+ return [this.z, this.x, this.y].join('/');
+};
diff --git a/src/ol3/tile/tilecoord_test.js b/src/ol3/tile/tilecoord_test.js
new file mode 100644
index 0000000000..82580dcb5c
--- /dev/null
+++ b/src/ol3/tile/tilecoord_test.js
@@ -0,0 +1,39 @@
+goog.require('goog.testing.jsunit');
+goog.require('ol3.TileCoord');
+
+
+function testConstructorOrderZXY() {
+ var tc1 = new ol3.TileCoord(1, 2, 3);
+ assertEquals(1, tc1.z);
+ assertEquals(2, tc1.x);
+ assertEquals(3, tc1.y);
+}
+
+
+function testCreateFromQuadKey() {
+ var tileCoord = ol3.TileCoord.createFromQuadKey('213');
+ assertEquals(3, tileCoord.z);
+ assertEquals(3, tileCoord.x);
+ assertEquals(5, tileCoord.y);
+}
+
+
+function testCreateFromString() {
+ var str = '1/2/3';
+ var tc = ol3.TileCoord.createFromString(str);
+ assertEquals(1, tc.z);
+ assertEquals(2, tc.x);
+ assertEquals(3, tc.y);
+}
+
+
+function testQuadKey() {
+ assertEquals('213', (new ol3.TileCoord(3, 3, 5)).quadKey());
+}
+
+
+function testHash() {
+ var tc1 = new ol3.TileCoord(3, 2, 1);
+ var tc2 = new ol3.TileCoord(3, 1, 1);
+ assertTrue(tc1.hash() != tc2.hash());
+}
diff --git a/src/ol3/tile/tilecoveragearea.js b/src/ol3/tile/tilecoveragearea.js
new file mode 100644
index 0000000000..6fd721b525
--- /dev/null
+++ b/src/ol3/tile/tilecoveragearea.js
@@ -0,0 +1,71 @@
+goog.provide('ol3.TileCoverageArea');
+
+goog.require('ol3.CoverageArea');
+goog.require('ol3.Extent');
+goog.require('ol3.TileGrid');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.CoverageArea}
+ * @param {ol3.TileGrid} tileGrid Tile grid.
+ * @param {ol3.Extent} extent Extent.
+ * @param {number} minZ Minimum Z.
+ * @param {number} maxZ Maximum Z.
+ */
+ol3.TileCoverageArea = function(tileGrid, extent, minZ, maxZ) {
+
+ goog.base(this, extent);
+
+ /**
+ * @private
+ * @type {ol3.TileGrid}
+ */
+ this.tileGrid_ = tileGrid;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.minZ_ = minZ;
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.maxZ_ = maxZ;
+
+};
+goog.inherits(ol3.TileCoverageArea, ol3.CoverageArea);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.TileCoverageArea.prototype.intersectsExtentAndResolution =
+ function(extent, resolution) {
+ var z = this.tileGrid_.getZForResolution(resolution);
+ return this.intersectsExtentAndZ(extent, z);
+};
+
+
+/**
+ * @param {ol3.Extent} extent Extent.
+ * @param {number} z Z.
+ * @return {boolean} Intersects.
+ */
+ol3.TileCoverageArea.prototype.intersectsExtentAndZ = function(extent, z) {
+ return this.minZ_ <= z && z <= this.maxZ_ && this.intersectsExtent(extent);
+};
+
+
+/**
+ * @param {ol3.TransformFunction} transformFn Transform.
+ * @return {ol3.TileCoverageArea} Transformed tile coverage area.
+ */
+ol3.TileCoverageArea.prototype.transform = function(transformFn) {
+ var extent = this.extent.transform(transformFn);
+ return new ol3.TileCoverageArea(
+ this.tileGrid_, extent, this.minZ_, this.maxZ_);
+};
diff --git a/src/ol3/tile/tilegrid.js b/src/ol3/tile/tilegrid.js
new file mode 100644
index 0000000000..809b63d0b4
--- /dev/null
+++ b/src/ol3/tile/tilegrid.js
@@ -0,0 +1,311 @@
+// FIXME cope with tile grids whose minium zoom is not zero
+
+goog.provide('ol3.TileGrid');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Extent');
+goog.require('ol3.PixelBounds');
+goog.require('ol3.Size');
+goog.require('ol3.TileBounds');
+goog.require('ol3.TileCoord');
+goog.require('ol3.array');
+
+
+
+/**
+ * @constructor
+ * @param {!Array.} resolutions Resolutions.
+ * @param {ol3.Extent} extent Extent.
+ * @param {ol3.Coordinate|!Array.} origin Origin.
+ * @param {ol3.Size=} opt_tileSize Tile size.
+ */
+ol3.TileGrid = function(resolutions, extent, origin, opt_tileSize) {
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.resolutions_ = resolutions;
+ goog.asserts.assert(goog.array.isSorted(resolutions, function(a, b) {
+ return b - a;
+ }, true));
+
+ /**
+ * @private
+ * @type {number}
+ */
+ this.numResolutions_ = this.resolutions_.length;
+
+ /**
+ * @private
+ * @type {ol3.Extent}
+ */
+ this.extent_ = extent;
+
+ /**
+ * @private
+ * @type {ol3.Coordinate}
+ */
+ this.origin_ = null;
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.origins_ = null;
+
+ if (origin instanceof ol3.Coordinate) {
+ this.origin_ = origin;
+ } else if (goog.isArray(origin)) {
+ goog.asserts.assert(origin.length == this.numResolutions_);
+ this.origins_ = origin;
+ } else {
+ goog.asserts.assert(false);
+ }
+
+ /**
+ * @private
+ * @type {ol3.Size}
+ */
+ this.tileSize_ = opt_tileSize || new ol3.Size(256, 256);
+
+};
+
+
+/**
+ * @param {ol3.TileCoord} tileCoord Tile coordinate.
+ * @param {function(this: T, number, ol3.TileBounds): boolean} callback
+ * Callback.
+ * @param {T=} opt_obj Object.
+ * @template T
+ */
+ol3.TileGrid.prototype.forEachTileCoordParentTileBounds =
+ function(tileCoord, callback, opt_obj) {
+ var tileCoordExtent = this.getTileCoordExtent(tileCoord);
+ var z = tileCoord.z - 1;
+ while (z >= 0) {
+ if (callback.call(
+ opt_obj, z, this.getTileBoundsForExtentAndZ(tileCoordExtent, z))) {
+ return;
+ }
+ --z;
+ }
+};
+
+
+/**
+ * @return {ol3.Extent} Extent.
+ */
+ol3.TileGrid.prototype.getExtent = function() {
+ return this.extent_;
+};
+
+
+/**
+ * @param {number} z Z.
+ * @return {ol3.Coordinate} Origin.
+ */
+ol3.TileGrid.prototype.getOrigin = function(z) {
+ if (!goog.isNull(this.origin_)) {
+ return this.origin_;
+ } else {
+ goog.asserts.assert(!goog.isNull(this.origins_));
+ goog.asserts.assert(0 <= z && z < this.origins_.length);
+ return this.origins_[z];
+ }
+};
+
+
+/**
+ * @param {ol3.TileCoord} tileCoord Tile coordinate.
+ * @param {number} resolution Resolution.
+ * @return {ol3.PixelBounds} Pixel bounds.
+ */
+ol3.TileGrid.prototype.getPixelBoundsForTileCoordAndResolution = function(
+ tileCoord, resolution) {
+ var scale = resolution / this.getResolution(tileCoord.z);
+ var tileSize = this.getTileSize();
+ tileSize = new ol3.Size(tileSize.width / scale,
+ tileSize.height / scale);
+ var minX, maxX, minY, maxY;
+ minX = Math.round(tileCoord.x * tileSize.width);
+ maxX = Math.round((tileCoord.x + 1) * tileSize.width);
+ minY = Math.round(tileCoord.y * tileSize.height);
+ maxY = Math.round((tileCoord.y + 1) * tileSize.height);
+ return new ol3.PixelBounds(minX, minY, maxX, maxY);
+};
+
+
+/**
+ * @param {number} z Z.
+ * @return {number} Resolution.
+ */
+ol3.TileGrid.prototype.getResolution = function(z) {
+ goog.asserts.assert(0 <= z && z < this.numResolutions_);
+ return this.resolutions_[z];
+};
+
+
+/**
+ * @return {Array.} Resolutions.
+ */
+ol3.TileGrid.prototype.getResolutions = function() {
+ return this.resolutions_;
+};
+
+
+/**
+ * @param {number} z Z.
+ * @param {ol3.TileBounds} tileBounds Tile bounds.
+ * @return {ol3.Extent} Extent.
+ */
+ol3.TileGrid.prototype.getTileBoundsExtent = function(z, tileBounds) {
+ var origin = this.getOrigin(z);
+ var resolution = this.getResolution(z);
+ var tileSize = this.tileSize_;
+ var minX = origin.x + tileBounds.minX * tileSize.width * resolution;
+ var minY = origin.y + tileBounds.minY * tileSize.height * resolution;
+ var maxX = origin.x + (tileBounds.maxX + 1) * tileSize.width * resolution;
+ var maxY = origin.y + (tileBounds.maxY + 1) * tileSize.height * resolution;
+ return new ol3.Extent(minX, minY, maxX, maxY);
+};
+
+
+/**
+ * @param {ol3.Extent} extent Extent.
+ * @param {number} resolution Resolution.
+ * @return {ol3.TileBounds} Tile bounds.
+ */
+ol3.TileGrid.prototype.getTileBoundsForExtentAndResolution = function(
+ extent, resolution) {
+ var min = this.getTileCoordForCoordAndResolution(
+ new ol3.Coordinate(extent.minX, extent.minY), resolution);
+ var max = this.getTileCoordForCoordAndResolution(
+ new ol3.Coordinate(extent.maxX, extent.maxY), resolution);
+ return new ol3.TileBounds(min.x, min.y, max.x, max.y);
+};
+
+
+/**
+ * @param {ol3.Extent} extent Extent.
+ * @param {number} z Z.
+ * @return {ol3.TileBounds} Tile bounds.
+ */
+ol3.TileGrid.prototype.getTileBoundsForExtentAndZ = function(extent, z) {
+ var resolution = this.getResolution(z);
+ return this.getTileBoundsForExtentAndResolution(extent, resolution);
+};
+
+
+/**
+ * @param {ol3.TileCoord} tileCoord Tile coordinate.
+ * @return {ol3.Coordinate} Tile center.
+ */
+ol3.TileGrid.prototype.getTileCoordCenter = function(tileCoord) {
+ var origin = this.getOrigin(tileCoord.z);
+ var resolution = this.getResolution(tileCoord.z);
+ var tileSize = this.tileSize_;
+ var x = origin.x + (tileCoord.x + 0.5) * tileSize.width * resolution;
+ var y = origin.y + (tileCoord.y + 0.5) * tileSize.height * resolution;
+ return new ol3.Coordinate(x, y);
+};
+
+
+/**
+ * @param {ol3.TileCoord} tileCoord Tile coordinate.
+ * @return {ol3.Extent} Extent.
+ */
+ol3.TileGrid.prototype.getTileCoordExtent = function(tileCoord) {
+ var origin = this.getOrigin(tileCoord.z);
+ var resolution = this.getResolution(tileCoord.z);
+ var tileSize = this.tileSize_;
+ var minX = origin.x + tileCoord.x * tileSize.width * resolution;
+ var minY = origin.y + tileCoord.y * tileSize.height * resolution;
+ var maxX = minX + tileSize.width * resolution;
+ var maxY = minY + tileSize.height * resolution;
+ return new ol3.Extent(minX, minY, maxX, maxY);
+};
+
+
+/**
+ * @param {ol3.Coordinate} coordinate Coordinate.
+ * @param {number} resolution Resolution.
+ * @return {ol3.TileCoord} Tile coordinate.
+ */
+ol3.TileGrid.prototype.getTileCoordForCoordAndResolution = function(
+ coordinate, resolution) {
+ var z = this.getZForResolution(resolution);
+ var scale = resolution / this.getResolution(z);
+ var origin = this.getOrigin(z);
+
+ var offsetFromOrigin = new ol3.Coordinate(
+ Math.floor((coordinate.x - origin.x) / resolution),
+ Math.floor((coordinate.y - origin.y) / resolution));
+
+ var tileSize = this.getTileSize();
+ tileSize = new ol3.Size(tileSize.width / scale,
+ tileSize.height / scale);
+
+ var x, y;
+ x = Math.floor(offsetFromOrigin.x / tileSize.width);
+ y = Math.floor(offsetFromOrigin.y / tileSize.height);
+
+ var tileCoord = new ol3.TileCoord(z, x, y);
+ var tileCoordPixelBounds = this.getPixelBoundsForTileCoordAndResolution(
+ tileCoord, resolution);
+
+ // adjust x to allow for stretched tiles
+ if (offsetFromOrigin.x < tileCoordPixelBounds.minX) {
+ tileCoord.x -= 1;
+ } else if (offsetFromOrigin.x >= tileCoordPixelBounds.maxX) {
+ tileCoord.x += 1;
+ }
+ // adjust y to allow for stretched tiles
+ if (offsetFromOrigin.y < tileCoordPixelBounds.minY) {
+ tileCoord.y -= 1;
+ } else if (offsetFromOrigin.y >= tileCoordPixelBounds.maxY) {
+ tileCoord.y += 1;
+ }
+
+ return tileCoord;
+};
+
+
+/**
+ * @param {ol3.Coordinate} coordinate Coordinate.
+ * @param {number} z Z.
+ * @return {ol3.TileCoord} Tile coordinate.
+ */
+ol3.TileGrid.prototype.getTileCoordForCoordAndZ = function(coordinate, z) {
+ var resolution = this.getResolution(z);
+ return this.getTileCoordForCoordAndResolution(coordinate, resolution);
+};
+
+
+/**
+ * @param {ol3.TileCoord} tileCoord Tile coordinate.
+ * @return {number} Tile resolution.
+ */
+ol3.TileGrid.prototype.getTileCoordResolution = function(tileCoord) {
+ goog.asserts.assert(0 <= tileCoord.z && tileCoord.z < this.numResolutions_);
+ return this.resolutions_[tileCoord.z];
+};
+
+
+/**
+ * @return {ol3.Size} Tile size.
+ */
+ol3.TileGrid.prototype.getTileSize = function() {
+ return this.tileSize_;
+};
+
+
+/**
+ * @param {number} resolution Resolution.
+ * @return {number} Z.
+ */
+ol3.TileGrid.prototype.getZForResolution = function(resolution) {
+ return ol3.array.linearFindNearest(this.resolutions_, resolution);
+};
diff --git a/src/ol3/tile/tilegrid_test.js b/src/ol3/tile/tilegrid_test.js
new file mode 100644
index 0000000000..0650885e18
--- /dev/null
+++ b/src/ol3/tile/tilegrid_test.js
@@ -0,0 +1,465 @@
+goog.require('goog.testing.jsunit');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Extent');
+goog.require('ol3.Size');
+goog.require('ol3.TileCoord');
+goog.require('ol3.TileGrid');
+
+
+var extent;
+var resolutions;
+var origin;
+var origins;
+var tileSize;
+
+
+function setUp() {
+ resolutions = [1000, 500, 250, 100];
+ extent = new ol3.Extent(0, 0, 100000, 100000);
+ origin = new ol3.Coordinate(0, 0);
+ origins = [];
+ tileSize = new ol3.Size(100, 100);
+}
+
+
+function testCreateValid() {
+ assertNotThrows(function() {
+ return new ol3.TileGrid(resolutions, extent, origin, tileSize);
+ });
+}
+
+
+function testCreateDuplicateResolutions() {
+ var resolutions = [100, 50, 50, 25, 10];
+ assertThrows(function() {
+ return new ol3.TileGrid(resolutions, extent, origin, tileSize);
+ });
+}
+
+
+function testCreateOutOfOrderResolutions() {
+ var resolutions = [100, 25, 50, 10];
+ assertThrows(function() {
+ return new ol3.TileGrid(resolutions, extent, origin, tileSize);
+ });
+}
+
+
+function testCreateOrigins() {
+ var resolutions = [100, 50, 25, 10];
+ var origins = [origin, origin, origin, origin];
+ assertNotThrows(function() {
+ return new ol3.TileGrid(resolutions, extent, origins, tileSize);
+ });
+}
+
+
+function testCreateTooFewOrigins() {
+ var resolutions = [100, 50, 25, 10];
+ var origins = [origin, origin, origin];
+ assertThrows(function() {
+ return new ol3.TileGrid(resolutions, extent, origins, tileSize);
+ });
+}
+
+
+function testCreateTooManyOrigins() {
+ var resolutions = [100, 50, 25, 10];
+ var origins = [origin, origin, origin, origin, origin];
+ assertThrows(function() {
+ return new ol3.TileGrid(resolutions, extent, origins, tileSize);
+ });
+}
+
+
+function testGetTileCoord() {
+
+ origin = new ol3.Coordinate(0, 0);
+ var tileGrid = new ol3.TileGrid(resolutions, extent, origin, tileSize);
+ var tileCoord;
+
+ tileCoord = tileGrid.getTileCoordForCoordAndZ(
+ new ol3.Coordinate(0, 0), 3);
+ assertEquals(3, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ tileCoord = tileGrid.getTileCoordForCoordAndZ(
+ new ol3.Coordinate(0, 100000), 3);
+ assertEquals(3, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(10, tileCoord.y);
+
+ tileCoord = tileGrid.getTileCoordForCoordAndZ(
+ new ol3.Coordinate(100000, 0), 3);
+ assertEquals(3, tileCoord.z);
+ assertEquals(10, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ tileCoord = tileGrid.getTileCoordForCoordAndZ(
+ new ol3.Coordinate(100000, 100000), 3);
+ assertEquals(3, tileCoord.z);
+ assertEquals(10, tileCoord.x);
+ assertEquals(10, tileCoord.y);
+
+}
+
+
+function testGetTileCoordYSouth() {
+
+ origin = new ol3.Coordinate(0, 100000);
+ var tileGrid = new ol3.TileGrid(resolutions, extent, origin, tileSize);
+ var tileCoord;
+
+ tileCoord = tileGrid.getTileCoordForCoordAndZ(
+ new ol3.Coordinate(0, 0), 3);
+ assertEquals(3, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(-10, tileCoord.y);
+
+ tileCoord = tileGrid.getTileCoordForCoordAndZ(
+ new ol3.Coordinate(0, 100000), 3);
+ assertEquals(3, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ tileCoord = tileGrid.getTileCoordForCoordAndZ(
+ new ol3.Coordinate(100000, 0), 3);
+ assertEquals(3, tileCoord.z);
+ assertEquals(10, tileCoord.x);
+ assertEquals(-10, tileCoord.y);
+
+ tileCoord = tileGrid.getTileCoordForCoordAndZ(
+ new ol3.Coordinate(100000, 100000), 3);
+ assertEquals(3, tileCoord.z);
+ assertEquals(10, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+}
+
+function testGetTileCoordForCoordAndResolution() {
+
+ var tileSize = new ol3.Size(256, 256);
+ var tileGrid = new ol3.TileGrid([10], extent, origin, tileSize);
+
+ var coordinate;
+ var tileCoord;
+
+ // gets the first tile at the origin
+ coordinate = new ol3.Coordinate(0, 0);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 10);
+ assertEquals(0, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ // gets one tile northwest of the origin
+ coordinate = new ol3.Coordinate(-1280, 1280);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 10);
+ assertEquals(0, tileCoord.z);
+ assertEquals(-1, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ // gets one tile northeast of the origin
+ coordinate = new ol3.Coordinate(1280, 1280);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 10);
+ assertEquals(0, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ // gets one tile southeast of the origin
+ coordinate = new ol3.Coordinate(1280, -1280);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 10);
+ assertEquals(0, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(-1, tileCoord.y);
+
+ // gets one tile southwest of the origin
+ coordinate = new ol3.Coordinate(-1280, -1280);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 10);
+ assertEquals(0, tileCoord.z);
+ assertEquals(-1, tileCoord.x);
+ assertEquals(-1, tileCoord.y);
+
+ // gets the tile to the east when on the edge
+ coordinate = new ol3.Coordinate(2560, -1280);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 10);
+ assertEquals(0, tileCoord.z);
+ assertEquals(1, tileCoord.x);
+ assertEquals(-1, tileCoord.y);
+
+ // gets the tile to the north when on the edge
+ coordinate = new ol3.Coordinate(1280, -2560);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 10);
+ assertEquals(0, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(-1, tileCoord.y);
+
+ // pixels are top aligned to the origin
+ coordinate = new ol3.Coordinate(1280, -2559.999);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 10);
+ assertEquals(0, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(-1, tileCoord.y);
+
+ // pixels are left aligned to the origin
+ coordinate = new ol3.Coordinate(2559.999, -1280);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 10);
+ assertEquals(0, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(-1, tileCoord.y);
+}
+
+function testGetTileCoordForCoordAndResolutionFractional() {
+
+ var tileSize = new ol3.Size(256, 256);
+ var tileGrid = new ol3.TileGrid([1 / 3], extent, origin, tileSize);
+
+ var coordinate;
+ var tileCoord;
+
+ // These tests render at a resolution of 1. Because the layer's
+ // closest resolution is 1/3, the images are scaled by 1/3.
+ // In this scenario, every third tile will be one pixel wider when
+ // rendered (0,0 is normal; 1,0 is wider; 0,1 is taller; etc.)
+
+ // gets the first tile at the origin
+ coordinate = new ol3.Coordinate(0, 0);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 1);
+ assertEquals(0, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ // gets the 1,0 tile at 256/3,0
+ coordinate = new ol3.Coordinate(256 / 3, 0);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 1);
+ assertEquals(0, tileCoord.z);
+ assertEquals(1, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ // still gets the 1,0 tile at 512/3,0 - wider tile
+ coordinate = new ol3.Coordinate(512 / 3, 0);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 1);
+ assertEquals(0, tileCoord.z);
+ assertEquals(1, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ // gets the 2,0 tile at 513/3,0
+ coordinate = new ol3.Coordinate(513 / 3, 0);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 1);
+ assertEquals(0, tileCoord.z);
+ assertEquals(2, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ // gets the 3,0 tile at 768/3,0
+ coordinate = new ol3.Coordinate(768 / 3, 0);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 1);
+ assertEquals(0, tileCoord.z);
+ assertEquals(3, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ // gets the 4,0 tile at 1024/3,0
+ coordinate = new ol3.Coordinate(1024 / 3, 0);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 1);
+ assertEquals(0, tileCoord.z);
+ assertEquals(4, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ // still gets the 4,0 tile at 1280/3,0 - wider tile
+ coordinate = new ol3.Coordinate(1280 / 3, 0);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 1);
+ assertEquals(0, tileCoord.z);
+ assertEquals(4, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ // gets the 5,0 tile at 1281/3,0
+ coordinate = new ol3.Coordinate(1281 / 3, 0);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 1);
+ assertEquals(0, tileCoord.z);
+ assertEquals(5, tileCoord.x);
+ assertEquals(0, tileCoord.y);
+
+ // gets the 0,1 tile at 0,-256/3
+ coordinate = new ol3.Coordinate(0, -256 / 3);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 1);
+ assertEquals(0, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(-2, tileCoord.y);
+
+ // still gets the 0,1 tile at 0,-512/3 - taller tile
+ coordinate = new ol3.Coordinate(0, -512 / 3);
+ tileCoord = tileGrid.getTileCoordForCoordAndResolution(
+ coordinate, 1);
+ assertEquals(0, tileCoord.z);
+ assertEquals(0, tileCoord.x);
+ assertEquals(-2, tileCoord.y);
+}
+
+
+function testGetTileCoordCenter() {
+
+ var tileGrid = new ol3.TileGrid(resolutions, extent, origin, tileSize);
+ var center;
+
+ center = tileGrid.getTileCoordCenter(new ol3.TileCoord(0, 0, 0));
+ assertEquals(50000, center.x);
+ assertEquals(50000, center.y);
+
+ center = tileGrid.getTileCoordCenter(new ol3.TileCoord(3, 0, 0));
+ assertEquals(5000, center.x);
+ assertEquals(5000, center.y);
+
+ center = tileGrid.getTileCoordCenter(new ol3.TileCoord(3, 9, 9));
+ assertEquals(95000, center.x);
+ assertEquals(95000, center.y);
+
+}
+
+
+
+function testGetTileCoordExtent() {
+
+ var tileGrid = new ol3.TileGrid(resolutions, extent, origin, tileSize);
+ var tileCoordExtent;
+
+ tileCoordExtent = tileGrid.getTileCoordExtent(new ol3.TileCoord(0, 0, 0));
+ assertEquals(0, tileCoordExtent.minX);
+ assertEquals(0, tileCoordExtent.minY);
+ assertEquals(100000, tileCoordExtent.maxX);
+ assertEquals(100000, tileCoordExtent.maxY);
+
+ tileCoordExtent = tileGrid.getTileCoordExtent(new ol3.TileCoord(3, 9, 0));
+ assertEquals(90000, tileCoordExtent.minX);
+ assertEquals(0, tileCoordExtent.minY);
+ assertEquals(100000, tileCoordExtent.maxX);
+ assertEquals(10000, tileCoordExtent.maxY);
+
+ tileCoordExtent = tileGrid.getTileCoordExtent(new ol3.TileCoord(3, 0, 9));
+ assertEquals(0, tileCoordExtent.minX);
+ assertEquals(90000, tileCoordExtent.minY);
+ assertEquals(10000, tileCoordExtent.maxX);
+ assertEquals(100000, tileCoordExtent.maxY);
+
+}
+
+
+function testGetExtentTileBounds() {
+
+ var tileGrid = new ol3.TileGrid(resolutions, extent, origin, tileSize);
+ var e = new ol3.Extent(45000, 5000, 55000, 15000);
+ var tileBounds;
+
+ tileBounds = tileGrid.getTileBoundsForExtentAndZ(e, 0);
+ assertEquals(0, tileBounds.minY);
+ assertEquals(0, tileBounds.minX);
+ assertEquals(0, tileBounds.maxX);
+ assertEquals(0, tileBounds.maxY);
+
+ tileBounds = tileGrid.getTileBoundsForExtentAndZ(e, 1);
+ assertEquals(0, tileBounds.minX);
+ assertEquals(0, tileBounds.minY);
+ assertEquals(1, tileBounds.maxX);
+ assertEquals(0, tileBounds.maxY);
+
+ tileBounds = tileGrid.getTileBoundsForExtentAndZ(e, 2);
+ assertEquals(1, tileBounds.minX);
+ assertEquals(0, tileBounds.minY);
+ assertEquals(2, tileBounds.maxX);
+ assertEquals(0, tileBounds.maxY);
+
+ tileBounds = tileGrid.getTileBoundsForExtentAndZ(e, 3);
+ assertEquals(4, tileBounds.minX);
+ assertEquals(0, tileBounds.minY);
+ assertEquals(5, tileBounds.maxX);
+ assertEquals(1, tileBounds.maxY);
+
+}
+
+
+function testForEachTileCoordParent() {
+
+ var tileGrid = new ol3.TileGrid(resolutions, extent, origin, tileSize);
+ var zs = [], tileBoundss = [];
+
+ tileGrid.forEachTileCoordParentTileBounds(
+ new ol3.TileCoord(3, 7, 3),
+ function(z, tileBounds) {
+ zs.push(z);
+ tileBoundss.push(tileBounds);
+ return false;
+ });
+
+ assertEquals(3, zs.length);
+ assertEquals(3, tileBoundss.length);
+
+ assertEquals(2, zs[0]);
+ assertEquals(2, tileBoundss[0].minX);
+ assertEquals(1, tileBoundss[0].minY);
+ assertEquals(3, tileBoundss[0].maxX);
+ assertEquals(1, tileBoundss[0].maxY);
+
+ assertEquals(1, zs[1]);
+ assertEquals(1, tileBoundss[1].minX);
+ assertEquals(0, tileBoundss[1].minY);
+ assertEquals(1, tileBoundss[1].maxX);
+ assertEquals(0, tileBoundss[1].maxY);
+
+ assertEquals(0, zs[2]);
+ assertEquals(0, tileBoundss[2].minX);
+ assertEquals(0, tileBoundss[2].minY);
+ assertEquals(0, tileBoundss[2].maxX);
+ assertEquals(0, tileBoundss[2].maxY);
+
+}
+
+
+function testGetZForResolutionExact() {
+
+ var tileGrid =
+ new ol3.TileGrid(resolutions, extent, origin, tileSize);
+
+ assertEquals(0, tileGrid.getZForResolution(1000));
+ assertEquals(1, tileGrid.getZForResolution(500));
+ assertEquals(2, tileGrid.getZForResolution(250));
+ assertEquals(3, tileGrid.getZForResolution(100));
+
+}
+
+
+function testGetZForResolutionApproximate() {
+
+ var tileGrid =
+ new ol3.TileGrid(resolutions, extent, origin, tileSize);
+
+ assertEquals(0, tileGrid.getZForResolution(2000));
+ assertEquals(0, tileGrid.getZForResolution(1000));
+ assertEquals(0, tileGrid.getZForResolution(900));
+ assertEquals(1, tileGrid.getZForResolution(750));
+ assertEquals(1, tileGrid.getZForResolution(625));
+ assertEquals(1, tileGrid.getZForResolution(500));
+ assertEquals(1, tileGrid.getZForResolution(475));
+ assertEquals(2, tileGrid.getZForResolution(375));
+ assertEquals(2, tileGrid.getZForResolution(250));
+ assertEquals(2, tileGrid.getZForResolution(200));
+ assertEquals(3, tileGrid.getZForResolution(125));
+ assertEquals(3, tileGrid.getZForResolution(100));
+ assertEquals(3, tileGrid.getZForResolution(50));
+
+}
diff --git a/src/ol3/tile/tilejson.js b/src/ol3/tile/tilejson.js
new file mode 100644
index 0000000000..37abd086ff
--- /dev/null
+++ b/src/ol3/tile/tilejson.js
@@ -0,0 +1,180 @@
+// FIXME add some error checking
+// FIXME check order of async callbacks
+// FIXME use minzoom when supported by ol3.TileGrid
+
+/**
+ * @see http://mapbox.com/developers/api/
+ */
+
+goog.provide('ol3.layer.TileJSON');
+goog.provide('ol3.tilejson');
+goog.provide('ol3.tilestore.TileJSON');
+
+goog.require('goog.asserts');
+goog.require('goog.events.EventType');
+goog.require('goog.net.jsloader');
+goog.require('goog.string');
+goog.require('ol3.TileLayer');
+goog.require('ol3.TileStore');
+goog.require('ol3.TileUrlFunction');
+
+
+/**
+ * @private
+ * @type {Array.}
+ */
+ol3.tilejson.grids_ = [];
+
+
+/**
+ * @param {TileJSON} tileJSON Tile JSON.
+ */
+var grid = function(tileJSON) {
+ ol3.tilejson.grids_.push(tileJSON);
+};
+goog.exportSymbol('grid', grid);
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.TileLayer}
+ * @param {string} url URL.
+ * @param {Object.=} opt_values Values.
+ */
+ol3.layer.TileJSON = function(url, opt_values) {
+ goog.asserts.assert(goog.string.endsWith(url, '.jsonp'));
+ var tileStore = new ol3.tilestore.TileJSON(url, function(tileStore) {
+ this.dispatchEvent(goog.events.EventType.LOAD);
+ }, this);
+ goog.base(this, tileStore, opt_values);
+};
+goog.inherits(ol3.layer.TileJSON, ol3.TileLayer);
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.TileStore}
+ * @param {string} uri URI.
+ * @param {?function(ol3.tilestore.TileJSON)=} opt_callback Callback.
+ * @param {*=} opt_obj Object.
+ */
+ol3.tilestore.TileJSON = function(uri, opt_callback, opt_obj) {
+
+ var projection = ol3.Projection.getFromCode('EPSG:3857');
+
+ goog.base(
+ this, projection, null, ol3.TileUrlFunction.nullTileUrlFunction, null);
+
+ /**
+ * @private
+ * @type {?function(ol3.tilestore.TileJSON)}
+ */
+ this.callback_ = opt_callback || null;
+
+ /**
+ * @private
+ * @type {*}
+ */
+ this.object_ = opt_obj;
+
+ /**
+ * @private
+ * @type {boolean}
+ */
+ this.ready_ = false;
+
+ /**
+ * @private
+ * @type {!goog.async.Deferred}
+ */
+ this.deferred_ = goog.net.jsloader.load(uri, {cleanupWhenDone: true});
+ this.deferred_.addCallback(this.handleTileJSONResponse, this);
+
+};
+goog.inherits(ol3.tilestore.TileJSON, ol3.TileStore);
+
+
+/**
+ * @protected
+ */
+ol3.tilestore.TileJSON.prototype.handleTileJSONResponse = function() {
+
+ var tileJSON = ol3.tilejson.grids_.pop();
+
+ var epsg4326Projection = ol3.Projection.getFromCode('EPSG:4326');
+
+ var epsg4326Extent, extent;
+ if (goog.isDef(tileJSON.bounds)) {
+ var bounds = tileJSON.bounds;
+ epsg4326Extent = new ol3.Extent(
+ bounds[0], bounds[1], bounds[2], bounds[3]);
+ extent = epsg4326Extent.transform(
+ ol3.Projection.getTransform(epsg4326Projection, this.getProjection()));
+ this.setExtent(extent);
+ } else {
+ epsg4326Extent = null;
+ extent = null;
+ }
+
+ var scheme = goog.isDef(tileJSON.scheme) || 'xyz';
+ if (goog.isDef(tileJSON.scheme)) {
+ goog.asserts.assert(tileJSON.scheme == 'xyz');
+ }
+ var minzoom = tileJSON.minzoom || 0;
+ goog.asserts.assert(minzoom === 0); // FIXME
+ var maxzoom = tileJSON.maxzoom || 22;
+ var tileGrid = new ol3.tilegrid.XYZ(maxzoom);
+ this.tileGrid = tileGrid;
+
+ this.tileUrlFunction = ol3.TileUrlFunction.withTileCoordTransform(
+ function(tileCoord) {
+ if (tileCoord.z < minzoom || maxzoom < tileCoord.z) {
+ return null;
+ }
+ var n = 1 << tileCoord.z;
+ var y = -tileCoord.y - 1;
+ if (y < 0 || n <= y) {
+ return null;
+ }
+ var x = goog.math.modulo(tileCoord.x, n);
+ if (!goog.isNull(extent)) {
+ var tileExtent = tileGrid.getTileCoordExtent(
+ new ol3.TileCoord(tileCoord.z, x, tileCoord.y));
+ if (!tileExtent.intersects(extent)) {
+ return null;
+ }
+ }
+ return new ol3.TileCoord(tileCoord.z, x, y);
+ },
+ ol3.TileUrlFunction.createFromTemplates(tileJSON.tiles));
+
+ if (goog.isDef(tileJSON.attribution)) {
+ var coverageAreas = [
+ new ol3.TileCoverageArea(tileGrid, epsg4326Extent, minzoom, maxzoom)
+ ];
+ var coverageAreaProjection = epsg4326Projection;
+ this.setAttributions([
+ new ol3.Attribution(
+ tileJSON.attribution, coverageAreas, coverageAreaProjection)
+ ]);
+ }
+
+ this.ready_ = true;
+
+ if (!goog.isNull(this.callback_)) {
+ this.callback_.call(this.object_, this);
+ this.callback_ = null;
+ this.object_ = null;
+ }
+
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.tilestore.TileJSON.prototype.isReady = function() {
+ return this.ready_;
+};
diff --git a/src/ol3/tile/tilelayer.js b/src/ol3/tile/tilelayer.js
new file mode 100644
index 0000000000..5e254e2ee3
--- /dev/null
+++ b/src/ol3/tile/tilelayer.js
@@ -0,0 +1,26 @@
+goog.provide('ol3.TileLayer');
+
+goog.require('ol3.Layer');
+goog.require('ol3.TileStore');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Layer}
+ * @param {ol3.TileStore} tileStore Tile store.
+ * @param {Object.=} opt_values Values.
+ */
+ol3.TileLayer = function(tileStore, opt_values) {
+ goog.base(this, tileStore, opt_values);
+};
+goog.inherits(ol3.TileLayer, ol3.Layer);
+
+
+/**
+ * @override
+ * @return {ol3.TileStore} Store.
+ */
+ol3.TileLayer.prototype.getStore = function() {
+ return /** @type {ol3.TileStore} */ goog.base(this, 'getStore');
+};
diff --git a/src/ol3/tile/tilestore.js b/src/ol3/tile/tilestore.js
new file mode 100644
index 0000000000..5040a2ec2d
--- /dev/null
+++ b/src/ol3/tile/tilestore.js
@@ -0,0 +1,101 @@
+goog.provide('ol3.TileStore');
+
+goog.require('ol3.Attribution');
+goog.require('ol3.Store');
+goog.require('ol3.Tile');
+goog.require('ol3.TileCoord');
+goog.require('ol3.TileGrid');
+goog.require('ol3.TileUrlFunctionType');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.Store}
+ * @param {ol3.Projection} projection Projection.
+ * @param {ol3.TileGrid} tileGrid Tile grid.
+ * @param {ol3.TileUrlFunctionType} tileUrlFunction Tile URL.
+ * @param {ol3.Extent=} opt_extent Extent.
+ * @param {Array.=} opt_attributions Attributions.
+ * @param {?string=} opt_crossOrigin Cross origin.
+ */
+ol3.TileStore = function(projection, tileGrid, tileUrlFunction, opt_extent,
+ opt_attributions, opt_crossOrigin) {
+
+ goog.base(this, projection, opt_extent, opt_attributions);
+
+ /**
+ * @protected
+ * @type {ol3.TileGrid}
+ */
+ this.tileGrid = tileGrid;
+
+ /**
+ * @protected
+ * @type {ol3.TileUrlFunctionType}
+ */
+ this.tileUrlFunction = tileUrlFunction;
+
+ /**
+ * @private
+ * @type {?string}
+ */
+ this.crossOrigin_ = opt_crossOrigin || 'anonymous';
+
+ /**
+ * @private
+ * @type {Object.}
+ * FIXME will need to expire elements from this cache
+ * FIXME see elemoine's work with goog.structs.LinkedMap
+ */
+ this.tileCache_ = {};
+
+};
+goog.inherits(ol3.TileStore, ol3.Store);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.TileStore.prototype.getResolutions = function() {
+ return this.tileGrid.getResolutions();
+};
+
+
+/**
+ * @param {ol3.TileCoord} tileCoord Tile coordinate.
+ * @return {ol3.Tile} Tile.
+ */
+ol3.TileStore.prototype.getTile = function(tileCoord) {
+ var key = tileCoord.toString();
+ if (goog.object.containsKey(this.tileCache_, key)) {
+ return this.tileCache_[key];
+ } else {
+ var tileUrl = this.getTileCoordUrl(tileCoord);
+ var tile;
+ if (goog.isDef(tileUrl)) {
+ tile = new ol3.Tile(tileCoord, tileUrl, this.crossOrigin_);
+ } else {
+ tile = null;
+ }
+ this.tileCache_[key] = tile;
+ return tile;
+ }
+};
+
+
+/**
+ * @param {ol3.TileCoord} tileCoord Tile coordinate.
+ * @return {string|undefined} Tile URL.
+ */
+ol3.TileStore.prototype.getTileCoordUrl = function(tileCoord) {
+ return this.tileUrlFunction(tileCoord);
+};
+
+
+/**
+ * @return {ol3.TileGrid} Tile grid.
+ */
+ol3.TileStore.prototype.getTileGrid = function() {
+ return this.tileGrid;
+};
diff --git a/src/ol3/tile/tileurlfunction.js b/src/ol3/tile/tileurlfunction.js
new file mode 100644
index 0000000000..b9626a85e5
--- /dev/null
+++ b/src/ol3/tile/tileurlfunction.js
@@ -0,0 +1,94 @@
+goog.provide('ol3.TileUrlFunction');
+goog.provide('ol3.TileUrlFunctionType');
+
+goog.require('goog.math');
+goog.require('ol3.TileCoord');
+
+
+/**
+ * @typedef {function(ol3.TileCoord): (string|undefined)}
+ */
+ol3.TileUrlFunctionType;
+
+
+/**
+ * @param {string} template Template.
+ * @return {ol3.TileUrlFunctionType} Tile URL function.
+ */
+ol3.TileUrlFunction.createFromTemplate = function(template) {
+ var match =
+ /\{(\d)-(\d)\}/.exec(template) || /\{([a-z])-([a-z])\}/.exec(template);
+ if (match) {
+ var templates = [];
+ var startCharCode = match[1].charCodeAt(0);
+ var stopCharCode = match[2].charCodeAt(0);
+ var charCode;
+ for (charCode = startCharCode; charCode <= stopCharCode; ++charCode) {
+ templates.push(template.replace(match[0], String.fromCharCode(charCode)));
+ }
+ return ol3.TileUrlFunction.createFromTemplates(templates);
+ } else {
+ return function(tileCoord) {
+ if (goog.isNull(tileCoord)) {
+ return undefined;
+ } else {
+ return template.replace('{z}', tileCoord.z)
+ .replace('{x}', tileCoord.x)
+ .replace('{y}', tileCoord.y);
+ }
+ };
+ }
+};
+
+
+/**
+ * @param {Array.} templates Templates.
+ * @return {ol3.TileUrlFunctionType} Tile URL function.
+ */
+ol3.TileUrlFunction.createFromTemplates = function(templates) {
+ return ol3.TileUrlFunction.createFromTileUrlFunctions(
+ goog.array.map(templates, ol3.TileUrlFunction.createFromTemplate));
+};
+
+
+/**
+ * @param {Array.} tileUrlFunctions Tile URL Functions.
+ * @return {ol3.TileUrlFunctionType} Tile URL function.
+ */
+ol3.TileUrlFunction.createFromTileUrlFunctions = function(tileUrlFunctions) {
+ return function(tileCoord) {
+ if (goog.isNull(tileCoord)) {
+ return undefined;
+ } else {
+ var index = goog.math.modulo(tileCoord.hash(), tileUrlFunctions.length);
+ return tileUrlFunctions[index](tileCoord);
+ }
+ };
+};
+
+
+/**
+ * @param {ol3.TileCoord} tileCoord Tile coordinate.
+ * @return {string|undefined} Tile URL.
+ */
+ol3.TileUrlFunction.nullTileUrlFunction = function(tileCoord) {
+ return undefined;
+};
+
+
+/**
+ * @param {function(ol3.TileCoord): ol3.TileCoord} transformFn
+ * Transform.function.
+ * @param {ol3.TileUrlFunctionType} tileUrlFunction Tile URL function.
+ * @return {ol3.TileUrlFunctionType} Tile URL function.
+ */
+ol3.TileUrlFunction.withTileCoordTransform =
+ function(transformFn, tileUrlFunction) {
+ return function(tileCoord) {
+ if (goog.isNull(tileCoord)) {
+ return undefined;
+ } else {
+ return tileUrlFunction(transformFn(tileCoord));
+ }
+ };
+};
diff --git a/src/ol3/tile/tileurlfunction_test.js b/src/ol3/tile/tileurlfunction_test.js
new file mode 100644
index 0000000000..20f45f6f06
--- /dev/null
+++ b/src/ol3/tile/tileurlfunction_test.js
@@ -0,0 +1,33 @@
+goog.require('goog.testing.jsunit');
+goog.require('ol3.TileCoord');
+goog.require('ol3.TileUrlFunction');
+
+
+function testCreateFromTemplate() {
+ var tileUrl = ol3.TileUrlFunction.createFromTemplate('{z}/{x}/{y}');
+ assertEquals('3/2/1', tileUrl(new ol3.TileCoord(3, 2, 1)));
+ assertUndefined(tileUrl(null));
+}
+
+
+function testWithTileCoordTransform() {
+ var tileUrl = ol3.TileUrlFunction.withTileCoordTransform(
+ function(tileCoord) {
+ return new ol3.TileCoord(tileCoord.z, tileCoord.x, -tileCoord.y);
+ },
+ ol3.TileUrlFunction.createFromTemplate('{z}/{x}/{y}'));
+ assertEquals('3/2/1', tileUrl(new ol3.TileCoord(3, 2, -1)));
+ assertUndefined(tileUrl(null));
+}
+
+
+function testCreateFromTileUrlFunctions() {
+ var tileUrl = ol3.TileUrlFunction.createFromTileUrlFunctions([
+ ol3.TileUrlFunction.createFromTemplate('a'),
+ ol3.TileUrlFunction.createFromTemplate('b')
+ ]);
+ var tileUrl1 = tileUrl(new ol3.TileCoord(1, 0, 0));
+ var tileUrl2 = tileUrl(new ol3.TileCoord(1, 0, 1));
+ assertTrue(tileUrl1 != tileUrl2);
+ assertUndefined(tileUrl(null));
+}
diff --git a/src/ol3/tile/xyz.js b/src/ol3/tile/xyz.js
new file mode 100644
index 0000000000..012bcc972b
--- /dev/null
+++ b/src/ol3/tile/xyz.js
@@ -0,0 +1,118 @@
+goog.provide('ol3.layer.XYZ');
+goog.provide('ol3.tilegrid.XYZ');
+goog.provide('ol3.tilestore.XYZ');
+
+goog.require('goog.math');
+goog.require('ol3.Attribution');
+goog.require('ol3.Coordinate');
+goog.require('ol3.Layer');
+goog.require('ol3.Projection');
+goog.require('ol3.Size');
+goog.require('ol3.TileCoord');
+goog.require('ol3.TileGrid');
+goog.require('ol3.TileLayer');
+goog.require('ol3.TileStore');
+goog.require('ol3.TileUrlFunction');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.TileGrid}
+ * @param {number} maxZoom Maximum zoom.
+ * @param {ol3.Size=} opt_tileSize Tile size.
+ */
+ol3.tilegrid.XYZ = function(maxZoom, opt_tileSize) {
+
+ var resolutions = new Array(maxZoom + 1);
+ var z;
+ for (z = 0; z <= maxZoom; ++z) {
+ resolutions[z] = ol3.Projection.EPSG_3857_HALF_SIZE / (128 << z);
+ }
+
+ var extent = ol3.Projection.EPSG_3857_EXTENT;
+ var origin = new ol3.Coordinate(
+ -ol3.Projection.EPSG_3857_HALF_SIZE, ol3.Projection.EPSG_3857_HALF_SIZE);
+
+ goog.base(this, resolutions, extent, origin, opt_tileSize);
+
+};
+goog.inherits(ol3.tilegrid.XYZ, ol3.TileGrid);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.tilegrid.XYZ.prototype.forEachTileCoordParentTileBounds =
+ function(tileCoord, callback, opt_obj) {
+ var x = tileCoord.x;
+ var y = tileCoord.y;
+ var z = tileCoord.z;
+ var tileBounds;
+ while (true) {
+ z -= 1;
+ if (z < 0) {
+ break;
+ }
+ x = Math.floor(x / 2);
+ y = Math.floor(y / 2);
+ tileBounds = new ol3.TileBounds(x, y, x, y);
+ if (callback.call(opt_obj, z, tileBounds)) {
+ break;
+ }
+ }
+};
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.TileLayer}
+ * @param {number} maxZoom Maximum zoom.
+ * @param {ol3.TileUrlFunctionType} tileUrlFunction Tile URL function.
+ * @param {Array.=} opt_attributions Attributions.
+ * @param {string=} opt_crossOrigin Cross origin.
+ * @param {Object.=} opt_values Values.
+ */
+ol3.layer.XYZ = function(
+ maxZoom, tileUrlFunction, opt_attributions, opt_crossOrigin, opt_values) {
+ var tileStore = new ol3.tilestore.XYZ(
+ maxZoom, tileUrlFunction, opt_attributions, opt_crossOrigin);
+ goog.base(this, tileStore, opt_values);
+};
+goog.inherits(ol3.layer.XYZ, ol3.TileLayer);
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.TileStore}
+ * @param {number} maxZoom Maximum zoom.
+ * @param {ol3.TileUrlFunctionType} tileUrlFunction Tile URL function.
+ * @param {Array.=} opt_attributions Attributions.
+ * @param {string=} opt_crossOrigin Cross origin.
+ */
+ol3.tilestore.XYZ =
+ function(maxZoom, tileUrlFunction, opt_attributions, opt_crossOrigin) {
+
+ var projection = ol3.Projection.getFromCode('EPSG:3857');
+ var tileGrid = new ol3.tilegrid.XYZ(maxZoom);
+ var tileUrlFunction2 = ol3.TileUrlFunction.withTileCoordTransform(
+ function(tileCoord) {
+ var n = 1 << tileCoord.z;
+ var y = -tileCoord.y - 1;
+ if (y < 0 || n <= y) {
+ return null;
+ } else {
+ var x = goog.math.modulo(tileCoord.x, n);
+ return new ol3.TileCoord(tileCoord.z, x, y);
+ }
+ },
+ tileUrlFunction);
+ var extent = projection.getExtent();
+
+ goog.base(this, projection, tileGrid, tileUrlFunction2, extent,
+ opt_attributions, opt_crossOrigin);
+
+};
+goog.inherits(ol3.tilestore.XYZ, ol3.TileStore);
diff --git a/src/ol3/tile/xyz_test.js b/src/ol3/tile/xyz_test.js
new file mode 100644
index 0000000000..d4c5612842
--- /dev/null
+++ b/src/ol3/tile/xyz_test.js
@@ -0,0 +1,129 @@
+goog.require('goog.testing.jsunit');
+goog.require('ol3.Coordinate');
+goog.require('ol3.TileCoord');
+goog.require('ol3.TileUrlFunction');
+goog.require('ol3.tilestore.XYZ');
+
+
+function testXYZ() {
+
+ var xyzTileStore = new ol3.tilestore.XYZ(
+ 6, ol3.TileUrlFunction.createFromTemplate('{z}/{x}/{y}'));
+ var tileGrid = xyzTileStore.getTileGrid();
+
+ var coordinate = new ol3.Coordinate(829330.2064098881, 5933916.615134273);
+ var tileUrl;
+
+ tileUrl = xyzTileStore.getTileCoordUrl(
+ tileGrid.getTileCoordForCoordAndZ(coordinate, 0));
+ assertEquals('0/0/0', tileUrl);
+
+ tileUrl = xyzTileStore.getTileCoordUrl(
+ tileGrid.getTileCoordForCoordAndZ(coordinate, 1));
+ assertEquals('1/1/0', tileUrl);
+
+ tileUrl = xyzTileStore.getTileCoordUrl(
+ tileGrid.getTileCoordForCoordAndZ(coordinate, 2));
+ assertEquals('2/2/1', tileUrl);
+
+ tileUrl = xyzTileStore.getTileCoordUrl(
+ tileGrid.getTileCoordForCoordAndZ(coordinate, 3));
+ assertEquals('3/4/2', tileUrl);
+
+ tileUrl = xyzTileStore.getTileCoordUrl(
+ tileGrid.getTileCoordForCoordAndZ(coordinate, 4));
+ assertEquals('4/8/5', tileUrl);
+
+ tileUrl = xyzTileStore.getTileCoordUrl(
+ tileGrid.getTileCoordForCoordAndZ(coordinate, 5));
+ assertEquals('5/16/11', tileUrl);
+
+ tileUrl = xyzTileStore.getTileCoordUrl(
+ tileGrid.getTileCoordForCoordAndZ(coordinate, 6));
+ assertEquals('6/33/22', tileUrl);
+
+}
+
+
+function testXYZWrapX() {
+
+ var xyzTileStore = new ol3.tilestore.XYZ(
+ 6, ol3.TileUrlFunction.createFromTemplate('{z}/{x}/{y}'));
+
+ tileUrl = xyzTileStore.getTileCoordUrl(new ol3.TileCoord(6, -31, -23));
+ assertEquals('6/33/22', tileUrl);
+
+ tileUrl = xyzTileStore.getTileCoordUrl(new ol3.TileCoord(6, 33, -23));
+ assertEquals('6/33/22', tileUrl);
+
+ tileUrl = xyzTileStore.getTileCoordUrl(new ol3.TileCoord(6, 97, -23));
+ assertEquals('6/33/22', tileUrl);
+
+}
+
+
+function testXYZCropY() {
+
+ var xyzTileStore = new ol3.tilestore.XYZ(
+ 6, ol3.TileUrlFunction.createFromTemplate('{z}/{x}/{y}'));
+
+ tileUrl = xyzTileStore.getTileCoordUrl(new ol3.TileCoord(6, 33, -87));
+ assertUndefined(tileUrl);
+
+ tileUrl = xyzTileStore.getTileCoordUrl(new ol3.TileCoord(6, 33, -23));
+ assertEquals('6/33/22', tileUrl);
+
+ tileUrl = xyzTileStore.getTileCoordUrl(new ol3.TileCoord(6, 33, 41));
+ assertUndefined(tileUrl);
+
+}
+
+
+function testXYZTileGridForEachTileCoordParentTileBounds() {
+
+ var xyzTileGrid = new ol3.tilegrid.XYZ(6);
+
+ var tileCoord = new ol3.TileCoord(5, 11, 21);
+ var zs = [], tileBoundss = [];
+ xyzTileGrid.forEachTileCoordParentTileBounds(
+ tileCoord,
+ function(z, tileBounds) {
+ zs.push(z);
+ tileBoundss.push(tileBounds);
+ return false;
+ });
+
+ assertEquals(5, zs.length);
+ assertEquals(5, tileBoundss.length);
+
+ assertEquals(4, zs[0]);
+ assertEquals(5, tileBoundss[0].minX);
+ assertEquals(10, tileBoundss[0].minY);
+ assertEquals(5, tileBoundss[0].maxX);
+ assertEquals(10, tileBoundss[0].maxY);
+
+ assertEquals(3, zs[1]);
+ assertEquals(2, tileBoundss[1].minX);
+ assertEquals(5, tileBoundss[1].minY);
+ assertEquals(2, tileBoundss[1].maxX);
+ assertEquals(5, tileBoundss[1].maxY);
+
+ assertEquals(2, zs[2]);
+ assertEquals(1, tileBoundss[2].minX);
+ assertEquals(2, tileBoundss[2].minY);
+ assertEquals(1, tileBoundss[2].maxX);
+ assertEquals(2, tileBoundss[2].maxY);
+
+ assertEquals(1, zs[3]);
+ assertEquals(0, tileBoundss[3].minX);
+ assertEquals(1, tileBoundss[3].minY);
+ assertEquals(0, tileBoundss[3].maxX);
+ assertEquals(1, tileBoundss[3].maxY);
+
+ assertEquals(0, zs[4]);
+ assertEquals(0, tileBoundss[4].minX);
+ assertEquals(0, tileBoundss[4].minY);
+ assertEquals(0, tileBoundss[4].maxX);
+ assertEquals(0, tileBoundss[4].maxY);
+
+}
diff --git a/src/ol3/webgl/layerrenderer.js b/src/ol3/webgl/layerrenderer.js
new file mode 100644
index 0000000000..b53d04cb84
--- /dev/null
+++ b/src/ol3/webgl/layerrenderer.js
@@ -0,0 +1,113 @@
+goog.provide('ol3.webgl.LayerRenderer');
+
+goog.require('goog.vec.Mat4');
+goog.require('ol3.Layer');
+goog.require('ol3.LayerRenderer');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.LayerRenderer}
+ * @param {ol3.MapRenderer} mapRenderer Map renderer.
+ * @param {ol3.Layer} layer Layer.
+ */
+ol3.webgl.LayerRenderer = function(mapRenderer, layer) {
+ goog.base(this, mapRenderer, layer);
+};
+goog.inherits(ol3.webgl.LayerRenderer, ol3.LayerRenderer);
+
+
+/**
+ * @protected
+ */
+ol3.webgl.LayerRenderer.prototype.dispatchChangeEvent = function() {
+ this.dispatchEvent(goog.events.EventType.CHANGE);
+};
+
+
+/**
+ * @override
+ * @return {ol3.MapRenderer} MapRenderer.
+ */
+ol3.webgl.LayerRenderer.prototype.getMapRenderer = function() {
+ return /** @type {ol3.webgl.MapRenderer} */ goog.base(this, 'getMapRenderer');
+};
+
+
+/**
+ * @return {goog.vec.Mat4.AnyType} Matrix.
+ */
+ol3.webgl.LayerRenderer.prototype.getMatrix = goog.abstractMethod;
+
+
+/**
+ * @return {WebGLTexture} Texture.
+ */
+ol3.webgl.LayerRenderer.prototype.getTexture = goog.abstractMethod;
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.LayerRenderer.prototype.handleLayerBrightnessChange = function() {
+ this.dispatchChangeEvent();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.LayerRenderer.prototype.handleLayerContrastChange = function() {
+ this.dispatchChangeEvent();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.LayerRenderer.prototype.handleLayerHueChange = function() {
+ this.dispatchChangeEvent();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.LayerRenderer.prototype.handleLayerLoad = function() {
+ this.dispatchChangeEvent();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.LayerRenderer.prototype.handleLayerOpacityChange = function() {
+ this.dispatchChangeEvent();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.LayerRenderer.prototype.handleLayerSaturationChange = function() {
+ this.dispatchChangeEvent();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.LayerRenderer.prototype.handleLayerVisibleChange = function() {
+ this.dispatchChangeEvent();
+};
+
+
+/**
+ */
+ol3.webgl.LayerRenderer.prototype.handleWebGLContextLost = goog.nullFunction;
+
+
+/**
+ */
+ol3.webgl.LayerRenderer.prototype.render = goog.abstractMethod;
diff --git a/src/ol3/webgl/maprenderer.js b/src/ol3/webgl/maprenderer.js
new file mode 100644
index 0000000000..8c328b9973
--- /dev/null
+++ b/src/ol3/webgl/maprenderer.js
@@ -0,0 +1,637 @@
+// FIXME clear textureCache
+// FIXME defer texture loads until after render when animating
+// FIXME generational tile texture garbage collector newFrame/get
+// FIXME defer cleanup until post-render
+// FIXME check against gl.getParameter(webgl.MAX_TEXTURE_SIZE)
+
+goog.provide('ol3.webgl.MapRenderer');
+goog.provide('ol3.webgl.map.shader');
+
+goog.require('goog.debug.Logger');
+goog.require('goog.dispose');
+goog.require('goog.dom');
+goog.require('goog.dom.TagName');
+goog.require('goog.events');
+goog.require('goog.events.Event');
+goog.require('goog.events.EventType');
+goog.require('goog.functions');
+goog.require('goog.style');
+goog.require('goog.webgl');
+goog.require('ol3.Layer');
+goog.require('ol3.Map');
+goog.require('ol3.TileLayer');
+goog.require('ol3.webgl.TileLayerRenderer');
+goog.require('ol3.webgl.WebGLContextEventType');
+goog.require('ol3.webgl.shader.Fragment');
+goog.require('ol3.webgl.shader.Vertex');
+
+
+/**
+ * @typedef {{magFilter: number, minFilter: number, texture: WebGLTexture}}
+ */
+ol3.webgl.TextureCacheEntry;
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.webgl.shader.Fragment}
+ * @see https://github.com/evanw/glfx.js/blob/master/src/filters/adjust/brightnesscontrast.js
+ * @see https://github.com/evanw/glfx.js/blob/master/src/filters/adjust/huesaturation.js
+ */
+ol3.webgl.map.shader.Fragment = function() {
+ goog.base(this, [
+ 'precision mediump float;',
+ '',
+ 'uniform float uBrightness;',
+ 'uniform float uContrast;',
+ 'uniform float uHue;',
+ 'uniform float uOpacity;',
+ 'uniform mat4 uMatrix;',
+ 'uniform float uSaturation;',
+ 'uniform sampler2D uTexture;',
+ '',
+ 'varying vec2 vTexCoord;',
+ '',
+ 'void main(void) {',
+ '',
+ ' vec4 texCoord = uMatrix * vec4(vTexCoord, 0., 1.);',
+ ' vec4 color = texture2D(uTexture, texCoord.st);',
+ '',
+ ' if (uHue != 0.) {',
+ ' float angle = uHue * 3.14159265;',
+ ' float s = sin(angle), c = cos(angle);',
+ ' vec3 weights = (vec3(2. * c, -sqrt(3.) * s - c, sqrt(3.) * s - c)',
+ ' + 1.) / 3.;',
+ ' color.rgb = vec3(',
+ ' dot(color.rgb, weights.xyz),',
+ ' dot(color.rgb, weights.zxy),',
+ ' dot(color.rgb, weights.yzx)',
+ ' );',
+ ' }',
+ '',
+ ' if (uSaturation != 0.) {',
+ ' float average = (color.r + color.g + color.b) / 3.;',
+ ' if (uSaturation > 0.) {',
+ ' color.rgb += (average - color.rgb)',
+ ' * (1. - 1. / (1. - uSaturation));',
+ ' } else {',
+ ' color.rgb += (average - color.rgb) * -uSaturation;',
+ ' }',
+ ' }',
+ '',
+ ' color.rgb += uBrightness;',
+ '',
+ ' if (uContrast != 0.) {',
+ ' if (uContrast > 0.) {',
+ ' color.rgb = (color.rgb - 0.5) / (1. - uContrast) + 0.5;',
+ ' } else {',
+ ' color.rgb = (color.rgb - 0.5) * (1. + uContrast) + 0.5;',
+ ' }',
+ ' }',
+ '',
+ ' color.a = color.a * uOpacity;',
+ '',
+ ' gl_FragColor = color;',
+ '',
+ '}'
+ ].join('\n'));
+};
+goog.inherits(ol3.webgl.map.shader.Fragment, ol3.webgl.shader.Fragment);
+goog.addSingletonGetter(ol3.webgl.map.shader.Fragment);
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.webgl.shader.Vertex}
+ */
+ol3.webgl.map.shader.Vertex = function() {
+ goog.base(this, [
+ 'attribute vec2 aPosition;',
+ 'attribute vec2 aTexCoord;',
+ '',
+ 'varying vec2 vTexCoord;',
+ '',
+ 'void main(void) {',
+ ' gl_Position = vec4(aPosition, 0., 1.);',
+ ' vTexCoord = aTexCoord;',
+ '}'
+ ].join('\n'));
+};
+goog.inherits(ol3.webgl.map.shader.Vertex, ol3.webgl.shader.Vertex);
+goog.addSingletonGetter(ol3.webgl.map.shader.Vertex);
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.MapRenderer}
+ * @param {Element} container Container.
+ * @param {ol3.Map} map Map.
+ */
+ol3.webgl.MapRenderer = function(container, map) {
+
+ goog.base(this, container, map);
+
+ if (goog.DEBUG) {
+ /**
+ * @inheritDoc
+ */
+ this.logger = goog.debug.Logger.getLogger(
+ 'ol3.webgl.maprenderer.' + goog.getUid(this));
+ }
+
+ /**
+ * @private
+ * @type {Element}
+ */
+ this.canvas_ = goog.dom.createElement(goog.dom.TagName.CANVAS);
+ this.canvas_.height = container.clientHeight;
+ this.canvas_.width = container.clientWidth;
+ goog.dom.appendChild(container, this.canvas_);
+
+ /**
+ * @private
+ * @type {WebGLRenderingContext}
+ */
+ this.gl_ = this.canvas_.getContext('experimental-webgl', {
+ alpha: false,
+ antialias: true,
+ depth: false,
+ preserveDrawingBuffer: false,
+ stencil: false
+ });
+ goog.asserts.assert(!goog.isNull(this.gl_));
+
+ goog.events.listen(this.canvas_, ol3.webgl.WebGLContextEventType.LOST,
+ this.handleWebGLContextLost, false, this);
+ goog.events.listen(this.canvas_, ol3.webgl.WebGLContextEventType.RESTORED,
+ this.handleWebGLContextRestored, false, this);
+
+ /**
+ * @private
+ * @type {ol3.Color}
+ */
+ this.clearColor_ = new ol3.Color(1, 1, 1, 1);
+
+ /**
+ * @private
+ * @type {{aPosition: number,
+ * aTexCoord: number,
+ * uBrightness: WebGLUniformLocation,
+ * uContrast: WebGLUniformLocation,
+ * uHue: WebGLUniformLocation,
+ * uMatrix: WebGLUniformLocation,
+ * uOpacity: WebGLUniformLocation,
+ * uSaturation: WebGLUniformLocation,
+ * uTexture: WebGLUniformLocation}|null}
+ */
+ this.locations_ = null;
+
+ /**
+ * @private
+ * @type {WebGLBuffer}
+ */
+ this.arrayBuffer_ = null;
+
+ /**
+ * @private
+ * @type {Object.}
+ */
+ this.shaderCache_ = {};
+
+ /**
+ * @private
+ * @type {Object.}
+ */
+ this.programCache_ = {};
+
+ /**
+ * @private
+ * @type {Object.}
+ */
+ this.textureCache_ = {};
+
+ /**
+ * @private
+ * @type {ol3.webgl.shader.Fragment}
+ */
+ this.fragmentShader_ = ol3.webgl.map.shader.Fragment.getInstance();
+
+ /**
+ * @private
+ * @type {ol3.webgl.shader.Vertex}
+ */
+ this.vertexShader_ = ol3.webgl.map.shader.Vertex.getInstance();
+
+ /**
+ * @private
+ * @type {Object.}
+ */
+ this.layerRendererChangeListenKeys_ = {};
+
+ this.initializeGL_();
+
+};
+goog.inherits(ol3.webgl.MapRenderer, ol3.MapRenderer);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.addLayer = function(layer) {
+ goog.base(this, 'addLayer', layer);
+ if (layer.getVisible()) {
+ this.getMap().render();
+ }
+};
+
+
+/**
+ * @param {Image} image Image.
+ * @param {number} magFilter Mag filter.
+ * @param {number} minFilter Min filter.
+ */
+ol3.webgl.MapRenderer.prototype.bindImageTexture =
+ function(image, magFilter, minFilter) {
+ var gl = this.getGL();
+ var imageKey = image.src;
+ var textureCacheEntry = this.textureCache_[imageKey];
+ if (goog.isDef(textureCacheEntry)) {
+ gl.bindTexture(goog.webgl.TEXTURE_2D, textureCacheEntry.texture);
+ if (textureCacheEntry.magFilter != magFilter) {
+ gl.texParameteri(
+ goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, magFilter);
+ textureCacheEntry.magFilter = magFilter;
+ }
+ if (textureCacheEntry.minFilter != minFilter) {
+ gl.texParameteri(
+ goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, minFilter);
+ textureCacheEntry.minFilter = minFilter;
+ }
+ } else {
+ var texture = gl.createTexture();
+ gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
+ gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA, goog.webgl.RGBA,
+ goog.webgl.UNSIGNED_BYTE, image);
+ gl.texParameteri(
+ goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, magFilter);
+ gl.texParameteri(
+ goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER, minFilter);
+ 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);
+ this.textureCache_[imageKey] = {
+ texture: texture,
+ magFilter: magFilter,
+ minFilter: minFilter
+ };
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.canRotate = goog.functions.TRUE;
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.createLayerRenderer = function(layer) {
+ var gl = this.getGL();
+ if (layer instanceof ol3.TileLayer) {
+ return new ol3.webgl.TileLayerRenderer(this, layer);
+ } else {
+ goog.asserts.assert(false);
+ return null;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.disposeInternal = function() {
+ var gl = this.getGL();
+ if (!gl.isContextLost()) {
+ goog.object.forEach(this.programCache_, function(program) {
+ gl.deleteProgram(program);
+ });
+ goog.object.forEach(this.shaderCache_, function(shader) {
+ gl.deleteShader(shader);
+ });
+ goog.object.forEach(this.textureCache_, function(textureCacheEntry) {
+ gl.deleteTexture(textureCacheEntry.texture);
+ });
+ }
+ goog.base(this, 'disposeInternal');
+};
+
+
+/**
+ * @return {WebGLRenderingContext} GL.
+ */
+ol3.webgl.MapRenderer.prototype.getGL = function() {
+ return this.gl_;
+};
+
+
+/**
+ * @param {ol3.webgl.shader.Fragment} fragmentShaderObject Fragment shader.
+ * @param {ol3.webgl.shader.Vertex} vertexShaderObject Vertex shader.
+ * @return {WebGLProgram} Program.
+ */
+ol3.webgl.MapRenderer.prototype.getProgram = function(
+ fragmentShaderObject, vertexShaderObject) {
+ var programKey =
+ goog.getUid(fragmentShaderObject) + '/' + goog.getUid(vertexShaderObject);
+ if (programKey in this.programCache_) {
+ return this.programCache_[programKey];
+ } else {
+ var gl = this.getGL();
+ var program = gl.createProgram();
+ gl.attachShader(program, this.getShader(fragmentShaderObject));
+ gl.attachShader(program, this.getShader(vertexShaderObject));
+ gl.linkProgram(program);
+ if (goog.DEBUG) {
+ if (!gl.getProgramParameter(program, goog.webgl.LINK_STATUS) &&
+ !gl.isContextLost()) {
+ this.logger.severe(gl.getProgramInfoLog(program));
+ goog.asserts.assert(
+ gl.getProgramParameter(program, goog.webgl.LINK_STATUS));
+ }
+ }
+ this.programCache_[programKey] = program;
+ return program;
+ }
+};
+
+
+/**
+ * @param {ol3.webgl.Shader} shaderObject Shader object.
+ * @return {WebGLShader} Shader.
+ */
+ol3.webgl.MapRenderer.prototype.getShader = function(shaderObject) {
+ var shaderKey = goog.getUid(shaderObject);
+ if (shaderKey in this.shaderCache_) {
+ return this.shaderCache_[shaderKey];
+ } else {
+ var gl = this.getGL();
+ var shader = gl.createShader(shaderObject.getType());
+ gl.shaderSource(shader, shaderObject.getSource());
+ gl.compileShader(shader);
+ if (goog.DEBUG) {
+ if (!gl.getShaderParameter(shader, goog.webgl.COMPILE_STATUS) &&
+ !gl.isContextLost()) {
+ this.logger.severe(gl.getShaderInfoLog(shader));
+ goog.asserts.assert(
+ gl.getShaderParameter(shader, goog.webgl.COMPILE_STATUS));
+ }
+ }
+ this.shaderCache_[shaderKey] = shader;
+ return shader;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.handleBackgroundColorChanged = function() {
+ var backgroundColor = this.getMap().getBackgroundColor();
+ this.clearColor_ = new ol3.Color(
+ backgroundColor.r / 255,
+ backgroundColor.g / 255,
+ backgroundColor.b / 255,
+ backgroundColor.a / 255);
+ this.getMap().render();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.handleCenterChanged = function() {
+ goog.base(this, 'handleCenterChanged');
+ this.getMap().render();
+};
+
+
+/**
+ * @param {goog.events.Event} event Event.
+ * @protected
+ */
+ol3.webgl.MapRenderer.prototype.handleLayerRendererChange = function(event) {
+ this.getMap().render();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.handleResolutionChanged = function() {
+ goog.base(this, 'handleResolutionChanged');
+ this.getMap().render();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.handleRotationChanged = function() {
+ goog.base(this, 'handleRotationChanged');
+ this.getMap().render();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.handleSizeChanged = function() {
+ goog.base(this, 'handleSizeChanged');
+ var size = this.getMap().getSize();
+ if (!goog.isDef(size)) {
+ return;
+ }
+ this.canvas_.width = size.width;
+ this.canvas_.height = size.height;
+ var gl = this.gl_;
+ if (!goog.isNull(gl)) {
+ gl.viewport(0, 0, size.width, size.height);
+ this.getMap().render();
+ }
+};
+
+
+/**
+ * @param {goog.events.Event} event Event.
+ * @protected
+ */
+ol3.webgl.MapRenderer.prototype.handleWebGLContextLost = function(event) {
+ if (goog.DEBUG) {
+ this.logger.info('WebGLContextLost');
+ }
+ event.preventDefault();
+ this.locations_ = null;
+ this.arrayBuffer_ = null;
+ this.shaderCache_ = {};
+ this.programCache_ = {};
+ this.textureCache_ = {};
+ goog.object.forEach(this.layerRenderers, function(layerRenderer) {
+ layerRenderer.handleWebGLContextLost();
+ });
+};
+
+
+/**
+ * @protected
+ */
+ol3.webgl.MapRenderer.prototype.handleWebGLContextRestored = function() {
+ if (goog.DEBUG) {
+ this.logger.info('WebGLContextRestored');
+ }
+ this.initializeGL_();
+ this.getMap().render();
+};
+
+
+/**
+ * @private
+ */
+ol3.webgl.MapRenderer.prototype.initializeGL_ = function() {
+ var gl = this.gl_;
+ gl.activeTexture(goog.webgl.TEXTURE0);
+ gl.blendFunc(goog.webgl.SRC_ALPHA, goog.webgl.ONE_MINUS_SRC_ALPHA);
+ gl.disable(goog.webgl.CULL_FACE);
+ gl.disable(goog.webgl.DEPTH_TEST);
+ gl.disable(goog.webgl.SCISSOR_TEST);
+};
+
+
+/**
+ * @param {Image} image Image.
+ * @return {boolean} Is image texture loaded.
+ */
+ol3.webgl.MapRenderer.prototype.isImageTextureLoaded = function(image) {
+ return image.src in this.textureCache_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.removeLayer = function(layer) {
+ goog.base(this, 'removeLayer', layer);
+ if (layer.getVisible()) {
+ this.getMap().render();
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.removeLayerRenderer = function(layer) {
+ var layerRenderer = goog.base(this, 'removeLayerRenderer', layer);
+ if (!goog.isNull(layerRenderer)) {
+ var layerKey = goog.getUid(layer);
+ goog.events.unlistenByKey(this.layerRendererChangeListenKeys_[layerKey]);
+ delete this.layerRendererChangeListenKeys_[layerKey];
+ }
+ return layerRenderer;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.render = function() {
+
+ if (!this.getMap().isDef()) {
+ return false;
+ }
+
+ var size = this.getMap().getSize();
+
+ var animate = goog.base(this, 'render');
+
+ var gl = this.getGL();
+
+ gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, null);
+
+ gl.clearColor(this.clearColor_.r, this.clearColor_.g, this.clearColor_.b,
+ this.clearColor_.a);
+ gl.clear(goog.webgl.COLOR_BUFFER_BIT);
+ gl.enable(goog.webgl.BLEND);
+ gl.viewport(0, 0, size.width, size.height);
+
+ var program = this.getProgram(this.fragmentShader_, this.vertexShader_);
+ gl.useProgram(program);
+ if (goog.isNull(this.locations_)) {
+ this.locations_ = {
+ aPosition: gl.getAttribLocation(program, 'aPosition'),
+ aTexCoord: gl.getAttribLocation(program, 'aTexCoord'),
+ uBrightness: gl.getUniformLocation(program, 'uBrightness'),
+ uContrast: gl.getUniformLocation(program, 'uContrast'),
+ uHue: gl.getUniformLocation(program, 'uHue'),
+ uMatrix: gl.getUniformLocation(program, 'uMatrix'),
+ uOpacity: gl.getUniformLocation(program, 'uOpacity'),
+ uSaturation: gl.getUniformLocation(program, 'uSaturation'),
+ uTexture: gl.getUniformLocation(program, 'uTexture')
+ };
+ }
+
+ if (goog.isNull(this.arrayBuffer_)) {
+ var arrayBuffer = gl.createBuffer();
+ gl.bindBuffer(goog.webgl.ARRAY_BUFFER, arrayBuffer);
+ gl.bufferData(goog.webgl.ARRAY_BUFFER, new Float32Array([
+ -1, -1, 0, 0,
+ 1, -1, 1, 0,
+ -1, 1, 0, 1,
+ 1, 1, 1, 1
+ ]), goog.webgl.STATIC_DRAW);
+ this.arrayBuffer_ = arrayBuffer;
+ } else {
+ gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.arrayBuffer_);
+ }
+
+ gl.enableVertexAttribArray(this.locations_.aPosition);
+ gl.vertexAttribPointer(
+ this.locations_.aPosition, 2, goog.webgl.FLOAT, false, 16, 0);
+ gl.enableVertexAttribArray(this.locations_.aTexCoord);
+ gl.vertexAttribPointer(
+ this.locations_.aTexCoord, 2, goog.webgl.FLOAT, false, 16, 8);
+ gl.uniform1i(this.locations_.uTexture, 0);
+
+ this.forEachReadyVisibleLayer(function(layer, layerRenderer) {
+ gl.uniformMatrix4fv(
+ this.locations_.uMatrix, false, layerRenderer.getMatrix());
+ gl.uniform1f(this.locations_.uBrightness, layer.getBrightness());
+ gl.uniform1f(this.locations_.uContrast, layer.getContrast());
+ gl.uniform1f(this.locations_.uHue, layer.getHue());
+ gl.uniform1f(this.locations_.uOpacity, layer.getOpacity());
+ gl.uniform1f(this.locations_.uSaturation, layer.getSaturation());
+ gl.bindTexture(goog.webgl.TEXTURE_2D, layerRenderer.getTexture());
+ gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4);
+ }, this);
+
+ return animate;
+
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.MapRenderer.prototype.setLayerRenderer = function(
+ layer, layerRenderer) {
+ goog.base(this, 'setLayerRenderer', layer, layerRenderer);
+ var layerKey = goog.getUid(layer);
+ this.layerRendererChangeListenKeys_[layerKey] = goog.events.listen(
+ layerRenderer, goog.events.EventType.CHANGE,
+ this.handleLayerRendererChange, false, this);
+};
diff --git a/src/ol3/webgl/shader.js b/src/ol3/webgl/shader.js
new file mode 100644
index 0000000000..5260134a58
--- /dev/null
+++ b/src/ol3/webgl/shader.js
@@ -0,0 +1,81 @@
+goog.provide('ol3.webgl.shader.Fragment');
+goog.provide('ol3.webgl.shader.Vertex');
+
+goog.require('goog.functions');
+goog.require('goog.webgl');
+
+
+
+/**
+ * @constructor
+ * @param {string} source Source.
+ */
+ol3.webgl.Shader = function(source) {
+
+ /**
+ * @private
+ * @type {string}
+ */
+ this.source_ = source;
+
+};
+
+
+/**
+ * @return {number} Type.
+ */
+ol3.webgl.Shader.prototype.getType = goog.abstractMethod;
+
+
+/**
+ * @return {string} Source.
+ */
+ol3.webgl.Shader.prototype.getSource = function() {
+ return this.source_;
+};
+
+
+/**
+ * @return {boolean} Is animated?
+ */
+ol3.webgl.Shader.prototype.isAnimated = goog.functions.FALSE;
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.webgl.Shader}
+ * @param {string} source Source.
+ */
+ol3.webgl.shader.Fragment = function(source) {
+ goog.base(this, source);
+};
+goog.inherits(ol3.webgl.shader.Fragment, ol3.webgl.Shader);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.shader.Fragment.prototype.getType = function() {
+ return goog.webgl.FRAGMENT_SHADER;
+};
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.webgl.Shader}
+ * @param {string} source Source.
+ */
+ol3.webgl.shader.Vertex = function(source) {
+ goog.base(this, source);
+};
+goog.inherits(ol3.webgl.shader.Vertex, ol3.webgl.Shader);
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.shader.Vertex.prototype.getType = function() {
+ return goog.webgl.VERTEX_SHADER;
+};
diff --git a/src/ol3/webgl/tilelayerrenderer.js b/src/ol3/webgl/tilelayerrenderer.js
new file mode 100644
index 0000000000..d39cb704b2
--- /dev/null
+++ b/src/ol3/webgl/tilelayerrenderer.js
@@ -0,0 +1,493 @@
+// FIXME large resolutions lead to too large framebuffers :-(
+// FIXME animated shaders! check in redraw
+
+goog.provide('ol3.webgl.TileLayerRenderer');
+goog.provide('ol3.webgl.tilelayerrenderer');
+goog.provide('ol3.webgl.tilelayerrenderer.shader.Fragment');
+goog.provide('ol3.webgl.tilelayerrenderer.shader.Vertex');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.debug.Logger');
+goog.require('goog.events.EventType');
+goog.require('goog.object');
+goog.require('goog.vec.Mat4');
+goog.require('goog.vec.Vec4');
+goog.require('goog.webgl');
+goog.require('ol3.Coordinate');
+goog.require('ol3.MapEventType');
+goog.require('ol3.Size');
+goog.require('ol3.TileLayer');
+goog.require('ol3.TileState');
+goog.require('ol3.webgl.LayerRenderer');
+goog.require('ol3.webgl.shader.Fragment');
+goog.require('ol3.webgl.shader.Vertex');
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.webgl.shader.Fragment}
+ */
+ol3.webgl.tilelayerrenderer.shader.Fragment = function() {
+ goog.base(this, [
+ 'precision mediump float;',
+ '',
+ 'uniform sampler2D uTexture;',
+ '',
+ 'varying vec2 vTexCoord;',
+ '',
+ 'void main(void) {',
+ ' gl_FragColor = texture2D(uTexture, vTexCoord);',
+ '}'
+ ].join('\n'));
+};
+goog.inherits(
+ ol3.webgl.tilelayerrenderer.shader.Fragment, ol3.webgl.shader.Fragment);
+goog.addSingletonGetter(ol3.webgl.tilelayerrenderer.shader.Fragment);
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.webgl.shader.Vertex}
+ */
+ol3.webgl.tilelayerrenderer.shader.Vertex = function() {
+ goog.base(this, [
+ 'attribute vec2 aPosition;',
+ 'attribute vec2 aTexCoord;',
+ '',
+ 'varying vec2 vTexCoord;',
+ '',
+ 'uniform vec4 uTileOffset;',
+ '',
+ 'void main(void) {',
+ ' gl_Position.xy = aPosition * uTileOffset.xy + uTileOffset.zw;',
+ ' gl_Position.z = 0.;',
+ ' gl_Position.w = 1.;',
+ ' vTexCoord = aTexCoord;',
+ '}'
+ ].join('\n'));
+};
+goog.inherits(
+ ol3.webgl.tilelayerrenderer.shader.Vertex, ol3.webgl.shader.Vertex);
+goog.addSingletonGetter(ol3.webgl.tilelayerrenderer.shader.Vertex);
+
+
+
+/**
+ * @constructor
+ * @extends {ol3.webgl.LayerRenderer}
+ * @param {ol3.MapRenderer} mapRenderer Map renderer.
+ * @param {ol3.TileLayer} tileLayer Tile layer.
+ */
+ol3.webgl.TileLayerRenderer = function(mapRenderer, tileLayer) {
+
+ goog.base(this, mapRenderer, tileLayer);
+
+ if (goog.DEBUG) {
+ /**
+ * @inheritDoc
+ */
+ this.logger = goog.debug.Logger.getLogger(
+ 'ol3.webgl.tilelayerrenderer.' + goog.getUid(this));
+ }
+
+ /**
+ * @private
+ * @type {ol3.webgl.shader.Fragment}
+ */
+ this.fragmentShader_ =
+ ol3.webgl.tilelayerrenderer.shader.Fragment.getInstance();
+
+ /**
+ * @private
+ * @type {ol3.webgl.shader.Vertex}
+ */
+ this.vertexShader_ = ol3.webgl.tilelayerrenderer.shader.Vertex.getInstance();
+
+ /**
+ * @private
+ * @type {{aPosition: number,
+ * aTexCoord: number,
+ * uTileOffset: WebGLUniformLocation,
+ * uTexture: WebGLUniformLocation}|null}
+ */
+ this.locations_ = null;
+
+ /**
+ * @private
+ * @type {WebGLBuffer}
+ */
+ this.arrayBuffer_ = null;
+
+ /**
+ * @private
+ * @type {WebGLTexture}
+ */
+ this.texture_ = null;
+
+ /**
+ * @private
+ * @type {WebGLFramebuffer}
+ */
+ this.framebuffer_ = null;
+
+ /**
+ * @private
+ * @type {number|undefined}
+ */
+ this.framebufferDimension_ = undefined;
+
+ /**
+ * @private
+ * @type {Object.}
+ */
+ this.tileChangeListenerKeys_ = {};
+
+ /**
+ * @private
+ * @type {goog.vec.Mat4.AnyType}
+ */
+ this.matrix_ = goog.vec.Mat4.createNumber();
+
+};
+goog.inherits(ol3.webgl.TileLayerRenderer, ol3.webgl.LayerRenderer);
+
+
+/**
+ * @param {number} framebufferDimension Framebuffer dimension.
+ * @private
+ */
+ol3.webgl.TileLayerRenderer.prototype.bindFramebuffer_ =
+ function(framebufferDimension) {
+
+ var mapRenderer = this.getMapRenderer();
+ var gl = mapRenderer.getGL();
+
+ if (!goog.isDef(this.framebufferDimension_) ||
+ this.framebufferDimension_ != framebufferDimension) {
+
+ if (goog.DEBUG) {
+ this.logger.info('re-sizing framebuffer');
+ }
+
+ if (ol3.webgl.FREE_RESOURCES_IMMEDIATELY) {
+ if (goog.DEBUG) {
+ this.logger.info('freeing WebGL resources');
+ }
+ if (!gl.isContextLost()) {
+ gl.deleteFramebuffer(this.framebuffer_);
+ gl.deleteTexture(this.texture_);
+ }
+ } else {
+ var map = this.getMap();
+ goog.events.listenOnce(
+ map,
+ ol3.MapEventType.POST_RENDER,
+ goog.partial(function(gl, framebuffer, texture) {
+ if (goog.DEBUG) {
+ this.logger.info('freeing WebGL resources on postrender');
+ }
+ if (!gl.isContextLost()) {
+ gl.deleteFramebuffer(framebuffer);
+ gl.deleteTexture(texture);
+ }
+ }, gl, this.framebuffer_, this.texture_));
+ }
+
+ var texture = gl.createTexture();
+ gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
+ gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA,
+ framebufferDimension, framebufferDimension, 0, goog.webgl.RGBA,
+ goog.webgl.UNSIGNED_BYTE, null);
+ gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER,
+ goog.webgl.LINEAR);
+ gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER,
+ goog.webgl.LINEAR);
+
+ var framebuffer = gl.createFramebuffer();
+ gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, framebuffer);
+ gl.framebufferTexture2D(goog.webgl.FRAMEBUFFER,
+ goog.webgl.COLOR_ATTACHMENT0, goog.webgl.TEXTURE_2D, texture, 0);
+
+ this.texture_ = texture;
+ this.framebuffer_ = framebuffer;
+ this.framebufferDimension_ = framebufferDimension;
+
+ } else {
+ gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, this.framebuffer_);
+ }
+
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.TileLayerRenderer.prototype.disposeInternal = function() {
+ var mapRenderer = this.getMapRenderer();
+ var gl = mapRenderer.getGL();
+ if (!gl.isContextLost()) {
+ gl.deleteBuffer(this.arrayBuffer_);
+ gl.deleteFramebuffer(this.framebuffer_);
+ gl.deleteTexture(this.texture_);
+ }
+ goog.base(this, 'disposeInternal');
+};
+
+
+/**
+ * @return {ol3.TileLayer} Layer.
+ * @override
+ */
+ol3.webgl.TileLayerRenderer.prototype.getLayer = function() {
+ return /** @type {ol3.TileLayer} */ goog.base(this, 'getLayer');
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.TileLayerRenderer.prototype.getMatrix = function() {
+ return this.matrix_;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.TileLayerRenderer.prototype.getTexture = function() {
+ return this.texture_;
+};
+
+
+/**
+ * @protected
+ */
+ol3.webgl.TileLayerRenderer.prototype.handleTileChange = function() {
+ this.dispatchChangeEvent();
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.TileLayerRenderer.prototype.handleWebGLContextLost = function() {
+ this.locations_ = null;
+ this.arrayBuffer_ = null;
+ this.texture_ = null;
+ this.framebuffer_ = null;
+ this.framebufferDimension_ = undefined;
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol3.webgl.TileLayerRenderer.prototype.render = function() {
+
+ var animate = false;
+
+ var mapRenderer = this.getMapRenderer();
+ var map = this.getMap();
+ var gl = mapRenderer.getGL();
+
+ goog.asserts.assert(map.isDef());
+ var mapCenter = map.getCenter();
+ var mapExtent = map.getExtent();
+ var mapResolution = /** @type {number} */ map.getResolution();
+ var mapRotatedExtent = map.getRotatedExtent();
+ var mapRotation = map.getRotation();
+
+ var tileLayer = this.getLayer();
+ var tileStore = tileLayer.getStore();
+ var tileGrid = tileStore.getTileGrid();
+ var z = tileGrid.getZForResolution(mapResolution);
+ var tileResolution = tileGrid.getResolution(z);
+ var tileBounds = tileGrid.getTileBoundsForExtentAndResolution(
+ mapRotatedExtent, tileResolution);
+ var tileBoundsSize = tileBounds.getSize();
+ var tileSize = tileGrid.getTileSize();
+
+ var maxDimension = Math.max(
+ tileBoundsSize.width * tileSize.width,
+ tileBoundsSize.height * tileSize.height);
+ var framebufferDimension =
+ Math.pow(2, Math.ceil(Math.log(maxDimension) / Math.log(2)));
+ var framebufferExtentSize = new ol3.Size(
+ tileResolution * framebufferDimension,
+ tileResolution * framebufferDimension);
+ var origin = tileGrid.getOrigin(z);
+ var minX = origin.x + tileBounds.minX * tileSize.width * tileResolution;
+ var minY = origin.y + tileBounds.minY * tileSize.height * tileResolution;
+ var framebufferExtent = new ol3.Extent(
+ minX,
+ minY,
+ minX + framebufferExtentSize.width,
+ minY + framebufferExtentSize.height);
+
+ this.bindFramebuffer_(framebufferDimension);
+ gl.viewport(0, 0, framebufferDimension, framebufferDimension);
+
+ gl.clearColor(0, 0, 0, 0);
+ gl.clear(goog.webgl.COLOR_BUFFER_BIT);
+ gl.disable(goog.webgl.BLEND);
+
+ var program = mapRenderer.getProgram(
+ this.fragmentShader_, this.vertexShader_);
+ gl.useProgram(program);
+ if (goog.isNull(this.locations_)) {
+ this.locations_ = {
+ aPosition: gl.getAttribLocation(program, 'aPosition'),
+ aTexCoord: gl.getAttribLocation(program, 'aTexCoord'),
+ uTileOffset: gl.getUniformLocation(program, 'uTileOffset'),
+ uTexture: gl.getUniformLocation(program, 'uTexture')
+ };
+ }
+
+ if (goog.isNull(this.arrayBuffer_)) {
+ var arrayBuffer = gl.createBuffer();
+ gl.bindBuffer(goog.webgl.ARRAY_BUFFER, arrayBuffer);
+ gl.bufferData(goog.webgl.ARRAY_BUFFER, new Float32Array([
+ 0, 0, 0, 1,
+ 1, 0, 1, 1,
+ 0, 1, 0, 0,
+ 1, 1, 1, 0
+ ]), goog.webgl.STATIC_DRAW);
+ this.arrayBuffer_ = arrayBuffer;
+ } else {
+ gl.bindBuffer(goog.webgl.ARRAY_BUFFER, this.arrayBuffer_);
+ }
+
+ gl.enableVertexAttribArray(this.locations_.aPosition);
+ gl.vertexAttribPointer(
+ this.locations_.aPosition, 2, goog.webgl.FLOAT, false, 16, 0);
+ gl.enableVertexAttribArray(this.locations_.aTexCoord);
+ gl.vertexAttribPointer(
+ this.locations_.aTexCoord, 2, goog.webgl.FLOAT, false, 16, 8);
+ gl.uniform1i(this.locations_.uTexture, 0);
+
+ /**
+ * @type {Object.>}
+ */
+ var tilesToDrawByZ = {};
+
+ /**
+ * @type {Array.}
+ */
+ var imagesToLoad = [];
+
+ tilesToDrawByZ[z] = {};
+ tileBounds.forEachTileCoord(z, function(tileCoord) {
+
+ var tile = tileStore.getTile(tileCoord);
+
+ if (goog.isNull(tile)) {
+ } else if (tile.getState() == ol3.TileState.LOADED) {
+ if (mapRenderer.isImageTextureLoaded(tile.getImage())) {
+ tilesToDrawByZ[z][tileCoord.toString()] = tile;
+ return;
+ } else {
+ imagesToLoad.push(tile.getImage());
+ }
+ } else {
+ var tileKey = goog.getUid(tile);
+ if (!(tileKey in this.tileChangeListenerKeys_)) {
+ tile.load();
+ // FIXME will need to handle aborts as well
+ this.tileChangeListenerKeys_[tileKey] = goog.events.listen(tile,
+ goog.events.EventType.CHANGE, this.handleTileChange, false, this);
+ }
+ }
+
+ // FIXME this could be more efficient about filling partial holes
+ tileGrid.forEachTileCoordParentTileBounds(
+ tileCoord,
+ function(z, tileBounds) {
+ var fullyCovered = true;
+ tileBounds.forEachTileCoord(z, function(tileCoord) {
+ var tileCoordKey = tileCoord.toString();
+ if (tilesToDrawByZ[z] && tilesToDrawByZ[z][tileCoordKey]) {
+ return;
+ }
+ var tile = tileStore.getTile(tileCoord);
+ if (!goog.isNull(tile) &&
+ tile.getState() == ol3.TileState.LOADED) {
+ if (!tilesToDrawByZ[z]) {
+ tilesToDrawByZ[z] = {};
+ }
+ tilesToDrawByZ[z][tileCoordKey] = tile;
+ } else {
+ fullyCovered = false;
+ }
+ });
+ return fullyCovered;
+ });
+
+ }, this);
+
+ var zs = goog.object.getKeys(tilesToDrawByZ);
+ goog.array.sort(zs);
+ var uTileOffset = goog.vec.Vec4.createFloat32();
+ goog.array.forEach(zs, function(z) {
+ goog.object.forEach(tilesToDrawByZ[z], function(tile) {
+ var tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord);
+ var sx = 2 * tileExtent.getWidth() / framebufferExtentSize.width;
+ var sy = 2 * tileExtent.getHeight() / framebufferExtentSize.height;
+ var tx = 2 * (tileExtent.minX - framebufferExtent.minX) /
+ framebufferExtentSize.width - 1;
+ var ty = 2 * (tileExtent.minY - framebufferExtent.minY) /
+ framebufferExtentSize.height - 1;
+ goog.vec.Vec4.setFromValues(uTileOffset, sx, sy, tx, ty);
+ gl.uniform4fv(this.locations_.uTileOffset, uTileOffset);
+ mapRenderer.bindImageTexture(
+ tile.getImage(), goog.webgl.LINEAR, goog.webgl.LINEAR);
+ gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4);
+ }, this);
+ }, this);
+
+ goog.vec.Mat4.makeIdentity(this.matrix_);
+ goog.vec.Mat4.translate(this.matrix_,
+ (mapCenter.x - framebufferExtent.minX) /
+ (framebufferExtent.maxX - framebufferExtent.minX),
+ (mapCenter.y - framebufferExtent.minY) /
+ (framebufferExtent.maxY - framebufferExtent.minY),
+ 0);
+ goog.vec.Mat4.scale(this.matrix_,
+ (mapExtent.maxX - mapExtent.minX) /
+ (framebufferExtent.maxX - framebufferExtent.minX),
+ (mapExtent.maxY - mapExtent.minY) /
+ (framebufferExtent.maxY - framebufferExtent.minY),
+ 1);
+ if (goog.isDef(mapRotation)) {
+ goog.vec.Mat4.rotate(this.matrix_,
+ mapRotation,
+ 0,
+ 0,
+ 1);
+ }
+ goog.vec.Mat4.translate(this.matrix_,
+ -0.5,
+ -0.5,
+ 0);
+
+ if (!goog.array.isEmpty(imagesToLoad)) {
+ goog.events.listenOnce(
+ map,
+ ol3.MapEventType.POST_RENDER,
+ goog.partial(function(mapRenderer, imagesToLoad) {
+ if (goog.DEBUG) {
+ this.logger.info('uploading textures');
+ }
+ goog.array.forEach(imagesToLoad, function(image) {
+ mapRenderer.bindImageTexture(
+ image, goog.webgl.LINEAR, goog.webgl.LINEAR);
+ });
+ }, mapRenderer, imagesToLoad));
+ animate = true;
+ }
+
+ return animate;
+
+};
diff --git a/src/ol3/webgl/webgl.js b/src/ol3/webgl/webgl.js
new file mode 100644
index 0000000000..beaf9566f9
--- /dev/null
+++ b/src/ol3/webgl/webgl.js
@@ -0,0 +1,25 @@
+goog.provide('ol3.webgl');
+goog.provide('ol3.webgl.WebGLContextEventType');
+
+
+/**
+ * @define {boolean} Free resources immediately.
+ */
+ol3.webgl.FREE_RESOURCES_IMMEDIATELY = false;
+
+
+/**
+ * @enum {string}
+ */
+ol3.webgl.WebGLContextEventType = {
+ LOST: 'webglcontextlost',
+ RESTORED: 'webglcontextrestored'
+};
+
+
+/**
+ * @return {boolean} Is supported.
+ */
+ol3.webgl.isSupported = function() {
+ return 'WebGLRenderingContext' in goog.global;
+};