Compare commits

..

119 Commits

Author SHA1 Message Date
Tim Schaub
0c06a429fb Update package version to 3.8.2 2015-08-07 10:18:53 -06:00
Tim Schaub
4233061dda Changelog vor v3.8.2 2015-08-07 10:18:21 -06:00
Tim Schaub
7e579f1ec0 Merge pull request #3979 from tschaub/ol-url
Fix URLs for openlayers.org resources.
2015-08-07 10:14:38 -06:00
Andreas Hocevar
0e538b29c0 Fix URLs for openlayers.org resources 2015-08-07 10:03:00 -06:00
Tim Schaub
47b53ded6d Update package version to 3.8.1 2015-08-06 09:12:51 -06:00
Tim Schaub
3c8aeb7287 Changelog for v3.8.1 2015-08-06 09:12:08 -06:00
Tim Schaub
7b899fa767 Merge pull request #3970 from tschaub/cdn-switch
Pull builds from openlayers.org.
2015-08-06 09:05:00 -06:00
Tim Schaub
f695cb012a Pull builds from openlayers.org 2015-08-05 21:29:08 -06:00
Tim Schaub
929ce05f81 Merge pull request #3958 from openlayers/release-v3.8.0
Release v3.8.0.
2015-08-04 00:16:31 -06:00
Tim Schaub
81b7a77954 Update package version to 3.8.0 2015-08-04 00:03:33 -06:00
Tim Schaub
91045b0ad1 Changelog for v3.8.0 2015-08-04 00:03:24 -06:00
Tim Schaub
b11f6abb3c Merge pull request #3957 from tschaub/shared-delete
Properly handle vertex deletion with multiple features.
2015-08-03 23:38:43 -06:00
Tim Schaub
571f3f30a4 Regression test for shared vertex deletion 2015-08-03 23:07:19 -06:00
Tim Schaub
85ddded15c Only remove the vertex feature if it exists
When deleting a vertex shared by multiple features, we iterate through drag segments and only need to remove the vertex feature once.
2015-08-03 22:44:22 -06:00
Tim Schaub
d3c8880b22 Merge pull request #3954 from fredj/rm_bindMouseOutFocusOutBlur
Remove ol.control.Control.bindMouseOutFocusOutBlur function.
2015-08-03 21:51:09 -06:00
Tim Schaub
54d3bbd625 Merge pull request #3214 from tschaub/raster
Pixel manipulation with raster sources.
2015-08-03 21:49:19 -06:00
Tim Schaub
405a8db075 Merge pull request #3946 from Turbo87/modify
Fix vertex deletion for Modify interaction on mobile devices.
2015-08-03 21:47:41 -06:00
Tim Schaub
27e58be43d Merge pull request #3910 from ahocevar/ol-ext-no-amd
Do not provide an AMD environment to ol.ext modules.
2015-08-03 21:43:42 -06:00
Tim Schaub
cee34fa51b Table for controls 2015-08-03 21:33:46 -06:00
Tim Schaub
16aa548383 Only create a worker if an operation is provided 2015-08-03 21:32:16 -06:00
Tim Schaub
860fdabd76 Simplify raster sources by working with a single operation 2015-08-03 20:10:46 -06:00
Frederic Junod
761aa0ea5c Remove ol.control.Control.bindMouseOutFocusOutBlur function
It was a workaround added in #1761 to hide the button tooltip on
OSX / Chrome 32 and 33.

Because we're not using the tooltips anymore (see #2781) and this
version of chrome is deprecated this workaround can be removed.
2015-08-03 17:12:33 +02:00
Frédéric Junod
d493d0a820 Merge pull request #3934 from fredj/drawpoint_events
Fix `drawstart` and `drawend` events when drawing a point
2015-08-03 16:26:00 +02:00
Pierre GIRAUD
d6e35edac5 Merge pull request #3774 from pgiraud/measure_tooltips_touchdevice
Measure tooltips touchdevice
2015-08-03 15:57:57 +02:00
Frederic Junod
2143eebd30 Fix drawstart and drawend events when drawing a point
The previous behavior was to fire a `drawstart` on the first `mousemove`.
2015-08-03 13:40:56 +02:00
Frédéric Junod
64225776d4 Merge pull request #3949 from fredj/expect_called
Remove count argument from `called` function
2015-08-03 13:40:16 +02:00
Éric Lemoine
355c3c7b58 Merge pull request #3950 from elemoine/vbarray
Remove reference to vbarray.js
2015-08-03 11:19:29 +02:00
Éric Lemoine
e792a121f4 Remove reference to vbarray.js
This file was removed with https://github.com/openlayers/ol3/pull/3516.

Fixes #3702.
2015-08-03 11:06:43 +02:00
Frederic Junod
3364dbb46e Remove count argument from called function
This function takes no arguments, use the `callCount` property instead.
2015-08-03 10:02:21 +02:00
Tim Schaub
23610efe30 Merge pull request #3947 from alvinlindstam/clarify-ratio-docs
Clarify documentation of Image source ratio option.
2015-08-02 12:46:00 -06:00
Alvin Lindstam
75bf3e1ccf Clarify documentation of Image source ratio option. 2015-08-02 19:51:00 +02:00
Tim Schaub
f2f5cd2630 Make seed coordinate sticky 2015-08-01 17:06:40 -06:00
Ian Schneider
6f6698dd6a Add a region growing example 2015-08-01 17:06:33 -06:00
Tim Schaub
4320b07c5d Doc corrections 2015-08-01 16:33:43 -06:00
Tim Schaub
25603d7cf1 Merge pull request #3920 from probins/patch-1
Remove use_types_for_optimization from custom build tutorial.
2015-08-01 16:23:51 -06:00
Tim Schaub
935eb63876 Merge pull request #3922 from probins/expandurl
Document {?-?} pattern in expandUrl
2015-08-01 15:54:51 -06:00
Tim Schaub
6111b91cca Merge pull request #3921 from bjornharrtell/travis-cache
Cache node_modules on Travis.
2015-08-01 15:52:20 -06:00
Tobias Bieniek
7a34d22b37 interaction/modify: Add missing goog.require() to test 2015-08-01 22:09:32 +02:00
Tobias Bieniek
c3f51c676a interaction/modify: Fix identation issue 2015-08-01 22:05:44 +02:00
Tobias Bieniek
968c8aa34e interaction/modify: Replace lastNewVertexPixel with ignoreNextSingleClick
The previous approach did not work on mobile devices where no `pointermove`
event is sent except from dragging.

Logic now is: Upon vertex creation due to `pointerdown` we will ignore
the next `singleclick` event unless there is a `pointerdrag` event, which will
not lead to a `singleclick` event following the vertex creation.

Resolves #3935
2015-08-01 22:00:41 +02:00
Tobias Bieniek
b001a48567 interaction/modify: Simplify lastNewVertexPixel condition 2015-08-01 21:12:59 +02:00
Tobias Bieniek
bf9156cbeb interaction/modify: Adjust simulated events
This is exactly matching the event sent by Chrome now
2015-08-01 21:03:18 +02:00
Tobias Bieniek
bafd8548d1 interaction/modify: Add tests for deleteCondition option 2015-08-01 20:52:31 +02:00
Tobias Bieniek
e3ead5df06 events/condition: Add doubleClick condition 2015-08-01 20:46:21 +02:00
Tobias Bieniek
22ca08179d interaction/modify: Use expect().to.be() assertion
... instead of expect(a === b).to.be.ok()
2015-08-01 20:20:06 +02:00
Andreas Hocevar
c8df907ff2 Merge pull request #3942 from ahocevar/matrixset-by-projection
Fix WMTS TileMatrixSet lookup by SRS identifier
2015-07-31 11:45:06 +02:00
Andreas Hocevar
c0950dee11 Merge pull request #3945 from ahocevar/popup-on-icon
Simplify icon example and show popup at clicked position
2015-07-31 11:24:58 +02:00
Andreas Hocevar
da3d8952da Simplify icon example and show popup at clicked position
By using the event's coordinate instead of the feature's, we make sure
that the popup is shown where the user clicked when on a wrapped world.
2015-07-31 10:09:16 +02:00
Andreas Hocevar
95e43c852d Fix WMTS TileMatrixSet lookup by SRS identifier 2015-07-30 19:48:28 +02:00
Frédéric Junod
d4d3555a88 Merge pull request #3930 from fredj/goog.functions.identity
Use goog.functions.identity instead of goog.identityFunction
2015-07-29 08:53:53 +02:00
Andreas Hocevar
ef9a1a25b1 Merge pull request #3929 from probins/patch-2
Expand description for XYZ source
2015-07-28 22:15:16 +02:00
Peter Robins
44fdfaa630 Expand description for XYZ source 2015-07-28 12:54:01 +00:00
Andreas Hocevar
112473afee Merge pull request #3933 from ahocevar/snap-center-to-pixel
Snap center to pixel to avoid floating point issues
2015-07-28 14:20:43 +02:00
Andreas Hocevar
5e505f200a Snap center to pixel to avoid floating point issues 2015-07-28 14:02:09 +02:00
Frédéric Junod
5e4474ca8c Merge pull request #3932 from Turbo87/patch-1
SnapOptions: Fix typo in pixelTolerance JSDoc
2015-07-28 13:03:14 +02:00
Tobias Bieniek
d3f766c748 SnapOptions: Fix typo in pixelTolerance JSDoc
This was apparently copy-pasted from the `ModifyOptions` without adjustment
2015-07-28 12:26:54 +02:00
Frédéric Junod
2b1acc6216 Merge pull request #3931 from fredj/rm_htmlparser2
Remove unused htmlparser2 package
2015-07-28 11:07:48 +02:00
Frederic Junod
c008de1a88 Remove unused htmlparser2 package
No longer used since #3542
2015-07-28 10:31:58 +02:00
Frederic Junod
e6a38d8211 Use goog.functions.identity instead of goog.identityFunction
goog.identityFunction is deprecated
2015-07-28 09:44:51 +02:00
Peter Robins
e6f4054d3b Document {?-?} pattern in expandUrl 2015-07-25 13:41:36 +00:00
Björn Harrtell
fe636a0e98 Use Travis CI dependency cache 2015-07-25 15:12:59 +02:00
Peter Robins
76d36d4b20 Remove use_types_for_optimization from custom build tutorial 2015-07-23 15:11:55 +01:00
Frédéric Junod
abb5fef043 Merge pull request #3912 from fredj/zoomslider_event_type
Fix the event type fired by goog.fx.Dragger
2015-07-22 14:39:40 +02:00
Tim Schaub
0c486c522a Allow UI thread to be used
Where workers are not available, or if operations are trivial to run, the main UI thread can be used instead.  This also adds tests that run in real browsers.
2015-07-21 17:12:08 -06:00
Tim Schaub
af3c38052e Avoid examples that cannot be run in Phantom 2015-07-21 17:12:08 -06:00
Tim Schaub
f1ff39cc8b Avoid compiling the shaded relief example
The compiler doesn't support the use of the ImageData constructor.
2015-07-21 17:12:08 -06:00
Tim Schaub
643c2e6f21 Only update canvas if not dirty 2015-07-21 17:12:07 -06:00
Tim Schaub
d5aa0d9a8e Update example to work with the latest pixelworks 2015-07-21 17:12:07 -06:00
Tim Schaub
793b27e9f5 Allow operations to be updated 2015-07-21 17:12:07 -06:00
Tim Schaub
9d28549b2b Pass along potentially modified data 2015-07-21 17:12:07 -06:00
Tim Schaub
ef90f5a097 Run operations in a worker 2015-07-21 17:12:07 -06:00
Tim Schaub
c50d775330 Vertical exaggeration control 2015-07-21 17:12:07 -06:00
Tim Schaub
6da6cef760 Improved shaded relief example 2015-07-21 17:12:07 -06:00
Tim Schaub
1d94d71a5b Shaded relief example 2015-07-21 17:12:07 -06:00
Tim Schaub
65fee5b7ac Pass data object to operations 2015-07-21 17:12:07 -06:00
Tim Schaub
5267776627 Provide extent and resolution in raster events 2015-07-21 17:12:06 -06:00
Tim Schaub
a721ce03c9 Support for image or pixel operations 2015-07-21 17:12:06 -06:00
Tim Schaub
23e2fcefef Only render if sources are ready 2015-07-21 17:12:06 -06:00
Tim Schaub
d17d470d48 Fire change when updating operations 2015-07-21 17:12:06 -06:00
Tim Schaub
c6dedbc40b Use the first pixel for rendering, allow setting operations 2015-07-21 17:12:06 -06:00
Tim Schaub
de107c5502 Frame and canvas have equal size 2015-07-21 17:12:06 -06:00
Tim Schaub
6740ca9ee8 More interactive example 2015-07-21 17:12:06 -06:00
Tim Schaub
2c82ca86f0 Fire events before and after running ops 2015-07-21 17:12:06 -06:00
Tim Schaub
b7ad9160ef Nicer example 2015-07-21 17:12:06 -06:00
Tim Schaub
acc97a53eb Raster source for composing pixels from other sources 2015-07-21 17:12:05 -06:00
Frederic Junod
7634c0c2c4 Fix the event type fired by goog.fx.Dragger 2015-07-20 15:33:44 +02:00
Andreas Hocevar
49cc39c4dd Merge pull request #3871 from ahocevar/change-event
Document change events properly
2015-07-18 15:41:05 +02:00
Andreas Hocevar
3595c2cce7 Document change events properly
With this change, the generic 'change' event is properly documented, as
all other events. It is no longer necessary to annotate `@fires change` for
every ol.Observable subclass.
2015-07-18 15:27:16 +02:00
Andreas Hocevar
80c4809aee Do not provide an AMD environment to ol.ext modules
This fixes issues with ol.source.Vector being unable to use ol.ext.rbush,
but it also means that potential future external modules without node
module loader support will not work. If we ever depend on such a module,
we can replace "var define;" with a minimal AMD define shim.
2015-07-17 18:07:39 +02:00
Andreas Hocevar
10b4aa1bab Merge pull request #3906 from ahocevar/featurescollection-only
Clear features properly when there is no spatial index
2015-07-16 08:57:14 +02:00
Andreas Hocevar
8f7cbc5ed6 Clear features properly when there is no spatial index
Previously clear() only kept the features collection in sync whern there
was also a features RTree.
2015-07-15 15:15:41 +02:00
Andreas Hocevar
7094f65ef7 Merge pull request #3896 from ahocevar/webgl-precompose
Fire WebGL precompose event in same sequence as other renderers
2015-07-12 10:05:45 +02:00
Andreas Hocevar
ac8e62818f Fire WebGL precompose event in same sequence as other renderers
In other map renderers, the precompose event is fired before preparing and
composing layer frames. In WebGL, it is fired in between. This change makes
it so the sequence of events is the same for all renderers.

Because the WebGL renderer creates the list of layers to render before the
precompose event, unmanaged layers are never rendered. This is also fixed
by dispatching the precompose event earlier.
2015-07-11 14:58:31 +02:00
Tim Schaub
c89fb3ccfe Merge pull request #3359 from Kenny806/deep_clone
Enable deep clone of MultiPolygon.
2015-07-10 23:11:25 -06:00
Tim Schaub
223d5ab60d Merge pull request #3895 from planetlabs/greedy-multi-queue
Rework the tile queue for multiple queues.
2015-07-10 16:33:49 -06:00
Alessandro Isaacs
2142b538ac Greedify the queue loading strategy 2015-07-10 15:06:41 -07:00
Alessandro Isaacs
5149889bd2 Improve the test 2015-07-10 15:06:41 -07:00
Alessandro Isaacs
b57cdb730c Only load tiles that are not yet loaded 2015-07-10 15:06:35 -07:00
Tim Schaub
8600d46a0e Merge pull request #3894 from tschaub/no-sudo
Install Python dependencies without sudo.
2015-07-10 14:20:35 -06:00
Tim Schaub
7509425aa4 Install Python dependencies without sudo 2015-07-10 11:00:13 -06:00
Pierre GIRAUD
c9ab9bc711 Hide the help tooltip when cursor is out of the map 2015-07-10 09:32:32 +02:00
Tim Schaub
cd6ac857b9 Merge pull request #3824 from probins/select
Improve docs for interaction.Select.
2015-07-09 16:54:05 -06:00
Tim Schaub
7b35557cee Merge pull request #3884 from tschaub/debug-server
Provide a debug loader for the library.
2015-07-09 16:02:12 -06:00
Tim Schaub
4549d2f7a8 Try multiple ports 2015-07-09 10:49:24 -06:00
Tim Schaub
7780d77ade Update to closure-util@1.5.0 2015-07-09 10:16:15 -06:00
Tim Schaub
bfaac061c8 Provide a debug loader for the library 2015-07-09 10:16:15 -06:00
Andreas Hocevar
afce912f11 Merge pull request #3883 from ahocevar/foreachfeatureatpixel-unmanaged-always
Ignore layer filter for unmanaged layers
2015-07-09 00:03:25 +02:00
Tim Schaub
a705c6fe11 Test two queues sharing tiles 2015-07-08 15:12:31 -06:00
Andreas Hocevar
26e146b1d8 Ignore layer filter for unmanaged layers
To make unmanaged vector layers work like the removed ol.FeatureOverlay,
the layer filter for ol.Map#forEachFeatureAtPixel needs to ignore unmanaged
layers.
2015-07-07 22:24:57 +02:00
Bart van den Eijnden
a62bbd6650 Merge pull request #3859 from llambanna/patch-2
Add in crossOrigin option
2015-07-06 11:58:39 +02:00
Bart van den Eijnden
38fa805f03 Merge pull request #3873 from probins/patch-1
Correct minor typo in modifyinteraction
2015-07-06 09:36:07 +02:00
Anna Lambrechtsen
1d3f8b5d7d Add in crossOrigin option 2015-07-06 14:07:40 +12:00
Peter Robins
c53aa7e8d5 Correct minor typo in modifyinteraction 2015-07-05 10:57:59 +01:00
Andreas Hocevar
109cd6f3a6 Merge pull request #3872 from probins/patch-1
Correct event notations in ol.Feature
2015-07-04 14:20:25 +02:00
Peter Robins
4f703efd23 Correct event notations in ol.Feature 2015-07-04 11:43:06 +01:00
Bart van den Eijnden
a157fff318 Merge pull request #3869 from openlayers/release-v3.7.0
Release v3.7.0
2015-07-03 11:36:40 +02:00
Peter Robins
d6118f31e4 Improve docs for interaction.Select 2015-06-19 13:11:27 +00:00
Pierre GIRAUD
7065722fa6 Show measurement tooltip on geometry change instead of relying on pointermove 2015-06-08 16:19:28 +02:00
Jiri Matyas
ab9100450b enable deep clone of multipolygon endss 2015-03-17 11:09:03 +01:00
71 changed files with 2415 additions and 178 deletions

View File

@@ -1,8 +1,13 @@
language: python
sudo: false
cache:
directories:
- node_modules
env:
- DISPLAY=:99.0
before_install:
- "sudo pip install -r requirements.txt"
- "pip install -r requirements.txt"
- "npm install -g npm && npm install"
before_script:

View File

@@ -19,7 +19,8 @@ BUILD_HOSTED := build/hosted/$(BRANCH)
BUILD_HOSTED_EXAMPLES := $(addprefix $(BUILD_HOSTED)/,$(EXAMPLES))
BUILD_HOSTED_EXAMPLES_JS := $(addprefix $(BUILD_HOSTED)/,$(EXAMPLES_JS))
CHECK_EXAMPLE_TIMESTAMPS = $(patsubst examples/%.html,build/timestamps/check-%-timestamp,$(EXAMPLES_HTML))
UNPHANTOMABLE_EXAMPLES = examples/shaded-relief.html examples/raster.html examples/region-growing.html
CHECK_EXAMPLE_TIMESTAMPS = $(patsubst examples/%.html,build/timestamps/check-%-timestamp,$(filter-out $(UNPHANTOMABLE_EXAMPLES),$(EXAMPLES_HTML)))
TASKS_JS := $(shell find tasks -name '*.js')

View File

@@ -2,6 +2,8 @@
### v3.8.0
There should be nothing special required when upgrading from v3.7.0 to v3.8.0.
### v3.7.0
#### Removal of `ol.FeatureOverlay`

41
changelog/v3.8.0.md Normal file
View File

@@ -0,0 +1,41 @@
# v3.8.0
## Summary
The v3.8.0 release includes features and fixes from 33 pull requests since v3.7.0. While summer vacations have slowed the pace of development a bit this month, there are some nice improvements in this release. See the complete list below for details.
## New features and fixes
* [#3957](https://github.com/openlayers/ol3/pull/3957) - Properly handle vertex deletion with multiple features. ([@tschaub](https://github.com/tschaub))
* [#3954](https://github.com/openlayers/ol3/pull/3954) - Remove ol.control.Control.bindMouseOutFocusOutBlur function. ([@fredj](https://github.com/fredj))
* [#3214](https://github.com/openlayers/ol3/pull/3214) - Pixel manipulation with raster sources. ([@tschaub](https://github.com/tschaub))
* [#3946](https://github.com/openlayers/ol3/pull/3946) - Fix vertex deletion for Modify interaction on mobile devices. ([@Turbo87](https://github.com/Turbo87))
* [#3910](https://github.com/openlayers/ol3/pull/3910) - Do not provide an AMD environment to ol.ext modules. ([@ahocevar](https://github.com/ahocevar))
* [#3934](https://github.com/openlayers/ol3/pull/3934) - Fix `drawstart` and `drawend` events when drawing a point ([@fredj](https://github.com/fredj))
* [#3774](https://github.com/openlayers/ol3/pull/3774) - Measure tooltips touchdevice ([@pgiraud](https://github.com/pgiraud))
* [#3949](https://github.com/openlayers/ol3/pull/3949) - Remove count argument from `called` function ([@fredj](https://github.com/fredj))
* [#3950](https://github.com/openlayers/ol3/pull/3950) - Remove reference to vbarray.js ([@elemoine](https://github.com/elemoine))
* [#3947](https://github.com/openlayers/ol3/pull/3947) - Clarify documentation of Image source ratio option. ([@alvinlindstam](https://github.com/alvinlindstam))
* [#3920](https://github.com/openlayers/ol3/pull/3920) - Remove use_types_for_optimization from custom build tutorial. ([@probins](https://github.com/probins))
* [#3922](https://github.com/openlayers/ol3/pull/3922) - Document {?-?} pattern in expandUrl ([@probins](https://github.com/probins))
* [#3921](https://github.com/openlayers/ol3/pull/3921) - Cache node_modules on Travis. ([@bjornharrtell](https://github.com/bjornharrtell))
* [#3942](https://github.com/openlayers/ol3/pull/3942) - Fix WMTS TileMatrixSet lookup by SRS identifier ([@ahocevar](https://github.com/ahocevar))
* [#3945](https://github.com/openlayers/ol3/pull/3945) - Simplify icon example and show popup at clicked position ([@ahocevar](https://github.com/ahocevar))
* [#3930](https://github.com/openlayers/ol3/pull/3930) - Use goog.functions.identity instead of goog.identityFunction ([@fredj](https://github.com/fredj))
* [#3929](https://github.com/openlayers/ol3/pull/3929) - Expand description for XYZ source ([@probins](https://github.com/probins))
* [#3933](https://github.com/openlayers/ol3/pull/3933) - Snap center to pixel to avoid floating point issues ([@ahocevar](https://github.com/ahocevar))
* [#3932](https://github.com/openlayers/ol3/pull/3932) - SnapOptions: Fix typo in pixelTolerance JSDoc ([@Turbo87](https://github.com/Turbo87))
* [#3931](https://github.com/openlayers/ol3/pull/3931) - Remove unused htmlparser2 package ([@fredj](https://github.com/fredj))
* [#3912](https://github.com/openlayers/ol3/pull/3912) - Fix the event type fired by goog.fx.Dragger ([@fredj](https://github.com/fredj))
* [#3871](https://github.com/openlayers/ol3/pull/3871) - Document change events properly ([@ahocevar](https://github.com/ahocevar))
* [#3906](https://github.com/openlayers/ol3/pull/3906) - Clear features properly when there is no spatial index ([@ahocevar](https://github.com/ahocevar))
* [#3896](https://github.com/openlayers/ol3/pull/3896) - Fire WebGL precompose event in same sequence as other renderers ([@ahocevar](https://github.com/ahocevar))
* [#3359](https://github.com/openlayers/ol3/pull/3359) - Enable deep clone of MultiPolygon. ([@Kenny806](https://github.com/Kenny806))
* [#3895](https://github.com/openlayers/ol3/pull/3895) - Rework the tile queue for multiple queues. ([@aisaacs](https://github.com/aisaacs))
* [#3894](https://github.com/openlayers/ol3/pull/3894) - Install Python dependencies without sudo. ([@tschaub](https://github.com/tschaub))
* [#3824](https://github.com/openlayers/ol3/pull/3824) - Improve docs for interaction.Select. ([@probins](https://github.com/probins))
* [#3884](https://github.com/openlayers/ol3/pull/3884) - Provide a debug loader for the library. ([@tschaub](https://github.com/tschaub))
* [#3883](https://github.com/openlayers/ol3/pull/3883) - Ignore layer filter for unmanaged layers ([@ahocevar](https://github.com/ahocevar))
* [#3859](https://github.com/openlayers/ol3/pull/3859) - Add in crossOrigin option ([@llambanna](https://github.com/llambanna))
* [#3873](https://github.com/openlayers/ol3/pull/3873) - Correct minor typo in modifyinteraction ([@probins](https://github.com/probins))
* [#3872](https://github.com/openlayers/ol3/pull/3872) - Correct event notations in ol.Feature ([@probins](https://github.com/probins))

7
changelog/v3.8.1.md Normal file
View File

@@ -0,0 +1,7 @@
# v3.8.1
## Summary
This is a patch release that updates the URL for builds shown in the examples. Details below.
* [#3970](https://github.com/openlayers/ol3/pull/3970) - Pull builds from openlayers.org. ([@tschaub](https://github.com/tschaub))

7
changelog/v3.8.2.md Normal file
View File

@@ -0,0 +1,7 @@
# v3.8.2
## Summary
This is a patch release that corrects the URL for builds shown in the examples. Details below.
* [#3979](https://github.com/openlayers/ol3/pull/3979) - Fix URLs for openlayers.org resources. ([@tschaub](https://github.com/tschaub))

View File

@@ -20,8 +20,7 @@
"externs/jquery-1.9.js",
"externs/proj4js.js",
"externs/tilejson.js",
"externs/topojson.js",
"externs/vbarray.js"
"externs/topojson.js"
],
"define": [
"goog.array.ASSUME_NATIVE_FUNCTIONS=true",

View File

@@ -44,7 +44,7 @@
<textarea class="hidden" name="css">{{ css.source }}</textarea>
<textarea class="hidden" name="html">{{ contents }}</textarea>
<input type="hidden" name="wrap" value="l">
<input type="hidden" name="resources" value="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css,https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js,https://cdnjs.cloudflare.com/ajax/libs/ol3/{{ olVersion }}/ol.css,https://cdnjs.cloudflare.com/ajax/libs/ol3/{{ olVersion }}/ol.js">
<input type="hidden" name="resources" value="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css,https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js,http://openlayers.org/en/v{{ olVersion }}/css/ol.css,http://openlayers.org/en/v{{ olVersion }}/build/ol.js">
<pre><code id="example-source" class="language-markup">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
@@ -52,8 +52,8 @@
&lt;script src="https://code.jquery.com/jquery-1.11.2.min.js"&gt;&lt;/script&gt;
&lt;link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"&gt;
&lt;script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"&gt;&lt;/script&gt;
&lt;link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ol3/{{ olVersion }}/ol.css" type="text/css"&gt;
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/ol3/{{ olVersion }}/ol.js"&gt;&lt;/script&gt;
&lt;link rel="stylesheet" href="http://openlayers.org/en/v{{ olVersion }}/css/ol.css" type="text/css"&gt;
&lt;script src="http://openlayers.org/en/v{{ olVersion }}/build/ol.js"&gt;&lt;/script&gt;
{{ extraHead }}
{{#if css.source}}
&lt;style&gt;

View File

@@ -24,7 +24,7 @@ var self = this;
<?js } ?>
</dt>
<dd class="<?js= (data.stability && data.stability !== 'stable') ? 'unstable' : '' ?>">
<?js if (data.description) { ?>
<div class="description">
<?js= data.description ?>
@@ -39,20 +39,20 @@ var self = this;
</li>
</ul>
<?js } ?>
<?js if (data['this']) { ?>
<h5>This:</h5>
<ul><li><?js= this.linkto(data['this'], data['this']) ?></li></ul>
<?js } ?>
<?js if (data.stability || kind !== 'class') { ?>
<?js if (data.params && params.length) { ?>
<?js= this.partial('params.tmpl', params) ?>
<?js } ?>
<?js } ?>
<?js= this.partial('details.tmpl', data) ?>
<?js if (data.fires && fires.length) { ?>
<h5>Fires:</h5>
<ul><?js fires.forEach(function(f) {
@@ -68,7 +68,7 @@ var self = this;
}
?>
<li class="<?js= (eventDoclet || data).stability !== 'stable' ? 'unstable' : '' ?>">
<code><?js= self.linkto(f, type) ?></code>
<code><?js= eventClassName ? self.linkto(f, type) : type ?></code>
<?js if (eventClassName) {
var eventClass = self.find({longname: eventClassName})[0];
if (eventClass) { ?>
@@ -96,7 +96,7 @@ var self = this;
<li><?js= self.linkto(f) ?></li>
<?js }); ?></ul>
<?js } ?>
<?js if (data.exceptions && exceptions.length) { ?>
<h5>Throws:</h5>
<?js if (exceptions.length > 1) { ?><ul><?js
@@ -108,12 +108,12 @@ var self = this;
<?js= self.partial('exceptions.tmpl', r) ?>
<?js });
} } ?>
<?js if (data.returns && returns.length) { ?>
<?js if (returns.length > 1) { ?><h5>Returns:</h5><?js } ?>
<?js= self.partial('returns.tmpl', data.returns) ?>
<?js } ?>
<?js if (data.examples && examples.length) { ?>
<h5>Example<?js= examples.length > 1? 's':'' ?></h5>
<?js= this.partial('examples.tmpl', examples) ?>

View File

@@ -72,7 +72,6 @@ Creating a custom build requires writing a build configuration file. The format
"api", "observable"
],
"compilation_level": "ADVANCED_OPTIMIZATIONS",
"use_types_for_optimization": true,
"manage_closure_dependencies": true
}
}
@@ -180,7 +179,7 @@ The Closure documentation explains that "externs" are for external names used in
### Other compiler options
There are a couple of other compiler options in the config file above. `manage_closure_dependencies` should always be used. `use_types_for_optimization` should be used for better compression rates.
There are a couple of other compiler options in the config file above. `manage_closure_dependencies` should always be used.
You can specify any of the other compiler options here as needed, such as the renaming reports, output manifest, or source maps. There is a full list of available options in [closure-util](https://github.com/openlayers/closure-util/blob/master/compiler-options.txt).
@@ -219,7 +218,6 @@ Now let's try a more complicated example: [`heatmaps-earthquakes`](http://openla
"goog.DEBUG=false"
],
"compilation_level": "ADVANCED_OPTIMIZATIONS",
"use_types_for_optimization": true,
"manage_closure_dependencies": true
}
}

View File

@@ -1,6 +1,3 @@
#map {
position: relative;
}
#popup {
padding-bottom: 45px;
}

View File

@@ -71,9 +71,7 @@ map.on('click', function(evt) {
return feature;
});
if (feature) {
var geometry = feature.getGeometry();
var coord = geometry.getCoordinates();
popup.setPosition(coord);
popup.setPosition(evt.coordinate);
$(element).popover({
'placement': 'top',
'html': true,

View File

@@ -1,4 +1,5 @@
goog.require('ol.Map');
goog.require('ol.Observable');
goog.require('ol.Overlay');
goog.require('ol.Sphere');
goog.require('ol.View');
@@ -103,27 +104,20 @@ var pointerMoveHandler = function(evt) {
}
/** @type {string} */
var helpMsg = 'Click to start drawing';
/** @type {ol.Coordinate|undefined} */
var tooltipCoord = evt.coordinate;
if (sketch) {
var output;
var geom = (sketch.getGeometry());
if (geom instanceof ol.geom.Polygon) {
output = formatArea(/** @type {ol.geom.Polygon} */ (geom));
helpMsg = continuePolygonMsg;
tooltipCoord = geom.getInteriorPoint().getCoordinates();
} else if (geom instanceof ol.geom.LineString) {
output = formatLength( /** @type {ol.geom.LineString} */ (geom));
helpMsg = continueLineMsg;
tooltipCoord = geom.getLastCoordinate();
}
measureTooltipElement.innerHTML = output;
measureTooltip.setPosition(tooltipCoord);
}
helpTooltipElement.innerHTML = helpMsg;
helpTooltip.setPosition(evt.coordinate);
$(helpTooltipElement).removeClass('hidden');
};
@@ -138,6 +132,10 @@ var map = new ol.Map({
map.on('pointermove', pointerMoveHandler);
$(map.getViewport()).on('mouseout', function() {
$(helpTooltipElement).addClass('hidden');
});
var typeSelect = document.getElementById('type');
var geodesicCheckbox = document.getElementById('geodesic');
@@ -172,10 +170,28 @@ function addInteraction() {
createMeasureTooltip();
createHelpTooltip();
var listener;
draw.on('drawstart',
function(evt) {
// set sketch
sketch = evt.feature;
/** @type {ol.Coordinate|undefined} */
var tooltipCoord = evt.coordinate;
listener = sketch.getGeometry().on('change', function(evt) {
var geom = evt.target;
var output;
if (geom instanceof ol.geom.Polygon) {
output = formatArea(/** @type {ol.geom.Polygon} */ (geom));
tooltipCoord = geom.getInteriorPoint().getCoordinates();
} else if (geom instanceof ol.geom.LineString) {
output = formatLength( /** @type {ol.geom.LineString} */ (geom));
tooltipCoord = geom.getLastCoordinate();
}
measureTooltipElement.innerHTML = output;
measureTooltip.setPosition(tooltipCoord);
});
}, this);
draw.on('drawend',
@@ -187,6 +203,7 @@ function addInteraction() {
// unset tooltip so that a new one can be created
measureTooltipElement = null;
createMeasureTooltip();
ol.Observable.unByKey(listener);
}, this);
}
@@ -199,7 +216,7 @@ function createHelpTooltip() {
helpTooltipElement.parentNode.removeChild(helpTooltipElement);
}
helpTooltipElement = document.createElement('div');
helpTooltipElement.className = 'tooltip';
helpTooltipElement.className = 'tooltip hidden';
helpTooltip = new ol.Overlay({
element: helpTooltipElement,
offset: [15, 0],

31
examples/raster.css Normal file
View File

@@ -0,0 +1,31 @@
.rel {
position: relative
}
#plot {
pointer-events: none;
position: absolute;
bottom: 10px;
left: 10px;
}
.bar {
pointer-events: auto;
fill: #AFAFB9;
}
.bar.selected {
fill: green;
}
.tip {
position: absolute;
background: black;
color: white;
padding: 6px;
font-size: 12px;
border-radius: 4px;
margin-bottom: 10px;
display: none;
opacity: 0;
}

31
examples/raster.html Normal file
View File

@@ -0,0 +1,31 @@
---
template: example.html
title: Raster Source
shortdesc: Demonstrates pixelwise operations with a raster source.
docs: >
<p>
This example uses a <code>ol.source.Raster</code> to generate data
based on another source. The raster source accepts any number of
input sources (tile or image based) and runs a pipeline of
operations on the input pixels. The return from the final
operation is used as the data for the output source.
</p>
<p>
In this case, a single tiled source of imagery is used as input.
For each pixel, the Vegetaion Greenness Index
(<a href="http://www.tandfonline.com/doi/abs/10.1080/10106040108542184#.Vb90ITBViko">VGI</a>)
is calculated from the input pixels. A second operation colors
those pixels based on a threshold value (values above the
threshold are green and those below are transparent).
</p>
tags: "raster, pixel"
resources:
- http://d3js.org/d3.v3.min.js
- raster.css
---
<div class="row-fluid">
<div class="span12 rel">
<div id="map" class="map"></div>
<div id="plot"></div>
</div>
</div>

200
examples/raster.js Normal file
View File

@@ -0,0 +1,200 @@
// NOCOMPILE
// this example uses d3 for which we don't have an externs file.
goog.require('ol.Map');
goog.require('ol.View');
goog.require('ol.layer.Image');
goog.require('ol.layer.Tile');
goog.require('ol.source.BingMaps');
goog.require('ol.source.Raster');
var minVgi = 0;
var maxVgi = 0.25;
var bins = 10;
/**
* Calculate the Vegetation Greenness Index (VGI) from an input pixel. This
* is a rough estimate assuming that pixel values correspond to reflectance.
* @param {ol.raster.Pixel} pixel An array of [R, G, B, A] values.
* @return {number} The VGI value for the given pixel.
*/
function vgi(pixel) {
var r = pixel[0] / 255;
var g = pixel[1] / 255;
var b = pixel[2] / 255;
return (2 * g - r - b) / (2 * g + r + b);
}
/**
* Summarize values for a histogram.
* @param {numver} value A VGI value.
* @param {Object} counts An object for keeping track of VGI counts.
*/
function summarize(value, counts) {
var min = counts.min;
var max = counts.max;
var num = counts.values.length;
if (value < min) {
// do nothing
} else if (value >= max) {
counts.values[num - 1] += 1;
} else {
var index = Math.floor((value - min) / counts.delta);
counts.values[index] += 1;
}
}
/**
* Use aerial imagery as the input data for the raster source.
*/
var bing = new ol.source.BingMaps({
key: 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3',
imagerySet: 'Aerial'
});
/**
* Create a raster source where pixels with VGI values above a threshold will
* be colored green.
*/
var raster = new ol.source.Raster({
sources: [bing],
operation: function(pixels, data) {
var pixel = pixels[0];
var value = vgi(pixel);
summarize(value, data.counts);
if (value >= data.threshold) {
pixel[0] = 0;
pixel[1] = 255;
pixel[2] = 0;
pixel[3] = 128;
} else {
pixel[3] = 0;
}
return pixel;
},
lib: {
vgi: vgi,
summarize: summarize
}
});
raster.set('threshold', 0.1);
function createCounts(min, max, num) {
var values = new Array(num);
for (var i = 0; i < num; ++i) {
values[i] = 0;
}
return {
min: min,
max: max,
values: values,
delta: (max - min) / num
};
}
raster.on('beforeoperations', function(event) {
event.data.counts = createCounts(minVgi, maxVgi, bins);
event.data.threshold = raster.get('threshold');
});
raster.on('afteroperations', function(event) {
schedulePlot(event.resolution, event.data.counts, event.data.threshold);
});
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: bing
}),
new ol.layer.Image({
source: raster
})
],
target: 'map',
view: new ol.View({
center: [-9651695, 4937351],
zoom: 13,
minZoom: 12,
maxZoom: 19
})
});
var timer = null;
function schedulePlot(resolution, counts, threshold) {
if (timer) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout(plot.bind(null, resolution, counts, threshold), 1000 / 60);
}
var barWidth = 15;
var plotHeight = 150;
var chart = d3.select('#plot').append('svg')
.attr('width', barWidth * bins)
.attr('height', plotHeight);
var chartRect = chart[0][0].getBoundingClientRect();
var tip = d3.select(document.body).append('div')
.attr('class', 'tip');
function plot(resolution, counts, threshold) {
var yScale = d3.scale.linear()
.domain([0, d3.max(counts.values)])
.range([0, plotHeight]);
var bar = chart.selectAll('rect').data(counts.values);
bar.enter().append('rect');
bar.attr('class', function(count, index) {
var value = counts.min + (index * counts.delta);
return 'bar' + (value >= threshold ? ' selected' : '');
})
.attr('width', barWidth - 2);
bar.transition().attr('transform', function(value, index) {
return 'translate(' + (index * barWidth) + ', ' +
(plotHeight - yScale(value)) + ')';
})
.attr('height', yScale);
bar.on('mousemove', function(count, index) {
var threshold = counts.min + (index * counts.delta);
if (raster.get('threshold') !== threshold) {
raster.set('threshold', threshold);
raster.changed();
}
});
bar.on('mouseover', function(count, index) {
var area = 0;
for (var i = counts.values.length - 1; i >= index; --i) {
area += resolution * resolution * counts.values[i];
}
tip.html(message(counts.min + (index * counts.delta), area));
tip.style('display', 'block');
tip.transition().style({
left: (chartRect.left + (index * barWidth) + (barWidth / 2)) + 'px',
top: (d3.event.y - 60) + 'px',
opacity: 1
});
});
bar.on('mouseout', function() {
tip.transition().style('opacity', 0).each('end', function() {
tip.style('display', 'none');
});
});
}
function message(value, area) {
var acres = (area / 4046.86).toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return acres + ' acres at<br>' + value.toFixed(2) + ' VGI or above';
}

View File

@@ -0,0 +1,4 @@
table.controls td {
min-width: 110px;
padding: 2px 5px;
}

View File

@@ -0,0 +1,38 @@
---
template: example.html
title: Region Growing
shortdesc: Grow a region from a seed pixel
docs: >
<p>Click a region on the map. The computed region will be red.</p>
<p>
This example uses a <code>ol.source.Raster</code> to generate data
based on another source. The raster source accepts any number of
input sources (tile or image based) and runs a pipeline of
operations on the input data. The return from the final
operation is used as the data for the output source.
</p>
<p>
In this case, a single tiled source of imagery data is used as input.
The region is calculated in a single "image" operation using the "seed"
pixel provided by the user clicking on the map. The "threshold" value
determines whether a given contiguous pixel belongs to the "region" - the
difference between a candidate pixel's RGB values and the seed values must
be below the threshold.
</p>
<p>
This example also shows how an additional function can be made available
to the operation.
</p>
tags: "raster, region growing"
---
<div class="row-fluid">
<div class="span12">
<div id="map" class="map" style="cursor: pointer"></div>
<table class="controls">
<tr>
<td>Threshold: <span id="threshold-value"></span></td>
<td><input id="threshold" type="range" min="1" max="50" value="20"></td>
</tr>
</table>
</div>
</div>

132
examples/region-growing.js Normal file
View File

@@ -0,0 +1,132 @@
// NOCOMPILE
goog.require('ol.Map');
goog.require('ol.View');
goog.require('ol.layer.Image');
goog.require('ol.layer.Tile');
goog.require('ol.proj');
goog.require('ol.source.BingMaps');
goog.require('ol.source.Raster');
function growRegion(inputs, data) {
var image = inputs[0];
var seed = data.pixel;
var delta = parseInt(data.delta);
if (!seed) {
return image;
}
seed = seed.map(Math.round);
var width = image.width;
var height = image.height;
var inputData = image.data;
var outputData = new Uint8ClampedArray(inputData);
var seedIdx = (seed[1] * width + seed[0]) * 4;
var seedR = inputData[seedIdx];
var seedG = inputData[seedIdx + 1];
var seedB = inputData[seedIdx + 2];
var edge = [seed];
while (edge.length) {
var newedge = [];
for (var i = 0, ii = edge.length; i < ii; i++) {
// As noted in the Raster source constructor, this function is provided
// using the `lib` option. Other functions will NOT be visible unless
// provided using the `lib` option.
var next = nextEdges(edge[i]);
for (var j = 0, jj = next.length; j < jj; j++) {
var s = next[j][0], t = next[j][1];
if (s >= 0 && s < width && t >= 0 && t < height) {
var ci = (t * width + s) * 4;
var cr = inputData[ci];
var cg = inputData[ci + 1];
var cb = inputData[ci + 2];
var ca = inputData[ci + 3];
// if alpha is zero, carry on
if (ca === 0) {
continue;
}
if (Math.abs(seedR - cr) < delta && Math.abs(seedG - cg) < delta &&
Math.abs(seedB - cb) < delta) {
outputData[ci] = 255;
outputData[ci + 1] = 0;
outputData[ci + 2] = 0;
outputData[ci + 3] = 255;
newedge.push([s, t]);
}
// mark as visited
inputData[ci + 3] = 0;
}
}
}
edge = newedge;
}
return new ImageData(outputData, width, height);
}
function next4Edges(edge) {
var x = edge[0], y = edge[1];
return [
[x + 1, y],
[x - 1, y],
[x, y + 1],
[x, y - 1]
];
}
var key = 'Ak-dzM4wZjSqTlzveKz5u0d4IQ4bRzVI309GxmkgSVr1ewS6iPSrOvOKhA-CJlm3';
var imagery = new ol.layer.Tile({
source: new ol.source.BingMaps({key: key, imagerySet: 'Aerial'})
});
var raster = new ol.source.Raster({
sources: [imagery.getSource()],
operationType: 'image',
operation: growRegion,
// Functions in the `lib` object will be available to the operation run in
// the web worker.
lib: {
nextEdges: next4Edges
}
});
var rasterImage = new ol.layer.Image({
opacity: 0.7,
source: raster
});
var map = new ol.Map({
layers: [imagery, rasterImage],
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([-119.07, 47.65]),
zoom: 11
})
});
var coordinate;
map.on('click', function(event) {
coordinate = event.coordinate;
raster.changed();
});
raster.on('beforeoperations', function(event) {
// the event.data object will be passed to operations
var data = event.data;
data.delta = thresholdControl.value;
if (coordinate) {
data.pixel = map.getPixelFromCoordinate(coordinate);
}
});
var thresholdControl = document.getElementById('threshold');
function updateControlValue() {
document.getElementById('threshold-value').innerText = thresholdControl.value;
}
updateControlValue();
thresholdControl.addEventListener('input', function() {
updateControlValue();
raster.changed();
});

View File

@@ -0,0 +1,4 @@
table.controls td {
text-align: center;
padding: 2px 5px;
}

View File

@@ -0,0 +1,46 @@
---
template: example.html
title: Shaded Relief
shortdesc: Calculate shaded relief from elevation data
docs: >
<p>
This example uses a <code>ol.source.Raster</code> to generate data
based on another source. The raster source accepts any number of
input sources (tile or image based) and runs a pipeline of
operations on the input data. The return from the final
operation is used as the data for the output source.
</p>
<p>
In this case, a single tiled source of elevation data is used as input.
The shaded relief is calculated in a single "image" operation. By setting
<code>operationType: 'image'</code> on the raster source, operations are
called with an <code>ImageData</code> object for each of the input sources.
Operations are also called with a general purpose <code>data</code> object.
In this example, the sun elevation and azimuth data from the inputs above
are assigned to this <code>data</code> object and accessed in the shading
operation. The shading operation returns an array of <code>ImageData</code>
objects. When the raster source is used by an image layer, the first
<code>ImageData</code> object returned by the last operation in the pipeline
is used for rendering.
</p>
tags: "raster, shaded relief"
---
<div class="row-fluid">
<div class="span12">
<div id="map" class="map"></div>
<table class="controls">
<tr>
<td>vertical exaggeration: <span id="vertOut"></span>x</td>
<td><input id="vert" type="range" min="1" max="5" value="1"/></td>
</tr>
<tr>
<td>sun elevation: <span id="sunElOut"></span>°</td>
<td><input id="sunEl" type="range" min="0" max="90" value="45"/></td>
</tr>
<tr>
<td>sun azimuth: <span id="sunAzOut"></span>°</td>
<td><input id="sunAz" type="range" min="0" max="360" value="45"/></td>
</tr>
</table>
</div>
</div>

158
examples/shaded-relief.js Normal file
View File

@@ -0,0 +1,158 @@
// NOCOMPILE
goog.require('ol.Map');
goog.require('ol.View');
goog.require('ol.layer.Image');
goog.require('ol.layer.Tile');
goog.require('ol.source.Raster');
goog.require('ol.source.TileJSON');
goog.require('ol.source.XYZ');
/**
* Generates a shaded relief image given elevation data. Uses a 3x3
* neighborhood for determining slope and aspect.
* @param {Array.<ImageData>} inputs Array of input images.
* @param {Object} data Data added in the "beforeoperations" event.
* @return {Array.<ImageData>} Output images (only the first is rendered).
*/
function shade(inputs, data) {
var elevationImage = inputs[0];
var width = elevationImage.width;
var height = elevationImage.height;
var elevationData = elevationImage.data;
var shadeData = new Uint8ClampedArray(elevationData.length);
var dp = data.resolution * 2;
var maxX = width - 1;
var maxY = height - 1;
var pixel = [0, 0, 0, 0];
var twoPi = 2 * Math.PI;
var halfPi = Math.PI / 2;
var sunEl = Math.PI * data.sunEl / 180;
var sunAz = Math.PI * data.sunAz / 180;
var cosSunEl = Math.cos(sunEl);
var sinSunEl = Math.sin(sunEl);
var pixelX, pixelY, x0, x1, y0, y1, offset,
z0, z1, dzdx, dzdy, slope, aspect, cosIncidence, scaled;
for (pixelY = 0; pixelY <= maxY; ++pixelY) {
y0 = pixelY === 0 ? 0 : pixelY - 1;
y1 = pixelY === maxY ? maxY : pixelY + 1;
for (pixelX = 0; pixelX <= maxX; ++pixelX) {
x0 = pixelX === 0 ? 0 : pixelX - 1;
x1 = pixelX === maxX ? maxX : pixelX + 1;
// determine elevation for (x0, pixelY)
offset = (pixelY * width + x0) * 4;
pixel[0] = elevationData[offset];
pixel[1] = elevationData[offset + 1];
pixel[2] = elevationData[offset + 2];
pixel[3] = elevationData[offset + 3];
z0 = data.vert * (pixel[0] + pixel[1] * 2 + pixel[2] * 3);
// determine elevation for (x1, pixelY)
offset = (pixelY * width + x1) * 4;
pixel[0] = elevationData[offset];
pixel[1] = elevationData[offset + 1];
pixel[2] = elevationData[offset + 2];
pixel[3] = elevationData[offset + 3];
z1 = data.vert * (pixel[0] + pixel[1] * 2 + pixel[2] * 3);
dzdx = (z1 - z0) / dp;
// determine elevation for (pixelX, y0)
offset = (y0 * width + pixelX) * 4;
pixel[0] = elevationData[offset];
pixel[1] = elevationData[offset + 1];
pixel[2] = elevationData[offset + 2];
pixel[3] = elevationData[offset + 3];
z0 = data.vert * (pixel[0] + pixel[1] * 2 + pixel[2] * 3);
// determine elevation for (pixelX, y1)
offset = (y1 * width + pixelX) * 4;
pixel[0] = elevationData[offset];
pixel[1] = elevationData[offset + 1];
pixel[2] = elevationData[offset + 2];
pixel[3] = elevationData[offset + 3];
z1 = data.vert * (pixel[0] + pixel[1] * 2 + pixel[2] * 3);
dzdy = (z1 - z0) / dp;
slope = Math.atan(Math.sqrt(dzdx * dzdx + dzdy * dzdy));
aspect = Math.atan2(dzdy, -dzdx);
if (aspect < 0) {
aspect = halfPi - aspect;
} else if (aspect > halfPi) {
aspect = twoPi - aspect + halfPi;
} else {
aspect = halfPi - aspect;
}
cosIncidence = sinSunEl * Math.cos(slope) +
cosSunEl * Math.sin(slope) * Math.cos(sunAz - aspect);
offset = (pixelY * width + pixelX) * 4;
scaled = 255 * cosIncidence;
shadeData[offset] = scaled;
shadeData[offset + 1] = scaled;
shadeData[offset + 2] = scaled;
shadeData[offset + 3] = elevationData[offset + 3];
}
}
return new ImageData(shadeData, width, height);
}
var elevation = new ol.source.XYZ({
url: 'https://{a-d}.tiles.mapbox.com/v3/aj.sf-dem/{z}/{x}/{y}.png',
crossOrigin: 'anonymous'
});
var raster = new ol.source.Raster({
sources: [elevation],
operationType: 'image',
operation: shade
});
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.TileJSON({
url: 'http://api.tiles.mapbox.com/v3/tschaub.miapgppd.jsonp'
})
}),
new ol.layer.Image({
opacity: 0.3,
source: raster
})
],
view: new ol.View({
extent: [-13675026, 4439648, -13580856, 4580292],
center: [-13615645, 4497969],
minZoom: 10,
maxZoom: 16,
zoom: 13
})
});
var controlIds = ['vert', 'sunEl', 'sunAz'];
var controls = {};
controlIds.forEach(function(id) {
var control = document.getElementById(id);
var output = document.getElementById(id + 'Out');
control.addEventListener('input', function() {
output.innerText = control.value;
raster.changed();
});
output.innerText = control.value;
controls[id] = control;
});
raster.on('beforeoperations', function(event) {
// the event.data object will be passed to operations
var data = event.data;
data.resolution = event.resolution;
for (var id in controls) {
data[id] = Number(controls[id].value);
}
});

View File

@@ -266,6 +266,30 @@ oli.source.ImageEvent = function() {};
oli.source.ImageEvent.prototype.image;
/**
* @interface
*/
oli.source.RasterEvent = function() {};
/**
* @type {ol.Extent}
*/
oli.source.RasterEvent.prototype.extent;
/**
* @type {number}
*/
oli.source.RasterEvent.prototype.resolution;
/**
* @type {Object}
*/
oli.source.RasterEvent.prototype.data;
/**
* @interface
*/

View File

@@ -473,6 +473,7 @@ olx.ProjectionOptions.prototype.global;
*/
olx.ProjectionOptions.prototype.worldExtent;
/**
* Function to determine resolution at a point. The function is called with a
* `{number}` view resolution and an `{ol.Coordinate}` as arguments, and returns
@@ -912,6 +913,7 @@ olx.control.AttributionOptions.prototype.tipLabel;
*/
olx.control.AttributionOptions.prototype.label;
/**
* Text label to use for the expanded attributions button. Default is `»`.
* Instead of text, also a Node (e.g. a `span` element) can be used.
@@ -2829,6 +2831,7 @@ olx.interaction.SelectOptions.prototype.removeCondition;
*/
olx.interaction.SelectOptions.prototype.toggleCondition;
/**
* A boolean that determines if the default behaviour should select only
* single features or all (overlapping) features at the clicked map
@@ -2838,6 +2841,7 @@ olx.interaction.SelectOptions.prototype.toggleCondition;
*/
olx.interaction.SelectOptions.prototype.multi;
/**
* A function that takes an {@link ol.Feature} and an {@link ol.layer.Layer} and
* returns `true` if the feature may be selected or `false` otherwise.
@@ -2877,7 +2881,7 @@ olx.interaction.SnapOptions.prototype.features;
/**
* Pixel tolerance for considering the pointer close enough to a segment or
* vertex for editing. Default is `10` pixels.
* vertex for snapping. Default is `10` pixels.
* @type {number|undefined}
* @api
*/
@@ -4180,7 +4184,8 @@ olx.source.ImageMapGuideOptions.prototype.projection;
/**
* Ratio. `1` means image requests are the size of the map viewport, `2` means
* twice the size of the map viewport, and so on. Default is `1`.
* twice the width and height of the map viewport, and so on. Must be `1` or
* higher. Default is `1`.
* @type {number|undefined}
* @api stable
*/
@@ -4195,7 +4200,6 @@ olx.source.ImageMapGuideOptions.prototype.ratio;
olx.source.ImageMapGuideOptions.prototype.resolutions;
/**
* Optional function to load an image given a URL.
* @type {ol.TileLoadFunctionType|undefined}
@@ -4399,7 +4403,8 @@ olx.source.ImageCanvasOptions.prototype.projection;
/**
* Ratio. 1 means canvases are the size of the map viewport, 2 means twice the
* size of the map viewport, and so on. Default is `1.5`.
* width and height of the map viewport, and so on. Must be `1` or higher.
* Default is `1.5`.
* @type {number|undefined}
* @api
*/
@@ -4462,7 +4467,8 @@ olx.source.ImageVectorOptions.prototype.projection;
/**
* Ratio. 1 means canvases are the size of the map viewport, 2 means twice the
* size of the map viewport, and so on. Default is `1.5`.
* width and height of the map viewport, and so on. Must be `1` or higher.
* Default is `1.5`.
* @type {number|undefined}
* @api
*/
@@ -4495,6 +4501,65 @@ olx.source.ImageVectorOptions.prototype.source;
olx.source.ImageVectorOptions.prototype.style;
/**
* @typedef {{sources: Array.<ol.source.Source>,
* operation: (ol.raster.Operation|undefined),
* lib: (Object|undefined),
* threads: (number|undefined),
* operationType: (ol.raster.OperationType|undefined)}}
* @api
*/
olx.source.RasterOptions;
/**
* Input sources.
* @type {Array.<ol.source.Source>}
* @api
*/
olx.source.RasterOptions.prototype.sources;
/**
* Raster operation. The operation will be called with data from input sources
* and the output will be assigned to the raster source.
* @type {ol.raster.Operation|undefined}
* @api
*/
olx.source.RasterOptions.prototype.operation;
/**
* Functions that will be made available to operations run in a worker.
* @type {Object|undefined}
* @api
*/
olx.source.RasterOptions.prototype.lib;
/**
* By default, operations will be run in a single worker thread. To avoid using
* workers altogether, set `threads: 0`. For pixel operations, operations can
* be run in multiple worker threads. Note that there is additional overhead in
* transferring data to multiple workers, and that depending on the user's
* system, it may not be possible to parallelize the work.
* @type {number|undefined}
* @api
*/
olx.source.RasterOptions.prototype.threads;
/**
* Operation type. Supported values are `'pixel'` and `'image'`. By default,
* `'pixel'` operations are assumed, and operations will be called with an
* array of pixels from input sources. If set to `'image'`, operations will
* be called with an array of ImageData objects from input sources.
* @type {ol.raster.OperationType|undefined}
* @api
*/
olx.source.RasterOptions.prototype.operationType;
/**
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
* crossOrigin: (null|string|undefined),
@@ -4586,7 +4651,8 @@ olx.source.ImageWMSOptions.prototype.projection;
/**
* Ratio. `1` means image requests are the size of the map viewport, `2` means
* twice the size of the map viewport, and so on. Default is `1.5`.
* twice the width and height of the map viewport, and so on. Must be `1` or
* higher. Default is `1.5`.
* @type {number|undefined}
* @api stable
*/
@@ -4754,6 +4820,7 @@ olx.source.ImageStaticOptions.prototype.url;
/**
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
* crossOrigin: (null|string|undefined),
* params: (Object.<string, *>|undefined),
* logo: (string|olx.LogoOptions|undefined),
* tileGrid: (ol.tilegrid.TileGrid|undefined),
@@ -4766,6 +4833,7 @@ olx.source.ImageStaticOptions.prototype.url;
*/
olx.source.TileArcGISRestOptions;
/**
* Attributions.
* @type {Array.<ol.Attribution>|undefined}
@@ -4774,6 +4842,18 @@ olx.source.TileArcGISRestOptions;
olx.source.TileArcGISRestOptions.prototype.attributions;
/**
* The `crossOrigin` attribute for loaded images. Note that you must provide a
* `crossOrigin` value if you are using the WebGL renderer or if you want to
* access pixel data with the Canvas renderer. See
* {@link https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image}
* for more detail.
* @type {null|string|undefined}
* @api
*/
olx.source.TileArcGISRestOptions.prototype.crossOrigin;
/**
* ArcGIS Rest parameters. This field is optional. Service defaults will be
* used for any fields not specified. `FORMAT` is `PNG32` by default. `F` is `IMAGE` by
@@ -4807,6 +4887,7 @@ olx.source.TileArcGISRestOptions.prototype.logo;
*/
olx.source.TileArcGISRestOptions.prototype.tileGrid;
/**
* Projection.
* @type {ol.proj.ProjectionLike}
@@ -5324,7 +5405,9 @@ olx.source.WMTSOptions.prototype.dimensions;
/**
* A URL for the service. For the RESTful request encoding, this is a URL
* template. For KVP encoding, it is normal URL.
* template. For KVP encoding, it is normal URL. A `{?-?}` template pattern,
* for example `subdomain{a-f}.domain.com`, may be used instead of defining
* each one separately in the `urls` option.
* @type {string|undefined}
* @api stable
*/
@@ -5481,6 +5564,8 @@ olx.source.XYZOptions.prototype.tileUrlFunction;
/**
* URL template. Must include `{x}`, `{y}` or `{-y}`, and `{z}` placeholders.
* A `{?-?}` template pattern, for example `subdomain{a-f}.domain.com`, may be
* used instead of defining each one separately in the `urls` option.
* @type {string|undefined}
* @api stable
*/

View File

@@ -1,6 +1,6 @@
{
"name": "openlayers",
"version": "3.7.0",
"version": "3.8.2",
"description": "Build tools and sources for developing OpenLayers based mapping applications",
"keywords": [
"map",
@@ -12,7 +12,8 @@
"install": "node tasks/install.js",
"postinstall": "closure-util update",
"start": "node tasks/serve.js",
"test": "node tasks/test.js"
"test": "node tasks/test.js",
"debug-server": "node tasks/serve-lib.js"
},
"main": "dist/ol.js",
"repository": {
@@ -25,22 +26,22 @@
},
"dependencies": {
"async": "0.9.0",
"closure-util": "1.4.0",
"browserify": "9.0.3",
"closure-util": "1.5.0",
"fs-extra": "0.12.0",
"glob": "5.0.3",
"graceful-fs": "3.0.2",
"handlebars": "3.0.1",
"htmlparser2": "3.7.3",
"jsdoc": "3.3.0-alpha9",
"marked": "0.3.3",
"metalsmith": "1.6.0",
"metalsmith-templates": "0.7.0",
"nomnom": "1.8.0",
"pixelworks": "1.0.0",
"rbush": "1.3.5",
"temp": "0.8.1",
"walk": "2.3.4",
"wrench": "1.5.8",
"browserify": "9.0.3"
"wrench": "1.5.8"
},
"devDependencies": {
"clean-css": "2.2.16",
@@ -60,6 +61,7 @@
"slimerjs-edge": "0.10.0-pre-2"
},
"ext": [
"rbush"
"rbush",
{"module": "pixelworks", "browserify": true}
]
}

View File

@@ -3,7 +3,6 @@ goog.provide('ol.control.Control');
goog.require('goog.array');
goog.require('goog.dom');
goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('ol.MapEventType');
goog.require('ol.Object');
@@ -79,24 +78,6 @@ ol.control.Control = function(options) {
goog.inherits(ol.control.Control, ol.Object);
/**
* Bind a listener that blurs the passed element on any mouseout- or
* focusout-event.
*
* @param {!Element} button The button which shall blur on mouseout- or
* focusout-event.
* @protected
*/
ol.control.Control.bindMouseOutFocusOutBlur = function(button) {
goog.events.listen(button, [
goog.events.EventType.MOUSEOUT,
goog.events.EventType.FOCUSOUT
], /** @this {Element} */ function() {
this.blur();
}, false);
};
/**
* @inheritDoc
*/

View File

@@ -67,8 +67,6 @@ ol.control.FullScreen = function(opt_options) {
goog.events.listen(button, goog.events.EventType.CLICK,
this.handleClick_, false, this);
ol.control.Control.bindMouseOutFocusOutBlur(button);
goog.events.listen(goog.global.document,
goog.dom.fullscreen.EventType.CHANGE,
this.handleFullScreenChange_, false, this);

View File

@@ -90,8 +90,6 @@ ol.control.OverviewMap = function(opt_options) {
goog.events.listen(button, goog.events.EventType.CLICK,
this.handleClick_, false, this);
ol.control.Control.bindMouseOutFocusOutBlur(button);
var ovmapDiv = goog.dom.createDom(goog.dom.TagName.DIV, 'ol-overviewmap-map');
/**

View File

@@ -60,8 +60,6 @@ ol.control.Rotate = function(opt_options) {
goog.events.listen(button, goog.events.EventType.CLICK,
ol.control.Rotate.prototype.handleClick_, false, this);
ol.control.Control.bindMouseOutFocusOutBlur(button);
var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
ol.css.CLASS_CONTROL;
var element = goog.dom.createDom(goog.dom.TagName.DIV, cssClasses, button);

View File

@@ -50,8 +50,6 @@ ol.control.Zoom = function(opt_options) {
goog.events.EventType.CLICK, goog.partial(
ol.control.Zoom.prototype.handleClick_, delta), false, this);
ol.control.Control.bindMouseOutFocusOutBlur(inElement);
var outElement = goog.dom.createDom(goog.dom.TagName.BUTTON, {
'class': className + '-out',
'type' : 'button',

View File

@@ -8,7 +8,7 @@ goog.require('goog.dom.TagName');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.events.EventType');
goog.require('goog.fx.DragDropEvent');
goog.require('goog.fx.DragEvent');
goog.require('goog.fx.Dragger');
goog.require('goog.fx.Dragger.EventType');
goog.require('goog.math');
@@ -222,7 +222,7 @@ ol.control.ZoomSlider.prototype.handleContainerClick_ = function(browserEvent) {
/**
* Handle dragger start events.
* @param {goog.fx.DragDropEvent} event The dragdropevent.
* @param {goog.fx.DragEvent} event The drag event.
* @private
*/
ol.control.ZoomSlider.prototype.handleDraggerStart_ = function(event) {
@@ -233,7 +233,7 @@ ol.control.ZoomSlider.prototype.handleDraggerStart_ = function(event) {
/**
* Handle dragger drag events.
*
* @param {goog.fx.DragDropEvent} event The dragdropevent.
* @param {goog.fx.DragEvent} event The drag event.
* @private
*/
ol.control.ZoomSlider.prototype.handleDraggerDrag_ = function(event) {
@@ -245,7 +245,7 @@ ol.control.ZoomSlider.prototype.handleDraggerDrag_ = function(event) {
/**
* Handle dragger end events.
* @param {goog.fx.DragDropEvent} event The dragdropevent.
* @param {goog.fx.DragEvent} event The drag event.
* @private
*/
ol.control.ZoomSlider.prototype.handleDraggerEnd_ = function(event) {

View File

@@ -43,8 +43,6 @@ ol.control.ZoomToExtent = function(opt_options) {
goog.events.listen(button, goog.events.EventType.CLICK,
this.handleClick_, false, this);
ol.control.Control.bindMouseOutFocusOutBlur(button);
var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' +
ol.css.CLASS_CONTROL;
var element = goog.dom.createDom(goog.dom.TagName.DIV, cssClasses, button);

View File

@@ -67,9 +67,11 @@ ol.DeviceOrientationProperty = {
*
* @see http://www.w3.org/TR/orientation-event/
*
* To get notified of device orientation changes, register a listener for the
* generic `change` event on your `ol.DeviceOrientation` instance.
*
* @constructor
* @extends {ol.Object}
* @fires change Triggered when the device orientation changes.
* @param {olx.DeviceOrientationOptions=} opt_options Options.
* @api
*/

View File

@@ -111,6 +111,18 @@ ol.events.condition.singleClick = function(mapBrowserEvent) {
};
/**
* Return `true` if the event is a map `dblclick` event, `false` otherwise.
*
* @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event.
* @return {boolean} True if the event is a map `dblclick` event.
* @api stable
*/
ol.events.condition.doubleClick = function(mapBrowserEvent) {
return mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DBLCLICK;
};
/**
* Return `true` if no modifier key (alt-, shift- or platform-modifier-key) is
* pressed.

View File

@@ -50,8 +50,6 @@ goog.require('ol.style.Style');
*
* @constructor
* @extends {ol.Object}
* @fires change Triggered when the id, the geometry or the style of the
* feature changes.
* @param {ol.geom.Geometry|Object.<string, *>=} opt_geometryOrProperties
* You may pass a Geometry object directly, or an object literal
* containing properties. If you pass an object literal, you may
@@ -153,6 +151,7 @@ ol.Feature.prototype.getGeometry = function() {
/**
* @return {number|string|undefined} Id.
* @api stable
* @observable
*/
ol.Feature.prototype.getId = function() {
return this.id_;
@@ -177,6 +176,7 @@ ol.Feature.prototype.getGeometryName = function() {
* @return {ol.style.Style|Array.<ol.style.Style>|
* ol.FeatureStyleFunction} The feature style.
* @api stable
* @observable
*/
ol.Feature.prototype.getStyle = function() {
return this.style_;
@@ -238,6 +238,7 @@ ol.Feature.prototype.setGeometry = function(geometry) {
* @param {ol.style.Style|Array.<ol.style.Style>|
* ol.FeatureStyleFunction} style Style for this feature.
* @api stable
* @observable
*/
ol.Feature.prototype.setStyle = function(style) {
this.style_ = style;
@@ -254,6 +255,7 @@ ol.Feature.prototype.setStyle = function(style) {
* method.
* @param {number|string|undefined} id The feature id.
* @api stable
* @observable
*/
ol.Feature.prototype.setId = function(id) {
this.id_ = id;

View File

@@ -39,6 +39,9 @@ ol.GeolocationProperty = {
* The [Geolocation API](http://www.w3.org/TR/geolocation-API/)
* is used to locate a user's position.
*
* To get notified of position changes, register a listener for the generic
* `change` event on your instance of `ol.Geolocation`.
*
* Example:
*
* var geolocation = new ol.Geolocation({
@@ -52,7 +55,6 @@ ol.GeolocationProperty = {
*
* @constructor
* @extends {ol.Object}
* @fires change Triggered when the position changes.
* @param {olx.GeolocationOptions=} opt_options Options.
* @api stable
*/

View File

@@ -50,9 +50,11 @@ ol.geom.GeometryLayout = {
* instantiated in apps.
* Base class for vector geometries.
*
* To get notified of changes to the geometry, register a listener for the
* generic `change` event on your geometry instance.
*
* @constructor
* @extends {ol.Object}
* @fires change Triggered when the geometry changes.
* @api stable
*/
ol.geom.Geometry = function() {

View File

@@ -2,6 +2,7 @@ goog.provide('ol.geom.MultiPolygon');
goog.require('goog.array');
goog.require('goog.asserts');
goog.require('goog.object');
goog.require('ol.extent');
goog.require('ol.geom.GeometryLayout');
goog.require('ol.geom.GeometryType');
@@ -119,8 +120,10 @@ ol.geom.MultiPolygon.prototype.appendPolygon = function(polygon) {
*/
ol.geom.MultiPolygon.prototype.clone = function() {
var multiPolygon = new ol.geom.MultiPolygon(null);
var newEndss = /** @type {Array.<Array.<number>>} */
(goog.object.unsafeClone(this.endss_));
multiPolygon.setFlatCoordinates(
this.layout, this.flatCoordinates.slice(), this.endss_.slice());
this.layout, this.flatCoordinates.slice(), newEndss);
return multiPolygon;
};

View File

@@ -1,5 +1,6 @@
goog.provide('ol.ImageCanvas');
goog.require('goog.asserts');
goog.require('ol.ImageBase');
goog.require('ol.ImageState');
@@ -13,12 +14,23 @@ goog.require('ol.ImageState');
* @param {number} pixelRatio Pixel ratio.
* @param {Array.<ol.Attribution>} attributions Attributions.
* @param {HTMLCanvasElement} canvas Canvas.
* @param {ol.ImageCanvasLoader=} opt_loader Optional loader function to
* support asynchronous canvas drawing.
*/
ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions,
canvas) {
canvas, opt_loader) {
goog.base(this, extent, resolution, pixelRatio, ol.ImageState.LOADED,
attributions);
/**
* Optional canvas loader function.
* @type {?ol.ImageCanvasLoader}
* @private
*/
this.loader_ = goog.isDef(opt_loader) ? opt_loader : null;
var state = goog.isDef(opt_loader) ?
ol.ImageState.IDLE : ol.ImageState.LOADED;
goog.base(this, extent, resolution, pixelRatio, state, attributions);
/**
* @private
@@ -26,13 +38,68 @@ ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions,
*/
this.canvas_ = canvas;
/**
* @private
* @type {Error}
*/
this.error_ = null;
};
goog.inherits(ol.ImageCanvas, ol.ImageBase);
/**
* Get any error associated with asynchronous rendering.
* @return {Error} Any error that occurred during rendering.
*/
ol.ImageCanvas.prototype.getError = function() {
return this.error_;
};
/**
* Handle async drawing complete.
* @param {Error} err Any error during drawing.
* @private
*/
ol.ImageCanvas.prototype.handleLoad_ = function(err) {
if (err) {
this.error_ = err;
this.state = ol.ImageState.ERROR;
} else {
this.state = ol.ImageState.LOADED;
}
this.changed();
};
/**
* Trigger drawing on canvas.
*/
ol.ImageCanvas.prototype.load = function() {
if (this.state == ol.ImageState.IDLE) {
goog.asserts.assert(!goog.isNull(this.loader_));
this.state = ol.ImageState.LOADING;
this.changed();
this.loader_(goog.bind(this.handleLoad_, this));
}
};
/**
* @inheritDoc
*/
ol.ImageCanvas.prototype.getImage = function(opt_context) {
return this.canvas_;
};
/**
* A function that is called to trigger asynchronous canvas drawing. It is
* called with a "done" callback that should be called when drawing is done.
* If any error occurs during drawing, the "done" callback should be called with
* that error.
*
* @typedef {function(function(Error))}
*/
ol.ImageCanvasLoader;

View File

@@ -398,10 +398,12 @@ ol.interaction.Draw.handleUpEvent_ = function(event) {
this.handlePointerMove_(event);
if (goog.isNull(this.finishCoordinate_)) {
this.startDrawing_(event);
} else if ((this.mode_ === ol.interaction.DrawMode.POINT ||
this.mode_ === ol.interaction.DrawMode.CIRCLE) &&
!goog.isNull(this.finishCoordinate_) ||
this.atFinish_(event)) {
if (this.mode_ === ol.interaction.DrawMode.POINT) {
this.finishDrawing();
}
} else if (this.mode_ === ol.interaction.DrawMode.CIRCLE) {
this.finishDrawing();
} else if (this.atFinish_(event)) {
this.finishDrawing();
} else {
this.addToDrawing_(event);
@@ -419,10 +421,7 @@ ol.interaction.Draw.handleUpEvent_ = function(event) {
* @private
*/
ol.interaction.Draw.prototype.handlePointerMove_ = function(event) {
if (this.mode_ === ol.interaction.DrawMode.POINT &&
goog.isNull(this.finishCoordinate_)) {
this.startDrawing_(event);
} else if (!goog.isNull(this.finishCoordinate_)) {
if (!goog.isNull(this.finishCoordinate_)) {
this.modifyDrawing_(event);
} else {
this.createOrUpdateSketchPoint_(event);

View File

@@ -142,12 +142,12 @@ ol.interaction.Modify = function(options) {
this.lastPixel_ = [0, 0];
/**
* Keep track of the last inserted pixel location to avoid
* unintentional deletion.
* @type {ol.Pixel}
* Tracks if the next `singleclick` event should be ignored to prevent
* accidental deletion right after vertex creation.
* @type {boolean}
* @private
*/
this.lastNewVertexPixel_ = [NaN, NaN];
this.ignoreNextSingleClick_ = false;
/**
* Segment RTree for each layer
@@ -176,7 +176,7 @@ ol.interaction.Modify = function(options) {
this.dragSegments_ = null;
/**
* Draw overlay where are sketch features are drawn.
* Draw overlay where sketch features are drawn.
* @type {ol.layer.Vector}
* @private
*/
@@ -543,6 +543,8 @@ ol.interaction.Modify.handleDownEvent_ = function(evt) {
* @private
*/
ol.interaction.Modify.handleDragEvent_ = function(evt) {
this.ignoreNextSingleClick_ = false;
var vertex = evt.coordinate;
for (var i = 0, ii = this.dragSegments_.length; i < ii; ++i) {
var dragSegment = this.dragSegments_[i];
@@ -626,8 +628,8 @@ ol.interaction.Modify.handleEvent = function(mapBrowserEvent) {
}
if (!goog.isNull(this.vertexFeature_) &&
this.deleteCondition_(mapBrowserEvent)) {
if (!(this.lastNewVertexPixel_[0] === this.lastPixel_[0] &&
this.lastNewVertexPixel_[1] === this.lastPixel_[1])) {
if (mapBrowserEvent.type != ol.MapBrowserEvent.EventType.SINGLECLICK ||
!this.ignoreNextSingleClick_) {
var geometry = this.vertexFeature_.getGeometry();
goog.asserts.assertInstanceof(geometry, ol.geom.Point,
'geometry should be an ol.geom.Point');
@@ -636,6 +638,11 @@ ol.interaction.Modify.handleEvent = function(mapBrowserEvent) {
handled = true;
}
}
if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.SINGLECLICK) {
this.ignoreNextSingleClick_ = false;
}
return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) &&
!handled;
};
@@ -789,7 +796,7 @@ ol.interaction.Modify.prototype.insertVertex_ = function(segmentData, vertex) {
rTree.insert(ol.extent.boundingExtent(newSegmentData2.segment),
newSegmentData2);
this.dragSegments_.push([newSegmentData2, 0]);
this.lastNewVertexPixel_ = this.lastPixel_;
this.ignoreNextSingleClick_ = true;
};
@@ -881,8 +888,10 @@ ol.interaction.Modify.prototype.removeVertex_ = function() {
newSegmentData);
this.updateSegmentIndices_(geometry, index, segmentData.depth, -1);
this.overlay_.getSource().removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
if (!goog.isNull(this.vertexFeature_)) {
this.overlay_.getSource().removeFeature(this.vertexFeature_);
this.vertexFeature_ = null;
}
}
}
}

View File

@@ -83,10 +83,13 @@ goog.inherits(ol.SelectEvent, goog.events.Event);
/**
* @classdesc
* Handles selection of vector data. An {@link ol.source.Vector} is maintained
* internally to store the selected feature(s). Which features are selected is
* determined by the `condition` option, and optionally the `toggle` or
* `add`/`remove` options.
* Interaction for selecting vector features. By default, selected features are
* styled differently, so this interaction can be used for visual highlighting,
* as well as selecting features for other actions, such as modification or
* output. There are three ways of controlling which features are selected:
* using the browser event as defined by the `condition` and optionally the
* `toggle`, `add`/`remove`, and `multi` options; a `layers` filter; and a
* further feature filter using the `filter` option.
*
* @constructor
* @extends {ol.interaction.Interaction}

View File

@@ -19,10 +19,11 @@ goog.require('ol.source.State');
* Layers group together those properties that pertain to how the data is to be
* displayed, irrespective of the source of that data.
*
* A generic `change` event is fired when the state of the source changes.
*
* @constructor
* @extends {ol.layer.Base}
* @fires ol.render.Event
* @fires change Triggered when the state of the source changes.
* @param {olx.layer.LayerOptions} options Layer options.
* @api stable
*/
@@ -148,10 +149,13 @@ ol.layer.Layer.prototype.handleSourcePropertyChange_ = function() {
/**
* Sets the layer to be rendered on a map. The map will not manage this layer in
* its layers collection, and the layer will be rendered on top. This is useful
* for temporary layers. To remove an unmanaged layer from the map, use
* `#setMap(null)`. To add the layer to a map and have it managed by the map,
* use {@link ol.Map#addLayer} instead.
* its layers collection, layer filters in {@link ol.Map#forEachLayerAtPixel}
* will not filter the layer, and it will be rendered on top. This is useful for
* temporary layers. To remove an unmanaged layer from the map, use
* `#setMap(null)`.
*
* To add the layer to a map and have it managed by the map, use
* {@link ol.Map#addLayer} instead.
* @param {ol.Map} map Map.
* @api
*/

View File

@@ -29,9 +29,10 @@ ol.layer.GroupProperty = {
* @classdesc
* A {@link ol.Collection} of layers that are handled together.
*
* A generic `change` event is triggered when the group/Collection changes.
*
* @constructor
* @extends {ol.layer.Base}
* @fires change Triggered when the group/Collection changes.
* @param {olx.layer.GroupOptions=} opt_options Layer options.
* @api stable
*/

View File

@@ -16,6 +16,7 @@ goog.require('goog.events.EventType');
*
* @constructor
* @extends {goog.events.EventTarget}
* @fires change
* @suppress {checkStructDictInheritance}
* @struct
* @api stable
@@ -46,7 +47,6 @@ ol.Observable.unByKey = function(key) {
/**
* Increases the revision counter and dispatches a 'change' event.
* @fires change
* @api
*/
ol.Observable.prototype.changed = function() {
@@ -55,6 +55,13 @@ ol.Observable.prototype.changed = function() {
};
/**
* Triggered when the revision counter is increased.
* @event change
* @api
*/
/**
* @return {number} Revision.
* @api

View File

@@ -0,0 +1,32 @@
goog.provide('ol.raster.Operation');
goog.provide('ol.raster.OperationType');
/**
* Raster operation type. Supported values are `'pixel'` and `'image'`.
* @enum {string}
* @api
*/
ol.raster.OperationType = {
PIXEL: 'pixel',
IMAGE: 'image'
};
/**
* A function that takes an array of input data, performs some operation, and
* returns an array of ouput data. For `'pixel'` type operations, functions
* will be called with an array of {@link ol.raster.Pixel} data and should
* return an array of the same. For `'image'` type operations, functions will
* be called with an array of {@link ImageData
* https://developer.mozilla.org/en-US/docs/Web/API/ImageData} and should return
* an array of the same. The operations are called with a second "data"
* argument, which can be used for storage. The data object is accessible
* from raster events, where it can be initialized in "beforeoperations" and
* accessed again in "afteroperations".
*
* @typedef {function((Array.<ol.raster.Pixel>|Array.<ImageData>), Object):
* (Array.<ol.raster.Pixel>|Array.<ImageData>)}
* @api
*/
ol.raster.Operation;

9
src/ol/raster/pixel.js Normal file
View File

@@ -0,0 +1,9 @@
goog.provide('ol.raster.Pixel');
/**
* An array of numbers representing pixel values.
* @typedef {Array.<number>} ol.raster.Pixel
* @api
*/
ol.raster.Pixel;

View File

@@ -113,7 +113,6 @@ ol.renderer.Layer.prototype.createLoadedTileFinder = function(source, tiles) {
/**
* @protected
* @return {ol.layer.Layer} Layer.
*/
ol.renderer.Layer.prototype.getLayer = function() {

View File

@@ -167,8 +167,9 @@ ol.renderer.Map.prototype.forEachFeatureAtCoordinate =
for (i = numLayers - 1; i >= 0; --i) {
var layerState = layerStates[i];
var layer = layerState.layer;
if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) &&
layerFilter.call(thisArg2, layer)) {
if (!layerState.managed ||
(ol.layer.Layer.visibleAtResolution(layerState, viewResolution) &&
layerFilter.call(thisArg2, layer))) {
var layerRenderer = this.getLayerRenderer(layer);
result = layerRenderer.forEachFeatureAtCoordinate(
layer.getSource().getWrapX() ? translatedCoordinate : coordinate,

View File

@@ -467,6 +467,8 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
this.textureCache_.set((-frameState.index).toString(), null);
++this.textureCacheFrameMarkerCount_;
this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState);
/** @type {Array.<ol.layer.LayerState>} */
var layerStatesToDraw = [];
var layerStatesArray = frameState.layerStatesArray;
@@ -499,8 +501,6 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
gl.enable(goog.webgl.BLEND);
gl.viewport(0, 0, this.canvas_.width, this.canvas_.height);
this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState);
for (i = 0, ii = layerStatesToDraw.length; i < ii; ++i) {
layerState = layerStatesToDraw[i];
layerRenderer = this.getLayerRenderer(layerState.layer);

View File

@@ -0,0 +1,535 @@
goog.provide('ol.source.Raster');
goog.provide('ol.source.RasterEvent');
goog.provide('ol.source.RasterEventType');
goog.require('goog.asserts');
goog.require('goog.events');
goog.require('goog.events.Event');
goog.require('goog.events.EventType');
goog.require('goog.functions');
goog.require('goog.object');
goog.require('goog.vec.Mat4');
goog.require('ol.ImageCanvas');
goog.require('ol.TileQueue');
goog.require('ol.dom');
goog.require('ol.ext.pixelworks');
goog.require('ol.extent');
goog.require('ol.layer.Image');
goog.require('ol.layer.Tile');
goog.require('ol.raster.OperationType');
goog.require('ol.renderer.canvas.ImageLayer');
goog.require('ol.renderer.canvas.TileLayer');
goog.require('ol.source.Image');
goog.require('ol.source.State');
goog.require('ol.source.Tile');
/**
* @classdesc
* A source that transforms data from any number of input sources using an array
* of {@link ol.raster.Operation} functions to transform input pixel values into
* output pixel values.
*
* @constructor
* @extends {ol.source.Image}
* @param {olx.source.RasterOptions} options Options.
* @api
*/
ol.source.Raster = function(options) {
/**
* @private
* @type {*}
*/
this.worker_ = null;
/**
* @private
* @type {ol.raster.OperationType}
*/
this.operationType_ = goog.isDef(options.operationType) ?
options.operationType : ol.raster.OperationType.PIXEL;
/**
* @private
* @type {number}
*/
this.threads_ = goog.isDef(options.threads) ? options.threads : 1;
/**
* @private
* @type {Array.<ol.renderer.canvas.Layer>}
*/
this.renderers_ = ol.source.Raster.createRenderers_(options.sources);
for (var r = 0, rr = this.renderers_.length; r < rr; ++r) {
goog.events.listen(this.renderers_[r], goog.events.EventType.CHANGE,
this.changed, false, this);
}
/**
* @private
* @type {CanvasRenderingContext2D}
*/
this.canvasContext_ = ol.dom.createCanvasContext2D();
/**
* @private
* @type {ol.TileQueue}
*/
this.tileQueue_ = new ol.TileQueue(
goog.functions.constant(1),
goog.bind(this.changed, this));
var layerStatesArray = ol.source.Raster.getLayerStatesArray_(this.renderers_);
var layerStates = {};
for (var i = 0, ii = layerStatesArray.length; i < ii; ++i) {
layerStates[goog.getUid(layerStatesArray[i].layer)] = layerStatesArray[i];
}
/**
* The most recently rendered state.
* @type {?ol.source.Raster.RenderedState}
* @private
*/
this.renderedState_ = null;
/**
* The most recently rendered image canvas.
* @type {ol.ImageCanvas}
* @private
*/
this.renderedImageCanvas_ = null;
/**
* @private
* @type {olx.FrameState}
*/
this.frameState_ = {
animate: false,
attributions: {},
coordinateToPixelMatrix: goog.vec.Mat4.createNumber(),
extent: null,
focus: null,
index: 0,
layerStates: layerStates,
layerStatesArray: layerStatesArray,
logos: {},
pixelRatio: 1,
pixelToCoordinateMatrix: goog.vec.Mat4.createNumber(),
postRenderFunctions: [],
size: [0, 0],
skippedFeatureUids: {},
tileQueue: this.tileQueue_,
time: Date.now(),
usedTiles: {},
viewState: /** @type {olx.ViewState} */ ({
rotation: 0
}),
viewHints: [],
wantedTiles: {}
};
goog.base(this, {});
if (goog.isDef(options.operation)) {
this.setOperation(options.operation, options.lib);
}
};
goog.inherits(ol.source.Raster, ol.source.Image);
/**
* Set the operation.
* @param {ol.raster.Operation} operation New operation.
* @param {Object=} opt_lib Functions that will be available to operations run
* in a worker.
* @api
*/
ol.source.Raster.prototype.setOperation = function(operation, opt_lib) {
this.worker_ = new ol.ext.pixelworks.Processor({
operation: operation,
imageOps: this.operationType_ === ol.raster.OperationType.IMAGE,
queue: 1,
lib: opt_lib,
threads: this.threads_
});
this.changed();
};
/**
* Update the stored frame state.
* @param {ol.Extent} extent The view extent (in map units).
* @param {number} resolution The view resolution.
* @param {ol.proj.Projection} projection The view projection.
* @return {olx.FrameState} The updated frame state.
* @private
*/
ol.source.Raster.prototype.updateFrameState_ =
function(extent, resolution, projection) {
var frameState = /** @type {olx.FrameState} */ (
goog.object.clone(this.frameState_));
frameState.viewState = /** @type {olx.ViewState} */ (
goog.object.clone(frameState.viewState));
var center = ol.extent.getCenter(extent);
var width = Math.round(ol.extent.getWidth(extent) / resolution);
var height = Math.round(ol.extent.getHeight(extent) / resolution);
frameState.extent = extent;
frameState.focus = ol.extent.getCenter(extent);
frameState.size[0] = width;
frameState.size[1] = height;
var viewState = frameState.viewState;
viewState.center = center;
viewState.projection = projection;
viewState.resolution = resolution;
return frameState;
};
/**
* Determine if the most recently rendered image canvas is dirty.
* @param {ol.Extent} extent The requested extent.
* @param {number} resolution The requested resolution.
* @return {boolean} The image is dirty.
* @private
*/
ol.source.Raster.prototype.isDirty_ = function(extent, resolution) {
var state = this.renderedState_;
return !state ||
this.getRevision() !== state.revision ||
resolution !== state.resolution ||
!ol.extent.equals(extent, state.extent);
};
/**
* @inheritDoc
*/
ol.source.Raster.prototype.getImage =
function(extent, resolution, pixelRatio, projection) {
if (!this.allSourcesReady_()) {
return null;
}
if (!this.isDirty_(extent, resolution)) {
return this.renderedImageCanvas_;
}
var context = this.canvasContext_;
var canvas = context.canvas;
var width = Math.round(ol.extent.getWidth(extent) / resolution);
var height = Math.round(ol.extent.getHeight(extent) / resolution);
if (width !== canvas.width ||
height !== canvas.height) {
canvas.width = width;
canvas.height = height;
}
var frameState = this.updateFrameState_(extent, resolution, projection);
var imageCanvas = new ol.ImageCanvas(
extent, resolution, 1, this.getAttributions(), canvas,
this.composeFrame_.bind(this, frameState));
this.renderedImageCanvas_ = imageCanvas;
this.renderedState_ = {
extent: extent,
resolution: resolution,
revision: this.getRevision()
};
return imageCanvas;
};
/**
* Determine if all sources are ready.
* @return {boolean} All sources are ready.
* @private
*/
ol.source.Raster.prototype.allSourcesReady_ = function() {
var ready = true;
var source;
for (var i = 0, ii = this.renderers_.length; i < ii; ++i) {
source = this.renderers_[i].getLayer().getSource();
if (source.getState() !== ol.source.State.READY) {
ready = false;
break;
}
}
return ready;
};
/**
* Compose the frame. This renders data from all sources, runs pixel-wise
* operations, and renders the result to the stored canvas context.
* @param {olx.FrameState} frameState The frame state.
* @param {function(Error)} callback Called when composition is complete.
* @private
*/
ol.source.Raster.prototype.composeFrame_ = function(frameState, callback) {
var len = this.renderers_.length;
var imageDatas = new Array(len);
for (var i = 0; i < len; ++i) {
var imageData = ol.source.Raster.getImageData_(
this.renderers_[i], frameState, frameState.layerStatesArray[i]);
if (imageData) {
imageDatas[i] = imageData;
} else {
// image not yet ready
return;
}
}
var data = {};
this.dispatchEvent(new ol.source.RasterEvent(
ol.source.RasterEventType.BEFOREOPERATIONS, frameState, data));
this.worker_.process(imageDatas, data,
this.onWorkerComplete_.bind(this, frameState, callback));
frameState.tileQueue.loadMoreTiles(16, 16);
};
/**
* Called when pixel processing is complete.
* @param {olx.FrameState} frameState The frame state.
* @param {function(Error)} callback Called when rendering is complete.
* @param {Error} err Any error during processing.
* @param {ImageData} output The output image data.
* @param {Object} data The user data.
* @private
*/
ol.source.Raster.prototype.onWorkerComplete_ =
function(frameState, callback, err, output, data) {
if (err) {
callback(err);
return;
}
if (goog.isNull(output)) {
// job aborted
return;
}
this.dispatchEvent(new ol.source.RasterEvent(
ol.source.RasterEventType.AFTEROPERATIONS, frameState, data));
var resolution = frameState.viewState.resolution / frameState.pixelRatio;
if (!this.isDirty_(frameState.extent, resolution)) {
this.canvasContext_.putImageData(output, 0, 0);
}
callback(null);
};
/**
* Get image data from a renderer.
* @param {ol.renderer.canvas.Layer} renderer Layer renderer.
* @param {olx.FrameState} frameState The frame state.
* @param {ol.layer.LayerState} layerState The layer state.
* @return {ImageData} The image data.
* @private
*/
ol.source.Raster.getImageData_ = function(renderer, frameState, layerState) {
renderer.prepareFrame(frameState, layerState);
// We should be able to call renderer.composeFrame(), but this is inefficient
// for tiled sources (we've already rendered to an intermediate canvas in the
// prepareFrame call and we don't need to render again to the output canvas).
// TODO: make all canvas renderers render to a single canvas
var image = renderer.getImage();
if (!image) {
return null;
}
var imageTransform = renderer.getImageTransform();
var dx = Math.round(goog.vec.Mat4.getElement(imageTransform, 0, 3));
var dy = Math.round(goog.vec.Mat4.getElement(imageTransform, 1, 3));
var width = frameState.size[0];
var height = frameState.size[1];
if (image instanceof Image) {
if (!ol.source.Raster.context_) {
ol.source.Raster.context_ = ol.dom.createCanvasContext2D(width, height);
} else {
var canvas = ol.source.Raster.context_.canvas;
if (canvas.width !== width || canvas.height !== height) {
ol.source.Raster.context_ = ol.dom.createCanvasContext2D(width, height);
} else {
ol.source.Raster.context_.clearRect(0, 0, width, height);
}
}
var dw = Math.round(
image.width * goog.vec.Mat4.getElement(imageTransform, 0, 0));
var dh = Math.round(
image.height * goog.vec.Mat4.getElement(imageTransform, 1, 1));
ol.source.Raster.context_.drawImage(image, dx, dy, dw, dh);
return ol.source.Raster.context_.getImageData(0, 0, width, height);
} else {
return image.getContext('2d').getImageData(-dx, -dy, width, height);
}
};
/**
* A reusable canvas context.
* @type {CanvasRenderingContext2D}
* @private
*/
ol.source.Raster.context_ = null;
/**
* Get a list of layer states from a list of renderers.
* @param {Array.<ol.renderer.canvas.Layer>} renderers Layer renderers.
* @return {Array.<ol.layer.LayerState>} The layer states.
* @private
*/
ol.source.Raster.getLayerStatesArray_ = function(renderers) {
return renderers.map(function(renderer) {
return renderer.getLayer().getLayerState();
});
};
/**
* Create renderers for all sources.
* @param {Array.<ol.source.Source>} sources The sources.
* @return {Array.<ol.renderer.canvas.Layer>} Array of layer renderers.
* @private
*/
ol.source.Raster.createRenderers_ = function(sources) {
var len = sources.length;
var renderers = new Array(len);
for (var i = 0; i < len; ++i) {
renderers[i] = ol.source.Raster.createRenderer_(sources[i]);
}
return renderers;
};
/**
* Create a renderer for the provided source.
* @param {ol.source.Source} source The source.
* @return {ol.renderer.canvas.Layer} The renderer.
* @private
*/
ol.source.Raster.createRenderer_ = function(source) {
var renderer = null;
if (source instanceof ol.source.Tile) {
renderer = ol.source.Raster.createTileRenderer_(
/** @type {ol.source.Tile} */ (source));
} else if (source instanceof ol.source.Image) {
renderer = ol.source.Raster.createImageRenderer_(
/** @type {ol.source.Image} */ (source));
} else {
goog.asserts.fail('Unsupported source type: ' + source);
}
return renderer;
};
/**
* Create an image renderer for the provided source.
* @param {ol.source.Image} source The source.
* @return {ol.renderer.canvas.Layer} The renderer.
* @private
*/
ol.source.Raster.createImageRenderer_ = function(source) {
var layer = new ol.layer.Image({source: source});
return new ol.renderer.canvas.ImageLayer(layer);
};
/**
* Create a tile renderer for the provided source.
* @param {ol.source.Tile} source The source.
* @return {ol.renderer.canvas.Layer} The renderer.
* @private
*/
ol.source.Raster.createTileRenderer_ = function(source) {
var layer = new ol.layer.Tile({source: source});
return new ol.renderer.canvas.TileLayer(layer);
};
/**
* @typedef {{revision: number,
* resolution: number,
* extent: ol.Extent}}
*/
ol.source.Raster.RenderedState;
/**
* @classdesc
* Events emitted by {@link ol.source.Raster} instances are instances of this
* type.
*
* @constructor
* @extends {goog.events.Event}
* @implements {oli.source.RasterEvent}
* @param {string} type Type.
* @param {olx.FrameState} frameState The frame state.
* @param {Object} data An object made available to operations.
*/
ol.source.RasterEvent = function(type, frameState, data) {
goog.base(this, type);
/**
* The raster extent.
* @type {ol.Extent}
* @api
*/
this.extent = frameState.extent;
/**
* The pixel resolution (map units per pixel).
* @type {number}
* @api
*/
this.resolution = frameState.viewState.resolution / frameState.pixelRatio;
/**
* An object made available to all operations. This can be used by operations
* as a storage object (e.g. for calculating statistics).
* @type {Object}
* @api
*/
this.data = data;
};
goog.inherits(ol.source.RasterEvent, goog.events.Event);
/**
* @enum {string}
*/
ol.source.RasterEventType = {
/**
* Triggered before operations are run.
* @event ol.source.RasterEvent#beforeoperations
* @api
*/
BEFOREOPERATIONS: 'beforeoperations',
/**
* Triggered after operations are run.
* @event ol.source.RasterEvent#afteroperations
* @api
*/
AFTEROPERATIONS: 'afteroperations'
};

View File

@@ -37,9 +37,10 @@ ol.source.SourceOptions;
* instantiated in apps.
* Base class for {@link ol.layer.Layer} sources.
*
* A generic `change` event is triggered when the state of the source changes.
*
* @constructor
* @extends {ol.Object}
* @fires change Triggered when the state of the source changes.
* @param {ol.source.SourceOptions} options Source options.
* @api stable
*/

View File

@@ -39,6 +39,7 @@ ol.source.TileArcGISRest = function(opt_options) {
goog.base(this, {
attributions: options.attributions,
crossOrigin: options.crossOrigin,
logo: options.logo,
projection: options.projection,
tileGrid: options.tileGrid,

View File

@@ -372,16 +372,14 @@ ol.source.Vector.prototype.bindFeaturesCollection_ = function(collection) {
*/
ol.source.Vector.prototype.clear = function(opt_fast) {
if (opt_fast) {
for (var featureId in this.featureChangeKeys_) {
var keys = this.featureChangeKeys_[featureId];
goog.array.forEach(keys, goog.events.unlistenByKey);
}
if (goog.isNull(this.featuresCollection_)) {
for (var featureId in this.featureChangeKeys_) {
var keys = this.featureChangeKeys_[featureId];
goog.array.forEach(keys, goog.events.unlistenByKey);
}
this.featureChangeKeys_ = {};
this.idIndex_ = {};
this.undefIdIndex_ = {};
} else {
this.featuresCollection_.clear();
}
} else {
var rmFeatureInternal = this.removeFeatureInternal;
@@ -390,6 +388,9 @@ ol.source.Vector.prototype.clear = function(opt_fast) {
goog.object.forEach(this.nullGeometryFeatures_, rmFeatureInternal, this);
}
}
if (!goog.isNull(this.featuresCollection_)) {
this.featuresCollection_.clear();
}
goog.asserts.assert(goog.object.isEmpty(this.featureChangeKeys_),
'featureChangeKeys is an empty object now');
goog.asserts.assert(goog.object.isEmpty(this.idIndex_),

View File

@@ -351,19 +351,25 @@ ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) {
goog.asserts.assert(l['TileMatrixSetLink'].length > 0,
'layer has TileMatrixSetLink');
var tileMatrixSets = wmtsCap['Contents']['TileMatrixSet'];
var idx, matrixSet;
if (l['TileMatrixSetLink'].length > 1) {
idx = goog.array.findIndex(l['TileMatrixSetLink'],
function(elt, index, array) {
return elt['TileMatrixSet'] == config['matrixSet'];
});
} else if (goog.isDef(config['projection'])) {
idx = goog.array.findIndex(l['TileMatrixSetLink'],
function(elt, index, array) {
return elt['TileMatrixSet']['SupportedCRS'].replace(
/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3'
) == config['projection'];
});
if (goog.isDef(config['projection'])) {
idx = goog.array.findIndex(l['TileMatrixSetLink'],
function(elt, index, array) {
var tileMatrixSet = goog.array.find(tileMatrixSets, function(el) {
return el['Identifier'] == elt['TileMatrixSet'];
});
return tileMatrixSet['SupportedCRS'].replace(
/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3'
) == config['projection'];
});
} else {
idx = goog.array.findIndex(l['TileMatrixSetLink'],
function(elt, index, array) {
return elt['TileMatrixSet'] == config['matrixSet'];
});
}
} else {
idx = 0;
}

View File

@@ -8,7 +8,20 @@ goog.require('ol.source.TileImage');
/**
* @classdesc
* Layer source for tile data with URLs in a set XYZ format.
* Layer source for tile data with URLs in a set XYZ format that are
* defined in a URL template. By default, this follows the widely-used
* Google grid where `x` 0 and `y` 0 are in the top left. Grids like
* TMS where `x` 0 and `y` 0 are in the bottom left can be used by
* using the `{-y}` placeholder in the URL template, so long as the
* source does not have a custom tile grid. In this case,
* {@link ol.source.TileImage} can be used with a `tileUrlFunction`
* such as:
*
* tileUrlFunction: function(coordinate) {
* return 'http://mapserver.com/' + coordinate[0] + '/' +
* coordinate[1] + '/' + coordinate[2] + '.png';
* }
*
*
* @constructor
* @extends {ol.source.TileImage}

View File

@@ -89,14 +89,17 @@ ol.TileQueue.prototype.handleTileChange = function(event) {
* @param {number} maxNewLoads Maximum number of new tiles to load.
*/
ol.TileQueue.prototype.loadMoreTiles = function(maxTotalLoading, maxNewLoads) {
var newLoads = Math.min(
maxTotalLoading - this.getTilesLoading(), maxNewLoads, this.getCount());
var i, tile;
for (i = 0; i < newLoads; ++i) {
var newLoads = 0;
var tile;
while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads &&
this.getCount() > 0) {
tile = /** @type {ol.Tile} */ (this.dequeue()[0]);
goog.events.listen(tile, goog.events.EventType.CHANGE,
this.handleTileChange, false, this);
tile.load();
if (tile.getState() === ol.TileState.IDLE) {
goog.events.listen(tile, goog.events.EventType.CHANGE,
this.handleTileChange, false, this);
tile.load();
++this.tilesLoading_;
++newLoads;
}
}
this.tilesLoading_ += newLoads;
};

View File

@@ -400,7 +400,11 @@ ol.View.prototype.getState = function() {
var resolution = /** @type {number} */ (this.getResolution());
var rotation = this.getRotation();
return /** @type {olx.ViewState} */ ({
center: center.slice(),
// Snap center to closest pixel
center: [
Math.round(center[0] / resolution) * resolution,
Math.round(center[1] / resolution) * resolution
],
projection: goog.isDef(projection) ? projection : null,
resolution: resolution,
rotation: rotation

View File

@@ -12,6 +12,7 @@
"globals": {
"Buffer": false,
"__dirname": false,
"__filename": false,
"exports": true,
"module": false,
"process": false,

View File

@@ -50,6 +50,7 @@ function wrapModule(mod, callback) {
'(function() {\n' +
'var exports = {};\n' +
'var module = {exports: exports};\n' +
'var define;\n' +
'/**\n' +
' * @fileoverview\n' +
' * @suppress {accessControls, ambiguousFunctionDecl, ' +

113
tasks/serve-lib.js Normal file
View File

@@ -0,0 +1,113 @@
/**
* This task starts a dev server that provides a script loader for the
* OpenLayers library.
*/
var path = require('path');
var url = require('url');
var closure = require('closure-util');
var nomnom = require('nomnom');
var log = closure.log;
var name = path.basename(__filename, '.js');
/**
* Create a debug server for the OpenLayers and Closure Library sources.
* @param {function(Error, closure.Server)} callback Callback.
*/
var createServer = exports.createServer = function(callback) {
var server;
var manager = new closure.Manager({
lib: [
'src/**/*.js',
'build/ol.ext/*.js',
]
});
manager.on('error', function(err) {
if (server) {
log.error('serve', err.message);
} else {
callback(err);
}
});
manager.on('ready', function() {
server = new closure.Server({
manager: manager,
loader: '/loader.js'
});
callback(null, server);
});
};
/**
* Try listening for incoming connections on a range of ports.
* @param {number} min Minimum port to try.
* @param {number} max Maximum port to try.
* @param {http.Server} server The server.
* @param {function(Error)} callback Callback called with any error.
*/
function listen(min, max, server, callback) {
function _listen(port) {
server.once('error', function(err) {
if (err.code === 'EADDRINUSE') {
log.warn(name, 'Port %d in use, trying next one', port);
++port;
if (port < max) {
_listen(port);
} else {
callback(new Error('Could not find an open port'));
}
} else {
callback(err);
}
});
server.listen(port);
}
server.once('listening', function() {
callback(null);
});
_listen(min);
}
/**
* If running this module directly start the server.
*/
if (require.main === module) {
var options = nomnom.options({
port: {
abbr: 'p',
default: 3000,
help: 'Port for incoming connections (will try additional ports if used)',
metavar: 'PORT'
},
loglevel: {
abbr: 'l',
choices: ['silly', 'verbose', 'info', 'warn', 'error'],
default: 'info',
help: 'Log level',
metavar: 'LEVEL'
}
}).parse();
/** @type {string} */
log.level = options.loglevel;
log.info(name, 'Parsing dependencies.');
createServer(function(err, server) {
if (err) {
log.error(name, 'Parsing failed');
log.error(name, err.message);
process.exit(1);
}
listen(options.port, options.port + 4, server, function(err) {
if (err) {
log.error(name, 'Server failed to start: ' + err.message);
process.exit(1);
}
log.info(name, 'Debug server running http://localhost:' +
server.address().port + '/loader.js (Ctrl+C to stop)');
});
});
}

View File

@@ -31,8 +31,11 @@ function listen(min, max, server, callback) {
callback(err);
}
});
server.listen(port, '127.0.0.1', callback);
server.listen(port, '127.0.0.1');
}
server.once('listening', function() {
callback(null);
});
_listen(min);
}

View File

@@ -168,9 +168,11 @@ describe('ol.interaction.Draw', function() {
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
expect(ds).to.be.called();
expect(de).to.be.called();
simulateEvent('pointermove', 20, 20);
expect(ds).to.be.called(2);
expect(de).to.be.called(1);
expect(ds.callCount).to.be(1);
expect(de.callCount).to.be(1);
});
it('triggers drawend event before inserting the feature', function() {
@@ -325,8 +327,10 @@ describe('ol.interaction.Draw', function() {
simulateEvent('pointerup', 30, 20);
simulateEvent('pointermove', 10, 20);
expect(ds).to.be.called(1);
expect(de).to.be.called(1);
expect(ds).to.be.called();
expect(ds.callCount).to.be(1);
expect(de).to.be.called();
expect(de.callCount).to.be(1);
});
});
@@ -486,8 +490,10 @@ describe('ol.interaction.Draw', function() {
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
expect(ds).to.be.called(1);
expect(de).to.be.called(1);
expect(ds).to.be.called();
expect(ds.callCount).to.be(1);
expect(de).to.be.called();
expect(de.callCount).to.be(1);
});
});
@@ -614,8 +620,10 @@ describe('ol.interaction.Draw', function() {
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
expect(ds).to.be.called(1);
expect(de).to.be.called(1);
expect(ds).to.be.called();
expect(ds.callCount).to.be(1);
expect(de).to.be.called();
expect(de.callCount).to.be(1);
});
});

View File

@@ -18,14 +18,13 @@ describe('ol.interaction.Modify', function() {
style.height = height + 'px';
document.body.appendChild(target);
var geometry = new ol.geom.Polygon([
[[0, 0], [10, 20], [0, 40], [40, 40], [40, 0]]]);
features = [];
features.push(
new ol.Feature({
geometry: geometry
}));
features = [
new ol.Feature({
geometry: new ol.geom.Polygon([
[[0, 0], [10, 20], [0, 40], [40, 40], [40, 0]]
])
})
];
source = new ol.source.Vector({
features: features
@@ -89,10 +88,41 @@ describe('ol.interaction.Modify', function() {
});
var rbushEntries = modify.rBush_.getAll();
expect(rbushEntries.length).to.be(1);
expect(rbushEntries[0].feature === feature).to.be.ok();
expect(rbushEntries[0].feature).to.be(feature);
});
});
describe('vertex deletion', function() {
it('works when clicking on a shared vertex', function() {
features.push(features[0].clone());
var modify = new ol.interaction.Modify({
features: new ol.Collection(features)
});
map.addInteraction(modify);
var first = features[0];
var second = features[0];
expect(first.getGeometry().getRevision()).to.equal(1);
expect(first.getGeometry().getCoordinates()[0]).to.have.length(5);
expect(second.getGeometry().getRevision()).to.equal(1);
expect(second.getGeometry().getCoordinates()[0]).to.have.length(5);
simulateEvent('pointerdown', 10, -20, false, 0);
simulateEvent('pointerup', 10, -20, false, 0);
simulateEvent('click', 10, -20, false, 0);
simulateEvent('singleclick', 10, -20, false, 0);
expect(first.getGeometry().getRevision()).to.equal(2);
expect(first.getGeometry().getCoordinates()[0]).to.have.length(4);
expect(second.getGeometry().getRevision()).to.equal(2);
expect(second.getGeometry().getCoordinates()[0]).to.have.length(4);
});
});
describe('boundary modification', function() {
it('clicking vertex should delete it and +r1', function() {
@@ -106,7 +136,6 @@ describe('ol.interaction.Modify', function() {
expect(feature.getGeometry().getRevision()).to.equal(1);
expect(feature.getGeometry().getCoordinates()[0]).to.have.length(5);
simulateEvent('pointermove', 10, -20, false, 0);
simulateEvent('pointerdown', 10, -20, false, 0);
simulateEvent('pointerup', 10, -20, false, 0);
simulateEvent('click', 10, -20, false, 0);
@@ -127,7 +156,6 @@ describe('ol.interaction.Modify', function() {
expect(feature.getGeometry().getRevision()).to.equal(1);
expect(feature.getGeometry().getCoordinates()[0]).to.have.length(5);
simulateEvent('pointermove', 40, -20, false, 0);
simulateEvent('pointerdown', 40, -20, false, 0);
simulateEvent('pointerup', 40, -20, false, 0);
simulateEvent('click', 40, -20, false, 0);
@@ -137,6 +165,34 @@ describe('ol.interaction.Modify', function() {
expect(feature.getGeometry().getCoordinates()[0]).to.have.length(6);
});
it('single clicking on created vertex should delete it again', function() {
var modify = new ol.interaction.Modify({
features: new ol.Collection(features)
});
map.addInteraction(modify);
var feature = features[0];
expect(feature.getGeometry().getRevision()).to.equal(1);
expect(feature.getGeometry().getCoordinates()[0]).to.have.length(5);
simulateEvent('pointerdown', 40, -20, false, 0);
simulateEvent('pointerup', 40, -20, false, 0);
simulateEvent('click', 40, -20, false, 0);
simulateEvent('singleclick', 40, -20, false, 0);
expect(feature.getGeometry().getRevision()).to.equal(2);
expect(feature.getGeometry().getCoordinates()[0]).to.have.length(6);
simulateEvent('pointerdown', 40, -20, false, 0);
simulateEvent('pointerup', 40, -20, false, 0);
simulateEvent('click', 40, -20, false, 0);
simulateEvent('singleclick', 40, -20, false, 0);
expect(feature.getGeometry().getRevision()).to.equal(3);
expect(feature.getGeometry().getCoordinates()[0]).to.have.length(5);
});
it('clicking with drag should add vertex and +r3', function() {
var modify = new ol.interaction.Modify({
features: new ol.Collection(features)
@@ -150,6 +206,7 @@ describe('ol.interaction.Modify', function() {
simulateEvent('pointermove', 40, -20, false, 0);
simulateEvent('pointerdown', 40, -20, false, 0);
simulateEvent('pointermove', 30, -20, false, 0);
simulateEvent('pointerdrag', 30, -20, false, 0);
simulateEvent('pointerup', 30, -20, false, 0);
@@ -158,6 +215,53 @@ describe('ol.interaction.Modify', function() {
});
});
describe('double click deleteCondition', function() {
it('should delete vertex on double click', function() {
var modify = new ol.interaction.Modify({
features: new ol.Collection(features),
deleteCondition: ol.events.condition.doubleClick
});
map.addInteraction(modify);
var feature = features[0];
expect(feature.getGeometry().getRevision()).to.equal(1);
expect(feature.getGeometry().getCoordinates()[0]).to.have.length(5);
simulateEvent('pointerdown', 10, -20, false, 0);
simulateEvent('pointerup', 10, -20, false, 0);
simulateEvent('click', 10, -20, false, 0);
simulateEvent('pointerdown', 10, -20, false, 0);
simulateEvent('pointerup', 10, -20, false, 0);
simulateEvent('click', 10, -20, false, 0);
simulateEvent('dblclick', 10, -20, false, 0);
expect(feature.getGeometry().getRevision()).to.equal(2);
expect(feature.getGeometry().getCoordinates()[0]).to.have.length(4);
});
it('should do nothing on single click', function() {
var modify = new ol.interaction.Modify({
features: new ol.Collection(features),
deleteCondition: ol.events.condition.doubleClick
});
map.addInteraction(modify);
var feature = features[0];
expect(feature.getGeometry().getRevision()).to.equal(1);
expect(feature.getGeometry().getCoordinates()[0]).to.have.length(5);
simulateEvent('pointerdown', 10, -20, false, 0);
simulateEvent('pointerup', 10, -20, false, 0);
simulateEvent('click', 10, -20, false, 0);
simulateEvent('singleclick', 10, -20, false, 0);
expect(feature.getGeometry().getRevision()).to.equal(1);
expect(feature.getGeometry().getCoordinates()[0]).to.have.length(5);
});
});
});
goog.require('goog.dispose');
@@ -169,6 +273,7 @@ goog.require('ol.Feature');
goog.require('ol.Map');
goog.require('ol.MapBrowserPointerEvent');
goog.require('ol.View');
goog.require('ol.events.condition');
goog.require('ol.geom.Point');
goog.require('ol.geom.Polygon');
goog.require('ol.interaction.Modify');

View File

@@ -14,6 +14,58 @@ describe('ol.renderer.canvas.Map', function() {
});
describe('#forEachFeatureAtCoordinate', function() {
var layer, map, target;
beforeEach(function() {
target = document.createElement('div');
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new ol.Map({
target: target,
view: new ol.View({
center: [0, 0],
zoom: 0
})
});
layer = new ol.layer.Vector({
source: new ol.source.Vector({
features: [
new ol.Feature({
geometry: new ol.geom.Point([0, 0])
})
]
})
});
});
afterEach(function() {
map.setTarget(null);
document.body.removeChild(target);
});
it('always includes unmanaged layers', function() {
layer.setMap(map);
map.renderSync();
var cb = sinon.spy();
map.forEachFeatureAtPixel(map.getPixelFromCoordinate([0, 0]), cb, null,
function() { return false; });
expect(cb).to.be.called();
});
it('filters managed layers', function() {
map.addLayer(layer);
map.renderSync();
cb = sinon.spy();
map.forEachFeatureAtPixel(map.getPixelFromCoordinate([0, 0]), cb, null,
function() { return false; });
expect(cb).to.not.be.called();
});
});
describe('#renderFrame()', function() {
var layer, map, renderer;
@@ -36,8 +88,11 @@ describe('ol.renderer.canvas.Map', function() {
});
goog.require('ol.layer.Vector');
goog.require('ol.Feature');
goog.require('ol.Map');
goog.require('ol.View');
goog.require('ol.geom.Point');
goog.require('ol.layer.Vector');
goog.require('ol.renderer.canvas.Layer');
goog.require('ol.renderer.canvas.Map');
goog.require('ol.source.Vector');

View File

@@ -0,0 +1,307 @@
goog.provide('ol.test.source.RasterSource');
var red = 'data:image/gif;base64,R0lGODlhAQABAPAAAP8AAP///yH5BAAAAAAALAAAAAA' +
'BAAEAAAICRAEAOw==';
var green = 'data:image/gif;base64,R0lGODlhAQABAPAAAAD/AP///yH5BAAAAAAALAAAA' +
'AABAAEAAAICRAEAOw==';
var blue = 'data:image/gif;base64,R0lGODlhAQABAPAAAAAA/////yH5BAAAAAAALAAAAA' +
'ABAAEAAAICRAEAOw==';
function itNoPhantom() {
if (window.mochaPhantomJS) {
return xit.apply(this, arguments);
} else {
return it.apply(this, arguments);
}
}
describe('ol.source.Raster', function() {
var target, map, redSource, greenSource, blueSource;
beforeEach(function() {
target = document.createElement('div');
var style = target.style;
style.position = 'absolute';
style.left = '-1000px';
style.top = '-1000px';
style.width = '2px';
style.height = '2px';
document.body.appendChild(target);
var extent = [-1, -1, 1, 1];
redSource = new ol.source.ImageStatic({
url: red,
imageExtent: extent
});
greenSource = new ol.source.ImageStatic({
url: green,
imageExtent: extent
});
blueSource = new ol.source.ImageStatic({
url: blue,
imageExtent: extent
});
raster = new ol.source.Raster({
threads: 0,
sources: [redSource, greenSource, blueSource],
operation: function(inputs) {
return inputs[0];
}
});
map = new ol.Map({
target: target,
view: new ol.View({
resolutions: [1],
projection: new ol.proj.Projection({
code: 'image',
units: 'pixels',
extent: extent
})
}),
layers: [
new ol.layer.Image({
source: raster
})
]
});
});
afterEach(function() {
goog.dispose(map);
document.body.removeChild(target);
});
describe('constructor', function() {
it('returns a tile source', function() {
var source = new ol.source.Raster({
threads: 0,
sources: [new ol.source.Tile({})]
});
expect(source).to.be.a(ol.source.Source);
expect(source).to.be.a(ol.source.Raster);
});
itNoPhantom('defaults to "pixel" operation', function(done) {
var log = [];
raster = new ol.source.Raster({
threads: 0,
sources: [redSource, greenSource, blueSource],
operation: function(inputs) {
log.push(inputs);
return inputs[0];
}
});
raster.on('afteroperations', function() {
expect(log.length).to.equal(4);
var inputs = log[0];
var pixel = inputs[0];
expect(pixel).to.be.an('array');
done();
});
map.getLayers().item(0).setSource(raster);
var view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
});
itNoPhantom('allows operation type to be set to "image"', function(done) {
var log = [];
raster = new ol.source.Raster({
operationType: ol.raster.OperationType.IMAGE,
threads: 0,
sources: [redSource, greenSource, blueSource],
operation: function(inputs) {
log.push(inputs);
return inputs[0];
}
});
raster.on('afteroperations', function() {
expect(log.length).to.equal(1);
var inputs = log[0];
expect(inputs[0]).to.be.an(ImageData);
done();
});
map.getLayers().item(0).setSource(raster);
var view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
});
});
describe('#setOperation()', function() {
itNoPhantom('allows operation to be set', function(done) {
var count = 0;
raster.setOperation(function(pixels) {
++count;
var redPixel = pixels[0];
var greenPixel = pixels[1];
var bluePixel = pixels[2];
expect(redPixel).to.eql([255, 0, 0, 255]);
expect(greenPixel).to.eql([0, 255, 0, 255]);
expect(bluePixel).to.eql([0, 0, 255, 255]);
return pixels[0];
});
var view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
raster.on('afteroperations', function(event) {
expect(count).to.equal(4);
done();
});
});
itNoPhantom('updates and re-runs the operation', function(done) {
var view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
var count = 0;
raster.on('afteroperations', function(event) {
++count;
if (count === 1) {
raster.setOperation(function(inputs) {
return inputs[0];
});
} else {
done();
}
});
});
});
describe('beforeoperations', function() {
itNoPhantom('gets called before operations are run', function(done) {
var count = 0;
raster.setOperation(function(inputs) {
++count;
return inputs[0];
});
raster.on('beforeoperations', function(event) {
expect(count).to.equal(0);
expect(!!event).to.be(true);
expect(event.extent).to.be.an('array');
expect(event.resolution).to.be.a('number');
expect(event.data).to.be.an('object');
done();
});
var view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
});
itNoPhantom('allows data to be set for the operation', function(done) {
raster.setOperation(function(inputs, data) {
++data.count;
return inputs[0];
});
raster.on('beforeoperations', function(event) {
event.data.count = 0;
});
raster.on('afteroperations', function(event) {
expect(event.data.count).to.equal(4);
done();
});
var view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
});
});
describe('afteroperations', function() {
itNoPhantom('gets called after operations are run', function(done) {
var count = 0;
raster.setOperation(function(inputs) {
++count;
return inputs[0];
});
raster.on('afteroperations', function(event) {
expect(count).to.equal(4);
expect(!!event).to.be(true);
expect(event.extent).to.be.an('array');
expect(event.resolution).to.be.a('number');
expect(event.data).to.be.an('object');
done();
});
var view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
});
itNoPhantom('receives data set by the operation', function(done) {
raster.setOperation(function(inputs, data) {
data.message = 'hello world';
return inputs[0];
});
raster.on('afteroperations', function(event) {
expect(event.data.message).to.equal('hello world');
done();
});
var view = map.getView();
view.setCenter([0, 0]);
view.setZoom(0);
});
});
});
goog.require('ol.Map');
goog.require('ol.View');
goog.require('ol.layer.Image');
goog.require('ol.proj.Projection');
goog.require('ol.raster.OperationType');
goog.require('ol.source.Image');
goog.require('ol.source.ImageStatic');
goog.require('ol.source.Raster');
goog.require('ol.source.Source');
goog.require('ol.source.Tile');

View File

@@ -447,6 +447,53 @@ describe('ol.source.Vector', function() {
});
describe('with a collection of features', function() {
var collection, source;
beforeEach(function() {
source = new ol.source.Vector({
useSpatialIndex: false
});
collection = source.getFeaturesCollection();
});
it('creates a features collection', function() {
expect(source.getFeaturesCollection()).to.not.be(null);
});
it('adding/removing features keeps the collection in sync', function() {
var feature = new ol.Feature();
source.addFeature(feature);
expect(collection.getLength()).to.be(1);
source.removeFeature(feature);
expect(collection.getLength()).to.be(0);
});
it('#clear() features keeps the collection in sync', function() {
var feature = new ol.Feature();
source.addFeatures([feature]);
expect(collection.getLength()).to.be(1);
source.clear();
expect(collection.getLength()).to.be(0);
source.addFeatures([feature]);
expect(collection.getLength()).to.be(1);
source.clear(true);
expect(collection.getLength()).to.be(0);
});
it('keeps the source\'s features in sync with the collection', function() {
var feature = new ol.Feature();
collection.push(feature);
expect(source.getFeatures().length).to.be(1);
collection.remove(feature);
expect(source.getFeatures().length).to.be(0);
collection.extend([feature]);
expect(source.getFeatures().length).to.be(1);
collection.clear();
expect(source.getFeatures().length).to.be(0);
});
});
describe('with a collection of features plus spatial index', function() {
var collection, source;
beforeEach(function() {
collection = new ol.Collection();

View File

@@ -76,6 +76,16 @@ describe('ol.source.WMTS', function() {
expect(options.dimensions).to.eql({});
});
it('can find a MatrixSet by SRS identifier', function() {
var options = ol.source.WMTS.optionsFromCapabilities(
capabilities,
{ layer: 'BlueMarbleNextGeneration', projection: 'EPSG:3857',
requestEncoding: 'REST' });
expect(options.matrixSet).to.be.eql('google3857');
});
});
describe('when creating tileUrlFunction', function() {

View File

@@ -8,7 +8,7 @@ describe('ol.structs.PriorityQueue', function() {
var pq;
beforeEach(function() {
pq = new ol.structs.PriorityQueue(
goog.identityFunction, goog.identityFunction);
goog.functions.identity, goog.functions.identity);
});
it('is valid', function() {
@@ -54,7 +54,7 @@ describe('ol.structs.PriorityQueue', function() {
beforeEach(function() {
elements = [];
pq = new ol.structs.PriorityQueue(
goog.identityFunction, goog.identityFunction);
goog.functions.identity, goog.functions.identity);
var element, i;
for (i = 0; i < 32; ++i) {
element = Math.random();
@@ -81,7 +81,7 @@ describe('ol.structs.PriorityQueue', function() {
target = 0.5;
pq = new ol.structs.PriorityQueue(function(element) {
return Math.abs(element - target);
}, goog.identityFunction);
}, goog.functions.identity);
var i;
for (i = 0; i < 32; ++i) {
pq.enqueue(Math.random());
@@ -138,7 +138,7 @@ describe('ol.structs.PriorityQueue', function() {
var pq;
beforeEach(function() {
pq = new ol.structs.PriorityQueue(
goog.identityFunction, goog.identityFunction);
goog.functions.identity, goog.functions.identity);
pq.enqueue('a');
pq.enqueue('b');
pq.enqueue('c');
@@ -183,4 +183,5 @@ describe('ol.structs.PriorityQueue', function() {
});
goog.require('goog.functions');
goog.require('ol.structs.PriorityQueue');

View File

@@ -13,6 +13,73 @@ describe('ol.TileQueue', function() {
}
}
var tileId = 0;
function createImageTile() {
++tileId;
var tileCoord = [tileId, tileId, tileId];
var state = ol.TileState.IDLE;
var src = 'data:image/gif;base64,R0lGODlhAQABAPAAAP8AAP///' +
'yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==#' + tileId;
return new ol.ImageTile(tileCoord, state, src, null,
ol.source.Image.defaultImageLoadFunction);
}
describe('#loadMoreTiles()', function() {
var noop = function() {};
it('works when tile queues share tiles', function(done) {
var q1 = new ol.TileQueue(noop, noop);
var q2 = new ol.TileQueue(noop, noop);
var numTiles = 20;
for (var i = 0; i < numTiles; ++i) {
var tile = createImageTile();
q1.enqueue([tile]);
q2.enqueue([tile]);
}
// Initially, both have all tiles.
expect(q1.getCount()).to.equal(numTiles);
expect(q2.getCount()).to.equal(numTiles);
var maxLoading = numTiles / 2;
// and nothing is loading
expect(q1.getTilesLoading()).to.equal(0);
expect(q2.getTilesLoading()).to.equal(0);
// ask both to load
q1.loadMoreTiles(maxLoading, maxLoading);
q2.loadMoreTiles(maxLoading, maxLoading);
// both tiles will be loading the max
expect(q1.getTilesLoading()).to.equal(maxLoading);
expect(q2.getTilesLoading()).to.equal(maxLoading);
// the second queue will be empty now
expect(q1.getCount()).to.equal(numTiles - maxLoading);
expect(q2.getCount()).to.equal(0);
// let all tiles load
setTimeout(function() {
expect(q1.getTilesLoading()).to.equal(0);
expect(q2.getTilesLoading()).to.equal(0);
// ask both to load, this should clear q1
q1.loadMoreTiles(maxLoading, maxLoading);
q2.loadMoreTiles(maxLoading, maxLoading);
expect(q1.getCount()).to.equal(0);
expect(q2.getCount()).to.equal(0);
done();
}, 20);
});
});
describe('heapify', function() {
it('does convert an arbitrary array into a heap', function() {
@@ -56,6 +123,9 @@ describe('ol.TileQueue', function() {
});
});
goog.require('ol.ImageTile');
goog.require('ol.Tile');
goog.require('ol.TileState');
goog.require('ol.TileQueue');
goog.require('ol.source.Image');
goog.require('ol.structs.PriorityQueue');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB