Compare commits
172 Commits
v6.0.0-bet
...
v6.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7853ad839b | ||
|
|
a1026a50bd | ||
|
|
c117ec79f7 | ||
|
|
b37eb29444 | ||
|
|
4df545dfb0 | ||
|
|
70f5822217 | ||
|
|
d49a41def0 | ||
|
|
e4941f60db | ||
|
|
d1fe22a872 | ||
|
|
5756003c88 | ||
|
|
e5e2129a88 | ||
|
|
c5e6728d6d | ||
|
|
d9b3986c95 | ||
|
|
a67f8e3f38 | ||
|
|
7989991da9 | ||
|
|
afa70e5a1e | ||
|
|
ce9ea5cc93 | ||
|
|
5f5cdc9645 | ||
|
|
6370426d52 | ||
|
|
fb7bd2158d | ||
|
|
387b4188f2 | ||
|
|
c922d4eb8f | ||
|
|
9d4eda2318 | ||
|
|
bd235b7b49 | ||
|
|
7daba05548 | ||
|
|
64e67ae351 | ||
|
|
40ea2a8b7e | ||
|
|
4629fe5028 | ||
|
|
ba2c558b72 | ||
|
|
84f1e0c66e | ||
|
|
72e41d3703 | ||
|
|
6775723840 | ||
|
|
d332b6a0f4 | ||
|
|
f68b8d8df9 | ||
|
|
c2cbae95c6 | ||
|
|
b36ad87cb5 | ||
|
|
b7c004f95c | ||
|
|
7bfaa3b6ad | ||
|
|
0cffee6f83 | ||
|
|
3895a59c5e | ||
|
|
a2b39c9c53 | ||
|
|
12154d98b4 | ||
|
|
c25dba415f | ||
|
|
27d943dcc3 | ||
|
|
66b5b5d7e1 | ||
|
|
3cf9b5aa28 | ||
|
|
b77177ed74 | ||
|
|
f61562a51a | ||
|
|
e4a531de8c | ||
|
|
063bc51c59 | ||
|
|
3f3fbe7e4b | ||
|
|
01a6381756 | ||
|
|
0ab7ad741f | ||
|
|
58efe1f850 | ||
|
|
66b41a53b8 | ||
|
|
c00400c500 | ||
|
|
944af1fe80 | ||
|
|
84db84bc23 | ||
|
|
f366eaea52 | ||
|
|
f5ae41f03e | ||
|
|
6b1bce2b9d | ||
|
|
bd57128ca4 | ||
|
|
5012e4987c | ||
|
|
6e3d3d4877 | ||
|
|
d30f6175ce | ||
|
|
0e08d9e0fd | ||
|
|
4973281f3c | ||
|
|
8f3820be22 | ||
|
|
033a18bc1c | ||
|
|
99fb0bf57e | ||
|
|
49a9f21388 | ||
|
|
e4d25400ee | ||
|
|
1e350677fb | ||
|
|
86f304ae02 | ||
|
|
00c09eb281 | ||
|
|
6c8c8a6477 | ||
|
|
1142caf5e8 | ||
|
|
ba6ac43a28 | ||
|
|
12289b8ef9 | ||
|
|
f69c37566e | ||
|
|
6cfd0b70ed | ||
|
|
d95bcc8594 | ||
|
|
d3e6f4c3b2 | ||
|
|
eb21369d86 | ||
|
|
68a29f1ac6 | ||
|
|
6413badada | ||
|
|
d6485b1e94 | ||
|
|
98a5f552b2 | ||
|
|
067260170b | ||
|
|
9850c0134e | ||
|
|
284c1dff83 | ||
|
|
d166c79242 | ||
|
|
e4f377fb46 | ||
|
|
e22af6dbd6 | ||
|
|
0f998b4522 | ||
|
|
2cdfcf8b21 | ||
|
|
b3e770226f | ||
|
|
981328576f | ||
|
|
3b1a415d6b | ||
|
|
fde36b237e | ||
|
|
dde9c59021 | ||
|
|
a12b323907 | ||
|
|
1687f9bb84 | ||
|
|
9a12a668a2 | ||
|
|
b98268ffee | ||
|
|
4d1ae3d483 | ||
|
|
2f6f110fa3 | ||
|
|
93c25d4f82 | ||
|
|
2ac1095c52 | ||
|
|
d5b868f4a6 | ||
|
|
17081fac46 | ||
|
|
4a18b57021 | ||
|
|
58c4c9ebb8 | ||
|
|
a16234faaa | ||
|
|
154265a2d9 | ||
|
|
440ae897a5 | ||
|
|
86fac0937b | ||
|
|
682102d4d6 | ||
|
|
22774b4821 | ||
|
|
046e73e785 | ||
|
|
a71a823676 | ||
|
|
3ba7ecc602 | ||
|
|
887d8e8a90 | ||
|
|
251fc79484 | ||
|
|
fc0ed7b96f | ||
|
|
752b69680e | ||
|
|
756f63e212 | ||
|
|
84a82ea5b2 | ||
|
|
08dd5f58a2 | ||
|
|
04963c83d6 | ||
|
|
711dacf4b7 | ||
|
|
6fbd196132 | ||
|
|
5969ac31ea | ||
|
|
21c51ff784 | ||
|
|
3d10e92218 | ||
|
|
84371fe4be | ||
|
|
1995164219 | ||
|
|
4f128a1ec0 | ||
|
|
f11744da56 | ||
|
|
08434ed3d5 | ||
|
|
2c76d7531b | ||
|
|
bbec7d76d4 | ||
|
|
255c4b34ba | ||
|
|
fd220f9cce | ||
|
|
34d68aadbd | ||
|
|
3bc822e8ef | ||
|
|
7340e865a3 | ||
|
|
5b231fe990 | ||
|
|
9c55256de2 | ||
|
|
491020f027 | ||
|
|
2989c84248 | ||
|
|
db1515a9f3 | ||
|
|
a774b1a278 | ||
|
|
81865f70e4 | ||
|
|
c6db2d07bb | ||
|
|
91215b303e | ||
|
|
319a905ec0 | ||
|
|
9883b7d3d9 | ||
|
|
07e31840eb | ||
|
|
03483b5439 | ||
|
|
9a42ab73d8 | ||
|
|
746f92d597 | ||
|
|
238fbca650 | ||
|
|
3875147812 | ||
|
|
be065cdacc | ||
|
|
187f58c1c3 | ||
|
|
c20bdedcac | ||
|
|
617dd9f031 | ||
|
|
077afac90a | ||
|
|
f091d96b6c | ||
|
|
a30aa78726 | ||
|
|
66f49559ee |
@@ -27,6 +27,10 @@ jobs:
|
||||
name: Run Tests
|
||||
command: npm test
|
||||
|
||||
- store_artifacts:
|
||||
path: coverage/
|
||||
destination: coverage
|
||||
|
||||
- store_artifacts:
|
||||
path: rendering/cases/
|
||||
destination: rendering
|
||||
@@ -38,3 +42,11 @@ jobs:
|
||||
- store_artifacts:
|
||||
path: build/examples
|
||||
destination: examples
|
||||
|
||||
- run:
|
||||
name: Build API Docs
|
||||
command: npm run apidoc
|
||||
|
||||
- store_artifacts:
|
||||
path: build/apidoc
|
||||
destination: apidoc
|
||||
|
||||
@@ -4,6 +4,16 @@
|
||||
|
||||
#### Backwards incompatible changes
|
||||
|
||||
#### Removal of optional this arguments
|
||||
|
||||
The optional this (i.e. opt_this) arguments were removed from the following methods.
|
||||
Please use closures, the es6 arrow function or the bind method to achieve this effect (Bind is explained here:
|
||||
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
|
||||
|
||||
* `forEachCorner` in `ol/extent`
|
||||
* `LRUCache#forEach`
|
||||
* `RBush#forEach` and `RBush#forEachInExtent`
|
||||
|
||||
##### The `setCenter`, `setZoom`, `setResolution` and `setRotation` methods on `ol/View` do not bypass constraints anymore
|
||||
|
||||
Previously, these methods allowed setting values that were inconsistent with the given view constraints.
|
||||
@@ -88,6 +98,14 @@ If you were previously using `VectorTile` layers with `renderMode: 'vector'`, yo
|
||||
|
||||
If you were previously using `Vector` layers with `renderMode: 'image'`, you have to remove this configuration option. Instead, use the new `ol/layer/VectorImage` layer with your `ol/source/Vector`.
|
||||
|
||||
##### New declutter behavior
|
||||
|
||||
If a map has more than one layer with `declutter` set to true, decluttering now considers all `Vector` and `VectorTile` layers, instead of decluttering each layer separately. Only `VectorImage` layers continue to be decluttered separately. The higher the z-index of a layer, the higher the priority of its decluttered items.
|
||||
|
||||
Within a layer, the declutter order has changed. Previously, styles with a lower `zIndex` were prioritized over those with a higher `zIndex`. Now the opposite order is used.
|
||||
|
||||
On vector layers, even if decluttered images or texts have a lower z-Index than polygons or lines, they will now be rendered on top of the polygons or lines. For vector tile layers, this was the case already in previous releases.
|
||||
|
||||
##### New `prerender` and `postrender` layer events replace old `precompose`, `render` and `postcompose` events
|
||||
|
||||
If you were previously registering for `precompose` and `postcompose` events, you should now register for `prerender` and `postrender` events on layers. Instead of the previous `render` event, you should now listen for `postrender`. Layers are no longer composed to a single Canvas element. Instead, they are added to the map viewport as individual elements.
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
<table><tr>
|
||||
<th width="33.3%">Map</th><th width="33.3%">View</th><th width="33.3%">Layers</th>
|
||||
</tr><tr>
|
||||
<td><p>A [map](module-ol_Map-Map.html) is made of [layers](module-ol_layer_Base-BaseLayer.html), a [view](module-ol_View-View.html) to visualize them, [interactions](module-ol_interaction_Interaction-Interaction.html) to modify map content and [controls](module-ol_control_Control-Control.html) with UI components.</p>
|
||||
[Overview](module-ol_Map-Map.html)<br>
|
||||
[Creation](module-ol_Map-Map.html#Map)<br>
|
||||
[Events](module-ol_MapBrowserEvent-MapBrowserEvent.html)</td>
|
||||
<td><p>A <a href="module-ol_Map-Map.html">map</a> is made of <a href="module-ol_layer_Base-BaseLayer.html">layers</a>, a <a href="module-ol_View-View.html">view</a> to visualize them, <a href="module-ol_interaction_Interaction-Interaction.html">interactions</a> to modify map content and <a href="module-ol_control_Control-Control.html">controls</a> with UI components.</p>
|
||||
<a href="module-ol_Map-Map.html">Overview</a><br>
|
||||
<a href="module-ol_Map-Map.html#Map">Creation</a><br>
|
||||
<a href="module-ol_MapBrowserEvent-MapBrowserEvent.html">Events</a></td>
|
||||
<td><p>The view manages the visual parameters of the map view, like resolution or rotation.</p>
|
||||
[View](module-ol_View-View.html) with center, projection, resolution and rotation</td>
|
||||
<td><p>Layers are lightweight containers that get their data from [sources](module-ol_source_Source-Source.html).</p>
|
||||
[ol/layer/Tile](module-ol_layer_Tile-TileLayer.html)<br>
|
||||
[ol/layer/Image](module-ol_layer_Image-ImageLayer.html)<br>
|
||||
[ol/layer/Vector](module-ol_layer_Vector-VectorLayer.html)<br>
|
||||
[ol/layer/VectorTile](module-ol_layer_VectorTile-VectorTileLayer.html)</td>
|
||||
<a href="module-ol_View-View.html">View</a> with center, projection, resolution and rotation</td>
|
||||
<td><p>Layers are lightweight containers that get their data from <a href="module-ol_source_Source-Source.html">sources</a>.</p>
|
||||
<a href="module-ol_layer_Tile-TileLayer.html">ol/layer/Tile</a><br>
|
||||
<a href="module-ol_layer_Image-ImageLayer.html">ol/layer/Image</a><br>
|
||||
<a href="module-ol_layer_Vector-VectorLayer.html">ol/layer/Vector</a><br>
|
||||
<a href="module-ol_layer_VectorTile-VectorTileLayer.html">ol/layer/VectorTile</a></td>
|
||||
</tr><tr>
|
||||
<th>Controls</th><th>Interactions</th><th>Sources and formats</th>
|
||||
</tr><tr>
|
||||
<td>[Map default controls](module-ol_control_util.html#.defaults)<br>
|
||||
[All controls](module-ol_control_Control-Control.html)
|
||||
<td><a href="module-ol_control_util.html#.defaults">Map default controls</a><br>
|
||||
<a href="module-ol_control_Control-Control.html">All controls</a>
|
||||
</td>
|
||||
<td>
|
||||
[Map default interactions](module-ol_interaction.html#~defaults)<br>
|
||||
Interactions for [vector features](module-ol_Feature-Feature.html)
|
||||
<ul><li>[ol/interaction/Select](module-ol_interaction_Select-Select.html)</li>
|
||||
<li>[ol/interaction/Draw](module-ol_interaction_Draw-Draw.html)</li>
|
||||
<li>[ol/interaction/Modify](module-ol_interaction_Modify-Modify.html)</li></ul>
|
||||
[All interactions](module-ol_interaction_Interaction-Interaction.html)</td>
|
||||
<td>[Tile sources](module-ol_source_Tile-TileSource.html) for [ol/layer/Tile](module-ol_layer_Tile-TileLayer.html)
|
||||
<br>[Image sources](module-ol_source_Image-ImageSource.html) for [ol/layer/Image](module-ol_layer_Image-ImageLayer.html)
|
||||
<br>[Vector sources](module-ol_source_Vector-VectorSource.html) for [ol/layer/Vector](module-ol_layer_Vector-VectorLayer.html)
|
||||
<br>[Vector tile sources](module-ol_source_VectorTile-VectorTile.html) for [ol/layer/VectorTile](module-ol_layer_VectorTile-VectorTileLayer.html)
|
||||
<br>[Formats](module-ol_format_Feature-FeatureFormat.html) for reading/writing vector data
|
||||
<br>[ol/format/WMSCapabilities](module-ol_format_WMSCapabilities-WMSCapabilities.html)</td></tr>
|
||||
<a href="module-ol_interaction.html#~defaults">Map default interactions</a><br>
|
||||
Interactions for <a href="module-ol_Feature-Feature.html">vector features</a>
|
||||
<ul><li><a href="module-ol_interaction_Select-Select.html">ol/interaction/Select</a></li>
|
||||
<li><a href="module-ol_interaction_Draw-Draw.html">ol/interaction/Draw</a></li>
|
||||
<li><a href="module-ol_interaction_Modify-Modify.html">ol/interaction/Modify</a></li></ul>
|
||||
<a href="module-ol_interaction_Interaction-Interaction.html">All interactions</a></td>
|
||||
<td><a href="module-ol_source_Tile-TileSource.html">Tile sources</a> for <a href="module-ol_layer_Tile-TileLayer.html">ol/layer/Tile</a>
|
||||
<br><a href="module-ol_source_Image-ImageSource.html">Image sources</a> for <a href="module-ol_layer_Image-ImageLayer.html">ol/layer/Image</a>
|
||||
<br><a href="module-ol_source_Vector-VectorSource.html">Vector sources</a> for <a href="module-ol_layer_Vector-VectorLayer.html">ol/layer/Vector</a>
|
||||
<br><a href="module-ol_source_VectorTile-VectorTile.html">Vector tile sources</a> for <a href="module-ol_layer_VectorTile-VectorTileLayer.html">ol/layer/VectorTile</a>
|
||||
<br><a href="module-ol_format_Feature-FeatureFormat.html">Formats</a> for reading/writing vector data
|
||||
<br><a href="module-ol_format_WMSCapabilities-WMSCapabilities.html">ol/format/WMSCapabilities</a></td></tr>
|
||||
<tr><th>Projections</th><th>Observable objects</th><th>Other components</th></tr>
|
||||
<tr><td><p>All coordinates and extents need to be provided in view projection (default: EPSG:3857). To transform, use [ol/proj#transform()](module-ol_proj.html#.transform) and [ol/proj#transformExtent()](module-ol_proj.html#.transformExtent).</p>
|
||||
[ol/proj](module-ol_proj.html)</td>
|
||||
<td><p>Changes to all [ol/Object](module-ol_Object-BaseObject.html)s can be observed by calling the [object.on('propertychange')](module-ol_Object-BaseObject.html#on) method. Listeners receive an [ol/Object.ObjectEvent](module-ol_Object-ObjectEvent.html) with information on the changed property and old value.</p>
|
||||
<tr><td><p>All coordinates and extents need to be provided in view projection (default: EPSG:3857). To transform, use <a href="module-ol_proj.html#.transform">ol/proj#transform()</a> and <a href="module-ol_proj.html#.transformExtent">ol/proj#transformExtent()</a>.</p>
|
||||
<a href="module-ol_proj.html">ol/proj</a></td>
|
||||
<td><p>Changes to all <a href="module-ol_Object-BaseObject.html">ol/Object</a>s can be observed by calling the <a href="module-ol_Object-BaseObject.html#on">object.on('propertychange')</a> method. Listeners receive an <a href="module-ol_Object-ObjectEvent.html">ol/Object.ObjectEvent</a> with information on the changed property and old value.</p>
|
||||
<td>
|
||||
[ol/Geolocation](module-ol_Geolocation.html)<br>
|
||||
[ol/Overlay](module-ol_Overlay-Overlay.html)<br></td>
|
||||
<a href="module-ol_Geolocation.html">ol/Geolocation</a><br>
|
||||
<a href="module-ol_Overlay-Overlay.html">ol/Overlay</a><br></td>
|
||||
</tr></table>
|
||||
|
||||
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
const events = {};
|
||||
const classes = {};
|
||||
|
||||
exports.handlers = {
|
||||
|
||||
newDoclet: function(e) {
|
||||
const doclet = e.doclet;
|
||||
let cls;
|
||||
if (doclet.kind == 'event') {
|
||||
cls = doclet.longname.split('#')[0];
|
||||
if (!(cls in events)) {
|
||||
events[cls] = [];
|
||||
}
|
||||
events[cls].push(doclet.longname);
|
||||
} else if (doclet.kind == 'class' && !(doclet.longname in classes)) {
|
||||
classes[doclet.longname] = doclet;
|
||||
if (doclet.kind !== 'event') {
|
||||
return;
|
||||
}
|
||||
|
||||
const cls = doclet.longname.split('#')[0];
|
||||
if (!(cls in events)) {
|
||||
events[cls] = [];
|
||||
}
|
||||
events[cls].push(doclet.longname);
|
||||
},
|
||||
|
||||
parseComplete: function(e) {
|
||||
const doclets = e.doclets;
|
||||
let doclet, i, ii, j, jj, event, fires;
|
||||
for (i = 0, ii = doclets.length - 1; i < ii; ++i) {
|
||||
doclet = doclets[i];
|
||||
for (let i = 0, ii = doclets.length - 1; i < ii; ++i) {
|
||||
const doclet = doclets[i];
|
||||
if (doclet.fires) {
|
||||
if (doclet.kind == 'class') {
|
||||
fires = [];
|
||||
for (j = 0, jj = doclet.fires.length; j < jj; ++j) {
|
||||
event = doclet.fires[j].replace('event:', '');
|
||||
const fires = [];
|
||||
for (let j = 0, jj = doclet.fires.length; j < jj; ++j) {
|
||||
const event = doclet.fires[j].replace('event:', '');
|
||||
if (events[event]) {
|
||||
fires.push.apply(fires, events[event]);
|
||||
} else if (doclet.fires[j] !== 'event:ObjectEvent') {
|
||||
|
||||
@@ -220,3 +220,11 @@ Duplicate item added to a unique collection. For example, it may be that you tr
|
||||
### 59
|
||||
|
||||
Invalid command found in the PBF. This indicates that the loaded vector tile may be corrupt.
|
||||
|
||||
### 60
|
||||
|
||||
Missing or invalid `size`.
|
||||
|
||||
### 61
|
||||
|
||||
Cannot determine IIIF Image API version from provided image information JSON.
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"common": false,
|
||||
"createMapboxStreetsV6Style": false,
|
||||
"d3": false,
|
||||
"domtoimage": false,
|
||||
"geojsonvt": false,
|
||||
"GyroNorm": false,
|
||||
"jsPDF": false,
|
||||
|
||||
@@ -100,8 +100,7 @@ function xyz2rgb(x) {
|
||||
|
||||
const raster = new RasterSource({
|
||||
sources: [new Stamen({
|
||||
layer: 'watercolor',
|
||||
transition: 0
|
||||
layer: 'watercolor'
|
||||
})],
|
||||
operation: function(pixels, data) {
|
||||
const hcl = rgb2hcl(pixels[0]);
|
||||
|
||||
@@ -40,6 +40,6 @@ gn.init().then(function() {
|
||||
center[0] -= resolution * gamma * 25;
|
||||
center[1] += resolution * beta * 25;
|
||||
|
||||
view.setCenter(view.constrainCenter(center));
|
||||
view.setCenter(center);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,11 +3,9 @@ layout: example.html
|
||||
title: Map Export
|
||||
shortdesc: Example of exporting a map as a PNG image.
|
||||
docs: >
|
||||
Example of exporting a map as a PNG image. This example use the <a href="https://www.npmjs.com/package/dom-to-image-more">dom-to-image-more</a>
|
||||
Example of exporting a map as a PNG image. This example use the <a href="https://www.npmjs.com/package/html-to-image">html-to-image</a>
|
||||
library.
|
||||
tags: "export, png, openstreetmap"
|
||||
resources:
|
||||
- https://unpkg.com/dom-to-image-more@2.7.1/dist/dom-to-image-more.min.js
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
<a id="export-png" class="btn btn-default"><i class="fa fa-download"></i> Download PNG</a>
|
||||
|
||||
@@ -4,6 +4,8 @@ import GeoJSON from '../src/ol/format/GeoJSON.js';
|
||||
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
||||
import {OSM, Vector as VectorSource} from '../src/ol/source.js';
|
||||
|
||||
import {toPng} from 'html-to-image';
|
||||
|
||||
const map = new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
@@ -23,9 +25,17 @@ const map = new Map({
|
||||
})
|
||||
});
|
||||
|
||||
// export options for html-to-image.
|
||||
// See: https://github.com/bubkoo/html-to-image#options
|
||||
const exportOptions = {
|
||||
filter: function(element) {
|
||||
return element.className.indexOf('ol-control') === -1;
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementById('export-png').addEventListener('click', function() {
|
||||
map.once('rendercomplete', function() {
|
||||
domtoimage.toPng(map.getViewport().querySelector('.ol-layers'))
|
||||
toPng(map.getTargetElement(), exportOptions)
|
||||
.then(function(dataURL) {
|
||||
const link = document.getElementById('image-download');
|
||||
link.href = dataURL;
|
||||
|
||||
@@ -7,7 +7,6 @@ docs: >
|
||||
tags: "export, pdf, openstreetmap"
|
||||
resources:
|
||||
- https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.2.61/jspdf.min.js
|
||||
- https://unpkg.com/dom-to-image-more@2.7.1/dist/dom-to-image-more.min.js
|
||||
---
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
|
||||
@@ -4,6 +4,8 @@ import WKT from '../src/ol/format/WKT.js';
|
||||
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
||||
import {OSM, Vector as VectorSource} from '../src/ol/source.js';
|
||||
|
||||
import {toJpeg} from 'html-to-image';
|
||||
|
||||
const raster = new TileLayer({
|
||||
source: new OSM()
|
||||
});
|
||||
@@ -41,6 +43,15 @@ const dims = {
|
||||
a5: [210, 148]
|
||||
};
|
||||
|
||||
|
||||
// export options for html-to-image.
|
||||
// See: https://github.com/bubkoo/html-to-image#options
|
||||
const exportOptions = {
|
||||
filter: function(element) {
|
||||
return element.className.indexOf('ol-control') === -1;
|
||||
}
|
||||
};
|
||||
|
||||
const exportButton = document.getElementById('export-pdf');
|
||||
|
||||
exportButton.addEventListener('click', function() {
|
||||
@@ -57,15 +68,14 @@ exportButton.addEventListener('click', function() {
|
||||
const extent = map.getView().calculateExtent(size);
|
||||
|
||||
map.once('rendercomplete', function() {
|
||||
domtoimage.toJpeg(map.getViewport().querySelector('.ol-layers')).then(function(dataUrl) {
|
||||
toJpeg(map.getTargetElement(), exportOptions).then(function(dataUrl) {
|
||||
const pdf = new jsPDF('landscape', undefined, format);
|
||||
pdf.addImage(dataUrl, 'JPEG', 0, 0, dim[0], dim[1]);
|
||||
pdf.save('map.pdf');
|
||||
// Reset original map size
|
||||
map.setSize(size);
|
||||
map.getView().fit(extent, {
|
||||
size: size,
|
||||
constrainResolution: false
|
||||
size: size
|
||||
});
|
||||
exportButton.disabled = false;
|
||||
document.body.style.cursor = 'auto';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Map from '../src/ol/Map.js';
|
||||
import View from '../src/ol/View.js';
|
||||
import {platformModifierKeyOnly} from '../src/ol/events/condition.js';
|
||||
import GeoJSON from '../src/ol/format/GeoJSON.js';
|
||||
import ExtentInteraction from '../src/ol/interaction/Extent.js';
|
||||
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
||||
@@ -27,9 +26,7 @@ const map = new Map({
|
||||
})
|
||||
});
|
||||
|
||||
const extent = new ExtentInteraction({
|
||||
condition: platformModifierKeyOnly
|
||||
});
|
||||
const extent = new ExtentInteraction();
|
||||
map.addInteraction(extent);
|
||||
extent.setActive(false);
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import WebGLPointsLayerRenderer from '../src/ol/renderer/webgl/PointsLayer';
|
||||
import {clamp, lerp} from '../src/ol/math';
|
||||
import Stamen from '../src/ol/source/Stamen';
|
||||
|
||||
const features = [];
|
||||
const vectorSource = new Vector({
|
||||
features: [],
|
||||
attributions: 'NASA'
|
||||
@@ -38,15 +37,16 @@ updateStatusText();
|
||||
class WebglPointsLayer extends VectorLayer {
|
||||
createRenderer() {
|
||||
return new WebGLPointsLayerRenderer(this, {
|
||||
colorCallback: function(feature, vertex, component) {
|
||||
// component at index 3 is alpha
|
||||
if (component === 3) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
colorCallback: function(feature, color) {
|
||||
// color is interpolated based on year
|
||||
const ratio = clamp((feature.get('year') - 1800) / (2013 - 1800), 0, 1);
|
||||
return lerp(oldColor[component], newColor[component], ratio) / 255;
|
||||
|
||||
color[0] = lerp(oldColor[0], newColor[0], ratio) / 255;
|
||||
color[1] = lerp(oldColor[1], newColor[1], ratio) / 255;
|
||||
color[2] = lerp(oldColor[2], newColor[2], ratio) / 255;
|
||||
color[3] = 1;
|
||||
|
||||
return color;
|
||||
},
|
||||
sizeCallback: function(feature) {
|
||||
return 18 * clamp(feature.get('mass') / 200000, 0, 1) + 8;
|
||||
@@ -109,26 +109,28 @@ function loadData() {
|
||||
client.open('GET', 'data/csv/meteorite_landings.csv');
|
||||
client.onload = function() {
|
||||
const csv = client.responseText;
|
||||
const features = [];
|
||||
|
||||
let prevIndex = csv.indexOf('\n') + 1; // scan past the header line
|
||||
|
||||
let curIndex;
|
||||
let prevIndex = 0;
|
||||
let line;
|
||||
while ((curIndex = csv.indexOf('\n', prevIndex)) > 0) {
|
||||
line = csv.substr(prevIndex, curIndex - prevIndex).split(',');
|
||||
while ((curIndex = csv.indexOf('\n', prevIndex)) != -1) {
|
||||
const line = csv.substr(prevIndex, curIndex - prevIndex).split(',');
|
||||
prevIndex = curIndex + 1;
|
||||
|
||||
// skip header
|
||||
if (prevIndex === 0) {
|
||||
const coords = fromLonLat([parseFloat(line[4]), parseFloat(line[3])]);
|
||||
if (isNaN(coords[0]) || isNaN(coords[1])) {
|
||||
// guard against bad data
|
||||
continue;
|
||||
}
|
||||
|
||||
const coords = fromLonLat([parseFloat(line[4]), parseFloat(line[3])]);
|
||||
|
||||
features.push(new Feature({
|
||||
mass: parseFloat(line[1]) || 0,
|
||||
year: parseInt(line[2]) || 0,
|
||||
geometry: new Point(coords)
|
||||
}));
|
||||
}
|
||||
|
||||
vectorSource.addFeatures(features);
|
||||
};
|
||||
client.send();
|
||||
|
||||
@@ -10,7 +10,6 @@ import {fromLonLat} from '../src/ol/proj';
|
||||
import WebGLPointsLayerRenderer from '../src/ol/renderer/webgl/PointsLayer';
|
||||
import {lerp} from '../src/ol/math';
|
||||
|
||||
const features = [];
|
||||
const vectorSource = new Vector({
|
||||
features: [],
|
||||
attributions: 'National UFO Reporting Center'
|
||||
@@ -39,16 +38,17 @@ class WebglPointsLayer extends VectorLayer {
|
||||
createRenderer() {
|
||||
return new WebGLPointsLayerRenderer(this, {
|
||||
texture: texture,
|
||||
colorCallback: function(feature, vertex, component) {
|
||||
// component at index 3 is alpha
|
||||
if (component === 3) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
colorCallback: function(feature, color) {
|
||||
// color is interpolated based on year (min is 1910, max is 2013)
|
||||
// please note: most values are between 2000-2013
|
||||
const ratio = (feature.get('year') - 1950) / (2013 - 1950);
|
||||
return lerp(oldColor[component], newColor[component], ratio * ratio) / 255;
|
||||
|
||||
color[0] = lerp(oldColor[0], newColor[0], ratio * ratio) / 255;
|
||||
color[1] = lerp(oldColor[1], newColor[1], ratio * ratio) / 255;
|
||||
color[2] = lerp(oldColor[2], newColor[2], ratio * ratio) / 255;
|
||||
color[3] = 1;
|
||||
|
||||
return color;
|
||||
},
|
||||
texCoordCallback: function(feature, component) {
|
||||
let coords = shapeTextureCoords[feature.get('shape')];
|
||||
@@ -70,17 +70,14 @@ function loadData() {
|
||||
client.open('GET', 'data/csv/ufo_sighting_data.csv');
|
||||
client.onload = function() {
|
||||
const csv = client.responseText;
|
||||
let curIndex;
|
||||
let prevIndex = 0;
|
||||
let line;
|
||||
while ((curIndex = csv.indexOf('\n', prevIndex)) > 0) {
|
||||
line = csv.substr(prevIndex, curIndex - prevIndex).split(',');
|
||||
prevIndex = curIndex + 1;
|
||||
const features = [];
|
||||
|
||||
// skip header
|
||||
if (prevIndex === 0) {
|
||||
continue;
|
||||
}
|
||||
let prevIndex = csv.indexOf('\n') + 1; // scan past the header line
|
||||
|
||||
let curIndex;
|
||||
while ((curIndex = csv.indexOf('\n', prevIndex)) != -1) {
|
||||
const line = csv.substr(prevIndex, curIndex - prevIndex).split(',');
|
||||
prevIndex = curIndex + 1;
|
||||
|
||||
const coords = fromLonLat([parseFloat(line[5]), parseFloat(line[4])]);
|
||||
|
||||
|
||||
16
examples/iiif.html
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
layout: example.html
|
||||
title: IIIF Image API
|
||||
shortdesc: Example of a IIIF Image API source.
|
||||
docs: >
|
||||
Example of a tile source for an International Image Interoperability Framework (IIIF) Image Service.
|
||||
Try any Image API version 1 or 2 service.
|
||||
tags: "IIIF, IIIF Image API, tile source"
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
<div class="controls">
|
||||
<div id="iiif-notification"> </div>
|
||||
Enter <code>info.json</code> URL:
|
||||
<input type="text" id="imageInfoUrl" value="https://iiif.ub.uni-leipzig.de/iiif/j2k/0000/0107/0000010732/00000072.jpx/info.json">
|
||||
<button id="display">Display image</button>
|
||||
</div>
|
||||
46
examples/iiif.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import Map from '../src/ol/Map.js';
|
||||
import View from '../src/ol/View.js';
|
||||
import TileLayer from '../src/ol/layer/Tile.js';
|
||||
import IIIF from '../src/ol/source/IIIF.js';
|
||||
import IIIFInfo from '../src/ol/format/IIIFInfo.js';
|
||||
|
||||
const layer = new TileLayer(),
|
||||
map = new Map({
|
||||
layers: [layer],
|
||||
target: 'map'
|
||||
}),
|
||||
notifyDiv = document.getElementById('iiif-notification'),
|
||||
urlInput = document.getElementById('imageInfoUrl'),
|
||||
displayButton = document.getElementById('display');
|
||||
|
||||
function refreshMap(imageInfoUrl) {
|
||||
fetch(imageInfoUrl).then(function(response) {
|
||||
response.json().then(function(imageInfo) {
|
||||
const options = new IIIFInfo(imageInfo).getTileSourceOptions();
|
||||
if (options === undefined || options.version === undefined) {
|
||||
notifyDiv.textContent = 'Data seems to be no valid IIIF image information.';
|
||||
return;
|
||||
}
|
||||
options.zDirection = -1;
|
||||
const iiifTileSource = new IIIF(options);
|
||||
layer.setSource(iiifTileSource);
|
||||
map.setView(new View({
|
||||
resolutions: iiifTileSource.getTileGrid().getResolutions(),
|
||||
extent: iiifTileSource.getTileGrid().getExtent(),
|
||||
constrainOnlyCenter: true
|
||||
}));
|
||||
map.getView().fit(iiifTileSource.getTileGrid().getExtent());
|
||||
notifyDiv.textContent = '';
|
||||
}).catch(function(body) {
|
||||
notifyDiv.textContent = 'Could not read image info json. ' + body;
|
||||
});
|
||||
}).catch(function() {
|
||||
notifyDiv.textContent = 'Could not read data from URL.';
|
||||
});
|
||||
}
|
||||
|
||||
displayButton.addEventListener('click', function() {
|
||||
refreshMap(urlInput.value);
|
||||
});
|
||||
|
||||
refreshMap(urlInput.value);
|
||||
@@ -11,9 +11,25 @@
|
||||
body {
|
||||
padding-top: 70px;
|
||||
}
|
||||
img.header-logo {
|
||||
padding-left: 18px;
|
||||
}
|
||||
input.search-query {
|
||||
color: #333;
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
input.search-query {
|
||||
width: 110px;
|
||||
}
|
||||
#count {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 374px) {
|
||||
input.search-query {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.example {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
@@ -182,9 +198,9 @@
|
||||
<body>
|
||||
|
||||
<header class="navbar navbar-fixed-top" role="navigation">
|
||||
<div class="container">
|
||||
<div class="container-fluid">
|
||||
<div class="display-table pull-left">
|
||||
<a class="navbar-brand" href="./"><img src="./resources/logo-70x70.png"> OpenLayers Examples</a>
|
||||
<a class="navbar-brand" href="./"><img class="header-logo" src="./resources/logo-70x70.png"> OpenLayers</a>
|
||||
<form class="navbar-form" role="search">
|
||||
<input name="q" type="text" id="keywords" class="search-query" placeholder="Search" autofocus>
|
||||
<span id="count"></span>
|
||||
|
||||
@@ -6,7 +6,7 @@ docs: >
|
||||
Show how to add a mapbox-gl-js layer in an openlayers map. **Note**: Make sure to get your own Mapbox API key when using this example. No map will be visible when the API key has expired.
|
||||
tags: "simple, mapbox, vector, tiles"
|
||||
resources:
|
||||
- https://unpkg.com/mapbox-gl@0.51.0/dist/mapbox-gl.js
|
||||
- https://unpkg.com/mapbox-gl@0.54.0/dist/mapbox-gl.js
|
||||
cloak:
|
||||
- key: ER67WIiPdCQvhgsUjoWK
|
||||
value: Your Mapbox access token from http://mapbox.com/ here
|
||||
|
||||
@@ -2,7 +2,7 @@ import Map from '../src/ol/Map.js';
|
||||
import View from '../src/ol/View.js';
|
||||
import Layer from '../src/ol/layer/Layer';
|
||||
import {assign} from '../src/ol/obj';
|
||||
import {getTransform} from '../src/ol/proj';
|
||||
import {toLonLat} from '../src/ol/proj';
|
||||
import SourceState from '../src/ol/source/State';
|
||||
import {Stroke, Style} from '../src/ol/style.js';
|
||||
import VectorLayer from '../src/ol/layer/Vector.js';
|
||||
@@ -12,7 +12,7 @@ import GeoJSON from '../src/ol/format/GeoJSON.js';
|
||||
class Mapbox extends Layer {
|
||||
|
||||
/**
|
||||
* @param {import('./Base.js').Options} options Layer options.
|
||||
* @param {import('../src/ol/layer/Layer').Options} options Layer options.
|
||||
*/
|
||||
constructor(options) {
|
||||
const baseOptions = assign({}, options);
|
||||
@@ -32,8 +32,7 @@ class Mapbox extends Layer {
|
||||
initMap() {
|
||||
const map = this.map_;
|
||||
const view = map.getView();
|
||||
const transformToLatLng = getTransform(view.getProjection(), 'EPSG:4326');
|
||||
const center = transformToLatLng(view.getCenter());
|
||||
const center = toLonLat(view.getCenter(), view.getProjection());
|
||||
|
||||
this.centerLastRender = view.getCenter();
|
||||
this.zoomLastRender = view.getZoom();
|
||||
@@ -61,9 +60,7 @@ class Mapbox extends Layer {
|
||||
this.mbmap.getCanvas().remove();
|
||||
this.loaded = true;
|
||||
this.map_.render();
|
||||
[
|
||||
'mapboxgl-control-container'
|
||||
].forEach(className => document.getElementsByClassName(className)[0].remove());
|
||||
this.mbmap.getContainer().querySelector('.mapboxgl-control-container').remove();
|
||||
}.bind(this));
|
||||
|
||||
this.mbmap.on('render', function() {
|
||||
@@ -74,7 +71,7 @@ class Mapbox extends Layer {
|
||||
if (this.zoomNextRender) {
|
||||
this.zoomLastRender = this.zoomNextRender;
|
||||
}
|
||||
this.updateRenderedPosition([0, 0], 1);
|
||||
this.updateRenderedPosition(0, 0, 1);
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
@@ -86,15 +83,13 @@ class Mapbox extends Layer {
|
||||
render(frameState) {
|
||||
const map = this.map_;
|
||||
const view = map.getView();
|
||||
const transformToLatLng = getTransform(view.getProjection(), 'EPSG:4326');
|
||||
|
||||
this.centerNextRender = view.getCenter();
|
||||
const lastRender = map.getPixelFromCoordinate(this.centerLastRender);
|
||||
const nextRender = map.getPixelFromCoordinate(this.centerNextRender);
|
||||
const centerOffset = [lastRender[0] - nextRender[0], lastRender[1] - nextRender[1]];
|
||||
this.zoomNextRender = view.getZoom();
|
||||
const zoomOffset = Math.pow(2, this.zoomNextRender - this.zoomLastRender);
|
||||
this.updateRenderedPosition(centerOffset, zoomOffset);
|
||||
const scale = Math.pow(2, this.zoomNextRender - this.zoomLastRender);
|
||||
this.updateRenderedPosition(lastRender[0] - nextRender[0], lastRender[1] - nextRender[1], scale);
|
||||
|
||||
const rotation = frameState.viewState.rotation;
|
||||
if (rotation) {
|
||||
@@ -104,7 +99,7 @@ class Mapbox extends Layer {
|
||||
}
|
||||
|
||||
// Re-render mbmap
|
||||
const center = transformToLatLng(this.centerNextRender);
|
||||
const center = toLonLat(this.centerNextRender, view.getProjection());
|
||||
const zoom = view.getZoom() - 1;
|
||||
this.mbmap.jumpTo({
|
||||
center: center,
|
||||
@@ -113,11 +108,11 @@ class Mapbox extends Layer {
|
||||
return this.mbmap.getCanvas();
|
||||
}
|
||||
|
||||
updateRenderedPosition(centerOffset, zoomOffset) {
|
||||
updateRenderedPosition(left, top, scale) {
|
||||
const style = this.mbmap.getCanvas().style;
|
||||
style.left = Math.round(centerOffset[0]) + 'px';
|
||||
style.top = Math.round(centerOffset[1]) + 'px';
|
||||
style.transform = 'scale(' + zoomOffset + ')';
|
||||
style.left = Math.round(left) + 'px';
|
||||
style.top = Math.round(top) + 'px';
|
||||
style.transform = 'scale(' + scale + ')';
|
||||
}
|
||||
|
||||
setVisible(visible) {
|
||||
|
||||
@@ -24,8 +24,7 @@ function flood(pixels, data) {
|
||||
const key = 'pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg';
|
||||
const elevation = new XYZ({
|
||||
url: 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=' + key,
|
||||
crossOrigin: 'anonymous',
|
||||
transition: 0
|
||||
crossOrigin: 'anonymous'
|
||||
});
|
||||
|
||||
const raster = new RasterSource({
|
||||
|
||||
@@ -100,8 +100,7 @@ function shade(inputs, data) {
|
||||
|
||||
const elevation = new XYZ({
|
||||
url: 'https://{a-d}.tiles.mapbox.com/v3/aj.sf-dem/{z}/{x}/{y}.png',
|
||||
crossOrigin: 'anonymous',
|
||||
transition: 0
|
||||
crossOrigin: 'anonymous'
|
||||
});
|
||||
|
||||
const raster = new Raster({
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
<header class="navbar" role="navigation">
|
||||
<div class="container">
|
||||
<div class="display-table pull-left" id="navbar-logo-container">
|
||||
<a class="navbar-brand" href="./"><img src="./resources/logo-70x70.png"> OpenLayers Examples</a>
|
||||
<a class="navbar-brand" href="./"><img src="./resources/logo-70x70.png"> OpenLayers</a>
|
||||
</div>
|
||||
<!-- menu items that get hidden below 768px width -->
|
||||
<nav class='collapse navbar-collapse navbar-responsive-collapse'>
|
||||
@@ -122,7 +122,7 @@
|
||||
<a class="copy-button" id="copy-html-button" data-clipboard-target="#example-html-source"><i class="fa fa-clipboard"></i> Copy</a>
|
||||
</div>
|
||||
<pre><legend>index.html</legend><code id="example-html-source" class="language-markup"><!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{ title }}</title>
|
||||
<!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
|
||||
|
||||
@@ -46,7 +46,7 @@ function updateInfo() {
|
||||
|
||||
function setTime() {
|
||||
startDate.setMinutes(startDate.getMinutes() + 15);
|
||||
if (startDate > Date.now()) {
|
||||
if (startDate > new Date()) {
|
||||
startDate = threeHoursAgo();
|
||||
}
|
||||
layers[1].getSource().updateParams({'TIME': startDate.toISOString()});
|
||||
|
||||
60
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ol",
|
||||
"version": "5.3.0",
|
||||
"version": "6.0.0-beta.7",
|
||||
"description": "OpenLayers mapping library",
|
||||
"keywords": [
|
||||
"map",
|
||||
@@ -24,7 +24,7 @@
|
||||
"copy-css": "shx cp src/ol/ol.css build/ol/ol.css",
|
||||
"transpile": "shx rm -rf build/ol && shx mkdir -p build/ol && shx cp -rf src/ol build/ol/src && tsc --project config/tsconfig-build.json",
|
||||
"typecheck": "tsc --pretty",
|
||||
"apidoc": "jsdoc config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -P package.json -d build/apidoc"
|
||||
"apidoc": "jsdoc -R config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -P package.json -d build/apidoc"
|
||||
},
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -41,56 +41,62 @@
|
||||
"rbush": "2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openlayers/eslint-plugin": "^4.0.0-beta.1",
|
||||
"@babel/core": "^7.4.0",
|
||||
"@babel/preset-env": "^7.4.2",
|
||||
"@openlayers/eslint-plugin": "^4.0.0-beta.2",
|
||||
"@types/arcgis-rest-api": "^10.4.4",
|
||||
"@types/geojson": "^7946.0.6",
|
||||
"@types/geojson": "^7946.0.7",
|
||||
"@types/pbf": "^3.0.1",
|
||||
"@types/rbush": "^2.0.2",
|
||||
"@types/topojson-specification": "^1.0.1",
|
||||
"babel-loader": "^8.0.5",
|
||||
"buble": "^0.19.7",
|
||||
"buble-loader": "^0.5.1",
|
||||
"chaikin-smooth": "^1.0.4",
|
||||
"clean-css-cli": "4.2.1",
|
||||
"copy-webpack-plugin": "^5.0.1",
|
||||
"clean-css-cli": "4.3.0",
|
||||
"copy-webpack-plugin": "^5.0.3",
|
||||
"coveralls": "3.0.3",
|
||||
"eslint": "^5.15.2",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-openlayers": "^11.0.0",
|
||||
"expect.js": "0.3.1",
|
||||
"front-matter": "^3.0.1",
|
||||
"fs-extra": "^7.0.1",
|
||||
"glob": "^7.1.2",
|
||||
"globby": "^9.1.0",
|
||||
"handlebars": "4.1.1",
|
||||
"front-matter": "^3.0.2",
|
||||
"fs-extra": "^8.0.0",
|
||||
"glob": "^7.1.4",
|
||||
"globby": "^9.2.0",
|
||||
"handlebars": "4.1.2",
|
||||
"html-to-image": "^0.1.0",
|
||||
"istanbul": "0.4.5",
|
||||
"jquery": "3.3.1",
|
||||
"jsdoc": "3.5.5",
|
||||
"jsdoc-plugin-typescript": "^1.0.7",
|
||||
"karma": "^4.0.1",
|
||||
"istanbul-instrumenter-loader": "^3.0.1",
|
||||
"jquery": "3.4.1",
|
||||
"jsdoc": "3.6.1",
|
||||
"jsdoc-plugin-typescript": "^2.0.0",
|
||||
"karma": "^4.1.0",
|
||||
"karma-chrome-launcher": "2.2.0",
|
||||
"karma-coverage": "^1.1.2",
|
||||
"karma-coverage-istanbul-reporter": "^2.0.5",
|
||||
"karma-firefox-launcher": "^1.1.0",
|
||||
"karma-mocha": "1.3.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^4.0.0-rc.2",
|
||||
"loglevelnext": "^3.0.0",
|
||||
"loglevelnext": "^3.0.1",
|
||||
"marked": "0.6.2",
|
||||
"mocha": "6.0.2",
|
||||
"ol-mapbox-style": "^4.2.1",
|
||||
"mocha": "6.1.4",
|
||||
"ol-mapbox-style": "^4.3.1",
|
||||
"pixelmatch": "^4.0.2",
|
||||
"pngjs": "^3.4.0",
|
||||
"proj4": "2.5.0",
|
||||
"puppeteer": "~1.14.0",
|
||||
"serve-static": "^1.13.2",
|
||||
"puppeteer": "~1.15.0",
|
||||
"serve-static": "^1.14.0",
|
||||
"shx": "^0.3.2",
|
||||
"sinon": "^7.2.7",
|
||||
"sinon": "^7.3.2",
|
||||
"terser-webpack-plugin": "^1.2.3",
|
||||
"typescript": "^3.2.2",
|
||||
"typescript": "^3.4.5",
|
||||
"url-polyfill": "^1.1.5",
|
||||
"walk": "^2.3.9",
|
||||
"webpack": "4.29.6",
|
||||
"webpack-cli": "^3.3.0",
|
||||
"webpack-dev-middleware": "^3.6.1",
|
||||
"webpack-dev-server": "^3.2.1",
|
||||
"webpack": "4.31.0",
|
||||
"webpack-cli": "^3.3.2",
|
||||
"webpack-dev-middleware": "^3.6.2",
|
||||
"webpack-dev-server": "^3.3.1",
|
||||
"yargs": "^13.2.2"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
||||
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 6.2 KiB |
@@ -7,6 +7,7 @@ import Point from '../../../src/ol/geom/Point.js';
|
||||
import Style from '../../../src/ol/style/Style.js';
|
||||
import Text from '../../../src/ol/style/Text.js';
|
||||
import CircleStyle from '../../../src/ol/style/Circle.js';
|
||||
import Fill from '../../../src/ol/style/Fill.js';
|
||||
import Stroke from '../../../src/ol/style/Stroke.js';
|
||||
import LineString from '../../../src/ol/geom/LineString.js';
|
||||
|
||||
@@ -59,9 +60,10 @@ source1.addFeature(new Feature({
|
||||
}));
|
||||
layer1.setStyle(function(feature) {
|
||||
return new Style({
|
||||
zIndex: feature.get('zIndex'),
|
||||
image: new CircleStyle({
|
||||
radius: 15,
|
||||
stroke: new Stroke({
|
||||
fill: new Fill({
|
||||
color: 'blue'
|
||||
})
|
||||
})
|
||||
@@ -69,7 +71,7 @@ layer1.setStyle(function(feature) {
|
||||
});
|
||||
map.addLayer(layer1);
|
||||
|
||||
center = [center[0] + 500, center[1] + 500];
|
||||
center = [center[0] + 500, center[1] + 700];
|
||||
const feature2 = new Feature({
|
||||
geometry: new Point(center),
|
||||
text: 'center',
|
||||
@@ -88,15 +90,16 @@ source2.addFeature(new Feature({
|
||||
}));
|
||||
layer2.setStyle(function(feature) {
|
||||
return new Style({
|
||||
zIndex: feature.get('zIndex'),
|
||||
text: new Text({
|
||||
text: feature.get('text'),
|
||||
font: '16px Ubuntu'
|
||||
font: 'italic bold 18px Ubuntu'
|
||||
})
|
||||
});
|
||||
});
|
||||
map.addLayer(layer2);
|
||||
|
||||
center = [center[0] + 500, center[1] + 500];
|
||||
center = [center[0] + 500, center[1] + 300];
|
||||
source3.addFeature(new Feature({
|
||||
geometry: new Point(center),
|
||||
text: 'center'
|
||||
@@ -112,16 +115,17 @@ source3.addFeature(new Feature({
|
||||
layer3.setStyle(function(feature) {
|
||||
return new Style({
|
||||
image: new CircleStyle({
|
||||
radius: 5,
|
||||
radius: 10,
|
||||
stroke: new Stroke({
|
||||
color: 'red'
|
||||
color: 'red',
|
||||
width: 8
|
||||
})
|
||||
}),
|
||||
text: new Text({
|
||||
text: feature.get('text'),
|
||||
font: '16px Ubuntu',
|
||||
font: 'italic bold 18px Ubuntu',
|
||||
textBaseline: 'bottom',
|
||||
offsetY: -5
|
||||
offsetY: -12
|
||||
})
|
||||
});
|
||||
});
|
||||
@@ -130,11 +134,12 @@ map.addLayer(layer3);
|
||||
center = [center[0] - 2000, center[1] - 2000];
|
||||
const point = new Feature(new Point(center));
|
||||
point.setStyle(new Style({
|
||||
zIndex: 2,
|
||||
zIndex: 1,
|
||||
image: new CircleStyle({
|
||||
radius: 8,
|
||||
stroke: new Stroke({
|
||||
color: 'blue'
|
||||
color: 'blue',
|
||||
width: 4
|
||||
})
|
||||
})
|
||||
}));
|
||||
@@ -143,7 +148,7 @@ const line = new Feature(new LineString([
|
||||
[center[0] + 650, center[1] - 200]
|
||||
]));
|
||||
line.setStyle(new Style({
|
||||
zIndex: 1,
|
||||
zIndex: 2,
|
||||
stroke: new Stroke({
|
||||
color: '#CCC',
|
||||
width: 12
|
||||
@@ -151,7 +156,7 @@ line.setStyle(new Style({
|
||||
text: new Text({
|
||||
placement: 'line',
|
||||
text: 'east-west',
|
||||
font: '16px Ubuntu',
|
||||
font: 'italic bold 18px Ubuntu',
|
||||
overflow: true
|
||||
})
|
||||
}));
|
||||
@@ -159,4 +164,4 @@ source4.addFeature(point);
|
||||
source4.addFeature(line);
|
||||
map.addLayer(layer4);
|
||||
|
||||
render({tolerance: 0.02});
|
||||
render({tolerance: 0.007});
|
||||
|
||||
|
Before Width: | Height: | Size: 935 B After Width: | Height: | Size: 1016 B |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 6.7 KiB |
@@ -1,72 +1,19 @@
|
||||
import Feature from '../../../src/ol/Feature.js';
|
||||
import Map from '../../../src/ol/Map.js';
|
||||
import View from '../../../src/ol/View.js';
|
||||
import VectorSource from '../../../src/ol/source/Vector.js';
|
||||
import Style from '../../../src/ol/style/Style.js';
|
||||
import Stroke from '../../../src/ol/style/Stroke.js';
|
||||
import VectorImageLayer from '../../../src/ol/layer/VectorImage.js';
|
||||
import CircleStyle from '../../../src/ol/style/Circle.js';
|
||||
import Feature from '../../../src/ol/Feature.js';
|
||||
import Point from '../../../src/ol/geom/Point.js';
|
||||
import LineString from '../../../src/ol/geom/LineString.js';
|
||||
import Style from '../../../src/ol/style/Style.js';
|
||||
import Text from '../../../src/ol/style/Text.js';
|
||||
import CircleStyle from '../../../src/ol/style/Circle.js';
|
||||
import Fill from '../../../src/ol/style/Fill.js';
|
||||
import Stroke from '../../../src/ol/style/Stroke.js';
|
||||
import LineString from '../../../src/ol/geom/LineString.js';
|
||||
|
||||
const center = [1825927.7316762917, 6143091.089223046];
|
||||
|
||||
const source = new VectorSource();
|
||||
const vectorLayer1 = new VectorImageLayer({
|
||||
source: source,
|
||||
style: function(feature) {
|
||||
return new Style({
|
||||
image: new CircleStyle({
|
||||
radius: 15,
|
||||
stroke: new Stroke({
|
||||
color: 'blue'
|
||||
})
|
||||
}),
|
||||
text: new Text({
|
||||
text: feature.get('text'),
|
||||
font: '16px Ubuntu'
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const centerFeature = new Feature({
|
||||
geometry: new Point(center),
|
||||
text: 'center'
|
||||
});
|
||||
source.addFeature(centerFeature);
|
||||
source.addFeature(new Feature({
|
||||
geometry: new Point([center[0] - 540, center[1]]),
|
||||
text: 'west'
|
||||
}));
|
||||
source.addFeature(new Feature({
|
||||
geometry: new Point([center[0] + 540, center[1]]),
|
||||
text: 'east'
|
||||
}));
|
||||
|
||||
const line = new Feature(new LineString([
|
||||
[center[0] - 650, center[1] - 200],
|
||||
[center[0] + 650, center[1] - 200]
|
||||
]));
|
||||
line.setStyle(new Style({
|
||||
stroke: new Stroke({
|
||||
color: '#CCC',
|
||||
width: 12
|
||||
}),
|
||||
text: new Text({
|
||||
placement: 'line',
|
||||
text: 'east-west',
|
||||
font: '16px Ubuntu'
|
||||
})
|
||||
}));
|
||||
source.addFeature(line);
|
||||
|
||||
let center = [1825927.7316762917, 6143091.089223046];
|
||||
const map = new Map({
|
||||
pixelRatio: 1,
|
||||
layers: [
|
||||
vectorLayer1
|
||||
],
|
||||
target: 'map',
|
||||
view: new View({
|
||||
center: center,
|
||||
@@ -74,6 +21,147 @@ const map = new Map({
|
||||
})
|
||||
});
|
||||
|
||||
map.getView().fit(source.getExtent());
|
||||
const source1 = new VectorSource();
|
||||
const layer1 = new VectorImageLayer({
|
||||
declutter: true,
|
||||
source: source1
|
||||
});
|
||||
|
||||
render({tolerance: 0.02});
|
||||
const source2 = new VectorSource();
|
||||
const layer2 = new VectorImageLayer({
|
||||
declutter: true,
|
||||
source: source2
|
||||
});
|
||||
|
||||
const source3 = new VectorSource();
|
||||
const layer3 = new VectorImageLayer({
|
||||
declutter: true,
|
||||
source: source3
|
||||
});
|
||||
|
||||
const source4 = new VectorSource();
|
||||
const layer4 = new VectorImageLayer({
|
||||
declutter: true,
|
||||
source: source4
|
||||
});
|
||||
|
||||
const feature1 = new Feature({
|
||||
geometry: new Point(center),
|
||||
zIndex: 2
|
||||
});
|
||||
source1.addFeature(feature1);
|
||||
source1.addFeature(new Feature({
|
||||
geometry: new Point([center[0] - 540, center[1]]),
|
||||
zIndex: 3
|
||||
}));
|
||||
source1.addFeature(new Feature({
|
||||
geometry: new Point([center[0] + 540, center[1]]),
|
||||
zIndex: 1
|
||||
}));
|
||||
layer1.setStyle(function(feature) {
|
||||
return new Style({
|
||||
zIndex: feature.get('zIndex'),
|
||||
image: new CircleStyle({
|
||||
radius: 15,
|
||||
fill: new Fill({
|
||||
color: 'blue'
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
map.addLayer(layer1);
|
||||
|
||||
center = [center[0] + 500, center[1] + 700];
|
||||
const feature2 = new Feature({
|
||||
geometry: new Point(center),
|
||||
text: 'center',
|
||||
zIndex: 2
|
||||
});
|
||||
source2.addFeature(feature2);
|
||||
source2.addFeature(new Feature({
|
||||
geometry: new Point([center[0] - 540, center[1]]),
|
||||
text: 'west',
|
||||
zIndex: 3
|
||||
}));
|
||||
source2.addFeature(new Feature({
|
||||
geometry: new Point([center[0] + 540, center[1]]),
|
||||
text: 'east',
|
||||
zIndex: 1
|
||||
}));
|
||||
layer2.setStyle(function(feature) {
|
||||
return new Style({
|
||||
zIndex: feature.get('zIndex'),
|
||||
text: new Text({
|
||||
text: feature.get('text'),
|
||||
font: 'italic bold 18px Ubuntu'
|
||||
})
|
||||
});
|
||||
});
|
||||
map.addLayer(layer2);
|
||||
|
||||
center = [center[0] + 500, center[1] + 300];
|
||||
source3.addFeature(new Feature({
|
||||
geometry: new Point(center),
|
||||
text: 'center'
|
||||
}));
|
||||
source3.addFeature(new Feature({
|
||||
geometry: new Point([center[0] - 540, center[1]]),
|
||||
text: 'west'
|
||||
}));
|
||||
source3.addFeature(new Feature({
|
||||
geometry: new Point([center[0] + 540, center[1]]),
|
||||
text: 'east'
|
||||
}));
|
||||
layer3.setStyle(function(feature) {
|
||||
return new Style({
|
||||
image: new CircleStyle({
|
||||
radius: 10,
|
||||
stroke: new Stroke({
|
||||
color: 'red',
|
||||
width: 8
|
||||
})
|
||||
}),
|
||||
text: new Text({
|
||||
text: feature.get('text'),
|
||||
font: 'italic bold 18px Ubuntu',
|
||||
textBaseline: 'bottom',
|
||||
offsetY: -12
|
||||
})
|
||||
});
|
||||
});
|
||||
map.addLayer(layer3);
|
||||
|
||||
center = [center[0] - 2000, center[1] - 2000];
|
||||
const point = new Feature(new Point(center));
|
||||
point.setStyle(new Style({
|
||||
zIndex: 1,
|
||||
image: new CircleStyle({
|
||||
radius: 8,
|
||||
stroke: new Stroke({
|
||||
color: 'blue',
|
||||
width: 4
|
||||
})
|
||||
})
|
||||
}));
|
||||
const line = new Feature(new LineString([
|
||||
[center[0] - 650, center[1] - 200],
|
||||
[center[0] + 650, center[1] - 200]
|
||||
]));
|
||||
line.setStyle(new Style({
|
||||
zIndex: 2,
|
||||
stroke: new Stroke({
|
||||
color: '#CCC',
|
||||
width: 12
|
||||
}),
|
||||
text: new Text({
|
||||
placement: 'line',
|
||||
text: 'east-west',
|
||||
font: 'italic bold 18px Ubuntu',
|
||||
overflow: true
|
||||
})
|
||||
}));
|
||||
source4.addFeature(point);
|
||||
source4.addFeature(line);
|
||||
map.addLayer(layer4);
|
||||
|
||||
render({tolerance: 0.007});
|
||||
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
@@ -4,8 +4,10 @@ import {Vector as VectorLayer, Tile as TileLayer} from '../../../src/ol/layer.js
|
||||
import {Vector as VectorSource, XYZ} from '../../../src/ol/source.js';
|
||||
import GeoJSON from '../../../src/ol/format/GeoJSON.js';
|
||||
import {Style, Stroke} from '../../../src/ol/style.js';
|
||||
import Feature from '../../../src/ol/Feature.js';
|
||||
import Point from '../../../src/ol/geom/Point.js';
|
||||
|
||||
new Map({
|
||||
const map = new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new XYZ({
|
||||
@@ -14,6 +16,7 @@ new Map({
|
||||
})
|
||||
}),
|
||||
new VectorLayer({
|
||||
zIndex: 1,
|
||||
style: new Style({
|
||||
stroke: new Stroke({
|
||||
color: 'rgba(255,255,255,0.5)',
|
||||
@@ -33,4 +36,11 @@ new Map({
|
||||
})
|
||||
});
|
||||
|
||||
const unmanaged = new VectorLayer({
|
||||
source: new VectorSource({
|
||||
features: [new Feature(new Point([0, 0]))]
|
||||
})
|
||||
});
|
||||
unmanaged.setMap(map);
|
||||
|
||||
render();
|
||||
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 891 B After Width: | Height: | Size: 904 B |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
@@ -131,4 +131,4 @@ const map = new Map({
|
||||
});
|
||||
map.getView().fit(vectorSource.getExtent());
|
||||
|
||||
render({tolerance: 0.02});
|
||||
render({tolerance: 0.021});
|
||||
|
||||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.3 KiB |
@@ -103,4 +103,4 @@ const map = new Map({
|
||||
});
|
||||
map.getView().fit(vectorSource.getExtent());
|
||||
|
||||
render({tolerance: 0.02});
|
||||
render({tolerance: 0.024});
|
||||
|
||||
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 778 B After Width: | Height: | Size: 886 B |
|
Before Width: | Height: | Size: 801 B After Width: | Height: | Size: 910 B |
@@ -39,6 +39,7 @@ import {create as createTransform, apply as applyTransform} from './transform.js
|
||||
* @property {boolean} animate
|
||||
* @property {import("./transform.js").Transform} coordinateToPixelTransform
|
||||
* @property {null|import("./extent.js").Extent} extent
|
||||
* @property {Array<*>} declutterItems
|
||||
* @property {import("./coordinate.js").Coordinate} focus
|
||||
* @property {number} index
|
||||
* @property {Array<import("./layer/Layer.js").State>} layerStatesArray
|
||||
@@ -1175,6 +1176,7 @@ class PluggableMap extends BaseObject {
|
||||
frameState = /** @type {FrameState} */ ({
|
||||
animate: false,
|
||||
coordinateToPixelTransform: this.coordinateToPixelTransform_,
|
||||
declutterItems: previousFrameState ? previousFrameState.declutterItems : [],
|
||||
extent: extent,
|
||||
focus: this.focus_ ? this.focus_ : viewState.center,
|
||||
index: this.frameIndex_++,
|
||||
|
||||
@@ -44,7 +44,7 @@ class TileCache extends LRUCache {
|
||||
this.remove(getKey(tile.tileCoord));
|
||||
tile.dispose();
|
||||
}
|
||||
}, this);
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -191,6 +191,11 @@ const DEFAULT_MIN_ZOOM = 0;
|
||||
* This is the object to act upon to change the center, resolution,
|
||||
* and rotation of the map.
|
||||
*
|
||||
* A View has a `projection`. The projection determines the
|
||||
* coordinate system of the center, and its units determine the units of the
|
||||
* resolution (projection units per pixel). The default projection is
|
||||
* Spherical Mercator (EPSG:3857).
|
||||
*
|
||||
* ### The view states
|
||||
*
|
||||
* An View is determined by three states: `center`, `resolution`,
|
||||
@@ -202,11 +207,6 @@ const DEFAULT_MIN_ZOOM = 0;
|
||||
* methods are available, as well as `getResolutionForZoom` and
|
||||
* `getZoomForResolution` to switch from one system to the other.
|
||||
*
|
||||
* A View has a `projection`. The projection determines the
|
||||
* coordinate system of the center, and its units determine the units of the
|
||||
* resolution (projection units per pixel). The default projection is
|
||||
* Spherical Mercator (EPSG:3857).
|
||||
*
|
||||
* ### The constraints
|
||||
*
|
||||
* `setCenter`, `setResolution` and `setRotation` can be used to change the
|
||||
@@ -218,7 +218,7 @@ const DEFAULT_MIN_ZOOM = 0;
|
||||
*
|
||||
* The *resolution constraint* typically restricts min/max values and
|
||||
* snaps to specific resolutions. It is determined by the following
|
||||
* options: `resolutions`, `maxResolution`, `maxZoom`, and `zoomFactor`.
|
||||
* options: `resolutions`, `maxResolution`, `maxZoom` and `zoomFactor`.
|
||||
* If `resolutions` is set, the other three options are ignored. See
|
||||
* documentation for each option for more information. By default, the view
|
||||
* only has a min/max restriction and allow intermediary zoom levels when
|
||||
@@ -226,7 +226,7 @@ const DEFAULT_MIN_ZOOM = 0;
|
||||
*
|
||||
* The *rotation constraint* snaps to specific angles. It is determined
|
||||
* by the following options: `enableRotation` and `constrainRotation`.
|
||||
* By default the rotation value is snapped to zero when approaching the
|
||||
* By default rotation is allowed and its value is snapped to zero when approaching the
|
||||
* horizontal.
|
||||
*
|
||||
* The *center constraint* is determined by the `extent` option. By
|
||||
@@ -934,13 +934,9 @@ class View extends BaseObject {
|
||||
const center = /** @type {import("./coordinate.js").Coordinate} */ (this.getCenter());
|
||||
const projection = this.getProjection();
|
||||
const resolution = /** @type {number} */ (this.getResolution());
|
||||
const pixelResolution = resolution / pixelRatio;
|
||||
const rotation = this.getRotation();
|
||||
return {
|
||||
center: [
|
||||
Math.round(center[0] / pixelResolution) * pixelResolution,
|
||||
Math.round(center[1] / pixelResolution) * pixelResolution
|
||||
],
|
||||
center: center.slice(0),
|
||||
projection: projection !== undefined ? projection : null,
|
||||
resolution: resolution,
|
||||
rotation: rotation,
|
||||
|
||||
@@ -400,26 +400,25 @@ export function extendXY(extent, x, y) {
|
||||
* callback returns a truthy value the function returns that value
|
||||
* immediately. Otherwise the function returns `false`.
|
||||
* @param {Extent} extent Extent.
|
||||
* @param {function(this:T, import("./coordinate.js").Coordinate): S} callback Callback.
|
||||
* @param {T=} opt_this Value to use as `this` when executing `callback`.
|
||||
* @param {function(import("./coordinate.js").Coordinate): S} callback Callback.
|
||||
* @return {S|boolean} Value.
|
||||
* @template S, T
|
||||
* @template S
|
||||
*/
|
||||
export function forEachCorner(extent, callback, opt_this) {
|
||||
export function forEachCorner(extent, callback) {
|
||||
let val;
|
||||
val = callback.call(opt_this, getBottomLeft(extent));
|
||||
val = callback(getBottomLeft(extent));
|
||||
if (val) {
|
||||
return val;
|
||||
}
|
||||
val = callback.call(opt_this, getBottomRight(extent));
|
||||
val = callback(getBottomRight(extent));
|
||||
if (val) {
|
||||
return val;
|
||||
}
|
||||
val = callback.call(opt_this, getTopRight(extent));
|
||||
val = callback(getTopRight(extent));
|
||||
if (val) {
|
||||
return val;
|
||||
}
|
||||
val = callback.call(opt_this, getTopLeft(extent));
|
||||
val = callback(getTopLeft(extent));
|
||||
if (val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export {default as GeoJSON} from './format/GeoJSON.js';
|
||||
export {default as GML} from './format/GML.js';
|
||||
export {default as GPX} from './format/GPX.js';
|
||||
export {default as IGC} from './format/IGC.js';
|
||||
export {default as IIIFInfo} from './format/IIIFInfo.js';
|
||||
export {default as KML} from './format/KML.js';
|
||||
export {default as MVT} from './format/MVT.js';
|
||||
export {default as OWS} from './format/OWS.js';
|
||||
|
||||
426
src/ol/format/IIIFInfo.js
Normal file
@@ -0,0 +1,426 @@
|
||||
/**
|
||||
* @module ol/format/IIIFInfo
|
||||
*/
|
||||
|
||||
import {assert} from '../asserts.js';
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {Object} PreferredOptions
|
||||
* @property {string} [format] Preferred image format. Will be used if the image information
|
||||
* indicates support for that format.
|
||||
* @property {string} [quality] IIIF image qualitiy. Will be used if the image information
|
||||
* indicates support for that quality.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} SupportedFeatures
|
||||
* @property {Array<string>} [supports] Supported IIIF image size and region
|
||||
* calculation features.
|
||||
* @property {Array<string>} [formats] Supported image formats.
|
||||
* @property {Array<string>} [qualities] Supported IIIF image qualities.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ImageInformationResponse1_0
|
||||
* @property {string} identifier
|
||||
* @property {number} width
|
||||
* @property {number} height
|
||||
* @property {Array<number>} [scale_factors] Resolution scaling factors.
|
||||
* @property {number} [tile_width]
|
||||
* @property {number} [tile_height]
|
||||
* @property {Array<string>} [formats] Supported image formats.
|
||||
* @property {string} [profile] Compliance level URI.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ImageInformationResponse1_1
|
||||
* @property {string} "@id" The base URI of the image service.
|
||||
* @property {string} "@context" JSON-LD context URI.
|
||||
* @property {number} width Full image width.
|
||||
* @property {number} height Full image height.
|
||||
* @property {Array<number>} [scale_factors] Resolution scaling factors.
|
||||
* @property {number} [tile_width]
|
||||
* @property {number} [tile_height]
|
||||
* @property {Array<string>} [formats] Supported image formats.
|
||||
* @property {string} [profile] Compliance level URI.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TileInfo
|
||||
* @property {Array<number>} scaleFactors Supported resolution scaling factors.
|
||||
* @property {number} width Tile width in pixels.
|
||||
* @property {number} [height] Tile height in pixels. Same as tile width if height is
|
||||
* not given.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} IiifProfile
|
||||
* @property {Array<string>} [formats] Supported image formats for the image service.
|
||||
* @property {Array<string>} [qualities] Supported IIIF image qualities.
|
||||
* @property {Array<string>} [supports] Supported features.
|
||||
* @property {number} [maxArea] Maximum area (pixels) available for this image service.
|
||||
* @property {number} [maxHeight] Maximum height.
|
||||
* @property {number} [maxWidth] Maximum width.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ImageInformationResponse2
|
||||
* @property {string} "@id" The base URI of the image service.
|
||||
* @property {string} "@context" JSON-LD context IRI
|
||||
* @property {number} width Full image width.
|
||||
* @property {number} height Full image height.
|
||||
* @property {Array<string|IiifProfile>} profile Additional informations about the image
|
||||
* service's capabilities.
|
||||
* @property {Array<Object<string, number>>} [sizes] Supported full image dimensions.
|
||||
* @property {Array<TileInfo>} [tiles] Supported tile sizes and resolution scaling factors.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ImageInformationResponse3
|
||||
* @property {string} id The base URI of the image service.
|
||||
* @property {string} "@context" JSON-LD context IRI
|
||||
* @property {number} width Full image width.
|
||||
* @property {number} height Full image height.
|
||||
* @property {string} profile Compliance level, one of 'level0', 'level1' or 'level2'
|
||||
* @property {Array<Object<string, number>>} [sizes] Supported full image dimensions.
|
||||
* @property {Array<TileInfo>} [tiles] Supported tile sizes and resolution scaling factors.
|
||||
* @property {number} [maxArea] Maximum area (pixels) available for this image service.
|
||||
* @property {number} [maxHeight] Maximum height.
|
||||
* @property {number} [maxWidth] Maximum width.
|
||||
* @property {Array<string>} [extraQualities] IIIF image qualities supported by the
|
||||
* image service additional to the ones indicated by the compliance level.
|
||||
* @property {Array<string>} [extraFormats] Image formats supported by the
|
||||
* image service additional to the ones indicated by the compliance level.
|
||||
* @property {Array<string>} [extraFeatures] Additional supported features whose support
|
||||
* is not indicated by the compliance level.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
export const Versions = {
|
||||
VERSION1: 'version1',
|
||||
VERSION2: 'version2',
|
||||
VERSION3: 'version3'
|
||||
};
|
||||
|
||||
/**
|
||||
* Supported image formats, qualities and supported region / size calculation features
|
||||
* for different image API versions and compliance levels
|
||||
* @const
|
||||
* @type {Object<string, Object<string, SupportedFeatures>>}
|
||||
*/
|
||||
const IIIF_PROFILE_VALUES = {};
|
||||
IIIF_PROFILE_VALUES[Versions.VERSION1] = {
|
||||
'level0': {
|
||||
supports: [],
|
||||
formats: [],
|
||||
qualities: ['native']
|
||||
},
|
||||
'level1': {
|
||||
supports: ['regionByPx', 'sizeByW', 'sizeByH', 'sizeByPct'],
|
||||
formats: ['jpg'],
|
||||
qualities: ['native']
|
||||
},
|
||||
'level2': {
|
||||
supports: ['regionByPx', 'regionByPct', 'sizeByW', 'sizeByH', 'sizeByPct',
|
||||
'sizeByConfinedWh', 'sizeByWh'],
|
||||
formats: ['jpg', 'png'],
|
||||
qualities: ['native', 'color', 'grey', 'bitonal']
|
||||
}
|
||||
};
|
||||
IIIF_PROFILE_VALUES[Versions.VERSION2] = {
|
||||
'level0': {
|
||||
supports: [],
|
||||
formats: ['jpg'],
|
||||
qualities: ['default']
|
||||
},
|
||||
'level1': {
|
||||
supports: ['regionByPx', 'sizeByW', 'sizeByH', 'sizeByPct'],
|
||||
formats: ['jpg'],
|
||||
qualities: ['default']
|
||||
},
|
||||
'level2': {
|
||||
supports: ['regionByPx', 'regionByPct', 'sizeByW', 'sizeByH', 'sizeByPct',
|
||||
'sizeByConfinedWh', 'sizeByDistortedWh', 'sizeByWh'],
|
||||
formats: ['jpg', 'png'],
|
||||
qualities: ['default', 'bitonal']
|
||||
}
|
||||
};
|
||||
IIIF_PROFILE_VALUES[Versions.VERSION3] = {
|
||||
'level0': {
|
||||
supports: [],
|
||||
formats: ['jpg'],
|
||||
qualities: ['default']
|
||||
},
|
||||
'level1': {
|
||||
supports: ['regionByPx', 'regionSquare', 'sizeByW', 'sizeByH'],
|
||||
formats: ['jpg'],
|
||||
qualities: ['default']
|
||||
},
|
||||
'level2': {
|
||||
supports: ['regionByPx', 'regionSquare', 'regionByPct',
|
||||
'sizeByW', 'sizeByH', 'sizeByPct', 'sizeByConfinedWh', 'sizeByWh'],
|
||||
formats: ['jpg'],
|
||||
qualities: ['default', 'bitonal']
|
||||
}
|
||||
};
|
||||
IIIF_PROFILE_VALUES['none'] = {
|
||||
'none': {
|
||||
supports: [],
|
||||
formats: [],
|
||||
qualities: []
|
||||
}
|
||||
};
|
||||
|
||||
const COMPLIANCE_VERSION1 = new RegExp('^https?\:\/\/library\.stanford\.edu\/iiif\/image-api\/(1\.1\/)?compliance\.html#level[0-2]$');
|
||||
const COMPLIANCE_VERSION2 = new RegExp('^https?\:\/\/iiif\.io\/api\/image\/2\/level[0-2](\.json)?$');
|
||||
const COMPLIANCE_VERSION3 = new RegExp('(^https?\:\/\/iiif\.io\/api\/image\/3\/level[0-2](\.json)?$)|(^level[0-2]$)');
|
||||
|
||||
function generateVersion1Options(iiifInfo) {
|
||||
let levelProfile = iiifInfo.getComplianceLevelSupportedFeatures();
|
||||
// Version 1.0 and 1.1 do not require a profile.
|
||||
if (levelProfile === undefined) {
|
||||
levelProfile = IIIF_PROFILE_VALUES[Versions.VERSION1]['level0'];
|
||||
}
|
||||
return {
|
||||
url: iiifInfo.imageInfo['@id'] === undefined ? undefined : iiifInfo.imageInfo['@id'].replace(/\/?(info.json)?$/g, ''),
|
||||
supports: levelProfile.supports,
|
||||
formats: [...levelProfile.formats, iiifInfo.imageInfo.formats === undefined ?
|
||||
[] : iiifInfo.imageInfo.formats
|
||||
],
|
||||
qualities: [...levelProfile.qualities, iiifInfo.imageInfo.qualities === undefined ?
|
||||
[] : iiifInfo.imageInfo.qualities
|
||||
],
|
||||
resolutions: iiifInfo.imageInfo.scale_factors,
|
||||
tileSize: iiifInfo.imageInfo.tile_width !== undefined ? (iiifInfo.imageInfo.tile_height !== undefined ?
|
||||
[iiifInfo.imageInfo.tile_width, iiifInfo.imageInfo.tile_height] : [iiifInfo.imageInfo.tile_width, iiifInfo.imageInfo.tile_width]) :
|
||||
(iiifInfo.imageInfo.tile_height != undefined ? [iiifInfo.imageInfo.tile_height, iiifInfo.imageInfo.tile_height] : undefined)
|
||||
};
|
||||
}
|
||||
|
||||
function generateVersion2Options(iiifInfo) {
|
||||
const levelProfile = iiifInfo.getComplianceLevelSupportedFeatures(),
|
||||
additionalProfile = Array.isArray(iiifInfo.imageInfo.profile) && iiifInfo.imageInfo.profile.length > 1,
|
||||
profileSupports = additionalProfile && iiifInfo.imageInfo.profile[1].supports ? iiifInfo.imageInfo.profile[1].supports : [],
|
||||
profileFormats = additionalProfile && iiifInfo.imageInfo.profile[1].formats ? iiifInfo.imageInfo.profile[1].formats : [],
|
||||
profileQualities = additionalProfile && iiifInfo.imageInfo.profile[1].qualities ? iiifInfo.imageInfo.profile[1].qualities : [];
|
||||
return {
|
||||
url: iiifInfo.imageInfo['@id'].replace(/\/?(info.json)?$/g, ''),
|
||||
sizes: iiifInfo.imageInfo.sizes === undefined ? undefined : iiifInfo.imageInfo.sizes.map(function(size) {
|
||||
return [size.width, size.height];
|
||||
}),
|
||||
tileSize: iiifInfo.imageInfo.tiles === undefined ? undefined : [
|
||||
iiifInfo.imageInfo.tiles.map(function(tile) {
|
||||
return tile.width;
|
||||
})[0],
|
||||
iiifInfo.imageInfo.tiles.map(function(tile) {
|
||||
return tile.height === undefined ? tile.width : tile.height;
|
||||
})[0]
|
||||
],
|
||||
resolutions: iiifInfo.imageInfo.tiles === undefined ? undefined :
|
||||
iiifInfo.imageInfo.tiles.map(function(tile) {
|
||||
return tile.scaleFactors;
|
||||
})[0],
|
||||
supports: [...levelProfile.supports, ...profileSupports],
|
||||
formats: [...levelProfile.formats, ...profileFormats],
|
||||
qualities: [...levelProfile.qualities, ...profileQualities]
|
||||
};
|
||||
}
|
||||
|
||||
function generateVersion3Options(iiifInfo) {
|
||||
const levelProfile = iiifInfo.getComplianceLevelSupportedFeatures();
|
||||
return {
|
||||
url: iiifInfo.imageInfo['id'],
|
||||
sizes: iiifInfo.imageInfo.sizes === undefined ? undefined : iiifInfo.imageInfo.sizes.map(function(size) {
|
||||
return [size.width, size.height];
|
||||
}),
|
||||
tileSize: iiifInfo.imageInfo.tiles === undefined ? undefined : [
|
||||
iiifInfo.imageInfo.tiles.map(function(tile) {
|
||||
return tile.width;
|
||||
})[0],
|
||||
iiifInfo.imageInfo.tiles.map(function(tile) {
|
||||
return tile.height;
|
||||
})[0]
|
||||
],
|
||||
resolutions: iiifInfo.imageInfo.tiles === undefined ? undefined :
|
||||
iiifInfo.imageInfo.tiles.map(function(tile) {
|
||||
return tile.scaleFactors;
|
||||
})[0],
|
||||
supports: iiifInfo.imageInfo.extraFeatures === undefined ? levelProfile.supports :
|
||||
[...levelProfile.supports, ...iiifInfo.imageInfo.extraFeatures],
|
||||
formats: iiifInfo.imageInfo.extraFormats === undefined ? levelProfile.formats :
|
||||
[...levelProfile.formats, ...iiifInfo.imageInfo.extraFormats],
|
||||
qualities: iiifInfo.imageInfo.extraQualities === undefined ? levelProfile.qualities :
|
||||
[...levelProfile.supports, ...iiifInfo.imageInfo.extraQualities],
|
||||
maxWidth: undefined,
|
||||
maxHeight: undefined,
|
||||
maxArea: undefined
|
||||
};
|
||||
}
|
||||
|
||||
const versionFunctions = {};
|
||||
versionFunctions[Versions.VERSION1] = generateVersion1Options;
|
||||
versionFunctions[Versions.VERSION2] = generateVersion2Options;
|
||||
versionFunctions[Versions.VERSION3] = generateVersion3Options;
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* Format for transforming IIIF Image API image information responses into
|
||||
* IIIF tile source ready options
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class IIIFInfo {
|
||||
|
||||
/**
|
||||
* @param {ImageInformationResponse1_0|ImageInformationResponse1_1|ImageInformationResponse2|ImageInformationResponse3|string} imageInfo
|
||||
* Deserialized image information JSON response object or JSON response as string
|
||||
*/
|
||||
constructor(imageInfo) {
|
||||
this.setImageInfo(imageInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object|string} imageInfo Deserialized image information JSON response
|
||||
* object or JSON response as string
|
||||
*/
|
||||
setImageInfo(imageInfo) {
|
||||
if (typeof imageInfo == 'string') {
|
||||
this.imageInfo = JSON.parse(imageInfo);
|
||||
} else {
|
||||
this.imageInfo = imageInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Versions} Major IIIF version.
|
||||
*/
|
||||
getImageApiVersion() {
|
||||
if (this.imageInfo === undefined) {
|
||||
return;
|
||||
}
|
||||
let context = this.imageInfo['@context'] || 'ol-no-context';
|
||||
if (typeof context == 'string') {
|
||||
context = [context];
|
||||
}
|
||||
for (let i = 0; i < context.length; i++) {
|
||||
switch (context[i]) {
|
||||
case 'http://library.stanford.edu/iiif/image-api/1.1/context.json':
|
||||
case 'http://iiif.io/api/image/1/context.json':
|
||||
return Versions.VERSION1;
|
||||
case 'http://iiif.io/api/image/2/context.json':
|
||||
return Versions.VERSION2;
|
||||
case 'http://iiif.io/api/image/3/context.json':
|
||||
return Versions.VERSION3;
|
||||
case 'ol-no-context':
|
||||
// Image API 1.0 has no '@context'
|
||||
if (this.getComplianceLevelEntryFromProfile(Versions.VERSION1) && this.imageInfo.identifier) {
|
||||
return Versions.VERSION1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
assert(false, 61);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Versions} version Optional IIIF image API version
|
||||
* @returns {string} Compliance level as it appears in the IIIF image information
|
||||
* response.
|
||||
*/
|
||||
getComplianceLevelEntryFromProfile(version) {
|
||||
if (this.imageInfo === undefined || this.imageInfo.profile === undefined) {
|
||||
return;
|
||||
}
|
||||
if (version === undefined) {
|
||||
version = this.getImageApiVersion();
|
||||
}
|
||||
switch (version) {
|
||||
case Versions.VERSION1:
|
||||
if (COMPLIANCE_VERSION1.test(this.imageInfo.profile)) {
|
||||
return this.imageInfo.profile;
|
||||
}
|
||||
break;
|
||||
case Versions.VERSION3:
|
||||
if (COMPLIANCE_VERSION3.test(this.imageInfo.profile)) {
|
||||
return this.imageInfo.profile;
|
||||
}
|
||||
break;
|
||||
case Versions.VERSION2:
|
||||
if (typeof this.imageInfo.profile === 'string' && COMPLIANCE_VERSION2.test(this.imageInfo.profile)) {
|
||||
return this.imageInfo.profile;
|
||||
}
|
||||
if (Array.isArray(this.imageInfo.profile) && this.imageInfo.profile.length > 0
|
||||
&& typeof this.imageInfo.profile[0] === 'string' && COMPLIANCE_VERSION2.test(this.imageInfo.profile[0])) {
|
||||
return this.imageInfo.profile[0];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Versions} version Optional IIIF image API version
|
||||
* @returns {string} Compliance level, on of 'level0', 'level1' or 'level2' or undefined
|
||||
*/
|
||||
getComplianceLevelFromProfile(version) {
|
||||
const complianceLevel = this.getComplianceLevelEntryFromProfile(version);
|
||||
if (complianceLevel === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const level = complianceLevel.match(/level[0-2](\.json)?$/g);
|
||||
return Array.isArray(level) ? level[0].replace('.json', '') : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {SupportedFeatures} Image formats, qualities and region / size calculation
|
||||
* methods that are supported by the IIIF service.
|
||||
*/
|
||||
getComplianceLevelSupportedFeatures() {
|
||||
if (this.imageInfo === undefined) {
|
||||
return;
|
||||
}
|
||||
const version = this.getImageApiVersion();
|
||||
const level = this.getComplianceLevelFromProfile(version);
|
||||
if (level === undefined) {
|
||||
return IIIF_PROFILE_VALUES['none']['none'];
|
||||
}
|
||||
return IIIF_PROFILE_VALUES[version][level];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PreferredOptions} opt_preferredOptions Optional options for preferred format and quality.
|
||||
* @returns {import("../source/IIIF.js").Options} IIIF tile source ready constructor options.
|
||||
*/
|
||||
getTileSourceOptions(opt_preferredOptions) {
|
||||
const options = opt_preferredOptions || {},
|
||||
version = this.getImageApiVersion();
|
||||
if (version === undefined) {
|
||||
return;
|
||||
}
|
||||
const imageOptions = version === undefined ? undefined : versionFunctions[version](this);
|
||||
if (imageOptions === undefined) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
url: imageOptions.url,
|
||||
version: version,
|
||||
size: [this.imageInfo.width, this.imageInfo.height],
|
||||
sizes: imageOptions.sizes,
|
||||
format: imageOptions.formats.includes(options.format) ? options.format : 'jpg',
|
||||
supports: imageOptions.supports,
|
||||
quality: options.quality && imageOptions.qualities.includes(options.quality) ?
|
||||
options.quality : imageOptions.qualities.includes('native') ? 'native' : 'default',
|
||||
resolutions: Array.isArray(imageOptions.resolutions) ? imageOptions.resolutions.sort(function(a, b) {
|
||||
return b - a;
|
||||
}) : undefined,
|
||||
tileSize: imageOptions.tileSize
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default IIIFInfo;
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
export {default as Circle} from './geom/Circle.js';
|
||||
export {default as Geometry} from './geom/Geometry.js';
|
||||
export {default as GeometryCollection} from './geom/GeometryCollection.js';
|
||||
export {default as LineString} from './geom/LineString.js';
|
||||
export {default as MultiLineString} from './geom/MultiLineString.js';
|
||||
export {default as MultiPoint} from './geom/MultiPoint.js';
|
||||
|
||||
@@ -5,6 +5,8 @@ import {createOrUpdate, forEachCorner, intersects} from '../extent.js';
|
||||
import GeometryType from './GeometryType.js';
|
||||
import SimpleGeometry from './SimpleGeometry.js';
|
||||
import {deflateCoordinate} from './flat/deflate.js';
|
||||
import {rotate, translate} from './flat/transform.js';
|
||||
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
@@ -143,7 +145,7 @@ class Circle extends SimpleGeometry {
|
||||
return true;
|
||||
}
|
||||
|
||||
return forEachCorner(extent, this.intersectsCoordinate, this);
|
||||
return forEachCorner(extent, this.intersectsCoordinate.bind(this));
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -212,6 +214,29 @@ class Circle extends SimpleGeometry {
|
||||
this.flatCoordinates[this.stride] = this.flatCoordinates[0] + radius;
|
||||
this.changed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
rotate(angle, anchor) {
|
||||
const center = this.getCenter();
|
||||
const stride = this.getStride();
|
||||
this.setCenter(rotate(center, 0, center.length, stride, angle, anchor, center));
|
||||
this.changed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @api
|
||||
*/
|
||||
translate(deltaX, deltaY) {
|
||||
const center = this.getCenter();
|
||||
const stride = this.getStride();
|
||||
this.setCenter(translate(center, 0, center.length, stride, deltaX, deltaY, center));
|
||||
this.changed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -73,6 +73,10 @@ class DragPan extends PointerInteraction {
|
||||
* @inheritDoc
|
||||
*/
|
||||
handleDragEvent(mapBrowserEvent) {
|
||||
if (!this.panning_) {
|
||||
this.panning_ = true;
|
||||
this.getMap().getView().beginInteraction();
|
||||
}
|
||||
const targetPointers = this.targetPointers;
|
||||
const centroid = centroidFromPointers(targetPointers);
|
||||
if (targetPointers.length == this.lastPointersCount_) {
|
||||
@@ -149,10 +153,6 @@ class DragPan extends PointerInteraction {
|
||||
if (view.getAnimating()) {
|
||||
view.cancelAnimations();
|
||||
}
|
||||
if (!this.panning_) {
|
||||
this.panning_ = true;
|
||||
this.getMap().getView().beginInteraction();
|
||||
}
|
||||
if (this.kinetic_) {
|
||||
this.kinetic_.begin();
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import {createEditingStyle} from '../style/Style.js';
|
||||
const ExtentEventType = {
|
||||
/**
|
||||
* Triggered after the extent is changed
|
||||
* @event ExtentEventType#extentchanged
|
||||
* @event ExtentEvent#extentchanged
|
||||
* @api
|
||||
*/
|
||||
EXTENTCHANGED: 'extentchanged'
|
||||
@@ -47,10 +47,10 @@ const ExtentEventType = {
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* Events emitted by {@link module:ol/interaction/Extent~ExtentInteraction} instances are
|
||||
* Events emitted by {@link module:ol/interaction/Extent~Extent} instances are
|
||||
* instances of this type.
|
||||
*/
|
||||
class ExtentInteractionEvent extends Event {
|
||||
class ExtentEvent extends Event {
|
||||
|
||||
/**
|
||||
* @param {import("../extent.js").Extent} extent the new extent
|
||||
@@ -75,10 +75,10 @@ class ExtentInteractionEvent extends Event {
|
||||
* Once drawn, the vector box can be modified by dragging its vertices or edges.
|
||||
* This interaction is only supported for mouse devices.
|
||||
*
|
||||
* @fires Event
|
||||
* @fires ExtentEvent
|
||||
* @api
|
||||
*/
|
||||
class ExtentInteraction extends PointerInteraction {
|
||||
class Extent extends PointerInteraction {
|
||||
/**
|
||||
* @param {Options=} opt_options Options.
|
||||
*/
|
||||
@@ -399,7 +399,7 @@ class ExtentInteraction extends PointerInteraction {
|
||||
//Null extent means no bbox
|
||||
this.extent_ = extent ? extent : null;
|
||||
this.createOrUpdateExtentFeature_(extent);
|
||||
this.dispatchEvent(new ExtentInteractionEvent(this.extent_));
|
||||
this.dispatchEvent(new ExtentEvent(this.extent_));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,4 +470,4 @@ function getSegments(extent) {
|
||||
}
|
||||
|
||||
|
||||
export default ExtentInteraction;
|
||||
export default Extent;
|
||||
|
||||
@@ -8,13 +8,6 @@ import Interaction, {zoomByDelta} from './Interaction.js';
|
||||
import {clamp} from '../math.js';
|
||||
|
||||
|
||||
/**
|
||||
* Maximum mouse wheel delta.
|
||||
* @type {number}
|
||||
*/
|
||||
const MAX_DELTA = 1;
|
||||
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
@@ -30,6 +23,7 @@ export const Mode = {
|
||||
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
|
||||
* boolean to indicate whether that event should be handled. Default is
|
||||
* {@link module:ol/events/condition~always}.
|
||||
* @property {number} [maxDelta=1] Maximum mouse wheel delta.
|
||||
* @property {number} [duration=250] Animation duration in milliseconds.
|
||||
* @property {number} [timeout=80] Mouse wheel timeout duration in milliseconds.
|
||||
* @property {boolean} [useAnchor=true] Enable zooming using the mouse's
|
||||
@@ -65,6 +59,12 @@ class MouseWheelZoom extends Interaction {
|
||||
*/
|
||||
this.lastDelta_ = 0;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.maxDelta_ = options.maxDelta !== undefined ? options.maxDelta : 1;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
@@ -235,8 +235,7 @@ class MouseWheelZoom extends Interaction {
|
||||
if (view.getAnimating()) {
|
||||
view.cancelAnimations();
|
||||
}
|
||||
const maxDelta = MAX_DELTA;
|
||||
const delta = clamp(this.totalDelta_, -maxDelta, maxDelta);
|
||||
const delta = clamp(this.totalDelta_, -this.maxDelta_, this.maxDelta_);
|
||||
zoomByDelta(view, -delta, this.lastAnchor_, this.duration_);
|
||||
this.mode_ = undefined;
|
||||
this.totalDelta_ = 0;
|
||||
|
||||
@@ -83,19 +83,20 @@ class BaseLayer extends BaseObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {boolean=} opt_managed Layer is managed.
|
||||
* @return {import("./Layer.js").State} Layer state.
|
||||
*/
|
||||
getLayerState() {
|
||||
getLayerState(opt_managed) {
|
||||
/** @type {import("./Layer.js").State} */
|
||||
const state = this.state_ || /** @type {?} */ ({
|
||||
layer: this,
|
||||
managed: true
|
||||
managed: opt_managed === undefined ? true : opt_managed
|
||||
});
|
||||
state.opacity = clamp(Math.round(this.getOpacity() * 100) / 100, 0, 1);
|
||||
state.sourceState = this.getSourceState();
|
||||
state.visible = this.getVisible();
|
||||
state.extent = this.getExtent();
|
||||
state.zIndex = this.getZIndex() || 0;
|
||||
state.zIndex = this.getZIndex() || (state.managed === false ? Infinity : 0);
|
||||
state.maxResolution = this.getMaxResolution();
|
||||
state.minResolution = Math.max(this.getMinResolution(), 0);
|
||||
this.state_ = state;
|
||||
|
||||
@@ -8,6 +8,7 @@ import {assign} from '../obj.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {string} [className='ol-layer'] A CSS class name to set to the layer element.
|
||||
* @property {number} [opacity=1] Opacity (0, 1).
|
||||
* @property {boolean} [visible=true] Visibility.
|
||||
* @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering. The layer will not be
|
||||
|
||||
@@ -32,8 +32,10 @@ import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style.
|
||||
* temporary layers. The standard way to add a layer to a map and have it managed by the map is to
|
||||
* use {@link module:ol/Map#addLayer}.
|
||||
* @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all
|
||||
* image and text styles, and the priority is defined by the z-index of the style. Lower z-index
|
||||
* means higher priority.
|
||||
* image and text styles of all Vector and VectorTile layers that have set this to `true`. The priority
|
||||
* is defined by the z-index of the layer, the `zIndex` of the style and the render order of features.
|
||||
* Higher z-index means higher priority. Within the same z-index, a feature rendered before another has
|
||||
* higher priority.
|
||||
* @property {import("../style/Style.js").StyleLike} [style] Layer style. See
|
||||
* {@link module:ol/style} for default style which will be used if this is not defined.
|
||||
* @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will
|
||||
|
||||
@@ -27,7 +27,6 @@ import WebGLPointsLayerRenderer from '../renderer/webgl/PointsLayer';
|
||||
* of the heatmap, specified as an array of CSS color strings.
|
||||
* @property {number} [radius=8] Radius size in pixels.
|
||||
* @property {number} [blur=15] Blur size in pixels.
|
||||
* @property {number} [shadow=250] Shadow size in pixels.
|
||||
* @property {string|function(import("../Feature.js").default):number} [weight='weight'] The feature
|
||||
* attribute to use for the weight or a function that returns a weight from a feature. Weight values
|
||||
* should range from 0 to 1 (and values outside will be clamped to that range).
|
||||
@@ -75,7 +74,6 @@ class Heatmap extends VectorLayer {
|
||||
delete baseOptions.gradient;
|
||||
delete baseOptions.radius;
|
||||
delete baseOptions.blur;
|
||||
delete baseOptions.shadow;
|
||||
delete baseOptions.weight;
|
||||
super(baseOptions);
|
||||
|
||||
@@ -85,24 +83,6 @@ class Heatmap extends VectorLayer {
|
||||
*/
|
||||
this.gradient_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.shadow_ = options.shadow !== undefined ? options.shadow : 250;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {string|undefined}
|
||||
*/
|
||||
this.circleImage_ = undefined;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Array<Array<import("../style/Style.js").default>>}
|
||||
*/
|
||||
this.styleCache_ = null;
|
||||
|
||||
listen(this,
|
||||
getChangeEventType(Property.GRADIENT),
|
||||
this.handleGradientChanged_, this);
|
||||
@@ -273,9 +253,7 @@ class Heatmap extends VectorLayer {
|
||||
}
|
||||
}
|
||||
],
|
||||
opacityCallback: function(feature) {
|
||||
return this.weightFunction_(feature);
|
||||
}.bind(this)
|
||||
opacityCallback: this.weightFunction_
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,12 +212,7 @@ class Layer extends BaseLayer {
|
||||
if (map) {
|
||||
this.mapPrecomposeKey_ = listen(map, RenderEventType.PRECOMPOSE, function(evt) {
|
||||
const renderEvent = /** @type {import("../render/Event.js").default} */ (evt);
|
||||
const layerState = this.getLayerState();
|
||||
layerState.managed = false;
|
||||
if (this.getZIndex() === undefined) {
|
||||
layerState.zIndex = Infinity;
|
||||
}
|
||||
renderEvent.frameState.layerStatesArray.push(layerState);
|
||||
renderEvent.frameState.layerStatesArray.push(this.getLayerState(false));
|
||||
}, this);
|
||||
this.mapRenderKey_ = listen(this, EventType.CHANGE, map.render, map);
|
||||
this.changed();
|
||||
|
||||
@@ -30,9 +30,9 @@ import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer.
|
||||
* this layer in its layers collection, and the layer will be rendered on top. This is useful for
|
||||
* temporary layers. The standard way to add a layer to a map and have it managed by the map is to
|
||||
* use {@link module:ol/Map#addLayer}.
|
||||
* @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all
|
||||
* image and text styles, and the priority is defined by the z-index of the style. Lower z-index
|
||||
* means higher priority.
|
||||
* @property {boolean} [declutter=false] Declutter images and text on this layer. The priority is defined
|
||||
* by the `zIndex` of the style and the render order of features. Higher z-index means higher priority.
|
||||
* Within the same z-index, a feature rendered before another has higher priority.
|
||||
* @property {import("../style/Style.js").StyleLike} [style] Layer style. See
|
||||
* {@link module:ol/style} for default style which will be used if this is not defined.
|
||||
* @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will
|
||||
|
||||
@@ -46,8 +46,10 @@ import {assign} from '../obj.js';
|
||||
* temporary layers. The standard way to add a layer to a map and have it managed by the map is to
|
||||
* use {@link module:ol/Map#addLayer}.
|
||||
* @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all
|
||||
* image and text styles, and the priority is defined by the z-index of the style. Lower z-index
|
||||
* means higher priority.
|
||||
* image and text styles of all Vector and VectorTile layers that have set this to `true`. The priority
|
||||
* is defined by the z-index of the layer, the `zIndex` of the style and the render order of features.
|
||||
* Higher z-index means higher priority. Within the same z-index, a feature rendered before another has
|
||||
* higher priority.
|
||||
* @property {import("../style/Style.js").StyleLike} [style] Layer style. See
|
||||
* {@link module:ol/style} for default style which will be used if this is not defined.
|
||||
* @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will be
|
||||
|
||||
@@ -110,3 +110,23 @@ export function getRenderPixel(event, pixel) {
|
||||
applyTransform(event.inversePixelTransform.slice(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("./PluggableMap.js").FrameState} frameState Frame state.
|
||||
* @param {?} declutterTree Declutter tree.
|
||||
* @returns {?} Declutter tree.
|
||||
*/
|
||||
export function renderDeclutterItems(frameState, declutterTree) {
|
||||
if (declutterTree) {
|
||||
declutterTree.clear();
|
||||
}
|
||||
const items = frameState.declutterItems;
|
||||
for (let z = items.length - 1; z >= 0; --z) {
|
||||
const zIndexItems = items[z];
|
||||
for (let i = 0, ii = zIndexItems.length; i < ii; i += 3) {
|
||||
declutterTree = zIndexItems[i].renderDeclutter(zIndexItems[i + 1], zIndexItems[i + 2], declutterTree);
|
||||
}
|
||||
}
|
||||
items.length = 0;
|
||||
return declutterTree;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
import {createCanvasContext2D} from '../../dom.js';
|
||||
import {labelCache, defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js';
|
||||
import Disposable from '../../Disposable.js';
|
||||
import rbush from 'rbush';
|
||||
|
||||
|
||||
/**
|
||||
@@ -58,15 +59,10 @@ class Executor extends Disposable {
|
||||
* @param {number} resolution Resolution.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @param {boolean} overlaps The replay can have overlapping geometries.
|
||||
* @param {?} declutterTree Declutter tree.
|
||||
* @param {SerializableInstructions} instructions The serializable instructions
|
||||
*/
|
||||
constructor(resolution, pixelRatio, overlaps, declutterTree, instructions) {
|
||||
constructor(resolution, pixelRatio, overlaps, instructions) {
|
||||
super();
|
||||
/**
|
||||
* @type {?}
|
||||
*/
|
||||
this.declutterTree = declutterTree;
|
||||
|
||||
/**
|
||||
* @protected
|
||||
@@ -93,6 +89,11 @@ class Executor extends Disposable {
|
||||
*/
|
||||
this.alignFill_;
|
||||
|
||||
/**
|
||||
* @type {Array<*>}
|
||||
*/
|
||||
this.declutterItems = [];
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {Array<*>}
|
||||
@@ -412,8 +413,10 @@ class Executor extends Disposable {
|
||||
/**
|
||||
* @param {import("../canvas.js").DeclutterGroup} declutterGroup Declutter group.
|
||||
* @param {import("../../Feature.js").FeatureLike} feature Feature.
|
||||
* @param {?} declutterTree Declutter tree.
|
||||
* @return {?} Declutter tree.
|
||||
*/
|
||||
renderDeclutter_(declutterGroup, feature) {
|
||||
renderDeclutter(declutterGroup, feature, declutterTree) {
|
||||
if (declutterGroup && declutterGroup.length > 5) {
|
||||
const groupCount = declutterGroup[4];
|
||||
if (groupCount == 1 || groupCount == declutterGroup.length - 5) {
|
||||
@@ -425,8 +428,11 @@ class Executor extends Disposable {
|
||||
maxY: /** @type {number} */ (declutterGroup[3]),
|
||||
value: feature
|
||||
};
|
||||
if (!this.declutterTree.collides(box)) {
|
||||
this.declutterTree.insert(box);
|
||||
if (!declutterTree) {
|
||||
declutterTree = rbush(9, undefined);
|
||||
}
|
||||
if (!declutterTree.collides(box)) {
|
||||
declutterTree.insert(box);
|
||||
for (let j = 5, jj = declutterGroup.length; j < jj; ++j) {
|
||||
const declutterData = /** @type {Array} */ (declutterGroup[j]);
|
||||
if (declutterData) {
|
||||
@@ -443,6 +449,7 @@ class Executor extends Disposable {
|
||||
createOrUpdateEmpty(declutterGroup);
|
||||
}
|
||||
}
|
||||
return declutterTree;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -497,6 +504,7 @@ class Executor extends Disposable {
|
||||
featureCallback,
|
||||
opt_hitExtent
|
||||
) {
|
||||
this.declutterItems.length = 0;
|
||||
/** @type {Array<number>} */
|
||||
let pixelCoordinates;
|
||||
if (this.pixelCoordinates_ && equals(transform, this.renderedTransform_)) {
|
||||
@@ -670,7 +678,7 @@ class Executor extends Disposable {
|
||||
backgroundFill ? /** @type {Array<*>} */ (lastFillInstruction) : null,
|
||||
backgroundStroke ? /** @type {Array<*>} */ (lastStrokeInstruction) : null);
|
||||
}
|
||||
this.renderDeclutter_(declutterGroup, feature);
|
||||
this.declutterItems.push(this, declutterGroup, feature);
|
||||
++i;
|
||||
break;
|
||||
case CanvasInstruction.DRAW_CHARS:
|
||||
@@ -739,7 +747,7 @@ class Executor extends Disposable {
|
||||
}
|
||||
}
|
||||
}
|
||||
this.renderDeclutter_(declutterGroup, feature);
|
||||
this.declutterItems.push(this, declutterGroup, feature);
|
||||
++i;
|
||||
break;
|
||||
case CanvasInstruction.END_GEOMETRY:
|
||||
|
||||
@@ -35,18 +35,12 @@ class ExecutorGroup extends Disposable {
|
||||
* @param {number} resolution Resolution.
|
||||
* @param {number} pixelRatio Pixel ratio.
|
||||
* @param {boolean} overlaps The executor group can have overlapping geometries.
|
||||
* @param {?} declutterTree Declutter tree for declutter processing in postrender.
|
||||
* @param {!Object<string, !Object<BuilderType, import("./Builder.js").SerializableInstructions>>} allInstructions
|
||||
* The serializable instructions.
|
||||
* @param {number=} opt_renderBuffer Optional rendering buffer.
|
||||
*/
|
||||
constructor(maxExtent, resolution, pixelRatio, overlaps, declutterTree, allInstructions, opt_renderBuffer) {
|
||||
constructor(maxExtent, resolution, pixelRatio, overlaps, allInstructions, opt_renderBuffer) {
|
||||
super();
|
||||
/**
|
||||
* Declutter tree.
|
||||
* @private
|
||||
*/
|
||||
this.declutterTree_ = declutterTree;
|
||||
|
||||
/**
|
||||
* @private
|
||||
@@ -128,7 +122,7 @@ class ExecutorGroup extends Disposable {
|
||||
for (const builderType in instructionByZindex) {
|
||||
const instructions = instructionByZindex[builderType];
|
||||
executors[builderType] = new Executor(
|
||||
this.resolution_, this.pixelRatio_, this.overlaps_, this.declutterTree_, instructions);
|
||||
this.resolution_, this.pixelRatio_, this.overlaps_, instructions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,7 +166,7 @@ class ExecutorGroup extends Disposable {
|
||||
* @param {number} hitTolerance Hit tolerance in pixels.
|
||||
* @param {Object<string, boolean>} skippedFeaturesHash Ids of features to skip.
|
||||
* @param {function(import("../../Feature.js").FeatureLike): T} callback Feature callback.
|
||||
* @param {Object<string, import("../canvas.js").DeclutterGroup>} declutterReplays Declutter replays.
|
||||
* @param {Array<import("../../Feature.js").FeatureLike>} declutteredFeatures Decluttered features.
|
||||
* @return {T|undefined} Callback result.
|
||||
* @template T
|
||||
*/
|
||||
@@ -183,7 +177,7 @@ class ExecutorGroup extends Disposable {
|
||||
hitTolerance,
|
||||
skippedFeaturesHash,
|
||||
callback,
|
||||
declutterReplays
|
||||
declutteredFeatures
|
||||
) {
|
||||
|
||||
hitTolerance = Math.round(hitTolerance);
|
||||
@@ -213,12 +207,6 @@ class ExecutorGroup extends Disposable {
|
||||
}
|
||||
|
||||
const mask = getCircleArray(hitTolerance);
|
||||
let declutteredFeatures;
|
||||
if (this.declutterTree_) {
|
||||
declutteredFeatures = this.declutterTree_.all().map(function(entry) {
|
||||
return entry.value;
|
||||
});
|
||||
}
|
||||
|
||||
let builderType;
|
||||
|
||||
@@ -261,20 +249,10 @@ class ExecutorGroup extends Disposable {
|
||||
builderType = ORDER[j];
|
||||
executor = executors[builderType];
|
||||
if (executor !== undefined) {
|
||||
if (declutterReplays &&
|
||||
(builderType == BuilderType.IMAGE || builderType == BuilderType.TEXT)) {
|
||||
const declutter = declutterReplays[zIndexKey];
|
||||
if (!declutter) {
|
||||
declutterReplays[zIndexKey] = [executor, transform.slice(0)];
|
||||
} else {
|
||||
declutter.push(executor, transform.slice(0));
|
||||
}
|
||||
} else {
|
||||
result = executor.executeHitDetection(context, transform, rotation,
|
||||
skippedFeaturesHash, featureCallback, hitExtent);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = executor.executeHitDetection(context, transform, rotation,
|
||||
skippedFeaturesHash, featureCallback, hitExtent);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -446,14 +424,20 @@ export function getCircleArray(radius) {
|
||||
* @param {CanvasRenderingContext2D} context Context.
|
||||
* @param {number} rotation Rotation.
|
||||
* @param {boolean} snapToPixel Snap point symbols and text to integer pixels.
|
||||
* @param {Array<Array<*>>} declutterItems Declutter items.
|
||||
*/
|
||||
export function replayDeclutter(declutterReplays, context, rotation, snapToPixel) {
|
||||
export function replayDeclutter(declutterReplays, context, rotation, snapToPixel, declutterItems) {
|
||||
const zs = Object.keys(declutterReplays).map(Number).sort(numberSafeCompareFunction);
|
||||
const skippedFeatureUids = {};
|
||||
for (let z = 0, zz = zs.length; z < zz; ++z) {
|
||||
const executorData = declutterReplays[zs[z].toString()];
|
||||
let currentExecutor;
|
||||
for (let i = 0, ii = executorData.length; i < ii;) {
|
||||
const executor = executorData[i++];
|
||||
if (executor !== currentExecutor) {
|
||||
currentExecutor = executor;
|
||||
declutterItems.push(executor.declutterItems);
|
||||
}
|
||||
const transform = executorData[i++];
|
||||
executor.execute(context, transform, rotation, skippedFeatureUids, snapToPixel);
|
||||
}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/**
|
||||
* @module ol/render/webgl
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
export const DEFAULT_FONT = '10px sans-serif';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {import("../color.js").Color}
|
||||
*/
|
||||
export const DEFAULT_FILLSTYLE = [0.0, 0.0, 0.0, 1.0];
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
export const DEFAULT_LINECAP = 'round';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {Array<number>}
|
||||
*/
|
||||
export const DEFAULT_LINEDASH = [];
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const DEFAULT_LINEDASHOFFSET = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {string}
|
||||
*/
|
||||
export const DEFAULT_LINEJOIN = 'round';
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const DEFAULT_MITERLIMIT = 10;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {import("../color.js").Color}
|
||||
*/
|
||||
export const DEFAULT_STROKESTYLE = [0.0, 0.0, 0.0, 1.0];
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const DEFAULT_TEXTALIGN = 0.5;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const DEFAULT_TEXTBASELINE = 0.5;
|
||||
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const DEFAULT_LINEWIDTH = 1;
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
export const EPSILON = Number.EPSILON || 2.220446049250313e-16;
|
||||
|
||||
/**
|
||||
* Calculates the orientation of a triangle based on the determinant method.
|
||||
* @param {number} x1 First X coordinate.
|
||||
* @param {number} y1 First Y coordinate.
|
||||
* @param {number} x2 Second X coordinate.
|
||||
* @param {number} y2 Second Y coordinate.
|
||||
* @param {number} x3 Third X coordinate.
|
||||
* @param {number} y3 Third Y coordinate.
|
||||
* @return {boolean|undefined} Triangle is clockwise.
|
||||
*/
|
||||
export const triangleIsCounterClockwise = function(x1, y1, x2, y2, x3, y3) {
|
||||
const area = (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1);
|
||||
return (area <= EPSILON && area >= -EPSILON) ?
|
||||
undefined : area > 0;
|
||||
};
|
||||
|
||||
@@ -94,11 +94,12 @@ class CompositeMapRenderer extends MapRenderer {
|
||||
if (element) {
|
||||
const zIndex = layerState.zIndex;
|
||||
if (zIndex !== element.style.zIndex) {
|
||||
element.style.zIndex = zIndex;
|
||||
element.style.zIndex = zIndex === Infinity ? Number.MAX_SAFE_INTEGER : zIndex;
|
||||
}
|
||||
this.children_.push(element);
|
||||
}
|
||||
}
|
||||
super.renderFrame(frameState);
|
||||
|
||||
replaceChildren(this.element_, this.children_);
|
||||
|
||||
|
||||
@@ -89,10 +89,11 @@ class LayerRenderer extends Observable {
|
||||
* @param {import("../PluggableMap.js").FrameState} frameState Frame state.
|
||||
* @param {number} hitTolerance Hit tolerance in pixels.
|
||||
* @param {function(import("../Feature.js").FeatureLike, import("../layer/Layer.js").default): T} callback Feature callback.
|
||||
* @param {Array<import("../Feature.js").FeatureLike>} declutteredFeatures Decluttered features.
|
||||
* @return {T|void} Callback result.
|
||||
* @template T
|
||||
*/
|
||||
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback) {}
|
||||
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {}
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
|
||||
@@ -10,6 +10,7 @@ import {TRUE} from '../functions.js';
|
||||
import {visibleAtResolution} from '../layer/Layer.js';
|
||||
import {shared as iconImageCache} from '../style/IconImageCache.js';
|
||||
import {compose as composeTransform, makeInverse} from '../transform.js';
|
||||
import {renderDeclutterItems} from '../render.js';
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
@@ -28,6 +29,11 @@ class MapRenderer extends Disposable {
|
||||
*/
|
||||
this.map_ = map;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this.declutterTree_ = null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {!Object<string, import("./Layer.js").default>}
|
||||
@@ -133,6 +139,12 @@ class MapRenderer extends Disposable {
|
||||
|
||||
const layerStates = frameState.layerStatesArray;
|
||||
const numLayers = layerStates.length;
|
||||
let declutteredFeatures;
|
||||
if (this.declutterTree_) {
|
||||
declutteredFeatures = this.declutterTree_.all().map(function(entry) {
|
||||
return entry.value;
|
||||
});
|
||||
}
|
||||
let i;
|
||||
for (i = numLayers - 1; i >= 0; --i) {
|
||||
const layerState = layerStates[i];
|
||||
@@ -144,7 +156,7 @@ class MapRenderer extends Disposable {
|
||||
const callback = forEachFeatureAtCoordinate.bind(null, layerState.managed);
|
||||
result = layerRenderer.forEachFeatureAtCoordinate(
|
||||
source.getWrapX() ? translatedCoordinate : coordinate,
|
||||
frameState, hitTolerance, callback);
|
||||
frameState, hitTolerance, callback, declutteredFeatures);
|
||||
}
|
||||
if (result) {
|
||||
return result;
|
||||
@@ -252,11 +264,10 @@ class MapRenderer extends Disposable {
|
||||
|
||||
/**
|
||||
* Render.
|
||||
* @abstract
|
||||
* @param {?import("../PluggableMap.js").FrameState} frameState Frame state.
|
||||
*/
|
||||
renderFrame(frameState) {
|
||||
abstract();
|
||||
this.declutterTree_ = renderDeclutterItems(frameState, this.declutterTree_);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@ import CanvasVectorLayerRenderer from './VectorLayer.js';
|
||||
import {listen} from '../../events.js';
|
||||
import EventType from '../../events/EventType.js';
|
||||
import ImageState from '../../ImageState.js';
|
||||
import {renderDeclutterItems} from '../../render.js';
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
@@ -72,6 +73,7 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
|
||||
let skippedFeatures = this.skippedFeatures_;
|
||||
const context = vectorRenderer.context;
|
||||
const imageFrameState = /** @type {import("../../PluggableMap.js").FrameState} */ (assign({}, frameState, {
|
||||
declutterItems: [],
|
||||
size: [
|
||||
getWidth(renderedExtent) / viewResolution,
|
||||
getHeight(renderedExtent) / viewResolution
|
||||
@@ -86,6 +88,7 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
|
||||
(vectorRenderer.replayGroupChanged ||
|
||||
!equals(skippedFeatures, newSkippedFeatures))) {
|
||||
vectorRenderer.renderFrame(imageFrameState, layerState);
|
||||
renderDeclutterItems(imageFrameState, null);
|
||||
skippedFeatures = newSkippedFeatures;
|
||||
callback();
|
||||
}
|
||||
@@ -123,11 +126,11 @@ class CanvasVectorImageLayerRenderer extends CanvasImageLayerRenderer {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback) {
|
||||
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {
|
||||
if (this.vectorRenderer_) {
|
||||
return this.vectorRenderer_.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback);
|
||||
return this.vectorRenderer_.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures);
|
||||
} else {
|
||||
return super.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback);
|
||||
return super.forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,10 @@ import {getUid} from '../../util.js';
|
||||
import ViewHint from '../../ViewHint.js';
|
||||
import {listen, unlisten} from '../../events.js';
|
||||
import EventType from '../../events/EventType.js';
|
||||
import rbush from 'rbush';
|
||||
import {buffer, createEmpty, containsExtent, getWidth} from '../../extent.js';
|
||||
import {labelCache} from '../../render/canvas.js';
|
||||
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
|
||||
import ExecutorGroup from '../../render/canvas/ExecutorGroup.js';
|
||||
import ExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
|
||||
import CanvasLayerRenderer from './Layer.js';
|
||||
import {defaultOrder as defaultRenderOrder, getTolerance as getRenderTolerance, getSquaredTolerance as getSquaredRenderTolerance, renderFeature} from '../vector.js';
|
||||
import {toString as transformToString, makeScale, makeInverse} from '../../transform.js';
|
||||
@@ -28,12 +27,6 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
|
||||
super(vectorLayer);
|
||||
|
||||
/**
|
||||
* Declutter tree.
|
||||
* @private
|
||||
*/
|
||||
this.declutterTree_ = vectorLayer.getDeclutter() ? rbush(9, undefined) : null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {boolean}
|
||||
@@ -138,17 +131,14 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
this.clip(context, frameState, clipExtent);
|
||||
}
|
||||
|
||||
if (this.declutterTree_) {
|
||||
this.declutterTree_.clear();
|
||||
}
|
||||
|
||||
|
||||
const viewHints = frameState.viewHints;
|
||||
const snapToPixel = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]);
|
||||
|
||||
const transform = this.getRenderTransform(frameState, width, height, 0);
|
||||
const skippedFeatureUids = layerState.managed ? frameState.skippedFeatureUids : {};
|
||||
replayGroup.execute(context, transform, rotation, skippedFeatureUids, snapToPixel);
|
||||
const declutterReplays = /** @type {import("../../layer/Vector.js").default} */ (this.getLayer()).getDeclutter() ? {} : null;
|
||||
replayGroup.execute(context, transform, rotation, skippedFeatureUids, snapToPixel, undefined, declutterReplays);
|
||||
|
||||
if (vectorSource.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, extent)) {
|
||||
let startX = extent[0];
|
||||
@@ -159,7 +149,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
--world;
|
||||
offsetX = worldWidth * world;
|
||||
const transform = this.getRenderTransform(frameState, width, height, offsetX);
|
||||
replayGroup.execute(context, transform, rotation, skippedFeatureUids, snapToPixel);
|
||||
replayGroup.execute(context, transform, rotation, skippedFeatureUids, snapToPixel, undefined, declutterReplays);
|
||||
startX += worldWidth;
|
||||
}
|
||||
world = 0;
|
||||
@@ -168,10 +158,15 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
++world;
|
||||
offsetX = worldWidth * world;
|
||||
const transform = this.getRenderTransform(frameState, width, height, offsetX);
|
||||
replayGroup.execute(context, transform, rotation, skippedFeatureUids, snapToPixel);
|
||||
replayGroup.execute(context, transform, rotation, skippedFeatureUids, snapToPixel, undefined, declutterReplays);
|
||||
startX -= worldWidth;
|
||||
}
|
||||
}
|
||||
if (declutterReplays) {
|
||||
const viewHints = frameState.viewHints;
|
||||
const hifi = !(viewHints[ViewHint.ANIMATING] || viewHints[ViewHint.INTERACTING]);
|
||||
replayDeclutter(declutterReplays, context, rotation, hifi, frameState.declutterItems);
|
||||
}
|
||||
|
||||
if (clipped) {
|
||||
context.restore();
|
||||
@@ -190,7 +185,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) {
|
||||
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {
|
||||
if (!this.replayGroup_) {
|
||||
return undefined;
|
||||
} else {
|
||||
@@ -208,9 +203,9 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
const key = getUid(feature);
|
||||
if (!(key in features)) {
|
||||
features[key] = true;
|
||||
return callback.call(thisArg, feature, layer);
|
||||
return callback(feature, layer);
|
||||
}
|
||||
}, null);
|
||||
}, declutteredFeatures);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -299,7 +294,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
|
||||
const replayGroup = new CanvasBuilderGroup(
|
||||
getRenderTolerance(resolution, pixelRatio), extent, resolution,
|
||||
pixelRatio, !!this.declutterTree_);
|
||||
pixelRatio, vectorLayer.getDeclutter());
|
||||
|
||||
vectorSource.loadFeatures(extent, resolution, projection);
|
||||
|
||||
@@ -339,7 +334,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
|
||||
const replayGroupInstructions = replayGroup.finish();
|
||||
const executorGroup = new ExecutorGroup(extent, resolution,
|
||||
pixelRatio, vectorSource.getOverlaps(), this.declutterTree_,
|
||||
pixelRatio, vectorSource.getOverlaps(),
|
||||
replayGroupInstructions, vectorLayer.getRenderBuffer());
|
||||
|
||||
this.renderedResolution_ = resolution;
|
||||
|
||||
@@ -7,7 +7,6 @@ import TileState from '../../TileState.js';
|
||||
import ViewHint from '../../ViewHint.js';
|
||||
import {listen, unlisten, unlistenByKey} from '../../events.js';
|
||||
import EventType from '../../events/EventType.js';
|
||||
import rbush from 'rbush';
|
||||
import {buffer, containsCoordinate, equals, getIntersection, getTopLeft, intersects} from '../../extent.js';
|
||||
import VectorTileRenderType from '../../layer/VectorTileRenderType.js';
|
||||
import ReplayType from '../../render/canvas/BuilderType.js';
|
||||
@@ -103,12 +102,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
*/
|
||||
this.inverseOverlayPixelTransform_ = createTransform();
|
||||
|
||||
/**
|
||||
* Declutter tree.
|
||||
* @private
|
||||
*/
|
||||
this.declutterTree_ = layer.getDeclutter() ? rbush(9, undefined) : null;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {boolean}
|
||||
@@ -138,8 +131,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
*/
|
||||
this.tmpTransform_ = createTransform();
|
||||
|
||||
// Use closest resolution.
|
||||
this.zDirection = 0;
|
||||
// Use nearest lower resolution.
|
||||
this.zDirection = 1;
|
||||
|
||||
listen(labelCache, EventType.CLEAR, this.handleFontsChanged_, this);
|
||||
|
||||
@@ -274,7 +267,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
buffer(sharedExtent, layer.getRenderBuffer() * resolution, this.tmpExtent);
|
||||
builderState.dirty = false;
|
||||
const builderGroup = new CanvasBuilderGroup(0, sharedExtent, resolution,
|
||||
pixelRatio, !!this.declutterTree_);
|
||||
pixelRatio, layer.getDeclutter());
|
||||
const squaredTolerance = getSquaredRenderTolerance(resolution, pixelRatio);
|
||||
|
||||
/**
|
||||
@@ -310,7 +303,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
null :
|
||||
sharedExtent;
|
||||
const renderingReplayGroup = new CanvasExecutorGroup(replayExtent, resolution,
|
||||
pixelRatio, source.getOverlaps(), this.declutterTree_, executorGroupInstructions, layer.getRenderBuffer());
|
||||
pixelRatio, source.getOverlaps(), executorGroupInstructions, layer.getRenderBuffer());
|
||||
tile.executorGroups[layerUid].push(renderingReplayGroup);
|
||||
}
|
||||
builderState.renderedRevision = revision;
|
||||
@@ -322,11 +315,12 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, thisArg) {
|
||||
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {
|
||||
const resolution = frameState.viewState.resolution;
|
||||
const rotation = frameState.viewState.rotation;
|
||||
hitTolerance = hitTolerance == undefined ? 0 : hitTolerance;
|
||||
const layer = this.getLayer();
|
||||
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
|
||||
const declutter = layer.getDeclutter();
|
||||
const source = layer.getSource();
|
||||
const tileGrid = source.getTileGridForProjection(frameState.viewState.projection);
|
||||
/** @type {!Object<string, boolean>} */
|
||||
@@ -338,7 +332,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
let i, ii;
|
||||
for (i = 0, ii = renderedTiles.length; i < ii; ++i) {
|
||||
const tile = renderedTiles[i];
|
||||
if (!this.declutterTree_) {
|
||||
if (!declutter) {
|
||||
// When not decluttering, we only need to consider the tile that contains the given
|
||||
// coordinate, because each feature will be rendered for each tile that contains it.
|
||||
const tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord);
|
||||
@@ -361,9 +355,9 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
}
|
||||
if (!(key in features)) {
|
||||
features[key] = true;
|
||||
return callback.call(thisArg, feature, layer);
|
||||
return callback(feature, layer);
|
||||
}
|
||||
}, null);
|
||||
}, declutteredFeatures);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
@@ -464,9 +458,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
context.clearRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
if (declutterReplays) {
|
||||
this.declutterTree_.clear();
|
||||
}
|
||||
const tiles = this.renderedTiles;
|
||||
const tileGrid = source.getTileGridForProjection(frameState.viewState.projection);
|
||||
const clips = [];
|
||||
@@ -522,7 +513,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
}
|
||||
}
|
||||
if (declutterReplays) {
|
||||
replayDeclutter(declutterReplays, context, rotation, hifi);
|
||||
replayDeclutter(declutterReplays, context, rotation, hifi, frameState.declutterItems);
|
||||
}
|
||||
|
||||
const opacity = layerState.opacity;
|
||||
@@ -552,9 +543,6 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
frameState.animate = true;
|
||||
delete this.renderTileImageQueue_[uid];
|
||||
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
|
||||
if (this.declutterTree_ && layer.getRenderMode() === VectorTileRenderType.IMAGE) {
|
||||
this.declutterTree_.clear();
|
||||
}
|
||||
const viewState = frameState.viewState;
|
||||
const tileGrid = layer.getSource().getTileGridForProjection(viewState.projection);
|
||||
const tileResolution = tileGrid.getResolution(tile.tileCoord[0]);
|
||||
@@ -626,6 +614,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
const tileGrid = source.getTileGridForProjection(projection);
|
||||
const resolution = tileGrid.getResolution(z);
|
||||
const context = tile.getContext(layer);
|
||||
// Increase tile size when overzooming for low pixel ratio, to avoid blurry tiles
|
||||
pixelRatio = Math.max(pixelRatio, renderPixelRatio / pixelRatio);
|
||||
const size = source.getTilePixelSize(z, pixelRatio, projection);
|
||||
context.canvas.width = size[0];
|
||||
context.canvas.height = size[1];
|
||||
|
||||
@@ -74,11 +74,11 @@ const FRAGMENT_SHADER = `
|
||||
* @property {function(import("../../Feature").default, number):number} [texCoordCallback] Will be called on every feature in the
|
||||
* source to compute the texture coordinates of each corner of the quad (without effect if no `texture` option defined). This is only done on source change.
|
||||
* The second argument is 0 for `u0` component, 1 for `v0`, 2 for `u1`, and 3 for `v1`.
|
||||
* @property {function(import("../../Feature").default, number, number):number} [colorCallback] Will be called on every feature in the
|
||||
* source to compute the color of each corner of the quad. This is only done on source change.
|
||||
* The second argument is 0 for bottom left, 1 for bottom right, 2 for top right and 3 for top left
|
||||
* The third argument is 0 for red, 1 for green, 2 for blue and 3 for alpha
|
||||
* The return value should be between 0 and 1.
|
||||
* @property {function(import("../../Feature").default, Array<number>=):Array<number>} [colorCallback] Will be called on every feature in the
|
||||
* source to compute the color for use in the fragment shader (available as the `v_color` varying). This is only done on source change.
|
||||
* The return value should be between an array of R, G, B, A values between 0 and 1. To reduce unnecessary
|
||||
* allocation, the function is called with a reusable array that can serve as the return value after updating
|
||||
* the R, G, B, and A values.
|
||||
* @property {function(import("../../Feature").default):number} [opacityCallback] Will be called on every feature in the
|
||||
* source to compute the opacity of the quad on screen (from 0 to 1). This is only done on source change.
|
||||
* Note: this is multiplied with the color of the point which can also have an alpha value < 1.
|
||||
@@ -114,6 +114,7 @@ const FRAGMENT_SHADER = `
|
||||
*
|
||||
* Points are rendered as quads with the following structure:
|
||||
*
|
||||
* ```
|
||||
* (u0, v1) (u1, v1)
|
||||
* [3]----------[2]
|
||||
* |` |
|
||||
@@ -124,6 +125,7 @@ const FRAGMENT_SHADER = `
|
||||
* | ` |
|
||||
* [0]----------[1]
|
||||
* (u0, v0) (u1, v0)
|
||||
* ```
|
||||
*
|
||||
* This uses {@link module:ol/webgl/Helper~WebGLHelper} internally.
|
||||
*
|
||||
@@ -227,9 +229,12 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
|
||||
this.texCoordCallback_ = options.texCoordCallback || function(feature, index) {
|
||||
return index < 2 ? 0 : 1;
|
||||
};
|
||||
this.colorCallback_ = options.colorCallback || function(feature, index, component) {
|
||||
return 1;
|
||||
|
||||
this.colorArray_ = [1, 1, 1, 1];
|
||||
this.colorCallback_ = options.colorCallback || function(feature, color) {
|
||||
return this.colorArray_;
|
||||
};
|
||||
|
||||
this.rotateWithViewCallback_ = options.rotateWithViewCallback || function() {
|
||||
return false;
|
||||
};
|
||||
@@ -293,29 +298,18 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
|
||||
const size = this.sizeCallback_(feature);
|
||||
const opacity = this.opacityCallback_(feature);
|
||||
const rotateWithView = this.rotateWithViewCallback_(feature) ? 1 : 0;
|
||||
const v0_r = this.colorCallback_(feature, 0, 0);
|
||||
const v0_g = this.colorCallback_(feature, 0, 1);
|
||||
const v0_b = this.colorCallback_(feature, 0, 2);
|
||||
const v0_a = this.colorCallback_(feature, 0, 3);
|
||||
const v1_r = this.colorCallback_(feature, 1, 0);
|
||||
const v1_g = this.colorCallback_(feature, 1, 1);
|
||||
const v1_b = this.colorCallback_(feature, 1, 2);
|
||||
const v1_a = this.colorCallback_(feature, 1, 3);
|
||||
const v2_r = this.colorCallback_(feature, 2, 0);
|
||||
const v2_g = this.colorCallback_(feature, 2, 1);
|
||||
const v2_b = this.colorCallback_(feature, 2, 2);
|
||||
const v2_a = this.colorCallback_(feature, 2, 3);
|
||||
const v3_r = this.colorCallback_(feature, 3, 0);
|
||||
const v3_g = this.colorCallback_(feature, 3, 1);
|
||||
const v3_b = this.colorCallback_(feature, 3, 2);
|
||||
const v3_a = this.colorCallback_(feature, 3, 3);
|
||||
const color = this.colorCallback_(feature, this.colorArray_);
|
||||
const red = color[0];
|
||||
const green = color[1];
|
||||
const blue = color[2];
|
||||
const alpha = color[3];
|
||||
const baseIndex = this.verticesBuffer_.getArray().length / stride;
|
||||
|
||||
this.verticesBuffer_.getArray().push(
|
||||
x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, v0_r, v0_g, v0_b, v0_a,
|
||||
x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, v1_r, v1_g, v1_b, v1_a,
|
||||
x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, v2_r, v2_g, v2_b, v2_a,
|
||||
x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, v3_r, v3_g, v3_b, v3_a
|
||||
x, y, -size / 2, -size / 2, u0, v0, opacity, rotateWithView, red, green, blue, alpha,
|
||||
x, y, +size / 2, -size / 2, u1, v0, opacity, rotateWithView, red, green, blue, alpha,
|
||||
x, y, +size / 2, +size / 2, u1, v1, opacity, rotateWithView, red, green, blue, alpha,
|
||||
x, y, -size / 2, +size / 2, u0, v1, opacity, rotateWithView, red, green, blue, alpha
|
||||
);
|
||||
this.indicesBuffer_.getArray().push(
|
||||
baseIndex, baseIndex + 1, baseIndex + 3,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
export {default as BingMaps} from './source/BingMaps.js';
|
||||
export {default as CartoDB} from './source/CartoDB.js';
|
||||
export {default as Cluster} from './source/Cluster.js';
|
||||
export {default as IIIF} from './source/IIIF.js';
|
||||
export {default as Image} from './source/Image.js';
|
||||
export {default as ImageArcGISRest} from './source/ImageArcGISRest.js';
|
||||
export {default as ImageCanvas} from './source/ImageCanvas.js';
|
||||
|
||||
284
src/ol/source/IIIF.js
Normal file
@@ -0,0 +1,284 @@
|
||||
/**
|
||||
* @module ol/source/IIIF
|
||||
*/
|
||||
|
||||
import {DEFAULT_TILE_SIZE} from '../tilegrid/common.js';
|
||||
import {getTopLeft} from '../extent.js';
|
||||
import {CustomTile} from './Zoomify.js';
|
||||
import {Versions} from '../format/IIIFInfo.js';
|
||||
import {assert} from '../asserts.js';
|
||||
import TileGrid from '../tilegrid/TileGrid.js';
|
||||
import TileImage from './TileImage.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
|
||||
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
|
||||
* @property {number} [cacheSize]
|
||||
* @property {null|string} [crossOrigin]
|
||||
* @property {import("../extent.js").Extent} [extent=[0, -height, width, 0]]
|
||||
* @property {string} [format='jpg'] Requested image format.
|
||||
* @property {import("../proj.js").ProjectionLike} [projection]
|
||||
* @property {string} [quality] Requested IIIF image quality. Default is 'native'
|
||||
* for version 1, 'default' for versions 2 and 3.
|
||||
* @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels).
|
||||
* Higher values can increase reprojection performance, but decrease precision.
|
||||
* @property {import("../size.js").Size} size Size of the image [width, height].
|
||||
* @property {import("../size.js").Size[]} [sizes] Supported scaled image sizes.
|
||||
* Content of the IIIF info.json 'sizes' property, but as array of Size objects.
|
||||
* @property {import("./State.js").default} [state] Source state.
|
||||
* @property {Array<string>} [supports=[]] Supported IIIF region and size calculation
|
||||
* features.
|
||||
* @property {number} [tilePixelRatio]
|
||||
* @property {number|import("../size.js").Size} [tileSize] Tile size.
|
||||
* Same tile size is used for all zoom levels. If tile size is a number,
|
||||
* a square tile is assumed. If the IIIF image service supports arbitrary
|
||||
* tiling (sizeByH, sizeByW, sizeByWh or sizeByPct as well as regionByPx or regionByPct
|
||||
* are supported), the default tilesize is 256.
|
||||
* @property {number} [transition]
|
||||
* @property {string} [url] Base URL of the IIIF Image service.
|
||||
* This should be the same as the IIIF Image @id.
|
||||
* @property {Versions} [version=Versions.VERSION2] Service's IIIF Image API version.
|
||||
* @property {number} [zDirection] Indicate which resolution should be used
|
||||
* by a renderer if the views resolution does not match any resolution of the tile source.
|
||||
* If 0, the nearest resolution will be used. If 1, the nearest lower resolution
|
||||
* will be used. If -1, the nearest higher resolution will be used.
|
||||
*/
|
||||
|
||||
function formatPercentage(percentage) {
|
||||
return percentage.toLocaleString('en', {maximumFractionDigits: 10});
|
||||
}
|
||||
|
||||
/**
|
||||
* @classdesc
|
||||
* Layer source for IIIF Image API services.
|
||||
* @api
|
||||
*/
|
||||
class IIIF extends TileImage {
|
||||
|
||||
constructor(opt_options) {
|
||||
|
||||
const options = opt_options || {};
|
||||
|
||||
let baseUrl = options.url || '';
|
||||
baseUrl = baseUrl + (baseUrl.lastIndexOf('/') === baseUrl.length - 1 || baseUrl === '' ? '' : '/');
|
||||
const version = options.version || Versions.VERSION2;
|
||||
const sizes = options.sizes || [];
|
||||
const size = options.size;
|
||||
assert(size != undefined && Array.isArray(size) && size.length == 2 &&
|
||||
!isNaN(size[0]) && size[0] > 0 && !isNaN(size[1]) && size[1] > 0, 60);
|
||||
const width = size[0];
|
||||
const height = size[1];
|
||||
const tileSize = options.tileSize;
|
||||
const format = options.format || 'jpg';
|
||||
const quality = options.quality || (options.version == Versions.VERSION1 ? 'native' : 'default');
|
||||
let resolutions = options.resolutions || [];
|
||||
const supports = options.supports || [];
|
||||
const extent = options.extent || [0, -height, width, 0];
|
||||
|
||||
const supportsListedSizes = sizes != undefined && Array.isArray(sizes) && sizes.length > 0;
|
||||
const supportsListedTiles = tileSize != undefined && (Number.isInteger(tileSize) && tileSize > 0 || Array.isArray(tileSize) && tileSize.length > 0);
|
||||
const supportsArbitraryTiling = supports != undefined && Array.isArray(supports) &&
|
||||
(supports.includes('regionByPx') || supports.includes('regionByPct')) &&
|
||||
(supports.includes('sizeByWh') || supports.includes('sizeByH') ||
|
||||
supports.includes('sizeByW') || supports.includes('sizeByPct'));
|
||||
|
||||
let tileWidth,
|
||||
tileHeight,
|
||||
maxZoom;
|
||||
|
||||
resolutions.sort(function(a, b) {
|
||||
return b - a;
|
||||
});
|
||||
|
||||
if (supportsListedTiles || supportsArbitraryTiling) {
|
||||
if (tileSize != undefined) {
|
||||
if (Number.isInteger(tileSize) && tileSize > 0) {
|
||||
tileWidth = tileSize;
|
||||
tileHeight = tileSize;
|
||||
} else if (Array.isArray(tileSize) && tileSize.length > 0) {
|
||||
if (tileSize.length == 1 || tileSize[1] == undefined && Number.isInteger(tileSize[0])) {
|
||||
tileWidth = tileSize[0];
|
||||
tileHeight = tileSize[0];
|
||||
}
|
||||
if (tileSize.length == 2) {
|
||||
if (Number.isInteger(tileSize[0]) && Number.isInteger(tileSize[1])) {
|
||||
tileWidth = tileSize[0];
|
||||
tileHeight = tileSize[1];
|
||||
} else if (tileSize[0] == undefined && Number.isInteger(tileSize[1])) {
|
||||
tileWidth = tileSize[1];
|
||||
tileHeight = tileSize[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tileWidth === undefined || tileHeight === undefined) {
|
||||
tileWidth = DEFAULT_TILE_SIZE;
|
||||
tileHeight = DEFAULT_TILE_SIZE;
|
||||
}
|
||||
if (resolutions.length == 0) {
|
||||
maxZoom = Math.max(
|
||||
Math.ceil(Math.log(width / tileWidth) / Math.LN2),
|
||||
Math.ceil(Math.log(height / tileHeight) / Math.LN2)
|
||||
);
|
||||
for (let i = maxZoom; i >= 0; i--) {
|
||||
resolutions.push(Math.pow(2, i));
|
||||
}
|
||||
} else {
|
||||
const maxScaleFactor = Math.max([...resolutions]);
|
||||
// TODO maxScaleFactor might not be a power to 2
|
||||
maxZoom = Math.round(Math.log(maxScaleFactor) / Math.LN2);
|
||||
}
|
||||
} else {
|
||||
// No tile support.
|
||||
tileWidth = width;
|
||||
tileHeight = height;
|
||||
resolutions = [];
|
||||
if (supportsListedSizes) {
|
||||
/*
|
||||
* 'sizes' provided. Use full region in different resolutions. Every
|
||||
* resolution has only one tile.
|
||||
*/
|
||||
sizes.sort(function(a, b) {
|
||||
return a[0] - b[0];
|
||||
});
|
||||
maxZoom = -1;
|
||||
const ignoredSizesIndex = [];
|
||||
for (let i = 0; i < sizes.length; i++) {
|
||||
const resolution = width / sizes[i][0];
|
||||
if (resolutions.length > 0 && resolutions[resolutions.length - 1] == resolution) {
|
||||
ignoredSizesIndex.push(i);
|
||||
continue;
|
||||
}
|
||||
resolutions.push(resolution);
|
||||
maxZoom++;
|
||||
}
|
||||
if (ignoredSizesIndex.length > 0) {
|
||||
for (let i = 0; i < ignoredSizesIndex.length; i++) {
|
||||
sizes.splice(ignoredSizesIndex[i] - i, 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No useful image information at all. Try pseudo tile with full image.
|
||||
resolutions.push(1);
|
||||
sizes.push([width, height]);
|
||||
maxZoom = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const tileGrid = new TileGrid({
|
||||
tileSize: [tileWidth, tileHeight],
|
||||
extent: extent,
|
||||
origin: getTopLeft(extent),
|
||||
resolutions: resolutions
|
||||
});
|
||||
|
||||
const tileUrlFunction = function(tileCoord, pixelRatio, projection) {
|
||||
let regionParam,
|
||||
sizeParam;
|
||||
const zoom = tileCoord[0];
|
||||
if (zoom > maxZoom) {
|
||||
return;
|
||||
}
|
||||
const tileX = tileCoord[1],
|
||||
tileY = tileCoord[2],
|
||||
scale = resolutions[zoom];
|
||||
if (tileX === undefined || tileY === undefined || scale === undefined ||
|
||||
tileX < 0 || Math.ceil(width / scale / tileWidth) <= tileX ||
|
||||
tileY < 0 || Math.ceil(height / scale / tileHeight) <= tileY) {
|
||||
return;
|
||||
}
|
||||
if (supportsArbitraryTiling || supportsListedTiles) {
|
||||
const regionX = tileX * tileWidth * scale,
|
||||
regionY = tileY * tileHeight * scale;
|
||||
let regionW = tileWidth * scale,
|
||||
regionH = tileHeight * scale,
|
||||
sizeW = tileWidth,
|
||||
sizeH = tileHeight;
|
||||
if (regionX + regionW > width) {
|
||||
regionW = width - regionX;
|
||||
}
|
||||
if (regionY + regionH > height) {
|
||||
regionH = height - regionY;
|
||||
}
|
||||
if (regionX + tileWidth * scale > width) {
|
||||
sizeW = Math.floor((width - regionX + scale - 1) / scale);
|
||||
}
|
||||
if (regionY + tileHeight * scale > height) {
|
||||
sizeH = Math.floor((height - regionY + scale - 1) / scale);
|
||||
}
|
||||
if (regionX == 0 && regionW == width && regionY == 0 && regionH == height) {
|
||||
// canonical full image region parameter is 'full', not 'x,y,w,h'
|
||||
regionParam = 'full';
|
||||
} else if (!supportsArbitraryTiling || supports.includes('regionByPx')) {
|
||||
regionParam = regionX + ',' + regionY + ',' + regionW + ',' + regionH;
|
||||
} else if (supports.includes('regionByPct')) {
|
||||
const pctX = formatPercentage(regionX / width * 100),
|
||||
pctY = formatPercentage(regionY / height * 100),
|
||||
pctW = formatPercentage(regionW / width * 100),
|
||||
pctH = formatPercentage(regionH / height * 100);
|
||||
regionParam = 'pct:' + pctX + ',' + pctY + ',' + pctW + ',' + pctH;
|
||||
}
|
||||
if (version == Versions.VERSION3 && (!supportsArbitraryTiling || supports.includes('sizeByWh'))) {
|
||||
sizeParam = sizeW + ',' + sizeH;
|
||||
} else if (!supportsArbitraryTiling || supports.includes('sizeByW')) {
|
||||
sizeParam = sizeW + ',';
|
||||
} else if (supports.includes('sizeByH')) {
|
||||
sizeParam = ',' + sizeH;
|
||||
} else if (supports.includes('sizeByWh')) {
|
||||
sizeParam = sizeW + ',' + sizeH;
|
||||
} else if (supports.includes('sizeByPct')) {
|
||||
sizeParam = 'pct:' + formatPercentage(100 / scale);
|
||||
}
|
||||
} else {
|
||||
regionParam = 'full';
|
||||
if (supportsListedSizes) {
|
||||
const regionWidth = sizes[zoom][0],
|
||||
regionHeight = sizes[zoom][1];
|
||||
if (version == Versions.VERSION3) {
|
||||
if (regionWidth == width && regionHeight == height) {
|
||||
sizeParam = 'max';
|
||||
} else {
|
||||
sizeParam = regionWidth + ',' + regionHeight;
|
||||
}
|
||||
} else {
|
||||
if (regionWidth == width) {
|
||||
sizeParam = 'full';
|
||||
} else {
|
||||
sizeParam = regionWidth + ',';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sizeParam = version == Versions.VERSION3 ? 'max' : 'full';
|
||||
}
|
||||
}
|
||||
return baseUrl + regionParam + '/' + sizeParam + '/0/' + quality + '.' + format;
|
||||
};
|
||||
|
||||
const IiifTileClass = CustomTile.bind(null, tileGrid);
|
||||
|
||||
super({
|
||||
attributions: options.attributions,
|
||||
attributionsCollapsible: options.attributionsCollapsible,
|
||||
cacheSize: options.cacheSize,
|
||||
crossOrigin: options.crossOrigin,
|
||||
projection: options.projection,
|
||||
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
|
||||
state: options.state,
|
||||
tileClass: IiifTileClass,
|
||||
tileGrid: tileGrid,
|
||||
tilePixelRatio: options.tilePixelRatio,
|
||||
tileUrlFunction: tileUrlFunction,
|
||||
transition: options.transition
|
||||
});
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
this.zDirection = options.zDirection;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default IIIF;
|
||||
@@ -227,7 +227,8 @@ class RasterSource extends ImageSource {
|
||||
rotation: 0
|
||||
}),
|
||||
viewHints: [],
|
||||
wantedTiles: {}
|
||||
wantedTiles: {},
|
||||
declutterItems: []
|
||||
};
|
||||
|
||||
this.setAttributions(function(frameState) {
|
||||
@@ -288,8 +289,7 @@ class RasterSource extends ImageSource {
|
||||
frameState.focus = center;
|
||||
frameState.size[0] = Math.round(getWidth(extent) / resolution);
|
||||
frameState.size[1] = Math.round(getHeight(extent) / resolution);
|
||||
frameState.time = Date.now();
|
||||
frameState.animate = false;
|
||||
frameState.time = Infinity;
|
||||
|
||||
const viewState = frameState.viewState;
|
||||
viewState.center = center;
|
||||
|
||||
@@ -485,7 +485,7 @@ class VectorSource extends Source {
|
||||
}
|
||||
} else {
|
||||
if (this.featuresRtree_) {
|
||||
this.featuresRtree_.forEach(this.removeFeatureInternal, this);
|
||||
this.featuresRtree_.forEach(this.removeFeatureInternal.bind(this));
|
||||
for (const id in this.nullGeometryFeatures_) {
|
||||
this.removeFeatureInternal(this.nullGeometryFeatures_[id]);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import {equals} from '../array.js';
|
||||
/**
|
||||
* @typedef {Object} Options
|
||||
* @property {import("./Source.js").AttributionLike} [attributions] Attributions.
|
||||
* @property {boolean} [attributionsCollapsible=true] Attributions are collapsible.
|
||||
* @property {number} [cacheSize=128] Cache size.
|
||||
* @property {import("../extent.js").Extent} [extent]
|
||||
* @property {import("../format/Feature.js").default} [format] Feature format for tiles. Used and required by the default.
|
||||
@@ -97,6 +98,7 @@ class VectorTile extends UrlTile {
|
||||
|
||||
super({
|
||||
attributions: options.attributions,
|
||||
attributionsCollapsible: options.attributionsCollapsible,
|
||||
cacheSize: options.cacheSize,
|
||||
opaque: false,
|
||||
projection: projection,
|
||||
|
||||
@@ -19,8 +19,9 @@ import GeometryType from './geom/GeometryType.js';
|
||||
* @property {import("./proj.js").ProjectionLike} [projection='EPSG:3857']
|
||||
* Projection of the geometry. By default, the geometry is assumed to be in
|
||||
* Web Mercator.
|
||||
* @property {number} [radius=6371008.8] Sphere radius. By default, the radius of the
|
||||
* earth is used (Clarke 1866 Authalic Sphere).
|
||||
* @property {number} [radius=6371008.8] Sphere radius. By default, the
|
||||
* [mean Earth radius](https://en.wikipedia.org/wiki/Earth_radius#Mean_radius)
|
||||
* for the WGS84 ellipsoid is used.
|
||||
*/
|
||||
|
||||
|
||||
|
||||