Merge remote-tracking branch 'origin/master' into fix_triangulation

Conflicts:
	src/ol/TileCache.js
This commit is contained in:
philip
2020-01-11 17:36:40 +00:00
139 changed files with 10057 additions and 2161 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ Interactions for <a href="module-ol_Feature-Feature.html">vector features</a>
<tr><th>Projections</th><th>Observable objects</th><th>Other components</th></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 <a href="module-ol_proj.html#.transform">ol/proj#transform()</a> and <a href="module-ol_proj.html#.transformExtent">ol/proj#transformExtent()</a>.</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> <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><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> <td>
<a href="module-ol_Geolocation.html">ol/Geolocation</a><br> <a href="module-ol_Geolocation.html">ol/Geolocation</a><br>
<a href="module-ol_Overlay-Overlay.html">ol/Overlay</a><br></td> <a href="module-ol_Overlay-Overlay.html">ol/Overlay</a><br></td>
@@ -8,7 +8,7 @@
<th>Name</th> <th>Name</th>
<th>Type</th> <th>Type</th>
<th>Settable</th> <th>Settable</th>
<th><a href="module-ol_Object-ObjectEvent.html">ol/Object.ObjectEvent</a> type</th> <th><a href="module-ol_Object.ObjectEvent.html">ol/Object.ObjectEvent</a> type</th>
<th class="last">Description</th> <th class="last">Description</th>
</tr> </tr>
</thead> </thead>
+1 -1
View File
@@ -111,7 +111,7 @@ Features for `updates` must have an id set by the feature reader or `ol.Feature#
### 28 ### 28
`renderMode` must be `'image'` or `'hybrid'`. `renderMode` must be `'image'`, `'hybrid'` or `'vector'`.
### 29 ### 29
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 3.2 MiB

+3 -3
View File
@@ -6,10 +6,10 @@ docs: >
This example shows how to use <b>postrender</b> events and a <b>vector context</b> to This example shows how to use <b>postrender</b> events and a <b>vector context</b> to
animate a marker feature along a line. In this example an encoded polyline animate a marker feature along a line. In this example an encoded polyline
is being used. is being used.
tags: "animation, feature, postrender, polyline" tags: "animation, feature, postrender, polyline, maptiler"
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
<label for="speed"> <label for="speed">
+9 -4
View File
@@ -4,7 +4,7 @@ import View from '../src/ol/View.js';
import Polyline from '../src/ol/format/Polyline.js'; import Polyline from '../src/ol/format/Polyline.js';
import Point from '../src/ol/geom/Point.js'; import Point from '../src/ol/geom/Point.js';
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js'; import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
import BingMaps from '../src/ol/source/BingMaps.js'; import XYZ from '../src/ol/source/XYZ.js';
import VectorSource from '../src/ol/source/Vector.js'; import VectorSource from '../src/ol/source/Vector.js';
import {Circle as CircleStyle, Fill, Icon, Stroke, Style} from '../src/ol/style.js'; import {Circle as CircleStyle, Fill, Icon, Stroke, Style} from '../src/ol/style.js';
import {getVectorContext} from '../src/ol/render.js'; import {getVectorContext} from '../src/ol/render.js';
@@ -121,6 +121,10 @@ const vectorLayer = new VectorLayer({
} }
}); });
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const center = [-5639523.95, -3501274.52]; const center = [-5639523.95, -3501274.52];
const map = new Map({ const map = new Map({
target: document.getElementById('map'), target: document.getElementById('map'),
@@ -132,9 +136,10 @@ const map = new Map({
}), }),
layers: [ layers: [
new TileLayer({ new TileLayer({
source: new BingMaps({ source: new XYZ({
imagerySet: 'AerialWithLabelsOnDemand', attributions: attributions,
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5' url: 'https://api.maptiler.com/maps/hybrid/{z}/{x}/{y}.jpg?key=' + key,
tileSize: 512
}) })
}), }),
vectorLayer vectorLayer
@@ -5,9 +5,9 @@ shortdesc: Example of drag rotate and zoom control with full screen effect.
docs: > docs: >
<p>Hold down <code>Shift+Drag</code> to rotate and zoom. Click the button in the top right corner to go full screen. Then do the <code>Shift+Drag</code> thing again.</p> <p>Hold down <code>Shift+Drag</code> to rotate and zoom. Click the button in the top right corner to go full screen. Then do the <code>Shift+Drag</code> thing again.</p>
<p>If there is no button on the map, your browser does not support the <a href="http://caniuse.com/#feat=fullscreen">Full Screen API</a>.</p> <p>If there is no button on the map, your browser does not support the <a href="http://caniuse.com/#feat=fullscreen">Full Screen API</a>.</p>
tags: "full-screen, drag, rotate, zoom, bing, bing-maps" tags: "full-screen, drag, rotate, zoom, xyz, maptiler"
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
+9 -5
View File
@@ -3,9 +3,13 @@ import View from '../src/ol/View.js';
import {defaults as defaultControls, FullScreen} from '../src/ol/control.js'; import {defaults as defaultControls, FullScreen} from '../src/ol/control.js';
import {defaults as defaultInteractions, DragRotateAndZoom} from '../src/ol/interaction.js'; import {defaults as defaultInteractions, DragRotateAndZoom} from '../src/ol/interaction.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import BingMaps from '../src/ol/source/BingMaps.js'; import XYZ from '../src/ol/source/XYZ.js';
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const map = new Map({ const map = new Map({
controls: defaultControls().extend([ controls: defaultControls().extend([
new FullScreen() new FullScreen()
@@ -15,13 +19,13 @@ const map = new Map({
]), ]),
layers: [ layers: [
new TileLayer({ new TileLayer({
source: new BingMaps({ source: new XYZ({
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5', attributions: attributions,
imagerySet: 'Aerial' url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20
}) })
}) })
], ],
// Use the canvas renderer because it's currently the fastest
target: 'map', target: 'map',
view: new View({ view: new View({
center: [-33519607, 5616436], center: [-33519607, 5616436],
+3 -3
View File
@@ -5,9 +5,9 @@ shortdesc: Example of a full screen control.
docs: > docs: >
<p>Click the control in the top right corner to go full screen. Click it again to exit full screen.</p> <p>Click the control in the top right corner to go full screen. Click it again to exit full screen.</p>
<p>If there is no button on the map, your browser does not support the <a href="http://caniuse.com/#feat=fullscreen">Full Screen API</a>.</p> <p>If there is no button on the map, your browser does not support the <a href="http://caniuse.com/#feat=fullscreen">Full Screen API</a>.</p>
tags: "full-screen, bing, bing-maps" tags: "full-screen, xyz, maptiler"
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
+9 -4
View File
@@ -2,7 +2,7 @@ import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import {defaults as defaultControls, FullScreen} from '../src/ol/control.js'; import {defaults as defaultControls, FullScreen} from '../src/ol/control.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import BingMaps from '../src/ol/source/BingMaps.js'; import XYZ from '../src/ol/source/XYZ.js';
const view = new View({ const view = new View({
@@ -10,15 +10,20 @@ const view = new View({
zoom: 14 zoom: 14
}); });
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const map = new Map({ const map = new Map({
controls: defaultControls().extend([ controls: defaultControls().extend([
new FullScreen() new FullScreen()
]), ]),
layers: [ layers: [
new TileLayer({ new TileLayer({
source: new BingMaps({ source: new XYZ({
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5', attributions: attributions,
imagerySet: 'Aerial' url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20
}) })
}) })
], ],
+3 -3
View File
@@ -4,10 +4,10 @@ title: GPX Data
shortdesc: Example of using the GPX source. shortdesc: Example of using the GPX source.
docs: > docs: >
Example of using the GPX source. Example of using the GPX source.
tags: "GPX" tags: "GPX, maptiler"
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
<div id="info">&nbsp;</div> <div id="info">&nbsp;</div>
+9 -4
View File
@@ -2,14 +2,19 @@ import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import GPX from '../src/ol/format/GPX.js'; import GPX from '../src/ol/format/GPX.js';
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js'; import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
import BingMaps from '../src/ol/source/BingMaps.js'; import XYZ from '../src/ol/source/XYZ.js';
import VectorSource from '../src/ol/source/Vector.js'; import VectorSource from '../src/ol/source/Vector.js';
import {Circle as CircleStyle, Fill, Stroke, Style} from '../src/ol/style.js'; import {Circle as CircleStyle, Fill, Stroke, Style} from '../src/ol/style.js';
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const raster = new TileLayer({ const raster = new TileLayer({
source: new BingMaps({ source: new XYZ({
imagerySet: 'Aerial', attributions: attributions,
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5' url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20
}) })
}); });
+3 -3
View File
@@ -6,10 +6,10 @@ docs: >
<p>Layer rendering can be manipulated in <code>prerender</code> and <code>postrender</code> event listeners. <p>Layer rendering can be manipulated in <code>prerender</code> and <code>postrender</code> event listeners.
These listeners get an event with a reference to the Canvas rendering context. These listeners get an event with a reference to the Canvas rendering context.
In this example, the <code>postrender</code> listener applies a filter to the image data.</p> In this example, the <code>postrender</code> listener applies a filter to the image data.</p>
tags: "filter, image manipulation" tags: "filter, image manipulation, maptiler"
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
<select id="kernel" name="kernel"> <select id="kernel" name="kernel">
+10 -3
View File
@@ -2,12 +2,19 @@ import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import {fromLonLat} from '../src/ol/proj.js'; import {fromLonLat} from '../src/ol/proj.js';
import BingMaps from '../src/ol/source/BingMaps.js'; import XYZ from '../src/ol/source/XYZ.js';
const key = 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'; const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const imagery = new TileLayer({ const imagery = new TileLayer({
source: new BingMaps({key: key, imagerySet: 'Aerial'}) source: new XYZ({
attributions: attributions,
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20,
crossOrigin: ''
})
}); });
const map = new Map({ const map = new Map({
-14
View File
@@ -1,14 +0,0 @@
---
layout: example.html
title: Interaction Options
shortdesc: Shows interaction options for custom scroll and zoom behavior.
docs: >
This example uses a custom `ol/interaction/defaults` configuration:
by default, wheel/trackpad zoom and drag panning is always active, which
can be unexpected on pages with a lot of scrollable content and an embedded
map. To perform wheel/trackpad zoom and drag-pan actions only when the map
has the focus, set `onFocusOnly: true` as option. This requires a map div
with a `tabindex` attribute set.
tags: "trackpad, mousewheel, zoom, scroll, interaction, fractional"
---
<div tabindex="1" id="map" class="map"></div>
+15 -6
View File
@@ -18,11 +18,17 @@ const map = new Map({
osm.on('prerender', function(event) { osm.on('prerender', function(event) {
const ctx = event.context; const ctx = event.context;
// calculate the pixel ratio and rotation of the canvas
const matrix = event.inversePixelTransform;
const canvasPixelRatio = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]);
const canvasRotation = -Math.atan2(matrix[1], matrix[0]);
ctx.save(); ctx.save();
const pixelRatio = event.frameState.pixelRatio; // center the canvas and remove rotation to position clipping
const size = map.getSize(); ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
ctx.translate(size[0] / 2 * pixelRatio, size[1] / 2 * pixelRatio); ctx.rotate(-canvasRotation);
ctx.scale(3 * pixelRatio, 3 * pixelRatio);
ctx.scale(3 * canvasPixelRatio, 3 * canvasPixelRatio);
ctx.translate(-75, -80); ctx.translate(-75, -80);
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(75, 40); ctx.moveTo(75, 40);
@@ -34,8 +40,11 @@ osm.on('prerender', function(event) {
ctx.bezierCurveTo(85, 25, 75, 37, 75, 40); ctx.bezierCurveTo(85, 25, 75, 37, 75, 40);
ctx.clip(); ctx.clip();
ctx.translate(75, 80); ctx.translate(75, 80);
ctx.scale(1 / 3 / pixelRatio, 1 / 3 / pixelRatio); ctx.scale(1 / 3 / canvasPixelRatio, 1 / 3 / canvasPixelRatio);
ctx.translate(-size[0] / 2 * pixelRatio, -size[1] / 2 * pixelRatio);
// reapply canvas rotation and position
ctx.rotate(canvasRotation);
ctx.translate(-ctx.canvas.width / 2, -ctx.canvas.height / 2);
}); });
osm.on('postrender', function(event) { osm.on('postrender', function(event) {
+3 -3
View File
@@ -8,9 +8,9 @@ docs: >
In this example, the <code>prerender</code> listener sets a clipping mask around the most In this example, the <code>prerender</code> listener sets a clipping mask around the most
recent mouse position, giving you a spyglass effect for viewing one layer over another.</p> recent mouse position, giving you a spyglass effect for viewing one layer over another.</p>
<p>Move around the map to see the effect. Use the ↑ up and ↓ down arrow keys to adjust the spyglass size.</p> <p>Move around the map to see the effect. Use the ↑ up and ↓ down arrow keys to adjust the spyglass size.</p>
tags: "spy, image manipulation" tags: "spy, image manipulation, maptiler"
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
+21 -8
View File
@@ -2,16 +2,28 @@ import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import {fromLonLat} from '../src/ol/proj.js'; import {fromLonLat} from '../src/ol/proj.js';
import BingMaps from '../src/ol/source/BingMaps.js'; import XYZ from '../src/ol/source/XYZ.js';
import {getRenderPixel} from '../src/ol/render.js';
const key = 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'; const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const roads = new TileLayer({ const roads = new TileLayer({
source: new BingMaps({key: key, imagerySet: 'RoadOnDemand'}) source: new XYZ({
attributions: attributions,
url: 'https://api.maptiler.com/maps/streets/{z}/{x}/{y}.png?key=' + key,
tileSize: 512,
maxZoom: 22
})
}); });
const imagery = new TileLayer({ const imagery = new TileLayer({
source: new BingMaps({key: key, imagerySet: 'Aerial'}) source: new XYZ({
attributions: attributions,
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20
})
}); });
const container = document.getElementById('map'); const container = document.getElementById('map');
@@ -54,14 +66,15 @@ container.addEventListener('mouseout', function() {
// before rendering the layer, do some clipping // before rendering the layer, do some clipping
imagery.on('prerender', function(event) { imagery.on('prerender', function(event) {
const ctx = event.context; const ctx = event.context;
const pixelRatio = event.frameState.pixelRatio;
ctx.save(); ctx.save();
ctx.beginPath(); ctx.beginPath();
if (mousePosition) { if (mousePosition) {
// only show a circle around the mouse // only show a circle around the mouse
ctx.arc(mousePosition[0] * pixelRatio, mousePosition[1] * pixelRatio, const pixel = getRenderPixel(event, mousePosition);
radius * pixelRatio, 0, 2 * Math.PI); const offset = getRenderPixel(event, [mousePosition[0] + radius, mousePosition[1]]);
ctx.lineWidth = 5 * pixelRatio; const canvasRadius = Math.sqrt(Math.pow(offset[0] - pixel[0], 2) + Math.pow(offset[1] - pixel[1], 2));
ctx.arc(pixel[0], pixel[1], canvasRadius, 0, 2 * Math.PI);
ctx.lineWidth = 5 * canvasRadius / radius;
ctx.strokeStyle = 'rgba(0,0,0,0.5)'; ctx.strokeStyle = 'rgba(0,0,0,0.5)';
ctx.stroke(); ctx.stroke();
} }
+3 -3
View File
@@ -4,10 +4,10 @@ title: Layer Swipe
shortdesc: Example of a Layer swipe map. shortdesc: Example of a Layer swipe map.
docs: > docs: >
Example of a Layer swipe map. Example of a Layer swipe map.
tags: "swipe, openstreetmap" tags: "swipe, openstreetmap, maptiler"
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
<input id="swipe" type="range" style="width: 100%"> <input id="swipe" type="range" style="width: 100%">
+26 -10
View File
@@ -1,21 +1,28 @@
import Map from '../src/ol/Map.js'; import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import BingMaps from '../src/ol/source/BingMaps.js';
import OSM from '../src/ol/source/OSM.js'; import OSM from '../src/ol/source/OSM.js';
import XYZ from '../src/ol/source/XYZ.js';
import {getRenderPixel} from '../src/ol/render.js';
const osm = new TileLayer({ const osm = new TileLayer({
source: new OSM() source: new OSM()
}); });
const bing = new TileLayer({
source: new BingMaps({ const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5', const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
imagerySet: 'Aerial' '<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const aerial = new TileLayer({
source: new XYZ({
attributions: attributions,
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20
}) })
}); });
const map = new Map({ const map = new Map({
layers: [osm, bing], layers: [osm, aerial],
target: 'map', target: 'map',
view: new View({ view: new View({
center: [0, 0], center: [0, 0],
@@ -25,17 +32,26 @@ const map = new Map({
const swipe = document.getElementById('swipe'); const swipe = document.getElementById('swipe');
bing.on('prerender', function(event) { aerial.on('prerender', function(event) {
const ctx = event.context; const ctx = event.context;
const width = ctx.canvas.width * (swipe.value / 100); const mapSize = map.getSize();
const width = mapSize[0] * (swipe.value / 100);
const tl = getRenderPixel(event, [width, 0]);
const tr = getRenderPixel(event, [mapSize[0], 0]);
const bl = getRenderPixel(event, [width, mapSize[1]]);
const br = getRenderPixel(event, mapSize);
ctx.save(); ctx.save();
ctx.beginPath(); ctx.beginPath();
ctx.rect(width, 0, ctx.canvas.width - width, ctx.canvas.height); ctx.moveTo(tl[0], tl[1]);
ctx.lineTo(bl[0], bl[1]);
ctx.lineTo(br[0], br[1]);
ctx.lineTo(tr[0], tr[1]);
ctx.closePath();
ctx.clip(); ctx.clip();
}); });
bing.on('postrender', function(event) { aerial.on('postrender', function(event) {
const ctx = event.context; const ctx = event.context;
ctx.restore(); ctx.restore();
}); });
+3 -3
View File
@@ -6,9 +6,9 @@ docs: >
<p>This example makes use of the <code>postrender</code> event listener to <p>This example makes use of the <code>postrender</code> event listener to
oversample imagery in a circle around the pointer location. Listeners for this event have access to the Canvas context and can manipulate image data.</p> oversample imagery in a circle around the pointer location. Listeners for this event have access to the Canvas context and can manipulate image data.</p>
<p>Move around the map to see the effect. Use the ↑ up and ↓ down arrow keys to adjust the magnified circle size.</p> <p>Move around the map to see the effect. Use the ↑ up and ↓ down arrow keys to adjust the magnified circle size.</p>
tags: "magnify, image manipulation" tags: "magnify, image manipulation, maptiler"
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
+10 -3
View File
@@ -2,13 +2,20 @@ import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import {fromLonLat} from '../src/ol/proj.js'; import {fromLonLat} from '../src/ol/proj.js';
import BingMaps from '../src/ol/source/BingMaps.js'; import XYZ from '../src/ol/source/XYZ.js';
import {getRenderPixel} from '../src/ol/render.js'; import {getRenderPixel} from '../src/ol/render.js';
const key = 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'; const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const imagery = new TileLayer({ const imagery = new TileLayer({
source: new BingMaps({key: key, imagerySet: 'Aerial'}) source: new XYZ({
attributions: attributions,
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20,
crossOrigin: ''
})
}); });
const container = document.getElementById('map'); const container = document.getElementById('map');
+10
View File
@@ -0,0 +1,10 @@
---
layout: example.html
title: Page Scrolling
shortdesc: Shows a map that does not interrupt page scrolling.
docs: >
To perform wheel/trackpad zoom and drag-pan actions only when the map
has the focus, configure your map div with a `tabindex` attribute.
tags: "trackpad, mousewheel, zoom, scroll, page"
---
<div tabindex="1" id="map" class="map"></div>
@@ -1,14 +1,10 @@
import Map from '../src/ol/Map.js'; import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import {defaults as defaultInteractions} from '../src/ol/interaction.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import OSM from '../src/ol/source/OSM.js'; import OSM from '../src/ol/source/OSM.js';
const map = new Map({ const map = new Map({
interactions: defaultInteractions({
onFocusOnly: true
}),
layers: [ layers: [
new TileLayer({ new TileLayer({
source: new OSM() source: new OSM()
+1 -2
View File
@@ -1,8 +1,7 @@
.ol-popup { .ol-popup {
position: absolute; position: absolute;
background-color: white; background-color: white;
-webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); box-shadow: 0 1px 4px rgba(0,0,0,0.2);
filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2));
padding: 15px; padding: 15px;
border-radius: 10px; border-radius: 10px;
border: 1px solid #cccccc; border: 1px solid #cccccc;
+3 -3
View File
@@ -18,12 +18,12 @@ docs: >
those pixels based on a threshold value (values above the those pixels based on a threshold value (values above the
threshold are green and those below are transparent). threshold are green and those below are transparent).
</p> </p>
tags: "raster, pixel" tags: "raster, pixel, maptiler"
resources: resources:
- https://unpkg.com/d3@4.12.0/build/d3.js - https://unpkg.com/d3@4.12.0/build/d3.js
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div class="rel"> <div class="rel">
<div id="map" class="map"></div> <div id="map" class="map"></div>
+13 -6
View File
@@ -1,7 +1,7 @@
import Map from '../src/ol/Map.js'; import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import {Image as ImageLayer, Tile as TileLayer} from '../src/ol/layer.js'; import {Image as ImageLayer, Tile as TileLayer} from '../src/ol/layer.js';
import BingMaps from '../src/ol/source/BingMaps.js'; import XYZ from '../src/ol/source/XYZ.js';
import RasterSource from '../src/ol/source/Raster.js'; import RasterSource from '../src/ol/source/Raster.js';
const minVgi = 0; const minVgi = 0;
@@ -46,9 +46,16 @@ function summarize(value, counts) {
/** /**
* Use aerial imagery as the input data for the raster source. * Use aerial imagery as the input data for the raster source.
*/ */
const bing = new BingMaps({
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5', const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
imagerySet: 'Aerial' const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const aerial = new XYZ({
attributions: attributions,
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20,
crossOrigin: ''
}); });
@@ -57,7 +64,7 @@ const bing = new BingMaps({
* be colored green. * be colored green.
*/ */
const raster = new RasterSource({ const raster = new RasterSource({
sources: [bing], sources: [aerial],
/** /**
* Run calculations on pixel data. * Run calculations on pixel data.
* @param {Array} pixels List of pixels (one per source). * @param {Array} pixels List of pixels (one per source).
@@ -110,7 +117,7 @@ raster.on('afteroperations', function(event) {
const map = new Map({ const map = new Map({
layers: [ layers: [
new TileLayer({ new TileLayer({
source: bing source: aerial
}), }),
new ImageLayer({ new ImageLayer({
source: raster source: raster
+3 -3
View File
@@ -23,10 +23,10 @@ docs: >
This example also shows how an additional function can be made available This example also shows how an additional function can be made available
to the operation. to the operation.
</p> </p>
tags: "raster, region growing" tags: "raster, region growing, maptiler"
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div id="map" class="map" style="cursor: pointer"></div> <div id="map" class="map" style="cursor: pointer"></div>
<table class="controls"> <table class="controls">
+10 -3
View File
@@ -2,7 +2,7 @@ import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import {Image as ImageLayer, Tile as TileLayer} from '../src/ol/layer.js'; import {Image as ImageLayer, Tile as TileLayer} from '../src/ol/layer.js';
import {fromLonLat} from '../src/ol/proj.js'; import {fromLonLat} from '../src/ol/proj.js';
import BingMaps from '../src/ol/source/BingMaps.js'; import XYZ from '../src/ol/source/XYZ.js';
import RasterSource from '../src/ol/source/Raster.js'; import RasterSource from '../src/ol/source/Raster.js';
function growRegion(inputs, data) { function growRegion(inputs, data) {
@@ -72,10 +72,17 @@ function next4Edges(edge) {
]; ];
} }
const key = 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'; const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const imagery = new TileLayer({ const imagery = new TileLayer({
source: new BingMaps({key: key, imagerySet: 'Aerial'}) source: new XYZ({
attributions: attributions,
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20,
crossOrigin: ''
})
}); });
const raster = new RasterSource({ const raster = new RasterSource({
+20 -13
View File
@@ -55,15 +55,6 @@ proj54009.setExtent([-18e6, -9e6, 18e6, 9e6]);
const layers = {}; const layers = {};
layers['bng'] = new TileLayer({
source: new XYZ({
projection: 'EPSG:27700',
url: 'https://tileserver.maptiler.com/miniscale/{z}/{x}/{y}.png',
crossOrigin: '',
maxZoom: 6
})
});
layers['osm'] = new TileLayer({ layers['osm'] = new TileLayer({
source: new OSM() source: new OSM()
}); });
@@ -95,9 +86,11 @@ layers['wms21781'] = new TileLayer({
}); });
const parser = new WMTSCapabilities(); const parser = new WMTSCapabilities();
const url = 'https://map1.vis.earthdata.nasa.gov/wmts-arctic/' +
layers['wmts3413'] = new TileLayer();
const urlA = 'https://map1.vis.earthdata.nasa.gov/wmts-arctic/' +
'wmts.cgi?SERVICE=WMTS&request=GetCapabilities'; 'wmts.cgi?SERVICE=WMTS&request=GetCapabilities';
fetch(url).then(function(response) { fetch(urlA).then(function(response) {
return response.text(); return response.text();
}).then(function(text) { }).then(function(text) {
const result = parser.read(text); const result = parser.read(text);
@@ -108,9 +101,23 @@ fetch(url).then(function(response) {
options.crossOrigin = ''; options.crossOrigin = '';
options.projection = 'EPSG:3413'; options.projection = 'EPSG:3413';
options.wrapX = false; options.wrapX = false;
layers['wmts3413'] = new TileLayer({ layers['wmts3413'].setSource(new WMTS(options));
source: new WMTS(options) });
layers['bng'] = new TileLayer();
const urlB = 'https://tiles.arcgis.com/tiles/qHLhLQrcvEnxjtPr/arcgis/rest/services/OS_Open_Raster/MapServer/WMTS';
fetch(urlB).then(function(response) {
return response.text();
}).then(function(text) {
const result = parser.read(text);
const options = optionsFromCapabilities(result, {
layer: 'OS_Open_Raster'
}); });
options.attributions = 'Contains OS data © Crown Copyright and database right 2019';
options.crossOrigin = '';
options.projection = 'EPSG:27700';
options.wrapX = false;
layers['bng'].setSource(new WMTS(options));
}); });
layers['grandcanyon'] = new TileLayer({ layers['grandcanyon'] = new TileLayer({
+5
View File
@@ -8,6 +8,11 @@ docs: >
<a href="https://www.mapbox.com/blog/terrain-rgb/">Mapbox Terrain-RGB tiles</a> <a href="https://www.mapbox.com/blog/terrain-rgb/">Mapbox Terrain-RGB tiles</a>
to "flood" areas below the elevation shown on the sea level slider. to "flood" areas below the elevation shown on the sea level slider.
</p> </p>
<p>
<code>ol/source/Raster</code> can take either a tile source or layer.
In this case a layer is used to allow disabling at the <code>prerender</code> event
of image smoothing which would change the precise elevation values set in the pixels.
</p>
tags: "raster, pixel operation, flood" tags: "raster, pixel operation, flood"
cloak: cloak:
- key: pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg - key: pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg
+9 -3
View File
@@ -22,9 +22,15 @@ function flood(pixels, data) {
} }
const key = 'pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg'; const key = 'pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg';
const elevation = new XYZ({ const elevation = new TileLayer({
url: 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=' + key, source: new XYZ({
crossOrigin: 'anonymous' url: 'https://api.mapbox.com/v4/mapbox.terrain-rgb/{z}/{x}/{y}.pngraw?access_token=' + key,
crossOrigin: 'anonymous'
})
});
elevation.on('prerender', function(evt) {
evt.context.imageSmoothingEnabled = false;
evt.context.msImageSmoothingEnabled = false;
}); });
const raster = new RasterSource({ const raster = new RasterSource({
+3 -3
View File
@@ -4,10 +4,10 @@ title: Shared Views
shortdesc: Two maps share view properties shortdesc: Two maps share view properties
docs: > docs: >
Two maps (one Road, one Aerial) share the same center, resolution and rotation. Two maps (one Road, one Aerial) share the same center, resolution and rotation.
tags: "side-by-side, bing, bing-maps" tags: "side-by-side, xyz, maptiler"
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div class="wrapper"> <div class="wrapper">
<div class="half"> <div class="half">
+14 -9
View File
@@ -1,21 +1,26 @@
import Map from '../src/ol/Map.js'; import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import BingMaps from '../src/ol/source/BingMaps.js'; import XYZ from '../src/ol/source/XYZ.js';
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const roadLayer = new TileLayer({ const roadLayer = new TileLayer({
source: new BingMaps({ source: new XYZ({
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5', attributions: attributions,
imagerySet: 'RoadOnDemand', url: 'https://api.maptiler.com/maps/streets/{z}/{x}/{y}.png?key=' + key,
maxZoom: 19 tileSize: 512,
maxZoom: 22
}) })
}); });
const aerialLayer = new TileLayer({ const aerialLayer = new TileLayer({
source: new BingMaps({ source: new XYZ({
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5', attributions: attributions,
imagerySet: 'Aerial', url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 19 maxZoom: 20
}) })
}); });
+3 -3
View File
@@ -4,9 +4,9 @@ title: Street Labels
shortdesc: Render street names. shortdesc: Render street names.
docs: > docs: >
Example showing the use of a text style with `placement: 'line'` to render text along a path. Example showing the use of a text style with `placement: 'line'` to render text along a path.
tags: "vector, label, streets" tags: "vector, label, streets, maptiler"
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
+9 -4
View File
@@ -3,7 +3,7 @@ import View from '../src/ol/View.js';
import {getCenter} from '../src/ol/extent.js'; import {getCenter} from '../src/ol/extent.js';
import GeoJSON from '../src/ol/format/GeoJSON.js'; import GeoJSON from '../src/ol/format/GeoJSON.js';
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js'; import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
import BingMaps from '../src/ol/source/BingMaps.js'; import XYZ from '../src/ol/source/XYZ.js';
import VectorSource from '../src/ol/source/Vector.js'; import VectorSource from '../src/ol/source/Vector.js';
import {Fill, Style, Text} from '../src/ol/style.js'; import {Fill, Style, Text} from '../src/ol/style.js';
@@ -17,12 +17,17 @@ const style = new Style({
}) })
}); });
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const viewExtent = [1817379, 6139595, 1827851, 6143616]; const viewExtent = [1817379, 6139595, 1827851, 6143616];
const map = new Map({ const map = new Map({
layers: [new TileLayer({ layers: [new TileLayer({
source: new BingMaps({ source: new XYZ({
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5', attributions: attributions,
imagerySet: 'Aerial' url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
maxZoom: 20
}) })
}), new VectorLayer({ }), new VectorLayer({
declutter: true, declutter: true,
+7
View File
@@ -0,0 +1,7 @@
#map {
background: black;
}
.svg-layer path:hover {
opacity: 0.4;
}
+9
View File
@@ -0,0 +1,9 @@
---
layout: example.html
title: SVG Layer
shortdesc: Example of a map with an interactive svg layer.
docs: >
With a plain `ol/Layer` and a `render` function, we can use an interactive svg as layer. Hover over countries to see the interactivity that is defined purely with a css `:hover` pseudo-class.
tags: "svg, layer, render, transform"
---
<div id="map" class="map"></div>
+48
View File
@@ -0,0 +1,48 @@
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import Layer from '../src/ol/layer/Layer.js';
import {composeCssTransform} from '../src/ol/transform.js';
const map = new Map({
target: 'map',
view: new View({
center: [0, 0],
extent: [-180, -90, 180, 90],
projection: 'EPSG:4326',
zoom: 2
})
});
const svgContainer = document.createElement('div');
const xhr = new XMLHttpRequest();
xhr.open('GET', 'data/world.svg');
xhr.addEventListener('load', function() {
const svg = xhr.responseXML.documentElement;
svgContainer.ownerDocument.importNode(svg);
svgContainer.appendChild(svg);
});
xhr.send();
const width = 2560;
const height = 1280;
const svgResolution = 360 / width;
svgContainer.style.width = width + 'px';
svgContainer.style.height = height + 'px';
svgContainer.style.transformOrigin = 'top left';
svgContainer.className = 'svg-layer';
map.addLayer(new Layer({
render: function(frameState) {
const scale = svgResolution / frameState.viewState.resolution;
const center = frameState.viewState.center;
const size = frameState.size;
const cssTransform = composeCssTransform(
size[0] / 2, size[1] / 2,
scale, scale,
frameState.viewState.rotation,
-center[0] / svgResolution - width / 2, center[1] / svgResolution - height / 2);
svgContainer.style.transform = cssTransform;
svgContainer.style.opacity = this.getOpacity();
return svgContainer;
}
}));
+8 -1
View File
@@ -108,7 +108,14 @@
<div class="row-fluid"> <div class="row-fluid">
<a class="codepen-button pull-right"><i class="fa fa-codepen"></i> Edit</a> <a class="codepen-button pull-right"><i class="fa fa-codepen"></i> Edit</a>
<div class="span12"> <div class="span12">
<h4 id="title">{{ title }}</h4> <h4 id="title">
{{ title }}
</h4>
<p class="tags">
{{#each tags}}
<a href="./index.html?q={{.}}" class="label label-primary">{{.}}</a>
{{/each}}
</p>
{{{ contents }}} {{{ contents }}}
</div> </div>
<form method="POST" id="codepen-form" target="_blank" action="https://codesandbox.io/api/v1/sandboxes/define"> <form method="POST" id="codepen-form" target="_blank" action="https://codesandbox.io/api/v1/sandboxes/define">
+12
View File
@@ -0,0 +1,12 @@
---
layout: example.html
title: Panning and page scrolling
shortdesc: Shows a map that allows page scrolling unless two fingers or Cmd/Ctrl are used to pan the map.
docs: >
This example shows a common behavior for page scrolling: on touch devices, when one finger
is placed on the map, it can be used to scroll the page. Only two fingers will perform drag pan.
For mouse or trackpad devices, the platform modifier key (Cmd or Ctrl) will enable drag pan on
the map, otherwise the page will scroll.
tags: "trackpad, mousewheel, zoom, scroll, page"
---
<div id="map" class="map"></div>
+29
View File
@@ -0,0 +1,29 @@
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js';
import OSM from '../src/ol/source/OSM.js';
import {defaults, DragPan, MouseWheelZoom} from '../src/ol/interaction.js';
import {platformModifierKeyOnly} from '../src/ol/events/condition.js';
const map = new Map({
interactions: defaults({dragPan: false, mouseWheelZoom: false}).extend([
new DragPan({
condition: function(event) {
return this.getPointerCount() === 2 || platformModifierKeyOnly(event);
}
}),
new MouseWheelZoom({
condition: platformModifierKeyOnly
})
]),
layers: [
new TileLayer({
source: new OSM()
})
],
target: 'map',
view: new View({
center: [0, 0],
zoom: 2
})
});
+6 -1
View File
@@ -5,7 +5,11 @@ shortdesc: Select features from vector tiles.
docs: > docs: >
<p> <p>
Click a rendered vector-tile feature to highlight it on the map. Click on an empty spot (ocean) to reset the selection. Click a rendered vector-tile feature to highlight it on the map. Click on an empty spot (ocean) to reset the selection.
By changing the action type to "Multi Select" you can select multiple features at a time. By changing the action type to "Multi Select" you can select multiple features at a time. With "Single Select on hover",
features will be higlighted when the pointer is above them.
</p><p>
The selection layer is configured with `renderMode: 'vector'` for better performance on frequent redraws,
i.e. when 'Single select on hover' is selected.
</p> </p>
tags: "vector tiles, selection" tags: "vector tiles, selection"
--- ---
@@ -15,5 +19,6 @@ tags: "vector tiles, selection"
<select id="type" class="form-control"> <select id="type" class="form-control">
<option value="singleselect" selected>Single Select</option> <option value="singleselect" selected>Single Select</option>
<option value="multiselect">Multi Select</option> <option value="multiselect">Multi Select</option>
<option value="singleselect-hover">Single Select on hover</option>
</select> </select>
</form> </form>
+42 -20
View File
@@ -8,6 +8,25 @@ import {Fill, Stroke, Style} from '../src/ol/style.js';
// lookup for selection objects // lookup for selection objects
let selection = {}; let selection = {};
const country = new Style({
stroke: new Stroke({
color: 'gray',
width: 1
}),
fill: new Fill({
color: 'rgba(20,20,20,0.9)'
})
});
const selectedCountry = new Style({
stroke: new Stroke({
color: 'rgba(200,20,20,0.8)',
width: 2
}),
fill: new Fill({
color: 'rgba(200,20,20,0.4)'
})
});
const vtLayer = new VectorTileLayer({ const vtLayer = new VectorTileLayer({
declutter: true, declutter: true,
source: new VectorTileSource({ source: new VectorTileSource({
@@ -18,18 +37,7 @@ const vtLayer = new VectorTileLayer({
url: 'https://ahocevar.com/geoserver/gwc/service/tms/1.0.0/' + url: 'https://ahocevar.com/geoserver/gwc/service/tms/1.0.0/' +
'ne:ne_10m_admin_0_countries@EPSG%3A900913@pbf/{z}/{x}/{-y}.pbf' 'ne:ne_10m_admin_0_countries@EPSG%3A900913@pbf/{z}/{x}/{-y}.pbf'
}), }),
style: function(feature) { style: country
const selected = !!selection[feature.getId()];
return new Style({
stroke: new Stroke({
color: selected ? 'rgba(200,20,20,0.8)' : 'gray',
width: selected ? 2 : 1
}),
fill: new Fill({
color: selected ? 'rgba(200,20,20,0.2)' : 'rgba(20,20,20,0.9)'
})
});
}
}); });
const map = new Map({ const map = new Map({
@@ -39,18 +47,34 @@ const map = new Map({
target: 'map', target: 'map',
view: new View({ view: new View({
center: [0, 0], center: [0, 0],
zoom: 2 zoom: 2,
multiWorld: true
}) })
}); });
// Selection
const selectionLayer = new VectorTileLayer({
map: map,
renderMode: 'vector',
source: vtLayer.getSource(),
style: function(feature) {
if (feature.getId() in selection) {
return selectedCountry;
}
}
});
const selectElement = document.getElementById('type'); const selectElement = document.getElementById('type');
map.on('click', function(event) { map.on(['click', 'pointermove'], function(event) {
if (selectElement.value === 'singleselect-hover' && event.type !== 'pointermove' ||
selectElement.value !== 'singleselect-hover' && event.type === 'pointermove') {
return;
}
vtLayer.getFeatures(event.pixel).then(function(features) { vtLayer.getFeatures(event.pixel).then(function(features) {
if (!features.length) { if (!features.length) {
selection = {}; selection = {};
// force redraw of layer style selectionLayer.changed();
vtLayer.setStyle(vtLayer.getStyle());
return; return;
} }
const feature = features[0]; const feature = features[0];
@@ -59,14 +83,12 @@ map.on('click', function(event) {
} }
const fid = feature.getId(); const fid = feature.getId();
if (selectElement.value === 'singleselect') { if (selectElement.value.startsWith('singleselect')) {
selection = {}; selection = {};
} }
// add selected feature to lookup // add selected feature to lookup
selection[fid] = feature; selection[fid] = feature;
// force redraw of layer style selectionLayer.changed();
vtLayer.setStyle(vtLayer.getStyle());
}); });
}); });
+62 -13
View File
@@ -3,19 +3,67 @@ layout: example.html
title: WebGL points layer title: WebGL points layer
shortdesc: Using a WebGL-optimized layer to render a large quantities of points shortdesc: Using a WebGL-optimized layer to render a large quantities of points
docs: > docs: >
<p>This example shows how to use a <code>WebGLPointsLayer</code> to show a large amount of points on the map.</p> This example shows how to use a `WebGLPointsLayer` to show a large amount of points on the map.
<p>The layer is given a style in JSON format which allows a certain level of customization of the final reprensentation.</p> The layer is given a style in JSON format which allows a certain level of customization of the final reprensentation.
<p>
The following operators can be used for numerical values: The following operators can be used:
<ul>
<li><code>["get", "attributeName"]</code> fetches a numeric attribute value for each feature</li> * Reading operators:
<li><code>["+", value, value]</code> adds two values (which an either be numeric, or the result of another operator)</li> * `['get', 'attributeName']` fetches a feature attribute (it will be prefixed by `a_` in the shader)
<li><code>["*", value, value]</code> multiplies two values</li> Note: those will be taken from the attributes provided to the renderer
<li><code>["clamp", value, min, max]</code> outputs a value between <code>min</code> and <code>max</code></li> * `['var', 'varName']` fetches a value from the style variables, or 0 if undefined
<li><code>["stretch", value, low1, high1, low2, high2]</code> outputs a value which has been mapped from the * `['time']` returns the time in seconds since the creation of the layer
<code>low1..high1</code> range to the <code>low2..high2</code> range</li> * `['zoom']` returns the current zoom level
</ul> * `['resolution']` returns the current resolution
</p>
* Math operators:
* `['*', value1, value2]` multiplies `value1` by `value2`
* `['/', value1, value2]` divides `value1` by `value2`
* `['+', value1, value2]` adds `value1` and `value2`
* `['-', value1, value2]` subtracts `value2` from `value1`
* `['clamp', value, low, high]` clamps `value` between `low` and `high`
* `['%', value1, value2]` returns the result of `value1 % value2` (modulo)
* `['^', value1, value2]` returns the value of `value1` raised to the `value2` power
* Transform operators:
* `['case', condition1, output1, ...conditionN, outputN, fallback]` selects the first output whose corresponding
condition evaluates to `true`. If no match is found, returns the `fallback` value.
All conditions should be `boolean`, output and fallback can be any kind.
* `['match', input, match1, output1, ...matchN, outputN, fallback]` compares the `input` value against all
provided `matchX` values, returning the output associated with the first valid match. If no match is found,
returns the `fallback` value.
`input` and `matchX` values must all be of the same type, and can be `number` or `string`. `outputX` and
`fallback` values must be of the same type, and can be of any kind.
* `['interpolate', interpolation, input, stop1, output1, ...stopN, outputN]` returns a value by interpolating between
pairs of inputs and outputs; `interpolation` can either be `['linear']` or `['exponential', base]` where `base` is
the rate of increase from stop A to stop B (i.e. power to which the interpolation ratio is raised); a value
of 1 is equivalent to `['linear']`.
`input` and `stopX` values must all be of type `number`. `outputX` values can be `number` or `color` values.
Note: `input` will be clamped between `stop1` and `stopN`, meaning that all output values will be comprised
between `output1` and `outputN`.
* Logical operators:
* `['<', value1, value2]` returns `true` if `value1` is strictly lower than `value2`, or `false` otherwise.
* `['<=', value1, value2]` returns `true` if `value1` is lower than or equals `value2`, or `false` otherwise.
* `['>', value1, value2]` returns `true` if `value1` is strictly greater than `value2`, or `false` otherwise.
* `['>=', value1, value2]` returns `true` if `value1` is greater than or equals `value2`, or `false` otherwise.
* `['==', value1, value2]` returns `true` if `value1` equals `value2`, or `false` otherwise.
* `['!=', value1, value2]` returns `true` if `value1` does not equal `value2`, or `false` otherwise.
* `['!', value1]` returns `false` if `value1` is `true` or greater than `0`, or `true` otherwise.
* `['between', value1, value2, value3]` returns `true` if `value1` is contained between `value2` and `value3`
(inclusively), or `false` otherwise.
* Conversion operators:
* `['array', value1, ...valueN]` creates a numerical array from `number` values; please note that the amount of
values can currently only be 2, 3 or 4.
* `['color', red, green, blue, alpha]` creates a `color` value from `number` values; the `alpha` parameter is
optional; if not specified, it will be set to 1.
Note: `red`, `green` and `blue` components must be values between 0 and 255; `alpha` between 0 and 1.
Values can either be literals or another operator, as they will be evaluated recursively.
Literal values can be of the following types:
* `boolean`
* `number`
* `string`
tags: "webgl, point, layer, feature" tags: "webgl, point, layer, feature"
experimental: true experimental: true
@@ -29,6 +77,7 @@ experimental: true
<option value="triangles-latitude">Triangles, color related to latitude</option> <option value="triangles-latitude">Triangles, color related to latitude</option>
<option value="circles">Circles, size related to population</option> <option value="circles">Circles, size related to population</option>
<option value="circles-zoom">Circles, size related to zoom</option> <option value="circles-zoom">Circles, size related to zoom</option>
<option value="rotating-bars">Rotating bars</option>
</select> </select>
<textarea style="width: 100%; height: 20rem; font-family: monospace; font-size: small;" id="style-editor"></textarea> <textarea style="width: 100%; height: 20rem; font-family: monospace; font-size: small;" id="style-editor"></textarea>
<small> <small>
+36
View File
@@ -95,6 +95,35 @@ const predefinedStyles = {
offset: [0, 0], offset: [0, 0],
opacity: 0.95 opacity: 0.95
} }
},
'rotating-bars': {
symbol: {
symbolType: 'square',
rotation: ['*', [
'time'
], 0.1],
size: ['array', 4, [
'interpolate',
['linear'],
['get', 'population'],
20000, 4,
300000, 28]
],
color: [
'interpolate',
['linear'],
['get', 'population'],
20000, '#ffdc00',
300000, '#ff5b19'
],
offset: ['array', 0, [
'interpolate',
['linear'],
['get', 'population'],
20000, 2,
300000, 14]
]
}
} }
}; };
@@ -167,3 +196,10 @@ function onSelectChange() {
} }
onSelectChange(); onSelectChange();
select.addEventListener('change', onSelectChange); select.addEventListener('change', onSelectChange);
// animate the map
function animate() {
map.render();
window.requestAnimationFrame(animate);
}
animate();
+13 -1
View File
@@ -138,9 +138,13 @@ ExampleBuilder.prototype.apply = function(compiler) {
.filter(chunk => chunk.names[0] !== this.common); .filter(chunk => chunk.names[0] !== this.common);
const exampleData = []; const exampleData = [];
const uniqueTags = new Set();
const promises = chunks.map(async chunk => { const promises = chunks.map(async chunk => {
const [assets, data] = await this.render(compiler.context, chunk); const [assets, data] = await this.render(compiler.context, chunk);
// collect tags for main page... TODO: implement index tag links
data.tags.forEach(tag => uniqueTags.add(tag));
exampleData.push({ exampleData.push({
link: data.filename, link: data.filename,
example: data.filename, example: data.filename,
@@ -158,7 +162,8 @@ ExampleBuilder.prototype.apply = function(compiler) {
const info = { const info = {
examples: exampleData, examples: exampleData,
index: createWordIndex(exampleData) index: createWordIndex(exampleData),
tags: Array.from(uniqueTags)
}; };
const indexSource = `var info = ${JSON.stringify(info)}`; const indexSource = `var info = ${JSON.stringify(info)}`;
@@ -182,6 +187,13 @@ ExampleBuilder.prototype.render = async function(dir, chunk) {
data.olVersion = pkg.version; data.olVersion = pkg.version;
data.filename = htmlName; data.filename = htmlName;
// process tags
if (data.tags) {
data.tags = data.tags.replace(/[\s"]+/g, '').split(',');
} else {
data.tags = [];
}
// add in script tag // add in script tag
const jsName = `${name}.js`; const jsName = `${name}.js`;
let jsSource = getJsSource(chunk, path.join('.', jsName)); let jsSource = getJsSource(chunk, path.join('.', jsName));
+1 -1
View File
@@ -12,4 +12,4 @@ tags: "GetLegendGraphic, getLegendUrl, WMS"
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
Legend: Legend:
<div><img id="legend" src=""/></div> <div><img id="legend"/></div>
+3 -3
View File
@@ -4,9 +4,9 @@ title: Constrained Zoom
shortdesc: Example of a zoom constrained view. shortdesc: Example of a zoom constrained view.
docs: > docs: >
This map has a view that is constrained between zoom levels 9 and 13. This is done using the `minZoom` and `maxZoom` view options. This map has a view that is constrained between zoom levels 9 and 13. This is done using the `minZoom` and `maxZoom` view options.
tags: "bing, zoom, minZoom, maxZoom" tags: "maptiler, zoom, minZoom, maxZoom"
cloak: cloak:
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5 - key: get_your_own_D6rA4zTHduk6KOKTXzGB
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here value: Get your own API key at https://www.maptiler.com/cloud/
--- ---
<div id="map" class="map"></div> <div id="map" class="map"></div>
+8 -4
View File
@@ -1,16 +1,20 @@
import Map from '../src/ol/Map.js'; import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js'; import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js'; import TileLayer from '../src/ol/layer/Tile.js';
import BingMaps from '../src/ol/source/BingMaps.js'; import XYZ from '../src/ol/source/XYZ.js';
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">&copy; MapTiler</a> ' +
'<a href="https://www.openstreetmap.org/copyright" target="_blank">&copy; OpenStreetMap contributors</a>';
const map = new Map({ const map = new Map({
target: 'map', target: 'map',
layers: [ layers: [
new TileLayer({ new TileLayer({
source: new BingMaps({ source: new XYZ({
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5', attributions: attributions,
imagerySet: 'Aerial' url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key
}) })
}) })
], ],
+2851 -1183
View File
File diff suppressed because it is too large Load Diff
+10 -10
View File
@@ -36,7 +36,7 @@
"url": "https://github.com/openlayers/openlayers/issues" "url": "https://github.com/openlayers/openlayers/issues"
}, },
"dependencies": { "dependencies": {
"@openlayers/pepjs": "^0.5.3", "elm-pep": "^1.0.2",
"pbf": "3.2.1", "pbf": "3.2.1",
"pixelworks": "1.1.0", "pixelworks": "1.1.0",
"rbush": "^3.0.1" "rbush": "^3.0.1"
@@ -55,7 +55,7 @@
"chaikin-smooth": "^1.0.4", "chaikin-smooth": "^1.0.4",
"clean-css-cli": "4.3.0", "clean-css-cli": "4.3.0",
"copy-webpack-plugin": "^5.0.4", "copy-webpack-plugin": "^5.0.4",
"coveralls": "3.0.7", "coveralls": "3.0.9",
"eslint": "^6.0.0", "eslint": "^6.0.0",
"eslint-config-openlayers": "^13.0.0", "eslint-config-openlayers": "^13.0.0",
"expect.js": "0.3.1", "expect.js": "0.3.1",
@@ -63,7 +63,7 @@
"fs-extra": "^8.0.0", "fs-extra": "^8.0.0",
"glob": "^7.1.5", "glob": "^7.1.5",
"globby": "^10.0.0", "globby": "^10.0.0",
"handlebars": "4.4.5", "handlebars": "4.5.3",
"html-to-image": "^0.1.0", "html-to-image": "^0.1.0",
"istanbul": "0.4.5", "istanbul": "0.4.5",
"istanbul-instrumenter-loader": "^3.0.1", "istanbul-instrumenter-loader": "^3.0.1",
@@ -79,13 +79,13 @@
"karma-sourcemap-loader": "^0.3.7", "karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^4.0.0-rc.2", "karma-webpack": "^4.0.0-rc.2",
"loglevelnext": "^3.0.1", "loglevelnext": "^3.0.1",
"marked": "0.7.0", "marked": "0.8.0",
"mocha": "6.2.2", "mocha": "7.0.0",
"ol-mapbox-style": "^5.0.2", "ol-mapbox-style": "^5.0.2",
"pixelmatch": "^5.1.0", "pixelmatch": "^5.1.0",
"pngjs": "^3.4.0", "pngjs": "^3.4.0",
"proj4": "2.5.0", "proj4": "2.6.0",
"puppeteer": "~1.20.0", "puppeteer": "~2.0.0",
"rollup": "^1.25.1", "rollup": "^1.25.1",
"rollup-plugin-babel": "^4.3.3", "rollup-plugin-babel": "^4.3.3",
"rollup-plugin-commonjs": "^10.0.0", "rollup-plugin-commonjs": "^10.0.0",
@@ -93,16 +93,16 @@
"rollup-plugin-terser": "^5.0.0", "rollup-plugin-terser": "^5.0.0",
"serve-static": "^1.14.0", "serve-static": "^1.14.0",
"shx": "^0.3.2", "shx": "^0.3.2",
"sinon": "^7.3.2", "sinon": "^8.0.0",
"terser-webpack-plugin": "^2.0.1", "terser-webpack-plugin": "^2.0.1",
"typescript": "3.5.3", "typescript": "3.5.3",
"url-polyfill": "^1.1.5", "url-polyfill": "^1.1.5",
"walk": "^2.3.9", "walk": "^2.3.9",
"webpack": "4.41.2", "webpack": "4.41.5",
"webpack-cli": "^3.3.2", "webpack-cli": "^3.3.2",
"webpack-dev-middleware": "^3.6.2", "webpack-dev-middleware": "^3.6.2",
"webpack-dev-server": "^3.3.1", "webpack-dev-server": "^3.3.1",
"yargs": "^14.2.0" "yargs": "^15.0.2"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "openlayers", "extends": "openlayers",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

@@ -0,0 +1,31 @@
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import VectorTileSource from '../../../src/ol/source/VectorTile.js';
import MVT from '../../../src/ol/format/MVT.js';
import {createXYZ} from '../../../src/ol/tilegrid.js';
import VectorTileLayer from '../../../src/ol/layer/VectorTile.js';
import VectorTileRenderType from '../../../src/ol/layer/VectorTileRenderType.js';
new Map({
layers: [
new VectorTileLayer({
renderMode: VectorTileRenderType.VECTOR,
source: new VectorTileSource({
format: new MVT(),
tileGrid: createXYZ(),
url: '/data/tiles/mapbox-streets-v6/{z}/{x}/{y}.vector.pbf',
transition: 0
})
})
],
target: 'map',
view: new View({
center: [1825927.7316762917, 6143091.089223046],
zoom: 14
})
});
render({
message: 'Vector tile layer renders with vector render mode',
tolerance: 0.01
});
Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 23 KiB

-14
View File
@@ -58,20 +58,6 @@ class ImageTile extends Tile {
} }
/**
* @inheritDoc
*/
disposeInternal() {
if (this.state == TileState.LOADING) {
this.unlistenImage_();
this.image_ = getBlankImage();
}
if (this.interimTile) {
this.interimTile.dispose();
}
super.disposeInternal();
}
/** /**
* Get the HTML image element for this tile (may be a Canvas, Image, or Video). * Get the HTML image element for this tile (may be a Canvas, Image, or Video).
* @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image. * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
+33 -2
View File
@@ -2,13 +2,14 @@
* @module ol/MapBrowserEventHandler * @module ol/MapBrowserEventHandler
*/ */
import '@openlayers/pepjs'; import 'elm-pep';
import {DEVICE_PIXEL_RATIO} from './has.js'; import {DEVICE_PIXEL_RATIO, PASSIVE_EVENT_LISTENERS} from './has.js';
import MapBrowserEventType from './MapBrowserEventType.js'; import MapBrowserEventType from './MapBrowserEventType.js';
import MapBrowserPointerEvent from './MapBrowserPointerEvent.js'; import MapBrowserPointerEvent from './MapBrowserPointerEvent.js';
import {listen, unlistenByKey} from './events.js'; import {listen, unlistenByKey} from './events.js';
import EventTarget from './events/Target.js'; import EventTarget from './events/Target.js';
import PointerEventType from './pointer/EventType.js'; import PointerEventType from './pointer/EventType.js';
import EventType from './events/EventType.js';
class MapBrowserEventHandler extends EventTarget { class MapBrowserEventHandler extends EventTarget {
@@ -84,6 +85,12 @@ class MapBrowserEventHandler extends EventTarget {
PointerEventType.POINTERDOWN, PointerEventType.POINTERDOWN,
this.handlePointerDown_, this); this.handlePointerDown_, this);
/**
* @type {PointerEvent}
* @private
*/
this.originalPointerMoveEvent_;
/** /**
* @type {?import("./events.js").EventsKey} * @type {?import("./events.js").EventsKey}
* @private * @private
@@ -92,6 +99,13 @@ class MapBrowserEventHandler extends EventTarget {
PointerEventType.POINTERMOVE, PointerEventType.POINTERMOVE,
this.relayEvent_, this); this.relayEvent_, this);
/**
* @private
*/
this.boundHandleTouchMove_ = this.handleTouchMove_.bind(this);
this.element_.addEventListener(EventType.TOUCHMOVE, this.boundHandleTouchMove_,
PASSIVE_EVENT_LISTENERS ? {passive: false} : false);
} }
/** /**
@@ -246,11 +260,26 @@ class MapBrowserEventHandler extends EventTarget {
* @private * @private
*/ */
relayEvent_(pointerEvent) { relayEvent_(pointerEvent) {
this.originalPointerMoveEvent_ = pointerEvent;
const dragging = !!(this.down_ && this.isMoving_(pointerEvent)); const dragging = !!(this.down_ && this.isMoving_(pointerEvent));
this.dispatchEvent(new MapBrowserPointerEvent( this.dispatchEvent(new MapBrowserPointerEvent(
pointerEvent.type, this.map_, pointerEvent, dragging)); pointerEvent.type, this.map_, pointerEvent, dragging));
} }
/**
* Flexible handling of a `touch-action: none` css equivalent: because calling
* `preventDefault()` on a `pointermove` event does not stop native page scrolling
* and zooming, we also listen for `touchmove` and call `preventDefault()` on it
* when an interaction (currently `DragPan` handles the event.
* @param {TouchEvent} event Event.
* @private
*/
handleTouchMove_(event) {
if (this.originalPointerMoveEvent_.defaultPrevented) {
event.preventDefault();
}
}
/** /**
* @param {PointerEvent} pointerEvent Pointer * @param {PointerEvent} pointerEvent Pointer
* event. * event.
@@ -271,6 +300,8 @@ class MapBrowserEventHandler extends EventTarget {
unlistenByKey(this.relayedListenerKey_); unlistenByKey(this.relayedListenerKey_);
this.relayedListenerKey_ = null; this.relayedListenerKey_ = null;
} }
this.element_.removeEventListener(EventType.TOUCHMOVE, this.boundHandleTouchMove_);
if (this.pointerdownListenerKey_) { if (this.pointerdownListenerKey_) {
unlistenByKey(this.pointerdownListenerKey_); unlistenByKey(this.pointerdownListenerKey_);
this.pointerdownListenerKey_ = null; this.pointerdownListenerKey_ = null;
+33 -41
View File
@@ -1,7 +1,6 @@
/** /**
* @module ol/PluggableMap * @module ol/PluggableMap
*/ */
import {getUid} from './util.js';
import Collection from './Collection.js'; import Collection from './Collection.js';
import CollectionEventType from './CollectionEventType.js'; import CollectionEventType from './CollectionEventType.js';
import MapBrowserEvent from './MapBrowserEvent.js'; import MapBrowserEvent from './MapBrowserEvent.js';
@@ -22,7 +21,7 @@ import {listen, unlistenByKey} from './events.js';
import EventType from './events/EventType.js'; import EventType from './events/EventType.js';
import {clone, createOrUpdateEmpty, equals, getForViewAndSize, isEmpty} from './extent.js'; import {clone, createOrUpdateEmpty, equals, getForViewAndSize, isEmpty} from './extent.js';
import {TRUE} from './functions.js'; import {TRUE} from './functions.js';
import {DEVICE_PIXEL_RATIO, IMAGE_DECODE} from './has.js'; import {DEVICE_PIXEL_RATIO, IMAGE_DECODE, PASSIVE_EVENT_LISTENERS} from './has.js';
import LayerGroup from './layer/Group.js'; import LayerGroup from './layer/Group.js';
import {hasArea} from './size.js'; import {hasArea} from './size.js';
import {DROP} from './structs/PriorityQueue.js'; import {DROP} from './structs/PriorityQueue.js';
@@ -131,17 +130,6 @@ import {toUserCoordinate, fromUserCoordinate} from './proj.js';
*/ */
/**
* @param {HTMLElement} element Element.
* @param {string} touchAction Value for `touch-action'.
*/
function setTouchAction(element, touchAction) {
element.style.msTouchAction = touchAction;
element.style.touchAction = touchAction;
element.setAttribute('touch-action', touchAction);
}
/** /**
* @fires import("./MapBrowserEvent.js").MapBrowserEvent * @fires import("./MapBrowserEvent.js").MapBrowserEvent
* @fires import("./MapEvent.js").MapEvent * @fires import("./MapEvent.js").MapEvent
@@ -305,15 +293,10 @@ class PluggableMap extends BaseObject {
*/ */
this.keyHandlerKeys_ = null; this.keyHandlerKeys_ = null;
/**
* @private
* @type {?Array<import("./events.js").EventsKey>}
*/
this.focusHandlerKeys_ = null;
const handleBrowserEvent = this.handleBrowserEvent.bind(this); const handleBrowserEvent = this.handleBrowserEvent.bind(this);
this.viewport_.addEventListener(EventType.CONTEXTMENU, handleBrowserEvent, false); this.viewport_.addEventListener(EventType.CONTEXTMENU, handleBrowserEvent, false);
this.viewport_.addEventListener(EventType.WHEEL, handleBrowserEvent, false); this.viewport_.addEventListener(EventType.WHEEL, handleBrowserEvent,
PASSIVE_EVENT_LISTENERS ? {passive: false} : false);
/** /**
* @type {Collection<import("./control/Control.js").default>} * @type {Collection<import("./control/Control.js").default>}
@@ -949,12 +932,14 @@ class PluggableMap extends BaseObject {
// coordinates so interactions cannot be used. // coordinates so interactions cannot be used.
return; return;
} }
let target = mapBrowserEvent.originalEvent.target; let target = /** @type {Node} */ (mapBrowserEvent.originalEvent.target);
while (target instanceof HTMLElement) { if (!mapBrowserEvent.dragging) {
if (target.parentElement === this.overlayContainerStopEvent_) { while (target && target !== this.viewport_) {
return; if (target.parentElement === this.overlayContainerStopEvent_) {
return;
}
target = target.parentElement;
} }
target = target.parentElement;
} }
mapBrowserEvent.frameState = this.frameState_; mapBrowserEvent.frameState = this.frameState_;
const interactionsArray = this.getInteractions().getArray(); const interactionsArray = this.getInteractions().getArray();
@@ -1043,12 +1028,6 @@ class PluggableMap extends BaseObject {
targetElement = this.getTargetElement(); targetElement = this.getTargetElement();
} }
if (this.focusHandlerKeys_) {
for (let i = 0, ii = this.focusHandlerKeys_.length; i < ii; ++i) {
unlistenByKey(this.focusHandlerKeys_[i]);
}
this.focusHandlerKeys_ = null;
}
if (this.keyHandlerKeys_) { if (this.keyHandlerKeys_) {
for (let i = 0, ii = this.keyHandlerKeys_.length; i < ii; ++i) { for (let i = 0, ii = this.keyHandlerKeys_.length; i < ii; ++i) {
unlistenByKey(this.keyHandlerKeys_[i]); unlistenByKey(this.keyHandlerKeys_[i]);
@@ -1077,15 +1056,6 @@ class PluggableMap extends BaseObject {
if (!this.renderer_) { if (!this.renderer_) {
this.renderer_ = this.createRenderer(); this.renderer_ = this.createRenderer();
} }
let hasFocus = true;
if (targetElement.hasAttribute('tabindex')) {
hasFocus = document.activeElement === targetElement;
this.focusHandlerKeys_ = [
listen(targetElement, EventType.FOCUS, setTouchAction.bind(this, this.viewport_, 'none')),
listen(targetElement, EventType.BLUR, setTouchAction.bind(this, this.viewport_, 'auto'))
];
}
setTouchAction(this.viewport_, hasFocus ? 'none' : 'auto');
const keyboardEventTarget = !this.keyboardEventTarget_ ? const keyboardEventTarget = !this.keyboardEventTarget_ ?
targetElement : this.keyboardEventTarget_; targetElement : this.keyboardEventTarget_;
@@ -1133,7 +1103,8 @@ class PluggableMap extends BaseObject {
} }
const view = this.getView(); const view = this.getView();
if (view) { if (view) {
this.viewport_.setAttribute('data-view', getUid(view)); this.updateViewportSize_();
this.viewPropertyListenerKey_ = listen( this.viewPropertyListenerKey_ = listen(
view, ObjectEventType.PROPERTYCHANGE, view, ObjectEventType.PROPERTYCHANGE,
this.handleViewPropertyChanged_, this); this.handleViewPropertyChanged_, this);
@@ -1391,6 +1362,27 @@ class PluggableMap extends BaseObject {
parseFloat(computedStyle['borderBottomWidth']) parseFloat(computedStyle['borderBottomWidth'])
]); ]);
} }
this.updateViewportSize_();
}
/**
* Recomputes the viewport size and save it on the view object (if any)
* @private
*/
updateViewportSize_() {
const view = this.getView();
if (view) {
let size = undefined;
const computedStyle = getComputedStyle(this.viewport_);
if (computedStyle.width && computedStyle.height) {
size = [
parseInt(computedStyle.width, 10),
parseInt(computedStyle.height, 10)
];
}
view.setViewportSize(size);
}
} }
} }
+2 -3
View File
@@ -145,10 +145,9 @@ class Tile extends EventTarget {
} }
/** /**
* @inheritDoc * Called by the tile cache when the tile is removed from the cache due to expiry
*/ */
disposeInternal() { release() {
this.setState(TileState.ABORT);
} }
/** /**
+2 -12
View File
@@ -6,15 +6,6 @@ import {fromKey, getKey} from './tilecoord.js';
class TileCache extends LRUCache { class TileCache extends LRUCache {
/**
* @param {number=} opt_highWaterMark High water mark.
*/
constructor(opt_highWaterMark) {
super(opt_highWaterMark);
}
/** /**
* @param {!Object<string, import("./TileRange.js").default>} usedTiles Used tiles. * @param {!Object<string, import("./TileRange.js").default>} usedTiles Used tiles.
*/ */
@@ -24,8 +15,7 @@ class TileCache extends LRUCache {
if (tile.getKey() in usedTiles) { if (tile.getKey() in usedTiles) {
break; break;
} else { } else {
// This lets the GC clean up the object this.pop().release();
this.pop();
} }
} }
} }
@@ -43,7 +33,7 @@ class TileCache extends LRUCache {
this.forEach(function(tile) { this.forEach(function(tile) {
if (tile.tileCoord[0] !== z) { if (tile.tileCoord[0] !== z) {
this.remove(getKey(tile.tileCoord)); this.remove(getKey(tile.tileCoord));
tile.dispose(); tile.release();
} }
}.bind(this)); }.bind(this));
} }
+2 -11
View File
@@ -84,8 +84,7 @@ class TileQueue extends PriorityQueue {
handleTileChange(event) { handleTileChange(event) {
const tile = /** @type {import("./Tile.js").default} */ (event.target); const tile = /** @type {import("./Tile.js").default} */ (event.target);
const state = tile.getState(); const state = tile.getState();
if (tile.hifi && state === TileState.LOADED || state === TileState.ERROR || if (tile.hifi && state === TileState.LOADED || state === TileState.ERROR || state === TileState.EMPTY) {
state === TileState.EMPTY || state === TileState.ABORT) {
tile.removeEventListener(EventType.CHANGE, this.boundHandleTileChange_); tile.removeEventListener(EventType.CHANGE, this.boundHandleTileChange_);
const tileKey = tile.getKey(); const tileKey = tile.getKey();
if (tileKey in this.tilesLoadingKeys_) { if (tileKey in this.tilesLoadingKeys_) {
@@ -102,27 +101,19 @@ class TileQueue extends PriorityQueue {
*/ */
loadMoreTiles(maxTotalLoading, maxNewLoads) { loadMoreTiles(maxTotalLoading, maxNewLoads) {
let newLoads = 0; let newLoads = 0;
let abortedTiles = false;
let state, tile, tileKey; let state, tile, tileKey;
while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads && while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads &&
this.getCount() > 0) { this.getCount() > 0) {
tile = /** @type {import("./Tile.js").default} */ (this.dequeue()[0]); tile = /** @type {import("./Tile.js").default} */ (this.dequeue()[0]);
tileKey = tile.getKey(); tileKey = tile.getKey();
state = tile.getState(); state = tile.getState();
if (state === TileState.ABORT) { if (state === TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) {
abortedTiles = true;
} else if (state === TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) {
this.tilesLoadingKeys_[tileKey] = true; this.tilesLoadingKeys_[tileKey] = true;
++this.tilesLoading_; ++this.tilesLoading_;
++newLoads; ++newLoads;
tile.load(); tile.load();
} }
} }
if (newLoads === 0 && abortedTiles) {
// Do not stop the render loop when all wanted tiles were aborted due to
// a small, saturated tile cache.
this.tileChangeCallback_();
}
} }
} }
+1 -2
View File
@@ -14,6 +14,5 @@ export default {
* @type {number} * @type {number}
*/ */
ERROR: 3, ERROR: 3,
EMPTY: 4, EMPTY: 4
ABORT: 5
}; };
+25 -47
View File
@@ -4,7 +4,6 @@
import {getUid} from './util.js'; import {getUid} from './util.js';
import Tile from './Tile.js'; import Tile from './Tile.js';
import {createCanvasContext2D} from './dom.js'; import {createCanvasContext2D} from './dom.js';
import {unlistenByKey} from './events.js';
/** /**
@@ -19,6 +18,10 @@ import {unlistenByKey} from './events.js';
* @property {number} renderedTileZ * @property {number} renderedTileZ
*/ */
/**
* @type {Array<HTMLCanvasElement>}
*/
const canvasPool = [];
class VectorRenderTile extends Tile { class VectorRenderTile extends Tile {
@@ -26,13 +29,10 @@ class VectorRenderTile extends Tile {
* @param {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate. * @param {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate.
* @param {import("./TileState.js").default} state State. * @param {import("./TileState.js").default} state State.
* @param {import("./tilecoord.js").TileCoord} urlTileCoord Wrapped tile coordinate for source urls. * @param {import("./tilecoord.js").TileCoord} urlTileCoord Wrapped tile coordinate for source urls.
* @param {import("./tilegrid/TileGrid.js").default} sourceTileGrid Tile grid of the source.
* @param {function(VectorRenderTile):Array<import("./VectorTile").default>} getSourceTiles Function * @param {function(VectorRenderTile):Array<import("./VectorTile").default>} getSourceTiles Function
* to get an source tiles for this tile. * to get source tiles for this tile.
* @param {function(VectorRenderTile):void} removeSourceTiles Function to remove this tile from its
* source tiles's consumer count.
*/ */
constructor(tileCoord, state, urlTileCoord, sourceTileGrid, getSourceTiles, removeSourceTiles) { constructor(tileCoord, state, urlTileCoord, getSourceTiles) {
super(tileCoord, state, {transition: 0}); super(tileCoord, state, {transition: 0});
@@ -61,9 +61,9 @@ class VectorRenderTile extends Tile {
this.errorSourceTileKeys = {}; this.errorSourceTileKeys = {};
/** /**
* @type {ImageData} * @type {Object<number, ImageData>}
*/ */
this.hitDetectionImageData = null; this.hitDetectionImageData = {};
/** /**
* @private * @private
@@ -71,6 +71,11 @@ class VectorRenderTile extends Tile {
*/ */
this.replayState_ = {}; this.replayState_ = {};
/**
* @type {Array<import("./VectorTile.js").default>}
*/
this.sourceTiles = null;
/** /**
* @type {number} * @type {number}
*/ */
@@ -79,23 +84,7 @@ class VectorRenderTile extends Tile {
/** /**
* @type {!function():Array<import("./VectorTile.js").default>} * @type {!function():Array<import("./VectorTile.js").default>}
*/ */
this.getSourceTiles = getSourceTiles.bind(this, this); this.getSourceTiles = getSourceTiles.bind(undefined, this);
/**
* @type {!function(import("./VectorRenderTile.js").default):void}
*/
this.removeSourceTiles_ = removeSourceTiles;
/**
* @private
* @type {import("./tilegrid/TileGrid.js").default}
*/
this.sourceTileGrid_ = sourceTileGrid;
/**
* @type {Array<import("./events.js").EventsKey>}
*/
this.sourceTileListenerKeys = [];
/** /**
* z of the source tiles of the last getSourceTiles call. * z of the source tiles of the last getSourceTiles call.
@@ -115,27 +104,6 @@ class VectorRenderTile extends Tile {
this.wrappedTileCoord = urlTileCoord; this.wrappedTileCoord = urlTileCoord;
} }
/**
* @inheritDoc
*/
disposeInternal() {
this.sourceTileListenerKeys.forEach(unlistenByKey);
this.sourceTileListenerKeys.length = 0;
this.removeSourceTiles_(this);
for (const key in this.context_) {
const canvas = this.context_[key].canvas;
canvas.width = 0;
canvas.height = 0;
}
for (const key in this.executorGroups) {
const executorGroups = this.executorGroups[key];
for (let i = 0, ii = executorGroups.length; i < ii; ++i) {
executorGroups[i].disposeInternal();
}
}
super.disposeInternal();
}
/** /**
* @param {import("./layer/Layer.js").default} layer Layer. * @param {import("./layer/Layer.js").default} layer Layer.
* @return {CanvasRenderingContext2D} The rendering context. * @return {CanvasRenderingContext2D} The rendering context.
@@ -143,7 +111,7 @@ class VectorRenderTile extends Tile {
getContext(layer) { getContext(layer) {
const key = getUid(layer); const key = getUid(layer);
if (!(key in this.context_)) { if (!(key in this.context_)) {
this.context_[key] = createCanvasContext2D(); this.context_[key] = createCanvasContext2D(1, 1, canvasPool);
} }
return this.context_[key]; return this.context_[key];
} }
@@ -192,6 +160,16 @@ class VectorRenderTile extends Tile {
load() { load() {
this.getSourceTiles(); this.getSourceTiles();
} }
/**
* @inheritDoc
*/
release() {
for (const key in this.context_) {
canvasPool.push(this.context_[key].canvas);
}
super.release();
}
} }
+3 -6
View File
@@ -18,11 +18,6 @@ class VectorTile extends Tile {
super(tileCoord, state, opt_options); super(tileCoord, state, opt_options);
/**
* @type {number}
*/
this.consumers = 0;
/** /**
* Extent of this tile; set by the source. * Extent of this tile; set by the source.
* @type {import("./extent.js").Extent} * @type {import("./extent.js").Extent}
@@ -105,7 +100,9 @@ class VectorTile extends Tile {
if (this.state == TileState.IDLE) { if (this.state == TileState.IDLE) {
this.setState(TileState.LOADING); this.setState(TileState.LOADING);
this.tileLoadFunction_(this, this.url_); this.tileLoadFunction_(this, this.url_);
this.loader_(this.extent, this.resolution, this.projection); if (this.loader_) {
this.loader_(this.extent, this.resolution, this.projection);
}
} }
} }
+63 -35
View File
@@ -2,7 +2,6 @@
* @module ol/View * @module ol/View
*/ */
import {DEFAULT_TILE_SIZE} from './tilegrid/common.js'; import {DEFAULT_TILE_SIZE} from './tilegrid/common.js';
import {getUid} from './util.js';
import {VOID} from './functions.js'; import {VOID} from './functions.js';
import {createExtent, none as centerNone} from './centerconstraint.js'; import {createExtent, none as centerNone} from './centerconstraint.js';
import BaseObject from './Object.js'; import BaseObject from './Object.js';
@@ -294,6 +293,12 @@ class View extends BaseObject {
*/ */
this.projection_ = createProjection(options.projection, 'EPSG:3857'); this.projection_ = createProjection(options.projection, 'EPSG:3857');
/**
* @private
* @type {import("./size.js").Size}
*/
this.viewportSize_ = [100, 100];
/** /**
* @private * @private
* @type {import("./coordinate.js").Coordinate|undefined} * @type {import("./coordinate.js").Coordinate|undefined}
@@ -312,6 +317,12 @@ class View extends BaseObject {
*/ */
this.targetRotation_; this.targetRotation_;
/**
* @private
* @type {import("./coordinate.js").Coordinate|undefined}
*/
this.cancelAnchor_ = undefined;
if (options.center) { if (options.center) {
options.center = fromUserCoordinate(options.center, this.projection_); options.center = fromUserCoordinate(options.center, this.projection_);
} }
@@ -584,13 +595,19 @@ class View extends BaseObject {
*/ */
cancelAnimations() { cancelAnimations() {
this.setHint(ViewHint.ANIMATING, -this.hints_[ViewHint.ANIMATING]); this.setHint(ViewHint.ANIMATING, -this.hints_[ViewHint.ANIMATING]);
let anchor;
for (let i = 0, ii = this.animations_.length; i < ii; ++i) { for (let i = 0, ii = this.animations_.length; i < ii; ++i) {
const series = this.animations_[i]; const series = this.animations_[i];
if (series[0].callback) { if (series[0].callback) {
animationCallback(series[0].callback, false); animationCallback(series[0].callback, false);
} }
anchor = anchor ||
series.filter(function(animation) {
return !animation.complete;
})[0].anchor;
} }
this.animations_.length = 0; this.animations_.length = 0;
this.cancelAnchor_ = anchor;
} }
/** /**
@@ -637,7 +654,7 @@ class View extends BaseObject {
animation.targetResolution : animation.targetResolution :
animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution); animation.sourceResolution + progress * (animation.targetResolution - animation.sourceResolution);
if (animation.anchor) { if (animation.anchor) {
const size = this.getSizeFromViewport_(this.getRotation()); const size = this.getViewportSize_(this.getRotation());
const constrainedResolution = this.constraints_.resolution(resolution, 0, size, true); const constrainedResolution = this.constraints_.resolution(resolution, 0, size, true);
this.targetCenter_ = this.calculateCenterZoom(constrainedResolution, animation.anchor); this.targetCenter_ = this.calculateCenterZoom(constrainedResolution, animation.anchor);
} }
@@ -710,26 +727,33 @@ class View extends BaseObject {
} }
/** /**
* Returns the current viewport size.
* @private * @private
* @param {number=} opt_rotation Take into account the rotation of the viewport when giving the size * @param {number=} opt_rotation Take into account the rotation of the viewport when giving the size
* @return {import("./size.js").Size} Viewport size or `[100, 100]` when no viewport is found. * @return {import("./size.js").Size} Viewport size or `[100, 100]` when no viewport is found.
*/ */
getSizeFromViewport_(opt_rotation) { getViewportSize_(opt_rotation) {
const size = [100, 100]; const size = this.viewportSize_;
const selector = '.ol-viewport[data-view="' + getUid(this) + '"]';
const element = document.querySelector(selector);
if (element) {
const metrics = getComputedStyle(element);
size[0] = parseInt(metrics.width, 10);
size[1] = parseInt(metrics.height, 10);
}
if (opt_rotation) { if (opt_rotation) {
const w = size[0]; const w = size[0];
const h = size[1]; const h = size[1];
size[0] = Math.abs(w * Math.cos(opt_rotation)) + Math.abs(h * Math.sin(opt_rotation)); return [
size[1] = Math.abs(w * Math.sin(opt_rotation)) + Math.abs(h * Math.cos(opt_rotation)); Math.abs(w * Math.cos(opt_rotation)) + Math.abs(h * Math.sin(opt_rotation)),
Math.abs(w * Math.sin(opt_rotation)) + Math.abs(h * Math.cos(opt_rotation))
];
} else {
return size;
} }
return size; }
/**
* Stores the viewport size on the view. The viewport size is not read every time from the DOM
* to avoid performance hit and layout reflow.
* This should be done on map size change.
* @param {import("./size.js").Size=} opt_size Viewport size; if undefined, [100, 100] is assumed
*/
setViewportSize(opt_size) {
this.viewportSize_ = Array.isArray(opt_size) ? opt_size.slice() : [100, 100];
} }
/** /**
@@ -780,8 +804,8 @@ class View extends BaseObject {
* The size is the pixel dimensions of the box into which the calculated extent * The size is the pixel dimensions of the box into which the calculated extent
* should fit. In most cases you want to get the extent of the entire map, * should fit. In most cases you want to get the extent of the entire map,
* that is `map.getSize()`. * that is `map.getSize()`.
* @param {import("./size.js").Size=} opt_size Box pixel size. If not provided, the size of the * @param {import("./size.js").Size=} opt_size Box pixel size. If not provided, the size
* first map that uses this view will be used. * of the map that uses this view will be used.
* @return {import("./extent.js").Extent} Extent. * @return {import("./extent.js").Extent} Extent.
* @api * @api
*/ */
@@ -796,7 +820,7 @@ class View extends BaseObject {
* @return {import("./extent.js").Extent} Extent. * @return {import("./extent.js").Extent} Extent.
*/ */
calculateExtentInternal(opt_size) { calculateExtentInternal(opt_size) {
const size = opt_size || this.getSizeFromViewport_(); const size = opt_size || this.getViewportSize_();
const center = /** @type {!import("./coordinate.js").Coordinate} */ (this.getCenterInternal()); const center = /** @type {!import("./coordinate.js").Coordinate} */ (this.getCenterInternal());
assert(center, 1); // The view center is not defined assert(center, 1); // The view center is not defined
const resolution = /** @type {!number} */ (this.getResolution()); const resolution = /** @type {!number} */ (this.getResolution());
@@ -919,7 +943,7 @@ class View extends BaseObject {
* the given size. * the given size.
*/ */
getResolutionForExtentInternal(extent, opt_size) { getResolutionForExtentInternal(extent, opt_size) {
const size = opt_size || this.getSizeFromViewport_(); const size = opt_size || this.getViewportSize_();
const xResolution = getWidth(extent) / size[0]; const xResolution = getWidth(extent) / size[0];
const yResolution = getHeight(extent) / size[1]; const yResolution = getHeight(extent) / size[1];
return Math.max(xResolution, yResolution); return Math.max(xResolution, yResolution);
@@ -933,7 +957,7 @@ class View extends BaseObject {
*/ */
getResolutionForValueFunction(opt_power) { getResolutionForValueFunction(opt_power) {
const power = opt_power || 2; const power = opt_power || 2;
const maxResolution = this.maxResolution_; const maxResolution = this.getConstrainedResolution(this.maxResolution_);
const minResolution = this.minResolution_; const minResolution = this.minResolution_;
const max = Math.log(maxResolution / minResolution) / Math.log(power); const max = Math.log(maxResolution / minResolution) / Math.log(power);
return ( return (
@@ -964,17 +988,17 @@ class View extends BaseObject {
* @return {function(number): number} Value for resolution function. * @return {function(number): number} Value for resolution function.
*/ */
getValueForResolutionFunction(opt_power) { getValueForResolutionFunction(opt_power) {
const power = opt_power || 2; const logPower = Math.log(opt_power || 2);
const maxResolution = this.maxResolution_; const maxResolution = this.getConstrainedResolution(this.maxResolution_);
const minResolution = this.minResolution_; const minResolution = this.minResolution_;
const max = Math.log(maxResolution / minResolution) / Math.log(power); const max = Math.log(maxResolution / minResolution) / logPower;
return ( return (
/** /**
* @param {number} resolution Resolution. * @param {number} resolution Resolution.
* @return {number} Value. * @return {number} Value.
*/ */
function(resolution) { function(resolution) {
const value = (Math.log(maxResolution / resolution) / Math.log(power)) / max; const value = (Math.log(maxResolution / resolution) / logPower) / max;
return value; return value;
}); });
} }
@@ -1067,7 +1091,7 @@ class View extends BaseObject {
* @api * @api
*/ */
fit(geometryOrExtent, opt_options) { fit(geometryOrExtent, opt_options) {
const options = assign({size: this.getSizeFromViewport_()}, opt_options || {}); const options = assign({size: this.getViewportSize_()}, opt_options || {});
/** @type {import("./geom/SimpleGeometry.js").default} */ /** @type {import("./geom/SimpleGeometry.js").default} */
let geometry; let geometry;
@@ -1085,7 +1109,7 @@ class View extends BaseObject {
} else { } else {
const userProjection = getUserProjection(); const userProjection = getUserProjection();
if (userProjection) { if (userProjection) {
geometry = /** @type {import("./geom/SimpleGeometry.js").default} */ (geometry.clone().transform(userProjection, this.getProjection())); geometry = /** @type {import("./geom/SimpleGeometry.js").default} */ (geometryOrExtent.clone().transform(userProjection, this.getProjection()));
} else { } else {
geometry = geometryOrExtent; geometry = geometryOrExtent;
} }
@@ -1102,7 +1126,7 @@ class View extends BaseObject {
const options = opt_options || {}; const options = opt_options || {};
let size = options.size; let size = options.size;
if (!size) { if (!size) {
size = this.getSizeFromViewport_(); size = this.getViewportSize_();
} }
const padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0]; const padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0];
const nearest = options.nearest !== undefined ? options.nearest : false; const nearest = options.nearest !== undefined ? options.nearest : false;
@@ -1249,10 +1273,10 @@ class View extends BaseObject {
*/ */
adjustResolutionInternal(ratio, opt_anchor) { adjustResolutionInternal(ratio, opt_anchor) {
const isMoving = this.getAnimating() || this.getInteracting(); const isMoving = this.getAnimating() || this.getInteracting();
const size = this.getSizeFromViewport_(this.getRotation()); const size = this.getViewportSize_(this.getRotation());
const newResolution = this.constraints_.resolution(this.targetResolution_ * ratio, 0, size, isMoving); const newResolution = this.constraints_.resolution(this.targetResolution_ * ratio, 0, size, isMoving);
if (opt_anchor !== undefined) { if (opt_anchor) {
this.targetCenter_ = this.calculateCenterZoom(newResolution, opt_anchor); this.targetCenter_ = this.calculateCenterZoom(newResolution, opt_anchor);
} }
@@ -1292,7 +1316,7 @@ class View extends BaseObject {
adjustRotationInternal(delta, opt_anchor) { adjustRotationInternal(delta, opt_anchor) {
const isMoving = this.getAnimating() || this.getInteracting(); const isMoving = this.getAnimating() || this.getInteracting();
const newRotation = this.constraints_.rotation(this.targetRotation_ + delta, isMoving); const newRotation = this.constraints_.rotation(this.targetRotation_ + delta, isMoving);
if (opt_anchor !== undefined) { if (opt_anchor) {
this.targetCenter_ = this.calculateCenterRotate(newRotation, opt_anchor); this.targetCenter_ = this.calculateCenterRotate(newRotation, opt_anchor);
} }
this.targetRotation_ += delta; this.targetRotation_ += delta;
@@ -1373,7 +1397,7 @@ class View extends BaseObject {
// compute rotation // compute rotation
const newRotation = this.constraints_.rotation(this.targetRotation_, isMoving); const newRotation = this.constraints_.rotation(this.targetRotation_, isMoving);
const size = this.getSizeFromViewport_(newRotation); const size = this.getViewportSize_(newRotation);
const newResolution = this.constraints_.resolution(this.targetResolution_, 0, size, isMoving); const newResolution = this.constraints_.resolution(this.targetResolution_, 0, size, isMoving);
const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size, isMoving); const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size, isMoving);
@@ -1390,6 +1414,7 @@ class View extends BaseObject {
if (this.getAnimating() && !opt_doNotCancelAnims) { if (this.getAnimating() && !opt_doNotCancelAnims) {
this.cancelAnimations(); this.cancelAnimations();
} }
this.cancelAnchor_ = undefined;
} }
/** /**
@@ -1406,11 +1431,11 @@ class View extends BaseObject {
const direction = opt_resolutionDirection || 0; const direction = opt_resolutionDirection || 0;
const newRotation = this.constraints_.rotation(this.targetRotation_); const newRotation = this.constraints_.rotation(this.targetRotation_);
const size = this.getSizeFromViewport_(newRotation); const size = this.getViewportSize_(newRotation);
const newResolution = this.constraints_.resolution(this.targetResolution_, direction, size); const newResolution = this.constraints_.resolution(this.targetResolution_, direction, size);
const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size); const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size);
if (duration === 0) { if (duration === 0 && !this.cancelAnchor_) {
this.targetResolution_ = newResolution; this.targetResolution_ = newResolution;
this.targetRotation_ = newRotation; this.targetRotation_ = newRotation;
this.targetCenter_ = newCenter; this.targetCenter_ = newCenter;
@@ -1418,6 +1443,9 @@ class View extends BaseObject {
return; return;
} }
const anchor = opt_anchor || (duration === 0 ? this.cancelAnchor_ : undefined);
this.cancelAnchor_ = undefined;
if (this.getResolution() !== newResolution || if (this.getResolution() !== newResolution ||
this.getRotation() !== newRotation || this.getRotation() !== newRotation ||
!this.getCenterInternal() || !this.getCenterInternal() ||
@@ -1433,7 +1461,7 @@ class View extends BaseObject {
resolution: newResolution, resolution: newResolution,
duration: duration, duration: duration,
easing: easeOut, easing: easeOut,
anchor: opt_anchor anchor: anchor
}); });
} }
} }
@@ -1484,7 +1512,7 @@ class View extends BaseObject {
* @return {import("./coordinate.js").Coordinate|undefined} Valid center position. * @return {import("./coordinate.js").Coordinate|undefined} Valid center position.
*/ */
getConstrainedCenter(targetCenter, opt_targetResolution) { getConstrainedCenter(targetCenter, opt_targetResolution) {
const size = this.getSizeFromViewport_(this.getRotation()); const size = this.getViewportSize_(this.getRotation());
return this.constraints_.center(targetCenter, opt_targetResolution || this.getResolution(), size); return this.constraints_.center(targetCenter, opt_targetResolution || this.getResolution(), size);
} }
@@ -1513,7 +1541,7 @@ class View extends BaseObject {
*/ */
getConstrainedResolution(targetResolution, opt_direction) { getConstrainedResolution(targetResolution, opt_direction) {
const direction = opt_direction || 0; const direction = opt_direction || 0;
const size = this.getSizeFromViewport_(this.getRotation()); const size = this.getViewportSize_(this.getRotation());
return this.constraints_.resolution(targetResolution, direction, size); return this.constraints_.resolution(targetResolution, direction, size);
} }
+2 -1
View File
@@ -1,12 +1,13 @@
/** /**
* @module ol/control/MousePosition * @module ol/control/MousePosition
*/ */
import 'elm-pep';
import {listen} from '../events.js'; import {listen} from '../events.js';
import EventType from '../pointer/EventType.js'; import EventType from '../pointer/EventType.js';
import {getChangeEventType} from '../Object.js'; import {getChangeEventType} from '../Object.js';
import Control from './Control.js'; import Control from './Control.js';
import {getTransformFromProjections, identityTransform, get as getProjection, getUserProjection} from '../proj.js'; import {getTransformFromProjections, identityTransform, get as getProjection, getUserProjection} from '../proj.js';
import '@openlayers/pepjs';
/** /**
+2
View File
@@ -304,6 +304,8 @@ class OverviewMap extends Control {
*/ */
bindView_(view) { bindView_(view) {
view.addEventListener(getChangeEventType(ViewProperty.ROTATION), this.boundHandleRotationChanged_); view.addEventListener(getChangeEventType(ViewProperty.ROTATION), this.boundHandleRotationChanged_);
// Sync once with the new view
this.handleRotationChanged_();
} }
/** /**
+2 -2
View File
@@ -1,6 +1,8 @@
/** /**
* @module ol/control/ZoomSlider * @module ol/control/ZoomSlider
*/ */
import 'elm-pep';
import Control from './Control.js'; import Control from './Control.js';
import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js'; import {CLASS_CONTROL, CLASS_UNSELECTABLE} from '../css.js';
import {easeOut} from '../easing.js'; import {easeOut} from '../easing.js';
@@ -9,7 +11,6 @@ import {stopPropagation} from '../events/Event.js';
import EventType from '../events/EventType.js'; import EventType from '../events/EventType.js';
import {clamp} from '../math.js'; import {clamp} from '../math.js';
import PointerEventType from '../pointer/EventType.js'; import PointerEventType from '../pointer/EventType.js';
import '@openlayers/pepjs';
/** /**
@@ -135,7 +136,6 @@ class ZoomSlider extends Control {
thumbElement.setAttribute('type', 'button'); thumbElement.setAttribute('type', 'button');
thumbElement.className = className + '-thumb ' + CLASS_UNSELECTABLE; thumbElement.className = className + '-thumb ' + CLASS_UNSELECTABLE;
const containerElement = this.element; const containerElement = this.element;
containerElement.setAttribute('touch-action', 'none');
containerElement.className = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL; containerElement.className = className + ' ' + CLASS_UNSELECTABLE + ' ' + CLASS_CONTROL;
containerElement.appendChild(thumbElement); containerElement.appendChild(thumbElement);
+4 -2
View File
@@ -7,10 +7,12 @@
* Create an html canvas element and returns its 2d context. * Create an html canvas element and returns its 2d context.
* @param {number=} opt_width Canvas width. * @param {number=} opt_width Canvas width.
* @param {number=} opt_height Canvas height. * @param {number=} opt_height Canvas height.
* @param {Array<HTMLCanvasElement>=} opt_canvasPool Canvas pool to take existing canvas from.
* @return {CanvasRenderingContext2D} The context. * @return {CanvasRenderingContext2D} The context.
*/ */
export function createCanvasContext2D(opt_width, opt_height) { export function createCanvasContext2D(opt_width, opt_height, opt_canvasPool) {
const canvas = document.createElement('canvas'); const canvas = opt_canvasPool && opt_canvasPool.length ?
opt_canvasPool.shift() : document.createElement('canvas');
if (opt_width) { if (opt_width) {
canvas.width = opt_width; canvas.width = opt_width;
} }
+1
View File
@@ -34,5 +34,6 @@ export default {
KEYPRESS: 'keypress', KEYPRESS: 'keypress',
LOAD: 'load', LOAD: 'load',
RESIZE: 'resize', RESIZE: 'resize',
TOUCHMOVE: 'touchmove',
WHEEL: 'wheel' WHEEL: 'wheel'
}; };
+3 -6
View File
@@ -207,12 +207,9 @@ export const shiftKeyOnly = function(mapBrowserEvent) {
* @api * @api
*/ */
export const targetNotEditable = function(mapBrowserEvent) { export const targetNotEditable = function(mapBrowserEvent) {
const target = mapBrowserEvent.target; const originalEvent = /** @type {KeyboardEvent|MouseEvent|TouchEvent} */ (mapBrowserEvent.originalEvent);
const tagName = /** @type {Element} */ (target).tagName; const tagName = /** @type {Element} */ (originalEvent.target).tagName;
return ( return tagName !== 'INPUT' && tagName !== 'SELECT' && tagName !== 'TEXTAREA';
tagName !== 'INPUT' &&
tagName !== 'SELECT' &&
tagName !== 'TEXTAREA');
}; };
+21 -8
View File
@@ -386,6 +386,8 @@ function createStyleDefaults() {
* @property {Array<Style>} [defaultStyle] Default style. The * @property {Array<Style>} [defaultStyle] Default style. The
* default default style is the same as Google Earth. * default default style is the same as Google Earth.
* @property {boolean} [writeStyles=true] Write styles into KML. * @property {boolean} [writeStyles=true] Write styles into KML.
* @property {null|string} [crossOrigin='anonymous'] The `crossOrigin` attribute for loaded images. Note that you must provide a
* `crossOrigin` value if you want to access pixel data with the Canvas renderer.
*/ */
@@ -458,6 +460,13 @@ class KML extends XMLFeature {
this.showPointNames_ = options.showPointNames !== undefined ? this.showPointNames_ = options.showPointNames !== undefined ?
options.showPointNames : true; options.showPointNames : true;
/**
* @private
* @type {null|string}
*/
this.crossOrigin_ = options.crossOrigin !== undefined ?
options.crossOrigin : 'anonymous';
} }
/** /**
@@ -494,7 +503,7 @@ class KML extends XMLFeature {
*/ */
readPlacemark_(node, objectStack) { readPlacemark_(node, objectStack) {
const object = pushParseAndPop({'geometry': null}, const object = pushParseAndPop({'geometry': null},
PLACEMARK_PARSERS, node, objectStack); PLACEMARK_PARSERS, node, objectStack, this);
if (!object) { if (!object) {
return undefined; return undefined;
} }
@@ -537,7 +546,7 @@ class KML extends XMLFeature {
readSharedStyle_(node, objectStack) { readSharedStyle_(node, objectStack) {
const id = node.getAttribute('id'); const id = node.getAttribute('id');
if (id !== null) { if (id !== null) {
const style = readStyle(node, objectStack); const style = readStyle.call(this, node, objectStack);
if (style) { if (style) {
let styleUri; let styleUri;
let baseURI = node.baseURI; let baseURI = node.baseURI;
@@ -565,7 +574,7 @@ class KML extends XMLFeature {
if (id === null) { if (id === null) {
return; return;
} }
const styleMapValue = readStyleMapValue(node, objectStack); const styleMapValue = readStyleMapValue.call(this, node, objectStack);
if (!styleMapValue) { if (!styleMapValue) {
return; return;
} }
@@ -1112,13 +1121,14 @@ const STYLE_MAP_PARSERS = makeStructureNS(
/** /**
* @this {KML}
* @param {Element} node Node. * @param {Element} node Node.
* @param {Array<*>} objectStack Object stack. * @param {Array<*>} objectStack Object stack.
* @return {Array<Style>|string|undefined} StyleMap. * @return {Array<Style>|string|undefined} StyleMap.
*/ */
function readStyleMapValue(node, objectStack) { function readStyleMapValue(node, objectStack) {
return pushParseAndPop(undefined, return pushParseAndPop(undefined,
STYLE_MAP_PARSERS, node, objectStack); STYLE_MAP_PARSERS, node, objectStack, this);
} }
@@ -1137,6 +1147,7 @@ const ICON_STYLE_PARSERS = makeStructureNS(
/** /**
* @this {KML}
* @param {Element} node Node. * @param {Element} node Node.
* @param {Array<*>} objectStack Object stack. * @param {Array<*>} objectStack Object stack.
*/ */
@@ -1223,7 +1234,7 @@ function iconStyleParser(node, objectStack) {
anchorOrigin: anchorOrigin, anchorOrigin: anchorOrigin,
anchorXUnits: anchorXUnits, anchorXUnits: anchorXUnits,
anchorYUnits: anchorYUnits, anchorYUnits: anchorYUnits,
crossOrigin: 'anonymous', // FIXME should this be configurable? crossOrigin: this.crossOrigin_,
offset: offset, offset: offset,
offsetOrigin: IconOrigin.BOTTOM_LEFT, offsetOrigin: IconOrigin.BOTTOM_LEFT,
rotation: rotation, rotation: rotation,
@@ -1719,13 +1730,14 @@ const STYLE_PARSERS = makeStructureNS(
/** /**
* @this {KML}
* @param {Element} node Node. * @param {Element} node Node.
* @param {Array<*>} objectStack Object stack. * @param {Array<*>} objectStack Object stack.
* @return {Array<Style>} Style. * @return {Array<Style>} Style.
*/ */
function readStyle(node, objectStack) { function readStyle(node, objectStack) {
const styleObject = pushParseAndPop( const styleObject = pushParseAndPop(
{}, STYLE_PARSERS, node, objectStack); {}, STYLE_PARSERS, node, objectStack, this);
if (!styleObject) { if (!styleObject) {
return null; return null;
} }
@@ -1885,7 +1897,7 @@ const PAIR_PARSERS = makeStructureNS(
*/ */
function pairDataParser(node, objectStack) { function pairDataParser(node, objectStack) {
const pairObject = pushParseAndPop( const pairObject = pushParseAndPop(
{}, PAIR_PARSERS, node, objectStack); {}, PAIR_PARSERS, node, objectStack, this);
if (!pairObject) { if (!pairObject) {
return; return;
} }
@@ -1907,11 +1919,12 @@ function pairDataParser(node, objectStack) {
/** /**
* @this {KML}
* @param {Element} node Node. * @param {Element} node Node.
* @param {Array<*>} objectStack Object stack. * @param {Array<*>} objectStack Object stack.
*/ */
function placemarkStyleMapParser(node, objectStack) { function placemarkStyleMapParser(node, objectStack) {
const styleMapValue = readStyleMapValue(node, objectStack); const styleMapValue = readStyleMapValue.call(this, node, objectStack);
if (!styleMapValue) { if (!styleMapValue) {
return; return;
} }
+2 -2
View File
@@ -5,7 +5,7 @@ import {abstract} from '../util.js';
import {extend} from '../array.js'; import {extend} from '../array.js';
import FeatureFormat from '../format/Feature.js'; import FeatureFormat from '../format/Feature.js';
import FormatType from '../format/FormatType.js'; import FormatType from '../format/FormatType.js';
import {isDocument, parse} from '../xml.js'; import {isDocument, parse, getXMLSerializer} from '../xml.js';
/** /**
* @classdesc * @classdesc
@@ -23,7 +23,7 @@ class XMLFeature extends FeatureFormat {
* @type {XMLSerializer} * @type {XMLSerializer}
* @private * @private
*/ */
this.xmlSerializer_ = new XMLSerializer(); this.xmlSerializer_ = getXMLSerializer();
} }
/** /**
+6 -6
View File
@@ -1,7 +1,7 @@
/** /**
* @module ol/format/xsd * @module ol/format/xsd
*/ */
import {getAllTextContent, DOCUMENT} from '../xml.js'; import {getAllTextContent, getDocument} from '../xml.js';
import {padNumber} from '../string.js'; import {padNumber} from '../string.js';
@@ -112,7 +112,7 @@ export function writeBooleanTextNode(node, bool) {
* @param {string} string String. * @param {string} string String.
*/ */
export function writeCDATASection(node, string) { export function writeCDATASection(node, string) {
node.appendChild(DOCUMENT.createCDATASection(string)); node.appendChild(getDocument().createCDATASection(string));
} }
@@ -128,7 +128,7 @@ export function writeDateTimeTextNode(node, dateTime) {
padNumber(date.getUTCHours(), 2) + ':' + padNumber(date.getUTCHours(), 2) + ':' +
padNumber(date.getUTCMinutes(), 2) + ':' + padNumber(date.getUTCMinutes(), 2) + ':' +
padNumber(date.getUTCSeconds(), 2) + 'Z'; padNumber(date.getUTCSeconds(), 2) + 'Z';
node.appendChild(DOCUMENT.createTextNode(string)); node.appendChild(getDocument().createTextNode(string));
} }
@@ -138,7 +138,7 @@ export function writeDateTimeTextNode(node, dateTime) {
*/ */
export function writeDecimalTextNode(node, decimal) { export function writeDecimalTextNode(node, decimal) {
const string = decimal.toPrecision(); const string = decimal.toPrecision();
node.appendChild(DOCUMENT.createTextNode(string)); node.appendChild(getDocument().createTextNode(string));
} }
@@ -148,7 +148,7 @@ export function writeDecimalTextNode(node, decimal) {
*/ */
export function writeNonNegativeIntegerTextNode(node, nonNegativeInteger) { export function writeNonNegativeIntegerTextNode(node, nonNegativeInteger) {
const string = nonNegativeInteger.toString(); const string = nonNegativeInteger.toString();
node.appendChild(DOCUMENT.createTextNode(string)); node.appendChild(getDocument().createTextNode(string));
} }
@@ -157,5 +157,5 @@ export function writeNonNegativeIntegerTextNode(node, nonNegativeInteger) {
* @param {string} string String. * @param {string} string String.
*/ */
export function writeStringTextNode(node, string) { export function writeStringTextNode(node, string) {
node.appendChild(DOCUMENT.createTextNode(string)); node.appendChild(getDocument().createTextNode(string));
} }
+20
View File
@@ -44,3 +44,23 @@ export const DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1;
* @type {boolean} * @type {boolean}
*/ */
export const IMAGE_DECODE = typeof Image !== 'undefined' && Image.prototype.decode; export const IMAGE_DECODE = typeof Image !== 'undefined' && Image.prototype.decode;
/**
* @type {boolean}
*/
export const PASSIVE_EVENT_LISTENERS = (function() {
let passive = false;
try {
const options = Object.defineProperty({}, 'passive', {
get: function() {
passive = true;
}
});
window.addEventListener('_', null, options);
window.removeEventListener('_', null, options);
} catch (error) {
// passive not supported
}
return passive;
})();
+4 -19
View File
@@ -3,7 +3,7 @@
*/ */
// FIXME draw drag box // FIXME draw drag box
import Event from '../events/Event.js'; import Event from '../events/Event.js';
import {always, mouseOnly, mouseActionButton} from '../events/condition.js'; import {mouseActionButton} from '../events/condition.js';
import {VOID} from '../functions.js'; import {VOID} from '../functions.js';
import PointerInteraction from './Pointer.js'; import PointerInteraction from './Pointer.js';
import RenderBox from '../render/Box.js'; import RenderBox from '../render/Box.js';
@@ -22,7 +22,7 @@ import RenderBox from '../render/Box.js';
* @property {string} [className='ol-dragbox'] CSS class name for styling the box. * @property {string} [className='ol-dragbox'] CSS class name for styling the box.
* @property {import("../events/condition.js").Condition} [condition] A function that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a boolean * @property {import("../events/condition.js").Condition} [condition] A function that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a boolean
* to indicate whether that event should be handled. * to indicate whether that event should be handled.
* Default is {@link ol/events/condition~always}. * Default is {@link ol/events/condition~mouseActionButton}.
* @property {number} [minArea=64] The minimum area of the box in pixel, this value is used by the default * @property {number} [minArea=64] The minimum area of the box in pixel, this value is used by the default
* `boxEndCondition` function. * `boxEndCondition` function.
* @property {EndCondition} [boxEndCondition] A function that takes a {@link module:ol/MapBrowserEvent~MapBrowserEvent} and two * @property {EndCondition} [boxEndCondition] A function that takes a {@link module:ol/MapBrowserEvent~MapBrowserEvent} and two
@@ -104,8 +104,6 @@ class DragBoxEvent extends Event {
* (see {@link module:ol/interaction/DragZoom~DragZoom} and * (see {@link module:ol/interaction/DragZoom~DragZoom} and
* {@link module:ol/interaction/DragRotateAndZoom}). * {@link module:ol/interaction/DragRotateAndZoom}).
* *
* This interaction is only supported for mouse devices.
*
* @fires DragBoxEvent * @fires DragBoxEvent
* @api * @api
*/ */
@@ -148,7 +146,7 @@ class DragBox extends PointerInteraction {
* @private * @private
* @type {import("../events/condition.js").Condition} * @type {import("../events/condition.js").Condition}
*/ */
this.condition_ = options.condition ? options.condition : always; this.condition_ = options.condition ? options.condition : mouseActionButton;
/** /**
* @private * @private
@@ -186,10 +184,6 @@ class DragBox extends PointerInteraction {
* @inheritDoc * @inheritDoc
*/ */
handleDragEvent(mapBrowserEvent) { handleDragEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return;
}
this.box_.setPixels(this.startPixel_, mapBrowserEvent.pixel); this.box_.setPixels(this.startPixel_, mapBrowserEvent.pixel);
this.dispatchEvent(new DragBoxEvent(DragBoxEventType.BOXDRAG, this.dispatchEvent(new DragBoxEvent(DragBoxEventType.BOXDRAG,
@@ -200,10 +194,6 @@ class DragBox extends PointerInteraction {
* @inheritDoc * @inheritDoc
*/ */
handleUpEvent(mapBrowserEvent) { handleUpEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) {
return true;
}
this.box_.setMap(null); this.box_.setMap(null);
if (this.boxEndCondition_(mapBrowserEvent, this.startPixel_, mapBrowserEvent.pixel)) { if (this.boxEndCondition_(mapBrowserEvent, this.startPixel_, mapBrowserEvent.pixel)) {
@@ -218,12 +208,7 @@ class DragBox extends PointerInteraction {
* @inheritDoc * @inheritDoc
*/ */
handleDownEvent(mapBrowserEvent) { handleDownEvent(mapBrowserEvent) {
if (!mouseOnly(mapBrowserEvent)) { if (this.condition_(mapBrowserEvent)) {
return false;
}
if (mouseActionButton(mapBrowserEvent) &&
this.condition_(mapBrowserEvent)) {
this.startPixel_ = mapBrowserEvent.pixel; this.startPixel_ = mapBrowserEvent.pixel;
this.box_.setMap(mapBrowserEvent.map); this.box_.setMap(mapBrowserEvent.map);
this.box_.setPixels(this.startPixel_, this.startPixel_); this.box_.setPixels(this.startPixel_, this.startPixel_);
+17 -2
View File
@@ -3,7 +3,7 @@
*/ */
import {scale as scaleCoordinate, rotate as rotateCoordinate} from '../coordinate.js'; import {scale as scaleCoordinate, rotate as rotateCoordinate} from '../coordinate.js';
import {easeOut} from '../easing.js'; import {easeOut} from '../easing.js';
import {noModifierKeys, primaryAction} from '../events/condition.js'; import {noModifierKeys, primaryAction, focus} from '../events/condition.js';
import {FALSE} from '../functions.js'; import {FALSE} from '../functions.js';
import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js'; import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js';
@@ -69,6 +69,20 @@ class DragPan extends PointerInteraction {
} }
/**
* @private
* @param {import("../MapBrowserEvent").default} mapBrowserEvent Event.
* @return {boolean} Condition passes.
*/
conditionInternal_(mapBrowserEvent) {
let pass = true;
if (mapBrowserEvent.map.getTargetElement().hasAttribute('tabindex')) {
pass = focus(mapBrowserEvent);
}
return pass && this.condition_(mapBrowserEvent);
}
/** /**
* @inheritDoc * @inheritDoc
*/ */
@@ -101,6 +115,7 @@ class DragPan extends PointerInteraction {
} }
this.lastCentroid = centroid; this.lastCentroid = centroid;
this.lastPointersCount_ = targetPointers.length; this.lastPointersCount_ = targetPointers.length;
mapBrowserEvent.originalEvent.preventDefault();
} }
/** /**
@@ -145,7 +160,7 @@ class DragPan extends PointerInteraction {
* @inheritDoc * @inheritDoc
*/ */
handleDownEvent(mapBrowserEvent) { handleDownEvent(mapBrowserEvent) {
if (this.targetPointers.length > 0 && this.condition_(mapBrowserEvent)) { if (this.targetPointers.length > 0 && this.conditionInternal_(mapBrowserEvent)) {
const map = mapBrowserEvent.map; const map = mapBrowserEvent.map;
const view = map.getView(); const view = map.getView();
this.lastCentroid = null; this.lastCentroid = null;
+50 -28
View File
@@ -24,6 +24,7 @@ import InteractionProperty from './Property.js';
import VectorLayer from '../layer/Vector.js'; import VectorLayer from '../layer/Vector.js';
import VectorSource from '../source/Vector.js'; import VectorSource from '../source/Vector.js';
import {createEditingStyle} from '../style/Style.js'; import {createEditingStyle} from '../style/Style.js';
import {fromUserCoordinate, getUserProjection} from '../proj.js';
/** /**
@@ -104,11 +105,12 @@ import {createEditingStyle} from '../style/Style.js';
/** /**
* Function that takes an array of coordinates and an optional existing geometry as * Function that takes an array of coordinates and an optional existing geometry
* arguments, and returns a geometry. The optional existing geometry is the * and a projection as arguments, and returns a geometry. The optional existing
* geometry that is returned when the function is called without a second * geometry is the geometry that is returned when the function is called without
* argument. * a second argument.
* @typedef {function(!SketchCoordType, import("../geom/SimpleGeometry.js").default=): * @typedef {function(!SketchCoordType, import("../geom/SimpleGeometry.js").default=,
* import("../proj/Projection.js").default):
* import("../geom/SimpleGeometry.js").default} GeometryFunction * import("../geom/SimpleGeometry.js").default} GeometryFunction
*/ */
@@ -296,14 +298,20 @@ class Draw extends PointerInteraction {
/** /**
* @param {!LineCoordType} coordinates The coordinates. * @param {!LineCoordType} coordinates The coordinates.
* @param {import("../geom/SimpleGeometry.js").default=} opt_geometry Optional geometry. * @param {import("../geom/SimpleGeometry.js").default=} opt_geometry Optional geometry.
* @param {import("../proj/Projection.js").default} projection The view projection.
* @return {import("../geom/SimpleGeometry.js").default} A geometry. * @return {import("../geom/SimpleGeometry.js").default} A geometry.
*/ */
geometryFunction = function(coordinates, opt_geometry) { geometryFunction = function(coordinates, opt_geometry, projection) {
const circle = opt_geometry ? /** @type {Circle} */ (opt_geometry) : const circle = opt_geometry ? /** @type {Circle} */ (opt_geometry) :
new Circle([NaN, NaN]); new Circle([NaN, NaN]);
const center = fromUserCoordinate(coordinates[0], projection);
const squaredLength = squaredCoordinateDistance( const squaredLength = squaredCoordinateDistance(
coordinates[0], coordinates[1]); center, fromUserCoordinate(coordinates[1], projection));
circle.setCenterAndRadius(coordinates[0], Math.sqrt(squaredLength)); circle.setCenterAndRadius(center, Math.sqrt(squaredLength));
const userProjection = getUserProjection();
if (userProjection) {
circle.transform(projection, userProjection);
}
return circle; return circle;
}; };
} else { } else {
@@ -319,9 +327,10 @@ class Draw extends PointerInteraction {
/** /**
* @param {!LineCoordType} coordinates The coordinates. * @param {!LineCoordType} coordinates The coordinates.
* @param {import("../geom/SimpleGeometry.js").default=} opt_geometry Optional geometry. * @param {import("../geom/SimpleGeometry.js").default=} opt_geometry Optional geometry.
* @param {import("../proj/Projection.js").default} projection The view projection.
* @return {import("../geom/SimpleGeometry.js").default} A geometry. * @return {import("../geom/SimpleGeometry.js").default} A geometry.
*/ */
geometryFunction = function(coordinates, opt_geometry) { geometryFunction = function(coordinates, opt_geometry, projection) {
let geometry = opt_geometry; let geometry = opt_geometry;
if (geometry) { if (geometry) {
if (mode === Mode.POLYGON) { if (mode === Mode.POLYGON) {
@@ -675,6 +684,7 @@ class Draw extends PointerInteraction {
*/ */
startDrawing_(event) { startDrawing_(event) {
const start = event.coordinate; const start = event.coordinate;
const projection = event.map.getView().getProjection();
this.finishCoordinate_ = start; this.finishCoordinate_ = start;
if (this.mode_ === Mode.POINT) { if (this.mode_ === Mode.POINT) {
this.sketchCoords_ = start.slice(); this.sketchCoords_ = start.slice();
@@ -688,7 +698,7 @@ class Draw extends PointerInteraction {
this.sketchLine_ = new Feature( this.sketchLine_ = new Feature(
new LineString(this.sketchLineCoords_)); new LineString(this.sketchLineCoords_));
} }
const geometry = this.geometryFunction_(this.sketchCoords_); const geometry = this.geometryFunction_(this.sketchCoords_, undefined, projection);
this.sketchFeature_ = new Feature(); this.sketchFeature_ = new Feature();
if (this.geometryName_) { if (this.geometryName_) {
this.sketchFeature_.setGeometryName(this.geometryName_); this.sketchFeature_.setGeometryName(this.geometryName_);
@@ -706,6 +716,7 @@ class Draw extends PointerInteraction {
modifyDrawing_(event) { modifyDrawing_(event) {
let coordinate = event.coordinate; let coordinate = event.coordinate;
const geometry = this.sketchFeature_.getGeometry(); const geometry = this.sketchFeature_.getGeometry();
const projection = event.map.getView().getProjection();
let coordinates, last; let coordinates, last;
if (this.mode_ === Mode.POINT) { if (this.mode_ === Mode.POINT) {
last = this.sketchCoords_; last = this.sketchCoords_;
@@ -722,7 +733,7 @@ class Draw extends PointerInteraction {
} }
last[0] = coordinate[0]; last[0] = coordinate[0];
last[1] = coordinate[1]; last[1] = coordinate[1];
this.geometryFunction_(/** @type {!LineCoordType} */ (this.sketchCoords_), geometry); this.geometryFunction_(/** @type {!LineCoordType} */ (this.sketchCoords_), geometry, projection);
if (this.sketchPoint_) { if (this.sketchPoint_) {
const sketchPointGeom = this.sketchPoint_.getGeometry(); const sketchPointGeom = this.sketchPoint_.getGeometry();
sketchPointGeom.setCoordinates(coordinate); sketchPointGeom.setCoordinates(coordinate);
@@ -759,6 +770,7 @@ class Draw extends PointerInteraction {
addToDrawing_(event) { addToDrawing_(event) {
const coordinate = event.coordinate; const coordinate = event.coordinate;
const geometry = this.sketchFeature_.getGeometry(); const geometry = this.sketchFeature_.getGeometry();
const projection = event.map.getView().getProjection();
let done; let done;
let coordinates; let coordinates;
if (this.mode_ === Mode.LINE_STRING) { if (this.mode_ === Mode.LINE_STRING) {
@@ -772,7 +784,7 @@ class Draw extends PointerInteraction {
} }
} }
coordinates.push(coordinate.slice()); coordinates.push(coordinate.slice());
this.geometryFunction_(coordinates, geometry); this.geometryFunction_(coordinates, geometry, projection);
} else if (this.mode_ === Mode.POLYGON) { } else if (this.mode_ === Mode.POLYGON) {
coordinates = /** @type {PolyCoordType} */ (this.sketchCoords_)[0]; coordinates = /** @type {PolyCoordType} */ (this.sketchCoords_)[0];
if (coordinates.length >= this.maxPoints_) { if (coordinates.length >= this.maxPoints_) {
@@ -786,7 +798,7 @@ class Draw extends PointerInteraction {
if (done) { if (done) {
this.finishCoordinate_ = coordinates[0]; this.finishCoordinate_ = coordinates[0];
} }
this.geometryFunction_(this.sketchCoords_, geometry); this.geometryFunction_(this.sketchCoords_, geometry, projection);
} }
this.updateSketchFeatures_(); this.updateSketchFeatures_();
if (done) { if (done) {
@@ -803,13 +815,14 @@ class Draw extends PointerInteraction {
return; return;
} }
const geometry = this.sketchFeature_.getGeometry(); const geometry = this.sketchFeature_.getGeometry();
const projection = this.getMap().getView().getProjection();
let coordinates; let coordinates;
/** @type {LineString} */ /** @type {LineString} */
let sketchLineGeom; let sketchLineGeom;
if (this.mode_ === Mode.LINE_STRING) { if (this.mode_ === Mode.LINE_STRING) {
coordinates = /** @type {LineCoordType} */ (this.sketchCoords_); coordinates = /** @type {LineCoordType} */ (this.sketchCoords_);
coordinates.splice(-2, 1); coordinates.splice(-2, 1);
this.geometryFunction_(coordinates, geometry); this.geometryFunction_(coordinates, geometry, projection);
if (coordinates.length >= 2) { if (coordinates.length >= 2) {
this.finishCoordinate_ = coordinates[coordinates.length - 2].slice(); this.finishCoordinate_ = coordinates[coordinates.length - 2].slice();
} }
@@ -818,7 +831,7 @@ class Draw extends PointerInteraction {
coordinates.splice(-2, 1); coordinates.splice(-2, 1);
sketchLineGeom = this.sketchLine_.getGeometry(); sketchLineGeom = this.sketchLine_.getGeometry();
sketchLineGeom.setCoordinates(coordinates); sketchLineGeom.setCoordinates(coordinates);
this.geometryFunction_(this.sketchCoords_, geometry); this.geometryFunction_(this.sketchCoords_, geometry, projection);
} }
if (coordinates.length === 0) { if (coordinates.length === 0) {
@@ -841,14 +854,15 @@ class Draw extends PointerInteraction {
} }
let coordinates = this.sketchCoords_; let coordinates = this.sketchCoords_;
const geometry = sketchFeature.getGeometry(); const geometry = sketchFeature.getGeometry();
const projection = this.getMap().getView().getProjection();
if (this.mode_ === Mode.LINE_STRING) { if (this.mode_ === Mode.LINE_STRING) {
// remove the redundant last point // remove the redundant last point
coordinates.pop(); coordinates.pop();
this.geometryFunction_(coordinates, geometry); this.geometryFunction_(coordinates, geometry, projection);
} else if (this.mode_ === Mode.POLYGON) { } else if (this.mode_ === Mode.POLYGON) {
// remove the redundant last point in ring // remove the redundant last point in ring
/** @type {PolyCoordType} */ (coordinates)[0].pop(); /** @type {PolyCoordType} */ (coordinates)[0].pop();
this.geometryFunction_(coordinates, geometry); this.geometryFunction_(coordinates, geometry, projection);
coordinates = geometry.getCoordinates(); coordinates = geometry.getCoordinates();
} }
@@ -881,12 +895,10 @@ class Draw extends PointerInteraction {
abortDrawing_() { abortDrawing_() {
this.finishCoordinate_ = null; this.finishCoordinate_ = null;
const sketchFeature = this.sketchFeature_; const sketchFeature = this.sketchFeature_;
if (sketchFeature) { this.sketchFeature_ = null;
this.sketchFeature_ = null; this.sketchPoint_ = null;
this.sketchPoint_ = null; this.sketchLine_ = null;
this.sketchLine_ = null; this.overlay_.getSource().clear(true);
this.overlay_.getSource().clear(true);
}
return sketchFeature; return sketchFeature;
} }
@@ -968,9 +980,9 @@ function getDefaultStyleFunction() {
* @api * @api
*/ */
export function createRegularPolygon(opt_sides, opt_angle) { export function createRegularPolygon(opt_sides, opt_angle) {
return function(coordinates, opt_geometry) { return function(coordinates, opt_geometry, projection) {
const center = /** @type {LineCoordType} */ (coordinates)[0]; const center = fromUserCoordinate(/** @type {LineCoordType} */ (coordinates)[0], projection);
const end = /** @type {LineCoordType} */ (coordinates)[1]; const end = fromUserCoordinate(/** @type {LineCoordType} */ (coordinates)[1], projection);
const radius = Math.sqrt( const radius = Math.sqrt(
squaredCoordinateDistance(center, end)); squaredCoordinateDistance(center, end));
const geometry = opt_geometry ? /** @type {Polygon} */ (opt_geometry) : const geometry = opt_geometry ? /** @type {Polygon} */ (opt_geometry) :
@@ -982,6 +994,10 @@ export function createRegularPolygon(opt_sides, opt_angle) {
angle = Math.atan(y / x) - (x < 0 ? Math.PI : 0); angle = Math.atan(y / x) - (x < 0 ? Math.PI : 0);
} }
makeRegular(geometry, center, radius, angle); makeRegular(geometry, center, radius, angle);
const userProjection = getUserProjection();
if (userProjection) {
geometry.transform(projection, userProjection);
}
return geometry; return geometry;
}; };
} }
@@ -996,8 +1012,10 @@ export function createRegularPolygon(opt_sides, opt_angle) {
*/ */
export function createBox() { export function createBox() {
return ( return (
function(coordinates, opt_geometry) { function(coordinates, opt_geometry, projection) {
const extent = boundingExtent(/** @type {LineCoordType} */ (coordinates)); const extent = boundingExtent(/** @type {LineCoordType} */ (coordinates).map(function(coordinate) {
return fromUserCoordinate(coordinate, projection);
}));
const boxCoordinates = [[ const boxCoordinates = [[
getBottomLeft(extent), getBottomLeft(extent),
getBottomRight(extent), getBottomRight(extent),
@@ -1011,6 +1029,10 @@ export function createBox() {
} else { } else {
geometry = new Polygon(boxCoordinates); geometry = new Polygon(boxCoordinates);
} }
const userProjection = getUserProjection();
if (userProjection) {
geometry.transform(projection, userProjection);
}
return geometry; return geometry;
} }
); );
+5 -4
View File
@@ -807,8 +807,8 @@ class Modify extends PointerInteraction {
if (!this.condition_(evt)) { if (!this.condition_(evt)) {
return false; return false;
} }
this.handlePointerAtPixel_(evt.pixel, evt.map);
const pixelCoordinate = evt.coordinate; const pixelCoordinate = evt.coordinate;
this.handlePointerAtPixel_(evt.pixel, evt.map, pixelCoordinate);
this.dragSegments_.length = 0; this.dragSegments_.length = 0;
this.modified_ = false; this.modified_ = false;
const vertexFeature = this.vertexFeature_; const vertexFeature = this.vertexFeature_;
@@ -916,16 +916,17 @@ class Modify extends PointerInteraction {
*/ */
handlePointerMove_(evt) { handlePointerMove_(evt) {
this.lastPixel_ = evt.pixel; this.lastPixel_ = evt.pixel;
this.handlePointerAtPixel_(evt.pixel, evt.map); this.handlePointerAtPixel_(evt.pixel, evt.map, evt.coordinate);
} }
/** /**
* @param {import("../pixel.js").Pixel} pixel Pixel * @param {import("../pixel.js").Pixel} pixel Pixel
* @param {import("../PluggableMap.js").default} map Map. * @param {import("../PluggableMap.js").default} map Map.
* @param {import("../coordinate.js").Coordinate=} opt_coordinate The pixel Coordinate.
* @private * @private
*/ */
handlePointerAtPixel_(pixel, map) { handlePointerAtPixel_(pixel, map, opt_coordinate) {
const pixelCoordinate = map.getCoordinateFromPixel(pixel); const pixelCoordinate = opt_coordinate || map.getCoordinateFromPixel(pixel);
const projection = map.getView().getProjection(); const projection = map.getView().getProjection();
const sortByDistance = function(a, b) { const sortByDistance = function(a, b) {
return projectedDistanceToSegmentDataSquared(pixelCoordinate, a, projection) - return projectedDistanceToSegmentDataSquared(pixelCoordinate, a, projection) -
+17 -3
View File
@@ -1,7 +1,7 @@
/** /**
* @module ol/interaction/MouseWheelZoom * @module ol/interaction/MouseWheelZoom
*/ */
import {always} from '../events/condition.js'; import {always, focus} from '../events/condition.js';
import EventType from '../events/EventType.js'; import EventType from '../events/EventType.js';
import {DEVICE_PIXEL_RATIO, FIREFOX} from '../has.js'; import {DEVICE_PIXEL_RATIO, FIREFOX} from '../has.js';
import Interaction, {zoomByDelta} from './Interaction.js'; import Interaction, {zoomByDelta} from './Interaction.js';
@@ -134,13 +134,27 @@ class MouseWheelZoom extends Interaction {
} }
/**
* @private
* @param {import("../MapBrowserEvent").default} mapBrowserEvent Event.
* @return {boolean} Condition passes.
*/
conditionInternal_(mapBrowserEvent) {
let pass = true;
if (mapBrowserEvent.map.getTargetElement().hasAttribute('tabindex')) {
pass = focus(mapBrowserEvent);
}
return pass && this.condition_(mapBrowserEvent);
}
/** /**
* @private * @private
*/ */
endInteraction_() { endInteraction_() {
this.trackpadTimeoutId_ = undefined; this.trackpadTimeoutId_ = undefined;
const view = this.getMap().getView(); const view = this.getMap().getView();
view.endInteraction(undefined, Math.sign(this.lastDelta_), this.lastAnchor_); view.endInteraction(undefined, this.lastDelta_ ? (this.lastDelta_ > 0 ? 1 : -1) : 0, this.lastAnchor_);
} }
/** /**
@@ -149,7 +163,7 @@ class MouseWheelZoom extends Interaction {
* @override * @override
*/ */
handleEvent(mapBrowserEvent) { handleEvent(mapBrowserEvent) {
if (!this.condition_(mapBrowserEvent)) { if (!this.conditionInternal_(mapBrowserEvent)) {
return true; return true;
} }
const type = mapBrowserEvent.type; const type = mapBrowserEvent.type;
+10 -3
View File
@@ -95,6 +95,16 @@ class PointerInteraction extends Interaction {
} }
/**
* Returns the current number of pointers involved in the interaction,
* e.g. `2` when two fingers are used.
* @return {number} The number of pointers.
* @api
*/
getPointerCount() {
return this.targetPointers.length;
}
/** /**
* Handle pointer down events. * Handle pointer down events.
* @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event. * @param {import("../MapBrowserPointerEvent.js").default} mapBrowserEvent Event.
@@ -136,9 +146,6 @@ class PointerInteraction extends Interaction {
} else { } else {
if (mapBrowserEvent.type == MapBrowserEventType.POINTERDOWN) { if (mapBrowserEvent.type == MapBrowserEventType.POINTERDOWN) {
const handled = this.handleDownEvent(mapBrowserEvent); const handled = this.handleDownEvent(mapBrowserEvent);
if (handled) {
mapBrowserEvent.preventDefault();
}
this.handlingDownUpSequence = handled; this.handlingDownUpSequence = handled;
stopEvent = this.stopDown(handled); stopEvent = this.stopDown(handled);
} else if (mapBrowserEvent.type == MapBrowserEventType.POINTERMOVE) { } else if (mapBrowserEvent.type == MapBrowserEventType.POINTERMOVE) {
+3 -1
View File
@@ -294,7 +294,9 @@ class Heatmap extends VectorLayer {
gl_FragColor.rgb *= gl_FragColor.a; gl_FragColor.rgb *= gl_FragColor.a;
}`, }`,
uniforms: { uniforms: {
u_gradientTexture: this.gradient_ u_gradientTexture: function() {
return this.gradient_;
}.bind(this)
} }
} }
] ]
+6 -3
View File
@@ -40,7 +40,9 @@ import {assign} from '../obj.js';
* * `'hybrid'`: Polygon and line elements are rendered as images, so pixels are scaled during zoom * * `'hybrid'`: Polygon and line elements are rendered as images, so pixels are scaled during zoom
* animations. Point symbols and texts are accurately rendered as vectors and can stay upright on * animations. Point symbols and texts are accurately rendered as vectors and can stay upright on
* rotated views. * rotated views.
* * * `'vector'`: Everything is rendered as vectors. Use this mode for improved performance on vector
* tile layers with only a few rendered features (e.g. for highlighting a subset of features of
* another layer with the same source).
* @property {import("../source/VectorTile.js").default} [source] Source. * @property {import("../source/VectorTile.js").default} [source] Source.
* @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage * @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage
* this layer in its layers collection, and the layer will be rendered on top. This is useful for * this layer in its layers collection, and the layer will be rendered on top. This is useful for
@@ -92,8 +94,9 @@ class VectorTileLayer extends BaseVectorLayer {
const renderMode = options.renderMode || VectorTileRenderType.HYBRID; const renderMode = options.renderMode || VectorTileRenderType.HYBRID;
assert(renderMode == undefined || assert(renderMode == undefined ||
renderMode == VectorTileRenderType.IMAGE || renderMode == VectorTileRenderType.IMAGE ||
renderMode == VectorTileRenderType.HYBRID, renderMode == VectorTileRenderType.HYBRID ||
28); // `renderMode` must be `'image'` or `'hybrid'` renderMode == VectorTileRenderType.VECTOR,
28); // `renderMode` must be `'image'`, `'hybrid'` or `'vector'`.
/** /**
* @private * @private
+6 -1
View File
@@ -11,9 +11,14 @@
* * `'hybrid'`: Polygon and line elements are rendered as images, so pixels * * `'hybrid'`: Polygon and line elements are rendered as images, so pixels
* are scaled during zoom animations. Point symbols and texts are accurately * are scaled during zoom animations. Point symbols and texts are accurately
* rendered as vectors and can stay upright on rotated views. * rendered as vectors and can stay upright on rotated views.
* * `'vector'`: Everything is rendered as vectors. Use this mode for improved
* performance on vector tile layers with only a few rendered features (e.g.
* for highlighting a subset of features of another layer with the same
* source).
* @api * @api
*/ */
export default { export default {
IMAGE: 'image', IMAGE: 'image',
HYBRID: 'hybrid' HYBRID: 'hybrid',
VECTOR: 'vector'
}; };
+1 -1
View File
@@ -24,7 +24,7 @@ import Layer from './Layer.js';
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will * @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
* be visible. * be visible.
* @property {import("../source/Vector.js").default} [source] Source. * @property {import("../source/Vector.js").default} [source] Source.
* @property {boolean} [disableHitDetection] Setting this to true will provide a slight performance boost, but will * @property {boolean} [disableHitDetection=false] Setting this to true will provide a slight performance boost, but will
* prevent all hit detection on the layer. * prevent all hit detection on the layer.
*/ */
+9
View File
@@ -74,6 +74,12 @@
user-select: none; user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0); -webkit-tap-highlight-color: rgba(0,0,0,0);
} }
.ol-overlaycontainer, .ol-overlaycontainer-stopevent {
pointer-events: none;
}
.ol-overlaycontainer > *, .ol-overlaycontainer-stopevent > * {
pointer-events: auto;
}
.ol-selectable { .ol-selectable {
-webkit-touch-callout: default; -webkit-touch-callout: default;
-webkit-user-select: text; -webkit-user-select: text;
@@ -144,6 +150,9 @@
border: none; border: none;
padding: 0; padding: 0;
} }
.ol-control button span {
pointer-events: none;
}
.ol-zoom-extent button { .ol-zoom-extent button {
line-height: 1.4em; line-height: 1.4em;
} }
+5 -17
View File
@@ -216,15 +216,12 @@ export const checkFont = (function() {
* @return {boolean} Font with style and weight is available * @return {boolean} Font with style and weight is available
*/ */
function isAvailable(fontStyle, fontWeight, fontFamily) { function isAvailable(fontStyle, fontWeight, fontFamily) {
const context = getMeasureContext();
let available = true; let available = true;
for (let i = 0; i < len; ++i) { for (let i = 0; i < len; ++i) {
const referenceFont = referenceFonts[i]; const referenceFont = referenceFonts[i];
context.font = fontStyle + ' ' + fontWeight + ' ' + size + referenceFont; referenceWidth = measureTextWidth(fontStyle + ' ' + fontWeight + ' ' + size + referenceFont, text);
referenceWidth = context.measureText(text).width;
if (fontFamily != referenceFont) { if (fontFamily != referenceFont) {
context.font = fontStyle + ' ' + fontWeight + ' ' + size + fontFamily + ',' + referenceFont; const width = measureTextWidth(fontStyle + ' ' + fontWeight + ' ' + size + fontFamily + ',' + referenceFont, text);
const width = context.measureText(text).width;
// If width and referenceWidth are the same, then the fallback was used // If width and referenceWidth are the same, then the fallback was used
// instead of the font we wanted, so the font is not available. // instead of the font we wanted, so the font is not available.
available = available && width != referenceWidth; available = available && width != referenceWidth;
@@ -284,17 +281,6 @@ export const checkFont = (function() {
})(); })();
/**
* @return {CanvasRenderingContext2D} Measure context.
*/
function getMeasureContext() {
if (!measureContext) {
measureContext = createCanvasContext2D(1, 1);
}
return measureContext;
}
/** /**
* @param {string} font Font to use for measuring. * @param {string} font Font to use for measuring.
* @return {import("../size.js").Size} Measurement. * @return {import("../size.js").Size} Measurement.
@@ -333,7 +319,9 @@ export const measureTextHeight = (function() {
* @return {number} Width. * @return {number} Width.
*/ */
export function measureTextWidth(font, text) { export function measureTextWidth(font, text) {
const measureContext = getMeasureContext(); if (!measureContext) {
measureContext = createCanvasContext2D(1, 1);
}
if (font != measureFont) { if (font != measureFont) {
measureContext.font = font; measureContext.font = font;
measureFont = measureContext.font; measureFont = measureContext.font;
+2 -13
View File
@@ -18,8 +18,7 @@ import {
} from '../../transform.js'; } from '../../transform.js';
import {createCanvasContext2D} from '../../dom.js'; import {createCanvasContext2D} from '../../dom.js';
import {labelCache, defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js'; import {labelCache, defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js';
import Disposable from '../../Disposable.js'; import RBush from 'rbush/rbush.js';
import RBush from 'rbush';
/** /**
@@ -52,7 +51,7 @@ const p3 = [];
const p4 = []; const p4 = [];
class Executor extends Disposable { class Executor {
/** /**
* @param {number} resolution Resolution. * @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio. * @param {number} pixelRatio Pixel ratio.
@@ -60,7 +59,6 @@ class Executor extends Disposable {
* @param {SerializableInstructions} instructions The serializable instructions * @param {SerializableInstructions} instructions The serializable instructions
*/ */
constructor(resolution, pixelRatio, overlaps, instructions) { constructor(resolution, pixelRatio, overlaps, instructions) {
super();
/** /**
* @protected * @protected
@@ -156,15 +154,6 @@ class Executor extends Disposable {
this.widths_ = {}; this.widths_ = {};
} }
/**
* @inheritDoc
*/
disposeInternal() {
labelCache.release(this);
super.disposeInternal();
}
/** /**
* @param {string} text Text. * @param {string} text Text.
* @param {string} textKey Text style key. * @param {string} textKey Text style key.
+1 -21
View File
@@ -10,7 +10,6 @@ import {isEmpty} from '../../obj.js';
import BuilderType from './BuilderType.js'; import BuilderType from './BuilderType.js';
import {create as createTransform, compose as composeTransform} from '../../transform.js'; import {create as createTransform, compose as composeTransform} from '../../transform.js';
import Executor from './Executor.js'; import Executor from './Executor.js';
import Disposable from '../../Disposable.js';
/** /**
* @const * @const
@@ -26,7 +25,7 @@ const ORDER = [
]; ];
class ExecutorGroup extends Disposable { class ExecutorGroup {
/** /**
* @param {import("../../extent.js").Extent} maxExtent Max extent for clipping. When a * @param {import("../../extent.js").Extent} maxExtent Max extent for clipping. When a
* `maxExtent` was set on the Buillder for this executor group, the same `maxExtent` * `maxExtent` was set on the Buillder for this executor group, the same `maxExtent`
@@ -40,7 +39,6 @@ class ExecutorGroup extends Disposable {
* @param {number=} opt_renderBuffer Optional rendering buffer. * @param {number=} opt_renderBuffer Optional rendering buffer.
*/ */
constructor(maxExtent, resolution, pixelRatio, overlaps, allInstructions, opt_renderBuffer) { constructor(maxExtent, resolution, pixelRatio, overlaps, allInstructions, opt_renderBuffer) {
super();
/** /**
* @private * @private
@@ -128,24 +126,6 @@ class ExecutorGroup extends Disposable {
} }
} }
/**
* @inheritDoc
*/
disposeInternal() {
for (const z in this.executorsByZIndex_) {
const executors = this.executorsByZIndex_[z];
for (const key in executors) {
executors[key].disposeInternal();
}
}
if (this.hitDetectionContext_) {
const canvas = this.hitDetectionContext_.canvas;
canvas.width = 0;
canvas.height = 0;
}
super.disposeInternal();
}
/** /**
* @param {Array<BuilderType>} executors Executors. * @param {Array<BuilderType>} executors Executors.
+2 -51
View File
@@ -1,4 +1,3 @@
import {getUid} from '../../util.js';
import LRUCache from '../../structs/LRUCache.js'; import LRUCache from '../../structs/LRUCache.js';
/** /**
@@ -11,59 +10,11 @@ import LRUCache from '../../structs/LRUCache.js';
*/ */
class LabelCache extends LRUCache { class LabelCache extends LRUCache {
/** expireCache() {
* @inheritDoc
*/
constructor(opt_highWaterMark) {
super(opt_highWaterMark);
this.consumers = {};
}
clear() {
this.consumers = {};
super.clear();
}
/**
* @override
* @param {string} key Label key.
* @param {import("./Executor.js").default} consumer Label consumer.
* @return {HTMLCanvasElement} Label.
*/
get(key, consumer) {
const canvas = super.get(key);
const consumerId = getUid(consumer);
if (!(consumerId in this.consumers)) {
this.consumers[consumerId] = {};
}
this.consumers[consumerId][key] = true;
return canvas;
}
prune() {
outer:
while (this.canExpireCache()) { while (this.canExpireCache()) {
const key = this.peekLastKey(); this.pop();
for (const consumerId in this.consumers) {
if (key in this.consumers[consumerId]) {
break outer;
}
}
const canvas = this.pop();
canvas.width = 0;
canvas.height = 0;
for (const consumerId in this.consumers) {
delete this.consumers[consumerId][key];
}
} }
} }
/**
* @param {import("./Executor.js").default} consumer Label consumer.
*/
release(consumer) {
delete this.consumers[getUid(consumer)];
}
} }
export default LabelCache; export default LabelCache;
+2 -3
View File
@@ -131,8 +131,6 @@ class CanvasTextBuilder extends CanvasBuilder {
* @type {string} * @type {string}
*/ */
this.strokeKey_ = ''; this.strokeKey_ = '';
labelCache.prune();
} }
/** /**
@@ -140,6 +138,7 @@ class CanvasTextBuilder extends CanvasBuilder {
*/ */
finish() { finish() {
const instructions = super.finish(); const instructions = super.finish();
labelCache.expireCache();
instructions.textStates = this.textStates; instructions.textStates = this.textStates;
instructions.fillStates = this.fillStates; instructions.fillStates = this.fillStates;
instructions.strokeStates = this.strokeStates; instructions.strokeStates = this.strokeStates;
@@ -461,7 +460,7 @@ class CanvasTextBuilder extends CanvasBuilder {
strokeState.lineCap + strokeState.lineDashOffset + '|' + strokeState.lineWidth + strokeState.lineCap + strokeState.lineDashOffset + '|' + strokeState.lineWidth +
strokeState.lineJoin + strokeState.miterLimit + '[' + strokeState.lineDash.join() + ']' : strokeState.lineJoin + strokeState.miterLimit + '[' + strokeState.lineDash.join() + ']' :
''; '';
this.textKey_ = textState.font + textState.scale + (textState.textAlign || '?'); this.textKey_ = textState.font + textState.scale + (textState.textAlign || '?') + (textState.textBaseline || '?');
this.fillKey_ = fillState ? this.fillKey_ = fillState ?
(typeof fillState.fillStyle == 'string' ? fillState.fillStyle : ('|' + getUid(fillState.fillStyle))) : (typeof fillState.fillStyle == 'string' ? fillState.fillStyle : ('|' + getUid(fillState.fillStyle))) :
''; '';
+9 -6
View File
@@ -33,15 +33,18 @@ export function createHitDetectionImageData(size, transforms, features, styleFun
const renderer = new CanvasImmediateRenderer(context, 0.5, extent, null, rotation); const renderer = new CanvasImmediateRenderer(context, 0.5, extent, null, rotation);
const featureCount = features.length; const featureCount = features.length;
// Stretch hit detection index to use the whole available color range // Stretch hit detection index to use the whole available color range
const indexFactor = Math.ceil((256 * 256 * 256) / featureCount); const indexFactor = Math.ceil((256 * 256 * 256 - 1) / featureCount);
const featuresByZIndex = {}; const featuresByZIndex = {};
for (let i = 0; i < featureCount; ++i) { for (let i = 1; i <= featureCount; ++i) {
const feature = features[i]; const feature = features[i - 1];
const featureStyleFunction = feature.getStyleFunction() || styleFunction; const featureStyleFunction = feature.getStyleFunction() || styleFunction;
if (!styleFunction) { if (!styleFunction) {
continue; continue;
} }
let styles = featureStyleFunction(feature, resolution); let styles = featureStyleFunction(feature, resolution);
if (!styles) {
continue;
}
if (!Array.isArray(styles)) { if (!Array.isArray(styles)) {
styles = [styles]; styles = [styles];
} }
@@ -138,9 +141,9 @@ export function hitDetect(pixel, features, imageData) {
const g = imageData.data[index + 1]; const g = imageData.data[index + 1];
const b = imageData.data[index + 2]; const b = imageData.data[index + 2];
const i = b + (256 * (g + (256 * r))); const i = b + (256 * (g + (256 * r)));
const indexFactor = Math.ceil((256 * 256 * 256) / features.length); const indexFactor = Math.ceil((256 * 256 * 256 - 1) / features.length);
if (i % indexFactor === 0) { if (i && i % indexFactor === 0) {
resultFeatures.push(features[i / indexFactor]); resultFeatures.push(features[i / indexFactor - 1]);
} }
} }
return resultFeatures; return resultFeatures;
+1
View File
@@ -74,6 +74,7 @@ class CompositeMapRenderer extends MapRenderer {
disposeInternal() { disposeInternal() {
unlistenByKey(this.labelCacheKey_); unlistenByKey(this.labelCacheKey_);
this.element_.parentNode.removeChild(this.element_);
super.disposeInternal(); super.disposeInternal();
} }

Some files were not shown because too many files have changed in this diff Show More